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 {
*/
private static Map<String,String> allowedIPs = new HashMap<String,String>();
private static SocketConnection.TLSPolicy tlsPolicy;
/**
* The authentication token for this session.
*/
......@@ -86,6 +88,11 @@ public class ClientSession extends Session {
String address = tokens.nextToken().trim();
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 {
connection.setLanaguage(language);
connection.setXMPPVersion(majorVersion, minorVersion);
// Indicate the TLS policy to use for this connection
connection.setTlsPolicy(tlsPolicy);
// Create a ClientSession for this user.
Session session = SessionManager.getInstance().createClientSession(connection);
......@@ -235,10 +245,13 @@ public class ClientSession extends Session {
sb = new StringBuilder();
sb.append("<stream:features>");
if (tlsPolicy != SocketConnection.TLSPolicy.disabled) {
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
// TODO Consider that STARTTLS may be optional (add TLS options to the AC - disabled, optional, required)
// sb.append("<required/>");
if (tlsPolicy == SocketConnection.TLSPolicy.required) {
sb.append("<required/>");
}
sb.append("</starttls>");
}
// Include available SASL Mechanisms
sb.append(SASLAuthentication.getSASLMechanisms(session));
sb.append("</stream:features>");
......@@ -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.
*
......
......@@ -58,6 +58,10 @@ public class SocketConnection implements Connection {
private int minorVersion = 0;
private String language = null;
private TLSStreamHandler tlsStreamHandler;
/**
* TLS policy currently in use for this connection.
*/
private TLSPolicy tlsPolicy = TLSPolicy.optional;
/**
* Create a new session using the supplied socket.
......@@ -168,6 +172,32 @@ public class SocketConnection implements Connection {
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() {
return majorVersion;
}
......@@ -375,4 +405,28 @@ public class SocketConnection implements Connection {
public String toString() {
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 @@
package org.jivesoftware.messenger.net;
import org.dom4j.Element;
import org.dom4j.DocumentException;
import org.dom4j.io.XPPPacketReader;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
......@@ -29,7 +30,6 @@ import org.xmpp.packet.*;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.AsynchronousCloseException;
......@@ -249,8 +249,6 @@ public abstract class SocketReader implements Runnable {
processIQ(packet);
}
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
if (negotiateTLS()) {
tlsNegotiated();
......@@ -263,8 +261,7 @@ public abstract class SocketReader implements Runnable {
}
else if ("auth".equals(tag)) {
// User is trying to authenticate using SASL
SASLAuthentication saslAuth = new SASLAuthentication(session, reader);
if (saslAuth.doHandshake(doc)) {
if (authenticateClient(doc)) {
// SASL authentication was successful so open a new stream and offer
// resource binding and session establishment (to client sessions only)
saslSuccessful();
......@@ -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
* {@link org.jivesoftware.messenger.interceptor.PacketInterceptor} will be invoked before
......@@ -297,6 +306,12 @@ public abstract class SocketReader implements Runnable {
* @param packet the received packet.
*/
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 {
// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true,
......@@ -340,6 +355,12 @@ public abstract class SocketReader implements Runnable {
* @param packet the received packet.
*/
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 {
// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true,
......@@ -382,6 +403,12 @@ public abstract class SocketReader implements Runnable {
* @param packet the received packet.
*/
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 {
// Invoke the interceptors before we process the read packet
InterceptorManager.getInstance().invokeInterceptors(packet, session, true,
......@@ -418,6 +445,24 @@ public abstract class SocketReader implements Runnable {
*/
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) {
Element query = doc.element("query");
if (query != null && "jabber:iq:roster".equals(query.getNamespaceURI())) {
......@@ -536,6 +581,22 @@ public abstract class SocketReader implements Runnable {
* @throws XmlPullParserException if an error occures while parsing.
*/
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.
try {
connection.startTLS(false);
......
......@@ -17,6 +17,10 @@
java.io.ByteArrayInputStream"
errorPage="error.jsp"
%>
<%@ page import="org.jivesoftware.messenger.ClientSession"%>
<%@ page import="org.jivesoftware.messenger.net.SocketConnection"%>
<%@ page import="org.jivesoftware.messenger.XMPPServer"%>
<%@ page import="org.jivesoftware.messenger.ConnectionManager"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
......@@ -29,10 +33,81 @@
boolean install = request.getParameter("install") != null;
boolean uninstall = ParamUtils.getBooleanParameter(request,"uninstall");
boolean update = request.getParameter("update") != null;
boolean success = ParamUtils.getBooleanParameter(request, "success");
String clientSecurityRequired = ParamUtils.getParameter(request,"clientSecurityRequired");
String ssl = ParamUtils.getParameter(request, "ssl");
String tls = ParamUtils.getParameter(request, "tls");
KeyStore keyStore = SSLConfig.getKeyStore();
KeyStore trustStore = SSLConfig.getTrustStore();
Map<String, Object> errors = new HashMap<String, Object>();
if (update) {
if ("req".equals(clientSecurityRequired)) {
// User selected that security is required
// Enable 5222 port and make TLS required
XMPPServer.getInstance().getConnectionManager().enableClientListener(true);
ClientSession.setTLSPolicy(SocketConnection.TLSPolicy.required);
// Enable 5223 port (old SSL port)
XMPPServer.getInstance().getConnectionManager().enableClientSSLListener(true);
}
else if ("notreq".equals(clientSecurityRequired)) {
// User selected that security is NOT required
// Enable 5222 port and make TLS optional
XMPPServer.getInstance().getConnectionManager().enableClientListener(true);
ClientSession.setTLSPolicy(SocketConnection.TLSPolicy.optional);
// Enable 5223 port (old SSL port)
XMPPServer.getInstance().getConnectionManager().enableClientSSLListener(true);
}
else if ("custom".equals(clientSecurityRequired)) {
// User selected custom client authentication
// Enable or disable 5223 port (old SSL port)
XMPPServer.getInstance().getConnectionManager().enableClientSSLListener("available".equals(ssl));
// Enable port 5222 and configure TLS policy
XMPPServer.getInstance().getConnectionManager().enableClientListener(true);
if ("notavailable".equals(tls)) {
ClientSession.setTLSPolicy(SocketConnection.TLSPolicy.disabled);
}
else if ("optional".equals(tls)) {
ClientSession.setTLSPolicy(SocketConnection.TLSPolicy.optional);
}
else {
ClientSession.setTLSPolicy(SocketConnection.TLSPolicy.required);
}
}
success = true;
}
// Set page vars
ConnectionManager connectionManager = XMPPServer.getInstance().getConnectionManager();
if (connectionManager.isClientListenerEnabled() && connectionManager.isClientSSLListenerEnabled()) {
if (SocketConnection.TLSPolicy.required.equals(ClientSession.getTLSPolicy())) {
clientSecurityRequired = "req";
ssl = "available";
tls = "required";
}
else if (SocketConnection.TLSPolicy.optional.equals(ClientSession.getTLSPolicy())) {
clientSecurityRequired = "notreq";
ssl = "available";
tls = "optional";
}
else {
clientSecurityRequired = "custom";
ssl = "available";
tls = "notavailable";
}
}
else {
clientSecurityRequired = "custom";
ssl = connectionManager.isClientSSLListenerEnabled() ? "available" : "notavailable";
tls = SocketConnection.TLSPolicy.disabled.equals(ClientSession.getTLSPolicy()) ? "notavailable" : ClientSession.getTLSPolicy().toString();
}
if (install) {
if (cert == null){
errors.put("cert","");
......@@ -85,10 +160,71 @@
<title><fmt:message key="ssl.settings.title"/></title>
<meta name="pageID" content="server-ssl"/>
<meta name="helpPage" content="manage_security_certificates.html"/>
<script language="JavaScript" type="text/javascript">
<!-- // code for window popups
function showOrHide(whichLayer, mode)
{
if (mode == "show") {
mode = "";
}
else {
mode = "none";
}
if (document.getElementById)
{
// this is the way the standards work
var style2 = document.getElementById(whichLayer).style;
style2.display = mode;
}
else if (document.all)
{
// this is the way old msie versions work
var style2 = document.all[whichLayer].style;
style2.display = mode;
}
else if (document.layers)
{
// this is the way nn4 works
var style2 = document.layers[whichLayer].style;
style2.display = mode;
}
}
function togglePublicKey(pkLayer, indexLayer)
{
if (document.getElementById)
{
// this is the way the standards work
var style2 = document.getElementById(pkLayer).style;
var certs = document.getElementById(indexLayer);
certs.rowSpan = style2.display? 2:1;
style2.display = style2.display? "":"none";
}
else if (document.all)
{
// this is the way old msie versions work
var style2 = document.all[pkLayer].style;
var certs = document.all[indexLayer];
certs.rowSpan = style2.display? 2:1;
style2.display = style2.display? "":"none";
}
else if (document.layers)
{
// this is the way nn4 works
var style2 = document.layers[pkLayer].style;
var certs = document.layers[indexLayer];
certs.rowSpan = style2.display? 2:1;
style2.display = style2.display? "":"none";
}
}
//-->
</script>
</head>
<body>
<% if (ParamUtils.getBooleanParameter(request,"success")) { %>
<% if (success) { %>
<div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0">
......@@ -148,13 +284,103 @@
<% } %>
<p>
<fmt:message key="ssl.settings.info" />
<fmt:message key="ssl.settings.client.info" />
</p>
<form action="ssl-settings.jsp" method="post">
<fieldset>
<legend><fmt:message key="ssl.settings.client.legend" /></legend>
<div>
<table id="certificates" cellpadding="3" cellspacing="0" border="0" width="100%">
<tbody>
<tr valign="middle">
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="clientSecurityRequired" value="req" id="rb01" onclick="showOrHide('custom', 'hide')"
<%= ("req".equals(clientSecurityRequired) ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb01">
<b><fmt:message key="ssl.settings.client.label_required" /></b> - <fmt:message key="ssl.settings.client.label_required_info" />
</label>
</td>
</tr>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="clientSecurityRequired" value="notreq" id="rb02" onclick="showOrHide('custom', 'hide')"
<%= ("notreq".equals(clientSecurityRequired) ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb02">
<b><fmt:message key="ssl.settings.client.label_notrequired" /></b> - <fmt:message key="ssl.settings.client.label_notrequired_info" />
</label>
</td>
</tr>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="clientSecurityRequired" value="custom" id="rb03" onclick="showOrHide('custom', 'show')"
<%= ("custom".equals(clientSecurityRequired) ? "checked" : "") %>>
</td>
<td width="99%">
<label for="rb03">
<b><fmt:message key="ssl.settings.client.label_custom" /></b> - <fmt:message key="ssl.settings.client.label_custom_info" />
</label>
</td>
</tr>
<tr valign="top" id="custom" <% if (!"custom".equals(clientSecurityRequired)) out.write("style=\"display:none\""); %>>
<td width="1%" nowrap>
&nbsp;
</td>
<td width="99%">
<table cellpadding="3" cellspacing="0" border="0" width="100%">
<tr valign="top">
<td width="1%" nowrap>
<fmt:message key="ssl.settings.client.customSSL" />
</td>
<td width="99%">
<input type="radio" name="ssl" value="notavailable" id="rb04" <%= ("notavailable".equals(ssl) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb04"><fmt:message key="ssl.settings.notavailable" /></label>&nbsp;&nbsp;
<input type="radio" name="ssl" value="available" id="rb05" <%= ("available".equals(ssl) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb05"><fmt:message key="ssl.settings.available" /></label>
</td>
</tr>
<tr valign="top">
<td width="1%" nowrap>
<fmt:message key="ssl.settings.client.customTLS" />
</td>
<td width="99%">
<input type="radio" name="tls" value="notavailable" id="rb06" <%= ("notavailable".equals(tls) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb06"><fmt:message key="ssl.settings.notavailable" /></label>&nbsp;&nbsp;
<input type="radio" name="tls" value="optional" id="rb07" <%= ("optional".equals(tls) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb07"><fmt:message key="ssl.settings.optional" /></label>&nbsp;&nbsp;
<input type="radio" name="tls" value="required" id="rb08" <%= ("required".equals(tls) ? "checked" : "") %>
onclick="this.form.clientSecurityRequired[2].checked=true;">&nbsp;<label for="rb08"><fmt:message key="ssl.settings.required" /></label>
</td>
</tr>
</table>
</td>
</tr>
</tr>
</tbody>
</table>
</div>
</fieldset>
<br>
<input type="submit" name="update" value="<fmt:message key="global.save_settings" />">
</form>
<br><br>
<p><b><fmt:message key="ssl.settings.certificate" /></b></p>
<div class="jive-table">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<p>
<fmt:message key="ssl.settings.info" />
</p>
<table class="jive-table" cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th width="1%">&nbsp;</th>
......@@ -164,6 +390,9 @@
<th>
<fmt:message key="ssl.settings.type" />
</th>
<th>
<fmt:message key="ssl.settings.publickey" />
</th>
<th width="1%">
<fmt:message key="ssl.settings.uninstall" />
</th>
......@@ -178,13 +407,16 @@
Certificate c = keyStore.getCertificate(a);
%>
<tr valign="top">
<td width="1" rowspan="2"><%= (i) %>.</td>
<td id="rs<%=i%>" width="1" rowspan="1"><%= (i) %>.</td>
<td width="29%">
<%= a %>
</td>
<td width="69%">
<td width="67%">
<%= c.getType() %>
</td>
<td width="2%">
<a href="javascript:togglePublicKey('pk<%=i%>', 'rs<%=i%>');" title="<fmt:message key="ssl.settings.publickey.title" />"><fmt:message key="ssl.settings.publickey.label" /></a>
</td>
<td width="1" align="center">
<a href="ssl-settings.jsp?alias=<%= a %>&type=server&uninstall=true"
title="<fmt:message key="ssl.settings.click_uninstall" />"
......@@ -192,7 +424,7 @@
><img src="images/delete-16x16.gif" width="16" height="16" border="0" alt=""></a>
</td>
</tr>
<tr>
<tr id="pk<%=i%>" style="display:none">
<td colspan="3">
<span class="jive-description">
<fmt:message key="ssl.settings.key" />
......@@ -218,7 +450,6 @@
</tbody>
</table>
</div>
<br><br>
......
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