public void startTLS(boolean clientMode, String remoteServer, ClientAuth authentication) throws Exception { boolean c2s = (remoteServer == null); KeyStore ksKeys = SSLConfig.getKeyStore(); String keypass = SSLConfig.getKeyPassword(); KeyStore ksTrust = (c2s ? SSLConfig.getc2sTrustStore() : SSLConfig.gets2sTrustStore()); String trustpass = (c2s ? SSLConfig.getc2sTrustPassword() : SSLConfig.gets2sTrustPassword()); if (c2s) Log.debug("NIOConnection: startTLS: using c2s"); else Log.debug("NIOConnection: startTLS: using s2s"); // KeyManager's decide which key material to use. KeyManager[] km = SSLJiveKeyManagerFactory.getKeyManagers(ksKeys, keypass); // TrustManager's decide whether to allow connections. TrustManager[] tm = SSLJiveTrustManagerFactory.getTrustManagers(ksTrust, trustpass); if (clientMode || authentication == ClientAuth.needed || authentication == ClientAuth.wanted) { // We might need to verify a certificate from our peer, so get different TrustManager[]'s if (c2s) { // Check if we can trust certificates presented by the client tm = new TrustManager[] {new ClientTrustManager(ksTrust)}; } else { // Check if we can trust certificates presented by the server tm = new TrustManager[] {new ServerTrustManager(remoteServer, ksTrust, this)}; } } String algorithm = JiveGlobals.getProperty(ConnectionSettings.Client.TLS_ALGORITHM, "TLS"); SSLContext tlsContext = SSLContext.getInstance(algorithm); tlsContext.init(km, tm, null); SslFilter filter = new SslFilter(tlsContext); filter.setUseClientMode(clientMode); // Disable SSLv3 due to POODLE vulnerability. filter.setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}); if (authentication == ClientAuth.needed) { filter.setNeedClientAuth(true); } else if (authentication == ClientAuth.wanted) { // Just indicate that we would like to authenticate the client but if client // certificates are self-signed or have no certificate chain then we are still // good filter.setWantClientAuth(true); } ioSession.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, TLS_FILTER_NAME, filter); ioSession.setAttribute(SslFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE); if (!clientMode) { // Indicate the client that the server is ready to negotiate TLS deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>"); } }
@Override public String getAvailableStreamFeatures() { StringBuilder sb = new StringBuilder(); // Include Stream Compression Mechanism if (conn.getCompressionPolicy() != Connection.CompressionPolicy.disabled && !conn.isCompressed()) { sb.append( "<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>"); } // 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 { try { usingSelfSigned = CertificateManager.isSelfSignedCertificate( SSLConfig.getKeyStore(), (X509Certificate) chain[0]); } catch (KeyStoreException ex) { Log.warn( "Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex); usingSelfSigned = true; } catch (IOException ex) { Log.warn( "Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex); usingSelfSigned = true; } } if (usingSelfSigned && ServerDialback.isEnabledForSelfSigned() && validatedDomains.isEmpty()) { sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>"); } return sb.toString(); }
/** * Returns a newly created session between the server and a client. The session will be created * and returned only if correct name/prefix (i.e. 'stream' or 'flash') and namespace were provided * by the client. * * @param serverName the name of the server where the session is connecting to. * @param xpp the parser that is reading the provided XML through the connection. * @param connection the connection with the client. * @return a newly created session between the server and a client. * @throws org.xmlpull.v1.XmlPullParserException if an error occurs while parsing incoming data. */ public static LocalClientSession createSession( String serverName, XmlPullParser xpp, Connection connection) throws XmlPullParserException { boolean isFlashClient = xpp.getPrefix().equals("flash"); connection.setFlashClient(isFlashClient); // Conduct error checking, the opening tag should be 'stream' // in the 'etherx' namespace if (!xpp.getName().equals("stream") && !isFlashClient) { throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-stream")); } if (!xpp.getNamespace(xpp.getPrefix()).equals(ETHERX_NAMESPACE) && !(isFlashClient && xpp.getNamespace(xpp.getPrefix()).equals(FLASH_NAMESPACE))) { throw new XmlPullParserException(LocaleUtils.getLocalizedString("admin.error.bad-namespace")); } if (!allowedIPs.isEmpty()) { String hostAddress = "Unknown"; // The server is using a whitelist so check that the IP address of the client // is authorized to connect to the server try { hostAddress = connection.getHostAddress(); } catch (UnknownHostException e) { // Do nothing } if (!isAllowed(connection)) { // Client cannot connect from this IP address so end the stream and // TCP connection Log.debug( "LocalClientSession: Closed connection to client attempting to connect from: " + hostAddress); // Include the not-authorized error in the response StreamError error = new StreamError(StreamError.Condition.not_authorized); connection.deliverRawText(error.toXML()); // Close the underlying connection connection.close(); return null; } } // Default language is English ("en"). String language = "en"; // Default to a version of "0.0". Clients written before the XMPP 1.0 spec may // not report a version in which case "0.0" should be assumed (per rfc3920 // section 4.4.1). int majorVersion = 0; int minorVersion = 0; for (int i = 0; i < xpp.getAttributeCount(); i++) { if ("lang".equals(xpp.getAttributeName(i))) { language = xpp.getAttributeValue(i); } if ("version".equals(xpp.getAttributeName(i))) { try { int[] version = decodeVersion(xpp.getAttributeValue(i)); majorVersion = version[0]; minorVersion = version[1]; } catch (Exception e) { Log.error(e.getMessage(), e); } } } // If the client supports a greater major version than the server, // set the version to the highest one the server supports. if (majorVersion > MAJOR_VERSION) { majorVersion = MAJOR_VERSION; minorVersion = MINOR_VERSION; } else if (majorVersion == MAJOR_VERSION) { // If the client supports a greater minor version than the // server, set the version to the highest one that the server // supports. if (minorVersion > MINOR_VERSION) { minorVersion = MINOR_VERSION; } } // Store language and version information in the connection. connection.setLanaguage(language); connection.setXMPPVersion(majorVersion, minorVersion); // Indicate the TLS policy to use for this connection if (!connection.isSecure()) { boolean hasCertificates = false; try { hasCertificates = SSLConfig.getKeyStore().size() > 0; } catch (Exception e) { Log.error(e.getMessage(), e); } Connection.TLSPolicy tlsPolicy = getTLSPolicy(); if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates) { Log.error( "Client session rejected. TLS is required but no certificates " + "were created."); return null; } // Set default TLS policy connection.setTlsPolicy(hasCertificates ? tlsPolicy : Connection.TLSPolicy.disabled); } else { // Set default TLS policy connection.setTlsPolicy(Connection.TLSPolicy.disabled); } // Indicate the compression policy to use for this connection connection.setCompressionPolicy(getCompressionPolicy()); // Create a ClientSession for this user. LocalClientSession session = SessionManager.getInstance().createClientSession(connection); // Build the start packet response StringBuilder sb = new StringBuilder(200); sb.append("<?xml version='1.0' encoding='"); sb.append(CHARSET); sb.append("'?>"); if (isFlashClient) { sb.append("<flash:stream xmlns:flash=\"http://www.jabber.com/streams/flash\" "); } else { sb.append("<stream:stream "); } sb.append("xmlns:stream=\"http://etherx.jabber.org/streams\" xmlns=\"jabber:client\" from=\""); sb.append(serverName); sb.append("\" id=\""); sb.append(session.getStreamID().toString()); sb.append("\" xml:lang=\""); sb.append(language); // Don't include version info if the version is 0.0. if (majorVersion != 0) { sb.append("\" version=\""); sb.append(majorVersion).append(".").append(minorVersion); } sb.append("\">"); connection.deliverRawText(sb.toString()); // If this is a "Jabber" connection, the session is now initialized and we can // return to allow normal packet parsing. if (majorVersion == 0) { return session; } // Otherwise, this is at least XMPP 1.0 so we need to announce stream features. sb = new StringBuilder(490); sb.append("<stream:features>"); if (connection.getTlsPolicy() != Connection.TLSPolicy.disabled) { sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">"); if (connection.getTlsPolicy() == Connection.TLSPolicy.required) { sb.append("<required/>"); } sb.append("</starttls>"); } // Include available SASL Mechanisms sb.append(SASLAuthentication.getSASLMechanisms(session)); // Include Stream features String specificFeatures = session.getAvailableStreamFeatures(); if (specificFeatures != null) { sb.append(specificFeatures); } sb.append("</stream:features>"); connection.deliverRawText(sb.toString()); return session; }
/** Starts the Jetty instance. */ public void startup() { restartNeeded = false; // Add listener for certificate events certificateListener = new CertificateListener(); CertificateManager.addListener(certificateListener); adminPort = JiveGlobals.getXMLProperty("adminConsole.port", 9090); adminSecurePort = JiveGlobals.getXMLProperty("adminConsole.securePort", 9091); adminServer = new Server(); final QueuedThreadPool tp = new QueuedThreadPool(254); tp.setName("Jetty-QTP-AdminConsole"); adminServer.setThreadPool(tp); // Do not send Jetty info in HTTP headers adminServer.setSendServerVersion(false); // Create connector for http traffic if it's enabled. if (adminPort > 0) { Connector httpConnector = new SelectChannelConnector(); // Listen on a specific network interface if it has been set. String bindInterface = getBindInterface(); httpConnector.setHost(bindInterface); httpConnector.setPort(adminPort); adminServer.addConnector(httpConnector); } // Create a connector for https traffic if it's enabled. sslEnabled = false; try { if (adminSecurePort > 0 && CertificateManager.isRSACertificate(SSLConfig.getKeyStore(), "*")) { if (!CertificateManager.isRSACertificate( SSLConfig.getKeyStore(), XMPPServer.getInstance().getServerInfo().getXMPPDomain())) { Log.warn( "Admin console: Using RSA certificates but they are not valid for the hosted domain"); } JiveSslConnector httpsConnector = new JiveSslConnector(); String bindInterface = getBindInterface(); httpsConnector.setHost(bindInterface); httpsConnector.setPort(adminSecurePort); httpsConnector.setTrustPassword(SSLConfig.gets2sTrustPassword()); httpsConnector.setTruststoreType(SSLConfig.getStoreType()); httpsConnector.setTruststore(SSLConfig.gets2sTruststoreLocation()); httpsConnector.setNeedClientAuth(false); httpsConnector.setWantClientAuth(false); httpsConnector.setKeyPassword(SSLConfig.getKeyPassword()); httpsConnector.setKeystoreType(SSLConfig.getStoreType()); httpsConnector.setKeystore(SSLConfig.getKeystoreLocation()); adminServer.addConnector(httpsConnector); sslEnabled = true; } } catch (Exception e) { Log.error(e.getMessage(), e); } // Make sure that at least one connector was registered. if (adminServer.getConnectors() == null || adminServer.getConnectors().length == 0) { adminServer = null; // Log warning. log(LocaleUtils.getLocalizedString("admin.console.warning")); return; } HandlerCollection collection = new HandlerCollection(); adminServer.setHandler(collection); collection.setHandlers(new Handler[] {contexts, new DefaultHandler()}); try { adminServer.start(); } catch (Exception e) { Log.error("Could not start admin conosle server", e); } // Log the ports that the admin server is listening on. logAdminConsolePorts(); }
@Override protected SSLContext createSSLContext() throws Exception { return SSLConfig.getSSLContext(); }
/** * 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"); int[] serverVersion = version != null ? decodeVersion(version) : new int[] {0, 0}; 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); // 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(serverName).append("\""); openingStream.append(" id=\"").append(streamID).append("\""); openingStream.append(" version=\"1.0\">"); 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 = ServerDialback.isEnabled() ? Connection.TLSPolicy.optional : Connection.TLSPolicy.required; boolean hasCertificates = false; try { hasCertificates = SSLConfig.getKeyStore().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 String policyName = JiveGlobals.getProperty( "xmpp.server.compression.policy", Connection.CompressionPolicy.disabled.toString()); Connection.CompressionPolicy compressionPolicy = Connection.CompressionPolicy.valueOf(policyName); connection.setCompressionPolicy(compressionPolicy); 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("xmpp.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\"/>"); } 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; } }