summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/hx509/cert.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/hx509/cert.c')
-rw-r--r--source4/heimdal/lib/hx509/cert.c878
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);
+}