From 1485aa278cfc1d8640994be100aa5984f596e773 Mon Sep 17 00:00:00 2001 From: Alexander Sosedkin Date: Mon, 27 Apr 2026 15:39:24 +0200 Subject: [PATCH] Fix 3.8.13 CVEs and security issues, update Makefile.in - Fix CVE-2026-33846 - Fix CVE-2026-42009 - Fix CVE-2026-33845 - Fix CVE-2026-42010 - Fix CVE-2026-3833 - Fix CVE-2026-42011 - Fix CVE-2026-42012 - Fix CVE-2026-42013 - Fix CVE-2026-42014 - Fix CVE-2026-5260 - Fix CVE-2026-42015 - Fix CVE-2026-3832 - Fix CVE-2026-5419 - Fix upstream security issue #1808 - Fix upstream security issue #1810 - Fix upstream security issue #1813 - Fix upstream security issue #1818 - Fix upstream security issue #1818 - Fix upstream security issue #1819 - Fix upstream security issue #1822 - Fix upstream security issue #1841 - Fix upstream security issue #1823 - Fix upstream security issue #1817 - Fix upstream security issue #1820 - gnutls-3.8.10-CVE-2025-9820.patch: update Makefile.in Resolves: RHEL-159071 Resolves: RHEL-159066 Resolves: RHEL-154340 --- gnutls-3.8.10-1808-psk-rehandshake.patch | 212 +++ gnutls-3.8.10-1810-ocsp-truncated-eku.patch | 1423 +++++++++++++++++ gnutls-3.8.10-1813-p11p-aes-ephemeral.patch | 25 + gnutls-3.8.10-1817-security-parameters.patch | 35 + gnutls-3.8.10-1818-pem-parsing.patch | 116 ++ gnutls-3.8.10-1818-rsa-coprime.patch | 39 + gnutls-3.8.10-1819-dblfree-mid-import.patch | 60 + gnutls-3.8.10-1820-p11p-kdf.patch | 61 + gnutls-3.8.10-1822-sct-overread.patch | 59 + gnutls-3.8.10-1823-cfg-clear-options.patch | 62 + gnutls-3.8.10-1841-hybrid-kx-zeroize.patch | 80 + gnutls-3.8.10-CVE-2025-9820.patch | 257 ++- gnutls-3.8.10-CVE-2026-33845-dtls-uflow.patch | 470 ++++++ gnutls-3.8.10-CVE-2026-33846-dtls-len.patch | 851 ++++++++++ gnutls-3.8.10-CVE-2026-3832-ocsp-rev-0.patch | 372 +++++ gnutls-3.8.10-CVE-2026-3833-nc-case.patch | 166 ++ gnutls-3.8.10-CVE-2026-42009-dtls-qsort.patch | 95 ++ gnutls-3.8.10-CVE-2026-42010-psk-nul.patch | 443 +++++ ...s-3.8.10-CVE-2026-42011-nc-intersect.patch | 176 ++ gnutls-3.8.10-CVE-2026-42012-url-san-cn.patch | 506 ++++++ ...-3.8.10-CVE-2026-42013-oversized-san.patch | 245 +++ gnutls-3.8.10-CVE-2026-42014-so-pin-uaf.patch | 61 + gnutls-3.8.10-CVE-2026-42015-p12-bag32.patch | 44 + ....8.10-CVE-2026-5260-p11-rsa-overread.patch | 107 ++ ....8.10-CVE-2026-5419-p7-constant-time.patch | 360 +++++ gnutls.spec | 58 +- 26 files changed, 6380 insertions(+), 3 deletions(-) create mode 100644 gnutls-3.8.10-1808-psk-rehandshake.patch create mode 100644 gnutls-3.8.10-1810-ocsp-truncated-eku.patch create mode 100644 gnutls-3.8.10-1813-p11p-aes-ephemeral.patch create mode 100644 gnutls-3.8.10-1817-security-parameters.patch create mode 100644 gnutls-3.8.10-1818-pem-parsing.patch create mode 100644 gnutls-3.8.10-1818-rsa-coprime.patch create mode 100644 gnutls-3.8.10-1819-dblfree-mid-import.patch create mode 100644 gnutls-3.8.10-1820-p11p-kdf.patch create mode 100644 gnutls-3.8.10-1822-sct-overread.patch create mode 100644 gnutls-3.8.10-1823-cfg-clear-options.patch create mode 100644 gnutls-3.8.10-1841-hybrid-kx-zeroize.patch create mode 100644 gnutls-3.8.10-CVE-2026-33845-dtls-uflow.patch create mode 100644 gnutls-3.8.10-CVE-2026-33846-dtls-len.patch create mode 100644 gnutls-3.8.10-CVE-2026-3832-ocsp-rev-0.patch create mode 100644 gnutls-3.8.10-CVE-2026-3833-nc-case.patch create mode 100644 gnutls-3.8.10-CVE-2026-42009-dtls-qsort.patch create mode 100644 gnutls-3.8.10-CVE-2026-42010-psk-nul.patch create mode 100644 gnutls-3.8.10-CVE-2026-42011-nc-intersect.patch create mode 100644 gnutls-3.8.10-CVE-2026-42012-url-san-cn.patch create mode 100644 gnutls-3.8.10-CVE-2026-42013-oversized-san.patch create mode 100644 gnutls-3.8.10-CVE-2026-42014-so-pin-uaf.patch create mode 100644 gnutls-3.8.10-CVE-2026-42015-p12-bag32.patch create mode 100644 gnutls-3.8.10-CVE-2026-5260-p11-rsa-overread.patch create mode 100644 gnutls-3.8.10-CVE-2026-5419-p7-constant-time.patch diff --git a/gnutls-3.8.10-1808-psk-rehandshake.patch b/gnutls-3.8.10-1808-psk-rehandshake.patch new file mode 100644 index 0000000..b848860 --- /dev/null +++ b/gnutls-3.8.10-1808-psk-rehandshake.patch @@ -0,0 +1,212 @@ +From 35dbb0e4ebcc07acecfd060ffc6ca076cf397920 Mon Sep 17 00:00:00 2001 +From: Joshua Rogers +Date: Wed, 18 Mar 2026 17:08:03 +0100 +Subject: [PATCH 1/3] handshake-checks: fix username comparison during + rehandshake + +This is definitely a security issue +subverting the GNUTLS_ALLOW_ID_CHANGE protection, +but its real-life exploitability is under question. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1808 +Signed-off-by: Joshua Rogers +--- + lib/handshake-checks.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/handshake-checks.c b/lib/handshake-checks.c +index 5231046e8..ad92932d7 100644 +--- a/lib/handshake-checks.c ++++ b/lib/handshake-checks.c +@@ -80,10 +80,10 @@ int _gnutls_check_id_for_change(gnutls_session_t session) + + if (session->internals.saved_username && + session->internals.saved_username_size != -1) { +- if (session->internals.saved_username_size == +- username_length && +- strncmp(session->internals.saved_username, username, +- username_length)) { ++ if (session->internals.saved_username_size != ++ username_length || ++ memcmp(session->internals.saved_username, username, ++ username_length)) { + _gnutls_debug_log( + "Session's PSK username changed during rehandshake; aborting!\n"); + return gnutls_assert_val( +-- +2.53.0 + + +From 7153b4668234f2687cab354c418b40624a650567 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 18 Mar 2026 16:08:51 +0100 +Subject: [PATCH 2/3] tests/rehandshake-switch-psk-id: refactor a bit + +Signed-off-by: Alexander Sosedkin +--- + tests/rehandshake-switch-psk-id.c | 46 +++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 21 deletions(-) + +diff --git a/tests/rehandshake-switch-psk-id.c b/tests/rehandshake-switch-psk-id.c +index 726ee06c2..a16048776 100644 +--- a/tests/rehandshake-switch-psk-id.c ++++ b/tests/rehandshake-switch-psk-id.c +@@ -26,7 +26,6 @@ + #include + #include + #include +-#include + #include + #include "utils.h" + #include "eagain-common.h" +@@ -34,6 +33,8 @@ + /* This test checks whether the server switching certificates is detected + * by the client */ + ++#define SIZEOF(array) (sizeof(array) / sizeof(array[0])) ++ + const char *side; + + static void tls_log_func(int level, const char *str) +@@ -41,8 +42,6 @@ static void tls_log_func(int level, const char *str) + fprintf(stderr, "%s|<%d>| %s", side, level, str); + } + +-#include "cert-common.h" +- + static int pskfunc(gnutls_session_t session, const char *username, + gnutls_datum_t *key) + { +@@ -74,6 +73,9 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, + const gnutls_datum_t key = { (void *)"DEADBEEF", 8 }; + int cret = GNUTLS_E_AGAIN; + ++ success("testing: prio=%s kx=%s allow_change=%d\n", prio, ++ gnutls_kx_get_name(kx), allow_change); ++ + /* General init. */ + gnutls_global_set_log_function(tls_log_func); + if (debug) +@@ -163,26 +165,28 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, + + void doit(void) + { ++ const char *prio_list[] = { ++ "NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", ++ "NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", ++ "NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", ++ }; ++ const gnutls_kx_algorithm_t kx_list[] = { ++ GNUTLS_KX_PSK, ++ GNUTLS_KX_DHE_PSK, ++ GNUTLS_KX_ECDHE_PSK, ++ }; ++ assert(SIZEOF(prio_list) == SIZEOF(kx_list)); ++ + global_init(); + +- /* Allow change of ID */ +- try("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", GNUTLS_KX_PSK, 0); +- reset_buffers(); +- try("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", GNUTLS_KX_DHE_PSK, +- 0); +- reset_buffers(); +- try("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", +- GNUTLS_KX_ECDHE_PSK, 0); +- reset_buffers(); ++ /* loop over allowed (0) and disallowed (1) ID change */ ++ for (unsigned allow = 0; allow <= 1; allow++) { ++ /* loop over priority strings/key exchange algorithms */ ++ for (unsigned i = 0; i < SIZEOF(prio_list); i++) { ++ try(prio_list[i], kx_list[i], allow); ++ reset_buffers(); ++ } ++ } + +- /* Prohibit (default) change of ID */ +- try("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", GNUTLS_KX_PSK, 1); +- reset_buffers(); +- try("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", GNUTLS_KX_DHE_PSK, +- 1); +- reset_buffers(); +- try("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", +- GNUTLS_KX_ECDHE_PSK, 1); +- reset_buffers(); + gnutls_global_deinit(); + } +-- +2.53.0 + + +From cb3b9c873555d3e458205cb554153553c7baaaae Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 18 Mar 2026 16:47:43 +0100 +Subject: [PATCH 3/3] tests/rehandshake-switch-psk-id: test usernames of varied + length + +Signed-off-by: Alexander Sosedkin +--- + tests/rehandshake-switch-psk-id.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/tests/rehandshake-switch-psk-id.c b/tests/rehandshake-switch-psk-id.c +index a16048776..84d8b9d67 100644 +--- a/tests/rehandshake-switch-psk-id.c ++++ b/tests/rehandshake-switch-psk-id.c +@@ -57,7 +57,7 @@ static int pskfunc(gnutls_session_t session, const char *username, + } + + static void try(const char *prio, gnutls_kx_algorithm_t kx, +- unsigned allow_change) ++ unsigned allow_change, const char *username) + { + int ret; + /* Server stuff. */ +@@ -73,8 +73,8 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, + const gnutls_datum_t key = { (void *)"DEADBEEF", 8 }; + int cret = GNUTLS_E_AGAIN; + +- success("testing: prio=%s kx=%s allow_change=%d\n", prio, +- gnutls_kx_get_name(kx), allow_change); ++ success("testing: prio=%s kx=%s allow_change=%d username=%s\n", prio, ++ gnutls_kx_get_name(kx), allow_change, username); + + /* General init. */ + gnutls_global_set_log_function(tls_log_func); +@@ -114,7 +114,7 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, + if (ret < 0) + exit(1); + +- gnutls_psk_set_client_credentials(clientpskcred2, "test2", &key, ++ gnutls_psk_set_client_credentials(clientpskcred2, username, &key, + GNUTLS_PSK_KEY_HEX); + + ret = gnutls_init(&client, GNUTLS_CLIENT); +@@ -177,14 +177,21 @@ void doit(void) + }; + assert(SIZEOF(prio_list) == SIZEOF(kx_list)); + ++ /* same length, different length */ ++ const char *usernames[] = { "test2", "test3-but-longer" }; ++ + global_init(); + + /* loop over allowed (0) and disallowed (1) ID change */ + for (unsigned allow = 0; allow <= 1; allow++) { + /* loop over priority strings/key exchange algorithms */ + for (unsigned i = 0; i < SIZEOF(prio_list); i++) { +- try(prio_list[i], kx_list[i], allow); +- reset_buffers(); ++ /* loop over usernames to rehandshake to */ ++ for (unsigned j = 0; j < SIZEOF(usernames); j++) { ++ try(prio_list[i], kx_list[i], allow, ++ usernames[j]); ++ reset_buffers(); ++ } + } + } + +-- +2.53.0 + diff --git a/gnutls-3.8.10-1810-ocsp-truncated-eku.patch b/gnutls-3.8.10-1810-ocsp-truncated-eku.patch new file mode 100644 index 0000000..ae271bd --- /dev/null +++ b/gnutls-3.8.10-1810-ocsp-truncated-eku.patch @@ -0,0 +1,1423 @@ +From 5e29fb9362af35e4950022325bcac18001df58ea Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 19 Mar 2026 19:33:02 +0100 +Subject: [PATCH 1/3] x509/ocsp: check OCSP delegated signer EKU OID to full + length + +The retrieved certificate purpose OID was compared against the expected +1.3.6.1.5.5.7.3.9 value without checking if the lengths were identical, +allowing a value that constitutes a prefix to match. +The check now compares the length as well. + +Reported-by: Joshua Rogers of AISLE Research Team +Co-authored-by: Joshua Rogers of AISLE Research Team +Signed-off-by: Alexander Sosedkin +--- + lib/x509/ocsp.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c +index 8f3423f0a..ae04864d4 100644 +--- a/lib/x509/ocsp.c ++++ b/lib/x509/ocsp.c +@@ -2132,7 +2132,8 @@ static int check_ocsp_purpose(gnutls_x509_crt_t signercert) + return gnutls_assert_val(rc); + } + +- if (memcmp(oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) { ++ if (oidsize != sizeof(GNUTLS_KP_OCSP_SIGNING) - 1 || ++ memcmp(oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) { + gnutls_assert(); + continue; + } +-- +2.53.0 + + +From e3030b7c2ef5546aa80dc228600b860882891c99 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 19 Mar 2026 20:12:19 +0100 +Subject: [PATCH 2/3] tests/ocsp: test against a truncated EKU OID + +Signed-off-by: Alexander Sosedkin +--- + tests/ocsp.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 163 insertions(+) + +diff --git a/tests/ocsp.c b/tests/ocsp.c +index 3f30f6c3d..7e092bb17 100644 +--- a/tests/ocsp.c ++++ b/tests/ocsp.c +@@ -492,6 +492,118 @@ static unsigned char long_resp_str[] = + + gnutls_datum_t long_resp = { long_resp_str, sizeof(long_resp_str) - 1 }; + ++/* EKU 1.3.6.1.5.5.7.3, not 1.3.6.1.5.5.7.3.9 (OCSPSigning) */ ++static unsigned char truncated_eku_pem[] = ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIC9DCCAdygAwIBAgIUCNcWsK6OPsX+aQNLkOm8lI8hmGAwDQYJKoZIhvcNAQEL\n" ++ "BQAwEjEQMA4GA1UEAxMHVGVzdCBDQTAeFw0xMjAxMDEwMDAwMDBaFw0xMzAxMDEw\n" ++ "MDAwMDBaMBIxEDAOBgNVBAMTB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" ++ "DwAwggEKAoIBAQDLvyWviDcP+xW8hAUIVssJ8e7SX0l3L3s/RyxEtK8Q7KpcDu4F\n" ++ "s3aM+6kH8DBFEW5a7kXM+swpQ0DmPhn4KNDP1l1fLlEhmTTasF0Z7HDK3m8IGUGe\n" ++ "NZr5V6ETIwYQz3uClfMrSNLxIGFa5LqviK3dvFiWYCTo2ID5sQ0M3R97/6By/H6A\n" ++ "uyC+OCJhnOXXG1I4psjwiM7AZdWvIbBaNG2KOCQvoe2Y0hB5mlwphPfd7vIJ74OY\n" ++ "fGuPjm7uw4RqQ5bzBd92wC2B/eyxPDjJYb6V0Q8PY5IGsimGJ2shA3EH+SF/f32S\n" ++ "sloD4c/GwxlAaOhAH1SEdp1BxS+v5v0SJHltAgMBAAGjQjBAMA8GA1UdEwEB/wQF\n" ++ "MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTQruchifR+BQ8EueuuaR6h\n" ++ "pBEQ9zANBgkqhkiG9w0BAQsFAAOCAQEADV8KFNK8yTe88asQwtX9dYaNE6ubeSjm\n" ++ "LQCjfupkrZU41XCBtu40LadkcozgtR8VMHcLLR65O2xo8V2RTFuu8HZvFJ8FGeN3\n" ++ "zCpC0yTxFZZOD15qBfgLBfESobSSIrvz4aHEH6kln5Xq0HkbSPOxfFUSx5frJZbH\n" ++ "H2gPoxbANba+ORkRPeQtdOAtY8is3549msllCydcM1cTbL1ejUXfv2WpEMnPqAtI\n" ++ "tkDE3lasja7buwpR1FZFgEc73RSI8bjWABvvi4rNs53m+JcE5nnMKpw9IPHgwgoe\n" ++ "It4uaS8yZ44YkKyMHb/AMFtmS91NI3eJZulZito16E1hcKUZJfZmCQ==\n" ++ "-----END CERTIFICATE-----\n"; ++ ++const gnutls_datum_t truncated_eku_data = { truncated_eku_pem, ++ sizeof(truncated_eku_pem) }; ++ ++static const char truncated_eku_resp_str[] = ++ "\x30\x82\x05\x09\x0a\x01\x00\xa0\x82\x05\x02\x30\x82\x04\xfe\x06" ++ "\x09\x2b\x06\x01\x05\x05\x07\x30\x01\x01\x04\x82\x04\xef\x30\x82" ++ "\x04\xeb\x30\x81\xa9\xa1\x1d\x30\x1b\x31\x19\x30\x17\x06\x03\x55" ++ "\x04\x03\x13\x10\x54\x65\x73\x74\x20\x4f\x43\x53\x50\x20\x53\x69" ++ "\x67\x6e\x65\x72\x18\x0f\x32\x30\x31\x32\x30\x33\x32\x34\x30\x30" ++ "\x30\x30\x30\x30\x5a\x30\x77\x30\x75\x30\x4d\x30\x09\x06\x05\x2b" ++ "\x0e\x03\x02\x1a\x05\x00\x04\x14\x02\xff\x75\xda\x24\xde\x8a\xdd" ++ "\x15\x0f\xab\x68\x9d\xcc\xe6\xe6\x63\x6d\x09\x01\x04\x14\x94\xca" ++ "\xbc\xfc\xa8\x14\x0c\xf4\x7d\xeb\x57\x6c\xfa\x33\x39\x0c\x18\x54" ++ "\xf9\xca\x02\x14\x37\xf6\x6c\x75\x5c\xbd\xd9\x85\x7f\x67\xf4\xc8" ++ "\xf1\xda\x70\x5d\x9c\x3d\x7e\x91\x80\x00\x18\x0f\x32\x30\x31\x32" ++ "\x30\x33\x32\x34\x30\x30\x30\x30\x30\x30\x5a\xa0\x11\x18\x0f\x32" ++ "\x30\x31\x33\x30\x33\x32\x34\x30\x36\x30\x30\x30\x30\x5a\x30\x0d" ++ "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01" ++ "\x01\x00\x04\x67\xc1\x2e\xdd\xaf\x61\xd3\xd5\x5b\xa1\x64\x9b\x34" ++ "\x70\x79\xf9\xf8\x48\x94\xb2\x04\x4d\x96\xe3\xb6\x40\x60\x96\x87" ++ "\x7b\xc1\x8a\xe1\x19\x38\xd4\x9a\x1e\x40\x84\x0d\x7f\xd0\x68\xbc" ++ "\x0d\x28\xef\x84\xd3\xec\xba\x84\x2a\x36\x6e\x61\x74\xef\xd8\xb8" ++ "\x36\x4d\xe2\xd6\x10\x45\xab\x19\x1d\xc4\x44\x88\xd2\xff\xbd\x0e" ++ "\x20\x0e\x37\xa4\xd6\xcf\x9a\x18\x9c\xe8\xdf\x6b\x0a\x47\xc5\x2d" ++ "\xf1\x78\x14\x37\xdf\x09\xfb\xc7\x0e\x46\x09\x1b\xc1\xba\x9e\x11" ++ "\x97\x0e\x85\x58\xb8\xae\xcd\x11\x66\xaa\x3c\x87\x1c\x3a\x28\x81" ++ "\xfa\x20\xd2\x67\xae\x5b\xa6\xee\x55\x2a\xed\xb5\xa2\xf8\xd6\x06" ++ "\xa4\x2a\x6b\x5a\x2b\xfa\x36\xea\xbd\x28\xac\x15\xd1\x7c\xf7\x4a" ++ "\xe8\x21\xd0\xce\x2d\xec\x71\x4d\xed\x7c\x3d\xdf\x6f\xb2\x91\xa0" ++ "\xef\x70\x05\x82\x84\xc4\x12\xc2\xd6\xcb\xab\xc1\xca\x90\x59\x7c" ++ "\x24\x12\xc8\x23\x7a\x67\x72\xe2\xe2\x18\x92\x71\x96\xc8\x3a\xa6" ++ "\x25\x97\x92\xe3\xdf\xe7\xd8\x43\x0a\x25\x40\x9c\xc5\xe5\x01\xbf" ++ "\x8e\x86\xb6\xaa\xad\x7a\xc6\x36\xb2\xba\xaf\xe4\x81\xa7\x19\xd1" ++ "\x88\x46\x81\x20\x7b\xd2\xd5\x6a\xf4\x0c\xc7\x7f\x46\x71\xc3\xdc" ++ "\xd1\xd9\xa0\x82\x03\x27\x30\x82\x03\x23\x30\x82\x03\x1f\x30\x82" ++ "\x02\x07\xa0\x03\x02\x01\x02\x02\x14\x13\xab\x70\xa7\x64\xed\x5d" ++ "\x40\x9e\x84\xba\x62\x79\x19\xbf\xfe\xec\x6a\xe9\x8b\x30\x0d\x06" ++ "\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x30\x12\x31\x10" ++ "\x30\x0e\x06\x03\x55\x04\x03\x13\x07\x54\x65\x73\x74\x20\x43\x41" ++ "\x30\x1e\x17\x0d\x31\x32\x30\x31\x30\x31\x30\x30\x30\x30\x30\x30" ++ "\x5a\x17\x0d\x31\x33\x30\x31\x30\x31\x30\x30\x30\x30\x30\x30\x5a" ++ "\x30\x1b\x31\x19\x30\x17\x06\x03\x55\x04\x03\x13\x10\x54\x65\x73" ++ "\x74\x20\x4f\x43\x53\x50\x20\x53\x69\x67\x6e\x65\x72\x30\x82\x01" ++ "\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" ++ "\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xc8\x85" ++ "\x05\x8c\x2a\x01\x95\x82\x7b\x28\x69\x48\xf1\x36\xa0\xd0\x9a\x4c" ++ "\x31\xdd\x01\x20\xb8\x5a\x36\xc5\xf5\x24\x4a\xcb\x21\x70\x87\x66" ++ "\x92\xcc\xac\x80\xda\x77\x9d\xe5\x9f\x35\xf0\xf7\x6e\xf3\x54\x4b" ++ "\xd9\x00\x7d\xca\xd2\x0b\x26\xd3\x2b\xea\x03\x99\x18\x99\xe4\x17" ++ "\x9b\x0c\xdd\x36\xc8\x69\xc4\x4a\xa2\x37\x04\x26\xfe\xef\xfe\x41" ++ "\x56\xfc\x93\xf4\xc9\x42\x45\xc4\x41\xb3\xbf\x31\x71\x43\xb3\x4e" ++ "\x50\x09\x9e\x87\xa4\x97\x05\x88\x72\xe2\x92\x81\xc9\xb9\x18\x9f" ++ "\x0b\x5c\x7f\xe4\xec\x57\x85\xbd\x84\x3e\x3c\xc1\xc0\x13\xaa\xb4" ++ "\x93\x4c\x22\xc3\x24\x9a\x99\x4d\x57\xc6\xdb\x2b\x19\xb4\x01\x75" ++ "\xf7\xa6\xe2\xf1\x5d\x75\x87\xac\xff\x43\x38\xac\x7f\x22\x40\xca" ++ "\xb2\xc9\x3d\x63\x2c\x0a\xb8\xb5\x42\xed\xa7\x53\x7e\x15\x5f\x54" ++ "\xe6\x16\x15\xde\xbe\x80\x81\x81\xe8\x8b\xbd\xc8\x6a\x06\xb6\x12" ++ "\xf8\x99\xae\x9d\x75\x38\xe0\xf4\x6e\x18\xae\xee\x0d\x4c\xa1\xe4" ++ "\xf3\xd3\xde\x8b\x0b\x79\xb0\xf3\x54\xa5\x02\xd4\x28\x6d\xfe\xc7" ++ "\x52\xf8\x5c\xc2\xe2\xe5\x08\x69\xec\x5c\xb4\x16\x46\x52\xe3\x7d" ++ "\x04\xf4\x95\xe3\x9a\x42\x00\x86\x5f\xa0\x49\x32\x1e\x7b\x02\x03" ++ "\x01\x00\x01\xa3\x64\x30\x62\x30\x12\x06\x03\x55\x1d\x25\x04\x0b" ++ "\x30\x09\x06\x07\x2b\x06\x01\x05\x05\x07\x03\x30\x0c\x06\x03\x55" ++ "\x1d\x13\x01\x01\xff\x04\x02\x30\x00\x30\x1d\x06\x03\x55\x1d\x0e" ++ "\x04\x16\x04\x14\x92\x21\x8c\x48\x87\x79\xcc\x6c\x8f\x92\x79\xbf" ++ "\xef\xde\x21\xd8\xd4\x3b\x6c\x3e\x30\x1f\x06\x03\x55\x1d\x23\x04" ++ "\x18\x30\x16\x80\x14\xd0\xae\xe7\x21\x89\xf4\x7e\x05\x0f\x04\xb9" ++ "\xeb\xae\x69\x1e\xa1\xa4\x11\x10\xf7\x30\x0d\x06\x09\x2a\x86\x48" ++ "\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x14\x52\xe8" ++ "\x26\x09\xaa\x79\x7e\x6a\x11\xc6\x46\xc9\x75\xb2\xfa\x7b\xd2\xb2" ++ "\x9e\x7f\xa2\x85\x0f\xb8\x67\xe4\x8a\x6f\xcc\x54\xae\xa7\x46\xc6" ++ "\xc4\x97\x4b\x54\xf7\xf3\x7d\x94\x72\x25\xb9\x2f\x70\x75\x2a\x15" ++ "\xbd\x77\x1c\x36\x7f\xb9\x68\x4e\x41\x8f\x28\xdb\x75\xce\x6b\xa7" ++ "\x88\xcb\x03\x8b\xd7\x46\x55\xfd\x07\xcc\xc0\xe2\x9e\x9b\x9a\x02" ++ "\xd7\x77\xb0\x9a\x1e\xe9\xf9\x46\x74\x16\x9c\xfe\xb9\x38\x0d\x55" ++ "\x1b\xae\xf9\x0e\xe2\x1c\x25\xc2\x3c\x60\x47\xb5\xfa\x89\xba\xc4" ++ "\x2e\x50\x09\x1d\x74\xb8\x5a\xb6\x2a\xc1\x13\xf6\xdc\xc4\x02\x4b" ++ "\x37\xbf\x06\xe7\xdf\x86\xb7\xbb\x82\xe6\x82\x59\x5a\x28\xc5\x95" ++ "\xd4\x4f\x30\x85\x09\x5c\x75\x5d\x9a\xdc\x81\xf7\x0e\xe5\x4c\xaf" ++ "\xe4\xac\x25\x94\xb8\xc7\x11\xba\x80\xf2\xbe\xaf\x18\x00\x74\xa6" ++ "\x2d\x6b\xc9\x35\xa6\xec\x51\x64\x1d\x08\x9d\xcb\xc7\x18\x06\x1a" ++ "\xd5\x19\xfa\x88\x33\x94\xf4\x51\xdb\xba\xe4\x2b\x11\x01\x4b\xdd" ++ "\xfa\x8c\xd8\xf7\x47\x30\x1d\x7c\x10\xba\x99\x12\xa0\x2b\xbe\x6b" ++ "\xda\x4a\x05\xed\x05\x4e\x87\x6b\x0e\xa5\xc7\x14\x2a\xf2\xdf\x4f" ++ "\x34\x42\x98\x7f\x23\xcb\x44\xa5\xe2\x70\xb2\xe4\x3f"; ++ ++const gnutls_datum_t truncated_eku_resp = { ++ (unsigned char *)truncated_eku_resp_str, ++ sizeof(truncated_eku_resp_str) - 1 ++}; ++ + static void ocsp_invalid_calls(void) + { + gnutls_ocsp_req_t req; +@@ -1603,6 +1715,56 @@ static void resp_verify(void) + gnutls_x509_crt_deinit(signer); + } + ++static void truncated_eku_check(void) ++{ ++ gnutls_ocsp_resp_t resp; ++ gnutls_x509_crt_t signer = NULL; ++ unsigned verify; ++ int ret; ++ ++ success("trunc\n"); ++ ret = gnutls_ocsp_resp_init(&resp); ++ if (ret != 0) { ++ fail("gnutls_ocsp_resp_init\n"); ++ exit(1); ++ } ++ ++ ret = gnutls_ocsp_resp_import(resp, &truncated_eku_resp); ++ if (ret != 0) { ++ fail("gnutls_ocsp_resp_import %d\n", ret); ++ exit(1); ++ } ++ ++ ret = gnutls_x509_crt_init(&signer); ++ if (ret < 0) { ++ fail("gnutls_x509_crt_init (signer) %d\n", ret); ++ exit(1); ++ } ++ ++ ret = gnutls_x509_crt_import(signer, &truncated_eku_data, ++ GNUTLS_X509_FMT_PEM); ++ if (ret < 0) { ++ fail("gnutls_x509_crt_import (signer) %d\n", ret); ++ exit(1); ++ } ++ ++ /* check direct verify with signer key usage truncated (should fail) */ ++ ++ ret = gnutls_ocsp_resp_verify_direct(resp, signer, &verify, 0); ++ if (ret < 0) { ++ fail("gnutls_ocsp_resp_verify_direct (signer) %d\n", ret); ++ exit(1); ++ } ++ ++ if (verify != GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR) { ++ fail("gnutls_ocsp_resp_verify_direct %d\n", verify); ++ exit(1); ++ } ++ ++ gnutls_x509_crt_deinit(signer); ++ gnutls_ocsp_resp_deinit(resp); ++} ++ + static void long_resp_check(void) + { + gnutls_ocsp_resp_t resp; +@@ -1678,6 +1840,7 @@ void doit(void) + req_addcert_id(); + req_addcert(); + resp_verify(); ++ truncated_eku_check(); + + _then = 1415974540; + long_resp_check(); +-- +2.53.0 + + +From c93bc6dfc7987a3fe5ab6936d00cadba627acf8a Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 23 Apr 2026 20:00:17 +0200 +Subject: [PATCH 3/3] tests/ocsp: do not exit(1), fail does that + +Signed-off-by: Alexander Sosedkin +--- + tests/ocsp.c | 558 +++++++++++++-------------------------------------- + 1 file changed, 138 insertions(+), 420 deletions(-) + +diff --git a/tests/ocsp.c b/tests/ocsp.c +index 7e092bb17..2447aa29c 100644 +--- a/tests/ocsp.c ++++ b/tests/ocsp.c +@@ -614,327 +614,221 @@ static void ocsp_invalid_calls(void) + int rc; + + rc = gnutls_ocsp_req_init(&req); +- if (rc != GNUTLS_E_SUCCESS) { ++ if (rc != GNUTLS_E_SUCCESS) + fail("gnutls_ocsp_req_init alloc\n"); +- exit(1); +- } + rc = gnutls_ocsp_resp_init(&resp); +- if (rc != GNUTLS_E_SUCCESS) { ++ if (rc != GNUTLS_E_SUCCESS) + fail("gnutls_ocsp_resp_init alloc\n"); +- exit(1); +- } + + gnutls_ocsp_req_deinit(NULL); + gnutls_ocsp_resp_deinit(NULL); + + rc = gnutls_ocsp_req_import(NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_import NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_import(NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_import NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_import(req, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_import NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_import(NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_import NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_import(NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_import NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_import(resp, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_import NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_export(NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_export NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_export(NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_export NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_export(req, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_export NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_export(NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_export NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_export(NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_export NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_export(resp, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_export NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_version(NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_get_version NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_cert_id(NULL, 0, NULL, NULL, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_get_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_cert_id(req, 0, NULL, NULL, NULL, NULL); +- if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { ++ if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + fail("gnutls_ocsp_req_get_cert_id empty\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(NULL, 0, NULL, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, 0, NULL, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, NULL, NULL, + NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, p, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, NULL, p, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, NULL, NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, p, p, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, p, NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, NULL, p, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert(NULL, 0, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert(req, 0, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert(req, 0, p, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_add_cert(req, 0, NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_add_cert_id NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_extension(NULL, 0, NULL, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_get_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_extension(req, 0, NULL, NULL, NULL); +- if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { ++ if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + fail("gnutls_ocsp_req_get_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_extension(req, 0, p, p, p); +- if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { ++ if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + fail("gnutls_ocsp_req_get_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_set_extension(NULL, NULL, 0, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_set_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_set_extension(req, NULL, 0, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_set_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_set_extension(req, p, 0, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_set_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_set_extension(req, NULL, 0, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_set_extension NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_nonce(NULL, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_get_nonce NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_get_nonce(NULL, NULL, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_get_nonce NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_set_nonce(NULL, 0, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_set_nonce NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_set_nonce(req, 0, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_set_nonce NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_req_randomize_nonce(NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_req_randomize_nonce NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_status(NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_get_status NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_status(resp); +- if (rc != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { ++ if (rc != GNUTLS_E_ASN1_VALUE_NOT_FOUND) + fail("gnutls_ocsp_resp_get_status %d\n", rc); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_response(NULL, NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_get_response NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_response(NULL, p, p); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_get_response NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_response(resp, NULL, NULL); +- if (rc != GNUTLS_E_SUCCESS) { ++ if (rc != GNUTLS_E_SUCCESS) + fail("gnutls_ocsp_resp_get_response %d\n", rc); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_version(NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_get_version NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_version(resp); +- if (rc != 1) { ++ if (rc != 1) + fail("gnutls_ocsp_resp_get_version ret %d\n", rc); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_responder(NULL, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_get_responder NULL\n"); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_responder(resp, NULL); +- if (rc != GNUTLS_E_INVALID_REQUEST) { ++ if (rc != GNUTLS_E_INVALID_REQUEST) + fail("gnutls_ocsp_resp_get_responder 2nd %d\n", rc); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_responder(resp, &dat); +- if (rc != 0 && dat.data != NULL) { ++ if (rc != 0 && dat.data != NULL) + fail("gnutls_ocsp_resp_get_responder %d\n", rc); +- exit(1); +- } + + rc = gnutls_ocsp_resp_get_responder_raw_id( + resp, GNUTLS_OCSP_RESP_ID_KEY, &dat); +- if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { ++ if (rc != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + fail("gnutls_ocsp_resp_get_responder_raw_id %s\n", + gnutls_strerror(rc)); +- exit(1); +- } + + gnutls_free(dat.data); + +@@ -952,26 +846,20 @@ static void req_parse(void) + /* init request */ + + ret = gnutls_ocsp_req_init(&req); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_init\n"); +- exit(1); +- } + + /* import ocsp request */ + + ret = gnutls_ocsp_req_import(req, &req1); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_import %d\n", ret); +- exit(1); +- } + + /* simple version query */ + + ret = gnutls_ocsp_req_get_version(req); +- if (ret != 1) { ++ if (ret != 1) + fail("gnutls_ocsp_req_get_version %d\n", ret); +- exit(1); +- } + + /* check nonce */ + { +@@ -981,21 +869,15 @@ static void req_parse(void) + unsigned int critical; + + ret = gnutls_ocsp_req_get_nonce(req, &critical, &got); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_get_nonce %d\n", ret); +- exit(1); +- } + +- if (critical != 0) { ++ if (critical != 0) + fail("unexpected critical %d\n", critical); +- exit(1); +- } + + if (expect.size != got.size || +- memcmp(expect.data, got.data, got.size) != 0) { ++ memcmp(expect.data, got.data, got.size) != 0) + fail("ocsp request nonce memcmp failed\n"); +- exit(1); +- } + + gnutls_free(got.data); + } +@@ -1003,10 +885,8 @@ static void req_parse(void) + /* print request */ + + ret = gnutls_ocsp_req_print(req, GNUTLS_OCSP_PRINT_FULL, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_print\n"); +- exit(1); +- } + + if (strlen(REQ1INFO) != d.size || + memcmp(REQ1INFO, d.data, strlen(REQ1INFO)) != 0) { +@@ -1014,23 +894,18 @@ static void req_parse(void) + strlen(REQ1INFO), REQ1INFO, (int)d.size, (int)d.size, + d.data); + fail("ocsp request print failed\n"); +- exit(1); + } + gnutls_free(d.data); + + /* test export */ + ret = gnutls_ocsp_req_export(req, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_export %d\n", ret); +- exit(1); +- } + + /* compare against earlier imported bytes */ + +- if (req1.size != d.size || memcmp(req1.data, d.data, d.size) != 0) { ++ if (req1.size != d.size || memcmp(req1.data, d.data, d.size) != 0) + fail("ocsp request export memcmp failed\n"); +- exit(1); +- } + gnutls_free(d.data); + + /* test setting nonce */ +@@ -1041,98 +916,68 @@ static void req_parse(void) + unsigned critical; + + ret = gnutls_ocsp_req_set_nonce(req, 0, &n1); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_set_nonce %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_req_get_nonce(req, &critical, &got); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_get_nonce %d\n", ret); +- exit(1); +- } + +- if (critical != 0) { ++ if (critical != 0) + fail("unexpected critical %d\n", critical); +- exit(1); +- } + + if (n1.size != got.size || +- memcmp(n1.data, got.data, got.size) != 0) { ++ memcmp(n1.data, got.data, got.size) != 0) + fail("ocsp request parse nonce memcmp failed\n"); +- exit(1); +- } + + gnutls_free(got.data); + + /* set another time */ + + ret = gnutls_ocsp_req_set_nonce(req, 1, &n2); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_set_nonce %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_req_get_nonce(req, &critical, &got); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_get_nonce %d\n", ret); +- exit(1); +- } + +- if (critical != 1) { ++ if (critical != 1) + fail("unexpected critical %d\n", critical); +- exit(1); +- } + + if (n2.size != got.size || +- memcmp(n2.data, got.data, got.size) != 0) { ++ memcmp(n2.data, got.data, got.size) != 0) + fail("ocsp request parse2 nonce memcmp failed\n"); +- exit(1); +- } + + gnutls_free(got.data); + + /* randomize nonce */ + + ret = gnutls_ocsp_req_randomize_nonce(req); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_randomize_nonce %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_req_get_nonce(req, &critical, &n1); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_get_nonce %d\n", ret); +- exit(1); +- } + +- if (critical != 0) { ++ if (critical != 0) + fail("unexpected random critical %d\n", critical); +- exit(1); +- } + + ret = gnutls_ocsp_req_randomize_nonce(req); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_randomize_nonce %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_req_get_nonce(req, &critical, &n2); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_get_nonce %d\n", ret); +- exit(1); +- } + +- if (critical != 0) { ++ if (critical != 0) + fail("unexpected random critical %d\n", critical); +- exit(1); +- } + + if (n2.size == got.size && +- memcmp(n1.data, n2.data, n1.size) == 0) { ++ memcmp(n1.data, n2.data, n1.size) == 0) + fail("ocsp request random nonce memcmp failed\n"); +- exit(1); +- } + + gnutls_free(n1.data); + gnutls_free(n2.data); +@@ -1154,10 +999,8 @@ static void req_addcert_id(void) + /* init request */ + + ret = gnutls_ocsp_req_init(&req); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_init\n"); +- exit(1); +- } + + /* add ocsp request nonce */ + +@@ -1167,10 +1010,8 @@ static void req_addcert_id(void) + + ret = gnutls_ocsp_req_set_extension(req, "1.3.6.1.5.5.7.48.1.2", + 0, &nonce); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_set_extension %d\n", ret); +- exit(1); +- } + } + + /* add cert_id */ +@@ -1186,19 +1027,15 @@ static void req_addcert_id(void) + &issuer_name_hash, + &issuer_key_hash, + &serial_number); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_add_cert_id %d\n", ret); +- exit(1); +- } + } + + /* print request */ + + ret = gnutls_ocsp_req_print(req, GNUTLS_OCSP_PRINT_FULL, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_print\n"); +- exit(1); +- } + + if (strlen(REQ1INFO) != d.size || + memcmp(REQ1INFO, d.data, strlen(REQ1INFO)) != 0) { +@@ -1206,23 +1043,18 @@ static void req_addcert_id(void) + strlen(REQ1INFO), REQ1INFO, (int)d.size, (int)d.size, + d.data); + fail("ocsp request print failed\n"); +- exit(1); + } + gnutls_free(d.data); + + /* test export */ + ret = gnutls_ocsp_req_export(req, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_export %d\n", ret); +- exit(1); +- } + + /* compare against earlier imported bytes */ + +- if (req1.size != d.size || memcmp(req1.data, d.data, d.size) != 0) { ++ if (req1.size != d.size || memcmp(req1.data, d.data, d.size) != 0) + fail("ocsp request export memcmp failed\n"); +- exit(1); +- } + gnutls_free(d.data); + + /* cleanup */ +@@ -1241,10 +1073,8 @@ static void req_addcert(void) + /* init request */ + + ret = gnutls_ocsp_req_init(&req); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_init\n"); +- exit(1); +- } + + /* add ocsp request nonce */ + +@@ -1254,10 +1084,8 @@ static void req_addcert(void) + + ret = gnutls_ocsp_req_set_extension(req, "1.3.6.1.5.5.7.48.1.2", + 0, &nonce); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_set_extension %d\n", ret); +- exit(1); +- } + } + + /* add cert_id */ +@@ -1265,37 +1093,27 @@ static void req_addcert(void) + gnutls_x509_crt_t issuer = NULL, subject = NULL; + + ret = gnutls_x509_crt_init(&issuer); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_init (issuer) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_init(&subject); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_init (subject) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_import(issuer, &issuer_data, + GNUTLS_X509_FMT_PEM); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_import (issuer) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_import(subject, &subject_data, + GNUTLS_X509_FMT_PEM); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_import (subject) %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_req_add_cert(req, GNUTLS_DIG_SHA1, issuer, + subject); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_add_cert %d\n", ret); +- exit(1); +- } + + gnutls_x509_crt_deinit(subject); + gnutls_x509_crt_deinit(issuer); +@@ -1304,10 +1122,8 @@ static void req_addcert(void) + /* print request */ + + ret = gnutls_ocsp_req_print(req, GNUTLS_OCSP_PRINT_FULL, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_print\n"); +- exit(1); +- } + + if (strlen(REQ1INFO) != d.size || + memcmp(REQ1INFO, d.data, strlen(REQ1INFO)) != 0) { +@@ -1315,23 +1131,18 @@ static void req_addcert(void) + strlen(REQ1INFO), REQ1INFO, (int)d.size, (int)d.size, + d.data); + fail("ocsp request print failed\n"); +- exit(1); + } + gnutls_free(d.data); + + /* test export */ + ret = gnutls_ocsp_req_export(req, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_req_export %d\n", ret); +- exit(1); +- } + + /* compare against earlier imported bytes */ + +- if (req1.size != d.size || memcmp(req1.data, d.data, d.size) != 0) { ++ if (req1.size != d.size || memcmp(req1.data, d.data, d.size) != 0) + fail("ocsp request export memcmp failed\n"); +- exit(1); +- } + gnutls_free(d.data); + + /* cleanup */ +@@ -1395,27 +1206,21 @@ static void resp_import(void) + /* init response */ + + ret = gnutls_ocsp_resp_init(&resp); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_init\n"); +- exit(1); +- } + + /* import ocsp response */ + + ret = gnutls_ocsp_resp_import(resp, &resp1); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); +- exit(1); +- } + + /* print response */ + + ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_print\n"); +- exit(1); +- } + + if (strlen(RESP1INFO) != d.size || + memcmp(RESP1INFO, d.data, strlen(RESP1INFO)) != 0) { +@@ -1423,34 +1228,28 @@ static void resp_import(void) + strlen(RESP1INFO), RESP1INFO, (int)d.size, (int)d.size, + d.data); + fail("ocsp response print failed\n"); +- exit(1); + } + gnutls_free(d.data); + + /* import ocsp response */ + + ret = gnutls_ocsp_resp_import(resp, &resp2); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); +- exit(1); +- } + + check_ocsp_resp(resp); + + /* print response */ + ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_print\n"); +- exit(1); +- } + + if (memcmp(RESP2INFO, d.data, strlen(RESP2INFO)) != 0) { + printf("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n", + strlen(RESP2INFO), RESP2INFO, (int)d.size, (int)d.size, + d.data); + fail("ocsp response print failed\n"); +- exit(1); + } + gnutls_free(d.data); + +@@ -1461,32 +1260,25 @@ static void resp_import(void) + /* import ocsp response 3 */ + + ret = gnutls_ocsp_resp_init(&resp); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_init\n"); +- exit(1); +- } + + ret = gnutls_ocsp_resp_import(resp, &resp3); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); +- exit(1); +- } + + /* print response */ + + ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &d); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_print 3\n"); +- exit(1); +- } + + if (memcmp(RESP3INFO, d.data, strlen(RESP3INFO)) != 0) { + printf("expected (len %ld):\n%s\ngot (len %d):\n%.*s\n", + strlen(RESP3INFO), RESP3INFO, (int)d.size, (int)d.size, + d.data); + fail("ocsp response 3 print failed\n"); +- exit(1); + } + gnutls_free(d.data); + +@@ -1506,204 +1298,144 @@ static void resp_verify(void) + /* init response */ + + ret = gnutls_ocsp_resp_init(&resp); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_init\n"); +- exit(1); +- } + + /* import ocsp response */ + + ret = gnutls_ocsp_resp_import(resp, &blog_resp); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_import %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_init(&cert); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_init (cert) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_init(&issuer); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_init (issuer) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_init(&signer); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_init (signer) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_import(cert, &blog_cert_data, + GNUTLS_X509_FMT_PEM); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_import (cert) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_import(issuer, &blog_issuer_data, + GNUTLS_X509_FMT_PEM); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_import (issuer) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_import(signer, &blog_signer_data, + GNUTLS_X509_FMT_PEM); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_import (signer) %d\n", ret); +- exit(1); +- } + + /* check direct verify with signer (should succeed) */ + + ret = gnutls_ocsp_resp_verify_direct(resp, signer, &verify, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify_direct (signer) %d\n", ret); +- exit(1); +- } + +- if (verify != 0) { ++ if (verify != 0) + fail("gnutls_ocsp_resp_verify_direct %d\n", verify); +- exit(1); +- } + + /* check direct verify with cert (should fail) */ + + ret = gnutls_ocsp_resp_verify_direct(resp, cert, &verify, + GNUTLS_VERIFY_ALLOW_BROKEN); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify_direct (cert) %d\n", ret); +- exit(1); +- } + +- if (verify != GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) { ++ if (verify != GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) + fail("gnutls_ocsp_resp_verify_direct3 %d\n", verify); +- exit(1); +- } + + /* check trust verify with issuer (should succeed) */ + + ret = gnutls_x509_trust_list_init(&list, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_init %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_trust_list_add_cas(list, &issuer, 1, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_add_cas %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_resp_verify(resp, list, &verify, + GNUTLS_VERIFY_ALLOW_BROKEN); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify (issuer) %d\n", ret); +- exit(1); +- } + +- if (verify != 0) { ++ if (verify != 0) + fail("gnutls_ocsp_resp_verify %d\n", verify); +- exit(1); +- } + + gnutls_x509_trust_list_deinit(list, 0); + + /* check trust verify with signer (should succeed) */ + + ret = gnutls_x509_trust_list_init(&list, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_init %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_trust_list_add_cas(list, &signer, 1, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_add_cas %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_resp_verify(resp, list, &verify, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify (issuer) %d\n", ret); +- exit(1); +- } + +- if (verify != 0) { ++ if (verify != 0) + fail("gnutls_ocsp_resp_verify %d\n", verify); +- exit(1); +- } + + gnutls_x509_trust_list_deinit(list, 0); + + /* check trust verify with cert (should fail) */ + + ret = gnutls_x509_trust_list_init(&list, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_init %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_trust_list_add_cas(list, &cert, 1, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_add_cas %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_resp_verify(resp, list, &verify, + GNUTLS_VERIFY_ALLOW_BROKEN); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify (issuer) %d\n", ret); +- exit(1); +- } + +- if (verify != GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) { ++ if (verify != GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) + fail("gnutls_ocsp_resp_verify %d\n", verify); +- exit(1); +- } + + gnutls_x509_trust_list_deinit(list, 0); + + /* check trust verify with all certs (should succeed) */ + + ret = gnutls_x509_trust_list_init(&list, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_init %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_trust_list_add_cas(list, &cert, 1, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_add_cas %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_trust_list_add_cas(list, &issuer, 1, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_add_cas %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_trust_list_add_cas(list, &signer, 1, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_trust_list_add_cas %d\n", ret); +- exit(1); +- } + + ret = gnutls_ocsp_resp_verify(resp, list, &verify, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify (issuer) %d\n", ret); +- exit(1); +- } + +- if (verify != 0) { ++ if (verify != 0) + fail("gnutls_ocsp_resp_verify %d\n", verify); +- exit(1); +- } + + gnutls_x509_trust_list_deinit(list, 0); + +@@ -1775,45 +1507,33 @@ static void long_resp_check(void) + /* init response */ + + ret = gnutls_ocsp_resp_init(&resp); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_init\n"); +- exit(1); +- } + + /* import ocsp response */ + + ret = gnutls_ocsp_resp_import(resp, &long_resp); +- if (ret != 0) { ++ if (ret != 0) + fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, + gnutls_strerror(ret)); +- exit(1); +- } + + ret = gnutls_x509_crt_init(&signer); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_init (signer) %d\n", ret); +- exit(1); +- } + + ret = gnutls_x509_crt_import(signer, &long_resp_signer_data, + GNUTLS_X509_FMT_PEM); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_x509_crt_import (cert) %d\n", ret); +- exit(1); +- } + + /* check direct verify with signer (should succeed) */ + + ret = gnutls_ocsp_resp_verify_direct(resp, signer, &verify, 0); +- if (ret < 0) { ++ if (ret < 0) + fail("gnutls_ocsp_resp_verify_direct (signer) %d\n", ret); +- exit(1); +- } + +- if (verify != 0) { ++ if (verify != 0) + fail("gnutls_ocsp_resp_verify_direct %d\n", verify); +- exit(1); +- } + + gnutls_x509_crt_deinit(signer); + gnutls_ocsp_resp_deinit(resp); +@@ -1824,10 +1544,8 @@ void doit(void) + int ret; + + ret = global_init(); +- if (ret < 0) { ++ if (ret < 0) + fail("global_init\n"); +- exit(1); +- } + + gnutls_global_set_time_function(mytime); + gnutls_global_set_log_function(tls_log_func); +-- +2.53.0 + diff --git a/gnutls-3.8.10-1813-p11p-aes-ephemeral.patch b/gnutls-3.8.10-1813-p11p-aes-ephemeral.patch new file mode 100644 index 0000000..e2da673 --- /dev/null +++ b/gnutls-3.8.10-1813-p11p-aes-ephemeral.patch @@ -0,0 +1,25 @@ +From 9f0350271b12cb61d93e25af43ba6e6eedfcc304 Mon Sep 17 00:00:00 2001 +From: Zoltan Fridrich +Date: Wed, 25 Mar 2026 19:43:33 +0100 +Subject: [PATCH] pkcs11/p11_cipher: make AES keys ephemeral objects + +Signed-off-by: Zoltan Fridrich +--- + lib/pkcs11/p11_cipher.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/lib/pkcs11/p11_cipher.c b/lib/pkcs11/p11_cipher.c +index 8837aa803..5e64364cb 100644 +--- a/lib/pkcs11/p11_cipher.c ++++ b/lib/pkcs11/p11_cipher.c +@@ -183,7 +183,6 @@ static int aes_set_key(struct p11_cipher_ctx *ctx, const void *key, + CK_ATTRIBUTE attrs[] = { { CKA_CLASS, &attr_class, sizeof(attr_class) }, + { CKA_KEY_TYPE, &attr_key_type, + sizeof(attr_key_type) }, +- { CKA_TOKEN, &attr_true, sizeof(attr_true) }, + { CKA_ENCRYPT, &attr_true, sizeof(attr_true) }, + { CKA_DECRYPT, &attr_true, sizeof(attr_true) }, + { CKA_LABEL, label, sizeof(label) - 1 }, +-- +2.53.0 + diff --git a/gnutls-3.8.10-1817-security-parameters.patch b/gnutls-3.8.10-1817-security-parameters.patch new file mode 100644 index 0000000..c743cfd --- /dev/null +++ b/gnutls-3.8.10-1817-security-parameters.patch @@ -0,0 +1,35 @@ +From 3d45a63b16f64ac53abe9f1a02135e8daf1020f8 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 7 Apr 2026 10:16:03 +0200 +Subject: [PATCH] session_pack: validate session_id_size on unpacking + +A check for session_id_size not exceeding GNUTLS_MAX_SESSION_ID_SIZE +on loading persisted TLS session data was overlooked, +leading to a heap overflow +were the data corrupted in a malicious manner. + +Reported-by: Haruto Kimura (Stella) +Fixes: #1817 +Signed-off-by: Alexander Sosedkin +--- + lib/session_pack.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/lib/session_pack.c b/lib/session_pack.c +index bd1ce3361..6c1d98270 100644 +--- a/lib/session_pack.c ++++ b/lib/session_pack.c +@@ -973,6 +973,10 @@ static int unpack_security_parameters(gnutls_session_t session, + &session->internals.resumed_security_parameters.session_id_size, + 1); + ++ if (session->internals.resumed_security_parameters.session_id_size > ++ GNUTLS_MAX_SESSION_ID_SIZE) ++ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ++ + BUFFER_POP( + ps, session->internals.resumed_security_parameters.session_id, + session->internals.resumed_security_parameters.session_id_size); +-- +2.53.0 + diff --git a/gnutls-3.8.10-1818-pem-parsing.patch b/gnutls-3.8.10-1818-pem-parsing.patch new file mode 100644 index 0000000..cd81612 --- /dev/null +++ b/gnutls-3.8.10-1818-pem-parsing.patch @@ -0,0 +1,116 @@ +From 09f3ab24fba0d2558db29c0e47b011f9b9b56c09 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 7 Apr 2026 20:23:29 +0200 +Subject: [PATCH 1/2] lib/x509/privkey_openssl: mind header size more carefully + +When parsing private keys in OpenSSL PEM format, GnuTLS did not perform +sufficient bounds checking for the length of the PEM header being parsed. +For specially crafted inputs, this could lead to heap overreads. +There was no confidentiality risk and +the crash potential was limited to instrumented builds in practice. +This change instates the overlooked bounds checking. + +Reported-by: Kamil Frankowicz +Reported-by: Joshua Rogers of AISLE Research Team +Related: #1818 +Fixes: #1854 +Signed-off-by: Alexander Sosedkin +--- + lib/x509/privkey_openssl.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/lib/x509/privkey_openssl.c b/lib/x509/privkey_openssl.c +index eb8db9353..50eb6c040 100644 +--- a/lib/x509/privkey_openssl.c ++++ b/lib/x509/privkey_openssl.c +@@ -173,7 +173,8 @@ int gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key, + + for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) { + l = strlen(pem_ciphers[i].name); +- if (!strncmp(pem_header, pem_ciphers[i].name, l) && ++ if (pem_header_size > l && ++ !strncmp(pem_header, pem_ciphers[i].name, l) && + pem_header[l] == ',') { + pem_header += l + 1; + cipher = pem_ciphers[i].cipher; +@@ -217,6 +218,8 @@ int gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key, + while (*pem_header == '\n' || *pem_header == '\r') + pem_header++; + ++ pem_header_size = ++ data->size - (ptrdiff_t)(pem_header - pem_header_start); + ret = _gnutls_base64_decode((const void *)pem_header, pem_header_size, + &b64_data); + if (ret < 0) { +-- +2.53.0 + + +From 3023e4d155affad82a42e1f076b73c5bf17c5931 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 7 Apr 2026 20:31:49 +0200 +Subject: [PATCH 2/2] tests/key-openssl: add a test for #1818.4 OpenSSL PEM + parsing + +Signed-off-by: Alexander Sosedkin +--- + tests/key-openssl.c | 37 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +diff --git a/tests/key-openssl.c b/tests/key-openssl.c +index 4a270312d..b769f142b 100644 +--- a/tests/key-openssl.c ++++ b/tests/key-openssl.c +@@ -108,6 +108,21 @@ const char key_lowercase_iv[] = + "57ohSPIR3bXgRZuefjxBhQYthUPcZ+qktrbURcvHNLs=\n" + "-----END RSA PRIVATE KEY-----\n"; + ++const char key_newlines_head[] = /* key2... */ ++ "-----BEGIN RSA PRIVATE KEY-----\n" ++ "Proc-Type: 4,ENCRYPTED\n" ++ "DEK-Info: AES-128-CBC,2A57FF97B701B3F760145D7446929481\n"; ++/* ... with many newlines inserted in here */ ++const char key_newlines_tail[] = ++ "mGAPhSw48wZBnkHOhfMDg8yL2IBgMuTmeKE4xoHi7T6isHBNfkqMd0iJ+DJP/OKb\n" ++ "t+7lkKjj/xQ7w/bOBvBxlfRe4MW6+ejCdAFD9XSolW6WN6CEJPMI4UtmOK5inqcC\n" ++ "8l2l54f/VGrVN9uavU3KlXCjrd3Jp9B0Mu4Zh/UU4+EWs9rJAZfLIn+vHZ3OHetx\n" ++ "g74LdV7nC7lt/fjxc1caNIfgHs40dUt9FVrnJvAtkcNMtcjX/D+L8ZrLgQzIWFcs\n" ++ "WAbUZj7Me22mCli3RPET7Je37K59IzfWgbWFCGaNu3X02g5xtCfdcn/Uqy9eofH0\n" ++ "YjKRhpgXPeGJCkoRqDeUHQNPpVP5HrzDZMVK3E4DC03C8qvgsYvuwYt3KkbG2fuA\n" ++ "F3bDyqlxSOm7uxF/K3YzI44v8/D8GGnLBTpN+ANBdiY=\n"; ++/* "-----END RSA PRIVATE KEY-----\n"; intentionally omitted */ ++ + static int good_pwd_cb(void *userdata, int attempt, const char *token_url, + const char *token_label, unsigned int flags, char *pin, + size_t pin_max) +@@ -281,5 +296,27 @@ void doit(void) + } + gnutls_x509_privkey_deinit(pkey); + ++ /* import_openssl with a key having too many newlines in the header, ++ * (fails with #1818.4 unfixed under ASAN) */ ++ ret = gnutls_x509_privkey_init(&pkey); ++ if (ret < 0) ++ fail("gnutls_x509_privkey_init: %d\n", ret); ++ ++ //gnutls_x509_privkey_set_pin_function(pkey, good_pwd_cb, NULL); ++ key.size = 1024 * 1024 * 1024; ++ key.data = gnutls_malloc(key.size); ++ memset(key.data, '\n', key.size); ++ memcpy(key.data, key_newlines_head, sizeof(key_newlines_head) - 1); ++ memcpy(key.data + key.size - sizeof(key_newlines_tail), ++ key_newlines_tail, sizeof(key_newlines_tail)); ++ ++ ret = gnutls_x509_privkey_import_openssl(pkey, &key, "a123456"); ++ if (ret != GNUTLS_E_BASE64_DECODING_ERROR) { ++ fail("gnutls_x509_import_openssl (full of newlines): %s\n", ++ gnutls_strerror(ret)); ++ } ++ gnutls_free(key.data); ++ gnutls_x509_privkey_deinit(pkey); ++ + gnutls_global_deinit(); + } +-- +2.53.0 + diff --git a/gnutls-3.8.10-1818-rsa-coprime.patch b/gnutls-3.8.10-1818-rsa-coprime.patch new file mode 100644 index 0000000..5984eaf --- /dev/null +++ b/gnutls-3.8.10-1818-rsa-coprime.patch @@ -0,0 +1,39 @@ +From f66b9933252ef90f9765124361b47951a34d456d Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 7 Apr 2026 20:18:40 +0200 +Subject: [PATCH] nettle/pk: check RSA key coprimality in verify_params + +Previously, gnutls_privkey_verify_params has overlooked +the scenario of p and q not being co-prime, +and proceeded with undefined behaviour that was extremely likely +to error out in practice anyway. +Now it returns GNUTLS_E_PK_INVALID_PRIVKEY in this case. + +Reported-by: Kamil Frankowicz +Related: #1818 +Signed-off-by: Alexander Sosedkin +--- + lib/nettle/pk.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c +index 4047df377..e8b36f5fa 100644 +--- a/lib/nettle/pk.c ++++ b/lib/nettle/pk.c +@@ -4434,8 +4434,11 @@ static int wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, + goto rsa_cleanup; + } + +- mpz_invert(TOMPZ(t1), TOMPZ(params->params[RSA_PRIME2]), +- TOMPZ(params->params[RSA_PRIME1])); ++ if (!mpz_invert(TOMPZ(t1), TOMPZ(params->params[RSA_PRIME2]), ++ TOMPZ(params->params[RSA_PRIME1]))) { ++ ret = gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY); ++ goto rsa_cleanup; ++ } + if (_gnutls_mpi_cmp(t1, params->params[RSA_COEF]) != 0) { + ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + goto rsa_cleanup; +-- +2.53.0 + diff --git a/gnutls-3.8.10-1819-dblfree-mid-import.patch b/gnutls-3.8.10-1819-dblfree-mid-import.patch new file mode 100644 index 0000000..3b1983b --- /dev/null +++ b/gnutls-3.8.10-1819-dblfree-mid-import.patch @@ -0,0 +1,60 @@ +From 2799775899d630d211647d5c916b987746dd7fbf Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 10 Apr 2026 20:30:27 +0200 +Subject: [PATCH] lib/x509: fix cleanup when gnutls_x509_crt_list_import_pkcs11 + fails + +Previously gnutls_x509_trust_list_remove_trust_file tried to free +the entire xcrt_list, even though one source of failures is +gnutls_pkcs11_obj_list_import_url2 that deinits it up to the correct +position. + +With this change, both functions zero unused entries. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1819 +Signed-off-by: Alexander Sosedkin +--- + lib/pkcs11.c | 1 + + lib/x509/verify-high2.c | 6 ++---- + 2 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/lib/pkcs11.c b/lib/pkcs11.c +index 1fe4ee61c..a93a8f3f3 100644 +--- a/lib/pkcs11.c ++++ b/lib/pkcs11.c +@@ -3869,6 +3869,7 @@ int gnutls_x509_crt_list_import_pkcs11(gnutls_x509_crt_t *certs, + cleanup: + for (j = 0; j < i; j++) { + gnutls_x509_crt_deinit(certs[j]); ++ certs[j] = NULL; + } + + return ret; +diff --git a/lib/x509/verify-high2.c b/lib/x509/verify-high2.c +index dc975baeb..3beb703ba 100644 +--- a/lib/x509/verify-high2.c ++++ b/lib/x509/verify-high2.c +@@ -207,8 +207,7 @@ static int add_trust_list_pkcs11_object_url(gnutls_x509_trust_list_t list, + goto cleanup; + } + +- xcrt_list = _gnutls_reallocarray(NULL, pcrt_list_size, +- sizeof(gnutls_x509_crt_t)); ++ xcrt_list = gnutls_calloc(pcrt_list_size, sizeof(gnutls_x509_crt_t)); + if (xcrt_list == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; +@@ -254,8 +253,7 @@ static int remove_pkcs11_object_url(gnutls_x509_trust_list_t list, + goto cleanup; + } + +- xcrt_list = _gnutls_reallocarray(NULL, pcrt_list_size, +- sizeof(gnutls_x509_crt_t)); ++ xcrt_list = gnutls_calloc(pcrt_list_size, sizeof(gnutls_x509_crt_t)); + if (xcrt_list == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; +-- +2.53.0 + diff --git a/gnutls-3.8.10-1820-p11p-kdf.patch b/gnutls-3.8.10-1820-p11p-kdf.patch new file mode 100644 index 0000000..42d3795 --- /dev/null +++ b/gnutls-3.8.10-1820-p11p-kdf.patch @@ -0,0 +1,61 @@ +From fdef6b6f493c303bdeb2513e1626ffef896a98f2 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 13 Apr 2026 13:42:52 +0200 +Subject: [PATCH] lib/pkcs11: do not silently pass on unimplemented + functionality + +When the relevant PKCS#11 header macros were not defined, +several functions for FIPS PKCS#11 provider wrongfully reported success. +They have been modified to return GNUTLS_E_UNIMPLEMENTED_FEATURE instead. + +Fixes: #1820 +Reported-by: Joshua Rogers of AISLE Research Team +Co-authored-by: Joshua Rogers of AISLE Research Team +Signed-off-by: Alexander Sosedkin +--- + lib/pkcs11/p11_mac.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/lib/pkcs11/p11_mac.c b/lib/pkcs11/p11_mac.c +index c2e3bcd61..02e897e68 100644 +--- a/lib/pkcs11/p11_mac.c ++++ b/lib/pkcs11/p11_mac.c +@@ -806,8 +806,10 @@ static int wrap_p11_hkdf_extract(gnutls_mac_algorithm_t _mac, const void *key, + } + + _p11_provider_close_session(session); +-#endif + return 0; ++#else ++ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); ++#endif + } + + static int wrap_p11_hkdf_expand(gnutls_mac_algorithm_t _mac, const void *key, +@@ -871,8 +873,10 @@ static int wrap_p11_hkdf_expand(gnutls_mac_algorithm_t _mac, const void *key, + } + + _p11_provider_close_session(session); +-#endif + return 0; ++#else ++ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); ++#endif + } + + static int wrap_p11_pbkdf2(gnutls_mac_algorithm_t _mac, const void *key, +@@ -952,8 +956,10 @@ static int wrap_p11_pbkdf2(gnutls_mac_algorithm_t _mac, const void *key, + } + + _p11_provider_close_session(session); +-#endif + return 0; ++#else ++ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); ++#endif + } + + gnutls_crypto_mac_st _gnutls_p11_mac_ops = { +-- +2.53.0 + diff --git a/gnutls-3.8.10-1822-sct-overread.patch b/gnutls-3.8.10-1822-sct-overread.patch new file mode 100644 index 0000000..cddaf59 --- /dev/null +++ b/gnutls-3.8.10-1822-sct-overread.patch @@ -0,0 +1,59 @@ +From 2a03da0d3d901dd4b5c87876f1903322114f8f74 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 13 Apr 2026 18:42:56 +0200 +Subject: [PATCH] lib/x509/x509_ext: avoid a heap overread in SCT extension + parser + +Parsing a specially crafted SCT extension could previously lead to +a short heap overread. +The list-length validation didn't account for the 2-byte length field. + +The fix now accounts for the header field length, +ensuring the parsing stays within the buffer. + +Fixes: #1822 +Reported-by: Joshua Rogers of AISLE Research Team +Signed-off-by: Alexander Sosedkin +--- + lib/x509/x509_ext.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c +index 33a4c913e..f5cabe3b6 100644 +--- a/lib/x509/x509_ext.c ++++ b/lib/x509/x509_ext.c +@@ -3758,13 +3758,13 @@ int gnutls_x509_ext_ct_import_scts(const gnutls_datum_t *ext, + if (retval < 0) + return gnutls_assert_val(retval); + +- if (scts_content.size < 2) { ++ if (scts_content.size < sizeof(uint16_t)) { + gnutls_free(scts_content.data); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + length = _gnutls_read_uint16(scts_content.data); +- if (length < 4 || length > scts_content.size) { ++ if (length < 4 || length > scts_content.size - sizeof(uint16_t)) { + gnutls_free(scts_content.data); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } +@@ -3775,12 +3775,12 @@ int gnutls_x509_ext_ct_import_scts(const gnutls_datum_t *ext, + break; + + sct_length = _gnutls_read_uint16(ptr); +- if (sct_length == 0 || sct_length > length) +- break; +- + ptr += sizeof(uint16_t); + length -= sizeof(uint16_t); + ++ if (sct_length == 0 || sct_length > length) ++ break; ++ + /* + * _gnutls_parse_ct_sct() will try to read exactly sct_length bytes, + * returning an error if it can't +-- +2.53.0 + diff --git a/gnutls-3.8.10-1823-cfg-clear-options.patch b/gnutls-3.8.10-1823-cfg-clear-options.patch new file mode 100644 index 0000000..08c2307 --- /dev/null +++ b/gnutls-3.8.10-1823-cfg-clear-options.patch @@ -0,0 +1,62 @@ +From 055b2c742d6faf44c2fdaaa7e37c744a01856abc Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 14 Apr 2026 18:21:19 +0200 +Subject: [PATCH 1/2] src/cfg: fix iterating in clear_options, on the error + path + +Calling testing tools bundled with GnuTLS with malformed arguments +could lead to crashing them. +This change makes the error path of option parsing more robust. + +Fixes: #1823 +Reported-by: Joshua Rogers of AISLE Research Team +Co-authored-by: Joshua Rogers of AISLE Research Team +Signed-off-by: Alexander Sosedkin +--- + src/cfg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/cfg.c b/src/cfg.c +index 9a9627f18..47d2d6434 100644 +--- a/src/cfg.c ++++ b/src/cfg.c +@@ -370,7 +370,7 @@ static int take_option(struct options_st *options, struct cfg_option_st *option) + + static void clear_options(struct options_st *options) + { +- for (size_t i = 0; options->length; i++) { ++ for (size_t i = 0; i < options->length; i++) { + clear_option(&options->data[i]); + } + } +-- +2.53.0 + + +From 9649e899b677fdd945bf8f4f67b3f9f25cea314a Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 14 Apr 2026 18:25:13 +0200 +Subject: [PATCH 2/2] src/cfg: avoid a data leak in clear_options, on the error + path + +Signed-off-by: Alexander Sosedkin +--- + src/cfg.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/cfg.c b/src/cfg.c +index 47d2d6434..c1f351dfb 100644 +--- a/src/cfg.c ++++ b/src/cfg.c +@@ -373,6 +373,8 @@ static void clear_options(struct options_st *options) + for (size_t i = 0; i < options->length; i++) { + clear_option(&options->data[i]); + } ++ free(options->data); ++ memset(options, 0, sizeof(struct options_st)); + } + + cfg_option_t cfg_load(const char *filename) +-- +2.53.0 + diff --git a/gnutls-3.8.10-1841-hybrid-kx-zeroize.patch b/gnutls-3.8.10-1841-hybrid-kx-zeroize.patch new file mode 100644 index 0000000..e220b19 --- /dev/null +++ b/gnutls-3.8.10-1841-hybrid-kx-zeroize.patch @@ -0,0 +1,80 @@ +From dcdce673516f4c578f37ae1c503f369d385ceb18 Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Wed, 15 Apr 2026 21:21:46 +0900 +Subject: [PATCH] key_share: zeroize derived shared secret after compositing + +Signed-off-by: Daiki Ueno +--- + lib/ext/key_share.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c +index 84cb031ae..85c1e46ec 100644 +--- a/lib/ext/key_share.c ++++ b/lib/ext/key_share.c +@@ -462,7 +462,7 @@ static int server_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(ret); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +@@ -506,7 +506,7 @@ static int server_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(ret); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +@@ -603,7 +603,7 @@ static int server_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +@@ -700,7 +700,7 @@ static int client_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(ret); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +@@ -739,7 +739,7 @@ static int client_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(ret); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +@@ -776,7 +776,7 @@ static int client_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(ret); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +@@ -797,7 +797,7 @@ static int client_use_key_share_single(gnutls_session_t session, + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + ret = append_key_datum(&session->key.key, &key); +- _gnutls_free_datum(&key); ++ _gnutls_free_key_datum(&key); + if (ret < 0) + return gnutls_assert_val(ret); + +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2025-9820.patch b/gnutls-3.8.10-CVE-2025-9820.patch index 19e6008..621bcb0 100644 --- a/gnutls-3.8.10-CVE-2025-9820.patch +++ b/gnutls-3.8.10-CVE-2025-9820.patch @@ -181,8 +181,9 @@ Signed-off-by: Daiki Ueno --- lib/pkcs11_write.c | 5 +- tests/Makefile.am | 4 +- + tests/Makefile.in | 86 +++++++++++++++++++++++++++++++------- tests/pkcs11/long-label.c | 164 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 170 insertions(+), 3 deletions(-) + 4 files changed, 237 insertions(+), 22 deletions(-) create mode 100644 tests/pkcs11/long-label.c diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c @@ -236,6 +237,260 @@ index 62c4ec2f9..0e4d04342 100644 endif endif +diff --git a/tests/Makefile.in b/tests/Makefile.in +index 86c271f..334d9fb 100644 +--- a/tests/Makefile.in ++++ b/tests/Makefile.in +@@ -124,7 +124,8 @@ host_triplet = @host@ + @CROSS_COMPILING_FALSE@am__append_9 = tls-pthread fips-mode-pthread dtls-pthread rng-pthread + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@am__append_10 = libpkcs11mock1.la \ + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ libpkcs11mock2.la \ +-@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ libpkcs11mock3.la ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ libpkcs11mock3.la \ ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ libpkcs11mock4.la + @ENABLE_PKCS11_FALSE@pkcs11_cert_import_url_exts_DEPENDENCIES = \ + @ENABLE_PKCS11_FALSE@ $(COMMON_GNUTLS_LDADD) libutils.la \ + @ENABLE_PKCS11_FALSE@ $(am__DEPENDENCIES_2) +@@ -171,7 +172,7 @@ host_triplet = @host@ + @HAVE_FORK_TRUE@ resume-with-record-size-limit + + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@am__append_17 = tls13/post-handshake-with-cert-pkcs11 pkcs11/tls-neg-pkcs11-no-key \ +-@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ global-init-override pkcs11/distrust-after ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ global-init-override pkcs11/distrust-after pkcs11/long-label + + @ENABLE_TPM2_TRUE@am__append_18 = tpm2.sh + +@@ -519,7 +520,8 @@ am__EXEEXT_2 = $(am__EXEEXT_1) + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@am__EXEEXT_14 = tls13/post-handshake-with-cert-pkcs11$(EXEEXT) \ + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ pkcs11/tls-neg-pkcs11-no-key$(EXEEXT) \ + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ global-init-override$(EXEEXT) \ +-@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ pkcs11/distrust-after$(EXEEXT) ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ pkcs11/distrust-after$(EXEEXT) \ ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ pkcs11/long-label$(EXEEXT) + @WINDOWS_TRUE@am__EXEEXT_15 = win32-certopenstore$(EXEEXT) + am__EXEEXT_16 = tls13/supported_versions$(EXEEXT) \ + tls13/tls12-no-tls13-exts$(EXEEXT) \ +@@ -789,6 +791,17 @@ libpkcs11mock3_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_CFLAGS) $(CFLAGS) $(libpkcs11mock3_la_LDFLAGS) $(LDFLAGS) \ + -o $@ + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@am_libpkcs11mock3_la_rpath = ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock4_la_DEPENDENCIES = \ ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ ../gl/libgnu.la ++am__libpkcs11mock4_la_SOURCES_DIST = pkcs11/pkcs11-mock4.c ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@am_libpkcs11mock4_la_OBJECTS = \ ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@ pkcs11/pkcs11-mock4.lo ++libpkcs11mock4_la_OBJECTS = $(am_libpkcs11mock4_la_OBJECTS) ++libpkcs11mock4_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ ++ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ ++ $(AM_CFLAGS) $(CFLAGS) $(libpkcs11mock4_la_LDFLAGS) $(LDFLAGS) \ ++ -o $@ ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@am_libpkcs11mock4_la_rpath = + libutils_la_DEPENDENCIES = ../lib/libgnutls.la + am_libutils_la_OBJECTS = utils.lo seccomp.lo utils-adv.lo + libutils_la_OBJECTS = $(am_libutils_la_OBJECTS) +@@ -1793,6 +1806,8 @@ pkcs11_list_tokens_OBJECTS = pkcs11/list-tokens.$(OBJEXT) + pkcs11_list_tokens_LDADD = $(LDADD) + pkcs11_list_tokens_DEPENDENCIES = $(COMMON_GNUTLS_LDADD) libutils.la \ + $(am__DEPENDENCIES_2) ++pkcs11_long_label_SOURCES = pkcs11/long-label.c ++pkcs11_long_label_OBJECTS = pkcs11/long-label.$(OBJEXT) + pkcs11_pkcs11_chainverify_SOURCES = pkcs11/pkcs11-chainverify.c + pkcs11_pkcs11_chainverify_OBJECTS = \ + pkcs11/pkcs11-chainverify.$(OBJEXT) +@@ -3602,7 +3617,7 @@ am__depfiles_remade = ./$(DEPDIR)/aead-cipher-vec.Po \ + pkcs11/$(DEPDIR)/gnutls_x509_crt_list_import_url.Po \ + pkcs11/$(DEPDIR)/import_url_privkey_caps-pkcs11-import-url-privkey.Po \ + pkcs11/$(DEPDIR)/list-objects.Po \ +- pkcs11/$(DEPDIR)/list-tokens.Po \ ++ pkcs11/$(DEPDIR)/list-tokens.Po pkcs11/$(DEPDIR)/long-label.Po \ + pkcs11/$(DEPDIR)/pkcs11-cert-import-url-exts.Po \ + pkcs11/$(DEPDIR)/pkcs11-cert-import-url4-exts.Po \ + pkcs11/$(DEPDIR)/pkcs11-chainverify.Po \ +@@ -3619,6 +3634,7 @@ am__depfiles_remade = ./$(DEPDIR)/aead-cipher-vec.Po \ + pkcs11/$(DEPDIR)/pkcs11-mock.Plo \ + pkcs11/$(DEPDIR)/pkcs11-mock2.Plo \ + pkcs11/$(DEPDIR)/pkcs11-mock3.Plo \ ++ pkcs11/$(DEPDIR)/pkcs11-mock4.Plo \ + pkcs11/$(DEPDIR)/pkcs11-obj-import.Po \ + pkcs11/$(DEPDIR)/pkcs11-obj-raw.Po \ + pkcs11/$(DEPDIR)/pkcs11-pin-func.Po \ +@@ -3712,16 +3728,17 @@ am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) + am__v_CXXLD_0 = @echo " CXXLD " $@; + am__v_CXXLD_1 = + SOURCES = $(libpkcs11mock1_la_SOURCES) $(libpkcs11mock2_la_SOURCES) \ +- $(libpkcs11mock3_la_SOURCES) $(libutils_la_SOURCES) \ +- aead-cipher-vec.c alerts.c alpn-server-prec.c anonself.c \ +- atfork.c auto-verify.c base64.c base64-raw.c buffer.c cert.c \ +- cert-status.c cert_verify_inv_utf8.c \ +- certificate_set_x509_crl.c certuniqueid.c chainverify.c \ +- chainverify-unsorted.c cipher-alignment.c cipher-padding.c \ +- ciphersuite-name.c client-fastopen.c client-sign-md5-rep.c \ +- client_dsa_key.c $(compress_cert_conf_SOURCES) conv-utf8.c \ +- crl-basic.c crl_apis.c crlverify.c crq-basic.c crq_apis.c \ +- crq_key_id.c crt_apis.c crt_inv_write.c custom-urls.c \ ++ $(libpkcs11mock3_la_SOURCES) $(libpkcs11mock4_la_SOURCES) \ ++ $(libutils_la_SOURCES) aead-cipher-vec.c alerts.c \ ++ alpn-server-prec.c anonself.c atfork.c auto-verify.c base64.c \ ++ base64-raw.c buffer.c cert.c cert-status.c \ ++ cert_verify_inv_utf8.c certificate_set_x509_crl.c \ ++ certuniqueid.c chainverify.c chainverify-unsorted.c \ ++ cipher-alignment.c cipher-padding.c ciphersuite-name.c \ ++ client-fastopen.c client-sign-md5-rep.c client_dsa_key.c \ ++ $(compress_cert_conf_SOURCES) conv-utf8.c crl-basic.c \ ++ crl_apis.c crlverify.c crq-basic.c crq_apis.c crq_key_id.c \ ++ crt_apis.c crt_inv_write.c custom-urls.c \ + custom-urls-override.c cve-2008-4989.c cve-2009-1415.c \ + cve-2009-1416.c dane.c dane-strcodes.c dh-compute.c \ + dh-compute2.c dh-params.c dhepskself.c dhex509self.c dn.c \ +@@ -3791,8 +3808,9 @@ SOURCES = $(libpkcs11mock1_la_SOURCES) $(libpkcs11mock2_la_SOURCES) \ + $(pkcs11_token_raw_SOURCES) pkcs11/distrust-after.c \ + pkcs11/gnutls_pcert_list_import_x509_file.c \ + pkcs11/gnutls_x509_crt_list_import_url.c pkcs11/list-objects.c \ +- pkcs11/list-tokens.c pkcs11/pkcs11-chainverify.c \ +- pkcs11/pkcs11-combo.c pkcs11/pkcs11-ec-privkey-test.c \ ++ pkcs11/list-tokens.c pkcs11/long-label.c \ ++ pkcs11/pkcs11-chainverify.c pkcs11/pkcs11-combo.c \ ++ pkcs11/pkcs11-ec-privkey-test.c \ + pkcs11/pkcs11-eddsa-privkey-test.c pkcs11/pkcs11-get-issuer.c \ + pkcs11/pkcs11-import-with-pin.c pkcs11/pkcs11-is-known.c \ + pkcs11/pkcs11-obj-import.c pkcs11/pkcs11-pin-func.c \ +@@ -3911,7 +3929,8 @@ SOURCES = $(libpkcs11mock1_la_SOURCES) $(libpkcs11mock2_la_SOURCES) \ + x509sign-verify-rsa.c xts-key-check.c + DIST_SOURCES = $(am__libpkcs11mock1_la_SOURCES_DIST) \ + $(am__libpkcs11mock2_la_SOURCES_DIST) \ +- $(am__libpkcs11mock3_la_SOURCES_DIST) $(libutils_la_SOURCES) \ ++ $(am__libpkcs11mock3_la_SOURCES_DIST) \ ++ $(am__libpkcs11mock4_la_SOURCES_DIST) $(libutils_la_SOURCES) \ + aead-cipher-vec.c alerts.c alpn-server-prec.c anonself.c \ + atfork.c auto-verify.c base64.c base64-raw.c buffer.c cert.c \ + cert-status.c cert_verify_inv_utf8.c \ +@@ -3992,8 +4011,9 @@ DIST_SOURCES = $(am__libpkcs11mock1_la_SOURCES_DIST) \ + $(am__pkcs11_token_raw_SOURCES_DIST) pkcs11/distrust-after.c \ + pkcs11/gnutls_pcert_list_import_x509_file.c \ + pkcs11/gnutls_x509_crt_list_import_url.c pkcs11/list-objects.c \ +- pkcs11/list-tokens.c pkcs11/pkcs11-chainverify.c \ +- pkcs11/pkcs11-combo.c pkcs11/pkcs11-ec-privkey-test.c \ ++ pkcs11/list-tokens.c pkcs11/long-label.c \ ++ pkcs11/pkcs11-chainverify.c pkcs11/pkcs11-combo.c \ ++ pkcs11/pkcs11-ec-privkey-test.c \ + pkcs11/pkcs11-eddsa-privkey-test.c pkcs11/pkcs11-get-issuer.c \ + pkcs11/pkcs11-import-with-pin.c pkcs11/pkcs11-is-known.c \ + pkcs11/pkcs11-obj-import.c pkcs11/pkcs11-pin-func.c \ +@@ -6747,6 +6767,7 @@ TESTS_ENVIRONMENT = HOST_OS=$$(uname) $(am__append_31) CC="$(CC)" \ + P11MOCKLIB1=$(abs_builddir)/.libs/libpkcs11mock1.so \ + P11MOCKLIB2=$(abs_builddir)/.libs/libpkcs11mock2.so \ + P11MOCKLIB3=$(abs_builddir)/.libs/libpkcs11mock3.so \ ++ P11MOCKLIB4=$(abs_builddir)/.libs/libpkcs11mock4.so \ + PKCS12_MANY_CERTS_FILE=$(srcdir)/cert-tests/data/pkcs12_5certs.p12 \ + PKCS12FILE=$(srcdir)/cert-tests/data/client.p12 \ + PKCS12PASSWORD=foobar \ +@@ -7083,6 +7104,9 @@ ssl30_cert_key_exchange_SOURCES = common-cert-key-exchange.c ssl30-cert-key-exch + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock3_la_SOURCES = pkcs11/pkcs11-mock3.c + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock3_la_LDFLAGS = -shared -rpath $(pkglibdir) -module -no-undefined -avoid-version + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock3_la_LIBADD = ../gl/libgnu.la ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock4_la_SOURCES = pkcs11/pkcs11-mock4.c ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock4_la_LDFLAGS = -shared -rpath $(pkglibdir) -module -no-undefined -avoid-version ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@libpkcs11mock4_la_LIBADD = ../gl/libgnu.la + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_cert_import_url_exts_SOURCES = pkcs11/pkcs11-cert-import-url-exts.c + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_cert_import_url_exts_DEPENDENCIES = libpkcs11mock1.la libutils.la + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_cert_import_url4_exts_SOURCES = pkcs11/pkcs11-cert-import-url4-exts.c +@@ -7173,6 +7197,8 @@ pathbuf_CPPFLAGS = $(AM_CPPFLAGS) \ + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_tls_neg_pkcs11_no_key_LDADD = $(LDADD) $(LIBDL) + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_distrust_after_DEPENDENCIES = libpkcs11mock3.la libutils.la + @ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_distrust_after_LDADD = $(LDADD) $(LIBDL) ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_long_label_DEPENDENCIES = libpkcs11mock4.la libutils.la ++@ENABLE_PKCS11_TRUE@@WINDOWS_FALSE@pkcs11_long_label_LDADD = $(LDADD) $(LIBDL) + dist_check_SCRIPTS = rfc2253-escape-test.sh \ + rsa-md5-collision/rsa-md5-collision.sh systemkey.sh \ + $(am__append_18) $(am__append_20) $(am__append_21) \ +@@ -7280,6 +7306,11 @@ pkcs11/pkcs11-mock3.lo: pkcs11/$(am__dirstamp) \ + + libpkcs11mock3.la: $(libpkcs11mock3_la_OBJECTS) $(libpkcs11mock3_la_DEPENDENCIES) $(EXTRA_libpkcs11mock3_la_DEPENDENCIES) + $(AM_V_CCLD)$(libpkcs11mock3_la_LINK) $(am_libpkcs11mock3_la_rpath) $(libpkcs11mock3_la_OBJECTS) $(libpkcs11mock3_la_LIBADD) $(LIBS) ++pkcs11/pkcs11-mock4.lo: pkcs11/$(am__dirstamp) \ ++ pkcs11/$(DEPDIR)/$(am__dirstamp) ++ ++libpkcs11mock4.la: $(libpkcs11mock4_la_OBJECTS) $(libpkcs11mock4_la_DEPENDENCIES) $(EXTRA_libpkcs11mock4_la_DEPENDENCIES) ++ $(AM_V_CCLD)$(libpkcs11mock4_la_LINK) $(am_libpkcs11mock4_la_rpath) $(libpkcs11mock4_la_OBJECTS) $(libpkcs11mock4_la_LIBADD) $(LIBS) + + libutils.la: $(libutils_la_OBJECTS) $(libutils_la_DEPENDENCIES) $(EXTRA_libutils_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libutils_la_OBJECTS) $(libutils_la_LIBADD) $(LIBS) +@@ -8145,6 +8176,12 @@ pkcs11/list-tokens.$(OBJEXT): pkcs11/$(am__dirstamp) \ + pkcs11/list-tokens$(EXEEXT): $(pkcs11_list_tokens_OBJECTS) $(pkcs11_list_tokens_DEPENDENCIES) $(EXTRA_pkcs11_list_tokens_DEPENDENCIES) pkcs11/$(am__dirstamp) + @rm -f pkcs11/list-tokens$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(pkcs11_list_tokens_OBJECTS) $(pkcs11_list_tokens_LDADD) $(LIBS) ++pkcs11/long-label.$(OBJEXT): pkcs11/$(am__dirstamp) \ ++ pkcs11/$(DEPDIR)/$(am__dirstamp) ++ ++pkcs11/long-label$(EXEEXT): $(pkcs11_long_label_OBJECTS) $(pkcs11_long_label_DEPENDENCIES) $(EXTRA_pkcs11_long_label_DEPENDENCIES) pkcs11/$(am__dirstamp) ++ @rm -f pkcs11/long-label$(EXEEXT) ++ $(AM_V_CCLD)$(LINK) $(pkcs11_long_label_OBJECTS) $(pkcs11_long_label_LDADD) $(LIBS) + pkcs11/pkcs11-chainverify.$(OBJEXT): pkcs11/$(am__dirstamp) \ + pkcs11/$(DEPDIR)/$(am__dirstamp) + +@@ -9778,6 +9815,7 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/import_url_privkey_caps-pkcs11-import-url-privkey.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/list-objects.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/list-tokens.Po@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/long-label.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-cert-import-url-exts.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-cert-import-url4-exts.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-chainverify.Po@am__quote@ # am--include-marker +@@ -9794,6 +9832,7 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-mock.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-mock2.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-mock3.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-mock4.Plo@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-obj-import.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-obj-raw.Po@am__quote@ # am--include-marker + @AMDEP_TRUE@@am__include@ @am__quote@pkcs11/$(DEPDIR)/pkcs11-pin-func.Po@am__quote@ # am--include-marker +@@ -13673,6 +13712,13 @@ pkcs11/distrust-after.log: pkcs11/distrust-after$(EXEEXT) + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) ++pkcs11/long-label.log: pkcs11/long-label$(EXEEXT) ++ @p='pkcs11/long-label$(EXEEXT)'; \ ++ b='pkcs11/long-label'; \ ++ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ ++ --log-file $$b.log --trs-file $$b.trs \ ++ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ ++ "$$tst" $(AM_TESTS_FD_REDIRECT) + win32-certopenstore.log: win32-certopenstore$(EXEEXT) + @p='win32-certopenstore$(EXEEXT)'; \ + b='win32-certopenstore'; \ +@@ -14211,6 +14257,7 @@ distclean: distclean-recursive + -rm -f pkcs11/$(DEPDIR)/import_url_privkey_caps-pkcs11-import-url-privkey.Po + -rm -f pkcs11/$(DEPDIR)/list-objects.Po + -rm -f pkcs11/$(DEPDIR)/list-tokens.Po ++ -rm -f pkcs11/$(DEPDIR)/long-label.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-cert-import-url-exts.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-cert-import-url4-exts.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-chainverify.Po +@@ -14227,6 +14274,7 @@ distclean: distclean-recursive + -rm -f pkcs11/$(DEPDIR)/pkcs11-mock.Plo + -rm -f pkcs11/$(DEPDIR)/pkcs11-mock2.Plo + -rm -f pkcs11/$(DEPDIR)/pkcs11-mock3.Plo ++ -rm -f pkcs11/$(DEPDIR)/pkcs11-mock4.Plo + -rm -f pkcs11/$(DEPDIR)/pkcs11-obj-import.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-obj-raw.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-pin-func.Po +@@ -14734,6 +14782,7 @@ maintainer-clean: maintainer-clean-recursive + -rm -f pkcs11/$(DEPDIR)/import_url_privkey_caps-pkcs11-import-url-privkey.Po + -rm -f pkcs11/$(DEPDIR)/list-objects.Po + -rm -f pkcs11/$(DEPDIR)/list-tokens.Po ++ -rm -f pkcs11/$(DEPDIR)/long-label.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-cert-import-url-exts.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-cert-import-url4-exts.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-chainverify.Po +@@ -14750,6 +14799,7 @@ maintainer-clean: maintainer-clean-recursive + -rm -f pkcs11/$(DEPDIR)/pkcs11-mock.Plo + -rm -f pkcs11/$(DEPDIR)/pkcs11-mock2.Plo + -rm -f pkcs11/$(DEPDIR)/pkcs11-mock3.Plo ++ -rm -f pkcs11/$(DEPDIR)/pkcs11-mock4.Plo + -rm -f pkcs11/$(DEPDIR)/pkcs11-obj-import.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-obj-raw.Po + -rm -f pkcs11/$(DEPDIR)/pkcs11-pin-func.Po + diff --git a/tests/pkcs11/long-label.c b/tests/pkcs11/long-label.c new file mode 100644 index 000000000..a70bc9728 diff --git a/gnutls-3.8.10-CVE-2026-33845-dtls-uflow.patch b/gnutls-3.8.10-CVE-2026-33845-dtls-uflow.patch new file mode 100644 index 0000000..d392caf --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-33845-dtls-uflow.patch @@ -0,0 +1,470 @@ +From bd70e112d4d1f063223f0f0886aaaf33699390d0 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 22 Apr 2026 14:19:57 +0200 +Subject: [PATCH 1/5] buffers: rename a variable in parse_handshake_header + +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 09779a8f3..e9ddf0403 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -853,7 +853,7 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + { + uint8_t *dataptr = NULL; /* for realloc */ + size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session), +- data_size, frag_size; ++ data_size, frag_length; + + /* Note: SSL2_HEADERS == 1 */ + if (_mbuffer_get_udata_size(bufel) < handshake_header_size) +@@ -868,7 +868,7 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + handshake_header_size = + SSL2_HEADERS; /* we've already read one byte */ + +- frag_size = ++ frag_length = + _mbuffer_get_udata_size(bufel) - + handshake_header_size; /* we've read the first byte */ + +@@ -879,7 +879,7 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + + hsk->sequence = 0; + hsk->start_offset = 0; +- hsk->length = frag_size; ++ hsk->length = frag_length; + } else + #endif + { /* TLS or DTLS handshake headers */ +@@ -894,13 +894,13 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + if (IS_DTLS(session)) { + hsk->sequence = _gnutls_read_uint16(&dataptr[4]); + hsk->start_offset = _gnutls_read_uint24(&dataptr[6]); +- frag_size = _gnutls_read_uint24(&dataptr[9]); ++ frag_length = _gnutls_read_uint24(&dataptr[9]); + } else { + hsk->sequence = 0; + hsk->start_offset = 0; +- frag_size = MIN((_mbuffer_get_udata_size(bufel) - +- handshake_header_size), +- hsk->length); ++ frag_length = MIN((_mbuffer_get_udata_size(bufel) - ++ handshake_header_size), ++ hsk->length); + } + + /* TLS1.3: distinguish server hello versus hello retry request. +@@ -919,8 +919,8 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + } + data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; + +- if (frag_size > 0) +- hsk->end_offset = hsk->start_offset + frag_size - 1; ++ if (frag_length > 0) ++ hsk->end_offset = hsk->start_offset + frag_length - 1; + else + hsk->end_offset = 0; + +@@ -928,15 +928,15 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + "HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n", + session, _gnutls_handshake2str(hsk->htype), + (unsigned)hsk->htype, (int)hsk->length, (int)data_size, +- hsk->start_offset, (int)frag_size, (int)hsk->sequence); ++ hsk->start_offset, (int)frag_length, (int)hsk->sequence); + + hsk->header_size = handshake_header_size; + memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), + handshake_header_size); + + if (hsk->length > 0 && +- (frag_size > data_size || +- (frag_size > 0 && hsk->end_offset >= hsk->length))) { ++ (frag_length > data_size || ++ (frag_length > 0 && hsk->end_offset >= hsk->length))) { + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } else if (hsk->length == 0 && hsk->end_offset != 0 && + hsk->start_offset != 0) +-- +2.53.0 + + +From e5b72c53c7d789d19d1d1cd10b275e87d0415413 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 23 Mar 2026 15:09:43 +0100 +Subject: [PATCH 2/5] buffers: switch from end_offset over to frag_length + +Instead of maintaining an inclusive [start_offset, end_offset] range +when reassembling DTLS handshake, +track start_offset and a relative frag_length instead. + +You'd think it'd be a no-op, but it fixes: + +* 0-length fragments triggering completion if message was 1 byte long +* a remotely triggerable underflow and an ensuing heap overrun + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1811 +Fixes: CVE-2026-33845 +Fixes: GNUTLS-SA-2026-04-29-3 +CVSS: 7.5 High CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 51 +++++++++++++++++++++++++----------------------- + lib/gnutls_int.h | 4 ++-- + 2 files changed, 29 insertions(+), 26 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index e9ddf0403..c3df8a37b 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -919,10 +919,7 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + } + data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; + +- if (frag_length > 0) +- hsk->end_offset = hsk->start_offset + frag_length - 1; +- else +- hsk->end_offset = 0; ++ hsk->frag_length = frag_length; + + _gnutls_handshake_log( + "HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n", +@@ -936,9 +933,11 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + + if (hsk->length > 0 && + (frag_length > data_size || +- (frag_length > 0 && hsk->end_offset >= hsk->length))) { ++ (frag_length > 0 && ++ hsk->start_offset + frag_length > hsk->length))) { + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); +- } else if (hsk->length == 0 && hsk->end_offset != 0 && ++ } else if (hsk->length == 0 && ++ hsk->start_offset + frag_length != hsk->start_offset && + hsk->start_offset != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + +@@ -1002,11 +1001,10 @@ static int merge_handshake_packet(gnutls_session_t session, + hsk->data.length = hsk->length; + } + +- if (hsk->length > 0 && hsk->end_offset > 0 && +- hsk->end_offset - hsk->start_offset + 1 != hsk->length) { ++ if (hsk->length > 0 && hsk->frag_length > 0 && ++ hsk->frag_length != hsk->length) { + memmove(&hsk->data.data[hsk->start_offset], +- hsk->data.data, +- hsk->end_offset - hsk->start_offset + 1); ++ hsk->data.data, hsk->frag_length); + } + + session->internals.handshake_recv_buffer_size++; +@@ -1040,20 +1038,27 @@ static int merge_handshake_packet(gnutls_session_t session, + } + + if (hsk->start_offset < recv_buf[pos].start_offset && +- hsk->end_offset + 1 >= recv_buf[pos].start_offset) { ++ hsk->start_offset + hsk->frag_length >= ++ recv_buf[pos].start_offset) { + memcpy(&recv_buf[pos].data.data[hsk->start_offset], + hsk->data.data, hsk->data.length); + recv_buf[pos].start_offset = hsk->start_offset; +- recv_buf[pos].end_offset = +- MIN(hsk->end_offset, recv_buf[pos].end_offset); +- } else if (hsk->end_offset > recv_buf[pos].end_offset && +- hsk->start_offset <= recv_buf[pos].end_offset + 1) { ++ recv_buf[pos].frag_length = MIN( ++ hsk->frag_length, recv_buf[pos].frag_length); ++ } else if (hsk->start_offset + hsk->frag_length > ++ recv_buf[pos].start_offset + ++ recv_buf[pos].frag_length && ++ hsk->start_offset <= ++ recv_buf[pos].start_offset + ++ recv_buf[pos].frag_length) { + memcpy(&recv_buf[pos].data.data[hsk->start_offset], + hsk->data.data, hsk->data.length); + +- recv_buf[pos].end_offset = hsk->end_offset; + recv_buf[pos].start_offset = MIN( + hsk->start_offset, recv_buf[pos].start_offset); ++ recv_buf[pos].frag_length = hsk->start_offset + ++ hsk->frag_length - ++ recv_buf[pos].start_offset; + } + _gnutls_handshake_buffer_clear(hsk); + } +@@ -1113,8 +1118,8 @@ static int get_last_packet(gnutls_session_t session, + } + + else if ((recv_buf[LAST_ELEMENT].start_offset == 0 && +- recv_buf[LAST_ELEMENT].end_offset == +- recv_buf[LAST_ELEMENT].length - 1) || ++ recv_buf[LAST_ELEMENT].frag_length == ++ recv_buf[LAST_ELEMENT].length) || + recv_buf[LAST_ELEMENT].length == 0) { + session->internals.dtls.hsk_read_seq++; + _gnutls_handshake_buffer_move(hsk, +@@ -1125,8 +1130,9 @@ static int get_last_packet(gnutls_session_t session, + /* if we don't have a complete handshake message, but we + * have queued data waiting, try again to reconstruct the + * handshake packet, using the queued */ +- if (recv_buf[LAST_ELEMENT].end_offset != +- recv_buf[LAST_ELEMENT].length - 1 && ++ if ((recv_buf[LAST_ELEMENT].start_offset + ++ recv_buf[LAST_ELEMENT].frag_length) != ++ recv_buf[LAST_ELEMENT].length && + record_check_unprocessed(session) > 0) + return gnutls_assert_val( + GNUTLS_E_INT_CHECK_AGAIN); +@@ -1313,9 +1319,7 @@ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) + &session->internals.record_buffer, + bufel, ret); + +- data_size = MIN(tmp.length, +- tmp.end_offset - +- tmp.start_offset + 1); ++ data_size = MIN(tmp.length, tmp.frag_length); + + ret = _gnutls_buffer_append_data( + &tmp.data, +@@ -1331,7 +1335,6 @@ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) + ret = merge_handshake_packet(session, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); +- + } while (_mbuffer_get_udata_size(bufel) > 0); + + prev = bufel; +diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h +index d1643be9d..3e5a8f361 100644 +--- a/lib/gnutls_int.h ++++ b/lib/gnutls_int.h +@@ -460,10 +460,10 @@ typedef struct { + uint16_t sequence; + + /* indicate whether that message is complete. +- * complete means start_offset == 0 and end_offset == length ++ * complete means start_offset == 0 and frag_length == length + */ + uint32_t start_offset; +- uint32_t end_offset; ++ uint32_t frag_length; /* used exclusively in DTLS reassembly */ + + uint8_t header[MAX_HANDSHAKE_HEADER_SIZE]; + int header_size; +-- +2.53.0 + + +From 21563d8778dfec2d87d415d7e7f7ba3b66f1d283 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 23 Mar 2026 15:57:39 +0100 +Subject: [PATCH 3/5] buffers: simplify and tighten parse_handshake_header + checks + +* frag_size > data_size is now rejected even when length == 0 +* length == 0 && frag_size > 0 is now rejected even when start_offset == 0 +* start_offset > length is now rejected even when frag_size == 0 + +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index c3df8a37b..af77c5c0f 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -931,14 +931,9 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel, + memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), + handshake_header_size); + +- if (hsk->length > 0 && +- (frag_length > data_size || +- (frag_length > 0 && +- hsk->start_offset + frag_length > hsk->length))) { ++ if (frag_length > data_size) /* fragment straight up lying to us */ + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); +- } else if (hsk->length == 0 && +- hsk->start_offset + frag_length != hsk->start_offset && +- hsk->start_offset != 0) ++ if (frag_length + hsk->start_offset > hsk->length) /* reassembly OOB */ + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + return handshake_header_size; +-- +2.53.0 + + +From 1057846cd5c611037113327f5ae38af2c5cd7c87 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 20 Mar 2026 16:55:10 +0100 +Subject: [PATCH 4/5] tests/mini-dtls-fragments: test injecting 0-length ones + +Signed-off-by: Alexander Sosedkin +--- + tests/mini-dtls-fragments.c | 47 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index 499a92a92..cde0ca5e6 100644 +--- a/tests/mini-dtls-fragments.c ++++ b/tests/mini-dtls-fragments.c +@@ -165,6 +165,50 @@ static uint64_t read_u48(const uint8_t *p) + return seq; + } + ++static void make_0frag(uint8_t *dst, const uint8_t *src) ++{ ++ memcpy(dst, src, 13 + 12); ++ dst[13 + 6] = dst[13 + 7] = dst[13 + 8] = 0; /* frag offset = 0 */ ++ dst[13 + 9] = dst[13 + 10] = dst[13 + 11] = 0; /* frag length = 0 */ ++ /* record payload length: just the 12-byte handshake header, no data */ ++ dst[11] = 0; ++ dst[12] = 12; ++} ++ ++ATTRIBUTE_NONNULL((2)) ++static ssize_t client_push_inj0(gnutls_transport_ptr_t tr, const void *d_, ++ size_t l) ++{ ++ static uint32_t seq = 0; ++ const uint8_t *d = (const uint8_t *)d_; ++ uint8_t frag[13 + 12]; ++ uint8_t *b; ++ ++ if (l < 13) /* too short for a DTLS record header */ ++ return queue_put(&c2s, d, l); ++ if (!(d[3] == 0 && d[4] == 0)) /* not epoch 0: encrypted, don't touch */ ++ return queue_put(&c2s, d, l); ++ ++ b = malloc(l); ++ assert(b); ++ memcpy(b, d, l); ++ ++ if (l >= 13 + 12 && d[0] == 22) { /* handshake record: inject 0-frag */ ++ make_0frag(frag, d); ++ write_u48(frag + 5, seq++); /* 0-frag first */ ++ queue_put(&c2s, frag, sizeof(frag)); ++ ++ write_u48(b + 5, seq++); /* real second */ ++ queue_put(&c2s, b, l); ++ } else { /* other (e.g. CCS): just renumber */ ++ write_u48(b + 5, seq++); ++ queue_put(&c2s, b, l); ++ } ++ ++ free(b); ++ return l; ++} ++ + static void test(gnutls_push_func client_push, bool expect_success) + { + gnutls_session_t client, server; +@@ -462,7 +506,10 @@ static ssize_t client_push_split_hello_bad_seq(gnutls_transport_ptr_t tr, + void doit(void) + { + global_init(); ++ success("normal:\n"); + test(client_push_normal, true); ++ success("valid 0-len fragments injected every 2nd push in epoch0:\n"); ++ test(client_push_inj0, true); + success("malicious reassembly bug exploitation (#1816):\n"); + test_malicious1816(); + success("split client hello smoke-test\n"); +-- +2.53.0 + + +From d59d9cc7943568ee06e5ba35fd7ae2a6e433059f Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 23 Mar 2026 20:24:26 +0100 +Subject: [PATCH 5/5] tests/mini-dtls-fragments: test #1811 crashing datagram + +Signed-off-by: Alexander Sosedkin +--- + tests/mini-dtls-fragments.c | 59 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 59 insertions(+) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index cde0ca5e6..ce61eb947 100644 +--- a/tests/mini-dtls-fragments.c ++++ b/tests/mini-dtls-fragments.c +@@ -503,6 +503,63 @@ static ssize_t client_push_split_hello_bad_seq(gnutls_transport_ptr_t tr, + return l; + } + ++static void test_malicious1811(void) ++{ ++ static const uint8_t dgram[] = { ++ 22, /* type = handshake */ ++ 0xfe, 0xfd, /* version = DTLS 1.2 */ ++ 0x00, 0x00, /* epoch = 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* seq = 0 */ ++ 0x00, 0x0c, /* record length = 12 */ ++ ++ 0x01, /* type = ClientHello */ ++ 0xff, 0xff, 0xff, /* length = 0xffffff (!) */ ++ 0x00, 0x00, /* msg seq = 0 */ ++ 0x00, 0x00, 0x02, /* frag_offset = 2 (!) */ ++ 0x00, 0x00, 0x00, /* frag_length = 0 (!) */ ++ }; ++ gnutls_session_t server; ++ gnutls_certificate_credentials_t scred; ++ int sr; ++ ++ if (debug) ++ gnutls_global_set_log_level(4711); ++ ++ gnutls_certificate_allocate_credentials(&scred); ++ gnutls_certificate_set_x509_key_mem(scred, &server_cert, &server_key, ++ GNUTLS_X509_FMT_PEM); ++ ++ gnutls_init(&server, GNUTLS_SERVER | GNUTLS_DATAGRAM); ++ gnutls_priority_set_direct(server, "NORMAL:+VERS-DTLS1.2", NULL); ++ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred); ++ ++ gnutls_dtls_set_timeouts(server, get_dtls_retransmit_timeout(), ++ get_timeout()); ++ ++ gnutls_transport_set_ptr(server, server); ++ gnutls_transport_set_push_function(server, server_push); ++ gnutls_transport_set_pull_function(server, server_pull); ++ gnutls_transport_set_pull_timeout_function(server, ++ c2s_pull_timeout_once); ++ ++ queue_put(&c2s, dgram, sizeof(dgram)); ++ ++ gnutls_global_set_log_function(server_log_func); ++ do { ++ sr = gnutls_handshake(server); /* crashes if vulnerable */ ++ } while (c2s.head != c2s.tail && !gnutls_error_is_fatal(sr)); ++ if (gnutls_error_is_fatal(sr)) ++ fail("server: %s\n", gnutls_strerror(sr)); ++ ++ success("OK\n"); ++ ++ queue_reset(&c2s); ++ queue_reset(&s2c); ++ ++ gnutls_deinit(server); ++ gnutls_certificate_free_credentials(scred); ++} ++ + void doit(void) + { + global_init(); +@@ -516,6 +573,8 @@ void doit(void) + test(client_push_split_hello, true); + success("split client hello smoke-test and mangle sequence number\n"); + test(client_push_split_hello_bad_seq, false); ++ success("malicious injection aiming for an underflow (#1811):\n"); ++ test_malicious1811(); + gnutls_global_deinit(); + } + +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-33846-dtls-len.patch b/gnutls-3.8.10-CVE-2026-33846-dtls-len.patch new file mode 100644 index 0000000..d8f0314 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-33846-dtls-len.patch @@ -0,0 +1,851 @@ +From 4f94e5cfe1f252a431e41642b0752e7e0daf43b9 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 20 Mar 2026 16:09:40 +0100 +Subject: [PATCH 1/7] tests/mini-dtls-fragments: implement a basic DTLS test + +Signed-off-by: Alexander Sosedkin +--- + tests/Makefile.am | 7 +- + tests/mini-dtls-fragments.c | 208 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 214 insertions(+), 1 deletion(-) + create mode 100644 tests/mini-dtls-fragments.c + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index aeeaaf79d..586f1952d 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -241,7 +241,8 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei + x509cert-dntypes id-on-xmppAddr tls13-compat-mode ciphersuite-name \ + x509-upnconstraint xts-key-check cipher-padding pkcs7-verify-double-free \ + fips-rsa-sizes tls12-rehandshake-ticket pathbuf tls-force-ems \ +- psk-importer privkey-derive dh-compute2 ecdh-compute2 ++ psk-importer privkey-derive dh-compute2 ecdh-compute2 \ ++ mini-dtls-fragments + + ctests += tls-channel-binding + +@@ -513,6 +514,10 @@ pathbuf_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(top_srcdir)/gl \ + -I$(top_builddir)/gl + ++mini_dtls_fragments_CPPFLAGS = $(AM_CPPFLAGS) \ ++ -I$(top_srcdir)/gl \ ++ -I$(top_builddir)/gl ++ + if ENABLE_PKCS11 + if !WINDOWS + ctests += tls13/post-handshake-with-cert-pkcs11 pkcs11/tls-neg-pkcs11-no-key \ +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +new file mode 100644 +index 000000000..ee75feeb6 +--- /dev/null ++++ b/tests/mini-dtls-fragments.c +@@ -0,0 +1,208 @@ ++/* ++ * Copyright (C) 2026 Red Hat, Inc. ++ * ++ * Author: Alexander Sosedkin ++ * ++ * This file is part of GnuTLS. ++ * ++ * GnuTLS is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GnuTLS is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program. If not, see ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++ ++#if defined(_WIN32) ++ ++int main(void) ++{ ++ exit(77); ++} ++ ++#else ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "cert-common.h" ++#include "utils.h" ++ ++#include "attribute.h" ++ ++static void server_log_func(int level, const char *str) ++{ ++ fprintf(stderr, "server|<%d>| %s", level, str); ++} ++ ++static void client_log_func(int level, const char *str) ++{ ++ fprintf(stderr, "client|<%d>| %s", level, str); ++} ++ ++#define QUEUE_SIZE 1024 ++#define PACKET_SIZE 2048 ++ ++typedef struct { ++ uint8_t buf[PACKET_SIZE]; ++ size_t len; ++} packet_t; ++typedef struct { ++ packet_t packets[QUEUE_SIZE]; ++ size_t head; ++ size_t tail; ++} queue_t; ++ ++static queue_t c2s, s2c; ++ ++static int queue_put(queue_t *q, const void *buf, size_t len) ++{ ++ assert(len <= PACKET_SIZE); ++ memcpy(q->packets[q->tail].buf, buf, len); ++ q->packets[q->tail].len = len; ++ q->tail++; ++ q->tail %= QUEUE_SIZE; ++ assert(q->tail != q->head); ++ return len; ++} ++ ++static ssize_t queue_get(queue_t *q, gnutls_session_t s, void *buf, size_t len) ++{ ++ if (q->head == q->tail) { ++ gnutls_transport_set_errno(s, EAGAIN); ++ return -1; ++ } ++ size_t n = q->packets[q->head].len; ++ memcpy(buf, q->packets[q->head].buf, n); ++ q->head++; ++ q->head %= QUEUE_SIZE; ++ return n; ++} ++ ++static void queue_reset(queue_t *q) ++{ ++ q->head = q->tail = 0; ++} ++ ++static int pull_timeout(gnutls_transport_ptr_t tr, unsigned ms) ++{ ++ return 1; ++} ++ ++static ssize_t server_pull(gnutls_transport_ptr_t tr, void *b, size_t l) ++{ ++ return queue_get(&c2s, (gnutls_session_t)tr, b, l); ++} ++ ++static ssize_t client_pull(gnutls_transport_ptr_t tr, void *b, size_t l) ++{ ++ return queue_get(&s2c, (gnutls_session_t)tr, b, l); ++} ++ ++static ssize_t server_push(gnutls_transport_ptr_t tr, const void *b, size_t l) ++{ ++ return queue_put(&s2c, b, l); ++} ++ ++static ssize_t client_push_normal(gnutls_transport_ptr_t tr, const void *b, ++ size_t l) ++{ ++ return queue_put(&c2s, b, l); ++} ++ ++static void test(gnutls_push_func client_push) ++{ ++ gnutls_session_t client, server; ++ gnutls_certificate_credentials_t ccred, scred; ++ int cr = 0, sr = 0; ++ bool cdone = false, sdone = false; ++ ++ if (debug) ++ gnutls_global_set_log_level(4711); ++ ++ gnutls_certificate_allocate_credentials(&scred); ++ gnutls_certificate_set_x509_key_mem(scred, &server_cert, &server_key, ++ GNUTLS_X509_FMT_PEM); ++ gnutls_certificate_allocate_credentials(&ccred); ++ ++ gnutls_init(&server, GNUTLS_SERVER | GNUTLS_DATAGRAM); ++ gnutls_init(&client, GNUTLS_CLIENT | GNUTLS_DATAGRAM); ++ ++ gnutls_priority_set_direct(server, "NORMAL:-VERS-ALL:+VERS-DTLS1.2", ++ NULL); ++ gnutls_priority_set_direct(client, "NORMAL:-VERS-ALL:+VERS-DTLS1.2", ++ NULL); ++ ++ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred); ++ gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred); ++ ++ gnutls_dtls_set_timeouts(client, get_dtls_retransmit_timeout(), ++ get_timeout()); ++ gnutls_dtls_set_timeouts(server, get_dtls_retransmit_timeout(), ++ get_timeout()); ++ ++ gnutls_transport_set_ptr(client, client); ++ gnutls_transport_set_push_function(client, client_push); ++ gnutls_transport_set_pull_function(client, client_pull); ++ gnutls_transport_set_pull_timeout_function(client, pull_timeout); ++ ++ gnutls_transport_set_ptr(server, server); ++ gnutls_transport_set_push_function(server, server_push); ++ gnutls_transport_set_pull_function(server, server_pull); ++ gnutls_transport_set_pull_timeout_function(server, pull_timeout); ++ ++ while (!cdone || !sdone) { ++ gnutls_global_set_log_function(client_log_func); ++ if (!cdone) ++ cr = gnutls_handshake(client); ++ if (!cr || gnutls_error_is_fatal(cr)) ++ cdone = true; ++ ++ gnutls_global_set_log_function(server_log_func); ++ if (!sdone) ++ sr = gnutls_handshake(server); ++ if (!sr || gnutls_error_is_fatal(sr)) ++ sdone = true; ++ } ++ ++ if (cr) ++ fail("client: %s\n", gnutls_strerror(cr)); ++ if (sr) ++ fail("server: %s\n", gnutls_strerror(sr)); ++ ++ success("OK\n"); ++ ++ queue_reset(&c2s); ++ queue_reset(&s2c); ++ ++ gnutls_deinit(client); ++ gnutls_deinit(server); ++ gnutls_certificate_free_credentials(ccred); ++ gnutls_certificate_free_credentials(scred); ++} ++ ++void doit(void) ++{ ++ global_init(); ++ test(client_push_normal); ++ gnutls_global_deinit(); ++} ++ ++#endif /* _WIN32 */ +-- +2.53.0 + + +From 9deffca528c23bbb218f5ec3bd4bb1bf4cbd1fc0 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 17 Apr 2026 17:49:31 +0200 +Subject: [PATCH 2/7] buffers: shorten merge_handshake_packet using recv_buf + +I had vague concerns about thread-safety of this, +but then this pattern already exists within the file. + +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 52 +++++++++++++++++---------------------------------- + 1 file changed, 17 insertions(+), 35 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 672380b05..d54c77022 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -967,9 +967,11 @@ static int merge_handshake_packet(gnutls_session_t session, + int exists = 0, i, pos = 0; + int ret; + ++ handshake_buffer_st *recv_buf = ++ session->internals.handshake_recv_buffer; ++ + for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) { +- if (session->internals.handshake_recv_buffer[i].htype == +- hsk->htype) { ++ if (recv_buf[i].htype == hsk->htype) { + exists = 1; + pos = i; + break; +@@ -1005,44 +1007,24 @@ static int merge_handshake_packet(gnutls_session_t session, + _gnutls_write_uint24(0, &hsk->header[6]); + _gnutls_write_uint24(hsk->length, &hsk->header[9]); + +- _gnutls_handshake_buffer_move( +- &session->internals.handshake_recv_buffer[pos], hsk); ++ _gnutls_handshake_buffer_move(&recv_buf[pos], hsk); + + } else { +- if (hsk->start_offset < +- session->internals.handshake_recv_buffer[pos] +- .start_offset && +- hsk->end_offset + 1 >= +- session->internals.handshake_recv_buffer[pos] +- .start_offset) { +- memcpy(&session->internals.handshake_recv_buffer[pos] +- .data.data[hsk->start_offset], ++ if (hsk->start_offset < recv_buf[pos].start_offset && ++ hsk->end_offset + 1 >= recv_buf[pos].start_offset) { ++ memcpy(&recv_buf[pos].data.data[hsk->start_offset], + hsk->data.data, hsk->data.length); +- session->internals.handshake_recv_buffer[pos] +- .start_offset = hsk->start_offset; +- session->internals.handshake_recv_buffer[pos] +- .end_offset = MIN( +- hsk->end_offset, +- session->internals.handshake_recv_buffer[pos] +- .end_offset); +- } else if (hsk->end_offset > +- session->internals.handshake_recv_buffer[pos] +- .end_offset && +- hsk->start_offset <= +- session->internals.handshake_recv_buffer[pos] +- .end_offset + +- 1) { +- memcpy(&session->internals.handshake_recv_buffer[pos] +- .data.data[hsk->start_offset], ++ recv_buf[pos].start_offset = hsk->start_offset; ++ recv_buf[pos].end_offset = ++ MIN(hsk->end_offset, recv_buf[pos].end_offset); ++ } else if (hsk->end_offset > recv_buf[pos].end_offset && ++ hsk->start_offset <= recv_buf[pos].end_offset + 1) { ++ memcpy(&recv_buf[pos].data.data[hsk->start_offset], + hsk->data.data, hsk->data.length); + +- session->internals.handshake_recv_buffer[pos] +- .end_offset = hsk->end_offset; +- session->internals.handshake_recv_buffer[pos] +- .start_offset = MIN( +- hsk->start_offset, +- session->internals.handshake_recv_buffer[pos] +- .start_offset); ++ recv_buf[pos].end_offset = hsk->end_offset; ++ recv_buf[pos].start_offset = MIN( ++ hsk->start_offset, recv_buf[pos].start_offset); + } + _gnutls_handshake_buffer_clear(hsk); + } +-- +2.53.0 + + +From 65ab33fa54e34fba69d793735b7df3d383d1ff78 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 17 Apr 2026 18:21:36 +0200 +Subject: [PATCH 3/7] buffers: add more checks to DTLS reassembly + +Previously, gnutls didn't check that DTLS fragments claimed +a consistent message_length value. +Additionally, a crucial array size check was missing, +enabling an attacker to cause a heap overwrite. +The updated version rejects fragments with mismatching length +and adds a missing boundary check. + +Reported-by: Haruto Kimura (Stella) +Reported-by: Oscar Reparaz +Reported-by: Zou Dikai +Fixes: #1816 +Fixes: #1838 +Fixes: #1839 +Fixes: CVE-2026-33846 +Fixes: GNUTLS-SA-2026-04-29-1 +CVSS: 7.4 High CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H +CVSS: 7.5 High CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/lib/buffers.c b/lib/buffers.c +index d54c77022..5d4d16276 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -1010,6 +1010,26 @@ static int merge_handshake_packet(gnutls_session_t session, + _gnutls_handshake_buffer_move(&recv_buf[pos], hsk); + + } else { ++ if (hsk->length != recv_buf[pos].length) { ++ /* inconsistent across fragments */ ++ _gnutls_handshake_buffer_clear(hsk); ++ return gnutls_assert_val( ++ GNUTLS_E_UNEXPECTED_PACKET_LENGTH); ++ } ++ /* start_offset + data.length <= hsk->length <= max_length */ ++ if (hsk->length < hsk->start_offset + hsk->data.length) { ++ /* impossible claims, overflow requested */ ++ _gnutls_handshake_buffer_clear(hsk); ++ return gnutls_assert_val( ++ GNUTLS_E_UNEXPECTED_PACKET_LENGTH); ++ } ++ if (hsk->length > recv_buf[pos].data.max_length) { ++ /* we don't have this much allocated, overflow guard */ ++ _gnutls_handshake_buffer_clear(hsk); ++ return gnutls_assert_val( ++ GNUTLS_E_UNEXPECTED_PACKET_LENGTH); ++ } ++ + if (hsk->start_offset < recv_buf[pos].start_offset && + hsk->end_offset + 1 >= recv_buf[pos].start_offset) { + memcpy(&recv_buf[pos].data.data[hsk->start_offset], +-- +2.53.0 + + +From cf3f1955e58cbcc10373b841bb101fb058565d87 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 1 Apr 2026 19:51:45 +0200 +Subject: [PATCH 4/7] tests/mini-dtls-fragments: extend with a #1816 reproducer + +Signed-off-by: Alexander Sosedkin +--- + tests/mini-dtls-fragments.c | 120 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 120 insertions(+) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index ee75feeb6..8d5a18acd 100644 +--- a/tests/mini-dtls-fragments.c ++++ b/tests/mini-dtls-fragments.c +@@ -106,6 +106,11 @@ static int pull_timeout(gnutls_transport_ptr_t tr, unsigned ms) + return 1; + } + ++static int c2s_pull_timeout_once(gnutls_transport_ptr_t tr, unsigned ms) ++{ ++ return c2s.head != c2s.tail ? 1 : 0; ++} ++ + static ssize_t server_pull(gnutls_transport_ptr_t tr, void *b, size_t l) + { + return queue_get(&c2s, (gnutls_session_t)tr, b, l); +@@ -198,10 +203,125 @@ static void test(gnutls_push_func client_push) + gnutls_certificate_free_credentials(scred); + } + ++static void test_malicious1816(void) ++{ ++ /* dgram1: msg_len=50, frag_offset=25, frag_len=25 */ ++ static const uint8_t dgram1_hdr[] = { ++ 0x16, /* type: handshake */ ++ 0xfe, 0xfd, /* version: DTLS 1.2 */ ++ 0x00, 0x00, /* epoch: 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* seq: 0 */ ++ 0x00, 0x25, /* record_length: 37 */ ++ 0x01, /* msg_type: ClientHello */ ++ 0x00, 0x00, 0x32, /* msg_length: 50 */ ++ 0x00, 0x00, /* msg_seq: 0 */ ++ 0x00, 0x00, 0x19, /* frag_offset: 25 */ ++ 0x00, 0x00, 0x19, /* frag_length: 25 */ ++ }; ++ /* dgram2: msg_len=3000, frag_offset=0, frag_len=48 */ ++ static const uint8_t dgram2_hdr[] = { ++ 0x16, /* type: handshake */ ++ 0xfe, 0xfd, /* version: DTLS 1.2 */ ++ 0x00, 0x00, /* epoch: 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* seq: 1 */ ++ 0x00, 0x3c, /* record_length: 60 */ ++ 0x01, /* msg_type: ClientHello */ ++ 0x00, 0x0b, 0xb8, /* msg_length: 3000 */ ++ 0x00, 0x00, /* msg_seq: 0 */ ++ 0x00, 0x00, 0x00, /* frag_offset: 0 */ ++ 0x00, 0x00, 0x30, /* frag_length: 48 */ ++ }; ++ /* dgram3: msg_len=3000, frag_offset=40, frag_len=1475 */ ++ static const uint8_t dgram3_hdr[] = { ++ 0x16, /* type: handshake */ ++ 0xfe, 0xfd, /* version: DTLS 1.2 */ ++ 0x00, 0x00, /* epoch: 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, /* seq: 2 */ ++ 0x05, 0xcf, /* record_length: 1487 */ ++ 0x01, /* msg_type: ClientHello */ ++ 0x00, 0x0b, 0xb8, /* msg_length: 3000 */ ++ 0x00, 0x00, /* msg_seq: 0 */ ++ 0x00, 0x00, 0x28, /* frag_offset: 40 */ ++ 0x00, 0x05, 0xc3, /* frag_length: 1475 */ ++ }; ++ /* dgram4: msg_len=3000, frag_offset=1500, frag_len=1475 */ ++ static const uint8_t dgram4_hdr[] = { ++ 0x16, /* type: handshake */ ++ 0xfe, 0xfd, /* version: DTLS 1.2 */ ++ 0x00, 0x00, /* epoch: 0 */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, /* seq: 3 */ ++ 0x05, 0xcf, /* record_length: 1487 */ ++ 0x01, /* msg_type: ClientHello */ ++ 0x00, 0x0b, 0xb8, /* msg_length: 3000 */ ++ 0x00, 0x00, /* msg_seq: 0 */ ++ 0x00, 0x05, 0xdc, /* frag_offset: 1500 */ ++ 0x00, 0x05, 0xc3, /* frag_length: 1475 */ ++ }; ++ gnutls_session_t server; ++ gnutls_certificate_credentials_t scred; ++ uint8_t dgram[1500]; ++ int sr; ++ ++ if (debug) ++ gnutls_global_set_log_level(4711); ++ ++ gnutls_certificate_allocate_credentials(&scred); ++ gnutls_certificate_set_x509_key_mem(scred, &server_cert, &server_key, ++ GNUTLS_X509_FMT_PEM); ++ ++ gnutls_init(&server, GNUTLS_SERVER | GNUTLS_DATAGRAM); ++ gnutls_priority_set_direct(server, "NORMAL:+VERS-DTLS1.2", NULL); ++ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred); ++ ++ gnutls_dtls_set_timeouts(server, get_dtls_retransmit_timeout(), ++ get_timeout()); ++ ++ gnutls_transport_set_ptr(server, server); ++ gnutls_transport_set_push_function(server, server_push); ++ gnutls_transport_set_pull_function(server, server_pull); ++ gnutls_transport_set_pull_timeout_function(server, ++ c2s_pull_timeout_once); ++ ++ memset(dgram, 0, sizeof(dgram)); ++ memcpy(dgram, dgram1_hdr, 25); ++ queue_put(&c2s, dgram, 25 + 25); ++ ++ memset(dgram, 0, sizeof(dgram)); ++ memcpy(dgram, dgram2_hdr, 25); ++ queue_put(&c2s, dgram, 25 + 48); ++ ++ memset(dgram, 0, sizeof(dgram)); ++ memcpy(dgram, dgram3_hdr, 25); ++ queue_put(&c2s, dgram, 25 + 1475); ++ ++ memset(dgram, 0, sizeof(dgram)); ++ memcpy(dgram, dgram4_hdr, 25); ++ queue_put(&c2s, dgram, 25 + 1475); ++ ++ gnutls_global_set_log_function(server_log_func); ++ do { ++ sr = gnutls_handshake(server); /* invalid write if vulnerable */ ++ } while (c2s.head != c2s.tail && !gnutls_error_is_fatal(sr)); ++ if (sr != GNUTLS_E_UNEXPECTED_PACKET_LENGTH) ++ fail("server: expected GNUTLS_E_UNEXPECTED_PACKET_LENGTH, " ++ "got: %s\n", ++ gnutls_strerror(sr)); ++ ++ success("OK\n"); ++ ++ queue_reset(&c2s); ++ queue_reset(&s2c); ++ ++ gnutls_deinit(server); ++ gnutls_certificate_free_credentials(scred); ++} ++ + void doit(void) + { + global_init(); + test(client_push_normal); ++ success("malicious reassembly bug exploitation (#1816):\n"); ++ test_malicious1816(); + gnutls_global_deinit(); + } + +-- +2.53.0 + + +From bb427ff74dba849d40753ed9c8511e873f762743 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 20 Apr 2026 16:08:11 +0200 +Subject: [PATCH 5/7] tests/mini-dtls-fragments: extend with fragmenting + ClientHello + +Signed-off-by: Alexander Sosedkin +--- + tests/mini-dtls-fragments.c | 107 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 107 insertions(+) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index 8d5a18acd..93490bac2 100644 +--- a/tests/mini-dtls-fragments.c ++++ b/tests/mini-dtls-fragments.c +@@ -132,6 +132,39 @@ static ssize_t client_push_normal(gnutls_transport_ptr_t tr, const void *b, + return queue_put(&c2s, b, l); + } + ++static void write_u16(uint8_t *p, uint16_t val) ++{ ++ p[0] = val >> 8; ++ p[1] = val & 0xff; ++} ++ ++static void write_u24(uint8_t *p, uint32_t val) ++{ ++ p[0] = (val >> 16) & 0xff; ++ p[1] = (val >> 8) & 0xff; ++ p[2] = val & 0xff; ++} ++ ++static void write_u48(uint8_t *p, uint64_t seq) ++{ ++ int i; ++ for (i = 5; i >= 0; i--) { ++ p[i] = seq & 0xff; ++ seq >>= 8; ++ } ++} ++ ++static uint64_t read_u48(const uint8_t *p) ++{ ++ uint64_t seq = 0; ++ int i; ++ for (i = 5; i >= 0; i--) { ++ seq <<= 8; ++ seq |= p[i]; ++ } ++ return seq; ++} ++ + static void test(gnutls_push_func client_push) + { + gnutls_session_t client, server; +@@ -316,12 +349,86 @@ static void test_malicious1816(void) + gnutls_certificate_free_credentials(scred); + } + ++static ssize_t queue_put_renumbered(queue_t *q, const uint8_t *data, size_t l, ++ int delta_n) ++{ ++ if (delta_n == 0 || l < 13 || data[3] != 0 || data[4] != 0) ++ return queue_put(&c2s, data, l); ++ ++ uint8_t *p = malloc(l); ++ assert(p); ++ memcpy(p, data, l); ++ write_u48(p + 5, read_u48(p + 5) + delta_n); ++ ssize_t ret = queue_put(q, p, l); ++ free(p); ++ return ret; ++} ++ ++static void split_client_hello(const uint8_t *data, size_t len, uint8_t **frag1, ++ size_t *frag1_len, uint8_t **frag2, ++ size_t *frag2_len) ++{ ++ size_t body_size = len - 25; ++ *frag1_len = 13 + 12 + 1; ++ *frag2_len = 13 + 12 + (body_size - 1); ++ ++ *frag1 = malloc(13 + 12 + 1); ++ assert(*frag1); ++ *frag2 = malloc(13 + 12 + body_size - 1); ++ assert(*frag2); ++ ++ /* first fragment: record header + handshake header + first body byte */ ++ memcpy(*frag1, data, 13); /* record header */ ++ write_u16(*frag1 + 11, 12 + 1); /* record length */ ++ memcpy(*frag1 + 13, data + 13, 12); /* handshake header */ ++ write_u24(*frag1 + 19, 0); /* fragment_offset = 0 */ ++ write_u24(*frag1 + 22, 1); /* fragment_length = 1 */ ++ (*frag1)[25] = data[25]; /* first body byte */ ++ ++ /* second fragment: record header + handshake header + remaining body */ ++ memcpy(*frag2, data, 13); /* record header */ ++ write_u16(*frag2 + 11, *frag2_len - 13); /* record length */ ++ write_u48(*frag2 + 5, read_u48(*frag2 + 5) + 1); /* sequence number */ ++ memcpy(*frag2 + 13, data + 13, 12); /* handshake header */ ++ write_u24(*frag2 + 19, 1); /* fragment_offset = 1 */ ++ write_u24(*frag2 + 22, body_size - 1); /* shortened fragment_length */ ++ memcpy(*frag2 + 25, data + 26, body_size - 1); /* remaining body */ ++} ++ ++static ssize_t client_push_split_hello(gnutls_transport_ptr_t tr, const void *b, ++ size_t l) ++{ ++ static int seq_offset = 0; /* for renumbering follow-up epoch0 ones */ ++ ++ const uint8_t *data = (const uint8_t *)b; ++ uint8_t *frag1, *frag2; ++ size_t frag1_len, frag2_len; ++ ++ /* Pass through anything that isn't an epoch0 ClientHello with body */ ++ if (l < 13 + 12 + 1 || /* too short for DTLS record header */ ++ data[0] != 22 || /* not a handshake record */ ++ data[3] != 0 || data[4] != 0 || /* not epoch 0 */ ++ data[13] != 1) /* not ClientHello */ ++ return queue_put_renumbered(&c2s, b, l, seq_offset); ++ ++ /* epoch0 Client Hello: special treatment of splitting into fragments */ ++ split_client_hello(data, l, &frag1, &frag1_len, &frag2, &frag2_len); ++ queue_put(&c2s, frag1, frag1_len); ++ queue_put(&c2s, frag2, frag2_len); ++ free(frag1); ++ free(frag2); ++ seq_offset++; ++ return l; ++} ++ + void doit(void) + { + global_init(); + test(client_push_normal); + success("malicious reassembly bug exploitation (#1816):\n"); + test_malicious1816(); ++ success("split client hello smoke-test\n"); ++ test(client_push_split_hello); + gnutls_global_deinit(); + } + +-- +2.53.0 + + +From 092c65d004e2f125f2fea3db84d801ac49a09f78 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 20 Apr 2026 16:32:02 +0200 +Subject: [PATCH 6/7] buffers: match DTLS datagrams by sequence number + +DTLS handshake fragment reassembly previously matched incoming fragments +by handshake type only, without checking the sequence number. +This allowed fragments from different handshake messages +to be merged into the same reassembly buffer. + +Now sequence number is accounted for during reassembly, +ensuring fragments are only merged when they belong +to the same handshake message. + +Reported-by: Zou Dikai +Fixes: #1839 +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 5d4d16276..62f140ed3 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -971,7 +971,8 @@ static int merge_handshake_packet(gnutls_session_t session, + session->internals.handshake_recv_buffer; + + for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) { +- if (recv_buf[i].htype == hsk->htype) { ++ if (recv_buf[i].htype == hsk->htype && ++ recv_buf[i].sequence == hsk->sequence) { + exists = 1; + pos = i; + break; +-- +2.53.0 + + +From a2b41be83a1a3529c551ccf54958da91a656550e Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 20 Apr 2026 16:36:08 +0200 +Subject: [PATCH 7/7] tests/mini-dtls-fragments: #1839 mismatching message_seq + +Signed-off-by: Alexander Sosedkin +--- + tests/mini-dtls-fragments.c | 54 ++++++++++++++++++++++++++++++++----- + 1 file changed, 47 insertions(+), 7 deletions(-) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index 93490bac2..499a92a92 100644 +--- a/tests/mini-dtls-fragments.c ++++ b/tests/mini-dtls-fragments.c +@@ -165,7 +165,7 @@ static uint64_t read_u48(const uint8_t *p) + return seq; + } + +-static void test(gnutls_push_func client_push) ++static void test(gnutls_push_func client_push, bool expect_success) + { + gnutls_session_t client, server; + gnutls_certificate_credentials_t ccred, scred; +@@ -218,12 +218,22 @@ static void test(gnutls_push_func client_push) + sr = gnutls_handshake(server); + if (!sr || gnutls_error_is_fatal(sr)) + sdone = true; ++ ++ if (c2s.head == c2s.tail && s2c.head == s2c.tail) ++ break; /* speed the test up */ + } + +- if (cr) +- fail("client: %s\n", gnutls_strerror(cr)); +- if (sr) +- fail("server: %s\n", gnutls_strerror(sr)); ++ if (expect_success) { ++ if (cr) ++ fail("client: %s\n", gnutls_strerror(cr)); ++ if (sr) ++ fail("server: %s\n", gnutls_strerror(sr)); ++ ++ } else { ++ if (cr == 0 && sr == 0) ++ fail("handshake unexpectedly succeeded: %s / %s\n", ++ gnutls_strerror(cr), gnutls_strerror(sr)); ++ } + + success("OK\n"); + +@@ -421,14 +431,44 @@ static ssize_t client_push_split_hello(gnutls_transport_ptr_t tr, const void *b, + return l; + } + ++static ssize_t client_push_split_hello_bad_seq(gnutls_transport_ptr_t tr, ++ const void *b, size_t l) ++{ ++ /* gnutls wasn't matching on message_seq on merging, see #1839 */ ++ static int seq_offset = 0; /* for renumbering follow-up epoch0 ones */ ++ ++ const uint8_t *data = (const uint8_t *)b; ++ uint8_t *frag1, *frag2; ++ size_t frag1_len, frag2_len; ++ ++ /* Pass through anything that isn't an epoch0 ClientHello with body */ ++ if (l < 13 + 12 + 1 || /* too short for DTLS record header */ ++ data[0] != 22 || /* not a handshake record */ ++ data[3] != 0 || data[4] != 0 || /* not epoch 0 */ ++ data[13] != 1) /* not ClientHello */ ++ return queue_put_renumbered(&c2s, b, l, seq_offset); ++ ++ /* epoch0 Client Hello: special treatment of splitting into fragments */ ++ split_client_hello(data, l, &frag1, &frag1_len, &frag2, &frag2_len); ++ queue_put(&c2s, frag1, frag1_len); ++ frag2[18]++; /* WRONG, message_seq mismatch must be rejected, #1839 */ ++ queue_put(&c2s, frag2, frag2_len); ++ free(frag1); ++ free(frag2); ++ seq_offset++; ++ return l; ++} ++ + void doit(void) + { + global_init(); +- test(client_push_normal); ++ test(client_push_normal, true); + success("malicious reassembly bug exploitation (#1816):\n"); + test_malicious1816(); + success("split client hello smoke-test\n"); +- test(client_push_split_hello); ++ test(client_push_split_hello, true); ++ success("split client hello smoke-test and mangle sequence number\n"); ++ test(client_push_split_hello_bad_seq, false); + gnutls_global_deinit(); + } + +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-3832-ocsp-rev-0.patch b/gnutls-3.8.10-CVE-2026-3832-ocsp-rev-0.patch new file mode 100644 index 0000000..99415df --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-3832-ocsp-rev-0.patch @@ -0,0 +1,372 @@ +From 731861b9de8dccaf7d3b0c1446833051e48670c2 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 12 Mar 2026 09:48:57 +0100 +Subject: [PATCH 1/5] cert-session: fix multi-entry OCSP revocation bypass + +In check_ocsp_response(), the code first searched +for the SingleResponse that matches the certificate being validated. +But later, the status was retrieved from entry 0 unconditionally, +rather than from the matched resp_indx. +As a result, if entry 0 corresponded to a different certificate and was good, +while the matched entry for the peer certificate is revoked, +the revocation check could've mistakenly accept the certificate. + +Reported-by: Oleh Konko (1seal) +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1801 +Fixes: #1812 +Fixes: CVE-2026-3832 +Fixes: GNUTLS-SA-2026-04-29-12 +CVSS: 3.7 Low CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N +Introduced-in: ae404fe8488dee424876b5963c00d7e041672415 3.8.9 +Signed-off-by: Alexander Sosedkin +--- + lib/cert-session.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/cert-session.c b/lib/cert-session.c +index 34a15b19e..b8a70ad00 100644 +--- a/lib/cert-session.c ++++ b/lib/cert-session.c +@@ -343,9 +343,9 @@ static int check_ocsp_response(gnutls_session_t session, gnutls_x509_crt_t cert, + goto cleanup; + } + +- ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL, +- &cert_status, &vtime, &ntime, &rtime, +- NULL); ++ ret = gnutls_ocsp_resp_get_single(resp, resp_indx, NULL, NULL, NULL, ++ NULL, &cert_status, &vtime, &ntime, ++ &rtime, NULL); + if (ret < 0) { + _gnutls_audit_log( + session, +-- +2.53.0 + + +From d52d5f4f383e8c5d8e9a03334f2421ff35d37d2e Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 12 Mar 2026 15:25:24 +0100 +Subject: [PATCH 2/5] tests/ocsp-tests/ocsp-must-staple-connection: test + CVE-2026-3832 + +Signed-off-by: Alexander Sosedkin +--- + .../ocsp-tests/ocsp-must-staple-connection.sh | 70 +++++++++++++++++++ + 1 file changed, 70 insertions(+) + +diff --git a/tests/ocsp-tests/ocsp-must-staple-connection.sh b/tests/ocsp-tests/ocsp-must-staple-connection.sh +index 94d41ce24..5e100b9d9 100755 +--- a/tests/ocsp-tests/ocsp-must-staple-connection.sh ++++ b/tests/ocsp-tests/ocsp-must-staple-connection.sh +@@ -85,6 +85,7 @@ OCSP_RESPONSE_FILE="$testdir/ms-resp.tmp" + OCSP_REQ_FILE="$testdir/ms-req.tmp" + INDEXFILE="$testdir/ocsp_index.txt" + ATTRFILE="${INDEXFILE}.attr" ++SERVER_CERT_BAD_FILE="$testdir/ms-cert-bad.pem.tmp" + + stop_servers () + { +@@ -118,6 +119,20 @@ ${CERTTOOL} \ + --load-privkey "${srcdir}/ocsp-tests/certs/server_good.key" \ + --template "${TEMPLATE_FILE}" --outfile "${SERVER_CERT_FILE}" 2>/dev/null + ++echo "=== Generating bad server certificate ===" ++ ++rm -f "$TEMPLATE_FILE" ++cp "${srcdir}/ocsp-tests/certs/server_bad.template" "$TEMPLATE_FILE" ++chmod u+w "$TEMPLATE_FILE" ++echo "ocsp_uri=http://localhost:${OCSP_PORT}/ocsp/" >>"$TEMPLATE_FILE" ++ ++${CERTTOOL} \ ++ --attime "${CERTDATE}" \ ++ --generate-certificate --load-ca-privkey "${srcdir}/ocsp-tests/certs/ca.key" \ ++ --load-ca-certificate "${srcdir}/ocsp-tests/certs/ca.pem" \ ++ --load-privkey "${srcdir}/ocsp-tests/certs/server_bad.key" \ ++ --template "${TEMPLATE_FILE}" --outfile "${SERVER_CERT_BAD_FILE}" 2>/dev/null ++ + echo "=== Bringing OCSP server up ===" + + cp "${srcdir}/ocsp-tests/certs/ocsp_index.txt" ${INDEXFILE} +@@ -486,6 +501,61 @@ kill "${TLS_SERVER_PID}" + wait "${TLS_SERVER_PID}" + unset TLS_SERVER_PID + ++echo "=== Test 10: Server with revoked certificate - CVE-2026-3832 ===" ++ ++# The revocation status was always mistakenly checked for the first cert. ++# Check a pair of responses: (irrelevant good unrevoked, relevant bad revoked). ++ ++rm -f "${OCSP_RESPONSE_FILE}" ++ ++"$FAKETIME" "${TESTDATE}" \ ++ ${OPENSSL} ocsp -index "${INDEXFILE}" \ ++ -issuer "${srcdir}/ocsp-tests/certs/ca.pem" \ ++ -CA "${srcdir}/ocsp-tests/certs/ca.pem" \ ++ -rsigner "${srcdir}/ocsp-tests/certs/ocsp-server.pem" \ ++ -rkey "${srcdir}/ocsp-tests/certs/ocsp-server.key" \ ++ -cert "${SERVER_CERT_FILE}" \ ++ -cert "${SERVER_CERT_BAD_FILE}" \ ++ -respout "${OCSP_RESPONSE_FILE}" ++ ++eval "${GETPORT}" ++# Port for gnutls-serv ++TLS_SERVER_PORT=$PORT ++PORT=${TLS_SERVER_PORT} ++launch_bare_server \ ++ "${SERV}" --attime "${TESTDATE}" --echo --disable-client-cert \ ++ --x509keyfile="${srcdir}/ocsp-tests/certs/server_bad.key" \ ++ --x509certfile="${SERVER_CERT_BAD_FILE}" \ ++ --port="${TLS_SERVER_PORT}" \ ++ --ocsp-response="${OCSP_RESPONSE_FILE}" --ignore-ocsp-response-errors ++TLS_SERVER_PID="${!}" ++wait_server $TLS_SERVER_PID ++ ++wait_for_port "${TLS_SERVER_PORT}" ++ ++out=$( ++ echo "test 123456" | \ ++ "${CLI}" -d1 --attime "${TESTDATE}" --ocsp \ ++ --x509cafile "${srcdir}/ocsp-tests/certs/ca.pem" \ ++ --port "${TLS_SERVER_PORT}" localhost \ ++ 2>&1 ++ rc=$? ++) ++printf '%s\n' "$out" ++ ++if test "${rc}" = "0"; then ++ echo 'ERROR: client accepted a revoked leaf (CVE-2026-3832)' ++ exit 1 ++fi ++if ! echo "${out}" | grep "The certificate was revoked via OCSP" >/dev/null ++then ++ echo '"The certificate was revoked via OCSP" not found in output' ++ exit 1 ++fi ++ ++kill "${TLS_SERVER_PID}" ++wait "${TLS_SERVER_PID}" ++unset TLS_SERVER_PID + + kill ${OCSP_PID} + wait ${OCSP_PID} +-- +2.53.0 + + +From 8cb066878ae6dcb71e19b7f104ff90a141973352 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 12 Mar 2026 10:42:49 +0100 +Subject: [PATCH 3/5] tests/ocsp-tests/ocsp-must-staple-connection: grep for + specific... + +... error message: 'Got OCSP response with an unrelated certificate'. + +Signed-off-by: Alexander Sosedkin +--- + .../ocsp-tests/ocsp-must-staple-connection.sh | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/tests/ocsp-tests/ocsp-must-staple-connection.sh b/tests/ocsp-tests/ocsp-must-staple-connection.sh +index 5e100b9d9..568aece2e 100755 +--- a/tests/ocsp-tests/ocsp-must-staple-connection.sh ++++ b/tests/ocsp-tests/ocsp-must-staple-connection.sh +@@ -292,21 +292,31 @@ wait_server $TLS_SERVER_PID + + wait_for_port "${TLS_SERVER_PORT}" + +-echo "test 123456" | \ +- "${CLI}" --attime "${TESTDATE}" --ocsp --x509cafile="${srcdir}/ocsp-tests/certs/ca.pem" \ +- --port="${TLS_SERVER_PORT}" localhost ++out=$( ++ echo "test 123456" | \ ++ "${CLI}" --attime "${TESTDATE}" --ocsp \ ++ --x509cafile="${srcdir}/ocsp-tests/certs/ca.pem" \ ++ --port="${TLS_SERVER_PORT}" localhost \ ++ 2>&1 ++) + rc=$? ++printf '%s\n' "$out" + + if test "${rc}" = "0"; then + echo "Connecting to server with valid certificate and invalid staple succeeded" + exit 1 + fi + ++if ! echo "${out}" | grep "Got OCSP response with an unrelated certificate" > /dev/null ++then ++ echo '"Got OCSP response with an unrelated certificate" not found in output' ++ exit 1 ++fi ++ + kill "${TLS_SERVER_PID}" + wait "${TLS_SERVER_PID}" + unset TLS_SERVER_PID + +- + echo "=== Test 5: Server with valid certificate - expired staple ===" + + rm -f "${OCSP_RESPONSE_FILE}" +-- +2.53.0 + + +From 6a7999807d72bd2320d959092235fb06e751c332 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 12 Mar 2026 10:25:41 +0100 +Subject: [PATCH 4/5] cert-session: log "no responses" case separately + +Signed-off-by: Alexander Sosedkin +--- + lib/cert-session.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/lib/cert-session.c b/lib/cert-session.c +index b8a70ad00..cb8abd736 100644 +--- a/lib/cert-session.c ++++ b/lib/cert-session.c +@@ -283,10 +283,16 @@ static int check_ocsp_response(gnutls_session_t session, gnutls_x509_crt_t cert, + break; + } + if (ret < 0) { ++ if (resp_indx == 0 && ++ ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { ++ _gnutls_audit_log(session, "Got OCSP response with" ++ " no certificates.\n"); ++ } else { ++ _gnutls_audit_log(session, ++ "Got OCSP response with" ++ " an unrelated certificate.\n"); ++ } + ret = gnutls_assert_val(0); +- _gnutls_audit_log( +- session, +- "Got OCSP response with an unrelated certificate.\n"); + check_failed = 1; + *ostatus |= GNUTLS_CERT_INVALID; + *ostatus |= GNUTLS_CERT_INVALID_OCSP_STATUS; +-- +2.53.0 + + +From f36276e1224719160584ae52398a0d2ceb670ac2 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Thu, 12 Mar 2026 10:57:14 +0100 +Subject: [PATCH 5/5] tests/ocsp-tests/ocsp-must-staple-connection: no response + case + +Signed-off-by: Alexander Sosedkin +--- + tests/Makefile.am | 4 +- + tests/ocsp-tests/certs/ocsp-staple-empty.der | Bin 0 -> 1202 bytes + .../ocsp-tests/ocsp-must-staple-connection.sh | 45 ++++++++++++++++++ + 3 files changed, 48 insertions(+), 1 deletion(-) + create mode 100644 tests/ocsp-tests/certs/ocsp-staple-empty.der + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index aeeaaf79d..f7d6254a9 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -61,7 +61,9 @@ EXTRA_DIST = suppressions.valgrind eagain-common.h cert-common.h test-chains.h \ + ocsp-tests/response2.der ocsp-tests/response3.der ocsp-tests/certs/ocsp_index.txt ocsp-tests/certs/ocsp_index.txt.attr \ + ocsp-tests/response1.pem ocsp-tests/response2.pem \ + ocsp-tests/certs/server_good.key ocsp-tests/certs/server_bad.key ocsp-tests/certs/server_good.template \ +- ocsp-tests/certs/server_bad.template ocsp-tests/certs/ocsp-staple-unrelated.der ocsp-tests/suppressions.valgrind \ ++ ocsp-tests/certs/server_bad.template ocsp-tests/certs/ocsp-staple-unrelated.der \ ++ ocsp-tests/certs/ocsp-staple-empty.der \ ++ ocsp-tests/suppressions.valgrind \ + ocsp-tests/signer-verify/response-ca.der \ + ocsp-tests/signer-verify/response-delegated.der \ + ocsp-tests/signer-verify/response-non-delegated.der \ +diff --git a/tests/ocsp-tests/certs/ocsp-staple-empty.der b/tests/ocsp-tests/certs/ocsp-staple-empty.der +new file mode 100644 +index 0000000000000000000000000000000000000000..eccb9ecbfc246819fc360849867741615fb973a2 +GIT binary patch +literal 1202 +zcmXqLVp+$<$grS^Ww}8U%VIW8Z8k<$R(1nMMwTX)DF#g}6AZK$iLr=q9@<;)`-D4E +z!%f`Pb8})(pWKuC68uI6hGqt)#s)@4rj{n=Q3ebKylk9WZ60mkc^MhGSs9p{7#SJ* +zKKDM+|K1uQylhIwtsoDzluFGA?ZXE&PIf+c5v+0TcGfcqZWZ&R{0~*Tet2d7TGO}l +z!+Vdn(S~bZU+(aTuuf$DRg@UAPN3;@PSjFIwM^bDm2XeicxBGnO&Vn0V-)09L +z*eSU130qD}Xy|i~uN$K$EwFzmD1OwO;ob#fqvNKGW+KNHT=iSTU#5BCQ0tnU0}2aP +zEc`I}(nVu^@z36~k2CM{{Ozt2QojE27PC#aKTXs1yO3lppy98VZkKq@xVgoCy=7h9 +zg-e0!EBM~03M^=1_BLo@b~k8Zb~b2Y(q6#K#K^?N6ZTo+n$W$O;yI@lK0pgL0~tdp +z0|_?fP!?uk!I0GAlFYnx1;^5ojQpa^l1c+Pab80+17jm&BO@bA6Qd|`UPE&vu7S3p +zrhz&{w*rQ41%Kz@0EM8`;)49Vl++@FCPpP>+Zn-;#?N5T#K^@2ioMHBDO|iss(BB; +zyvhC0a>vyoFYdL9cF)R5je>zUAG@5-jj_MDNl2{P?%k%HQ`cNh`h97qQE7>q`>YzV +z_4iLso+qxiV_v#y;@8jTUvtQn7yetLuUsJ@v5M!ho>k>n!7?#({%v-%xBh0=-T0p4 +z#<7YQvD24)IPQPOuz_XY)>}6v9g6=(i`Y%hRq#7gb+%~=8;`5~Ipg&kl|psD$2D8X +z?k|o?JXCx|_E2tGyzEZCc`GdCs~58D_gue{<^00N|I&>fV&#F;HJ)uPJ`%=s$o)^` +z5?@O$!wZ)SB&`&*jekx5*&`NrYi--H^=$1w-b|h+abdy1iRV^xcD`}9^@~c8D84e4 +ziJ6gsadDYJi2)BAbEvE^BjbM-CIbcoVGvK1g~x!4jRTTrm^lsjL4y1sC9KTM>X7y`c&Zj-seQS)ZrN=wg&PbX=N4(1F;4X-5Vma#IEN4`@P}8 +zy^mjyDsx{>+J_u7u>Q +zqOUiee2{c1IOO7SIYsShk7wOAcb*`bP--4)BSC^ +z9Xw{QbMG;oyqoaq20NQnL%N3Ux8QmDb`{bqU$5Ws&)&GsqiV&~>(|&1 ++) ++rc=$? ++printf '%s\n' "$out" ++ ++if test "${rc}" = "0"; then ++ echo "Connecting to server with valid certificate and no response staple succeeded" ++ exit 1 ++fi ++ ++if ! echo "${out}" | grep "Got OCSP response with no certificates" > /dev/null ++then ++ echo '"Got OCSP response with no certificates" not found in output' ++ exit 1 ++fi ++ ++kill "${TLS_SERVER_PID}" ++wait "${TLS_SERVER_PID}" ++unset TLS_SERVER_PID ++ + echo "=== Test 5: Server with valid certificate - expired staple ===" + + rm -f "${OCSP_RESPONSE_FILE}" +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-3833-nc-case.patch b/gnutls-3.8.10-CVE-2026-3833-nc-case.patch new file mode 100644 index 0000000..5f6bba6 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-3833-nc-case.patch @@ -0,0 +1,166 @@ +From 19f6508647bdcd3ce21130201e484d7ca6d962c5 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 16 Mar 2026 15:29:40 +0100 +Subject: [PATCH 1/2] x509/name-constraints: compare domain names + case-insensitive + +RFC 5280 7.2: +> When comparing DNS names for equality, conforming implementations +> MUST perform a case-insensitive exact match on the entire DNS name. +> When evaluating name constraints, conforming implementations MUST +> perform a case-insensitive exact match on a label-by-label basis. + +Domain name comparison during name constraints processing +was case-sensitive. For excluded name constraints, this could lead to +incorrectly accepting domain names that should've been rejected. +The code for comparing domain names and domain name parts of emails +has been modified to perform case-insensitive comparison instead. + +Reported-by: Oleh Konko +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1223 +Fixes: #1803 +Fixes: #1852 +Fixes: CVE-2026-3833 +Fixes: GNUTLS-SA-2026-04-29-5 +CVSS: 7.4 High CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N +Signed-off-by: Alexander Sosedkin +--- + lib/x509/name_constraints.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c +index a89728451..410022239 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -35,6 +35,7 @@ + #include "x509_int.h" + #include "x509_ext_int.h" + #include ++#include "c-strcase.h" + + #include "ip.h" + #include "ip-in-cidr.h" +@@ -100,7 +101,7 @@ enum name_constraint_relation { + NC_SORTS_AFTER = 2 /* unrelated constraints */ + }; + +-/* A helper to compare just a pair of strings with this rich comparison */ ++/* Helpers to compare just a pair of strings with this rich comparison */ + static enum name_constraint_relation + compare_strings(const void *n1, size_t n1_len, const void *n2, size_t n2_len) + { +@@ -116,6 +117,22 @@ compare_strings(const void *n1, size_t n1_len, const void *n2, size_t n2_len) + return NC_EQUAL; + } + ++static enum name_constraint_relation ++compare_strings_case_insensitive(const void *n1, size_t n1_len, const void *n2, ++ size_t n2_len) ++{ ++ int r = c_strncasecmp(n1, n2, MIN(n1_len, n2_len)); ++ if (r < 0) ++ return NC_SORTS_BEFORE; ++ if (r > 0) ++ return NC_SORTS_AFTER; ++ if (n1_len < n2_len) ++ return NC_SORTS_BEFORE; ++ if (n1_len > n2_len) ++ return NC_SORTS_AFTER; ++ return NC_EQUAL; ++} ++ + /* Rich-compare DNS names. Example order/relationships: + * z.x.a INCLUDED_BY x.a BEFORE y.a INCLUDED_BY a BEFORE x.b BEFORE y.b */ + static enum name_constraint_relation compare_dns_names(const gnutls_datum_t *n1, +@@ -141,8 +158,8 @@ static enum name_constraint_relation compare_dns_names(const gnutls_datum_t *n1, + while (j && n2->data[j - 1] != '.') + j--; + +- rel = compare_strings(&n1->data[i], i_end - i, &n2->data[j], +- j_end - j); ++ rel = compare_strings_case_insensitive(&n1->data[i], i_end - i, ++ &n2->data[j], j_end - j); + if (rel == NC_SORTS_BEFORE) /* x.a BEFORE y.a */ + return NC_SORTS_BEFORE; + if (rel == NC_SORTS_AFTER) /* y.a AFTER x.a */ +-- +2.53.0 + + +From 0fd5bfe0c777a4be30fb481c78c99d1052297f01 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 16 Mar 2026 15:48:57 +0100 +Subject: [PATCH 2/2] tests/name-constraints: add case-sensitivity check + +Signed-off-by: Alexander Sosedkin +--- + tests/name-constraints.c | 52 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/tests/name-constraints.c b/tests/name-constraints.c +index 71216b700..e85c03aae 100644 +--- a/tests/name-constraints.c ++++ b/tests/name-constraints.c +@@ -366,6 +366,58 @@ void doit(void) + + gnutls_x509_name_constraints_deinit(nc); + ++ /* 4b: case insensitivity */ ++ ++ ret = gnutls_x509_name_constraints_init(&nc); ++ check_for_error(ret); ++ ++ set_name("example.net", &name); ++ ret = gnutls_x509_name_constraints_add_excluded(nc, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_for_error(ret); ++ set_name("email@example.net", &name); ++ ret = gnutls_x509_name_constraints_add_excluded( ++ nc, GNUTLS_SAN_RFC822NAME, &name); ++ check_for_error(ret); ++ ++ set_name("example.org", &name); /* unrelated: accepted */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, &name); ++ check_test_result(ret, NAME_ACCEPTED, &name); ++ ++ set_name("example.net", &name); /* exact match: rejected */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, &name); ++ check_test_result(ret, NAME_REJECTED, &name); ++ ++ set_name("eXample.net", &name); /* case *insensitive*: rejected */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, &name); ++ check_test_result(ret, NAME_REJECTED, &name); ++ ++ set_name("mail@example.net", &name); /* unrelated: accepted */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, ++ &name); ++ check_test_result(ret, NAME_ACCEPTED, &name); ++ ++ set_name("email@example.net", &name); /* exact match: rejected */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, ++ &name); ++ check_test_result(ret, NAME_REJECTED, &name); ++ ++ set_name("eMail@example.net", &name); /* case *sensitive*: accepted */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, ++ &name); ++ check_test_result(ret, NAME_ACCEPTED, &name); ++ ++ set_name("email@EXAMPLE.NET", &name); /* case *insensitive*: rejected */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, ++ &name); ++ check_test_result(ret, NAME_REJECTED, &name); ++ ++ set_name("www.ExAmPlE.NeT", &name); /* case *insensitive*: inexact */ ++ ret = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME, &name); ++ check_test_result(ret, NAME_REJECTED, &name); ++ ++ gnutls_x509_name_constraints_deinit(nc); ++ + // Test suite end. + + if (debug) +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42009-dtls-qsort.patch b/gnutls-3.8.10-CVE-2026-42009-dtls-qsort.patch new file mode 100644 index 0000000..57b1db8 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42009-dtls-qsort.patch @@ -0,0 +1,95 @@ +From f01e21441e29052a6f0963840794c41d3b3ee66d Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 16:52:48 +0200 +Subject: [PATCH 1/2] lib/buffers: ensure packets have differing sequence + numbers + +There should normally be no packets with same sequence number and +differing handshake type, unless an adversary crafts them. +Discarding them allows to get rid of packets +with duplicate sequence ID in the buffer, +relieving us from the question of how to sort them later. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1848 +Fixes: CVE-2026-42009 +Fixes: GNUTLS-SA-2026-04-29-2 +CVSS: 7.5 High CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H +Signed-off-by: Alexander Sosedkin +--- + lib/buffers.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 62f140ed3..48f4a3210 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -971,8 +971,20 @@ static int merge_handshake_packet(gnutls_session_t session, + session->internals.handshake_recv_buffer; + + for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) { +- if (recv_buf[i].htype == hsk->htype && +- recv_buf[i].sequence == hsk->sequence) { ++ if (recv_buf[i].sequence == hsk->sequence) { ++ if (recv_buf[i].htype != hsk->htype) { ++ _gnutls_audit_log( ++ session, ++ "Discarded unexpected handshake packet " ++ "with duplicate sequence %d, but " ++ "mismatched type %s (previously %s)\n", ++ hsk->sequence, ++ _gnutls_handshake2str(hsk->htype), ++ _gnutls_handshake2str( ++ recv_buf[i].htype)); ++ _gnutls_handshake_buffer_clear(hsk); ++ return 0; ++ } + exists = 1; + pos = i; + break; +-- +2.53.0 + + +From f341441fad91142897d83b44a175ffc8f925b76f Mon Sep 17 00:00:00 2001 +From: Joshua Rogers +Date: Tue, 21 Apr 2026 18:11:39 +0200 +Subject: [PATCH 2/2] buffers: fix handshake_compare when sequence numbers + match + +The comparator function used for ordering DTLS packets +by sequence numbers did not follow qsort comparator contracts +in case of packets with duplicate sequence numbers, +which could lead to unstable ordering or undefined behaviour. +Returning 0 in such cases makes the sorting stable. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1848 +Fixes: CVE-2026-42009 +Fixes: GNUTLS-SA-2026-04-29-2 +CVSS: 7.5 High CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H +Signed-off-by: Joshua Rogers +--- + lib/buffers.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 48f4a3210..09779a8f3 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -844,11 +844,7 @@ static int handshake_compare(const void *_e1, const void *_e2) + { + const handshake_buffer_st *e1 = _e1; + const handshake_buffer_st *e2 = _e2; +- +- if (e1->sequence <= e2->sequence) +- return 1; +- else +- return -1; ++ return (e1->sequence < e2->sequence) - (e1->sequence > e2->sequence); + } + + #define SSL2_HEADERS 1 +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42010-psk-nul.patch b/gnutls-3.8.10-CVE-2026-42010-psk-nul.patch new file mode 100644 index 0000000..bfb02c5 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42010-psk-nul.patch @@ -0,0 +1,443 @@ +From e3ffd31846d1e6624338a26ca7fce7d1685b17cd Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 19:02:43 +0200 +Subject: [PATCH 1/5] tests/pskself2: extend with RSA-PSK support + +Signed-off-by: Alexander Sosedkin +--- + tests/pskself2.c | 79 ++++++++++++++++++++++++++++++++---------------- + 1 file changed, 53 insertions(+), 26 deletions(-) + +diff --git a/tests/pskself2.c b/tests/pskself2.c +index e16146884..04283ca08 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -27,6 +27,7 @@ + #include "config.h" + #endif + ++#include + #include + #include + +@@ -51,6 +52,7 @@ int main(int argc, char **argv) + + #include "utils.h" + #include "extras/hex.h" ++#include "cert-common.h" + + /* A very basic TLS client, with PSK authentication. + */ +@@ -65,12 +67,13 @@ static void tls_log_func(int level, const char *str) + #define MAX_BUF 1024 + #define MSG "Hello TLS" + +-static void client(int sd, const char *prio, unsigned exp_hint) ++static void client(int sd, const char *prio, bool exp_hint, bool rsa) + { + int ret, ii; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + gnutls_psk_client_credentials_t pskcred; ++ gnutls_certificate_credentials_t xcred = NULL; + /* Need to enable anonymous KX specifically. */ + const gnutls_datum_t key = { (void *)"DEADBEEF", 8 }; + gnutls_datum_t user; +@@ -110,6 +113,11 @@ static void client(int sd, const char *prio, unsigned exp_hint) + */ + gnutls_credentials_set(session, GNUTLS_CRD_PSK, pskcred); + ++ if (rsa) { ++ gnutls_certificate_allocate_credentials(&xcred); ++ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred); ++ } ++ + gnutls_transport_set_int(session, sd); + + /* Perform the TLS handshake +@@ -165,6 +173,8 @@ end: + + gnutls_free(user.data); + gnutls_psk_free_client_credentials(pskcred); ++ if (xcred) ++ gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); + } +@@ -192,9 +202,10 @@ static int pskfunc(gnutls_session_t session, const gnutls_datum_t *username, + return 0; + } + +-static void server(int sd, const char *prio) ++static void server(int sd, const char *prio, bool rsa) + { + gnutls_psk_server_credentials_t server_pskcred; ++ gnutls_certificate_credentials_t serverx509cred = NULL; + int ret; + gnutls_session_t session; + gnutls_datum_t psk_username; +@@ -214,6 +225,13 @@ static void server(int sd, const char *prio) + gnutls_psk_set_server_credentials_hint(server_pskcred, "hint"); + gnutls_psk_set_server_credentials_function2(server_pskcred, pskfunc); + ++ if (rsa) { ++ gnutls_certificate_allocate_credentials(&serverx509cred); ++ gnutls_certificate_set_x509_key_mem(serverx509cred, ++ &server_cert, &server_key, ++ GNUTLS_X509_FMT_PEM); ++ } ++ + gnutls_init(&session, GNUTLS_SERVER); + + /* avoid calling all the priority functions, since the defaults +@@ -222,6 +240,9 @@ static void server(int sd, const char *prio) + gnutls_priority_set_direct(session, prio, NULL); + + gnutls_credentials_set(session, GNUTLS_CRD_PSK, server_pskcred); ++ if (serverx509cred) ++ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ++ serverx509cred); + + gnutls_transport_set_int(session, sd); + ret = gnutls_handshake(session); +@@ -278,6 +299,8 @@ static void server(int sd, const char *prio) + gnutls_deinit(session); + + gnutls_psk_free_server_credentials(server_pskcred); ++ if (serverx509cred) ++ gnutls_certificate_free_credentials(serverx509cred); + + gnutls_global_deinit(); + +@@ -285,7 +308,7 @@ static void server(int sd, const char *prio) + success("server: finished\n"); + } + +-static void run_test(const char *prio, unsigned exp_hint) ++static void run_test(const char *prio, bool exp_hint, bool rsa) + { + pid_t child; + int err; +@@ -311,42 +334,46 @@ static void run_test(const char *prio, unsigned exp_hint) + int status; + /* parent */ + close(sockets[1]); +- server(sockets[0], prio); ++ server(sockets[0], prio, rsa); + wait(&status); + check_wait_status(status); + } else { + close(sockets[0]); +- client(sockets[1], prio, exp_hint); ++ client(sockets[1], prio, exp_hint, rsa); + exit(0); + } + } + + void doit(void) + { +- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", 1); +- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", 1); +- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", 1); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+PSK", true, false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+ECDHE-PSK", true, ++ false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", true, false); + +- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:+PSK", 0); +- run_test( +- "NORMAL:-VERS-ALL:+VERS-TLS1.2:-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK", +- 0); +- run_test( +- "NORMAL:-VERS-ALL:+VERS-TLS1.2:-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK", +- 0); +- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK", 0); +- run_test( +- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK", +- 0); +- run_test( +- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK", +- 0); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:+PSK", false, false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:" ++ "-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK", ++ false, false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:" ++ "-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK", ++ false, false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK", false, false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:" ++ "-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK", ++ false, false); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:" ++ "-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK", ++ false, false); + /* the following should work once we support PSK without DH */ +- run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+PSK", 0); ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+PSK", false, false); + +- run_test("NORMAL:-KX-ALL:+PSK", 0); +- run_test("NORMAL:-KX-ALL:+ECDHE-PSK", 0); +- run_test("NORMAL:-KX-ALL:+DHE-PSK", 0); ++ run_test("NORMAL:-KX-ALL:+PSK", false, false); ++ run_test("NORMAL:-KX-ALL:+ECDHE-PSK", false, false); ++ run_test("NORMAL:-KX-ALL:+DHE-PSK", false, false); ++ ++ /* RSA-PSK */ ++ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+RSA-PSK", false, true); + } + + #endif /* _WIN32 */ +-- +2.53.0 + + +From cb1833afd9b6309563211b1c0a7c291f52ca98d5 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 19:26:10 +0200 +Subject: [PATCH 2/5] lib/auth/rsa_psk: fix binary PSK identity lookup + +A server looking up PSK username with a NUL-character in it +was wrongfully matching username truncated at a NUL-character. +Fix the check to compare up to the full username length. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1850 +Fixes: CVE-2026-42010 +Fixes: GNUTLS-SA-2026-04-29-4 +CVSS: 7.1 High CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N +Signed-off-by: Alexander Sosedkin +--- + lib/auth/rsa_psk.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/lib/auth/rsa_psk.c b/lib/auth/rsa_psk.c +index 9f97569c5..8305f8386 100644 +--- a/lib/auth/rsa_psk.c ++++ b/lib/auth/rsa_psk.c +@@ -321,8 +321,7 @@ static int _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, + * filled in if the key is not found. + */ + ret = _gnutls_psk_pwd_find_entry(session, info->username, +- strlen(info->username), &pwd_psk, +- NULL); ++ info->username_len, &pwd_psk, NULL); + if (ret < 0) + return gnutls_assert_val(ret); + +-- +2.53.0 + + +From 83e579a80ec4f165dc3b8e670d879370081f5945 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 19:19:42 +0200 +Subject: [PATCH 3/5] tests/pskself2: test username with NUL in the middle + (#1850) + +Signed-off-by: Alexander Sosedkin +--- + tests/pskself2.c | 33 +++++++++++++++++++++------------ + 1 file changed, 21 insertions(+), 12 deletions(-) + +diff --git a/tests/pskself2.c b/tests/pskself2.c +index 04283ca08..247000077 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -86,12 +86,15 @@ static void client(int sd, const char *prio, bool exp_hint, bool rsa) + + side = "client"; + +- user.data = gnutls_malloc(4); ++ user.data = gnutls_malloc(5); ++ assert(user.data != NULL); ++ + user.data[0] = 0xCA; + user.data[1] = 0xFE; +- user.data[2] = 0xCA; +- user.data[3] = 0xFE; +- user.size = 4; ++ user.data[2] = 0x00; ++ user.data[3] = 0xCA; ++ user.data[4] = 0xFE; ++ user.size = 5; + + gnutls_psk_allocate_client_credentials(&pskcred); + ret = gnutls_psk_set_client_credentials2(pskcred, &user, &key, +@@ -189,14 +192,20 @@ end: + static int pskfunc(gnutls_session_t session, const gnutls_datum_t *username, + gnutls_datum_t *key) + { ++ const unsigned char expected_user[] = { 0xCA, 0xFE, 0x00, 0xCA, 0xFE }; ++ const unsigned char expected_key[] = { 0xDE, 0xAD, 0xBE, 0xEF }; ++ + if (debug) + printf("psk: Got username with length %d\n", username->size); + ++ /* verify callback received full 5-byte username (#1850) */ ++ if (username->size != 5 || ++ memcmp(username->data, expected_user, 5) != 0) ++ fail("pskfunc: username mismatch: got %u bytes, expected 5\n", ++ username->size); ++ + key->data = gnutls_malloc(4); +- key->data[0] = 0xDE; +- key->data[1] = 0xAD; +- key->data[2] = 0xBE; +- key->data[3] = 0xEF; ++ memcpy(key->data, expected_key, 4); + key->size = 4; + + return 0; +@@ -209,8 +218,8 @@ static void server(int sd, const char *prio, bool rsa) + int ret; + gnutls_session_t session; + gnutls_datum_t psk_username; +- char buffer[MAX_BUF + 1], +- expected_psk_username[] = { 0xDE, 0xAD, 0xBE, 0xEF }; ++ char buffer[MAX_BUF + 1]; ++ const char expected_psk_username[] = { 0xCA, 0xFE, 0x00, 0xCA, 0xFE }; + + /* this must be called once in the program + */ +@@ -262,8 +271,8 @@ static void server(int sd, const char *prio, bool rsa) + if (gnutls_psk_server_get_username2(session, &psk_username) < 0) + fail("server: Could not get PSK username\n"); + +- if (psk_username.size != 4 || +- memcmp(psk_username.data, expected_psk_username, 4)) ++ if (psk_username.size != 5 || ++ memcmp(psk_username.data, expected_psk_username, 5)) + fail("server: Unexpected PSK username\n"); + + success("server: PSK username length: %d\n", psk_username.size); +-- +2.53.0 + + +From 0f8539fac736a2cdcc79ee4ea5a2f2590a6bea6b Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 19:49:47 +0200 +Subject: [PATCH 4/5] tests/pskself2: sprinkle NUL into key for good measure + +Signed-off-by: Alexander Sosedkin +--- + tests/pskself2.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/tests/pskself2.c b/tests/pskself2.c +index 247000077..07f08adcd 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -75,7 +75,7 @@ static void client(int sd, const char *prio, bool exp_hint, bool rsa) + gnutls_psk_client_credentials_t pskcred; + gnutls_certificate_credentials_t xcred = NULL; + /* Need to enable anonymous KX specifically. */ +- const gnutls_datum_t key = { (void *)"DEADBEEF", 8 }; ++ const gnutls_datum_t key = { (void *)"DEAD00BEEF", 10 }; + gnutls_datum_t user; + const char *hint; + +@@ -193,7 +193,7 @@ static int pskfunc(gnutls_session_t session, const gnutls_datum_t *username, + gnutls_datum_t *key) + { + const unsigned char expected_user[] = { 0xCA, 0xFE, 0x00, 0xCA, 0xFE }; +- const unsigned char expected_key[] = { 0xDE, 0xAD, 0xBE, 0xEF }; ++ const unsigned char expected_key[] = { 0xDE, 0xAD, 0x00, 0xBE, 0xEF }; + + if (debug) + printf("psk: Got username with length %d\n", username->size); +@@ -204,9 +204,9 @@ static int pskfunc(gnutls_session_t session, const gnutls_datum_t *username, + fail("pskfunc: username mismatch: got %u bytes, expected 5\n", + username->size); + +- key->data = gnutls_malloc(4); +- memcpy(key->data, expected_key, 4); +- key->size = 4; ++ key->data = gnutls_malloc(5); ++ memcpy(key->data, expected_key, 5); ++ key->size = 5; + + return 0; + } +-- +2.53.0 + + +From b10ac69270cd5ab4353efa62b92d9e04a5fec464 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 27 Apr 2026 17:16:25 +0200 +Subject: [PATCH 5/5] lib/auth/psk_passwd: limit the length of the comparison + +Comparing a long username from a password file +to a short username from the wire +could lead to a heap overread up to the difference in their lengths. + +Fixes: #1864 +Reported-by: Joshua Rogers of AISLE Research Team +Signed-off-by: Alexander Sosedkin +--- + lib/auth/psk_passwd.c | 21 +++++++++++---------- + 1 file changed, 11 insertions(+), 10 deletions(-) + +diff --git a/lib/auth/psk_passwd.c b/lib/auth/psk_passwd.c +index 518756e7d..abefd0d4a 100644 +--- a/lib/auth/psk_passwd.c ++++ b/lib/auth/psk_passwd.c +@@ -78,7 +78,7 @@ ATTRIBUTE_NONNULL((1, 2)) + static bool username_matches(const gnutls_datum_t *username, const char *line, + size_t line_size) + { +- int retval; ++ bool retval; + unsigned i; + gnutls_datum_t hexline, hex_username = { NULL, 0 }; + +@@ -91,7 +91,7 @@ static bool username_matches(const gnutls_datum_t *username, const char *line, + return false; + + if (line_size == 0) +- return (username->size == 0); ++ return false; + + /* move to first ':' */ + i = 0; +@@ -99,6 +99,9 @@ static bool username_matches(const gnutls_datum_t *username, const char *line, + i++; + } + ++ if (line[i] != ':') ++ return false; ++ + /* if format is in hex, e.g. #FAFAFA */ + if (line[0] == '#' && line_size > 1) { + hexline.data = (void *)&line[1]; +@@ -107,19 +110,17 @@ static bool username_matches(const gnutls_datum_t *username, const char *line, + if (gnutls_hex_decode2(&hexline, &hex_username) < 0) + return gnutls_assert_val(0); + +- if (hex_username.size == username->size) +- retval = memcmp(username->data, hex_username.data, +- username->size); +- else +- retval = -1; ++ retval = hex_username.size == username->size && ++ memcmp(username->data, hex_username.data, ++ username->size) == 0; + + _gnutls_free_datum(&hex_username); + } else { +- retval = strncmp((const char *)username->data, line, +- MAX(i, username->size)); ++ retval = i == username->size && ++ strncmp((const char *)username->data, line, i) == 0; + } + +- return (retval == 0); ++ return retval; + } + + /* Randomizes the given password entry. It actually sets a random password. +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42011-nc-intersect.patch b/gnutls-3.8.10-CVE-2026-42011-nc-intersect.patch new file mode 100644 index 0000000..95765a2 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42011-nc-intersect.patch @@ -0,0 +1,176 @@ +From 1dead2faec6320aaba321eb56f20d442df192b83 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 14 Apr 2026 17:41:30 +0200 +Subject: [PATCH 1/2] x509/name_constraints: fix intersecting empty constraints + +Permitted name constraints were wrongfully ignored +when prior CAs only had excluded name constraints, +resulting in a name constraint bypass. + +With this change, they are taken into account and propagate. + +Reported-by: Haruto Kimura (Stella) +Fixes: #1824 +Fixes: CVE-2026-42011 +Fixes: GNUTLS-SA-2026-04-29-6 +CVSS: 4.8 Medium CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N +Signed-off-by: Alexander Sosedkin +--- + lib/x509/name_constraints.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c +index 04722bdf4..232d466c4 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -723,9 +723,6 @@ static int name_constraints_node_list_intersect( + type_bitmask_t types_in_p1 = 0, types_in_p2 = 0; + static const unsigned char universal_ip[32] = { 0 }; + +- if (permitted->size == 0 || permitted2->size == 0) +- return GNUTLS_E_SUCCESS; +- + /* make sorted views of the arrays */ + ret = ensure_sorted(permitted); + if (ret < 0) { +-- +2.53.0 + + +From 24713b8c63137ce0665b495d22ccce4f5ce05c84 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 14 Apr 2026 17:49:50 +0200 +Subject: [PATCH 2/2] tests/name-constraints-merge: extend to cover #1824 + +Signed-off-by: Alexander Sosedkin +--- + tests/name-constraints-merge.c | 113 +++++++++++++++++++++++++++++++++ + 1 file changed, 113 insertions(+) + +diff --git a/tests/name-constraints-merge.c b/tests/name-constraints-merge.c +index 70376aaa7..3ff8d6c60 100644 +--- a/tests/name-constraints-merge.c ++++ b/tests/name-constraints-merge.c +@@ -473,6 +473,119 @@ void doit(void) + gnutls_x509_name_constraints_deinit(nc1); + gnutls_x509_name_constraints_deinit(nc2); + ++ /* 6: test intersecting empty permitted with non-empty permitted ++ * NC1: excluded DNS excluded.example.org (empty permitted) ++ * NC2: permitted DNS permitted.example.org ++ * Expected result: ++ * permitted=[permitted.example.org], excluded=[excluded.example.org] ++ * unrelated.example.com is rejected ++ */ ++ suite = 6; ++ ++ ret = gnutls_x509_name_constraints_init(&nc1); ++ check_for_error(ret); ++ ++ ret = gnutls_x509_name_constraints_init(&nc2); ++ check_for_error(ret); ++ ++ set_name("excluded.example.org", &name); ++ ret = gnutls_x509_name_constraints_add_excluded(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_for_error(ret); ++ ++ set_name("permitted.example.org", &name); ++ ret = gnutls_x509_name_constraints_add_permitted( ++ nc2, GNUTLS_SAN_DNSNAME, &name); ++ check_for_error(ret); ++ ++ ret = _gnutls_x509_name_constraints_merge(nc1, nc2); ++ check_for_error(ret); ++ ++ set_name("unrelated.example.com", &name); /* entirely unrelated */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_REJECTED, &name); /* #1814 */ ++ ++ set_name("permitted.example.org", &name); /* permitted, direct */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */ ++ ++ set_name("sub.permitted.example.org", &name); /* permitted, subdomain */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */ ++ ++ set_name("excluded.example.org", &name); /* excluded, direct */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */ ++ ++ set_name("sub.excluded.example.org", &name); /* excluded, subdomain */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */ ++ ++ gnutls_x509_name_constraints_deinit(nc1); ++ gnutls_x509_name_constraints_deinit(nc2); ++ ++ /* 7: test intersecting non-empty permitted with empty permitted ++ * (same as 6, but swapped to ensure order doesn't matter) ++ * NC1: permitted DNS permitted.example.org ++ * NC2: excluded DNS excluded.example.org (empty permitted) ++ * Expected result: ++ * permitted=[permitted.example.org], excluded=[excluded.example.org] ++ * unrelated.example.com is rejected ++ */ ++ suite = 7; ++ ++ ret = gnutls_x509_name_constraints_init(&nc1); ++ check_for_error(ret); ++ ++ ret = gnutls_x509_name_constraints_init(&nc2); ++ check_for_error(ret); ++ ++ set_name("permitted.example.org", &name); ++ ret = gnutls_x509_name_constraints_add_permitted( ++ nc1, GNUTLS_SAN_DNSNAME, &name); ++ check_for_error(ret); ++ ++ set_name("excluded.example.org", &name); ++ ret = gnutls_x509_name_constraints_add_excluded(nc2, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_for_error(ret); ++ ++ ret = _gnutls_x509_name_constraints_merge(nc1, nc2); ++ check_for_error(ret); ++ ++ set_name("unrelated.example.com", &name); /* entirely unrelated */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_REJECTED, &name); /* #1814 */ ++ ++ set_name("permitted.example.org", &name); /* permitted, direct */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */ ++ ++ set_name("sub.permitted.example.org", &name); /* permitted, subdomain */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */ ++ ++ set_name("excluded.example.org", &name); /* excluded, direct */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */ ++ ++ set_name("sub.excluded.example.org", &name); /* excluded, subdomain */ ++ ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME, ++ &name); ++ check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */ ++ ++ gnutls_x509_name_constraints_deinit(nc1); ++ gnutls_x509_name_constraints_deinit(nc2); ++ + /* Test footer */ + + if (debug) +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42012-url-san-cn.patch b/gnutls-3.8.10-CVE-2026-42012-url-san-cn.patch new file mode 100644 index 0000000..dabb55d --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42012-url-san-cn.patch @@ -0,0 +1,506 @@ +From fc909c3abddcc2955bebf0de403136ed9ec689c2 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 28 Apr 2026 15:26:32 +0200 +Subject: [PATCH 1/5] x509/virt-san: a small OOM-correctness fix + +Signed-off-by: Alexander Sosedkin +--- + lib/x509/virt-san.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/lib/x509/virt-san.c b/lib/x509/virt-san.c +index 92fcab2c8..ce3d2ca39 100644 +--- a/lib/x509/virt-san.c ++++ b/lib/x509/virt-san.c +@@ -108,11 +108,8 @@ int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, + if (ret < 0) + return gnutls_assert_val(ret); + +- name->type = GNUTLS_SAN_OTHERNAME; + name->san.data = encoded.data; + name->san.size = encoded.size; +- name->othername_oid.data = (void *)gnutls_strdup(oid); +- name->othername_oid.size = strlen(oid); + break; + + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: +@@ -120,15 +117,19 @@ int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, + &name->san); + if (ret < 0) + return gnutls_assert_val(ret); +- +- name->othername_oid.data = (void *)gnutls_strdup(oid); +- name->othername_oid.size = strlen(oid); +- name->type = GNUTLS_SAN_OTHERNAME; + break; + + default: + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } ++ ret = _gnutls_set_strdatum(&name->othername_oid, oid, ++ strlen(oid)); ++ if (ret < 0) { ++ gnutls_assert(); ++ _gnutls_free_datum(&name->san); ++ return ret; ++ } ++ name->type = GNUTLS_SAN_OTHERNAME; + + gnutls_free(san->data); + } +-- +2.53.0 + + +From 5cc003b9688378f6c7934b1df0aa147e80006be4 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 13 Mar 2026 17:41:33 +0100 +Subject: [PATCH 2/5] x509: add bare-bones awareness of SRV virtual SAN + +There's no support for constraints, no certtool support, no nothing. +Just added what's easy to add because I needed a virtual SAN for them. + +Signed-off-by: Alexander Sosedkin +--- + lib/includes/gnutls/gnutls.h.in | 4 +++- + lib/x509/common.h | 1 + + lib/x509/name_constraints.c | 3 ++- + lib/x509/output.c | 6 ++++++ + lib/x509/virt-san.c | 24 ++++++++++++++++++++++++ + lib/x509/x509.c | 3 ++- + 6 files changed, 38 insertions(+), 3 deletions(-) + +diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in +index 964366ded..acce69301 100644 +--- a/lib/includes/gnutls/gnutls.h.in ++++ b/lib/includes/gnutls/gnutls.h.in +@@ -2683,6 +2683,7 @@ void gnutls_psk_set_server_params_function(gnutls_psk_server_credentials_t res, + * @GNUTLS_SAN_OTHERNAME_XMPP: Virtual SAN, used by certain functions for convenience. + * @GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: Virtual SAN, used by certain functions for convenience. + * @GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL: Virtual SAN, used by certain functions for convenience. ++ * @GNUTLS_SAN_OTHERNAME_SRV: Virtual SAN, used by certain functions for convenience. + * + * Enumeration of different subject alternative names types. + */ +@@ -2700,7 +2701,8 @@ typedef enum gnutls_x509_subject_alt_name_t { + Used by gnutls_x509_crt_get_subject_alt_othername_oid. */ + GNUTLS_SAN_OTHERNAME_XMPP = 1000, + GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL, +- GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL ++ GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL, ++ GNUTLS_SAN_OTHERNAME_SRV + } gnutls_x509_subject_alt_name_t; + + struct gnutls_openpgp_crt_int; +diff --git a/lib/x509/common.h b/lib/x509/common.h +index f039af15b..ed9409f62 100644 +--- a/lib/x509/common.h ++++ b/lib/x509/common.h +@@ -107,6 +107,7 @@ + #define XMPP_OID "1.3.6.1.5.5.7.8.5" + #define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2" + #define MSUSER_PRINCIPAL_NAME_OID "1.3.6.1.4.1.311.20.2.3" ++#define SRV_OID "1.3.6.1.5.5.7.8.7" + #define PKIX1_RSA_PSS_MGF1_OID "1.2.840.113549.1.1.8" + #define PKIX1_RSA_OAEP_P_SPECIFIED_OID "1.9" + +diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c +index 3c6e30630..d3c624284 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -146,7 +146,8 @@ static int validate_name_constraints_node(gnutls_x509_subject_alt_name_t type, + if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME && + type != GNUTLS_SAN_DN && type != GNUTLS_SAN_URI && + type != GNUTLS_SAN_IPADDRESS && +- type != GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL) { ++ type != GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL && ++ type != GNUTLS_SAN_OTHERNAME_SRV) { + return gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN); + } + +diff --git a/lib/x509/output.c b/lib/x509/output.c +index 4e983c659..78ad9cad7 100644 +--- a/lib/x509/output.c ++++ b/lib/x509/output.c +@@ -121,6 +121,7 @@ static void print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, + if ((type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_OTHERNAME_XMPP || + type == GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL || + type == GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL || ++ type == GNUTLS_SAN_OTHERNAME_SRV || + type == GNUTLS_SAN_RFC822NAME || type == GNUTLS_SAN_URI) && + sname != NULL && strlen(sname) != name->size) { + adds(str, _("warning: SAN contains an embedded NUL, " +@@ -180,6 +181,11 @@ static void print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, + name->size, NON_NULL(name->data)); + break; + ++ case GNUTLS_SAN_OTHERNAME_SRV: ++ addf(str, _("%sSRVName: %.*s\n"), prefix, name->size, ++ NON_NULL(name->data)); ++ break; ++ + default: + addf(str, _("%sUnknown name: "), prefix); + _gnutls_buffer_hexprint(str, name->data, name->size); +diff --git a/lib/x509/virt-san.c b/lib/x509/virt-san.c +index ce3d2ca39..e25b79b1c 100644 +--- a/lib/x509/virt-san.c ++++ b/lib/x509/virt-san.c +@@ -45,6 +45,9 @@ static int san_othername_to_virtual(const char *oid, size_t size) + memcmp(oid, MSUSER_PRINCIPAL_NAME_OID, + sizeof(MSUSER_PRINCIPAL_NAME_OID) - 1) == 0) + return GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL; ++ else if ((unsigned)size == (sizeof(SRV_OID) - 1) && ++ memcmp(oid, SRV_OID, sizeof(SRV_OID) - 1) == 0) ++ return GNUTLS_SAN_OTHERNAME_SRV; + } + + return GNUTLS_SAN_OTHERNAME; +@@ -59,6 +62,8 @@ static const char *virtual_to_othername_oid(unsigned type) + return KRB5_PRINCIPAL_OID; + case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL: + return MSUSER_PRINCIPAL_NAME_OID; ++ case GNUTLS_SAN_OTHERNAME_SRV: ++ return SRV_OID; + default: + return NULL; + } +@@ -119,6 +124,16 @@ int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, + return gnutls_assert_val(ret); + break; + ++ case GNUTLS_SAN_OTHERNAME_SRV: ++ ret = _gnutls_x509_encode_string(ASN1_ETYPE_IA5_STRING, ++ san->data, san->size, ++ &encoded); ++ if (ret < 0) ++ return gnutls_assert_val(ret); ++ name->san.data = encoded.data; ++ name->san.size = encoded.size; ++ break; ++ + default: + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + } +@@ -192,6 +207,15 @@ int gnutls_x509_othername_to_virtual(const char *oid, + return ret; + } + return 0; ++ case GNUTLS_SAN_OTHERNAME_SRV: ++ ret = _gnutls_x509_decode_string(ASN1_ETYPE_IA5_STRING, ++ othername->data, ++ othername->size, virt, 0); ++ if (ret < 0) { ++ gnutls_assert(); ++ return ret; ++ } ++ return 0; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } +diff --git a/lib/x509/x509.c b/lib/x509/x509.c +index a55389b34..e1d8c3cba 100644 +--- a/lib/x509/x509.c ++++ b/lib/x509/x509.c +@@ -1562,7 +1562,8 @@ inline static int is_type_printable(int type) + { + if (type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME || + type == GNUTLS_SAN_URI || type == GNUTLS_SAN_OTHERNAME_XMPP || +- type == GNUTLS_SAN_OTHERNAME || type == GNUTLS_SAN_REGISTERED_ID) ++ type == GNUTLS_SAN_OTHERNAME_SRV || type == GNUTLS_SAN_OTHERNAME || ++ type == GNUTLS_SAN_REGISTERED_ID) + return 1; + else + return 0; +-- +2.53.0 + + +From 6133fb459b74a9dcfa2d0ff010a4e03c56822d39 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 13 Mar 2026 17:00:03 +0100 +Subject: [PATCH 3/5] x509/hostname-verify: refactor and simplify CN fallback + logic + +Signed-off-by: Alexander Sosedkin +--- + lib/x509/hostname-verify.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c +index 04e17aa23..dda19b54d 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -108,7 +108,7 @@ unsigned gnutls_x509_crt_check_ip(gnutls_x509_crt_t cert, + * that we do not fallback to CN-ID if we encounter a supported name + * type. + */ +-#define IS_SAN_SUPPORTED(san) \ ++#define PRECLUDES_CN_FALLBACK(san) \ + (san == GNUTLS_SAN_DNSNAME || san == GNUTLS_SAN_IPADDRESS) + + /** +@@ -151,13 +151,12 @@ unsigned gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + { + char dnsname[MAX_CN]; + size_t dnsnamesize; +- int found_dnsname = 0; + int ret = 0; + int i = 0; + struct in_addr ipv4; + char *p = NULL; + char *a_hostname; +- unsigned have_other_addresses = 0; ++ bool cn_fallback_allowed = true; + gnutls_datum_t out; + + /* check whether @hostname is an ip address */ +@@ -213,9 +212,10 @@ hostname_fallback: + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, dnsname, + &dnsnamesize, NULL); + ++ if (PRECLUDES_CN_FALLBACK(ret)) ++ cn_fallback_allowed = false; ++ + if (ret == GNUTLS_SAN_DNSNAME) { +- found_dnsname = 1; +- + if (memchr(dnsname, '\0', dnsnamesize)) { + _gnutls_debug_log( + "certificate has %s with embedded null in name\n", +@@ -236,13 +236,10 @@ hostname_fallback: + ret = 1; + goto cleanup; + } +- } else { +- if (IS_SAN_SUPPORTED(ret)) +- have_other_addresses = 1; + } + } + +- if (!have_other_addresses && !found_dnsname && ++ if (cn_fallback_allowed && + _gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0) { + /* did not get the necessary extension, use CN instead, if the + * certificate would have been acceptable for a TLS WWW server purpose. +-- +2.53.0 + + +From 8dcc6a1f48945997666ac9f10896819edd01a03b Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 13 Mar 2026 17:02:07 +0100 +Subject: [PATCH 4/5] x509/hostname-verify: make URI/SRV SAN preclude CN + fallback + +URI/SRV SAN did not suppress CN fallback as required by RFC 6125 6.4.4: +> a client MUST NOT seek a match for a reference identifier of CN-ID +> if the presented identifiers include a DNS-ID, *SRV-ID*, *URI-ID*, +> or any application-specific identifier types supported by the client. + +With this change, certificates containing URI or SRV SAN +no longer pass DNS hostname checks via CN fallback +to avoid potential misuse of such certificates +beyond their original purpose. + +Reported-by: Oleh Konko +Fixes: #1802 +Fixes: CVE-2026-42012 +Fixes: GNUTLS-SA-2026-04-29-7 +CVSS: 6.5 Medium CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N +Signed-off-by: Alexander Sosedkin +--- + lib/x509/hostname-verify.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c +index dda19b54d..c772cece2 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -108,8 +108,9 @@ unsigned gnutls_x509_crt_check_ip(gnutls_x509_crt_t cert, + * that we do not fallback to CN-ID if we encounter a supported name + * type. + */ +-#define PRECLUDES_CN_FALLBACK(san) \ +- (san == GNUTLS_SAN_DNSNAME || san == GNUTLS_SAN_IPADDRESS) ++#define PRECLUDES_CN_FALLBACK(san) \ ++ (san == GNUTLS_SAN_DNSNAME || san == GNUTLS_SAN_IPADDRESS || \ ++ san == GNUTLS_SAN_URI || san == GNUTLS_SAN_OTHERNAME_SRV) + + /** + * gnutls_x509_crt_check_hostname2: +-- +2.53.0 + + +From b39429d77d4ba022f8597c99b84bbd0a073c815b Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 13 Mar 2026 17:54:56 +0100 +Subject: [PATCH 5/5] tests/hostname-check: extend to exercise no-CN-fallback + +Signed-off-by: Alexander Sosedkin +--- + tests/hostname-check.c | 140 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 140 insertions(+) + +diff --git a/tests/hostname-check.c b/tests/hostname-check.c +index 4edda6c40..4357f33f3 100644 +--- a/tests/hostname-check.c ++++ b/tests/hostname-check.c +@@ -804,6 +804,99 @@ char txt_ip_in_cn[] = + "f0+Un2eHAxFcRZPWdPy1/mn83NUMnjquuA/HHcju+pcoZrEwAI3PPQHgsGQ=\n" + "-----END CERTIFICATE-----\n"; + ++char dns_uri_and_cn[] = ++ "organization = GnuTLS test\n" ++ "cn = example.org\n" ++ "expiration_days = 365\n" ++ "tls_www_server\n" ++ "dns_name = alt.example.org\n" ++ "uri = http://example.org/\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIEWzCCAsOgAwIBAgIUZ2rP89A5RbTdVMCQ86E9qWLSZz4wDQYJKoZIhvcNAQEL\n" ++ "BQAwLDEUMBIGA1UEChMLR251VExTIHRlc3QxFDASBgNVBAMTC2V4YW1wbGUub3Jn\n" ++ "MB4XDTI2MDMxMzE1NDIwNVoXDTI3MDMxMzE1NDIwNVowLDEUMBIGA1UEChMLR251\n" ++ "VExTIHRlc3QxFDASBgNVBAMTC2V4YW1wbGUub3JnMIIBojANBgkqhkiG9w0BAQEF\n" ++ "AAOCAY8AMIIBigKCAYEArEgcC7WDqNJCpO0J3UfCraRWzmwk7soTg7pMmCv1FHWP\n" ++ "ywxfXxbl0jvRK/Owyv7q5QRdfcxSYhxydrloCOdWXhGQejgzcpgLu/Y3Ij0CjIzr\n" ++ "C38vMSS7yUjAuzB4IYtZsgyuB+bCfGH2Y227ntuniIjEnuekA18gfFemRUQ8PDao\n" ++ "EzNAPn8Q3cFlHh5Kr3gkDUduQlRhZdK0ryU/XaAUxz9G9TminIKCl4h1bKNT2pXN\n" ++ "08Cg3eOEi3Bl8lCCA7ufODl5frfrDcjNkxqRKN99zMa9A9hcRzYNF9/asX+BXFyh\n" ++ "7qbypKXAHBi6xi055+CRIBTxFC960qAluv2cOlo1tzZ0Nta75GhWCqLaVZgI0D0n\n" ++ "6SPZPEknVjGPQx1vSnq3ZNdNDdG83yw4QjFtujXdKrflcJafCg3D6LCvEyDPBdYC\n" ++ "reTJ9xWNCdsxdzlTdDk11CbxdIz4jA7qA/forbY9Dv7l0iC9d2zWmn4DOv23QIki\n" ++ "Uw46/ymnpeFaUTyNyyNHAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAwLwYDVR0RBCgw\n" ++ "JoIPYWx0LmV4YW1wbGUub3JnhhNodHRwOi8vZXhhbXBsZS5vcmcvMBMGA1UdJQQM\n" ++ "MAoGCCsGAQUFBwMBMB0GA1UdDgQWBBSXBONihYHHlbM9mrezDF7o607tFTANBgkq\n" ++ "hkiG9w0BAQsFAAOCAYEAHk3bGPAl8YvQE84KZnCnVWBfayFeHKXlN/o/MvpYtPb2\n" ++ "y9cnD8IMmruW3A/UL+md2xx24V+pQWmugB2e879N/Q3QVsSbHFlzPei4tieK5VVd\n" ++ "gLC2iG7N8YjQ0SNRDF22A1QQDcVzdCXOggivs4MelF1zaGfY3ywHOhiHXt0jDj1o\n" ++ "2bP5OflElDFVF7m38RDwdeGokb+raW/2lOJZe4oKpdmllyUtLvrQhdwpogwnbpvH\n" ++ "7ln5Tq4wDNIcxM+Y4MQwe6m0AEELdFZjBmfsZthmaGrbppLTbp14rzC6kKqZ9ay/\n" ++ "zG06DhPalDCR+Bqvmh2Qp25xgqThv9AX8JQU6W8avnkfyxFZwBEJZ0lNoOyLZV2Z\n" ++ "4vmflZyOih9ccGUaYXWXzyc+vxNZjFQwjWNss2vynVvp4+5DUeMWeAj/unvjrxy2\n" ++ "HOsI4FrD94g5PchhXyKXRmeJk4mcr0jtE5ycbmiDU3sXz6xM7hsdBeNFyr47L2nj\n" ++ "OfWEK8ArBc3uTEgp2tIA\n" ++ "-----END CERTIFICATE-----\n"; ++ ++char uri_and_cn[] = ++ "organization = GnuTLS test\n" ++ "expiration_days = 365\n" ++ "tls_www_server\n" ++ "uri = http://example.org/\n" ++ "cn = example.org\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIESjCCArKgAwIBAgIUNC/WOkhQZc6stg6RSrVPaUHRmLwwDQYJKoZIhvcNAQEL\n" ++ "BQAwLDEUMBIGA1UEChMLR251VExTIHRlc3QxFDASBgNVBAMTC2V4YW1wbGUub3Jn\n" ++ "MB4XDTI2MDMxMzE1NTAwNVoXDTI3MDMxMzE1NTAwNVowLDEUMBIGA1UEChMLR251\n" ++ "VExTIHRlc3QxFDASBgNVBAMTC2V4YW1wbGUub3JnMIIBojANBgkqhkiG9w0BAQEF\n" ++ "AAOCAY8AMIIBigKCAYEArEgcC7WDqNJCpO0J3UfCraRWzmwk7soTg7pMmCv1FHWP\n" ++ "ywxfXxbl0jvRK/Owyv7q5QRdfcxSYhxydrloCOdWXhGQejgzcpgLu/Y3Ij0CjIzr\n" ++ "C38vMSS7yUjAuzB4IYtZsgyuB+bCfGH2Y227ntuniIjEnuekA18gfFemRUQ8PDao\n" ++ "EzNAPn8Q3cFlHh5Kr3gkDUduQlRhZdK0ryU/XaAUxz9G9TminIKCl4h1bKNT2pXN\n" ++ "08Cg3eOEi3Bl8lCCA7ufODl5frfrDcjNkxqRKN99zMa9A9hcRzYNF9/asX+BXFyh\n" ++ "7qbypKXAHBi6xi055+CRIBTxFC960qAluv2cOlo1tzZ0Nta75GhWCqLaVZgI0D0n\n" ++ "6SPZPEknVjGPQx1vSnq3ZNdNDdG83yw4QjFtujXdKrflcJafCg3D6LCvEyDPBdYC\n" ++ "reTJ9xWNCdsxdzlTdDk11CbxdIz4jA7qA/forbY9Dv7l0iC9d2zWmn4DOv23QIki\n" ++ "Uw46/ymnpeFaUTyNyyNHAgMBAAGjZDBiMAwGA1UdEwEB/wQCMAAwHgYDVR0RBBcw\n" ++ "FYYTaHR0cDovL2V4YW1wbGUub3JnLzATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNV\n" ++ "HQ4EFgQUlwTjYoWBx5WzPZq3swxe6OtO7RUwDQYJKoZIhvcNAQELBQADggGBAE6w\n" ++ "uGfQy1pi+VbvHFc64QZJhf6r0FQl8Y5kWPu7OI2o+M5/FmY9hXmXxJzAfGH3ecE8\n" ++ "PL/bnR9zRCHTi1ONogukPKPmm/x3AQehn54hvgjZXGFOMxGBB5wSbsEzjCQxgKOO\n" ++ "uzUKZ0zgJin5YEi9g3DGKYi1qDNceNB9LjsWq372FKze0y2zZT7U2xiQcXlKgIZ6\n" ++ "KEcRBQVDygKNeU8ux0Q+lSaymsT9dhs6uahmGUTbbLcsKxsPhJjfC3IWTH+vK3tV\n" ++ "yjjnHcfcITAYSYHOM8+2+5EMOCZmGxCqv3unDkJRYY2xrp3+kXyGXXKRw+yNs4MT\n" ++ "Zc9zymseS+rB+9SDYO4DHDIV+jMJPMcqjJSlglMhs53Z4HFuWcuYJ6FzbRyM7hky\n" ++ "X4El+DWVaajh10QZApiWnRTTafJzJTbYljbpdZVgDX6chAyQRTKj6Di7YrrXmlYZ\n" ++ "iPKiHBqRUnCnxe8HYoBeK5Dw1lzCmgqXp5wjRqo5UHaemgZQTjdlPiWeovEiVg==\n" ++ "-----END CERTIFICATE-----\n"; ++ ++char srv_and_cn[] = ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIESjCCArKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMRQwEgYDVQQKEwtHbnVU\n" ++ "TFMgdGVzdDEUMBIGA1UEAxMLZXhhbXBsZS5vcmcwHhcNMjYwMzEzMTY0OTU5WhcN\n" ++ "MjcwMzEzMTY0OTU5WjAsMRQwEgYDVQQKEwtHbnVUTFMgdGVzdDEUMBIGA1UEAxML\n" ++ "ZXhhbXBsZS5vcmcwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCsSBwL\n" ++ "tYOo0kKk7QndR8KtpFbObCTuyhODukyYK/UUdY/LDF9fFuXSO9Er87DK/urlBF19\n" ++ "zFJiHHJ2uWgI51ZeEZB6ODNymAu79jciPQKMjOsLfy8xJLvJSMC7MHghi1myDK4H\n" ++ "5sJ8YfZjbbue26eIiMSe56QDXyB8V6ZFRDw8NqgTM0A+fxDdwWUeHkqveCQNR25C\n" ++ "VGFl0rSvJT9doBTHP0b1OaKcgoKXiHVso1Palc3TwKDd44SLcGXyUIIDu584OXl+\n" ++ "t+sNyM2TGpEo333Mxr0D2FxHNg0X39qxf4FcXKHupvKkpcAcGLrGLTnn4JEgFPEU\n" ++ "L3rSoCW6/Zw6WjW3NnQ21rvkaFYKotpVmAjQPSfpI9k8SSdWMY9DHW9Kerdk100N\n" ++ "0bzfLDhCMW26Nd0qt+Vwlp8KDcPosK8TIM8F1gKt5Mn3FY0J2zF3OVN0OTXUJvF0\n" ++ "jPiMDuoD9+ittj0O/uXSIL13bNaafgM6/bdAiSJTDjr/Kael4VpRPI3LI0cCAwEA\n" ++ "AaN3MHUwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4E\n" ++ "FgQUlwTjYoWBx5WzPZq3swxe6OtO7RUwMQYDVR0RBCowKKAmBggrBgEFBQcIB6Aa\n" ++ "FhhfeG1wcC1jbGllbnQuZXhhbXBsZS5vcmcwDQYJKoZIhvcNAQELBQADggGBAI0B\n" ++ "WAylO7fWCLtWJqfMrHa3JFH2rkQRf5WV+Z5JqcxlP47m6220+xBiV/iIZT0V6Un/\n" ++ "Z/4Je+jrx6vFIAxtTR1gmyfLo8TfzkEmwPT+uHb16RFkJbi5ik47Mm+31VOrq2G/\n" ++ "s1FP0oNCG6LAnVN1a3Np5uorbUJlTkrd9MxymIvbZMQ56pZvI8TeNgescxD3xY96\n" ++ "/yQXFBy3wu8PIJblG/7r5vPSY8BrEqpRZ0Dch4EoU2RVybE0vXUUTL2moripnHzF\n" ++ "4+mm4Fa8mNb6nII8RjmFDJJzKCQlpmm4R8iNaCvULv8jVO899XUwDSL8+hHt6jZK\n" ++ "aU0pWVCorxpUmgQiQmBAIYvreSM4nKtljSwT2+SBaMna+MaZk2vfBDO15tZH0LWW\n" ++ "OYpnqQQkihP9my4jESvn8FE4NtF5x44XuJVKTVSas1o49XLXq/94fT4DZGa6rdSx\n" ++ "p9Nnj64WFIqbTLoqM3nt7+zqFZDvwh+8ZEVcE1MazHOYhDQj1uU3jqIq/sZE8w==\n" ++ "-----END CERTIFICATE-----\n"; ++ + void doit(void) + { + gnutls_x509_crt_t x509; +@@ -1175,6 +1268,53 @@ void doit(void) + if (ret) + fail("%d: Hostname incorrectly matches (%d)\n", __LINE__, ret); + ++ if (debug) ++ success("Testing not falling back to CN with DNS+URI SAN...\n"); ++ data.data = (unsigned char *)dns_uri_and_cn; ++ data.size = strlen(dns_uri_and_cn); ++ ++ ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); ++ if (ret < 0) ++ fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ++ ++ ret = gnutls_x509_crt_check_hostname(x509, "example.org"); ++ if (ret) ++ fail("%d: Hostname incorrectly falls back to CN (%d)\n", ++ __LINE__, ret); ++ ++ ret = gnutls_x509_crt_check_hostname(x509, "alt.example.org"); ++ if (!ret) ++ fail("%d: Hostname does not match a valid DNS SAN (%d)\n", ++ __LINE__, ret); ++ ++ if (debug) ++ success("Testing not falling back to CN with URI SAN...\n"); ++ data.data = (unsigned char *)uri_and_cn; ++ data.size = strlen(uri_and_cn); ++ ++ ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); ++ if (ret < 0) ++ fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ++ ++ ret = gnutls_x509_crt_check_hostname(x509, "example.org"); ++ if (ret) ++ fail("%d: Hostname incorrectly falls back to CN (%d)\n", ++ __LINE__, ret); ++ ++ if (debug) ++ success("Testing not falling back to CN with SRV SAN...\n"); ++ data.data = (unsigned char *)srv_and_cn; ++ data.size = strlen(srv_and_cn); ++ ++ ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); ++ if (ret < 0) ++ fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ++ ++ ret = gnutls_x509_crt_check_hostname(x509, "example.org"); ++ if (ret) ++ fail("%d: Hostname incorrectly falls back to CN (%d)\n", ++ __LINE__, ret); ++ + gnutls_x509_crt_deinit(x509); + + gnutls_global_deinit(); +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42013-oversized-san.patch b/gnutls-3.8.10-CVE-2026-42013-oversized-san.patch new file mode 100644 index 0000000..a8d62e1 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42013-oversized-san.patch @@ -0,0 +1,245 @@ +From 3ee2cb707002f755e4bda3f75285caa0cb36c214 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 15 Apr 2026 15:35:59 +0200 +Subject: [PATCH 1/3] x509/email-verify: call fallback DN fallback + +A comment was inaccurately referring to DN email field fallback +as CN fallback. +Rename a few things as well to match x509/hostname-verify more closely. + +Signed-off-by: Alexander Sosedkin +--- + lib/x509/email-verify.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/lib/x509/email-verify.c b/lib/x509/email-verify.c +index dbef0bb86..3c22ffed3 100644 +--- a/lib/x509/email-verify.c ++++ b/lib/x509/email-verify.c +@@ -42,7 +42,7 @@ unsigned gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, const char *email, + { + char rfc822name[MAX_CN]; + size_t rfc822namesize; +- int found_rfc822name = 0; ++ bool dn_fallback_allowed = true; + int ret = 0; + int i = 0; + char *a_email; +@@ -76,7 +76,7 @@ unsigned gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, const char *email, + cert, i, rfc822name, &rfc822namesize, NULL); + + if (ret == GNUTLS_SAN_RFC822NAME) { +- found_rfc822name = 1; ++ dn_fallback_allowed = false; + + if (memchr(rfc822name, '\0', rfc822namesize)) { + _gnutls_debug_log( +@@ -102,12 +102,10 @@ unsigned gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, const char *email, + } + } + +- if (!found_rfc822name) { +- /* did not get the necessary extension, use CN instead +- */ ++ if (dn_fallback_allowed) { ++ /* did not get the necessary extension, use DN email instead */ + +- /* enforce the RFC6125 (ยง1.8) requirement that only +- * a single CN must be present */ ++ /* only a single one must be present */ + rfc822namesize = sizeof(rfc822name); + ret = gnutls_x509_crt_get_dn_by_oid(cert, + GNUTLS_OID_PKCS9_EMAIL, 1, +-- +2.53.0 + + +From 29801bef00ecc0f23c0bac4cd333b269cd2c1af4 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 15 Apr 2026 16:02:19 +0200 +Subject: [PATCH 2/3] x509: prevent fallback on oversized SAN + +Passing oversized SAN did not preclude CN (or DN email) fallback +during verification, which is an RFC 6125 6.4.4 violation. + +Now oversized SAN are skipped over, +but prevent the fallback from happening. + +Reported-by: Haruto Kimura (Stella) +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1825 +Fixes: #1849 +Fixes: CVE-2026-42013 +Fixes: GNUTLS-SA-2026-04-27-8 +CVSS: 6.5 Moderate CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N +Signed-off-by: Alexander Sosedkin +--- + lib/x509/email-verify.c | 14 ++++++++++++++ + lib/x509/hostname-verify.c | 14 ++++++++++++++ + 2 files changed, 28 insertions(+) + +diff --git a/lib/x509/email-verify.c b/lib/x509/email-verify.c +index 3c22ffed3..c6cf7a948 100644 +--- a/lib/x509/email-verify.c ++++ b/lib/x509/email-verify.c +@@ -75,6 +75,20 @@ unsigned gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, const char *email, + ret = gnutls_x509_crt_get_subject_alt_name( + cert, i, rfc822name, &rfc822namesize, NULL); + ++ if (ret < 0) { ++ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { ++ /* oversized SAN; proceed without DN fallback */ ++ _gnutls_debug_log("oversized SAN ignored, " ++ "disabling DN fallback\n"); ++ dn_fallback_allowed = false; ++ ret = 0; ++ continue; ++ } ++ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ++ gnutls_assert(); ++ break; ++ } ++ + if (ret == GNUTLS_SAN_RFC822NAME) { + dn_fallback_allowed = false; + +diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c +index c772cece2..2f1865a27 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -213,6 +213,20 @@ hostname_fallback: + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, dnsname, + &dnsnamesize, NULL); + ++ if (ret < 0) { ++ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { ++ /* oversized SAN; proceed without CN fallback */ ++ _gnutls_debug_log("oversized SAN ignored, " ++ "disabling CN fallback\n"); ++ cn_fallback_allowed = false; ++ ret = 0; ++ continue; ++ } ++ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ++ gnutls_assert(); ++ break; ++ } ++ + if (PRECLUDES_CN_FALLBACK(ret)) + cn_fallback_allowed = false; + +-- +2.53.0 + + +From 01a4fd98b5b85f6736333aa0381bb56c9aa8dbb9 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 15 Apr 2026 18:02:31 +0200 +Subject: [PATCH 3/3] tests/cert-tests: add tests for #1825 + +Signed-off-by: Alexander Sosedkin +--- + .../cert-tests/email-certs/oversized-san.pem | 16 +++++++++ + tests/cert-tests/email.sh | 11 ++++++ + tests/hostname-check.c | 34 +++++++++++++++++++ + 3 files changed, 61 insertions(+) + create mode 100644 tests/cert-tests/email-certs/oversized-san.pem + +diff --git a/tests/cert-tests/email-certs/oversized-san.pem b/tests/cert-tests/email-certs/oversized-san.pem +new file mode 100644 +index 000000000..44c0f6997 +--- /dev/null ++++ b/tests/cert-tests/email-certs/oversized-san.pem +@@ -0,0 +1,16 @@ ++-----BEGIN CERTIFICATE----- ++MIICezCCAi2gAwIBAgIUWECpllJihTypDAKZQgEJeM02fG8wBQYDK2VwMDcxFDAS ++BgNVBAMTC2V4YW1wbGUuY29tMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUu ++Y29tMB4XDTI2MDQxNTE1NTE1MloXDTI3MDQxNTE1NTE1MlowNzEUMBIGA1UEAxML ++ZXhhbXBsZS5jb20xHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wKjAF ++BgMrZXADIQCn95fhASNNgr5I/qAX+kiY8SiwPJcTVy1ugWJdX3d4uqOCAUkwggFF ++MA8GA1UdEwEB/wQFMAMBAf8wggERBgNVHREEggEIMIIBBIGCAQBhYWFhYWFhYWFh ++YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh ++YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh ++YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh ++YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh ++YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh ++YWFhYWFhMB0GA1UdDgQWBBTeT2MAM29EwVLvTom8wGN05B7QhDAFBgMrZXADQQAQ ++zTZqdt4LXX21VFce7S99k6XX+N+xPAUo4beursVrlaesdVsfvDtEk2t+0b5WLbtW ++7UI9PxB9CN4hULrxrI8N ++-----END CERTIFICATE----- +diff --git a/tests/cert-tests/email.sh b/tests/cert-tests/email.sh +index 68fbe3e12..8d3ca3317 100644 +--- a/tests/cert-tests/email.sh ++++ b/tests/cert-tests/email.sh +@@ -95,5 +95,16 @@ if test "${rc}" != "1"; then + exit 1 + fi + ++# #1825: oversized SAN does not preclude fallback to DN email ++${VALGRIND} "${CERTTOOL}" \ ++ --infile "${srcdir}/email-certs/oversized-san.pem" \ ++ --load-ca-certificate "${srcdir}/email-certs/oversized-san.pem" \ ++ --verify --verify-email test@example.com ++rc=$? ++ ++if test "${rc}" != "1"; then ++ echo "email test 9 failed" ++ exit 1 ++fi + + exit 0 +diff --git a/tests/hostname-check.c b/tests/hostname-check.c +index 4357f33f3..4a4cdf956 100644 +--- a/tests/hostname-check.c ++++ b/tests/hostname-check.c +@@ -897,6 +897,25 @@ char srv_and_cn[] = + "p9Nnj64WFIqbTLoqM3nt7+zqFZDvwh+8ZEVcE1MazHOYhDQj1uU3jqIq/sZE8w==\n" + "-----END CERTIFICATE-----\n"; + ++char pem_1825_oversized_san[] = ++ "ca\n" ++ "cn = example.com\n" ++ "dns_name = <'a' * 256>\n" ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIICOTCCAeugAwIBAgIURFygaiK3EBmc5AMZToFitMMikhcwBQYDK2VwMBYxFDAS\n" ++ "BgNVBAMTC2V4YW1wbGUuY29tMB4XDTI2MDQxNTE2MDYwMFoXDTI3MDQxNTE2MDYw\n" ++ "MFowFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wKjAFBgMrZXADIQBHqgbjhT1zZ3h9\n" ++ "okSrhd2+0Lr0Uj1q81sqHrcCEdqVpaOCAUkwggFFMA8GA1UdEwEB/wQFMAMBAf8w\n" ++ "ggERBgNVHREEggEIMIIBBIKCAQBhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" ++ "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" ++ "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" ++ "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" ++ "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\n" ++ "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhMB0GA1UdDgQWBBT+\n" ++ "/oWt1Lrfz7Awk9h8yDoz1TKyHjAFBgMrZXADQQBfR5ByQyxpLEsVM5+ihYjSbmYF\n" ++ "1pOFndq0UIKPkWsRqBpitzDIVrVTLlIcY0fQpsxITNgdoIU68WynLGVrRHIF\n" ++ "-----END CERTIFICATE-----\n"; ++ + void doit(void) + { + gnutls_x509_crt_t x509; +@@ -1315,6 +1334,21 @@ void doit(void) + fail("%d: Hostname incorrectly falls back to CN (%d)\n", + __LINE__, ret); + ++ if (debug) ++ success("Testing oversized SAN (#1825)...\n"); ++ data.data = (unsigned char *)pem_1825_oversized_san; ++ data.size = strlen(pem_1825_oversized_san); ++ ++ ret = gnutls_x509_crt_import(x509, &data, GNUTLS_X509_FMT_PEM); ++ if (ret < 0) ++ fail("%d: gnutls_x509_crt_import: %d\n", __LINE__, ret); ++ ++ ret = gnutls_x509_crt_check_hostname(x509, "example.com"); ++ if (ret) ++ fail("%d: Hostname incorrectly falls back to CN " ++ "with oversized SAN (%d)\n", ++ __LINE__, ret); ++ + gnutls_x509_crt_deinit(x509); + + gnutls_global_deinit(); +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42014-so-pin-uaf.patch b/gnutls-3.8.10-CVE-2026-42014-so-pin-uaf.patch new file mode 100644 index 0000000..cf2145c --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42014-so-pin-uaf.patch @@ -0,0 +1,61 @@ +From 3957f136e2ed23caf176a594b54b3827f5cef701 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Wed, 18 Mar 2026 18:19:06 +0100 +Subject: [PATCH] pkcs11_write: fix UAF and leak in gnutls_pkcs11_token_set_pin + +Changing Security Officer PIN with gnutls_pkcs11_token_set_pin() with +oldpin == NULL for a token that lacks a protected authentication path +led to a use-after-free. + +Reported-by: Luigino Camastra and Joshua Rogers of AISLE Research Team +Fixes: #1766 +Fixes: #1809 +Fixes: CVE-2026-42014 +Fixes: GNUTLS-SA-2026-04-29-9 +CVSS: 4.0 Medium CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L +Signed-off-by: Alexander Sosedkin +--- + lib/pkcs11_write.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/lib/pkcs11_write.c b/lib/pkcs11_write.c +index 64b85a2df..1dff578f2 100644 +--- a/lib/pkcs11_write.c ++++ b/lib/pkcs11_write.c +@@ -1266,10 +1266,9 @@ int gnutls_pkcs11_token_set_pin(const char *token_url, const char *oldpin, + ses_flags = SESSION_WRITE | SESSION_LOGIN; + + ret = pkcs11_open_session(&sinfo, NULL, info, ses_flags); +- p11_kit_uri_free(info); +- + if (ret < 0) { + gnutls_assert(); ++ p11_kit_uri_free(info); + return ret; + } + +@@ -1290,9 +1289,11 @@ int gnutls_pkcs11_token_set_pin(const char *token_url, const char *oldpin, + oldpin_size = L(oldpin); + + if (!(sinfo.tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { +- if (newpin == NULL) +- return gnutls_assert_val( ++ if (newpin == NULL) { ++ ret = gnutls_assert_val( + GNUTLS_E_INVALID_REQUEST); ++ goto finish; ++ } + + if (oldpin == NULL) { + struct pin_info_st pin_info; +@@ -1324,6 +1325,7 @@ int gnutls_pkcs11_token_set_pin(const char *token_url, const char *oldpin, + ret = 0; + + finish: ++ p11_kit_uri_free(info); + pkcs11_close_session(&sinfo); + return ret; + } +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-42015-p12-bag32.patch b/gnutls-3.8.10-CVE-2026-42015-p12-bag32.patch new file mode 100644 index 0000000..621f882 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-42015-p12-bag32.patch @@ -0,0 +1,44 @@ +From a3e7c50d3e1761e5ef1d4b225507cab8f2b2c3ca Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 20 Apr 2026 22:42:20 +0200 +Subject: [PATCH] x509/pkcs12_bag: fix off-by-one in bag element bounds check + +Appending elements to a PKCS#12 bag had a bounds check that +prevented adding the 32nd element. +On the other hand, it is possible to import one that already has 32. +Subsequent appending then led to writing past the 32-element array, +smashing its length. + +Tighten the check to reject any bag with 32 or more elements. + +We'll treat this vulnerability as a Low due to how contrived +the requirements are: for the code to be vulnerable, +it needs to append to an imported untrusted unencrypted PKCS#12 structure. + +Reported-by: Zou Dikai +Fixes: #1840 +Fixes: CVE-2026-42015 +Fixes: GNUTLS-SA-2026-04-29-11 +CVSS: 6.1 Medium CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:H +Severity: Low +Signed-off-by: Alexander Sosedkin +--- + lib/x509/pkcs12_bag.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/x509/pkcs12_bag.c b/lib/x509/pkcs12_bag.c +index 911aeff93..38228613c 100644 +--- a/lib/x509/pkcs12_bag.c ++++ b/lib/x509/pkcs12_bag.c +@@ -375,7 +375,7 @@ int gnutls_pkcs12_bag_set_data(gnutls_pkcs12_bag_t bag, + return GNUTLS_E_INVALID_REQUEST; + } + +- if (bag->bag_elements == MAX_BAG_ELEMENTS - 1) { ++ if (bag->bag_elements >= MAX_BAG_ELEMENTS - 1) { + gnutls_assert(); + /* bag is full */ + return GNUTLS_E_MEMORY_ERROR; +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-5260-p11-rsa-overread.patch b/gnutls-3.8.10-CVE-2026-5260-p11-rsa-overread.patch new file mode 100644 index 0000000..866db6d --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-5260-p11-rsa-overread.patch @@ -0,0 +1,107 @@ +From 77228f2d1ac207d2f894e5a168fbb47e5378e42f Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 30 Mar 2026 17:31:07 +0200 +Subject: [PATCH 1/2] lib/auth/rsa: check that ciphertext matches the modulus + size + +A client sending extremely short premaster secret as part of an +RSA key exchange could've theoretically triggered a short heap overread +to nowhere when the RSA key was backed with a PKCS#11 token. +With this fix, the internal decryption function will not be called +with an mismatching plaintext length specified, avoiding the overread. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1814 +Fixes: CVE-2026-5260 +Fixes: GNUTLS-SA-2026-04-29-10 +CVSS: 5.9 Medium CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H +Signed-off-by: Alexander Sosedkin +--- + lib/auth/rsa.c | 5 +++++ + lib/auth/rsa_psk.c | 5 +++++ + 2 files changed, 10 insertions(+) + +diff --git a/lib/auth/rsa.c b/lib/auth/rsa.c +index 4d181327b..496c378b3 100644 +--- a/lib/auth/rsa.c ++++ b/lib/auth/rsa.c +@@ -158,6 +158,7 @@ static int proc_rsa_client_kx(gnutls_session_t session, uint8_t *data, + int ret, dsize; + ssize_t data_size = _data_size; + volatile uint8_t ver_maj, ver_min; ++ unsigned int key_bits; + + #ifdef ENABLE_SSL3 + if (get_num_version(session) == GNUTLS_SSL3) { +@@ -180,6 +181,10 @@ static int proc_rsa_client_kx(gnutls_session_t session, uint8_t *data, + } + ciphertext.size = dsize; + } ++ gnutls_privkey_get_pk_algorithm(session->internals.selected_key, ++ &key_bits); ++ if (ciphertext.size != (key_bits + 7) / 8) ++ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + + ver_maj = _gnutls_get_adv_version_major(session); + ver_min = _gnutls_get_adv_version_minor(session); +diff --git a/lib/auth/rsa_psk.c b/lib/auth/rsa_psk.c +index cc92b4aa9..dba40119e 100644 +--- a/lib/auth/rsa_psk.c ++++ b/lib/auth/rsa_psk.c +@@ -257,6 +257,7 @@ static int _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, + ssize_t data_size = _data_size; + gnutls_psk_server_credentials_t cred; + volatile uint8_t ver_maj, ver_min; ++ unsigned int rsa_key_bits; + + cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred( + session, GNUTLS_CRD_PSK); +@@ -313,6 +314,10 @@ static int _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + ciphertext.size = dsize; ++ gnutls_privkey_get_pk_algorithm(session->internals.selected_key, ++ &rsa_key_bits); ++ if (ciphertext.size != (rsa_key_bits + 7) / 8) ++ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); + + ver_maj = _gnutls_get_adv_version_major(session); + ver_min = _gnutls_get_adv_version_minor(session); +-- +2.53.0 + + +From cf6bdc5e4df49e5583d3fb4d2296779785f10683 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 30 Mar 2026 17:46:40 +0200 +Subject: [PATCH 2/2] lib/pkcs11_privkey: guard against overreading on short + ciphertexts + +This is an alternative fix for the callee side. + +Reported-by: Joshua Rogers of AISLE Research Team +Fixes: #1814 +Fixes: CVE-2026-5260 +Fixes: GNUTLS-SA-2026-04-29-10 +CVSS: 5.9 Medium CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H +Signed-off-by: Alexander Sosedkin +--- + lib/pkcs11_privkey.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c +index 7f5db8d26..ea5054978 100644 +--- a/lib/pkcs11_privkey.c ++++ b/lib/pkcs11_privkey.c +@@ -838,7 +838,7 @@ int _gnutls_pkcs11_privkey_decrypt_data2(gnutls_pkcs11_privkey_t key, + if (ret != 0) + return gnutls_assert_val(GNUTLS_E_LOCKING_ERROR); + +- buffer = gnutls_malloc(siglen); ++ buffer = gnutls_malloc(MAX((size_t)siglen, plaintext_size)); + if (!buffer) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; +-- +2.53.0 + diff --git a/gnutls-3.8.10-CVE-2026-5419-p7-constant-time.patch b/gnutls-3.8.10-CVE-2026-5419-p7-constant-time.patch new file mode 100644 index 0000000..2dcea28 --- /dev/null +++ b/gnutls-3.8.10-CVE-2026-5419-p7-constant-time.patch @@ -0,0 +1,360 @@ +From 1e627aa5ad95c6dc0518d94e9a009997b081a1ab Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Wed, 1 Apr 2026 18:57:21 +0900 +Subject: [PATCH 1/2] gnutls_cipher_decrypt3: make PKCS#7 unpadding branch free + +This tries to make the logic of PKCS#7 padding removal constant-time, +by removing potential branching operations. + +Reported-by: Doria Tang of Stony Brook University +Fixes: #1815 +Fixes: CVE-2026-5419 +Fixes: GNUTLS-SA-2026-04-29-13 +CVSS: 3.7 Low CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N +Signed-off-by: Daiki Ueno +--- + lib/crypto-api.c | 54 +++++++++++++++++------ + lib/libgnutls.map | 2 + + tests/Makefile.am | 2 +- + tests/pkcs7-pad.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 153 insertions(+), 14 deletions(-) + create mode 100644 tests/pkcs7-pad.c + +diff --git a/lib/crypto-api.c b/lib/crypto-api.c +index 01539d5b5..32143e9de 100644 +--- a/lib/crypto-api.c ++++ b/lib/crypto-api.c +@@ -498,6 +498,39 @@ error: + return ret; + } + ++/* If succeeds, returns the number of padding bytes to be removed; ++ * zero otherwise. ++ */ ++unsigned int _gnutls_pkcs7_unpad(const uint8_t *block, unsigned int block_size) ++{ ++ uint8_t padding = block[block_size - 1]; ++ volatile unsigned int mask = ~0; ++ volatile unsigned int count = 0; ++ ++ /* Count consecutive PADDING bytes from the end, in a ++ * constant-time manner. ++ */ ++ for (size_t i = block_size; i > 0; i--) { ++ volatile unsigned int mask2; ++ ++ mask2 = -(unsigned int)(block[i - 1] == padding); ++ mask2 &= -(unsigned int)(count < padding); ++ ++ /* MASK is initially ~0 and will be flipped to 0 upon first ++ * non-padding bytes. ++ */ ++ mask &= mask2; ++ count += 1 & mask; ++ } ++ ++ /* PADDING == 0 is effectively excluded here, given COUNT ++ * will never be 0. ++ */ ++ mask = -(unsigned int)(count <= block_size); ++ mask &= -(unsigned int)(count == padding); ++ return count & mask; ++} ++ + /** + * gnutls_cipher_decrypt3: + * @handle: is a #gnutls_cipher_hd_t type +@@ -532,22 +565,17 @@ int gnutls_cipher_decrypt3(gnutls_cipher_hd_t handle, const void *ctext, + if (_gnutls_cipher_type(h->ctx_enc.e) == CIPHER_BLOCK && + (flags & GNUTLS_CIPHER_PADDING_PKCS7)) { + uint8_t *p = ptext; +- uint8_t padding = p[*ptext_len - 1]; +- if (!padding || +- padding > _gnutls_cipher_get_block_size(h->ctx_enc.e)) { +- return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); +- } +- /* Check that the prior bytes are all PADDING */ +- for (size_t i = *ptext_len - padding; i < *ptext_len; i++) { +- if (padding != p[*ptext_len - 1]) { +- return gnutls_assert_val( +- GNUTLS_E_DECRYPTION_FAILED); +- } +- } ++ size_t block_size = _gnutls_cipher_get_block_size(h->ctx_enc.e); ++ uint8_t *block = &p[*ptext_len - block_size]; ++ unsigned int padding = _gnutls_pkcs7_unpad(block, block_size); ++ volatile unsigned int mask; ++ ++ mask = -(unsigned int)(padding == 0); ++ ret = GNUTLS_E_DECRYPTION_FAILED & mask; + *ptext_len -= padding; + } + +- return 0; ++ return ret; + } + + /** +diff --git a/lib/libgnutls.map b/lib/libgnutls.map +index c2366833d..e22150033 100644 +--- a/lib/libgnutls.map ++++ b/lib/libgnutls.map +@@ -1560,4 +1560,6 @@ GNUTLS_PRIVATE_3_4 { + _gnutls_pathbuf_append; + _gnutls_pathbuf_truncate; + _gnutls_pathbuf_deinit; ++ # needed by tests/pkcs7-pad ++ _gnutls_pkcs7_unpad; + } GNUTLS_3_4; +diff --git a/tests/Makefile.am b/tests/Makefile.am +index b0311169c..3bc3d0340 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -241,7 +241,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei + x509cert-dntypes id-on-xmppAddr tls13-compat-mode ciphersuite-name \ + x509-upnconstraint xts-key-check cipher-padding pkcs7-verify-double-free \ + fips-rsa-sizes tls12-rehandshake-ticket pathbuf tls-force-ems \ +- psk-importer privkey-derive dh-compute2 ecdh-compute2 \ ++ psk-importer privkey-derive dh-compute2 ecdh-compute2 pkcs7-pad \ + mini-dtls-fragments + + ctests += tls-channel-binding +diff --git a/tests/pkcs7-pad.c b/tests/pkcs7-pad.c +new file mode 100644 +index 000000000..4a7c231c8 +--- /dev/null ++++ b/tests/pkcs7-pad.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (C) 2026 Red Hat, Inc. ++ * ++ * This file is part of GnuTLS. ++ * ++ * GnuTLS is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GnuTLS is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GnuTLS. If not, see . ++ */ ++ ++/* Test that _gnutls_pkcs7_unpad is branch-free, using valgrind */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++ ++#ifdef HAVE_VALGRIND_MEMCHECK_H ++#include ++#endif ++ ++#include "utils.h" ++ ++static inline void _gnutls_memory_mark_undefined(void *addr, size_t size) ++{ ++#ifdef HAVE_VALGRIND_MEMCHECK_H ++ if (RUNNING_ON_VALGRIND) ++ VALGRIND_MAKE_MEM_UNDEFINED(addr, size); ++#endif ++} ++ ++static inline void _gnutls_memory_mark_defined(void *addr, size_t size) ++{ ++#ifdef HAVE_VALGRIND_MEMCHECK_H ++ if (RUNNING_ON_VALGRIND) ++ VALGRIND_MAKE_MEM_DEFINED(addr, size); ++#endif ++} ++ ++extern unsigned int _gnutls_pkcs7_unpad(const uint8_t *block, ++ unsigned int block_size); ++ ++static unsigned int wrap_pkcs7_unpad(uint8_t *block, unsigned int block_size) ++{ ++ unsigned int padding; ++ ++ _gnutls_memory_mark_undefined(block, block_size); ++ ++ padding = _gnutls_pkcs7_unpad(block, block_size); ++ ++ _gnutls_memory_mark_defined(block, block_size); ++ _gnutls_memory_mark_defined(&padding, sizeof(padding)); ++ ++ return padding; ++} ++ ++#define PAD 5 ++ ++void doit(void) ++{ ++ uint8_t block[16]; ++ unsigned int padding; ++ ++ memset(block, 0xFF, sizeof(block)); ++ memset(&block[sizeof(block) - PAD], PAD, PAD); ++ ++ padding = wrap_pkcs7_unpad(block, sizeof(block)); ++ if (padding != PAD) ++ fail("padding should be %d\n", PAD); ++ ++ /* The last padding byte exceeds the block size */ ++ block[sizeof(block) - 1] = sizeof(block) + 1; ++ padding = wrap_pkcs7_unpad(block, sizeof(block)); ++ if (padding != 0) ++ fail("padding should be 0\n"); ++ block[sizeof(block) - 1] = PAD; ++ ++ /* The last padding byte is zero */ ++ block[sizeof(block) - 1] = 0; ++ padding = wrap_pkcs7_unpad(block, sizeof(block)); ++ if (padding != 0) ++ fail("padding should be 0\n"); ++ block[sizeof(block) - 1] = PAD; ++ ++ /* The first padding byte is invalid */ ++ block[sizeof(block) - PAD] = PAD + 1; ++ padding = wrap_pkcs7_unpad(block, sizeof(block)); ++ if (padding != 0) ++ fail("padding should be 0\n"); ++ block[sizeof(block) - PAD] = PAD; ++ ++ /* The byte before the first padding equals to PAD */ ++ block[sizeof(block) - PAD - 1] = PAD; ++ padding = wrap_pkcs7_unpad(block, sizeof(block)); ++ if (padding != PAD) ++ fail("padding should be %d\n", PAD); ++ block[sizeof(block) - PAD - 1] = 0xFF; ++} +-- +2.53.0 + + +From 74d8f53ed35a25c72c3756c5dfee52012dcf955e Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Wed, 1 Apr 2026 19:01:50 +0900 +Subject: [PATCH 2/2] tests/cipher-padding: exercise invalid padding case + +This adds a negative test case, where a PKCS#7 padding is manipulated. + +Signed-off-by: Daiki Ueno +--- + tests/cipher-padding.c | 53 +++++++++++++++++++++++++++++++----------- + 1 file changed, 40 insertions(+), 13 deletions(-) + +diff --git a/tests/cipher-padding.c b/tests/cipher-padding.c +index c5cca333f..2ee3588f5 100644 +--- a/tests/cipher-padding.c ++++ b/tests/cipher-padding.c +@@ -43,9 +43,11 @@ static void start(gnutls_cipher_algorithm_t algo, size_t plaintext_size, + uint8_t key16[64]; + uint8_t iv16[32]; + uint8_t plaintext[128]; ++ uint8_t plaintext2[128]; + uint8_t ciphertext[128]; + size_t block_size; + size_t size; ++ size_t ciphertext_size; + gnutls_datum_t key, iv; + + success("%s %zu %u\n", gnutls_cipher_get_name(algo), plaintext_size, +@@ -80,39 +82,41 @@ static void start(gnutls_cipher_algorithm_t algo, size_t plaintext_size, + } + + /* Get the ciphertext size */ +- ret = gnutls_cipher_encrypt3(ch, plaintext, plaintext_size, NULL, &size, +- flags); ++ ret = gnutls_cipher_encrypt3(ch, plaintext, plaintext_size, NULL, ++ &ciphertext_size, flags); + if (ret < 0) { + fail("gnutls_cipher_encrypt3 failed\n"); + } + + if (flags & GNUTLS_CIPHER_PADDING_PKCS7) { +- if (size <= plaintext_size) { ++ if (ciphertext_size <= plaintext_size) { + fail("no padding appended\n"); + } +- if (size != CLAMP(plaintext_size, block_size)) { +- fail("size does not match: %zu (expected %zu)\n", size, ++ if (ciphertext_size != CLAMP(plaintext_size, block_size)) { ++ fail("size does not match: %zu (expected %zu)\n", ++ ciphertext_size, + CLAMP(plaintext_size, block_size)); + } + } else { +- if (size != plaintext_size) { +- fail("size does not match: %zu (expected %zu)\n", size, +- plaintext_size); ++ if (ciphertext_size != plaintext_size) { ++ fail("size does not match: %zu (expected %zu)\n", ++ ciphertext_size, plaintext_size); + } + } + + /* Encrypt with padding */ + ret = gnutls_cipher_encrypt3(ch, plaintext, plaintext_size, ciphertext, +- &size, flags); ++ &ciphertext_size, flags); + if (ret < 0) { + fail("gnutls_cipher_encrypt3 failed\n"); + } + + /* Decrypt with padding */ +- ret = gnutls_cipher_decrypt3(ch, ciphertext, size, ciphertext, &size, +- flags); ++ size = ciphertext_size; ++ ret = gnutls_cipher_decrypt3(ch, ciphertext, ciphertext_size, ++ plaintext2, &size, flags); + if (ret < 0) { +- fail("gnutls_cipher_encrypt3 failed\n"); ++ fail("gnutls_cipher_decrypt3 failed\n"); + } + + if (size != plaintext_size) { +@@ -120,10 +124,33 @@ static void start(gnutls_cipher_algorithm_t algo, size_t plaintext_size, + plaintext_size); + } + +- if (memcmp(ciphertext, plaintext, size) != 0) { ++ if (memcmp(plaintext2, plaintext, size) != 0) { + fail("plaintext does not match\n"); + } + ++ if ((flags & GNUTLS_CIPHER_PADDING_PKCS7) && ++ plaintext_size % block_size != 0) { ++ /* Encrypt with manual padding */ ++ memset(&plaintext[plaintext_size], ++ ciphertext_size - plaintext_size, ++ ciphertext_size - plaintext_size); ++ /* Insert a wrong padding byte */ ++ plaintext[plaintext_size] = block_size; ++ ret = gnutls_cipher_encrypt3(ch, plaintext, ciphertext_size, ++ ciphertext, &ciphertext_size, 0); ++ if (ret < 0) { ++ fail("gnutls_cipher_encrypt3 failed\n"); ++ } ++ ++ /* Decrypt with padding */ ++ size = ciphertext_size; ++ ret = gnutls_cipher_decrypt3(ch, ciphertext, ciphertext_size, ++ plaintext, &size, flags); ++ if (ret != GNUTLS_E_DECRYPTION_FAILED) { ++ fail("gnutls_cipher_decrypt3 succeeded\n"); ++ } ++ } ++ + gnutls_cipher_deinit(ch); + } + +-- +2.53.0 + diff --git a/gnutls.spec b/gnutls.spec index 683145d..6e20745 100644 --- a/gnutls.spec +++ b/gnutls.spec @@ -13,7 +13,7 @@ print(string.sub(hash, 0, 16)) } Version: 3.8.10 -Release: 3%{?dist} +Release: 4%{?dist} # not upstreamed Patch: gnutls-3.2.7-rpath.patch Patch: gnutls-3.7.2-enable-intel-cet.patch @@ -35,13 +35,41 @@ Patch: gnutls-3.8.10-rhel9-revert-pbmac1-fips-default.patch # * da1df0a31 fips: Allow SigVer only with RSA keys with modulus >= 2048 bits Patch: gnutls-3.8.10-rhel9-revert-rsa-less-than-2048.patch +# CVE fixes backported from 3.8.12 release # upstreamed: https://gitlab.com/gnutls/gnutls/-/merge_requests/2041 Patch: gnutls-3.8.10-CVE-2025-9820.patch # upstreamed: https://gitlab.com/gnutls/gnutls/-/merge_requests/2062 Patch: gnutls-3.8.10-CVE-2025-14831.patch - # intentionally omitted: CVE-2026-1584, since 3.8.10 is not vulnerable +# CVE fixes backported from 3.8.13 release +# (https://gitlab.com/gnutls/gnutls/-/merge_requests/2102) +Patch: gnutls-3.8.10-CVE-2026-33846-dtls-len.patch +Patch: gnutls-3.8.10-CVE-2026-42009-dtls-qsort.patch +Patch: gnutls-3.8.10-CVE-2026-33845-dtls-uflow.patch +Patch: gnutls-3.8.10-CVE-2026-42010-psk-nul.patch +Patch: gnutls-3.8.10-CVE-2026-3833-nc-case.patch +Patch: gnutls-3.8.10-CVE-2026-42011-nc-intersect.patch +Patch: gnutls-3.8.10-CVE-2026-42012-url-san-cn.patch +Patch: gnutls-3.8.10-CVE-2026-42013-oversized-san.patch +Patch: gnutls-3.8.10-CVE-2026-42014-so-pin-uaf.patch +Patch: gnutls-3.8.10-CVE-2026-5260-p11-rsa-overread.patch +Patch: gnutls-3.8.10-CVE-2026-42015-p12-bag32.patch +Patch: gnutls-3.8.10-CVE-2026-3832-ocsp-rev-0.patch +Patch: gnutls-3.8.10-CVE-2026-5419-p7-constant-time.patch +# non-CVE security fixes from the same release +Patch: gnutls-3.8.10-1808-psk-rehandshake.patch +Patch: gnutls-3.8.10-1810-ocsp-truncated-eku.patch +Patch: gnutls-3.8.10-1813-p11p-aes-ephemeral.patch +Patch: gnutls-3.8.10-1818-rsa-coprime.patch +Patch: gnutls-3.8.10-1818-pem-parsing.patch +Patch: gnutls-3.8.10-1819-dblfree-mid-import.patch +Patch: gnutls-3.8.10-1822-sct-overread.patch +Patch: gnutls-3.8.10-1841-hybrid-kx-zeroize.patch +Patch: gnutls-3.8.10-1823-cfg-clear-options.patch +Patch: gnutls-3.8.10-1817-security-parameters.patch +Patch: gnutls-3.8.10-1820-p11p-kdf.patch + %bcond_without bootstrap %bcond_without dane %if 0%{?rhel} @@ -486,6 +514,32 @@ make check %{?_smp_mflags} GNUTLS_SYSTEM_PRIORITY_FILE=/dev/null XFAIL_TESTS="$x %endif %changelog +* Thu Apr 30 2026 Alexander Sosedkin - 3.8.10-4 +- Fix CVE-2026-33846 (DTLS fragment reassembly, High, heap overwrite) +- Fix CVE-2026-42009 (DTLS fragment reassembly, High, undefined behaviour) +- Fix CVE-2026-33845 (DTLS fragment reassembly, High, heap overread) +- Fix CVE-2026-42010 (PSK authentication, High, authentication bypass) +- Fix CVE-2026-3833 (Name constraints, Medium, name constraint bypass) +- Fix CVE-2026-42011 (Name constraints, Medium, name constraint bypass) +- Fix CVE-2026-42012 (CN fallback, Medium, certificate misuse) +- Fix CVE-2026-42013 (CN fallback, Medium, certificate misuse) +- Fix CVE-2026-42014 (PKCS#11 PIN change, Medium, use-after-free) +- Fix CVE-2026-5260 (PKCS#11 RSA, Medium, heap overread) +- Fix CVE-2026-42015 (PKCS#12 appending, Low, heap overwrite) +- Fix CVE-2026-3832 (OCSP, Low, revocation bypass) +- Fix CVE-2026-5419 (PKCS#7, Low, timing side-channel) +- Fix upstream security issue #1808 (PSK rehandshake) +- Fix upstream security issue #1810 (EKU OID prefix match) +- Fix upstream security issue #1813 (pkcs11-provider persistent keys) +- Fix upstream security issue #1818 (RSA correctness, OpenSSL format import) +- Fix upstream security issue #1819 (PKCS#11 trust removal error path) +- Fix upstream security issue #1822 (SCT extension parser OOB read) +- Fix upstream security issue #1841 (key zeroization in hybrid kex) +- Fix upstream security issue #1823 (malformed certtool template) +- Fix upstream security issue #1817 (session parameter loading robustness) +- Fix upstream security issue #1820 (PKCS#11 KDF succeeding w/o deriving) +- gnutls-3.8.10-CVE-2025-9820.patch: update Makefile.in + * Fri Feb 6 2026 Alexander Sosedkin - 3.8.10-3 - Fix PKCS#11 token initialization label overflow (CVE-2025-9820) - Fix name constraint processing performance issue (CVE-2025-14831)