summaryrefslogtreecommitdiff
path: root/trust/parser.c
diff options
context:
space:
mode:
authorStef Walter <stefw@gnome.org>2013-01-03 11:07:47 +0100
committerStef Walter <stefw@gnome.org>2013-02-05 14:54:53 +0100
commit8b02ff64b30311a4730b60dd72590435f56fb3a2 (patch)
tree20afcc494e8fd9032f9c3a94b27a316848b8728f /trust/parser.c
parent18bb2582c32f4373f7ed85894fb490f2733cb03b (diff)
downloadp11-kit-8b02ff64b30311a4730b60dd72590435f56fb3a2.tar.gz
Fill in certificate authority and trust data correctly
* Fill in CKA_CERTIFICATE_CATEGORY properly for authorities based on the presence of BasicConstraints and/or v1 certificates * Fill in CKA_TRUSTED and CKA_X_DISTRUSTED based on whether the parser is running for anchors or blacklist * In addition support the concept of blacklisted certificates mixed in with the anchors (without any purposes) since that's what exists in the real world. * We do this after the various hooks have had a chance to mess with the certificate extensions and such.
Diffstat (limited to 'trust/parser.c')
-rw-r--r--trust/parser.c351
1 files changed, 279 insertions, 72 deletions
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);