diff options
author | Michael Brown | 2014-03-31 02:11:06 +0200 |
---|---|---|
committer | Michael Brown | 2014-03-31 14:36:54 +0200 |
commit | f10726c8bb4e09bf53d0c40b90c1a2cb434672d4 (patch) | |
tree | c03009237acfb4fe00e334ffac244644543a0970 /src | |
parent | [test] Add subject alternative names to X.509 server test certificate (diff) | |
download | ipxe-f10726c8bb4e09bf53d0c40b90c1a2cb434672d4.tar.gz ipxe-f10726c8bb4e09bf53d0c40b90c1a2cb434672d4.tar.xz ipxe-f10726c8bb4e09bf53d0c40b90c1a2cb434672d4.zip |
[crypto] Add support for subjectAltName and wildcard certificates
Originally-implemented-by: Alex Chernyakhovsky <achernya@google.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/crypto/x509.c | 140 | ||||
-rw-r--r-- | src/include/ipxe/asn1.h | 5 | ||||
-rw-r--r-- | src/include/ipxe/x509.h | 14 | ||||
-rw-r--r-- | src/tests/x509_test.c | 9 |
4 files changed, 160 insertions, 8 deletions
diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 87b924c8..8e503f3b 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -603,7 +603,7 @@ static int x509_parse_ocsp ( struct x509_certificate *cert, /* Enter accessLocation */ memcpy ( uri, raw, sizeof ( *uri ) ); - if ( ( rc = asn1_enter ( uri, ASN1_IMPLICIT_TAG ( 6 ) ) ) != 0 ) { + if ( ( rc = asn1_enter ( uri, X509_GENERAL_NAME_URI ) ) != 0 ) { DBGC ( cert, "X509 %p OCSP does not contain " "uniformResourceIdentifier:\n", cert ); DBGC_HDA ( cert, 0, raw->data, raw->len ); @@ -708,6 +708,33 @@ static int x509_parse_authority_info_access ( struct x509_certificate *cert, return 0; } +/** + * Parse X.509 certificate subject alternative name + * + * @v cert X.509 certificate + * @v raw ASN.1 cursor + * @ret rc Return status code + */ +static int x509_parse_subject_alt_name ( struct x509_certificate *cert, + const struct asn1_cursor *raw ) { + struct x509_subject_alt_name *alt_name = &cert->extensions.alt_name; + struct asn1_cursor *names = &alt_name->names; + int rc; + + /* Enter subjectAltName */ + memcpy ( names, raw, sizeof ( *names ) ); + if ( ( rc = asn1_enter ( names, ASN1_SEQUENCE ) ) != 0 ) { + DBGC ( cert, "X509 %p invalid subjectAltName: %s\n", + cert, strerror ( rc ) ); + DBGC_HDA ( cert, 0, raw->data, raw->len ); + return rc; + } + DBGC2 ( cert, "X509 %p has subjectAltName:\n", cert ); + DBGC2_HDA ( cert, 0, names->data, names->len ); + + return 0; +} + /** "id-ce-basicConstraints" object identifier */ static uint8_t oid_ce_basic_constraints[] = { ASN1_OID_BASICCONSTRAINTS }; @@ -724,6 +751,10 @@ static uint8_t oid_ce_ext_key_usage[] = static uint8_t oid_pe_authority_info_access[] = { ASN1_OID_AUTHORITYINFOACCESS }; +/** "id-ce-subjectAltName" object identifier */ +static uint8_t oid_ce_subject_alt_name[] = + { ASN1_OID_SUBJECTALTNAME }; + /** Supported certificate extensions */ static struct x509_extension x509_extensions[] = { { @@ -746,6 +777,11 @@ static struct x509_extension x509_extensions[] = { .oid = ASN1_OID_CURSOR ( oid_pe_authority_info_access ), .parse = x509_parse_authority_info_access, }, + { + .name = "subjectAltName", + .oid = ASN1_OID_CURSOR ( oid_ce_subject_alt_name ), + .parse = x509_parse_subject_alt_name, + }, }; /** @@ -1341,6 +1377,82 @@ int x509_validate ( struct x509_certificate *cert, } /** + * Check X.509 certificate alternative dNSName + * + * @v cert X.509 certificate + * @v raw ASN.1 cursor + * @v name Name + * @ret rc Return status code + */ +static int x509_check_dnsname ( struct x509_certificate *cert, + const struct asn1_cursor *raw, + const char *name ) { + const char *fullname = name; + const char *dnsname = raw->data; + size_t len = raw->len; + + /* Check for wildcards */ + if ( ( len >= 2 ) && ( dnsname[0] == '*' ) && ( dnsname[1] == '.' ) ) { + + /* Skip initial "*." */ + dnsname += 2; + len -= 2; + + /* Skip initial portion of name to be tested */ + name = strchr ( name, '.' ); + if ( ! name ) + return -ENOENT; + name++; + } + + /* Compare names */ + if ( ! ( ( strlen ( name ) == len ) && + ( memcmp ( name, dnsname, len ) == 0 ) ) ) + return -ENOENT; + + if ( name == fullname ) { + DBGC2 ( cert, "X509 %p \"%s\" subjectAltName matches \"%s\"\n", + cert, x509_name ( cert ), name ); + } else { + DBGC2 ( cert, "X509 %p \"%s\" subjectAltName matches \"%s\" " + "(via \"*.%s\")\n", cert, x509_name ( cert ), + fullname, name ); + } + return 0; +} + +/** + * Check X.509 certificate alternative name + * + * @v cert X.509 certificate + * @v raw ASN.1 cursor + * @v name Name + * @ret rc Return status code + */ +static int x509_check_alt_name ( struct x509_certificate *cert, + const struct asn1_cursor *raw, + const char *name ) { + struct asn1_cursor alt_name; + unsigned int type; + + /* Enter generalName */ + memcpy ( &alt_name, raw, sizeof ( alt_name ) ); + type = asn1_type ( &alt_name ); + asn1_enter_any ( &alt_name ); + + /* Check this name */ + switch ( type ) { + case X509_GENERAL_NAME_DNS : + return x509_check_dnsname ( cert, &alt_name, name ); + default: + DBGC2 ( cert, "X509 %p \"%s\" unknown name of type %#02x:\n", + cert, x509_name ( cert ), type ); + DBGC2_HDA ( cert, 0, alt_name.data, alt_name.len ); + return -ENOTSUP; + } +} + +/** * Check X.509 certificate name * * @v cert X.509 certificate @@ -1349,17 +1461,29 @@ int x509_validate ( struct x509_certificate *cert, */ int x509_check_name ( struct x509_certificate *cert, const char *name ) { struct asn1_cursor *common_name = &cert->subject.common_name; - size_t len = strlen ( name ); + struct asn1_cursor alt_name; + int rc; /* Check commonName */ - if ( ! ( ( len == common_name->len ) && - ( memcmp ( name, common_name->data, len ) == 0 ) ) ) { - DBGC ( cert, "X509 %p \"%s\" does not match name \"%s\"\n", - cert, x509_name ( cert ), name ); - return -EACCES_WRONG_NAME; + if ( ( strlen ( name ) == common_name->len ) && + ( memcmp ( name, common_name->data, common_name->len ) == 0 ) ) { + DBGC2 ( cert, "X509 %p \"%s\" commonName matches \"%s\"\n", + cert, x509_name ( cert ), name ); + return 0; } - return 0; + /* Check any subjectAlternativeNames */ + memcpy ( &alt_name, &cert->extensions.alt_name.names, + sizeof ( alt_name ) ); + for ( ; alt_name.len ; asn1_skip_any ( &alt_name ) ) { + if ( ( rc = x509_check_alt_name ( cert, &alt_name, + name ) ) == 0 ) + return 0; + } + + DBGC ( cert, "X509 %p \"%s\" does not match name \"%s\"\n", + cert, x509_name ( cert ), name ); + return -EACCES_WRONG_NAME; } /** diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 3e73b59c..d12524dd 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -222,6 +222,11 @@ struct asn1_builder_header { ASN1_OID_SINGLE ( 5 ), ASN1_OID_SINGLE ( 7 ), \ ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 9 ) +/** ASN.1 OID for id-ce-subjectAltName (2.5.29.17) */ +#define ASN1_OID_SUBJECTALTNAME \ + ASN1_OID_INITIAL ( 2, 5 ), ASN1_OID_SINGLE ( 29 ), \ + ASN1_OID_SINGLE ( 17 ) + /** Define an ASN.1 cursor containing an OID */ #define ASN1_OID_CURSOR( oid_value ) { \ .data = oid_value, \ diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index c9254723..055a4460 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -136,6 +136,18 @@ struct x509_authority_info_access { struct x509_ocsp_responder ocsp; }; +/** X.509 certificate subject alternative name */ +struct x509_subject_alt_name { + /** Names */ + struct asn1_cursor names; +}; + +/** X.509 certificate general name types */ +enum x509_general_name_types { + X509_GENERAL_NAME_DNS = ASN1_IMPLICIT_TAG ( 2 ), + X509_GENERAL_NAME_URI = ASN1_IMPLICIT_TAG ( 6 ), +}; + /** An X.509 certificate extensions set */ struct x509_extensions { /** Basic constraints */ @@ -146,6 +158,8 @@ struct x509_extensions { struct x509_extended_key_usage ext_usage; /** Authority information access */ struct x509_authority_info_access auth_info; + /** Subject alternative name */ + struct x509_subject_alt_name alt_name; }; /** A link in an X.509 certificate chain */ diff --git a/src/tests/x509_test.c b/src/tests/x509_test.c index 69ede4c8..d3e01faf 100644 --- a/src/tests/x509_test.c +++ b/src/tests/x509_test.c @@ -1023,7 +1023,16 @@ static void x509_test_exec ( void ) { /* Check certificate names */ x509_check_name_ok ( &server_crt, "boot.test.ipxe.org" ); + x509_check_name_ok ( &server_crt, "demo.test.ipxe.org" ); x509_check_name_fail_ok ( &server_crt, "incorrect.test.ipxe.org" ); + x509_check_name_ok ( &server_crt, "anything.alt.test.ipxe.org" ); + x509_check_name_ok ( &server_crt, "wildcard.alt.test.ipxe.org" ); + x509_check_name_fail_ok ( &server_crt, "sub.domain.alt.test.ipxe.org" ); + x509_check_name_fail_ok ( &server_crt, "alt.test.ipxe.org" ); + x509_check_name_fail_ok ( &server_crt, "test.ipxe.org" ); + x509_check_name_fail_ok ( &server_crt, "ipxe.org" ); + x509_check_name_fail_ok ( &server_crt, "org" ); + x509_check_name_fail_ok ( &server_crt, "" ); /* Parse all certificate chains */ x509_chain_ok ( &server_chain ); |