diff options
Diffstat (limited to 'trust')
-rw-r--r-- | trust/Makefile.am | 1 | ||||
-rw-r--r-- | trust/mozilla.c | 284 | ||||
-rw-r--r-- | trust/mozilla.h | 44 | ||||
-rw-r--r-- | trust/parser.c | 724 | ||||
-rw-r--r-- | trust/parser.h | 39 | ||||
-rw-r--r-- | trust/tests/files/self-signed-with-ku.der | bin | 0 -> 478 bytes | |||
-rw-r--r-- | trust/tests/test-data.c | 9 | ||||
-rw-r--r-- | trust/tests/test-parser.c | 159 |
8 files changed, 894 insertions, 366 deletions
diff --git a/trust/Makefile.am b/trust/Makefile.am index 75684cc..9f3f119 100644 --- a/trust/Makefile.am +++ b/trust/Makefile.am @@ -12,6 +12,7 @@ INCLUDES = \ MODULE_SRCS = \ parser.c parser.h \ module.c module.h \ + mozilla.c mozilla.h \ session.c session.h \ token.c token.h \ $(NULL) diff --git a/trust/mozilla.c b/trust/mozilla.c new file mode 100644 index 0000000..cd8ff25 --- /dev/null +++ b/trust/mozilla.c @@ -0,0 +1,284 @@ +/* + * 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 <stefw@redhat.com> + */ + +#include "config.h" + +#include "attrs.h" +#include "checksum.h" +#define P11_DEBUG_FLAG P11_DEBUG_TRUST +#include "debug.h" +#include "dict.h" +#include "library.h" +#include "mozilla.h" +#include "oid.h" +#include "parser.h" + +#include "pkcs11.h" +#include "pkcs11x.h" + +#include <stdlib.h> +#include <string.h> + +static CK_ATTRIBUTE * +update_ku (p11_parser *parser, + p11_array *parsing, + CK_ATTRIBUTE *object) +{ + unsigned char *data; + unsigned int ku = 0; + size_t length; + CK_TRUST present; + CK_TRUST defawlt; + CK_ULONG i; + + struct { + CK_ATTRIBUTE_TYPE type; + unsigned int ku; + } ku_attribute_map[] = { + { CKA_TRUST_DIGITAL_SIGNATURE, P11_KU_DIGITAL_SIGNATURE }, + { CKA_TRUST_NON_REPUDIATION, P11_KU_NON_REPUDIATION }, + { CKA_TRUST_KEY_ENCIPHERMENT, P11_KU_KEY_ENCIPHERMENT }, + { CKA_TRUST_DATA_ENCIPHERMENT, P11_KU_DATA_ENCIPHERMENT }, + { CKA_TRUST_KEY_AGREEMENT, P11_KU_KEY_AGREEMENT }, + { CKA_TRUST_KEY_CERT_SIGN, P11_KU_KEY_CERT_SIGN }, + { CKA_TRUST_CRL_SIGN, P11_KU_CRL_SIGN }, + { CKA_INVALID }, + }; + + 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 (data) { + 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); + } + + for (i = 0; ku_attribute_map[i].type != CKA_INVALID; i++) { + attrs[i].type = ku_attribute_map[i].type; + if (data && (ku & ku_attribute_map[i].ku) == ku_attribute_map[i].ku) { + attrs[i].pValue = &present; + attrs[i].ulValueLen = sizeof (present); + } else { + attrs[i].pValue = &defawlt; + attrs[i].ulValueLen = sizeof (defawlt); + } + } + + return p11_attrs_buildn (object, attrs, i); +} + +static CK_ATTRIBUTE * +update_eku (p11_parser *parser, + p11_array *parsing, + CK_ATTRIBUTE *object) +{ + CK_TRUST trust; + CK_TRUST defawlt; + CK_TRUST distrust; + unsigned char *data; + p11_dict *ekus = NULL; + p11_dict *reject = NULL; + size_t length; + CK_ULONG i; + + struct { + CK_ATTRIBUTE_TYPE type; + const unsigned char *eku; + } eku_attribute_map[] = { + { CKA_TRUST_SERVER_AUTH, P11_OID_SERVER_AUTH }, + { CKA_TRUST_CLIENT_AUTH, P11_OID_CLIENT_AUTH }, + { CKA_TRUST_CODE_SIGNING, P11_OID_CODE_SIGNING }, + { CKA_TRUST_EMAIL_PROTECTION, P11_OID_EMAIL_PROTECTION }, + { CKA_TRUST_IPSEC_END_SYSTEM, P11_OID_IPSEC_END_SYSTEM }, + { CKA_TRUST_IPSEC_TUNNEL, P11_OID_IPSEC_TUNNEL }, + { CKA_TRUST_IPSEC_USER, P11_OID_IPSEC_USER }, + { CKA_TRUST_TIME_STAMPING, P11_OID_TIME_STAMPING }, + { CKA_INVALID }, + }; + + + 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 (data) { + 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"); + free (data); + } + + data = p11_parsing_get_extension (parser, parsing, P11_OID_OPENSSL_REJECT, &length); + if (data) { + reject = p11_parse_extended_key_usage (parser, data, length); + if (reject == NULL) + p11_message ("invalid reject key usage certificate extension"); + free (data); + } + + for (i = 0; eku_attribute_map[i].type != CKA_INVALID; i++) { + attrs[i].type = eku_attribute_map[i].type; + if (reject && p11_dict_get (reject, eku_attribute_map[i].eku)) { + attrs[i].pValue = &distrust; + attrs[i].ulValueLen = sizeof (distrust); + } else if (ekus && p11_dict_get (ekus, eku_attribute_map[i].eku)) { + attrs[i].pValue = &trust; + attrs[i].ulValueLen = sizeof (trust); + } else { + attrs[i].pValue = &defawlt; + attrs[i].ulValueLen = sizeof (defawlt); + } + } + + p11_dict_free (ekus); + p11_dict_free (reject); + + return p11_attrs_buildn (object, attrs, i); +} + + +static CK_ATTRIBUTE * +build_nss_trust_object (p11_parser *parser, + p11_array *parsing, + CK_ATTRIBUTE *cert) +{ + CK_ATTRIBUTE *object = NULL; + + CK_OBJECT_CLASS vclass = CKO_NETSCAPE_TRUST; + CK_BYTE vsha1_hash[P11_CHECKSUM_SHA1_LENGTH]; + CK_BYTE vmd5_hash[P11_CHECKSUM_MD5_LENGTH]; + CK_BBOOL vfalse = CK_FALSE; + CK_BBOOL vtrue = CK_TRUE; + + 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 invalid = { CKA_INVALID, }; + + CK_ATTRIBUTE md5_hash = { CKA_CERT_MD5_HASH, vmd5_hash, sizeof (vmd5_hash) }; + CK_ATTRIBUTE sha1_hash = { CKA_CERT_SHA1_HASH, vsha1_hash, sizeof (vsha1_hash) }; + + CK_ATTRIBUTE step_up_approved = { CKA_TRUST_STEP_UP_APPROVED, &vfalse, sizeof (vfalse) }; + + CK_ATTRIBUTE *label; + CK_ATTRIBUTE *id; + CK_ATTRIBUTE *der; + CK_ATTRIBUTE *subject; + CK_ATTRIBUTE *issuer; + CK_ATTRIBUTE *serial_number; + + /* Setup the hashes of the DER certificate value */ + der = p11_attrs_find (cert, CKA_VALUE); + return_val_if_fail (der != NULL, NULL); + p11_checksum_md5 (vmd5_hash, der->pValue, der->ulValueLen, NULL); + p11_checksum_sha1 (vsha1_hash, der->pValue, der->ulValueLen, NULL); + + /* Copy all of the following attributes from certificate */ + id = p11_attrs_find (cert, CKA_ID); + return_val_if_fail (id != NULL, NULL); + subject = p11_attrs_find (cert, CKA_SUBJECT); + return_val_if_fail (subject != NULL, NULL); + issuer = p11_attrs_find (cert, CKA_ISSUER); + return_val_if_fail (issuer != NULL, NULL); + serial_number = p11_attrs_find (cert, CKA_SERIAL_NUMBER); + return_val_if_fail (serial_number != NULL, NULL); + + /* Try to use the same label */ + label = p11_attrs_find (cert, CKA_LABEL); + if (label == NULL) + label = &invalid; + + object = p11_attrs_build (NULL, &klass, &token, &private, &modifiable, id, label, + subject, issuer, serial_number, &md5_hash, &sha1_hash, + &step_up_approved, NULL); + return_val_if_fail (object != NULL, NULL); + + object = update_ku (parser, parsing, object); + return_val_if_fail (object != NULL, NULL); + + object = update_eku (parser, parsing, object); + return_val_if_fail (object != NULL, NULL); + + if (!p11_array_push (parsing, object)) + return_val_if_reached (NULL); + + return object; +} + +void +p11_mozilla_build_trust_object (p11_parser *parser, + p11_array *parsing) +{ + CK_ATTRIBUTE *cert; + + cert = p11_parsing_get_certificate (parser, parsing); + return_if_fail (cert != NULL); + + build_nss_trust_object (parser, parsing, cert); +} diff --git a/trust/mozilla.h b/trust/mozilla.h new file mode 100644 index 0000000..8d5b20a --- /dev/null +++ b/trust/mozilla.h @@ -0,0 +1,44 @@ +/* + * 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 <stefw@redhat.com> + */ + +#include "array.h" +#include "parser.h" + +#ifndef P11_MOZILLA_H_ +#define P11_MOZILLA_H_ + +void p11_mozilla_build_trust_object (p11_parser *parser, + p11_array *parsing); + +#endif diff --git a/trust/parser.c b/trust/parser.c index 25af902..3a81dcd 100644 --- a/trust/parser.c +++ b/trust/parser.c @@ -34,6 +34,7 @@ #include "config.h" +#include "array.h" #include "attrs.h" #include "checksum.h" #define P11_DEBUG_FLAG P11_DEBUG_TRUST @@ -41,6 +42,8 @@ #include "dict.h" #include "library.h" #include "module.h" +#include "mozilla.h" +#include "oid.h" #include "parser.h" #include "pem.h" #include "pkcs11x.h" @@ -68,6 +71,12 @@ struct _p11_parser { void *sink_data; const char *probable_label; 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, @@ -122,13 +131,55 @@ decode_asn1 (p11_parser *parser, } static void -sink_object (p11_parser *parser, - CK_ATTRIBUTE *attrs) +begin_parsing (p11_parser *parser, + node_asn *cert_asn, + const unsigned char *cert_der, + size_t cert_len) { - if (parser->sink) - (parser->sink) (attrs, parser->sink_data); - else - p11_attrs_free (attrs); + 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); + + /* Call all the hooks for generating further objects */ + p11_mozilla_build_trust_object (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 @@ -627,46 +678,54 @@ build_x509_certificate (p11_parser *parser, attrs = build_object (parser, CKO_CERTIFICATE, vid, NULL); return_val_if_fail (attrs != NULL, NULL); - return p11_attrs_build (attrs, &certificate_type, &certificate_category, - &check_value, &trusted, &start_date, &end_date, - &subject, &issuer, &serial_number, &value, - NULL); + attrs = p11_attrs_build (attrs, &certificate_type, &certificate_category, + &check_value, &trusted, &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 unsigned char * -find_extension (node_asn *cert, - const char *extension_oid, - size_t *length) +find_cert_extension (node_asn *cert, + const unsigned char *der, + size_t der_len, + const unsigned char *oid, + size_t *length) { - unsigned char *data = NULL; char field[128]; - char oid[128]; - int len; + char *value; + int start; + int end; int ret; + int len; int i; - assert (extension_oid != NULL); - assert (strlen (extension_oid) < sizeof (oid)); + assert (oid != NULL); + assert (length != NULL); for (i = 1; ; i++) { if (snprintf (field, sizeof (field), "tbsCertificate.extensions.?%u.extnID", i) < 0) return_val_if_reached (NULL); - len = sizeof (oid) - 1; - ret = asn1_read_value (cert, field, oid, &len); + ret = asn1_der_decoding_startEnd (cert, der, der_len, field, &start, &end); /* No more extensions */ if (ret == ASN1_ELEMENT_NOT_FOUND) break; - /* A really, really long extension oid, not looking for it */ - else if (ret == ASN1_MEM_ERROR) - continue; - return_val_if_fail (ret == ASN1_SUCCESS, NULL); + /* Make sure it's a straightforward oid with certain assumptions */ + if (!p11_oid_simple (der + start, (end - start) + 1)) + continue; + /* The one we're lookin for? */ - if (strcmp (oid, extension_oid) != 0) + if (!p11_oid_equal (der + start, oid)) continue; if (snprintf (field, sizeof (field), "tbsCertificate.extensions.?%u.extnValue", i) < 0) @@ -676,17 +735,95 @@ find_extension (node_asn *cert, ret = asn1_read_value (cert, field, NULL, &len); return_val_if_fail (ret == ASN1_MEM_ERROR, NULL); - data = malloc (len); - return_val_if_fail (data != NULL, NULL); + value = malloc (len); + return_val_if_fail (value != NULL, NULL); - ret = asn1_read_value (cert, field, data, &len); + ret = asn1_read_value (cert, field, value, &len); return_val_if_fail (ret == ASN1_SUCCESS, NULL); *length = len; - break; + return (unsigned char *)value; } - return data; + return NULL; +} + +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 find_cert_extension (parser->cert_asn, parser->cert_der, + parser->cert_len, oid, length); + } + + 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) +{ + 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); } int @@ -717,233 +854,53 @@ p11_parse_key_usage (p11_parser *parser, return P11_PARSE_SUCCESS; } -static unsigned int -decode_ku (p11_parser *parser, - node_asn *cert) -{ - unsigned char *data; - size_t length; - unsigned int ku; - - /* - * 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. - */ - - data = find_extension (cert, "2.5.29.15", &length); - - if (!data) - return ~0U; - - if (p11_parse_key_usage (parser, data, length, &ku) != P11_PARSE_SUCCESS) { - p11_message ("invalid key usage certificate extension"); - ku = 0U; - } - - free (data); - - return ku; -} - -int +p11_dict * p11_parse_extended_key_usage (p11_parser *parser, - const unsigned char *data, - size_t length, - p11_dict *ekus) + const unsigned char *eku_der, + size_t eku_len) { node_asn *ext; char field[128]; - char *eku; - int len; + unsigned char *eku; + p11_dict *ekus; + int start; + int end; int ret; int i; - ext = decode_asn1 (parser, "PKIX1.ExtKeyUsageSyntax", data, length, NULL); + ext = decode_asn1 (parser, "PKIX1.ExtKeyUsageSyntax", eku_der, eku_len, NULL); if (ext == NULL) - return P11_PARSE_UNRECOGNIZED; + return NULL; + + ekus = p11_dict_new (p11_oid_hash, p11_oid_equal, free, NULL); for (i = 1; ; i++) { if (snprintf (field, sizeof (field), "?%u", i) < 0) - return_val_if_reached (P11_PARSE_FAILURE); + return_val_if_reached (NULL); - len = 0; - ret = asn1_read_value (ext, field, NULL, &len); + ret = asn1_der_decoding_startEnd (ext, eku_der, eku_len, field, &start, &end); if (ret == ASN1_ELEMENT_NOT_FOUND) break; - return_val_if_fail (ret == ASN1_MEM_ERROR, P11_PARSE_FAILURE); + return_val_if_fail (ret == ASN1_SUCCESS, NULL); - eku = malloc (len + 1); - return_val_if_fail (eku != NULL, P11_PARSE_FAILURE); + /* Make sure it's a simple OID with certain assumptions */ + if (!p11_oid_simple (eku_der + start, (end - start) + 1)) + continue; - ret = asn1_read_value (ext, field, eku, &len); - return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + eku = memdup (eku_der + start, (end - start) + 1); + return_val_if_fail (eku != NULL, NULL); if (!p11_dict_set (ekus, eku, eku)) - return_val_if_reached (P11_PARSE_FAILURE); - } - - asn1_delete_structure (&ext); - - return P11_PARSE_SUCCESS; - -} - -static p11_dict * -decode_eku (p11_parser *parser, - node_asn *cert) -{ - unsigned char *data; - p11_dict *ekus; - char *eku; - size_t length; - - ekus = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL); - return_val_if_fail (ekus != NULL, NULL); - - /* - * 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 = find_extension (cert, "2.5.29.37", &length); - if (data) { - if (p11_parse_extended_key_usage (parser, data, length, ekus) != P11_PARSE_SUCCESS) { - p11_message ("invalid extended key usage certificate extension"); - p11_dict_free (ekus); - ekus = NULL; - } - } else { - /* A star means anything to has_eku() */ - eku = strdup ("*"); - if (!eku || !p11_dict_set (ekus, eku, eku)) return_val_if_reached (NULL); } - free (data); + asn1_delete_structure (&ext); return ekus; } static int -has_eku (p11_dict *ekus, - const char *eku) -{ - return ekus != NULL && /* If a "*" present, then any thing allowed */ - (p11_dict_get (ekus, eku) || p11_dict_get (ekus, "*")); -} - -static CK_ATTRIBUTE * -build_nss_trust_object (p11_parser *parser, - CK_BYTE *vid, - node_asn *cert, - const unsigned char *data, - size_t length) -{ - CK_ATTRIBUTE *attrs = NULL; - - CK_BYTE vsha1_hash[P11_CHECKSUM_SHA1_LENGTH]; - CK_BYTE vmd5_hash[P11_CHECKSUM_MD5_LENGTH]; - CK_BBOOL vfalse = CK_FALSE; - - CK_TRUST vdigital_signature; - CK_TRUST vnon_repudiation; - CK_TRUST vkey_encipherment; - CK_TRUST vdata_encipherment; - CK_TRUST vkey_agreement; - CK_TRUST vkey_cert_sign; - CK_TRUST vcrl_sign; - - CK_TRUST vserver_auth; - CK_TRUST vclient_auth; - CK_TRUST vcode_signing; - CK_TRUST vemail_protection; - CK_TRUST vipsec_end_system; - CK_TRUST vipsec_tunnel; - CK_TRUST vipsec_user; - CK_TRUST vtime_stamping; - - CK_ATTRIBUTE subject = { CKA_SUBJECT, }; - CK_ATTRIBUTE issuer = { CKA_ISSUER, }; - CK_ATTRIBUTE serial_number = { CKA_SERIAL_NUMBER, }; - - CK_ATTRIBUTE md5_hash = { CKA_CERT_MD5_HASH, vmd5_hash, sizeof (vmd5_hash) }; - CK_ATTRIBUTE sha1_hash = { CKA_CERT_SHA1_HASH, vsha1_hash, sizeof (vsha1_hash) }; - - CK_ATTRIBUTE digital_signature = { CKA_TRUST_DIGITAL_SIGNATURE, &vdigital_signature, sizeof (vdigital_signature) }; - CK_ATTRIBUTE non_repudiation = { CKA_TRUST_NON_REPUDIATION, &vnon_repudiation, sizeof (vnon_repudiation) }; - CK_ATTRIBUTE key_encipherment = { CKA_TRUST_KEY_ENCIPHERMENT, &vkey_encipherment, sizeof (vkey_encipherment) }; - CK_ATTRIBUTE data_encipherment = { CKA_TRUST_DATA_ENCIPHERMENT, &vdata_encipherment, sizeof (vdata_encipherment) }; - CK_ATTRIBUTE key_agreement = { CKA_TRUST_KEY_AGREEMENT, &vkey_agreement, sizeof (vkey_agreement) }; - CK_ATTRIBUTE key_cert_sign = { CKA_TRUST_KEY_CERT_SIGN, &vkey_cert_sign, sizeof (vkey_cert_sign) }; - CK_ATTRIBUTE crl_sign = { CKA_TRUST_CRL_SIGN, &vcrl_sign, sizeof (vcrl_sign) }; - - CK_ATTRIBUTE server_auth = { CKA_TRUST_SERVER_AUTH, &vserver_auth, sizeof (vserver_auth) }; - CK_ATTRIBUTE client_auth = { CKA_TRUST_CLIENT_AUTH, &vclient_auth, sizeof (vclient_auth) }; - CK_ATTRIBUTE code_signing = { CKA_TRUST_CODE_SIGNING, &vcode_signing, sizeof (vcode_signing) }; - CK_ATTRIBUTE email_protection = { CKA_TRUST_EMAIL_PROTECTION, &vemail_protection, sizeof (vemail_protection) }; - CK_ATTRIBUTE ipsec_end_system = { CKA_TRUST_IPSEC_END_SYSTEM, &vipsec_end_system, sizeof (vipsec_end_system) }; - CK_ATTRIBUTE ipsec_tunnel = { CKA_TRUST_IPSEC_TUNNEL, &vipsec_tunnel, sizeof (vipsec_tunnel) }; - CK_ATTRIBUTE ipsec_user = { CKA_TRUST_IPSEC_USER, &vipsec_user, sizeof (vipsec_user) }; - CK_ATTRIBUTE time_stamping = { CKA_TRUST_TIME_STAMPING, &vtime_stamping, sizeof (vtime_stamping) }; - - CK_ATTRIBUTE step_up_approved = { CKA_TRUST_STEP_UP_APPROVED, &vfalse, sizeof (vfalse) }; - - p11_dict *ekus; - unsigned int ku; - CK_TRUST value; - CK_TRUST unknown; - - 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; - - p11_checksum_md5 (vmd5_hash, data, length, NULL); - p11_checksum_sha1 (vsha1_hash, data, length, NULL); - - unknown = CKT_NETSCAPE_TRUST_UNKNOWN; - if (parser->flags & P11_PARSE_FLAG_ANCHOR) - value = CKT_NETSCAPE_TRUSTED_DELEGATOR; - else - value = CKT_NETSCAPE_TRUSTED; - - ku = decode_ku (parser, cert); - vdigital_signature = (ku & P11_KU_DIGITAL_SIGNATURE) ? value : unknown; - vnon_repudiation = (ku & P11_KU_NON_REPUDIATION) ? value : unknown; - vkey_encipherment = (ku & P11_KU_KEY_ENCIPHERMENT) ? value : unknown; - vkey_agreement = (ku & P11_KU_KEY_AGREEMENT) ? value : unknown; - vkey_cert_sign = (ku & P11_KU_KEY_CERT_SIGN) ? value : unknown; - vcrl_sign = (ku & P11_KU_CRL_SIGN) ? value : unknown; - - ekus = decode_eku (parser, cert); - vserver_auth = has_eku (ekus, P11_EKU_SERVER_AUTH) ? value : unknown; - vclient_auth = has_eku (ekus, P11_EKU_CLIENT_AUTH) ? value : unknown; - vcode_signing = has_eku (ekus, P11_EKU_CODE_SIGNING) ? value : unknown; - vemail_protection = has_eku (ekus, P11_EKU_EMAIL) ? value : unknown; - vipsec_end_system = has_eku (ekus, P11_EKU_IPSEC_END_SYSTEM) ? value : unknown; - vipsec_tunnel = has_eku (ekus, P11_EKU_IPSEC_TUNNEL) ? value : unknown; - vipsec_user = has_eku (ekus, P11_EKU_IPSEC_USER) ? value : unknown; - vtime_stamping = has_eku (ekus, P11_EKU_TIME_STAMPING) ? value : unknown; - p11_dict_free (ekus); - - attrs = build_object (parser, CKO_NETSCAPE_TRUST, vid, NULL); - return_val_if_fail (attrs != NULL, NULL); - - return p11_attrs_build (attrs, &subject, &issuer, &serial_number, &md5_hash, &sha1_hash, - &digital_signature, &non_repudiation, &key_encipherment, - &data_encipherment, &key_agreement, &key_cert_sign, &crl_sign, - &server_auth, &client_auth, &code_signing, &email_protection, - &ipsec_end_system, &ipsec_tunnel, &ipsec_user, &time_stamping, - &step_up_approved, NULL); - -} - -static int parse_der_x509_certificate (p11_parser *parser, const unsigned char *data, size_t length) @@ -956,17 +913,15 @@ parse_der_x509_certificate (p11_parser *parser, 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); - sink_object (parser, attrs); - - attrs = build_nss_trust_object (parser, vid, cert, data, length); - return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); - sink_object (parser, attrs); + finish_parsing (parser, cert); asn1_delete_structure (&cert); return P11_PARSE_SUCCESS; } @@ -994,88 +949,219 @@ calc_der_length (const unsigned char *data, return -1; } -static CK_ATTRIBUTE * -overlay_cert_aux_on_nss_trust_object (p11_parser *parser, - CK_ATTRIBUTE *attrs, - node_asn *aux) +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) { - CK_TRUST vserver_auth = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vclient_auth = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vcode_signing = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vemail_protection = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vipsec_end_system = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vipsec_tunnel = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vipsec_user = CKT_NETSCAPE_TRUST_UNKNOWN; - CK_TRUST vtime_stamping = CKT_NETSCAPE_TRUST_UNKNOWN; - - CK_ATTRIBUTE server_auth = { CKA_TRUST_SERVER_AUTH, &vserver_auth, sizeof (vserver_auth) }; - CK_ATTRIBUTE client_auth = { CKA_TRUST_CLIENT_AUTH, &vclient_auth, sizeof (vclient_auth) }; - CK_ATTRIBUTE code_signing = { CKA_TRUST_CODE_SIGNING, &vcode_signing, sizeof (vcode_signing) }; - CK_ATTRIBUTE email_protection = { CKA_TRUST_EMAIL_PROTECTION, &vemail_protection, sizeof (vemail_protection) }; - CK_ATTRIBUTE ipsec_end_system = { CKA_TRUST_IPSEC_END_SYSTEM, &vipsec_end_system, sizeof (vipsec_end_system) }; - CK_ATTRIBUTE ipsec_tunnel = { CKA_TRUST_IPSEC_TUNNEL, &vipsec_tunnel, sizeof (vipsec_tunnel) }; - CK_ATTRIBUTE ipsec_user = { CKA_TRUST_IPSEC_USER, &vipsec_user, sizeof (vipsec_user) }; - CK_ATTRIBUTE time_stamping = { CKA_TRUST_TIME_STAMPING, &vtime_stamping, sizeof (vtime_stamping) }; - - CK_ULONG trust; - char field[256]; - char oid[256]; + 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 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; - int j; - - /* The various CertAux SEQ's we look at for trust information */ - struct { - const char *field; - CK_ULONG trust; - } trust_fields[] = { - { "trust", (parser->flags & P11_PARSE_FLAG_ANCHOR) ? CKT_NETSCAPE_TRUSTED_DELEGATOR : CKT_NETSCAPE_TRUSTED }, - { "reject", CKT_NETSCAPE_UNTRUSTED }, - { NULL, } - }; - /* Various accept/reject usages */ - for (i = 0; trust_fields[i].field != NULL; i++) { - for (j = 1; ; j++) { - if (snprintf (field, sizeof (field), "%s.?%u", trust_fields[i].field, j) < 0) - return_val_if_reached (NULL); - len = sizeof (oid) - 1; - ret = asn1_read_value (aux, field, oid, &len); - - /* No more extensions */ - if (ret == ASN1_ELEMENT_NOT_FOUND) - break; - - /* A really, really long extension oid, not interested */ - else if (ret == ASN1_MEM_ERROR) - continue; - - return_val_if_fail (ret == ASN1_SUCCESS, NULL); - trust = trust_fields[i].trust; - - if (strcmp (oid, P11_EKU_SERVER_AUTH) == 0) - vserver_auth = trust; - else if (strcmp (oid, P11_EKU_CLIENT_AUTH) == 0) - vclient_auth = trust; - else if (strcmp (oid, P11_EKU_CODE_SIGNING) == 0) - vcode_signing = trust; - else if (strcmp (oid, P11_EKU_EMAIL) == 0) - vemail_protection = trust; - else if (strcmp (oid, P11_EKU_IPSEC_END_SYSTEM) == 0) - vipsec_end_system = trust; - else if (strcmp (oid, P11_EKU_IPSEC_TUNNEL) == 0) - vipsec_tunnel = trust; - else if (strcmp (oid, P11_EKU_IPSEC_USER) == 0) - vipsec_user = trust; - else if (strcmp (oid, P11_EKU_TIME_STAMPING) == 0) - vtime_stamping = trust; - } + 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 p11_attrs_build (attrs, &server_auth, &client_auth, &code_signing, - &email_protection, &ipsec_end_system, &ipsec_tunnel, - &ipsec_user, &time_stamping, 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; + char *der; + int len; + int ret; + + ret = asn1_create_element (parser->pkix_definitions, "PKIX1.ExtKeyUsageSyntax", &dest); + return_val_if_fail (ret == ASN1_SUCCESS, 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 some sort of + * 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. + */ + + 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); + } + + 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 = asn1_der_coding (dest, "", der, &len, NULL); + return_val_if_fail (ret == ASN1_SUCCESS, P11_PARSE_FAILURE); + + ret = build_stapled_extension (parser, cert, oid, critical, + (unsigned char *)der, len); + + free (der); + asn1_delete_structure (&dest); + + 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) +{ + p11_dict *trust = NULL; + p11_dict *reject = NULL; + p11_dictiter iter; + void *key; + int start; + int end; + 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"); + + 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 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. 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); + } + + 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_stapled_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 @@ -1114,6 +1200,8 @@ parse_openssl_trusted_certificate (p11_parser *parser, return P11_PARSE_UNRECOGNIZED; } + begin_parsing (parser, cert, data, cert_len); + /* Pull the label out of the CertAux */ len = 0; ret = asn1_read_value (aux, "alias", NULL, &len); @@ -1133,13 +1221,11 @@ 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); - sink_object (parser, attrs); - attrs = build_nss_trust_object (parser, vid, cert, data, cert_len); - return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); - attrs = overlay_cert_aux_on_nss_trust_object (parser, attrs, aux); - return_val_if_fail (attrs != NULL, P11_PARSE_FAILURE); - sink_object (parser, attrs); + ret = build_cert_aux_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); @@ -1253,6 +1339,12 @@ p11_parse_memory (p11_parser *parser, 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) diff --git a/trust/parser.h b/trust/parser.h index 44529ba..31f307c 100644 --- a/trust/parser.h +++ b/trust/parser.h @@ -32,6 +32,7 @@ * Author: Stef Walter <stefw@redhat.com> */ +#include "array.h" #include "dict.h" #include "pkcs11.h" @@ -51,27 +52,6 @@ enum { #define P11_PARSER_FIRST_HANDLE 0xA0000000UL -#define P11_EKU_SERVER_AUTH "1.3.6.1.5.5.7.3.1" -#define P11_EKU_CLIENT_AUTH "1.3.6.1.5.5.7.3.2" -#define P11_EKU_CODE_SIGNING "1.3.6.1.5.5.7.3.3" -#define P11_EKU_EMAIL "1.3.6.1.5.5.7.3.4" -#define P11_EKU_IPSEC_END_SYSTEM "1.3.6.1.5.5.7.3.5" -#define P11_EKU_IPSEC_TUNNEL "1.3.6.1.5.5.7.3.6" -#define P11_EKU_IPSEC_USER "1.3.6.1.5.5.7.3.7" -#define P11_EKU_TIME_STAMPING "1.3.6.1.5.5.7.3.8" - -enum { - P11_KU_DIGITAL_SIGNATURE = 128, - P11_KU_NON_REPUDIATION = 64, - P11_KU_KEY_ENCIPHERMENT = 32, - P11_KU_DATA_ENCIPHERMENT = 16, - P11_KU_KEY_AGREEMENT = 8, - P11_KU_KEY_CERT_SIGN = 4, - P11_KU_CRL_SIGN = 2, - P11_KU_ENCIPHER_ONLY = 1, - P11_KU_DECIPHER_ONLY = 32768, -}; - typedef struct _p11_parser p11_parser; p11_parser * p11_parser_new (void); @@ -100,9 +80,20 @@ int p11_parse_key_usage (p11_parser *parser, size_t length, unsigned int *ku); -int p11_parse_extended_key_usage (p11_parser *parser, +p11_dict * p11_parse_extended_key_usage (p11_parser *parser, const unsigned char *data, - size_t length, - p11_dict *ekus); + size_t length); + +/* 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); + +unsigned char * p11_parsing_get_extension (p11_parser *parser, + p11_array *parsing, + const unsigned char *oid, + size_t *length); #endif diff --git a/trust/tests/files/self-signed-with-ku.der b/trust/tests/files/self-signed-with-ku.der Binary files differnew file mode 100644 index 0000000..51bb227 --- /dev/null +++ b/trust/tests/files/self-signed-with-ku.der diff --git a/trust/tests/test-data.c b/trust/tests/test-data.c index f95b89a..a3d5373 100644 --- a/trust/tests/test-data.c +++ b/trust/tests/test-data.c @@ -70,7 +70,7 @@ void test_check_cacert3_ca_msg (CuTest *cu, const char *file, int line, - CK_ATTRIBUTE_PTR attrs, + CK_ATTRIBUTE *attrs, const char *label) { CK_CERTIFICATE_TYPE x509 = CKC_X_509; @@ -132,3 +132,10 @@ test_check_attr_msg (CuTest *cu, CuFail_Line (cu, file, line, "attribute does not match", message); } } + +void +test_fail_attrs_match (CuTest *cu, + const char *file, + const char *line, + CK_ATTRIBUTE *expect, + CK_ATTRIBUTE *attrs); diff --git a/trust/tests/test-parser.c b/trust/tests/test-parser.c index 3ef979b..493dcb3 100644 --- a/trust/tests/test-parser.c +++ b/trust/tests/test-parser.c @@ -43,6 +43,7 @@ #include "attrs.h" #include "debug.h" #include "library.h" +#include "oid.h" #include "parser.h" #include "pkcs11x.h" #include "test-data.h" @@ -138,9 +139,30 @@ test_parse_openssl_trusted (CuTest *cu) CK_TRUST trusted = CKT_NETSCAPE_TRUSTED; CK_TRUST distrusted = CKT_NETSCAPE_UNTRUSTED; CK_TRUST unknown = CKT_NETSCAPE_TRUST_UNKNOWN; + CK_OBJECT_CLASS certificate_extension = CKO_X_CERTIFICATE_EXTENSION; + CK_OBJECT_CLASS trust_object = CKO_NETSCAPE_TRUST; + CK_BBOOL vtrue = CK_TRUE; + CK_BBOOL vfalse = CK_FALSE; + + CK_ATTRIBUTE eku_extension[] = { + { CKA_LABEL, "Custom Label", 12 }, + { 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_INVALID }, + }; - CK_ATTRIBUTE expected[] = { + CK_ATTRIBUTE reject_extension[] = { { CKA_LABEL, "Custom Label", 12 }, + { 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_INVALID }, + }; + + CK_ATTRIBUTE nss_trust[] = { + { CKA_LABEL, "Custom Label", 12 }, + { CKA_CLASS, &trust_object, sizeof (trust_object), }, { CKA_CERT_SHA1_HASH, "\xad\x7c\x3f\x64\xfc\x44\x39\xfe\xf4\xe9\x0b\xe8\xf4\x7c\x6c\xfa\x8a\xad\xfd\xce", 20 }, { CKA_CERT_MD5_HASH, "\xf7\x25\x12\x82\x4e\x67\xb5\xd0\x8d\x92\xb7\x7c\x0b\x86\x7a\x42", 16 }, { CKA_ISSUER, (void *)test_cacert3_ca_issuer, sizeof (test_cacert3_ca_issuer) }, @@ -154,6 +176,13 @@ test_parse_openssl_trusted (CuTest *cu) { CKA_TRUST_IPSEC_TUNNEL, &unknown, sizeof (unknown) }, { CKA_TRUST_IPSEC_USER, &unknown, sizeof (unknown) }, { CKA_TRUST_TIME_STAMPING, &unknown, sizeof (unknown) }, + { CKA_TRUST_DIGITAL_SIGNATURE, &trusted, sizeof (trusted) }, + { CKA_TRUST_NON_REPUDIATION, &trusted, sizeof (trusted) }, + { CKA_TRUST_KEY_ENCIPHERMENT, &trusted, sizeof (trusted) }, + { CKA_TRUST_DATA_ENCIPHERMENT, &trusted, sizeof (trusted) }, + { CKA_TRUST_KEY_AGREEMENT, &trusted, sizeof (trusted) }, + { CKA_TRUST_KEY_CERT_SIGN, &trusted, sizeof (trusted) }, + { CKA_TRUST_CRL_SIGN, &trusted, sizeof (trusted) }, { CKA_INVALID, } }; @@ -167,8 +196,8 @@ test_parse_openssl_trusted (CuTest *cu) 0, on_parse_object, cu); CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); - /* Should have gotten certificate and a trust object */ - CuAssertIntEquals (cu, 2, test.objects->num); + /* 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); @@ -177,7 +206,94 @@ test_parse_openssl_trusted (CuTest *cu) CuAssertPtrEquals (cu, NULL, attr); attrs = test.objects->elem[1]; - test_check_attrs (cu, expected, attrs); + test_check_attrs (cu, eku_extension, attrs); + + attrs = test.objects->elem[2]; + test_check_attrs (cu, reject_extension, attrs); + + attrs = test.objects->elem[3]; + test_check_attrs (cu, nss_trust, attrs); + + teardown (cu); +} + +static void +test_parse_with_key_usage (CuTest *cu) +{ + CK_TRUST trusted = CKT_NETSCAPE_TRUSTED; + CK_TRUST unknown = CKT_NETSCAPE_TRUST_UNKNOWN; + CK_OBJECT_CLASS klass = CKO_CERTIFICATE; + CK_OBJECT_CLASS trust_object = CKO_NETSCAPE_TRUST; + CK_BBOOL vtrue = CK_TRUE; + CK_BBOOL vfalse = CK_FALSE; + CK_CERTIFICATE_TYPE x509 = CKC_X_509; + CK_ULONG category = 0; /* TODO: Implement */ + + 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, "self-signed-with-ku.der", 23 }, + { CKA_CERTIFICATE_TYPE, &x509, sizeof (x509) }, + { CKA_CERTIFICATE_CATEGORY, &category, sizeof (category) }, + { CKA_CHECK_VALUE, "d/\x9c", 3 }, + { CKA_START_DATE, "20121211", 8 }, + { CKA_END_DATE, "20130110", 8, }, + { 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_INVALID }, + }; + + CK_ATTRIBUTE nss_trust[] = { + { CKA_LABEL, "self-signed-with-ku.der", 23 }, + { CKA_CLASS, &trust_object, sizeof (trust_object), }, + { CKA_CERT_SHA1_HASH, "d/\x9c=\xbc\x9a\x7f\x91\xc7wT\t`\x86\xe2\x8e\x8f\xa8J\x12", 20 }, + { CKA_CERT_MD5_HASH, "\xb1N=\x16\x12?dz\x97\x81""By/\xcc\x97\x82", 16 }, + { 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_TRUST_SERVER_AUTH, &trusted, sizeof (trusted) }, + { CKA_TRUST_CLIENT_AUTH, &trusted, sizeof (trusted) }, + { CKA_TRUST_EMAIL_PROTECTION, &trusted, sizeof (trusted) }, + { CKA_TRUST_CODE_SIGNING, &trusted, sizeof (trusted) }, + { CKA_TRUST_IPSEC_END_SYSTEM, &trusted, sizeof (trusted) }, + { CKA_TRUST_IPSEC_TUNNEL, &trusted, sizeof (trusted) }, + { CKA_TRUST_IPSEC_USER, &trusted, sizeof (trusted) }, + { CKA_TRUST_TIME_STAMPING, &trusted, sizeof (trusted) }, + { CKA_TRUST_DIGITAL_SIGNATURE, &trusted, sizeof (trusted) }, + { CKA_TRUST_NON_REPUDIATION, &unknown, sizeof (unknown) }, + { CKA_TRUST_KEY_ENCIPHERMENT, &unknown, sizeof (unknown) }, + { CKA_TRUST_DATA_ENCIPHERMENT, &unknown, sizeof (unknown) }, + { CKA_TRUST_KEY_AGREEMENT, &unknown, sizeof (unknown) }, + { CKA_TRUST_KEY_CERT_SIGN, &trusted, sizeof (trusted) }, + { CKA_TRUST_CRL_SIGN, &unknown, sizeof (unknown) }, + { CKA_INVALID, } + }; + + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *attr; + int ret; + + setup (cu); + + ret = p11_parse_file (test.parser, SRCDIR "/files/self-signed-with-ku.der", + 0, on_parse_object, cu); + CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); + + /* Should have gotten certificate, two stapled extensions, and a trust object */ + CuAssertIntEquals (cu, 2, test.objects->num); + + attrs = test.objects->elem[0]; + test_check_attrs (cu, certificate, attrs); + + attr = p11_attrs_find (attrs, CKA_TRUSTED); + CuAssertPtrEquals (cu, NULL, attr); + + attrs = test.objects->elem[1]; + test_check_attrs (cu, nss_trust, attrs); teardown (cu); } @@ -269,14 +385,14 @@ test_parse_unrecognized (CuTest *cu) struct { const char *eku; size_t length; - const char *expected[16]; + const unsigned char *expected[16]; } extended_key_usage_fixtures[] = { { test_eku_server_and_client, sizeof (test_eku_server_and_client), - { P11_EKU_CLIENT_AUTH, P11_EKU_SERVER_AUTH, NULL }, }, + { P11_OID_CLIENT_AUTH, P11_OID_SERVER_AUTH, NULL }, }, { test_eku_none, sizeof (test_eku_none), { NULL, }, }, { test_eku_client_email_and_timestamp, sizeof (test_eku_client_email_and_timestamp), - { P11_EKU_CLIENT_AUTH, P11_EKU_EMAIL, P11_EKU_TIME_STAMPING }, }, + { P11_OID_CLIENT_AUTH, P11_OID_EMAIL_PROTECTION, P11_OID_TIME_STAMPING }, }, { NULL }, }; @@ -285,17 +401,14 @@ test_parse_extended_key_usage (CuTest *cu) { p11_dict *ekus; int i, j; - int ret; setup (cu); for (i = 0; extended_key_usage_fixtures[i].eku != NULL; i++) { - ekus = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL); - - ret = p11_parse_extended_key_usage (test.parser, - (const unsigned char *)extended_key_usage_fixtures[i].eku, - extended_key_usage_fixtures[i].length, ekus); - CuAssertIntEquals (cu, P11_PARSE_SUCCESS, ret); + ekus = p11_parse_extended_key_usage (test.parser, + (const unsigned char *)extended_key_usage_fixtures[i].eku, + extended_key_usage_fixtures[i].length); + CuAssertPtrNotNull (cu, ekus); for (j = 0; extended_key_usage_fixtures[i].expected[j] != NULL; j++) CuAssertTrue (cu, p11_dict_get (ekus, extended_key_usage_fixtures[i].expected[j]) != NULL); @@ -311,16 +424,11 @@ static void test_bad_extended_key_usage (CuTest *cu) { p11_dict *ekus; - int ret; setup (cu); - ekus = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL); - - ret = p11_parse_extended_key_usage (test.parser, (const unsigned char *)"blah", 4, ekus); - CuAssertIntEquals (cu, P11_PARSE_UNRECOGNIZED, ret); - - p11_dict_free (ekus); + ekus = p11_parse_extended_key_usage (test.parser, (const unsigned char *)"blah", 4); + CuAssertPtrEquals (cu, NULL, ekus); teardown (cu); } @@ -384,18 +492,19 @@ main (void) p11_debug_init (); p11_message_quiet (); + SUITE_ADD_TEST (suite, test_bad_extended_key_usage); + SUITE_ADD_TEST (suite, test_parse_extended_key_usage); + SUITE_ADD_TEST (suite, test_bad_key_usage); + SUITE_ADD_TEST (suite, test_parse_key_usage); 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_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); SUITE_ADD_TEST (suite, test_parse_unrecognized); - SUITE_ADD_TEST (suite, test_bad_extended_key_usage); - SUITE_ADD_TEST (suite, test_parse_extended_key_usage); - SUITE_ADD_TEST (suite, test_bad_key_usage); - SUITE_ADD_TEST (suite, test_parse_key_usage); CuSuiteRun (suite); CuSuiteSummary (suite, output); |