Commit d0f3851d authored by Dele Olajide's avatar Dele Olajide

Merge pull request #59 from surevine/dwd/s2s-external-178

More S2S fixes
parents b1dfb9c1 dc21027b
...@@ -391,12 +391,12 @@ public class SessionManager extends BasicModule implements ClusterEventListener ...@@ -391,12 +391,12 @@ public class SessionManager extends BasicModule implements ClusterEventListener
* @return the newly created {@link IncomingServerSession}. * @return the newly created {@link IncomingServerSession}.
* @throws UnauthorizedException if the local server has not been initialized yet. * @throws UnauthorizedException if the local server has not been initialized yet.
*/ */
public LocalIncomingServerSession createIncomingServerSession(Connection conn, StreamID id) public LocalIncomingServerSession createIncomingServerSession(Connection conn, StreamID id, String fromDomain)
throws UnauthorizedException { throws UnauthorizedException {
if (serverName == null) { if (serverName == null) {
throw new UnauthorizedException("Server not initialized"); throw new UnauthorizedException("Server not initialized");
} }
LocalIncomingServerSession session = new LocalIncomingServerSession(serverName, conn, id); LocalIncomingServerSession session = new LocalIncomingServerSession(serverName, conn, id, fromDomain);
conn.init(session); conn.init(session);
// Register to receive close notification on this session so we can // Register to receive close notification on this session so we can
// remove its route from the sessions set // remove its route from the sessions set
......
...@@ -193,13 +193,17 @@ public class SASLAuthentication { ...@@ -193,13 +193,17 @@ public class SASLAuthentication {
Element mechs = DocumentHelper.createElement(new QName("mechanisms", Element mechs = DocumentHelper.createElement(new QName("mechanisms",
new Namespace("", "urn:ietf:params:xml:ns:xmpp-sasl"))); new Namespace("", "urn:ietf:params:xml:ns:xmpp-sasl")));
if (session instanceof IncomingServerSession) { if (session instanceof LocalIncomingServerSession) {
// Server connections don't follow the same rules as clients // Server connections don't follow the same rules as clients
if (session.isSecure()) { if (session.isSecure()) {
boolean haveTrustedCertificate = false; boolean haveTrustedCertificate = false;
try { try {
X509Certificate trusted = CertificateManager.getEndEntityCertificate(((LocalSession)session).getConnection().getPeerCertificates(), SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore()); LocalIncomingServerSession svr = (LocalIncomingServerSession)session;
X509Certificate trusted = CertificateManager.getEndEntityCertificate(svr.getConnection().getPeerCertificates(), SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore());
haveTrustedCertificate = trusted != null; haveTrustedCertificate = trusted != null;
if (trusted != null && svr.getDefaultIdentity() != null) {
haveTrustedCertificate = verifyCertificate(trusted, svr.getDefaultIdentity());
}
} catch (IOException ex) { } catch (IOException ex) {
Log.warn("Exception occurred while trying to determine whether remote certificate is trusted. Treating as untrusted.", ex); Log.warn("Exception occurred while trying to determine whether remote certificate is trusted. Treating as untrusted.", ex);
} }
...@@ -520,6 +524,28 @@ public class SASLAuthentication { ...@@ -520,6 +524,28 @@ public class SASLAuthentication {
} }
hostname = new String(StringUtils.decodeBase64(hostname), CHARSET); hostname = new String(StringUtils.decodeBase64(hostname), CHARSET);
if (hostname.length() == 0) {
hostname = null;
}
try {
LocalIncomingServerSession svr = (LocalIncomingServerSession)session;
String defHostname = svr.getDefaultIdentity();
if (hostname == null) {
hostname = defHostname;
} else if (!hostname.equals(defHostname)) {
// Mismatch; really odd.
Log.info("SASLAuthentication rejected from='{}' and authzid='{}'", hostname, defHostname);
authenticationFailed(session, Failure.NOT_AUTHORIZED);
return Status.failed;
}
} catch(Exception e) {
// Erm. Nothing?
}
if (hostname == null) {
Log.info("No authzid supplied for anonymous session.");
authenticationFailed(session, Failure.NOT_AUTHORIZED);
return Status.failed;
}
// Check if certificate validation is disabled for s2s // Check if certificate validation is disabled for s2s
// Flag that indicates if certificates of the remote server should be validated. // Flag that indicates if certificates of the remote server should be validated.
// Disabling certificate validation is not recommended for production environments. // Disabling certificate validation is not recommended for production environments.
...@@ -552,9 +578,18 @@ public class SASLAuthentication { ...@@ -552,9 +578,18 @@ public class SASLAuthentication {
return Status.failed; return Status.failed;
} }
for (Certificate certificate : connection.getPeerCertificates()) { X509Certificate trusted;
principals.addAll(CertificateManager.getPeerIdentities((X509Certificate)certificate)); try {
trusted = CertificateManager.getEndEntityCertificate(connection.getPeerCertificates(), SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore());
} catch (IOException e) {
trusted = null;
}
if (trusted == null) {
Log.debug("SASLAuthentication: EXTERNAL authentication requested, but EE cert untrusted.");
authenticationFailed(session, Failure.NOT_AUTHORIZED);
return Status.failed;
} }
principals.addAll(CertificateManager.getPeerIdentities((X509Certificate)trusted));
if(principals.size() == 1) { if(principals.size() == 1) {
principal = principals.get(0); principal = principals.get(0);
...@@ -603,12 +638,8 @@ public class SASLAuthentication { ...@@ -603,12 +638,8 @@ public class SASLAuthentication {
return Status.failed; return Status.failed;
} }
public static boolean verifyCertificates(Certificate[] chain, String hostname) { public static boolean verifyCertificate(X509Certificate trustedCert, String hostname) {
try { for (String identity : CertificateManager.getPeerIdentities(trustedCert)) {
X509Certificate trusted = CertificateManager.getEndEntityCertificate(chain, SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore());
if (trusted != null) {
for (String identity : CertificateManager.getPeerIdentities(trusted)) {
// Verify that either the identity is the same as the hostname, or for wildcarded // Verify that either the identity is the same as the hostname, or for wildcarded
// identities that the hostname ends with .domainspecified or -is- domainspecified. // identities that the hostname ends with .domainspecified or -is- domainspecified.
if ((identity.startsWith("*.") if ((identity.startsWith("*.")
...@@ -618,6 +649,15 @@ public class SASLAuthentication { ...@@ -618,6 +649,15 @@ public class SASLAuthentication {
return true; return true;
} }
} }
return false;
}
public static boolean verifyCertificates(Certificate[] chain, String hostname) {
try {
X509Certificate trusted = CertificateManager.getEndEntityCertificate(chain, SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore());
if (trusted != null) {
return verifyCertificate(trusted, hostname);
} }
} catch(IOException e) { } catch(IOException e) {
Log.warn("Keystore issue while verifying certificate chain: {}", e.getMessage()); Log.warn("Keystore issue while verifying certificate chain: {}", e.getMessage());
......
...@@ -431,7 +431,7 @@ public class ServerDialback { ...@@ -431,7 +431,7 @@ public class ServerDialback {
Log.debug("ServerDialback: RS - Validation of remote domain for incoming session from {} to {} was successful.", hostname, recipient); Log.debug("ServerDialback: RS - Validation of remote domain for incoming session from {} to {} was successful.", hostname, recipient);
// Create a server Session for the remote server // Create a server Session for the remote server
LocalIncomingServerSession session = sessionManager. LocalIncomingServerSession session = sessionManager.
createIncomingServerSession(connection, streamID); createIncomingServerSession(connection, streamID, hostname);
// Add the validated domain as a valid domain // Add the validated domain as a valid domain
session.addValidatedDomain(hostname); session.addValidatedDomain(hostname);
// Set the domain or subdomain of the local server used when // Set the domain or subdomain of the local server used when
......
...@@ -86,6 +86,11 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -86,6 +86,11 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
*/ */
private String localDomain = null; private String localDomain = null;
/**
* Default domain, as supplied in stream header typically.
*/
private String fromDomain = null;
/** /**
* Creates a new session that will receive packets. The new session will be authenticated * Creates a new session that will receive packets. The new session will be authenticated
* before being returned. If the authentication process fails then the answer will be * before being returned. If the authentication process fails then the answer will be
...@@ -105,6 +110,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -105,6 +110,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
XmlPullParser xpp = reader.getXPPParser(); XmlPullParser xpp = reader.getXPPParser();
String version = xpp.getAttributeValue("", "version"); String version = xpp.getAttributeValue("", "version");
String fromDomain = xpp.getAttributeValue("", "from");
int[] serverVersion = version != null ? decodeVersion(version) : new int[] {0,0}; int[] serverVersion = version != null ? decodeVersion(version) : new int[] {0,0};
try { try {
...@@ -112,7 +118,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -112,7 +118,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
StreamID streamID = SessionManager.getInstance().nextStreamID(); StreamID streamID = SessionManager.getInstance().nextStreamID();
// Create a server Session for the remote server // Create a server Session for the remote server
LocalIncomingServerSession session = LocalIncomingServerSession session =
SessionManager.getInstance().createIncomingServerSession(connection, streamID); SessionManager.getInstance().createIncomingServerSession(connection, streamID, fromDomain);
// Send the stream header // Send the stream header
StringBuilder openingStream = new StringBuilder(); StringBuilder openingStream = new StringBuilder();
...@@ -121,6 +127,9 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -121,6 +127,9 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\""); openingStream.append(" xmlns=\"jabber:server\"");
openingStream.append(" from=\"").append(serverName).append("\""); openingStream.append(" from=\"").append(serverName).append("\"");
if (fromDomain != null) {
openingStream.append(" to=\"").append(fromDomain).append("\"");
}
openingStream.append(" id=\"").append(streamID).append("\""); openingStream.append(" id=\"").append(streamID).append("\"");
// OF-443: Not responding with a 1.0 version in the stream header when federating with older // OF-443: Not responding with a 1.0 version in the stream header when federating with older
...@@ -184,7 +193,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -184,7 +193,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
if (ServerDialback.isEnabled()) { if (ServerDialback.isEnabled()) {
// Also offer server dialback (when TLS is not required). Server dialback may be offered // Also offer server dialback (when TLS is not required). Server dialback may be offered
// after TLS has been negotiated and a self-signed certificate is being used // after TLS has been negotiated and a self-signed certificate is being used
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>"); sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback>");
} }
sb.append("</stream:features>"); sb.append("</stream:features>");
...@@ -204,8 +213,13 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -204,8 +213,13 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
} }
public LocalIncomingServerSession(String serverName, Connection connection, StreamID streamID) { public LocalIncomingServerSession(String serverName, Connection connection, StreamID streamID, String fromDomain) {
super(serverName, connection, streamID); super(serverName, connection, streamID);
this.fromDomain = fromDomain;
}
public String getDefaultIdentity() {
return this.fromDomain;
} }
@Override @Override
...@@ -255,7 +269,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In ...@@ -255,7 +269,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
public boolean isValidDomain(String domain) { public boolean isValidDomain(String domain) {
// Check if the specified domain is contained in any of the validated domains // Check if the specified domain is contained in any of the validated domains
for (String validatedDomain : getValidatedDomains()) { for (String validatedDomain : getValidatedDomains()) {
if (domain.contains(validatedDomain)) { if (domain.equals(validatedDomain)) {
return true; return true;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment