Remove building with NOCRYPTO option
[minix.git] / crypto / external / bsd / heimdal / dist / lib / hx509 / revoke.c
blob6883c46f94a76d02028d2e5a97d6e1a3b44747bd
1 /* $NetBSD: revoke.c,v 1.3 2014/04/24 13:45:34 pettai Exp $ */
3 /*
4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 /**
37 * @page page_revoke Revocation methods
39 * There are two revocation method for PKIX/X.509: CRL and OCSP.
40 * Revocation is needed if the private key is lost and
41 * stolen. Depending on how picky you are, you might want to make
42 * revocation for destroyed private keys too (smartcard broken), but
43 * that should not be a problem.
45 * CRL is a list of certifiates that have expired.
47 * OCSP is an online checking method where the requestor sends a list
48 * of certificates to the OCSP server to return a signed reply if they
49 * are valid or not. Some services sends a OCSP reply as part of the
50 * hand-shake to make the revoktion decision simpler/faster for the
51 * client.
54 #include "hx_locl.h"
56 struct revoke_crl {
57 char *path;
58 time_t last_modfied;
59 CRLCertificateList crl;
60 int verified;
61 int failed_verify;
64 struct revoke_ocsp {
65 char *path;
66 time_t last_modfied;
67 OCSPBasicOCSPResponse ocsp;
68 hx509_certs certs;
69 hx509_cert signer;
73 struct hx509_revoke_ctx_data {
74 unsigned int ref;
75 struct {
76 struct revoke_crl *val;
77 size_t len;
78 } crls;
79 struct {
80 struct revoke_ocsp *val;
81 size_t len;
82 } ocsps;
85 /**
86 * Allocate a revokation context. Free with hx509_revoke_free().
88 * @param context A hx509 context.
89 * @param ctx returns a newly allocated revokation context.
91 * @return An hx509 error code, see hx509_get_error_string().
93 * @ingroup hx509_revoke
96 int
97 hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
99 *ctx = calloc(1, sizeof(**ctx));
100 if (*ctx == NULL)
101 return ENOMEM;
103 (*ctx)->ref = 1;
104 (*ctx)->crls.len = 0;
105 (*ctx)->crls.val = NULL;
106 (*ctx)->ocsps.len = 0;
107 (*ctx)->ocsps.val = NULL;
109 return 0;
112 hx509_revoke_ctx
113 _hx509_revoke_ref(hx509_revoke_ctx ctx)
115 if (ctx == NULL)
116 return NULL;
117 if (ctx->ref == 0)
118 _hx509_abort("revoke ctx refcount == 0 on ref");
119 ctx->ref++;
120 if (ctx->ref == UINT_MAX)
121 _hx509_abort("revoke ctx refcount == UINT_MAX on ref");
122 return ctx;
125 static void
126 free_ocsp(struct revoke_ocsp *ocsp)
128 free(ocsp->path);
129 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
130 hx509_certs_free(&ocsp->certs);
131 hx509_cert_free(ocsp->signer);
135 * Free a hx509 revokation context.
137 * @param ctx context to be freed
139 * @ingroup hx509_revoke
142 void
143 hx509_revoke_free(hx509_revoke_ctx *ctx)
145 size_t i ;
147 if (ctx == NULL || *ctx == NULL)
148 return;
150 if ((*ctx)->ref == 0)
151 _hx509_abort("revoke ctx refcount == 0 on free");
152 if (--(*ctx)->ref > 0)
153 return;
155 for (i = 0; i < (*ctx)->crls.len; i++) {
156 free((*ctx)->crls.val[i].path);
157 free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
160 for (i = 0; i < (*ctx)->ocsps.len; i++)
161 free_ocsp(&(*ctx)->ocsps.val[i]);
162 free((*ctx)->ocsps.val);
164 free((*ctx)->crls.val);
166 memset(*ctx, 0, sizeof(**ctx));
167 free(*ctx);
168 *ctx = NULL;
171 static int
172 verify_ocsp(hx509_context context,
173 struct revoke_ocsp *ocsp,
174 time_t time_now,
175 hx509_certs certs,
176 hx509_cert parent)
178 hx509_cert signer = NULL;
179 hx509_query q;
180 int ret;
182 _hx509_query_clear(&q);
185 * Need to match on issuer too in case there are two CA that have
186 * issued the same name to a certificate. One example of this is
187 * the www.openvalidation.org test's ocsp validator.
190 q.match = HX509_QUERY_MATCH_ISSUER_NAME;
191 q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
193 switch(ocsp->ocsp.tbsResponseData.responderID.element) {
194 case choice_OCSPResponderID_byName:
195 q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
196 q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
197 break;
198 case choice_OCSPResponderID_byKey:
199 q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
200 q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
201 break;
204 ret = hx509_certs_find(context, certs, &q, &signer);
205 if (ret && ocsp->certs)
206 ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
207 if (ret)
208 goto out;
211 * If signer certificate isn't the CA certificate, lets check the
212 * it is the CA that signed the signer certificate and the OCSP EKU
213 * is set.
215 if (hx509_cert_cmp(signer, parent) != 0) {
216 Certificate *p = _hx509_get_cert(parent);
217 Certificate *s = _hx509_get_cert(signer);
219 ret = _hx509_cert_is_parent_cmp(s, p, 0);
220 if (ret != 0) {
221 ret = HX509_PARENT_NOT_CA;
222 hx509_set_error_string(context, 0, ret, "Revoke OCSP signer is "
223 "doesn't have CA as signer certificate");
224 goto out;
227 ret = _hx509_verify_signature_bitstring(context,
228 parent,
229 &s->signatureAlgorithm,
230 &s->tbsCertificate._save,
231 &s->signatureValue);
232 if (ret) {
233 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
234 "OCSP signer signature invalid");
235 goto out;
238 ret = hx509_cert_check_eku(context, signer,
239 &asn1_oid_id_pkix_kp_OCSPSigning, 0);
240 if (ret)
241 goto out;
244 ret = _hx509_verify_signature_bitstring(context,
245 signer,
246 &ocsp->ocsp.signatureAlgorithm,
247 &ocsp->ocsp.tbsResponseData._save,
248 &ocsp->ocsp.signature);
249 if (ret) {
250 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
251 "OCSP signature invalid");
252 goto out;
255 ocsp->signer = signer;
256 signer = NULL;
257 out:
258 if (signer)
259 hx509_cert_free(signer);
261 return ret;
268 static int
269 parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
271 OCSPResponse resp;
272 size_t size;
273 int ret;
275 memset(basic, 0, sizeof(*basic));
277 ret = decode_OCSPResponse(data, length, &resp, &size);
278 if (ret)
279 return ret;
280 if (length != size) {
281 free_OCSPResponse(&resp);
282 return ASN1_EXTRA_DATA;
285 switch (resp.responseStatus) {
286 case successful:
287 break;
288 default:
289 free_OCSPResponse(&resp);
290 return HX509_REVOKE_WRONG_DATA;
293 if (resp.responseBytes == NULL) {
294 free_OCSPResponse(&resp);
295 return EINVAL;
298 ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
299 &asn1_oid_id_pkix_ocsp_basic);
300 if (ret != 0) {
301 free_OCSPResponse(&resp);
302 return HX509_REVOKE_WRONG_DATA;
305 ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
306 resp.responseBytes->response.length,
307 basic,
308 &size);
309 if (ret) {
310 free_OCSPResponse(&resp);
311 return ret;
313 if (size != resp.responseBytes->response.length) {
314 free_OCSPResponse(&resp);
315 free_OCSPBasicOCSPResponse(basic);
316 return ASN1_EXTRA_DATA;
318 free_OCSPResponse(&resp);
320 return 0;
327 static int
328 load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
330 OCSPBasicOCSPResponse basic;
331 hx509_certs certs = NULL;
332 size_t length;
333 struct stat sb;
334 void *data;
335 int ret;
337 ret = rk_undumpdata(ocsp->path, &data, &length);
338 if (ret)
339 return ret;
341 ret = stat(ocsp->path, &sb);
342 if (ret)
343 return errno;
345 ret = parse_ocsp_basic(data, length, &basic);
346 rk_xfree(data);
347 if (ret) {
348 hx509_set_error_string(context, 0, ret,
349 "Failed to parse OCSP response");
350 return ret;
353 if (basic.certs) {
354 size_t i;
356 ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
357 NULL, &certs);
358 if (ret) {
359 free_OCSPBasicOCSPResponse(&basic);
360 return ret;
363 for (i = 0; i < basic.certs->len; i++) {
364 hx509_cert c;
366 ret = hx509_cert_init(context, &basic.certs->val[i], &c);
367 if (ret)
368 continue;
370 ret = hx509_certs_add(context, certs, c);
371 hx509_cert_free(c);
372 if (ret)
373 continue;
377 ocsp->last_modfied = sb.st_mtime;
379 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
380 hx509_certs_free(&ocsp->certs);
381 hx509_cert_free(ocsp->signer);
383 ocsp->ocsp = basic;
384 ocsp->certs = certs;
385 ocsp->signer = NULL;
387 return 0;
391 * Add a OCSP file to the revokation context.
393 * @param context hx509 context
394 * @param ctx hx509 revokation context
395 * @param path path to file that is going to be added to the context.
397 * @return An hx509 error code, see hx509_get_error_string().
399 * @ingroup hx509_revoke
403 hx509_revoke_add_ocsp(hx509_context context,
404 hx509_revoke_ctx ctx,
405 const char *path)
407 void *data;
408 int ret;
409 size_t i;
411 if (strncmp(path, "FILE:", 5) != 0) {
412 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
413 "unsupport type in %s", path);
414 return HX509_UNSUPPORTED_OPERATION;
417 path += 5;
419 for (i = 0; i < ctx->ocsps.len; i++) {
420 if (strcmp(ctx->ocsps.val[0].path, path) == 0)
421 return 0;
424 data = realloc(ctx->ocsps.val,
425 (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
426 if (data == NULL) {
427 hx509_clear_error_string(context);
428 return ENOMEM;
431 ctx->ocsps.val = data;
433 memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
434 sizeof(ctx->ocsps.val[0]));
436 ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
437 if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
438 hx509_clear_error_string(context);
439 return ENOMEM;
442 ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
443 if (ret) {
444 free(ctx->ocsps.val[ctx->ocsps.len].path);
445 return ret;
447 ctx->ocsps.len++;
449 return ret;
456 static int
457 verify_crl(hx509_context context,
458 hx509_revoke_ctx ctx,
459 CRLCertificateList *crl,
460 time_t time_now,
461 hx509_certs certs,
462 hx509_cert parent)
464 hx509_cert signer;
465 hx509_query q;
466 time_t t;
467 int ret;
469 t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
470 if (t > time_now) {
471 hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
472 "CRL used before time");
473 return HX509_CRL_USED_BEFORE_TIME;
476 if (crl->tbsCertList.nextUpdate == NULL) {
477 hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
478 "CRL missing nextUpdate");
479 return HX509_CRL_INVALID_FORMAT;
482 t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
483 if (t < time_now) {
484 hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
485 "CRL used after time");
486 return HX509_CRL_USED_AFTER_TIME;
489 _hx509_query_clear(&q);
492 * If it's the signer have CRLSIGN bit set, use that as the signer
493 * cert for the certificate, otherwise, search for a certificate.
495 if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
496 signer = hx509_cert_ref(parent);
497 } else {
498 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
499 q.match |= HX509_QUERY_KU_CRLSIGN;
500 q.subject_name = &crl->tbsCertList.issuer;
502 ret = hx509_certs_find(context, certs, &q, &signer);
503 if (ret) {
504 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
505 "Failed to find certificate for CRL");
506 return ret;
510 ret = _hx509_verify_signature_bitstring(context,
511 signer,
512 &crl->signatureAlgorithm,
513 &crl->tbsCertList._save,
514 &crl->signatureValue);
515 if (ret) {
516 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
517 "CRL signature invalid");
518 goto out;
522 * If signer is not CA cert, need to check revoke status of this
523 * CRL signing cert too, this include all parent CRL signer cert
524 * up to the root *sigh*, assume root at least hve CERTSIGN flag
525 * set.
527 while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
528 hx509_cert crl_parent;
530 _hx509_query_clear(&q);
532 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
533 q.match |= HX509_QUERY_KU_CRLSIGN;
534 q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
536 ret = hx509_certs_find(context, certs, &q, &crl_parent);
537 if (ret) {
538 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
539 "Failed to find parent of CRL signer");
540 goto out;
543 ret = hx509_revoke_verify(context,
544 ctx,
545 certs,
546 time_now,
547 signer,
548 crl_parent);
549 hx509_cert_free(signer);
550 signer = crl_parent;
551 if (ret) {
552 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
553 "Failed to verify revoke "
554 "status of CRL signer");
555 goto out;
559 out:
560 hx509_cert_free(signer);
562 return ret;
565 static int
566 load_crl(const char *path, time_t *t, CRLCertificateList *crl)
568 size_t length, size;
569 struct stat sb;
570 void *data;
571 int ret;
573 memset(crl, 0, sizeof(*crl));
575 ret = rk_undumpdata(path, &data, &length);
576 if (ret)
577 return ret;
579 ret = stat(path, &sb);
580 if (ret)
581 return errno;
583 *t = sb.st_mtime;
585 ret = decode_CRLCertificateList(data, length, crl, &size);
586 rk_xfree(data);
587 if (ret)
588 return ret;
590 /* check signature is aligned */
591 if (crl->signatureValue.length & 7) {
592 free_CRLCertificateList(crl);
593 return HX509_CRYPTO_SIG_INVALID_FORMAT;
595 return 0;
599 * Add a CRL file to the revokation context.
601 * @param context hx509 context
602 * @param ctx hx509 revokation context
603 * @param path path to file that is going to be added to the context.
605 * @return An hx509 error code, see hx509_get_error_string().
607 * @ingroup hx509_revoke
611 hx509_revoke_add_crl(hx509_context context,
612 hx509_revoke_ctx ctx,
613 const char *path)
615 void *data;
616 size_t i;
617 int ret;
619 if (strncmp(path, "FILE:", 5) != 0) {
620 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
621 "unsupport type in %s", path);
622 return HX509_UNSUPPORTED_OPERATION;
626 path += 5;
628 for (i = 0; i < ctx->crls.len; i++) {
629 if (strcmp(ctx->crls.val[i].path, path) == 0)
630 return 0;
633 data = realloc(ctx->crls.val,
634 (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
635 if (data == NULL) {
636 hx509_clear_error_string(context);
637 return ENOMEM;
639 ctx->crls.val = data;
641 memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
643 ctx->crls.val[ctx->crls.len].path = strdup(path);
644 if (ctx->crls.val[ctx->crls.len].path == NULL) {
645 hx509_clear_error_string(context);
646 return ENOMEM;
649 ret = load_crl(path,
650 &ctx->crls.val[ctx->crls.len].last_modfied,
651 &ctx->crls.val[ctx->crls.len].crl);
652 if (ret) {
653 free(ctx->crls.val[ctx->crls.len].path);
654 return ret;
657 ctx->crls.len++;
659 return ret;
663 * Check that a certificate is not expired according to a revokation
664 * context. Also need the parent certificte to the check OCSP
665 * parent identifier.
667 * @param context hx509 context
668 * @param ctx hx509 revokation context
669 * @param certs
670 * @param now
671 * @param cert
672 * @param parent_cert
674 * @return An hx509 error code, see hx509_get_error_string().
676 * @ingroup hx509_revoke
681 hx509_revoke_verify(hx509_context context,
682 hx509_revoke_ctx ctx,
683 hx509_certs certs,
684 time_t now,
685 hx509_cert cert,
686 hx509_cert parent_cert)
688 const Certificate *c = _hx509_get_cert(cert);
689 const Certificate *p = _hx509_get_cert(parent_cert);
690 unsigned long i, j, k;
691 int ret;
693 hx509_clear_error_string(context);
695 for (i = 0; i < ctx->ocsps.len; i++) {
696 struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
697 struct stat sb;
699 /* check this ocsp apply to this cert */
701 /* check if there is a newer version of the file */
702 ret = stat(ocsp->path, &sb);
703 if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
704 ret = load_ocsp(context, ocsp);
705 if (ret)
706 continue;
709 /* verify signature in ocsp if not already done */
710 if (ocsp->signer == NULL) {
711 ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
712 if (ret)
713 continue;
716 for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
717 heim_octet_string os;
719 ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
720 &c->tbsCertificate.serialNumber);
721 if (ret != 0)
722 continue;
724 /* verify issuer hashes hash */
725 ret = _hx509_verify_signature(context,
726 NULL,
727 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
728 &c->tbsCertificate.issuer._save,
729 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
730 if (ret != 0)
731 continue;
733 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
734 os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
736 ret = _hx509_verify_signature(context,
737 NULL,
738 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
739 &os,
740 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
741 if (ret != 0)
742 continue;
744 switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
745 case choice_OCSPCertStatus_good:
746 break;
747 case choice_OCSPCertStatus_revoked:
748 hx509_set_error_string(context, 0,
749 HX509_CERT_REVOKED,
750 "Certificate revoked by issuer in OCSP");
751 return HX509_CERT_REVOKED;
752 case choice_OCSPCertStatus_unknown:
753 continue;
756 /* don't allow the update to be in the future */
757 if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
758 now + context->ocsp_time_diff)
759 continue;
761 /* don't allow the next update to be in the past */
762 if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
763 if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
764 continue;
765 } /* else should force a refetch, but can we ? */
767 return 0;
771 for (i = 0; i < ctx->crls.len; i++) {
772 struct revoke_crl *crl = &ctx->crls.val[i];
773 struct stat sb;
774 int diff;
776 /* check if cert.issuer == crls.val[i].crl.issuer */
777 ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
778 &crl->crl.tbsCertList.issuer, &diff);
779 if (ret || diff)
780 continue;
782 ret = stat(crl->path, &sb);
783 if (ret == 0 && crl->last_modfied != sb.st_mtime) {
784 CRLCertificateList cl;
786 ret = load_crl(crl->path, &crl->last_modfied, &cl);
787 if (ret == 0) {
788 free_CRLCertificateList(&crl->crl);
789 crl->crl = cl;
790 crl->verified = 0;
791 crl->failed_verify = 0;
794 if (crl->failed_verify)
795 continue;
797 /* verify signature in crl if not already done */
798 if (crl->verified == 0) {
799 ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
800 if (ret) {
801 crl->failed_verify = 1;
802 continue;
804 crl->verified = 1;
807 if (crl->crl.tbsCertList.crlExtensions) {
808 for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
809 if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
810 hx509_set_error_string(context, 0,
811 HX509_CRL_UNKNOWN_EXTENSION,
812 "Unknown CRL extension");
813 return HX509_CRL_UNKNOWN_EXTENSION;
818 if (crl->crl.tbsCertList.revokedCertificates == NULL)
819 return 0;
821 /* check if cert is in crl */
822 for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
823 time_t t;
825 ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
826 &c->tbsCertificate.serialNumber);
827 if (ret != 0)
828 continue;
830 t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
831 if (t > now)
832 continue;
834 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
835 for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
836 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
837 return HX509_CRL_UNKNOWN_EXTENSION;
839 hx509_set_error_string(context, 0,
840 HX509_CERT_REVOKED,
841 "Certificate revoked by issuer in CRL");
842 return HX509_CERT_REVOKED;
845 return 0;
849 if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
850 return 0;
851 hx509_set_error_string(context, HX509_ERROR_APPEND,
852 HX509_REVOKE_STATUS_MISSING,
853 "No revoke status found for "
854 "certificates");
855 return HX509_REVOKE_STATUS_MISSING;
858 struct ocsp_add_ctx {
859 OCSPTBSRequest *req;
860 hx509_certs certs;
861 const AlgorithmIdentifier *digest;
862 hx509_cert parent;
865 static int
866 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
868 struct ocsp_add_ctx *ctx = ptr;
869 OCSPInnerRequest *one;
870 hx509_cert parent = NULL;
871 Certificate *p, *c = _hx509_get_cert(cert);
872 heim_octet_string os;
873 int ret;
874 hx509_query q;
875 void *d;
877 d = realloc(ctx->req->requestList.val,
878 sizeof(ctx->req->requestList.val[0]) *
879 (ctx->req->requestList.len + 1));
880 if (d == NULL)
881 return ENOMEM;
882 ctx->req->requestList.val = d;
884 one = &ctx->req->requestList.val[ctx->req->requestList.len];
885 memset(one, 0, sizeof(*one));
887 _hx509_query_clear(&q);
889 q.match |= HX509_QUERY_FIND_ISSUER_CERT;
890 q.subject = c;
892 ret = hx509_certs_find(context, ctx->certs, &q, &parent);
893 if (ret)
894 goto out;
896 if (ctx->parent) {
897 if (hx509_cert_cmp(ctx->parent, parent) != 0) {
898 ret = HX509_REVOKE_NOT_SAME_PARENT;
899 hx509_set_error_string(context, 0, ret,
900 "Not same parent certifate as "
901 "last certificate in request");
902 goto out;
904 } else
905 ctx->parent = hx509_cert_ref(parent);
907 p = _hx509_get_cert(parent);
909 ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
910 if (ret)
911 goto out;
913 ret = _hx509_create_signature(context,
914 NULL,
915 &one->reqCert.hashAlgorithm,
916 &c->tbsCertificate.issuer._save,
917 NULL,
918 &one->reqCert.issuerNameHash);
919 if (ret)
920 goto out;
922 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
923 os.length =
924 p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
926 ret = _hx509_create_signature(context,
927 NULL,
928 &one->reqCert.hashAlgorithm,
929 &os,
930 NULL,
931 &one->reqCert.issuerKeyHash);
932 if (ret)
933 goto out;
935 ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
936 &one->reqCert.serialNumber);
937 if (ret)
938 goto out;
940 ctx->req->requestList.len++;
941 out:
942 hx509_cert_free(parent);
943 if (ret) {
944 free_OCSPInnerRequest(one);
945 memset(one, 0, sizeof(*one));
948 return ret;
952 * Create an OCSP request for a set of certificates.
954 * @param context a hx509 context
955 * @param reqcerts list of certificates to request ocsp data for
956 * @param pool certificate pool to use when signing
957 * @param signer certificate to use to sign the request
958 * @param digest the signing algorithm in the request, if NULL use the
959 * default signature algorithm,
960 * @param request the encoded request, free with free_heim_octet_string().
961 * @param nonce nonce in the request, free with free_heim_octet_string().
963 * @return An hx509 error code, see hx509_get_error_string().
965 * @ingroup hx509_revoke
969 hx509_ocsp_request(hx509_context context,
970 hx509_certs reqcerts,
971 hx509_certs pool,
972 hx509_cert signer,
973 const AlgorithmIdentifier *digest,
974 heim_octet_string *request,
975 heim_octet_string *nonce)
977 OCSPRequest req;
978 size_t size;
979 int ret;
980 struct ocsp_add_ctx ctx;
981 Extensions *es;
983 memset(&req, 0, sizeof(req));
985 if (digest == NULL)
986 digest = _hx509_crypto_default_digest_alg;
988 ctx.req = &req.tbsRequest;
989 ctx.certs = pool;
990 ctx.digest = digest;
991 ctx.parent = NULL;
993 ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
994 hx509_cert_free(ctx.parent);
995 if (ret)
996 goto out;
998 if (nonce) {
999 req.tbsRequest.requestExtensions =
1000 calloc(1, sizeof(*req.tbsRequest.requestExtensions));
1001 if (req.tbsRequest.requestExtensions == NULL) {
1002 ret = ENOMEM;
1003 goto out;
1006 es = req.tbsRequest.requestExtensions;
1008 es->val = calloc(es->len, sizeof(es->val[0]));
1009 if (es->val == NULL) {
1010 ret = ENOMEM;
1011 goto out;
1013 es->len = 1;
1014 ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
1015 if (ret) {
1016 free_OCSPRequest(&req);
1017 return ret;
1020 es->val[0].extnValue.data = malloc(10);
1021 if (es->val[0].extnValue.data == NULL) {
1022 ret = ENOMEM;
1023 goto out;
1025 es->val[0].extnValue.length = 10;
1027 ret = RAND_bytes(es->val[0].extnValue.data,
1028 es->val[0].extnValue.length);
1029 if (ret != 1) {
1030 ret = HX509_CRYPTO_INTERNAL_ERROR;
1031 goto out;
1033 ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1034 if (ret) {
1035 ret = ENOMEM;
1036 goto out;
1040 ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1041 &req, &size, ret);
1042 free_OCSPRequest(&req);
1043 if (ret)
1044 goto out;
1045 if (size != request->length)
1046 _hx509_abort("internal ASN.1 encoder error");
1048 return 0;
1050 out:
1051 free_OCSPRequest(&req);
1052 return ret;
1055 static char *
1056 printable_time(time_t t)
1058 static char s[128];
1059 char *p;
1060 if ((p = ctime(&t)) == NULL)
1061 strlcpy(s, "?", sizeof(s));
1062 else {
1063 strlcpy(s, p + 4, sizeof(s));
1064 s[20] = 0;
1066 return s;
1070 * Print the OCSP reply stored in a file.
1072 * @param context a hx509 context
1073 * @param path path to a file with a OCSP reply
1074 * @param out the out FILE descriptor to print the reply on
1076 * @return An hx509 error code, see hx509_get_error_string().
1078 * @ingroup hx509_revoke
1082 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1084 struct revoke_ocsp ocsp;
1085 int ret;
1086 size_t i;
1088 if (out == NULL)
1089 out = stdout;
1091 memset(&ocsp, 0, sizeof(ocsp));
1093 ocsp.path = strdup(path);
1094 if (ocsp.path == NULL)
1095 return ENOMEM;
1097 ret = load_ocsp(context, &ocsp);
1098 if (ret) {
1099 free_ocsp(&ocsp);
1100 return ret;
1103 fprintf(out, "signer: ");
1105 switch(ocsp.ocsp.tbsResponseData.responderID.element) {
1106 case choice_OCSPResponderID_byName: {
1107 hx509_name n;
1108 char *s;
1109 _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
1110 hx509_name_to_string(n, &s);
1111 hx509_name_free(&n);
1112 fprintf(out, " byName: %s\n", s);
1113 free(s);
1114 break;
1116 case choice_OCSPResponderID_byKey: {
1117 char *s;
1118 hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
1119 ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
1120 &s);
1121 fprintf(out, " byKey: %s\n", s);
1122 free(s);
1123 break;
1125 default:
1126 _hx509_abort("choice_OCSPResponderID unknown");
1127 break;
1130 fprintf(out, "producedAt: %s\n",
1131 printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1133 fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1135 for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1136 const char *status;
1137 switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1138 case choice_OCSPCertStatus_good:
1139 status = "good";
1140 break;
1141 case choice_OCSPCertStatus_revoked:
1142 status = "revoked";
1143 break;
1144 case choice_OCSPCertStatus_unknown:
1145 status = "unknown";
1146 break;
1147 default:
1148 status = "element unknown";
1151 fprintf(out, "\t%zu. status: %s\n", i, status);
1153 fprintf(out, "\tthisUpdate: %s\n",
1154 printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1155 if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
1156 fprintf(out, "\tproducedAt: %s\n",
1157 printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1161 fprintf(out, "appended certs:\n");
1162 if (ocsp.certs)
1163 ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);
1165 free_ocsp(&ocsp);
1166 return ret;
1170 * Verify that the certificate is part of the OCSP reply and it's not
1171 * expired. Doesn't verify signature the OCSP reply or it's done by a
1172 * authorized sender, that is assumed to be already done.
1174 * @param context a hx509 context
1175 * @param now the time right now, if 0, use the current time.
1176 * @param cert the certificate to verify
1177 * @param flags flags control the behavior
1178 * @param data pointer to the encode ocsp reply
1179 * @param length the length of the encode ocsp reply
1180 * @param expiration return the time the OCSP will expire and need to
1181 * be rechecked.
1183 * @return An hx509 error code, see hx509_get_error_string().
1185 * @ingroup hx509_verify
1189 hx509_ocsp_verify(hx509_context context,
1190 time_t now,
1191 hx509_cert cert,
1192 int flags,
1193 const void *data, size_t length,
1194 time_t *expiration)
1196 const Certificate *c = _hx509_get_cert(cert);
1197 OCSPBasicOCSPResponse basic;
1198 int ret;
1199 size_t i;
1201 if (now == 0)
1202 now = time(NULL);
1204 *expiration = 0;
1206 ret = parse_ocsp_basic(data, length, &basic);
1207 if (ret) {
1208 hx509_set_error_string(context, 0, ret,
1209 "Failed to parse OCSP response");
1210 return ret;
1213 for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1215 ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1216 &c->tbsCertificate.serialNumber);
1217 if (ret != 0)
1218 continue;
1220 /* verify issuer hashes hash */
1221 ret = _hx509_verify_signature(context,
1222 NULL,
1223 &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1224 &c->tbsCertificate.issuer._save,
1225 &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1226 if (ret != 0)
1227 continue;
1229 switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1230 case choice_OCSPCertStatus_good:
1231 break;
1232 case choice_OCSPCertStatus_revoked:
1233 case choice_OCSPCertStatus_unknown:
1234 continue;
1237 /* don't allow the update to be in the future */
1238 if (basic.tbsResponseData.responses.val[i].thisUpdate >
1239 now + context->ocsp_time_diff)
1240 continue;
1242 /* don't allow the next update to be in the past */
1243 if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1244 if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1245 continue;
1246 *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1247 } else
1248 *expiration = now;
1250 free_OCSPBasicOCSPResponse(&basic);
1251 return 0;
1254 free_OCSPBasicOCSPResponse(&basic);
1257 hx509_name name;
1258 char *subject;
1260 ret = hx509_cert_get_subject(cert, &name);
1261 if (ret) {
1262 hx509_clear_error_string(context);
1263 goto out;
1265 ret = hx509_name_to_string(name, &subject);
1266 hx509_name_free(&name);
1267 if (ret) {
1268 hx509_clear_error_string(context);
1269 goto out;
1271 hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1272 "Certificate %s not in OCSP response "
1273 "or not good",
1274 subject);
1275 free(subject);
1277 out:
1278 return HX509_CERT_NOT_IN_OCSP;
1281 struct hx509_crl {
1282 hx509_certs revoked;
1283 time_t expire;
1287 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1289 * @param context a hx509 context.
1290 * @param crl return pointer to a newly allocated CRL context.
1292 * @return An hx509 error code, see hx509_get_error_string().
1294 * @ingroup hx509_verify
1298 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1300 int ret;
1302 *crl = calloc(1, sizeof(**crl));
1303 if (*crl == NULL) {
1304 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1305 return ENOMEM;
1308 ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1309 if (ret) {
1310 free(*crl);
1311 *crl = NULL;
1312 return ret;
1314 (*crl)->expire = 0;
1315 return ret;
1319 * Add revoked certificate to an CRL context.
1321 * @param context a hx509 context.
1322 * @param crl the CRL to add the revoked certificate to.
1323 * @param certs keyset of certificate to revoke.
1325 * @return An hx509 error code, see hx509_get_error_string().
1327 * @ingroup hx509_verify
1331 hx509_crl_add_revoked_certs(hx509_context context,
1332 hx509_crl crl,
1333 hx509_certs certs)
1335 return hx509_certs_merge(context, crl->revoked, certs);
1339 * Set the lifetime of a CRL context.
1341 * @param context a hx509 context.
1342 * @param crl a CRL context
1343 * @param delta delta time the certificate is valid, library adds the
1344 * current time to this.
1346 * @return An hx509 error code, see hx509_get_error_string().
1348 * @ingroup hx509_verify
1352 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1354 crl->expire = time(NULL) + delta;
1355 return 0;
1359 * Free a CRL context.
1361 * @param context a hx509 context.
1362 * @param crl a CRL context to free.
1364 * @ingroup hx509_verify
1367 void
1368 hx509_crl_free(hx509_context context, hx509_crl *crl)
1370 if (*crl == NULL)
1371 return;
1372 hx509_certs_free(&(*crl)->revoked);
1373 memset(*crl, 0, sizeof(**crl));
1374 free(*crl);
1375 *crl = NULL;
1378 static int
1379 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1381 TBSCRLCertList *c = ctx;
1382 unsigned int num;
1383 void *ptr;
1384 int ret;
1386 num = c->revokedCertificates->len;
1387 ptr = realloc(c->revokedCertificates->val,
1388 (num + 1) * sizeof(c->revokedCertificates->val[0]));
1389 if (ptr == NULL) {
1390 hx509_clear_error_string(context);
1391 return ENOMEM;
1393 c->revokedCertificates->val = ptr;
1395 ret = hx509_cert_get_serialnumber(cert,
1396 &c->revokedCertificates->val[num].userCertificate);
1397 if (ret) {
1398 hx509_clear_error_string(context);
1399 return ret;
1401 c->revokedCertificates->val[num].revocationDate.element =
1402 choice_Time_generalTime;
1403 c->revokedCertificates->val[num].revocationDate.u.generalTime =
1404 time(NULL) - 3600 * 24;
1405 c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1407 c->revokedCertificates->len++;
1409 return 0;
1413 * Sign a CRL and return an encode certificate.
1415 * @param context a hx509 context.
1416 * @param signer certificate to sign the CRL with
1417 * @param crl the CRL to sign
1418 * @param os return the signed and encoded CRL, free with
1419 * free_heim_octet_string()
1421 * @return An hx509 error code, see hx509_get_error_string().
1423 * @ingroup hx509_verify
1427 hx509_crl_sign(hx509_context context,
1428 hx509_cert signer,
1429 hx509_crl crl,
1430 heim_octet_string *os)
1432 const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1433 CRLCertificateList c;
1434 size_t size;
1435 int ret;
1436 hx509_private_key signerkey;
1438 memset(&c, 0, sizeof(c));
1440 signerkey = _hx509_cert_private_key(signer);
1441 if (signerkey == NULL) {
1442 ret = HX509_PRIVATE_KEY_MISSING;
1443 hx509_set_error_string(context, 0, ret,
1444 "Private key missing for CRL signing");
1445 return ret;
1448 c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1449 if (c.tbsCertList.version == NULL) {
1450 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1451 return ENOMEM;
1454 *c.tbsCertList.version = 1;
1456 ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1457 if (ret) {
1458 hx509_clear_error_string(context);
1459 goto out;
1462 ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1463 &c.tbsCertList.issuer);
1464 if (ret) {
1465 hx509_clear_error_string(context);
1466 goto out;
1469 c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1470 c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1472 c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1473 if (c.tbsCertList.nextUpdate == NULL) {
1474 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1475 ret = ENOMEM;
1476 goto out;
1480 time_t next = crl->expire;
1481 if (next == 0)
1482 next = time(NULL) + 24 * 3600 * 365;
1484 c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1485 c.tbsCertList.nextUpdate->u.generalTime = next;
1488 c.tbsCertList.revokedCertificates =
1489 calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1490 if (c.tbsCertList.revokedCertificates == NULL) {
1491 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1492 ret = ENOMEM;
1493 goto out;
1495 c.tbsCertList.crlExtensions = NULL;
1497 ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
1498 if (ret)
1499 goto out;
1501 /* if not revoked certs, remove OPTIONAL entry */
1502 if (c.tbsCertList.revokedCertificates->len == 0) {
1503 free(c.tbsCertList.revokedCertificates);
1504 c.tbsCertList.revokedCertificates = NULL;
1507 ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1508 &c.tbsCertList, &size, ret);
1509 if (ret) {
1510 hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1511 goto out;
1513 if (size != os->length)
1514 _hx509_abort("internal ASN.1 encoder error");
1517 ret = _hx509_create_signature_bitstring(context,
1518 signerkey,
1519 sigalg,
1521 &c.signatureAlgorithm,
1522 &c.signatureValue);
1523 free(os->data);
1524 if (ret) {
1525 hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
1526 goto out;
1529 ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1530 &c, &size, ret);
1531 if (ret) {
1532 hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1533 goto out;
1535 if (size != os->length)
1536 _hx509_abort("internal ASN.1 encoder error");
1538 free_CRLCertificateList(&c);
1540 return 0;
1542 out:
1543 free_CRLCertificateList(&c);
1544 return ret;