Fix for TLS handshake issue with GnuTLS #28902#28955
Fix for TLS handshake issue with GnuTLS #28902#28955martinRa2 wants to merge 9 commits intoopenssl:masterfrom
Conversation
apps/s_server.c
Outdated
|
|
||
| /* | ||
| * in the case the root CA certificate is not included in the chain | ||
| * we assme that the last remaining response is issued by it |
|
My preference would for multi-stapling support to be withdrawn from OpenSSL until we have a robust well-documented design for that feature. The new As it stands, as in the initial review, I am not convinced we have a robust design for the lifecycle and robust construct of multi-stapled server responses. |
That will be because the ossltest provider is in use. That supplies various dummy implementations for various hash functions: Lines 193 to 207 in ca95d13 Those particular tests are using the TLSProxy which uses dummy ciphers and hashes so that the proxy can amend TLS messages for test purposes without breaking the handshake Finished messges. We really should not be putting workarounds for test code into the library itself. |
|
I created a new OCSP response using the test provider and removed the workaround. |
|
The CI is relevant. It would be also good if you could please include a test for the regression. |
|
What type of test do you have in mind? |
Could a testcase be added to one of the tls13 test recipes? |
|
I had a look at the failed CI/non-caching job and found that in that particular case the correct issuer name SHA-1 value is calculated when the OCSP responses are checked. I'm a little bit lost on that point. Implement a workaround I understood is not a good idea. A other solution would be to remove the check if the correct OCSP response is returned and just take the one which is on this place. @mattcaswell , @t8m any adivse? |
apps/s_server.c
Outdated
There was a problem hiding this comment.
The variable name server_certs is confusing.
Please rename to, e.g., server_chain and add a comment that it may include the root (TA cert) or not.
| /* issuer certificate is next in chain */ | ||
| issuer = sk_X509_value(server_certs, i); | ||
|
|
||
| /* | ||
| * in the case the root CA certificate is not included in the chain | ||
| * we assume that the last remaining response is issued by it | ||
| */ | ||
| if (issuer == NULL && i == (num - 1) && sk_OCSP_RESPONSE_num(sk_resp_unordered) == 1) { | ||
| resp = sk_OCSP_RESPONSE_value(sk_resp_unordered, 0); | ||
| (void)sk_OCSP_RESPONSE_push(*sk_resp, resp); | ||
| sk_OCSP_RESPONSE_delete(sk_resp_unordered, 0); | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Better not even attempt to call issuer = sk_X509_value(server_certs, i) in case i is beyond the end of the chain.
And this whole loop with all its exceptions, including this new one, is a mess.
Please re-think it as a whole and come up with a more straightforward and well-readable overall algorithm.
I'm still struggling with the failed check. Can anybody tell me why in this particular test not the test value from the test engine is returned? |
|
@martinRa2 Can you run the failing test(s) locally with V=1 and post the output here. It almost certainly has something to do with @mattcaswell s comments here |
ssl/statem/extensions_srvr.c
Outdated
| resp = get_ocsp_response(s, chainidx); | ||
|
|
||
| /* If no OCSP response was found the extension is not sent */ | ||
| if (SSL_CONNECTION_IS_TLS13(s) && resp == NULL) |
There was a problem hiding this comment.
If, somehow, the application has configured an OCSP response for an intermediate certificate, but not the leaf, I think this PR will still get it wrong? It is also forbidden to send an empty CertificateStatus message. I believe this means you should unconditionally skip the extension when resp == NULL, not only if SSL_CONNECTION_IS_TLS13(s).
(Footnote: is chainidx guaranteed to be zero in contexts where this is not relevant? This would rely on this.)
There was a problem hiding this comment.
In the comment below it is mentioned that for TLS <=1.2 an empty extension is send to the client. Therefore, the check here is to see if we have a response for TLS 1.3 or not. If not we don't send the extension for the current certificate.
I checked. If for the leaf certificate is no OCSP response is given, no response is send to the client. I have to check why.
There was a problem hiding this comment.
In TLS 1.2, you send an empty extension to the client to signal that you are about to staple a response. If you aren't going to staple a response, you shouldn't send the extension.
(Strictly speaking, servers are allowed to signal the extension and then skip the CertificateStatus message. But that doesn't seem to be what the PR is doing (seems to be sending an empty CertificateStatus message, if I'm reading it right? It's not returning CON_FUNC_DONT_SEND), and is also kinda silly because the server already knows whether it has an OCSP response at this point.)
ssl/statem/statem_srvr.c
Outdated
|
|
||
| resp = get_ocsp_response(s, 0); | ||
|
|
||
| if (resp != NULL && !tls_construct_cert_status_body(s, resp, pkt)) { |
There was a problem hiding this comment.
Related to the comment in extensions_srvr.c, I don't believe this resp != NULL check is correct. It'll just send an empty CertificateStatus message, which isn't allowed. I believe this should trigger ERR_R_INTERNAL_ERROR or an assert or whatever because, after fixing extensions_srvr.c, it should be impossible to get to this point without an OCSP response.
ssl/statem/statem_srvr.c
Outdated
| } | ||
| } | ||
|
|
||
| if (resp != NULL) |
There was a problem hiding this comment.
I believe resp == NULL is now impossible and should probably fail with ERR_R_INTERNAL_ERROR or an assert or whatever is preferred these days.
If resp ever were NULL, I believe you all would output a syntax error.
There was a problem hiding this comment.
(This condition is now impossible.)
@nhorman here are the output from my tests. It is shown that the test provider is used even in the failed test cases, but somehow the hash for the issuer name is not set to the fixed value 000102030405060708090A0B0C0D0E0F as in the other jobs. Instead the correct SHA-1 value for the issuer name hash is provided, but only for the failed test cases. If I know which parameter influences that at this point the correct hash value is used I could implement the test that it uses a other response file with that hash values. |
|
@martinRa2 Thank you for the traces. though I'm not sure I see in the trace what you are observing (certificate issuer hashes, as far as I can see aren't printed in the logs). At the moment, what I can say (keeping in mind that I'm not super familiar with with ocsp) is:
The things I note about this primarily is that the certificate status response has a zero length, and an unkown response type. I think at minumum, that response should be at least 4 bytes with a value between 0 and 6 (based on my read of RFC 2560. Now the server may be responding with such a malformed response based on its inability to parse the certificate above, I'm honestly not sure, I just wanted to share what I've observed so far. |
Thanks for analyzing the issue. An empty CertificateStatus message is according RFC 6066 not correct. I have to analyze why this is the case. For the test case I added some output to check the issuer name hash in the OCSP response and the calculated one from the certificate: If I use a response file with the correct issuer name hash the test passes with that configuration. |
|
This is the file with the correct issuer name hash. You should place it in ./test/recipes/ocsp-response.der |
|
just a note, the empty ocsp response is likely related to #28989 |
|
@nhorman @davidben GNU Wget 1.21.1 everything on my CentOS computer (Linux artax 5.14.0-630.el9.x86_64) I didn't face any problems with TLS 1.2 and 1.3 Please let me know which issues you observed exactly. I will try to reproduce them here. |
|
Did you configure the server to serve TLS 1.2, with a stapled OCSP response for the intermediate and not the leaf? That's the scenario where the PR seems to not get things right. Since TLS 1.2 can only staple a response for the leaf, it should not send the |
ssl/statem/statem_srvr.c
Outdated
| if (cert_id_md_oid != NULL) | ||
| cert_id_md = EVP_get_digestbyobj(cert_id_md_oid); | ||
| cert_id_md = EVP_MD_fetch(sctx->libctx, | ||
| OBJ_nid2sn(OBJ_obj2nid(cert_id_md_oid)), sctx->propq); |
There was a problem hiding this comment.
I think using OBJ_obj2txt() would be better way as it avoids going through nids. Please note that algorithms should be fetchable by the textual representation of the oid.
apps/s_server.c
Outdated
| SSL_get0_chain_certs(s, &server_chain); | ||
| ssl_ctx = SSL_get_SSL_CTX(s); | ||
| SSL_CTX_get0_chain_certs(ssl_ctx, &server_chain); |
There was a problem hiding this comment.
This doesn't make sense - why do you first call it on SSL when the second call on SSL_CTX will overwrite it?
ssl/ssl_local.h
Outdated
|
|
||
| __owur int send_certificate_request(SSL_CONNECTION *s); | ||
|
|
||
| OCSP_RESPONSE *get_ocsp_response(SSL_CONNECTION *s, int chainidx); |
There was a problem hiding this comment.
Please rename the function to ossl_get_ocsp_response
When a Cert status body is received and the length of the response is zero error out. This PR is dependent on openssl#28955 It will not pass tests until this PR is merged into master.
|
I have this PR to fix the client to fail when it receives a cert status body of zero length: The tests will fail until this PR is merged into master. |
|
This pull request is ready to merge |
|
Squashed and merged with a rewritten commit message to the master and 3.6 branches. Thank you. |
If the OCSP response was not present for a certificate the server created a non-conforming empty CertificateStatus extension instead of not sending the extension at all. Fixes #28902 Fixes b1b4b15 Reviewed-by: Matt Caswell <[email protected]> Reviewed-by: Tomas Mraz <[email protected]> (Merged from #28955)
If the OCSP response was not present for a certificate the server created a non-conforming empty CertificateStatus extension instead of not sending the extension at all. Fixes #28902 Fixes b1b4b15 Reviewed-by: Matt Caswell <[email protected]> Reviewed-by: Tomas Mraz <[email protected]> (Merged from #28955) (cherry picked from commit c5c8b44)
If the OCSP response was not present for a certificate the server created a non-conforming empty CertificateStatus extension instead of not sending the extension at all. Fixes openssl#28902 Fixes b1b4b15 Reviewed-by: Matt Caswell <[email protected]> Reviewed-by: Tomas Mraz <[email protected]> (Merged from openssl#28955)
If the OCSP response was not present for a certificate the server created a non-conforming empty CertificateStatus extension instead of not sending the extension at all. Fixes openssl#28902 Fixes b1b4b15 Reviewed-by: Matt Caswell <[email protected]> Reviewed-by: Tomas Mraz <[email protected]> (Merged from openssl#28955)
When a Cert status body is received and the length of the response is zero error out. This PR is dependent on openssl#28955 It will not pass tests until this PR is merged into master.
If the OCSP response was not present for a certificate the server created a non-conforming empty CertificateStatus extension instead of not sending the extension at all. Fixes openssl#28902 Fixes b1b4b15 Reviewed-by: Matt Caswell <[email protected]> Reviewed-by: Tomas Mraz <[email protected]> (Merged from openssl#28955)
3.6.1 CHANGES.md includes the following: * #28760 "Improve the CPUINFO display for RISC-V" * #28797 "Fix regression when X509_V_FLAG_CRL_CHECK_ALL is set" * #28955 "Fix for TLS handshake issue with GnuTLS #28902" * #29155 "fix(x509.c): fixed -checkend return values" * #29214 "s390x: Check and fail on invalid malformed ECDSA signatures" * #29245 "Clang format 3.6" * #29251 "Fix change of behavior of the single stapled OCSP response API" 3.6.1 NEWS.md includes the following: * #28797 "Fix regression when X509_V_FLAG_CRL_CHECK_ALL is set" * #28955 "Fix for TLS handshake issue with GnuTLS #28902" Co-Authored-by: Tomáš Mráz <[email protected]> Signed-off-by: Eugene Syromiatnikov <[email protected]> Reviewed-by: Nikola Pajkovsky <[email protected]> Reviewed-by: Neil Horman <[email protected]> Reviewed-by: Saša Nedvědický <[email protected]> Reviewed-by: Tomas Mraz <[email protected]> MergeDate: Mon Jan 26 20:01:30 2026


This is a fix for the Issue
TLS handshake fails between OpenSSL 3.6.0 and GnuTLS #28902
The OCSP response is now looked up before the packet is created. If the correct OCSP response is not found the extension for TLS 1.3 is not sent.
I have to put in a workaround for
test_tls13messagesandtest_sslmessages. For some reason I don't know, the hash for the issuer name I get is0x01 0x02 ... 0x015. I introduced a workaround that in this case the check if the OCSP response is the correct one is skipped.Checklist