Search in sources :

Example 1 with SocketConnection

use of org.jivesoftware.openfire.net.SocketConnection 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 2 with SocketConnection

use of org.jivesoftware.openfire.net.SocketConnection in project Openfire by igniterealtime.

the class LocalIncomingServerSession method createSession.

/**
     * Creates a new session that will receive packets. The new session will be authenticated
     * before being returned. If the authentication process fails then the answer will be
     * <tt>null</tt>.<p>
     *
     * @param serverName hostname of this server.
     * @param reader reader on the new established connection with the remote server.
     * @param connection the new established connection with the remote server.
     * @return a new session that will receive packets or null if a problem occured while
     *         authenticating the remote server or when acting as the Authoritative Server during
     *         a Server Dialback authentication process.
     * @throws org.xmlpull.v1.XmlPullParserException if an error occurs while parsing the XML.
     * @throws java.io.IOException if an input/output error occurs while using the connection.
     */
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader, SocketConnection connection) throws XmlPullParserException, IOException {
    XmlPullParser xpp = reader.getXPPParser();
    String version = xpp.getAttributeValue("", "version");
    String fromDomain = xpp.getAttributeValue("", "from");
    String toDomain = xpp.getAttributeValue("", "to");
    int[] serverVersion = version != null ? decodeVersion(version) : new int[] { 0, 0 };
    if (toDomain == null) {
        toDomain = serverName;
    }
    try {
        // 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, fromDomain);
        // 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(toDomain).append("\"");
        if (fromDomain != null) {
            openingStream.append(" to=\"").append(fromDomain).append("\"");
        }
        openingStream.append(" id=\"").append(streamID).append("\"");
        // implementations appears to reduce connection issues with those domains (patch by Marcin Cieślak).
        if (serverVersion[0] >= 1) {
            openingStream.append(" version=\"1.0\">");
        } else {
            openingStream.append('>');
        }
        connection.deliverRawText(openingStream.toString());
        if (serverVersion[0] >= 1) {
            // Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
            // Indicate the TLS policy to use for this connection
            Connection.TLSPolicy tlsPolicy = connection.getTlsPolicy();
            boolean hasCertificates = false;
            try {
                hasCertificates = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore(ConnectionType.SOCKET_S2S).getStore().size() > 0;
            } catch (Exception e) {
                Log.error(e.getMessage(), e);
            }
            if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
                Log.error("Server session rejected. TLS is required but no certificates " + "were created.");
                return null;
            }
            connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled);
        }
        // Indicate the compression policy to use for this connection
        connection.setCompressionPolicy(connection.getConfiguration().getCompressionPolicy());
        StringBuilder sb = new StringBuilder();
        if (serverVersion[0] >= 1) {
            // Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
            // 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) 
            sb.append("<stream:features>");
            if (JiveGlobals.getBooleanProperty(ConnectionSettings.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\"><errors/></dialback>");
            }
            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;
    } catch (Exception e) {
        Log.error("Error establishing connection from remote server:" + connection, e);
        connection.close();
        return null;
    }
}
Also used : StreamID(org.jivesoftware.openfire.StreamID) XmlPullParser(org.xmlpull.v1.XmlPullParser) Connection(org.jivesoftware.openfire.Connection) SocketConnection(org.jivesoftware.openfire.net.SocketConnection) KeyStoreException(java.security.KeyStoreException) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) IOException(java.io.IOException) XmlPullParserException(org.xmlpull.v1.XmlPullParserException)

Example 3 with SocketConnection

use of org.jivesoftware.openfire.net.SocketConnection in project Openfire by igniterealtime.

the class LocalIncomingServerSession method createSession.

/**
 * Creates a new session that will receive packets. The new session will be authenticated
 * before being returned. If the authentication process fails then the answer will be
 * {@code null}.<p>
 *
 * @param serverName hostname of this server.
 * @param reader reader on the new established connection with the remote server.
 * @param connection the new established connection with the remote server.
 * @param directTLS true of connections are immediately encrypted (as opposed to plain text / startls).
 * @return a new session that will receive packets or null if a problem occured while
 *         authenticating the remote server or when acting as the Authoritative Server during
 *         a Server Dialback authentication process.
 * @throws org.xmlpull.v1.XmlPullParserException if an error occurs while parsing the XML.
 * @throws java.io.IOException if an input/output error occurs while using the connection.
 */
public static LocalIncomingServerSession createSession(String serverName, XMPPPacketReader reader, SocketConnection connection, boolean directTLS) throws XmlPullParserException, IOException {
    XmlPullParser xpp = reader.getXPPParser();
    String version = xpp.getAttributeValue("", "version");
    String fromDomain = xpp.getAttributeValue("", "from");
    String toDomain = xpp.getAttributeValue("", "to");
    int[] serverVersion = version != null ? decodeVersion(version) : new int[] { 0, 0 };
    if (toDomain == null) {
        toDomain = serverName;
    }
    try {
        // 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, fromDomain);
        // 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(toDomain).append("\"");
        if (fromDomain != null) {
            openingStream.append(" to=\"").append(fromDomain).append("\"");
        }
        openingStream.append(" id=\"").append(streamID).append("\"");
        // implementations appears to reduce connection issues with those domains (patch by Marcin Cieślak).
        if (serverVersion[0] >= 1) {
            openingStream.append(" version=\"1.0\">");
        } else {
            openingStream.append('>');
        }
        connection.deliverRawText(openingStream.toString());
        if (serverVersion[0] >= 1) {
            // Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
            // Indicate the TLS policy to use for this connection
            Connection.TLSPolicy tlsPolicy = connection.getTlsPolicy();
            boolean hasCertificates = false;
            try {
                hasCertificates = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore(ConnectionType.SOCKET_S2S).getStore().size() > 0;
            } catch (Exception e) {
                Log.error(e.getMessage(), e);
            }
            if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) {
                Log.error("Server session rejected. TLS is required but no certificates " + "were created.");
                return null;
            }
            connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled);
        }
        // Indicate the compression policy to use for this connection
        connection.setCompressionPolicy(connection.getConfiguration().getCompressionPolicy());
        StringBuilder sb = new StringBuilder();
        if (serverVersion[0] >= 1) {
            // Remote server is XMPP 1.0 compliant so offer TLS and SASL to establish the connection (and server dialback)
            // 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)
            sb.append("<stream:features>");
            if (!directTLS && JiveGlobals.getBooleanProperty(ConnectionSettings.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\"><errors/></dialback>");
            }
            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;
    } catch (Exception e) {
        Log.error("Error establishing connection from remote server:" + connection, e);
        connection.close();
        return null;
    }
}
Also used : StreamID(org.jivesoftware.openfire.StreamID) XmlPullParser(org.xmlpull.v1.XmlPullParser) Connection(org.jivesoftware.openfire.Connection) SocketConnection(org.jivesoftware.openfire.net.SocketConnection) UnauthorizedException(org.jivesoftware.openfire.auth.UnauthorizedException) IOException(java.io.IOException) XmlPullParserException(org.xmlpull.v1.XmlPullParserException)

Aggregations

IOException (java.io.IOException)3 UnauthorizedException (org.jivesoftware.openfire.auth.UnauthorizedException)3 SocketConnection (org.jivesoftware.openfire.net.SocketConnection)3 XmlPullParser (org.xmlpull.v1.XmlPullParser)3 XmlPullParserException (org.xmlpull.v1.XmlPullParserException)3 Connection (org.jivesoftware.openfire.Connection)2 StreamID (org.jivesoftware.openfire.StreamID)2 InputStream (java.io.InputStream)1 InputStreamReader (java.io.InputStreamReader)1 Socket (java.net.Socket)1 SocketAddress (java.net.SocketAddress)1 KeyStoreException (java.security.KeyStoreException)1 Map (java.util.Map)1 SSLException (javax.net.ssl.SSLException)1 SSLHandshakeException (javax.net.ssl.SSLHandshakeException)1 DocumentException (org.dom4j.DocumentException)1 Element (org.dom4j.Element)1 XMPPPacketReader (org.dom4j.io.XMPPPacketReader)1 OutgoingServerSocketReader (org.jivesoftware.openfire.server.OutgoingServerSocketReader)1 ServerDialback (org.jivesoftware.openfire.server.ServerDialback)1