All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jivesoftware.openfire.session.LocalIncomingServerSession Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2005-2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jivesoftware.openfire.session;

import java.io.IOException;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.StreamID;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.server.ServerDialback;
import org.jivesoftware.openfire.spi.ConnectionType;
import org.jivesoftware.util.CertificateManager;
import org.jivesoftware.util.JiveGlobals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;

/**
 * Server-to-server communication is done using two TCP connections between the servers. One
 * connection is used for sending packets while the other connection is used for receiving packets.
 * The IncomingServerSession represents the connection to a remote server that will only
 * be used for receiving packets.

* * Currently only the Server Dialback method is being used for authenticating the remote server. * Once the remote server has been authenticated incoming packets will be processed by this server. * It is also possible for remote servers to authenticate more domains once the session has been * established. For optimization reasons the existing connection is used between the servers. * Therefore, the incoming server session holds the list of authenticated domains which are allowed * to send packets to this server.

* * Using the Server Dialback method it is possible that this server may also act as the * Authoritative Server. This implies that an incoming connection will be established with this * server for authenticating a domain. This incoming connection will only last for a brief moment * and after the domain has been authenticated the connection will be closed and no session will * exist. * * @author Gaston Dombiak */ public class LocalIncomingServerSession extends LocalServerSession implements IncomingServerSession { private static final Logger Log = LoggerFactory.getLogger(LocalIncomingServerSession.class); /** * List of domains, subdomains and virtual hostnames of the remote server that were * validated with this server. The remote server is allowed to send packets to this * server from any of the validated domains. */ private Set validatedDomains = new HashSet<>(); /** * Domains or subdomain of this server that was used by the remote server * when validating the new connection. This information is useful to prevent * many connections from the same remote server to the same local domain. */ private String localDomain = null; /** * Default domain, as supplied in stream header typically. */ private String fromDomain = null; /** * 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 * null.

* * @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("= 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(""); if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ENABLED, true)) { sb.append(""); if (!ServerDialback.isEnabled()) { // Server dialback is disabled so TLS is required sb.append(""); } sb.append(""); } // 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(""); } sb.append(""); } 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; } } public LocalIncomingServerSession(String serverName, Connection connection, StreamID streamID, String fromDomain) { super(serverName, connection, streamID); this.fromDomain = fromDomain; } public String getDefaultIdentity() { return this.fromDomain; } @Override boolean canProcess(Packet packet) { return true; } @Override void deliver(Packet packet) throws UnauthorizedException { // Do nothing } /** * Returns true if the request of a new domain was valid. Sessions may receive subsequent * domain validation request. If the validation of the new domain fails then the session and * the underlying TCP connection will be closed.

* * For optimization reasons, the same session may be servicing several domains of a * remote server. * * @param dbResult the DOM stanza requesting the domain validation. * @return true if the requested domain was valid. */ public boolean validateSubsequentDomain(Element dbResult) { ServerDialback method = new ServerDialback(getConnection(), getServerName()); if (method.validateRemoteDomain(dbResult, getStreamID())) { // Add the validated domain as a valid domain addValidatedDomain(dbResult.attributeValue("from")); return true; } return false; } /** * Returns true if the specified domain has been validated for this session. The remote * server should send a "db:result" packet for registering new subdomains or even * virtual hosts.

* * In the spirit of being flexible we allow remote servers to not register subdomains * and even so consider subdomains that include the server domain in their domain part * as valid domains. * * @param domain the domain to validate. * @return true if the specified domain has been validated for this session. */ public boolean isValidDomain(String domain) { // Check if the specified domain is contained in any of the validated domains for (String validatedDomain : getValidatedDomains()) { if (domain.equals(validatedDomain)) { return true; } } return false; } /** * Returns a collection with all the domains, subdomains and virtual hosts that where * validated. The remote server is allowed to send packets from any of these domains, * subdomains and virtual hosts. * * @return domains, subdomains and virtual hosts that where validated. */ public Collection getValidatedDomains() { return Collections.unmodifiableCollection(validatedDomains); } /** * Adds a new validated domain, subdomain or virtual host to the list of * validated domains for the remote server. * * @param domain the new validated domain, subdomain or virtual host to add. */ 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); } } /** * Removes the previously validated domain from the list of validated domains. The remote * server will no longer be able to send packets from the removed domain, subdomain or * virtual host. * * @param domain the domain, subdomain or virtual host to remove from the list of * validated domains. */ public void removeValidatedDomain(String domain) { validatedDomains.remove(domain); // Unregister the validated domain for this server session in SessionManager SessionManager.getInstance().unregisterIncomingServerSession(domain, this); } /** * Returns the domain or subdomain of the local server used by the remote server * when validating the session. This information is only used to prevent many * connections from the same remote server to the same domain or subdomain of * the local server. * * @return the domain or subdomain of the local server used by the remote server * when validating the session. */ @Override public String getLocalDomain() { return localDomain; } /** * Sets the domain or subdomain of the local server used by the remote server when asking * to validate the session. This information is only used to prevent many connections from * the same remote server to the same domain or subdomain of the local server. * * @param domain the domain or subdomain of the local server used when validating the * session. */ public void setLocalDomain(String domain) { localDomain = domain; } /** * Verifies the received key sent by the remote server. This server is trying to generate * an outgoing connection to the remote server and the remote server is reusing an incoming * connection for validating the key. * * @param doc the received Element that contains the key to verify. */ public void verifyReceivedKey(Element doc) { ServerDialback.verifyReceivedKey(doc, getConnection()); } @Override public String getAvailableStreamFeatures() { StringBuilder sb = new StringBuilder(); // Include Stream Compression Mechanism if (conn.getCompressionPolicy() != Connection.CompressionPolicy.disabled && !conn.isCompressed()) { sb.append("zlib"); } // Offer server dialback if using self-signed certificates and no authentication has been done yet boolean usingSelfSigned; final Certificate[] chain = conn.getLocalCertificates(); if (chain == null || chain.length == 0) { usingSelfSigned = true; } else { usingSelfSigned = CertificateManager.isSelfSignedCertificate((X509Certificate) chain[0]); } if (usingSelfSigned && ServerDialback.isEnabledForSelfSigned() && validatedDomains.isEmpty()) { sb.append(""); } return sb.toString(); } public void tlsAuth() { usingServerDialback = false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy