From 09d4f85c6e57e76e43ed1ffffd22f91dd44f03c4 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 26 May 2026 03:09:04 -0400 Subject: [PATCH] import CS git gnutls-3.6.16-8.el8_10.6 --- .../gnutls-3.6.16-1808-psk-rehandshake.patch | 226 +++ ...nutls-3.6.16-1810-ocsp-truncated-eku.patch | 1474 +++++++++++++++++ ...utls-3.6.16-1817-security-parameters.patch | 35 + SOURCES/gnutls-3.6.16-1818-pem-parsing.patch | 116 ++ SOURCES/gnutls-3.6.16-1818-rsa-coprime.patch | 41 + ...nutls-3.6.16-1819-dblfree-mid-import.patch | 58 + ...tls-3.6.16-CVE-2026-33845-dtls-uflow.patch | 488 ++++++ ...nutls-3.6.16-CVE-2026-33846-dtls-len.patch | 944 +++++++++++ .../gnutls-3.6.16-CVE-2026-3833-nc-case.patch | 166 ++ ...tls-3.6.16-CVE-2026-42009-dtls-qsort.patch | 95 ++ ...gnutls-3.6.16-CVE-2026-42010-psk-nul.patch | 498 ++++++ ...s-3.6.16-CVE-2026-42011-nc-intersect.patch | 176 ++ ...tls-3.6.16-CVE-2026-42012-url-san-cn.patch | 552 ++++++ ...-3.6.16-CVE-2026-42013-oversized-san.patch | 244 +++ ...tls-3.6.16-CVE-2026-42014-so-pin-uaf.patch | 60 + ...utls-3.6.16-CVE-2026-42015-p12-bag32.patch | 44 + ....6.16-CVE-2026-5260-p11-rsa-overread.patch | 107 ++ SPECS/gnutls.spec | 49 +- 18 files changed, 5371 insertions(+), 2 deletions(-) create mode 100644 SOURCES/gnutls-3.6.16-1808-psk-rehandshake.patch create mode 100644 SOURCES/gnutls-3.6.16-1810-ocsp-truncated-eku.patch create mode 100644 SOURCES/gnutls-3.6.16-1817-security-parameters.patch create mode 100644 SOURCES/gnutls-3.6.16-1818-pem-parsing.patch create mode 100644 SOURCES/gnutls-3.6.16-1818-rsa-coprime.patch create mode 100644 SOURCES/gnutls-3.6.16-1819-dblfree-mid-import.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-33845-dtls-uflow.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-33846-dtls-len.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-3833-nc-case.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42009-dtls-qsort.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42010-psk-nul.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42011-nc-intersect.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42012-url-san-cn.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42013-oversized-san.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42014-so-pin-uaf.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-42015-p12-bag32.patch create mode 100644 SOURCES/gnutls-3.6.16-CVE-2026-5260-p11-rsa-overread.patch diff --git a/SOURCES/gnutls-3.6.16-1808-psk-rehandshake.patch b/SOURCES/gnutls-3.6.16-1808-psk-rehandshake.patch new file mode 100644 index 0000000..90a2ba5 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-1808-psk-rehandshake.patch @@ -0,0 +1,226 @@ +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 | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/lib/handshake-checks.c b/lib/handshake-checks.c +index b07b9680c..e02210531 100644 +--- a/lib/handshake-checks.c ++++ b/lib/handshake-checks.c +@@ -75,11 +75,16 @@ int _gnutls_check_id_for_change(gnutls_session_t session) + if (username == NULL) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + +- if (session->internals.saved_username_size != -1) { +- if (session->internals.saved_username_size == username_length && +- strncmp(session->internals.saved_username, username, username_length) != 0) { +- _gnutls_debug_log("Session's PSK username changed during rehandshake; aborting!\n"); +- return gnutls_assert_val(GNUTLS_E_SESSION_USER_ID_CHANGED); ++ if (session->internals.saved_username && ++ session->internals.saved_username_size != -1) { ++ 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( ++ GNUTLS_E_SESSION_USER_ID_CHANGED); + } + } else { + memcpy(session->internals.saved_username, username, username_length); +-- +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 | 50 ++++++++++++++++++------------- + 1 file changed, 29 insertions(+), 21 deletions(-) + +diff --git a/tests/rehandshake-switch-psk-id.c b/tests/rehandshake-switch-psk-id.c +index c8beec13f..62593060a 100644 +--- a/tests/rehandshake-switch-psk-id.c ++++ b/tests/rehandshake-switch-psk-id.c +@@ -24,10 +24,10 @@ + #include + #endif + ++#include + #include + #include + #include +-#include + #include + #include "utils.h" + #include "eagain-common.h" +@@ -35,6 +35,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) +@@ -42,11 +44,8 @@ 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) ++static int pskfunc(gnutls_session_t session, const char *username, ++ gnutls_datum_t *key) + { + if (debug) + printf("psk: username %s\n", username); +@@ -76,6 +75,9 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, unsigned allow_chang + 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) +@@ -170,22 +172,28 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, unsigned allow_chang + + 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(); +- +- /* 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(); ++ /* 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(); ++ } ++ } ++ + 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 | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/tests/rehandshake-switch-psk-id.c b/tests/rehandshake-switch-psk-id.c +index 62593060a..5e06abe05 100644 +--- a/tests/rehandshake-switch-psk-id.c ++++ b/tests/rehandshake-switch-psk-id.c +@@ -58,7 +58,8 @@ static int pskfunc(gnutls_session_t session, const char *username, + return 0; + } + +-static void try(const char *prio, gnutls_kx_algorithm_t kx, unsigned allow_change) ++static void try(const char *prio, gnutls_kx_algorithm_t kx, ++ unsigned allow_change, const char *username) + { + int ret; + /* Server stuff. */ +@@ -75,8 +76,8 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, unsigned allow_chang + 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); +@@ -120,7 +121,7 @@ static void try(const char *prio, gnutls_kx_algorithm_t kx, unsigned allow_chang + 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); +@@ -184,14 +185,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/SOURCES/gnutls-3.6.16-1810-ocsp-truncated-eku.patch b/SOURCES/gnutls-3.6.16-1810-ocsp-truncated-eku.patch new file mode 100644 index 0000000..ea1f393 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-1810-ocsp-truncated-eku.patch @@ -0,0 +1,1474 @@ +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 caa511e9d..74fd19ace 100644 +--- a/lib/x509/ocsp.c ++++ b/lib/x509/ocsp.c +@@ -2174,7 +2174,10 @@ static int check_ocsp_purpose(gnutls_x509_crt_t signercert) + return gnutls_assert_val(rc); + } + +- if (memcmp(oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) { ++ /* x509_read_value() includes NUL in size for OIDs on 3.6, ++ unlike in 3.8 */ ++ if (oidsize != sizeof(GNUTLS_KP_OCSP_SIGNING) || ++ 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 0f1a1b8cf..ff50d974f 100644 +--- a/tests/ocsp.c ++++ b/tests/ocsp.c +@@ -489,6 +489,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; +@@ -1601,6 +1713,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; +@@ -1676,6 +1838,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 | 642 ++++++++++++++------------------------------------- + 1 file changed, 178 insertions(+), 464 deletions(-) + +diff --git a/tests/ocsp.c b/tests/ocsp.c +index ff50d974f..e42b10463 100644 +--- a/tests/ocsp.c ++++ b/tests/ocsp.c +@@ -611,329 +611,222 @@ 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) { ++ rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, p, NULL, NULL); ++ 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) { ++ rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, NULL, p, NULL); ++ 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) { ++ rc = gnutls_ocsp_req_add_cert_id(req, GNUTLS_DIG_SHA1, NULL, NULL, p); ++ 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) { +- fail("gnutls_ocsp_resp_get_responder_raw_id %s\n", gnutls_strerror(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) ++ fail("gnutls_ocsp_resp_get_responder_raw_id %s\n", ++ gnutls_strerror(rc)); + + gnutls_free(dat.data); + +@@ -951,26 +844,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 +868,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 +884,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 +893,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 +915,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) { ++ if (n2.size == got.size && ++ 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 +998,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 */ + +@@ -1165,14 +1007,10 @@ static void req_addcert_id(void) + gnutls_datum_t nonce = + { (unsigned char *) REQ1NONCE, sizeof(REQ1NONCE) - 1 }; + +- ret = +- gnutls_ocsp_req_set_extension(req, +- "1.3.6.1.5.5.7.48.1.2", +- 0, &nonce); +- if (ret != 0) { ++ ret = gnutls_ocsp_req_set_extension(req, "1.3.6.1.5.5.7.48.1.2", ++ 0, &nonce); ++ if (ret != 0) + fail("gnutls_ocsp_req_set_extension %d\n", ret); +- exit(1); +- } + } + + /* add cert_id */ +@@ -1188,19 +1026,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) { +@@ -1208,23 +1042,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 */ +@@ -1243,10 +1072,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,14 +1081,10 @@ static void req_addcert(void) + gnutls_datum_t nonce = + { (unsigned char *) REQ1NONCE, sizeof(REQ1NONCE) - 1 }; + +- ret = +- gnutls_ocsp_req_set_extension(req, +- "1.3.6.1.5.5.7.48.1.2", +- 0, &nonce); +- if (ret != 0) { ++ ret = gnutls_ocsp_req_set_extension(req, "1.3.6.1.5.5.7.48.1.2", ++ 0, &nonce); ++ if (ret != 0) + fail("gnutls_ocsp_req_set_extension %d\n", ret); +- exit(1); +- } + } + + /* add cert_id */ +@@ -1269,39 +1092,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) { ++ ret = gnutls_x509_crt_import(issuer, &issuer_data, ++ GNUTLS_X509_FMT_PEM); ++ 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) { ++ ret = gnutls_x509_crt_import(subject, &subject_data, ++ GNUTLS_X509_FMT_PEM); ++ 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) { ++ ret = gnutls_ocsp_req_add_cert(req, GNUTLS_DIG_SHA1, issuer, ++ subject); ++ if (ret != 0) + fail("gnutls_ocsp_add_cert %d\n", ret); +- exit(1); +- } + + gnutls_x509_crt_deinit(subject); + gnutls_x509_crt_deinit(issuer); +@@ -1310,10 +1121,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) { +@@ -1321,23 +1130,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 */ +@@ -1396,26 +1200,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) { +- fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, gnutls_strerror(ret)); +- exit(1); +- } ++ if (ret != 0) ++ fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, ++ gnutls_strerror(ret)); + + /* 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,33 +1222,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) { +- fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, gnutls_strerror(ret)); +- exit(1); +- } ++ if (ret != 0) ++ fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, ++ gnutls_strerror(ret)); + + 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); + +@@ -1460,31 +1254,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) { +- fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, gnutls_strerror(ret)); +- exit(1); +- } ++ if (ret != 0) ++ fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, ++ gnutls_strerror(ret)); + + /* 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); + +@@ -1504,204 +1292,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) { ++ ret = gnutls_x509_crt_import(cert, &blog_cert_data, ++ GNUTLS_X509_FMT_PEM); ++ 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) { ++ ret = gnutls_x509_crt_import(issuer, &blog_issuer_data, ++ GNUTLS_X509_FMT_PEM); ++ 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) { ++ ret = gnutls_x509_crt_import(signer, &blog_signer_data, ++ GNUTLS_X509_FMT_PEM); ++ 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) { ++ ret = gnutls_ocsp_resp_verify_direct(resp, cert, &verify, ++ GNUTLS_VERIFY_ALLOW_BROKEN); ++ 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) { ++ ret = gnutls_ocsp_resp_verify(resp, list, &verify, ++ GNUTLS_VERIFY_ALLOW_BROKEN); ++ 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) { ++ ret = gnutls_ocsp_resp_verify(resp, list, &verify, ++ GNUTLS_VERIFY_ALLOW_BROKEN); ++ 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); + +@@ -1773,45 +1501,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) { +- fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, gnutls_strerror(ret)); +- exit(1); +- } ++ if (ret != 0) ++ fail("gnutls_ocsp_resp_import[%d]: %s\n", __LINE__, ++ gnutls_strerror(ret)); + + 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) { ++ ret = gnutls_x509_crt_import(signer, &long_resp_signer_data, ++ GNUTLS_X509_FMT_PEM); ++ 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); +@@ -1822,10 +1538,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/SOURCES/gnutls-3.6.16-1817-security-parameters.patch b/SOURCES/gnutls-3.6.16-1817-security-parameters.patch new file mode 100644 index 0000000..6baa8ea --- /dev/null +++ b/SOURCES/gnutls-3.6.16-1817-security-parameters.patch @@ -0,0 +1,35 @@ +From 4e8d3ba9160dfd3155c2fab12e9d5ab973013c2d 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 | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/lib/session_pack.c b/lib/session_pack.c +index a6d11c4cf..823824e4c 100644 +--- a/lib/session_pack.c ++++ b/lib/session_pack.c +@@ -1006,6 +1006,10 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps) + &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, +-- +2.53.0 + diff --git a/SOURCES/gnutls-3.6.16-1818-pem-parsing.patch b/SOURCES/gnutls-3.6.16-1818-pem-parsing.patch new file mode 100644 index 0000000..7e8d784 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-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 9fc70e032..b81671e7f 100644 +--- a/lib/x509/privkey_openssl.c ++++ b/lib/x509/privkey_openssl.c +@@ -174,7 +174,8 @@ 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; +@@ -225,6 +226,8 @@ 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); +-- +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 d2c8a724b..0e1e74804 100644 +--- a/tests/key-openssl.c ++++ b/tests/key-openssl.c +@@ -95,6 +95,21 @@ const char key2[] = + "F3bDyqlxSOm7uxF/K3YzI44v8/D8GGnLBTpN+ANBdiY=\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 */ ++ + void doit(void) + { + gnutls_x509_privkey_t pkey; +@@ -167,5 +182,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/SOURCES/gnutls-3.6.16-1818-rsa-coprime.patch b/SOURCES/gnutls-3.6.16-1818-rsa-coprime.patch new file mode 100644 index 0000000..2abf0a2 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-1818-rsa-coprime.patch @@ -0,0 +1,41 @@ +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 | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c +index b19fe3804..9d1c97b86 100644 +--- a/lib/nettle/pk.c ++++ b/lib/nettle/pk.c +@@ -2911,9 +2911,12 @@ 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 = +-- +2.53.0 + diff --git a/SOURCES/gnutls-3.6.16-1819-dblfree-mid-import.patch b/SOURCES/gnutls-3.6.16-1819-dblfree-mid-import.patch new file mode 100644 index 0000000..94d919c --- /dev/null +++ b/SOURCES/gnutls-3.6.16-1819-dblfree-mid-import.patch @@ -0,0 +1,58 @@ +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 | 4 ++-- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/lib/pkcs11.c b/lib/pkcs11.c +index d8d4a6511..6a4915f3f 100644 +--- a/lib/pkcs11.c ++++ b/lib/pkcs11.c +@@ -3661,6 +3661,7 @@ 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 9820595e9..9f30a219d 100644 +--- a/lib/x509/verify-high2.c ++++ b/lib/x509/verify-high2.c +@@ -216,7 +216,7 @@ int add_trust_list_pkcs11_object_url(gnutls_x509_trust_list_t list, const char * + goto cleanup; + } + +- xcrt_list = gnutls_malloc(sizeof(gnutls_x509_crt_t) * pcrt_list_size); ++ xcrt_list = gnutls_calloc(pcrt_list_size, sizeof(gnutls_x509_crt_t)); + if (xcrt_list == NULL) { + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; +@@ -264,7 +264,7 @@ int remove_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url) + goto cleanup; + } + +- xcrt_list = gnutls_malloc(sizeof(gnutls_x509_crt_t) * pcrt_list_size); ++ 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/SOURCES/gnutls-3.6.16-CVE-2026-33845-dtls-uflow.patch b/SOURCES/gnutls-3.6.16-CVE-2026-33845-dtls-uflow.patch new file mode 100644 index 0000000..15806b6 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-CVE-2026-33845-dtls-uflow.patch @@ -0,0 +1,488 @@ +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 | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index be51f3aac..d9ef1d907 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -876,7 +876,7 @@ 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; ++ HANDSHAKE_HEADER_SIZE(session), data_size, frag_length; + + /* Note: SSL2_HEADERS == 1 */ + if (_mbuffer_get_udata_size(bufel) < handshake_header_size) +@@ -892,7 +892,7 @@ parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel, + && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) { + handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ + +- frag_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* we've read the first byte */ ++ frag_length = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* we've read the first byte */ + + if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO) + return +@@ -902,7 +902,7 @@ 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 */ +@@ -919,12 +919,12 @@ parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel, + hsk->sequence = _gnutls_read_uint16(&dataptr[4]); + hsk->start_offset = + _gnutls_read_uint24(&dataptr[6]); +- frag_size = ++ frag_length = + _gnutls_read_uint24(&dataptr[9]); + } else { + hsk->sequence = 0; + hsk->start_offset = 0; +- frag_size = ++ frag_length = + MIN((_mbuffer_get_udata_size(bufel) - + handshake_header_size), hsk->length); + } +@@ -940,8 +940,8 @@ 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; + +@@ -949,15 +949,15 @@ 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, ++ 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 && ++ if (hsk->length > 0 && (frag_length > data_size || ++ (frag_length > 0 && + hsk->end_offset >= hsk->length))) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); +-- +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 | 64 +++++++++++++++++++++++++----------------------- + lib/gnutls_int.h | 4 +-- + 2 files changed, 36 insertions(+), 32 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index d9ef1d907..134d680f4 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -940,10 +940,7 @@ 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", +@@ -956,14 +953,16 @@ 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->end_offset >= hsk->length))) { ++ if (hsk->length > 0 && ++ (frag_length > data_size || ++ (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 +- && hsk->start_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); + +@@ -1020,19 +1019,19 @@ static int merge_handshake_packet(gnutls_session_t session, + gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); + + if (!exists) { +- if (hsk->length > 0 && hsk->end_offset > 0 +- && hsk->end_offset - hsk->start_offset + 1 != +- hsk->length) { ++ if (hsk->length > 0) { + ret = + _gnutls_buffer_resize(&hsk->data, hsk->length); + if (ret < 0) + return gnutls_assert_val(ret); + + hsk->data.length = 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++; +@@ -1066,20 +1065,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); + } +@@ -1140,8 +1146,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, +@@ -1153,7 +1159,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); + else +@@ -1341,9 +1349,7 @@ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) + ret); + + data_size = +- MIN(tmp.length, +- tmp.end_offset - tmp.start_offset + +- 1); ++ MIN(tmp.length, tmp.frag_length); + + ret = + _gnutls_buffer_append_data(&tmp.data, +@@ -1361,9 +1367,7 @@ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) + merge_handshake_packet(session, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); +- +- } +- while (_mbuffer_get_udata_size(bufel) > 0); ++ } while (_mbuffer_get_udata_size(bufel) > 0); + + prev = bufel; + bufel = +diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h +index 815f69b10..0e753a739 100644 +--- a/lib/gnutls_int.h ++++ b/lib/gnutls_int.h +@@ -396,10 +396,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 | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 134d680f4..7aa22811f 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -953,16 +953,10 @@ 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); + +-- +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 4f4c8f623..5d226a56a 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; +@@ -459,7 +503,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 | 58 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 58 insertions(+) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index 5d226a56a..03881058a 100644 +--- a/tests/mini-dtls-fragments.c ++++ b/tests/mini-dtls-fragments.c +@@ -500,6 +500,62 @@ 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, 80 * 1000, 8 * 1000); ++ ++ 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(); +@@ -513,6 +569,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/SOURCES/gnutls-3.6.16-CVE-2026-33846-dtls-len.patch b/SOURCES/gnutls-3.6.16-CVE-2026-33846-dtls-len.patch new file mode 100644 index 0000000..1a87be5 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-CVE-2026-33846-dtls-len.patch @@ -0,0 +1,944 @@ +From 4f94e5cfe1f252a431e41642b0752e7e0daf43b9 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Fri, 20 Mar 2026 16:09:40 +0100 +Subject: [PATCH 1/8] tests/mini-dtls-fragments: implement a basic DTLS test + +Signed-off-by: Alexander Sosedkin +--- + tests/Makefile.am | 6 +- + tests/mini-dtls-fragments.c | 206 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 211 insertions(+), 1 deletion(-) + create mode 100644 tests/mini-dtls-fragments.c + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index afb665597..f85a6947d 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -224,7 +224,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei + set_x509_ocsp_multi_cli kdf-api keylog-func \ + dtls_hello_random_value tls_hello_random_value x509cert-dntypes \ + pkcs7-verify-double-free \ +- tls12-rehandshake-ticket ++ tls12-rehandshake-ticket mini-dtls-fragments + + if HAVE_SECCOMP_TESTS + ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp +@@ -487,6 +487,10 @@ buffer_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..0abe557f7 +--- /dev/null ++++ b/tests/mini-dtls-fragments.c +@@ -0,0 +1,206 @@ ++/* ++ * 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, 80 * 1000, 8 * 1000); ++ gnutls_dtls_set_timeouts(server, 80 * 1000, 8 * 1000); ++ ++ 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 87b63fec37a9bae87ec34f6a55c57cb64fe4c7aa Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 4 May 2026 09:16:13 +0000 +Subject: [PATCH 2/8] buffers: reformat ssmerge_handshake_packet + +Signed-off-by: Alexander Sosedkin + +--- + lib/buffers.c | 64 +++++++++++++++++++++++++-------------------------- + 1 file changed, 31 insertions(+), 33 deletions(-) + +diff --git a/lib/buffers.c b/lib/buffers.c +index 2d0e3d8af..827e97fe5 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -1031,46 +1031,44 @@ 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( ++ &session->internals.handshake_recv_buffer[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], ++ 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], + 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); ++ 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], ++ 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], + 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); ++ 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); + } + _gnutls_handshake_buffer_clear(hsk); + } +-- +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 3/8] 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 827e97fe5..9ff606501 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -992,9 +992,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; +@@ -1031,44 +1033,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 4/8] 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 9ff606501..20ff909bd 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -1036,6 +1036,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 5/8] tests/mini-dtls-fragments: extend with a #1816 reproducer + +Signed-off-by: Alexander Sosedkin +--- + tests/mini-dtls-fragments.c | 119 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 119 insertions(+) + +diff --git a/tests/mini-dtls-fragments.c b/tests/mini-dtls-fragments.c +index 0abe557f7..d997ad64f 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); +@@ -196,10 +201,124 @@ 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, 80 * 1000, 8 * 1000); ++ ++ 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 6/8] 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 d997ad64f..a1fa7f8dd 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; +@@ -313,12 +346,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 7/8] 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 20ff909bd..48715adcc 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -996,7 +996,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 8/8] 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 a1fa7f8dd..77daa6225 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; +@@ -216,12 +216,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"); + +@@ -418,14 +428,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/SOURCES/gnutls-3.6.16-CVE-2026-3833-nc-case.patch b/SOURCES/gnutls-3.6.16-CVE-2026-3833-nc-case.patch new file mode 100644 index 0000000..a09afc9 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-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 197fc47ac..a126123b1 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include "c-strcase.h" + + #include "ip.h" + #include "ip-in-cidr.h" +@@ -80,7 +81,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) + { +@@ -96,6 +97,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, +@@ -121,8 +138,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 64e82ad35..7683da433 100644 +--- a/tests/name-constraints.c ++++ b/tests/name-constraints.c +@@ -324,6 +324,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/SOURCES/gnutls-3.6.16-CVE-2026-42009-dtls-qsort.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42009-dtls-qsort.patch new file mode 100644 index 0000000..b9a5c10 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-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 48715adcc..9f33821dd 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -996,8 +996,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 9f33821dd..be51f3aac 100644 +--- a/lib/buffers.c ++++ b/lib/buffers.c +@@ -866,11 +866,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/SOURCES/gnutls-3.6.16-CVE-2026-42010-psk-nul.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42010-psk-nul.patch new file mode 100644 index 0000000..76a342f --- /dev/null +++ b/SOURCES/gnutls-3.6.16-CVE-2026-42010-psk-nul.patch @@ -0,0 +1,498 @@ +From 382afcb74f8cdabd2234374c730c33332f06c7b2 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 4 May 2026 10:08:34 +0000 +Subject: [PATCH 1/6] tests/pskself2: reformat + +--- + tests/pskself2.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/tests/pskself2.c b/tests/pskself2.c +index 81286a035..c587df060 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -287,8 +287,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, unsigned exp_hint) + { + pid_t child; + int err; +@@ -331,11 +330,19 @@ void doit(void) + run_test("NORMAL:-VERS-ALL:+VERS-TLS1.2:-KX-ALL:+DHE-PSK", 1); + + 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.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.3:-GROUP-ALL:+GROUP-FFDHE2048:+DHE-PSK", ++ 0); ++ run_test( ++ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+ECDHE-PSK", ++ 0); + /* the following should work once we support PSK without DH */ + run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+PSK", 0); + +-- +2.53.0 + + +From e3ffd31846d1e6624338a26ca7fce7d1685b17cd Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 19:02:43 +0200 +Subject: [PATCH 2/6] tests/pskself2: extend with RSA-PSK support + +Signed-off-by: Alexander Sosedkin +--- + tests/pskself2.c | 81 ++++++++++++++++++++++++++++++++---------------- + 1 file changed, 54 insertions(+), 27 deletions(-) + +diff --git a/tests/pskself2.c b/tests/pskself2.c +index c587df060..974d48334 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -28,6 +28,7 @@ + #include + #endif + ++#include + #include + #include + +@@ -52,6 +53,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. + */ +@@ -66,12 +68,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; +@@ -111,6 +114,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 +@@ -166,6 +174,8 @@ static void client(int sd, const char *prio, unsigned exp_hint) + + gnutls_free(user.data); + gnutls_psk_free_client_credentials(pskcred); ++ if (xcred) ++ gnutls_certificate_free_credentials(xcred); + + gnutls_global_deinit(); + } +@@ -195,9 +205,10 @@ pskfunc(gnutls_session_t session, const gnutls_datum_t *username, + } + + +-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; +@@ -217,6 +228,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 +@@ -225,6 +243,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); +@@ -280,6 +301,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(); + +@@ -287,7 +310,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; +@@ -313,42 +336,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:+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:-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", 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", false, false); ++ run_test("NORMAL:-KX-ALL:+ECDHE-PSK", false, false); ++ run_test("NORMAL:-KX-ALL:+DHE-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); ++ /* 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 3/6] 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, 2 insertions(+), 1 deletion(-) + +diff --git a/lib/auth/rsa_psk.c b/lib/auth/rsa_psk.c +index 8f3fe5a4b..8813eeeec 100644 +--- a/lib/auth/rsa_psk.c ++++ b/lib/auth/rsa_psk.c +@@ -332,7 +332,7 @@ _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, uint8_t * data, + * filled in if the key is not found. + */ + ret = +- _gnutls_psk_pwd_find_entry(session, info->username, strlen(info->username), &pwd_psk); ++ _gnutls_psk_pwd_find_entry(session, info->username, info->username_len, &pwd_psk); + if (ret < 0) + return gnutls_assert_val(ret); + +-- +2.53.0 + + +From cf20434d5cb8f3508e6ed2abdcb3e07bf28b9b6f Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 21 Apr 2026 19:19:42 +0200 +Subject: [PATCH 4/6] tests/pskself2: test username with NUL in the middle + (#1850) + +Signed-off-by: Alexander Sosedkin +--- + tests/pskself2.c | 31 +++++++++++++++++++++---------- + 1 file changed, 22 insertions(+), 10 deletions(-) + +diff --git a/tests/pskself2.c b/tests/pskself2.c +index 974d48334..508711aa9 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -28,6 +28,7 @@ + #include + #endif + ++#include + #include + #include + #include +@@ -87,12 +87,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, +@@ -191,14 +194,20 @@ 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; +@@ -212,7 +221,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 + */ +@@ -265,7 +275,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 5/6] 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 508711aa9..71c94cc1d 100644 +--- a/tests/pskself2.c ++++ b/tests/pskself2.c +@@ -76,7 +76,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; + +@@ -195,7 +195,7 @@ 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); +@@ -206,9 +206,9 @@ 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 6/6] 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 | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/lib/auth/psk_passwd.c b/lib/auth/psk_passwd.c +index 9a9d68c48..c87f2d68e 100644 +--- a/lib/auth/psk_passwd.c ++++ b/lib/auth/psk_passwd.c +@@ -78,7 +78,7 @@ static int pwd_put_values(gnutls_datum_t * psk, char *str) + 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, + return false; + + if (line_size == 0) +- return (username->size == 0); ++ return false; + + /* move to first ':' */ + i = 0; +@@ -100,6 +100,9 @@ static bool username_matches(const gnutls_datum_t *username, + 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]; +@@ -108,17 +111,17 @@ static bool username_matches(const gnutls_datum_t *username, + if ((retval = 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; + } + + +-- +2.53.0 + diff --git a/SOURCES/gnutls-3.6.16-CVE-2026-42011-nc-intersect.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42011-nc-intersect.patch new file mode 100644 index 0000000..6b82c06 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-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 a126123b1..5161f9926 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -717,9 +717,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 76430fb80..387395c6c 100644 +--- a/tests/name-constraints-merge.c ++++ b/tests/name-constraints-merge.c +@@ -369,6 +369,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/SOURCES/gnutls-3.6.16-CVE-2026-42012-url-san-cn.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42012-url-san-cn.patch new file mode 100644 index 0000000..5da2f6c --- /dev/null +++ b/SOURCES/gnutls-3.6.16-CVE-2026-42012-url-san-cn.patch @@ -0,0 +1,552 @@ +From fc909c3abddcc2955bebf0de403136ed9ec689c2 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Tue, 28 Apr 2026 15:26:32 +0200 +Subject: [PATCH 1/6] 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 d2adc4e25..4bbfa1e0e 100644 +--- a/lib/x509/virt-san.c ++++ b/lib/x509/virt-san.c +@@ -98,26 +98,27 @@ int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutl + 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: + ret = _gnutls_krb5_principal_to_der((char*)san->data, &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/6] 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 | 9 ++++++++- + lib/x509/virt-san.c | 25 +++++++++++++++++++++++++ + lib/x509/x509.c | 6 ++++-- + 6 files changed, 43 insertions(+), 5 deletions(-) + +diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in +index 264da238a..e5906617a 100644 +--- a/lib/includes/gnutls/gnutls.h.in ++++ b/lib/includes/gnutls/gnutls.h.in +@@ -2692,6 +2692,7 @@ gnutls_psk_set_server_params_function(gnutls_psk_server_credentials_t + * @GNUTLS_SAN_REGISTERED_ID: RegisteredID. + * @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_SRV: Virtual SAN, used by certain functions for convenience. + * + * Enumeration of different subject alternative names types. + */ +@@ -2708,7 +2709,8 @@ typedef enum gnutls_x509_subject_alt_name_t { + that they are represented by an otherName value and an OID. + Used by gnutls_x509_crt_get_subject_alt_othername_oid. */ + GNUTLS_SAN_OTHERNAME_XMPP = 1000, +- GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL ++ GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL, ++ 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 483bd1de6..37ed0b160 100644 +--- a/lib/x509/common.h ++++ b/lib/x509/common.h +@@ -102,6 +102,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 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 GOST28147_89_OID "1.2.643.2.2.21" +diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c +index 5161f9926..37e1c098e 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -520,7 +520,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_IPADDRESS && ++ 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 705e8babf..3c996186b 100644 +--- a/lib/x509/output.c ++++ b/lib/x509/output.c +@@ -108,8 +108,10 @@ print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, gnutls_datu + + if ((type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_OTHERNAME_XMPP + || type == GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL ++ || type == GNUTLS_SAN_OTHERNAME_SRV + || type == GNUTLS_SAN_RFC822NAME +- || type == GNUTLS_SAN_URI) && sname != NULL && strlen(sname) != name->size) { ++ || type == GNUTLS_SAN_URI) && sname != NULL ++ && strlen(sname) != name->size) { + adds(str, + _("warning: SAN contains an embedded NUL, " + "replacing with '!'\n")); +@@ -156,6 +158,11 @@ print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, gnutls_datu + addf(str, _("%sKRB5Principal: %.*s\n"), prefix, 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 4bbfa1e0e..a59da4299 100644 +--- a/lib/x509/virt-san.c ++++ b/lib/x509/virt-san.c +@@ -40,6 +40,9 @@ int san_othername_to_virtual(const char *oid, size_t size) + else if ((unsigned) size == (sizeof(KRB5_PRINCIPAL_OID)-1) + && memcmp(oid, KRB5_PRINCIPAL_OID, sizeof(KRB5_PRINCIPAL_OID)-1) == 0) + return GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL; ++ 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; +@@ -53,6 +56,8 @@ const char * virtual_to_othername_oid(unsigned type) + return XMPP_OID; + case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL: + return KRB5_PRINCIPAL_OID; ++ case GNUTLS_SAN_OTHERNAME_SRV: ++ return SRV_OID; + default: + return NULL; + } +@@ -108,6 +113,17 @@ int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutl + 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); + } +@@ -172,6 +188,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 c713f857a..877b88b26 100644 +--- a/lib/x509/x509.c ++++ b/lib/x509/x509.c +@@ -1382,7 +1382,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; +@@ -1855,7 +1856,8 @@ get_alt_name(gnutls_subject_alt_names_t san, + goto cleanup; + } + +- if (othername_oid && type == GNUTLS_SAN_OTHERNAME && ooid.data) { ++ /* API uses othername_oid=0; map to virtual types regardless */ ++ if (type == GNUTLS_SAN_OTHERNAME && ooid.data) { + unsigned vtype; + ret = gnutls_x509_othername_to_virtual((char*)ooid.data, &oname, &vtype, &virt); + if (ret >= 0) { +-- +2.53.0 + + +From 5a21e1e175f6c853ab3ee39a4d2d9adfb80e3731 Mon Sep 17 00:00:00 2001 +From: Alexander Sosedkin +Date: Mon, 4 May 2026 10:53:26 +0000 +Subject: [PATCH 3/6] x509/hostname-verify: use memchr for embedded-null check + +_gnutls_has_embedded_null uses strlen, which reads past the buffer if +there is no NUL within the first size bytes. memchr(p, '\0', size) is +the bounded equivalent. +--- + lib/x509/hostname-verify.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c +index 6ef8ba030..15d548661 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -220,7 +220,7 @@ gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + if (ret == GNUTLS_SAN_DNSNAME) { + found_dnsname = 1; + +- if (_gnutls_has_embedded_null(dnsname, dnsnamesize)) { ++ if (memchr(dnsname, '\0', dnsnamesize)) { + _gnutls_debug_log("certificate has %s with embedded null in name\n", dnsname); + continue; + } +-- +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/6] x509/hostname-verify: refactor and simplify CN fallback + logic + +Signed-off-by: Alexander Sosedkin +--- + lib/x509/hostname-verify.c | 17 ++++++++--------- + 1 file changed, 8 insertions(+), 9 deletions(-) + +diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c +index 6ef8ba030..698356f32 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -112,7 +112,8 @@ 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) (san==GNUTLS_SAN_DNSNAME||san==GNUTLS_SAN_IPADDRESS) ++#define PRECLUDES_CN_FALLBACK(san) \ ++ (san == GNUTLS_SAN_DNSNAME || san == GNUTLS_SAN_IPADDRESS) + + /** + * gnutls_x509_crt_check_hostname2: +@@ -154,13 +155,12 @@ 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 */ +@@ -217,9 +217,10 @@ gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + &dnsnamesize, + NULL); + +- if (ret == GNUTLS_SAN_DNSNAME) { +- found_dnsname = 1; ++ if (PRECLUDES_CN_FALLBACK(ret)) ++ cn_fallback_allowed = false; + ++ if (ret == GNUTLS_SAN_DNSNAME) { + if (memchr(dnsname, '\0', dnsnamesize)) { + _gnutls_debug_log("certificate has %s with embedded null in name\n", dnsname); + continue; +@@ -235,13 +236,11 @@ gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + ret = 1; + goto cleanup; + } +- } else { +- if (IS_SAN_SUPPORTED(ret)) +- have_other_addresses = 1; + } + } + +- if (!have_other_addresses && !found_dnsname && _gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0) { ++ 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. + * That is because only for that purpose the CN is a valid field to +-- +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 5/6] 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 eb0fddaa8..d46fd965f 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -112,8 +112,9 @@ 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 6/6] tests/hostname-check: extend to exercise no-CN-fallback + +Signed-off-by: Alexander Sosedkin +--- + tests/hostname-check.c | 141 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 141 insertions(+) + +diff --git a/tests/hostname-check.c b/tests/hostname-check.c +index 47f8d355d..71481cafb 100644 +--- a/tests/hostname-check.c ++++ b/tests/hostname-check.c +@@ -831,6 +831,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) + { +@@ -1214,6 +1307,54 @@ void doit(void) + + gnutls_openpgp_crt_deinit(pgp); + #endif ++ ++ 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/SOURCES/gnutls-3.6.16-CVE-2026-42013-oversized-san.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42013-oversized-san.patch new file mode 100644 index 0000000..379362b --- /dev/null +++ b/SOURCES/gnutls-3.6.16-CVE-2026-42013-oversized-san.patch @@ -0,0 +1,244 @@ +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 053e51287..0d55e5524 100644 +--- a/lib/x509/email-verify.c ++++ b/lib/x509/email-verify.c +@@ -43,7 +43,7 @@ gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, + { + 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; +@@ -79,7 +79,7 @@ gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, + NULL); + + if (ret == GNUTLS_SAN_RFC822NAME) { +- found_rfc822name = 1; ++ dn_fallback_allowed = false; + + if (_gnutls_has_embedded_null(rfc822name, rfc822namesize)) { + _gnutls_debug_log("certificate has %s with embedded null in rfc822name\n", rfc822name); +@@ -99,12 +99,10 @@ gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, + } + } + +- 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, 0, rfc822name, +-- +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 0d55e5524..f755d766e 100644 +--- a/lib/x509/email-verify.c ++++ b/lib/x509/email-verify.c +@@ -78,6 +78,20 @@ gnutls_x509_crt_check_email(gnutls_x509_crt_t cert, + &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 d46fd965f..6d6de00ca 100644 +--- a/lib/x509/hostname-verify.c ++++ b/lib/x509/hostname-verify.c +@@ -218,6 +218,20 @@ gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert, + &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 | 11 +++++++ + tests/hostname-check.c | 33 +++++++++++++++++++ + 3 files changed, 60 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 b/tests/cert-tests/email +index a00281028..d2bd48ee3 100755 +--- a/tests/cert-tests/email ++++ b/tests/cert-tests/email +@@ -96,5 +96,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 71481cafb..068bf7831 100644 +--- a/tests/hostname-check.c ++++ b/tests/hostname-check.c +@@ -924,6 +924,24 @@ 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) + { +@@ -1355,6 +1373,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/SOURCES/gnutls-3.6.16-CVE-2026-42014-so-pin-uaf.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42014-so-pin-uaf.patch new file mode 100644 index 0000000..420cfd3 --- /dev/null +++ b/SOURCES/gnutls-3.6.16-CVE-2026-42014-so-pin-uaf.patch @@ -0,0 +1,60 @@ +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 5685411ee..194126e95 100644 +--- a/lib/pkcs11_write.c ++++ b/lib/pkcs11_write.c +@@ -1297,10 +1297,9 @@ gnutls_pkcs11_token_set_pin(const char *token_url, + 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; + } + +@@ -1322,8 +1321,10 @@ gnutls_pkcs11_token_set_pin(const char *token_url, + oldpin_size = L(oldpin); + + if (!(sinfo.tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { +- if (newpin == NULL) +- return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ++ if (newpin == NULL) { ++ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ++ goto finish; ++ } + + if (oldpin == NULL) { + struct pin_info_st pin_info; +@@ -1354,6 +1355,7 @@ gnutls_pkcs11_token_set_pin(const char *token_url, + ret = 0; + + finish: ++ p11_kit_uri_free(info); + pkcs11_close_session(&sinfo); + return ret; + +-- +2.53.0 + diff --git a/SOURCES/gnutls-3.6.16-CVE-2026-42015-p12-bag32.patch b/SOURCES/gnutls-3.6.16-CVE-2026-42015-p12-bag32.patch new file mode 100644 index 0000000..23b2e8c --- /dev/null +++ b/SOURCES/gnutls-3.6.16-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 163b0fadb..351996b2f 100644 +--- a/lib/x509/pkcs12_bag.c ++++ b/lib/x509/pkcs12_bag.c +@@ -394,7 +394,7 @@ 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/SOURCES/gnutls-3.6.16-CVE-2026-5260-p11-rsa-overread.patch b/SOURCES/gnutls-3.6.16-CVE-2026-5260-p11-rsa-overread.patch new file mode 100644 index 0000000..59a0f8d --- /dev/null +++ b/SOURCES/gnutls-3.6.16-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 02b6a3425..b2665d3af 100644 +--- a/lib/auth/rsa.c ++++ b/lib/auth/rsa.c +@@ -159,6 +159,7 @@ 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) { +@@ -181,6 +182,10 @@ 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 8813eeeec..7768b60f2 100644 +--- a/lib/auth/rsa_psk.c ++++ b/lib/auth/rsa_psk.c +@@ -270,6 +270,7 @@ _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, uint8_t * data, + 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); +@@ -324,6 +325,10 @@ _gnutls_proc_rsa_psk_client_kx(gnutls_session_t session, uint8_t * data, + 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 3ecd1837b..334132ef3 100644 +--- a/lib/pkcs11_privkey.c ++++ b/lib/pkcs11_privkey.c +@@ -769,7 +769,7 @@ _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/SPECS/gnutls.spec b/SPECS/gnutls.spec index 0f913fd..8ba9939 100644 --- a/SPECS/gnutls.spec +++ b/SPECS/gnutls.spec @@ -1,5 +1,5 @@ Version: 3.6.16 -Release: 8%{?dist}.5 +Release: 8%{?dist}.6 Patch1: gnutls-3.2.7-rpath.patch Patch2: gnutls-3.6.4-no-now-guile.patch Patch3: gnutls-3.6.13-enable-intel-cet.patch @@ -21,6 +21,33 @@ Patch24: gnutls-3.6.16-cve-2025-32988.patch Patch25: gnutls-3.6.16-cve-2025-32990.patch Patch26: gnutls-3.6.16-CVE-2025-9820.patch Patch27: gnutls-3.6.16-CVE-2025-14831.patch +# CVE fixes backported from 3.8.13 release +# (https://gitlab.com/gnutls/gnutls/-/merge_requests/2102) +Patch28: gnutls-3.6.16-CVE-2026-33846-dtls-len.patch +Patch29: gnutls-3.6.16-CVE-2026-42009-dtls-qsort.patch +Patch30: gnutls-3.6.16-CVE-2026-33845-dtls-uflow.patch +Patch31: gnutls-3.6.16-CVE-2026-42010-psk-nul.patch +Patch32: gnutls-3.6.16-CVE-2026-3833-nc-case.patch +Patch33: gnutls-3.6.16-CVE-2026-42011-nc-intersect.patch +Patch34: gnutls-3.6.16-CVE-2026-42012-url-san-cn.patch +Patch35: gnutls-3.6.16-CVE-2026-42013-oversized-san.patch +Patch36: gnutls-3.6.16-CVE-2026-42014-so-pin-uaf.patch +Patch37: gnutls-3.6.16-CVE-2026-5260-p11-rsa-overread.patch +Patch38: gnutls-3.6.16-CVE-2026-42015-p12-bag32.patch +# not in 3.6: CVE-2026-3832-ocsp-rev-0 - since 3.8.9 +# not in 3.6: CVE-2026-5419-p7-constant-time - since 3.7.7 +# non-CVE security fixes from the same release +Patch39: gnutls-3.6.16-1808-psk-rehandshake.patch +Patch40: gnutls-3.6.16-1810-ocsp-truncated-eku.patch +# not in 3.6: 1813-p11p-aes-ephemeral +Patch41: gnutls-3.6.16-1818-rsa-coprime.patch +Patch42: gnutls-3.6.16-1818-pem-parsing.patch +Patch43: gnutls-3.6.16-1819-dblfree-mid-import.patch +# not in 3.6: 1822-sct-overread +# not in 3.6: 1823-cfg-clear-options +Patch44: gnutls-3.6.16-1817-security-parameters.patch +# not in 3.6: 1820-p11p-kdf + %bcond_without dane %if 0%{?rhel} %bcond_with guile @@ -235,7 +262,7 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/gnutls-dane.pc %find_lang gnutls %check -make check %{?_smp_mflags} +make check %{?_smp_mflags} V=1 VERBOSE=1 %post devel if [ -f %{_infodir}/gnutls.info.gz ]; then @@ -305,6 +332,24 @@ fi %endif %changelog +* Thu Apr 30 2026 Alexander Sosedkin - 3.6.16-8.6 +- 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 upstream security issue #1808 (PSK rehandshake) +- Fix upstream security issue #1810 (EKU OID prefix match) +- 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 #1817 (session parameter loading robustness) + * Thu Feb 12 2026 Alexander Sosedkin - 3.6.16-8.5 - Backport the fixes for CVE-2025-9820 and CVE-2025-14831