libressl / portable

LibreSSL Portable itself. This includes the build scaffold and compatibility layer that builds portable LibreSSL from the OpenBSD source code. Pull requests or patches sent to tech@openbsd.org are welcome.
https://www.libressl.org
1.37k stars 267 forks source link

handshake failed: error:1400A09F:SSL routines:CONNECT_CR_CERT_REQ:length mismatch #950

Open jpavco-quadient opened 11 months ago

jpavco-quadient commented 11 months ago

Hello,

I can't connect to server https://api.communicalia.com with LibreSSL. Connection failed on handshake:

handshake failed: error:1400A09F:SSL routines:CONNECT_CR_CERT_REQ:length mismatch

I have simple c++ application to try it:

#include <stdlib.h>
#include <stdio.h>
#include "tls.h"

int main(int argc, char* argv[]) {
    if (argc != 3) {
        printf("Usage: %s hostname port\n", argv[0]);
        return 1;
    }

    tls_config *mTLSConfig = tls_config_new();
    tls *mTLS = tls_client();
    tls_config_set_protocols(mTLSConfig, TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3);
    tls_configure(mTLS, mTLSConfig);

    if (tls_connect(mTLS, argv[1], argv[2]) != 0)
        {
        const char* error = tls_error(mTLS);
        printf("tls_connect failed: %s\n", error);
        return 2;
        }

    if (tls_handshake(mTLS) != 0)
        {
        const char* error = tls_error(mTLS);
        printf("tls_handshake failed: %s\n", error);
        return 2;
        }

    printf("TLS handhake has sucess\n");
    tls_close(mTLS);
    tls_free(mTLS);
    tls_config_free(mTLSConfig);
    return 0;
}

I built it by:

[developer@2d4e2a14d2bc data]# g++ /data/libressl-handshake-test/main.cpp -o handshake-test -I/data/libressl/include -L/data/libressl/lib/ -Wl,-rpath /data/libressl/lib -lcrypto -lssl -ltls

I linked it with different version of LibreSSL (v3.5.3, v3.6.3, v3.7.3, v3.8.2), but handshake to this server has failed with every tested version:

[developer@2d4e2a14d2bc data]# ./handshake-test api.communicalia.com 443
tls_handshake failed: handshake failed: error:1400A09F:SSL routines:CONNECT_CR_CERT_REQ:length mismatch

I found in curl output, that server requires client certificate:

curl -v https://api.communicalia.com
*   Trying 52.50.170.116:443...
* Connected to api.communicalia.com (52.50.170.116) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=*.communicalia.com
*  start date: Mar  9 00:00:00 2023 GMT
*  expire date: Apr  8 23:59:59 2024 GMT
*  subjectAltName: host "api.communicalia.com" matched cert's "*.communicalia.com"
*  issuer: C=ES; ST=Illes Balears; L=Manacor; O=Soluciones Corporativas IP, SL; CN=Don Dominio / MrDomain RSA DV CA
*  SSL certificate verify ok.

Handshake failed with TLSv1.3. When I downgrade tls version to 1.2, handshake successes.

Why does handshake fail in connection to this server? It is bug in implementation of TLSv1.3 in LibreSSL?

Thanks, Jakub

botovq commented 11 months ago

The error you are hitting is this error in ssl_tlsext_ocsp_client_parse():

    if (ssl_effective_tls_version(s) >= TLS1_3_VERSION) {
        if (msg_type == SSL_TLSEXT_MSG_CR) {
            /*
             * RFC 8446, 4.4.2.1 - the server may request an OCSP
             * response with an empty status_request.
             */
            if (CBS_len(cbs) == 0)
                return 1;

            SSLerror(s, SSL_R_LENGTH_MISMATCH);
            return 0;
        }
    ...

If you comment out the SSLerror and the return 0 the handshake completes. The point is that the server sends its OCSP response data as part of the CertificateRequest.

However, RFC 8446, section 4.4.2.1 says this:

   In TLS 1.3, the server's OCSP information is carried in an extension in 
   the CertificateEntry containing the associated certificate.

The definition of the CertificateRequest does not mention CertificateEntry . There is the exception mentioned in the comment that the server may request an OCSP status for the client's cert by sending an empty OCSP extension as part of the CertificateRequest.

Now we seem to be stricter than OpenSSL who complete the handshake, but gnutls-cli doesn't seem to be able to connect to this server either:

$ gnutls-cli --ocsp -V api.communicalia.com
Processed 134 CA certificate(s).
Resolving 'api.communicalia.com:443'...
Connecting to '52.50.170.116:443'...
*** Fatal error: Error decoding the received TLS packet.

So I'd argue this is the server misbehaving, not a LibreSSL bug.

Added: Running gnutls-cli with -d 3, we see this:

...
|<3>| ASSERT: buffers.c[get_last_packet]:1139
|<3>| ASSERT: buffers.c[get_last_packet]:1139
|<3>| ASSERT: tls13/certificate_request.c[parse_cert_extension]:125
|<3>| ASSERT: extv.c[_gnutls_extv_parse]:71
|<3>| ASSERT: tls13/certificate_request.c[_gnutls13_recv_certificate_request_int]:201
|<3>| ASSERT: handshake-tls13.c[_gnutls13_handshake_client]:123
*** Fatal error: Error decoding the received TLS packet.

Which points at this code in tls13/certificate_request.c:

    } else if (tls_id == ext_mod_status_request.tls_id) {
        if (data_size != 0)
            return gnutls_assert_val(
                GNUTLS_E_TLS_PACKET_DECODING_ERROR);

which is precisely the same check as we have.

jpavco-quadient commented 11 months ago

I will turn to server's provider. Thank you very much for your help.

botovq commented 11 months ago

I don't know what TLS library the server provider is running, but I can reproduce this with a LibreSSL-backed Apache http2 server, so this may well involve a LibreSSL server-side bug. I can work around it, but then there are other issues...

I'll get back to you when I have time to look more deeply into it.