You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2020/12/04 09:10:43 UTC
[tomcat] branch 8.5.x updated: Fix BZ 64110 - request attr for
requested ciphers and protocols
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push:
new 2448e04 Fix BZ 64110 - request attr for requested ciphers and protocols
2448e04 is described below
commit 2448e046c314bbf356535ac6c5a6f93f2cde5e6f
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Nov 25 18:44:11 2020 +0000
Fix BZ 64110 - request attr for requested ciphers and protocols
https://bz.apache.org/bugzilla/show_bug.cgi?id=64110
---
java/org/apache/catalina/connector/Request.java | 8 ++
java/org/apache/catalina/util/TLSUtil.java | 19 +++--
java/org/apache/coyote/AbstractProcessor.java | 8 ++
java/org/apache/tomcat/util/buf/HexUtils.java | 14 ++++
java/org/apache/tomcat/util/net/AprSSLSupport.java | 13 ++++
.../apache/tomcat/util/net/LocalStrings.properties | 1 +
java/org/apache/tomcat/util/net/Nio2Endpoint.java | 7 +-
java/org/apache/tomcat/util/net/NioEndpoint.java | 7 +-
.../apache/tomcat/util/net/SSLImplementation.java | 27 +++++++
java/org/apache/tomcat/util/net/SSLSupport.java | 33 +++++++-
.../apache/tomcat/util/net/SecureNio2Channel.java | 22 +++++-
.../apache/tomcat/util/net/SecureNioChannel.java | 20 +++++
.../tomcat/util/net/TLSClientHelloExtractor.java | 89 ++++++++++++++++++++--
.../tomcat/util/net/jsse/JSSEImplementation.java | 11 ++-
.../apache/tomcat/util/net/jsse/JSSESupport.java | 34 ++++++++-
.../util/net/openssl/OpenSSLImplementation.java | 9 +++
webapps/docs/changelog.xml | 9 +++
webapps/docs/config/http.xml | 6 ++
18 files changed, 306 insertions(+), 31 deletions(-)
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 094bdec..dcf6c7f 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -944,6 +944,14 @@ public class Request implements HttpServletRequest {
if (attr != null) {
attributes.put(SSLSupport.PROTOCOL_VERSION_KEY, attr);
}
+ attr = coyoteRequest.getAttribute(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY);
+ if (attr != null) {
+ attributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY, attr);
+ }
+ attr = coyoteRequest.getAttribute(SSLSupport.REQUESTED_CIPHERS_KEY);
+ if (attr != null) {
+ attributes.put(SSLSupport.REQUESTED_CIPHERS_KEY, attr);
+ }
attr = attributes.get(name);
sslAttributesParsed = true;
}
diff --git a/java/org/apache/catalina/util/TLSUtil.java b/java/org/apache/catalina/util/TLSUtil.java
index a739021..7f895dd 100644
--- a/java/org/apache/catalina/util/TLSUtil.java
+++ b/java/org/apache/catalina/util/TLSUtil.java
@@ -33,11 +33,18 @@ public class TLSUtil {
* information, otherwise {@code false}
*/
public static boolean isTLSRequestAttribute(String name) {
- return Globals.CERTIFICATES_ATTR.equals(name) ||
- Globals.CIPHER_SUITE_ATTR.equals(name) ||
- Globals.KEY_SIZE_ATTR.equals(name) ||
- Globals.SSL_SESSION_ID_ATTR.equals(name) ||
- Globals.SSL_SESSION_MGR_ATTR.equals(name) ||
- SSLSupport.PROTOCOL_VERSION_KEY.equals(name);
+ switch (name) {
+ case Globals.CERTIFICATES_ATTR:
+ case Globals.CIPHER_SUITE_ATTR:
+ case Globals.KEY_SIZE_ATTR:
+ case Globals.SSL_SESSION_ID_ATTR:
+ case Globals.SSL_SESSION_MGR_ATTR:
+ case SSLSupport.PROTOCOL_VERSION_KEY:
+ case SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY:
+ case SSLSupport.REQUESTED_CIPHERS_KEY:
+ return true;
+ default:
+ return false;
+ }
}
}
diff --git a/java/org/apache/coyote/AbstractProcessor.java b/java/org/apache/coyote/AbstractProcessor.java
index 6480ddf..6140839 100644
--- a/java/org/apache/coyote/AbstractProcessor.java
+++ b/java/org/apache/coyote/AbstractProcessor.java
@@ -798,6 +798,14 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
if (sslO != null) {
request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, sslO);
}
+ sslO = sslSupport.getRequestedProtocols();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY, sslO);
+ }
+ sslO = sslSupport.getRequestedCiphers();
+ if (sslO != null) {
+ request.setAttribute(SSLSupport.REQUESTED_CIPHERS_KEY, sslO);
+ }
request.setAttribute(SSLSupport.SESSION_MGR, sslSupport);
}
} catch (Exception e) {
diff --git a/java/org/apache/tomcat/util/buf/HexUtils.java b/java/org/apache/tomcat/util/buf/HexUtils.java
index e80179b..fffa5f9 100644
--- a/java/org/apache/tomcat/util/buf/HexUtils.java
+++ b/java/org/apache/tomcat/util/buf/HexUtils.java
@@ -75,6 +75,20 @@ public final class HexUtils {
}
+ public static String toHexString(char c) {
+ // 2 bytes / 4 hex digits
+ StringBuilder sb = new StringBuilder(4);
+
+ sb.append(hex[(c & 0xf000) >> 4]);
+ sb.append(hex[(c & 0x0f00)]);
+
+ sb.append(hex[(c & 0xf0) >> 4]);
+ sb.append(hex[(c & 0x0f)]);
+
+ return sb.toString();
+ }
+
+
public static String toHexString(byte[] bytes) {
if (null == bytes) {
return null;
diff --git a/java/org/apache/tomcat/util/net/AprSSLSupport.java b/java/org/apache/tomcat/util/net/AprSSLSupport.java
index 8f9be75..f36eaf4 100644
--- a/java/org/apache/tomcat/util/net/AprSSLSupport.java
+++ b/java/org/apache/tomcat/util/net/AprSSLSupport.java
@@ -104,6 +104,7 @@ public class AprSSLSupport implements SSLSupport {
}
}
+
@Override
public String getProtocol() throws IOException {
try {
@@ -112,4 +113,16 @@ public class AprSSLSupport implements SSLSupport {
throw new IOException(e);
}
}
+
+
+ @Override
+ public String getRequestedProtocols() throws IOException {
+ return null;
+ }
+
+
+ @Override
+ public String getRequestedCiphers() throws IOException {
+ return null;
+ }
}
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties
index 17c3309..94913e8 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -124,6 +124,7 @@ endpoint.warn.unlockAcceptorFailed=Acceptor thread [{0}] failed to unlock. Forci
sniExtractor.clientHelloInvalid=The ClientHello message was not correctly formatted
sniExtractor.clientHelloTooBig=The ClientHello was not presented in a single TLS record so no SNI information could be extracted
+sniExtractor.tooEarly=It is illegal to call this method before the client hello has been parsed
socket.apr.clientAbort=The client aborted the connection.
socket.apr.closed=The socket [{0}] associated with this connection has been closed.
diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
index b28941c..5e0dfe1 100644
--- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
+++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
@@ -39,7 +39,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -1520,11 +1519,7 @@ public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel> {
public SSLSupport getSslSupport(String clientCertProvider) {
if (getSocket() instanceof SecureNio2Channel) {
SecureNio2Channel ch = (SecureNio2Channel) getSocket();
- SSLEngine sslEngine = ch.getSslEngine();
- if (sslEngine != null) {
- SSLSession session = sslEngine.getSession();
- return ((Nio2Endpoint) getEndpoint()).getSslImplementation().getSSLSupport(session);
- }
+ return ch.getSSLSupport();
}
return null;
}
diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java
index 281c914..b32b856 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -44,7 +44,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -1431,11 +1430,7 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
public SSLSupport getSslSupport(String clientCertProvider) {
if (getSocket() instanceof SecureNioChannel) {
SecureNioChannel ch = (SecureNioChannel) getSocket();
- SSLEngine sslEngine = ch.getSslEngine();
- if (sslEngine != null) {
- SSLSession session = sslEngine.getSession();
- return ((NioEndpoint) getEndpoint()).getSslImplementation().getSSLSupport(session);
- }
+ return ch.getSSLSupport();
}
return null;
}
diff --git a/java/org/apache/tomcat/util/net/SSLImplementation.java b/java/org/apache/tomcat/util/net/SSLImplementation.java
index 43ccbe5..4bafd25 100644
--- a/java/org/apache/tomcat/util/net/SSLImplementation.java
+++ b/java/org/apache/tomcat/util/net/SSLImplementation.java
@@ -17,6 +17,9 @@
package org.apache.tomcat.util.net;
+import java.util.List;
+import java.util.Map;
+
import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
@@ -63,7 +66,31 @@ public abstract class SSLImplementation {
}
}
+ /**
+ * Obtain an instance of SSLSupport.
+ *
+ * @param session The SSL session
+ * @param additionalAttributes Additional SSL attributes that are not
+ * available from the session.
+ *
+ * @return An instance of SSLSupport based on the given session and the
+ * provided additional attributes
+ */
+ public SSLSupport getSSLSupport(SSLSession session, Map<String,List<String>> additionalAttributes) {
+ return getSSLSupport(session);
+ }
+ /**
+ * Obtain an instance of SSLSupport.
+ *
+ * @param session The TLS session
+ *
+ * @return An instance of SSLSupport based on the given session.
+ *
+ * @deprecated This will be removed in Tomcat 10.1.x onwards.
+ * Use {@link #getSSLSupport(SSLSession, Map)}.
+ */
+ @Deprecated
public abstract SSLSupport getSSLSupport(SSLSession session);
public abstract SSLUtil getSSLUtil(SSLHostConfigCertificate certificate);
diff --git a/java/org/apache/tomcat/util/net/SSLSupport.java b/java/org/apache/tomcat/util/net/SSLSupport.java
index 75740f9..a085b87 100644
--- a/java/org/apache/tomcat/util/net/SSLSupport.java
+++ b/java/org/apache/tomcat/util/net/SSLSupport.java
@@ -63,6 +63,20 @@ public interface SSLSupport {
"org.apache.tomcat.util.net.secure_protocol_version";
/**
+ * The request attribute key under which the String indicating the ciphers
+ * requested by the client are recorded.
+ */
+ public static final String REQUESTED_CIPHERS_KEY =
+ "org.apache.tomcat.util.net.secure_requested_ciphers";
+
+ /**
+ * The request attribute key under which the String indicating the protocols
+ * requested by the client are recorded.
+ */
+ public static final String REQUESTED_PROTOCOL_VERSIONS_KEY =
+ "org.apache.tomcat.util.net.secure_requested_protocol_versions";
+
+ /**
* The cipher suite being used on this connection.
*
* @return The name of the cipher suite as returned by the SSL/TLS
@@ -121,5 +135,22 @@ public interface SSLSupport {
* information from the socket
*/
public String getProtocol() throws IOException;
-}
+ /**
+ *
+ * @return the list of SSL/TLS protocol versions requested by the client
+ *
+ * @throws IOException If an error occurs trying to obtain the client
+ * requested protocol information from the socket
+ */
+ public String getRequestedProtocols() throws IOException;
+
+ /**
+ *
+ * @return the list of SSL/TLS ciphers requested by the client
+ *
+ * @throws IOException If an error occurs trying to obtain the client
+ * request cipher information from the socket
+ */
+ public String getRequestedCiphers() throws IOException;
+}
diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
index 8899da4..92fe2f0 100644
--- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java
+++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
@@ -23,7 +23,9 @@ import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.WritePendingException;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -34,6 +36,7 @@ import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -61,7 +64,7 @@ public class SecureNio2Channel extends Nio2Channel {
protected SSLEngine sslEngine;
protected final Nio2Endpoint endpoint;
- protected boolean sniComplete = false;
+ protected volatile boolean sniComplete = false;
private volatile boolean handshakeComplete;
private volatile HandshakeStatus handshakeStatus; //gets set by handshake
@@ -71,6 +74,8 @@ public class SecureNio2Channel extends Nio2Channel {
protected boolean closed;
protected boolean closing;
+ private final Map<String,List<String>> additionalTlsAttributes = new HashMap<>();
+
private final CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> handshakeReadCompletionHandler;
private final CompletionHandler<Integer, SocketWrapperBase<Nio2Channel>> handshakeWriteCompletionHandler;
@@ -432,6 +437,13 @@ public class SecureNio2Channel extends Nio2Channel {
sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers,
clientRequestedApplicationProtocols);
+ // Populate additional TLS attributes obtained from the handshake that
+ // aren't available from the session
+ additionalTlsAttributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY,
+ extractor.getClientRequestedProtocols());
+ additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY,
+ extractor.getClientRequestedCipherNames());
+
// Ensure the application buffers (which have to be created earlier) are
// big enough.
getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize());
@@ -563,6 +575,14 @@ public class SecureNio2Channel extends Nio2Channel {
return result;
}
+ public SSLSupport getSSLSupport() {
+ if (sslEngine != null) {
+ SSLSession session = sslEngine.getSession();
+ return endpoint.getSslImplementation().getSSLSupport(session, additionalTlsAttributes);
+ }
+ return null;
+ }
+
/**
* Sends an SSL close message, will not physically close the connection here.<br>
* To close the connection, you could do something like
diff --git a/java/org/apache/tomcat/util/net/SecureNioChannel.java b/java/org/apache/tomcat/util/net/SecureNioChannel.java
index 5f59899..f235ade 100644
--- a/java/org/apache/tomcat/util/net/SecureNioChannel.java
+++ b/java/org/apache/tomcat/util/net/SecureNioChannel.java
@@ -24,13 +24,16 @@ import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
@@ -65,6 +68,8 @@ public class SecureNioChannel extends NioChannel {
protected boolean closed = false;
protected boolean closing = false;
+ private final Map<String,List<String>> additionalTlsAttributes = new HashMap<>();
+
protected NioSelectorPool pool;
private final NioEndpoint endpoint;
@@ -328,6 +333,13 @@ public class SecureNioChannel extends NioChannel {
sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers,
clientRequestedApplicationProtocols);
+ // Populate additional TLS attributes obtained from the handshake that
+ // aren't available from the session
+ additionalTlsAttributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY,
+ extractor.getClientRequestedProtocols());
+ additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY,
+ extractor.getClientRequestedCipherNames());
+
// Ensure the application buffers (which have to be created earlier) are
// big enough.
getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize());
@@ -502,6 +514,14 @@ public class SecureNioChannel extends NioChannel {
return result;
}
+ public SSLSupport getSSLSupport() {
+ if (sslEngine != null) {
+ SSLSession session = sslEngine.getSession();
+ return endpoint.getSslImplementation().getSSLSupport(session, additionalTlsAttributes);
+ }
+ return null;
+ }
+
/**
* Sends an SSL close message, will not physically close the connection here.
* <br>To close the connection, you could do something like
diff --git a/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java b/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
index 6ce27cb..21a5924 100644
--- a/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
+++ b/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java
@@ -25,6 +25,7 @@ import java.util.List;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.http.parser.HttpParser;
import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
import org.apache.tomcat.util.res.StringManager;
@@ -40,13 +41,16 @@ public class TLSClientHelloExtractor {
private final ExtractorResult result;
private final List<Cipher> clientRequestedCiphers;
+ private final List<String> clientRequestedCipherNames;
private final String sniValue;
private final List<String> clientRequestedApplicationProtocols;
+ private final List<String> clientRequestedProtocols;
private static final int TLS_RECORD_HEADER_LEN = 5;
private static final int TLS_EXTENSION_SERVER_NAME = 0;
private static final int TLS_EXTENSION_ALPN = 16;
+ private static final int TLS_EXTENSION_SUPPORTED_VERSION = 43;
public static byte[] USE_TLS_RESPONSE = ("HTTP/1.1 400 \r\n" +
"Content-Type: text/plain;charset=UTF-8\r\n" +
@@ -72,7 +76,9 @@ public class TLSClientHelloExtractor {
int limit = netInBuffer.limit();
ExtractorResult result = ExtractorResult.NOT_PRESENT;
List<Cipher> clientRequestedCiphers = new ArrayList<>();
+ List<String> clientRequestedCipherNames = new ArrayList<>();
List<String> clientRequestedApplicationProtocols = new ArrayList<>();
+ List<String> clientRequestedProtocols = new ArrayList<>();
String sniValue = null;
try {
// Switch to read mode.
@@ -110,7 +116,7 @@ public class TLSClientHelloExtractor {
}
// Protocol Version
- skipBytes(netInBuffer, 2);
+ String legacyVersion = readProtocol(netInBuffer);
// Random
skipBytes(netInBuffer, 32);
// Session ID (single byte for length)
@@ -120,8 +126,15 @@ public class TLSClientHelloExtractor {
// (2 bytes for length, each cipher ID is 2 bytes)
int cipherCount = netInBuffer.getChar() / 2;
for (int i = 0; i < cipherCount; i++) {
- int cipherId = netInBuffer.getChar();
- clientRequestedCiphers.add(Cipher.valueOf(cipherId));
+ char cipherId = netInBuffer.getChar();
+ Cipher c = Cipher.valueOf(cipherId);
+ // Some clients transmit grease values (see RFC 8701)
+ if (c == null) {
+ clientRequestedCipherNames.add("Unknown(0x" + HexUtils.toHexString(cipherId) + ")");
+ } else {
+ clientRequestedCiphers.add(c);
+ clientRequestedCipherNames.add(c.name());
+ }
}
// Compression methods (single byte for length)
@@ -136,8 +149,8 @@ public class TLSClientHelloExtractor {
skipBytes(netInBuffer, 2);
// Read the extensions until we run out of data or find the data
// we need
- while (netInBuffer.hasRemaining() &&
- (sniValue == null || clientRequestedApplicationProtocols.size() == 0)) {
+ while (netInBuffer.hasRemaining() && (sniValue == null ||
+ clientRequestedApplicationProtocols.isEmpty()) || clientRequestedProtocols.isEmpty()) {
// Extension type is two byte
char extensionType = netInBuffer.getChar();
// Extension size is another two bytes
@@ -150,19 +163,27 @@ public class TLSClientHelloExtractor {
case TLS_EXTENSION_ALPN:
readAlpnExtension(netInBuffer, clientRequestedApplicationProtocols);
break;
+ case TLS_EXTENSION_SUPPORTED_VERSION:
+ readSupportedVersions(netInBuffer, clientRequestedProtocols);
+ break;
default: {
skipBytes(netInBuffer, extensionDataSize);
}
}
}
+ if (clientRequestedProtocols.isEmpty()) {
+ clientRequestedProtocols.add(legacyVersion);
+ }
result = ExtractorResult.COMPLETE;
} catch (BufferUnderflowException | IllegalArgumentException e) {
throw new IOException(sm.getString("sniExtractor.clientHelloInvalid"), e);
} finally {
this.result = result;
this.clientRequestedCiphers = clientRequestedCiphers;
+ this.clientRequestedCipherNames = clientRequestedCipherNames;
this.clientRequestedApplicationProtocols = clientRequestedApplicationProtocols;
this.sniValue = sniValue;
+ this.clientRequestedProtocols = clientRequestedProtocols;
// Whatever happens, return the buffer to its original state
netInBuffer.limit(limit);
netInBuffer.position(pos);
@@ -179,7 +200,7 @@ public class TLSClientHelloExtractor {
if (result == ExtractorResult.COMPLETE) {
return sniValue;
} else {
- throw new IllegalStateException();
+ throw new IllegalStateException(sm.getString("sniExtractor.tooEarly"));
}
}
@@ -188,7 +209,16 @@ public class TLSClientHelloExtractor {
if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) {
return clientRequestedCiphers;
} else {
- throw new IllegalStateException();
+ throw new IllegalStateException(sm.getString("sniExtractor.tooEarly"));
+ }
+ }
+
+
+ public List<String> getClientRequestedCipherNames() {
+ if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) {
+ return clientRequestedCipherNames;
+ } else {
+ throw new IllegalStateException(sm.getString("sniExtractor.tooEarly"));
}
}
@@ -197,7 +227,16 @@ public class TLSClientHelloExtractor {
if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) {
return clientRequestedApplicationProtocols;
} else {
- throw new IllegalStateException();
+ throw new IllegalStateException(sm.getString("sniExtractor.tooEarly"));
+ }
+ }
+
+
+ public List<String> getClientRequestedProtocols() {
+ if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) {
+ return clientRequestedProtocols;
+ } else {
+ throw new IllegalStateException(sm.getString("sniExtractor.tooEarly"));
}
}
@@ -328,6 +367,30 @@ public class TLSClientHelloExtractor {
}
+ private static String readProtocol(ByteBuffer bb) {
+ char protocol = bb.getChar();
+ switch (protocol) {
+ case 0x0300: {
+ return Constants.SSL_PROTO_SSLv3;
+ }
+ case 0x0301: {
+ return Constants.SSL_PROTO_TLSv1_0;
+ }
+ case 0x0302: {
+ return Constants.SSL_PROTO_TLSv1_1;
+ }
+ case 0x0303: {
+ return Constants.SSL_PROTO_TLSv1_2;
+ }
+ case 0x0304: {
+ return Constants.SSL_PROTO_TLSv1_3;
+ }
+ default:
+ return "Unknown(0x" + HexUtils.toHexString(protocol) + ")";
+ }
+ }
+
+
private static String readSniExtension(ByteBuffer bb) {
// First 2 bytes are size of server name list (only expecting one)
// Next byte is type (0 for hostname)
@@ -356,6 +419,16 @@ public class TLSClientHelloExtractor {
}
+ private static void readSupportedVersions(ByteBuffer bb, List<String> protocolNames) {
+ // First byte is the size of the list in bytes
+ int count = (bb.get() & 0xFF) / 2;
+ // Then the list of protocols
+ for (int i = 0; i < count; i++) {
+ protocolNames.add(readProtocol(bb));
+ }
+ }
+
+
public enum ExtractorResult {
COMPLETE,
NOT_PRESENT,
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java b/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
index 1c1eae8..2c90513 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
@@ -16,6 +16,9 @@
*/
package org.apache.tomcat.util.net.jsse;
+import java.util.List;
+import java.util.Map;
+
import javax.net.ssl.SSLSession;
import org.apache.tomcat.util.compat.JreCompat;
@@ -40,9 +43,15 @@ public class JSSEImplementation extends SSLImplementation {
JSSESupport.init();
}
+ @Deprecated
@Override
public SSLSupport getSSLSupport(SSLSession session) {
- return new JSSESupport(session);
+ return getSSLSupport(session, null);
+ }
+
+ @Override
+ public SSLSupport getSSLSupport(SSLSession session, Map<String, List<String>> additionalAttributes) {
+ return new JSSESupport(session, additionalAttributes);
}
@Override
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
index 98c6eb9..5d15e2c 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
@@ -22,12 +22,14 @@ import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLSession;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.net.SSLSessionManager;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.openssl.ciphers.Cipher;
@@ -72,10 +74,22 @@ public class JSSESupport implements SSLSupport, SSLSessionManager {
}
private SSLSession session;
+ private Map<String,List<String>> additionalAttributes;
-
+ /**
+ * @param session SSLSession from which information is to be extracted
+ *
+ * @deprecated This will be removed in Tomcat 10.1.x onwards
+ * Use {@link JSSESupport#JSSESupport(SSLSession, Map)}
+ */
+ @Deprecated
public JSSESupport(SSLSession session) {
+ this(session, null);
+ }
+
+ public JSSESupport(SSLSession session, Map<String,List<String>> additionalAttributes) {
this.session = session;
+ this.additionalAttributes = additionalAttributes;
}
@Override
@@ -186,6 +200,22 @@ public class JSSESupport implements SSLSupport, SSLSessionManager {
return null;
}
return session.getProtocol();
- }
+ }
+
+ @Override
+ public String getRequestedProtocols() throws IOException {
+ if (additionalAttributes == null) {
+ return null;
+ }
+ return StringUtils.join(additionalAttributes.get(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY));
+ }
+
+ @Override
+ public String getRequestedCiphers() throws IOException {
+ if (additionalAttributes == null) {
+ return null;
+ }
+ return StringUtils.join(additionalAttributes.get(SSLSupport.REQUESTED_CIPHERS_KEY));
+ }
}
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java
index 94b4bf2..da36f08 100644
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java
@@ -16,6 +16,9 @@
*/
package org.apache.tomcat.util.net.openssl;
+import java.util.List;
+import java.util.Map;
+
import javax.net.ssl.SSLSession;
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
@@ -26,12 +29,18 @@ import org.apache.tomcat.util.net.jsse.JSSESupport;
public class OpenSSLImplementation extends SSLImplementation {
+ @Deprecated
@Override
public SSLSupport getSSLSupport(SSLSession session) {
return new JSSESupport(session);
}
@Override
+ public SSLSupport getSSLSupport(SSLSession session, Map<String, List<String>> additionalAttributes) {
+ return new JSSESupport(session, additionalAttributes);
+ }
+
+ @Override
public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
return new OpenSSLUtil(certificate);
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 33db707..b9e4544 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -104,6 +104,15 @@
issues do not "pop up" wrt. others).
-->
<section name="Tomcat 8.5.62 (markt)" rtext="in development">
+ <subsection name="Catalina">
+ <changelog>
+ <add>
+ <bug>64110</bug>: Add support for additional TLS related request
+ attributes that provide details of the protocols and ciphers requested
+ by a client in the initial TLS handshake. (markt)
+ </add>
+ </changelog>
+ </subsection>
</section>
<section name="Tomcat 8.5.61 (markt)" rtext="release in progress">
<subsection name="Catalina">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 27fb3f2..9d66d5d 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1219,6 +1219,12 @@
error. It is expected that Tomcat 10 will drop support for the SSL
configuration attributes in the <strong>Connector</strong>.</p>
+ <p>In addition to the standard TLS related request attributes defined in
+ section 3.10 of the Servlet specification, Tomcat supports a number of
+ additional TLS related attributes. The full list may be found in the <a
+ href="http://tomcat.apache.org/tomcat-10.0-doc/api/index.html">SSLSupport
+ Javadoc</a>.</p>
+
<p>For more information, see the
<a href="../ssl-howto.html">SSL Configuration How-To</a>.</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org