Example #1
0
  /**
   * Process the received stanza using the given XMPP packet reader.
   *
   * @param stanza the received statza
   * @param reader the XMPP packet reader
   * @throws Exception if the XML stream is not valid.
   */
  public void process(String stanza, XMPPPacketReader reader) throws Exception {
    boolean initialStream = stanza.startsWith("<stream:stream");
    if (!sessionCreated || initialStream) {
      if (!initialStream) {
        return; // Ignore <?xml version="1.0"?>
      }
      if (!sessionCreated) {
        sessionCreated = true;
        MXParser parser = reader.getXPPParser();
        parser.setInput(new StringReader(stanza));
        createSession(parser);
      } else if (startedTLS) {
        startedTLS = false;
        tlsNegotiated();
      }
      return;
    }

    // If end of stream was requested
    if (stanza.equals("</stream:stream>")) {
      session.close();
      return;
    }
    // Ignore <?xml version="1.0"?>
    if (stanza.startsWith("<?xml")) {
      return;
    }
    // Create DOM object
    Element doc = reader.read(new StringReader(stanza)).getRootElement();
    if (doc == null) {
      return;
    }

    String tag = doc.getName();
    if ("starttls".equals(tag)) {
      if (negotiateTLS()) { // Negotiate TLS
        startedTLS = true;
      } else {
        connection.close();
        session = null;
      }
    } else if ("message".equals(tag)) {
      processMessage(doc);
    } else if ("presence".equals(tag)) {
      log.debug("presence...");
      processPresence(doc);
    } else if ("iq".equals(tag)) {
      log.debug("iq...");
      processIQ(doc);
    } else {
      log.warn("Unexpected packet tag (not message, iq, presence)" + doc.asXML());
      session.close();
    }
  }
  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;
    }
  }