Search in sources :

Example 1 with ServerDialback

use of org.jivesoftware.openfire.server.ServerDialback in project Openfire by igniterealtime.

the class LocalOutgoingServerSession method createOutgoingSession.

/**
     * Establishes a new outgoing session to a remote domain. If the remote domain supports TLS and SASL then the new
     * outgoing connection will be secured with TLS and authenticated  using SASL. However, if TLS or SASL is not
     * supported by the remote domain or if an error occurred while securing or authenticating the connection using SASL
     * then server dialback will be used.
     *
     * @param localDomain the local domain to authenticate with the remote domain.
     * @param remoteDomain the remote domain.
     * @param port default port to use to establish the connection.
     * @return new outgoing session to a remote domain, or null.
     */
private static LocalOutgoingServerSession createOutgoingSession(String localDomain, String remoteDomain, int port) {
    final Logger log = LoggerFactory.getLogger(Log.getName() + "[Create outgoing session for: " + localDomain + " to " + remoteDomain + "]");
    log.debug("Creating new session...");
    // Connect to remote server using XMPP 1.0 (TLS + SASL EXTERNAL or TLS + server dialback or server dialback)
    log.debug("Creating plain socket connection to a host that belongs to the remote XMPP domain.");
    final Socket socket = SocketUtil.createSocketToXmppDomain(remoteDomain, port);
    if (socket == null) {
        log.info("Unable to create new session: Cannot create a plain socket connection with any applicable remote host.");
        return null;
    }
    SocketConnection connection = null;
    try {
        connection = new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket, false);
        log.debug("Send the stream header and wait for response...");
        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\"");
        // OF-673
        openingStream.append(" from=\"").append(localDomain).append("\"");
        openingStream.append(" to=\"").append(remoteDomain).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(), StandardCharsets.UTF_8));
        // Get the answer from the Receiving Server
        XmlPullParser xpp = reader.getXPPParser();
        for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG; ) {
            eventType = xpp.next();
        }
        String serverVersion = xpp.getAttributeValue("", "version");
        String id = xpp.getAttributeValue("", "id");
        log.debug("Got a response (stream ID: {}, version: {}). Check if the remote server is XMPP 1.0 compliant...", id, serverVersion);
        if (serverVersion != null && decodeVersion(serverVersion)[0] >= 1) {
            log.debug("The remote server is XMPP 1.0 compliant (or at least reports to be).");
            // Restore default timeout
            socket.setSoTimeout(soTimeout);
            log.debug("Processing stream features of the remote domain...");
            Element features = reader.parseDocument().getRootElement();
            if (features != null) {
                log.debug("Check if both us as well as the remote server have enabled STARTTLS and/or dialback ...");
                final boolean useTLS = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true);
                if (useTLS && features.element("starttls") != null) {
                    log.debug("Both us and the remote server support the STARTTLS feature. Secure and authenticate the connection with TLS & SASL...");
                    LocalOutgoingServerSession answer = secureAndAuthenticate(remoteDomain, connection, reader, openingStream, localDomain);
                    if (answer != null) {
                        log.debug("Successfully secured/authenticated the connection with TLS/SASL)!");
                        // Everything went fine so return the secured and
                        // authenticated connection
                        log.debug("Successfully created new session!");
                        return answer;
                    }
                    log.debug("Unable to secure and authenticate the connection with TLS & SASL.");
                } else if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
                    log.debug("I have no StartTLS yet I must TLS");
                    connection.close();
                    return null;
                } else // Check if we are going to try server dialback (XMPP 1.0)
                if (ServerDialback.isEnabled() && features.element("dialback") != null) {
                    log.debug("Both us and the remote server support the 'dialback' feature. Authenticate the connection with dialback...");
                    ServerDialback method = new ServerDialback(connection, localDomain);
                    OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
                    if (method.authenticateDomain(newSocketReader, localDomain, remoteDomain, id)) {
                        log.debug("Successfully authenticated the connection with dialback!");
                        StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
                        LocalOutgoingServerSession session = new LocalOutgoingServerSession(localDomain, connection, newSocketReader, streamID);
                        connection.init(session);
                        // Set the hostname as the address of the session
                        session.setAddress(new JID(null, remoteDomain, null));
                        log.debug("Successfully created new session!");
                        return session;
                    } else {
                        log.debug("Unable to authenticate the connection with dialback.");
                    }
                }
            } else {
                log.debug("Error! No data from the remote server (expected a 'feature' element).");
            }
        } else {
            log.debug("The remote server is not XMPP 1.0 compliant.");
        }
        log.debug("Something went wrong so close the connection and try server dialback over a plain connection");
        if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
            log.debug("I have no StartTLS yet I must TLS");
            connection.close();
            return null;
        }
        connection.close();
    } catch (SSLHandshakeException e) {
        // This is a failure as described in RFC3620, section 5.4.3.2 "STARTTLS Failure".
        log.info("STARTTLS negotiation failed. Closing connection (without sending any data such as <failure/> or </stream>).", e);
        // It is probably (see OF-794) best if we, as the initiating entity, therefor don't send any data either.
        if (connection != null) {
            connection.forceClose();
        }
    } catch (Exception e) {
        // This might be RFC3620, section 5.4.2.2 "Failure Case" or even an unrelated problem. Handle 'normally'.
        log.warn("An exception occurred while creating an encrypted session. Closing connection.", e);
        if (connection != null) {
            connection.close();
        }
    }
    if (ServerDialback.isEnabled()) {
        log.debug("Unable to create a new session. Going to try connecting using server dialback as a fallback.");
        // Use server dialback (pre XMPP 1.0) over a plain connection
        final LocalOutgoingServerSession outgoingSession = new ServerDialback().createOutgoingSession(localDomain, remoteDomain, port);
        if (outgoingSession != null) {
            // TODO this success handler behaves differently from a similar success handler above. Shouldn't those be the same?
            log.debug("Successfully created new session (using dialback as a fallback)!");
            return outgoingSession;
        } else {
            log.warn("Unable to create a new session: Dialback (as a fallback) failed.");
            return null;
        }
    } else {
        log.warn("Unable to create a new session: exhausted all options (not trying dialback as a fallback, as server dialback is disabled by configuration.");
        return null;
    }
}
Also used : StreamID(org.jivesoftware.openfire.StreamID) XMPPPacketReader(org.dom4j.io.XMPPPacketReader) InputStreamReader(java.io.InputStreamReader) JID(org.xmpp.packet.JID) Element(org.dom4j.Element) XmlPullParser(org.xmlpull.v1.XmlPullParser) Logger(org.slf4j.Logger) ServerDialback(org.jivesoftware.openfire.server.ServerDialback) SSLHandshakeException(javax.net.ssl.SSLHandshakeException) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) DocumentException(org.dom4j.DocumentException) SSLHandshakeException(javax.net.ssl.SSLHandshakeException) IOException(java.io.IOException) XmlPullParserException(org.xmlpull.v1.XmlPullParserException) OutgoingServerSocketReader(org.jivesoftware.openfire.server.OutgoingServerSocketReader) BasicStreamIDFactory(org.jivesoftware.openfire.spi.BasicStreamIDFactory) Socket(java.net.Socket)

Example 2 with ServerDialback

use of org.jivesoftware.openfire.server.ServerDialback in project Openfire by igniterealtime.

the class LocalOutgoingServerSession method attemptDialbackOverTLS.

private static LocalOutgoingServerSession attemptDialbackOverTLS(Connection connection, XMPPPacketReader reader, DomainPair domainPair, String id) {
    final Logger log = LoggerFactory.getLogger(Log.getName() + "[Dialback over TLS for: " + domainPair + " (Stream ID: " + id + ")]");
    if (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned()) {
        log.debug("Trying to connecting using dialback over TLS.");
        ServerDialback method = new ServerDialback(connection, domainPair);
        OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
        if (method.authenticateDomain(newSocketReader, id)) {
            log.debug("Dialback over TLS was successful.");
            StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
            LocalOutgoingServerSession session = new LocalOutgoingServerSession(domainPair.getLocal(), connection, newSocketReader, streamID);
            connection.init(session);
            // Set the remote domain name as the address of the session.
            session.setAddress(new JID(null, domainPair.getRemote(), null));
            return session;
        } else {
            log.debug("Dialback over TLS failed");
            return null;
        }
    } else {
        log.debug("Skipping server dialback attempt as it has been disabled by local configuration.");
        return null;
    }
}
Also used : OutgoingServerSocketReader(org.jivesoftware.openfire.server.OutgoingServerSocketReader) BasicStreamIDFactory(org.jivesoftware.openfire.spi.BasicStreamIDFactory) Logger(org.slf4j.Logger) ServerDialback(org.jivesoftware.openfire.server.ServerDialback)

Example 3 with ServerDialback

use of org.jivesoftware.openfire.server.ServerDialback in project Openfire by igniterealtime.

the class LocalOutgoingServerSession method createOutgoingSession.

/**
 * Establishes a new outgoing session to a remote domain. If the remote domain supports TLS and SASL then the new
 * outgoing connection will be secured with TLS and authenticated  using SASL. However, if TLS or SASL is not
 * supported by the remote domain or if an error occurred while securing or authenticating the connection using SASL
 * then server dialback will be used.
 *
 * @param domainPair the local and remote domain for which a session is to be established.
 * @param port default port to use to establish the connection.
 * @return new outgoing session to a remote domain, or null.
 */
private static LocalOutgoingServerSession createOutgoingSession(@Nonnull final DomainPair domainPair, int port) {
    final Logger log = LoggerFactory.getLogger(Log.getName() + "[Create outgoing session for: " + domainPair + "]");
    log.debug("Creating new session...");
    // Connect to remote server using XMPP 1.0 (TLS + SASL EXTERNAL or TLS + server dialback or server dialback)
    log.debug("Creating plain socket connection to a host that belongs to the remote XMPP domain.");
    final Map.Entry<Socket, Boolean> socketToXmppDomain = SocketUtil.createSocketToXmppDomain(domainPair.getRemote(), port);
    if (socketToXmppDomain == null) {
        log.info("Unable to create new session: Cannot create a plain socket connection with any applicable remote host.");
        return null;
    }
    Socket socket = socketToXmppDomain.getKey();
    boolean directTLS = socketToXmppDomain.getValue();
    SocketConnection connection = null;
    try {
        final SocketAddress socketAddress = socket.getRemoteSocketAddress();
        log.debug("Opening a new connection to {} {}.", socketAddress, directTLS ? "using directTLS" : "that is initially not encrypted");
        connection = new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket, false);
        if (directTLS) {
            try {
                connection.startTLS(true, true);
            } catch (SSLException ex) {
                if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ON_PLAIN_DETECTION_ALLOW_NONDIRECTTLS_FALLBACK, true) && ex.getMessage().contains("plaintext connection?")) {
                    Log.warn("Plaintext detected on a new connection that is was started in DirectTLS mode (socket address: {}). Attempting to restart the connection in non-DirectTLS mode.", socketAddress);
                    try {
                        // Close old socket
                        socket.close();
                    } catch (Exception e) {
                        Log.debug("An exception occurred (and is ignored) while trying to close a socket that was already in an error state.", e);
                    }
                    socket = new Socket();
                    socket.connect(socketAddress, RemoteServerManager.getSocketTimeout());
                    connection = new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket, false);
                    directTLS = false;
                    Log.info("Re-established connection to {}. Proceeding without directTLS.", socketAddress);
                } else {
                    // Do not retry as non-DirectTLS, rethrow the exception.
                    throw ex;
                }
            }
        }
        log.debug("Send the stream header and wait for response...");
        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\"");
        // OF-673
        openingStream.append(" from=\"").append(domainPair.getLocal()).append("\"");
        openingStream.append(" to=\"").append(domainPair.getRemote()).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();
        final InputStream inputStream;
        if (directTLS) {
            inputStream = connection.getTLSStreamHandler().getInputStream();
        } else {
            inputStream = socket.getInputStream();
        }
        reader.getXPPParser().setInput(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        // Get the answer from the Receiving Server
        XmlPullParser xpp = reader.getXPPParser();
        for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG; ) {
            eventType = xpp.next();
        }
        String serverVersion = xpp.getAttributeValue("", "version");
        String id = xpp.getAttributeValue("", "id");
        log.debug("Got a response (stream ID: {}, version: {}). Check if the remote server is XMPP 1.0 compliant...", id, serverVersion);
        if (serverVersion != null && decodeVersion(serverVersion)[0] >= 1) {
            log.debug("The remote server is XMPP 1.0 compliant (or at least reports to be).");
            // Restore default timeout
            socket.setSoTimeout(soTimeout);
            log.debug("Processing stream features of the remote domain...");
            Element features = reader.parseDocument().getRootElement();
            if (features != null) {
                if (directTLS) {
                    log.debug("We connected to the remote server using direct TLS. Authenticate the connection with SASL...");
                    LocalOutgoingServerSession answer = authenticate(domainPair, connection, reader, openingStream, features, id);
                    if (answer != null) {
                        log.debug("Successfully authenticated the connection with SASL)!");
                        // Everything went fine so return the secured and
                        // authenticated connection
                        log.debug("Successfully created new session!");
                        return answer;
                    }
                    log.debug("Unable to authenticate the connection with SASL.");
                } else {
                    log.debug("Check if both us as well as the remote server have enabled STARTTLS and/or dialback ...");
                    final boolean useTLS = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true);
                    if (useTLS && features.element("starttls") != null) {
                        log.debug("Both us and the remote server support the STARTTLS feature. Secure and authenticate the connection with TLS & SASL...");
                        LocalOutgoingServerSession answer = secureAndAuthenticate(domainPair, connection, reader, openingStream);
                        if (answer != null) {
                            log.debug("Successfully secured/authenticated the connection with TLS/SASL)!");
                            // Everything went fine so return the secured and
                            // authenticated connection
                            log.debug("Successfully created new session!");
                            return answer;
                        }
                        log.debug("Unable to secure and authenticate the connection with TLS & SASL.");
                    } else if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
                        log.debug("I have no StartTLS yet I must TLS");
                        connection.close();
                        return null;
                    } else // Check if we are going to try server dialback (XMPP 1.0)
                    if (ServerDialback.isEnabled() && features.element("dialback") != null) {
                        log.debug("Both us and the remote server support the 'dialback' feature. Authenticate the connection with dialback...");
                        ServerDialback method = new ServerDialback(connection, domainPair);
                        OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
                        if (method.authenticateDomain(newSocketReader, id)) {
                            log.debug("Successfully authenticated the connection with dialback!");
                            StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
                            LocalOutgoingServerSession session = new LocalOutgoingServerSession(domainPair.getLocal(), connection, newSocketReader, streamID);
                            connection.init(session);
                            // Set the remote domain name as the address of the session.
                            session.setAddress(new JID(null, domainPair.getRemote(), null));
                            log.debug("Successfully created new session!");
                            return session;
                        } else {
                            log.debug("Unable to authenticate the connection with dialback.");
                        }
                    }
                }
            } else {
                log.debug("Error! No data from the remote server (expected a 'feature' element).");
            }
        } else {
            log.debug("The remote server is not XMPP 1.0 compliant.");
        }
        log.debug("Something went wrong so close the connection and try server dialback over a plain connection");
        if (connection.getTlsPolicy() == Connection.TLSPolicy.required) {
            log.debug("I have no StartTLS yet I must TLS");
            connection.close();
            return null;
        }
        connection.close();
    } catch (SSLHandshakeException e) {
        // When not doing direct TLS but startTLS, this a failure as described in RFC3620, section 5.4.3.2 "STARTTLS Failure".
        log.info("{} negotiation failed. Closing connection (without sending any data such as <failure/> or </stream>).", (directTLS ? "Direct TLS" : "StartTLS"), e);
        // It is probably (see OF-794) best if we, as the initiating entity, therefor don't send any data either.
        if (connection != null) {
            connection.forceClose();
        }
    } catch (Exception e) {
        // This might be RFC3620, section 5.4.2.2 "Failure Case" or even an unrelated problem. Handle 'normally'.
        log.warn("An exception occurred while creating an encrypted session. Closing connection.", e);
        if (connection != null) {
            connection.close();
        }
    }
    if (ServerDialback.isEnabled()) {
        log.debug("Unable to create a new session. Going to try connecting using server dialback as a fallback.");
        // Use server dialback (pre XMPP 1.0) over a plain connection
        final LocalOutgoingServerSession outgoingSession = new ServerDialback(domainPair).createOutgoingSession(port);
        if (outgoingSession != null) {
            // TODO this success handler behaves differently from a similar success handler above. Shouldn't those be the same?
            log.debug("Successfully created new session (using dialback as a fallback)!");
            return outgoingSession;
        } else {
            log.warn("Unable to create a new session: Dialback (as a fallback) failed.");
            return null;
        }
    } else {
        log.warn("Unable to create a new session: exhausted all options (not trying dialback as a fallback, as server dialback is disabled by configuration.");
        return null;
    }
}
Also used : Element(org.dom4j.Element) XmlPullParser(org.xmlpull.v1.XmlPullParser) Logger(org.slf4j.Logger) SSLException(javax.net.ssl.SSLException) SSLHandshakeException(javax.net.ssl.SSLHandshakeException) OutgoingServerSocketReader(org.jivesoftware.openfire.server.OutgoingServerSocketReader) SocketConnection(org.jivesoftware.openfire.net.SocketConnection) BasicStreamIDFactory(org.jivesoftware.openfire.spi.BasicStreamIDFactory) SocketAddress(java.net.SocketAddress) XMPPPacketReader(org.dom4j.io.XMPPPacketReader) InputStreamReader(java.io.InputStreamReader) InputStream(java.io.InputStream) ServerDialback(org.jivesoftware.openfire.server.ServerDialback) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) DocumentException(org.dom4j.DocumentException) SSLHandshakeException(javax.net.ssl.SSLHandshakeException) IOException(java.io.IOException) XmlPullParserException(org.xmlpull.v1.XmlPullParserException) SSLException(javax.net.ssl.SSLException) Map(java.util.Map) Socket(java.net.Socket)

Example 4 with ServerDialback

use of org.jivesoftware.openfire.server.ServerDialback in project Openfire by igniterealtime.

the class LocalOutgoingServerSession method authenticateSubdomain.

@Override
public boolean authenticateSubdomain(String localDomain, String remoteDomain) {
    if (!usingServerDialback) {
        // Using SASL so just assume that the domain was validated
        // (note: this may not be correct)
        addAuthenticatedDomain(localDomain);
        addHostname(remoteDomain);
        return true;
    }
    ServerDialback method = new ServerDialback(getConnection(), localDomain);
    if (method.authenticateDomain(socketReader, localDomain, remoteDomain, getStreamID().getID())) {
        // Add the validated domain as an authenticated domain
        addAuthenticatedDomain(localDomain);
        addHostname(remoteDomain);
        return true;
    }
    return false;
}
Also used : ServerDialback(org.jivesoftware.openfire.server.ServerDialback)

Example 5 with ServerDialback

use of org.jivesoftware.openfire.server.ServerDialback in project Openfire by igniterealtime.

the class LocalOutgoingServerSession method attemptDialbackOverTLS.

private static LocalOutgoingServerSession attemptDialbackOverTLS(Connection connection, XMPPPacketReader reader, String localDomain, String remoteDomain, String id) {
    final Logger log = LoggerFactory.getLogger(Log.getName() + "[Dialback over TLS for: " + localDomain + " to: " + remoteDomain + " (Stream ID: " + id + ")]");
    if (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned()) {
        log.debug("Trying to connecting using dialback over TLS.");
        ServerDialback method = new ServerDialback(connection, localDomain);
        OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
        if (method.authenticateDomain(newSocketReader, localDomain, remoteDomain, id)) {
            log.debug("Dialback over TLS was successful.");
            StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
            LocalOutgoingServerSession session = new LocalOutgoingServerSession(localDomain, connection, newSocketReader, streamID);
            connection.init(session);
            // Set the hostname as the address of the session
            session.setAddress(new JID(null, remoteDomain, null));
            return session;
        } else {
            log.debug("Dialback over TLS failed");
            return null;
        }
    } else {
        log.debug("Skipping server dialback attempt as it has been disabled by local configuration.");
        return null;
    }
}
Also used : StreamID(org.jivesoftware.openfire.StreamID) OutgoingServerSocketReader(org.jivesoftware.openfire.server.OutgoingServerSocketReader) JID(org.xmpp.packet.JID) BasicStreamIDFactory(org.jivesoftware.openfire.spi.BasicStreamIDFactory) Logger(org.slf4j.Logger) ServerDialback(org.jivesoftware.openfire.server.ServerDialback)

Aggregations

ServerDialback (org.jivesoftware.openfire.server.ServerDialback)6 OutgoingServerSocketReader (org.jivesoftware.openfire.server.OutgoingServerSocketReader)4 BasicStreamIDFactory (org.jivesoftware.openfire.spi.BasicStreamIDFactory)4 Logger (org.slf4j.Logger)4 IOException (java.io.IOException)2 InputStreamReader (java.io.InputStreamReader)2 Socket (java.net.Socket)2 SSLHandshakeException (javax.net.ssl.SSLHandshakeException)2 DocumentException (org.dom4j.DocumentException)2 Element (org.dom4j.Element)2 XMPPPacketReader (org.dom4j.io.XMPPPacketReader)2 StreamID (org.jivesoftware.openfire.StreamID)2 UnauthorizedException (org.jivesoftware.openfire.auth.UnauthorizedException)2 XmlPullParser (org.xmlpull.v1.XmlPullParser)2 XmlPullParserException (org.xmlpull.v1.XmlPullParserException)2 JID (org.xmpp.packet.JID)2 InputStream (java.io.InputStream)1 SocketAddress (java.net.SocketAddress)1 Map (java.util.Map)1 SSLException (javax.net.ssl.SSLException)1