diff --git a/ChangeLog.d/basic-uri-verification.txt b/ChangeLog.d/basic-uri-verification.txt new file mode 100644 index 0000000000..aa039ea299 --- /dev/null +++ b/ChangeLog.d/basic-uri-verification.txt @@ -0,0 +1,4 @@ +Features + * X.509 hostname verification now partially supports URI Subject Alternate + Names. Only exact matching, without any normalization procedures + described in 7.4 of RFC5280, will result in a positive URI verification. diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index e1b4aa238d..6675bf89df 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -641,8 +641,12 @@ int mbedtls_x509_crt_verify_info(char *buf, size_t size, const char *prefix, * \param cn The expected Common Name. This will be checked to be * present in the certificate's subjectAltNames extension or, * if this extension is absent, as a CN component in its - * Subject name. DNS names and IP addresses are supported. This - * may be \c NULL if the CN need not be verified. + * Subject name. DNS names and IP addresses are fully + * supported, while the URI subtype is partially supported: + * only exact matching, without any normalization procedures + * described in 7.4 of RFC5280, will result in a positive + * URI verification. + * This may be \c NULL if the CN need not be verified. * \param flags The address at which to store the result of the verification. * If the verification couldn't be completed, the flag value is * set to (uint32_t) -1. diff --git a/library/x509_crt.c b/library/x509_crt.c index 6d62e4494a..abba05b845 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -2904,6 +2904,21 @@ static int x509_crt_check_san_ip(const mbedtls_x509_sequence *san, return -1; } +static int x509_crt_check_san_uri(const mbedtls_x509_sequence *san, + const char *cn, size_t cn_len) +{ + for (const mbedtls_x509_sequence *cur = san; cur != NULL; cur = cur->next) { + const unsigned char san_type = (unsigned char) cur->buf.tag & + MBEDTLS_ASN1_TAG_VALUE_MASK; + if (san_type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER && + cur->buf.len == cn_len && memcmp(cur->buf.p, cn, cn_len) == 0) { + return 0; + } + } + + return -1; +} + /* * Check for SAN match, see RFC 5280 Section 4.2.1.6 */ @@ -2911,23 +2926,38 @@ static int x509_crt_check_san(const mbedtls_x509_sequence *san, const char *cn, size_t cn_len) { int san_ip = 0; + int san_uri = 0; + /* Prioritize DNS name over other subtypes due to popularity */ for (const mbedtls_x509_sequence *cur = san; cur != NULL; cur = cur->next) { switch ((unsigned char) cur->buf.tag & MBEDTLS_ASN1_TAG_VALUE_MASK) { - case MBEDTLS_X509_SAN_DNS_NAME: /* dNSName */ + case MBEDTLS_X509_SAN_DNS_NAME: if (x509_crt_check_cn(&cur->buf, cn, cn_len) == 0) { return 0; } break; - case MBEDTLS_X509_SAN_IP_ADDRESS: /* iPAddress */ + case MBEDTLS_X509_SAN_IP_ADDRESS: san_ip = 1; break; + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + san_uri = 1; + break; /* (We may handle other types here later.) */ default: /* Unrecognized type */ break; } } + if (san_ip) { + if (x509_crt_check_san_ip(san, cn, cn_len) == 0) { + return 0; + } + } + if (san_uri) { + if (x509_crt_check_san_uri(san, cn, cn_len) == 0) { + return 0; + } + } - return san_ip ? x509_crt_check_san_ip(san, cn, cn_len) : -1; + return -1; } /* diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 55ed0c55d5..0193e07ffe 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -1043,6 +1043,22 @@ X509 CRT verification: mismatching IPv6 in SubjectAltName depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_RSA_C x509_verify:"data_files/server5-tricky-ip-san.crt":"data_files/server5-tricky-ip-san.crt":"data_files/crl_sha256.pem":"6162\:6364\:\:6F6D":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"":"NULL" +X509 CRT verification: matching URI in SubjectAltName +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_RSA_C +x509_verify:"data_files/rsa_single_san_uri.crt.der":"data_files/rsa_single_san_uri.crt.der":"data_files/crl_sha256.pem":"urn\:example.com\:5ff40f78-9210-494f-8206-c2c082f0609c":0:0:"":"NULL" + +X509 CRT verification: URI with trailing data in SubjectAltName +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_RSA_C +x509_verify:"data_files/rsa_single_san_uri.crt.der":"data_files/rsa_single_san_uri.crt.der":"data_files/crl_sha256.pem":"urn\:example.com\:5ff40f78-9210-494f-8206-c2c082f0609cz":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"":"NULL" + +X509 CRT verification: URI with preceding data in SubjectAltName +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_RSA_C +x509_verify:"data_files/rsa_single_san_uri.crt.der":"data_files/rsa_single_san_uri.crt.der":"data_files/crl_sha256.pem":"zurn\:example.com\:5ff40f78-9210-494f-8206-c2c082f0609c":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"":"NULL" + +X509 CRT verification: URI with bad data in SubjectAltName +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_MD_CAN_SHA256:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_RSA_C +x509_verify:"data_files/rsa_single_san_uri.crt.der":"data_files/rsa_single_san_uri.crt.der":"data_files/crl_sha256.pem":"bad\:example.com\:5ff40f78-9210-494f-8206-c2c082f0609c":MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:MBEDTLS_X509_BADCERT_CN_MISMATCH:"":"NULL" + X509 CRT parse CN: IPv4 valid address x509_crt_parse_cn_inet_pton:"10.10.10.10":"0A0A0A0A":4