wolfssl icon indicating copy to clipboard operation
wolfssl copied to clipboard

[Bug]: ECHConfig decoding doesn't seem to consider ``maximum_name_length`` field

Open sftcd opened this issue 2 years ago • 11 comments

Contact Details

github is fine

Version

cloned from master last week

Description

Looks like there's an error in the decoding of ECHConfig structures, when those contain a non-zero maximum_name_length field. The relevant bit of spec here is:

       struct {
           uint8 config_id;
           HpkeKemId kem_id;
           HpkePublicKey public_key;
           HpkeSymmetricCipherSuite cipher_suites<4..2^16-4>;
       } HpkeKeyConfig;

       struct {
           HpkeKeyConfig key_config;
           uint8 maximum_name_length;
           opaque public_name<1..255>;
           Extension extensions<0..2^16-1>;
       } ECHConfigContents;

Problem is here where wolfssl assumes the public_name length is two octets when it's only one, preceeded by a one octet maximum_name_length that most people do set to zero (sensibly). Usefully however (for test purposes) tls-ech.dev publishes an ECHConfig with a non-zero maximum_name_length which triggers a invalid read error.

The fix to decode this correctly is pretty obvious. ('case it helps at all, I agree the existence of the maximum_name_length field is just silly;-).

Reproduction steps

If you use my ECH-enabled cURL build (HOWTO) then you can see the error if you run using valgrind:

$ $ LD_LIBRARY_PATH=lib/.libs:src/.libs:$HOME/code/wolfssl/lib  valgrind --leak-check=full src/.libs/curl  -vvv https://tls-ech.dev

The resulting output is below, with the valgrind errors at the end.

Relevant log output

$ LD_LIBRARY_PATH=lib/.libs:src/.libs:$HOME/code/wolfssl/lib  valgrind --leak-check=full src/.libs/curl  -vvv https://tls-ech.dev
==30703== Memcheck, a memory error detector
==30703== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==30703== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==30703== Command: src/.libs/curl -vvv https://tls-ech.dev
==30703== 
* !!! WARNING !!!
* This is a debug build of libcurl, do not use in production.
* STATE: INIT => CONNECT handle 0x4e42d58; line 1948
* Added connection 0. The cache now contains 1 members
* STATE: CONNECT => RESOLVING handle 0x4e42d58; line 1991
* STATE: INIT => CONNECT handle 0x4e61558; line 1948
* Added connection 1. The cache now contains 2 members
* STATE: CONNECT => RESOLVING handle 0x4e61558; line 1991
* STATE: INIT => CONNECT handle 0x4e62d28; line 1948
* Found bundle for host: 0x4e6aa28 [serially]
* Added connection 2. The cache now contains 3 members
* STATE: CONNECT => RESOLVING handle 0x4e62d28; line 1991
* STATE: INIT => CONNECT handle 0x4e64558; line 1948
* Found bundle for host: 0x4e6aa28 [serially]
* Added connection 3. The cache now contains 4 members
* STATE: CONNECT => RESOLVING handle 0x4e64558; line 1991
* STATE: RESOLVING => CONNECTING handle 0x4e62d28; line 2065
*   Trying [2606:4700:4700::1001]:443...
* Hostname 'one.one.one.one' was found in DNS cache
* STATE: RESOLVING => CONNECTING handle 0x4e64558; line 2065
*   Trying [2606:4700:4700::1001]:443...
* Hostname 'one.one.one.one' was found in DNS cache
* STATE: RESOLVING => CONNECTING handle 0x4e61558; line 2065
*   Trying [2606:4700:4700::1001]:443...
* Connected to one.one.one.one (2606:4700:4700::1001) port 443
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* ALPN: curl offers http/1.1
* Didn't find Session ID in cache for host HTTPS://one.one.one.one:443
* Connected to one.one.one.one (2606:4700:4700::1001) port 443
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* ALPN: curl offers http/1.1
* Didn't find Session ID in cache for host HTTPS://one.one.one.one:443
* Connected to one.one.one.one (2606:4700:4700::1001) port 443
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* ALPN: curl offers http/1.1
* Didn't find Session ID in cache for host HTTPS://one.one.one.one:443
* ALPN: server accepted http/1.1
* SSL connection using TLSv1.3 / TLS13-AES128-GCM-SHA256
* Didn't find Session ID in cache for host HTTPS://one.one.one.one:443
* Added Session ID to cache for HTTPS://one.one.one.one:443 [server]
* using HTTP/1.1
* STATE: CONNECTING => PROTOCONNECT handle 0x4e62d28; line 2109
* STATE: PROTOCONNECT => DO handle 0x4e62d28; line 2139
> POST /dns-query HTTP/1.1
Host: one.one.one.one
Accept: */*
Content-Type: application/dns-message
Content-Length: 29

* STATE: DO => DID handle 0x4e62d28; line 2233
* STATE: DID => PERFORMING handle 0x4e62d28; line 2351
* ALPN: server accepted http/1.1
* SSL connection using TLSv1.3 / TLS13-AES128-GCM-SHA256
* Found Session ID in cache for host HTTPS://one.one.one.one:443
* old SSL session ID is stale, removing
* Added Session ID to cache for HTTPS://one.one.one.one:443 [server]
* using HTTP/1.1
* STATE: CONNECTING => PROTOCONNECT handle 0x4e64558; line 2109
* STATE: PROTOCONNECT => DO handle 0x4e64558; line 2139
> POST /dns-query HTTP/1.1
Host: one.one.one.one
Accept: */*
Content-Type: application/dns-message
Content-Length: 29

* STATE: DO => DID handle 0x4e64558; line 2233
* STATE: DID => PERFORMING handle 0x4e64558; line 2351
* ALPN: server accepted http/1.1
* SSL connection using TLSv1.3 / TLS13-AES128-GCM-SHA256
* Found Session ID in cache for host HTTPS://one.one.one.one:443
* old SSL session ID is stale, removing
* Added Session ID to cache for HTTPS://one.one.one.one:443 [server]
* using HTTP/1.1
* STATE: CONNECTING => PROTOCONNECT handle 0x4e61558; line 2109
* STATE: PROTOCONNECT => DO handle 0x4e61558; line 2139
> POST /dns-query HTTP/1.1
Host: one.one.one.one
Accept: */*
Content-Type: application/dns-message
Content-Length: 29

* STATE: DO => DID handle 0x4e61558; line 2233
* STATE: DID => PERFORMING handle 0x4e61558; line 2351
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 200 OK
< Server: cloudflare
< Date: Sun, 24 Sep 2023 14:36:31 GMT
< Content-Type: application/dns-message
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Content-Length: 122
< CF-RAY: 80bbc298d9e156fc-DUB
< 
* STATE: PERFORMING => DONE handle 0x4e62d28; line 2550
* multi_done[DONE]: status: 0 prem: 0 done: 0
* Connection #2 to host one.one.one.one left intact
* Expire cleared
* a DoH request is completed, 2 to go
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 200 OK
< Server: cloudflare
< Date: Sun, 24 Sep 2023 14:36:31 GMT
< Content-Type: application/dns-message
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Content-Length: 123
< CF-RAY: 80bbc29958c756ed-DUB
< 
* STATE: PERFORMING => DONE handle 0x4e64558; line 2550
* multi_done[DONE]: status: 0 prem: 0 done: 0
* Connection #3 to host one.one.one.one left intact
* Expire cleared
* a DoH request is completed, 1 to go
* HTTP 1.1 or later with persistent connection
< HTTP/1.1 200 OK
< Server: cloudflare
< Date: Sun, 24 Sep 2023 14:36:31 GMT
< Content-Type: application/dns-message
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Content-Length: 45
< CF-RAY: 80bbc299cffa9569-DUB
< 
* STATE: PERFORMING => DONE handle 0x4e61558; line 2550
* multi_done[DONE]: status: 0 prem: 0 done: 0
* Connection #1 to host one.one.one.one left intact
* Expire cleared
* a DoH request is completed, 0 to go
* DoH Host name: tls-ech.dev
* TTL: 60 seconds
* DoH A: 34.138.246.121
* DoH HTTPS:
* 	0001000005004B0049FE0D00452B00200020015881D41A3E2EF8F2208185DC479245D20624DDD0918A8056F2E26AF47E26280008000100010001000340127075626C69632E746C732D6563682E6465760000
* Some HTTPS RR to process
* HTTPS_RR: priority 1, target: .
* HTTPS RR: no alpns
* HTTPS RR: no_def_alpn not set
* HTTPS RR: no ipv4hints
* HTTPS RR: ECHConfigList of length 75
* 	0049FE0D00452B00200020015881D41A3E2EF8F2208185DC479245D20624DDD0918A8056F2E26AF47E26280008000100010001000340127075626C69632E746C732D6563682E6465760000
* HTTPS RR: no ipv6hints
* STATE: RESOLVING => CONNECTING handle 0x4e42d58; line 2065
*   Trying 34.138.246.121:443...
* Connected to tls-ech.dev (34.138.246.121) port 443
* successfully set certificate verify locations:
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* ALPN: curl offers http/1.1
* Didn't find Session ID in cache for host HTTPS://tls-ech.dev:443
* ECH: ECHConfig from DoH HTTPS RR
==30703== Invalid read of size 1
==30703==    at 0x484DAFD: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==30703==    by 0x4C6D4AE: wolfSSL_SetEchConfigs (ssl.c:791)
==30703==    by 0x4902547: wolfssl_connect_step1 (wolfssl.c:764)
==30703==    by 0x4903862: wolfssl_connect_common (wolfssl.c:1300)
==30703==    by 0x4903ABD: wolfssl_connect_nonblocking (wolfssl.c:1387)
==30703==    by 0x48FD4C7: ssl_connect_nonblocking (vtls.c:375)
==30703==    by 0x48FFC73: ssl_cf_connect (vtls.c:1536)
==30703==    by 0x4870FDE: Curl_conn_cf_connect (cfilters.c:296)
==30703==    by 0x4875BA9: cf_setup_connect (connect.c:1206)
==30703==    by 0x4870FDE: Curl_conn_cf_connect (cfilters.c:296)
==30703==    by 0x486B0DF: cf_hc_baller_connect (cf-https-connect.c:135)
==30703==    by 0x486B86B: cf_hc_connect (cf-https-connect.c:290)
==30703==  Address 0x73426db is 0 bytes after a block of size 91 alloc'd
==30703==    at 0x4848A13: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==30703==    by 0x48B142F: curl_dbg_calloc (memdebug.c:175)
==30703==    by 0x4882A10: Curl_doh_decode_httpsrr (doh.c:1158)
==30703==    by 0x48832FC: Curl_doh_is_resolved (doh.c:1320)
==30703==    by 0x489CB80: Curl_resolv_check (hostip.c:1318)
==30703==    by 0x48BDCEF: multi_runsingle (multi.c:2038)
==30703==    by 0x48BF53B: curl_multi_perform (multi.c:2742)
==30703==    by 0x48863E6: easy_transfer (easy.c:682)
==30703==    by 0x488666B: easy_perform (easy.c:772)
==30703==    by 0x48866D6: curl_easy_perform (easy.c:791)
==30703==    by 0x131489: serial_transfers (tool_operate.c:2499)
==30703==    by 0x131AA9: run_all_transfers (tool_operate.c:2690)
==30703== 
==30703== Invalid read of size 1
==30703==    at 0x484DAF0: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==30703==    by 0x4C6D4AE: wolfSSL_SetEchConfigs (ssl.c:791)
==30703==    by 0x4902547: wolfssl_connect_step1 (wolfssl.c:764)
==30703==    by 0x4903862: wolfssl_connect_common (wolfssl.c:1300)
==30703==    by 0x4903ABD: wolfssl_connect_nonblocking (wolfssl.c:1387)
==30703==    by 0x48FD4C7: ssl_connect_nonblocking (vtls.c:375)
==30703==    by 0x48FFC73: ssl_cf_connect (vtls.c:1536)
==30703==    by 0x4870FDE: Curl_conn_cf_connect (cfilters.c:296)
==30703==    by 0x4875BA9: cf_setup_connect (connect.c:1206)
==30703==    by 0x4870FDE: Curl_conn_cf_connect (cfilters.c:296)
==30703==    by 0x486B0DF: cf_hc_baller_connect (cf-https-connect.c:135)
==30703==    by 0x486B86B: cf_hc_connect (cf-https-connect.c:290)
==30703==  Address 0x73426dc is 1 bytes after a block of size 91 alloc'd
==30703==    at 0x4848A13: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==30703==    by 0x48B142F: curl_dbg_calloc (memdebug.c:175)
==30703==    by 0x4882A10: Curl_doh_decode_httpsrr (doh.c:1158)
==30703==    by 0x48832FC: Curl_doh_is_resolved (doh.c:1320)
==30703==    by 0x489CB80: Curl_resolv_check (hostip.c:1318)
==30703==    by 0x48BDCEF: multi_runsingle (multi.c:2038)
==30703==    by 0x48BF53B: curl_multi_perform (multi.c:2742)
==30703==    by 0x48863E6: easy_transfer (easy.c:682)
==30703==    by 0x488666B: easy_perform (easy.c:772)
==30703==    by 0x48866D6: curl_easy_perform (easy.c:791)
==30703==    by 0x131489: serial_transfers (tool_operate.c:2499)
==30703==    by 0x131AA9: run_all_transfers (tool_operate.c:2690)
==30703== 
* ECH: imported ECHConfigList of length 75
* SSL_connect failed with error -313: received alert fatal error
* multi_done[CONNECTING]: status: 35 prem: 1 done: 0
* multi_done, not reusing connection=0, forbid=0, close=0, premature=1, conn_multiplex=0
* The cache now contains 3 members
* Curl_disconnect(conn #0, dead=1)
* Closing connection
==30703== 
==30703== HEAP SUMMARY:
==30703==     in use at exit: 0 bytes in 0 blocks
==30703==   total heap usage: 37,063 allocs, 37,063 frees, 7,614,829 bytes allocated
==30703== 
==30703== All heap blocks were freed -- no leaks are possible
==30703== 
==30703== For lists of detected and suppressed errors, rerun with: -s
==30703== ERROR SUMMARY: 14094 errors from 2 contexts (suppressed: 0 from 0)

sftcd avatar Sep 24 '23 15:09 sftcd

Hey @sftcd,

I updated my pr to handle the public name length correctly, we can discuss it but I wanted to mark it here.

Best Wishes, John Bland

jpbland1 avatar Sep 27 '23 00:09 jpbland1

Now www.facebook.com sets maximum_name_length=100, this bug causes a lot of trouble (guess what). I think this should be fixed quickly.

tatsuhiro-t avatar Jan 20 '24 02:01 tatsuhiro-t

On 20/01/2024 02:12, Tatsuhiro Tsujikawa wrote:

Now www.facebook.com sets maximum_name_length=100, this bug causes a lot of trouble (guess what). I think this should be fixed quickly.

I don't see an HTTPS RR for www.facebook.com - where are you seeing one with an ECHConfig with a max name length? (Not that I'm involved with wolfssl, but I'd like to test myself.)

Ta, S.

sftcd avatar Jan 20 '24 02:01 sftcd

I saw it when I accessed facebook with wsslclient from ngtcp2, that is QUIC client. So is it a deployment issue on facebook side? Anyway facebook sends ECH and wolfssl tries to process it.

tatsuhiro-t avatar Jan 20 '24 02:01 tatsuhiro-t

Is it maybe using some other port rather than 443 perhaps?

sftcd avatar Jan 20 '24 02:01 sftcd

It is 443, but UDP.

tatsuhiro-t avatar Jan 20 '24 02:01 tatsuhiro-t

Ah, I don't have any way to test QUIC myself. Can you see the HTTPS RR in DNS that contains the ECHConfig?

sftcd avatar Jan 20 '24 02:01 sftcd

I do not see HTTPS RR too. After tracing wolfssl code, it looks like it is greasing ECH: https://github.com/wolfSSL/wolfssl/blob/d043333bee254f3fe94545920e4685114381d875/src/tls.c#L11680

tatsuhiro-t avatar Jan 20 '24 03:01 tatsuhiro-t

I guess maybe some other bug then, but good for some wolfssl person to check. (And if there's a way to check ECH with FB sites, I'd be happy to add that to tests I regularly run.)

sftcd avatar Jan 20 '24 03:01 sftcd

Hey @tatsuhiro-t,

I'm not seeing any ech retry options coming from facebook's end and when when an ECH enabled client doesn't receive any usable configs it will send a grease ECH by default which I assume is what you're seeing. If you could send me a reproducer or some logs that show facebook's ech configs I could assist in this issue.

Best Wishes, John Bland

jpbland1 avatar Jan 22 '24 17:01 jpbland1

Here is the minimum reproducer. Compiled examples/client/client on master branch, then:

$ examples/client/client -h facebook.com -p 443 -v 4 -A /etc/ssl/certs/ca-certificates.crt
...
ECH extension received
TLSX_ECH_Parse
=================================================================
==60347==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x619000013079 at pc 0x55a5ceb013e1 bp 0x7fff3d20f470 sp 0x7fff3d20ec38
    #0 0x55a5ceb013e0 in __asan_memcpy (/path/to/wolfssl/examples/client/.libs/client+0xc73e0) (BuildId: 5c18d04e97abda4484654637bd4c30d12fb0bfa8)
    #1 0x7f5e565de068 in wolfSSL_SetEchConfigs /path/to/wolfssl/src/ssl.c:797:9
    #2 0x7f5e56715387 in TLSX_ECH_Parse /path/to/wolfssl/src/tls.c:11946:15
    #3 0x7f5e5670963a in TLSX_Parse /path/to/wolfssl/src/tls.c:14905:23
    #4 0x7f5e56751832 in DoTls13EncryptedExtensions /path/to/wolfssl/src/tls13.c:5598:16
    #5 0x7f5e5674c031 in DoTls13HandShakeMsgType /path/to/wolfssl/src/tls13.c:12085:15
    #6 0x7f5e5675bd1a in DoTls13HandShakeMsg /path/to/wolfssl/src/tls13.c:12447:15
    #7 0x7f5e564cb717 in ProcessReplyEx /path/to/wolfssl/src/internal.c:21430:31
    #8 0x7f5e564c58c6 in ProcessReply /path/to/wolfssl/src/internal.c:20673:12
    #9 0x7f5e56759af5 in wolfSSL_connect_TLSv13 /path/to/wolfssl/src/tls13.c:12730:35
    #10 0x7f5e565ea0ae in wolfSSL_connect /path/to/wolfssl/src/ssl.c:12782:32
    #11 0x55a5ceb41525 in client_test /path/to/wolfssl/examples/client/client.c:3867:19
    #12 0x55a5ceb4b9f4 in main /path/to/wolfssl/examples/client/client.c:4657:9
    #13 0x7f5e55e456c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #14 0x7f5e55e45784 in __libc_start_main csu/../csu/libc-start.c:360:3
    #15 0x55a5cea67560 in _start (/path/to/wolfssl/examples/client/.libs/client+0x2d560) (BuildId: 5c18d04e97abda4484654637bd4c30d12fb0bfa8)

In addition to the incorrect parsing of length field, the current code does not check the input buffer has enough length before accessing that space which is very dangerous.

tatsuhiro-t avatar Mar 24 '24 12:03 tatsuhiro-t