diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalOutgoingServerSession.java b/xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalOutgoingServerSession.java index f999869532..09d2eaa292 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalOutgoingServerSession.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/session/LocalOutgoingServerSession.java @@ -18,38 +18,24 @@ import com.google.common.collect.Interner; import com.google.common.collect.Interners; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.dom4j.io.XMPPPacketReader; import org.jivesoftware.openfire.*; import org.jivesoftware.openfire.auth.UnauthorizedException; import org.jivesoftware.openfire.event.ServerSessionEventDispatcher; -import org.jivesoftware.openfire.net.MXParser; -import org.jivesoftware.openfire.net.SASLAuthentication; -import org.jivesoftware.openfire.net.SocketConnection; import org.jivesoftware.openfire.net.SocketUtil; import org.jivesoftware.openfire.nio.NettySessionInitializer; import org.jivesoftware.openfire.server.OutgoingServerSocketReader; import org.jivesoftware.openfire.server.RemoteServerManager; import org.jivesoftware.openfire.server.ServerDialback; -import org.jivesoftware.openfire.spi.BasicStreamIDFactory; -import org.jivesoftware.util.StringUtils; import org.jivesoftware.util.TaskEngine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; import org.xmpp.packet.*; import javax.annotation.Nonnull; -import java.io.IOException; -import java.io.InputStreamReader; import java.net.Socket; import java.net.SocketAddress; -import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -298,202 +284,6 @@ private static LocalOutgoingServerSession createOutgoingSession(@Nonnull final D } } - public static LocalOutgoingServerSession encryptAndAuthenticate(DomainPair domainPair, SocketConnection connection, XMPPPacketReader reader, StringBuilder openingStream) throws Exception { - final Logger log = LoggerFactory.getLogger(Log.getName() + "[Encrypt connection for: " + domainPair + "]" ); - Element features; - - log.debug( "Encrypting and authenticating connection ..."); - - log.debug( "Indicating we want TLS and wait for response." ); - connection.deliverRawText( "" ); - - MXParser xpp = reader.getXPPParser(); - // Wait for the response - Element proceed = reader.parseDocument().getRootElement(); - if (proceed != null && proceed.getName().equals("proceed")) { - log.debug( "Received 'proceed' from remote server. Negotiating TLS..." ); - try { -// boolean needed = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true) && -// JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_CHAIN_VERIFY, true) && -// !JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS, false); - connection.startTLS(true, false); - } catch(Exception e) { - log.debug("TLS negotiation failed: " + e.getMessage()); - throw e; - } - log.debug( "TLS negotiation was successful. Connection encrypted. Proceeding with authentication..." ); - if (!SASLAuthentication.verifyCertificates(connection.getPeerCertificates(), domainPair.getRemote(), true)) { - if (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned()) { - log.debug( "SASL authentication failed. Will continue with dialback." ); - } else { - log.warn( "Unable to authenticate the connection: SASL authentication failed (and dialback is not available)." ); - return null; - } - } - - log.debug( "TLS negotiation was successful so initiate a new stream." ); - connection.deliverRawText( openingStream.toString() ); - - // Reset the parser to use the new secured reader - xpp.setInput(new InputStreamReader(connection.getTLSStreamHandler().getInputStream(), StandardCharsets.UTF_8)); - // Skip new stream element - 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) { - return authenticate( domainPair, connection, reader, openingStream, features, id ); - } - else { - log.debug( "Failed to encrypt and authenticate connection: neither SASL mechanisms nor SERVER DIALBACK were offered by the remote host." ); - return null; - } - } - else { - log.debug( "Failed to encrypt and authenticate connection: was not received!" ); - return null; - } - } - - private static LocalOutgoingServerSession authenticate( final DomainPair domainPair, - final SocketConnection connection, - final XMPPPacketReader reader, - final StringBuilder openingStream, - final Element features, - final String id ) throws DocumentException, IOException, XmlPullParserException - { - final Logger log = LoggerFactory.getLogger(Log.getName() + "[Authenticate connection for: " + domainPair + "]" ); - - MXParser xpp = reader.getXPPParser(); - - // Bookkeeping: determine what functionality the remote server offers. - boolean saslEXTERNALoffered = false; - if (features.element("mechanisms") != null) { - Iterator it = features.element( "mechanisms").elementIterator(); - while (it.hasNext()) { - Element mechanism = it.next(); - if ("EXTERNAL".equals(mechanism.getTextTrim())) { - saslEXTERNALoffered = true; - break; - } - } - } - final boolean dialbackOffered = features.element("dialback") != null; - - log.debug("Remote server is offering dialback: {}, EXTERNAL SASL: {}", dialbackOffered, saslEXTERNALoffered ); - - LocalOutgoingServerSession result = null; - - // first, try SASL - if (saslEXTERNALoffered) { - log.debug( "Trying to authenticate with EXTERNAL SASL." ); - result = attemptSASLexternal(connection, xpp, reader, domainPair, id, openingStream); - if (result == null) { - log.debug( "Failed to authenticate with EXTERNAL SASL." ); - } else { - log.debug( "Successfully authenticated with EXTERNAL SASL." ); - } - } - - // SASL unavailable or failed, try dialback. - if (result == null) { - log.debug( "Trying to authenticate with dialback." ); - result = attemptDialbackOverTLS(connection, reader, domainPair, id); - if (result == null) { - log.debug( "Failed to authenticate with dialback." ); - } else { - log.debug( "Successfully authenticated with dialback." ); - } - } - - if ( result != null ) { - log.debug( "Successfully encrypted and authenticated connection!" ); - return result; - } else { - log.warn( "Unable to encrypt and authenticate connection: Exhausted all options." ); - return null; - } - } - - 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 = 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)); - session.setAuthenticationMethod(AuthenticationMethod.DIALBACK); - 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; - } - } - - private static LocalOutgoingServerSession attemptSASLexternal(SocketConnection connection, MXParser xpp, XMPPPacketReader reader, DomainPair domainPair, String id, StringBuilder openingStream) throws DocumentException, IOException, XmlPullParserException { - final Logger log = LoggerFactory.getLogger( Log.getName() + "[EXTERNAL SASL for: " + domainPair + " (Stream ID: " + id + ")]" ); - - log.debug("Starting EXTERNAL SASL."); - if (doExternalAuthentication(domainPair.getLocal(), connection, reader)) { - log.debug("EXTERNAL SASL was successful."); - // SASL was successful so initiate a new stream - connection.deliverRawText(openingStream.toString()); - - // Reset the parser - //xpp.resetInput(); - // // Reset the parser to use the new secured reader - xpp.setInput(new InputStreamReader(connection.getTLSStreamHandler().getInputStream(), StandardCharsets.UTF_8)); - // 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 = BasicStreamIDFactory.createStreamID(id); - LocalOutgoingServerSession session = new LocalOutgoingServerSession(domainPair.getLocal(), connection, new OutgoingServerSocketReader(reader), streamID); - connection.init(session); - // Set the remote domain name as the address of the session - session.setAddress(new JID(null, domainPair.getRemote(), null)); - // Set that the session was created using TLS+SASL (no server dialback) - session.setAuthenticationMethod(AuthenticationMethod.SASL_EXTERNAL); - return session; - } - else { - log.debug("EXTERNAL SASL failed."); - return null; - } - } - - private static boolean doExternalAuthentication(String localDomain, SocketConnection connection, - XMPPPacketReader reader) throws DocumentException, IOException, XmlPullParserException { - - StringBuilder sb = new StringBuilder(); - sb.append(""); - sb.append(StringUtils.encodeBase64(localDomain)); - sb.append(""); - connection.deliverRawText(sb.toString()); - - Element response = reader.parseDocument().getRootElement(); - return response != null && "success".equals(response.getName()); - } - public LocalOutgoingServerSession(String localDomain, Connection connection, OutgoingServerSocketReader socketReader, StreamID streamID) { super(localDomain, connection, streamID); this.socketReader = socketReader;