Commit 55d8c08f authored by guus's avatar guus

OF-443: No longer use the dialback namespace definition as a trigger to start...

OF-443: No longer use the dialback namespace definition as a trigger to start dialback (but use the db:verify / db:result elements instead). When the reported version of the stream is absent (prevents compatibility issues with openfire before version 3.7.1) or smaller than one, nothing should be sent on the stream before a db:verify / db:result stanza is sent (no stream features, for example). This mimics old behaviour. This way, dialback appears to be triggered by the namespace definition in the stream (even though in actuality it's triggered by a child element) which makes things backwards compatible.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@12837 b35dd754-fafc-0310-a699-88a17e54d16e
parent 6bed7b67
...@@ -335,35 +335,36 @@ public class ServerDialback { ...@@ -335,35 +335,36 @@ public class ServerDialback {
// Process the answer from the Receiving Server // Process the answer from the Receiving Server
try { try {
Element doc = socketReader.getElement(RemoteServerManager.getSocketTimeout(), while (true) {
TimeUnit.MILLISECONDS); Element doc = socketReader.getElement(RemoteServerManager.getSocketTimeout(),
if (doc == null) { TimeUnit.MILLISECONDS);
Log.debug("ServerDialback: OS - Time out waiting for answer in validation from: " + hostname + if (doc == null) {
" id: " + Log.debug("ServerDialback: OS - Time out waiting for answer in validation from: " + hostname +
id + " id: " +
" for domain: " + id +
domain); " for domain: " +
return false; domain);
} return false;
else if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) { }
boolean success = "valid".equals(doc.attributeValue("type")); else if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
Log.debug("ServerDialback: OS - Validation " + (success ? "GRANTED" : "FAILED") + " from: " + boolean success = "valid".equals(doc.attributeValue("type"));
hostname + Log.debug("ServerDialback: OS - Validation " + (success ? "GRANTED" : "FAILED") + " from: " +
" id: " + hostname +
id + " id: " +
" for domain: " + id +
domain); " for domain: " +
return success; domain);
} return success;
else { }
Log.debug("ServerDialback: OS - Unexpected answer in validation from: " + hostname + " id: " + else {
id + Log.warn("ServerDialback: OS - Ignoring unexpected answer in validation from: " + hostname + " id: " +
" for domain: " + id +
domain + " for domain: " +
" answer:" + domain +
doc.asXML()); " answer:" +
return false; doc.asXML());
} }
}
} }
catch (InterruptedException e) { catch (InterruptedException e) {
Log.debug("ServerDialback: OS - Validation FAILED from: " + hostname + Log.debug("ServerDialback: OS - Validation FAILED from: " + hostname +
...@@ -398,6 +399,7 @@ public class ServerDialback { ...@@ -398,6 +399,7 @@ public class ServerDialback {
XmlPullParser xpp = reader.getXPPParser(); XmlPullParser xpp = reader.getXPPParser();
StringBuilder sb; StringBuilder sb;
if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) { if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) {
Log.debug("ServerDialback: Processing incoming session.");
StreamID streamID = sessionManager.nextStreamID(); StreamID streamID = sessionManager.nextStreamID();
...@@ -413,9 +415,11 @@ public class ServerDialback { ...@@ -413,9 +415,11 @@ public class ServerDialback {
try { try {
Element doc = reader.parseDocument().getRootElement(); Element doc = reader.parseDocument().getRootElement();
if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) { if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
String hostname = doc.attributeValue("from");
String recipient = doc.attributeValue("to");
Log.debug("ServerDialback: RS - Validating remote domain for incoming session from {} to {}", hostname, recipient);
if (validateRemoteDomain(doc, streamID)) { if (validateRemoteDomain(doc, streamID)) {
String hostname = doc.attributeValue("from"); Log.debug("ServerDialback: RS - Validation of remote domain for incoming session from {} to {} was successful.", hostname, recipient);
String recipient = doc.attributeValue("to");
// 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);
...@@ -425,6 +429,9 @@ public class ServerDialback { ...@@ -425,6 +429,9 @@ public class ServerDialback {
// validating the session // validating the session
session.setLocalDomain(recipient); session.setLocalDomain(recipient);
return session; return session;
} else {
Log.debug("ServerDialback: RS - Validation of remote domain for incoming session from {} to {} was not successful.", hostname, recipient);
return null;
} }
} }
else if ("db".equals(doc.getNamespacePrefix()) && "verify".equals(doc.getName())) { else if ("db".equals(doc.getNamespacePrefix()) && "verify".equals(doc.getName())) {
...@@ -440,6 +447,7 @@ public class ServerDialback { ...@@ -440,6 +447,7 @@ public class ServerDialback {
return null; return null;
} }
else { else {
Log.debug("ServerDialback: Received an invalid/unknown packet while trying to process an incoming session: {}", doc.asXML());
// The remote server sent an invalid/unknown packet // The remote server sent an invalid/unknown packet
connection.deliverRawText( connection.deliverRawText(
new StreamError(StreamError.Condition.invalid_xml).toXML()); new StreamError(StreamError.Condition.invalid_xml).toXML());
...@@ -457,14 +465,13 @@ public class ServerDialback { ...@@ -457,14 +465,13 @@ public class ServerDialback {
} }
else { else {
// Include the invalid-namespace stream error condition in the response Log.debug("ServerDialback: Received a stanza in an invalid namespace while trying to process an incoming session: {}", xpp.getNamespace("db"));
connection.deliverRawText( connection.deliverRawText(
new StreamError(StreamError.Condition.invalid_namespace).toXML()); new StreamError(StreamError.Condition.invalid_namespace).toXML());
// Close the underlying connection // Close the underlying connection
connection.close(); connection.close();
return null; return null;
} }
return null;
} }
/** /**
...@@ -473,7 +480,7 @@ public class ServerDialback { ...@@ -473,7 +480,7 @@ public class ServerDialback {
* Authoritative Server. The Authoritative Server may be the same Originating Server or * Authoritative Server. The Authoritative Server may be the same Originating Server or
* some other machine in the Originating Server's network.<p> * some other machine in the Originating Server's network.<p>
* *
* If the domain was not valid or some error occured while validating the domain then the * If the domain was not valid or some error occurred while validating the domain then the
* underlying TCP connection will be closed. * underlying TCP connection will be closed.
* *
* @param doc the request for validating the new domain. * @param doc the request for validating the new domain.
......
...@@ -91,14 +91,6 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming ...@@ -91,14 +91,6 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
* 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
* <tt>null</tt>.<p> * <tt>null</tt>.<p>
* *
* Currently the Server Dialback method is the only way to authenticate a remote server. Since
* Server Dialback requires an Authoritative Server, it is possible for this server to receive
* an incoming connection that will only exist until the requested domain has been validated.
* In this case, this method will return <tt>null</tt> since the connection is closed after
* the domain was validated. See
* {@link org.jivesoftware.openfire.server.ServerDialback#createIncomingSession(org.dom4j.io.XMPPPacketReader)} for more
* information.
*
* @param serverName hostname of this server. * @param serverName hostname of this server.
* @param reader reader on the new established connection with the remote server. * @param reader reader on the new established connection with the remote server.
* @param connection the new established connection with the remote server. * @param connection the new established connection with the remote server.
...@@ -111,120 +103,99 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming ...@@ -111,120 +103,99 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader, public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader,
SocketConnection connection) throws XmlPullParserException, IOException { SocketConnection connection) throws XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser(); XmlPullParser xpp = reader.getXPPParser();
if (xpp.getNamespace("db") != null) {
if (ServerDialback.isEnabled()) {
Log.debug("Server is trying to establish connection and authenticate using server dialback: " + connection);
ServerDialback method = new ServerDialback(connection, serverName);
return method.createIncomingSession(reader);
} else {
Log.debug("Server is trying to establish connection and authenticate using server dialback, " +
"but Server Dialback is disabled by configuration. Rejecting connection: " + connection);
connection.close();
return null;
}
}
String version = xpp.getAttributeValue("", "version"); String version = xpp.getAttributeValue("", "version");
int[] serverVersion = version != null ? decodeVersion(version) : new int[] {0,0}; int[] serverVersion = version != null ? decodeVersion(version) : new int[] {0,0};
if (serverVersion[0] >= 1) {
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
try {
return createIncomingSession(connection, serverName);
}
catch (Exception e) {
Log.error("Error establishing connection from remote server:" + connection, e);
connection.close();
return null;
}
}
Log.debug("Server is not 1.0 compliant and is not using dialback to establish and " try {
+ "authenticate the connection. Rejecting connection: " // Get the stream ID for the new session
+ connection); StreamID streamID = SessionManager.getInstance().nextStreamID();
connection.close(); // Create a server Session for the remote server
return null; LocalIncomingServerSession session =
} SessionManager.getInstance().createIncomingServerSession(connection, streamID);
/** // Send the stream header
* Returns a new incoming server session pending to be authenticated. The remote server StringBuilder openingStream = new StringBuilder();
* will be notified that TLS and SASL are available. The remote server will be able to openingStream.append("<stream:stream");
* send packets to this server only after SASL authentication has been finished. openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
* openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
* @param connection the new established connection with the remote server. openingStream.append(" xmlns=\"jabber:server\"");
* @param serverName hostname of this server. openingStream.append(" from=\"").append(serverName).append("\"");
* @return a new incoming server session pending to be authenticated. openingStream.append(" id=\"").append(streamID).append("\"");
* @throws org.jivesoftware.openfire.auth.UnauthorizedException if this server is being shutdown. openingStream.append(" version=\"1.0\">");
*/ connection.deliverRawText(openingStream.toString());
private static LocalIncomingServerSession createIncomingSession(SocketConnection connection, String serverName)
throws UnauthorizedException {
// Get the stream ID for the new session
StreamID streamID = SessionManager.getInstance().nextStreamID();
// Create a server Session for the remote server
LocalIncomingServerSession session =
SessionManager.getInstance().createIncomingServerSession(connection, streamID);
// Send the stream header if (serverVersion[0] >= 1) {
StringBuilder openingStream = new StringBuilder(); // Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
openingStream.append("<stream:stream");
openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\"");
openingStream.append(" from=\"").append(serverName).append("\"");
openingStream.append(" id=\"").append(streamID).append("\"");
openingStream.append(" version=\"1.0\">");
connection.deliverRawText(openingStream.toString());
// Indicate the TLS policy to use for this connection // Indicate the TLS policy to use for this connection
Connection.TLSPolicy tlsPolicy = Connection.TLSPolicy tlsPolicy =
ServerDialback.isEnabled() ? Connection.TLSPolicy.optional : ServerDialback.isEnabled() ? Connection.TLSPolicy.optional :
Connection.TLSPolicy.required; Connection.TLSPolicy.required;
boolean hasCertificates = false; boolean hasCertificates = false;
try { try {
hasCertificates = SSLConfig.getKeyStore().size() > 0; hasCertificates = SSLConfig.getKeyStore().size() > 0;
} }
catch (Exception e) { catch (Exception e) {
Log.error(e.getMessage(), e); Log.error(e.getMessage(), e);
} }
if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) { if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
Log.error("Server session rejected. TLS is required but no certificates " + Log.error("Server session rejected. TLS is required but no certificates " +
"were created."); "were created.");
return null; return null;
} }
connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled); connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled);
}
// Indicate the compression policy to use for this connection // Indicate the compression policy to use for this connection
String policyName = JiveGlobals.getProperty("xmpp.server.compression.policy", String policyName = JiveGlobals.getProperty("xmpp.server.compression.policy",
Connection.CompressionPolicy.disabled.toString()); Connection.CompressionPolicy.disabled.toString());
Connection.CompressionPolicy compressionPolicy = Connection.CompressionPolicy compressionPolicy =
Connection.CompressionPolicy.valueOf(policyName); Connection.CompressionPolicy.valueOf(policyName);
connection.setCompressionPolicy(compressionPolicy); connection.setCompressionPolicy(compressionPolicy);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("<stream:features>");
if (JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true)) { if (serverVersion[0] >= 1) {
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">"); // Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
if (!ServerDialback.isEnabled()) { // Don't offer stream-features to pre-1.0 servers, as it confuses them. Sending features to Openfire < 3.7.1 confuses it too - OF-443)
// Server dialback is disabled so TLS is required sb.append("<stream:features>");
sb.append("<required/>");
if (JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true)) {
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
if (!ServerDialback.isEnabled()) {
// Server dialback is disabled so TLS is required
sb.append("<required/>");
}
sb.append("</starttls>");
}
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
if (ServerDialback.isEnabled()) {
// 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
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>");
}
sb.append("</stream:features>");
} }
sb.append("</starttls>");
connection.deliverRawText(sb.toString());
// Set the domain or subdomain of the local server targeted by the remote server
session.setLocalDomain(serverName);
return session;
} }
if (ServerDialback.isEnabled()) { catch (Exception e) {
// Also offer server dialback (when TLS is not required). Server dialback may be offered Log.error("Error establishing connection from remote server:" + connection, e);
// after TLS has been negotiated and a self-signed certificate is being used connection.close();
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>"); return null;
} }
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
sb.append("</stream:features>");
connection.deliverRawText(sb.toString());
// Set the domain or subdomain of the local server targeted by the remote server
session.setLocalDomain(serverName);
return session;
} }
public LocalIncomingServerSession(String serverName, Connection connection, StreamID streamID) { public LocalIncomingServerSession(String serverName, Connection connection, StreamID streamID) {
super(serverName, connection, streamID); super(serverName, connection, streamID);
} }
......
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