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 <david.von.oheimb@siemens.com>
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25861)
This commit is contained in:
Ivan Stanković 2025-03-04 17:30:34 +01:00 committed by Dr. David von Oheimb
parent 4e9b542868
commit e599893a9f
11 changed files with 138 additions and 37 deletions

View File

@ -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';
}
}

View 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

View 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) */

View 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] != '/')
if ((uri_copy = OPENSSL_strndup((const char *)uri->data, uri->length)) == NULL)
return X509_V_ERR_UNSPECIFIED;
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;
hostptr = p + 3;
}
/* Determine length of hostname part of URI */
/* 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;
}
/* Look for a port indicator as end of hostname first */
/* We don't need these anymore */
OPENSSL_free(scheme);
OPENSSL_free(uri_copy);
p = ia5memchr(uri, hostptr, ':');
/* Otherwise look for trailing slash */
if (p == NULL)
p = ia5memchr(uri, hostptr, '/');
if (p == NULL)
hostlen = IA5_OFFSET_LEN(uri, hostptr);
else
hostlen = p - hostptr;
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;
}

View 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);

View File

@ -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-----

19
test/certs/ncca4-cert.pem Normal file
View File

@ -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-----

28
test/certs/ncca4-key.pem Normal file
View File

@ -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-----

View File

@ -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 \

View 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 {

View 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