private void initDialback(S2SIOService serv, String remote_id) { try { CID cid = (CID) serv.getSessionData().get("cid"); String secret = handler.getSecretForDomain(cid.getLocalHost()); String key = Algorithms.generateDialbackKey( cid.getLocalHost(), cid.getRemoteHost(), secret, remote_id); if (!serv.isHandshakingOnly()) { Element elem = new Element( DB_RESULT_EL_NAME, key, new String[] {XMLNS_DB_ATT}, new String[] {XMLNS_DB_VAL}); addToResultRequested(serv, cid.getRemoteHost()); serv.getS2SConnection() .addControlPacket( Packet.packetInstance( elem, JID.jidInstanceNS(cid.getLocalHost()), JID.jidInstanceNS(cid.getRemoteHost()))); } serv.getS2SConnection().sendAllControlPackets(); } catch (NotLocalhostException ex) { generateStreamError(false, "host-unknown", serv); } }
@Override public boolean process(Packet p, S2SIOService serv, Queue<Packet> results) { CID cid = (CID) serv.getSessionData().get("cid"); boolean skipTLS = (cid == null) ? false : skipTLSForHost(cid.getRemoteHost()); // If this is a dialback packet, process it accordingly if (p.getXMLNS() == XMLNS_DB_VAL) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0}, Processing dialback packet: {1}", new Object[] {serv, p}); } processDialback(p, serv); return true; } // If this is stream features, then it depends.... if (p.isElement(FEATURES_EL, FEATURES_NS)) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0}, Stream features received packet: {1}", new Object[] {serv, p}); } CertCheckResult certCheckResult = (CertCheckResult) serv.getSessionData().get(S2SIOService.CERT_CHECK_RESULT); if (log.isLoggable(Level.FINEST)) { log.log( Level.FINEST, "{0}, TLS Certificate check: {1}, packet: {2}", new Object[] {serv, certCheckResult, p}); } // If TLS is not yet started (announced in stream features) then it is not // the right time for dialback yet // Some servers send starttls in stream features, even if TLS is already // initialized.... if (p.isXMLNSStaticStr(FEATURES_STARTTLS_PATH, START_TLS_NS) && (certCheckResult == null) && !skipTLS) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0}, Waiting for starttls, packet: {1}", new Object[] {serv, p}); } return true; } // If TLS has been started and it is a trusted peer, we do not need // dialback here // but... sometimes the remote server may request dialback anyway, // especially if they // do not trust us. if ((certCheckResult == CertCheckResult.trusted) && !(p.isXMLNSStaticStr(FEATURES_DIALBACK_PATH, DIALBACK_NS))) { if (ejabberd_bug_workaround_active) { if (log.isLoggable(Level.FINEST)) { log.log( Level.FINEST, "{0}, Ejabberd bug workaround active, proceeding to dialback anyway, packet: {1}", new Object[] {serv, p}); } } else { if (log.isLoggable(Level.FINEST)) { log.log( Level.FINEST, "{0}, TLS trusted peer, no dialback needed or requested, packet: {1}", new Object[] {serv, p}); } CIDConnections cid_conns; try { cid_conns = handler.getCIDConnections(cid, true); cid_conns.connectionAuthenticated(serv, cid); } catch (NotLocalhostException ex) { // Should not happen.... log.log( Level.INFO, "{0}, Incorrect local hostname, packet: {1}", new Object[] {serv, p}); serv.forceStop(); } catch (LocalhostException ex) { // Should not happen.... log.log( Level.INFO, "{0}, Incorrect remote hostname name, packet: {1}", new Object[] {serv, p}); serv.stop(); } return true; } } // we need to check if TLS is required if (!skipTLS && cid != null && !serv.getSessionData().containsKey("TLS") && handler.isTlsRequired(cid.getLocalHost())) { log.log( Level.FINER, "{0}, TLS is required for domain {1} but STARTTLS was not " + "offered by {2} - policy-violation", new Object[] {serv, cid.getLocalHost(), cid.getRemoteHost()}); serv.forceStop(); return true; } // Nothing else can be done right now except the dialback if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "{0}, Initializing dialback, packet: {1}", new Object[] {serv, p}); } initDialback(serv, serv.getSessionId()); } return false; }
private void processDialback(Packet p, S2SIOService serv) { // Get the cid for which the connection has been created, the cid calculated // from the packet may be different though if the remote server tries to // multiplexing CID cid_main = (CID) serv.getSessionData().get("cid"); CID cid_packet = new CID(p.getStanzaTo().getDomain(), p.getStanzaFrom().getDomain()); if (log.isLoggable(Level.FINEST)) { log.log( Level.FINEST, "{0}, DIALBACK packet: {1}, CID_packet: {2}", new Object[] {serv, p, cid_packet}); } CIDConnections cid_conns = null; // Some servers (ejabberd) do not send from/to attributes in the stream:open // which // violates the spec, they seem not to care though, so here we handle the // case. if (cid_main == null) { // This actually can only happen for 'accept' connection type // what we did not get in stream open we can get from here cid_main = cid_packet; serv.getSessionData().put("cid", cid_main); // For debuging purposes only.... serv.getSessionData().put("local-hostname", cid_main.getLocalHost()); serv.getSessionData().put("remote-hostname", cid_main.getRemoteHost()); } try { cid_conns = handler.getCIDConnections(cid_main, true); } catch (NotLocalhostException ex) { log.log(Level.FINER, "{0} Incorrect local hostname: {1}", new Object[] {serv, p}); generateStreamError(false, "host-unknown", serv); return; } catch (LocalhostException ex) { log.log(Level.FINER, "{0} Incorrect remote hostname: {1}", new Object[] {serv, p}); generateStreamError(false, "invalid-from", serv); return; } if (serv.connectionType() == ConnectionType.accept) { cid_conns.addIncoming(serv); } String remote_key = p.getElemCData(); // Dummy dialback implementation for now.... if ((p.getElemName() == RESULT_EL_NAME) || (p.getElemName() == DB_RESULT_EL_NAME)) { if (p.getType() == null) { CID cid = (CID) serv.getSessionData().get("cid"); boolean skipTls = this.skipTLSForHost(cid.getRemoteHost()); if (!skipTls && !serv.getSessionData().containsKey("TLS") && handler.isTlsRequired(cid.getLocalHost())) { log.log( Level.FINER, "{0}, rejecting S2S connection from {1} to {2} due to policy violation - STARTTLS is required", new Object[] {serv, cid.getRemoteHost(), cid.getLocalHost()}); handler.sendVerifyResult( DB_RESULT_EL_NAME, cid_main, cid_packet, false, null, serv.getSessionId(), null, false, new Element( "error", new Element[] { new Element( "policy-violation", new String[] {"xmlns"}, new String[] {"urn:ietf:params:xml:ns:xmpp-stanzas"}) }, new String[] {"type"}, new String[] {"cancel"})); } else { String conn_sessionId = serv.getSessionId(); handler.sendVerifyResult( DB_VERIFY_EL_NAME, cid_main, cid_packet, null, conn_sessionId, null, p.getElemCData(), true); } } else { if (p.getType() == StanzaType.valid) { if (wasResultRequested(serv, p.getStanzaFrom().toString())) { // serv.addCID(new CID(p.getStanzaTo().getDomain(), // p.getStanzaFrom().getDomain())); cid_conns.connectionAuthenticated(serv, cid_packet); } else if (log.isLoggable(Level.FINE)) { log.log( Level.FINE, "Received result with type valid for {0} but it was not requested!", p.getStanzaFrom()); } } else { if (log.isLoggable(Level.FINE)) { log.log( Level.FINE, "Invalid result for DB authentication: {0}, stopping connection: {1}", new Object[] {cid_packet, serv}); } serv.stop(); } } } if ((p.getElemName() == VERIFY_EL_NAME) || (p.getElemName() == DB_VERIFY_EL_NAME)) { if (p.getType() == null) { boolean result; try { String secret = handler.getSecretForDomain(cid_packet.getLocalHost()); String local_key = Algorithms.generateDialbackKey( cid_packet.getLocalHost(), cid_packet.getRemoteHost(), secret, p.getStanzaId()); if (local_key == null) { if (log.isLoggable(Level.FINER)) { log.log( Level.FINER, "The key is not available for connection CID: {0}, " + "or the packet CID: {1} ", new Object[] {cid_main, cid_packet}); } } result = local_key != null && local_key.equals(remote_key); } catch (NotLocalhostException ex) { if (log.isLoggable(Level.FINER)) { log.log(Level.FINER, "Could not retreive secret for " + cid_packet.getLocalHost(), ex); } result = false; } handler.sendVerifyResult( DB_VERIFY_EL_NAME, cid_main, cid_packet, result, p.getStanzaId(), serv.getSessionId(), null, false); } else { if (wasVerifyRequested(serv, p.getStanzaFrom().toString())) { handler.sendVerifyResult( DB_RESULT_EL_NAME, cid_main, cid_packet, (p.getType() == StanzaType.valid), null, p.getStanzaId(), null, false); if (p.getType() == StanzaType.valid) { cid_conns.connectionAuthenticated(p.getStanzaId(), cid_packet); } } else { if (log.isLoggable(Level.FINE)) { log.log( Level.FINE, "received verify for {0} but it was not requested!", p.getStanzaFrom()); } } if (serv.isHandshakingOnly()) { serv.stop(); } } } }