summaryrefslogtreecommitdiff
path: root/source4/heimdal/lib/hx509/revoke.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/lib/hx509/revoke.c')
-rw-r--r--source4/heimdal/lib/hx509/revoke.c398
1 files changed, 331 insertions, 67 deletions
diff --git a/source4/heimdal/lib/hx509/revoke.c b/source4/heimdal/lib/hx509/revoke.c
index ddcb17ee38..2010f945f0 100644
--- a/source4/heimdal/lib/hx509/revoke.c
+++ b/source4/heimdal/lib/hx509/revoke.c
@@ -31,14 +31,33 @@
* SUCH DAMAGE.
*/
+/**
+ * @page page_revoke Revocation methods
+ *
+ * There are two revocation method for PKIX/X.509: CRL and OCSP.
+ * Revocation is needed if the private key is lost and
+ * stolen. Depending on how picky you are, you might want to make
+ * revocation for destroyed private keys too (smartcard broken), but
+ * that should not be a problem.
+ *
+ * CRL is a list of certifiates that have expired.
+ *
+ * OCSP is an online checking method where the requestor sends a list
+ * of certificates to the OCSP server to return a signed reply if they
+ * are valid or not. Some services sends a OCSP reply as part of the
+ * hand-shake to make the revoktion decision simpler/faster for the
+ * client.
+ */
+
#include "hx_locl.h"
-RCSID("$Id: revoke.c 21153 2007-06-18 21:55:46Z lha $");
+RCSID("$Id: revoke.c 22583 2008-02-11 20:46:21Z lha $");
struct revoke_crl {
char *path;
time_t last_modfied;
CRLCertificateList crl;
int verified;
+ int failed_verify;
};
struct revoke_ocsp {
@@ -51,6 +70,7 @@ struct revoke_ocsp {
struct hx509_revoke_ctx_data {
+ unsigned ref;
struct {
struct revoke_crl *val;
size_t len;
@@ -61,6 +81,17 @@ struct hx509_revoke_ctx_data {
} ocsps;
};
+/**
+ * Allocate a revokation context. Free with hx509_revoke_free().
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a newly allocated revokation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
{
@@ -68,6 +99,7 @@ hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
if (*ctx == NULL)
return ENOMEM;
+ (*ctx)->ref = 1;
(*ctx)->crls.len = 0;
(*ctx)->crls.val = NULL;
(*ctx)->ocsps.len = 0;
@@ -76,6 +108,19 @@ hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
return 0;
}
+hx509_revoke_ctx
+_hx509_revoke_ref(hx509_revoke_ctx ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ if (ctx->ref <= 0)
+ _hx509_abort("revoke ctx refcount <= 0");
+ ctx->ref++;
+ if (ctx->ref == 0)
+ _hx509_abort("revoke ctx refcount == 0");
+ return ctx;
+}
+
static void
free_ocsp(struct revoke_ocsp *ocsp)
{
@@ -85,6 +130,14 @@ free_ocsp(struct revoke_ocsp *ocsp)
hx509_cert_free(ocsp->signer);
}
+/**
+ * Free a hx509 revokation context.
+ *
+ * @param ctx context to be freed
+ *
+ * @ingroup hx509_revoke
+ */
+
void
hx509_revoke_free(hx509_revoke_ctx *ctx)
{
@@ -93,6 +146,11 @@ hx509_revoke_free(hx509_revoke_ctx *ctx)
if (ctx == NULL || *ctx == NULL)
return;
+ if ((*ctx)->ref <= 0)
+ _hx509_abort("revoke ctx refcount <= 0 on free");
+ if (--(*ctx)->ref > 0)
+ return;
+
for (i = 0; i < (*ctx)->crls.len; i++) {
free((*ctx)->crls.val[i].path);
free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
@@ -150,7 +208,7 @@ verify_ocsp(hx509_context context,
/*
* If signer certificate isn't the CA certificate, lets check the
- * its the CA that signed the signer certificate and the OCSP EKU
+ * it is the CA that signed the signer certificate and the OCSP EKU
* is set.
*/
if (hx509_cert_cmp(signer, parent) != 0) {
@@ -324,6 +382,18 @@ load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
return 0;
}
+/**
+ * Add a OCSP file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_add_ocsp(hx509_context context,
hx509_revoke_ctx ctx,
@@ -380,6 +450,7 @@ hx509_revoke_add_ocsp(hx509_context context,
static int
verify_crl(hx509_context context,
+ hx509_revoke_ctx ctx,
CRLCertificateList *crl,
time_t time_now,
hx509_certs certs,
@@ -391,52 +462,44 @@ verify_crl(hx509_context context,
int ret;
t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
- if (t > time_now)
+ if (t > time_now) {
+ hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
+ "CRL used before time");
return HX509_CRL_USED_BEFORE_TIME;
+ }
- if (crl->tbsCertList.nextUpdate == NULL)
+ if (crl->tbsCertList.nextUpdate == NULL) {
+ hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
+ "CRL missing nextUpdate");
return HX509_CRL_INVALID_FORMAT;
+ }
t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
- if (t < time_now)
+ if (t < time_now) {
+ hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
+ "CRL used after time");
return HX509_CRL_USED_AFTER_TIME;
+ }
_hx509_query_clear(&q);
- q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
- q.subject_name = &crl->tbsCertList.issuer;
+ /*
+ * If it's the signer have CRLSIGN bit set, use that as the signer
+ * cert for the certificate, otherwise, search for a certificate.
+ */
+ if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
+ signer = hx509_cert_ref(parent);
+ } else {
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.match |= HX509_QUERY_KU_CRLSIGN;
+ q.subject_name = &crl->tbsCertList.issuer;
- ret = hx509_certs_find(context, certs, &q, &signer);
- if (ret)
- return ret;
-
- /* verify is parent or CRLsigner */
- if (hx509_cert_cmp(signer, parent) != 0) {
- Certificate *p = _hx509_get_cert(parent);
- Certificate *s = _hx509_get_cert(signer);
-
- ret = _hx509_cert_is_parent_cmp(s, p, 0);
- if (ret != 0) {
- ret = HX509_PARENT_NOT_CA;
- hx509_set_error_string(context, 0, ret, "Revoke CRL signer is "
- "doesn't have CA as signer certificate");
- goto out;
- }
-
- ret = _hx509_verify_signature_bitstring(context,
- p,
- &s->signatureAlgorithm,
- &s->tbsCertificate._save,
- &s->signatureValue);
+ ret = hx509_certs_find(context, certs, &q, &signer);
if (ret) {
hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
- "CRL signer signature invalid");
- goto out;
+ "Failed to find certificate for CRL");
+ return ret;
}
-
- ret = _hx509_check_key_usage(context, signer, 1 << 6, TRUE); /* crl */
- if (ret != 0)
- goto out;
}
ret = _hx509_verify_signature_bitstring(context,
@@ -450,6 +513,44 @@ verify_crl(hx509_context context,
goto out;
}
+ /*
+ * If signer is not CA cert, need to check revoke status of this
+ * CRL signing cert too, this include all parent CRL signer cert
+ * up to the root *sigh*, assume root at least hve CERTSIGN flag
+ * set.
+ */
+ while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
+ hx509_cert crl_parent;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.match |= HX509_QUERY_KU_CRLSIGN;
+ q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
+
+ ret = hx509_certs_find(context, certs, &q, &crl_parent);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to find parent of CRL signer");
+ goto out;
+ }
+
+ ret = hx509_revoke_verify(context,
+ ctx,
+ certs,
+ time_now,
+ signer,
+ crl_parent);
+ hx509_cert_free(signer);
+ signer = crl_parent;
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify revoke "
+ "status of CRL signer");
+ goto out;
+ }
+ }
+
out:
hx509_cert_free(signer);
@@ -485,6 +586,18 @@ load_crl(const char *path, time_t *t, CRLCertificateList *crl)
return 0;
}
+/**
+ * Add a CRL file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_add_crl(hx509_context context,
hx509_revoke_ctx ctx,
@@ -537,6 +650,23 @@ hx509_revoke_add_crl(hx509_context context,
return ret;
}
+/**
+ * Check that a certificate is not expired according to a revokation
+ * context. Also need the parent certificte to the check OCSP
+ * parent identifier.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param certs
+ * @param now
+ * @param cert
+ * @param parent_cert
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_verify(hx509_context context,
@@ -551,6 +681,8 @@ hx509_revoke_verify(hx509_context context,
unsigned long i, j, k;
int ret;
+ hx509_clear_error_string(context);
+
for (i = 0; i < ctx->ocsps.len; i++) {
struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
struct stat sb;
@@ -604,6 +736,10 @@ hx509_revoke_verify(hx509_context context,
case choice_OCSPCertStatus_good:
break;
case choice_OCSPCertStatus_revoked:
+ hx509_set_error_string(context, 0,
+ HX509_CERT_REVOKED,
+ "Certificate revoked by issuer in OCSP");
+ return HX509_CERT_REVOKED;
case choice_OCSPCertStatus_unknown:
continue;
}
@@ -613,7 +749,7 @@ hx509_revoke_verify(hx509_context context,
now + context->ocsp_time_diff)
continue;
- /* don't allow the next updte to be in the past */
+ /* don't allow the next update to be in the past */
if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
continue;
@@ -627,11 +763,12 @@ hx509_revoke_verify(hx509_context context,
for (i = 0; i < ctx->crls.len; i++) {
struct revoke_crl *crl = &ctx->crls.val[i];
struct stat sb;
+ int diff;
/* check if cert.issuer == crls.val[i].crl.issuer */
ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
- &crl->crl.tbsCertList.issuer);
- if (ret)
+ &crl->crl.tbsCertList.issuer, &diff);
+ if (ret || diff)
continue;
ret = stat(crl->path, &sb);
@@ -643,21 +780,32 @@ hx509_revoke_verify(hx509_context context,
free_CRLCertificateList(&crl->crl);
crl->crl = cl;
crl->verified = 0;
+ crl->failed_verify = 0;
}
}
+ if (crl->failed_verify)
+ continue;
/* verify signature in crl if not already done */
if (crl->verified == 0) {
- ret = verify_crl(context, &crl->crl, now, certs, parent_cert);
- if (ret)
- return ret;
+ ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
+ if (ret) {
+ crl->failed_verify = 1;
+ continue;
+ }
crl->verified = 1;
}
-
- if (crl->crl.tbsCertList.crlExtensions)
- for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++)
- if (crl->crl.tbsCertList.crlExtensions->val[j].critical)
+
+ if (crl->crl.tbsCertList.crlExtensions) {
+ for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
+ if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
+ hx509_set_error_string(context, 0,
+ HX509_CRL_UNKNOWN_EXTENSION,
+ "Unknown CRL extension");
return HX509_CRL_UNKNOWN_EXTENSION;
+ }
+ }
+ }
if (crl->crl.tbsCertList.revokedCertificates == NULL)
return 0;
@@ -667,7 +815,7 @@ hx509_revoke_verify(hx509_context context,
time_t t;
ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
- &c->tbsCertificate.serialNumber);
+ &c->tbsCertificate.serialNumber);
if (ret != 0)
continue;
@@ -680,7 +828,10 @@ hx509_revoke_verify(hx509_context context,
if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
return HX509_CRL_UNKNOWN_EXTENSION;
- return HX509_CRL_CERT_REVOKED;
+ hx509_set_error_string(context, 0,
+ HX509_CERT_REVOKED,
+ "Certificate revoked by issuer in CRL");
+ return HX509_CERT_REVOKED;
}
return 0;
@@ -689,6 +840,10 @@ hx509_revoke_verify(hx509_context context,
if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
return 0;
+ hx509_set_error_string(context, HX509_ERROR_APPEND,
+ HX509_REVOKE_STATUS_MISSING,
+ "No revoke status found for "
+ "certificates");
return HX509_REVOKE_STATUS_MISSING;
}
@@ -785,6 +940,22 @@ out:
return ret;
}
+/**
+ * Create an OCSP request for a set of certificates.
+ *
+ * @param context a hx509 context
+ * @param reqcerts list of certificates to request ocsp data for
+ * @param pool certificate pool to use when signing
+ * @param signer certificate to use to sign the request
+ * @param digest the signing algorithm in the request, if NULL use the
+ * default signature algorithm,
+ * @param request the encoded request, free with free_heim_octet_string().
+ * @param nonce nonce in the request, free with free_heim_octet_string().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
int
hx509_ocsp_request(hx509_context context,
@@ -813,41 +984,49 @@ hx509_ocsp_request(hx509_context context,
ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx);
hx509_cert_free(ctx.parent);
- if (ret) {
- free_OCSPRequest(&req);
- return ret;
- }
+ if (ret)
+ goto out;
if (nonce) {
-
req.tbsRequest.requestExtensions =
calloc(1, sizeof(*req.tbsRequest.requestExtensions));
if (req.tbsRequest.requestExtensions == NULL) {
- free_OCSPRequest(&req);
- return ENOMEM;
+ ret = ENOMEM;
+ goto out;
}
es = req.tbsRequest.requestExtensions;
- es->len = 1;
es->val = calloc(es->len, sizeof(es->val[0]));
+ if (es->val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ es->len = 1;
ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID);
- if (ret)
- abort();
-
+ if (ret) {
+ free_OCSPRequest(&req);
+ return ret;
+ }
+
es->val[0].extnValue.data = malloc(10);
if (es->val[0].extnValue.data == NULL) {
- free_OCSPRequest(&req);
- return ENOMEM;
+ ret = ENOMEM;
+ goto out;
}
es->val[0].extnValue.length = 10;
ret = RAND_bytes(es->val[0].extnValue.data,
es->val[0].extnValue.length);
if (ret != 1) {
- free_OCSPRequest(&req);
- return HX509_CRYPTO_INTERNAL_ERROR;
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+ ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
+ if (ret) {
+ ret = ENOMEM;
+ goto out;
}
}
@@ -855,12 +1034,15 @@ hx509_ocsp_request(hx509_context context,
&req, &size, ret);
free_OCSPRequest(&req);
if (ret)
- return ret;
+ goto out;
if (size != request->length)
_hx509_abort("internal ASN.1 encoder error");
-
return 0;
+
+out:
+ free_OCSPRequest(&req);
+ return ret;
}
static char *
@@ -872,6 +1054,18 @@ printable_time(time_t t)
return s;
}
+/**
+ * Print the OCSP reply stored in a file.
+ *
+ * @param context a hx509 context
+ * @param path path to a file with a OCSP reply
+ * @param out the out FILE descriptor to print the reply on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
{
@@ -959,10 +1153,23 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
return ret;
}
-/*
- * Verify that the `cert' is part of the OCSP reply and its not
- * expired. Doesn't verify signature the OCSP reply or its done by a
+/**
+ * Verify that the certificate is part of the OCSP reply and it's not
+ * expired. Doesn't verify signature the OCSP reply or it's done by a
* authorized sender, that is assumed to be already done.
+ *
+ * @param context a hx509 context
+ * @param now the time right now, if 0, use the current time.
+ * @param cert the certificate to verify
+ * @param flags flags control the behavior
+ * @param data pointer to the encode ocsp reply
+ * @param length the length of the encode ocsp reply
+ * @param expiration return the time the OCSP will expire and need to
+ * be rechecked.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
*/
int
@@ -1062,6 +1269,17 @@ struct hx509_crl {
time_t expire;
};
+/**
+ * Create a CRL context. Use hx509_crl_free() to free the CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl return pointer to a newly allocated CRL context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_alloc(hx509_context context, hx509_crl *crl)
{
@@ -1083,6 +1301,18 @@ hx509_crl_alloc(hx509_context context, hx509_crl *crl)
return ret;
}
+/**
+ * Add revoked certificate to an CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl the CRL to add the revoked certificate to.
+ * @param certs keyset of certificate to revoke.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_add_revoked_certs(hx509_context context,
hx509_crl crl,
@@ -1091,6 +1321,19 @@ hx509_crl_add_revoked_certs(hx509_context context,
return hx509_certs_merge(context, crl->revoked, certs);
}
+/**
+ * Set the lifetime of a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context
+ * @param delta delta time the certificate is valid, library adds the
+ * current time to this.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
{
@@ -1098,6 +1341,14 @@ hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
return 0;
}
+/**
+ * Free a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context to free.
+ *
+ * @ingroup hx509_verify
+ */
void
hx509_crl_free(hx509_context context, hx509_crl *crl)
@@ -1144,6 +1395,19 @@ add_revoked(hx509_context context, void *ctx, hx509_cert cert)
return 0;
}
+/**
+ * Sign a CRL and return an encode certificate.
+ *
+ * @param context a hx509 context.
+ * @param signer certificate to sign the CRL with
+ * @param crl the CRL to sign
+ * @param os return the signed and encoded CRL, free with
+ * free_heim_octet_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
int
hx509_crl_sign(hx509_context context,