From: Ivan Stanković Date: Tue, 4 Mar 2025 16:30:34 +0000 (+0100) Subject: x509: allow SAN URIs to contain userinfo X-Git-Tag: openssl-3.5.0-alpha1~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e599893a9fec932701ca824d73a794a0c9ce02e9;p=thirdparty%2Fopenssl.git x509: allow SAN URIs to contain userinfo The way we're currently handling SAN URIs does not allow for userinfo, meaning the name constraint check on such URIs will fail. Fix this by skipping over the userinfo component: authority = [ userinfo "@" ] host [ ":" port ] (per RFC 3986). Reviewed-by: David von Oheimb Reviewed-by: Viktor Dukhovni (Merged from https://github.com/openssl/openssl/pull/25861) --- diff --git a/Configure b/Configure index 87c606f9fbb..d96ef95e0e8 100755 --- a/Configure +++ b/Configure @@ -1940,7 +1940,7 @@ foreach my $what (sort keys %disabled) { $skipdir{engines} = $what if $what eq 'engine'; $skipdir{"crypto/$skipdir"} = $what - unless $what eq 'async' || $what eq 'err' || $what eq 'dso'; + unless $what eq 'async' || $what eq 'err' || $what eq 'dso' || $what eq 'http'; } } diff --git a/crypto/http/build.info b/crypto/http/build.info index b4626b13de7..656bc29841e 100644 --- a/crypto/http/build.info +++ b/crypto/http/build.info @@ -1,2 +1,6 @@ LIBS=../../libcrypto -SOURCE[../../libcrypto]=http_client.c http_err.c http_lib.c +SOURCE[../../libcrypto]=http_lib.c + +IF[{- !$disabled{http} -}] + SOURCE[../../libcrypto]=http_client.c http_err.c +ENDIF diff --git a/crypto/http/http_lib.c b/crypto/http/http_lib.c index 0046dc5303d..9b5fce2c933 100644 --- a/crypto/http/http_lib.c +++ b/crypto/http/http_lib.c @@ -196,6 +196,8 @@ int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost, return 0; } +#ifndef OPENSSL_NO_HTTP + int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost, char **pport, int *pport_num, char **ppath, char **pquery, char **pfrag) @@ -305,3 +307,5 @@ const char *OSSL_HTTP_adapt_proxy(const char *proxy, const char *no_proxy, return NULL; return proxy; } + +#endif /* !defined(OPENSSL_NO_HTTP) */ diff --git a/crypto/x509/v3_ncons.c b/crypto/x509/v3_ncons.c index c6ebb2f6e1a..0a192a3313b 100644 --- a/crypto/x509/v3_ncons.c +++ b/crypto/x509/v3_ncons.c @@ -14,6 +14,7 @@ #include "crypto/asn1.h" #include #include +#include #include #include @@ -782,50 +783,57 @@ static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) { const char *baseptr = (char *)base->data; - const char *hostptr = (char *)uri->data; - const char *p = ia5memchr(uri, (char *)uri->data, ':'); + char *uri_copy; + char *scheme; + char *host; int hostlen; + int ret; - /* Check for foo:// and skip past it */ - if (p == NULL - || IA5_OFFSET_LEN(uri, p) < 3 - || p[1] != '/' - || p[2] != '/') - return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; - hostptr = p + 3; - - /* Determine length of hostname part of URI */ + if ((uri_copy = OPENSSL_strndup((const char *)uri->data, uri->length)) == NULL) + return X509_V_ERR_UNSPECIFIED; - /* Look for a port indicator as end of hostname first */ + if (!OSSL_parse_url(uri_copy, &scheme, NULL, &host, NULL, NULL, NULL, NULL, NULL)) { + OPENSSL_free(uri_copy); + return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + } - p = ia5memchr(uri, hostptr, ':'); - /* Otherwise look for trailing slash */ - if (p == NULL) - p = ia5memchr(uri, hostptr, '/'); + /* Make sure the scheme is there */ + if (scheme == NULL || *scheme == '\0') { + ERR_raise_data(ERR_LIB_X509V3, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, + "x509: missing scheme in URI: %s\n", uri_copy); + OPENSSL_free(uri_copy); + ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + goto end; + } - if (p == NULL) - hostlen = IA5_OFFSET_LEN(uri, hostptr); - else - hostlen = p - hostptr; + /* We don't need these anymore */ + OPENSSL_free(scheme); + OPENSSL_free(uri_copy); - if (hostlen == 0) - return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; + hostlen = strlen(host); /* Special case: initial '.' is RHS match */ if (base->length > 0 && *baseptr == '.') { if (hostlen > base->length) { - p = hostptr + hostlen - base->length; - if (ia5ncasecmp(p, baseptr, base->length) == 0) - return X509_V_OK; + if (ia5ncasecmp(host + hostlen - base->length, baseptr, base->length) == 0) { + ret = X509_V_OK; + goto end; + } } - return X509_V_ERR_PERMITTED_VIOLATION; + ret = X509_V_ERR_PERMITTED_VIOLATION; + goto end; } - if ((base->length != (int)hostlen) - || ia5ncasecmp(hostptr, baseptr, hostlen)) - return X509_V_ERR_PERMITTED_VIOLATION; + if ((base->length != hostlen) + || ia5ncasecmp(host, baseptr, hostlen) != 0) { + ret = X509_V_ERR_PERMITTED_VIOLATION; + goto end; + } - return X509_V_OK; + ret = X509_V_OK; +end: + OPENSSL_free(host); + return ret; } diff --git a/include/openssl/http.h b/include/openssl/http.h index ed1679e2c9c..e51d7036907 100644 --- a/include/openssl/http.h +++ b/include/openssl/http.h @@ -33,6 +33,11 @@ extern "C" { # define OPENSSL_HTTP_PROXY "HTTP_PROXY" # define OPENSSL_HTTPS_PROXY "HTTPS_PROXY" +/* We want to have this even in case of OPENSSL_NO_HTTP */ +int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost, + char **pport, int *pport_num, + char **ppath, char **pquery, char **pfrag); + # ifndef OPENSSL_NO_HTTP # define OSSL_HTTP_DEFAULT_MAX_LINE_LEN (4 * 1024) @@ -101,9 +106,6 @@ BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx, int OSSL_HTTP_close(OSSL_HTTP_REQ_CTX *rctx, int ok); /* Auxiliary functions */ -int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost, - char **pport, int *pport_num, - char **ppath, char **pquery, char **pfrag); int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost, char **pport, int *pport_num, char **ppath, char **pquery, char **pfrag); diff --git a/test/certs/nc-uri-cert.pem b/test/certs/nc-uri-cert.pem new file mode 100644 index 00000000000..fc060ecc98c --- /dev/null +++ b/test/certs/nc-uri-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZDCCAkygAwIBAgIBAjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxUZXN0 +IE5DIENBIDQwIBcNMjUwMjI4MDkzNDQxWhgPMjEyNTAzMDEwOTM0NDFaMDoxIzAh +BgNVBAoMGkdvb2QgTkMgVGVzdCBDZXJ0aWZpY2F0ZSAxMRMwEQYDVQQDDApKb2Ug +QmxvZ2dzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA06r3+o/lsQrZ ++EVPP+rl14Ak3JLL+jQ1wmfSQM+Fb3EGyX4RWw1qRmUGQzgqsLY/uwklSK2sSLR8 +z5a7d+nHr7KD5qbqhmx61Gji53pntsfNUl1KtM/w5g78MVRDZkY9bu4grd7C+2sH +IneEoqLSzmNhdYwWs5qqa6imQVIskF6qUqfvbkmr++8ncA1wq0KMwXZ7NWEbIYFP +aJkK03CKhwo9X/S6PgNsuPk2WGZL0g7lGYk3z0z6LkerKKTPCS8gGXPk7xRZ3BIN +uDfs89zt48B/NgWRI82r8PdifZ6SQVF7ym/Dvc+1AOuVmLi9oXu5EkArDzS6EVmZ +1aXIcK91RwIDAQABo4GVMIGSMB0GA1UdDgQWBBR7y4RlnJBY1V3QyN3bPVrYus4F +wDAfBgNVHSMEGDAWgBT2G6AIlmw4+di4eTt/BxBHASEsKDAJBgNVHRMEAjAAMEUG +A1UdEQQ+MDyGG2ZvbzovLyU0MHNvbWV0aGluZ0Bnb29kLm9yZ4YdYmFyOi8vb3Ro +ZXJAZ29vZC5vcmcvYmF6L3F1dXgwDQYJKoZIhvcNAQELBQADggEBABYu4QcL/6Ud +rELZa1ZuySp/TZQJoJTH44wMFQ4jiWMujlV3sn9UlY/fX1DTlXR1I7BkxokV7dTG +3h/eRtqF6oVbiSAAXvIoGk0Hho3GMRVw3pFDCl0jfreTlkMxYQf77ZkdjSaWHhlQ +yKLBoiEQIOLyH8nJyNtwxmupoB2NjgfwGPuhiY7UcJHiUhQhfUycWowdraBrT+hF +cgPo0IMa8nimu1NbKv1oIp5CTuDWFFTi31GrVMxYDtKYrhDvXRVGmPWc68Cjs0j3 +etEqgYkf0kZVYtDTnN/8WAGPUp58YDtv2rR0xB53cQSRXMZ5XPP9UeH1KtJAf+r+ +G6XSRC09G+M= +-----END CERTIFICATE----- diff --git a/test/certs/ncca4-cert.pem b/test/certs/ncca4-cert.pem new file mode 100644 index 00000000000..25777a148a0 --- /dev/null +++ b/test/certs/ncca4-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHzCCAgegAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTI1MDIyODA5MzQ0MVoYDzIxMjUwMzAxMDkzNDQxWjAXMRUwEwYDVQQD +DAxUZXN0IE5DIENBIDQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCe +J0Lc2JvLgB3Edv6HEIkpDI7rcPP+y2mILcFV8IsYTI0roB1JmF+v2bd6K5sKI2KX +QB6luj1x9M7ywC5fJtLbi1gKRgtZwRh/scMdquGAnquUBX0WI3K4ZZFl7jxAeGog +ALG+j4pEJSTtq8PjdbN/857w0ZTJd+eVe9HY+wSKe4bUKXzA+4vh9YnJ+4hDB3V/ +n8fYrCJI+CFiV56Cy8JTkHnHN38LY6Lm2/441730N2vEMlOdZ3Lmqa6wPZbVxGTR +jlRgOKnkO7AhTIxqUc0RHGrCz6P2n0fBXSCNAbYkaZV4EwLw5/dwPwxHVggkg2ra +pLm47wrEoe+N+1/Zz2BPAgMBAAGjeTB3MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0P +BAQDAgEGMB0GA1UdDgQWBBT2G6AIlmw4+di4eTt/BxBHASEsKDAfBgNVHSMEGDAW +gBSO9SWvHptrhD18gJrJU5xNcvejUjAXBgNVHR4EEDAOoAwwCoYIZ29vZC5vcmcw +DQYJKoZIhvcNAQELBQADggEBAG6hOlTjcLQ1viKLCJf2VO3llSnmTLqVWILs/0EK +wyP9z4KIQ6zoS3+XpiyWN5t5AjysobWI1TcAxH1+vPwcPOx+dNXbRZsKyw8HulQk +4JMO14HF8DjTaDTYhpn5h38tRHAhFw9i4/VfWsM0Z4/QGXE7gNtNr9tkaguL3DiH +Hhh7Q64Zf3cNQ0Q4Pj0NofHmQK9RFZuG2bh1UMoeD6A8NCZoBwvju8ktslTYVCXl +gxpXJ2TjC8vA/LCdPLnNvI9n4CNCHy3Uj9IgPly+04Ago5iiVJyLzWLg+FL7HE/I +c/rQGWSts8vzKdLqFT4A9jAcW8FHvHwudPC0YLqypqTkePA= +-----END CERTIFICATE----- diff --git a/test/certs/ncca4-key.pem b/test/certs/ncca4-key.pem new file mode 100644 index 00000000000..2577ba07c17 --- /dev/null +++ b/test/certs/ncca4-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeJ0Lc2JvLgB3E +dv6HEIkpDI7rcPP+y2mILcFV8IsYTI0roB1JmF+v2bd6K5sKI2KXQB6luj1x9M7y +wC5fJtLbi1gKRgtZwRh/scMdquGAnquUBX0WI3K4ZZFl7jxAeGogALG+j4pEJSTt +q8PjdbN/857w0ZTJd+eVe9HY+wSKe4bUKXzA+4vh9YnJ+4hDB3V/n8fYrCJI+CFi +V56Cy8JTkHnHN38LY6Lm2/441730N2vEMlOdZ3Lmqa6wPZbVxGTRjlRgOKnkO7Ah +TIxqUc0RHGrCz6P2n0fBXSCNAbYkaZV4EwLw5/dwPwxHVggkg2rapLm47wrEoe+N ++1/Zz2BPAgMBAAECggEAQs/HOVDwkAmxiZvTbu+XYhYCEoiHKy53iKX7tPiHal58 +jN95P+v1EG7jSeM9/gxwzAC0ccK5znhjLL3vWRcnoMO/D6gDh4lBdkB8cv4LgbCG +P2QKMd4LysZtpCf+oCW+f4KLlDtDaAJhFV6oxGCm0fjzPjzrpCjZVpcWUZnJk05t +jzS/sdjJ6N41RHtlBmH6v1k1Jh9nTm4fBZnj8rt3fzHlCQXsrDwEkIZEVLvmkg49 +yPdb8qG9CO4aZRZoN/Vk5N8BY3boT/SGDWv8FC0ObDvWF8bYrdloqimGL/JazOib +ZgOhWQM1c/F5Z4unSJQKPcdWi+icpGku+ifCJyC9gQKBgQDWaOxXXZqgbEddY+lJ +nM5rk4fKHEb87MP3GL8b/qN1lkWD5WhPiengdU+4XrZ73YDuqbnody2pqVDVshir +kbHtrgIY7hh7KnJW/qyM1qpDQivaho3oHpR4sBHOyhq/COTzSh5VDraOSIe0KMMN +vXD2k819OVmFBtPVTXT7x9AULwKBgQC81MawfOBItIAWXfcVOhDcfav8oe0mRJMr +WBq5UvPjYMvDRsXUtALJObpjAaXEhV44+22qZVO8KKlHxh8GsBNO5ToaGTCNDRJb +32u0WGkZngXmxKc4PZZHcXAnhyobV6BL3dDO+vM7jxpAWKtv1Oirs8TeomVXwhQf +gpzNO3zN4QKBgQCzPlv2XaZa3qp6hIAOrixS+q7WY/VklHrvI50AxkvYjZvnu+0M +MXt3zhqrQ2LDAlY7L2Df2mIuKAIP5CeDpvVcgc/3D3Uf4khcOeP+iaclOzh2I26W +0pnEm00H1yWs9r6QNTJOYVJ0eGYaUsldvzWkrcNoIH2aHC8TbwGRS2XEuQKBgCxC +byO00VkZPaCAe8Z06rjTl/lJ9uzuS9Rv/SuM/u8/o+LsdrgpTTHfHwnPvAv4+qG+ +hPDYeSz0FuFk1abapFvsrJaras7UzoXMM1F9G31OpbF2TH+JJ+0s8I3DR6JLAp5l +qmipN1OxcgS9A8ndjH+aTj2ksL5GFjNgiOIt3E3hAoGANiEu1EGPzL+2Bt7bKyAD +sRHqdm1Auu5spR/jX54yiydf+I7DevaBb/xOGOwGEUDiiJliYcRnXzTehi8Rar3/ +kDx4helU1xQQ2G8ne2bB8Dl5vsbGHJxh42LCDYxB5ndKzvAiMOPcbZq1EV6Rkl6/ +zbX3xa17bFhXTWXEoYANhtI= +-----END PRIVATE KEY----- diff --git a/test/certs/setup.sh b/test/certs/setup.sh index 4280ac3a8d3..28b8196c784 100755 --- a/test/certs/setup.sh +++ b/test/certs/setup.sh @@ -411,6 +411,18 @@ REQMASK=MASK:0x800 ./mkcert.sh req badalt7-key "O = Bad NC Test Certificate 7" \ "email.1 = good@good.org" "email.2 = any@good.com" \ "IP = 127.0.0.1" "IP = 192.168.0.1" +# NC CA4 only permits URIs matching good.org. + +NC="permitted;URI:good.org" +NC=$NC ./mkcert.sh genca "Test NC CA 4" ncca4-key ncca4-cert root-key root-cert + +# A certificate with an URI SAN +./mkcert.sh req alt1-key "O = Good NC Test Certificate 1" \ + "CN=Joe Bloggs" | \ + ./mkcert.sh geneealt nc-uri-key nc-uri-cert ncca4-key ncca4-cert \ + "URI.1 = foo://%40something@good.org" \ + "URI.2 = bar://other@good.org/baz/quux" + # Certs for CVE-2022-4203 testcase NC="excluded;otherName:SRVName;UTF8STRING:foo@example.org" ./mkcert.sh genca \ diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t index 80e9026556f..8528c4b33ec 100644 --- a/test/recipes/25-test_verify.t +++ b/test/recipes/25-test_verify.t @@ -29,7 +29,7 @@ sub verify { run(app([@args])); } -plan tests => 193; +plan tests => 194; # Canonical success ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]), @@ -467,6 +467,9 @@ ok(!verify("badalt10-cert", "", ["root-cert"], ["ncca1-cert", "ncca3-cert"], ), ok(!verify("bad-othername-cert", "", ["root-cert"], ["nccaothername-cert"], ), "CVE-2022-4203 type confusion test"); +ok(verify("nc-uri-cert", "", ["root-cert"], ["ncca4-cert"], ), + "Name constraints URI with userinfo"); + #Check that we get the expected failure return code with({ exit_checker => sub { return shift == 2; } }, sub { diff --git a/util/libcrypto.num b/util/libcrypto.num index 07c81191465..d86007f719f 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4882,7 +4882,7 @@ ASN1_item_verify_ex 5009 3_0_0 EXIST::FUNCTION: BIO_socket_wait 5010 3_0_0 EXIST::FUNCTION:SOCK BIO_wait 5011 3_0_0 EXIST::FUNCTION: BIO_do_connect_retry 5012 3_0_0 EXIST::FUNCTION: -OSSL_parse_url 5013 3_0_0 EXIST::FUNCTION:HTTP +OSSL_parse_url 5013 3_0_0 EXIST::FUNCTION: OSSL_HTTP_adapt_proxy 5014 3_0_0 EXIST::FUNCTION:HTTP OSSL_HTTP_REQ_CTX_get_resp_len 5015 3_0_0 EXIST::FUNCTION:HTTP OSSL_HTTP_REQ_CTX_set_expected 5016 3_0_0 EXIST::FUNCTION:HTTP