diff options
Diffstat (limited to 'crypto/heimdal/lib/hx509/cert.c')
-rw-r--r-- | crypto/heimdal/lib/hx509/cert.c | 3108 |
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); -} |