summaryrefslogtreecommitdiffstats
path: root/crypto/heimdal/lib/hx509/cert.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/heimdal/lib/hx509/cert.c')
-rw-r--r--crypto/heimdal/lib/hx509/cert.c3108
1 files changed, 0 insertions, 3108 deletions
diff --git a/crypto/heimdal/lib/hx509/cert.c b/crypto/heimdal/lib/hx509/cert.c
deleted file mode 100644
index 1520e23..0000000
--- a/crypto/heimdal/lib/hx509/cert.c
+++ /dev/null
@@ -1,3108 +0,0 @@
-/*
- * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. 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.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
- */
-
-#include "hx_locl.h"
-RCSID("$Id: cert.c 22450 2008-01-15 19:39:14Z lha $");
-#include "crypto-headers.h"
-#include <rtbl.h>
-
-/**
- * @page page_cert The basic certificate
- *
- * The basic hx509 cerificate object in hx509 is hx509_cert. The
- * hx509_cert object is representing one X509/PKIX certificate and
- * associated attributes; like private key, friendly name, etc.
- *
- * A hx509_cert object is usully found via the keyset interfaces (@ref
- * page_keyset), but its also possible to create a certificate
- * directly from a parsed object with hx509_cert_init() and
- * hx509_cert_init_data().
- *
- * See the library functions here: @ref hx509_cert
- */
-
-struct hx509_verify_ctx_data {
- hx509_certs trust_anchors;
- int flags;
-#define HX509_VERIFY_CTX_F_TIME_SET 1
-#define HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE 2
-#define HX509_VERIFY_CTX_F_REQUIRE_RFC3280 4
-#define HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS 8
-#define HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS 16
- time_t time_now;
- unsigned int max_depth;
-#define HX509_VERIFY_MAX_DEPTH 30
- hx509_revoke_ctx revoke_ctx;
-};
-
-#define REQUIRE_RFC3280(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_REQUIRE_RFC3280)
-#define CHECK_TA(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS)
-#define ALLOW_DEF_TA(ctx) (((ctx)->flags & HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS) == 0)
-
-struct _hx509_cert_attrs {
- size_t len;
- hx509_cert_attribute *val;
-};
-
-struct hx509_cert_data {
- unsigned int ref;
- char *friendlyname;
- Certificate *data;
- hx509_private_key private_key;
- struct _hx509_cert_attrs attrs;
- hx509_name basename;
- _hx509_cert_release_func release;
- void *ctx;
-};
-
-typedef struct hx509_name_constraints {
- NameConstraints *val;
- size_t len;
-} hx509_name_constraints;
-
-#define GeneralSubtrees_SET(g,var) \
- (g)->len = (var)->len, (g)->val = (var)->val;
-
-/**
- * Creates a hx509 context that most functions in the library
- * uses. The context is only allowed to be used by one thread at each
- * moment. Free the context with hx509_context_free().
- *
- * @param context Returns a pointer to new hx509 context.
- *
- * @return Returns an hx509 error code.
- *
- * @ingroup hx509
- */
-
-int
-hx509_context_init(hx509_context *context)
-{
- *context = calloc(1, sizeof(**context));
- if (*context == NULL)
- return ENOMEM;
-
- _hx509_ks_null_register(*context);
- _hx509_ks_mem_register(*context);
- _hx509_ks_file_register(*context);
- _hx509_ks_pkcs12_register(*context);
- _hx509_ks_pkcs11_register(*context);
- _hx509_ks_dir_register(*context);
- _hx509_ks_keychain_register(*context);
-
- ENGINE_add_conf_module();
- OpenSSL_add_all_algorithms();
-
- (*context)->ocsp_time_diff = HX509_DEFAULT_OCSP_TIME_DIFF;
-
- initialize_hx_error_table_r(&(*context)->et_list);
- initialize_asn1_error_table_r(&(*context)->et_list);
-
-#ifdef HX509_DEFAULT_ANCHORS
- (void)hx509_certs_init(*context, HX509_DEFAULT_ANCHORS, 0,
- NULL, &(*context)->default_trust_anchors);
-#endif
-
- return 0;
-}
-
-/**
- * Selects if the hx509_revoke_verify() function is going to require
- * the existans of a revokation method (OSCP, CRL) or not. Note that
- * hx509_verify_path(), hx509_cms_verify_signed(), and other function
- * call hx509_revoke_verify().
- *
- * @param context hx509 context to change the flag for.
- * @param flag zero, revokation method required, non zero missing
- * revokation method ok
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_context_set_missing_revoke(hx509_context context, int flag)
-{
- if (flag)
- context->flags |= HX509_CTX_VERIFY_MISSING_OK;
- else
- context->flags &= ~HX509_CTX_VERIFY_MISSING_OK;
-}
-
-/**
- * Free the context allocated by hx509_context_init().
- *
- * @param context context to be freed.
- *
- * @ingroup hx509
- */
-
-void
-hx509_context_free(hx509_context *context)
-{
- hx509_clear_error_string(*context);
- if ((*context)->ks_ops) {
- free((*context)->ks_ops);
- (*context)->ks_ops = NULL;
- }
- (*context)->ks_num_ops = 0;
- free_error_table ((*context)->et_list);
- if ((*context)->querystat)
- free((*context)->querystat);
- memset(*context, 0, sizeof(**context));
- free(*context);
- *context = NULL;
-}
-
-/*
- *
- */
-
-Certificate *
-_hx509_get_cert(hx509_cert cert)
-{
- return cert->data;
-}
-
-/*
- *
- */
-
-int
-_hx509_cert_get_version(const Certificate *t)
-{
- return t->tbsCertificate.version ? *t->tbsCertificate.version + 1 : 1;
-}
-
-/**
- * Allocate and init an hx509 certificate object from the decoded
- * certificate `c´.
- *
- * @param context A hx509 context.
- * @param c
- * @param cert
- *
- * @return Returns an hx509 error code.
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert)
-{
- int ret;
-
- *cert = malloc(sizeof(**cert));
- if (*cert == NULL)
- return ENOMEM;
- (*cert)->ref = 1;
- (*cert)->friendlyname = NULL;
- (*cert)->attrs.len = 0;
- (*cert)->attrs.val = NULL;
- (*cert)->private_key = NULL;
- (*cert)->basename = NULL;
- (*cert)->release = NULL;
- (*cert)->ctx = NULL;
-
- (*cert)->data = calloc(1, sizeof(*(*cert)->data));
- if ((*cert)->data == NULL) {
- free(*cert);
- return ENOMEM;
- }
- ret = copy_Certificate(c, (*cert)->data);
- if (ret) {
- free((*cert)->data);
- free(*cert);
- *cert = NULL;
- }
- return ret;
-}
-
-/**
- * Just like hx509_cert_init(), but instead of a decode certificate
- * takes an pointer and length to a memory region that contains a
- * DER/BER encoded certificate.
- *
- * If the memory region doesn't contain just the certificate and
- * nothing more the function will fail with
- * HX509_EXTRA_DATA_AFTER_STRUCTURE.
- *
- * @param context A hx509 context.
- * @param ptr pointer to memory region containing encoded certificate.
- * @param len length of memory region.
- * @param cert a return pointer to a hx509 certificate object, will
- * contain NULL on error.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_init_data(hx509_context context,
- const void *ptr,
- size_t len,
- hx509_cert *cert)
-{
- Certificate t;
- size_t size;
- int ret;
-
- ret = decode_Certificate(ptr, len, &t, &size);
- if (ret) {
- hx509_set_error_string(context, 0, ret, "Failed to decode certificate");
- return ret;
- }
- if (size != len) {
- hx509_set_error_string(context, 0, HX509_EXTRA_DATA_AFTER_STRUCTURE,
- "Extra data after certificate");
- return HX509_EXTRA_DATA_AFTER_STRUCTURE;
- }
-
- ret = hx509_cert_init(context, &t, cert);
- free_Certificate(&t);
- return ret;
-}
-
-void
-_hx509_cert_set_release(hx509_cert cert,
- _hx509_cert_release_func release,
- void *ctx)
-{
- cert->release = release;
- cert->ctx = ctx;
-}
-
-
-/* Doesn't make a copy of `private_key'. */
-
-int
-_hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key)
-{
- if (cert->private_key)
- _hx509_private_key_free(&cert->private_key);
- cert->private_key = _hx509_private_key_ref(private_key);
- return 0;
-}
-
-/**
- * Free reference to the hx509 certificate object, if the refcounter
- * reaches 0, the object if freed. Its allowed to pass in NULL.
- *
- * @param cert the cert to free.
- *
- * @ingroup hx509_cert
- */
-
-void
-hx509_cert_free(hx509_cert cert)
-{
- int i;
-
- if (cert == NULL)
- return;
-
- if (cert->ref <= 0)
- _hx509_abort("cert refcount <= 0 on free");
- if (--cert->ref > 0)
- return;
-
- if (cert->release)
- (cert->release)(cert, cert->ctx);
-
- if (cert->private_key)
- _hx509_private_key_free(&cert->private_key);
-
- free_Certificate(cert->data);
- free(cert->data);
-
- for (i = 0; i < cert->attrs.len; i++) {
- der_free_octet_string(&cert->attrs.val[i]->data);
- der_free_oid(&cert->attrs.val[i]->oid);
- free(cert->attrs.val[i]);
- }
- free(cert->attrs.val);
- free(cert->friendlyname);
- if (cert->basename)
- hx509_name_free(&cert->basename);
- memset(cert, 0, sizeof(cert));
- free(cert);
-}
-
-/**
- * Add a reference to a hx509 certificate object.
- *
- * @param cert a pointer to an hx509 certificate object.
- *
- * @return the same object as is passed in.
- *
- * @ingroup hx509_cert
- */
-
-hx509_cert
-hx509_cert_ref(hx509_cert cert)
-{
- if (cert == NULL)
- return NULL;
- if (cert->ref <= 0)
- _hx509_abort("cert refcount <= 0");
- cert->ref++;
- if (cert->ref == 0)
- _hx509_abort("cert refcount == 0");
- return cert;
-}
-
-/**
- * Allocate an verification context that is used fo control the
- * verification process.
- *
- * @param context A hx509 context.
- * @param ctx returns a pointer to a hx509_verify_ctx object.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_verify
- */
-
-int
-hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx)
-{
- hx509_verify_ctx c;
-
- c = calloc(1, sizeof(*c));
- if (c == NULL)
- return ENOMEM;
-
- c->max_depth = HX509_VERIFY_MAX_DEPTH;
-
- *ctx = c;
-
- return 0;
-}
-
-/**
- * Free an hx509 verification context.
- *
- * @param ctx the context to be freed.
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_destroy_ctx(hx509_verify_ctx ctx)
-{
- if (ctx) {
- hx509_certs_free(&ctx->trust_anchors);
- hx509_revoke_free(&ctx->revoke_ctx);
- memset(ctx, 0, sizeof(*ctx));
- }
- free(ctx);
-}
-
-/**
- * Set the trust anchors in the verification context, makes an
- * reference to the keyset, so the consumer can free the keyset
- * independent of the destruction of the verification context (ctx).
- *
- * @param ctx a verification context
- * @param set a keyset containing the trust anchors.
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set)
-{
- ctx->trust_anchors = _hx509_certs_ref(set);
-}
-
-/**
- * Attach an revocation context to the verfication context, , makes an
- * reference to the revoke context, so the consumer can free the
- * revoke context independent of the destruction of the verification
- * context. If there is no revoke context, the verification process is
- * NOT going to check any verification status.
- *
- * @param ctx a verification context.
- * @param revoke_ctx a revoke context.
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_attach_revoke(hx509_verify_ctx ctx, hx509_revoke_ctx revoke_ctx)
-{
- if (ctx->revoke_ctx)
- hx509_revoke_free(&ctx->revoke_ctx);
- ctx->revoke_ctx = _hx509_revoke_ref(revoke_ctx);
-}
-
-/**
- * Set the clock time the the verification process is going to
- * use. Used to check certificate in the past and future time. If not
- * set the current time will be used.
- *
- * @param ctx a verification context.
- * @param t the time the verifiation is using.
- *
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_set_time(hx509_verify_ctx ctx, time_t t)
-{
- ctx->flags |= HX509_VERIFY_CTX_F_TIME_SET;
- ctx->time_now = t;
-}
-
-/**
- * Set the maximum depth of the certificate chain that the path
- * builder is going to try.
- *
- * @param ctx a verification context
- * @param max_depth maxium depth of the certificate chain, include
- * trust anchor.
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_set_max_depth(hx509_verify_ctx ctx, unsigned int max_depth)
-{
- ctx->max_depth = max_depth;
-}
-
-/**
- * Allow or deny the use of proxy certificates
- *
- * @param ctx a verification context
- * @param boolean if non zero, allow proxy certificates.
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean)
-{
- if (boolean)
- ctx->flags |= HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
- else
- ctx->flags &= ~HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
-}
-
-/**
- * Select strict RFC3280 verification of certificiates. This means
- * checking key usage on CA certificates, this will make version 1
- * certificiates unuseable.
- *
- * @param ctx a verification context
- * @param boolean if non zero, use strict verification.
- *
- * @ingroup hx509_verify
- */
-
-void
-hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean)
-{
- if (boolean)
- ctx->flags |= HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
- else
- ctx->flags &= ~HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
-}
-
-/**
- * Allow using the operating system builtin trust anchors if no other
- * trust anchors are configured.
- *
- * @param ctx a verification context
- * @param boolean if non zero, useing the operating systems builtin
- * trust anchors.
- *
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-void
-hx509_verify_ctx_f_allow_default_trustanchors(hx509_verify_ctx ctx, int boolean)
-{
- if (boolean)
- ctx->flags &= ~HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;
- else
- ctx->flags |= HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;
-}
-
-static const Extension *
-find_extension(const Certificate *cert, const heim_oid *oid, int *idx)
-{
- const TBSCertificate *c = &cert->tbsCertificate;
-
- if (c->version == NULL || *c->version < 2 || c->extensions == NULL)
- return NULL;
-
- for (;*idx < c->extensions->len; (*idx)++) {
- if (der_heim_oid_cmp(&c->extensions->val[*idx].extnID, oid) == 0)
- return &c->extensions->val[(*idx)++];
- }
- return NULL;
-}
-
-static int
-find_extension_auth_key_id(const Certificate *subject,
- AuthorityKeyIdentifier *ai)
-{
- const Extension *e;
- size_t size;
- int i = 0;
-
- memset(ai, 0, sizeof(*ai));
-
- e = find_extension(subject, oid_id_x509_ce_authorityKeyIdentifier(), &i);
- if (e == NULL)
- return HX509_EXTENSION_NOT_FOUND;
-
- return decode_AuthorityKeyIdentifier(e->extnValue.data,
- e->extnValue.length,
- ai, &size);
-}
-
-int
-_hx509_find_extension_subject_key_id(const Certificate *issuer,
- SubjectKeyIdentifier *si)
-{
- const Extension *e;
- size_t size;
- int i = 0;
-
- memset(si, 0, sizeof(*si));
-
- e = find_extension(issuer, oid_id_x509_ce_subjectKeyIdentifier(), &i);
- if (e == NULL)
- return HX509_EXTENSION_NOT_FOUND;
-
- return decode_SubjectKeyIdentifier(e->extnValue.data,
- e->extnValue.length,
- si, &size);
-}
-
-static int
-find_extension_name_constraints(const Certificate *subject,
- NameConstraints *nc)
-{
- const Extension *e;
- size_t size;
- int i = 0;
-
- memset(nc, 0, sizeof(*nc));
-
- e = find_extension(subject, oid_id_x509_ce_nameConstraints(), &i);
- if (e == NULL)
- return HX509_EXTENSION_NOT_FOUND;
-
- return decode_NameConstraints(e->extnValue.data,
- e->extnValue.length,
- nc, &size);
-}
-
-static int
-find_extension_subject_alt_name(const Certificate *cert, int *i,
- GeneralNames *sa)
-{
- const Extension *e;
- size_t size;
-
- memset(sa, 0, sizeof(*sa));
-
- e = find_extension(cert, oid_id_x509_ce_subjectAltName(), i);
- if (e == NULL)
- return HX509_EXTENSION_NOT_FOUND;
-
- return decode_GeneralNames(e->extnValue.data,
- e->extnValue.length,
- sa, &size);
-}
-
-static int
-find_extension_eku(const Certificate *cert, ExtKeyUsage *eku)
-{
- const Extension *e;
- size_t size;
- int i = 0;
-
- memset(eku, 0, sizeof(*eku));
-
- e = find_extension(cert, oid_id_x509_ce_extKeyUsage(), &i);
- if (e == NULL)
- return HX509_EXTENSION_NOT_FOUND;
-
- return decode_ExtKeyUsage(e->extnValue.data,
- e->extnValue.length,
- eku, &size);
-}
-
-static int
-add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry)
-{
- void *p;
- int ret;
-
- p = realloc(list->val, (list->len + 1) * sizeof(list->val[0]));
- if (p == NULL)
- return ENOMEM;
- list->val = p;
- ret = der_copy_octet_string(entry, &list->val[list->len]);
- if (ret)
- return ret;
- list->len++;
- return 0;
-}
-
-/**
- * Free a list of octet strings returned by another hx509 library
- * function.
- *
- * @param list list to be freed.
- *
- * @ingroup hx509_misc
- */
-
-void
-hx509_free_octet_string_list(hx509_octet_string_list *list)
-{
- int i;
- for (i = 0; i < list->len; i++)
- der_free_octet_string(&list->val[i]);
- free(list->val);
- list->val = NULL;
- list->len = 0;
-}
-
-/**
- * Return a list of subjectAltNames specified by oid in the
- * certificate. On error the
- *
- * The returned list of octet string should be freed with
- * hx509_free_octet_string_list().
- *
- * @param context A hx509 context.
- * @param cert a hx509 certificate object.
- * @param oid an oid to for SubjectAltName.
- * @param list list of matching SubjectAltName.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_find_subjectAltName_otherName(hx509_context context,
- hx509_cert cert,
- const heim_oid *oid,
- hx509_octet_string_list *list)
-{
- GeneralNames sa;
- int ret, i, j;
-
- list->val = NULL;
- list->len = 0;
-
- i = 0;
- while (1) {
- ret = find_extension_subject_alt_name(_hx509_get_cert(cert), &i, &sa);
- i++;
- if (ret == HX509_EXTENSION_NOT_FOUND) {
- ret = 0;
- break;
- } else if (ret != 0) {
- hx509_set_error_string(context, 0, ret, "Error searching for SAN");
- hx509_free_octet_string_list(list);
- return ret;
- }
-
- for (j = 0; j < sa.len; j++) {
- if (sa.val[j].element == choice_GeneralName_otherName &&
- der_heim_oid_cmp(&sa.val[j].u.otherName.type_id, oid) == 0)
- {
- ret = add_to_list(list, &sa.val[j].u.otherName.value);
- if (ret) {
- hx509_set_error_string(context, 0, ret,
- "Error adding an exra SAN to "
- "return list");
- hx509_free_octet_string_list(list);
- free_GeneralNames(&sa);
- return ret;
- }
- }
- }
- free_GeneralNames(&sa);
- }
- return 0;
-}
-
-
-static int
-check_key_usage(hx509_context context, const Certificate *cert,
- unsigned flags, int req_present)
-{
- const Extension *e;
- KeyUsage ku;
- size_t size;
- int ret, i = 0;
- unsigned ku_flags;
-
- if (_hx509_cert_get_version(cert) < 3)
- return 0;
-
- e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i);
- if (e == NULL) {
- if (req_present) {
- hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
- "Required extension key "
- "usage missing from certifiate");
- return HX509_KU_CERT_MISSING;
- }
- return 0;
- }
-
- ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &size);
- if (ret)
- return ret;
- ku_flags = KeyUsage2int(ku);
- if ((ku_flags & flags) != flags) {
- unsigned missing = (~ku_flags) & flags;
- char buf[256], *name;
-
- unparse_flags(missing, asn1_KeyUsage_units(), buf, sizeof(buf));
- _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
- hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
- "Key usage %s required but missing "
- "from certifiate %s", buf, name);
- free(name);
- return HX509_KU_CERT_MISSING;
- }
- return 0;
-}
-
-/*
- * Return 0 on matching key usage 'flags' for 'cert', otherwise return
- * an error code. If 'req_present' the existance is required of the
- * KeyUsage extension.
- */
-
-int
-_hx509_check_key_usage(hx509_context context, hx509_cert cert,
- unsigned flags, int req_present)
-{
- return check_key_usage(context, _hx509_get_cert(cert), flags, req_present);
-}
-
-enum certtype { PROXY_CERT, EE_CERT, CA_CERT };
-
-static int
-check_basic_constraints(hx509_context context, const Certificate *cert,
- enum certtype type, int depth)
-{
- BasicConstraints bc;
- const Extension *e;
- size_t size;
- int ret, i = 0;
-
- if (_hx509_cert_get_version(cert) < 3)
- return 0;
-
- e = find_extension(cert, oid_id_x509_ce_basicConstraints(), &i);
- if (e == NULL) {
- switch(type) {
- case PROXY_CERT:
- case EE_CERT:
- return 0;
- case CA_CERT: {
- char *name;
- ret = _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
- assert(ret == 0);
- hx509_set_error_string(context, 0, HX509_EXTENSION_NOT_FOUND,
- "basicConstraints missing from "
- "CA certifiacte %s", name);
- free(name);
- return HX509_EXTENSION_NOT_FOUND;
- }
- }
- }
-
- ret = decode_BasicConstraints(e->extnValue.data,
- e->extnValue.length, &bc,
- &size);
- if (ret)
- return ret;
- switch(type) {
- case PROXY_CERT:
- if (bc.cA != NULL && *bc.cA)
- ret = HX509_PARENT_IS_CA;
- break;
- case EE_CERT:
- ret = 0;
- break;
- case CA_CERT:
- if (bc.cA == NULL || !*bc.cA)
- ret = HX509_PARENT_NOT_CA;
- else if (bc.pathLenConstraint)
- if (depth - 1 > *bc.pathLenConstraint)
- ret = HX509_CA_PATH_TOO_DEEP;
- break;
- }
- free_BasicConstraints(&bc);
- return ret;
-}
-
-int
-_hx509_cert_is_parent_cmp(const Certificate *subject,
- const Certificate *issuer,
- int allow_self_signed)
-{
- int diff;
- AuthorityKeyIdentifier ai;
- SubjectKeyIdentifier si;
- int ret_ai, ret_si;
-
- diff = _hx509_name_cmp(&issuer->tbsCertificate.subject,
- &subject->tbsCertificate.issuer);
- if (diff)
- return diff;
-
- memset(&ai, 0, sizeof(ai));
- memset(&si, 0, sizeof(si));
-
- /*
- * Try to find AuthorityKeyIdentifier, if it's not present in the
- * subject certificate nor the parent.
- */
-
- ret_ai = find_extension_auth_key_id(subject, &ai);
- if (ret_ai && ret_ai != HX509_EXTENSION_NOT_FOUND)
- return 1;
- ret_si = _hx509_find_extension_subject_key_id(issuer, &si);
- if (ret_si && ret_si != HX509_EXTENSION_NOT_FOUND)
- return -1;
-
- if (ret_si && ret_ai)
- goto out;
- if (ret_ai)
- goto out;
- if (ret_si) {
- if (allow_self_signed) {
- diff = 0;
- goto out;
- } else if (ai.keyIdentifier) {
- diff = -1;
- goto out;
- }
- }
-
- if (ai.keyIdentifier == NULL) {
- Name name;
-
- if (ai.authorityCertIssuer == NULL)
- return -1;
- if (ai.authorityCertSerialNumber == NULL)
- return -1;
-
- diff = der_heim_integer_cmp(ai.authorityCertSerialNumber,
- &issuer->tbsCertificate.serialNumber);
- if (diff)
- return diff;
- if (ai.authorityCertIssuer->len != 1)
- return -1;
- if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName)
- return -1;
-
- name.element =
- ai.authorityCertIssuer->val[0].u.directoryName.element;
- name.u.rdnSequence =
- ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence;
-
- diff = _hx509_name_cmp(&issuer->tbsCertificate.subject,
- &name);
- if (diff)
- return diff;
- diff = 0;
- } else
- diff = der_heim_octet_string_cmp(ai.keyIdentifier, &si);
- if (diff)
- goto out;
-
- out:
- free_AuthorityKeyIdentifier(&ai);
- free_SubjectKeyIdentifier(&si);
- return diff;
-}
-
-static int
-certificate_is_anchor(hx509_context context,
- hx509_certs trust_anchors,
- const hx509_cert cert)
-{
- hx509_query q;
- hx509_cert c;
- int ret;
-
- if (trust_anchors == NULL)
- return 0;
-
- _hx509_query_clear(&q);
-
- q.match = HX509_QUERY_MATCH_CERTIFICATE;
- q.certificate = _hx509_get_cert(cert);
-
- ret = hx509_certs_find(context, trust_anchors, &q, &c);
- if (ret == 0)
- hx509_cert_free(c);
- return ret == 0;
-}
-
-static int
-certificate_is_self_signed(const Certificate *cert)
-{
- return _hx509_name_cmp(&cert->tbsCertificate.subject,
- &cert->tbsCertificate.issuer) == 0;
-}
-
-/*
- * The subjectName is "null" when it's empty set of relative DBs.
- */
-
-static int
-subject_null_p(const Certificate *c)
-{
- return c->tbsCertificate.subject.u.rdnSequence.len == 0;
-}
-
-
-static int
-find_parent(hx509_context context,
- time_t time_now,
- hx509_certs trust_anchors,
- hx509_path *path,
- hx509_certs pool,
- hx509_cert current,
- hx509_cert *parent)
-{
- AuthorityKeyIdentifier ai;
- hx509_query q;
- int ret;
-
- *parent = NULL;
- memset(&ai, 0, sizeof(ai));
-
- _hx509_query_clear(&q);
-
- if (!subject_null_p(current->data)) {
- q.match |= HX509_QUERY_FIND_ISSUER_CERT;
- q.subject = _hx509_get_cert(current);
- } else {
- ret = find_extension_auth_key_id(current->data, &ai);
- if (ret) {
- hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
- "Subjectless certificate missing AuthKeyID");
- return HX509_CERTIFICATE_MALFORMED;
- }
-
- if (ai.keyIdentifier == NULL) {
- free_AuthorityKeyIdentifier(&ai);
- hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
- "Subjectless certificate missing keyIdentifier "
- "inside AuthKeyID");
- return HX509_CERTIFICATE_MALFORMED;
- }
-
- q.subject_id = ai.keyIdentifier;
- q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
- }
-
- q.path = path;
- q.match |= HX509_QUERY_NO_MATCH_PATH;
-
- if (pool) {
- q.timenow = time_now;
- q.match |= HX509_QUERY_MATCH_TIME;
-
- ret = hx509_certs_find(context, pool, &q, parent);
- if (ret == 0) {
- free_AuthorityKeyIdentifier(&ai);
- return 0;
- }
- q.match &= ~HX509_QUERY_MATCH_TIME;
- }
-
- if (trust_anchors) {
- ret = hx509_certs_find(context, trust_anchors, &q, parent);
- if (ret == 0) {
- free_AuthorityKeyIdentifier(&ai);
- return ret;
- }
- }
- free_AuthorityKeyIdentifier(&ai);
-
- {
- hx509_name name;
- char *str;
-
- ret = hx509_cert_get_subject(current, &name);
- if (ret) {
- hx509_clear_error_string(context);
- return HX509_ISSUER_NOT_FOUND;
- }
- ret = hx509_name_to_string(name, &str);
- hx509_name_free(&name);
- if (ret) {
- hx509_clear_error_string(context);
- return HX509_ISSUER_NOT_FOUND;
- }
-
- hx509_set_error_string(context, 0, HX509_ISSUER_NOT_FOUND,
- "Failed to find issuer for "
- "certificate with subject: '%s'", str);
- free(str);
- }
- return HX509_ISSUER_NOT_FOUND;
-}
-
-/*
- *
- */
-
-static int
-is_proxy_cert(hx509_context context,
- const Certificate *cert,
- ProxyCertInfo *rinfo)
-{
- ProxyCertInfo info;
- const Extension *e;
- size_t size;
- int ret, i = 0;
-
- if (rinfo)
- memset(rinfo, 0, sizeof(*rinfo));
-
- e = find_extension(cert, oid_id_pkix_pe_proxyCertInfo(), &i);
- if (e == NULL) {
- hx509_clear_error_string(context);
- return HX509_EXTENSION_NOT_FOUND;
- }
-
- ret = decode_ProxyCertInfo(e->extnValue.data,
- e->extnValue.length,
- &info,
- &size);
- if (ret) {
- hx509_clear_error_string(context);
- return ret;
- }
- if (size != e->extnValue.length) {
- free_ProxyCertInfo(&info);
- hx509_clear_error_string(context);
- return HX509_EXTRA_DATA_AFTER_STRUCTURE;
- }
- if (rinfo == NULL)
- free_ProxyCertInfo(&info);
- else
- *rinfo = info;
-
- return 0;
-}
-
-/*
- * Path operations are like MEMORY based keyset, but with exposed
- * internal so we can do easy searches.
- */
-
-int
-_hx509_path_append(hx509_context context, hx509_path *path, hx509_cert cert)
-{
- hx509_cert *val;
- val = realloc(path->val, (path->len + 1) * sizeof(path->val[0]));
- if (val == NULL) {
- hx509_set_error_string(context, 0, ENOMEM, "out of memory");
- return ENOMEM;
- }
-
- path->val = val;
- path->val[path->len] = hx509_cert_ref(cert);
- path->len++;
-
- return 0;
-}
-
-void
-_hx509_path_free(hx509_path *path)
-{
- unsigned i;
-
- for (i = 0; i < path->len; i++)
- hx509_cert_free(path->val[i]);
- free(path->val);
- path->val = NULL;
- path->len = 0;
-}
-
-/*
- * Find path by looking up issuer for the top certificate and continue
- * until an anchor certificate is found or max limit is found. A
- * certificate never included twice in the path.
- *
- * If the trust anchors are not given, calculate optimistic path, just
- * follow the chain upward until we no longer find a parent or we hit
- * the max path limit. In this case, a failure will always be returned
- * depending on what error condition is hit first.
- *
- * The path includes a path from the top certificate to the anchor
- * certificate.
- *
- * The caller needs to free `path´ both on successful built path and
- * failure.
- */
-
-int
-_hx509_calculate_path(hx509_context context,
- int flags,
- time_t time_now,
- hx509_certs anchors,
- unsigned int max_depth,
- hx509_cert cert,
- hx509_certs pool,
- hx509_path *path)
-{
- hx509_cert parent, current;
- int ret;
-
- if (max_depth == 0)
- max_depth = HX509_VERIFY_MAX_DEPTH;
-
- ret = _hx509_path_append(context, path, cert);
- if (ret)
- return ret;
-
- current = hx509_cert_ref(cert);
-
- while (!certificate_is_anchor(context, anchors, current)) {
-
- ret = find_parent(context, time_now, anchors, path,
- pool, current, &parent);
- hx509_cert_free(current);
- if (ret)
- return ret;
-
- ret = _hx509_path_append(context, path, parent);
- if (ret)
- return ret;
- current = parent;
-
- if (path->len > max_depth) {
- hx509_cert_free(current);
- hx509_set_error_string(context, 0, HX509_PATH_TOO_LONG,
- "Path too long while bulding "
- "certificate chain");
- return HX509_PATH_TOO_LONG;
- }
- }
-
- if ((flags & HX509_CALCULATE_PATH_NO_ANCHOR) &&
- path->len > 0 &&
- certificate_is_anchor(context, anchors, path->val[path->len - 1]))
- {
- hx509_cert_free(path->val[path->len - 1]);
- path->len--;
- }
-
- hx509_cert_free(current);
- return 0;
-}
-
-int
-_hx509_AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p,
- const AlgorithmIdentifier *q)
-{
- int diff;
- diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm);
- if (diff)
- return diff;
- if (p->parameters) {
- if (q->parameters)
- return heim_any_cmp(p->parameters,
- q->parameters);
- else
- return 1;
- } else {
- if (q->parameters)
- return -1;
- else
- return 0;
- }
-}
-
-int
-_hx509_Certificate_cmp(const Certificate *p, const Certificate *q)
-{
- int diff;
- diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue);
- if (diff)
- return diff;
- diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm,
- &q->signatureAlgorithm);
- if (diff)
- return diff;
- diff = der_heim_octet_string_cmp(&p->tbsCertificate._save,
- &q->tbsCertificate._save);
- return diff;
-}
-
-/**
- * Compare to hx509 certificate object, useful for sorting.
- *
- * @param p a hx509 certificate object.
- * @param q a hx509 certificate object.
- *
- * @return 0 the objects are the same, returns > 0 is p is "larger"
- * then q, < 0 if p is "smaller" then q.
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_cmp(hx509_cert p, hx509_cert q)
-{
- return _hx509_Certificate_cmp(p->data, q->data);
-}
-
-/**
- * Return the name of the issuer of the hx509 certificate.
- *
- * @param p a hx509 certificate object.
- * @param name a pointer to a hx509 name, should be freed by
- * hx509_name_free().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_get_issuer(hx509_cert p, hx509_name *name)
-{
- return _hx509_name_from_Name(&p->data->tbsCertificate.issuer, name);
-}
-
-/**
- * Return the name of the subject of the hx509 certificate.
- *
- * @param p a hx509 certificate object.
- * @param name a pointer to a hx509 name, should be freed by
- * hx509_name_free(). See also hx509_cert_get_base_subject().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_get_subject(hx509_cert p, hx509_name *name)
-{
- return _hx509_name_from_Name(&p->data->tbsCertificate.subject, name);
-}
-
-/**
- * Return the name of the base subject of the hx509 certificate. If
- * the certiicate is a verified proxy certificate, the this function
- * return the base certificate (root of the proxy chain). If the proxy
- * certificate is not verified with the base certificate
- * HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED is returned.
- *
- * @param context a hx509 context.
- * @param c a hx509 certificate object.
- * @param name a pointer to a hx509 name, should be freed by
- * hx509_name_free(). See also hx509_cert_get_subject().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_get_base_subject(hx509_context context, hx509_cert c,
- hx509_name *name)
-{
- if (c->basename)
- return hx509_name_copy(context, c->basename, name);
- if (is_proxy_cert(context, c->data, NULL) == 0) {
- int ret = HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED;
- hx509_set_error_string(context, 0, ret,
- "Proxy certificate have not been "
- "canonicalize yet, no base name");
- return ret;
- }
- return _hx509_name_from_Name(&c->data->tbsCertificate.subject, name);
-}
-
-/**
- * Get serial number of the certificate.
- *
- * @param p a hx509 certificate object.
- * @param i serial number, should be freed ith der_free_heim_integer().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_get_serialnumber(hx509_cert p, heim_integer *i)
-{
- return der_copy_heim_integer(&p->data->tbsCertificate.serialNumber, i);
-}
-
-/**
- * Get notBefore time of the certificate.
- *
- * @param p a hx509 certificate object.
- *
- * @return return not before time
- *
- * @ingroup hx509_cert
- */
-
-time_t
-hx509_cert_get_notBefore(hx509_cert p)
-{
- return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notBefore);
-}
-
-/**
- * Get notAfter time of the certificate.
- *
- * @param p a hx509 certificate object.
- *
- * @return return not after time.
- *
- * @ingroup hx509_cert
- */
-
-time_t
-hx509_cert_get_notAfter(hx509_cert p)
-{
- return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notAfter);
-}
-
-/**
- * Get the SubjectPublicKeyInfo structure from the hx509 certificate.
- *
- * @param context a hx509 context.
- * @param p a hx509 certificate object.
- * @param spki SubjectPublicKeyInfo, should be freed with
- * free_SubjectPublicKeyInfo().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_get_SPKI(hx509_context context, hx509_cert p, SubjectPublicKeyInfo *spki)
-{
- int ret;
-
- ret = copy_SubjectPublicKeyInfo(&p->data->tbsCertificate.subjectPublicKeyInfo, spki);
- if (ret)
- hx509_set_error_string(context, 0, ret, "Failed to copy SPKI");
- return ret;
-}
-
-/**
- * Get the AlgorithmIdentifier from the hx509 certificate.
- *
- * @param context a hx509 context.
- * @param p a hx509 certificate object.
- * @param alg AlgorithmIdentifier, should be freed with
- * free_AlgorithmIdentifier().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context,
- hx509_cert p,
- AlgorithmIdentifier *alg)
-{
- int ret;
-
- ret = copy_AlgorithmIdentifier(&p->data->tbsCertificate.subjectPublicKeyInfo.algorithm, alg);
- if (ret)
- hx509_set_error_string(context, 0, ret,
- "Failed to copy SPKI AlgorithmIdentifier");
- return ret;
-}
-
-
-hx509_private_key
-_hx509_cert_private_key(hx509_cert p)
-{
- return p->private_key;
-}
-
-int
-hx509_cert_have_private_key(hx509_cert p)
-{
- return p->private_key ? 1 : 0;
-}
-
-
-int
-_hx509_cert_private_key_exportable(hx509_cert p)
-{
- if (p->private_key == NULL)
- return 0;
- return _hx509_private_key_exportable(p->private_key);
-}
-
-int
-_hx509_cert_private_decrypt(hx509_context context,
- const heim_octet_string *ciphertext,
- const heim_oid *encryption_oid,
- hx509_cert p,
- heim_octet_string *cleartext)
-{
- cleartext->data = NULL;
- cleartext->length = 0;
-
- if (p->private_key == NULL) {
- hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
- "Private key missing");
- return HX509_PRIVATE_KEY_MISSING;
- }
-
- return _hx509_private_key_private_decrypt(context,
- ciphertext,
- encryption_oid,
- p->private_key,
- cleartext);
-}
-
-int
-_hx509_cert_public_encrypt(hx509_context context,
- const heim_octet_string *cleartext,
- const hx509_cert p,
- heim_oid *encryption_oid,
- heim_octet_string *ciphertext)
-{
- return _hx509_public_encrypt(context,
- cleartext, p->data,
- encryption_oid, ciphertext);
-}
-
-/*
- *
- */
-
-time_t
-_hx509_Time2time_t(const Time *t)
-{
- switch(t->element) {
- case choice_Time_utcTime:
- return t->u.utcTime;
- case choice_Time_generalTime:
- return t->u.generalTime;
- }
- return 0;
-}
-
-/*
- *
- */
-
-static int
-init_name_constraints(hx509_name_constraints *nc)
-{
- memset(nc, 0, sizeof(*nc));
- return 0;
-}
-
-static int
-add_name_constraints(hx509_context context, const Certificate *c, int not_ca,
- hx509_name_constraints *nc)
-{
- NameConstraints tnc;
- int ret;
-
- ret = find_extension_name_constraints(c, &tnc);
- if (ret == HX509_EXTENSION_NOT_FOUND)
- return 0;
- else if (ret) {
- hx509_set_error_string(context, 0, ret, "Failed getting NameConstraints");
- return ret;
- } else if (not_ca) {
- ret = HX509_VERIFY_CONSTRAINTS;
- hx509_set_error_string(context, 0, ret, "Not a CA and "
- "have NameConstraints");
- } else {
- NameConstraints *val;
- val = realloc(nc->val, sizeof(nc->val[0]) * (nc->len + 1));
- if (val == NULL) {
- hx509_clear_error_string(context);
- ret = ENOMEM;
- goto out;
- }
- nc->val = val;
- ret = copy_NameConstraints(&tnc, &nc->val[nc->len]);
- if (ret) {
- hx509_clear_error_string(context);
- goto out;
- }
- nc->len += 1;
- }
-out:
- free_NameConstraints(&tnc);
- return ret;
-}
-
-static int
-match_RDN(const RelativeDistinguishedName *c,
- const RelativeDistinguishedName *n)
-{
- int i;
-
- if (c->len != n->len)
- return HX509_NAME_CONSTRAINT_ERROR;
-
- for (i = 0; i < n->len; i++) {
- if (der_heim_oid_cmp(&c->val[i].type, &n->val[i].type) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- if (_hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- }
- return 0;
-}
-
-static int
-match_X501Name(const Name *c, const Name *n)
-{
- int i, ret;
-
- if (c->element != choice_Name_rdnSequence
- || n->element != choice_Name_rdnSequence)
- return 0;
- if (c->u.rdnSequence.len > n->u.rdnSequence.len)
- return HX509_NAME_CONSTRAINT_ERROR;
- for (i = 0; i < c->u.rdnSequence.len; i++) {
- ret = match_RDN(&c->u.rdnSequence.val[i], &n->u.rdnSequence.val[i]);
- if (ret)
- return ret;
- }
- return 0;
-}
-
-
-static int
-match_general_name(const GeneralName *c, const GeneralName *n, int *match)
-{
- /*
- * Name constraints only apply to the same name type, see RFC3280,
- * 4.2.1.11.
- */
- assert(c->element == n->element);
-
- switch(c->element) {
- case choice_GeneralName_otherName:
- if (der_heim_oid_cmp(&c->u.otherName.type_id,
- &n->u.otherName.type_id) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- if (heim_any_cmp(&c->u.otherName.value,
- &n->u.otherName.value) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- *match = 1;
- return 0;
- case choice_GeneralName_rfc822Name: {
- const char *s;
- size_t len1, len2;
- s = strchr(c->u.rfc822Name, '@');
- if (s) {
- if (strcasecmp(c->u.rfc822Name, n->u.rfc822Name) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- } else {
- s = strchr(n->u.rfc822Name, '@');
- if (s == NULL)
- return HX509_NAME_CONSTRAINT_ERROR;
- len1 = strlen(c->u.rfc822Name);
- len2 = strlen(s + 1);
- if (len1 > len2)
- return HX509_NAME_CONSTRAINT_ERROR;
- if (strcasecmp(s + 1 + len2 - len1, c->u.rfc822Name) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- if (len1 < len2 && s[len2 - len1 + 1] != '.')
- return HX509_NAME_CONSTRAINT_ERROR;
- }
- *match = 1;
- return 0;
- }
- case choice_GeneralName_dNSName: {
- size_t lenc, lenn;
-
- lenc = strlen(c->u.dNSName);
- lenn = strlen(n->u.dNSName);
- if (lenc > lenn)
- return HX509_NAME_CONSTRAINT_ERROR;
- if (strcasecmp(&n->u.dNSName[lenn - lenc], c->u.dNSName) != 0)
- return HX509_NAME_CONSTRAINT_ERROR;
- if (lenc != lenn && n->u.dNSName[lenn - lenc - 1] != '.')
- return HX509_NAME_CONSTRAINT_ERROR;
- *match = 1;
- return 0;
- }
- case choice_GeneralName_directoryName: {
- Name c_name, n_name;
- int ret;
-
- c_name._save.data = NULL;
- c_name._save.length = 0;
- c_name.element = c->u.directoryName.element;
- c_name.u.rdnSequence = c->u.directoryName.u.rdnSequence;
-
- n_name._save.data = NULL;
- n_name._save.length = 0;
- n_name.element = n->u.directoryName.element;
- n_name.u.rdnSequence = n->u.directoryName.u.rdnSequence;
-
- ret = match_X501Name(&c_name, &n_name);
- if (ret == 0)
- *match = 1;
- return ret;
- }
- case choice_GeneralName_uniformResourceIdentifier:
- case choice_GeneralName_iPAddress:
- case choice_GeneralName_registeredID:
- default:
- return HX509_NAME_CONSTRAINT_ERROR;
- }
-}
-
-static int
-match_alt_name(const GeneralName *n, const Certificate *c,
- int *same, int *match)
-{
- GeneralNames sa;
- int ret, i, j;
-
- i = 0;
- do {
- ret = find_extension_subject_alt_name(c, &i, &sa);
- if (ret == HX509_EXTENSION_NOT_FOUND) {
- ret = 0;
- break;
- } else if (ret != 0)
- break;
-
- for (j = 0; j < sa.len; j++) {
- if (n->element == sa.val[j].element) {
- *same = 1;
- ret = match_general_name(n, &sa.val[j], match);
- }
- }
- free_GeneralNames(&sa);
- } while (1);
- return ret;
-}
-
-
-static int
-match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)
-{
- int name, alt_name, same;
- unsigned int i;
- int ret = 0;
-
- name = alt_name = same = *match = 0;
- for (i = 0; i < t->len; i++) {
- if (t->val[i].minimum && t->val[i].maximum)
- return HX509_RANGE;
-
- /*
- * If the constraint apply to directoryNames, test is with
- * subjectName of the certificate if the certificate have a
- * non-null (empty) subjectName.
- */
-
- if (t->val[i].base.element == choice_GeneralName_directoryName
- && !subject_null_p(c))
- {
- GeneralName certname;
-
- memset(&certname, 0, sizeof(certname));
- certname.element = choice_GeneralName_directoryName;
- certname.u.directoryName.element =
- c->tbsCertificate.subject.element;
- certname.u.directoryName.u.rdnSequence =
- c->tbsCertificate.subject.u.rdnSequence;
-
- ret = match_general_name(&t->val[i].base, &certname, &name);
- }
-
- /* Handle subjectAltNames, this is icky since they
- * restrictions only apply if the subjectAltName is of the
- * same type. So if there have been a match of type, require
- * altname to be set.
- */
- ret = match_alt_name(&t->val[i].base, c, &same, &alt_name);
- }
- if (name && (!same || alt_name))
- *match = 1;
- return ret;
-}
-
-static int
-check_name_constraints(hx509_context context,
- const hx509_name_constraints *nc,
- const Certificate *c)
-{
- int match, ret;
- int i;
-
- for (i = 0 ; i < nc->len; i++) {
- GeneralSubtrees gs;
-
- if (nc->val[i].permittedSubtrees) {
- GeneralSubtrees_SET(&gs, nc->val[i].permittedSubtrees);
- ret = match_tree(&gs, c, &match);
- if (ret) {
- hx509_clear_error_string(context);
- return ret;
- }
- /* allow null subjectNames, they wont matches anything */
- if (match == 0 && !subject_null_p(c)) {
- hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS,
- "Error verify constraints, "
- "certificate didn't match any "
- "permitted subtree");
- return HX509_VERIFY_CONSTRAINTS;
- }
- }
- if (nc->val[i].excludedSubtrees) {
- GeneralSubtrees_SET(&gs, nc->val[i].excludedSubtrees);
- ret = match_tree(&gs, c, &match);
- if (ret) {
- hx509_clear_error_string(context);
- return ret;
- }
- if (match) {
- hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS,
- "Error verify constraints, "
- "certificate included in excluded "
- "subtree");
- return HX509_VERIFY_CONSTRAINTS;
- }
- }
- }
- return 0;
-}
-
-static void
-free_name_constraints(hx509_name_constraints *nc)
-{
- int i;
-
- for (i = 0 ; i < nc->len; i++)
- free_NameConstraints(&nc->val[i]);
- free(nc->val);
-}
-
-/**
- * Build and verify the path for the certificate to the trust anchor
- * specified in the verify context. The path is constructed from the
- * certificate, the pool and the trust anchors.
- *
- * @param context A hx509 context.
- * @param ctx A hx509 verification context.
- * @param cert the certificate to build the path from.
- * @param pool A keyset of certificates to build the chain from.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_verify
- */
-
-int
-hx509_verify_path(hx509_context context,
- hx509_verify_ctx ctx,
- hx509_cert cert,
- hx509_certs pool)
-{
- hx509_name_constraints nc;
- hx509_path path;
-#if 0
- const AlgorithmIdentifier *alg_id;
-#endif
- int ret, i, proxy_cert_depth, selfsigned_depth;
- enum certtype type;
- Name proxy_issuer;
- hx509_certs anchors = NULL;
-
- memset(&proxy_issuer, 0, sizeof(proxy_issuer));
-
- ret = init_name_constraints(&nc);
- if (ret)
- return ret;
-
- path.val = NULL;
- path.len = 0;
-
- if ((ctx->flags & HX509_VERIFY_CTX_F_TIME_SET) == 0)
- ctx->time_now = time(NULL);
-
- /*
- *
- */
- if (ctx->trust_anchors)
- anchors = _hx509_certs_ref(ctx->trust_anchors);
- else if (context->default_trust_anchors && ALLOW_DEF_TA(ctx))
- anchors = _hx509_certs_ref(context->default_trust_anchors);
- else {
- ret = hx509_certs_init(context, "MEMORY:no-TA", 0, NULL, &anchors);
- if (ret)
- goto out;
- }
-
- /*
- * Calculate the path from the certificate user presented to the
- * to an anchor.
- */
- ret = _hx509_calculate_path(context, 0, ctx->time_now,
- anchors, ctx->max_depth,
- cert, pool, &path);
- if (ret)
- goto out;
-
-#if 0
- alg_id = path.val[path->len - 1]->data->tbsCertificate.signature;
-#endif
-
- /*
- * Check CA and proxy certificate chain from the top of the
- * certificate chain. Also check certificate is valid with respect
- * to the current time.
- *
- */
-
- proxy_cert_depth = 0;
- selfsigned_depth = 0;
-
- if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE)
- type = PROXY_CERT;
- else
- type = EE_CERT;
-
- for (i = 0; i < path.len; i++) {
- Certificate *c;
- time_t t;
-
- c = _hx509_get_cert(path.val[i]);
-
- /*
- * Lets do some basic check on issuer like
- * keyUsage.keyCertSign and basicConstraints.cA bit depending
- * on what type of certificate this is.
- */
-
- switch (type) {
- case CA_CERT:
- /* XXX make constants for keyusage */
- ret = check_key_usage(context, c, 1 << 5,
- REQUIRE_RFC3280(ctx) ? TRUE : FALSE);
- if (ret) {
- hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
- "Key usage missing from CA certificate");
- goto out;
- }
-
- if (i + 1 != path.len && certificate_is_self_signed(c))
- selfsigned_depth++;
-
- break;
- case PROXY_CERT: {
- ProxyCertInfo info;
-
- if (is_proxy_cert(context, c, &info) == 0) {
- int j;
-
- if (info.pCPathLenConstraint != NULL &&
- *info.pCPathLenConstraint < i)
- {
- free_ProxyCertInfo(&info);
- ret = HX509_PATH_TOO_LONG;
- hx509_set_error_string(context, 0, ret,
- "Proxy certificate chain "
- "longer then allowed");
- goto out;
- }
- /* XXX MUST check info.proxyPolicy */
- free_ProxyCertInfo(&info);
-
- j = 0;
- if (find_extension(c, oid_id_x509_ce_subjectAltName(), &j)) {
- ret = HX509_PROXY_CERT_INVALID;
- hx509_set_error_string(context, 0, ret,
- "Proxy certificate have explicity "
- "forbidden subjectAltName");
- goto out;
- }
-
- j = 0;
- if (find_extension(c, oid_id_x509_ce_issuerAltName(), &j)) {
- ret = HX509_PROXY_CERT_INVALID;
- hx509_set_error_string(context, 0, ret,
- "Proxy certificate have explicity "
- "forbidden issuerAltName");
- goto out;
- }
-
- /*
- * The subject name of the proxy certificate should be
- * CN=XXX,<proxy issuer>, prune of CN and check if its
- * the same over the whole chain of proxy certs and
- * then check with the EE cert when we get to it.
- */
-
- if (proxy_cert_depth) {
- ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject);
- if (ret) {
- ret = HX509_PROXY_CERT_NAME_WRONG;
- hx509_set_error_string(context, 0, ret,
- "Base proxy name not right");
- goto out;
- }
- }
-
- free_Name(&proxy_issuer);
-
- ret = copy_Name(&c->tbsCertificate.subject, &proxy_issuer);
- if (ret) {
- hx509_clear_error_string(context);
- goto out;
- }
-
- j = proxy_issuer.u.rdnSequence.len;
- if (proxy_issuer.u.rdnSequence.len < 2
- || proxy_issuer.u.rdnSequence.val[j - 1].len > 1
- || der_heim_oid_cmp(&proxy_issuer.u.rdnSequence.val[j - 1].val[0].type,
- oid_id_at_commonName()))
- {
- ret = HX509_PROXY_CERT_NAME_WRONG;
- hx509_set_error_string(context, 0, ret,
- "Proxy name too short or "
- "does not have Common name "
- "at the top");
- goto out;
- }
-
- free_RelativeDistinguishedName(&proxy_issuer.u.rdnSequence.val[j - 1]);
- proxy_issuer.u.rdnSequence.len -= 1;
-
- ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer);
- if (ret != 0) {
- ret = HX509_PROXY_CERT_NAME_WRONG;
- hx509_set_error_string(context, 0, ret,
- "Proxy issuer name not as expected");
- goto out;
- }
-
- break;
- } else {
- /*
- * Now we are done with the proxy certificates, this
- * cert was an EE cert and we we will fall though to
- * EE checking below.
- */
- type = EE_CERT;
- /* FALLTHOUGH */
- }
- }
- case EE_CERT:
- /*
- * If there where any proxy certificates in the chain
- * (proxy_cert_depth > 0), check that the proxy issuer
- * matched proxy certificates "base" subject.
- */
- if (proxy_cert_depth) {
-
- ret = _hx509_name_cmp(&proxy_issuer,
- &c->tbsCertificate.subject);
- if (ret) {
- ret = HX509_PROXY_CERT_NAME_WRONG;
- hx509_clear_error_string(context);
- goto out;
- }
- if (cert->basename)
- hx509_name_free(&cert->basename);
-
- ret = _hx509_name_from_Name(&proxy_issuer, &cert->basename);
- if (ret) {
- hx509_clear_error_string(context);
- goto out;
- }
- }
-
- break;
- }
-
- ret = check_basic_constraints(context, c, type,
- i - proxy_cert_depth - selfsigned_depth);
- if (ret)
- goto out;
-
- /*
- * Don't check the trust anchors expiration time since they
- * are transported out of band, from RFC3820.
- */
- if (i + 1 != path.len || CHECK_TA(ctx)) {
-
- t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
- if (t > ctx->time_now) {
- ret = HX509_CERT_USED_BEFORE_TIME;
- hx509_clear_error_string(context);
- goto out;
- }
- t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
- if (t < ctx->time_now) {
- ret = HX509_CERT_USED_AFTER_TIME;
- hx509_clear_error_string(context);
- goto out;
- }
- }
-
- if (type == EE_CERT)
- type = CA_CERT;
- else if (type == PROXY_CERT)
- proxy_cert_depth++;
- }
-
- /*
- * Verify constraints, do this backward so path constraints are
- * checked in the right order.
- */
-
- for (ret = 0, i = path.len - 1; i >= 0; i--) {
- Certificate *c;
-
- c = _hx509_get_cert(path.val[i]);
-
- /* verify name constraints, not for selfsigned and anchor */
- if (!certificate_is_self_signed(c) || i + 1 != path.len) {
- ret = check_name_constraints(context, &nc, c);
- if (ret) {
- goto out;
- }
- }
- ret = add_name_constraints(context, c, i == 0, &nc);
- if (ret)
- goto out;
-
- /* XXX verify all other silly constraints */
-
- }
-
- /*
- * Verify that no certificates has been revoked.
- */
-
- if (ctx->revoke_ctx) {
- hx509_certs certs;
-
- ret = hx509_certs_init(context, "MEMORY:revoke-certs", 0,
- NULL, &certs);
- if (ret)
- goto out;
-
- for (i = 0; i < path.len; i++) {
- ret = hx509_certs_add(context, certs, path.val[i]);
- if (ret) {
- hx509_certs_free(&certs);
- goto out;
- }
- }
- ret = hx509_certs_merge(context, certs, pool);
- if (ret) {
- hx509_certs_free(&certs);
- goto out;
- }
-
- for (i = 0; i < path.len - 1; i++) {
- int parent = (i < path.len - 1) ? i + 1 : i;
-
- ret = hx509_revoke_verify(context,
- ctx->revoke_ctx,
- certs,
- ctx->time_now,
- path.val[i],
- path.val[parent]);
- if (ret) {
- hx509_certs_free(&certs);
- goto out;
- }
- }
- hx509_certs_free(&certs);
- }
-
- /*
- * Verify signatures, do this backward so public key working
- * parameter is passed up from the anchor up though the chain.
- */
-
- for (i = path.len - 1; i >= 0; i--) {
- Certificate *signer, *c;
-
- c = _hx509_get_cert(path.val[i]);
-
- /* is last in chain (trust anchor) */
- if (i + 1 == path.len) {
- signer = path.val[i]->data;
-
- /* if trust anchor is not self signed, don't check sig */
- if (!certificate_is_self_signed(signer))
- continue;
- } else {
- /* take next certificate in chain */
- signer = path.val[i + 1]->data;
- }
-
- /* verify signatureValue */
- ret = _hx509_verify_signature_bitstring(context,
- signer,
- &c->signatureAlgorithm,
- &c->tbsCertificate._save,
- &c->signatureValue);
- if (ret) {
- hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
- "Failed to verify signature of certificate");
- goto out;
- }
- }
-
-out:
- hx509_certs_free(&anchors);
- free_Name(&proxy_issuer);
- free_name_constraints(&nc);
- _hx509_path_free(&path);
-
- return ret;
-}
-
-/**
- * Verify a signature made using the private key of an certificate.
- *
- * @param context A hx509 context.
- * @param signer the certificate that made the signature.
- * @param alg algorthm that was used to sign the data.
- * @param data the data that was signed.
- * @param sig the sigature to verify.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_crypto
- */
-
-int
-hx509_verify_signature(hx509_context context,
- const hx509_cert signer,
- const AlgorithmIdentifier *alg,
- const heim_octet_string *data,
- const heim_octet_string *sig)
-{
- return _hx509_verify_signature(context, signer->data, alg, data, sig);
-}
-
-
-/**
- * Verify that the certificate is allowed to be used for the hostname
- * and address.
- *
- * @param context A hx509 context.
- * @param cert the certificate to match with
- * @param flags Flags to modify the behavior:
- * - HX509_VHN_F_ALLOW_NO_MATCH no match is ok
- * @param type type of hostname:
- * - HX509_HN_HOSTNAME for plain hostname.
- * - HX509_HN_DNSSRV for DNS SRV names.
- * @param hostname the hostname to check
- * @param sa address of the host
- * @param sa_size length of address
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_verify_hostname(hx509_context context,
- const hx509_cert cert,
- int flags,
- hx509_hostname_type type,
- const char *hostname,
- const struct sockaddr *sa,
- /* XXX krb5_socklen_t */ int sa_size)
-{
- GeneralNames san;
- int ret, i, j;
-
- if (sa && sa_size <= 0)
- return EINVAL;
-
- memset(&san, 0, sizeof(san));
-
- i = 0;
- do {
- ret = find_extension_subject_alt_name(cert->data, &i, &san);
- if (ret == HX509_EXTENSION_NOT_FOUND) {
- ret = 0;
- break;
- } else if (ret != 0)
- break;
-
- for (j = 0; j < san.len; j++) {
- switch (san.val[j].element) {
- case choice_GeneralName_dNSName:
- if (strcasecmp(san.val[j].u.dNSName, hostname) == 0) {
- free_GeneralNames(&san);
- return 0;
- }
- break;
- default:
- break;
- }
- }
- free_GeneralNames(&san);
- } while (1);
-
- {
- Name *name = &cert->data->tbsCertificate.subject;
-
- /* match if first component is a CN= */
- if (name->u.rdnSequence.len > 0
- && name->u.rdnSequence.val[0].len == 1
- && der_heim_oid_cmp(&name->u.rdnSequence.val[0].val[0].type,
- oid_id_at_commonName()) == 0)
- {
- DirectoryString *ds = &name->u.rdnSequence.val[0].val[0].value;
-
- switch (ds->element) {
- case choice_DirectoryString_printableString:
- if (strcasecmp(ds->u.printableString, hostname) == 0)
- return 0;
- break;
- case choice_DirectoryString_ia5String:
- if (strcasecmp(ds->u.ia5String, hostname) == 0)
- return 0;
- break;
- case choice_DirectoryString_utf8String:
- if (strcasecmp(ds->u.utf8String, hostname) == 0)
- return 0;
- default:
- break;
- }
- }
- }
-
- if ((flags & HX509_VHN_F_ALLOW_NO_MATCH) == 0)
- ret = HX509_NAME_CONSTRAINT_ERROR;
-
- return ret;
-}
-
-int
-_hx509_set_cert_attribute(hx509_context context,
- hx509_cert cert,
- const heim_oid *oid,
- const heim_octet_string *attr)
-{
- hx509_cert_attribute a;
- void *d;
-
- if (hx509_cert_get_attribute(cert, oid) != NULL)
- return 0;
-
- d = realloc(cert->attrs.val,
- sizeof(cert->attrs.val[0]) * (cert->attrs.len + 1));
- if (d == NULL) {
- hx509_clear_error_string(context);
- return ENOMEM;
- }
- cert->attrs.val = d;
-
- a = malloc(sizeof(*a));
- if (a == NULL)
- return ENOMEM;
-
- der_copy_octet_string(attr, &a->data);
- der_copy_oid(oid, &a->oid);
-
- cert->attrs.val[cert->attrs.len] = a;
- cert->attrs.len++;
-
- return 0;
-}
-
-/**
- * Get an external attribute for the certificate, examples are
- * friendly name and id.
- *
- * @param cert hx509 certificate object to search
- * @param oid an oid to search for.
- *
- * @return an hx509_cert_attribute, only valid as long as the
- * certificate is referenced.
- *
- * @ingroup hx509_cert
- */
-
-hx509_cert_attribute
-hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid)
-{
- int i;
- for (i = 0; i < cert->attrs.len; i++)
- if (der_heim_oid_cmp(oid, &cert->attrs.val[i]->oid) == 0)
- return cert->attrs.val[i];
- return NULL;
-}
-
-/**
- * Set the friendly name on the certificate.
- *
- * @param cert The certificate to set the friendly name on
- * @param name Friendly name.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_set_friendly_name(hx509_cert cert, const char *name)
-{
- if (cert->friendlyname)
- free(cert->friendlyname);
- cert->friendlyname = strdup(name);
- if (cert->friendlyname == NULL)
- return ENOMEM;
- return 0;
-}
-
-/**
- * Get friendly name of the certificate.
- *
- * @param cert cert to get the friendly name from.
- *
- * @return an friendly name or NULL if there is. The friendly name is
- * only valid as long as the certificate is referenced.
- *
- * @ingroup hx509_cert
- */
-
-const char *
-hx509_cert_get_friendly_name(hx509_cert cert)
-{
- hx509_cert_attribute a;
- PKCS9_friendlyName n;
- size_t sz;
- int ret, i;
-
- if (cert->friendlyname)
- return cert->friendlyname;
-
- a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_friendlyName());
- if (a == NULL) {
- /* XXX use subject name ? */
- return NULL;
- }
-
- ret = decode_PKCS9_friendlyName(a->data.data, a->data.length, &n, &sz);
- if (ret)
- return NULL;
-
- if (n.len != 1) {
- free_PKCS9_friendlyName(&n);
- return NULL;
- }
-
- cert->friendlyname = malloc(n.val[0].length + 1);
- if (cert->friendlyname == NULL) {
- free_PKCS9_friendlyName(&n);
- return NULL;
- }
-
- for (i = 0; i < n.val[0].length; i++) {
- if (n.val[0].data[i] <= 0xff)
- cert->friendlyname[i] = n.val[0].data[i] & 0xff;
- else
- cert->friendlyname[i] = 'X';
- }
- cert->friendlyname[i] = '\0';
- free_PKCS9_friendlyName(&n);
-
- return cert->friendlyname;
-}
-
-void
-_hx509_query_clear(hx509_query *q)
-{
- memset(q, 0, sizeof(*q));
-}
-
-/**
- * Allocate an query controller. Free using hx509_query_free().
- *
- * @param context A hx509 context.
- * @param q return pointer to a hx509_query.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_query_alloc(hx509_context context, hx509_query **q)
-{
- *q = calloc(1, sizeof(**q));
- if (*q == NULL)
- return ENOMEM;
- return 0;
-}
-
-/**
- * Set match options for the hx509 query controller.
- *
- * @param q query controller.
- * @param option options to control the query controller.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-void
-hx509_query_match_option(hx509_query *q, hx509_query_option option)
-{
- switch(option) {
- case HX509_QUERY_OPTION_PRIVATE_KEY:
- q->match |= HX509_QUERY_PRIVATE_KEY;
- break;
- case HX509_QUERY_OPTION_KU_ENCIPHERMENT:
- q->match |= HX509_QUERY_KU_ENCIPHERMENT;
- break;
- case HX509_QUERY_OPTION_KU_DIGITALSIGNATURE:
- q->match |= HX509_QUERY_KU_DIGITALSIGNATURE;
- break;
- case HX509_QUERY_OPTION_KU_KEYCERTSIGN:
- q->match |= HX509_QUERY_KU_KEYCERTSIGN;
- break;
- case HX509_QUERY_OPTION_END:
- default:
- break;
- }
-}
-
-/**
- * Set the issuer and serial number of match in the query
- * controller. The function make copies of the isser and serial number.
- *
- * @param q a hx509 query controller
- * @param issuer issuer to search for
- * @param serialNumber the serialNumber of the issuer.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_query_match_issuer_serial(hx509_query *q,
- const Name *issuer,
- const heim_integer *serialNumber)
-{
- int ret;
- if (q->serial) {
- der_free_heim_integer(q->serial);
- free(q->serial);
- }
- q->serial = malloc(sizeof(*q->serial));
- if (q->serial == NULL)
- return ENOMEM;
- ret = der_copy_heim_integer(serialNumber, q->serial);
- if (ret) {
- free(q->serial);
- q->serial = NULL;
- return ret;
- }
- if (q->issuer_name) {
- free_Name(q->issuer_name);
- free(q->issuer_name);
- }
- q->issuer_name = malloc(sizeof(*q->issuer_name));
- if (q->issuer_name == NULL)
- return ENOMEM;
- ret = copy_Name(issuer, q->issuer_name);
- if (ret) {
- free(q->issuer_name);
- q->issuer_name = NULL;
- return ret;
- }
- q->match |= HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
- return 0;
-}
-
-/**
- * Set the query controller to match on a friendly name
- *
- * @param q a hx509 query controller.
- * @param name a friendly name to match on
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_query_match_friendly_name(hx509_query *q, const char *name)
-{
- if (q->friendlyname)
- free(q->friendlyname);
- q->friendlyname = strdup(name);
- if (q->friendlyname == NULL)
- return ENOMEM;
- q->match |= HX509_QUERY_MATCH_FRIENDLY_NAME;
- return 0;
-}
-
-/**
- * Set the query controller to match using a specific match function.
- *
- * @param q a hx509 query controller.
- * @param func function to use for matching, if the argument is NULL,
- * the match function is removed.
- * @param ctx context passed to the function.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_query_match_cmp_func(hx509_query *q,
- int (*func)(void *, hx509_cert),
- void *ctx)
-{
- if (func)
- q->match |= HX509_QUERY_MATCH_FUNCTION;
- else
- q->match &= ~HX509_QUERY_MATCH_FUNCTION;
- q->cmp_func = func;
- q->cmp_func_ctx = ctx;
- return 0;
-}
-
-/**
- * Free the query controller.
- *
- * @param context A hx509 context.
- * @param q a pointer to the query controller.
- *
- * @ingroup hx509_cert
- */
-
-void
-hx509_query_free(hx509_context context, hx509_query *q)
-{
- if (q->serial) {
- der_free_heim_integer(q->serial);
- free(q->serial);
- q->serial = NULL;
- }
- if (q->issuer_name) {
- free_Name(q->issuer_name);
- free(q->issuer_name);
- q->issuer_name = NULL;
- }
- if (q) {
- free(q->friendlyname);
- memset(q, 0, sizeof(*q));
- }
- free(q);
-}
-
-int
-_hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert)
-{
- Certificate *c = _hx509_get_cert(cert);
-
- _hx509_query_statistic(context, 1, q);
-
- if ((q->match & HX509_QUERY_FIND_ISSUER_CERT) &&
- _hx509_cert_is_parent_cmp(q->subject, c, 0) != 0)
- return 0;
-
- if ((q->match & HX509_QUERY_MATCH_CERTIFICATE) &&
- _hx509_Certificate_cmp(q->certificate, c) != 0)
- return 0;
-
- if ((q->match & HX509_QUERY_MATCH_SERIALNUMBER)
- && der_heim_integer_cmp(&c->tbsCertificate.serialNumber, q->serial) != 0)
- return 0;
-
- if ((q->match & HX509_QUERY_MATCH_ISSUER_NAME)
- && _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name) != 0)
- return 0;
-
- if ((q->match & HX509_QUERY_MATCH_SUBJECT_NAME)
- && _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name) != 0)
- return 0;
-
- if (q->match & HX509_QUERY_MATCH_SUBJECT_KEY_ID) {
- SubjectKeyIdentifier si;
- int ret;
-
- ret = _hx509_find_extension_subject_key_id(c, &si);
- if (ret == 0) {
- if (der_heim_octet_string_cmp(&si, q->subject_id) != 0)
- ret = 1;
- free_SubjectKeyIdentifier(&si);
- }
- if (ret)
- return 0;
- }
- if ((q->match & HX509_QUERY_MATCH_ISSUER_ID))
- return 0;
- if ((q->match & HX509_QUERY_PRIVATE_KEY) &&
- _hx509_cert_private_key(cert) == NULL)
- return 0;
-
- {
- unsigned ku = 0;
- if (q->match & HX509_QUERY_KU_DIGITALSIGNATURE)
- ku |= (1 << 0);
- if (q->match & HX509_QUERY_KU_NONREPUDIATION)
- ku |= (1 << 1);
- if (q->match & HX509_QUERY_KU_ENCIPHERMENT)
- ku |= (1 << 2);
- if (q->match & HX509_QUERY_KU_DATAENCIPHERMENT)
- ku |= (1 << 3);
- if (q->match & HX509_QUERY_KU_KEYAGREEMENT)
- ku |= (1 << 4);
- if (q->match & HX509_QUERY_KU_KEYCERTSIGN)
- ku |= (1 << 5);
- if (q->match & HX509_QUERY_KU_CRLSIGN)
- ku |= (1 << 6);
- if (ku && check_key_usage(context, c, ku, TRUE))
- return 0;
- }
- if ((q->match & HX509_QUERY_ANCHOR))
- return 0;
-
- if (q->match & HX509_QUERY_MATCH_LOCAL_KEY_ID) {
- hx509_cert_attribute a;
-
- a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_localKeyId());
- if (a == NULL)
- return 0;
- if (der_heim_octet_string_cmp(&a->data, q->local_key_id) != 0)
- return 0;
- }
-
- if (q->match & HX509_QUERY_NO_MATCH_PATH) {
- size_t i;
-
- for (i = 0; i < q->path->len; i++)
- if (hx509_cert_cmp(q->path->val[i], cert) == 0)
- return 0;
- }
- if (q->match & HX509_QUERY_MATCH_FRIENDLY_NAME) {
- const char *name = hx509_cert_get_friendly_name(cert);
- if (name == NULL)
- return 0;
- if (strcasecmp(q->friendlyname, name) != 0)
- return 0;
- }
- if (q->match & HX509_QUERY_MATCH_FUNCTION) {
- int ret = (*q->cmp_func)(q->cmp_func_ctx, cert);
- if (ret != 0)
- return 0;
- }
-
- if (q->match & HX509_QUERY_MATCH_KEY_HASH_SHA1) {
- heim_octet_string os;
- int ret;
-
- os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
- os.length =
- c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
-
- ret = _hx509_verify_signature(context,
- NULL,
- hx509_signature_sha1(),
- &os,
- q->keyhash_sha1);
- if (ret != 0)
- return 0;
- }
-
- if (q->match & HX509_QUERY_MATCH_TIME) {
- time_t t;
- t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
- if (t > q->timenow)
- return 0;
- t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
- if (t < q->timenow)
- return 0;
- }
-
- if (q->match & ~HX509_QUERY_MASK)
- return 0;
-
- return 1;
-}
-
-/**
- * Set a statistic file for the query statistics.
- *
- * @param context A hx509 context.
- * @param fn statistics file name
- *
- * @ingroup hx509_cert
- */
-
-void
-hx509_query_statistic_file(hx509_context context, const char *fn)
-{
- if (context->querystat)
- free(context->querystat);
- context->querystat = strdup(fn);
-}
-
-void
-_hx509_query_statistic(hx509_context context, int type, const hx509_query *q)
-{
- FILE *f;
- if (context->querystat == NULL)
- return;
- f = fopen(context->querystat, "a");
- if (f == NULL)
- return;
- fprintf(f, "%d %d\n", type, q->match);
- fclose(f);
-}
-
-static const char *statname[] = {
- "find issuer cert",
- "match serialnumber",
- "match issuer name",
- "match subject name",
- "match subject key id",
- "match issuer id",
- "private key",
- "ku encipherment",
- "ku digitalsignature",
- "ku keycertsign",
- "ku crlsign",
- "ku nonrepudiation",
- "ku keyagreement",
- "ku dataencipherment",
- "anchor",
- "match certificate",
- "match local key id",
- "no match path",
- "match friendly name",
- "match function",
- "match key hash sha1",
- "match time"
-};
-
-struct stat_el {
- unsigned long stats;
- unsigned int index;
-};
-
-
-static int
-stat_sort(const void *a, const void *b)
-{
- const struct stat_el *ae = a;
- const struct stat_el *be = b;
- return be->stats - ae->stats;
-}
-
-/**
- * Unparse the statistics file and print the result on a FILE descriptor.
- *
- * @param context A hx509 context.
- * @param printtype tyep to print
- * @param out the FILE to write the data on.
- *
- * @ingroup hx509_cert
- */
-
-void
-hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)
-{
- rtbl_t t;
- FILE *f;
- int type, mask, i, num;
- unsigned long multiqueries = 0, totalqueries = 0;
- struct stat_el stats[32];
-
- if (context->querystat == NULL)
- return;
- f = fopen(context->querystat, "r");
- if (f == NULL) {
- fprintf(out, "No statistic file %s: %s.\n",
- context->querystat, strerror(errno));
- return;
- }
-
- for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {
- stats[i].index = i;
- stats[i].stats = 0;
- }
-
- while (fscanf(f, "%d %d\n", &type, &mask) == 2) {
- if (type != printtype)
- continue;
- num = i = 0;
- while (mask && i < sizeof(stats)/sizeof(stats[0])) {
- if (mask & 1) {
- stats[i].stats++;
- num++;
- }
- mask = mask >>1 ;
- i++;
- }
- if (num > 1)
- multiqueries++;
- totalqueries++;
- }
- fclose(f);
-
- qsort(stats, sizeof(stats)/sizeof(stats[0]), sizeof(stats[0]), stat_sort);
-
- t = rtbl_create();
- if (t == NULL)
- errx(1, "out of memory");
-
- rtbl_set_separator (t, " ");
-
- rtbl_add_column_by_id (t, 0, "Name", 0);
- rtbl_add_column_by_id (t, 1, "Counter", 0);
-
-
- for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {
- char str[10];
-
- if (stats[i].index < sizeof(statname)/sizeof(statname[0]))
- rtbl_add_column_entry_by_id (t, 0, statname[stats[i].index]);
- else {
- snprintf(str, sizeof(str), "%d", stats[i].index);
- rtbl_add_column_entry_by_id (t, 0, str);
- }
- snprintf(str, sizeof(str), "%lu", stats[i].stats);
- rtbl_add_column_entry_by_id (t, 1, str);
- }
-
- rtbl_format(t, out);
- rtbl_destroy(t);
-
- fprintf(out, "\nQueries: multi %lu total %lu\n",
- multiqueries, totalqueries);
-}
-
-/**
- * Check the extended key usage on the hx509 certificate.
- *
- * @param context A hx509 context.
- * @param cert A hx509 context.
- * @param eku the EKU to check for
- * @param allow_any_eku if the any EKU is set, allow that to be a
- * substitute.
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_check_eku(hx509_context context, hx509_cert cert,
- const heim_oid *eku, int allow_any_eku)
-{
- ExtKeyUsage e;
- int ret, i;
-
- ret = find_extension_eku(_hx509_get_cert(cert), &e);
- if (ret) {
- hx509_clear_error_string(context);
- return ret;
- }
-
- for (i = 0; i < e.len; i++) {
- if (der_heim_oid_cmp(eku, &e.val[i]) == 0) {
- free_ExtKeyUsage(&e);
- return 0;
- }
- if (allow_any_eku) {
-#if 0
- if (der_heim_oid_cmp(id_any_eku, &e.val[i]) == 0) {
- free_ExtKeyUsage(&e);
- return 0;
- }
-#endif
- }
- }
- free_ExtKeyUsage(&e);
- hx509_clear_error_string(context);
- return HX509_CERTIFICATE_MISSING_EKU;
-}
-
-int
-_hx509_cert_get_keyusage(hx509_context context,
- hx509_cert c,
- KeyUsage *ku)
-{
- Certificate *cert;
- const Extension *e;
- size_t size;
- int ret, i = 0;
-
- memset(ku, 0, sizeof(*ku));
-
- cert = _hx509_get_cert(c);
-
- if (_hx509_cert_get_version(cert) < 3)
- return 0;
-
- e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i);
- if (e == NULL)
- return HX509_KU_CERT_MISSING;
-
- ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, ku, &size);
- if (ret)
- return ret;
- return 0;
-}
-
-int
-_hx509_cert_get_eku(hx509_context context,
- hx509_cert cert,
- ExtKeyUsage *e)
-{
- int ret;
-
- memset(e, 0, sizeof(*e));
-
- ret = find_extension_eku(_hx509_get_cert(cert), e);
- if (ret && ret != HX509_EXTENSION_NOT_FOUND) {
- hx509_clear_error_string(context);
- return ret;
- }
- return 0;
-}
-
-/**
- * Encodes the hx509 certificate as a DER encode binary.
- *
- * @param context A hx509 context.
- * @param c the certificate to encode.
- * @param os the encode certificate, set to NULL, 0 on case of
- * error. Free the returned structure with hx509_xfree().
- *
- * @return An hx509 error code, see hx509_get_error_string().
- *
- * @ingroup hx509_cert
- */
-
-int
-hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os)
-{
- size_t size;
- int ret;
-
- os->data = NULL;
- os->length = 0;
-
- ASN1_MALLOC_ENCODE(Certificate, os->data, os->length,
- _hx509_get_cert(c), &size, ret);
- if (ret) {
- os->data = NULL;
- os->length = 0;
- return ret;
- }
- if (os->length != size)
- _hx509_abort("internal ASN.1 encoder error");
-
- return ret;
-}
-
-/*
- * Last to avoid lost __attribute__s due to #undef.
- */
-
-#undef __attribute__
-#define __attribute__(X)
-
-void
-_hx509_abort(const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 1, 2)))
-{
- va_list ap;
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
- printf("\n");
- fflush(stdout);
- abort();
-}
-
-/**
- * Free a data element allocated in the library.
- *
- * @param ptr data to be freed.
- *
- * @ingroup hx509_misc
- */
-
-void
-hx509_xfree(void *ptr)
-{
- free(ptr);
-}
OpenPOWER on IntegriCloud