diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index d1db0d825a..20eca56325 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -1318,6 +1318,18 @@ */ #define MBEDTLS_X509_RSASSA_PSS_SUPPORT +/** + * \def MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT + * + * Enable support for IP addresses (IPv4 and IPv6) in subjectAltName in + * certificates. This includes parsing, printing with + * \c mbedtls_x509_crt_info(), and verification - see the documentation of + * \c mbedtls_x509_crt_verify_with_profile() + * + * Comment this macro to disallow using IP addresses in + */ +#define MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT + /** * \def MBEDTLS_ZLIB_SUPPORT * diff --git a/library/version_features.c b/library/version_features.c index 1575e093e3..1ddd222c0c 100644 --- a/library/version_features.c +++ b/library/version_features.c @@ -414,6 +414,9 @@ static const char *features[] = { #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) "MBEDTLS_X509_RSASSA_PSS_SUPPORT", #endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ +#if defined(MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT) + "MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT", +#endif /* MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT */ #if defined(MBEDTLS_ZLIB_SUPPORT) "MBEDTLS_ZLIB_SUPPORT", #endif /* MBEDTLS_ZLIB_SUPPORT */ diff --git a/library/x509_crt.c b/library/x509_crt.c index 9573438b07..f915b21581 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -438,8 +438,13 @@ static int x509_get_ext_key_usage( unsigned char **p, * nameAssigner [0] DirectoryString OPTIONAL, * partyName [1] DirectoryString } * - * NOTE: we only parse and use dNSName at this point. + * NOTE: we only parse and use dNSName and iPAddress at this point. + * NOTE: update x509_info_subject_alt_name() and x509_crt_verify() if and when + * adding support for more name types. */ +#define X509_CRT_SAN_DNS_NAME 2 +#define X509_CRT_SAN_IP_ADDRESS 7 + static int x509_get_subject_alt_name( unsigned char **p, const unsigned char *end, mbedtls_x509_sequence *subject_alt_name ) @@ -474,8 +479,17 @@ static int x509_get_subject_alt_name( unsigned char **p, return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); - /* Skip everything but DNS name */ - if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) ) + /* Skip everything but DNS name and IP address */ +#if defined(MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT) + if( tag == ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_IP_ADDRESS ) ) + { + /* If IP adress, only valid lengths are 4 and 16 */ + if( tag_len != 4 && tag_len != 16 ) + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS ); + } + else +#endif + if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_DNS_NAME ) ) { *p += tag_len; continue; @@ -1213,20 +1227,74 @@ static int x509_info_subject_alt_name( char **buf, size_t *size, const mbedtls_x509_sequence *cur = subject_alt_name; const char *sep = ""; size_t sep_len = 0; + int ret; while( cur != NULL ) { - if( cur->buf.len + sep_len >= n ) + if( cur->buf.tag == ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_DNS_NAME ) ) { - *p = '\0'; - return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL ); - } + if( cur->buf.len + sep_len >= n ) + { + *p = '\0'; + return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL ); + } - n -= cur->buf.len + sep_len; - for( i = 0; i < sep_len; i++ ) - *p++ = sep[i]; - for( i = 0; i < cur->buf.len; i++ ) - *p++ = cur->buf.p[i]; + n -= cur->buf.len + sep_len; + for( i = 0; i < sep_len; i++ ) + *p++ = sep[i]; + for( i = 0; i < cur->buf.len; i++ ) + *p++ = cur->buf.p[i]; + } +#if defined(MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT) + else if( cur->buf.tag == ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | X509_CRT_SAN_IP_ADDRESS ) ) + { + if( cur->buf.len == 4 ) + { + ret = mbedtls_snprintf( p, n, "%sIP:%d.%d.%d.%d", sep, + cur->buf.p[0], cur->buf.p[1], + cur->buf.p[2], cur->buf.p[4] ); + MBEDTLS_X509_SAFE_SNPRINTF; + } + else if( cur->buf.len == 16 ) + { + if( sep_len + 2 >= n ) + { + *p = '\0'; + return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL ); + } + + memcpy( p, sep, sep_len ); + p += sep_len; + n -= sep_len; + + memcpy( p, "IP", 2 ); + p += 2; + n -= 2; + + for( i = 0; i < 8; i++ ) + { + ret = mbedtls_snprintf( p, n, ":%02x%02x", + cur->buf.p[2 * i], + cur->buf.p[2 * i + 1] ); + MBEDTLS_X509_SAFE_SNPRINTF; + } + } + else + { + ret = mbedtls_snprintf( p, n, "IP:???" ); + MBEDTLS_X509_SAFE_SNPRINTF; + } + } +#endif /* MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT */ + /* + * tag == 0 happens when the extension is present but with only unknow + * types: we get a list with one uninitialized element + */ + else if( cur->buf.tag != 0 ) + { + ret = mbedtls_snprintf( p, n, "???unknown name type???" ); + MBEDTLS_X509_SAFE_SNPRINTF; + } sep = ", "; sep_len = 2; diff --git a/tests/data_files/server5-san-ip.crt b/tests/data_files/server5-san-ip.crt new file mode 100644 index 0000000000..93b727c1e2 --- /dev/null +++ b/tests/data_files/server5-san-ip.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrjCCATOgAwIBAgIBUDAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G +A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN +MTYwMTI3MTYzNDQzWhcNMjYwMTI0MTYzNDQzWjAPMQ0wCwYDVQQDEwRUZXN0MFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEN8xW2XYJHlpyPsdZLf8gbu58+QaRdNCt +FLX3aCJZYpJO5QDYIxH/6i/SNF1dFr2KiMJrdw1VzYoqDvoByLTt/6NRME8wTQYD +VR0RBEYwRIIIZm9vLnRlc3SCCGJhci50ZXN0hwR/AAABhwTAqAAqhxD+gAAAAAAA +AAAAAAAAAAABhxASNFZ4mrze8A/ey6mHZUMhMAoGCCqGSM49BAMCA2kAMGYCMQCI +TJvNYovxkfjoMcNqHMbrSSNAity3RSqrAA/loKCiw6kpsEOAItjXwxkHj0WH/4kC +MQDhdrbfoTxxD6AqNelMlpk2oE4c5ewsA2s53CLICqEy951jw47MqI+LYMmlsS/A +T5A= +-----END CERTIFICATE----- diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 2f2137f543..8b0e7ac4b9 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -99,8 +99,8 @@ depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C x509_cert_info:"data_files/cert_example_multi.crt":"cert. version \: 3\nserial number \: 11\nissuer name \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name \: C=NL, O=PolarSSL, CN=www.example.com\nissued on \: 2012-05-10 13\:23\:41\nexpires on \: 2022-05-11 13\:23\:41\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: example.com, example.net, *.example.org\n" X509 Certificate information, Subject Alt Name + Key Usage -depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C -x509_cert_info:"data_files/cert_example_multi_nocn.crt":"cert. version \: 3\nserial number \: F7\:C6\:7F\:F8\:E9\:A9\:63\:F9\nissuer name \: C=NL\nsubject name \: C=NL\nissued on \: 2014-01-22 10\:04\:33\nexpires on \: 2024-01-22 10\:04\:33\nsigned using \: RSA with SHA1\nRSA key size \: 1024 bits\nbasic constraints \: CA=false\nsubject alt name \: www.shotokan-braunschweig.de, www.massimo-abate.eu\nkey usage \: Digital Signature, Non Repudiation, Key Encipherment\n" +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT +x509_cert_info:"data_files/cert_example_multi_nocn.crt":"cert. version \: 3\nserial number \: F7\:C6\:7F\:F8\:E9\:A9\:63\:F9\nissuer name \: C=NL\nsubject name \: C=NL\nissued on \: 2014-01-22 10\:04\:33\nexpires on \: 2024-01-22 10\:04\:33\nsigned using \: RSA with SHA1\nRSA key size \: 1024 bits\nbasic constraints \: CA=false\nsubject alt name \: www.shotokan-braunschweig.de, www.massimo-abate.eu, IP\:192.168.1.135, IP\:192.168.69.48\nkey usage \: Digital Signature, Non Repudiation, Key Encipherment\n" X509 Certificate information, Key Usage + Extended Key Usage depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C @@ -118,6 +118,10 @@ X509 Certificate information Bitstring in subject name depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C x509_cert_info:"data_files/bitstring-in-dn.pem":"cert. version \: 3\nserial number \: 02\nissuer name \: CN=Test CA 01, ST=Ecnivorp, C=XX, emailAddress=tca@example.com, O=Test CA Authority\nsubject name \: C=XX, O=tca, ST=Ecnivorp, OU=TCA, CN=Client, emailAddress=client@example.com, serialNumber=7101012255, uniqueIdentifier=?7101012255\nissued on \: 2015-03-11 12\:06\:51\nexpires on \: 2025-03-08 12\:06\:51\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name \: \next key usage \: TLS Web Client Authentication\n" +X509 Certificate information SAN with IP +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_X509_SAN_IP_ADDRESS_SUPPORT +x509_cert_info:"data_files/server5-san-ip.crt":"cert. version \: 3\nserial number \: 50\nissuer name \: C=NL, O=PolarSSL, CN=Polarssl Test EC CA\nsubject name \: CN=Test\nissued on \: 2016-01-27 16\:34\:43\nexpires on \: 2026-01-24 16\:34\:43\nsigned using \: ECDSA with SHA256\nEC key size \: 256 bits\nsubject alt name \: foo.test, bar.test, IP\:127.0.0.135, IP\:192.168.0.135, IP\:fe80\:0000\:0000\:0000\:0000\:0000\:0000\:0001, IP\:1234\:5678\:9abc\:def0\:0fde\:cba9\:8765\:4321\n" + X509 certificate v1 with extension depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3 x509_cert_info:"data_files/cert_v1_with_ext.crt":"cert. version \: 1\nserial number \: BD\:ED\:44\:C7\:D2\:3E\:C2\:A4\nissuer name \: C=XX, ST=XX, L=XX, O=XX, OU=XX, emailAddress=admin@identity-check.org, CN=identity-check.org\nsubject name \: C=XX, ST=XX, L=XX, O=XX, OU=XX, emailAddress=admin@identity-check.org, CN=identity-check.org\nissued on \: 2013-07-04 16\:17\:02\nexpires on \: 2014-07-04 16\:17\:02\nsigned using \: RSA with SHA1\nRSA key size \: 2048 bits\nsubject alt name \: identity-check.org, www.identity-check.org\n"