/* * Copyright (C) 2012 Red Hat Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or * other materials provided with the distribution. * * The names of contributors to this software may not be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * Author: Stef Walter */ #include "config.h" #include "adapter.h" #include "array.h" #include "asn1.h" #include "attrs.h" #include "checksum.h" #define P11_DEBUG_FLAG P11_DEBUG_TRUST #include "debug.h" #include "dict.h" #include "library.h" #include "module.h" #include "oid.h" #include "parser.h" #include "pem.h" #include "pkcs11x.h" #include "x509.h" #include #include #include #include #include #include #include #include #include struct _p11_parser { p11_dict *asn1_defs; /* Set during a parse */ p11_parser_sink sink; void *sink_data; const char *basename; int flags; /* Parsing state */ p11_array *parsing; node_asn *cert_asn; const unsigned char *cert_der; size_t cert_len; }; typedef int (* parser_func) (p11_parser *parser, const unsigned char *data, size_t length); static void begin_parsing (p11_parser *parser, node_asn *cert_asn, const unsigned char *cert_der, size_t cert_len) { return_if_fail (parser->parsing == NULL); return_if_fail (parser->cert_asn == NULL); return_if_fail (parser->cert_der == NULL); parser->parsing = p11_array_new (NULL); /* * We make note of these for later looking up certificate * extensions. See p11_parsed_find_extension(). */ parser->cert_asn = cert_asn; parser->cert_der = cert_der; parser->cert_len = cert_len; } static void finish_parsing (p11_parser *parser, node_asn *cert_asn) { CK_ATTRIBUTE *attrs; int i; return_if_fail (parser->parsing != NULL); /* 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_adapter_build_objects (parser, parser->parsing); for (i = 0; i < parser->parsing->num; i++) { attrs = parser->parsing->elem[i]; if (parser->sink) (parser->sink) (attrs, parser->sink_data); else p11_attrs_free (attrs); } p11_array_free (parser->parsing); parser->parsing = NULL; parser->cert_asn = NULL; parser->cert_der = NULL; parser->cert_len = 0; } #define ID_LENGTH P11_CHECKSUM_SHA1_LENGTH static void id_generate (p11_parser *parser, CK_BYTE *vid) { CK_ULONG val = p11_module_next_id (); p11_checksum_sha1 (vid, &val, sizeof (val), NULL); } static CK_ATTRIBUTE * build_object (p11_parser *parser, CK_OBJECT_CLASS vclass, CK_BYTE *vid, const char *vlabel) { CK_ATTRIBUTE *attrs = NULL; CK_BBOOL vtrue = CK_TRUE; CK_BBOOL vfalse = CK_FALSE; CK_ATTRIBUTE klass = { CKA_CLASS, &vclass, sizeof (vclass) }; CK_ATTRIBUTE token = { CKA_TOKEN, &vtrue, sizeof (vtrue) }; CK_ATTRIBUTE private = { CKA_PRIVATE, &vfalse, sizeof (vfalse) }; CK_ATTRIBUTE modifiable = { CKA_MODIFIABLE, &vfalse, sizeof (vfalse) }; CK_ATTRIBUTE id = { CKA_ID, vid, ID_LENGTH }; CK_ATTRIBUTE label = { CKA_LABEL, }; if (!vlabel) vlabel = parser->basename; if (vlabel) { label.pValue = (void *)vlabel; label.ulValueLen = strlen (vlabel); } else { label.type = CKA_INVALID; } if (!vid) id.type = CKA_INVALID; return p11_attrs_build (attrs, &klass, &token, &private, &modifiable, &id, &label, NULL); } static void calc_check_value (const unsigned char *data, size_t length, CK_BYTE *check_value) { unsigned char checksum[P11_CHECKSUM_SHA1_LENGTH]; p11_checksum_sha1 (checksum, data, length, NULL); memcpy (check_value, checksum, 3); } static bool calc_date (node_asn *cert, const char *field, CK_DATE *date) { node_asn *choice; struct tm when; char buf[64]; time_t timet; char *sub; int len; int ret; choice = asn1_find_node (cert, field); return_val_if_fail (choice != NULL, false); len = sizeof (buf) - 1; ret = asn1_read_value (cert, field, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); sub = strconcat (field, ".", buf, NULL); if (strcmp (buf, "generalTime") == 0) { len = sizeof (buf) - 1; ret = asn1_read_value (cert, sub, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); timet = p11_asn1_parse_general (buf, &when); return_val_if_fail (timet >= 0, false); } else if (strcmp (buf, "utcTime") == 0) { len = sizeof (buf) - 1; ret = asn1_read_value (cert, sub, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); timet = p11_asn1_parse_utc (buf, &when); return_val_if_fail (timet >= 0, false); } else { return_val_if_reached (false); } free (sub); assert (sizeof (date->year) == 4); snprintf ((char *)buf, 5, "%04d", 1900 + when.tm_year); memcpy (date->year, buf, 4); assert (sizeof (date->month) == 2); snprintf ((char *)buf, 3, "%02d", when.tm_mon + 1); memcpy (date->month, buf, 2); assert (sizeof (date->day) == 2); snprintf ((char *)buf, 3, "%02d", when.tm_mday); memcpy (date->day, buf, 2); return true; } static bool calc_element (node_asn *el, const unsigned char *data, size_t length, const char *field, CK_ATTRIBUTE *attr) { int ret; int start, end; ret = asn1_der_decoding_startEnd (el, data, length, field, &start, &end); return_val_if_fail (ret == ASN1_SUCCESS, false); return_val_if_fail (end >= start, false); attr->pValue = (void *)(data + start); attr->ulValueLen = (end - start) + 1; return true; } static CK_ATTRIBUTE * build_x509_certificate (p11_parser *parser, CK_BYTE *vid, node_asn *cert, const unsigned char *data, size_t length) { CK_ATTRIBUTE *attrs; CK_CERTIFICATE_TYPE vx509 = CKC_X_509; CK_BYTE vchecksum[3]; char *label; CK_DATE vstart; CK_DATE vend; /* Filled in later */ CK_ULONG vcategory = 0; CK_BBOOL vtrusted = (parser->flags & P11_PARSE_FLAG_ANCHOR) ? CK_TRUE : CK_FALSE; CK_BBOOL vdistrusted = (parser->flags & P11_PARSE_FLAG_BLACKLIST) ? CK_TRUE : 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, }; CK_ATTRIBUTE issuer = { CKA_ISSUER, }; CK_ATTRIBUTE serial_number = { CKA_SERIAL_NUMBER, }; /* * The following are not present: * CKA_URL * CKA_HASH_OF_SUBJECT_PUBLIC_KEY * CKA_HASH_OF_ISSUER_PUBLIC_KEY * CKA_JAVA_MIDP_SECURITY_DOMAIN */ calc_check_value (data, length, vchecksum); if (!calc_date (cert, "tbsCertificate.validity.notBefore", &vstart)) start_date.type = CKA_INVALID; if (!calc_date (cert, "tbsCertificate.validity.notAfter", &vend)) end_date.type = CKA_INVALID; if (!calc_element (cert, data, length, "tbsCertificate.issuer.rdnSequence", &issuer)) issuer.type = CKA_INVALID; if (!calc_element (cert, data, length, "tbsCertificate.subject.rdnSequence", &subject)) subject.type = CKA_INVALID; if (!calc_element (cert, data, length, "tbsCertificate.serialNumber", &serial_number)) serial_number.type = CKA_INVALID; label = p11_x509_lookup_dn_name (parser->cert_asn, "tbsCertificate.subject", parser->cert_der, parser->cert_len, P11_OID_CN); if (!label) label = p11_x509_lookup_dn_name (parser->cert_asn, "tbsCertificate.subject", parser->cert_der, parser->cert_len, P11_OID_OU); if (!label) label = p11_x509_lookup_dn_name (parser->cert_asn, "tbsCertificate.subject", parser->cert_der, parser->cert_len, P11_OID_O); attrs = build_object (parser, CKO_CERTIFICATE, vid, label); return_val_if_fail (attrs != NULL, NULL); free (label); attrs = p11_attrs_build (attrs, &certificate_type, &certificate_category, &check_value, &trusted, &distrusted, &start_date, &end_date, &subject, &issuer, &serial_number, &value, NULL); return_val_if_fail (attrs != NULL, NULL); if (!p11_array_push (parser->parsing, attrs)) return_val_if_reached (NULL); return attrs; } static CK_ATTRIBUTE * match_parsing_object (p11_parser *parser, CK_ATTRIBUTE *match) { CK_ATTRIBUTE *attrs; int i; for (i = 0; i < parser->parsing->num; i++) { attrs = parser->parsing->elem[i]; if (p11_attrs_match (attrs, match)) return attrs; } return NULL; } unsigned char * p11_parsing_get_extension (p11_parser *parser, p11_array *parsing, const unsigned char *oid, size_t *length) { CK_OBJECT_CLASS klass = CKO_X_CERTIFICATE_EXTENSION; CK_ATTRIBUTE *attrs; CK_ATTRIBUTE *attr; CK_ATTRIBUTE match[] = { { CKA_OBJECT_ID, (void *)oid, p11_oid_length (oid) }, { CKA_CLASS, &klass, sizeof (klass) }, { CKA_INVALID }, }; return_val_if_fail (parser != NULL, NULL); return_val_if_fail (parser->parsing == parsing, NULL); return_val_if_fail (length != NULL, NULL); return_val_if_fail (oid != NULL, NULL); attrs = match_parsing_object (parser, match); if (attrs != NULL) { attr = p11_attrs_find (attrs, CKA_VALUE); return_val_if_fail (attr != NULL, NULL); *length = attr->ulValueLen; return memdup (attr->pValue, attr->ulValueLen); /* Couldn't find a parsed extension, so look in the current certificate */ } else if (parser->cert_asn) { return p11_x509_find_extension (parser->cert_asn, oid, parser->cert_der, parser->cert_len, length); } return NULL; } CK_ATTRIBUTE * p11_parsing_get_certificate (p11_parser *parser, p11_array *parsing) { CK_OBJECT_CLASS klass = CKO_CERTIFICATE; CK_ATTRIBUTE match[] = { { CKA_CLASS, &klass, sizeof (klass) }, { CKA_INVALID }, }; return_val_if_fail (parser != NULL, NULL); return_val_if_fail (parser->parsing == parsing, NULL); return match_parsing_object (parser, match); } static int parse_der_x509_certificate (p11_parser *parser, const unsigned char *data, size_t length) { CK_BYTE vid[ID_LENGTH]; CK_ATTRIBUTE *attrs; node_asn *cert; cert = p11_asn1_decode (parser->asn1_defs, "PKIX1.Certificate", data, length, NULL); if (cert == NULL) return P11_PARSE_UNRECOGNIZED; begin_parsing (parser, cert, data, length); /* The CKA_ID links related objects */ id_generate (parser, vid); attrs = build_x509_certificate (parser, vid, cert, data, length); return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); finish_parsing (parser, cert); asn1_delete_structure (&cert); return P11_PARSE_SUCCESS; } static int 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) }; CK_ATTRIBUTE value = { CKA_VALUE, (void *)ext_der, ext_len }; CK_ATTRIBUTE invalid = { CKA_INVALID, }; CK_ATTRIBUTE *attrs; CK_ATTRIBUTE *id; CK_ATTRIBUTE *label; attrs = build_object (parser, CKO_X_CERTIFICATE_EXTENSION, NULL, NULL); return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); id = p11_attrs_find (cert, CKA_ID); if (id == NULL) id = &invalid; label = p11_attrs_find (cert, CKA_LABEL); if (id == NULL) label = &invalid; attrs = p11_attrs_build (attrs, id, label, &oid, &critical, &value, NULL); return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); if (!p11_array_push (parser->parsing, attrs)) return_val_if_reached (P11_PARSE_FAILURE); 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) { unsigned char *der; size_t len; int ret; der = p11_asn1_encode (ext, &len); return_val_if_fail (der != NULL, 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) { p11_dict *oids; char field[128]; char *oid; int len; int ret; int i; oids = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL); for (i = 1; ; i++) { if (snprintf (field, sizeof (field), "%s.?%u", seqof, i) < 0) return_val_if_reached (NULL); len = 0; ret = asn1_read_value (node, field, NULL, &len); if (ret == ASN1_ELEMENT_NOT_FOUND) break; return_val_if_fail (ret == ASN1_MEM_ERROR, NULL); oid = malloc (len + 1); return_val_if_fail (oid != NULL, NULL); ret = asn1_read_value (node, field, oid, &len); return_val_if_fail (ret == ASN1_SUCCESS, NULL); if (!p11_dict_set (oids, oid, oid)) return_val_if_reached (NULL); } return oids; } static int build_eku_extension (p11_parser *parser, CK_ATTRIBUTE *cert, const unsigned char *oid, CK_BBOOL critical, p11_dict *oid_strs) { p11_dictiter iter; node_asn *dest; int count = 0; void *value; int ret; dest = p11_asn1_create (parser->asn1_defs, "PKIX1.ExtKeyUsageSyntax"); return_val_if_fail (dest != NULL, P11_PARSE_FAILURE); p11_dict_iterate (oid_strs, &iter); while (p11_dict_next (&iter, NULL, &value)) { ret = asn1_write_value (dest, "", "NEW", 1); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); ret = asn1_write_value (dest, "?LAST", value, -1); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); count++; } /* * 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) { ret = asn1_write_value (dest, "", "NEW", 1); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); ret = asn1_write_value (dest, "?LAST", P11_OID_RESERVED_PURPOSE_STR, -1); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); } ret = build_stapled_extension (parser, cert, oid, critical, dest); asn1_delete_structure (&dest); return ret; } static int build_bc_extension (p11_parser *parser, CK_ATTRIBUTE *cert, CK_BBOOL critical, int is_ca) { node_asn *ext; int ret; ext = p11_asn1_create (parser->asn1_defs, "PKIX1.BasicConstraints"); return_val_if_fail (ext != NULL, P11_PARSE_FAILURE); /* 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); /* 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 bool 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, false); /* * 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 false; /* 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; bool 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_x509_parse_basic_constraints (parser->asn1_defs, 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_array *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; data = p11_parsing_get_extension (parser, parser->parsing, P11_OID_EXTENDED_KEY_USAGE, &length); if (data) { ekus = p11_x509_parse_extended_key_usage (parser->asn1_defs, data, length); if (ekus == NULL) p11_message ("invalid extendend key usage certificate extension"); else if (ekus->num == 0) { distrusted = CK_TRUE; trusted = CK_FALSE; } p11_array_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; p11_dictiter iter; CK_ATTRIBUTE *attr; CK_BBOOL trusted; CK_BBOOL distrust; void *key; int start; int end; int ret; int num; /* * 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); if (ret == ASN1_SUCCESS) reject = load_seq_of_oid_str (aux, "reject"); /* Remove all rejected oids from the trust set */ if (trust && reject) { p11_dict_iterate (reject, &iter); while (p11_dict_next (&iter, &key, NULL)) p11_dict_remove (trust, key); } /* * The trust field (or lack of it) becomes a standard ExtKeyUsageSyntax. * * critical: require that this is enforced */ if (trust) { ret = build_eku_extension (parser, cert, P11_OID_EXTENDED_KEY_USAGE, CK_TRUE, trust); return_val_if_fail (ret == P11_PARSE_SUCCESS, ret); } /* * 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 */ if (reject && p11_dict_size (reject) > 0) { ret = build_eku_extension (parser, cert, P11_OID_OPENSSL_REJECT, CK_FALSE, reject); return_val_if_fail (ret == P11_PARSE_SUCCESS, ret); } /* * If loading from an blacklist flagged directory, then override all * trust assumptionsinformation and mark this as a blacklisted certificate */ if (parser->flags & P11_PARSE_FLAG_BLACKLIST) { trusted = CK_FALSE; distrust = CK_TRUE; /* * 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. */ } else if (trust && p11_dict_size (trust) == 0) { trusted = CK_FALSE; distrust = CK_TRUE; /* * Otherwise a 'TRUSTED CERTIFICATE' in an input directory is enough to * mark this as a trusted certificate, even if we're not explicitly * parsing an directory with the anchors flag. */ } else { trusted = CK_TRUE; distrust = CK_FALSE; } 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) = distrust; p11_dict_free (trust); p11_dict_free (reject); /* * For the keyid field we use the SubjectKeyIdentifier extension. It * is already in the correct form, an OCTET STRING. * * non-critical: as recommended in RFC 5280 */ ret = asn1_der_decoding_startEnd (aux, aux_der, aux_len, "keyid", &start, &end); return_val_if_fail (ret == ASN1_SUCCESS || ret == ASN1_ELEMENT_NOT_FOUND, P11_PARSE_FAILURE); if (ret == ASN1_SUCCESS) { 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; } static int parse_openssl_trusted_certificate (p11_parser *parser, const unsigned char *data, size_t length) { CK_ATTRIBUTE *attrs; CK_BYTE vid[ID_LENGTH]; CK_ATTRIBUTE *attr; char *label = NULL; node_asn *cert; node_asn *aux; ssize_t cert_len; int len; int ret; /* * This OpenSSL format is a wierd. It's just two DER structures * placed end to end without any wrapping SEQ. So calculate the * length of the first DER TLV we see and try to parse that as * the X.509 certificate. */ cert_len = p11_asn1_tlv_length (data, length); if (cert_len <= 0) return P11_PARSE_UNRECOGNIZED; cert = p11_asn1_decode (parser->asn1_defs, "PKIX1.Certificate", data, cert_len, NULL); if (cert == NULL) return P11_PARSE_UNRECOGNIZED; aux = p11_asn1_decode (parser->asn1_defs, "OPENSSL.CertAux", data + cert_len, length - cert_len, NULL); if (aux == NULL) { asn1_delete_structure (&cert); return P11_PARSE_UNRECOGNIZED; } begin_parsing (parser, cert, data, cert_len); /* The CKA_ID links related objects */ id_generate (parser, vid); attrs = build_x509_certificate (parser, vid, cert, data, cert_len); return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); /* Pull the label out of the CertAux */ len = 0; ret = asn1_read_value (aux, "alias", NULL, &len); if (ret != ASN1_ELEMENT_NOT_FOUND) { return_val_if_fail (ret == ASN1_MEM_ERROR, P11_PARSE_FAILURE); label = calloc (len + 1, 1); return_val_if_fail (label != NULL, P11_PARSE_FAILURE); ret = asn1_read_value (aux, "alias", label, &len); return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); attr = p11_attrs_find (attrs, CKA_LABEL); assert (attr != NULL); free (attr->pValue); attr->pValue = label; attr->ulValueLen = strlen (label); } 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); asn1_delete_structure (&cert); asn1_delete_structure (&aux); return P11_PARSE_SUCCESS; } static void on_pem_block (const char *type, const unsigned char *contents, size_t length, void *user_data) { p11_parser *parser = user_data; int ret; if (strcmp (type, "CERTIFICATE") == 0) { ret = parse_der_x509_certificate (parser, contents, length); } else if (strcmp (type, "TRUSTED CERTIFICATE") == 0) { ret = parse_openssl_trusted_certificate (parser, contents, length); } else { p11_debug ("Saw unsupported or unrecognized PEM block of type %s", type); ret = P11_PARSE_SUCCESS; } if (ret != P11_PARSE_SUCCESS) p11_message ("Couldn't parse PEM block of type %s", type); } static int parse_pem_certificates (p11_parser *parser, const unsigned char *data, size_t length) { int num; num = p11_pem_parse ((const char *)data, length, on_pem_block, parser); if (num == 0) return P11_PARSE_UNRECOGNIZED; return P11_PARSE_SUCCESS; } static parser_func all_parsers[] = { parse_pem_certificates, parse_der_x509_certificate, NULL, }; p11_parser * p11_parser_new (void) { p11_parser parser = { 0, }; parser.asn1_defs = p11_asn1_defs_load (); return_val_if_fail (parser.asn1_defs != NULL, NULL); return memdup (&parser, sizeof (parser)); } void p11_parser_free (p11_parser *parser) { if (!parser) return; p11_dict_free (parser->asn1_defs); free (parser); } int p11_parse_memory (p11_parser *parser, const char *filename, int flags, const unsigned char *data, size_t length, p11_parser_sink sink, void *sink_data) { int ret = P11_PARSE_UNRECOGNIZED; char *base; int i; return_val_if_fail (parser != NULL, P11_PARSE_FAILURE); return_val_if_fail (parser->sink == NULL, P11_PARSE_FAILURE); base = basename (filename); parser->basename = base; parser->sink = sink; parser->sink_data = sink_data; parser->flags = flags; /* Expected that state is cleaned via finish_parsing () */ parser->parsing = NULL; parser->cert_asn = NULL; parser->cert_der = NULL; parser->cert_len = 0; for (i = 0; all_parsers[i] != NULL; i++) { ret = (all_parsers[i]) (parser, data, length); if (ret != P11_PARSE_UNRECOGNIZED) break; } parser->basename = NULL; parser->sink = NULL; parser->sink_data = NULL; parser->flags = 0; return ret; } int p11_parse_file (p11_parser *parser, const char *filename, int flags, p11_parser_sink sink, void *sink_data) { p11_mmap *map; void *data; size_t size; int ret; map = p11_mmap_open (filename, &data, &size); if (map == NULL) { p11_message ("couldn't open and map file: %s: %s", filename, strerror (errno)); return P11_PARSE_FAILURE; } ret = p11_parse_memory (parser, filename, flags, data, size, sink, sink_data); p11_mmap_close (map); return ret; } p11_dict * p11_parser_get_asn1_defs (p11_parser *parser) { return_val_if_fail (parser != NULL, NULL); return parser->asn1_defs; }