Commit 6aa0314b authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

Added support for server dialback over TLS and server dialback a la XMPP 1.0 (XEP-220 & XEP-238)

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10683 b35dd754-fafc-0310-a699-88a17e54d16e
parent 9f725b9a
......@@ -2417,4 +2417,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2403,4 +2403,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -460,6 +460,7 @@
## Added key: 'prelogin.setup.sidebar.title.clearspace'
## Added key: 'prelogin.setup.error.clearspace.sharedsecret'
## Added key: 'prelogin.setup.error.clearspace.connection'
## Added key: 'ssl.settings.client.label_self-signed'
......@@ -2115,6 +2116,7 @@ ssl.settings.client.label_notrequired=Optional
ssl.settings.client.label_notrequired_info=Clients may connect to the server using secured connections.
ssl.settings.client.label_custom=Custom
ssl.settings.client.label_custom_info=Advanced configuration
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
ssl.settings.client.customSSL=Old SSL method:
ssl.settings.client.customTLS=TLS method:
ssl.settings.available=Available
......
......@@ -2454,4 +2454,5 @@ setup.clearspace.service.certificate.verify.root_help=Ultimo certificado en la c
setup.clearspace.service.certificate.verify.validity=Verificar certificado no expirado
setup.clearspace.service.certificate.verify.validity_help=Certificado sea valido en el tiempo actual
reg.settings.ips_all=Restringir TODOS los ingresos de estas IPs:
reg.settings.ips_anonymous=Restringir ingresos anonimos de estas IPs:
\ No newline at end of file
reg.settings.ips_anonymous=Restringir ingresos anonimos de estas IPs:
ssl.settings.client.label_self-signed=Aceptar certificados auto-firmados. Server dialback sobre TLS esta habilitado.
\ No newline at end of file
......@@ -2023,4 +2023,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2550,4 +2550,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2408,4 +2408,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2383,4 +2383,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2417,4 +2417,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2532,4 +2532,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -2195,4 +2195,5 @@ setup.clearspace.service.certificate.verify.root_help=Last certificate in the ch
setup.clearspace.service.certificate.verify.validity=Verify certificate is not expired
setup.clearspace.service.certificate.verify.validity_help=Certificate is valid at the current time.
reg.settings.ips_all=Restrict ALL (including anonymous) logins by these IP's:
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
\ No newline at end of file
reg.settings.ips_anonymous=Restrict anonymous logins by these IP's:
ssl.settings.client.label_self-signed=Accept self-signed certificates. Server dialback over TLS is now available.
\ No newline at end of file
......@@ -90,12 +90,39 @@ public interface Connection {
public String getHostName() throws UnknownHostException;
/**
* Returns the underlying {@link javax.security.cert.X509Certificate} for the connection.
* Returns the local underlying {@link javax.security.cert.X509Certificate} for the connection.
*
* @return <tt>null</tt> if no {@link javax.security.cert.X509Certificate} is present for the connection.
*/
public Certificate[] getLocalCertificates();
/**
* Returns the underlying {@link javax.security.cert.X509Certificate} for the connection of the peer.
*
* @return <tt>null</tt> if no {@link javax.security.cert.X509Certificate} is present for the connection.
*/
public Certificate[] getPeerCertificates();
/**
* Keeps track if the other peer of this session presented a self-signed certificate. When
* using self-signed certificate for server-2-server sessions then SASL EXTERNAL will not be
* used and instead server-dialback will be preferred for vcerifying the identify of the remote
* server.
*
* @param isSelfSigned true if the other peer presented a self-signed certificate.
*/
public void setUsingSelfSignedCertificate(boolean isSelfSigned);
/**
* Returns true if the other peer of this session presented a self-signed certificate. When
* using self-signed certificate for server-2-server sessions then SASL EXTERNAL will not be
* used and instead server-dialback will be preferred for vcerifying the identify of the remote
* server.
*
* @return true if the other peer of this session presented a self-signed certificate.
*/
public boolean isUsingSelfSignedCertificate();
/**
* Close this session including associated socket connection. The order of
* events for closing the session is:
......
......@@ -27,7 +27,6 @@ import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.xmpp.packet.JID;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslException;
......@@ -123,7 +122,7 @@ public class SASLAuthentication {
*
* @return a string with the valid SASL mechanisms available for the specified session.
*/
public static String getSASLMechanisms(Session session) {
public static String getSASLMechanisms(LocalSession session) {
if (!(session instanceof ClientSession) && !(session instanceof IncomingServerSession)) {
return "";
}
......@@ -132,8 +131,23 @@ public class SASLAuthentication {
if (session instanceof IncomingServerSession) {
// Server connections dont follow the same rules as clients
if (session.isSecure()) {
// Offer SASL EXTERNAL only if TLS has already been negotiated
sb.append("<mechanism>EXTERNAL</mechanism>");
boolean usingSelfSigned = false;
Certificate[] certificates = session.getConnection().getLocalCertificates();
for (Certificate certificate : certificates) {
try {
if (CertificateManager
.isSelfSignedCertificate(SSLConfig.getKeyStore(), (X509Certificate) certificate)) {
usingSelfSigned = true;
}
} catch (Exception e) {
usingSelfSigned = true;
}
}
if (!usingSelfSigned) {
// Offer SASL EXTERNAL only if TLS has already been negotiated and we are not
// using a self-signed certificate
sb.append("<mechanism>EXTERNAL</mechanism>");
}
}
}
else {
......@@ -604,8 +618,6 @@ public class SASLAuthentication {
}
else if (session instanceof IncomingServerSession) {
String hostname = username;
// Set the first validated domain as the address of the session
session.setAddress(new JID(null, hostname, null));
// Add the validated domain as a valid domain. The remote server can
// now send packets from this address
((LocalIncomingServerSession) session).addValidatedDomain(hostname);
......
......@@ -219,6 +219,10 @@ public class ServerSocketReader extends SocketReader {
return "jabber:server";
}
public String getExtraNamespaces() {
return "xmlns:db=\"jabber:server:dialback\"";
}
String getName() {
return "Server SR - " + hashCode();
}
......
......@@ -8,6 +8,7 @@
package org.jivesoftware.openfire.net;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
......@@ -40,11 +41,16 @@ public class ServerTrustManager implements X509TrustManager {
* Holds the domain of the remote server we are trying to connect
*/
private String server;
/**
* Holds the LocalIncomingServerSession that is part of the TLS negotiation.
*/
private Connection connection;
public ServerTrustManager(String server, KeyStore trustTrust) {
public ServerTrustManager(String server, KeyStore trustTrust, Connection connection) {
super();
this.server = server;
this.trustStore = trustTrust;
this.connection = connection;
}
public void checkClientTrusted(X509Certificate[] x509Certificates,
......@@ -120,6 +126,8 @@ public class ServerTrustManager implements X509TrustManager {
boolean trusted = false;
try {
trusted = trustStore.getCertificateAlias(x509Certificates[nSize - 1]) != null;
// Keep track if the other peer presented a self-signed certificate
connection.setUsingSelfSignedCertificate(!trusted && nSize == 1);
if (!trusted && nSize == 1 && JiveGlobals
.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false))
{
......
......@@ -102,6 +102,7 @@ public class SocketConnection implements Connection {
* TLS policy currently in use for this connection.
*/
private TLSPolicy tlsPolicy = TLSPolicy.optional;
private boolean usingSelfSignedCertificate;
/**
* Compression policy currently in use for this connection.
......@@ -158,7 +159,8 @@ public class SocketConnection implements Connection {
if (!secure) {
secure = true;
// Prepare for TLS
tlsStreamHandler = new TLSStreamHandler(socket, clientMode, remoteServer, session instanceof IncomingServerSession);
tlsStreamHandler = new TLSStreamHandler(this, socket, clientMode, remoteServer,
session instanceof IncomingServerSession);
if (!clientMode) {
// Indicate the client that the server is ready to negotiate TLS
deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
......@@ -395,6 +397,13 @@ public class SocketConnection implements Connection {
this.flashClient = flashClient;
}
public Certificate[] getLocalCertificates() {
if (tlsStreamHandler != null) {
return tlsStreamHandler.getSSLSession().getLocalCertificates();
}
return new Certificate[0];
}
public Certificate[] getPeerCertificates() {
if (tlsStreamHandler != null) {
try {
......@@ -407,6 +416,14 @@ public class SocketConnection implements Connection {
return new Certificate[0];
}
public void setUsingSelfSignedCertificate(boolean isSelfSigned) {
this.usingSelfSignedCertificate = isSelfSigned;
}
public boolean isUsingSelfSignedCertificate() {
return usingSelfSignedCertificate;
}
public PacketDeliverer getPacketDeliverer() {
return backupDeliverer;
}
......
......@@ -471,4 +471,8 @@ public abstract class SocketReader implements Runnable {
*/
abstract boolean createSession(String namespace) throws UnauthorizedException,
XmlPullParserException, IOException;
public String getExtraNamespaces() {
return null;
}
}
......@@ -248,8 +248,12 @@ abstract class SocketReadingMode {
sb.append("<stream:stream ");
}
sb.append("xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"");
sb.append(socketReader.getNamespace());
sb.append("\" from=\"");
sb.append(socketReader.getNamespace()).append("\"");
if (socketReader.getExtraNamespaces() != null) {
sb.append(" ");
sb.append(socketReader.getExtraNamespaces());
}
sb.append(" from=\"");
sb.append(socketReader.session.getServerName());
sb.append("\" id=\"");
sb.append(socketReader.session.getStreamID().toString());
......
......@@ -12,6 +12,7 @@
package org.jivesoftware.openfire.net;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.util.JiveGlobals;
import javax.net.ssl.SSLEngine;
......@@ -83,6 +84,7 @@ public class TLSStreamHandler {
* <tt>remoteServer</tt> is the server name of the remote server. Otherwise <tt>clientMode</tt>
* will be <code>false</code> and <tt>remoteServer</tt> null.
*
* @param connection the connection to secure
* @param socket the plain socket connection to secure
* @param clientMode boolean indicating if this entity is a client or a server.
* @param remoteServer server name of the remote server we are connecting to or <tt>null</tt>
......@@ -92,9 +94,9 @@ public class TLSStreamHandler {
* EXTERNAL SASL is going to be used.
* @throws java.io.IOException
*/
public TLSStreamHandler(Socket socket, boolean clientMode, String remoteServer,
public TLSStreamHandler(Connection connection, Socket socket, boolean clientMode, String remoteServer,
boolean needClientAuth) throws IOException {
wrapper = new TLSWrapper(clientMode, needClientAuth, remoteServer);
wrapper = new TLSWrapper(connection, clientMode, needClientAuth, remoteServer);
tlsEngine = wrapper.getTlsEngine();
reader = new TLSStreamReader(wrapper, socket);
writer = new TLSStreamWriter(wrapper, socket);
......
......@@ -12,6 +12,7 @@
package org.jivesoftware.openfire.net;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.util.Log;
import javax.net.ssl.*;
......@@ -57,7 +58,7 @@ public class TLSWrapper {
private int netBuffSize;
private int appBuffSize;
public TLSWrapper(boolean clientMode, boolean needClientAuth, String remoteServer) {
public TLSWrapper(Connection connection, boolean clientMode, boolean needClientAuth, String remoteServer) {
boolean c2sConnection = (remoteServer == null);
if (debug) {
......@@ -85,7 +86,7 @@ public class TLSWrapper {
}
else {
// Check if we can trust certificates presented by the server
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust)};
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust, connection)};
}
}
......@@ -101,10 +102,10 @@ public class TLSWrapper {
*/
tlsEngine = tlsContext.createSSLEngine();
tlsEngine.setUseClientMode(clientMode);
SSLSession session = tlsEngine.getSession();
SSLSession sslSession = tlsEngine.getSession();
netBuffSize = session.getPacketBufferSize();
appBuffSize = session.getApplicationBufferSize();
netBuffSize = sslSession.getPacketBufferSize();
appBuffSize = sslSession.getApplicationBufferSize();
} catch (KeyManagementException e) {
Log.error("TLSHandler startup problem.\n" + " SSLContext initialisation failed.", e);
......
......@@ -58,11 +58,23 @@ public abstract class VirtualConnection implements Connection {
return 0;
}
public Certificate[] getLocalCertificates() {
// Ignore
return new Certificate[0];
}
public Certificate[] getPeerCertificates() {
// Ignore
return new Certificate[0];
}
public void setUsingSelfSignedCertificate(boolean isSelfSigned) {
}
public boolean isUsingSelfSignedCertificate() {
return false;
}
public boolean isClosed() {
if (session == null) {
return closed;
......
......@@ -72,6 +72,7 @@ public class NIOConnection implements Connection {
* TLS policy currently in use for this connection.
*/
private TLSPolicy tlsPolicy = TLSPolicy.optional;
private boolean usingSelfSignedCertificate;
/**
* Compression policy currently in use for this connection.
......@@ -131,6 +132,14 @@ public class NIOConnection implements Connection {
return ((InetSocketAddress) ioSession.getRemoteAddress()).getAddress().getHostName();
}
public Certificate[] getLocalCertificates() {
SSLSession sslSession = (SSLSession) ioSession.getAttribute(SSLFilter.SSL_SESSION);
if (sslSession != null) {
return sslSession.getLocalCertificates();
}
return new Certificate[0];
}
public Certificate[] getPeerCertificates() {
try {
SSLSession sslSession = (SSLSession) ioSession.getAttribute(SSLFilter.SSL_SESSION);
......@@ -143,6 +152,14 @@ public class NIOConnection implements Connection {
return new Certificate[0];
}
public void setUsingSelfSignedCertificate(boolean isSelfSigned) {
this.usingSelfSignedCertificate = isSelfSigned;
}
public boolean isUsingSelfSignedCertificate() {
return usingSelfSignedCertificate;
}
public PacketDeliverer getPacketDeliverer() {
return backupDeliverer;
}
......@@ -304,7 +321,7 @@ public class NIOConnection implements Connection {
tm = new TrustManager[]{new ClientTrustManager(ksTrust)};
} else {
// Check if we can trust certificates presented by the server
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust)};
tm = new TrustManager[]{new ServerTrustManager(remoteServer, ksTrust, this)};
}
}
......
......@@ -99,7 +99,8 @@ public class ServerDialback {
*
* When TLS is enabled between servers and server dialback method is enabled then TLS is going
* to be tried first, when connecting to a remote server, and if TLS fails then server dialback
* is going to be used as a last resort.
* is going to be used as a last resort. If enabled and the remote server offered server-dialback
* after TLS and no SASL EXTERNAL then server dialback will be used.
*
* @return true if server dialback is enabled.
*/
......@@ -107,6 +108,36 @@ public class ServerDialback {
return JiveGlobals.getBooleanProperty("xmpp.server.dialback.enabled", true);
}
/**
* Returns true if server dialback can be used when the remote server presented a self-signed
* certificate. During TLS the remote server can present a self-signed certificate, if this
* setting is enabled then the self-signed certificate will be accepted and if SASL EXTERNAL
* is not offered then server dialback will be used for verifying the remote server.<p>
*
* If self-signed certificates are accepted then server dialback over TLS is enabled.
*
* @return true if server dialback can be used when the remote server presented a self-signed
* certificate.
*/
public static boolean isEnabledForSelfSigned() {
return JiveGlobals.getBooleanProperty("xmpp.server.certificate.accept-selfsigned", false);
}
/**
* Sets if server dialback can be used when the remote server presented a self-signed
* certificate. During TLS the remote server can present a self-signed certificate, if this
* setting is enabled then the self-signed certificate will be accepted and if SASL EXTERNAL
* is not offered then server dialback will be used for verifying the remote server.<p>
*
* If self-signed certificates are accepted then server dialback over TLS is enabled.
*
* @param enabled if server dialback can be used when the remote server presented a self-signed
* certificate.
*/
public static void setEnabledForSelfSigned(boolean enabled) {
JiveGlobals.setProperty("xmpp.server.certificate.accept-selfsigned", Boolean.toString(enabled));
}
/**
* Creates a new instance that will be used for creating {@link IncomingServerSession},
* validating subsequent domains or authenticatig new domains. Use
......@@ -352,8 +383,6 @@ public class ServerDialback {
// Create a server Session for the remote server
LocalIncomingServerSession session = sessionManager.
createIncomingServerSession(connection, streamID);
// Set the first validated domain as the address of the session
session.setAddress(new JID(null, hostname, null));
// Add the validated domain as a valid domain
session.addValidatedDomain(hostname);
// Set the domain or subdomain of the local server used when
......
......@@ -21,14 +21,17 @@ import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SSLConfig;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.server.ServerDialback;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.StreamError;
import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
......@@ -95,32 +98,25 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader,
SocketConnection connection) throws XmlPullParserException, IOException {
XmlPullParser xpp = reader.getXPPParser();
if (xpp.getNamespace("db") != null) {
// Server is trying to establish connection and authenticate using server dialback
if (ServerDialback.isEnabled()) {
ServerDialback method = new ServerDialback(connection, serverName);
return method.createIncomingSession(reader);
}
Log.debug("LocalIncomingServerSession: Server dialback is disabled. Rejecting connection: " + connection);
}
String version = xpp.getAttributeValue("", "version");
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
if (JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true)) {
try {
return createIncomingSession(connection, serverName);
}
catch (Exception e) {
Log.error("Error establishing connection from remote server", e);
}
// Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
try {
return createIncomingSession(connection, serverName);
}
else {
connection.deliverRawText(
new StreamError(StreamError.Condition.invalid_namespace).toXML());
Log.debug("LocalIncomingServerSession: Server TLS is disabled. Rejecting connection: " + connection);
catch (Exception e) {
Log.error("Error establishing connection from remote server", e);
}
}
else if (xpp.getNamespace("db") != null) {
// Server is trying to establish connection and authenticate using server dialback (pre XMPP 1.0)
if (ServerDialback.isEnabled()) {
ServerDialback method = new ServerDialback(connection, serverName);
return method.createIncomingSession(reader);
}
Log.debug("LocalIncomingServerSession: Server dialback is disabled. Rejecting connection: " + connection);
}
// Close the connection since remote server is not XMPP 1.0 compliant and is not
// using server dialback to establish and authenticate the connection
connection.close();
......@@ -148,6 +144,7 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
// Send the stream header
StringBuilder openingStream = new StringBuilder();
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("\"");
......@@ -182,12 +179,19 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
StringBuilder sb = new StringBuilder();
sb.append("<stream:features>");
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/>");
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>");
}
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("</starttls>");
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
sb.append("</stream:features>");
......@@ -273,6 +277,10 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
*/
public void addValidatedDomain(String domain) {
if (validatedDomains.add(domain)) {
// Set the first validated domain as the address of the session
if (validatedDomains.size() < 2) {
setAddress(new JID(null, domain, null));
}
// Register the new validated domain for this server session in SessionManager
SessionManager.getInstance().registerIncomingServerSession(domain, this);
}
......@@ -329,12 +337,28 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
}
public String getAvailableStreamFeatures() {
StringBuilder sb = new StringBuilder();
// Include Stream Compression Mechanism
if (conn.getCompressionPolicy() != Connection.CompressionPolicy.disabled &&
!conn.isCompressed()) {
return "<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>";
sb.append("<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>");
}
// Nothing special to add
return null;
// Offer server dialback if using self-signed certificaftes and no authentication has been done yet
boolean usingSelfSigned = false;
Certificate[] certificates = conn.getLocalCertificates();
for (Certificate certificate : certificates) {
try {
if (CertificateManager
.isSelfSignedCertificate(SSLConfig.getKeyStore(), (X509Certificate) certificate)) {
usingSelfSigned = true;
}
} catch (Exception e) {
// Ignore
}
}
if (usingSelfSigned && ServerDialback.isEnabledForSelfSigned() && validatedDomains.isEmpty()) {
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>");
}
return sb.toString();
}
}
......@@ -237,67 +237,69 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
//useTLS = configuration.isTLSEnabled();
}
if (useTLS) {
// Connect to remote server using TLS + SASL
SocketConnection connection = null;
String realHostname = null;
int realPort = port;
Socket socket = new Socket();
try {
// Get the real hostname to connect to using DNS lookup of the specified hostname
DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname, port);
realHostname = address.getHost();
realPort = address.getPort();
Log.debug("LocalOutgoingServerSession: OS - Trying to connect to " + hostname + ":" + port +
"(DNS lookup: " + realHostname + ":" + realPort + ")");
// Establish a TCP connection to the Receiving Server
socket.connect(new InetSocketAddress(realHostname, realPort),
RemoteServerManager.getSocketTimeout());
Log.debug("LocalOutgoingServerSession: OS - Plain connection to " + hostname + ":" + port + " successful");
}
catch (Exception e) {
Log.error("Error trying to connect to remote server: " + hostname +
"(DNS lookup: " + realHostname + ":" + realPort + ")", e);
return null;
}
// Connect to remote server using XMPP 1.0 (TLS + SASL EXTERNAL or TLS + server dialback or server dialback)
SocketConnection connection = null;
String realHostname = null;
int realPort = port;
Socket socket = new Socket();
try {
// Get the real hostname to connect to using DNS lookup of the specified hostname
DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname, port);
realHostname = address.getHost();
realPort = address.getPort();
Log.debug("LocalOutgoingServerSession: OS - Trying to connect to " + hostname + ":" + port +
"(DNS lookup: " + realHostname + ":" + realPort + ")");
// Establish a TCP connection to the Receiving Server
socket.connect(new InetSocketAddress(realHostname, realPort),
RemoteServerManager.getSocketTimeout());
Log.debug("LocalOutgoingServerSession: OS - Plain connection to " + hostname + ":" + port + " successful");
}
catch (Exception e) {
Log.error("Error trying to connect to remote server: " + hostname +
"(DNS lookup: " + realHostname + ":" + realPort + ")", e);
return null;
}
try {
connection =
new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket,
false);
// Send the stream header
StringBuilder openingStream = new StringBuilder();
openingStream.append("<stream:stream");
openingStream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
openingStream.append(" xmlns=\"jabber:server\"");
openingStream.append(" to=\"").append(hostname).append("\"");
openingStream.append(" version=\"1.0\">");
connection.deliverRawText(openingStream.toString());
// Set a read timeout (of 5 seconds) so we don't keep waiting forever
int soTimeout = socket.getSoTimeout();
socket.setSoTimeout(5000);
XMPPPacketReader reader = new XMPPPacketReader();
reader.getXPPParser().setInput(new InputStreamReader(socket.getInputStream(),
CHARSET));
// Get the answer from the Receiving Server
XmlPullParser xpp = reader.getXPPParser();
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
try {
connection =
new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket,
false);
// Send the stream header
StringBuilder openingStream = new StringBuilder();
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(" to=\"").append(hostname).append("\"");
openingStream.append(" version=\"1.0\">");
connection.deliverRawText(openingStream.toString());
String serverVersion = xpp.getAttributeValue("", "version");
// Set a read timeout (of 5 seconds) so we don't keep waiting forever
int soTimeout = socket.getSoTimeout();
socket.setSoTimeout(5000);
XMPPPacketReader reader = new XMPPPacketReader();
reader.getXPPParser().setInput(new InputStreamReader(socket.getInputStream(),
CHARSET));
// Get the answer from the Receiving Server
XmlPullParser xpp = reader.getXPPParser();
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
// Check if the remote server is XMPP 1.0 compliant
if (serverVersion != null && decodeVersion(serverVersion)[0] >= 1) {
// Restore default timeout
socket.setSoTimeout(soTimeout);
// Get the stream features
Element features = reader.parseDocument().getRootElement();
String serverVersion = xpp.getAttributeValue("", "version");
String id = xpp.getAttributeValue("", "id");
// Check if the remote server is XMPP 1.0 compliant
if (serverVersion != null && decodeVersion(serverVersion)[0] >= 1) {
// Restore default timeout
socket.setSoTimeout(soTimeout);
// Get the stream features
Element features = reader.parseDocument().getRootElement();
if (features != null) {
// Check if TLS is enabled
if (features != null && features.element("starttls") != null) {
if (useTLS && features.element("starttls") != null) {
// Secure the connection with TLS and authenticate using SASL
LocalOutgoingServerSession answer;
answer = secureAndAuthenticate(hostname, connection, reader, openingStream,
......@@ -308,45 +310,64 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
return answer;
}
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, <starttls> was not received");
// Check if we are going to try server dialback (XMPP 1.0)
else if (ServerDialback.isEnabled() && features.element("dialback") != null) {
Log.debug("LocalOutgoingServerSession: OS - About to try connecting using server dialback XMPP 1.0 with: " + hostname);
ServerDialback method = new ServerDialback(connection, domain);
OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
if (method.authenticateDomain(newSocketReader, domain, hostname, id)) {
Log.debug("LocalOutgoingServerSession: OS - SERVER DIALBACK XMPP 1.0 with " + hostname + " was successful");
StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain, connection, newSocketReader, streamID);
connection.init(session);
// Set the hostname as the address of the session
session.setAddress(new JID(null, hostname, null));
return session;
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, SERVER DIALBACK with " + hostname + " failed");
}
}
}
// Something went wrong so close the connection and try server dialback over
// a plain connection
if (connection != null) {
connection.close();
else {
Log.debug("LocalOutgoingServerSession: OS - Error, <starttls> was not received");
}
}
catch (SSLHandshakeException e) {
Log.debug("LocalOutgoingServerSession: Handshake error while creating secured outgoing session to remote " +
"server: " + hostname + "(DNS lookup: " + realHostname + ":" + realPort +
")", e);
// Close the connection
if (connection != null) {
connection.close();
}
// Something went wrong so close the connection and try server dialback over
// a plain connection
if (connection != null) {
connection.close();
}
catch (XmlPullParserException e) {
Log.warn("Error creating secured outgoing session to remote server: " + hostname +
"(DNS lookup: " + realHostname + ":" + realPort + ")", e);
// Close the connection
if (connection != null) {
connection.close();
}
}
catch (SSLHandshakeException e) {
Log.debug("LocalOutgoingServerSession: Handshake error while creating secured outgoing session to remote " +
"server: " + hostname + "(DNS lookup: " + realHostname + ":" + realPort +
")", e);
// Close the connection
if (connection != null) {
connection.close();
}
catch (Exception e) {
Log.error("Error creating secured outgoing session to remote server: " + hostname +
"(DNS lookup: " + realHostname + ":" + realPort + ")", e);
// Close the connection
if (connection != null) {
connection.close();
}
}
catch (XmlPullParserException e) {
Log.warn("Error creating secured outgoing session to remote server: " + hostname +
"(DNS lookup: " + realHostname + ":" + realPort + ")", e);
// Close the connection
if (connection != null) {
connection.close();
}
}
catch (Exception e) {
Log.error("Error creating secured outgoing session to remote server: " + hostname +
"(DNS lookup: " + realHostname + ":" + realPort + ")", e);
// Close the connection
if (connection != null) {
connection.close();
}
}
if (ServerDialback.isEnabled()) {
Log.debug("LocalOutgoingServerSession: OS - Going to try connecting using server dialback with: " + hostname);
// Use server dialback over a plain connection
// Use server dialback (pre XMPP 1.0) over a plain connection
return new ServerDialback().createOutgoingSession(domain, hostname, port);
}
return null;
......@@ -380,9 +401,11 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
// Get the stream ID
String id = xpp.getAttributeValue("", "id");
// Get new stream features
features = reader.parseDocument().getRootElement();
if (features != null && features.element("mechanisms") != null) {
if (features != null && (features.element("mechanisms") != null || features.element("dialback") != null)) {
// Check if we can use stream compression
String policyName = JiveGlobals.getProperty("xmpp.server.compression.policy",
Connection.CompressionPolicy.disabled.toString());
......@@ -444,48 +467,73 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
}
}
Iterator it = features.element("mechanisms").elementIterator();
while (it.hasNext()) {
Element mechanism = (Element) it.next();
if ("EXTERNAL".equals(mechanism.getTextTrim())) {
Log.debug("LocalOutgoingServerSession: OS - Starting EXTERNAL SASL with " + hostname);
if (doExternalAuthentication(domain, connection, reader)) {
Log.debug("LocalOutgoingServerSession: OS - EXTERNAL SASL with " + hostname + " was successful");
// SASL was successful so initiate a new stream
connection.deliverRawText(openingStream.toString());
// Reset the parser
xpp.resetInput();
// Skip the opening stream sent by the server
for (int eventType = xpp.getEventType();
eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
// Skip SASL EXTERNAL and use server dialback over TLS when using self-signed certificates
boolean dialbackOffered = features.element("dialback") != null;
if (!dialbackOffered || !connection.isUsingSelfSignedCertificate()) {
Iterator it = features.element("mechanisms").elementIterator();
while (it.hasNext()) {
Element mechanism = (Element) it.next();
if ("EXTERNAL".equals(mechanism.getTextTrim())) {
Log.debug("LocalOutgoingServerSession: OS - Starting EXTERNAL SASL with " + hostname);
if (doExternalAuthentication(domain, connection, reader)) {
Log.debug("LocalOutgoingServerSession: OS - EXTERNAL SASL with " + hostname + " was successful");
// SASL was successful so initiate a new stream
connection.deliverRawText(openingStream.toString());
// SASL authentication was successful so create new
// OutgoingServerSession
String id = xpp.getAttributeValue("", "id");
StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain,
connection, new OutgoingServerSocketReader(reader), streamID);
connection.init(session);
// Set the hostname as the address of the session
session.setAddress(new JID(null, hostname, null));
// Set that the session was created using TLS+SASL (no server dialback)
session.usingServerDialback = false;
return session;
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, EXTERNAL SASL authentication with " + hostname +
" failed");
return null;
// Reset the parser
xpp.resetInput();
// Skip the opening stream sent by the server
for (int eventType = xpp.getEventType();
eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
// SASL authentication was successful so create new
// OutgoingServerSession
id = xpp.getAttributeValue("", "id");
StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain,
connection, new OutgoingServerSocketReader(reader), streamID);
connection.init(session);
// Set the hostname as the address of the session
session.setAddress(new JID(null, hostname, null));
// Set that the session was created using TLS+SASL (no server dialback)
session.usingServerDialback = false;
return session;
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, EXTERNAL SASL authentication with " + hostname +
" failed");
return null;
}
}
}
}
Log.debug("LocalOutgoingServerSession: OS - Error, EXTERNAL SASL was not offered by " + hostname);
// Check if server dialback (over TLS) was offered
if (dialbackOffered && (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned())) {
Log.debug("LocalOutgoingServerSession: OS - About to try connecting using server dialback over TLS with: " + hostname);
ServerDialback method = new ServerDialback(connection, domain);
OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
if (method.authenticateDomain(newSocketReader, domain, hostname, id)) {
Log.debug("LocalOutgoingServerSession: OS - SERVER DIALBACK OVER TLS with " + hostname + " was successful");
StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain, connection, newSocketReader, streamID);
connection.init(session);
// Set the hostname as the address of the session
session.setAddress(new JID(null, hostname, null));
return session;
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, SERVER DIALBACK with " + hostname + " failed");
}
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, EXTERNAL SASL and SERVER DIALBACK were not offered by " + hostname);
}
}
else {
Log.debug("LocalOutgoingServerSession: OS - Error, no SASL mechanisms were offered by " + hostname);
Log.debug("LocalOutgoingServerSession: OS - Error, no SASL mechanisms or SERVER DIALBACK were offered by " + hostname);
}
}
else {
......@@ -623,4 +671,4 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
public boolean isUsingServerDialback() {
return usingServerDialback;
}
}
\ No newline at end of file
}
......@@ -335,4 +335,16 @@ public abstract class LocalSession implements Session {
answer[1] = Integer.parseInt(versionString[1]);
return answer;
}
/**
* Returns true if the other peer of this session presented a self-signed certificate. When
* using self-signed certificate for server-2-server sessions then SASL EXTERNAL will not be
* used and instead server-dialback will be preferred for vcerifying the identify of the remote
* server.
*
* @return true if the other peer of this session presented a self-signed certificate.
*/
public boolean isUsingSelfSignedCertificate() {
return conn.isUsingSelfSignedCertificate();
}
}
......@@ -321,6 +321,23 @@ public class CertificateManager {
return certificateChain == null || certificateChain.length == 1;
}
/**
* Returns true if the specified certificate is a self-signed certificate. If the certificate
* was not found in the store then a KeyStoreException is returned.
*
* @param keyStore key store that holds the certificate to verify.
* @param certificate the certificate in the key store.
* @return true if the specified certificate is a self-signed certificate.
* @throws KeyStoreException if an error happens while usign the keystore
*/
public static boolean isSelfSignedCertificate(KeyStore keyStore, X509Certificate certificate) throws KeyStoreException {
String alias = keyStore.getCertificateAlias(certificate);
if (alias == null) {
throw new KeyStoreException("Certificate not found in store: " + certificate);
}
return isSelfSignedCertificate(keyStore, alias);
}
/**
* Returns true if the specified certificate is ready to be signed by a Certificate Authority. Self-signed
* certificates need to get their issuer information entered to be able to generate a Certificate
......
......@@ -12,11 +12,12 @@
<%@ page import="org.jivesoftware.openfire.Connection,
org.jivesoftware.openfire.ConnectionManager,
org.jivesoftware.openfire.XMPPServer,
org.jivesoftware.openfire.server.ServerDialback,
org.jivesoftware.openfire.session.LocalClientSession,
org.jivesoftware.util.JiveGlobals,
org.jivesoftware.util.ParamUtils"
org.jivesoftware.util.JiveGlobals"
errorPage="error.jsp"
%>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
......@@ -35,6 +36,7 @@
String serverSecurityRequired = ParamUtils.getParameter(request, "serverSecurityRequired");
String dialback = ParamUtils.getParameter(request, "dialback");
String server_tls = ParamUtils.getParameter(request, "server_tls");
boolean selfSigned = ParamUtils.getBooleanParameter(request, "selfSigned");
if (update) {
if ("req".equals(clientSecurityRequired)) {
......@@ -107,6 +109,7 @@
JiveGlobals.setProperty("xmpp.server.tls.enabled", "false");
}
}
ServerDialback.setEnabledForSelfSigned(selfSigned);
success = true;
// Log the event
webManager.logEvent("updated SSL configuration", "xmpp.server.dialback.enabled = "+JiveGlobals.getProperty("xmpp.server.dialback.enabled")+"\nxmpp.server.tls.enabled = "+JiveGlobals.getProperty("xmpp.server.tls.enabled"));
......@@ -152,6 +155,7 @@
dialback = dialbackEnabled ? "available" : "notavailable";
server_tls = "notavailable";
}
selfSigned = ServerDialback.isEnabledForSelfSigned();
%>
<html>
......@@ -380,6 +384,16 @@
</table>
</td>
</tr>
<tr valign="middle">
<td width="1%" nowrap>
<input type="checkbox" name="selfSigned" id="cb02" <%= (selfSigned ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb02">
<fmt:message key="ssl.settings.client.label_self-signed" />
</label>
</td>
</tr>
</tbody>
</table>
</div>
......
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