From 74bf9743541794221ebe097b2132ca6b4d0f0d76 Mon Sep 17 00:00:00 2001 From: Ruby Martin Date: Wed, 30 Apr 2025 08:33:26 -0600 Subject: [PATCH] JSSE: implement SNIMatcher logic for wolfSSLSockets add thread safety --- .../provider/jsse/WolfSSLEngineHelper.java | 60 +++++++++++++++++++ .../provider/jsse/WolfSSLParameters.java | 30 +++++++--- .../jsse/WolfSSLParametersHelper.java | 31 +++++++++- .../wolfssl/provider/jsse/WolfSSLSocket.java | 4 ++ 4 files changed, 116 insertions(+), 9 deletions(-) diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java index 4e4d02f..ecacba7 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLEngineHelper.java @@ -30,6 +30,8 @@ import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import javax.net.ssl.SSLSocket; +import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.X509TrustManager; @@ -1474,6 +1476,11 @@ public class WolfSSLEngineHelper { * collected (ex: protocol version). */ this.session.updateStoredSessionValues(); + if (!this.clientMode && !matchSNI()) { + throw new SSLHandshakeException( + "Unrecognized Server Name"); + } + return ret; } @@ -1531,6 +1538,59 @@ public class WolfSSLEngineHelper { return "legacy".equals(dhKeySize); } + /** + * Validates Server Name Indication (SNI) match between client request and + * server matchers. + * + * This helper method is used only on the server side during the TLS + * handshake to check if there is a server name in the list of requested + * server names that matches the SNI matcher parameter. The check will be + * ignored (return true) if no requested server name were sent by client + * or if the SNI matcher parameter has not been set. + * + * Triggers an SSLHandshakeException on server side during handshake when + * false. + * + * @return true on success or false if no match was found + */ + protected synchronized boolean matchSNI(){ + List matchers = this.params.getSNIMatchers(); + if (matchers != null && !matchers.isEmpty()) { + /* Match a server name to SNI requested by Client */ + List serverNames = this.session + .getRequestedServerNames(); + if (serverNames != null && !serverNames.isEmpty()) { + for (SNIServerName serverName : serverNames) { + if (serverName.getType() == WolfSSL.WOLFSSL_SNI_HOST_NAME) { + /* If the SNI is of type WOLFSSL_SNI_HOST_NAME, compare + * the name to matchers list */ + for (SNIMatcher matcher : matchers) { + if (matcher.matches(serverName)) { + /* If a match is found, accept the server name + * and return true value, break from loop */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + () -> "Accepted SNI: " + serverName); + return true; + } + } + } + } + } else { + /* If server names are null or empty, ignore server name + * indication */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + () -> "No server names found, ignoring SNI"); + return true; + } + } else { + /* If matchers are null or empty, ignore server name indication */ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + () -> "No SNIMatchers set"); + return true; + } + return false; + } + /** * Unset the native verify callback and reset internal verify * callback state. diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java index 8adc437..0f06d47 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParameters.java @@ -21,7 +21,9 @@ package com.wolfssl.provider.jsse; import java.util.List; +import javax.net.ssl.SNIMatcher; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; /** @@ -45,6 +47,7 @@ final class WolfSSLParameters { private boolean needClientAuth = false; private String endpointIdAlgorithm = null; private List serverNames; + private List sniMatchers; private boolean useCipherSuiteOrder = true; String[] applicationProtocols = new String[0]; private boolean useSessionTickets = false; @@ -72,7 +75,7 @@ final class WolfSSLParameters { /* TODO: duplicate other properties here when WolfSSLParameters * can handle them */ - + cp.setSNIMatchers(this.getSNIMatchers()); return cp; } @@ -184,14 +187,27 @@ final class WolfSSLParameters { } /* TODO, create our own class for SNIMatcher, in case Java doesn't support it */ - //void setSNIMatchers(Collection matchers) { - // /* TODO */ - //} + void setSNIMatchers(Collection matchers) { + if (matchers != null && !matchers.isEmpty()) { + if (this.sniMatchers == null) { + this.sniMatchers = new ArrayList(); + } + for (SNIMatcher matcher : matchers) { + this.sniMatchers.add(matcher); + } + } else { + this.sniMatchers = new ArrayList(); + } + } /* TODO, create our own class for SNIMatcher, in case Java doesn't support it */ - //Collection getSNIMatchers() { - // return null; /* TODO */ - //} + List getSNIMatchers() { + if (this.sniMatchers != null && !this.sniMatchers.isEmpty()) { + return Collections.unmodifiableList(new ArrayList(sniMatchers)); + } else { + return Collections.emptyList(); + } + } void setUseCipherSuitesOrder(boolean honorOrder) { this.useCipherSuiteOrder = honorOrder; diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java index a2b3474..2a39368 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLParametersHelper.java @@ -39,6 +39,8 @@ public class WolfSSLParametersHelper private static Method setApplicationProtocols = null; private static Method getEndpointIdentificationAlgorithm = null; private static Method setEndpointIdentificationAlgorithm = null; + private static Method getSNIMatchers = null; + private static Method setSNIMatchers = null; private static Method getMaximumPacketSize = null; private static Method setMaximumPacketSize = null; @@ -77,6 +79,12 @@ public class WolfSSLParametersHelper case "setEndpointIdentificationAlgorithm": setEndpointIdentificationAlgorithm = m; continue; + case "getSNIMatchers": + getSNIMatchers = m; + continue; + case "setSNIMatchers": + setSNIMatchers = m; + continue; case "getMaximumPacketSize": getMaximumPacketSize = m; continue; @@ -126,7 +134,7 @@ public class WolfSSLParametersHelper * do not existing in older JDKs. Since older JDKs will not have them, * use Java reflection to detect availability in helper class. */ if (setServerNames != null || setApplicationProtocols != null || - setEndpointIdentificationAlgorithm != null) { + setEndpointIdentificationAlgorithm != null || setSNIMatchers != null) { try { /* load WolfSSLJDK8Helper at runtime, not compiled @@ -155,6 +163,10 @@ public class WolfSSLParametersHelper mth.invoke(obj, ret, setEndpointIdentificationAlgorithm, in); } + if (setSNIMatchers != null) { + mth = cls.getDeclaredMethod("setSNIMatchers", paramList); + mth.invoke(obj, ret, setSNIMatchers, in); + } } catch (Exception e) { /* ignore, class not found */ @@ -173,6 +185,14 @@ public class WolfSSLParametersHelper /* Not available, just ignore and continue */ } + try { + if (setSNIMatchers != null) { + ret.setSNIMatchers(in.getSNIMatchers()); + } + } catch (Exception e) { + /* Not available, just ignore and continue */ + } + /* The following SSLParameters features are not yet supported * by wolfJSSE (see Android API 23 note above). They are supported * with newer versions of SSLParameters, but will need to be added @@ -222,7 +242,7 @@ public class WolfSSLParametersHelper * do not existing in older JDKs. Since older JDKs will not have them, * use Java reflection to detect availability in helper class. */ if (getServerNames != null || getApplicationProtocols != null || - getEndpointIdentificationAlgorithm != null) { + getEndpointIdentificationAlgorithm != null || getSNIMatchers != null) { try { /* load WolfSSLJDK8Helper at runtime, not compiled on older JDKs */ Class cls = Class.forName( @@ -247,6 +267,10 @@ public class WolfSSLParametersHelper "getEndpointIdentificationAlgorithm", paramList); mth.invoke(obj, in, out); } + if (getSNIMatchers != null){ + mth = cls.getDeclaredMethod("getSNIMatchers", paramList); + mth.invoke(obj, in, out); + } } catch (Exception e) { /* ignore, class not found */ @@ -276,6 +300,9 @@ public class WolfSSLParametersHelper out.setSNIMatchers(in.getSNIMatchers()); out.setUseCipherSuitesOrder(in.getUseCipherSuitesOrder()); */ + + out.setSNIMatchers(in.getSNIMatchers()); + } } diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java index 492cdbf..f59bb79 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java @@ -1569,6 +1569,10 @@ public class WolfSSLSocket extends SSLSocket { close(); throw e; + } catch (SSLHandshakeException e){ + WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, + () -> "got SSLHandshakeException in doHandshake()"); + throw e; } catch (SSLException e) { final int tmpErr = err; WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,