diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c index 2eab3422..55860558 100644 --- a/src/crypto/asn1.c +++ b/src/crypto/asn1.c @@ -21,7 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include +#include #include #include @@ -52,6 +54,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); __einfo_error ( EINFO_EINVAL_ASN1_INTEGER ) #define EINFO_EINVAL_ASN1_INTEGER \ __einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid integer" ) +#define EINVAL_ASN1_TIME \ + __einfo_error ( EINFO_EINVAL_ASN1_TIME ) +#define EINFO_EINVAL_ASN1_TIME \ + __einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid time" ) /** * Invalidate ASN.1 object cursor @@ -400,3 +406,110 @@ struct asn1_algorithm * asn1_algorithm ( const struct asn1_cursor *cursor ) { return algorithm; } + +/** + * Parse ASN.1 GeneralizedTime + * + * @v cursor ASN.1 cursor + * @v time Time to fill in + * @ret rc Return status code + * + * RFC 5280 section 4.1.2.5 places several restrictions on the allowed + * formats for UTCTime and GeneralizedTime, and mandates the + * interpretation of centuryless year values. + */ +int asn1_generalized_time ( const struct asn1_cursor *cursor, time_t *time ) { + struct asn1_cursor contents; + unsigned int have_century; + unsigned int type; + union { + struct { + uint8_t century; + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + } __attribute__ (( packed )) named; + uint8_t raw[7]; + } pairs; + struct tm tm; + const uint8_t *data; + size_t remaining; + unsigned int tens; + unsigned int units; + unsigned int i; + int rc; + + /* Determine time format utcTime/generalizedTime */ + memcpy ( &contents, cursor, sizeof ( contents ) ); + type = asn1_type ( &contents ); + switch ( type ) { + case ASN1_UTC_TIME: + have_century = 0; + break; + case ASN1_GENERALIZED_TIME: + have_century = 1; + break; + default: + DBGC ( cursor, "ASN1 %p invalid time type %02x\n", + cursor, type ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -EINVAL_ASN1_TIME; + } + + /* Enter utcTime/generalizedTime */ + if ( ( rc = asn1_enter ( &contents, type ) ) != 0 ) { + DBGC ( cursor, "ASN1 %p cannot locate %s time:\n", cursor, + ( ( type == ASN1_UTC_TIME ) ? "UTC" : "generalized" ) ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return rc; + } + + /* Parse digit string a pair at a time */ + data = contents.data; + remaining = contents.len; + for ( i = ( have_century ? 0 : 1 ) ; i < sizeof ( pairs.raw ) ; i++ ) { + if ( remaining < 2 ) { + DBGC ( cursor, "ASN1 %p invalid time:\n", cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -EINVAL_ASN1_TIME; + } + tens = data[0]; + units = data[1]; + if ( ! ( isdigit ( tens ) && isdigit ( units ) ) ) { + DBGC ( cursor, "ASN1 %p invalid time:\n", cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -EINVAL_ASN1_TIME; + } + pairs.raw[i] = ( ( 10 * ( tens - '0' ) ) + ( units - '0' ) ); + data += 2; + remaining -= 2; + } + + /* Determine century if applicable */ + if ( ! have_century ) + pairs.named.century = ( ( pairs.named.year >= 50 ) ? 19 : 20 ); + + /* Check for trailing "Z" */ + if ( ( remaining != 1 ) || ( data[0] != 'Z' ) ) { + DBGC ( cursor, "ASN1 %p invalid time:\n", cursor ); + DBGC_HDA ( cursor, 0, cursor->data, cursor->len ); + return -EINVAL_ASN1_TIME; + } + + /* Fill in time */ + tm.tm_year = ( ( ( pairs.named.century - 19 ) * 100 ) + + pairs.named.year ); + tm.tm_mon = ( pairs.named.month - 1 ); + tm.tm_mday = pairs.named.day; + tm.tm_hour = pairs.named.hour; + tm.tm_min = pairs.named.minute; + tm.tm_sec = pairs.named.second; + + /* Convert to seconds since the Epoch */ + *time = mktime ( &tm ); + + return 0; +} diff --git a/src/crypto/x509.c b/src/crypto/x509.c index cfecfde3..1cb46a1d 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -20,8 +20,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -#include -#include #include #include #include @@ -60,10 +58,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); __einfo_error ( EINFO_EINVAL_BIT_STRING ) #define EINFO_EINVAL_BIT_STRING \ __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid bit string" ) -#define EINVAL_TIME \ - __einfo_error ( EINFO_EINVAL_TIME ) -#define EINFO_EINVAL_TIME \ - __einfo_uniqify ( EINFO_EINVAL, 0x03, "Invalid time" ) #define EINVAL_ALGORITHM_MISMATCH \ __einfo_error ( EINFO_EINVAL_ALGORITHM_MISMATCH ) #define EINFO_EINVAL_ALGORITHM_MISMATCH \ @@ -301,114 +295,6 @@ static int x509_parse_integral_bit_string ( struct x509_certificate *cert, return 0; } -/** - * Parse X.509 certificate time - * - * @v cert X.509 certificate - * @v time Time to fill in - * @v raw ASN.1 cursor - * @ret rc Return status code - * - * RFC 5280 section 4.1.2.5 places several restrictions on the allowed - * formats for UTCTime and GeneralizedTime, and mandates the - * interpretation of centuryless year values. - */ -static int x509_parse_time ( struct x509_certificate *cert, - struct x509_time *time, - const struct asn1_cursor *raw ) { - struct asn1_cursor cursor; - unsigned int have_century; - unsigned int type; - union { - struct { - uint8_t century; - uint8_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; - } __attribute__ (( packed )) named; - uint8_t raw[7]; - } pairs; - struct tm tm; - const uint8_t *data; - size_t remaining; - unsigned int tens; - unsigned int units; - unsigned int i; - int rc; - - /* Determine time format utcTime/generalizedTime */ - memcpy ( &cursor, raw, sizeof ( cursor ) ); - type = asn1_type ( &cursor ); - switch ( type ) { - case ASN1_UTC_TIME: - have_century = 0; - break; - case ASN1_GENERALIZED_TIME: - have_century = 1; - break; - default: - DBGC ( cert, "X509 %p invalid time type %02x\n", cert, type ); - DBGC_HDA ( cert, 0, raw->data, raw->len ); - return -EINVAL_TIME; - } - - /* Enter utcTime/generalizedTime */ - if ( ( rc = asn1_enter ( &cursor, type ) ) != 0 ) { - DBGC ( cert, "X509 %p cannot locate %s time:\n", cert, - ( ( type == ASN1_UTC_TIME ) ? "UTC" : "generalized" ) ); - DBGC_HDA ( cert, 0, raw->data, raw->len ); - return rc; - } - - /* Parse digit string a pair at a time */ - data = cursor.data; - remaining = cursor.len; - for ( i = ( have_century ? 0 : 1 ) ; i < sizeof ( pairs.raw ) ; i++ ) { - if ( remaining < 2 ) { - DBGC ( cert, "X509 %p invalid time:\n", cert ); - DBGC_HDA ( cert, 0, raw->data, raw->len ); - return -EINVAL_TIME; - } - tens = data[0]; - units = data[1]; - if ( ! ( isdigit ( tens ) && isdigit ( units ) ) ) { - DBGC ( cert, "X509 %p invalid time:\n", cert ); - DBGC_HDA ( cert, 0, raw->data, raw->len ); - return -EINVAL_TIME; - } - pairs.raw[i] = ( ( 10 * ( tens - '0' ) ) + ( units - '0' ) ); - data += 2; - remaining -= 2; - } - - /* Determine century if applicable */ - if ( ! have_century ) - pairs.named.century = ( ( pairs.named.year >= 50 ) ? 19 : 20 ); - - /* Check for trailing "Z" */ - if ( ( remaining != 1 ) || ( data[0] != 'Z' ) ) { - DBGC ( cert, "X509 %p invalid time:\n", cert ); - DBGC_HDA ( cert, 0, raw->data, raw->len ); - return -EINVAL_TIME; - } - - /* Fill in time */ - tm.tm_year = ( ( ( pairs.named.century - 19 ) * 100 ) + - pairs.named.year ); - tm.tm_mon = ( pairs.named.month - 1 ); - tm.tm_mday = pairs.named.day; - tm.tm_hour = pairs.named.hour; - tm.tm_min = pairs.named.minute; - tm.tm_sec = pairs.named.second; - - /* Convert to seconds since the Epoch */ - time->time = mktime ( &tm ); - - return 0; -} /** * Parse X.509 certificate version @@ -520,15 +406,23 @@ static int x509_parse_validity ( struct x509_certificate *cert, asn1_enter ( &cursor, ASN1_SEQUENCE ); /* Parse notBefore */ - if ( ( rc = x509_parse_time ( cert, not_before, &cursor ) ) != 0 ) + if ( ( rc = asn1_generalized_time ( &cursor, + ¬_before->time ) ) != 0 ) { + DBGC ( cert, "X509 %p cannot parse notBefore: %s\n", + cert, strerror ( rc ) ); return rc; + } DBGC2 ( cert, "X509 %p valid from time %lld\n", cert, not_before->time ); asn1_skip_any ( &cursor ); /* Parse notAfter */ - if ( ( rc = x509_parse_time ( cert, not_after, &cursor ) ) != 0 ) + if ( ( rc = asn1_generalized_time ( &cursor, + ¬_after->time ) ) != 0 ) { + DBGC ( cert, "X509 %p cannot parse notAfter: %s\n", + cert, strerror ( rc ) ); return rc; + } DBGC2 ( cert, "X509 %p valid until time %lld\n", cert, not_after->time ); diff --git a/src/include/ipxe/asn1.h b/src/include/ipxe/asn1.h index 1c433c5e..38f0e5b0 100644 --- a/src/include/ipxe/asn1.h +++ b/src/include/ipxe/asn1.h @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include /** An ASN.1 object cursor */ @@ -239,5 +240,7 @@ extern int asn1_compare ( const struct asn1_cursor *cursor1, const struct asn1_cursor *cursor2 ); extern struct asn1_algorithm * asn1_algorithm ( const struct asn1_cursor *cursor ); +extern int asn1_generalized_time ( const struct asn1_cursor *cursor, + time_t *time ); #endif /* _IPXE_ASN1_H */