From: William Lallemand Date: Wed, 12 Aug 2026 09:19:54 +0000 (+0000) Subject: BUG/MINOR: ssl: reject an embedded NUL in the ssl_*_dn(entry) fetches X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=449133cba139e27afb291f509e884d3cf2789f94;p=thirdparty%2Fhaproxy.git BUG/MINOR: ssl: reject an embedded NUL in the ssl_*_dn(entry) fetches ssl_sock_get_dn_entry() copied the ASN.1-declared bytes of a DN entry verbatim, keeping the exact length. pat_match_str()'s tree branch (used by the default, exact-match "-m str" ACLs) then looks the value up with ebst_lookup(), which treats it as a NUL-terminated C string. A certificate with e.g. CN = "admin\0.attacker-owned.example" is therefore matched and authorized as "admin": the producer keeps the full value, but the ebtree consumer silently truncates at the first NUL. ACLs using "-i" are unaffected since they take the length-correct list branch instead of the tree. Reject such a value in the fetch itself, before it reaches the pattern-matching layer: a NUL followed by any further non-NUL byte now makes ssl_sock_get_dn_entry() fail as if the entry did not exist, so ssl_c_s_dn(CN)/ssl_c_i_dn(CN)/ssl_r_dn(CN) and any map/ACL relying on it can't be bypassed this way. Trailing NUL byte(s) with nothing after them aren't a truncation risk, so a single one is kept in the returned length instead of the whole ASN.1 length, and any extra ones are skipped. The impact is low since it would need to be signed by the CA anyway. This should be backported to every stable branch. Reported-by: Vivek Parikh --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 2e961e518..ad493e013 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -26364,6 +26364,9 @@ ssl_c_i_dn([[,[,]]]) : string LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_c_i_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_c_key_alg : string Returns the name of the algorithm used to generate the key of the certificate @@ -26413,6 +26416,9 @@ ssl_c_r_dn([[,[,]]]) : string different protocols. Currently supported is rfc2253 for LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_c_r_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_c_s_dn([[,[,]]]) : string When the incoming connection was made over an SSL/TLS transport layer, @@ -26428,6 +26434,9 @@ ssl_c_s_dn([[,[,]]]) : string LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_c_s_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_c_san : string When the incoming connection was made over an SSL/TLS transport layer, and was @@ -26499,6 +26508,9 @@ ssl_f_i_dn([[,[,]]]) : string LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_f_i_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_f_key_alg : string Returns the name of the algorithm used to generate the key of the certificate @@ -26529,6 +26541,9 @@ ssl_f_s_dn([[,[,]]]) : string LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_f_s_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_f_serial : binary Returns the serial of the certificate presented by the frontend when the @@ -26954,6 +26969,9 @@ ssl_s_i_dn([[,[,]]]) : string LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_s_i_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_s_key_alg : string Returns the name of the algorithm used to generate the key of the certificate @@ -26984,6 +27002,9 @@ ssl_s_s_dn([[,[,]]]) : string LDAP v3. If you'd like to modify the format only you can specify an empty string and zero for the first two parameters. Example: ssl_s_s_dn(,0,rfc2253) + If the requested entry's ASN.1 value contains an embedded NUL byte + followed by other data, it is considered malformed and no data is + returned. ssl_s_serial : binary Returns the serial of the certificate presented by the server when the diff --git a/src/ssl_utils.c b/src/ssl_utils.c index 836e44cc2..61f410629 100644 --- a/src/ssl_utils.c +++ b/src/ssl_utils.c @@ -194,6 +194,7 @@ int ssl_sock_get_dn_entry(__X509_NAME_CONST__ X509_NAME *a, const struct buffer __X509_NAME_CONST__ ASN1_OBJECT *obj; __X509_NAME_CONST__ ASN1_STRING *data; const unsigned char *data_ptr; + const unsigned char *nul; int data_len; int i, j, n; int cur = 0; @@ -235,6 +236,21 @@ int ssl_sock_get_dn_entry(__X509_NAME_CONST__ X509_NAME *a, const struct buffer if (data_len > out->size) return -1; + /* reject embedded NUL (truncation/injection risk for NUL-terminated + * consumers); trailing NUL(s) carry nothing after them so they're + * harmless padding, just keep one and skip the extra ones + */ + nul = memchr(data_ptr, 0, data_len); + if (nul) { + const unsigned char *p; + + for (p = nul; p < data_ptr + data_len; p++) { + if (*p) + return 0; + } + data_len = nul - data_ptr + 1; + } + memcpy(out->area, data_ptr, data_len); out->data = data_len; return 1;