diff options
-rw-r--r-- | common/oid.h | 9 | ||||
-rw-r--r-- | trust/mozilla.c | 74 | ||||
-rw-r--r-- | trust/parser.c | 351 | ||||
-rw-r--r-- | trust/parser.h | 11 | ||||
-rw-r--r-- | trust/tests/test-data.c | 18 | ||||
-rw-r--r-- | trust/tests/test-data.h | 9 | ||||
-rw-r--r-- | trust/tests/test-parser.c | 246 | ||||
-rw-r--r-- | trust/tests/test-token.c | 2 |
8 files changed, 552 insertions, 168 deletions
diff --git a/common/oid.h b/common/oid.h index b0c8538..181539a 100644 --- a/common/oid.h +++ b/common/oid.h @@ -105,8 +105,7 @@ static const unsigned char P11_OID_EXTENDED_KEY_USAGE[] = * The normal X.509 model is to only *include* the extended key * usages that are to be allowed (ie: a whitelist). It's not clear * exactly how valid and useful the reject per extended key usage - * model is. In fact, it appears that openssl does not use this - * information. + * model is. * * However in order to parse openssl trust policy information and * be able to write it back out in the same way, we define a custom @@ -196,10 +195,10 @@ static const unsigned char P11_OID_TIME_STAMPING[] = * be a place holder when no other purposes are defined. * * In theory such a certificate should be blacklisted. But in reality - * OpenSSL supports such empty sets of purposes. RFC 5280 requires at - * least one purpose in an ExtendedKeyUsage. + * many implementations use such empty sets of purposes. RFC 5280 requires + * at least one purpose in an ExtendedKeyUsage. * - * This purpose should never be used or checked. + * Obviously this purpose should never be checked against. */ static const unsigned char P11_OID_RESERVED_PURPOSE[] = { 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x99, 0x77, 0x06, 0x0a, 0x10 }; diff --git a/trust/mozilla.c b/trust/mozilla.c index cd8ff25..fd5e287 100644 --- a/trust/mozilla.c +++ b/trust/mozilla.c @@ -53,12 +53,12 @@ static CK_ATTRIBUTE * update_ku (p11_parser *parser, p11_array *parsing, - CK_ATTRIBUTE *object) + CK_ATTRIBUTE *object, + CK_TRUST present) { - unsigned char *data; + unsigned char *data = NULL; unsigned int ku = 0; size_t length; - CK_TRUST present; CK_TRUST defawlt; CK_ULONG i; @@ -78,22 +78,20 @@ update_ku (p11_parser *parser, CK_ATTRIBUTE attrs[sizeof (ku_attribute_map)]; - if (p11_parsing_get_flags (parser) & P11_PARSE_FLAG_ANCHOR) - present = CKT_NETSCAPE_TRUSTED_DELEGATOR; - else - present = CKT_NETSCAPE_TRUSTED; defawlt = present; - data = p11_parsing_get_extension (parser, parsing, P11_OID_KEY_USAGE, &length); - - /* - * If the certificate extension was missing, then *all* key - * usages are to be set. If the extension was invalid, then - * fail safe to none of the key usages. - */ + /* If blacklisted, don't even bother looking at extensions */ + if (present != CKT_NETSCAPE_UNTRUSTED) + data = p11_parsing_get_extension (parser, parsing, P11_OID_KEY_USAGE, &length); if (data) { + /* + * If the certificate extension was missing, then *all* key + * usages are to be set. If the extension was invalid, then + * fail safe to none of the key usages. + */ defawlt = CKT_NETSCAPE_TRUST_UNKNOWN; + if (p11_parse_key_usage (parser, data, length, &ku) != P11_PARSE_SUCCESS) p11_message ("invalid key usage certificate extension"); free (data); @@ -116,12 +114,12 @@ update_ku (p11_parser *parser, static CK_ATTRIBUTE * update_eku (p11_parser *parser, p11_array *parsing, - CK_ATTRIBUTE *object) + CK_ATTRIBUTE *object, + CK_TRUST trust) { - CK_TRUST trust; CK_TRUST defawlt; CK_TRUST distrust; - unsigned char *data; + unsigned char *data = NULL; p11_dict *ekus = NULL; p11_dict *reject = NULL; size_t length; @@ -145,28 +143,24 @@ update_eku (p11_parser *parser, CK_ATTRIBUTE attrs[sizeof (eku_attribute_map)]; - /* The value set if an eku is present */ - if (p11_parsing_get_flags (parser) & P11_PARSE_FLAG_ANCHOR) - trust = CKT_NETSCAPE_TRUSTED_DELEGATOR; - else - trust= CKT_NETSCAPE_TRUSTED; - /* The value set if an eku is not present, adjusted below */ defawlt = trust; /* The value set if an eku is explictly rejected */ distrust = CKT_NETSCAPE_UNTRUSTED; - /* - * If the certificate extension was missing, then *all* extended key - * usages are to be set. If the extension was invalid, then - * fail safe to none of the extended key usages. - */ - - data = p11_parsing_get_extension (parser, parsing, P11_OID_EXTENDED_KEY_USAGE, &length); + /* If blacklisted, don't even bother looking at extensions */ + if (trust != CKT_NETSCAPE_UNTRUSTED) + data = p11_parsing_get_extension (parser, parsing, P11_OID_EXTENDED_KEY_USAGE, &length); if (data) { + /* + * If the certificate extension was missing, then *all* extended key + * usages are to be set. If the extension was invalid, then + * fail safe to none of the extended key usages. + */ defawlt = CKT_NETSCAPE_TRUST_UNKNOWN; + ekus = p11_parse_extended_key_usage (parser, data, length); if (ekus == NULL) p11_message ("invalid extended key usage certificate extension"); @@ -208,6 +202,9 @@ build_nss_trust_object (p11_parser *parser, CK_ATTRIBUTE *cert) { CK_ATTRIBUTE *object = NULL; + CK_TRUST trust; + CK_ULONG category; + CK_BBOOL bval; CK_OBJECT_CLASS vclass = CKO_NETSCAPE_TRUST; CK_BYTE vsha1_hash[P11_CHECKSUM_SHA1_LENGTH]; @@ -259,10 +256,23 @@ build_nss_trust_object (p11_parser *parser, &step_up_approved, NULL); return_val_if_fail (object != NULL, NULL); - object = update_ku (parser, parsing, object); + /* Calculate the default trust */ + trust = CKT_NETSCAPE_TRUST_UNKNOWN; + + if (p11_attrs_find_bool (cert, CKA_TRUSTED, &bval) && bval) { + if (p11_attrs_find_ulong (cert, CKA_CERTIFICATE_CATEGORY, &category) && category == 2) + trust = CKT_NETSCAPE_TRUSTED_DELEGATOR; + else + trust = CKT_NETSCAPE_TRUSTED; + } + + if (p11_attrs_find_bool (cert, CKA_X_DISTRUSTED, &bval) && bval) + trust = CKT_NETSCAPE_UNTRUSTED; + + object = update_ku (parser, parsing, object, trust); return_val_if_fail (object != NULL, NULL); - object = update_eku (parser, parsing, object); + object = update_eku (parser, parsing, object, trust); return_val_if_fail (object != NULL, NULL); if (!p11_array_push (parsing, object)) diff --git a/trust/parser.c b/trust/parser.c index 3a81dcd..28464fd 100644 --- a/trust/parser.c +++ b/trust/parser.c @@ -163,6 +163,9 @@ finish_parsing (p11_parser *parser, /* This is a double check */ return_if_fail (parser->cert_asn == cert_asn); + /* Update the certificate state */ + p11_parsing_update_certificate (parser, parser->parsing); + /* Call all the hooks for generating further objects */ p11_mozilla_build_trust_object (parser, parser->parsing); @@ -579,29 +582,6 @@ calc_date (node_asn *cert, } static bool -calc_trusted (p11_parser *parser, - node_asn *cert, - CK_BBOOL *vtrusted) -{ - assert (parser != NULL); - assert (vtrusted != NULL); - - /* - * This calculates CKA_TRUSTED, which is a silly attribute, don't - * read too much into this. The real trust mechinisms are elsewhere. - */ - - *vtrusted = CK_FALSE; - if (parser->flags & P11_PARSE_FLAG_ANCHOR) { - *vtrusted = CK_TRUE; - return true; - } - - /* Don't add this attribute unless anchor */ - return false; -} - -static bool calc_element (node_asn *el, const unsigned char *data, size_t length, @@ -631,18 +611,21 @@ build_x509_certificate (p11_parser *parser, CK_CERTIFICATE_TYPE vx509 = CKC_X_509; CK_BYTE vchecksum[3]; - /* TODO: Implement */ - CK_ULONG vcategory = 0; - CK_BBOOL vtrusted = CK_FALSE; CK_DATE vstart; CK_DATE vend; + /* Filled in later */ + CK_ULONG vcategory = 0; + CK_BBOOL vtrusted = CK_FALSE; + CK_BBOOL vdistrusted = CK_FALSE; + CK_ATTRIBUTE certificate_type = { CKA_CERTIFICATE_TYPE, &vx509, sizeof (vx509) }; CK_ATTRIBUTE certificate_category = { CKA_CERTIFICATE_CATEGORY, &vcategory, sizeof (vcategory) }; CK_ATTRIBUTE value = { CKA_VALUE, (void *)data, length }; CK_ATTRIBUTE check_value = { CKA_CHECK_VALUE, &vchecksum, sizeof (vchecksum) }; CK_ATTRIBUTE trusted = { CKA_TRUSTED, &vtrusted, sizeof (vtrusted) }; + CK_ATTRIBUTE distrusted = { CKA_X_DISTRUSTED, &vdistrusted, sizeof (vdistrusted) }; CK_ATTRIBUTE start_date = { CKA_START_DATE, &vstart, sizeof (vstart) }; CK_ATTRIBUTE end_date = { CKA_END_DATE, &vend, sizeof (vend) }; CK_ATTRIBUTE subject = { CKA_SUBJECT, }; @@ -659,10 +642,6 @@ build_x509_certificate (p11_parser *parser, calc_check_value (data, length, vchecksum); - /* This is a silly trust flag, we set it if the cert is an anchor */ - if (!calc_trusted (parser, cert, &vtrusted)) - trusted.type = CKA_INVALID; - if (!calc_date (cert, "tbsCertificate.validity.notBefore", &vstart)) start_date.type = CKA_INVALID; if (!calc_date (cert, "tbsCertificate.validity.notAfter", &vend)) @@ -679,7 +658,7 @@ build_x509_certificate (p11_parser *parser, return_val_if_fail (attrs != NULL, NULL); attrs = p11_attrs_build (attrs, &certificate_type, &certificate_category, - &check_value, &trusted, &start_date, &end_date, + &check_value, &trusted, &distrusted, &start_date, &end_date, &subject, &issuer, &serial_number, &value, NULL); return_val_if_fail (attrs != NULL, NULL); @@ -802,13 +781,6 @@ p11_parsing_get_extension (p11_parser *parser, return NULL; } -int -p11_parsing_get_flags (p11_parser *parser) -{ - return_val_if_fail (parser != NULL, 0); - return parser->flags; -} - CK_ATTRIBUTE * p11_parsing_get_certificate (p11_parser *parser, p11_array *parsing) @@ -827,6 +799,32 @@ p11_parsing_get_certificate (p11_parser *parser, } int +p11_parse_basic_constraints (p11_parser *parser, + const unsigned char *data, + size_t length, + int *is_ca) +{ + char buffer[8]; + node_asn *ext; + int ret; + int len; + + return_val_if_fail (is_ca != NULL, P11_PARSE_FAILURE); + + ext = decode_asn1 (parser, "PKIX1.BasicConstraints", data, length, NULL); + return_val_if_fail (ext != NULL, P11_PARSE_UNRECOGNIZED); + + len = sizeof (buffer); + ret = asn1_read_value (ext, "cA", buffer, &len); + return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + + *is_ca = (strcmp (buffer, "TRUE") == 0); + asn1_delete_structure (&ext); + + return P11_PARSE_SUCCESS; +} + +int p11_parse_key_usage (p11_parser *parser, const unsigned char *data, size_t length, @@ -888,6 +886,10 @@ p11_parse_extended_key_usage (p11_parser *parser, if (!p11_oid_simple (eku_der + start, (end - start) + 1)) continue; + /* If it's our reserved OID, then skip */ + if (p11_oid_equal (eku_der + start, P11_OID_RESERVED_PURPOSE)) + continue; + eku = memdup (eku_der + start, (end - start) + 1); return_val_if_fail (eku != NULL, NULL); @@ -950,12 +952,12 @@ calc_der_length (const unsigned char *data, } static int -build_stapled_extension (p11_parser *parser, - CK_ATTRIBUTE *cert, - const unsigned char *oid_der, - CK_BBOOL vcritical, - const unsigned char *ext_der, - size_t ext_len) +build_der_extension (p11_parser *parser, + CK_ATTRIBUTE *cert, + const unsigned char *oid_der, + CK_BBOOL vcritical, + const unsigned char *ext_der, + int ext_len) { CK_ATTRIBUTE critical = { CKA_X_CRITICAL, &vcritical, sizeof (vcritical) }; CK_ATTRIBUTE oid = { CKA_OBJECT_ID, (void *)oid_der, p11_oid_length (oid_der) }; @@ -985,6 +987,34 @@ build_stapled_extension (p11_parser *parser, return P11_PARSE_SUCCESS; } +static int +build_stapled_extension (p11_parser *parser, + CK_ATTRIBUTE *cert, + const unsigned char *oid, + CK_BBOOL critical, + node_asn *ext) +{ + char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + char *der; + int len; + int ret; + + len = 0; + ret = asn1_der_coding (ext, "", NULL, &len, message); + return_val_if_fail (ret == ASN1_MEM_ERROR, P11_PARSE_FAILURE); + + der = malloc (len); + return_val_if_fail (der != NULL, P11_PARSE_FAILURE); + + ret = asn1_der_coding (ext, "", der, &len, message); + return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + + ret = build_der_extension (parser, cert, oid, critical, (unsigned char *)der, len); + free (der); + + return ret; +} + static p11_dict * load_seq_of_oid_str (node_asn *node, const char *seqof) @@ -1033,8 +1063,6 @@ build_eku_extension (p11_parser *parser, node_asn *dest; int count = 0; void *value; - char *der; - int len; int ret; ret = asn1_create_element (parser->pkix_definitions, "PKIX1.ExtKeyUsageSyntax", &dest); @@ -1052,11 +1080,16 @@ build_eku_extension (p11_parser *parser, } /* - * If no oids have been written, then we have to put some sort of + * If no oids have been written, then we have to put in a reserved * value, due to the way that ExtendedKeyUsage is defined in RFC 5280. * There must be at least one purpose. This is important since *not* * having an ExtendedKeyUsage is very different than having one without * certain usages. + * + * We account for this in p11_parse_extended_key_usage(). However for + * most callers this should not matter, as they only check whether a + * given purpose is present, and don't make assumptions about ones + * that they don't know about. */ if (count == 0) { @@ -1067,31 +1100,198 @@ build_eku_extension (p11_parser *parser, return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); } - len = 0; - ret = asn1_der_coding (dest, "", NULL, &len, NULL); - return_val_if_fail (ret == ASN1_MEM_ERROR, P11_PARSE_FAILURE); - der = malloc (len); - return_val_if_fail (der != NULL, P11_PARSE_FAILURE); + ret = build_stapled_extension (parser, cert, oid, critical, dest); + asn1_delete_structure (&dest); - ret = asn1_der_coding (dest, "", der, &len, NULL); + return ret; +} + +static int +build_bc_extension (p11_parser *parser, + CK_ATTRIBUTE *cert, + CK_BBOOL critical, + int is_ca) +{ + node_asn *ext; + int ret; + + ret = asn1_create_element (parser->pkix_definitions, "PKIX1.BasicConstraints", &ext); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); - ret = build_stapled_extension (parser, cert, oid, critical, - (unsigned char *)der, len); + /* FALSE is the default, so clear if not CA */ + ret = asn1_write_value (ext, "cA", is_ca ? "TRUE" : NULL, is_ca ? -1 : 0); + return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); - free (der); - asn1_delete_structure (&dest); + /* Clear this optional value */ + ret = asn1_write_value (ext, "pathLenConstraint", NULL, 0); + return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + + ret = build_stapled_extension (parser, cert, P11_OID_BASIC_CONSTRAINTS, critical, ext); + asn1_delete_structure (&ext); return ret; } static int -build_cert_aux_extensions (p11_parser *parser, - CK_ATTRIBUTE *cert, - node_asn *aux, - const unsigned char *aux_der, - size_t aux_len) +is_v1_x509_authority (CK_ATTRIBUTE *cert, + node_asn *node) +{ + CK_ATTRIBUTE *subject; + CK_ATTRIBUTE *issuer; + char buffer[16]; + int len; + int ret; + + len = sizeof (buffer); + ret = asn1_read_value (node, "tbsCertificate.version", buffer, &len); + + /* The default value */ + if (ret == ASN1_ELEMENT_NOT_FOUND) { + ret = ASN1_SUCCESS; + buffer[0] = 0; + len = 1; + } + + return_val_if_fail (ret == ASN1_SUCCESS, 0); + + /* + * In X.509 version v1 is the integer zero. Two's complement + * integer, but zero is easy to read. + */ + if (len != 1 || buffer[0] != 0) + return 0; + + /* Must be self-signed, ie: same subject and issuer */ + subject = p11_attrs_find (cert, CKA_SUBJECT); + issuer = p11_attrs_find (cert, CKA_ISSUER); + return (subject != NULL && issuer != NULL && + p11_attr_match_value (subject, issuer->pValue, issuer->ulValueLen)); +} + +static void +update_category (p11_parser *parser, + CK_ATTRIBUTE *cert) +{ + CK_ATTRIBUTE *category; + int is_ca = 0; + unsigned char *data; + size_t length; + int ret; + + /* See if we have a basic constraints extension */ + data = p11_parsing_get_extension (parser, parser->parsing, P11_OID_BASIC_CONSTRAINTS, &length); + if (data) { + if (!p11_parse_basic_constraints (parser, data, length, &is_ca)) + p11_message ("invalid basic constraints certificate extension"); + free (data); + + } else if (is_v1_x509_authority (cert, parser->cert_asn)) { + /* + * If there is no basic constraints extension, and the CA version is + * v1, and is self-signed, then we assume this is a certificate authority. + * So we add a BasicConstraints stapled certificate extension + */ + is_ca = 1; + ret = build_bc_extension (parser, cert, CK_FALSE, is_ca); + return_if_fail (ret == P11_PARSE_SUCCESS); + } + + category = p11_attrs_find (cert, CKA_CERTIFICATE_CATEGORY); + assert (category != NULL); + assert (category->pValue != NULL); + assert (category->ulValueLen == sizeof (CK_ULONG)); + + /* + * In the PKCS#11 spec: + * 0 = unspecified (default value) + * 1 = token user + * 2 = authority + * 3 = other entity + */ + *((CK_ULONG *)category->pValue) = is_ca ? 2 : 3; +} + +static void +update_trust_and_distrust (p11_parser *parser, + CK_ATTRIBUTE *cert) +{ + CK_ATTRIBUTE *attr; + CK_BBOOL trusted; + CK_BBOOL distrusted; + unsigned char *data; + size_t length; + p11_dict *ekus; + + /* + * This function is called to update the CKA_TRUSTED and CKA_X_DISTRUSTED + * fields (anchor and blacklist). Some other code may have updated the + * related extensions, so this may be called more than once. + * + * Since some input like OpenSSL model blacklists as anchors with all + * purposes being removed/rejected, we account for that here. If there + * is an ExtendedKeyUsage without any useful purposes, then treat + * like a blacklist. + * + * The certificate is an anchor if the parser is in anchor mode. + */ + + trusted = (parser->flags & P11_PARSE_FLAG_ANCHOR) ? CK_TRUE : CK_FALSE; + distrusted = (parser->flags & P11_PARSE_FLAG_BLACKLIST) ? CK_TRUE : CK_FALSE; + + /* See if we have a basic constraints extension */ + data = p11_parsing_get_extension (parser, parser->parsing, P11_OID_EXTENDED_KEY_USAGE, &length); + if (data) { + ekus = p11_parse_extended_key_usage (parser, data, length); + if (ekus == NULL) + p11_message ("invalid extendend key usage certificate extension"); + else if (p11_dict_size (ekus) == 0) { + distrusted = CK_TRUE; + trusted = CK_FALSE; + } + + p11_dict_free (ekus); + free (data); + } + + attr = p11_attrs_find (cert, CKA_TRUSTED); + assert (attr != NULL); + assert (attr->pValue != NULL); + assert (attr->ulValueLen == sizeof (CK_BBOOL)); + *((CK_BBOOL *)attr->pValue) = trusted; + + attr = p11_attrs_find (cert, CKA_X_DISTRUSTED); + assert (attr != NULL); + assert (attr->pValue != NULL); + assert (attr->ulValueLen == sizeof (CK_BBOOL)); + *((CK_BBOOL *)attr->pValue) = distrusted; +} + +void +p11_parsing_update_certificate (p11_parser *parser, + p11_array *parsing) +{ + CK_ATTRIBUTE *cert; + + /* Find the certificate to update */ + cert = p11_parsing_get_certificate (parser, parsing); + if (cert == NULL) + return; + + /* This should match the above cert */ + assert (parser->cert_asn != NULL); + + update_category (parser, cert); + update_trust_and_distrust (parser, cert); +} + + +static int +build_openssl_extensions (p11_parser *parser, + CK_ATTRIBUTE *cert, + node_asn *aux, + const unsigned char *aux_der, + size_t aux_len) { p11_dict *trust = NULL; p11_dict *reject = NULL; @@ -1102,10 +1302,15 @@ build_cert_aux_extensions (p11_parser *parser, int ret; int num; - ret = asn1_number_of_elements (aux, "trust", &num); - return_val_if_fail (ret == ASN1_SUCCESS || ret == ASN1_ELEMENT_NOT_FOUND, P11_PARSE_FAILURE); - if (ret == ASN1_SUCCESS) - trust = load_seq_of_oid_str (aux, "trust"); + /* + * This will load an empty list if there is no OPTIONAL trust field. + * OpenSSL assumes that for a TRUSTED CERTIFICATE a missing trust field + * is identical to untrusted for all purposes. + * + * This is different from ExtendedKeyUsage, where a missing certificate + * extension means that it is trusted for all purposes. + */ + trust = load_seq_of_oid_str (aux, "trust"); ret = asn1_number_of_elements (aux, "reject", &num); return_val_if_fail (ret == ASN1_SUCCESS || ret == ASN1_ELEMENT_NOT_FOUND, P11_PARSE_FAILURE); @@ -1120,7 +1325,7 @@ build_cert_aux_extensions (p11_parser *parser, } /* - * The trust field becomes a standard ExtKeyUsageSyntax. + * The trust field (or lack of it) becomes a standard ExtKeyUsageSyntax. * * critical: require that this is enforced */ @@ -1131,8 +1336,9 @@ build_cert_aux_extensions (p11_parser *parser, } /* - * For the reject field we use a custom defined extension. See oid.h for - * more details. It uses ExtKeyUsageSyntax structure. + * For the reject field we use a custom defined extension. We track this + * for completeness, although the above ExtendedKeyUsage extension handles + * this data fine. See oid.h for more details. It uses ExtKeyUsageSyntax structure. * * non-critical: non-standard, and also covered by trusts */ @@ -1156,11 +1362,12 @@ build_cert_aux_extensions (p11_parser *parser, return_val_if_fail (ret == ASN1_SUCCESS || ret == ASN1_ELEMENT_NOT_FOUND, P11_PARSE_FAILURE); if (ret == ASN1_SUCCESS) { - ret = build_stapled_extension (parser, cert, P11_OID_SUBJECT_KEY_IDENTIFIER, CK_FALSE, - aux_der + start, (end - start) + 1); + ret = build_der_extension (parser, cert, P11_OID_SUBJECT_KEY_IDENTIFIER, CK_FALSE, + aux_der + start, (end - start) + 1); return_val_if_fail (ret == P11_PARSE_SUCCESS, ret); } + return P11_PARSE_SUCCESS; } @@ -1222,7 +1429,7 @@ parse_openssl_trusted_certificate (p11_parser *parser, attrs = build_x509_certificate (parser, vid, cert, data, cert_len); return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); - ret = build_cert_aux_extensions (parser, attrs, aux, data + cert_len, length - cert_len); + ret = build_openssl_extensions (parser, attrs, aux, data + cert_len, length - cert_len); return_val_if_fail (ret == P11_PARSE_SUCCESS, ret); finish_parsing (parser, cert); diff --git a/trust/parser.h b/trust/parser.h index 31f307c..da19bce 100644 --- a/trust/parser.h +++ b/trust/parser.h @@ -48,6 +48,7 @@ enum { enum { P11_PARSE_FLAG_NONE = 0, P11_PARSE_FLAG_ANCHOR = 1 << 0, + P11_PARSE_FLAG_BLACKLIST = 1 << 1 }; #define P11_PARSER_FIRST_HANDLE 0xA0000000UL @@ -75,6 +76,11 @@ int p11_parse_file (p11_parser *parser, p11_parser_sink sink, void *sink_data); +int p11_parse_basic_constraints (p11_parser *parser, + const unsigned char *data, + size_t length, + int *is_ca); + int p11_parse_key_usage (p11_parser *parser, const unsigned char *data, size_t length, @@ -86,8 +92,6 @@ p11_dict * p11_parse_extended_key_usage (p11_parser *parser, /* Functions used for retrieving parsing information */ -int p11_parsing_get_flags (p11_parser *parser); - CK_ATTRIBUTE * p11_parsing_get_certificate (p11_parser *parser, p11_array *parsing); @@ -96,4 +100,7 @@ unsigned char * p11_parsing_get_extension (p11_parser *parser, const unsigned char *oid, size_t *length); +void p11_parsing_update_certificate (p11_parser *parser, + p11_array *parsing); + #endif diff --git a/trust/tests/test-data.c b/trust/tests/test-data.c index a3d5373..f159926 100644 --- a/trust/tests/test-data.c +++ b/trust/tests/test-data.c @@ -74,7 +74,7 @@ test_check_cacert3_ca_msg (CuTest *cu, const char *label) { CK_CERTIFICATE_TYPE x509 = CKC_X_509; - CK_ULONG category = 0; /* TODO: Implement */ + CK_ULONG category = 2; /* authority */ CK_ATTRIBUTE expected[] = { { CKA_CERTIFICATE_TYPE, &x509, sizeof (x509) }, @@ -94,6 +94,22 @@ test_check_cacert3_ca_msg (CuTest *cu, } void +test_check_id_msg (CuTest *cu, + const char *file, + int line, + CK_ATTRIBUTE *expected, + CK_ATTRIBUTE *attr) +{ + CK_ATTRIBUTE *one; + CK_ATTRIBUTE *two; + + one = p11_attrs_find (expected, CKA_ID); + two = p11_attrs_find (attr, CKA_ID); + + test_check_attr_msg (cu, file, line, one, two); +} + +void test_check_attrs_msg (CuTest *cu, const char *file, int line, diff --git a/trust/tests/test-data.h b/trust/tests/test-data.h index e4ff938..300e342 100644 --- a/trust/tests/test-data.h +++ b/trust/tests/test-data.h @@ -74,6 +74,15 @@ void test_check_attr_msg (CuTest *cu, CK_ATTRIBUTE *expected, CK_ATTRIBUTE *attr); +#define test_check_id(cu, expected, attrs) \ + test_check_id_msg (cu, __FILE__, __LINE__, expected, attrs) + +void test_check_id_msg (CuTest *cu, + const char *file, + int line, + CK_ATTRIBUTE *expected, + CK_ATTRIBUTE *attr); + static const unsigned char test_cacert3_ca_der[] = { 0x30, 0x82, 0x07, 0x59, 0x30, 0x82, 0x05, 0x41, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x0a, 0x41, 0x8a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, diff --git a/trust/tests/test-parser.c b/trust/tests/test-parser.c index 493dcb3..5bb690a 100644 --- a/trust/tests/test-parser.c +++ b/trust/tests/test-parser.c @@ -86,8 +86,9 @@ on_parse_object (CK_ATTRIBUTE *attrs, static void test_parse_der_certificate (CuTest *cu) { - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *cert; + CK_ATTRIBUTE *object; + CK_BBOOL bval; int ret; setup (cu); @@ -99,11 +100,19 @@ test_parse_der_certificate (CuTest *cu) /* Should have gotten certificate and a trust object */ CuAssertIntEquals (cu, 2, test.objects->num); - attrs = test.objects->elem[0]; - test_check_cacert3_ca (cu, attrs, NULL); + cert = test.objects->elem[0]; + test_check_cacert3_ca (cu, cert, NULL); + + if (!p11_attrs_find_bool (cert, CKA_TRUSTED, &bval)) + CuFail (cu, "missing CKA_TRUSTED"); + CuAssertIntEquals (cu, CK_FALSE, bval); + + if (!p11_attrs_find_bool (cert, CKA_X_DISTRUSTED, &bval)) + CuFail (cu, "missing CKA_X_DISTRUSTED"); + CuAssertIntEquals (cu, CK_FALSE, bval); - attr = p11_attrs_find (attrs, CKA_TRUSTED); - CuAssertPtrEquals (cu, NULL, attr); + object = test.objects->elem[1]; + test_check_id (cu, cert, object); teardown (cu); } @@ -111,8 +120,9 @@ test_parse_der_certificate (CuTest *cu) static void test_parse_pem_certificate (CuTest *cu) { - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *cert; + CK_ATTRIBUTE *object; + CK_BBOOL bval; int ret; setup (cu); @@ -124,11 +134,19 @@ test_parse_pem_certificate (CuTest *cu) /* Should have gotten certificate and a trust object */ CuAssertIntEquals (cu, 2, test.objects->num); - attrs = test.objects->elem[0]; - test_check_cacert3_ca (cu, attrs, NULL); + cert = test.objects->elem[0]; + test_check_cacert3_ca (cu, cert, NULL); + + if (!p11_attrs_find_bool (cert, CKA_TRUSTED, &bval)) + CuFail (cu, "missing CKA_TRUSTED"); + CuAssertIntEquals (cu, CK_FALSE, bval); + + if (!p11_attrs_find_bool (cert, CKA_X_DISTRUSTED, &bval)) + CuFail (cu, "missing CKA_X_DISTRUSTED"); + CuAssertIntEquals (cu, CK_FALSE, bval); - attr = p11_attrs_find (attrs, CKA_TRUSTED); - CuAssertPtrEquals (cu, NULL, attr); + object = test.objects->elem[1]; + test_check_id (cu, cert, object); teardown (cu); } @@ -136,7 +154,7 @@ test_parse_pem_certificate (CuTest *cu) static void test_parse_openssl_trusted (CuTest *cu) { - CK_TRUST trusted = CKT_NETSCAPE_TRUSTED; + CK_TRUST trusted = CKT_NETSCAPE_TRUSTED_DELEGATOR; CK_TRUST distrusted = CKT_NETSCAPE_UNTRUSTED; CK_TRUST unknown = CKT_NETSCAPE_TRUST_UNKNOWN; CK_OBJECT_CLASS certificate_extension = CKO_X_CERTIFICATE_EXTENSION; @@ -149,6 +167,8 @@ test_parse_openssl_trusted (CuTest *cu) { CKA_CLASS, &certificate_extension, sizeof (certificate_extension), }, { CKA_OBJECT_ID, (void *)P11_OID_EXTENDED_KEY_USAGE, sizeof (P11_OID_EXTENDED_KEY_USAGE) }, { CKA_X_CRITICAL, &vtrue, sizeof (vtrue) }, + { CKA_VALUE, "\x30\x14\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x01\x06\x08\x2b\x06" + "\x01\x05\x05\x07\x03\x02", 22 }, { CKA_INVALID }, }; @@ -157,6 +177,7 @@ test_parse_openssl_trusted (CuTest *cu) { CKA_CLASS, &certificate_extension, sizeof (certificate_extension), }, { CKA_OBJECT_ID, (void *)P11_OID_OPENSSL_REJECT, sizeof (P11_OID_OPENSSL_REJECT) }, { CKA_X_CRITICAL, &vfalse, sizeof (vfalse) }, + { CKA_VALUE, "\x30\x0a\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x04", 12 }, { CKA_INVALID }, }; @@ -186,33 +207,149 @@ test_parse_openssl_trusted (CuTest *cu) { CKA_INVALID, } }; - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *cert; + CK_ATTRIBUTE *object; + CK_BBOOL bval; int ret; setup (cu); ret = p11_parse_file (test.parser, SRCDIR "/files/cacert3-trusted.pem", - 0, on_parse_object, cu); + P11_PARSE_FLAG_ANCHOR, on_parse_object, cu); CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); /* Should have gotten certificate, two stapled extensions, and a trust object */ CuAssertIntEquals (cu, 4, test.objects->num); - attrs = test.objects->elem[0]; - test_check_cacert3_ca (cu, attrs, NULL); + cert = test.objects->elem[0]; + test_check_cacert3_ca (cu, cert, NULL); + + if (!p11_attrs_find_bool (cert, CKA_TRUSTED, &bval)) + CuFail (cu, "missing CKA_TRUSTED"); + CuAssertIntEquals (cu, CK_TRUE, bval); + + if (!p11_attrs_find_bool (cert, CKA_X_DISTRUSTED, &bval)) + CuFail (cu, "missing CKA_X_DISTRUSTED"); + CuAssertIntEquals (cu, CK_FALSE, bval); + + object = test.objects->elem[1]; + test_check_attrs (cu, eku_extension, object); + test_check_id (cu, cert, object); + + object = test.objects->elem[2]; + test_check_attrs (cu, reject_extension, object); + test_check_id (cu, cert, object); + + object = test.objects->elem[3]; + test_check_attrs (cu, nss_trust, object); + test_check_id (cu, cert, object); + + teardown (cu); +} + +static void +test_parse_openssl_distrusted (CuTest *cu) +{ + CK_TRUST distrusted = CKT_NETSCAPE_UNTRUSTED; + CK_OBJECT_CLASS certificate_extension = CKO_X_CERTIFICATE_EXTENSION; + CK_OBJECT_CLASS trust_object = CKO_NETSCAPE_TRUST; + CK_OBJECT_CLASS klass = CKO_CERTIFICATE; + CK_CERTIFICATE_TYPE x509 = CKC_X_509; + CK_ULONG category = 2; /* authority */ + CK_BBOOL vtrue = CK_TRUE; + CK_BBOOL vfalse = CK_FALSE; + + CK_ATTRIBUTE certificate[] = { + { CKA_CLASS, &klass, sizeof (klass), }, + { CKA_TOKEN, &vtrue, sizeof (vtrue) }, + { CKA_PRIVATE, &vfalse, sizeof (vfalse) }, + { CKA_MODIFIABLE, &vfalse, sizeof (vfalse) }, + { CKA_CLASS, &klass, sizeof (klass) }, + { CKA_LABEL, "Red Hat Is the CA", 17 }, + { CKA_CERTIFICATE_TYPE, &x509, sizeof (x509) }, + { CKA_CERTIFICATE_CATEGORY, &category, sizeof (category) }, + { CKA_CHECK_VALUE, "\xe9z}", 3 }, + { CKA_START_DATE, "20090916", 8 }, + { CKA_END_DATE, "20190914", 8, }, + { CKA_SERIAL_NUMBER, "\x02\x01\x01", 3 }, + { CKA_TRUSTED, &vfalse, sizeof (vfalse) }, + { CKA_X_DISTRUSTED, &vtrue, sizeof (vtrue) }, + { CKA_INVALID }, + }; + + CK_ATTRIBUTE eku_extension[] = { + { CKA_LABEL, "Red Hat Is the CA", 17 }, + { CKA_CLASS, &certificate_extension, sizeof (certificate_extension), }, + { CKA_OBJECT_ID, (void *)P11_OID_EXTENDED_KEY_USAGE, sizeof (P11_OID_EXTENDED_KEY_USAGE) }, + { CKA_X_CRITICAL, &vtrue, sizeof (vtrue) }, + { CKA_VALUE, "\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x99\x77\x06\x0a\x10", 14 }, + { CKA_INVALID }, + }; + + CK_ATTRIBUTE reject_extension[] = { + { CKA_LABEL, "Red Hat Is the CA", 17 }, + { CKA_CLASS, &certificate_extension, sizeof (certificate_extension), }, + { CKA_OBJECT_ID, (void *)P11_OID_OPENSSL_REJECT, sizeof (P11_OID_OPENSSL_REJECT) }, + { CKA_X_CRITICAL, &vfalse, sizeof (vfalse) }, + { CKA_VALUE, "\x30\x0a\x06\x08\x2b\x06\x01\x05\x05\x07\x03\x02", 12 }, + { CKA_INVALID }, + }; + + CK_ATTRIBUTE nss_trust[] = { + { CKA_LABEL, "Red Hat Is the CA", 17 }, + { CKA_CLASS, &trust_object, sizeof (trust_object), }, + { CKA_CERT_SHA1_HASH, "\xe9z}\xe3\x82""7\xa0U\xb1k\xfe\xffo.\x03\x15*\xba\xb9\x90", 20 }, + { CKA_CERT_MD5_HASH, "\xda\xb4<\xe7;QK\x1a\xe5\xeau\xa1\xc9 \xdf""B", 16 }, + { CKA_SERIAL_NUMBER, "\x02\x01\x01", 3 }, + { CKA_TRUST_SERVER_AUTH, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_CLIENT_AUTH, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_EMAIL_PROTECTION, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_CODE_SIGNING, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_IPSEC_END_SYSTEM, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_IPSEC_TUNNEL, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_IPSEC_USER, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_TIME_STAMPING, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_DIGITAL_SIGNATURE, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_NON_REPUDIATION, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_KEY_ENCIPHERMENT, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_DATA_ENCIPHERMENT, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_KEY_AGREEMENT, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_KEY_CERT_SIGN, &distrusted, sizeof (distrusted) }, + { CKA_TRUST_CRL_SIGN, &distrusted, sizeof (distrusted) }, + { CKA_INVALID, } + }; - attr = p11_attrs_find (attrs, CKA_TRUSTED); - CuAssertPtrEquals (cu, NULL, attr); + CK_ATTRIBUTE *cert; + CK_ATTRIBUTE *object; + int ret; - attrs = test.objects->elem[1]; - test_check_attrs (cu, eku_extension, attrs); + setup (cu); - attrs = test.objects->elem[2]; - test_check_attrs (cu, reject_extension, attrs); + /* + * OpenSSL style is to litter the blacklist in with the anchors, + * so we parse this as an anchor, but expect it to be blacklisted + */ + ret = p11_parse_file (test.parser, SRCDIR "/files/distrusted.pem", + P11_PARSE_FLAG_ANCHOR, on_parse_object, cu); + CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); - attrs = test.objects->elem[3]; - test_check_attrs (cu, nss_trust, attrs); + /* Should have gotten certificate, one stapled extensions, and a trust object */ + CuAssertIntEquals (cu, 4, test.objects->num); + + cert = test.objects->elem[0]; + test_check_attrs (cu, certificate, cert); + + object = test.objects->elem[1]; + test_check_attrs (cu, eku_extension, object); + test_check_id (cu, cert, object); + + object = test.objects->elem[2]; + test_check_attrs (cu, reject_extension, object); + test_check_id (cu, cert, object); + + object = test.objects->elem[3]; + test_check_attrs (cu, nss_trust, object); + test_check_id (cu, cert, object); teardown (cu); } @@ -227,7 +364,7 @@ test_parse_with_key_usage (CuTest *cu) CK_BBOOL vtrue = CK_TRUE; CK_BBOOL vfalse = CK_FALSE; CK_CERTIFICATE_TYPE x509 = CKC_X_509; - CK_ULONG category = 0; /* TODO: Implement */ + CK_ULONG category = 3; /* other entity */ CK_ATTRIBUTE certificate[] = { { CKA_CLASS, &klass, sizeof (klass), }, @@ -244,6 +381,8 @@ test_parse_with_key_usage (CuTest *cu) { CKA_ISSUER, "0*1(0&\x06\x03U\x04\x03\x13\x1f""self-signed-with-ku.example.com", 44 }, { CKA_SUBJECT, "0*1(0&\x06\x03U\x04\x03\x13\x1f""self-signed-with-ku.example.com", 44 }, { CKA_SERIAL_NUMBER, "\x02\x02\x03x", 4 }, + { CKA_TRUSTED, &vtrue, sizeof (vtrue) }, + { CKA_X_DISTRUSTED, &vfalse, sizeof (vfalse) }, { CKA_INVALID }, }; @@ -273,41 +412,34 @@ test_parse_with_key_usage (CuTest *cu) { CKA_INVALID, } }; - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *cert; + CK_ATTRIBUTE *object; + CK_BBOOL bval; int ret; setup (cu); ret = p11_parse_file (test.parser, SRCDIR "/files/self-signed-with-ku.der", - 0, on_parse_object, cu); + P11_PARSE_FLAG_ANCHOR, on_parse_object, cu); CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); - /* Should have gotten certificate, two stapled extensions, and a trust object */ + /* Should have gotten certificate, and a trust object */ CuAssertIntEquals (cu, 2, test.objects->num); - attrs = test.objects->elem[0]; - test_check_attrs (cu, certificate, attrs); + cert = test.objects->elem[0]; + test_check_attrs (cu, certificate, cert); - attr = p11_attrs_find (attrs, CKA_TRUSTED); - CuAssertPtrEquals (cu, NULL, attr); + if (!p11_attrs_find_bool (cert, CKA_TRUSTED, &bval)) + CuFail (cu, "missing CKA_TRUSTED"); + CuAssertIntEquals (cu, CK_TRUE, bval); - attrs = test.objects->elem[1]; - test_check_attrs (cu, nss_trust, attrs); + if (!p11_attrs_find_bool (cert, CKA_X_DISTRUSTED, &bval)) + CuFail (cu, "missing CKA_X_DISTRUSTED"); + CuAssertIntEquals (cu, CK_FALSE, bval); - teardown (cu); -} - -static void -test_parse_distrusted (CuTest *cu) -{ - int ret; - - setup (cu); - - ret = p11_parse_file (test.parser, SRCDIR "/files/distrusted.pem", - 0, on_parse_object, cu); - CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); + object = test.objects->elem[1]; + test_check_attrs (cu, nss_trust, object); + test_check_id (cu, cert, object); teardown (cu); } @@ -315,7 +447,8 @@ test_parse_distrusted (CuTest *cu) static void test_parse_anchor (CuTest *cu) { - CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *cert; + CK_ATTRIBUTE *object; CK_ATTRIBUTE *attr; CK_BBOOL vtrue = CK_TRUE; CK_ATTRIBUTE trusted = { CKA_TRUSTED, &vtrue, sizeof (vtrue) }; @@ -330,12 +463,15 @@ test_parse_anchor (CuTest *cu) /* Should have gotten a certificate and a trust object */ CuAssertIntEquals (cu, 2, test.objects->num); - attrs = test.objects->elem[0]; - test_check_cacert3_ca (cu, attrs, NULL); + cert = test.objects->elem[0]; + test_check_cacert3_ca (cu, cert, NULL); - attr = p11_attrs_find (attrs, CKA_TRUSTED); + attr = p11_attrs_find (cert, CKA_TRUSTED); test_check_attr (cu, &trusted, attr); + object = test.objects->elem[1]; + test_check_id (cu, cert, object); + teardown (cu); } @@ -499,8 +635,8 @@ main (void) SUITE_ADD_TEST (suite, test_parse_der_certificate); SUITE_ADD_TEST (suite, test_parse_pem_certificate); SUITE_ADD_TEST (suite, test_parse_openssl_trusted); + SUITE_ADD_TEST (suite, test_parse_openssl_distrusted); SUITE_ADD_TEST (suite, test_parse_with_key_usage); - SUITE_ADD_TEST (suite, test_parse_distrusted); SUITE_ADD_TEST (suite, test_parse_anchor); SUITE_ADD_TEST (suite, test_parse_no_sink); SUITE_ADD_TEST (suite, test_parse_invalid_file); diff --git a/trust/tests/test-token.c b/trust/tests/test-token.c index 8a5b34d..2ed1134 100644 --- a/trust/tests/test-token.c +++ b/trust/tests/test-token.c @@ -76,7 +76,7 @@ test_token_load (CuTest *cu) /* A certificate and trust object for each parsed object + builtin */ objects = p11_token_objects (test.token); - CuAssertIntEquals (cu, ((count - 1) * 2) + 1, p11_dict_size (objects)); + CuAssertTrue (cu, ((count - 1) * 2) + 1 <= p11_dict_size (objects)); teardown (cu); } |