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:09:05 UTC
[tomcat] branch 9.0.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 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 344d073 Fix BZ 64110 - request attr for requested ciphers and protocols
344d073 is described below
commit 344d07333dd3ef1b821d075a30a99a4226b90f53
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 34d06c8..cd6e343 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -917,6 +917,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 d6e3877..b214cad 100644
--- a/java/org/apache/coyote/AbstractProcessor.java
+++ b/java/org/apache/coyote/AbstractProcessor.java
@@ -805,6 +805,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 977205e..c7bada8 100644
--- a/java/org/apache/tomcat/util/buf/HexUtils.java
+++ b/java/org/apache/tomcat/util/buf/HexUtils.java
@@ -74,6 +74,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 cf717c9..bcf697c 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -146,6 +146,7 @@ nioBlockingSelector.selectError=Error selecting key
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 02e8c16..5775473 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;
@@ -1593,11 +1592,7 @@ public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel,AsynchronousS
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 159d55b..35e2a93 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -43,7 +43,6 @@ import java.util.concurrent.TimeUnit;
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;
@@ -1386,11 +1385,7 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel>
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 39c51e7..d6350bf 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
@@ -132,5 +146,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 394837c..611038e 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;
@@ -62,7 +65,7 @@ public class SecureNio2Channel extends Nio2Channel {
protected SSLEngine sslEngine;
- protected boolean sniComplete = false;
+ protected volatile boolean sniComplete = false;
private volatile boolean handshakeComplete = false;
private volatile HandshakeStatus handshakeStatus; //gets set by handshake
@@ -70,6 +73,8 @@ public class SecureNio2Channel extends Nio2Channel {
protected boolean closed;
protected boolean closing;
+ private final Map<String,List<String>> additionalTlsAttributes = new HashMap<>();
+
private volatile boolean unwrapBeforeRead;
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 820b736..0dedafe 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;
@@ -68,6 +71,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;
public SecureNioChannel(SocketBufferHandler bufHandler, NioSelectorPool pool, NioEndpoint endpoint) {
@@ -329,6 +334,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());
@@ -513,6 +525,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 0ca7ba7..10e9417 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
@@ -23,12 +23,14 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
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;
@@ -73,10 +75,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
@@ -201,6 +215,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 2923f37..afa7f40 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 9.0.42 (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 9.0.41 (markt)" rtext="release in progress">
<subsection name="Catalina">
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 8ad0a29..2a03bbe 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1210,6 +1210,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