From 3ad9437923249489066e18973e20e863981b1f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Thu, 28 Jan 2016 10:50:58 +0100 Subject: [PATCH] x509: add parse/print support for IPs in SAN RFC 5280 defines many type of names to be used in the subjectAltName extension of certificate. So far we only supported dNSName, but there is demand for IP addresses too. This is the first step, support for verification will be added in the next commit. --- include/mbedtls/config.h | 12 ++++ library/version_features.c | 3 + library/x509_crt.c | 92 ++++++++++++++++++++++---- tests/data_files/server5-san-ip.crt | 12 ++++ tests/suites/test_suite_x509parse.data | 8 ++- 5 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 tests/data_files/server5-san-ip.crt 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"