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 e53b36f0a9 Fix BZ 69728. Remove incorrect h2+optional msg. Improve 
CLIENT-CERT msgs
e53b36f0a9 is described below

commit e53b36f0a96478aa0c0bbda463ca629b68af8114
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Jun 26 12:01:08 2025 +0100

    Fix BZ 69728. Remove incorrect h2+optional msg. Improve CLIENT-CERT msgs
    
    https://bz.apache.org/bugzilla/show_bug.cgi?id=69728
---
 .../catalina/authenticator/LocalStrings.properties |  4 +-
 .../catalina/authenticator/SSLAuthenticator.java   | 62 +++++++++++++++-------
 .../apache/tomcat/util/net/AbstractEndpoint.java   |  6 ---
 .../apache/tomcat/util/net/LocalStrings.properties |  1 -
 .../tomcat/util/net/LocalStrings_fr.properties     |  1 -
 .../tomcat/util/net/LocalStrings_ja.properties     |  1 -
 .../tomcat/util/net/LocalStrings_ko.properties     |  1 -
 .../tomcat/util/net/LocalStrings_zh_CN.properties  |  1 -
 webapps/docs/changelog.xml                         |  6 +++
 9 files changed, 51 insertions(+), 32 deletions(-)

diff --git a/java/org/apache/catalina/authenticator/LocalStrings.properties 
b/java/org/apache/catalina/authenticator/LocalStrings.properties
index 62825a2578..3736c0b345 100644
--- a/java/org/apache/catalina/authenticator/LocalStrings.properties
+++ b/java/org/apache/catalina/authenticator/LocalStrings.properties
@@ -89,6 +89,6 @@ spnegoAuthenticator.serviceLoginFail=Unable to login as the 
service principal
 spnegoAuthenticator.ticketValidateFail=Failed to validate client supplied 
ticket
 
 sslAuthenticatorValve.authFailed=Authentication with the provided certificates 
failed
-sslAuthenticatorValve.http2=The context [{0}] in virtual host [{1}] is 
configured to use CLIENT-CERT authentication and [{2}] is configured to support 
HTTP/2. Use of CLIENT-CERT authentication is not compatible with the use of 
HTTP/2.
+sslAuthenticatorValve.http2=The context [{0}] in virtual host [{1}] is 
configured to use CLIENT-CERT authentication and [{2}] is configured to support 
HTTP/2. Use of CLIENT-CERT authentication is not compatible with the use of 
HTTP/2 unless certificateVerification is set to required.
 sslAuthenticatorValve.noCertificates=No certificates are included with this 
request
-sslAuthenticatorValve.tls13=The context [{0}] in virtual host [{1}] is 
configured to use CLIENT-CERT authentication and [{2}] is configured to support 
TLS 1.3 using JSSE. Use of CLIENT-CERT authentication is not compatible with 
the use of TLS 1.3 and JSSE.
+sslAuthenticatorValve.tls13=The context [{0}] in virtual host [{1}] is 
configured to use CLIENT-CERT authentication and [{2}] is configured to support 
TLS 1.3 using JSSE. Use of CLIENT-CERT authentication is not compatible with 
the use of TLS 1.3 and JSSE unless certificateVerification is set to required.
diff --git a/java/org/apache/catalina/authenticator/SSLAuthenticator.java 
b/java/org/apache/catalina/authenticator/SSLAuthenticator.java
index d7207510d8..c4d7012563 100644
--- a/java/org/apache/catalina/authenticator/SSLAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/SSLAuthenticator.java
@@ -37,6 +37,7 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.net.Constants;
 import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfig.CertificateVerification;
 
 /**
  * An <b>Authenticator</b> and <b>Valve</b> implementation of authentication 
that utilizes SSL certificates to identify
@@ -154,10 +155,10 @@ public class SSLAuthenticator extends AuthenticatorBase {
          * and an Engine but test at each stage to be safe.
          */
         Container container = getContainer();
-        if (!(container instanceof Context context2)) {
+        if (!(container instanceof Context context)) {
             return;
         }
-        container = context2.getParent();
+        container = context.getParent();
         if (!(container instanceof Host host)) {
             return;
         }
@@ -169,28 +170,51 @@ public class SSLAuthenticator extends AuthenticatorBase {
         Connector[] connectors = engine.getService().findConnectors();
 
         for (Connector connector : connectors) {
-            // First check for upgrade
-            UpgradeProtocol[] upgradeProtocols = 
connector.findUpgradeProtocols();
-            for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
-                if ("h2".equals(upgradeProtocol.getAlpnName())) {
-                    log.warn(sm.getString("sslAuthenticatorValve.http2", 
context2.getName(), host.getName(), connector));
+            /*
+             * There are two underlying issues here.
+             *
+             * 1. JSSE does not implement post-handshake authentication (PHA) 
for TLS 1.3. That means CLIENT-CERT
+             * authentication will only work if the virtual host requires a 
certificate OR the client never requests a
+             * protected resource.
+             *
+             * 2. HTTP/2 does not permit re-negotiation nor PHA. That means 
CLIENT-CERT authentication will only work if
+             * the virtual host requires a certificate OR the client never 
requests a protected resource.
+             *
+             * We can't rely on the client never requesting a protected 
resource but we can check if all the virtual
+             * hosts are configured to require a certificate.
+             */
+            boolean allHostsRequireCertificate = true;
+            for (SSLHostConfig sslHostConfig : connector.findSslHostConfigs()) 
{
+                if (sslHostConfig.getCertificateVerification() != 
CertificateVerification.REQUIRED) {
+                    allHostsRequireCertificate = false;
                     break;
                 }
             }
 
-            // Then check for TLS 1.3
-            SSLHostConfig[] sslHostConfigs = connector.findSslHostConfigs();
-            for (SSLHostConfig sslHostConfig : sslHostConfigs) {
-                if (!sslHostConfig.isTls13RenegotiationAvailable()) {
-                    String[] enabledProtocols = 
sslHostConfig.getEnabledProtocols();
-                    if (enabledProtocols == null) {
-                        // Possibly boundOnInit is used, so use the less 
accurate protocols
-                        enabledProtocols = 
sslHostConfig.getProtocols().toArray(new String[0]);
+            // Only need to check for use of HTTP/2 or TLS 1.3 if one or more 
hosts doesn't require a certificate
+            if (!allHostsRequireCertificate) {
+                // Check if the Connector is configured to support upgrade to 
HTTP/2
+                UpgradeProtocol[] upgradeProtocols = 
connector.findUpgradeProtocols();
+                for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
+                    if ("h2".equals(upgradeProtocol.getAlpnName())) {
+                        log.warn(sm.getString("sslAuthenticatorValve.http2", 
context.getName(), host.getName(), connector));
+                        break;
                     }
-                    for (String enabledProtocol : enabledProtocols) {
-                        if 
(Constants.SSL_PROTO_TLSv1_3.equals(enabledProtocol)) {
-                            
log.warn(sm.getString("sslAuthenticatorValve.tls13", context2.getName(), 
host.getName(),
-                                    connector));
+                }
+
+                // Check if any of the virtual hosts support TLS 1.3 without 
supporting PHA
+                for (SSLHostConfig sslHostConfig : 
connector.findSslHostConfigs()) {
+                    if (!sslHostConfig.isTls13RenegotiationAvailable()) {
+                        String[] enabledProtocols = 
sslHostConfig.getEnabledProtocols();
+                        if (enabledProtocols == null) {
+                            // Possibly boundOnInit is used, so use the less 
accurate protocols
+                            enabledProtocols = 
sslHostConfig.getProtocols().toArray(new String[0]);
+                        }
+                        for (String enabledProtocol : enabledProtocols) {
+                            if 
(Constants.SSL_PROTO_TLSv1_3.equals(enabledProtocol)) {
+                                
log.warn(sm.getString("sslAuthenticatorValve.tls13", context.getName(), 
host.getName(),
+                                        connector));
+                            }
                         }
                     }
                 }
diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java 
b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index ef2b9dc1b7..14e789294e 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -396,12 +396,6 @@ public abstract class AbstractEndpoint<S, U> {
      */
     protected void createSSLContext(SSLHostConfig sslHostConfig) throws 
IllegalArgumentException {
 
-        // HTTP/2 does not permit optional certificate authentication with any
-        // version of TLS.
-        if (sslHostConfig.getCertificateVerification().isOptional() && 
negotiableProtocols.contains("h2")) {
-            
getLog().warn(sm.getString("sslHostConfig.certificateVerificationWithHttp2", 
sslHostConfig.getHostName()));
-        }
-
         boolean firstCertificate = true;
         for (SSLHostConfigCertificate certificate : 
sslHostConfig.getCertificates(true)) {
             SSLUtil sslUtil = sslImplementation.getSSLUtil(certificate);
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties 
b/java/org/apache/tomcat/util/net/LocalStrings.properties
index befebc70ec..1fbdfad007 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -145,7 +145,6 @@ socketWrapper.writeTimeout=Write timeout
 
 sslHostConfig.certificate.notype=Multiple certificates were specified and at 
least one is missing the required attribute type
 sslHostConfig.certificateVerificationInvalid=The certificate verification 
value [{0}] is not recognised
-sslHostConfig.certificateVerificationWithHttp2=The TLS virtual host [{0}] is 
configured for optional certificate verification and the enclosing connector is 
configured to support upgrade to h2. HTTP/2 over TLS does not permit optional 
certificate verification.
 sslHostConfig.fileNotFound=Configured file [{0}] does not exist
 sslHostConfig.invalid_truststore_password=The provided trust store password 
could not be used to unlock and/or validate the trust store. Retrying to access 
the trust store with a null password which will skip validation.
 sslHostConfig.mismatch=The property [{0}] was set on the SSLHostConfig named 
[{1}] and is for the [{2}] configuration syntax but the SSLHostConfig is being 
used with the [{3}] configuration syntax
diff --git a/java/org/apache/tomcat/util/net/LocalStrings_fr.properties 
b/java/org/apache/tomcat/util/net/LocalStrings_fr.properties
index e8127fa44f..33bfbb254a 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings_fr.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings_fr.properties
@@ -148,7 +148,6 @@ socketWrapper.writeTimeout=Timeout en écriture
 
 sslHostConfig.certificate.notype=Plusieurs certificats ont été spécifiés et au 
moins un n'a pas d'attribut type
 sslHostConfig.certificateVerificationInvalid=La valeur de vérification de 
certificat [{0}] n''est pas reconnue
-sslHostConfig.certificateVerificationWithHttp2=L''hôte virtuel TLS [{0}] est 
configuré avec la validation optionelle du certificat, et le connecteur qui 
l''utilise est configuré pour supporter l''upgrade vers h2. HTTP/2 sur TLS ne 
permet pas la validation optionelle du certificat.
 sslHostConfig.fileNotFound=Le fichier [{0}] configuré n''existe pas.
 sslHostConfig.invalid_truststore_password=Le mot de passe de la base de 
confiance n'a pas pu être utilisé pour déverrouiller et ou valider celle ci, 
nouvel essai en utilisant un mot de passe null pour passer la validation
 sslHostConfig.mismatch=La propriété [{0}] a été fixée sur le SSLHostConfig 
nommé [{1}] et est pour la syntaxe de configuration [{2}] mais le SSLHostConfig 
est utilisé avec la syntaxe de configuration [{3}]
diff --git a/java/org/apache/tomcat/util/net/LocalStrings_ja.properties 
b/java/org/apache/tomcat/util/net/LocalStrings_ja.properties
index 2e196d6e62..c9475c54ac 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings_ja.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings_ja.properties
@@ -148,7 +148,6 @@ socketWrapper.writeTimeout=書き込みタイムアウト
 
 sslHostConfig.certificate.notype=指定された複数の証明書の中に、少なくとも1つは必須要素の存在しない証明書が含まれています。
 sslHostConfig.certificateVerificationInvalid=証明書検証値[{0}]が認識されません
-sslHostConfig.certificateVerificationWithHttp2=TLS仮想ホスト[{0}]はオプションの証明書検証用に構成されており、コネクタはh2へのアップグレードをサポートするように構成されています。
 HTTP/2 over TLSでは、オプションの証明書検証は許可されていません。
 sslHostConfig.fileNotFound=構成ファイル[{0}]は存在しません
 
sslHostConfig.invalid_truststore_password=提供されたトラストストアパスワードは、トラストストアのロック解除および検証に使用できませんでした。
 検証をスキップするnullパスワードでトラストストアにアクセスしようとしました。
 sslHostConfig.mismatch=[{0}] プロパティは [{1}] という名前のSSLHostConfigで設定され、[{2}] 
構成構文用ですが、[{3}] 構成構文でSSLHostConfigが使用されています
diff --git a/java/org/apache/tomcat/util/net/LocalStrings_ko.properties 
b/java/org/apache/tomcat/util/net/LocalStrings_ko.properties
index 1131eb91a5..b76790a130 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings_ko.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings_ko.properties
@@ -138,7 +138,6 @@ socketWrapper.writeTimeout=쓰기 타임아웃
 
 sslHostConfig.certificate.notype=여러 개의 인증서들이 지정되었는데, 적어도 하나의 인증서에 필수 속성 타입이 
없습니다.
 sslHostConfig.certificateVerificationInvalid=인증서 검증 값 [{0}]은(는) 인식되지 않는 값입니다.
-sslHostConfig.certificateVerificationWithHttp2=TLS 가상 호스트 [{0}](은)는 선택적 인증서 
확인을 하도록 설정되었고, 내부에 정의된 커넥터는 HTTP/2로 업그레이드를 지원하도록 설정되었습니다. TLS 기반의 HTTP/2는 선택적 
인증서 확인을 허용하지 않습니다.
 sslHostConfig.fileNotFound=설정된 파일 [{0}]이(가) 존재하지 않습니다.
 sslHostConfig.invalid_truststore_password=Trust 저장소를 잠금을 풀거나 유효한지 확인하는 용도로, 
제공된 Trust 저장소 비밀번호를 사용할 수 없었습니다. 널 비밀번호를 사용하여, 해당 Trust 저장소에 대한 접근을 다시 시도합니다. 
이는 유효한지 확인하는 작업을 건너뛸 것입니다.
 sslHostConfig.mismatch=[{1}](이)라는 이름의 SSLHostConfig에 프로퍼티 [{0}]이(가) 설정되었는데, 이 
프로퍼티는 [{2}] 설정 문법을 위한 것이나, 해당 SSLHostConfig은 [{3}] 설정 문법으로 사용되고 있습니다.
diff --git a/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties 
b/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties
index 5094653425..41e0583bab 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties
@@ -140,7 +140,6 @@ socketWrapper.writeTimeout=写入超时
 
 sslHostConfig.certificate.notype=指定了多个证书,并且至少有一个证书缺少必需的属性类型
 sslHostConfig.certificateVerificationInvalid=证书认证值[{0}]未识别
-sslHostConfig.certificateVerificationWithHttp2=TLS虚拟主机[{0}]被配置为可选的证书验证,包围的连接器被配置为支持升级到h2。HTTP/2
 over TLS不允许可选的证书验证。
 sslHostConfig.fileNotFound=配置文件 [{0}] 不存在
 
sslHostConfig.invalid_truststore_password=提供的信任存储密码无法用于解锁和/或验证信任存储。正在重试使用空密码访问信任存储,该密码将跳过验证。
 sslHostConfig.mismatch=属性[{0}]是在名为[{1}]的SSLHostConfig 
上设置的,用于[{2}]配置语法,但SSLHostConfig 正与[{3}]配置语法一起使用
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f6a20caad3..c5b998df1c 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -218,6 +218,12 @@
         Fix JMX value for <code>keepAliveCount</code> on the endpoint. Also add
         the value of <code>useVirtualThreads</code> in JMX. (remm)
       </fix>
+      <fix>
+        <bug>69728</bug>: Remove incorrect warning when HTTP/2 is used with
+        optional certificate verification and improve the warnings when a web
+        application tries to use CLIENT-CERT with either HTTP/2 or a JSSE
+        implementation of TLS 1.3. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to