From bfbc55c6cc3ae030df8ce4089f10dc0dbb682e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Love=20H=C3=B6rnquist=20=C3=85strand?= Date: Fri, 31 Mar 2006 22:14:12 +0000 Subject: [PATCH] Add ocsp checker. git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@16892 ec53bebd-3082-4978-b11e-865c3cabbd6b --- lib/hx509/revoke.c | 315 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 266 insertions(+), 49 deletions(-) diff --git a/lib/hx509/revoke.c b/lib/hx509/revoke.c index b079a9ccd..2d82f94f5 100644 --- a/lib/hx509/revoke.c +++ b/lib/hx509/revoke.c @@ -45,8 +45,8 @@ struct revoke_ocsp { char *path; time_t last_modfied; OCSPBasicOCSPResponse ocsp; - int verified; hx509_certs certs; + hx509_cert signer; }; @@ -101,9 +101,9 @@ static int verify_ocsp(hx509_context context, struct revoke_ocsp *ocsp, time_t time_now, - hx509_certs certs) + hx509_certs certs, + hx509_cert parent) { - heim_octet_string os; hx509_cert signer = NULL; hx509_query q; int ret; @@ -120,25 +120,49 @@ verify_ocsp(hx509_context context, goto out; } - os.data = ocsp->ocsp.signature.data; - os.length = ocsp->ocsp.signature.length / 8; - ret = hx509_certs_find(context, certs, &q, &signer); - if (ret) - ret = hx509_certs_find(context, ocsp->ocsp.certs, &q, &signer); + if (ret && ocsp->certs) + ret = hx509_certs_find(context, ocsp->certs, &q, &signer); if (ret) goto out; - ret = hx509_verify_signature(context, - signer, - &ocsp->ocsp.signatureAlgorithm, - &ocsp->ocsp.tbsResponseData._save, - &os); + /* + * If signer certificate isn't the CA certificate, lets check the + * its the CA that signed the signer certificate and the OCSP EKU + * is set. + */ + 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 = EINVAL; + goto out; + } + + ret = _hx509_verify_signature_bitstring(p, + &s->signatureAlgorithm, + &s->tbsCertificate._save, + &s->signatureValue); + if (ret) + goto out; + + ret = hx509_cert_check_eku(context, signer, + oid_id_kp_OCSPSigning(), 0); + if (ret) + goto out; + } + + ret = _hx509_verify_signature_bitstring(_hx509_get_cert(signer), + &ocsp->ocsp.signatureAlgorithm, + &ocsp->ocsp.tbsResponseData._save, + &ocsp->ocsp.signature); if (ret) goto out; - /* XXX verify signer is allowed to be OCSP signer */ - + ocsp->signer = signer; + signer = NULL; out: if (signer) hx509_cert_free(signer); @@ -153,6 +177,7 @@ out: static int load_ocsp(const char *path, time_t *t, OCSPBasicOCSPResponse *ocsp) { + OCSPResponse resp; size_t length, size; struct stat sb; void *data; @@ -166,16 +191,43 @@ load_ocsp(const char *path, time_t *t, OCSPBasicOCSPResponse *ocsp) *t = sb.st_mtime; - ret = decode_OCSPBasicOCSPResponse(data, length, ocsp, &size); + ret = decode_OCSPResponse(data, length, &resp, &size); _hx509_unmap_file(data, length); if (ret) return ret; - /* check signature is aligned */ - if (ocsp->signature.length & 7) { + switch (resp.responseStatus) { + case successful: + break; + default: + free_OCSPResponse(&resp); + return EINVAL; + } + + /* + * XXX find BasicReponse and act on it + */ + + if (resp.responseBytes == NULL) { + free_OCSPResponse(&resp); + return EINVAL; + } + + ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data, + resp.responseBytes->response.length, + ocsp, + &size); + if (ret) { + free_OCSPResponse(&resp); + return ret; + } + if (size != resp.responseBytes->response.length) { + free_OCSPResponse(&resp); free_OCSPBasicOCSPResponse(ocsp); return EINVAL; } + free_OCSPResponse(&resp); + return 0; } @@ -184,7 +236,6 @@ hx509_revoke_add_ocsp(hx509_context context, hx509_revoke_ctx revoke, const char *path) { - OCSPBasicOCSPResponse ocsp; void *data; int ret; size_t i; @@ -214,22 +265,38 @@ hx509_revoke_add_ocsp(hx509_context context, return ENOMEM; ret = load_ocsp(path, - &revoke->ocsps.val[revoke->ocsps.len].last_modfied, - &revoke->ocsps.val[revoke->ocsps.len].ocsp); + &revoke->ocsps.val[revoke->ocsps.len].last_modfied, + &revoke->ocsps.val[revoke->ocsps.len].ocsp); if (ret) { free(revoke->ocsps.val[revoke->ocsps.len].path); return ret; } + if (revoke->ocsps.val[revoke->ocsps.len].ocsp.certs) { + struct revoke_ocsp *ocsp = &revoke->ocsps.val[revoke->ocsps.len]; + int j; + + hx509_certs_free(&ocsp->certs); + + ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0, + NULL, &ocsp->certs); + if (ret == 0) { + for (j = 0; j < ocsp->ocsp.certs->len; j++) { + hx509_cert c; + + ret = hx509_cert_init(context, &ocsp->ocsp.certs->val[j], &c); + if (ret) + continue; + + ret = hx509_certs_add(context, ocsp->certs, c); + if (ret) + continue; + } + } + } revoke->ocsps.len++; - - if (ret) - return ret; - - free_OCSPBasicOCSPResponse(&ocsp); - - return 0; + return ret; } /* @@ -240,9 +307,9 @@ static int verify_crl(hx509_context context, CRLCertificateList *crl, time_t time_now, - hx509_certs certs) + hx509_certs certs, + hx509_cert parent) { - heim_octet_string os; hx509_cert signer; hx509_query q; time_t t; @@ -268,19 +335,36 @@ verify_crl(hx509_context context, if (ret) return ret; - os.data = crl->signatureValue.data; - os.length = crl->signatureValue.length / 8; + /* 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_verify_signature(context, - signer, - &crl->signatureAlgorithm, - &crl->tbsCertList._save, - &os); + ret = _hx509_cert_is_parent_cmp(s, p, 0); + if (ret != 0) { + ret = EINVAL; + goto out; + } + + ret = _hx509_verify_signature_bitstring(p, + &s->signatureAlgorithm, + &s->tbsCertificate._save, + &s->signatureValue); + if (ret) + goto out; + + ret = _hx509_check_key_usage(signer, 1 << 6, TRUE); /* crl */ + if (ret != 0) + goto out; + } + + ret = _hx509_verify_signature_bitstring(_hx509_get_cert(signer), + &crl->signatureAlgorithm, + &crl->tbsCertList._save, + &crl->signatureValue); if (ret) goto out; - /* XXX verify signer is allowed to be CRL signer */ - out: hx509_cert_free(signer); @@ -367,9 +451,11 @@ hx509_revoke_verify(hx509_context context, hx509_revoke_ctx revoke, hx509_certs certs, time_t now, - hx509_cert cert) + hx509_cert cert, + hx509_cert parent_cert) { const Certificate *c = _hx509_get_cert(cert); + const Certificate *p = _hx509_get_cert(parent_cert); unsigned long i, j, k; int ret; @@ -388,7 +474,8 @@ hx509_revoke_verify(hx509_context context, if (ret == 0) { free_OCSPBasicOCSPResponse(&ocsp->ocsp); ocsp->ocsp = o; - ocsp->verified = 0; + hx509_cert_free(ocsp->signer); + ocsp->signer = NULL; } if (ocsp->ocsp.certs) { @@ -416,14 +503,12 @@ hx509_revoke_verify(hx509_context context, /* verify signature in ocsp if not already done */ - if (ocsp->verified == 0) { - ret = verify_ocsp(context, ocsp, now, certs); + if (ocsp->signer == NULL) { + ret = verify_ocsp(context, ocsp, now, certs, parent_cert); if (ret) continue; - ocsp->verified = 1; } - for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) { heim_octet_string os; @@ -440,8 +525,8 @@ hx509_revoke_verify(hx509_context context, if (ret != 0) continue; - os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; - os.length = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; + os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; + os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; ret = _hx509_verify_signature(NULL, &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm, @@ -458,9 +543,12 @@ hx509_revoke_verify(hx509_context context, continue; } - if (ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate < now) + /* don't allow the update to be in the future */ + if (ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate > + now + context->ocsp_time_diff) continue; + /* don't allow the next updte to be in the past */ if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate) { if (*ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate < now) continue; @@ -495,7 +583,7 @@ hx509_revoke_verify(hx509_context context, /* verify signature in crl if not already done */ if (crl->verified == 0) { - ret = verify_crl(context, &crl->crl, now, certs); + ret = verify_crl(context, &crl->crl, now, certs, parent_cert); if (ret) return ret; crl->verified = 1; @@ -537,3 +625,132 @@ hx509_revoke_verify(hx509_context context, return 0; return HX509_CRL_MISSING; } + +struct ocsp_add_ctx { + OCSPTBSRequest *req; + hx509_certs certs; + const AlgorithmIdentifier *digest; + hx509_cert parent; +}; + +static int +add_to_req(hx509_context context, void *ptr, hx509_cert cert) +{ + struct ocsp_add_ctx *ctx = ptr; + OCSPInnerRequest *one; + hx509_cert parent = NULL; + Certificate *p, *c = _hx509_get_cert(cert); + heim_octet_string os; + int ret; + hx509_query q; + void *d; + + d = realloc(ctx->req->requestList.val, + sizeof(ctx->req->requestList.val[0]) * (ctx->req->requestList.len + 1)); + if (d == NULL) + return ENOMEM; + ctx->req->requestList.val = d; + + one = &ctx->req->requestList.val[ctx->req->requestList.len]; + memset(one, 0, sizeof(*one)); + + _hx509_query_clear(&q); + + q.match |= HX509_QUERY_FIND_ISSUER_CERT; + q.subject = c; + + ret = hx509_certs_find(context, ctx->certs, &q, &parent); + if (ret) + goto out; + + if (ctx->parent) { + if (hx509_cert_cmp(ctx->parent, parent) != 0) { + ret = EINVAL; + goto out; + } + } else + ctx->parent = hx509_cert_ref(parent); + + p = _hx509_get_cert(parent); + + ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm); + if (ret) + goto out; + + ret = _hx509_create_signature(NULL, + &one->reqCert.hashAlgorithm, + &c->tbsCertificate.issuer._save, + NULL, + &one->reqCert.issuerNameHash); + if (ret) + goto out; + + os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; + os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; + + ret = _hx509_create_signature(NULL, + &one->reqCert.hashAlgorithm, + &os, + NULL, + &one->reqCert.issuerKeyHash); + if (ret) + goto out; + + ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber, + &one->reqCert.serialNumber); + if (ret) + goto out; + + ctx->req->requestList.len++; +out: + hx509_cert_free(parent); + if (ret) { + free_OCSPInnerRequest(one); + memset(one, 0, sizeof(*one)); + } + + return ret; +} + + +int +hx509_ocsp_request(hx509_context context, + hx509_certs reqcerts, + hx509_certs pool, + hx509_cert signer, + const AlgorithmIdentifier *digest, + heim_octet_string *request) +{ + OCSPRequest req; + size_t size; + int ret; + struct ocsp_add_ctx ctx; + + memset(&req, 0, sizeof(req)); + + if (digest == NULL) + digest = hx509_signature_sha1(); + + ctx.req = &req.tbsRequest; + ctx.certs = pool; + ctx.digest = digest; + ctx.parent = NULL; + + ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx); + hx509_cert_free(ctx.parent); + if (ret) { + free_OCSPRequest(&req); + return ret; + } + + ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length, + &req, &size, ret); + free_OCSPRequest(&req); + if (ret) + return ret; + if (size != request->length) + _hx509_abort("internal ASN.1 encoder error"); + + + return 0; +}