]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
x509: allow SAN URIs to contain userinfo
authorIvan Stanković <ivan.stankovic@wire.com>
Tue, 4 Mar 2025 16:30:34 +0000 (17:30 +0100)
committerDr. David von Oheimb <dev@ddvo.net>
Tue, 4 Mar 2025 16:30:34 +0000 (17:30 +0100)
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 <david.von.oheimb@siemens.com>
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25861)

Configure
crypto/http/build.info
crypto/http/http_lib.c
crypto/x509/v3_ncons.c
include/openssl/http.h
test/certs/nc-uri-cert.pem [new file with mode: 0644]
test/certs/ncca4-cert.pem [new file with mode: 0644]
test/certs/ncca4-key.pem [new file with mode: 0644]
test/certs/setup.sh
test/recipes/25-test_verify.t
util/libcrypto.num

index 87c606f9fbb7d9ded101c9a21dfb545d7e106f8f..d96ef95e0e89740720f96c6da28ecdf633ec0b3f 100755 (executable)
--- 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';
     }
 }
 
index b4626b13de7f961b7f66dadf53265258a2e7db9c..656bc29841e3736ed56a32474485340c3dd83764 100644 (file)
@@ -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
index 0046dc5303dc906145ceedba027a0dfdc5decd78..9b5fce2c93321041fbb64791c50c2ccc80d97095 100644 (file)
@@ -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) */
index c6ebb2f6e1a24094582a031e8651943acb95d3b2..0a192a3313b0249dc0de67e878b13ac1fb1c775b 100644 (file)
@@ -14,6 +14,7 @@
 #include "crypto/asn1.h"
 #include <openssl/asn1t.h>
 #include <openssl/conf.h>
+#include <openssl/http.h>
 #include <openssl/x509v3.h>
 #include <openssl/bn.h>
 
@@ -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;
 
 }
 
index ed1679e2c9ced0c2edd25926eebdeedb0563a68f..e51d70369078af747fd64ae130b624357dbf44d3 100644 (file)
@@ -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 (file)
index 0000000..fc060ec
--- /dev/null
@@ -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 (file)
index 0000000..25777a1
--- /dev/null
@@ -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 (file)
index 0000000..2577ba0
--- /dev/null
@@ -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-----
index 4280ac3a8d30c2bd83ed27e57c096930730cf7e3..28b8196c7849766280c28da63423547c3cc4e305 100755 (executable)
@@ -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 \
index 80e9026556f3e78791defe8971629ba0108f29ef..8528c4b33ec6ba63b837e5f95a404d10baefb70c 100644 (file)
@@ -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 {
index 07c811914656cf6c279b4566353344da75383361..d86007f719fcb8a40b70d52eb445821317f0811f 100644 (file)
@@ -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