예제 #1
0
  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;
    }
  }