private static LocalOutgoingServerSession attemptSASLexternal( SocketConnection connection, MXParser xpp, XMPPPacketReader reader, String domain, String hostname, String id, StringBuilder openingStream) throws DocumentException, IOException, XmlPullParserException { final Logger log = LoggerFactory.getLogger( LocalOutgoingServerSession.class.getName() + "['" + hostname + "']"); log.debug("Starting EXTERNAL SASL."); if (doExternalAuthentication(domain, 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 = new BasicStreamIDFactory().createStreamID(id); LocalOutgoingServerSession session = new LocalOutgoingServerSession( domain, connection, new OutgoingServerSocketReader(reader), streamID); connection.init(session); // Set the hostname as the address of the session session.setAddress(new JID(null, hostname, null)); // Set that the session was created using TLS+SASL (no server dialback) session.usingServerDialback = false; return session; } else { log.debug("EXTERNAL SASL failed."); return null; } }
private static LocalOutgoingServerSession secureAndAuthenticate( String hostname, SocketConnection connection, XMPPPacketReader reader, StringBuilder openingStream, String domain) throws Exception { final Logger log = LoggerFactory.getLogger( LocalOutgoingServerSession.class.getName() + "['" + hostname + "']"); Element features; log.debug("Indicating we want TLS to " + hostname); connection.deliverRawText("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); MXParser xpp = reader.getXPPParser(); // Wait for the <proceed> response Element proceed = reader.parseDocument().getRootElement(); if (proceed != null && proceed.getName().equals("proceed")) { log.debug("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); } catch (Exception e) { log.debug("Got an exception whilst negotiating TLS: " + e.getMessage()); throw e; } log.debug("TLS negotiation was successful."); if (!SASLAuthentication.verifyCertificates( connection.getPeerCertificates(), hostname, true)) { log.debug("X.509/PKIX failure on outbound session"); if (ServerDialback.isEnabled() || ServerDialback.isEnabledForSelfSigned()) { log.debug("Will continue with dialback."); } else { log.warn("No TLS auth, but TLS auth required."); return null; } } // 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) { // Check if we can use stream compression final Connection.CompressionPolicy compressionPolicy = connection.getConfiguration().getCompressionPolicy(); if (Connection.CompressionPolicy.optional == compressionPolicy) { // Verify if the remote server supports stream compression Element compression = features.element("compression"); if (compression != null) { boolean zlibSupported = false; Iterator it = compression.elementIterator("method"); while (it.hasNext()) { Element method = (Element) it.next(); if ("zlib".equals(method.getTextTrim())) { zlibSupported = true; } } if (zlibSupported) { log.debug("Suppressing request to perform compression; unsupported in this version."); zlibSupported = false; } if (zlibSupported) { log.debug("Requesting stream compression (zlib)."); connection.deliverRawText( "<compress xmlns='http://jabber.org/protocol/compress'><method>zlib</method></compress>"); // Check if we are good to start compression Element answer = reader.parseDocument().getRootElement(); if ("compressed".equals(answer.getName())) { // Server confirmed that we can use zlib compression connection.addCompression(); connection.startCompression(); log.debug("Stream compression was successful."); // Stream compression was successful so initiate a new stream connection.deliverRawText(openingStream.toString()); // Reset the parser to use stream compression over TLS ZInputStream in = new ZInputStream(connection.getTLSStreamHandler().getInputStream()); in.setFlushMode(JZlib.Z_PARTIAL_FLUSH); xpp.setInput(new InputStreamReader(in, StandardCharsets.UTF_8)); // Skip the opening stream sent by the server for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG; ) { eventType = xpp.next(); } // Get new stream features features = reader.parseDocument().getRootElement(); if (features == null) { log.debug("Error, EXTERNAL SASL was not offered."); return null; } } else { log.debug("Stream compression was rejected by " + hostname); } } else { log.debug("Stream compression found but zlib method is not supported by " + hostname); } } else { log.debug("Stream compression not supported by " + hostname); } } // Bookkeeping: determine what functionality the remote server offers. boolean saslEXTERNALoffered = false; if (features != null) { if (features.element("mechanisms") != null) { Iterator<Element> 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("Offering dialback functionality: {}", dialbackOffered); log.debug("Offering EXTERNAL SASL: {}", saslEXTERNALoffered); LocalOutgoingServerSession result = null; // first, try SASL if (saslEXTERNALoffered) { result = attemptSASLexternal(connection, xpp, reader, domain, hostname, id, openingStream); } if (result == null) { // SASL unavailable or failed, try dialback. result = attemptDialbackOverTLS(connection, reader, domain, hostname, id); } return result; } else { log.debug( "Cannot create outgoing server session, as neither SASL mechanisms nor SERVER DIALBACK were offered by " + hostname); return null; } } else { log.debug("Error, <proceed> was not received!"); return null; } }