This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/main by this push:
     new 5914ca5f1b Extend OCSP checks for OpenSSL to align with JSSE
5914ca5f1b is described below

commit 5914ca5f1bc540d851be52202af7a97ee87eaac1
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Dec 4 17:18:13 2025 +0000

    Extend OCSP checks for OpenSSL to align with JSSE
---
 .../util/net/openssl/panama/OpenSSLContext.java    | 15 +++++-
 .../util/net/openssl/panama/OpenSSLEngine.java     | 53 ++++++++++++++++++----
 .../tomcat/util/net/ocsp/TestOcspIntegration.java  |  7 +++
 webapps/docs/changelog.xml                         |  3 +-
 4 files changed, 67 insertions(+), 11 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java 
b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
index 1abac2d005..7b732432ce 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
@@ -123,6 +123,8 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
     private boolean initialized = false;
 
     private boolean noOcspCheck = false;
+    private int ocspTimeout = 15000;
+    private int ocspVerifyFlags = 0;
     private X509TrustManager x509TrustManager;
 
     private final ContextState state;
@@ -355,6 +357,10 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
             try (var localArena = Arena.ofConfined()) {
                 if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) {
                     ok = true;
+                } else if (name.equals("OCSP_TIMEOUT")) {
+                    ok = true;
+                } else if (name.equals("OCSP_VERIFY_FLAGS")) {
+                    ok = true;
                 } else {
                     int code = SSL_CONF_cmd_value_type(state.confCtx, 
localArena.allocateFrom(name));
                     ok = true;
@@ -425,6 +431,12 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
                 if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) {
                     noOcspCheck = Boolean.parseBoolean(value);
                     rc = 1;
+                } else if (name.equals("OCSP_TIMEOUT")) {
+                    ocspTimeout = Integer.parseInt(value);
+                    rc = 1;
+                } else if (name.equals("OCSP_VERIFY_FLAGS")) {
+                    ocspVerifyFlags = Integer.parseInt(value);
+                    rc = 1;
                 } else {
                     rc = SSL_CONF_cmd(state.confCtx, 
localArena.allocateFrom(name), localArena.allocateFrom(value));
                     String errorMessage = OpenSSLLibrary.getLastError();
@@ -1349,7 +1361,8 @@ public class OpenSSLContext implements 
org.apache.tomcat.util.net.SSLContext {
     public SSLEngine createSSLEngine() {
         return new OpenSSLEngine(cleaner, state.sslCtx, defaultProtocol, 
false, sessionContext, alpn, initialized,
                 sslHostConfig.getCertificateVerificationDepth(),
-                sslHostConfig.getCertificateVerification() == 
CertificateVerification.OPTIONAL_NO_CA, noOcspCheck);
+                sslHostConfig.getCertificateVerification() == 
CertificateVerification.OPTIONAL_NO_CA,
+                noOcspCheck, ocspTimeout, ocspVerifyFlags);
     }
 
     @Override
diff --git a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java 
b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
index 97b1ffc49d..73666d347d 100644
--- a/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
+++ b/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
@@ -94,6 +94,8 @@ public final class OpenSSLEngine extends SSLEngine implements 
SSLUtil.ProtocolIn
     private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
     private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 
1024;
     private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 
1024;
+    // 15 minutes aligns with JSSE
+    private static final int OCSP_MAX_SKEW = 60 * 15;
 
     // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC 
(20) + Padding (256)
     private static final int MAX_ENCRYPTED_PACKET_LENGTH = 
MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
@@ -174,10 +176,13 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
      *                                                verification from the 
{@code SSL_CTX} {@code sslCtx}
      * @param certificateVerificationDepth        Certificate verification 
depth
      * @param certificateVerificationOptionalNoCA Skip CA verification in 
optional mode
+     * @param noOcspCheck                         Enable OCSP if true
+     * @param ocspTimeout                         Timout in ms to use for OCSP 
requests
+     * @param ocspVerifyFlags                     Verification flags for OCSP
      */
     OpenSSLEngine(Cleaner cleaner, MemorySegment sslCtx, String 
fallbackApplicationProtocol, boolean clientMode,
             OpenSSLSessionContext sessionContext, boolean alpn, boolean 
initialized, int certificateVerificationDepth,
-            boolean certificateVerificationOptionalNoCA, boolean noOcspCheck) {
+            boolean certificateVerificationOptionalNoCA, boolean noOcspCheck,  
int ocspTimeout, int ocspVerifyFlags) {
         if (sslCtx == null) {
             throw new 
IllegalArgumentException(sm.getString("engine.noSSLContext"));
         }
@@ -200,7 +205,7 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
             var internalBIO = internalBIOPointer.get(ValueLayout.ADDRESS, 0);
             var networkBIO = networkBIOPointer.get(ValueLayout.ADDRESS, 0);
             SSL_set_bio(ssl, internalBIO, internalBIO);
-            state = new EngineState(ssl, networkBIO, 
certificateVerificationDepth, noOcspCheck);
+            state = new EngineState(ssl, networkBIO, 
certificateVerificationDepth, noOcspCheck, ocspTimeout, ocspVerifyFlags);
         }
         this.fallbackApplicationProtocol = fallbackApplicationProtocol;
         this.clientMode = clientMode;
@@ -1167,7 +1172,7 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
                         ok = 0;
                     }
                 } else {
-                    int ocspResponse = processOCSP(x509ctx);
+                    int ocspResponse = processOCSP(state, x509ctx);
                     if (ocspResponse == V_OCSP_CERTSTATUS_REVOKED()) {
                         ok = 0;
                         errnum = X509_STORE_CTX_get_error(x509ctx);
@@ -1190,7 +1195,7 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
         }
     }
 
-    private static int processOCSP(MemorySegment /* X509_STORE_CTX */ x509ctx) 
{
+    private static int processOCSP(EngineState state, MemorySegment /* 
X509_STORE_CTX */ x509ctx) {
         int ocspResponse = V_OCSP_CERTSTATUS_UNKNOWN();
         MemorySegment x509 = X509_STORE_CTX_get_current_cert(x509ctx);
         if (!MemorySegment.NULL.equals(x509)) {
@@ -1239,7 +1244,7 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
                                     for (String urlString : urls) {
                                         try {
                                             URL url = (new 
URI(urlString)).toURL();
-                                            ocspResponse = 
processOCSPRequest(url, issuer, x509, x509ctx, localArena);
+                                            ocspResponse = 
processOCSPRequest(state, url, issuer, x509, x509ctx, localArena);
                                             if (log.isDebugEnabled()) {
                                                 
log.debug(sm.getString("engine.ocspResponse", urlString,
                                                         
Integer.toString(ocspResponse)));
@@ -1292,7 +1297,7 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
         }
     }
 
-    private static int processOCSPRequest(URL url, MemorySegment issuer, 
MemorySegment x509,
+    private static int processOCSPRequest(EngineState state, URL url, 
MemorySegment issuer, MemorySegment x509,
             MemorySegment /* X509_STORE_CTX */ x509ctx, Arena localArena) {
         if (openssl_h_Compatibility.BORINGSSL) {
             return V_OCSP_CERTSTATUS_UNKNOWN();
@@ -1317,6 +1322,7 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
             if (MemorySegment.NULL.equals(ocspOneReq)) {
                 return V_OCSP_CERTSTATUS_UNKNOWN();
             }
+            OCSP_request_add1_nonce(ocspRequest, (char) 0, -1);
             MemorySegment bufPointer = 
localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL);
             int requestLength = i2d_OCSP_REQUEST(ocspRequest, bufPointer);
             if (requestLength <= 0) {
@@ -1330,6 +1336,8 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
             // Content-Length: ocspRequestData.length
             byte[] ocspRequestData = buf.reinterpret(requestLength, 
localArena, null).toArray(ValueLayout.JAVA_BYTE);
             connection = (HttpURLConnection) url.openConnection();
+            connection.setConnectTimeout(state.ocspTimeout);
+            connection.setReadTimeout(state.ocspTimeout);
             connection.setRequestMethod(Method.POST);
             connection.setDoInput(true);
             connection.setDoOutput(true);
@@ -1354,15 +1362,38 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
             if (!MemorySegment.NULL.equals(ocspResponse)) {
                 if (OCSP_response_status(ocspResponse) == 
OCSP_RESPONSE_STATUS_SUCCESSFUL()) {
                     basicResponse = OCSP_response_get1_basic(ocspResponse);
+                    if (OCSP_check_nonce(ocspRequest, basicResponse) == 0) {
+                        X509_STORE_CTX_set_error(x509ctx, 
X509_V_ERR_OCSP_RESP_INVALID());
+                        return V_OCSP_CERTSTATUS_UNKNOWN();
+                    }
+                    MemorySegment certStack = 
OCSP_resp_get0_certs(basicResponse);
+                    if (OCSP_basic_verify(basicResponse, certStack, 
X509_STORE_CTX_get0_store(x509ctx), state.ocspVerifyFlags) <= 0) {
+                        X509_STORE_CTX_set_error(x509ctx, 
X509_V_ERR_OCSP_SIGNATURE_FAILURE());
+                        return V_OCSP_CERTSTATUS_UNKNOWN();
+                    }
                     certId = OCSP_cert_to_id(MemorySegment.NULL, x509, issuer);
                     if (MemorySegment.NULL.equals(certId)) {
+                        X509_STORE_CTX_set_error(x509ctx, 
X509_V_ERR_OCSP_RESP_INVALID());
                         return V_OCSP_CERTSTATUS_UNKNOWN();
                     }
                     // Find by serial number and get the matching response
                     MemorySegment singleResponse =
                             OCSP_resp_get0(basicResponse, 
OCSP_resp_find(basicResponse, certId, -1));
-                    return OCSP_single_get0_status(singleResponse, 
MemorySegment.NULL, MemorySegment.NULL,
-                            MemorySegment.NULL, MemorySegment.NULL);
+                    MemorySegment thisUpdatePointer = 
localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL);
+                    MemorySegment nextUpdatePointer = 
localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL);
+                    int status = OCSP_single_get0_status(singleResponse, 
MemorySegment.NULL, MemorySegment.NULL,
+                            thisUpdatePointer, nextUpdatePointer);
+                    if 
(OCSP_check_validity(thisUpdatePointer.get(ValueLayout.ADDRESS, 0),
+                            nextUpdatePointer.get(ValueLayout.ADDRESS, 0), 
OCSP_MAX_SKEW, -1) <= 0) {
+                        X509_STORE_CTX_set_error(x509ctx, 
X509_V_ERR_OCSP_NOT_YET_VALID());
+                        return V_OCSP_CERTSTATUS_UNKNOWN();
+                    }
+                    if 
(OCSP_check_validity(thisUpdatePointer.get(ValueLayout.ADDRESS, 0),
+                            nextUpdatePointer.get(ValueLayout.ADDRESS, 0), 
OCSP_MAX_SKEW, OCSP_MAX_SKEW) <= 0) {
+                        X509_STORE_CTX_set_error(x509ctx, 
X509_V_ERR_OCSP_HAS_EXPIRED());
+                        return V_OCSP_CERTSTATUS_UNKNOWN();
+                    }
+                    return status;
                 }
             }
         } catch (Exception e) {
@@ -1675,16 +1706,20 @@ public final class OpenSSLEngine extends SSLEngine 
implements SSLUtil.ProtocolIn
         private final MemorySegment networkBIO;
         private final int certificateVerificationDepth;
         private final boolean noOcspCheck;
+        private final int ocspTimeout;
+        private final int ocspVerifyFlags;
 
         private PHAState phaState = PHAState.NONE;
         private int certificateVerifyMode = 0;
         private int handshakeCount = 0;
 
         private EngineState(MemorySegment ssl, MemorySegment networkBIO, int 
certificateVerificationDepth,
-                boolean noOcspCheck) {
+                boolean noOcspCheck, int ocspTimeout, int ocspVerifyFlags) {
             states.put(Long.valueOf(ssl.address()), this);
             this.certificateVerificationDepth = certificateVerificationDepth;
             this.noOcspCheck = noOcspCheck;
+            this.ocspTimeout = ocspTimeout;
+            this.ocspVerifyFlags = ocspVerifyFlags;
             // Use another arena to avoid keeping a reference through segments
             // This also allows making further accesses to the main pointers 
safer
             this.ssl = ssl.reinterpret(ValueLayout.ADDRESS.byteSize(), 
stateArena, openssl_h::SSL_free);
diff --git a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java 
b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
index 74ccdc6f8c..b4e53e975d 100644
--- a/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
+++ b/test/org/apache/tomcat/util/net/ocsp/TestOcspIntegration.java
@@ -75,6 +75,8 @@ import org.apache.tomcat.util.net.Constants;
 import org.apache.tomcat.util.net.SSLHostConfig;
 import org.apache.tomcat.util.net.SSLHostConfigCertificate;
 import org.apache.tomcat.util.net.TesterSupport;
+import org.apache.tomcat.util.net.openssl.OpenSSLConf;
+import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
 import org.apache.tomcat.util.net.openssl.OpenSSLStatus;
 
 import com.sun.net.httpserver.Headers;
@@ -357,6 +359,11 @@ public class TestOcspIntegration extends TomcatBaseTest {
 
         if (useOpenSSLTrust) {
             
sslHostConfig.setCaCertificateFile(certificateChainFile.getAbsolutePath());
+            // Need to use OpenSSLConf settings
+            OpenSSLConf conf = new OpenSSLConf();
+            sslHostConfig.setOpenSslConf(conf);
+            // Verification
+            conf.addCmd(new OpenSSLConfCmd("OCSP_VERIFY_FLAGS", "16"));
         } else {
             sslHostConfig.setTruststoreType("PKCS12");
             sslHostConfig.setTruststoreFile(truststoreFile.getAbsolutePath());
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 37822bd41b..2a5a889281 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -282,7 +282,8 @@
       <add>
         Add OCSP support to JSSE based TLS connectors and make the use of OCSP
         configurable per connector for both JSSE and OpenSSL based TLS
-        implementations. (markt)
+        implementations and align the checks performed by OpenSSL with those
+        performed by JSSE. (markt)
       </add>
     </changelog>
   </subsection>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to