Search in sources :

Example 1 with IncomingServerSession

use of org.jivesoftware.openfire.session.IncomingServerSession in project Openfire by igniterealtime.

the class ClusterListener method cleanupNode.

/**
     * Executes close logic for each session hosted in the remote node that is
     * no longer available. This logic is similar to the close listeners used by
     * the {@link SessionManager}.<p>
     *
     * If the node that went down performed its own clean up logic then the other
     * cluster nodes will have the correct state. That means that this method
     * will not find any sessions to remove.<p>
     *
     * If this operation is too big and we are still in a cluster then we can
     * distribute the work in the cluster to go faster.
     *
     * @param key the key that identifies the node that is no longer available.
     */
private void cleanupNode(NodeID key) {
    // TODO Fork in another process and even ask other nodes to process work
    RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
    RemoteSessionLocator sessionLocator = XMPPServer.getInstance().getRemoteSessionLocator();
    SessionManager manager = XMPPServer.getInstance().getSessionManager();
    // TODO Consider removing each cached entry once processed instead of all at the end. Could be more error-prove.
    Set<String> registeredUsers = lookupJIDList(key, C2SCache.getName());
    if (!registeredUsers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(registeredUsers)) {
            JID offlineJID = new JID(fullJID);
            manager.removeSession(null, offlineJID, false, true);
        }
    }
    Set<String> anonymousUsers = lookupJIDList(key, anonymousC2SCache.getName());
    if (!anonymousUsers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(anonymousUsers)) {
            JID offlineJID = new JID(fullJID);
            manager.removeSession(null, offlineJID, true, true);
        }
    }
    // Remove outgoing server sessions hosted in node that left the cluster
    Set<String> remoteServers = lookupJIDList(key, S2SCache.getName());
    if (!remoteServers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(remoteServers)) {
            JID serverJID = new JID(fullJID);
            routingTable.removeServerRoute(serverJID);
        }
    }
    Set<String> components = lookupJIDList(key, componentsCache.getName());
    if (!components.isEmpty()) {
        for (String address : new ArrayList<String>(components)) {
            Lock lock = CacheFactory.getLock(address, componentsCache);
            try {
                lock.lock();
                Set<NodeID> nodes = (Set<NodeID>) componentsCache.get(address);
                if (nodes != null) {
                    nodes.remove(key);
                    if (nodes.isEmpty()) {
                        componentsCache.remove(address);
                    } else {
                        componentsCache.put(address, nodes);
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }
    Set<String> componentSessions = lookupJIDList(key, componentSessionsCache.getName());
    if (!componentSessions.isEmpty()) {
        for (String domain : new ArrayList<String>(componentSessions)) {
            componentSessionsCache.remove(domain);
        // Registered subdomains of external component will be removed
        // by the clean up of the component cache
        }
    }
    Set<String> multiplexers = lookupJIDList(key, multiplexerSessionsCache.getName());
    if (!multiplexers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(multiplexers)) {
            multiplexerSessionsCache.remove(fullJID);
        // c2s connections connected to node that went down will be cleaned up
        // by the c2s logic above. If the CM went down and the node is up then
        // connections will be cleaned up as usual
        }
    }
    Set<String> incomingSessions = lookupJIDList(key, incomingServerSessionsCache.getName());
    if (!incomingSessions.isEmpty()) {
        for (String streamIDValue : new ArrayList<>(incomingSessions)) {
            StreamID streamID = BasicStreamIDFactory.createStreamID(streamIDValue);
            IncomingServerSession session = sessionLocator.getIncomingServerSession(key.toByteArray(), streamID);
            // Remove all the hostnames that were registered for this server session
            for (String hostname : session.getValidatedDomains()) {
                manager.unregisterIncomingServerSession(hostname, session);
            }
        }
    }
    nodeSessions.remove(key);
// TODO Make sure that routing table has no entry referring to node that is gone
}
Also used : JID(org.xmpp.packet.JID) IncomingServerSession(org.jivesoftware.openfire.session.IncomingServerSession) Lock(java.util.concurrent.locks.Lock) RemoteSessionLocator(org.jivesoftware.openfire.session.RemoteSessionLocator) NodeID(org.jivesoftware.openfire.cluster.NodeID)

Example 2 with IncomingServerSession

use of org.jivesoftware.openfire.session.IncomingServerSession in project Openfire by igniterealtime.

the class ClusterListener method cleanupNode.

/**
     * Executes close logic for each session hosted in the remote node that is
     * no longer available. This logic is similar to the close listeners used by
     * the {@link SessionManager}.<p>
     *
     * If the node that went down performed its own clean up logic then the other
     * cluster nodes will have the correct state. That means that this method
     * will not find any sessions to remove.<p>
     *
     * If this operation is too big and we are still in a cluster then we can
     * distribute the work in the cluster to go faster.
     *
     * @param key the key that identifies the node that is no longer available.
     */
private void cleanupNode(NodeID key) {
    // TODO Fork in another process and even ask other nodes to process work
    RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
    RemoteSessionLocator sessionLocator = XMPPServer.getInstance().getRemoteSessionLocator();
    SessionManager manager = XMPPServer.getInstance().getSessionManager();
    // TODO Consider removing each cached entry once processed instead of all at the end. Could be more error-prove.
    Set<String> registeredUsers = lookupJIDList(key, C2SCache.getName());
    if (!registeredUsers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(registeredUsers)) {
            JID offlineJID = new JID(fullJID);
            manager.removeSession(null, offlineJID, false, true);
        }
    }
    Set<String> anonymousUsers = lookupJIDList(key, anonymousC2SCache.getName());
    if (!anonymousUsers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(anonymousUsers)) {
            JID offlineJID = new JID(fullJID);
            manager.removeSession(null, offlineJID, true, true);
        }
    }
    // Remove outgoing server sessions hosted in node that left the cluster
    Set<String> remoteServers = lookupJIDList(key, S2SCache.getName());
    if (!remoteServers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(remoteServers)) {
            JID serverJID = new JID(fullJID);
            routingTable.removeServerRoute(serverJID);
        }
    }
    Set<String> components = lookupJIDList(key, componentsCache.getName());
    if (!components.isEmpty()) {
        for (String address : new ArrayList<String>(components)) {
            Lock lock = CacheFactory.getLock(address, componentsCache);
            try {
                lock.lock();
                Set<NodeID> nodes = (Set<NodeID>) componentsCache.get(address);
                if (nodes != null) {
                    nodes.remove(key);
                    if (nodes.isEmpty()) {
                        componentsCache.remove(address);
                    } else {
                        componentsCache.put(address, nodes);
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }
    Set<String> sessionInfo = lookupJIDList(key, sessionInfoCache.getName());
    if (!sessionInfo.isEmpty()) {
        for (String session : new ArrayList<String>(sessionInfo)) {
            sessionInfoCache.remove(session);
        // Registered sessions will be removed
        // by the clean up of the session info cache
        }
    }
    Set<String> componentSessions = lookupJIDList(key, componentSessionsCache.getName());
    if (!componentSessions.isEmpty()) {
        for (String domain : new ArrayList<String>(componentSessions)) {
            componentSessionsCache.remove(domain);
        // Registered subdomains of external component will be removed
        // by the clean up of the component cache
        }
    }
    Set<String> multiplexers = lookupJIDList(key, multiplexerSessionsCache.getName());
    if (!multiplexers.isEmpty()) {
        for (String fullJID : new ArrayList<String>(multiplexers)) {
            multiplexerSessionsCache.remove(fullJID);
        // c2s connections connected to node that went down will be cleaned up
        // by the c2s logic above. If the CM went down and the node is up then
        // connections will be cleaned up as usual
        }
    }
    Set<String> incomingSessions = lookupJIDList(key, incomingServerSessionsCache.getName());
    if (!incomingSessions.isEmpty()) {
        for (String streamIDValue : new ArrayList<>(incomingSessions)) {
            StreamID streamID = BasicStreamIDFactory.createStreamID(streamIDValue);
            IncomingServerSession session = sessionLocator.getIncomingServerSession(key.toByteArray(), streamID);
            // Remove all the hostnames that were registered for this server session
            for (String hostname : session.getValidatedDomains()) {
                manager.unregisterIncomingServerSession(hostname, session);
            }
        }
    }
    nodeSessions.remove(key);
// TODO Make sure that routing table has no entry referring to node that is gone
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) JID(org.xmpp.packet.JID) IncomingServerSession(org.jivesoftware.openfire.session.IncomingServerSession) ArrayList(java.util.ArrayList) Lock(java.util.concurrent.locks.Lock) RemoteSessionLocator(org.jivesoftware.openfire.session.RemoteSessionLocator) NodeID(org.jivesoftware.openfire.cluster.NodeID)

Example 3 with IncomingServerSession

use of org.jivesoftware.openfire.session.IncomingServerSession in project Openfire by igniterealtime.

the class SASLAuthentication method handle.

/**
 * Handles the SASL authentication packet. The entity may be sending an initial
 * authentication request or a response to a challenge made by the server. The returned
 * value indicates whether the authentication has finished either successfully or not or
 * if the entity is expected to send a response to a challenge.
 *
 * @param session the session that is authenticating with the server.
 * @param doc the stanza sent by the authenticating entity.
 * @return value that indicates whether the authentication has finished either successfully
 *         or not or if the entity is expected to send a response to a challenge.
 */
public static Status handle(LocalSession session, Element doc) {
    try {
        if (!doc.getNamespaceURI().equals(SASL_NAMESPACE)) {
            throw new IllegalStateException("Unexpected data received while negotiating SASL authentication. Name of the offending root element: " + doc.getName() + " Namespace: " + doc.getNamespaceURI());
        }
        switch(ElementType.valueOfCaseInsensitive(doc.getName())) {
            case ABORT:
                throw new SaslFailureException(Failure.ABORTED);
            case AUTH:
                if (doc.attributeValue("mechanism") == null) {
                    throw new SaslFailureException(Failure.INVALID_MECHANISM, "Peer did not specify a mechanism.");
                }
                final String mechanismName = doc.attributeValue("mechanism").toUpperCase();
                // See if the mechanism is supported by configuration as well as by implementation.
                if (!mechanisms.contains(mechanismName)) {
                    throw new SaslFailureException(Failure.INVALID_MECHANISM, "The configuration of Openfire does not contain or allow the mechanism.");
                }
                // OF-477: The SASL implementation requires the fully qualified host name (not the domain name!) of this server,
                // yet, most of the XMPP implemenations of DIGEST-MD5 will actually use the domain name. To account for that,
                // here, we'll use the host name, unless DIGEST-MD5 is being negotiated!
                final XMPPServerInfo serverInfo = XMPPServer.getInstance().getServerInfo();
                final String serverName = (mechanismName.equals("DIGEST-MD5") ? serverInfo.getXMPPDomain() : serverInfo.getHostname());
                // Construct the configuration properties
                final Map<String, Object> props = new HashMap<>();
                props.put(LocalSession.class.getCanonicalName(), session);
                props.put(Sasl.POLICY_NOANONYMOUS, Boolean.toString(!AnonymousSaslServer.ENABLED.getValue()));
                props.put("com.sun.security.sasl.digest.realm", serverInfo.getXMPPDomain());
                SaslServer saslServer = Sasl.createSaslServer(mechanismName, "xmpp", serverName, props, new XMPPCallbackHandler());
                if (saslServer == null) {
                    throw new SaslFailureException(Failure.INVALID_MECHANISM, "There is no provider that can provide a SASL server for the desired mechanism and properties.");
                }
                session.setSessionData("SaslServer", saslServer);
                if (mechanismName.equals("DIGEST-MD5")) {
                    // RFC2831 (DIGEST-MD5) says the client MAY provide data in the initial response. Java SASL does
                    // not (currently) support this and throws an exception. For XMPP, such data violates
                    // the RFC, so we just strip any initial token.
                    doc.setText("");
                }
            // intended fall-through
            case RESPONSE:
                saslServer = (SaslServer) session.getSessionData("SaslServer");
                if (saslServer == null) {
                    // Client sends response without a preceding auth?
                    throw new IllegalStateException("A SaslServer instance was not initialized and/or stored on the session.");
                }
                // Decode any data that is provided in the client response.
                final String encoded = doc.getTextTrim();
                final byte[] decoded;
                if (// java SaslServer cannot handle a null.
                encoded == null || encoded.isEmpty() || encoded.equals("=")) {
                    decoded = new byte[0];
                } else {
                    // TODO: We shouldn't depend on regex-based validation. Instead, use a proper decoder implementation and handle any exceptions that it throws.
                    if (!BASE64_ENCODED.matcher(encoded).matches()) {
                        throw new SaslFailureException(Failure.INCORRECT_ENCODING);
                    }
                    decoded = StringUtils.decodeBase64(encoded);
                }
                // Process client response.
                // Either a challenge or success data.
                final byte[] challenge = saslServer.evaluateResponse(decoded);
                if (!saslServer.isComplete()) {
                    // Not complete: client is challenged for additional steps.
                    sendChallenge(session, challenge);
                    return Status.needResponse;
                }
                // Success!
                if (session instanceof IncomingServerSession) {
                    // Flag that indicates if certificates of the remote server should be validated.
                    final boolean verify = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true);
                    if (verify) {
                        if (verifyCertificates(session.getConnection().getPeerCertificates(), saslServer.getAuthorizationID(), true)) {
                            ((LocalIncomingServerSession) session).tlsAuth();
                        } else {
                            throw new SaslFailureException(Failure.NOT_AUTHORIZED, "Server-to-Server certificate verification failed.");
                        }
                    }
                }
                authenticationSuccessful(session, saslServer.getAuthorizationID(), challenge);
                session.removeSessionData("SaslServer");
                return Status.authenticated;
            default:
                throw new IllegalStateException("Unexpected data received while negotiating SASL authentication. Name of the offending root element: " + doc.getName() + " Namespace: " + doc.getNamespaceURI());
        }
    } catch (SaslException ex) {
        Log.debug("SASL negotiation failed for session: {}", session, ex);
        final Failure failure;
        if (ex instanceof SaslFailureException && ((SaslFailureException) ex).getFailure() != null) {
            failure = ((SaslFailureException) ex).getFailure();
        } else {
            failure = Failure.NOT_AUTHORIZED;
        }
        authenticationFailed(session, failure);
        session.removeSessionData("SaslServer");
        return Status.failed;
    } catch (Exception ex) {
        Log.warn("An unexpected exception occurred during SASL negotiation. Affected session: {}", session, ex);
        authenticationFailed(session, Failure.NOT_AUTHORIZED);
        session.removeSessionData("SaslServer");
        return Status.failed;
    }
}
Also used : HashMap(java.util.HashMap) JiveSharedSecretSaslServer(org.jivesoftware.openfire.sasl.JiveSharedSecretSaslServer) AnonymousSaslServer(org.jivesoftware.openfire.sasl.AnonymousSaslServer) SaslServer(javax.security.sasl.SaslServer) IncomingServerSession(org.jivesoftware.openfire.session.IncomingServerSession) LocalIncomingServerSession(org.jivesoftware.openfire.session.LocalIncomingServerSession) LocalSession(org.jivesoftware.openfire.session.LocalSession) SaslException(javax.security.sasl.SaslException) SaslFailureException(org.jivesoftware.openfire.sasl.SaslFailureException) SaslException(javax.security.sasl.SaslException) SaslFailureException(org.jivesoftware.openfire.sasl.SaslFailureException) LocalIncomingServerSession(org.jivesoftware.openfire.session.LocalIncomingServerSession) Failure(org.jivesoftware.openfire.sasl.Failure) XMPPServerInfo(org.jivesoftware.openfire.XMPPServerInfo)

Example 4 with IncomingServerSession

use of org.jivesoftware.openfire.session.IncomingServerSession in project Openfire by igniterealtime.

the class SASLAuthentication method authenticationSuccessful.

private static void authenticationSuccessful(LocalSession session, String username, byte[] successData) {
    if (username != null && LockOutManager.getInstance().isAccountDisabled(username)) {
        // Interception!  This person is locked out, fail instead!
        LockOutManager.getInstance().recordFailedLogin(username);
        authenticationFailed(session, Failure.ACCOUNT_DISABLED);
        return;
    }
    sendElement(session, "success", successData);
    // We only support SASL for c2s
    if (session instanceof ClientSession) {
        final AuthToken authToken;
        if (username == null) {
            // AuthzId is null, which indicates that authentication was anonymous.
            authToken = AuthToken.generateAnonymousToken();
        } else {
            authToken = AuthToken.generateUserToken(username);
        }
        ((LocalClientSession) session).setAuthToken(authToken);
    } else if (session instanceof IncomingServerSession) {
        String hostname = username;
        // Add the validated domain as a valid domain. The remote server can
        // now send packets from this address
        ((LocalIncomingServerSession) session).addValidatedDomain(hostname);
        Log.info("Inbound Server {} authenticated (via TLS)", username);
    }
}
Also used : LocalClientSession(org.jivesoftware.openfire.session.LocalClientSession) LocalClientSession(org.jivesoftware.openfire.session.LocalClientSession) ClientSession(org.jivesoftware.openfire.session.ClientSession) IncomingServerSession(org.jivesoftware.openfire.session.IncomingServerSession) LocalIncomingServerSession(org.jivesoftware.openfire.session.LocalIncomingServerSession) AuthToken(org.jivesoftware.openfire.auth.AuthToken)

Example 5 with IncomingServerSession

use of org.jivesoftware.openfire.session.IncomingServerSession in project Openfire by igniterealtime.

the class SocketConnection method startTLS.

public void startTLS(boolean clientMode, boolean directTLS) throws IOException {
    if (!secure) {
        secure = true;
        // Prepare for TLS
        final ClientAuth clientAuth;
        if (session instanceof IncomingServerSession) {
            clientAuth = ClientAuth.needed;
        } else {
            clientAuth = ClientAuth.wanted;
        }
        tlsStreamHandler = new TLSStreamHandler(socket, getConfiguration(), clientMode);
        if (!clientMode && !directTLS) {
            // Indicate the client that the server is ready to negotiate TLS
            deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
        }
        // Start handshake
        tlsStreamHandler.start();
        // Use new wrapped writers
        writer = new BufferedWriter(new OutputStreamWriter(tlsStreamHandler.getOutputStream(), StandardCharsets.UTF_8));
        xmlSerializer = new XMLSocketWriter(writer, this);
    }
}
Also used : IncomingServerSession(org.jivesoftware.openfire.session.IncomingServerSession) OutputStreamWriter(java.io.OutputStreamWriter) BufferedWriter(java.io.BufferedWriter)

Aggregations

IncomingServerSession (org.jivesoftware.openfire.session.IncomingServerSession)5 Lock (java.util.concurrent.locks.Lock)2 NodeID (org.jivesoftware.openfire.cluster.NodeID)2 LocalIncomingServerSession (org.jivesoftware.openfire.session.LocalIncomingServerSession)2 RemoteSessionLocator (org.jivesoftware.openfire.session.RemoteSessionLocator)2 JID (org.xmpp.packet.JID)2 BufferedWriter (java.io.BufferedWriter)1 OutputStreamWriter (java.io.OutputStreamWriter)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 Set (java.util.Set)1 SaslException (javax.security.sasl.SaslException)1 SaslServer (javax.security.sasl.SaslServer)1 XMPPServerInfo (org.jivesoftware.openfire.XMPPServerInfo)1 AuthToken (org.jivesoftware.openfire.auth.AuthToken)1 AnonymousSaslServer (org.jivesoftware.openfire.sasl.AnonymousSaslServer)1 Failure (org.jivesoftware.openfire.sasl.Failure)1 JiveSharedSecretSaslServer (org.jivesoftware.openfire.sasl.JiveSharedSecretSaslServer)1 SaslFailureException (org.jivesoftware.openfire.sasl.SaslFailureException)1