Path: blob/main/crypto/heimdal/lib/hx509/revoke.c
108574 views
/*1* Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233/**34* @page page_revoke Revocation methods35*36* There are two revocation method for PKIX/X.509: CRL and OCSP.37* Revocation is needed if the private key is lost and38* stolen. Depending on how picky you are, you might want to make39* revocation for destroyed private keys too (smartcard broken), but40* that should not be a problem.41*42* CRL is a list of certifiates that have expired.43*44* OCSP is an online checking method where the requestor sends a list45* of certificates to the OCSP server to return a signed reply if they46* are valid or not. Some services sends a OCSP reply as part of the47* hand-shake to make the revoktion decision simpler/faster for the48* client.49*/5051#include "hx_locl.h"5253struct revoke_crl {54char *path;55time_t last_modfied;56CRLCertificateList crl;57int verified;58int failed_verify;59};6061struct revoke_ocsp {62char *path;63time_t last_modfied;64OCSPBasicOCSPResponse ocsp;65hx509_certs certs;66hx509_cert signer;67};686970struct hx509_revoke_ctx_data {71unsigned int ref;72struct {73struct revoke_crl *val;74size_t len;75} crls;76struct {77struct revoke_ocsp *val;78size_t len;79} ocsps;80};8182/**83* Allocate a revokation context. Free with hx509_revoke_free().84*85* @param context A hx509 context.86* @param ctx returns a newly allocated revokation context.87*88* @return An hx509 error code, see hx509_get_error_string().89*90* @ingroup hx509_revoke91*/9293int94hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)95{96*ctx = calloc(1, sizeof(**ctx));97if (*ctx == NULL)98return ENOMEM;99100(*ctx)->ref = 1;101(*ctx)->crls.len = 0;102(*ctx)->crls.val = NULL;103(*ctx)->ocsps.len = 0;104(*ctx)->ocsps.val = NULL;105106return 0;107}108109hx509_revoke_ctx110_hx509_revoke_ref(hx509_revoke_ctx ctx)111{112if (ctx == NULL)113return NULL;114if (ctx->ref == 0)115_hx509_abort("revoke ctx refcount == 0 on ref");116ctx->ref++;117if (ctx->ref == UINT_MAX)118_hx509_abort("revoke ctx refcount == UINT_MAX on ref");119return ctx;120}121122static void123free_ocsp(struct revoke_ocsp *ocsp)124{125free(ocsp->path);126free_OCSPBasicOCSPResponse(&ocsp->ocsp);127hx509_certs_free(&ocsp->certs);128hx509_cert_free(ocsp->signer);129}130131/**132* Free a hx509 revokation context.133*134* @param ctx context to be freed135*136* @ingroup hx509_revoke137*/138139void140hx509_revoke_free(hx509_revoke_ctx *ctx)141{142size_t i ;143144if (ctx == NULL || *ctx == NULL)145return;146147if ((*ctx)->ref == 0)148_hx509_abort("revoke ctx refcount == 0 on free");149if (--(*ctx)->ref > 0)150return;151152for (i = 0; i < (*ctx)->crls.len; i++) {153free((*ctx)->crls.val[i].path);154free_CRLCertificateList(&(*ctx)->crls.val[i].crl);155}156157for (i = 0; i < (*ctx)->ocsps.len; i++)158free_ocsp(&(*ctx)->ocsps.val[i]);159free((*ctx)->ocsps.val);160161free((*ctx)->crls.val);162163memset(*ctx, 0, sizeof(**ctx));164free(*ctx);165*ctx = NULL;166}167168static int169verify_ocsp(hx509_context context,170struct revoke_ocsp *ocsp,171time_t time_now,172hx509_certs certs,173hx509_cert parent)174{175hx509_cert signer = NULL;176hx509_query q;177int ret;178179_hx509_query_clear(&q);180181/*182* Need to match on issuer too in case there are two CA that have183* issued the same name to a certificate. One example of this is184* the www.openvalidation.org test's ocsp validator.185*/186187q.match = HX509_QUERY_MATCH_ISSUER_NAME;188q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;189190switch(ocsp->ocsp.tbsResponseData.responderID.element) {191case choice_OCSPResponderID_byName:192q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;193q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;194break;195case choice_OCSPResponderID_byKey:196q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;197q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;198break;199}200201ret = hx509_certs_find(context, certs, &q, &signer);202if (ret && ocsp->certs)203ret = hx509_certs_find(context, ocsp->certs, &q, &signer);204if (ret)205goto out;206207/*208* If signer certificate isn't the CA certificate, lets check the209* it is the CA that signed the signer certificate and the OCSP EKU210* is set.211*/212if (hx509_cert_cmp(signer, parent) != 0) {213Certificate *p = _hx509_get_cert(parent);214Certificate *s = _hx509_get_cert(signer);215216ret = _hx509_cert_is_parent_cmp(s, p, 0);217if (ret != 0) {218ret = HX509_PARENT_NOT_CA;219hx509_set_error_string(context, 0, ret, "Revoke OCSP signer is "220"doesn't have CA as signer certificate");221goto out;222}223224ret = _hx509_verify_signature_bitstring(context,225parent,226&s->signatureAlgorithm,227&s->tbsCertificate._save,228&s->signatureValue);229if (ret) {230hx509_set_error_string(context, HX509_ERROR_APPEND, ret,231"OCSP signer signature invalid");232goto out;233}234235ret = hx509_cert_check_eku(context, signer,236&asn1_oid_id_pkix_kp_OCSPSigning, 0);237if (ret)238goto out;239}240241ret = _hx509_verify_signature_bitstring(context,242signer,243&ocsp->ocsp.signatureAlgorithm,244&ocsp->ocsp.tbsResponseData._save,245&ocsp->ocsp.signature);246if (ret) {247hx509_set_error_string(context, HX509_ERROR_APPEND, ret,248"OCSP signature invalid");249goto out;250}251252ocsp->signer = signer;253signer = NULL;254out:255if (signer)256hx509_cert_free(signer);257258return ret;259}260261/*262*263*/264265static int266parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)267{268OCSPResponse resp;269size_t size;270int ret;271272memset(basic, 0, sizeof(*basic));273274ret = decode_OCSPResponse(data, length, &resp, &size);275if (ret)276return ret;277if (length != size) {278free_OCSPResponse(&resp);279return ASN1_EXTRA_DATA;280}281282switch (resp.responseStatus) {283case successful:284break;285default:286free_OCSPResponse(&resp);287return HX509_REVOKE_WRONG_DATA;288}289290if (resp.responseBytes == NULL) {291free_OCSPResponse(&resp);292return EINVAL;293}294295ret = der_heim_oid_cmp(&resp.responseBytes->responseType,296&asn1_oid_id_pkix_ocsp_basic);297if (ret != 0) {298free_OCSPResponse(&resp);299return HX509_REVOKE_WRONG_DATA;300}301302ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,303resp.responseBytes->response.length,304basic,305&size);306if (ret) {307free_OCSPResponse(&resp);308return ret;309}310if (size != resp.responseBytes->response.length) {311free_OCSPResponse(&resp);312free_OCSPBasicOCSPResponse(basic);313return ASN1_EXTRA_DATA;314}315free_OCSPResponse(&resp);316317return 0;318}319320/*321*322*/323324static int325load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)326{327OCSPBasicOCSPResponse basic;328hx509_certs certs = NULL;329size_t length;330struct stat sb;331void *data;332int ret;333334ret = rk_undumpdata(ocsp->path, &data, &length);335if (ret)336return ret;337338ret = stat(ocsp->path, &sb);339if (ret)340return errno;341342ret = parse_ocsp_basic(data, length, &basic);343rk_xfree(data);344if (ret) {345hx509_set_error_string(context, 0, ret,346"Failed to parse OCSP response");347return ret;348}349350if (basic.certs) {351size_t i;352353ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,354NULL, &certs);355if (ret) {356free_OCSPBasicOCSPResponse(&basic);357return ret;358}359360for (i = 0; i < basic.certs->len; i++) {361hx509_cert c;362363ret = hx509_cert_init(context, &basic.certs->val[i], &c);364if (ret)365continue;366367ret = hx509_certs_add(context, certs, c);368hx509_cert_free(c);369if (ret)370continue;371}372}373374ocsp->last_modfied = sb.st_mtime;375376free_OCSPBasicOCSPResponse(&ocsp->ocsp);377hx509_certs_free(&ocsp->certs);378hx509_cert_free(ocsp->signer);379380ocsp->ocsp = basic;381ocsp->certs = certs;382ocsp->signer = NULL;383384return 0;385}386387/**388* Add a OCSP file to the revokation context.389*390* @param context hx509 context391* @param ctx hx509 revokation context392* @param path path to file that is going to be added to the context.393*394* @return An hx509 error code, see hx509_get_error_string().395*396* @ingroup hx509_revoke397*/398399int400hx509_revoke_add_ocsp(hx509_context context,401hx509_revoke_ctx ctx,402const char *path)403{404void *data;405int ret;406size_t i;407408if (strncmp(path, "FILE:", 5) != 0) {409hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,410"unsupport type in %s", path);411return HX509_UNSUPPORTED_OPERATION;412}413414path += 5;415416for (i = 0; i < ctx->ocsps.len; i++) {417if (strcmp(ctx->ocsps.val[0].path, path) == 0)418return 0;419}420421data = realloc(ctx->ocsps.val,422(ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));423if (data == NULL) {424hx509_clear_error_string(context);425return ENOMEM;426}427428ctx->ocsps.val = data;429430memset(&ctx->ocsps.val[ctx->ocsps.len], 0,431sizeof(ctx->ocsps.val[0]));432433ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);434if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {435hx509_clear_error_string(context);436return ENOMEM;437}438439ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);440if (ret) {441free(ctx->ocsps.val[ctx->ocsps.len].path);442return ret;443}444ctx->ocsps.len++;445446return ret;447}448449/*450*451*/452453static int454verify_crl(hx509_context context,455hx509_revoke_ctx ctx,456CRLCertificateList *crl,457time_t time_now,458hx509_certs certs,459hx509_cert parent)460{461hx509_cert signer;462hx509_query q;463time_t t;464int ret;465466t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);467if (t > time_now) {468hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,469"CRL used before time");470return HX509_CRL_USED_BEFORE_TIME;471}472473if (crl->tbsCertList.nextUpdate == NULL) {474hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,475"CRL missing nextUpdate");476return HX509_CRL_INVALID_FORMAT;477}478479t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);480if (t < time_now) {481hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,482"CRL used after time");483return HX509_CRL_USED_AFTER_TIME;484}485486_hx509_query_clear(&q);487488/*489* If it's the signer have CRLSIGN bit set, use that as the signer490* cert for the certificate, otherwise, search for a certificate.491*/492if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {493signer = hx509_cert_ref(parent);494} else {495q.match = HX509_QUERY_MATCH_SUBJECT_NAME;496q.match |= HX509_QUERY_KU_CRLSIGN;497q.subject_name = &crl->tbsCertList.issuer;498499ret = hx509_certs_find(context, certs, &q, &signer);500if (ret) {501hx509_set_error_string(context, HX509_ERROR_APPEND, ret,502"Failed to find certificate for CRL");503return ret;504}505}506507ret = _hx509_verify_signature_bitstring(context,508signer,509&crl->signatureAlgorithm,510&crl->tbsCertList._save,511&crl->signatureValue);512if (ret) {513hx509_set_error_string(context, HX509_ERROR_APPEND, ret,514"CRL signature invalid");515goto out;516}517518/*519* If signer is not CA cert, need to check revoke status of this520* CRL signing cert too, this include all parent CRL signer cert521* up to the root *sigh*, assume root at least hve CERTSIGN flag522* set.523*/524while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {525hx509_cert crl_parent;526527_hx509_query_clear(&q);528529q.match = HX509_QUERY_MATCH_SUBJECT_NAME;530q.match |= HX509_QUERY_KU_CRLSIGN;531q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;532533ret = hx509_certs_find(context, certs, &q, &crl_parent);534if (ret) {535hx509_set_error_string(context, HX509_ERROR_APPEND, ret,536"Failed to find parent of CRL signer");537goto out;538}539540ret = hx509_revoke_verify(context,541ctx,542certs,543time_now,544signer,545crl_parent);546hx509_cert_free(signer);547signer = crl_parent;548if (ret) {549hx509_set_error_string(context, HX509_ERROR_APPEND, ret,550"Failed to verify revoke "551"status of CRL signer");552goto out;553}554}555556out:557hx509_cert_free(signer);558559return ret;560}561562static int563load_crl(const char *path, time_t *t, CRLCertificateList *crl)564{565size_t length, size;566struct stat sb;567void *data;568int ret;569570memset(crl, 0, sizeof(*crl));571572ret = rk_undumpdata(path, &data, &length);573if (ret)574return ret;575576ret = stat(path, &sb);577if (ret)578return errno;579580*t = sb.st_mtime;581582ret = decode_CRLCertificateList(data, length, crl, &size);583rk_xfree(data);584if (ret)585return ret;586587/* check signature is aligned */588if (crl->signatureValue.length & 7) {589free_CRLCertificateList(crl);590return HX509_CRYPTO_SIG_INVALID_FORMAT;591}592return 0;593}594595/**596* Add a CRL file to the revokation context.597*598* @param context hx509 context599* @param ctx hx509 revokation context600* @param path path to file that is going to be added to the context.601*602* @return An hx509 error code, see hx509_get_error_string().603*604* @ingroup hx509_revoke605*/606607int608hx509_revoke_add_crl(hx509_context context,609hx509_revoke_ctx ctx,610const char *path)611{612void *data;613size_t i;614int ret;615616if (strncmp(path, "FILE:", 5) != 0) {617hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,618"unsupport type in %s", path);619return HX509_UNSUPPORTED_OPERATION;620}621622623path += 5;624625for (i = 0; i < ctx->crls.len; i++) {626if (strcmp(ctx->crls.val[0].path, path) == 0)627return 0;628}629630data = realloc(ctx->crls.val,631(ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));632if (data == NULL) {633hx509_clear_error_string(context);634return ENOMEM;635}636ctx->crls.val = data;637638memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));639640ctx->crls.val[ctx->crls.len].path = strdup(path);641if (ctx->crls.val[ctx->crls.len].path == NULL) {642hx509_clear_error_string(context);643return ENOMEM;644}645646ret = load_crl(path,647&ctx->crls.val[ctx->crls.len].last_modfied,648&ctx->crls.val[ctx->crls.len].crl);649if (ret) {650free(ctx->crls.val[ctx->crls.len].path);651return ret;652}653654ctx->crls.len++;655656return ret;657}658659/**660* Check that a certificate is not expired according to a revokation661* context. Also need the parent certificte to the check OCSP662* parent identifier.663*664* @param context hx509 context665* @param ctx hx509 revokation context666* @param certs667* @param now668* @param cert669* @param parent_cert670*671* @return An hx509 error code, see hx509_get_error_string().672*673* @ingroup hx509_revoke674*/675676677int678hx509_revoke_verify(hx509_context context,679hx509_revoke_ctx ctx,680hx509_certs certs,681time_t now,682hx509_cert cert,683hx509_cert parent_cert)684{685const Certificate *c = _hx509_get_cert(cert);686const Certificate *p = _hx509_get_cert(parent_cert);687unsigned long i, j, k;688int ret;689690hx509_clear_error_string(context);691692for (i = 0; i < ctx->ocsps.len; i++) {693struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];694struct stat sb;695696/* check this ocsp apply to this cert */697698/* check if there is a newer version of the file */699ret = stat(ocsp->path, &sb);700if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {701ret = load_ocsp(context, ocsp);702if (ret)703continue;704}705706/* verify signature in ocsp if not already done */707if (ocsp->signer == NULL) {708ret = verify_ocsp(context, ocsp, now, certs, parent_cert);709if (ret)710continue;711}712713for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {714heim_octet_string os;715716ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,717&c->tbsCertificate.serialNumber);718if (ret != 0)719continue;720721/* verify issuer hashes hash */722ret = _hx509_verify_signature(context,723NULL,724&ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,725&c->tbsCertificate.issuer._save,726&ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);727if (ret != 0)728continue;729730os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;731os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;732733ret = _hx509_verify_signature(context,734NULL,735&ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,736&os,737&ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);738if (ret != 0)739continue;740741switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {742case choice_OCSPCertStatus_good:743break;744case choice_OCSPCertStatus_revoked:745hx509_set_error_string(context, 0,746HX509_CERT_REVOKED,747"Certificate revoked by issuer in OCSP");748return HX509_CERT_REVOKED;749case choice_OCSPCertStatus_unknown:750continue;751}752753/* don't allow the update to be in the future */754if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >755now + context->ocsp_time_diff)756continue;757758/* don't allow the next update to be in the past */759if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {760if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)761continue;762} /* else should force a refetch, but can we ? */763764return 0;765}766}767768for (i = 0; i < ctx->crls.len; i++) {769struct revoke_crl *crl = &ctx->crls.val[i];770struct stat sb;771int diff;772773/* check if cert.issuer == crls.val[i].crl.issuer */774ret = _hx509_name_cmp(&c->tbsCertificate.issuer,775&crl->crl.tbsCertList.issuer, &diff);776if (ret || diff)777continue;778779ret = stat(crl->path, &sb);780if (ret == 0 && crl->last_modfied != sb.st_mtime) {781CRLCertificateList cl;782783ret = load_crl(crl->path, &crl->last_modfied, &cl);784if (ret == 0) {785free_CRLCertificateList(&crl->crl);786crl->crl = cl;787crl->verified = 0;788crl->failed_verify = 0;789}790}791if (crl->failed_verify)792continue;793794/* verify signature in crl if not already done */795if (crl->verified == 0) {796ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);797if (ret) {798crl->failed_verify = 1;799continue;800}801crl->verified = 1;802}803804if (crl->crl.tbsCertList.crlExtensions) {805for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {806if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {807hx509_set_error_string(context, 0,808HX509_CRL_UNKNOWN_EXTENSION,809"Unknown CRL extension");810return HX509_CRL_UNKNOWN_EXTENSION;811}812}813}814815if (crl->crl.tbsCertList.revokedCertificates == NULL)816return 0;817818/* check if cert is in crl */819for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {820time_t t;821822ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,823&c->tbsCertificate.serialNumber);824if (ret != 0)825continue;826827t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);828if (t > now)829continue;830831if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)832for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)833if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)834return HX509_CRL_UNKNOWN_EXTENSION;835836hx509_set_error_string(context, 0,837HX509_CERT_REVOKED,838"Certificate revoked by issuer in CRL");839return HX509_CERT_REVOKED;840}841842return 0;843}844845846if (context->flags & HX509_CTX_VERIFY_MISSING_OK)847return 0;848hx509_set_error_string(context, HX509_ERROR_APPEND,849HX509_REVOKE_STATUS_MISSING,850"No revoke status found for "851"certificates");852return HX509_REVOKE_STATUS_MISSING;853}854855struct ocsp_add_ctx {856OCSPTBSRequest *req;857hx509_certs certs;858const AlgorithmIdentifier *digest;859hx509_cert parent;860};861862static int863add_to_req(hx509_context context, void *ptr, hx509_cert cert)864{865struct ocsp_add_ctx *ctx = ptr;866OCSPInnerRequest *one;867hx509_cert parent = NULL;868Certificate *p, *c = _hx509_get_cert(cert);869heim_octet_string os;870int ret;871hx509_query q;872void *d;873874d = realloc(ctx->req->requestList.val,875sizeof(ctx->req->requestList.val[0]) *876(ctx->req->requestList.len + 1));877if (d == NULL)878return ENOMEM;879ctx->req->requestList.val = d;880881one = &ctx->req->requestList.val[ctx->req->requestList.len];882memset(one, 0, sizeof(*one));883884_hx509_query_clear(&q);885886q.match |= HX509_QUERY_FIND_ISSUER_CERT;887q.subject = c;888889ret = hx509_certs_find(context, ctx->certs, &q, &parent);890if (ret)891goto out;892893if (ctx->parent) {894if (hx509_cert_cmp(ctx->parent, parent) != 0) {895ret = HX509_REVOKE_NOT_SAME_PARENT;896hx509_set_error_string(context, 0, ret,897"Not same parent certifate as "898"last certificate in request");899goto out;900}901} else902ctx->parent = hx509_cert_ref(parent);903904p = _hx509_get_cert(parent);905906ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);907if (ret)908goto out;909910ret = _hx509_create_signature(context,911NULL,912&one->reqCert.hashAlgorithm,913&c->tbsCertificate.issuer._save,914NULL,915&one->reqCert.issuerNameHash);916if (ret)917goto out;918919os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;920os.length =921p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;922923ret = _hx509_create_signature(context,924NULL,925&one->reqCert.hashAlgorithm,926&os,927NULL,928&one->reqCert.issuerKeyHash);929if (ret)930goto out;931932ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,933&one->reqCert.serialNumber);934if (ret)935goto out;936937ctx->req->requestList.len++;938out:939hx509_cert_free(parent);940if (ret) {941free_OCSPInnerRequest(one);942memset(one, 0, sizeof(*one));943}944945return ret;946}947948/**949* Create an OCSP request for a set of certificates.950*951* @param context a hx509 context952* @param reqcerts list of certificates to request ocsp data for953* @param pool certificate pool to use when signing954* @param signer certificate to use to sign the request955* @param digest the signing algorithm in the request, if NULL use the956* default signature algorithm,957* @param request the encoded request, free with free_heim_octet_string().958* @param nonce nonce in the request, free with free_heim_octet_string().959*960* @return An hx509 error code, see hx509_get_error_string().961*962* @ingroup hx509_revoke963*/964965int966hx509_ocsp_request(hx509_context context,967hx509_certs reqcerts,968hx509_certs pool,969hx509_cert signer,970const AlgorithmIdentifier *digest,971heim_octet_string *request,972heim_octet_string *nonce)973{974OCSPRequest req;975size_t size;976int ret;977struct ocsp_add_ctx ctx;978Extensions *es;979980memset(&req, 0, sizeof(req));981982if (digest == NULL)983digest = _hx509_crypto_default_digest_alg;984985ctx.req = &req.tbsRequest;986ctx.certs = pool;987ctx.digest = digest;988ctx.parent = NULL;989990ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);991hx509_cert_free(ctx.parent);992if (ret)993goto out;994995if (nonce) {996req.tbsRequest.requestExtensions =997calloc(1, sizeof(*req.tbsRequest.requestExtensions));998if (req.tbsRequest.requestExtensions == NULL) {999ret = ENOMEM;1000goto out;1001}10021003es = req.tbsRequest.requestExtensions;10041005es->val = calloc(es->len, sizeof(es->val[0]));1006if (es->val == NULL) {1007ret = ENOMEM;1008goto out;1009}1010es->len = 1;1011ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);1012if (ret) {1013free_OCSPRequest(&req);1014return ret;1015}10161017es->val[0].extnValue.data = malloc(10);1018if (es->val[0].extnValue.data == NULL) {1019ret = ENOMEM;1020goto out;1021}1022es->val[0].extnValue.length = 10;10231024ret = RAND_bytes(es->val[0].extnValue.data,1025es->val[0].extnValue.length);1026if (ret != 1) {1027ret = HX509_CRYPTO_INTERNAL_ERROR;1028goto out;1029}1030ret = der_copy_octet_string(nonce, &es->val[0].extnValue);1031if (ret) {1032ret = ENOMEM;1033goto out;1034}1035}10361037ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,1038&req, &size, ret);1039free_OCSPRequest(&req);1040if (ret)1041goto out;1042if (size != request->length)1043_hx509_abort("internal ASN.1 encoder error");10441045return 0;10461047out:1048free_OCSPRequest(&req);1049return ret;1050}10511052static char *1053printable_time(time_t t)1054{1055static char s[128];1056char *p;1057if ((p = ctime(&t)) == NULL)1058strlcpy(s, "?", sizeof(s));1059else {1060strlcpy(s, p + 4, sizeof(s));1061s[20] = 0;1062}1063return s;1064}10651066/**1067* Print the OCSP reply stored in a file.1068*1069* @param context a hx509 context1070* @param path path to a file with a OCSP reply1071* @param out the out FILE descriptor to print the reply on1072*1073* @return An hx509 error code, see hx509_get_error_string().1074*1075* @ingroup hx509_revoke1076*/10771078int1079hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)1080{1081struct revoke_ocsp ocsp;1082int ret;1083size_t i;10841085if (out == NULL)1086out = stdout;10871088memset(&ocsp, 0, sizeof(ocsp));10891090ocsp.path = strdup(path);1091if (ocsp.path == NULL)1092return ENOMEM;10931094ret = load_ocsp(context, &ocsp);1095if (ret) {1096free_ocsp(&ocsp);1097return ret;1098}10991100fprintf(out, "signer: ");11011102switch(ocsp.ocsp.tbsResponseData.responderID.element) {1103case choice_OCSPResponderID_byName: {1104hx509_name n;1105char *s;1106_hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);1107hx509_name_to_string(n, &s);1108hx509_name_free(&n);1109fprintf(out, " byName: %s\n", s);1110free(s);1111break;1112}1113case choice_OCSPResponderID_byKey: {1114char *s;1115hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,1116ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,1117&s);1118fprintf(out, " byKey: %s\n", s);1119free(s);1120break;1121}1122default:1123_hx509_abort("choice_OCSPResponderID unknown");1124break;1125}11261127fprintf(out, "producedAt: %s\n",1128printable_time(ocsp.ocsp.tbsResponseData.producedAt));11291130fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);11311132for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {1133const char *status;1134switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {1135case choice_OCSPCertStatus_good:1136status = "good";1137break;1138case choice_OCSPCertStatus_revoked:1139status = "revoked";1140break;1141case choice_OCSPCertStatus_unknown:1142status = "unknown";1143break;1144default:1145status = "element unknown";1146}11471148fprintf(out, "\t%zu. status: %s\n", i, status);11491150fprintf(out, "\tthisUpdate: %s\n",1151printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));1152if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)1153fprintf(out, "\tproducedAt: %s\n",1154printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));11551156}11571158fprintf(out, "appended certs:\n");1159if (ocsp.certs)1160ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);11611162free_ocsp(&ocsp);1163return ret;1164}11651166/**1167* Verify that the certificate is part of the OCSP reply and it's not1168* expired. Doesn't verify signature the OCSP reply or it's done by a1169* authorized sender, that is assumed to be already done.1170*1171* @param context a hx509 context1172* @param now the time right now, if 0, use the current time.1173* @param cert the certificate to verify1174* @param flags flags control the behavior1175* @param data pointer to the encode ocsp reply1176* @param length the length of the encode ocsp reply1177* @param expiration return the time the OCSP will expire and need to1178* be rechecked.1179*1180* @return An hx509 error code, see hx509_get_error_string().1181*1182* @ingroup hx509_verify1183*/11841185int1186hx509_ocsp_verify(hx509_context context,1187time_t now,1188hx509_cert cert,1189int flags,1190const void *data, size_t length,1191time_t *expiration)1192{1193const Certificate *c = _hx509_get_cert(cert);1194OCSPBasicOCSPResponse basic;1195int ret;1196size_t i;11971198if (now == 0)1199now = time(NULL);12001201*expiration = 0;12021203ret = parse_ocsp_basic(data, length, &basic);1204if (ret) {1205hx509_set_error_string(context, 0, ret,1206"Failed to parse OCSP response");1207return ret;1208}12091210for (i = 0; i < basic.tbsResponseData.responses.len; i++) {12111212ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,1213&c->tbsCertificate.serialNumber);1214if (ret != 0)1215continue;12161217/* verify issuer hashes hash */1218ret = _hx509_verify_signature(context,1219NULL,1220&basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,1221&c->tbsCertificate.issuer._save,1222&basic.tbsResponseData.responses.val[i].certID.issuerNameHash);1223if (ret != 0)1224continue;12251226switch (basic.tbsResponseData.responses.val[i].certStatus.element) {1227case choice_OCSPCertStatus_good:1228break;1229case choice_OCSPCertStatus_revoked:1230case choice_OCSPCertStatus_unknown:1231continue;1232}12331234/* don't allow the update to be in the future */1235if (basic.tbsResponseData.responses.val[i].thisUpdate >1236now + context->ocsp_time_diff)1237continue;12381239/* don't allow the next update to be in the past */1240if (basic.tbsResponseData.responses.val[i].nextUpdate) {1241if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)1242continue;1243*expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;1244} else1245*expiration = now;12461247free_OCSPBasicOCSPResponse(&basic);1248return 0;1249}12501251free_OCSPBasicOCSPResponse(&basic);12521253{1254hx509_name name;1255char *subject;12561257ret = hx509_cert_get_subject(cert, &name);1258if (ret) {1259hx509_clear_error_string(context);1260goto out;1261}1262ret = hx509_name_to_string(name, &subject);1263hx509_name_free(&name);1264if (ret) {1265hx509_clear_error_string(context);1266goto out;1267}1268hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,1269"Certificate %s not in OCSP response "1270"or not good",1271subject);1272free(subject);1273}1274out:1275return HX509_CERT_NOT_IN_OCSP;1276}12771278struct hx509_crl {1279hx509_certs revoked;1280time_t expire;1281};12821283/**1284* Create a CRL context. Use hx509_crl_free() to free the CRL context.1285*1286* @param context a hx509 context.1287* @param crl return pointer to a newly allocated CRL context.1288*1289* @return An hx509 error code, see hx509_get_error_string().1290*1291* @ingroup hx509_verify1292*/12931294int1295hx509_crl_alloc(hx509_context context, hx509_crl *crl)1296{1297int ret;12981299*crl = calloc(1, sizeof(**crl));1300if (*crl == NULL) {1301hx509_set_error_string(context, 0, ENOMEM, "out of memory");1302return ENOMEM;1303}13041305ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);1306if (ret) {1307free(*crl);1308*crl = NULL;1309return ret;1310}1311(*crl)->expire = 0;1312return ret;1313}13141315/**1316* Add revoked certificate to an CRL context.1317*1318* @param context a hx509 context.1319* @param crl the CRL to add the revoked certificate to.1320* @param certs keyset of certificate to revoke.1321*1322* @return An hx509 error code, see hx509_get_error_string().1323*1324* @ingroup hx509_verify1325*/13261327int1328hx509_crl_add_revoked_certs(hx509_context context,1329hx509_crl crl,1330hx509_certs certs)1331{1332return hx509_certs_merge(context, crl->revoked, certs);1333}13341335/**1336* Set the lifetime of a CRL context.1337*1338* @param context a hx509 context.1339* @param crl a CRL context1340* @param delta delta time the certificate is valid, library adds the1341* current time to this.1342*1343* @return An hx509 error code, see hx509_get_error_string().1344*1345* @ingroup hx509_verify1346*/13471348int1349hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)1350{1351crl->expire = time(NULL) + delta;1352return 0;1353}13541355/**1356* Free a CRL context.1357*1358* @param context a hx509 context.1359* @param crl a CRL context to free.1360*1361* @ingroup hx509_verify1362*/13631364void1365hx509_crl_free(hx509_context context, hx509_crl *crl)1366{1367if (*crl == NULL)1368return;1369hx509_certs_free(&(*crl)->revoked);1370memset(*crl, 0, sizeof(**crl));1371free(*crl);1372*crl = NULL;1373}13741375static int1376add_revoked(hx509_context context, void *ctx, hx509_cert cert)1377{1378TBSCRLCertList *c = ctx;1379unsigned int num;1380void *ptr;1381int ret;13821383num = c->revokedCertificates->len;1384ptr = realloc(c->revokedCertificates->val,1385(num + 1) * sizeof(c->revokedCertificates->val[0]));1386if (ptr == NULL) {1387hx509_clear_error_string(context);1388return ENOMEM;1389}1390c->revokedCertificates->val = ptr;13911392ret = hx509_cert_get_serialnumber(cert,1393&c->revokedCertificates->val[num].userCertificate);1394if (ret) {1395hx509_clear_error_string(context);1396return ret;1397}1398c->revokedCertificates->val[num].revocationDate.element =1399choice_Time_generalTime;1400c->revokedCertificates->val[num].revocationDate.u.generalTime =1401time(NULL) - 3600 * 24;1402c->revokedCertificates->val[num].crlEntryExtensions = NULL;14031404c->revokedCertificates->len++;14051406return 0;1407}14081409/**1410* Sign a CRL and return an encode certificate.1411*1412* @param context a hx509 context.1413* @param signer certificate to sign the CRL with1414* @param crl the CRL to sign1415* @param os return the signed and encoded CRL, free with1416* free_heim_octet_string()1417*1418* @return An hx509 error code, see hx509_get_error_string().1419*1420* @ingroup hx509_verify1421*/14221423int1424hx509_crl_sign(hx509_context context,1425hx509_cert signer,1426hx509_crl crl,1427heim_octet_string *os)1428{1429const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;1430CRLCertificateList c;1431size_t size;1432int ret;1433hx509_private_key signerkey;14341435memset(&c, 0, sizeof(c));14361437signerkey = _hx509_cert_private_key(signer);1438if (signerkey == NULL) {1439ret = HX509_PRIVATE_KEY_MISSING;1440hx509_set_error_string(context, 0, ret,1441"Private key missing for CRL signing");1442return ret;1443}14441445c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));1446if (c.tbsCertList.version == NULL) {1447hx509_set_error_string(context, 0, ENOMEM, "out of memory");1448return ENOMEM;1449}14501451*c.tbsCertList.version = 1;14521453ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);1454if (ret) {1455hx509_clear_error_string(context);1456goto out;1457}14581459ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,1460&c.tbsCertList.issuer);1461if (ret) {1462hx509_clear_error_string(context);1463goto out;1464}14651466c.tbsCertList.thisUpdate.element = choice_Time_generalTime;1467c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;14681469c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));1470if (c.tbsCertList.nextUpdate == NULL) {1471hx509_set_error_string(context, 0, ENOMEM, "out of memory");1472ret = ENOMEM;1473goto out;1474}14751476{1477time_t next = crl->expire;1478if (next == 0)1479next = time(NULL) + 24 * 3600 * 365;14801481c.tbsCertList.nextUpdate->element = choice_Time_generalTime;1482c.tbsCertList.nextUpdate->u.generalTime = next;1483}14841485c.tbsCertList.revokedCertificates =1486calloc(1, sizeof(*c.tbsCertList.revokedCertificates));1487if (c.tbsCertList.revokedCertificates == NULL) {1488hx509_set_error_string(context, 0, ENOMEM, "out of memory");1489ret = ENOMEM;1490goto out;1491}1492c.tbsCertList.crlExtensions = NULL;14931494ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);1495if (ret)1496goto out;14971498/* if not revoked certs, remove OPTIONAL entry */1499if (c.tbsCertList.revokedCertificates->len == 0) {1500free(c.tbsCertList.revokedCertificates);1501c.tbsCertList.revokedCertificates = NULL;1502}15031504ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,1505&c.tbsCertList, &size, ret);1506if (ret) {1507hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");1508goto out;1509}1510if (size != os->length)1511_hx509_abort("internal ASN.1 encoder error");151215131514ret = _hx509_create_signature_bitstring(context,1515signerkey,1516sigalg,1517os,1518&c.signatureAlgorithm,1519&c.signatureValue);1520free(os->data);1521if (ret) {1522hx509_set_error_string(context, 0, ret, "Failed to sign CRL");1523goto out;1524}15251526ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,1527&c, &size, ret);1528if (ret) {1529hx509_set_error_string(context, 0, ret, "failed to encode CRL");1530goto out;1531}1532if (size != os->length)1533_hx509_abort("internal ASN.1 encoder error");15341535free_CRLCertificateList(&c);15361537return 0;15381539out:1540free_CRLCertificateList(&c);1541return ret;1542}154315441545