Bug 67818 - SSL#setVerify()/SSLContext#setVerify() silently set undocumented default verify paths
Summary: SSL#setVerify()/SSLContext#setVerify() silently set undocumented default veri...
Status: RESOLVED FIXED
Alias: None
Product: Tomcat Native
Classification: Unclassified
Component: Library (show other bugs)
Version: 2.0.6
Hardware: All All
: P2 normal (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-10-18 19:51 UTC by Michael Osipov
Modified: 2023-10-30 10:27 UTC (History)
1 user (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Osipov 2023-10-18 19:51:58 UTC
Note: This applies to 2.0.x and 1.2.x

Consider the following Connector (any Tomcat version):
> <Connector port="18444" connectionTimeout="20000" keepAliveTimeout="7200000" maxParameterCount="1000"
> 	maxHttpHeaderSize="24576" maxThreads="250"
> 	SSLEnabled="true" scheme="https" secure="true"
> 	defaultSSLHostConfigName="deblndw024v.ad001.siemens.net">
> 	<SSLHostConfig hostName="deblndw024v.ad001.siemens.net" protocols="TLSv1.2+TLSv1.3"
> 		honorCipherOrder="true" disableSessionTickets="true"
> 		certificateVerification="optional" certificateVerificationDepth="5"
> 		ciphers="HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK:!DSS:!SHA1:!SHA256:!SHA384">
> 		<Certificate certificateFile="/opt/openssl/deblndw024v.ad001.siemens.net/cert.crt"
> 			certificateKeyFile="/opt/openssl/deblndw024v.ad001.siemens.net/key.crt"
> 			certificateKeyPasswordFile="/opt/openssl/deblndw024v.ad001.siemens.net/password" type="RSA" />
> 		<OpenSSLConf>
> 			<OpenSSLConfCmd name="RequestCAFile" value="/opt/openssl/siemens-medium+strong-clientcert-cacerts.crt" />
> 			<OpenSSLConfCmd name="NO_OCSP_CHECK" value="true" />
> 		</OpenSSLConf>
> 	</SSLHostConfig>
> </Connector>

Conditions:
* The certificate file does not contain a chain of the issuers
* SSLCertificateChainFile (mod_ssl) or certificateChainFile (Tomcat) is not set
* Neither SSLCACertificatePath/SSLCACertificateFile (mod_ssl) or caCertificateFile/caCertificatePath (Tomcat) is not set

According then to my understanding Tomcat should construct any chain for the peer to verify the server certificate, nor when the server requests for for a client certificate would it be able to verify the client certificate chain.

Now let's probe the server:
> $ openssl s_client -connect deblndw024v:18444 -no-CApath -no-CAfile
> CONNECTED(00000004)
> Can't use SSL_get_servername
> depth=2 C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZA1, OU = Siemens Trust Center, CN = Siemens Root CA V3.0 2016
> verify error:num=19:self signed certificate in certificate chain
> verify return:1
> depth=2 C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZA1, OU = Siemens Trust Center, CN = Siemens Root CA V3.0 2016
> verify return:1
> depth=1 C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZE7, CN = Siemens Issuing CA Intranet Server 2022
> verify return:1
> depth=0 C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
> verify return:1
> ---
> Certificate chain
>  0 s:C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
>    i:C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZE7, CN = Siemens Issuing CA Intranet Server 2022
>  1 s:C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZE7, CN = Siemens Issuing CA Intranet Server 2022
>    i:C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZA1, OU = Siemens Trust Center, CN = Siemens Root CA V3.0 2016
>  2 s:C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZA1, OU = Siemens Trust Center, CN = Siemens Root CA V3.0 2016
>    i:C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZA1, OU = Siemens Trust Center, CN = Siemens Root CA V3.0 2016
> ---
> Server certificate
> -----BEGIN CERTIFICATE-----
> MIIIvjCCBqagAwIBAgIUFZyE3zc5lFsDVaFS9w2zaDea4mYwDQYJKoZIhvcNAQEL
> ...
> tiR7NMIYlOYgW/cUNFfwJUJk8D0L92oKlmT6JAfDN+rahjtOTUXXw3MD7uZ58+6T
> aYp+izk9yY90cqgrdGe82vv4kx2xkEozgvYlW2GyKg1Fhh9GYu64xn0ny4M5jE0N
> eFdmSs7MqQZBF6HSlucSXbkVV3zvoltvILbWXrMVYldJGA==
> -----END CERTIFICATE-----
> subject=C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
> 
> issuer=C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZE7, CN = Siemens Issuing CA Intranet Server 2022
> 
> ---
> Acceptable client certificate CA names
> C = DE, ST = Bayern, O = Siemens, serialNumber = ZZZZZZD2, CN = Siemens Issuing CA EE Auth 2021
> C = DE, ST = Bayern, O = Siemens, serialNumber = ZZZZZZDD, CN = Siemens Issuing CA EE Network Smartcard Auth 2021
> C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZB2, OU = Siemens Trust Center, CN = Siemens Issuing CA EE Auth 2020
> C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZBD, OU = Siemens Trust Center, CN = Siemens Issuing CA EE Network Smartcard Auth 2020
> C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZB6, OU = Siemens Trust Center, CN = Siemens Issuing CA Medium Strength Authentication 2020
> C = DE, ST = Bayern, O = Siemens, serialNumber = ZZZZZZD6, CN = Siemens Issuing CA Medium Strength Authentication 2021
> Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:ECDSA+SHA1:RSA+SHA224:RSA+SHA1
> Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512
> Peer signing digest: SHA256
> Peer signature type: RSA-PSS
> Server Temp Key: X25519, 253 bits
> ---
> SSL handshake has read 7914 bytes and written 403 bytes
> Verification error: self signed certificate in certificate chain
> ---

Where does the chain come from when I have not set it? Reproducing the same in HTTPd gives only the leaf, not the chain. it turns out that the admin does not have real control over this because sslcontext.c sets the default verify paths without admin's consent and without any documentation. This is likely unwanted in many scenarios.

>    if (!c->store) {
>        if (SSL_CTX_set_default_verify_paths(c->ctx)) {
>            c->store = SSL_CTX_get_cert_store(c->ctx);
>            X509_STORE_set_flags(c->store, 0);
>        }

Depending on how the default trust store is configured this might work silently or produce unwanted effects.
The solution is simple: don't do it just like in mod_ssl, give the admin full control over this.

Applied a patch and tried again:
> $ openssl s_client -connect deblndw024v:18444 -no-CApath -no-CAfile
> CONNECTED(00000004)
> Can't use SSL_get_servername
> depth=0 C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
> verify error:num=20:unable to get local issuer certificate
> verify return:1
> depth=0 C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
> verify error:num=21:unable to verify the first certificate
> verify return:1
> depth=0 C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
> verify return:1
> ---
> Certificate chain
>  0 s:C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
>    i:C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZE7, CN = Siemens Issuing CA Intranet Server 2022
> ---
> Server certificate
> -----BEGIN CERTIFICATE-----
> MIIIvjCCBqagAwIBAgIUFZyE3zc5lFsDVaFS9w2zaDea4mYwDQYJKoZIhvcNAQEL
> ...
> aYp+izk9yY90cqgrdGe82vv4kx2xkEozgvYlW2GyKg1Fhh9GYu64xn0ny4M5jE0N
> eFdmSs7MqQZBF6HSlucSXbkVV3zvoltvILbWXrMVYldJGA==
> -----END CERTIFICATE-----
> subject=C = DE, O = Siemens, OU = SMD HVM DW, CN = deblndw024v.ad001.siemens.net
> 
> issuer=C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZE7, CN = Siemens Issuing CA Intranet Server 2022
> 
> ---
> Acceptable client certificate CA names
> C = DE, ST = Bayern, O = Siemens, serialNumber = ZZZZZZD2, CN = Siemens Issuing CA EE Auth 2021
> C = DE, ST = Bayern, O = Siemens, serialNumber = ZZZZZZDD, CN = Siemens Issuing CA EE Network Smartcard Auth 2021
> C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZB2, OU = Siemens Trust Center, CN = Siemens Issuing CA EE Auth 2020
> C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZBD, OU = Siemens Trust Center, CN = Siemens Issuing CA EE Network Smartcard Auth 2020
> C = DE, ST = Bayern, L = Muenchen, O = Siemens, serialNumber = ZZZZZZB6, OU = Siemens Trust Center, CN = Siemens Issuing CA Medium Strength Authentication 2020
> C = DE, ST = Bayern, O = Siemens, serialNumber = ZZZZZZD6, CN = Siemens Issuing CA Medium Strength Authentication 2021
> Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:ECDSA+SHA1:RSA+SHA224:RSA+SHA1
> Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512
> Peer signing digest: SHA256
> Peer signature type: RSA-PSS
> Server Temp Key: X25519, 253 bits
> ---
> SSL handshake has read 4045 bytes and written 403 bytes
> Verification error: unable to verify the first certificate
> ---
> New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
> Server public key is 4096 bit
> Secure Renegotiation IS NOT supported
> Compression: NONE
> Expansion: NONE
> No ALPN negotiated
> Early data was not sent
> Verify return code: 21 (unable to verify the first certificate)
> ---

As one can see, truly no chain transmitted. Now I can properly construct the chain for the server certificate AND the chain for client certificate validation myself.

I will provide a PR.
Comment 1 Michael Osipov 2023-10-18 20:41:38 UTC
Tested the patch locally with my smartcard and Edge properly says:
Die Verbindung mit dieser Website ist nicht sicher.deblndw024v.ad001.siemens.net hat ihr Anmeldezertifikat nicht akzeptiert, oder es wurde kein Anmeldezertifikat bereitgestellt.
Wenden Sie sich an Ihre Organisation.
ERR_BAD_SSL_CLIENT_AUTH_CERT

Rough translation: deblndw024v.ad001.siemens.net did not accept your certificate.

Now lets add caCertificatePath="/opt/openssl/certs" and retry:
2023-10-18T22:39:55.656 [https-openssl-apr-18444-exec-2] 139.21.146.171 osipovmi@AD001.SIEMENS.NET "GET /manager/html HTTP/1.1" 200 13596 1197

So OpenSSL did accept my certificate which is issued by one of the accepted CA which is provided by RequestCAFile.
Comment 2 Michael Osipov 2023-10-30 10:27:51 UTC
Fixed in:
- main for 2.0.7 and onwards
- 1.2.x for 1.2.40 and onwards