add NO_STORE
[heimdal.git] / lib / hx509 / revoke.c
blob25d274fac22213857c1a3d66839d9eb0a3590510
1 /*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 /**
35 * @page page_revoke Revocation methods
37 * There are two revocation method for PKIX/X.509: CRL and OCSP.
38 * Revocation is needed if the private key is lost and
39 * stolen. Depending on how picky you are, you might want to make
40 * revocation for destroyed private keys too (smartcard broken), but
41 * that should not be a problem.
43 * CRL is a list of certifiates that have expired.
45 * OCSP is an online checking method where the requestor sends a list
46 * of certificates to the OCSP server to return a signed reply if they
47 * are valid or not. Some services sends a OCSP reply as part of the
48 * hand-shake to make the revoktion decision simpler/faster for the
49 * client.
52 #include "hx_locl.h"
53 RCSID("$Id$");
55 struct revoke_crl {
56 char *path;
57 time_t last_modfied;
58 CRLCertificateList crl;
59 int verified;
60 int failed_verify;
63 struct revoke_ocsp {
64 char *path;
65 time_t last_modfied;
66 OCSPBasicOCSPResponse ocsp;
67 hx509_certs certs;
68 hx509_cert signer;
72 struct hx509_revoke_ctx_data {
73 unsigned ref;
74 struct {
75 struct revoke_crl *val;
76 size_t len;
77 } crls;
78 struct {
79 struct revoke_ocsp *val;
80 size_t len;
81 } ocsps;
84 /**
85 * Allocate a revokation context. Free with hx509_revoke_free().
87 * @param context A hx509 context.
88 * @param ctx returns a newly allocated revokation context.
90 * @return An hx509 error code, see hx509_get_error_string().
92 * @ingroup hx509_revoke
95 int
96 hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
98 *ctx = calloc(1, sizeof(**ctx));
99 if (*ctx == NULL)
100 return ENOMEM;
102 (*ctx)->ref = 1;
103 (*ctx)->crls.len = 0;
104 (*ctx)->crls.val = NULL;
105 (*ctx)->ocsps.len = 0;
106 (*ctx)->ocsps.val = NULL;
108 return 0;
111 hx509_revoke_ctx
112 _hx509_revoke_ref(hx509_revoke_ctx ctx)
114 if (ctx == NULL)
115 return NULL;
116 if (ctx->ref <= 0)
117 _hx509_abort("revoke ctx refcount <= 0");
118 ctx->ref++;
119 if (ctx->ref == 0)
120 _hx509_abort("revoke ctx refcount == 0");
121 return ctx;
124 static void
125 free_ocsp(struct revoke_ocsp *ocsp)
127 free(ocsp->path);
128 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
129 hx509_certs_free(&ocsp->certs);
130 hx509_cert_free(ocsp->signer);
134 * Free a hx509 revokation context.
136 * @param ctx context to be freed
138 * @ingroup hx509_revoke
141 void
142 hx509_revoke_free(hx509_revoke_ctx *ctx)
144 size_t i ;
146 if (ctx == NULL || *ctx == NULL)
147 return;
149 if ((*ctx)->ref <= 0)
150 _hx509_abort("revoke ctx refcount <= 0 on free");
151 if (--(*ctx)->ref > 0)
152 return;
154 for (i = 0; i < (*ctx)->crls.len; i++) {
155 free((*ctx)->crls.val[i].path);
156 free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
159 for (i = 0; i < (*ctx)->ocsps.len; i++)
160 free_ocsp(&(*ctx)->ocsps.val[i]);
161 free((*ctx)->ocsps.val);
163 free((*ctx)->crls.val);
165 memset(*ctx, 0, sizeof(**ctx));
166 free(*ctx);
167 *ctx = NULL;
170 static int
171 verify_ocsp(hx509_context context,
172 struct revoke_ocsp *ocsp,
173 time_t time_now,
174 hx509_certs certs,
175 hx509_cert parent)
177 hx509_cert signer = NULL;
178 hx509_query q;
179 int ret;
181 _hx509_query_clear(&q);
184 * Need to match on issuer too in case there are two CA that have
185 * issued the same name to a certificate. One example of this is
186 * the www.openvalidation.org test's ocsp validator.
189 q.match = HX509_QUERY_MATCH_ISSUER_NAME;
190 q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
192 switch(ocsp->ocsp.tbsResponseData.responderID.element) {
193 case choice_OCSPResponderID_byName:
194 q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
195 q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
196 break;
197 case choice_OCSPResponderID_byKey:
198 q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
199 q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
200 break;
203 ret = hx509_certs_find(context, certs, &q, &signer);
204 if (ret && ocsp->certs)
205 ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
206 if (ret)
207 goto out;
210 * If signer certificate isn't the CA certificate, lets check the
211 * it is the CA that signed the signer certificate and the OCSP EKU
212 * is set.
214 if (hx509_cert_cmp(signer, parent) != 0) {
215 Certificate *p = _hx509_get_cert(parent);
216 Certificate *s = _hx509_get_cert(signer);
218 ret = _hx509_cert_is_parent_cmp(s, p, 0);
219 if (ret != 0) {
220 ret = HX509_PARENT_NOT_CA;
221 hx509_set_error_string(context, 0, ret, "Revoke OSCP signer is "
222 "doesn't have CA as signer certificate");
223 goto out;
226 ret = _hx509_verify_signature_bitstring(context,
228 &s->signatureAlgorithm,
229 &s->tbsCertificate._save,
230 &s->signatureValue);
231 if (ret) {
232 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
233 "OSCP signer signature invalid");
234 goto out;
237 ret = hx509_cert_check_eku(context, signer,
238 oid_id_pkix_kp_OCSPSigning(), 0);
239 if (ret)
240 goto out;
243 ret = _hx509_verify_signature_bitstring(context,
244 _hx509_get_cert(signer),
245 &ocsp->ocsp.signatureAlgorithm,
246 &ocsp->ocsp.tbsResponseData._save,
247 &ocsp->ocsp.signature);
248 if (ret) {
249 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
250 "OSCP signature invalid");
251 goto out;
254 ocsp->signer = signer;
255 signer = NULL;
256 out:
257 if (signer)
258 hx509_cert_free(signer);
260 return ret;
267 static int
268 parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
270 OCSPResponse resp;
271 size_t size;
272 int ret;
274 memset(basic, 0, sizeof(*basic));
276 ret = decode_OCSPResponse(data, length, &resp, &size);
277 if (ret)
278 return ret;
279 if (length != size) {
280 free_OCSPResponse(&resp);
281 return ASN1_EXTRA_DATA;
284 switch (resp.responseStatus) {
285 case successful:
286 break;
287 default:
288 free_OCSPResponse(&resp);
289 return HX509_REVOKE_WRONG_DATA;
292 if (resp.responseBytes == NULL) {
293 free_OCSPResponse(&resp);
294 return EINVAL;
297 ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
298 oid_id_pkix_ocsp_basic());
299 if (ret != 0) {
300 free_OCSPResponse(&resp);
301 return HX509_REVOKE_WRONG_DATA;
304 ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
305 resp.responseBytes->response.length,
306 basic,
307 &size);
308 if (ret) {
309 free_OCSPResponse(&resp);
310 return ret;
312 if (size != resp.responseBytes->response.length) {
313 free_OCSPResponse(&resp);
314 free_OCSPBasicOCSPResponse(basic);
315 return ASN1_EXTRA_DATA;
317 free_OCSPResponse(&resp);
319 return 0;
326 static int
327 load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
329 OCSPBasicOCSPResponse basic;
330 hx509_certs certs = NULL;
331 size_t length;
332 struct stat sb;
333 void *data;
334 int ret;
336 ret = _hx509_map_file(ocsp->path, &data, &length, &sb);
337 if (ret)
338 return ret;
340 ret = parse_ocsp_basic(data, length, &basic);
341 _hx509_unmap_file(data, length);
342 if (ret) {
343 hx509_set_error_string(context, 0, ret,
344 "Failed to parse OCSP response");
345 return ret;
348 if (basic.certs) {
349 int i;
351 ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
352 NULL, &certs);
353 if (ret) {
354 free_OCSPBasicOCSPResponse(&basic);
355 return ret;
358 for (i = 0; i < basic.certs->len; i++) {
359 hx509_cert c;
361 ret = hx509_cert_init(context, &basic.certs->val[i], &c);
362 if (ret)
363 continue;
365 ret = hx509_certs_add(context, certs, c);
366 hx509_cert_free(c);
367 if (ret)
368 continue;
372 ocsp->last_modfied = sb.st_mtime;
374 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
375 hx509_certs_free(&ocsp->certs);
376 hx509_cert_free(ocsp->signer);
378 ocsp->ocsp = basic;
379 ocsp->certs = certs;
380 ocsp->signer = NULL;
382 return 0;
386 * Add a OCSP file to the revokation context.
388 * @param context hx509 context
389 * @param ctx hx509 revokation context
390 * @param path path to file that is going to be added to the context.
392 * @return An hx509 error code, see hx509_get_error_string().
394 * @ingroup hx509_revoke
398 hx509_revoke_add_ocsp(hx509_context context,
399 hx509_revoke_ctx ctx,
400 const char *path)
402 void *data;
403 int ret;
404 size_t i;
406 if (strncmp(path, "FILE:", 5) != 0) {
407 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
408 "unsupport type in %s", path);
409 return HX509_UNSUPPORTED_OPERATION;
412 path += 5;
414 for (i = 0; i < ctx->ocsps.len; i++) {
415 if (strcmp(ctx->ocsps.val[0].path, path) == 0)
416 return 0;
419 data = realloc(ctx->ocsps.val,
420 (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
421 if (data == NULL) {
422 hx509_clear_error_string(context);
423 return ENOMEM;
426 ctx->ocsps.val = data;
428 memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
429 sizeof(ctx->ocsps.val[0]));
431 ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
432 if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
433 hx509_clear_error_string(context);
434 return ENOMEM;
437 ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
438 if (ret) {
439 free(ctx->ocsps.val[ctx->ocsps.len].path);
440 return ret;
442 ctx->ocsps.len++;
444 return ret;
451 static int
452 verify_crl(hx509_context context,
453 hx509_revoke_ctx ctx,
454 CRLCertificateList *crl,
455 time_t time_now,
456 hx509_certs certs,
457 hx509_cert parent)
459 hx509_cert signer;
460 hx509_query q;
461 time_t t;
462 int ret;
464 t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
465 if (t > time_now) {
466 hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
467 "CRL used before time");
468 return HX509_CRL_USED_BEFORE_TIME;
471 if (crl->tbsCertList.nextUpdate == NULL) {
472 hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
473 "CRL missing nextUpdate");
474 return HX509_CRL_INVALID_FORMAT;
477 t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
478 if (t < time_now) {
479 hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
480 "CRL used after time");
481 return HX509_CRL_USED_AFTER_TIME;
484 _hx509_query_clear(&q);
487 * If it's the signer have CRLSIGN bit set, use that as the signer
488 * cert for the certificate, otherwise, search for a certificate.
490 if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
491 signer = hx509_cert_ref(parent);
492 } else {
493 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
494 q.match |= HX509_QUERY_KU_CRLSIGN;
495 q.subject_name = &crl->tbsCertList.issuer;
497 ret = hx509_certs_find(context, certs, &q, &signer);
498 if (ret) {
499 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
500 "Failed to find certificate for CRL");
501 return ret;
505 ret = _hx509_verify_signature_bitstring(context,
506 _hx509_get_cert(signer),
507 &crl->signatureAlgorithm,
508 &crl->tbsCertList._save,
509 &crl->signatureValue);
510 if (ret) {
511 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
512 "CRL signature invalid");
513 goto out;
517 * If signer is not CA cert, need to check revoke status of this
518 * CRL signing cert too, this include all parent CRL signer cert
519 * up to the root *sigh*, assume root at least hve CERTSIGN flag
520 * set.
522 while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
523 hx509_cert crl_parent;
525 _hx509_query_clear(&q);
527 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
528 q.match |= HX509_QUERY_KU_CRLSIGN;
529 q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
531 ret = hx509_certs_find(context, certs, &q, &crl_parent);
532 if (ret) {
533 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
534 "Failed to find parent of CRL signer");
535 goto out;
538 ret = hx509_revoke_verify(context,
539 ctx,
540 certs,
541 time_now,
542 signer,
543 crl_parent);
544 hx509_cert_free(signer);
545 signer = crl_parent;
546 if (ret) {
547 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
548 "Failed to verify revoke "
549 "status of CRL signer");
550 goto out;
554 out:
555 hx509_cert_free(signer);
557 return ret;
560 static int
561 load_crl(const char *path, time_t *t, CRLCertificateList *crl)
563 size_t length, size;
564 struct stat sb;
565 void *data;
566 int ret;
568 memset(crl, 0, sizeof(*crl));
570 ret = _hx509_map_file(path, &data, &length, &sb);
571 if (ret)
572 return ret;
574 *t = sb.st_mtime;
576 ret = decode_CRLCertificateList(data, length, crl, &size);
577 _hx509_unmap_file(data, length);
578 if (ret)
579 return ret;
581 /* check signature is aligned */
582 if (crl->signatureValue.length & 7) {
583 free_CRLCertificateList(crl);
584 return HX509_CRYPTO_SIG_INVALID_FORMAT;
586 return 0;
590 * Add a CRL file to the revokation context.
592 * @param context hx509 context
593 * @param ctx hx509 revokation context
594 * @param path path to file that is going to be added to the context.
596 * @return An hx509 error code, see hx509_get_error_string().
598 * @ingroup hx509_revoke
602 hx509_revoke_add_crl(hx509_context context,
603 hx509_revoke_ctx ctx,
604 const char *path)
606 void *data;
607 size_t i;
608 int ret;
610 if (strncmp(path, "FILE:", 5) != 0) {
611 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
612 "unsupport type in %s", path);
613 return HX509_UNSUPPORTED_OPERATION;
617 path += 5;
619 for (i = 0; i < ctx->crls.len; i++) {
620 if (strcmp(ctx->crls.val[0].path, path) == 0)
621 return 0;
624 data = realloc(ctx->crls.val,
625 (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
626 if (data == NULL) {
627 hx509_clear_error_string(context);
628 return ENOMEM;
630 ctx->crls.val = data;
632 memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
634 ctx->crls.val[ctx->crls.len].path = strdup(path);
635 if (ctx->crls.val[ctx->crls.len].path == NULL) {
636 hx509_clear_error_string(context);
637 return ENOMEM;
640 ret = load_crl(path,
641 &ctx->crls.val[ctx->crls.len].last_modfied,
642 &ctx->crls.val[ctx->crls.len].crl);
643 if (ret) {
644 free(ctx->crls.val[ctx->crls.len].path);
645 return ret;
648 ctx->crls.len++;
650 return ret;
654 * Check that a certificate is not expired according to a revokation
655 * context. Also need the parent certificte to the check OCSP
656 * parent identifier.
658 * @param context hx509 context
659 * @param ctx hx509 revokation context
660 * @param certs
661 * @param now
662 * @param cert
663 * @param parent_cert
665 * @return An hx509 error code, see hx509_get_error_string().
667 * @ingroup hx509_revoke
672 hx509_revoke_verify(hx509_context context,
673 hx509_revoke_ctx ctx,
674 hx509_certs certs,
675 time_t now,
676 hx509_cert cert,
677 hx509_cert parent_cert)
679 const Certificate *c = _hx509_get_cert(cert);
680 const Certificate *p = _hx509_get_cert(parent_cert);
681 unsigned long i, j, k;
682 int ret;
684 hx509_clear_error_string(context);
686 for (i = 0; i < ctx->ocsps.len; i++) {
687 struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
688 struct stat sb;
690 /* check this ocsp apply to this cert */
692 /* check if there is a newer version of the file */
693 ret = stat(ocsp->path, &sb);
694 if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
695 ret = load_ocsp(context, ocsp);
696 if (ret)
697 continue;
700 /* verify signature in ocsp if not already done */
701 if (ocsp->signer == NULL) {
702 ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
703 if (ret)
704 continue;
707 for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
708 heim_octet_string os;
710 ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
711 &c->tbsCertificate.serialNumber);
712 if (ret != 0)
713 continue;
715 /* verify issuer hashes hash */
716 ret = _hx509_verify_signature(context,
717 NULL,
718 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
719 &c->tbsCertificate.issuer._save,
720 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
721 if (ret != 0)
722 continue;
724 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
725 os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
727 ret = _hx509_verify_signature(context,
728 NULL,
729 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
730 &os,
731 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
732 if (ret != 0)
733 continue;
735 switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
736 case choice_OCSPCertStatus_good:
737 break;
738 case choice_OCSPCertStatus_revoked:
739 hx509_set_error_string(context, 0,
740 HX509_CERT_REVOKED,
741 "Certificate revoked by issuer in OCSP");
742 return HX509_CERT_REVOKED;
743 case choice_OCSPCertStatus_unknown:
744 continue;
747 /* don't allow the update to be in the future */
748 if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
749 now + context->ocsp_time_diff)
750 continue;
752 /* don't allow the next update to be in the past */
753 if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
754 if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
755 continue;
756 } else
757 /* Should force a refetch, but can we ? */;
759 return 0;
763 for (i = 0; i < ctx->crls.len; i++) {
764 struct revoke_crl *crl = &ctx->crls.val[i];
765 struct stat sb;
767 /* check if cert.issuer == crls.val[i].crl.issuer */
768 ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
769 &crl->crl.tbsCertList.issuer);
770 if (ret)
771 continue;
773 ret = stat(crl->path, &sb);
774 if (ret == 0 && crl->last_modfied != sb.st_mtime) {
775 CRLCertificateList cl;
777 ret = load_crl(crl->path, &crl->last_modfied, &cl);
778 if (ret == 0) {
779 free_CRLCertificateList(&crl->crl);
780 crl->crl = cl;
781 crl->verified = 0;
782 crl->failed_verify = 0;
785 if (crl->failed_verify)
786 continue;
788 /* verify signature in crl if not already done */
789 if (crl->verified == 0) {
790 ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
791 if (ret) {
792 crl->failed_verify = 1;
793 continue;
795 crl->verified = 1;
798 if (crl->crl.tbsCertList.crlExtensions) {
799 for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
800 if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
801 hx509_set_error_string(context, 0,
802 HX509_CRL_UNKNOWN_EXTENSION,
803 "Unknown CRL extension");
804 return HX509_CRL_UNKNOWN_EXTENSION;
809 if (crl->crl.tbsCertList.revokedCertificates == NULL)
810 return 0;
812 /* check if cert is in crl */
813 for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
814 time_t t;
816 ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
817 &c->tbsCertificate.serialNumber);
818 if (ret != 0)
819 continue;
821 t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
822 if (t > now)
823 continue;
825 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
826 for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
827 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
828 return HX509_CRL_UNKNOWN_EXTENSION;
830 hx509_set_error_string(context, 0,
831 HX509_CERT_REVOKED,
832 "Certificate revoked by issuer in CRL");
833 return HX509_CERT_REVOKED;
836 return 0;
840 if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
841 return 0;
842 hx509_set_error_string(context, HX509_ERROR_APPEND,
843 HX509_REVOKE_STATUS_MISSING,
844 "No revoke status found for "
845 "certificates");
846 return HX509_REVOKE_STATUS_MISSING;
849 struct ocsp_add_ctx {
850 OCSPTBSRequest *req;
851 hx509_certs certs;
852 const AlgorithmIdentifier *digest;
853 hx509_cert parent;
856 static int
857 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
859 struct ocsp_add_ctx *ctx = ptr;
860 OCSPInnerRequest *one;
861 hx509_cert parent = NULL;
862 Certificate *p, *c = _hx509_get_cert(cert);
863 heim_octet_string os;
864 int ret;
865 hx509_query q;
866 void *d;
868 d = realloc(ctx->req->requestList.val,
869 sizeof(ctx->req->requestList.val[0]) *
870 (ctx->req->requestList.len + 1));
871 if (d == NULL)
872 return ENOMEM;
873 ctx->req->requestList.val = d;
875 one = &ctx->req->requestList.val[ctx->req->requestList.len];
876 memset(one, 0, sizeof(*one));
878 _hx509_query_clear(&q);
880 q.match |= HX509_QUERY_FIND_ISSUER_CERT;
881 q.subject = c;
883 ret = hx509_certs_find(context, ctx->certs, &q, &parent);
884 if (ret)
885 goto out;
887 if (ctx->parent) {
888 if (hx509_cert_cmp(ctx->parent, parent) != 0) {
889 ret = HX509_REVOKE_NOT_SAME_PARENT;
890 hx509_set_error_string(context, 0, ret,
891 "Not same parent certifate as "
892 "last certificate in request");
893 goto out;
895 } else
896 ctx->parent = hx509_cert_ref(parent);
898 p = _hx509_get_cert(parent);
900 ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
901 if (ret)
902 goto out;
904 ret = _hx509_create_signature(context,
905 NULL,
906 &one->reqCert.hashAlgorithm,
907 &c->tbsCertificate.issuer._save,
908 NULL,
909 &one->reqCert.issuerNameHash);
910 if (ret)
911 goto out;
913 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
914 os.length =
915 p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
917 ret = _hx509_create_signature(context,
918 NULL,
919 &one->reqCert.hashAlgorithm,
920 &os,
921 NULL,
922 &one->reqCert.issuerKeyHash);
923 if (ret)
924 goto out;
926 ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
927 &one->reqCert.serialNumber);
928 if (ret)
929 goto out;
931 ctx->req->requestList.len++;
932 out:
933 hx509_cert_free(parent);
934 if (ret) {
935 free_OCSPInnerRequest(one);
936 memset(one, 0, sizeof(*one));
939 return ret;
943 * Create an OCSP request for a set of certificates.
945 * @param context a hx509 context
946 * @param reqcerts list of certificates to request ocsp data for
947 * @param pool certificate pool to use when signing
948 * @param signer certificate to use to sign the request
949 * @param digest the signing algorithm in the request, if NULL use the
950 * default signature algorithm,
951 * @param request the encoded request, free with free_heim_octet_string().
952 * @param nonce nonce in the request, free with free_heim_octet_string().
954 * @return An hx509 error code, see hx509_get_error_string().
956 * @ingroup hx509_revoke
960 hx509_ocsp_request(hx509_context context,
961 hx509_certs reqcerts,
962 hx509_certs pool,
963 hx509_cert signer,
964 const AlgorithmIdentifier *digest,
965 heim_octet_string *request,
966 heim_octet_string *nonce)
968 OCSPRequest req;
969 size_t size;
970 int ret;
971 struct ocsp_add_ctx ctx;
972 Extensions *es;
974 memset(&req, 0, sizeof(req));
976 if (digest == NULL)
977 digest = _hx509_crypto_default_digest_alg;
979 ctx.req = &req.tbsRequest;
980 ctx.certs = pool;
981 ctx.digest = digest;
982 ctx.parent = NULL;
984 ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx);
985 hx509_cert_free(ctx.parent);
986 if (ret)
987 goto out;
989 if (nonce) {
990 req.tbsRequest.requestExtensions =
991 calloc(1, sizeof(*req.tbsRequest.requestExtensions));
992 if (req.tbsRequest.requestExtensions == NULL) {
993 ret = ENOMEM;
994 goto out;
997 es = req.tbsRequest.requestExtensions;
999 es->val = calloc(es->len, sizeof(es->val[0]));
1000 if (es->val == NULL) {
1001 ret = ENOMEM;
1002 goto out;
1004 es->len = 1;
1006 ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID);
1007 if (ret) {
1008 free_OCSPRequest(&req);
1009 return ret;
1012 es->val[0].extnValue.data = malloc(10);
1013 if (es->val[0].extnValue.data == NULL) {
1014 ret = ENOMEM;
1015 goto out;
1017 es->val[0].extnValue.length = 10;
1019 ret = RAND_bytes(es->val[0].extnValue.data,
1020 es->val[0].extnValue.length);
1021 if (ret != 1) {
1022 ret = HX509_CRYPTO_INTERNAL_ERROR;
1023 goto out;
1025 ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1026 if (ret) {
1027 ret = ENOMEM;
1028 goto out;
1032 ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1033 &req, &size, ret);
1034 free_OCSPRequest(&req);
1035 if (ret)
1036 goto out;
1037 if (size != request->length)
1038 _hx509_abort("internal ASN.1 encoder error");
1040 return 0;
1042 out:
1043 free_OCSPRequest(&req);
1044 return ret;
1047 static char *
1048 printable_time(time_t t)
1050 static char s[128];
1051 strlcpy(s, ctime(&t)+ 4, sizeof(s));
1052 s[20] = 0;
1053 return s;
1057 * Print the OCSP reply stored in a file.
1059 * @param context a hx509 context
1060 * @param path path to a file with a OCSP reply
1061 * @param out the out FILE descriptor to print the reply on
1063 * @return An hx509 error code, see hx509_get_error_string().
1065 * @ingroup hx509_revoke
1069 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1071 struct revoke_ocsp ocsp;
1072 int ret, i;
1074 if (out == NULL)
1075 out = stdout;
1077 memset(&ocsp, 0, sizeof(ocsp));
1079 ocsp.path = strdup(path);
1080 if (ocsp.path == NULL)
1081 return ENOMEM;
1083 ret = load_ocsp(context, &ocsp);
1084 if (ret) {
1085 free_ocsp(&ocsp);
1086 return ret;
1089 fprintf(out, "signer: ");
1091 switch(ocsp.ocsp.tbsResponseData.responderID.element) {
1092 case choice_OCSPResponderID_byName: {
1093 hx509_name n;
1094 char *s;
1095 _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
1096 hx509_name_to_string(n, &s);
1097 hx509_name_free(&n);
1098 fprintf(out, " byName: %s\n", s);
1099 free(s);
1100 break;
1102 case choice_OCSPResponderID_byKey: {
1103 char *s;
1104 hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
1105 ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
1106 &s);
1107 fprintf(out, " byKey: %s\n", s);
1108 free(s);
1109 break;
1111 default:
1112 _hx509_abort("choice_OCSPResponderID unknown");
1113 break;
1116 fprintf(out, "producedAt: %s\n",
1117 printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1119 fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1121 for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1122 const char *status;
1123 switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1124 case choice_OCSPCertStatus_good:
1125 status = "good";
1126 break;
1127 case choice_OCSPCertStatus_revoked:
1128 status = "revoked";
1129 break;
1130 case choice_OCSPCertStatus_unknown:
1131 status = "unknown";
1132 break;
1133 default:
1134 status = "element unknown";
1137 fprintf(out, "\t%d. status: %s\n", i, status);
1139 fprintf(out, "\tthisUpdate: %s\n",
1140 printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1141 if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
1142 fprintf(out, "\tproducedAt: %s\n",
1143 printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1147 fprintf(out, "appended certs:\n");
1148 if (ocsp.certs)
1149 ret = hx509_certs_iter(context, ocsp.certs, hx509_ci_print_names, out);
1151 free_ocsp(&ocsp);
1152 return ret;
1156 * Verify that the certificate is part of the OCSP reply and it's not
1157 * expired. Doesn't verify signature the OCSP reply or it's done by a
1158 * authorized sender, that is assumed to be already done.
1160 * @param context a hx509 context
1161 * @param now the time right now, if 0, use the current time.
1162 * @param cert the certificate to verify
1163 * @param flags flags control the behavior
1164 * @param data pointer to the encode ocsp reply
1165 * @param length the length of the encode ocsp reply
1166 * @param expiration return the time the OCSP will expire and need to
1167 * be rechecked.
1169 * @return An hx509 error code, see hx509_get_error_string().
1171 * @ingroup hx509_verify
1175 hx509_ocsp_verify(hx509_context context,
1176 time_t now,
1177 hx509_cert cert,
1178 int flags,
1179 const void *data, size_t length,
1180 time_t *expiration)
1182 const Certificate *c = _hx509_get_cert(cert);
1183 OCSPBasicOCSPResponse basic;
1184 int ret, i;
1186 if (now == 0)
1187 now = time(NULL);
1189 *expiration = 0;
1191 ret = parse_ocsp_basic(data, length, &basic);
1192 if (ret) {
1193 hx509_set_error_string(context, 0, ret,
1194 "Failed to parse OCSP response");
1195 return ret;
1198 for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1200 ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1201 &c->tbsCertificate.serialNumber);
1202 if (ret != 0)
1203 continue;
1205 /* verify issuer hashes hash */
1206 ret = _hx509_verify_signature(context,
1207 NULL,
1208 &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1209 &c->tbsCertificate.issuer._save,
1210 &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1211 if (ret != 0)
1212 continue;
1214 switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1215 case choice_OCSPCertStatus_good:
1216 break;
1217 case choice_OCSPCertStatus_revoked:
1218 case choice_OCSPCertStatus_unknown:
1219 continue;
1222 /* don't allow the update to be in the future */
1223 if (basic.tbsResponseData.responses.val[i].thisUpdate >
1224 now + context->ocsp_time_diff)
1225 continue;
1227 /* don't allow the next update to be in the past */
1228 if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1229 if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1230 continue;
1231 *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1232 } else
1233 *expiration = now;
1235 free_OCSPBasicOCSPResponse(&basic);
1236 return 0;
1239 free_OCSPBasicOCSPResponse(&basic);
1242 hx509_name name;
1243 char *subject;
1245 ret = hx509_cert_get_subject(cert, &name);
1246 if (ret) {
1247 hx509_clear_error_string(context);
1248 goto out;
1250 ret = hx509_name_to_string(name, &subject);
1251 hx509_name_free(&name);
1252 if (ret) {
1253 hx509_clear_error_string(context);
1254 goto out;
1256 hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1257 "Certificate %s not in OCSP response "
1258 "or not good",
1259 subject);
1260 free(subject);
1262 out:
1263 return HX509_CERT_NOT_IN_OCSP;
1266 struct hx509_crl {
1267 hx509_certs revoked;
1268 time_t expire;
1272 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1274 * @param context a hx509 context.
1275 * @param crl return pointer to a newly allocated CRL context.
1277 * @return An hx509 error code, see hx509_get_error_string().
1279 * @ingroup hx509_verify
1283 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1285 int ret;
1287 *crl = calloc(1, sizeof(**crl));
1288 if (*crl == NULL) {
1289 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1290 return ENOMEM;
1293 ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1294 if (ret) {
1295 free(*crl);
1296 *crl = NULL;
1297 return ret;
1299 (*crl)->expire = 0;
1300 return ret;
1304 * Add revoked certificate to an CRL context.
1306 * @param context a hx509 context.
1307 * @param crl the CRL to add the revoked certificate to.
1308 * @param certs keyset of certificate to revoke.
1310 * @return An hx509 error code, see hx509_get_error_string().
1312 * @ingroup hx509_verify
1316 hx509_crl_add_revoked_certs(hx509_context context,
1317 hx509_crl crl,
1318 hx509_certs certs)
1320 return hx509_certs_merge(context, crl->revoked, certs);
1324 * Set the lifetime of a CRL context.
1326 * @param context a hx509 context.
1327 * @param crl a CRL context
1328 * @param delta delta time the certificate is valid, library adds the
1329 * current time to this.
1331 * @return An hx509 error code, see hx509_get_error_string().
1333 * @ingroup hx509_verify
1337 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1339 crl->expire = time(NULL) + delta;
1340 return 0;
1344 * Free a CRL context.
1346 * @param context a hx509 context.
1347 * @param crl a CRL context to free.
1349 * @ingroup hx509_verify
1352 void
1353 hx509_crl_free(hx509_context context, hx509_crl *crl)
1355 if (*crl == NULL)
1356 return;
1357 hx509_certs_free(&(*crl)->revoked);
1358 memset(*crl, 0, sizeof(**crl));
1359 free(*crl);
1360 *crl = NULL;
1363 static int
1364 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1366 TBSCRLCertList *c = ctx;
1367 unsigned int num;
1368 void *ptr;
1369 int ret;
1371 num = c->revokedCertificates->len;
1372 ptr = realloc(c->revokedCertificates->val,
1373 (num + 1) * sizeof(c->revokedCertificates->val[0]));
1374 if (ptr == NULL) {
1375 hx509_clear_error_string(context);
1376 return ENOMEM;
1378 c->revokedCertificates->val = ptr;
1380 ret = hx509_cert_get_serialnumber(cert,
1381 &c->revokedCertificates->val[num].userCertificate);
1382 if (ret) {
1383 hx509_clear_error_string(context);
1384 return ret;
1386 c->revokedCertificates->val[num].revocationDate.element =
1387 choice_Time_generalTime;
1388 c->revokedCertificates->val[num].revocationDate.u.generalTime =
1389 time(NULL) - 3600 * 24;
1390 c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1392 c->revokedCertificates->len++;
1394 return 0;
1398 * Sign a CRL and return an encode certificate.
1400 * @param context a hx509 context.
1401 * @param signer certificate to sign the CRL with
1402 * @param crl the CRL to sign
1403 * @param os return the signed and encoded CRL, free with
1404 * free_heim_octet_string()
1406 * @return An hx509 error code, see hx509_get_error_string().
1408 * @ingroup hx509_verify
1412 hx509_crl_sign(hx509_context context,
1413 hx509_cert signer,
1414 hx509_crl crl,
1415 heim_octet_string *os)
1417 const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1418 CRLCertificateList c;
1419 size_t size;
1420 int ret;
1421 hx509_private_key signerkey;
1423 memset(&c, 0, sizeof(c));
1425 signerkey = _hx509_cert_private_key(signer);
1426 if (signerkey == NULL) {
1427 ret = HX509_PRIVATE_KEY_MISSING;
1428 hx509_set_error_string(context, 0, ret,
1429 "Private key missing for CRL signing");
1430 return ret;
1433 c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1434 if (c.tbsCertList.version == NULL) {
1435 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1436 return ENOMEM;
1439 *c.tbsCertList.version = 1;
1441 ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1442 if (ret) {
1443 hx509_clear_error_string(context);
1444 goto out;
1447 ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1448 &c.tbsCertList.issuer);
1449 if (ret) {
1450 hx509_clear_error_string(context);
1451 goto out;
1454 c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1455 c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1457 c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1458 if (c.tbsCertList.nextUpdate == NULL) {
1459 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1460 ret = ENOMEM;
1461 goto out;
1465 time_t next = crl->expire;
1466 if (next == 0)
1467 next = time(NULL) + 24 * 3600 * 365;
1469 c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1470 c.tbsCertList.nextUpdate->u.generalTime = next;
1473 c.tbsCertList.revokedCertificates =
1474 calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1475 if (c.tbsCertList.revokedCertificates == NULL) {
1476 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1477 ret = ENOMEM;
1478 goto out;
1480 c.tbsCertList.crlExtensions = NULL;
1482 ret = hx509_certs_iter(context, crl->revoked, add_revoked, &c.tbsCertList);
1483 if (ret)
1484 goto out;
1486 /* if not revoked certs, remove OPTIONAL entry */
1487 if (c.tbsCertList.revokedCertificates->len == 0) {
1488 free(c.tbsCertList.revokedCertificates);
1489 c.tbsCertList.revokedCertificates = NULL;
1492 ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1493 &c.tbsCertList, &size, ret);
1494 if (ret) {
1495 hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1496 goto out;
1498 if (size != os->length)
1499 _hx509_abort("internal ASN.1 encoder error");
1502 ret = _hx509_create_signature_bitstring(context,
1503 signerkey,
1504 sigalg,
1506 &c.signatureAlgorithm,
1507 &c.signatureValue);
1508 free(os->data);
1510 ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1511 &c, &size, ret);
1512 free_CRLCertificateList(&c);
1513 if (ret) {
1514 hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1515 goto out;
1517 if (size != os->length)
1518 _hx509_abort("internal ASN.1 encoder error");
1520 return 0;
1522 out:
1523 free_CRLCertificateList(&c);
1524 return ret;