summaryrefslogtreecommitdiff
path: root/trust/builder.c
diff options
context:
space:
mode:
Diffstat (limited to 'trust/builder.c')
-rw-r--r--trust/builder.c1556
1 files changed, 1556 insertions, 0 deletions
diff --git a/trust/builder.c b/trust/builder.c
new file mode 100644
index 0000000..4397b9b
--- /dev/null
+++ b/trust/builder.c
@@ -0,0 +1,1556 @@
+/*
+ * 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"
+
+#define P11_DEBUG_FLAG P11_DEBUG_TRUST
+
+#include "array.h"
+#include "asn1.h"
+#include "attrs.h"
+#include "builder.h"
+#include "checksum.h"
+#include "constants.h"
+#include "debug.h"
+#include "index.h"
+#include "library.h"
+#include "oid.h"
+#include "pkcs11x.h"
+#include "x509.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct _p11_builder {
+ p11_asn1_cache *asn1_cache;
+ p11_dict *asn1_defs;
+ int flags;
+};
+
+enum {
+ NONE = 0,
+ CREATE = 1 << 0,
+ MODIFY = 1 << 1,
+ REQUIRE = 1 << 2,
+ WANT = 1 << 3,
+};
+
+enum {
+ NORMAL_BUILD = 0,
+ GENERATED_CLASS = 1 << 0,
+};
+
+typedef struct {
+ int build_flags;
+ struct {
+ CK_ATTRIBUTE_TYPE type;
+ int flags;
+ } attrs[32];
+ CK_ATTRIBUTE * (*populate) (p11_builder *, p11_index *, CK_ATTRIBUTE *);
+} builder_schema;
+
+static node_asn *
+decode_or_get_asn1 (p11_builder *builder,
+ const char *struct_name,
+ const unsigned char *der,
+ size_t length)
+{
+ node_asn *node;
+
+ node = p11_asn1_cache_get (builder->asn1_cache, struct_name, der, length);
+ if (node != NULL)
+ return node;
+
+ node = p11_asn1_decode (builder->asn1_defs, struct_name, der, length, NULL);
+ if (node != NULL)
+ p11_asn1_cache_take (builder->asn1_cache, node, struct_name, der, length);
+
+ return node;
+}
+
+static unsigned char *
+lookup_extension (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert,
+ const unsigned char *oid,
+ size_t *ext_len)
+{
+ CK_OBJECT_CLASS klass = CKO_X_CERTIFICATE_EXTENSION;
+ CK_OBJECT_HANDLE obj;
+ CK_ATTRIBUTE *attrs;
+ unsigned char *ext;
+ CK_ATTRIBUTE *value;
+ CK_ATTRIBUTE *id;
+ node_asn *node;
+
+ CK_ATTRIBUTE match[] = {
+ { CKA_ID, },
+ { CKA_OBJECT_ID, (void *)oid, p11_oid_length (oid) },
+ { CKA_CLASS, &klass, sizeof (klass) },
+ { CKA_INVALID },
+ };
+
+ /* Look for a stapled certificate extension */
+ id = p11_attrs_find (cert, CKA_ID);
+ if (id != NULL) {
+ match[0].pValue = id->pValue;
+ match[0].ulValueLen = id->ulValueLen;
+
+ obj = p11_index_find (index, match);
+ attrs = p11_index_lookup (index, obj);
+ if (attrs != NULL) {
+ value = p11_attrs_find (attrs, CKA_VALUE);
+ return_val_if_fail (value != NULL, NULL);
+
+ *ext_len = value->ulValueLen;
+ ext = memdup (value->pValue, value->ulValueLen);
+ return_val_if_fail (ext != NULL, NULL);
+ return ext;
+ }
+ }
+
+ /* Couldn't find a parsed extension, so look in the current certificate */
+ value = p11_attrs_find (cert, CKA_VALUE);
+ if (value != NULL) {
+ node = decode_or_get_asn1 (builder, "PKIX1.Certificate",
+ value->pValue, value->ulValueLen);
+ return_val_if_fail (node != NULL, false);
+
+ return p11_x509_find_extension (node, oid, value->pValue,
+ value->ulValueLen, ext_len);
+ }
+
+ return NULL;
+}
+
+static CK_OBJECT_HANDLE *
+lookup_related (p11_index *index,
+ CK_OBJECT_CLASS klass,
+ CK_ATTRIBUTE *id)
+{
+ CK_ATTRIBUTE match[] = {
+ { CKA_ID, },
+ { CKA_CLASS, &klass, sizeof (klass) },
+ { CKA_INVALID }
+ };
+
+ return_val_if_fail (id != NULL, NULL);
+
+ match[0].pValue = id->pValue;
+ match[0].ulValueLen = id->ulValueLen;
+
+ return p11_index_find_all (index, match);
+}
+
+p11_builder *
+p11_builder_new (int flags)
+{
+ p11_builder *builder;
+
+ builder = calloc (1, sizeof (p11_builder));
+ return_val_if_fail (builder != NULL, NULL);
+
+ builder->asn1_cache = p11_asn1_cache_new ();
+ return_val_if_fail (builder->asn1_cache, NULL);
+ builder->asn1_defs = p11_asn1_cache_defs (builder->asn1_cache);
+
+ builder->flags = flags;
+ return builder;
+}
+
+#define COMMON_ATTRS \
+ { CKA_CLASS, REQUIRE | CREATE }, \
+ { CKA_TOKEN, CREATE | WANT }, \
+ { CKA_MODIFIABLE, CREATE | WANT }, \
+ { CKA_PRIVATE, CREATE }, \
+ { CKA_LABEL, CREATE | MODIFY | WANT }, \
+ { CKA_X_GENERATED, CREATE }
+
+static CK_ATTRIBUTE *
+common_populate (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *unused)
+{
+ CK_BBOOL tokenv = CK_FALSE;
+ CK_BBOOL modifiablev = CK_TRUE;
+ CK_BBOOL privatev = CK_FALSE;
+ CK_BBOOL generatedv = CK_FALSE;
+
+ CK_ATTRIBUTE token = { CKA_TOKEN, &tokenv, sizeof (tokenv), };
+ CK_ATTRIBUTE privat = { CKA_PRIVATE, &privatev, sizeof (privatev) };
+ CK_ATTRIBUTE modifiable = { CKA_MODIFIABLE, &modifiablev, sizeof (modifiablev) };
+ CK_ATTRIBUTE generated = { CKA_X_GENERATED, &generatedv, sizeof (generatedv) };
+ CK_ATTRIBUTE label = { CKA_LABEL, "", 0 };
+
+ if (builder->flags & P11_BUILDER_FLAG_TOKEN) {
+ tokenv = CK_TRUE;
+ modifiablev = CK_FALSE;
+ }
+
+ return p11_attrs_build (NULL, &token, &privat, &modifiable, &label, &generated, 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 *node,
+ const char *field,
+ CK_DATE *date)
+{
+ node_asn *choice;
+ struct tm when;
+ char buf[64];
+ time_t timet;
+ char *sub;
+ int len;
+ int ret;
+
+ if (!node)
+ return false;
+
+ choice = asn1_find_node (node, field);
+ return_val_if_fail (choice != NULL, false);
+
+ len = sizeof (buf) - 1;
+ ret = asn1_read_value (node, 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 (node, 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 (node, 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 *node,
+ const unsigned char *data,
+ size_t length,
+ const char *field,
+ CK_ATTRIBUTE *attr)
+{
+ int ret;
+ int start, end;
+
+ if (!node)
+ return false;
+
+ ret = asn1_der_decoding_startEnd (node, 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 bool
+is_v1_x509_authority (p11_builder *builder,
+ CK_ATTRIBUTE *cert)
+{
+ CK_ATTRIBUTE subject;
+ CK_ATTRIBUTE issuer;
+ CK_ATTRIBUTE *value;
+ char buffer[16];
+ node_asn *node;
+ int len;
+ int ret;
+
+ value = p11_attrs_find_valid (cert, CKA_VALUE);
+ if (value == NULL)
+ return false;
+
+ node = decode_or_get_asn1 (builder, "PKIX1.Certificate",
+ value->pValue, value->ulValueLen);
+ return_val_if_fail (node != NULL, false);
+
+ 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 */
+ if (!calc_element (node, value->pValue, value->ulValueLen, "tbsCertificate.subject", &subject))
+ return_val_if_reached (false);
+ if (!calc_element (node, value->pValue, value->ulValueLen, "tbsCertificate.issuer", &issuer))
+ return_val_if_reached (false);
+ return p11_attr_match_value (&subject, issuer.pValue, issuer.ulValueLen);
+}
+
+static bool
+calc_certificate_category (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert,
+ CK_ULONG *category)
+{
+ unsigned char *ext;
+ size_t ext_len;
+ bool is_ca = 0;
+ bool ret;
+
+ /*
+ * In the PKCS#11 spec:
+ * 0 = unspecified (default value)
+ * 1 = token user
+ * 2 = authority
+ * 3 = other entity
+ */
+
+ /* See if we have a basic constraints extension */
+ ext = lookup_extension (builder, index, cert, P11_OID_BASIC_CONSTRAINTS, &ext_len);
+ if (ext != NULL) {
+ ret = p11_x509_parse_basic_constraints (builder->asn1_defs, ext, ext_len, &is_ca);
+ free (ext);
+ if (!ret) {
+ p11_message ("invalid basic constraints certificate extension");
+ return false;
+ }
+
+ } else if (is_v1_x509_authority (builder, cert)) {
+ /*
+ * 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;
+
+ } else if (!p11_attrs_find_valid (cert, CKA_VALUE)) {
+ /*
+ * If we have no certificate value, then this is unknown
+ */
+ *category = 0;
+ return true;
+
+ }
+
+ *category = is_ca ? 2 : 3;
+ return true;
+}
+
+static CK_ATTRIBUTE *
+certificate_value_attrs (CK_ATTRIBUTE *attrs,
+ node_asn *node,
+ const unsigned char *der,
+ size_t der_len)
+{
+ CK_BBOOL falsev = CK_FALSE;
+ CK_ULONG zero = 0UL;
+ CK_BYTE checkv[3];
+ CK_DATE startv;
+ CK_DATE endv;
+ char *labelv = NULL;
+
+ CK_ATTRIBUTE trusted = { CKA_TRUSTED, &falsev, sizeof (falsev) };
+ CK_ATTRIBUTE distrusted = { CKA_X_DISTRUSTED, &falsev, sizeof (falsev) };
+ CK_ATTRIBUTE url = { CKA_URL, "", 0 };
+ CK_ATTRIBUTE hash_of_subject_public_key = { CKA_HASH_OF_SUBJECT_PUBLIC_KEY, "", 0 };
+ CK_ATTRIBUTE hash_of_issuer_public_key = { CKA_HASH_OF_ISSUER_PUBLIC_KEY, "", 0 };
+ CK_ATTRIBUTE java_midp_security_domain = { CKA_JAVA_MIDP_SECURITY_DOMAIN, &zero, sizeof (zero) };
+ CK_ATTRIBUTE check_value = { CKA_CHECK_VALUE, &checkv, sizeof (checkv) };
+ CK_ATTRIBUTE start_date = { CKA_START_DATE, &startv, sizeof (startv) };
+ CK_ATTRIBUTE end_date = { CKA_END_DATE, &endv, sizeof (endv) };
+ CK_ATTRIBUTE subject = { CKA_SUBJECT, };
+ CK_ATTRIBUTE issuer = { CKA_ISSUER, "", 0 };
+ CK_ATTRIBUTE serial_number = { CKA_SERIAL_NUMBER, "", 0 };
+ CK_ATTRIBUTE label = { CKA_LABEL };
+
+ return_val_if_fail (attrs != NULL, NULL);
+
+ if (der == NULL)
+ check_value.type = CKA_INVALID;
+ else
+ calc_check_value (der, der_len, checkv);
+
+ if (!calc_date (node, "tbsCertificate.validity.notBefore", &startv))
+ start_date.ulValueLen = 0;
+ if (!calc_date (node, "tbsCertificate.validity.notAfter", &endv))
+ end_date.ulValueLen = 0;
+
+ calc_element (node, der, der_len, "tbsCertificate.issuer.rdnSequence", &issuer);
+ if (!calc_element (node, der, der_len, "tbsCertificate.subject.rdnSequence", &subject))
+ subject.type = CKA_INVALID;
+ calc_element (node, der, der_len, "tbsCertificate.serialNumber", &serial_number);
+
+ if (node) {
+ labelv = p11_x509_lookup_dn_name (node, "tbsCertificate.subject",
+ der, der_len, P11_OID_CN);
+ if (!labelv)
+ labelv = p11_x509_lookup_dn_name (node, "tbsCertificate.subject",
+ der, der_len, P11_OID_OU);
+ if (!labelv)
+ labelv = p11_x509_lookup_dn_name (node, "tbsCertificate.subject",
+ der, der_len, P11_OID_O);
+ }
+
+ if (labelv) {
+ label.pValue = labelv;
+ label.ulValueLen = strlen (labelv);
+ } else {
+ label.type = CKA_INVALID;
+ }
+
+ attrs = p11_attrs_build (attrs, &trusted, &distrusted, &url, &hash_of_issuer_public_key,
+ &hash_of_subject_public_key, &java_midp_security_domain,
+ &check_value, &start_date, &end_date,
+ &subject, &issuer, &serial_number, &label,
+ NULL);
+ return_val_if_fail (attrs != NULL, NULL);
+
+ return attrs;
+}
+
+static CK_ATTRIBUTE *
+certificate_populate (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert)
+{
+ CK_ULONG categoryv = 0UL;
+ CK_ATTRIBUTE *attrs = NULL;
+ CK_ATTRIBUTE *value;
+ node_asn *node = NULL;
+ unsigned char *der = NULL;
+ size_t der_len = 0;
+
+ CK_ATTRIBUTE category = { CKA_CERTIFICATE_CATEGORY, &categoryv, sizeof (categoryv) };
+ CK_ATTRIBUTE empty_value = { CKA_VALUE, "", 0 };
+
+ attrs = common_populate (builder, index, cert);
+ return_val_if_fail (attrs != NULL, NULL);
+
+ value = p11_attrs_find_valid (cert, CKA_VALUE);
+ if (value != NULL) {
+ der = value->pValue;
+ der_len = value->ulValueLen;
+ node = decode_or_get_asn1 (builder, "PKIX1.Certificate",
+ value->pValue, value->ulValueLen);
+ }
+
+ attrs = certificate_value_attrs (attrs, node, der, der_len);
+ return_val_if_fail (attrs != NULL, NULL);
+
+ if (!calc_certificate_category (builder, index, cert, &categoryv))
+ categoryv = 0;
+
+ return p11_attrs_build (attrs, &category, &empty_value, NULL);
+}
+
+const static builder_schema certificate_schema = {
+ NORMAL_BUILD,
+ { COMMON_ATTRS,
+ { CKA_CERTIFICATE_TYPE, REQUIRE | CREATE },
+ { CKA_TRUSTED, },
+ { CKA_X_DISTRUSTED, },
+ { CKA_CERTIFICATE_CATEGORY, CREATE | MODIFY | WANT },
+ { CKA_CHECK_VALUE, CREATE | MODIFY | WANT },
+ { CKA_START_DATE, CREATE | MODIFY | WANT },
+ { CKA_END_DATE, CREATE | MODIFY | WANT },
+ { CKA_SUBJECT, CREATE | WANT },
+ { CKA_ID, CREATE | MODIFY | WANT },
+ { CKA_ISSUER, CREATE | MODIFY | WANT },
+ { CKA_SERIAL_NUMBER, CREATE | MODIFY | WANT },
+ { CKA_VALUE, CREATE },
+ { CKA_URL, CREATE },
+ { CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CREATE },
+ { CKA_HASH_OF_ISSUER_PUBLIC_KEY, CREATE },
+ { CKA_JAVA_MIDP_SECURITY_DOMAIN, CREATE },
+ { CKA_INVALID },
+ }, certificate_populate,
+};
+
+static CK_ATTRIBUTE *
+extension_populate (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *extension)
+{
+ CK_ATTRIBUTE *attrs = NULL;
+
+ attrs = common_populate (builder, index, extension);
+ return_val_if_fail (attrs != NULL, NULL);
+
+ if (!p11_attrs_find_valid (extension, CKA_X_CRITICAL)) {
+ CK_BBOOL falsev = CK_FALSE;
+ CK_ATTRIBUTE critical = { CKA_X_CRITICAL, &falsev, sizeof (falsev) };
+ attrs = p11_attrs_build (attrs, &critical, NULL);
+ return_val_if_fail (attrs != NULL, NULL);
+ }
+
+ return attrs;
+}
+
+const static builder_schema extension_schema = {
+ NORMAL_BUILD,
+ { COMMON_ATTRS,
+ { CKA_VALUE, REQUIRE | CREATE },
+ { CKA_X_CRITICAL, WANT },
+ { CKA_OBJECT_ID, REQUIRE | CREATE },
+ { CKA_ID, CREATE | MODIFY | WANT },
+ { CKA_INVALID },
+ }, extension_populate,
+};
+
+static CK_ATTRIBUTE *
+data_populate (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *data)
+{
+ static CK_ATTRIBUTE value = { CKA_VALUE, "", 0 };
+ static CK_ATTRIBUTE application = { CKA_APPLICATION, "", 0 };
+ static CK_ATTRIBUTE object_id = { CKA_OBJECT_ID, "", 0 };
+ CK_ATTRIBUTE *attrs;
+
+ attrs = common_populate (builder, index, data);
+ return_val_if_fail (attrs != NULL, NULL);
+
+ return p11_attrs_build (attrs, &value, &application, &object_id, NULL);
+}
+
+const static builder_schema data_schema = {
+ NORMAL_BUILD,
+ { COMMON_ATTRS,
+ { CKA_VALUE, CREATE | MODIFY | WANT },
+ { CKA_APPLICATION, CREATE | MODIFY | WANT },
+ { CKA_OBJECT_ID, CREATE | MODIFY | WANT },
+ { CKA_INVALID },
+ }, data_populate,
+};
+
+const static builder_schema trust_schema = {
+ GENERATED_CLASS,
+ { COMMON_ATTRS,
+ { CKA_CERT_SHA1_HASH, CREATE },
+ { CKA_CERT_MD5_HASH, CREATE },
+ { CKA_ISSUER, CREATE },
+ { CKA_SUBJECT, CREATE },
+ { CKA_SERIAL_NUMBER, CREATE },
+ { CKA_TRUST_SERVER_AUTH, CREATE },
+ { CKA_TRUST_CLIENT_AUTH, CREATE },
+ { CKA_TRUST_EMAIL_PROTECTION, CREATE },
+ { CKA_TRUST_CODE_SIGNING, CREATE },
+ { CKA_TRUST_IPSEC_END_SYSTEM, CREATE },
+ { CKA_TRUST_IPSEC_TUNNEL, CREATE },
+ { CKA_TRUST_IPSEC_USER, CREATE },
+ { CKA_TRUST_TIME_STAMPING, CREATE },
+ { CKA_TRUST_DIGITAL_SIGNATURE, CREATE },
+ { CKA_TRUST_NON_REPUDIATION, CREATE },
+ { CKA_TRUST_KEY_ENCIPHERMENT, CREATE },
+ { CKA_TRUST_DATA_ENCIPHERMENT, CREATE },
+ { CKA_TRUST_KEY_AGREEMENT, CREATE },
+ { CKA_TRUST_KEY_CERT_SIGN, CREATE },
+ { CKA_TRUST_CRL_SIGN, CREATE },
+ { CKA_TRUST_STEP_UP_APPROVED, CREATE },
+ { CKA_ID, CREATE },
+ { CKA_INVALID },
+ }, common_populate
+};
+
+const static builder_schema assertion_schema = {
+ GENERATED_CLASS,
+ { COMMON_ATTRS,
+ { CKA_X_PURPOSE, REQUIRE | CREATE },
+ { CKA_VALUE, CREATE },
+ { CKA_X_ASSERTION_TYPE, REQUIRE | CREATE },
+ { CKA_ISSUER, CREATE },
+ { CKA_SERIAL_NUMBER, CREATE },
+ { CKA_X_PEER, CREATE },
+ { CKA_ID, CREATE },
+ { CKA_INVALID },
+ }, common_populate
+};
+
+const static builder_schema builtin_schema = {
+ GENERATED_CLASS,
+ { COMMON_ATTRS,
+ { CKA_INVALID },
+ }, common_populate
+};
+
+static void
+attrs_filter_if_unchanged (CK_ATTRIBUTE *attrs,
+ CK_ATTRIBUTE *merge)
+{
+ CK_ATTRIBUTE *attr;
+ int in, out;
+
+ assert (attrs != NULL);
+ assert (merge != NULL);
+
+ for (in = 0, out = 0; !p11_attrs_is_empty (merge + in); in++) {
+ attr = p11_attrs_find (attrs, merge[in].type);
+ if (attr && p11_attr_equal (attr, merge + in)) {
+ free (merge[in].pValue);
+ merge[in].pValue = NULL;
+ merge[in].ulValueLen = 0;
+ } else {
+ if (in != out)
+ memcpy (merge + out, merge + in, sizeof (CK_ATTRIBUTE));
+ out++;
+ }
+ }
+
+ merge[out].type = CKA_INVALID;
+ assert (p11_attrs_is_empty (merge + out));
+}
+
+static const char *
+value_name (const p11_constant *info,
+ CK_ATTRIBUTE_TYPE type)
+{
+ const char *name = p11_constant_name (info, type);
+ return name ? name : "unknown";
+}
+
+static const char *
+type_name (CK_ATTRIBUTE_TYPE type)
+{
+ return value_name (p11_constant_types, type);
+}
+
+static CK_RV
+build_for_schema (p11_builder *builder,
+ p11_index *index,
+ const builder_schema *schema,
+ CK_ATTRIBUTE **object,
+ CK_ATTRIBUTE *merge)
+{
+ CK_ATTRIBUTE *extra;
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL modifiable;
+ bool modifying;
+ bool creating;
+ bool populate;
+ bool loading;
+ bool found;
+ int flags;
+ int i, j;
+
+ attrs = *object;
+ populate = false;
+
+ /* Signifies that data is being loaded */
+ loading = p11_index_in_batch (index);
+
+ /* Signifies that this is being created by a caller, instead of loaded */
+ creating = (attrs == NULL && !loading);
+
+ /* Item is being modified by a caller */
+ modifying = (attrs != NULL && !loading);
+
+ /* This item may not be modifiable */
+ if (modifying) {
+ if (!p11_attrs_find_bool (attrs, CKA_MODIFIABLE, &modifiable) || !modifiable) {
+ p11_message ("the object is not modifiable");
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ }
+
+ if (attrs != NULL)
+ attrs_filter_if_unchanged (attrs, merge);
+
+ if (creating) {
+ if (schema->build_flags & GENERATED_CLASS) {
+ p11_message ("objects of this type cannot be created");
+ return CKR_TEMPLATE_INCONSISTENT;
+ }
+ }
+
+ for (i = 0; merge[i].type != CKA_INVALID; i++) {
+ found = false;
+ for (j = 0; schema->attrs[j].type != CKA_INVALID; j++) {
+ if (schema->attrs[j].type != merge[i].type)
+ continue;
+
+ flags = schema->attrs[j].flags;
+ if (creating && !(flags & CREATE)) {
+ p11_message ("the %s attribute cannot be set",
+ type_name (schema->attrs[j].type));
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ if (modifying && !(flags & MODIFY)) {
+ p11_message ("the %s attribute cannot be changed",
+ type_name (schema->attrs[j].type));
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ p11_message ("the %s attribute is not valid for the object",
+ type_name (merge[i].type));
+ return CKR_TEMPLATE_INCONSISTENT;
+ }
+ }
+
+ if (attrs == NULL) {
+ for (j = 0; schema->attrs[j].type != CKA_INVALID; j++) {
+ flags = schema->attrs[j].flags;
+ found = false;
+
+ if ((flags & REQUIRE) || (flags & WANT)) {
+ for (i = 0; merge[i].type != CKA_INVALID; i++) {
+ if (schema->attrs[j].type == merge[i].type) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ if (flags & REQUIRE) {
+ p11_message ("missing the %s attribute",
+ type_name (schema->attrs[j].type));
+ return CKR_TEMPLATE_INCOMPLETE;
+ } else if (flags & WANT) {
+ populate = true;
+ }
+ }
+ }
+ }
+
+ if (populate && schema->populate) {
+ extra = schema->populate (builder, index, merge);
+ if (extra != NULL)
+ merge = p11_attrs_merge (merge, extra, false);
+ }
+
+ /*
+ * TODO: Validate the result, before committing to the change. We can
+ * do this by doing a shallow copy of merge + attrs and then validating
+ * that. Although there may be duplicate attributets, the validation
+ * code will see the new ones because they're first.
+ */
+
+ *object = p11_attrs_merge (attrs, merge, true);
+ return_val_if_fail (*object != NULL, CKR_HOST_MEMORY);
+
+ return CKR_OK;
+}
+
+CK_RV
+p11_builder_build (void *bilder,
+ p11_index *index,
+ CK_ATTRIBUTE **object,
+ CK_ATTRIBUTE *merge)
+{
+ p11_builder *builder = bilder;
+ CK_ATTRIBUTE *attrs;
+ CK_OBJECT_CLASS klass;
+ CK_CERTIFICATE_TYPE type;
+ CK_BBOOL token;
+
+ return_val_if_fail (builder != NULL, CKR_GENERAL_ERROR);
+ return_val_if_fail (index != NULL, CKR_GENERAL_ERROR);
+ return_val_if_fail (merge != NULL, CKR_GENERAL_ERROR);
+
+ attrs = *object;
+
+ if (!p11_attrs_find_ulong (attrs ? attrs : merge, CKA_CLASS, &klass)) {
+ p11_message ("no CKA_CLASS attribute found");
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ if (!attrs && p11_attrs_find_bool (merge, CKA_TOKEN, &token)) {
+ if (token != ((builder->flags & P11_BUILDER_FLAG_TOKEN) ? CK_TRUE : CK_FALSE)) {
+ p11_message ("cannot create a %s object", token ? "token" : "non-token");
+ return CKR_TEMPLATE_INCONSISTENT;
+ }
+ }
+
+ switch (klass) {
+ case CKO_CERTIFICATE:
+ if (!p11_attrs_find_ulong (attrs ? attrs : merge, CKA_CERTIFICATE_TYPE, &type)) {
+ p11_message ("missing %s on object", type_name (CKA_CERTIFICATE_TYPE));
+ return CKR_TEMPLATE_INCOMPLETE;
+ } else if (type == CKC_X_509) {
+ return build_for_schema (builder, index, &certificate_schema, object, merge);
+ } else {
+ p11_message ("%s unsupported %s", value_name (p11_constant_certs, type),
+ type_name (CKA_CERTIFICATE_TYPE));
+ return CKR_TEMPLATE_INCONSISTENT;
+ }
+
+ case CKO_X_CERTIFICATE_EXTENSION:
+ return build_for_schema (builder, index, &extension_schema, object, merge);
+
+ case CKO_DATA:
+ return build_for_schema (builder, index, &data_schema, object, merge);
+
+ case CKO_NSS_TRUST:
+ return build_for_schema (builder, index, &trust_schema, object, merge);
+
+ case CKO_NSS_BUILTIN_ROOT_LIST:
+ return build_for_schema (builder, index, &builtin_schema, object, merge);
+
+ case CKO_X_TRUST_ASSERTION:
+ return build_for_schema (builder, index, &assertion_schema, object, merge);
+
+ default:
+ p11_message ("%s unsupported object class",
+ value_name (p11_constant_classes, klass));
+ return CKR_TEMPLATE_INCONSISTENT;
+ }
+}
+
+void
+p11_builder_free (p11_builder *builder)
+{
+ return_if_fail (builder != NULL);
+
+ p11_asn1_cache_free (builder->asn1_cache);
+ free (builder);
+}
+
+p11_asn1_cache *
+p11_builder_get_cache (p11_builder *builder)
+{
+ return_val_if_fail (builder != NULL, NULL);
+ return builder->asn1_cache;
+}
+
+static CK_ATTRIBUTE *
+build_trust_object_ku (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert,
+ CK_ATTRIBUTE *object,
+ CK_TRUST present)
+{
+ unsigned char *data = NULL;
+ unsigned int ku = 0;
+ size_t length;
+ 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)];
+
+ defawlt = present;
+
+ /* If blacklisted, don't even bother looking at extensions */
+ if (present != CKT_NSS_NOT_TRUSTED)
+ data = lookup_extension (builder, index, cert, P11_OID_KEY_USAGE, &length);
+
+ if (data) {
+ /*
+ * If the certificate extension was missing, then *all* key
+ * usages are to be set. If the extension was invalid, then
+ * fail safe to none of the key usages.
+ */
+ defawlt = CKT_NSS_TRUST_UNKNOWN;
+
+ if (!p11_x509_parse_key_usage (builder->asn1_defs, data, length, &ku))
+ 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 bool
+strv_to_dict (const char **array,
+ p11_dict **dict)
+{
+ int i;
+
+ if (!array) {
+ *dict = NULL;
+ return true;
+ }
+
+ *dict = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, NULL, NULL);
+ return_val_if_fail (*dict != NULL, false);
+
+ for (i = 0; array[i] != NULL; i++) {
+ if (!p11_dict_set (*dict, (void *)array[i], (void *)array[i]))
+ return_val_if_reached (false);
+ }
+
+ return true;
+}
+
+static CK_ATTRIBUTE *
+build_trust_object_eku (CK_ATTRIBUTE *object,
+ CK_TRUST allow,
+ const char **purposes,
+ const char **rejects)
+{
+ p11_dict *dict_purp;
+ p11_dict *dict_rej;
+ CK_TRUST neutral;
+ CK_TRUST disallow;
+ CK_ULONG i;
+
+ struct {
+ CK_ATTRIBUTE_TYPE type;
+ const char *oid;
+ } eku_attribute_map[] = {
+ { CKA_TRUST_SERVER_AUTH, P11_OID_SERVER_AUTH_STR },
+ { CKA_TRUST_CLIENT_AUTH, P11_OID_CLIENT_AUTH_STR },
+ { CKA_TRUST_CODE_SIGNING, P11_OID_CODE_SIGNING_STR },
+ { CKA_TRUST_EMAIL_PROTECTION, P11_OID_EMAIL_PROTECTION_STR },
+ { CKA_TRUST_IPSEC_END_SYSTEM, P11_OID_IPSEC_END_SYSTEM_STR },
+ { CKA_TRUST_IPSEC_TUNNEL, P11_OID_IPSEC_TUNNEL_STR },
+ { CKA_TRUST_IPSEC_USER, P11_OID_IPSEC_USER_STR },
+ { CKA_TRUST_TIME_STAMPING, P11_OID_TIME_STAMPING_STR },
+ { CKA_INVALID },
+ };
+
+ CK_ATTRIBUTE attrs[sizeof (eku_attribute_map)];
+
+ if (!strv_to_dict (purposes, &dict_purp) ||
+ !strv_to_dict (rejects, &dict_rej))
+ return_val_if_reached (NULL);
+
+ /* The neutral value is set if an purpose is not present */
+ if (allow == CKT_NSS_NOT_TRUSTED)
+ neutral = CKT_NSS_NOT_TRUSTED;
+
+ /* If anything explicitly set, then neutral is unknown */
+ else if (purposes || rejects)
+ neutral = CKT_NSS_TRUST_UNKNOWN;
+
+ /* Otherwise neutral will allow any purpose */
+ else
+ neutral = allow;
+
+ /* The value set if a purpose is explictly rejected */
+ disallow = CKT_NSS_NOT_TRUSTED;
+
+ for (i = 0; eku_attribute_map[i].type != CKA_INVALID; i++) {
+ attrs[i].type = eku_attribute_map[i].type;
+ if (dict_rej && p11_dict_get (dict_rej, eku_attribute_map[i].oid)) {
+ attrs[i].pValue = &disallow;
+ attrs[i].ulValueLen = sizeof (disallow);
+ } else if (dict_purp && p11_dict_get (dict_purp, eku_attribute_map[i].oid)) {
+ attrs[i].pValue = &allow;
+ attrs[i].ulValueLen = sizeof (allow);
+ } else {
+ attrs[i].pValue = &neutral;
+ attrs[i].ulValueLen = sizeof (neutral);
+ }
+ }
+
+ p11_dict_free (dict_purp);
+ p11_dict_free (dict_rej);
+
+ return p11_attrs_buildn (object, attrs, i);
+}
+
+static void
+replace_nss_trust_object (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert,
+ CK_BBOOL trust,
+ CK_BBOOL distrust,
+ CK_BBOOL authority,
+ const char **purposes,
+ const char **rejects)
+{
+ CK_ATTRIBUTE *attrs = NULL;
+ CK_TRUST allow;
+ CK_RV rv;
+
+ CK_OBJECT_CLASS klassv = CKO_NSS_TRUST;
+ CK_BYTE sha1v[P11_CHECKSUM_SHA1_LENGTH];
+ CK_BYTE md5v[P11_CHECKSUM_MD5_LENGTH];
+ CK_BBOOL generated = CK_FALSE;
+ CK_BBOOL falsev = CK_FALSE;
+ CK_BBOOL truev = CK_TRUE;
+
+ CK_ATTRIBUTE klass = { CKA_CLASS, &klassv, sizeof (klassv) };
+ CK_ATTRIBUTE modifiable = { CKA_MODIFIABLE, &falsev, sizeof (falsev) };
+ CK_ATTRIBUTE autogen = { CKA_X_GENERATED, &truev, sizeof (truev) };
+ CK_ATTRIBUTE invalid = { CKA_INVALID, };
+
+ CK_ATTRIBUTE md5_hash = { CKA_CERT_MD5_HASH, md5v, sizeof (md5v) };
+ CK_ATTRIBUTE sha1_hash = { CKA_CERT_SHA1_HASH, sha1v, sizeof (sha1v) };
+
+ CK_ATTRIBUTE step_up_approved = { CKA_TRUST_STEP_UP_APPROVED, &falsev, sizeof (falsev) };
+
+ CK_ATTRIBUTE_PTR label;
+ CK_ATTRIBUTE_PTR id;
+ CK_ATTRIBUTE_PTR der;
+ CK_ATTRIBUTE_PTR subject;
+ CK_ATTRIBUTE_PTR issuer;
+ CK_ATTRIBUTE_PTR serial_number;
+
+ CK_ATTRIBUTE match[] = {
+ { CKA_CERT_SHA1_HASH, sha1v, sizeof (sha1v) },
+ { CKA_CLASS, &klassv, sizeof (klassv) },
+ { CKA_X_GENERATED, &generated, sizeof (generated) },
+ { CKA_INVALID }
+ };
+
+ /* Setup the hashes of the DER certificate value */
+ der = p11_attrs_find (cert, CKA_VALUE);
+ return_if_fail (der != NULL);
+ p11_checksum_md5 (md5v, der->pValue, der->ulValueLen, NULL);
+ p11_checksum_sha1 (sha1v, der->pValue, der->ulValueLen, NULL);
+
+ /* If there is a non-auto-generated NSS trust object, then step away */
+ generated = CK_FALSE;
+ if (p11_index_find (index, match))
+ return;
+
+ /* Copy all of the following attributes from certificate */
+ id = p11_attrs_find (cert, CKA_ID);
+ return_if_fail (id != NULL);
+ subject = p11_attrs_find (cert, CKA_SUBJECT);
+ if (subject == NULL)
+ subject = &invalid;
+ issuer = p11_attrs_find (cert, CKA_ISSUER);
+ if (issuer == NULL)
+ issuer = &invalid;
+ serial_number = p11_attrs_find (cert, CKA_SERIAL_NUMBER);
+ if (serial_number == NULL)
+ serial_number = &invalid;
+
+ /* Try to use the same label */
+ label = p11_attrs_find (cert, CKA_LABEL);
+ if (label == NULL)
+ label = &invalid;
+
+ attrs = p11_attrs_build (NULL, &klass, &modifiable, id, label,
+ subject, issuer, serial_number, &md5_hash, &sha1_hash,
+ &step_up_approved, &autogen, NULL);
+ return_if_fail (attrs != NULL);
+
+ /* Calculate the default allow trust */
+ if (distrust)
+ allow = CKT_NSS_NOT_TRUSTED;
+ else if (trust && authority)
+ allow = CKT_NSS_TRUSTED_DELEGATOR;
+ else if (trust)
+ allow = CKT_NSS_TRUSTED;
+ else
+ allow = CKT_NSS_TRUST_UNKNOWN;
+
+ attrs = build_trust_object_ku (builder, index, cert, attrs, allow);
+ return_if_fail (attrs != NULL);
+
+ attrs = build_trust_object_eku (attrs, allow, purposes, rejects);
+ return_if_fail (attrs != NULL);
+
+ /* Replace related generated objects with this new one */
+ generated = CK_TRUE;
+ rv = p11_index_replace (index, match, CKA_CERT_MD5_HASH, attrs);
+ return_if_fail (rv == CKR_OK);
+}
+
+static void
+build_assertions (p11_array *array,
+ CK_ATTRIBUTE *cert,
+ CK_X_ASSERTION_TYPE type,
+ const char **oids)
+{
+ CK_OBJECT_CLASS assertion = CKO_X_TRUST_ASSERTION;
+ CK_BBOOL truev = CK_TRUE;
+ CK_BBOOL falsev = CK_FALSE;
+
+ CK_ATTRIBUTE klass = { CKA_CLASS, &assertion, sizeof (assertion) };
+ CK_ATTRIBUTE private = { CKA_PRIVATE, &falsev, sizeof (falsev) };
+ CK_ATTRIBUTE modifiable = { CKA_MODIFIABLE, &falsev, sizeof (falsev) };
+ CK_ATTRIBUTE assertion_type = { CKA_X_ASSERTION_TYPE, &type, sizeof (type) };
+ CK_ATTRIBUTE autogen = { CKA_X_GENERATED, &truev, sizeof (truev) };
+ CK_ATTRIBUTE purpose = { CKA_X_PURPOSE, };
+ CK_ATTRIBUTE invalid = { CKA_INVALID, };
+
+ CK_ATTRIBUTE *issuer;
+ CK_ATTRIBUTE *serial;
+ CK_ATTRIBUTE *value;
+ CK_ATTRIBUTE *label;
+ CK_ATTRIBUTE *id;
+ CK_ATTRIBUTE *attrs;
+ int i;
+
+ label = p11_attrs_find (cert, CKA_LABEL);
+ if (label == NULL)
+ label = &invalid;
+
+ id = p11_attrs_find (cert, CKA_ID);
+ issuer = p11_attrs_find (cert, CKA_ISSUER);
+ serial = p11_attrs_find (cert, CKA_SERIAL_NUMBER);
+ value = p11_attrs_find (cert, CKA_VALUE);
+
+ return_if_fail (id != NULL && issuer != NULL && serial != NULL && value != NULL);
+
+ for (i = 0; oids[i] != NULL; i++) {
+ purpose.pValue = (void *)oids[i];
+ purpose.ulValueLen = strlen (oids[i]);
+
+ attrs = p11_attrs_build (NULL, &klass, &private, &modifiable,
+ id, label, &assertion_type, &purpose,
+ issuer, serial, value, &autogen, NULL);
+ return_if_fail (attrs != NULL);
+
+ if (!p11_array_push (array, attrs))
+ return_if_reached ();
+ }
+}
+
+static void
+build_trust_assertions (p11_array *built,
+ CK_ATTRIBUTE *cert,
+ CK_BBOOL trust,
+ CK_BBOOL distrust,
+ CK_BBOOL authority,
+ const char **purposes,
+ const char **rejects)
+{
+ const char *all_purposes[] = {
+ P11_OID_SERVER_AUTH_STR,
+ P11_OID_CLIENT_AUTH_STR,
+ P11_OID_CODE_SIGNING_STR,
+ P11_OID_EMAIL_PROTECTION_STR,
+ P11_OID_IPSEC_END_SYSTEM_STR,
+ P11_OID_IPSEC_TUNNEL_STR,
+ P11_OID_IPSEC_USER_STR,
+ P11_OID_TIME_STAMPING_STR,
+ NULL,
+ };
+
+ /* Build assertions for anything that's explicitly rejected */
+ if (rejects) {
+ build_assertions (built, cert, CKT_X_DISTRUSTED_CERTIFICATE, rejects);
+ }
+
+ if (distrust) {
+ /*
+ * Trust assertions are defficient in that they don't blacklist a certificate
+ * for any purposes. So we just have to go wild and write out a bunch of
+ * assertions for all our known purposes.
+ */
+ build_assertions (built, cert, CKT_X_DISTRUSTED_CERTIFICATE, all_purposes);
+ }
+
+ /*
+ * TODO: Build pinned certificate assertions. That is, trusted
+ * certificates where not an authority.
+ */
+
+ if (trust && authority) {
+ if (purposes) {
+ /* If purposes explicitly set, then anchor for those purposes */
+ build_assertions (built, cert, CKT_X_ANCHORED_CERTIFICATE, purposes);
+ } else {
+ /* If purposes not-explicitly set, then anchor for all known */
+ build_assertions (built, cert, CKT_X_ANCHORED_CERTIFICATE, all_purposes);
+ }
+ }
+}
+
+static void
+replace_trust_assertions (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert,
+ CK_BBOOL trust,
+ CK_BBOOL distrust,
+ CK_BBOOL authority,
+ const char **purposes,
+ const char **rejects)
+{
+ CK_OBJECT_CLASS assertion = CKO_X_TRUST_ASSERTION;
+ CK_BBOOL generated = CK_FALSE;
+ CK_ATTRIBUTE *value;
+ p11_array *built;
+ CK_RV rv;
+
+ CK_ATTRIBUTE match[] = {
+ { CKA_VALUE, },
+ { CKA_CLASS, &assertion, sizeof (assertion) },
+ { CKA_X_GENERATED, &generated, sizeof (generated) },
+ { CKA_INVALID }
+ };
+
+ value = p11_attrs_find (cert, CKA_VALUE);
+ return_if_fail (value != NULL);
+
+ built = p11_array_new (NULL);
+ build_trust_assertions (built, cert, trust, distrust, authority, purposes, rejects);
+
+ generated = CK_TRUE;
+ match[0].pValue = value->pValue;
+ match[0].ulValueLen = value->ulValueLen;
+ rv = p11_index_replace_all (index, match, CKA_X_PURPOSE, built);
+ return_if_fail (rv == CKR_OK);
+
+ p11_array_free (built);
+}
+
+static void
+remove_trust_and_assertions (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *attrs)
+{
+ CK_BBOOL truev = CK_TRUE;
+ CK_ATTRIBUTE *id;
+ p11_array *array;
+ CK_RV rv;
+
+ CK_ATTRIBUTE match[] = {
+ { CKA_ID, },
+ { CKA_X_GENERATED, &truev, sizeof (truev) },
+ { CKA_INVALID }
+ };
+
+ id = p11_attrs_find (attrs, CKA_ID);
+ return_if_fail (id != NULL);
+
+ /* An empty array of replacements */
+ array = p11_array_new (NULL);
+
+ /* Remove all related NSS trust objects */
+ match[0].pValue = id->pValue;
+ match[0].ulValueLen = id->ulValueLen;
+ rv = p11_index_replace_all (index, match, CKA_INVALID, array);
+ return_if_fail (rv == CKR_OK);
+
+ p11_array_free (array);
+}
+
+static void
+replace_trust_and_assertions (p11_builder *builder,
+ p11_index *index,
+ CK_ATTRIBUTE *cert)
+{
+ CK_BBOOL trust = CK_FALSE;
+ CK_BBOOL distrust = CK_FALSE;
+ CK_BBOOL authority = CK_FALSE;
+ p11_array *purposes = NULL;
+ p11_array *rejects = NULL;
+ const char **purposev;
+ const char **rejectv;
+ CK_ULONG category;
+ unsigned char *ext;
+ size_t ext_len;
+
+ /*
+ * We look up all this information in advance, since it's used
+ * by the various adapter objects, and we don't have to parse
+ * it multiple times.
+ */
+
+ if (!p11_attrs_find_bool (cert, CKA_TRUSTED, &trust))
+ trust = CK_FALSE;
+ if (!p11_attrs_find_bool (cert, CKA_X_DISTRUSTED, &distrust))
+ distrust = CK_FALSE;
+ if (p11_attrs_find_ulong (cert, CKA_CERTIFICATE_CATEGORY, &category) && category == 2)
+ authority = CK_TRUE;
+
+ if (!distrust) {
+ ext = lookup_extension (builder, index, cert, P11_OID_EXTENDED_KEY_USAGE, &ext_len);
+ if (ext != NULL) {
+ purposes = p11_x509_parse_extended_key_usage (builder->asn1_defs, ext, ext_len);
+ if (purposes == NULL)
+ p11_message ("invalid extended key usage certificate extension");
+ free (ext);
+ }
+
+ ext = lookup_extension (builder, index, cert, P11_OID_OPENSSL_REJECT, &ext_len);
+ if (ext != NULL) {
+ rejects = p11_x509_parse_extended_key_usage (builder->asn1_defs, ext, ext_len);
+ if (rejects == NULL)
+ p11_message ("invalid reject key usage certificate extension");
+ free (ext);
+ }
+ }
+
+ /* null-terminate these arrays and use as strv's */
+ purposev = rejectv = NULL;
+ if (rejects) {
+ if (!p11_array_push (rejects, NULL))
+ return_if_reached ();
+ rejectv = (const char **)rejects->elem;
+ }
+ if (purposes) {
+ if (!p11_array_push (purposes, NULL))
+ return_if_reached ();
+ purposev = (const char **)purposes->elem;
+ }
+
+ replace_nss_trust_object (builder, index, cert, trust, distrust,
+ authority, purposev, rejectv);
+ replace_trust_assertions (builder, index, cert, trust, distrust,
+ authority, purposev, rejectv);
+
+ p11_array_free (purposes);
+ p11_array_free (rejects);
+}
+
+static void
+replace_compat_for_cert (p11_builder *builder,
+ p11_index *index,
+ CK_OBJECT_HANDLE handle,
+ CK_ATTRIBUTE *attrs)
+{
+ static CK_OBJECT_CLASS certificate = CKO_CERTIFICATE;
+ static CK_CERTIFICATE_TYPE x509 = CKC_X_509;
+
+ CK_ATTRIBUTE *value;
+ CK_ATTRIBUTE *id;
+
+ CK_ATTRIBUTE match[] = {
+ { CKA_VALUE, },
+ { CKA_CLASS, &certificate, sizeof (certificate) },
+ { CKA_CERTIFICATE_TYPE, &x509, sizeof (x509) },
+ { CKA_INVALID }
+ };
+
+ value = p11_attrs_find (attrs, CKA_VALUE);
+ id = p11_attrs_find (attrs, CKA_ID);
+ if (value == NULL || id == NULL)
+ return;
+
+ /*
+ * If this certificate is going away, then find duplicate. In this
+ * case all the trust assertions are recalculated with this new
+ * certificate in mind.
+ */
+ if (handle == 0) {
+ match[0].pValue = value->pValue;
+ match[0].ulValueLen = value->ulValueLen;
+ handle = p11_index_find (index, match);
+ if (handle != 0)
+ attrs = p11_index_lookup (index, handle);
+ }
+
+ if (handle == 0)
+ remove_trust_and_assertions (builder, index, attrs);
+ else
+ replace_trust_and_assertions (builder, index, attrs);
+}
+
+static void
+replace_compat_for_ext (p11_builder *builder,
+ p11_index *index,
+ CK_OBJECT_HANDLE handle,
+ CK_ATTRIBUTE *attrs)
+{
+
+ CK_OBJECT_HANDLE *handles;
+ CK_ATTRIBUTE *id;
+ int i;
+
+ id = p11_attrs_find (attrs, CKA_ID);
+ if (id == NULL)
+ return;
+
+ handles = lookup_related (index, CKO_CERTIFICATE, id);
+ for (i = 0; handles && handles[i] != 0; i++) {
+ attrs = p11_index_lookup (index, handles[i]);
+ replace_trust_and_assertions (builder, index, attrs);
+ }
+ free (handles);
+}
+
+static void
+update_related_category (p11_builder *builder,
+ p11_index *index,
+ CK_OBJECT_HANDLE handle,
+ CK_ATTRIBUTE *attrs)
+{
+ CK_OBJECT_HANDLE *handles;
+ CK_ULONG categoryv = 0UL;
+ CK_ATTRIBUTE *update;
+ CK_ATTRIBUTE *cert;
+ CK_ATTRIBUTE *id;
+ CK_RV rv;
+ int i;
+
+ CK_ATTRIBUTE category[] = {
+ { CKA_CERTIFICATE_CATEGORY, &categoryv, sizeof (categoryv) },
+ { CKA_INVALID, },
+ };
+
+ id = p11_attrs_find (attrs, CKA_ID);
+ if (id == NULL)
+ return;
+
+ /* Find all other objects with this handle */
+ handles = lookup_related (index, CKO_CERTIFICATE, id);
+
+ for (i = 0; handles && handles[i] != 0; i++) {
+ cert = p11_index_lookup (index, handle);
+
+ if (calc_certificate_category (builder, index, cert, &categoryv)) {
+ update = p11_attrs_build (NULL, &category, NULL);
+ rv = p11_index_update (index, handles[i], update);
+ return_if_fail (rv == CKR_OK);
+ }
+ }
+
+ free (handles);
+}
+
+void
+p11_builder_changed (void *bilder,
+ p11_index *index,
+ CK_OBJECT_HANDLE handle,
+ CK_ATTRIBUTE *attrs)
+{
+ static CK_OBJECT_CLASS certificate = CKO_CERTIFICATE;
+ static CK_OBJECT_CLASS extension = CKO_X_CERTIFICATE_EXTENSION;
+ static CK_CERTIFICATE_TYPE x509 = CKC_X_509;
+
+ static CK_ATTRIBUTE match_cert[] = {
+ { CKA_CLASS, &certificate, sizeof (certificate) },
+ { CKA_CERTIFICATE_TYPE, &x509, sizeof (x509) },
+ { CKA_INVALID }
+ };
+
+ static CK_ATTRIBUTE match_eku[] = {
+ { CKA_CLASS, &extension, sizeof (extension) },
+ { CKA_OBJECT_ID, (void *)P11_OID_EXTENDED_KEY_USAGE,
+ sizeof (P11_OID_EXTENDED_KEY_USAGE) },
+ { CKA_INVALID }
+ };
+
+ static CK_ATTRIBUTE match_ku[] = {
+ { CKA_CLASS, &extension, sizeof (extension) },
+ { CKA_OBJECT_ID, (void *)P11_OID_KEY_USAGE,
+ sizeof (P11_OID_KEY_USAGE) },
+ { CKA_INVALID }
+ };
+
+ static CK_ATTRIBUTE match_bc[] = {
+ { CKA_CLASS, &extension, sizeof (extension) },
+ { CKA_OBJECT_ID, (void *)P11_OID_BASIC_CONSTRAINTS,
+ sizeof (P11_OID_BASIC_CONSTRAINTS) },
+ { CKA_INVALID }
+ };
+
+ p11_builder *builder = bilder;
+
+ return_if_fail (builder != NULL);
+ return_if_fail (index != NULL);
+ return_if_fail (attrs != NULL);
+
+ /*
+ * Treat these operations as loading, not modifying/creating, so we get
+ * around many of the rules that govern object creation
+ */
+ p11_index_batch (index);
+
+ /* A certificate */
+ if (p11_attrs_match (attrs, match_cert)) {
+ replace_compat_for_cert (builder, index, handle, attrs);
+
+ /* An ExtendedKeyUsage extension */
+ } else if (p11_attrs_match (attrs, match_eku) ||
+ p11_attrs_match (attrs, match_ku)) {
+ replace_compat_for_ext (builder, index, handle, attrs);
+
+ /* A BasicConstraints extension */
+ } else if (p11_attrs_match (attrs, match_bc)) {
+ update_related_category (builder, index, handle, attrs);
+ }
+
+ p11_index_finish (index);
+}