Commit 17fbe3a9 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

TLS and SSL can now be disabled, enabled (optional, required). JM-393

git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@3087 b35dd754-fafc-0310-a699-88a17e54d16e
parent b9a0ba1e
...@@ -55,6 +55,8 @@ public class ClientSession extends Session { ...@@ -55,6 +55,8 @@ public class ClientSession extends Session {
*/ */
private static Map<String,String> allowedIPs = new HashMap<String,String>(); private static Map<String,String> allowedIPs = new HashMap<String,String>();
private static SocketConnection.TLSPolicy tlsPolicy;
/** /**
* The authentication token for this session. * The authentication token for this session.
*/ */
...@@ -86,6 +88,11 @@ public class ClientSession extends Session { ...@@ -86,6 +88,11 @@ public class ClientSession extends Session {
String address = tokens.nextToken().trim(); String address = tokens.nextToken().trim();
allowedIPs.put(address, ""); allowedIPs.put(address, "");
} }
// Set the TLS policy stored as a system property
String policyName = JiveGlobals.getProperty("xmpp.client.tls.policy",
SocketConnection.TLSPolicy.optional.toString());
tlsPolicy = SocketConnection.TLSPolicy.valueOf(policyName);
} }
/** /**
...@@ -191,6 +198,9 @@ public class ClientSession extends Session { ...@@ -191,6 +198,9 @@ public class ClientSession extends Session {
connection.setLanaguage(language); connection.setLanaguage(language);
connection.setXMPPVersion(majorVersion, minorVersion); connection.setXMPPVersion(majorVersion, minorVersion);
// Indicate the TLS policy to use for this connection
connection.setTlsPolicy(tlsPolicy);
// Create a ClientSession for this user. // Create a ClientSession for this user.
Session session = SessionManager.getInstance().createClientSession(connection); Session session = SessionManager.getInstance().createClientSession(connection);
...@@ -235,10 +245,13 @@ public class ClientSession extends Session { ...@@ -235,10 +245,13 @@ public class ClientSession extends Session {
sb = new StringBuilder(); sb = new StringBuilder();
sb.append("<stream:features>"); sb.append("<stream:features>");
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">"); if (tlsPolicy != SocketConnection.TLSPolicy.disabled) {
// TODO Consider that STARTTLS may be optional (add TLS options to the AC - disabled, optional, required) sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
// sb.append("<required/>"); if (tlsPolicy == SocketConnection.TLSPolicy.required) {
sb.append("</starttls>"); sb.append("<required/>");
}
sb.append("</starttls>");
}
// Include available SASL Mechanisms // Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session)); sb.append(SASLAuthentication.getSASLMechanisms(session));
sb.append("</stream:features>"); sb.append("</stream:features>");
...@@ -288,6 +301,33 @@ public class ClientSession extends Session { ...@@ -288,6 +301,33 @@ public class ClientSession extends Session {
} }
} }
/**
* Returns whether TLS is mandatory, optional or is disabled for clients. When TLS is
* mandatory clients are required to secure their connections or otherwise their connections
* will be closed. On the other hand, when TLS is disabled clients are not allowed to secure
* their connections using TLS. Their connections will be closed if they try to secure the
* connection. in this last case.
*
* @return whether TLS is mandatory, optional or is disabled.
*/
public static SocketConnection.TLSPolicy getTLSPolicy() {
return tlsPolicy;
}
/**
* Sets whether TLS is mandatory, optional or is disabled for clients. When TLS is
* mandatory clients are required to secure their connections or otherwise their connections
* will be closed. On the other hand, when TLS is disabled clients are not allowed to secure
* their connections using TLS. Their connections will be closed if they try to secure the
* connection. in this last case.
*
* @param policy whether TLS is mandatory, optional or is disabled.
*/
public static void setTLSPolicy(SocketConnection.TLSPolicy policy) {
tlsPolicy = policy;
JiveGlobals.setProperty("xmpp.client.tls.policy", tlsPolicy.toString());
}
/** /**
* Creates a session with an underlying connection and permission protection. * Creates a session with an underlying connection and permission protection.
* *
......
...@@ -58,6 +58,10 @@ public class SocketConnection implements Connection { ...@@ -58,6 +58,10 @@ public class SocketConnection implements Connection {
private int minorVersion = 0; private int minorVersion = 0;
private String language = null; private String language = null;
private TLSStreamHandler tlsStreamHandler; private TLSStreamHandler tlsStreamHandler;
/**
* TLS policy currently in use for this connection.
*/
private TLSPolicy tlsPolicy = TLSPolicy.optional;
/** /**
* Create a new session using the supplied socket. * Create a new session using the supplied socket.
...@@ -168,6 +172,32 @@ public class SocketConnection implements Connection { ...@@ -168,6 +172,32 @@ public class SocketConnection implements Connection {
return secure; return secure;
} }
/**
* Returns whether TLS is mandatory, optional or is disabled. When TLS is mandatory clients
* are required to secure their connections or otherwise their connections will be closed.
* On the other hand, when TLS is disabled clients are not allowed to secure their connections
* using TLS. Their connections will be closed if they try to secure the connection. in this
* last case.
*
* @return whether TLS is mandatory, optional or is disabled.
*/
public TLSPolicy getTlsPolicy() {
return tlsPolicy;
}
/**
* Sets whether TLS is mandatory, optional or is disabled. When TLS is mandatory clients
* are required to secure their connections or otherwise their connections will be closed.
* On the other hand, when TLS is disabled clients are not allowed to secure their connections
* using TLS. Their connections will be closed if they try to secure the connection. in this
* last case.
*
* @param tlsPolicy whether TLS is mandatory, optional or is disabled.
*/
public void setTlsPolicy(TLSPolicy tlsPolicy) {
this.tlsPolicy = tlsPolicy;
}
public int getMajorXMPPVersion() { public int getMajorXMPPVersion() {
return majorVersion; return majorVersion;
} }
...@@ -375,4 +405,28 @@ public class SocketConnection implements Connection { ...@@ -375,4 +405,28 @@ public class SocketConnection implements Connection {
public String toString() { public String toString() {
return super.toString() + " socket: " + socket + " session: " + session; return super.toString() + " socket: " + socket + " session: " + session;
} }
/**
* Enumeration of possible TLS policies required to interact with the server.
*/
public enum TLSPolicy {
/**
* TLS is required to interact with the server. Entities that do not secure their
* connections using TLS will get a stream error and their connections will be closed.
*/
required,
/**
* TLS is optional to interact with the server. Entities may or may not secure their
* connections using TLS.
*/
optional,
/**
* TLS is not available. Entities that request a TLS negotiation will get a stream
* error and their connections will be closed.
*/
disabled;
}
} }
\ No newline at end of file
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
package org.jivesoftware.messenger.net; package org.jivesoftware.messenger.net;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.DocumentException;
import org.dom4j.io.XPPPacketReader; import org.dom4j.io.XPPPacketReader;
import org.jivesoftware.messenger.*; import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.UnauthorizedException; import org.jivesoftware.messenger.auth.UnauthorizedException;
...@@ -29,7 +30,6 @@ import org.xmpp.packet.*; ...@@ -29,7 +30,6 @@ import org.xmpp.packet.*;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Writer;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousCloseException;
...@@ -249,8 +249,6 @@ public abstract class SocketReader implements Runnable { ...@@ -249,8 +249,6 @@ public abstract class SocketReader implements Runnable {
processIQ(packet); processIQ(packet);
} }
else if ("starttls".equals(tag)) { else if ("starttls".equals(tag)) {
// Client requested to secure the connection using TLS
connection.deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
// Negotiate TLS // Negotiate TLS
if (negotiateTLS()) { if (negotiateTLS()) {
tlsNegotiated(); tlsNegotiated();
...@@ -263,8 +261,7 @@ public abstract class SocketReader implements Runnable { ...@@ -263,8 +261,7 @@ public abstract class SocketReader implements Runnable {
} }
else if ("auth".equals(tag)) { else if ("auth".equals(tag)) {
// User is trying to authenticate using SASL // User is trying to authenticate using SASL
SASLAuthentication saslAuth = new SASLAuthentication(session, reader); if (authenticateClient(doc)) {
if (saslAuth.doHandshake(doc)) {
// SASL authentication was successful so open a new stream and offer // SASL authentication was successful so open a new stream and offer
// resource binding and session establishment (to client sessions only) // resource binding and session establishment (to client sessions only)
saslSuccessful(); saslSuccessful();
...@@ -285,6 +282,18 @@ public abstract class SocketReader implements Runnable { ...@@ -285,6 +282,18 @@ public abstract class SocketReader implements Runnable {
} }
} }
private boolean authenticateClient(Element doc) throws DocumentException, IOException,
XmlPullParserException {
// Ensure that connection was secured if TLS was required
if (connection.getTlsPolicy() == SocketConnection.TLSPolicy.required &&
!connection.isSecure()) {
closeNeverSecuredConnection();
return false;
}
SASLAuthentication saslAuth = new SASLAuthentication(session, reader);
return saslAuth.doHandshake(doc);
}
/** /**
* Process the received IQ packet. Registered * Process the received IQ packet. Registered
* {@link org.jivesoftware.messenger.interceptor.PacketInterceptor} will be invoked before * {@link org.jivesoftware.messenger.interceptor.PacketInterceptor} will be invoked before
...@@ -297,6 +306,12 @@ public abstract class SocketReader implements Runnable { ...@@ -297,6 +306,12 @@ public abstract class SocketReader implements Runnable {
* @param packet the received packet. * @param packet the received packet.
*/ */
protected void processIQ(IQ packet) throws UnauthorizedException { protected void processIQ(IQ packet) throws UnauthorizedException {
// Ensure that connection was secured if TLS was required
if (connection.getTlsPolicy() == SocketConnection.TLSPolicy.required &&
!connection.isSecure()) {
closeNeverSecuredConnection();
return;
}
try { try {
// Invoke the interceptors before we process the read packet // Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, InterceptorManager.getInstance().invokeInterceptors(packet, session, true,
...@@ -340,6 +355,12 @@ public abstract class SocketReader implements Runnable { ...@@ -340,6 +355,12 @@ public abstract class SocketReader implements Runnable {
* @param packet the received packet. * @param packet the received packet.
*/ */
protected void processPresence(Presence packet) throws UnauthorizedException { protected void processPresence(Presence packet) throws UnauthorizedException {
// Ensure that connection was secured if TLS was required
if (connection.getTlsPolicy() == SocketConnection.TLSPolicy.required &&
!connection.isSecure()) {
closeNeverSecuredConnection();
return;
}
try { try {
// Invoke the interceptors before we process the read packet // Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, InterceptorManager.getInstance().invokeInterceptors(packet, session, true,
...@@ -382,6 +403,12 @@ public abstract class SocketReader implements Runnable { ...@@ -382,6 +403,12 @@ public abstract class SocketReader implements Runnable {
* @param packet the received packet. * @param packet the received packet.
*/ */
protected void processMessage(Message packet) throws UnauthorizedException { protected void processMessage(Message packet) throws UnauthorizedException {
// Ensure that connection was secured if TLS was required
if (connection.getTlsPolicy() == SocketConnection.TLSPolicy.required &&
!connection.isSecure()) {
closeNeverSecuredConnection();
return;
}
try { try {
// Invoke the interceptors before we process the read packet // Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true, InterceptorManager.getInstance().invokeInterceptors(packet, session, true,
...@@ -418,6 +445,24 @@ public abstract class SocketReader implements Runnable { ...@@ -418,6 +445,24 @@ public abstract class SocketReader implements Runnable {
*/ */
abstract boolean processUnknowPacket(Element doc); abstract boolean processUnknowPacket(Element doc);
/**
* Close the connection since TLS was mandatory and the entity never negotiated TLS. Before
* closing the connection a stream error will be sent to the entity.
*/
private void closeNeverSecuredConnection() {
StringBuilder sb = new StringBuilder();
// Set the not_authorized error
StreamError error = new StreamError(StreamError.Condition.not_authorized);
sb.append(error.toXML());
// Deliver stanza
connection.deliverRawText(sb.toString());
// Close the underlying connection
connection.close();
// Log a warning so that admins can track this case from the server side
Log.warn("TLS was required by the server and connection was never secured. " +
"Closing connection : " + connection);
}
private IQ getIQ(Element doc) { private IQ getIQ(Element doc) {
Element query = doc.element("query"); Element query = doc.element("query");
if (query != null && "jabber:iq:roster".equals(query.getNamespaceURI())) { if (query != null && "jabber:iq:roster".equals(query.getNamespaceURI())) {
...@@ -536,6 +581,22 @@ public abstract class SocketReader implements Runnable { ...@@ -536,6 +581,22 @@ public abstract class SocketReader implements Runnable {
* @throws XmlPullParserException if an error occures while parsing. * @throws XmlPullParserException if an error occures while parsing.
*/ */
private boolean negotiateTLS() throws IOException, XmlPullParserException { private boolean negotiateTLS() throws IOException, XmlPullParserException {
if (connection.getTlsPolicy() == SocketConnection.TLSPolicy.disabled) {
StringBuilder sb = new StringBuilder();
// Set the not_authorized error
StreamError error = new StreamError(StreamError.Condition.not_authorized);
sb.append(error.toXML());
// Deliver stanza
connection.deliverRawText(sb.toString());
// Close the underlying connection
connection.close();
// Log a warning so that admins can track this case from the server side
Log.warn("TLS requested by initiator when TLS was never offered by server. " +
"Closing connection : " + connection);
return false;
}
// Client requested to secure the connection using TLS
connection.deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
// Negotiate TLS. // Negotiate TLS.
try { try {
connection.startTLS(false); connection.startTLS(false);
......
This diff is collapsed.
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