From 4387f69dcbfc7ff78db9caf96fac3cb8ad98f050 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Fri, 14 Mar 2014 13:55:54 -0400 Subject: [PATCH 1/2] Merge subject alt name and wildcard certificate support From: Alexander Chernyakhovsky Date: Thu, 25 Jul 2013 21:35:14 -0400 Subject: [PATCH 1/4] Implement subject-alt-name and wildcard certificates --- src/crypto/x509.c | 56 +++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/asn1.h | 5 ++++ src/include/ipxe/x509.h | 17 +++++++++++++ src/net/tls.c | 36 +++++++++++++++++++++++--- 4 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/crypto/x509.c b/src/crypto/x509.c index d54124c5..ab963b3a 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -520,6 +520,53 @@ static int x509_parse_key_usage ( 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 *subject_alt_name = &cert->extensions.subject_alt_name; + struct asn1_cursor cursor; + struct asn1_cursor string_cursor; + int rc; + + INIT_LIST_HEAD ( &subject_alt_name->names ); + + DBGC ( cert, "X509 %p parsing subjectAltName\n", cert ); + + /* Enter GeneralNames */ + memcpy ( &cursor, raw, sizeof ( cursor ) ); + asn1_enter ( &cursor, ASN1_SEQUENCE ); + DBGC_HDA ( cert, 0, cursor.data, 128 ); + + /* Parse each name in turn */ + while ( cursor.len ) { + /* Mark extension as present */ + subject_alt_name->present = 1; + memcpy ( &string_cursor, &cursor, sizeof ( string_cursor ) ); + if ( ( rc = asn1_enter ( &string_cursor, ASN1_IMPLICIT_TAG ( 2 ) ) ) == 0 ) { + char* name = zalloc ( string_cursor.len + 1 ); + memcpy ( name, string_cursor.data, string_cursor.len ); + if ( strlen ( name ) != string_cursor.len ) { + DBGC ( cert, "X509 %p contains malicious subjectAltName\n", + cert ); + return rc; + } + //DBGC ( cert, "X509 %p subjectAltName %s\n", cert, name ); + struct x509_san_link* link = zalloc ( sizeof ( struct x509_san_link ) ); + link->name = name; + list_add ( &link->list, &subject_alt_name->names ); + } + asn1_skip_any ( &cursor ); + } + + return 0; +} + /** "id-kp-codeSigning" object identifier */ static uint8_t oid_code_signing[] = { ASN1_OID_CODESIGNING }; @@ -738,6 +785,10 @@ static uint8_t oid_ce_basic_constraints[] = static uint8_t oid_ce_key_usage[] = { ASN1_OID_KEYUSAGE }; +/** "id-ce-keyUsage" object identifier */ +static uint8_t oid_ce_subject_alt_name[] = + { ASN1_OID_SUBJECTALTNAME }; + /** "id-ce-extKeyUsage" object identifier */ static uint8_t oid_ce_ext_key_usage[] = { ASN1_OID_EXTKEYUSAGE }; @@ -758,6 +809,11 @@ static struct x509_extension x509_extensions[] = { .oid = ASN1_OID_CURSOR ( oid_ce_key_usage ), .parse = x509_parse_key_usage, }, + { + .name = "subjectAltName", + .oid = ASN1_OID_CURSOR ( oid_ce_subject_alt_name ), + .parse = x509_parse_subject_alt_name, + }, { .name = "extKeyUsage", .oid = ASN1_OID_CURSOR ( oid_ce_ext_key_usage ), diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 3e73b59c..2b84afc9 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -170,6 +170,11 @@ struct asn1_builder_header { ASN1_OID_INITIAL ( 2, 5 ), ASN1_OID_SINGLE ( 29 ), \ ASN1_OID_SINGLE ( 15 ) +/** ASN1. 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 ) + /** ASN.1 OID for id-ce-basicConstraints (2.5.29.19) */ #define ASN1_OID_BASICCONSTRAINTS \ ASN1_OID_INITIAL ( 2, 5 ), ASN1_OID_SINGLE ( 29 ), \ diff --git a/src/include/ipxe/x509.h b/src/include/ipxe/x509.h index a47942a7..e1a19d40 100644 --- a/src/include/ipxe/x509.h +++ b/src/include/ipxe/x509.h @@ -86,6 +86,21 @@ struct x509_basic_constraints { unsigned int path_len; }; +/** A link in an X.509 certificate chain */ +struct x509_san_link { + /** List of links */ + struct list_head list; + /** name */ + char* name; +}; + +struct x509_subject_alt_name { + /** Subject Alternative Name extension is present */ + int present; + /** General Name **/ + struct list_head names; +}; + /** Unlimited path length * * We use -2U, since this quantity represents one *fewer* than the @@ -150,6 +165,8 @@ struct x509_extensions { struct x509_basic_constraints basic; /** Key usage */ struct x509_key_usage usage; + /** Subject Alternative Name */ + struct x509_subject_alt_name subject_alt_name; /** Extended key usage */ struct x509_extended_key_usage ext_usage; /** Authority information access */ diff --git a/src/net/tls.c b/src/net/tls.c index 5e18f726..c7964728 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -2427,6 +2427,37 @@ static struct interface_descriptor tls_cipherstream_desc = ****************************************************************************** */ +static int dns_wildcard_matcher( const char* dns, const char* wildcard ) { + if ( strchr ( wildcard, '*' ) == NULL ) { + /* No wildcard, just strcmp */ + return strcmp ( dns, wildcard ); + } + if ( strrchr(wildcard, '*') != wildcard ) { + /* This is multiple-wildcard. We can't handle that, so bail. */ + return -1; + } + const char* first_dot = strchr (dns, '.') ; + return strcmp ( first_dot, wildcard + 1 ); +} + +static int tls_validator_name( struct tls_session *tls, struct x509_certificate *cert ) { + /* Verify server name */ + if ( ( cert->subject.name == NULL ) && ( !cert->extensions.subject_alt_name.present ) ) { + return -1; + } + struct x509_san_link* link; + list_for_each_entry ( link, &cert->extensions.subject_alt_name.names, list ) { + /* If the name matches, return 0, otherwise, continue */ + if ( dns_wildcard_matcher ( tls->name, link->name ) == 0) { + return 0; + } + } + if ( !cert->extensions.subject_alt_name.present ) { + return dns_wildcard_matcher ( tls->name, cert->subject.name ); + } + return -1; +} + /** * Handle certificate validation completion * @@ -2444,7 +2475,7 @@ static void tls_validator_done ( struct tls_session *tls, int rc ) { /* Check for validation failure */ if ( rc != 0 ) { DBGC ( tls, "TLS %p certificate validation failed: %s\n", - tls, strerror ( rc ) ); + tls, strerror ( rc ) ); goto err; } DBGC ( tls, "TLS %p certificate validation succeeded\n", tls ); @@ -2454,8 +2485,7 @@ static void tls_validator_done ( struct tls_session *tls, int rc ) { assert ( cert != NULL ); /* Verify server name */ - if ( ( cert->subject.name == NULL ) || - ( strcmp ( cert->subject.name, tls->name ) != 0 ) ) { + if ( tls_validator_name( tls, cert ) ) { DBGC ( tls, "TLS %p server name incorrect (expected %s, got " "%s)\n", tls, tls->name, cert->subject.name ); rc = -EACCES_WRONG_NAME; From d1ad2152315eb8b25c431e389cc0ae20b86d13d7 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Fri, 14 Mar 2014 13:57:31 -0400 Subject: [PATCH 2/2] Handle OCSP responses that don't provide certificates From 9f2bbf20533a6c006820c5b03be6f3a93e8b3e99 Mon Sep 17 00:00:00 2001 From: Alexander Chernyakhovsky Date: Tue, 15 Oct 2013 16:03:11 -0400 Subject: [PATCH 4/4] Handle OCSP responses that don't provide certificates Certificate authorities are not required to send the certificate used to sign the OCSP response under some scenarios, namely in the case when the certificate is the same as the one that did the original issue. The iPXE code previously assumed that such cases did not exist, and valid OCSP responses were dropped. Change these semantics by attempting to validate with the original issuer if no specific signing certificate was provided. --- src/crypto/ocsp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) --- src/crypto/ocsp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c index 20287c0b..7f9a2a7b 100644 --- a/src/crypto/ocsp.c +++ b/src/crypto/ocsp.c @@ -872,7 +872,14 @@ int ocsp_validate ( struct ocsp_check *ocsp, time_t time ) { /* Sanity checks */ assert ( response->data != NULL ); - assert ( signer != NULL ); + /* If the signer is NULL, then we did not receive any + * supplementary certificates. Assume it's the issuer, and + * move on with life. If it doesn't validate, then the OCSP + * response is invalid anyway. + */ + if ( signer == NULL ) { + signer = ocsp->issuer; + } /* Validate signer, if applicable. If the signer is not the * issuer, then it must be signed directly by the issuer.