diff options
Diffstat (limited to 'source4/heimdal/lib/hx509/cert.c')
-rw-r--r-- | source4/heimdal/lib/hx509/cert.c | 878 |
1 files changed, 767 insertions, 111 deletions
diff --git a/source4/heimdal/lib/hx509/cert.c b/source4/heimdal/lib/hx509/cert.c index b7f19d152a..09c85bc084 100644 --- a/source4/heimdal/lib/hx509/cert.c +++ b/source4/heimdal/lib/hx509/cert.c @@ -32,10 +32,25 @@ */ #include "hx_locl.h" -RCSID("$Id: cert.c 21380 2007-06-28 07:38:38Z lha $"); +RCSID("$Id: cert.c 22583 2008-02-11 20:46:21Z 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; @@ -78,8 +93,16 @@ typedef struct 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 @@ -113,6 +136,19 @@ hx509_context_init(hx509_context *context) 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) { @@ -122,6 +158,14 @@ hx509_context_set_missing_revoke(hx509_context context, int flag) 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) { @@ -139,7 +183,6 @@ hx509_context_free(hx509_context *context) *context = NULL; } - /* * */ @@ -154,39 +197,25 @@ _hx509_get_cert(hx509_cert cert) * */ -#if 0 -void -_hx509_print_cert_subject(hx509_cert cert) -{ - char *subject_name; - hx509_name name; - int ret; - - ret = hx509_cert_get_subject(cert, &name); - if (ret) - abort(); - - ret = hx509_name_to_string(name, &subject_name); - hx509_name_free(&name); - if (ret) - abort(); - - printf("name: %s\n", subject_name); - - free(subject_name); -} -#endif - -/* - * - */ - 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) { @@ -218,9 +247,29 @@ hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert) 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, + const void *ptr, size_t len, hx509_cert *cert) { @@ -265,6 +314,15 @@ _hx509_cert_assign_key(hx509_cert cert, hx509_private_key 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) { @@ -274,7 +332,7 @@ hx509_cert_free(hx509_cert cert) return; if (cert->ref <= 0) - _hx509_abort("refcount <= 0"); + _hx509_abort("cert refcount <= 0 on free"); if (--cert->ref > 0) return; @@ -300,9 +358,21 @@ hx509_cert_free(hx509_cert 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++; @@ -311,6 +381,18 @@ hx509_cert_ref(hx509_cert cert) 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) { @@ -327,26 +409,75 @@ hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx) 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) + 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 = 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) { - ctx->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) { @@ -354,12 +485,32 @@ hx509_verify_set_time(hx509_verify_ctx ctx, time_t t) 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) { @@ -369,6 +520,17 @@ hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean) 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) { @@ -378,6 +540,20 @@ hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean) 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) { @@ -512,6 +688,15 @@ add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry) 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) { @@ -523,8 +708,26 @@ hx509_free_octet_string_list(hx509_octet_string_list *list) 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_cert cert, +hx509_cert_find_subjectAltName_otherName(hx509_context context, + hx509_cert cert, const heim_oid *oid, hx509_octet_string_list *list) { @@ -541,9 +744,11 @@ hx509_cert_find_subjectAltName_otherName(hx509_cert cert, if (ret == HX509_EXTENSION_NOT_FOUND) { ret = 0; break; - } else if (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 && @@ -551,6 +756,10 @@ hx509_cert_find_subjectAltName_otherName(hx509_cert cert, { 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; } @@ -558,7 +767,7 @@ hx509_cert_find_subjectAltName_otherName(hx509_cert cert, } free_GeneralNames(&sa); } - return ret; + return 0; } @@ -605,6 +814,12 @@ check_key_usage(hx509_context context, const Certificate *cert, 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) @@ -678,10 +893,13 @@ _hx509_cert_is_parent_cmp(const Certificate *subject, int diff; AuthorityKeyIdentifier ai; SubjectKeyIdentifier si; - int ret_ai, ret_si; + int ret_ai, ret_si, ret; - diff = _hx509_name_cmp(&issuer->tbsCertificate.subject, - &subject->tbsCertificate.issuer); + ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, + &subject->tbsCertificate.issuer, + &diff); + if (ret) + return ret; if (diff) return diff; @@ -689,7 +907,7 @@ _hx509_cert_is_parent_cmp(const Certificate *subject, memset(&si, 0, sizeof(si)); /* - * Try to find AuthorityKeyIdentifier, if its not present in the + * Try to find AuthorityKeyIdentifier, if it's not present in the * subject certificate nor the parent. */ @@ -736,8 +954,11 @@ _hx509_cert_is_parent_cmp(const Certificate *subject, name.u.rdnSequence = ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence; - diff = _hx509_name_cmp(&issuer->tbsCertificate.subject, - &name); + ret = _hx509_name_cmp(&issuer->tbsCertificate.subject, + &name, + &diff); + if (ret) + return ret; if (diff) return diff; diff = 0; @@ -776,13 +997,22 @@ certificate_is_anchor(hx509_context context, } static int -certificate_is_self_signed(const Certificate *cert) -{ - return _hx509_cert_is_parent_cmp(cert, cert, 1) == 0; +certificate_is_self_signed(hx509_context context, + const Certificate *cert, + int *self_signed) +{ + int ret, diff; + ret = _hx509_name_cmp(&cert->tbsCertificate.subject, + &cert->tbsCertificate.issuer, &diff); + *self_signed = (diff == 0); + if (ret) + hx509_set_error_string(context, 0, ret, + "Failed to check if self signed"); + return ret; } /* - * The subjectName is "null" when its empty set of relative DBs. + * The subjectName is "null" when it's empty set of relative DBs. */ static int @@ -1032,9 +1262,9 @@ _hx509_calculate_path(hx509_context context, return 0; } -static int -AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p, - const AlgorithmIdentifier *q) +int +_hx509_AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p, + const AlgorithmIdentifier *q) { int diff; diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm); @@ -1061,8 +1291,8 @@ _hx509_Certificate_cmp(const Certificate *p, const Certificate *q) diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue); if (diff) return diff; - diff = AlgorithmIdentifier_cmp(&p->signatureAlgorithm, - &q->signatureAlgorithm); + diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm, + &q->signatureAlgorithm); if (diff) return diff; diff = der_heim_octet_string_cmp(&p->tbsCertificate._save, @@ -1070,24 +1300,77 @@ _hx509_Certificate_cmp(const Certificate *p, const Certificate *q) 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) @@ -1104,31 +1387,107 @@ hx509_cert_get_base_subject(hx509_context context, hx509_cert c, 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(hx509_cert p, SubjectPublicKeyInfo *spki) +hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context, + hx509_cert p, + AlgorithmIdentifier *alg) { - return copy_SubjectPublicKeyInfo(&p->data->tbsCertificate.subjectPublicKeyInfo, - spki); + 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) { @@ -1136,6 +1495,13 @@ _hx509_cert_private_key(hx509_cert p) } 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) @@ -1253,9 +1619,14 @@ match_RDN(const RelativeDistinguishedName *c, return HX509_NAME_CONSTRAINT_ERROR; for (i = 0; i < n->len; i++) { + int diff, ret; + 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) + ret = _hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value, &diff); + if (ret) + return ret; + if (diff != 0) return HX509_NAME_CONSTRAINT_ERROR; } return 0; @@ -1316,7 +1687,7 @@ match_general_name(const GeneralName *c, const GeneralName *n, int *match) 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] != '.') + if (len1 < len2 && s[len2 - len1 + 1] != '.') return HX509_NAME_CONSTRAINT_ERROR; } *match = 1; @@ -1387,7 +1758,6 @@ match_alt_name(const GeneralName *n, const Certificate *c, } free_GeneralNames(&sa); } while (1); - return ret; } @@ -1457,7 +1827,10 @@ check_name_constraints(hx509_context context, } /* allow null subjectNames, they wont matches anything */ if (match == 0 && !subject_null_p(c)) { - hx509_clear_error_string(context); + hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS, + "Error verify constraints, " + "certificate didn't match any " + "permitted subtree"); return HX509_VERIFY_CONSTRAINTS; } } @@ -1469,7 +1842,10 @@ check_name_constraints(hx509_context context, return ret; } if (match) { - hx509_clear_error_string(context); + hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS, + "Error verify constraints, " + "certificate included in excluded " + "subtree"); return HX509_VERIFY_CONSTRAINTS; } } @@ -1487,6 +1863,21 @@ free_name_constraints(hx509_name_constraints *nc) 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, @@ -1495,10 +1886,7 @@ hx509_verify_path(hx509_context context, { hx509_name_constraints nc; hx509_path path; -#if 0 - const AlgorithmIdentifier *alg_id; -#endif - int ret, i, proxy_cert_depth; + int ret, i, proxy_cert_depth, selfsigned_depth, diff; enum certtype type; Name proxy_issuer; hx509_certs anchors = NULL; @@ -1538,10 +1926,6 @@ hx509_verify_path(hx509_context context, 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 @@ -1550,6 +1934,7 @@ hx509_verify_path(hx509_context context, */ proxy_cert_depth = 0; + selfsigned_depth = 0; if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE) type = PROXY_CERT; @@ -1570,6 +1955,7 @@ hx509_verify_path(hx509_context context, switch (type) { case CA_CERT: + /* XXX make constants for keyusage */ ret = check_key_usage(context, c, 1 << 5, REQUIRE_RFC3280(ctx) ? TRUE : FALSE); @@ -1578,6 +1964,18 @@ hx509_verify_path(hx509_context context, "Key usage missing from CA certificate"); goto out; } + + /* self signed cert doesn't add to path length */ + if (i + 1 != path.len) { + int selfsigned; + + ret = certificate_is_self_signed(context, c, &selfsigned); + if (ret) + goto out; + if (selfsigned) + selfsigned_depth++; + } + break; case PROXY_CERT: { ProxyCertInfo info; @@ -1624,8 +2022,12 @@ hx509_verify_path(hx509_context context, */ if (proxy_cert_depth) { - ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject); + ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject, &diff); if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (diff) { ret = HX509_PROXY_CERT_NAME_WRONG; hx509_set_error_string(context, 0, ret, "Base proxy name not right"); @@ -1658,8 +2060,12 @@ hx509_verify_path(hx509_context context, 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_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer, &diff); + if (ret) { + hx509_set_error_string(context, 0, ret, "Out of memory"); + goto out; + } + if (diff != 0) { ret = HX509_PROXY_CERT_NAME_WRONG; hx509_set_error_string(context, 0, ret, "Proxy issuer name not as expected"); @@ -1685,9 +2091,13 @@ hx509_verify_path(hx509_context context, */ if (proxy_cert_depth) { - ret = _hx509_name_cmp(&proxy_issuer, - &c->tbsCertificate.subject); + ret = _hx509_name_cmp(&proxy_issuer, + &c->tbsCertificate.subject, &diff); if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + goto out; + } + if (diff) { ret = HX509_PROXY_CERT_NAME_WRONG; hx509_clear_error_string(context); goto out; @@ -1705,7 +2115,8 @@ hx509_verify_path(hx509_context context, break; } - ret = check_basic_constraints(context, c, type, i - proxy_cert_depth); + ret = check_basic_constraints(context, c, type, + i - proxy_cert_depth - selfsigned_depth); if (ret) goto out; @@ -1742,22 +2153,16 @@ hx509_verify_path(hx509_context context, for (ret = 0, i = path.len - 1; i >= 0; i--) { Certificate *c; + int selfsigned; c = _hx509_get_cert(path.val[i]); -#if 0 - /* check that algorithm and parameters is the same */ - /* XXX this is wrong */ - ret = alg_cmp(&c->tbsCertificate.signature, alg_id); - if (ret) { - hx509_clear_error_string(context); - ret = HX509_PATH_ALGORITHM_CHANGED; + ret = certificate_is_self_signed(context, c, &selfsigned); + if (ret) goto out; - } -#endif /* verify name constraints, not for selfsigned and anchor */ - if (!certificate_is_self_signed(c) || i == path.len - 1) { + if (!selfsigned || i + 1 != path.len) { ret = check_name_constraints(context, &nc, c); if (ret) { goto out; @@ -1813,12 +2218,6 @@ hx509_verify_path(hx509_context context, hx509_certs_free(&certs); } -#if 0 - for (i = path.len - 1; i >= 0; i--) { - _hx509_print_cert_subject(path.val[i]); - } -#endif - /* * Verify signatures, do this backward so public key working * parameter is passed up from the anchor up though the chain. @@ -1830,11 +2229,17 @@ hx509_verify_path(hx509_context context, c = _hx509_get_cert(path.val[i]); /* is last in chain (trust anchor) */ - if (i == path.len - 1) { + if (i + 1 == path.len) { + int selfsigned; + signer = path.val[i]->data; + ret = certificate_is_self_signed(context, signer, &selfsigned); + if (ret) + goto out; + /* if trust anchor is not self signed, don't check sig */ - if (!certificate_is_self_signed(signer)) + if (!selfsigned) continue; } else { /* take next certificate in chain */ @@ -1863,6 +2268,20 @@ out: 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, @@ -1873,7 +2292,26 @@ hx509_verify_signature(hx509_context context, return _hx509_verify_signature(context, signer->data, alg, data, sig); } -#define HX509_VHN_F_ALLOW_NO_MATCH 1 + +/** + * 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, @@ -1984,6 +2422,19 @@ _hx509_set_cert_attribute(hx509_context context, 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) { @@ -1994,6 +2445,17 @@ hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid) 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) { @@ -2005,6 +2467,16 @@ hx509_cert_set_friendly_name(hx509_cert cert, const char *name) 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) @@ -2056,6 +2528,17 @@ _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) { @@ -2065,6 +2548,17 @@ hx509_query_alloc(hx509_context context, hx509_query **q) 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) { @@ -2087,6 +2581,19 @@ hx509_query_match_option(hx509_query *q, hx509_query_option option) } } +/** + * 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, @@ -2123,6 +2630,16 @@ hx509_query_match_issuer_serial(hx509_query *q, 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) @@ -2136,6 +2653,63 @@ hx509_query_match_friendly_name(hx509_query *q, const char *name) return 0; } +/** + * Set the query controller to require an one specific EKU (extended + * key usage). Any previous EKU matching is overwitten. If NULL is + * passed in as the eku, the EKU requirement is reset. + * + * @param q a hx509 query controller. + * @param eku an EKU to match on. + * + * @return An hx509 error code, see hx509_get_error_string(). + * + * @ingroup hx509_cert + */ + +int +hx509_query_match_eku(hx509_query *q, const heim_oid *eku) +{ + int ret; + + if (eku == NULL) { + if (q->eku) { + der_free_oid(q->eku); + free(q->eku); + q->eku = NULL; + } + q->match &= ~HX509_QUERY_MATCH_EKU; + } else { + if (q->eku) { + der_free_oid(q->eku); + } else { + q->eku = calloc(1, sizeof(*q->eku)); + if (q->eku == NULL) + return ENOMEM; + } + ret = der_copy_oid(eku, q->eku); + if (ret) { + free(q->eku); + q->eku = NULL; + return ret; + } + q->match |= HX509_QUERY_MATCH_EKU; + } + 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), @@ -2150,24 +2724,36 @@ hx509_query_match_cmp_func(hx509_query *q, 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 == NULL) + return; + 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)); + if (q->eku) { + der_free_oid(q->eku); + free(q->eku); } + if (q->friendlyname) + free(q->friendlyname); + memset(q, 0, sizeof(*q)); free(q); } @@ -2175,6 +2761,7 @@ int _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert) { Certificate *c = _hx509_get_cert(cert); + int ret, diff; _hx509_query_statistic(context, 1, q); @@ -2190,17 +2777,20 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert && 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_ISSUER_NAME) { + ret = _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name, &diff); + if (ret || diff) + 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_NAME) { + ret = _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name, &diff); + if (ret || diff) + 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) { @@ -2264,14 +2854,13 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert return 0; } if (q->match & HX509_QUERY_MATCH_FUNCTION) { - int ret = (*q->cmp_func)(q->cmp_func_ctx, cert); + 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 = @@ -2296,12 +2885,26 @@ _hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert return 0; } + /* If an EKU is required, check the cert for it. */ + if ((q->match & HX509_QUERY_MATCH_EKU) && + hx509_cert_check_eku(context, cert, q->eku, 0)) + 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) { @@ -2362,6 +2965,16 @@ stat_sort(const void *a, const void *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) { @@ -2435,6 +3048,20 @@ hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out) 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) @@ -2511,6 +3138,19 @@ _hx509_cert_get_eku(hx509_context context, 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) { @@ -2522,8 +3162,11 @@ hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os) ASN1_MALLOC_ENCODE(Certificate, os->data, os->length, _hx509_get_cert(c), &size, ret); - if (ret) + if (ret) { + os->data = NULL; + os->length = 0; return ret; + } if (os->length != size) _hx509_abort("internal ASN.1 encoder error"); @@ -2550,3 +3193,16 @@ _hx509_abort(const char *fmt, ...) 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); +} |