Skip to content

Commit

Permalink
[crypto] Add support for subjectAltName and wildcard certificates
Browse files Browse the repository at this point in the history
Originally-implemented-by: Alex Chernyakhovsky <achernya@google.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Mar 31, 2014
1 parent f1c5f86 commit f10726c
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 8 deletions.
140 changes: 132 additions & 8 deletions src/crypto/x509.c
Expand Up @@ -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 );
Expand Down Expand Up @@ -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 };
Expand All @@ -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[] = {
{
Expand All @@ -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,
},
};

/**
Expand Down Expand Up @@ -1340,6 +1376,82 @@ int x509_validate ( struct x509_certificate *cert,
return 0;
}

/**
* 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
*
Expand All @@ -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;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/include/ipxe/asn1.h
Expand Up @@ -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, \
Expand Down
14 changes: 14 additions & 0 deletions src/include/ipxe/x509.h
Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down
9 changes: 9 additions & 0 deletions src/tests/x509_test.c
Expand Up @@ -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 );
Expand Down

0 comments on commit f10726c

Please sign in to comment.