Commit bcacd508 authored by Dele Olajide's avatar Dele Olajide

Merge pull request #47 from surevine/s2s-fixes

S2s fixes. Thanks for doing this. I am experimenting with websockets for Openfire s2s and I need these fixes.
parents 873a5f98 5eb60080
......@@ -13,6 +13,9 @@ clean:
dpkg:
cd build && ant installer.debian
plugins:
cd build && ant plugins
eclipse: .settings .classpath .project
.settings:
......
......@@ -1773,9 +1773,13 @@ server.session.details.info=Below are details about the sessions with the remote
server.session.details.hostname=Remote server IP / Hostname:
server.session.details.incoming_session=Incoming Session Details
server.session.details.streamid=Stream ID
server.session.details.incoming_statistics=Statistics (Packets Received)
server.session.details.authentication=Authentication
server.session.details.dialback=Dialback
server.session.details.tlsauth=Certificate
server.session.details.cipher=Cipher Suite
server.session.details.incoming_statistics=Packets RX
server.session.details.outgoing_session=Outgoing Session Details
server.session.details.outgoing_statistics=Statistics (Packets Sent)
server.session.details.outgoing_statistics=Packets TX
# External Component Session summary Page
......
......@@ -528,29 +528,14 @@ public class SASLAuthentication {
if (!verify) {
authenticationSuccessful(session, hostname, null);
return Status.authenticated;
}
// Check that hostname matches the one provided in a certificate
Connection connection = session.getConnection();
try {
X509Certificate trusted = CertificateManager.getEndEntityCertificate(connection.getPeerCertificates(), SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore());
if (trusted != null) {
for (String identity : CertificateManager.getPeerIdentities(trusted)) {
// Verify that either the identity is the same as the hostname, or for wildcarded
// identities that the hostname ends with .domainspecified or -is- domainspecified.
if ((identity.startsWith("*.")
&& (hostname.endsWith(identity.replace("*.", "."))
|| hostname.equals(identity.replace("*.", ""))))
|| hostname.equals(identity)) {
authenticationSuccessful(session, hostname, null);
return Status.authenticated;
}
}
} else if(verifyCertificates(session.getConnection().getPeerCertificates(), hostname)) {
authenticationSuccessful(session, hostname, null);
LocalIncomingServerSession s = (LocalIncomingServerSession)session;
if (s != null) {
s.tlsAuth();
}
} catch(IOException e) {
/// Keystore problem.
return Status.authenticated;
}
}
else if (session instanceof LocalClientSession) {
// Client EXTERNALL login
......@@ -618,6 +603,28 @@ public class SASLAuthentication {
return Status.failed;
}
public static boolean verifyCertificates(Certificate[] chain, String hostname) {
try {
X509Certificate trusted = CertificateManager.getEndEntityCertificate(chain, SSLConfig.getKeyStore(), SSLConfig.gets2sTrustStore());
if (trusted != null) {
for (String identity : CertificateManager.getPeerIdentities(trusted)) {
// Verify that either the identity is the same as the hostname, or for wildcarded
// identities that the hostname ends with .domainspecified or -is- domainspecified.
if ((identity.startsWith("*.")
&& (hostname.endsWith(identity.replace("*.", "."))
|| hostname.equals(identity.replace("*.", ""))))
|| hostname.equals(identity)) {
return true;
}
}
}
} catch(IOException e) {
Log.warn("Keystore issue while verifying certificate chain: {}", e.getMessage());
}
return false;
}
private static Status doSharedSecretAuthentication(LocalSession session, Element doc)
throws UnsupportedEncodingException
{
......@@ -686,6 +693,7 @@ public class SASLAuthentication {
// Add the validated domain as a valid domain. The remote server can
// now send packets from this address
((LocalIncomingServerSession) session).addValidatedDomain(hostname);
Log.info("Inbound Server {} authenticated (via TLS)", username);
}
}
......
......@@ -98,116 +98,7 @@ public class ServerTrustManager implements X509TrustManager {
*/
public void checkServerTrusted(X509Certificate[] x509Certificates, String string)
throws CertificateException {
// Flag that indicates if certificates of the remote server should be validated. Disabling
// certificate validation is not recommended for production environments.
boolean verify = JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY, true);
if (verify) {
int nSize = x509Certificates.length;
if (Log.isDebugEnabled()) {
Log.debug("Certificate chain:");
for (int i=1; i<= nSize; i++) {
Log.debug("Certificate " + i + ": " + x509Certificates[i-1].toString());
}
}
List<String> peerIdentities = CertificateManager.getPeerIdentities(x509Certificates[0]);
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_CHAIN_VERIFY, true)) {
Log.debug("Verifying certificate chain...");
// Working down the chain, for every certificate in the chain,
// verify that the subject of the certificate is the issuer of the
// next certificate in the chain.
Principal principalLast = null;
for (int i = nSize -1; i >= 0 ; i--) {
X509Certificate x509certificate = x509Certificates[i];
Principal principalIssuer = x509certificate.getIssuerDN();
Principal principalSubject = x509certificate.getSubjectDN();
Log.debug("Certificate " + (i+1) + " issuer: '" + principalIssuer + "' subject: '" + principalSubject + "'");
if (principalLast != null) {
if (principalIssuer.equals(principalLast)) {
try {
PublicKey publickey =
x509Certificates[i + 1].getPublicKey();
x509Certificates[i].verify(publickey);
}
catch (GeneralSecurityException generalsecurityexception) {
throw new CertificateException(
"signature verification failed of " + peerIdentities, generalsecurityexception);
}
}
else {
throw new CertificateException(
"subject/issuer verification failed of " + peerIdentities + ". In certificate "
+ (i+1) + " of the chain, I expected the issuer to be '" + principalLast
+"' but was '"+principalIssuer+"'.");
}
}
principalLast = principalSubject;
}
}
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_ROOT_VERIFY, true)) {
Log.debug("Verifying certificate chain root certificate...");
// Verify that the the last certificate in the chain was issued
// by a third-party that the client trusts.
boolean trusted = false;
try {
trusted = trustStore.getCertificateAlias(x509Certificates[nSize - 1]) != null;
// Keep track if the other peer presented a self-signed certificate
connection.setUsingSelfSignedCertificate(!trusted && nSize == 1);
if (!trusted && nSize == 1 && JiveGlobals
.getBooleanProperty(ConnectionSettings.Server.TLS_ACCEPT_SELFSIGNED_CERTS, false))
{
Log.warn("Accepting self-signed certificate of remote server: " +
peerIdentities);
trusted = true;
}
}
catch (KeyStoreException e) {
Log.error(e.getMessage(), e);
}
if (!trusted) {
throw new CertificateException("Root certificate (subject: "+x509Certificates[nSize - 1].getSubjectX500Principal()
+ ") of " + peerIdentities + " not trusted.");
}
}
// Verify that the server either matches an identity from the chain, or
// a wildcard.
Boolean found = false;
for (String identity : peerIdentities) {
// Verify that either the identity is the same as the hostname, or for wildcarded
// identities that the hostname ends with .domainspecified or -is- domainspecified.
if ((identity.startsWith("*.")
&& (server.endsWith(identity.replace("*.", "."))
|| server.equals(identity.replace("*.", ""))))
|| server.equals(identity)) {
found = true;
break;
}
}
if (!found) {
throw new CertificateException("target verification failed of " + peerIdentities);
}
if (JiveGlobals.getBooleanProperty(ConnectionSettings.Server.TLS_CERTIFICATE_VERIFY_VALIDITY, true)) {
Log.debug("Verifying certificate chain validity (by date)...");
// For every certificate in the chain, verify that the certificate
// is valid at the current time.
Date date = new Date();
for (X509Certificate x509Certificate : x509Certificates) {
try {
x509Certificate.checkValidity(date);
} catch (GeneralSecurityException generalsecurityexception) {
throw new CertificateException("invalid date of " + peerIdentities);
}
}
}
}
// Do nothing here. As before, the certificate will be validated when the remote server authenticates.
}
public X509Certificate[] getAcceptedIssuers() {
......
......@@ -32,6 +32,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.XMPPPacketReader;
......@@ -44,6 +45,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.AuthFactory;
import org.jivesoftware.openfire.net.DNSUtil;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.ServerTrafficCounter;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.session.ConnectionSettings;
......@@ -61,6 +63,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.StreamError;
/**
......@@ -85,8 +88,13 @@ import org.xmpp.packet.StreamError;
* @author Gaston Dombiak
*/
public class ServerDialback {
private static final Logger Log = LoggerFactory.getLogger(ServerDialback.class);
private enum VerifyResult {
decline, // For some reason, we declined to do the verify.
error, // Remote error from the authoritative server.
valid, // Explicitly valid.
invalid // Invalid.
}
private static final Logger Log = LoggerFactory.getLogger(ServerDialback.class);
/**
* The utf-8 charset for decoding and encoding Jabber packet streams.
......@@ -474,6 +482,17 @@ public class ServerDialback {
return null;
}
}
/**
* Send a dialback error.
*
* @param from From
* @param to To
* @param err Error type.
*/
protected void dialbackError(String from, String to, PacketError err) {
connection.deliverRawText("<db:result type=\"error\" from=\"" + from + "\" to=\"" + to + "\">" + err.toXML() + "</db:result>");
}
/**
* Returns true if the domain requested by the remote server was validated by the Authoritative
......@@ -482,7 +501,7 @@ public class ServerDialback {
* some other machine in the Originating Server's network.<p>
*
* If the domain was not valid or some error occurred while validating the domain then the
* underlying TCP connection will be closed.
* underlying TCP connection may be closed.
*
* @param doc the request for validating the new domain.
* @param streamID the stream id generated by this server for the Originating Server.
......@@ -495,7 +514,7 @@ public class ServerDialback {
Log.debug("ServerDialback: RS - Received dialback key from host: " + hostname + " to: " + recipient);
if (!RemoteServerManager.canAccess(hostname)) {
// Remote server is not allowed to establish a connection to this server
connection.deliverRawText(new StreamError(StreamError.Condition.host_unknown).toXML());
connection.deliverRawText(new StreamError(StreamError.Condition.policy_violation).toXML());
// Close the underlying connection
connection.close();
Log.debug("ServerDialback: RS - Error, hostname is not allowed to establish a connection to " +
......@@ -504,10 +523,7 @@ public class ServerDialback {
return false;
}
else if (isHostUnknown(recipient)) {
// address does not match a recognized hostname
connection.deliverRawText(new StreamError(StreamError.Condition.host_unknown).toXML());
// Close the underlying connection
connection.close();
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.item_not_found, PacketError.Type.cancel, "Service not hosted here"));
Log.debug("ServerDialback: RS - Error, hostname not recognized: " + recipient);
return false;
}
......@@ -520,15 +536,23 @@ public class ServerDialback {
}
}
if (alreadyExists && !sessionManager.isMultipleServerConnectionsAllowed()) {
// Remote server already has a IncomingServerSession created
connection.deliverRawText(
new StreamError(StreamError.Condition.not_authorized).toXML());
// Close the underlying connection
connection.close();
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.resource_constraint, PacketError.Type.cancel, "Incoming session already exists"));
Log.debug("ServerDialback: RS - Error, incoming connection already exists from: " + hostname);
return false;
}
else {
if (SASLAuthentication.verifyCertificates(connection.getPeerCertificates(), hostname)) {
// If the remote host passes strong auth, just skip the dialback.
Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname);
sb = new StringBuilder();
sb.append("<db:result");
sb.append(" from=\"").append(recipient).append("\"");
sb.append(" to=\"").append(hostname).append("\"");
sb.append(" type=\"valid\"");
sb.append("/>");
connection.deliverRawText(sb.toString());
return true;
}
String key = doc.getTextTrim();
// Get a list of real hostnames and try to connect using DNS lookup of the specified domain
......@@ -556,38 +580,47 @@ public class ServerDialback {
}
}
if (!socket.isConnected()) {
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.remote_server_not_found, PacketError.Type.cancel, "Unable to connect to authoritative server"));
Log.warn("No server available for verifying key of remote server: " + hostname);
try {
socket.close();
} catch (IOException e) {
Log.warn("Socket error on close", e);
}
return false;
}
try {
boolean valid = verifyKey(key, streamID.toString(), recipient, hostname, socket);
Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname);
sb = new StringBuilder();
sb.append("<db:result");
sb.append(" from=\"").append(recipient).append("\"");
sb.append(" to=\"").append(hostname).append("\"");
sb.append(" type=\"");
sb.append(valid ? "valid" : "invalid");
sb.append("\"/>");
connection.deliverRawText(sb.toString());
if (!valid) {
// Close the underlying connection
connection.close();
VerifyResult result = verifyKey(key, streamID.toString(), recipient, hostname, socket);
switch(result) {
case valid:
case invalid:
boolean valid = (result == VerifyResult.valid);
Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname);
sb = new StringBuilder();
sb.append("<db:result");
sb.append(" from=\"").append(recipient).append("\"");
sb.append(" to=\"").append(hostname).append("\"");
sb.append(" type=\"");
sb.append(valid ? "valid" : "invalid");
sb.append("\"/>");
connection.deliverRawText(sb.toString());
if (!valid) {
// Close the underlying connection
connection.close();
}
return valid;
default:
break;
}
return valid;
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.remote_server_timeout, PacketError.Type.cancel, "Authoritative server returned error"));
return false;
}
catch (Exception e) {
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.remote_server_timeout, PacketError.Type.cancel, "Authoritative server failed"));
Log.warn("Error verifying key of remote server: " + hostname, e);
// Send a <remote-connection-failed/> stream error condition
// and terminate both the XML stream and the underlying
// TCP connection
connection.deliverRawText(new StreamError(
StreamError.Condition.remote_connection_failed).toXML());
// Close the underlying connection
connection.close();
return false;
}
}
......@@ -608,13 +641,14 @@ public class ServerDialback {
/**
* Verifies the key with the Authoritative Server.
*/
private boolean verifyKey(String key, String streamID, String recipient, String hostname,
private VerifyResult verifyKey(String key, String streamID, String recipient, String hostname,
Socket socket) throws IOException, XmlPullParserException,
RemoteConnectionFailedException {
XMPPPacketReader reader;
Writer writer = null;
// Set a read timeout
socket.setSoTimeout(RemoteServerManager.getSocketTimeout());
VerifyResult result = VerifyResult.error;
try {
reader = new XMPPPacketReader();
reader.setXPPFactory(FACTORY);
......@@ -630,7 +664,8 @@ public class ServerDialback {
stream.append("<stream:stream");
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
stream.append(" xmlns=\"jabber:server\"");
stream.append(" xmlns:db=\"jabber:server:dialback\">");
stream.append(" xmlns:db=\"jabber:server:dialback\"");
stream.append(" version=\"1.0\">");
writer.write(stream.toString());
writer.flush();
......@@ -639,6 +674,23 @@ public class ServerDialback {
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
if (xpp.getAttributeValue("", "version").equals("1.0")) {
Document doc;
try {
doc = reader.parseDocument();
} catch (DocumentException e) {
// TODO Auto-generated catch block
Log.warn("XML Error!", e);
return VerifyResult.error;
}
Element features = doc.getRootElement();
Element starttls = features.element("starttls");
if (starttls != null) {
if (starttls.element("required") != null) {
Log.error("TLS required for db:verify but cannot yet do this.");
}
}
}
if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) {
Log.debug("ServerDialback: RS - Asking AS to verify dialback key for id" + streamID);
// Request for verification of the key
......@@ -681,12 +733,17 @@ public class ServerDialback {
// condition is sent to the Originating Server
throw new RemoteConnectionFailedException("Invalid From");
}
else if ("valid".equals(doc.attributeValue("type"))){
Log.debug("ServerDialback: RS - Key was VERIFIED by the Authoritative Server for: {}", hostname);
result = VerifyResult.valid;
}
else if ("invalid".equals(doc.attributeValue("type"))){
Log.debug("ServerDialback: RS - Key was NOT VERIFIED by the Authoritative Server for: {}", hostname);
result = VerifyResult.invalid;
}
else {
boolean valid = "valid".equals(doc.attributeValue("type"));
Log.debug("ServerDialback: RS - Key was " + (valid ? "" : "NOT ") +
"VERIFIED by the Authoritative Server for: " +
hostname);
return valid;
Log.debug("ServerDialback: RS - Key was ERRORED by the Authoritative Server for: {}", hostname);
result = VerifyResult.error;
}
}
else {
......@@ -725,7 +782,7 @@ public class ServerDialback {
// Do nothing
}
}
return false;
return result;
}
/**
......
......@@ -43,7 +43,7 @@ import java.util.Collection;
*
* @author Gaston Dombiak
*/
public interface IncomingServerSession extends Session {
public interface IncomingServerSession extends ServerSession {
/**
* Returns a collection with all the domains, subdomains and virtual hosts that where
......
......@@ -68,7 +68,7 @@ import org.xmpp.packet.Packet;
*
* @author Gaston Dombiak
*/
public class LocalIncomingServerSession extends LocalSession implements IncomingServerSession {
public class LocalIncomingServerSession extends LocalServerSession implements IncomingServerSession {
private static final Logger Log = LoggerFactory.getLogger(LocalIncomingServerSession.class);
......@@ -368,9 +368,13 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
}
if (usingSelfSigned && ServerDialback.isEnabledForSelfSigned() && validatedDomains.isEmpty()) {
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>");
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback>");
}
return sb.toString();
}
public void tlsAuth() {
usingServerDialback = false;
}
}
......@@ -44,6 +44,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.net.DNSUtil;
import org.jivesoftware.openfire.net.MXParser;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.server.OutgoingServerSocketReader;
import org.jivesoftware.openfire.server.RemoteServerConfiguration;
......@@ -87,7 +88,7 @@ import com.jcraft.jzlib.ZInputStream;
*
* @author Gaston Dombiak
*/
public class LocalOutgoingServerSession extends LocalSession implements OutgoingServerSession {
public class LocalOutgoingServerSession extends LocalServerSession implements OutgoingServerSession {
private static final Logger Log = LoggerFactory.getLogger(LocalOutgoingServerSession.class);
......@@ -99,10 +100,6 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
private Collection<String> authenticatedDomains = new HashSet<String>();
private final Collection<String> hostnames = new HashSet<String>();
private OutgoingServerSocketReader socketReader;
/**
* Flag that indicates if the session was created using server-dialback.
*/
private boolean usingServerDialback = true;
/**
* Creates a new outgoing connection to the specified hostname if no one exists. The port of
......@@ -173,55 +170,10 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
// Notify the SessionManager that a new session has been created
sessionManager.outgoingServerSessionCreated((LocalOutgoingServerSession) session);
return true;
} else {
// Ensure that the hostname is not an IP address (i.e. contains chars)
if (!pattern.matcher(hostname).find()) {
return false;
}
// Check if hostname is a subdomain of an existing outgoing session
for (String otherHost : sessionManager.getOutgoingServers()) {
if (hostname.contains(otherHost)) {
session = sessionManager.getOutgoingServerSession(otherHost);
// Add the new hostname to the found session
session.addHostname(hostname);
return true;
}
}
// Try to establish a connection to candidate hostnames. Iterate on the
// substring after the . and try to establish a connection. If a
// connection is established then the same session will be used for
// sending packets to the "candidate hostname" as well as for the
// requested hostname (i.e. the subdomain of the candidate hostname)
// This trick is useful when remote servers haven't registered in their
// DNSs an entry for their subdomains
int index = hostname.indexOf('.');
while (index > -1 && index < hostname.length()) {
String newHostname = hostname.substring(index + 1);
String serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
if ("com".equals(newHostname) || "net".equals(newHostname) ||
"org".equals(newHostname) ||
"gov".equals(newHostname) ||
"edu".equals(newHostname) ||
serverName.equals(newHostname)) {
return false;
}
session = createOutgoingSession(domain, newHostname, port);
if (session != null) {
// Add the validated domain as an authenticated domain
session.addAuthenticatedDomain(domain);
// Add the new hostname to the list of names that the server may have
session.addHostname(hostname);
// Add the new hostname to the found session
session.addHostname(newHostname);
// Notify the SessionManager that a new session has been created
sessionManager.outgoingServerSessionCreated((LocalOutgoingServerSession) session);
return true;
} else {
index = hostname.indexOf('.', index + 1);
}
}
return false;
}
} else {
Log.warn("Fail to connect to {} for {}", hostname, domain);
return false;
}
}
// A session already exists. The session was established using server dialback so
......@@ -427,11 +379,25 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
Element proceed = reader.parseDocument().getRootElement();
if (proceed != null && proceed.getName().equals("proceed")) {
log.debug("Negotiating TLS...");
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, hostname, needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted);
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, hostname, needed ? Connection.ClientAuth.needed : Connection.ClientAuth.wanted);
} 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)) {
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());
......@@ -462,6 +428,10 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
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>");
......@@ -506,38 +476,32 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
// Bookkeeping: determine what functionality the remote server offers.
boolean saslEXTERNALoffered = false;
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;
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;
final boolean usesSelfSigned = connection.isUsingSelfSignedCertificate();
log.debug("Offering dialback functionality: {}",dialbackOffered);
log.debug("Offering EXTERNAL SASL: {}", saslEXTERNALoffered);
log.debug("Is using a self-signed certificate: {}", usesSelfSigned);
// Skip SASL EXTERNAL and use server dialback over TLS when using self-signed certificates
LocalOutgoingServerSession result = null;
if (usesSelfSigned) {
log.debug("As remote server is using self-signed certificate, SASL EXTERNAL is skipped. Attempting dialback over TLS instead.");
result = attemptDialbackOverTLS(connection, reader, domain, hostname, id);
} else {
// 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);
}
}
// 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;
}
......@@ -585,9 +549,11 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
log.debug("EXTERNAL SASL was successful.");
// SASL was successful so initiate a new stream
connection.deliverRawText(openingStream.toString());
// Reset the parser
xpp.resetInput();
//xpp.resetInput();
// // Reset the parser to use the new secured reader
xpp.setInput(new InputStreamReader(connection.getTLSStreamHandler().getInputStream(), CHARSET));
// Skip the opening stream sent by the server
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
......@@ -739,8 +705,4 @@ public class LocalOutgoingServerSession extends LocalSession implements Outgoing
// Nothing special to add
return null;
}
public boolean isUsingServerDialback() {
return usingServerDialback;
}
}
/**
*
*/
package org.jivesoftware.openfire.session;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.StreamID;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.xmpp.packet.Packet;
/**
* @author dwd
*
*/
public class LocalServerSession extends LocalSession implements ServerSession {
protected boolean usingServerDialback = true;
protected boolean outboundAllowed = false;
protected boolean inboundAllowed = false;
public LocalServerSession(String serverName, Connection connection,
StreamID streamID) {
super(serverName, connection, streamID);
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.session.LocalSession#canProcess(org.xmpp.packet.Packet)
*/
@Override
boolean canProcess(Packet packet) {
// TODO Auto-generated method stub
return false;
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.session.LocalSession#deliver(org.xmpp.packet.Packet)
*/
@Override
void deliver(Packet packet) throws UnauthorizedException {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.jivesoftware.openfire.session.LocalSession#getAvailableStreamFeatures()
*/
@Override
public String getAvailableStreamFeatures() {
// TODO Auto-generated method stub
return null;
}
public boolean isUsingServerDialback() {
return usingServerDialback;
}
}
......@@ -21,6 +21,8 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.SSLSession;
import org.jivesoftware.openfire.Connection;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.StreamID;
......@@ -28,6 +30,8 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.net.TLSStreamHandler;
import org.jivesoftware.openfire.spi.RoutingTableImpl;
import org.jivesoftware.util.LocaleUtils;
import org.slf4j.Logger;
......@@ -387,4 +391,22 @@ public abstract class LocalSession implements Session {
public boolean isUsingSelfSignedCertificate() {
return conn.isUsingSelfSignedCertificate();
}
/**
* Returns a String representing the Cipher Suite Name, or "NONE".
* @return String
*/
public String getCipherSuiteName() {
SocketConnection s = (SocketConnection)getConnection();
if (s != null) {
TLSStreamHandler t = s.getTLSStreamHandler();
if (t != null) {
SSLSession ssl = t.getSSLSession();
if (ssl != null) {
return ssl.getCipherSuite();
}
}
}
return "NONE";
}
}
......@@ -36,8 +36,7 @@ import java.util.Collection;
*
* @author Gaston Dombiak
*/
public interface OutgoingServerSession extends Session {
public interface OutgoingServerSession extends ServerSession {
/**
* Returns a collection with all the domains, subdomains and virtual hosts that where
* authenticated. The remote server will accept packets sent from any of these domains,
......@@ -84,11 +83,4 @@ public interface OutgoingServerSession extends Session {
* @return True if the subdomain was authenticated by the remote server.
*/
boolean authenticateSubdomain(String domain, String hostname);
/**
* Returns true if this outgoing session was established using server dialback.
*
* @return true if this outgoing session was established using server dialback.
*/
boolean isUsingServerDialback();
}
package org.jivesoftware.openfire.session;
public interface ServerSession extends Session {
/**
* Returns true if this outgoing session was established using server dialback.
*
* @return true if this outgoing session was established using server dialback.
*/
boolean isUsingServerDialback();
}
......@@ -195,4 +195,11 @@ public interface Session extends RoutableChannelHandler {
* @return true if the socket remains valid, false otherwise.
*/
public boolean validate();
/**
* Returns the TLS cipher suite name, if any.
* Always returns a valid string, though the string may be "NONE"
* @return cipher suite name.
*/
public String getCipherSuiteName();
}
\ No newline at end of file
......@@ -123,9 +123,12 @@
<table cellpadding="3" cellspacing="1" border="0" width="100%">
<tr>
<th width="35%" colspan="2"><fmt:message key="server.session.details.streamid" /></th>
<th width="10%"><fmt:message key="server.session.details.authentication"/></th>
<th width="10%"><fmt:message key="server.session.details.cipher"/></th>
<th width="20%"><fmt:message key="server.session.label.creation" /></th>
<th width="20%"><fmt:message key="server.session.label.last_active" /></th>
<th width="25%" nowrap><fmt:message key="server.session.details.incoming_statistics" /></th>
<th width="25%" nowrap><fmt:message key="server.session.details.outgoing_statistics" /></th>
</tr>
<tr>
<% if (inSession.isSecure()) { %>
......@@ -151,9 +154,12 @@
boolean sameActiveDay = nowCal.get(Calendar.DAY_OF_YEAR) == lastActiveCal.get(Calendar.DAY_OF_YEAR) && nowCal.get(Calendar.YEAR) == lastActiveCal.get(Calendar.YEAR);
%>
<td><%= inSession.getStreamID()%></td>
<td><% if (inSession.isUsingServerDialback()) { %><fmt:message key="server.session.details.dialback"/><% } else { %><fmt:message key="server.session.details.tlsauth"/><% } %></td>
<td><%= inSession.getCipherSuiteName() %></td>
<td align="center"><%= sameCreationDay ? JiveGlobals.formatTime(creationDate) : JiveGlobals.formatDateTime(creationDate) %></td>
<td align="center"><%= sameActiveDay ? JiveGlobals.formatTime(lastActiveDate) : JiveGlobals.formatDateTime(lastActiveDate) %></td>
<td align="center"><%= numFormatter.format(inSession.getNumClientPackets()) %></td>
<td align="center"><%= numFormatter.format(inSession.getNumServerPackets()) %></td>
</tr>
</table>
</div>
......@@ -169,8 +175,11 @@
<table cellpadding="3" cellspacing="1" border="0" width="100%">
<tr>
<th width="35%" colspan="2"><fmt:message key="server.session.details.streamid" /></th>
<th width="10%"><fmt:message key="server.session.details.authentication"/></th>
<th width="10%"><fmt:message key="server.session.details.cipher"/></th>
<th width="20%"><fmt:message key="server.session.label.creation" /></th>
<th width="20%"><fmt:message key="server.session.label.last_active" /></th>
<th width="25%" nowrap><fmt:message key="server.session.details.incoming_statistics" /></th>
<th width="25%" nowrap><fmt:message key="server.session.details.outgoing_statistics" /></th>
</tr>
<tr>
......@@ -197,8 +206,11 @@
boolean sameActiveDay = nowCal.get(Calendar.DAY_OF_YEAR) == lastActiveCal.get(Calendar.DAY_OF_YEAR) && nowCal.get(Calendar.YEAR) == lastActiveCal.get(Calendar.YEAR);
%>
<td><%= outSession.getStreamID()%></td>
<td><% if (outSession.isUsingServerDialback()) { %><fmt:message key="server.session.details.dialback"/><% } else { %><fmt:message key="server.session.details.tlsauth"/><% } %></td>
<td><%= outSession.getCipherSuiteName() %></td>
<td align="center"><%= sameCreationDay ? JiveGlobals.formatTime(creationDate) : JiveGlobals.formatDateTime(creationDate) %></td>
<td align="center"><%= sameActiveDay ? JiveGlobals.formatTime(lastActiveDate) : JiveGlobals.formatDateTime(lastActiveDate) %></td>
<td align="center"><%= numFormatter.format(outSession.getNumClientPackets()) %></td>
<td align="center"><%= numFormatter.format(outSession.getNumServerPackets()) %></td>
</tr>
</table>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment