No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / lib / hx509 / revoke.c
blob4bf1cc113468644fbca88fcc889f9988abd97fc2
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("$Heimdal: revoke.c 22275 2007-12-11 11:02:11Z lha $"
54 "$NetBSD$");
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 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");
119 ctx->ref++;
120 if (ctx->ref == 0)
121 _hx509_abort("revoke ctx refcount == 0");
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 OSCP signer is "
223 "doesn't have CA as signer certificate");
224 goto out;
227 ret = _hx509_verify_signature_bitstring(context,
229 &s->signatureAlgorithm,
230 &s->tbsCertificate._save,
231 &s->signatureValue);
232 if (ret) {
233 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
234 "OSCP signer signature invalid");
235 goto out;
238 ret = hx509_cert_check_eku(context, signer,
239 oid_id_pkix_kp_OCSPSigning(), 0);
240 if (ret)
241 goto out;
244 ret = _hx509_verify_signature_bitstring(context,
245 _hx509_get_cert(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 "OSCP 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 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 = _hx509_map_file(ocsp->path, &data, &length, &sb);
338 if (ret)
339 return ret;
341 ret = parse_ocsp_basic(data, length, &basic);
342 _hx509_unmap_file(data, length);
343 if (ret) {
344 hx509_set_error_string(context, 0, ret,
345 "Failed to parse OCSP response");
346 return ret;
349 if (basic.certs) {
350 int i;
352 ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
353 NULL, &certs);
354 if (ret) {
355 free_OCSPBasicOCSPResponse(&basic);
356 return ret;
359 for (i = 0; i < basic.certs->len; i++) {
360 hx509_cert c;
362 ret = hx509_cert_init(context, &basic.certs->val[i], &c);
363 if (ret)
364 continue;
366 ret = hx509_certs_add(context, certs, c);
367 hx509_cert_free(c);
368 if (ret)
369 continue;
373 ocsp->last_modfied = sb.st_mtime;
375 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
376 hx509_certs_free(&ocsp->certs);
377 hx509_cert_free(ocsp->signer);
379 ocsp->ocsp = basic;
380 ocsp->certs = certs;
381 ocsp->signer = NULL;
383 return 0;
387 * Add a OCSP file to the revokation context.
389 * @param context hx509 context
390 * @param ctx hx509 revokation context
391 * @param path path to file that is going to be added to the context.
393 * @return An hx509 error code, see hx509_get_error_string().
395 * @ingroup hx509_revoke
399 hx509_revoke_add_ocsp(hx509_context context,
400 hx509_revoke_ctx ctx,
401 const char *path)
403 void *data;
404 int ret;
405 size_t i;
407 if (strncmp(path, "FILE:", 5) != 0) {
408 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
409 "unsupport type in %s", path);
410 return HX509_UNSUPPORTED_OPERATION;
413 path += 5;
415 for (i = 0; i < ctx->ocsps.len; i++) {
416 if (strcmp(ctx->ocsps.val[0].path, path) == 0)
417 return 0;
420 data = realloc(ctx->ocsps.val,
421 (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
422 if (data == NULL) {
423 hx509_clear_error_string(context);
424 return ENOMEM;
427 ctx->ocsps.val = data;
429 memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
430 sizeof(ctx->ocsps.val[0]));
432 ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
433 if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
434 hx509_clear_error_string(context);
435 return ENOMEM;
438 ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
439 if (ret) {
440 free(ctx->ocsps.val[ctx->ocsps.len].path);
441 return ret;
443 ctx->ocsps.len++;
445 return ret;
452 static int
453 verify_crl(hx509_context context,
454 hx509_revoke_ctx ctx,
455 CRLCertificateList *crl,
456 time_t time_now,
457 hx509_certs certs,
458 hx509_cert parent)
460 hx509_cert signer;
461 hx509_query q;
462 time_t t;
463 int ret;
465 t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
466 if (t > time_now) {
467 hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
468 "CRL used before time");
469 return HX509_CRL_USED_BEFORE_TIME;
472 if (crl->tbsCertList.nextUpdate == NULL) {
473 hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
474 "CRL missing nextUpdate");
475 return HX509_CRL_INVALID_FORMAT;
478 t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
479 if (t < time_now) {
480 hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
481 "CRL used after time");
482 return HX509_CRL_USED_AFTER_TIME;
485 _hx509_query_clear(&q);
488 * If it's the signer have CRLSIGN bit set, use that as the signer
489 * cert for the certificate, otherwise, search for a certificate.
491 if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
492 signer = hx509_cert_ref(parent);
493 } else {
494 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
495 q.match |= HX509_QUERY_KU_CRLSIGN;
496 q.subject_name = &crl->tbsCertList.issuer;
498 ret = hx509_certs_find(context, certs, &q, &signer);
499 if (ret) {
500 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
501 "Failed to find certificate for CRL");
502 return ret;
506 ret = _hx509_verify_signature_bitstring(context,
507 _hx509_get_cert(signer),
508 &crl->signatureAlgorithm,
509 &crl->tbsCertList._save,
510 &crl->signatureValue);
511 if (ret) {
512 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
513 "CRL signature invalid");
514 goto out;
518 * If signer is not CA cert, need to check revoke status of this
519 * CRL signing cert too, this include all parent CRL signer cert
520 * up to the root *sigh*, assume root at least hve CERTSIGN flag
521 * set.
523 while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
524 hx509_cert crl_parent;
526 _hx509_query_clear(&q);
528 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
529 q.match |= HX509_QUERY_KU_CRLSIGN;
530 q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
532 ret = hx509_certs_find(context, certs, &q, &crl_parent);
533 if (ret) {
534 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
535 "Failed to find parent of CRL signer");
536 goto out;
539 ret = hx509_revoke_verify(context,
540 ctx,
541 certs,
542 time_now,
543 signer,
544 crl_parent);
545 hx509_cert_free(signer);
546 signer = crl_parent;
547 if (ret) {
548 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
549 "Failed to verify revoke "
550 "status of CRL signer");
551 goto out;
555 out:
556 hx509_cert_free(signer);
558 return ret;
561 static int
562 load_crl(const char *path, time_t *t, CRLCertificateList *crl)
564 size_t length, size;
565 struct stat sb;
566 void *data;
567 int ret;
569 memset(crl, 0, sizeof(*crl));
571 ret = _hx509_map_file(path, &data, &length, &sb);
572 if (ret)
573 return ret;
575 *t = sb.st_mtime;
577 ret = decode_CRLCertificateList(data, length, crl, &size);
578 _hx509_unmap_file(data, length);
579 if (ret)
580 return ret;
582 /* check signature is aligned */
583 if (crl->signatureValue.length & 7) {
584 free_CRLCertificateList(crl);
585 return HX509_CRYPTO_SIG_INVALID_FORMAT;
587 return 0;
591 * Add a CRL file to the revokation context.
593 * @param context hx509 context
594 * @param ctx hx509 revokation context
595 * @param path path to file that is going to be added to the context.
597 * @return An hx509 error code, see hx509_get_error_string().
599 * @ingroup hx509_revoke
603 hx509_revoke_add_crl(hx509_context context,
604 hx509_revoke_ctx ctx,
605 const char *path)
607 void *data;
608 size_t i;
609 int ret;
611 if (strncmp(path, "FILE:", 5) != 0) {
612 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
613 "unsupport type in %s", path);
614 return HX509_UNSUPPORTED_OPERATION;
618 path += 5;
620 for (i = 0; i < ctx->crls.len; i++) {
621 if (strcmp(ctx->crls.val[0].path, path) == 0)
622 return 0;
625 data = realloc(ctx->crls.val,
626 (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
627 if (data == NULL) {
628 hx509_clear_error_string(context);
629 return ENOMEM;
631 ctx->crls.val = data;
633 memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
635 ctx->crls.val[ctx->crls.len].path = strdup(path);
636 if (ctx->crls.val[ctx->crls.len].path == NULL) {
637 hx509_clear_error_string(context);
638 return ENOMEM;
641 ret = load_crl(path,
642 &ctx->crls.val[ctx->crls.len].last_modfied,
643 &ctx->crls.val[ctx->crls.len].crl);
644 if (ret) {
645 free(ctx->crls.val[ctx->crls.len].path);
646 return ret;
649 ctx->crls.len++;
651 return ret;
655 * Check that a certificate is not expired according to a revokation
656 * context. Also need the parent certificte to the check OCSP
657 * parent identifier.
659 * @param context hx509 context
660 * @param ctx hx509 revokation context
661 * @param certs
662 * @param now
663 * @param cert
664 * @param parent_cert
666 * @return An hx509 error code, see hx509_get_error_string().
668 * @ingroup hx509_revoke
673 hx509_revoke_verify(hx509_context context,
674 hx509_revoke_ctx ctx,
675 hx509_certs certs,
676 time_t now,
677 hx509_cert cert,
678 hx509_cert parent_cert)
680 const Certificate *c = _hx509_get_cert(cert);
681 const Certificate *p = _hx509_get_cert(parent_cert);
682 unsigned long i, j, k;
683 int ret;
685 hx509_clear_error_string(context);
687 for (i = 0; i < ctx->ocsps.len; i++) {
688 struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
689 struct stat sb;
691 /* check this ocsp apply to this cert */
693 /* check if there is a newer version of the file */
694 ret = stat(ocsp->path, &sb);
695 if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
696 ret = load_ocsp(context, ocsp);
697 if (ret)
698 continue;
701 /* verify signature in ocsp if not already done */
702 if (ocsp->signer == NULL) {
703 ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
704 if (ret)
705 continue;
708 for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
709 heim_octet_string os;
711 ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
712 &c->tbsCertificate.serialNumber);
713 if (ret != 0)
714 continue;
716 /* verify issuer hashes hash */
717 ret = _hx509_verify_signature(context,
718 NULL,
719 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
720 &c->tbsCertificate.issuer._save,
721 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
722 if (ret != 0)
723 continue;
725 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
726 os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
728 ret = _hx509_verify_signature(context,
729 NULL,
730 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
731 &os,
732 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
733 if (ret != 0)
734 continue;
736 switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
737 case choice_OCSPCertStatus_good:
738 break;
739 case choice_OCSPCertStatus_revoked:
740 hx509_set_error_string(context, 0,
741 HX509_CERT_REVOKED,
742 "Certificate revoked by issuer in OCSP");
743 return HX509_CERT_REVOKED;
744 case choice_OCSPCertStatus_unknown:
745 continue;
748 /* don't allow the update to be in the future */
749 if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
750 now + context->ocsp_time_diff)
751 continue;
753 /* don't allow the next update to be in the past */
754 if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
755 if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
756 continue;
757 } else
758 /* Should force a refetch, but can we ? */;
760 return 0;
764 for (i = 0; i < ctx->crls.len; i++) {
765 struct revoke_crl *crl = &ctx->crls.val[i];
766 struct stat sb;
768 /* check if cert.issuer == crls.val[i].crl.issuer */
769 ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
770 &crl->crl.tbsCertList.issuer);
771 if (ret)
772 continue;
774 ret = stat(crl->path, &sb);
775 if (ret == 0 && crl->last_modfied != sb.st_mtime) {
776 CRLCertificateList cl;
778 ret = load_crl(crl->path, &crl->last_modfied, &cl);
779 if (ret == 0) {
780 free_CRLCertificateList(&crl->crl);
781 crl->crl = cl;
782 crl->verified = 0;
783 crl->failed_verify = 0;
786 if (crl->failed_verify)
787 continue;
789 /* verify signature in crl if not already done */
790 if (crl->verified == 0) {
791 ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
792 if (ret) {
793 crl->failed_verify = 1;
794 continue;
796 crl->verified = 1;
799 if (crl->crl.tbsCertList.crlExtensions) {
800 for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
801 if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
802 hx509_set_error_string(context, 0,
803 HX509_CRL_UNKNOWN_EXTENSION,
804 "Unknown CRL extension");
805 return HX509_CRL_UNKNOWN_EXTENSION;
810 if (crl->crl.tbsCertList.revokedCertificates == NULL)
811 return 0;
813 /* check if cert is in crl */
814 for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
815 time_t t;
817 ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
818 &c->tbsCertificate.serialNumber);
819 if (ret != 0)
820 continue;
822 t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
823 if (t > now)
824 continue;
826 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
827 for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
828 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
829 return HX509_CRL_UNKNOWN_EXTENSION;
831 hx509_set_error_string(context, 0,
832 HX509_CERT_REVOKED,
833 "Certificate revoked by issuer in CRL");
834 return HX509_CERT_REVOKED;
837 return 0;
841 if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
842 return 0;
843 hx509_set_error_string(context, HX509_ERROR_APPEND,
844 HX509_REVOKE_STATUS_MISSING,
845 "No revoke status found for "
846 "certificates");
847 return HX509_REVOKE_STATUS_MISSING;
850 struct ocsp_add_ctx {
851 OCSPTBSRequest *req;
852 hx509_certs certs;
853 const AlgorithmIdentifier *digest;
854 hx509_cert parent;
857 static int
858 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
860 struct ocsp_add_ctx *ctx = ptr;
861 OCSPInnerRequest *one;
862 hx509_cert parent = NULL;
863 Certificate *p, *c = _hx509_get_cert(cert);
864 heim_octet_string os;
865 int ret;
866 hx509_query q;
867 void *d;
869 d = realloc(ctx->req->requestList.val,
870 sizeof(ctx->req->requestList.val[0]) *
871 (ctx->req->requestList.len + 1));
872 if (d == NULL)
873 return ENOMEM;
874 ctx->req->requestList.val = d;
876 one = &ctx->req->requestList.val[ctx->req->requestList.len];
877 memset(one, 0, sizeof(*one));
879 _hx509_query_clear(&q);
881 q.match |= HX509_QUERY_FIND_ISSUER_CERT;
882 q.subject = c;
884 ret = hx509_certs_find(context, ctx->certs, &q, &parent);
885 if (ret)
886 goto out;
888 if (ctx->parent) {
889 if (hx509_cert_cmp(ctx->parent, parent) != 0) {
890 ret = HX509_REVOKE_NOT_SAME_PARENT;
891 hx509_set_error_string(context, 0, ret,
892 "Not same parent certifate as "
893 "last certificate in request");
894 goto out;
896 } else
897 ctx->parent = hx509_cert_ref(parent);
899 p = _hx509_get_cert(parent);
901 ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
902 if (ret)
903 goto out;
905 ret = _hx509_create_signature(context,
906 NULL,
907 &one->reqCert.hashAlgorithm,
908 &c->tbsCertificate.issuer._save,
909 NULL,
910 &one->reqCert.issuerNameHash);
911 if (ret)
912 goto out;
914 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
915 os.length =
916 p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
918 ret = _hx509_create_signature(context,
919 NULL,
920 &one->reqCert.hashAlgorithm,
921 &os,
922 NULL,
923 &one->reqCert.issuerKeyHash);
924 if (ret)
925 goto out;
927 ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
928 &one->reqCert.serialNumber);
929 if (ret)
930 goto out;
932 ctx->req->requestList.len++;
933 out:
934 hx509_cert_free(parent);
935 if (ret) {
936 free_OCSPInnerRequest(one);
937 memset(one, 0, sizeof(*one));
940 return ret;
944 * Create an OCSP request for a set of certificates.
946 * @param context a hx509 context
947 * @param reqcerts list of certificates to request ocsp data for
948 * @param pool certificate pool to use when signing
949 * @param signer certificate to use to sign the request
950 * @param digest the signing algorithm in the request, if NULL use the
951 * default signature algorithm,
952 * @param request the encoded request, free with free_heim_octet_string().
953 * @param nonce nonce in the request, free with free_heim_octet_string().
955 * @return An hx509 error code, see hx509_get_error_string().
957 * @ingroup hx509_revoke
961 hx509_ocsp_request(hx509_context context,
962 hx509_certs reqcerts,
963 hx509_certs pool,
964 hx509_cert signer,
965 const AlgorithmIdentifier *digest,
966 heim_octet_string *request,
967 heim_octet_string *nonce)
969 OCSPRequest req;
970 size_t size;
971 int ret;
972 struct ocsp_add_ctx ctx;
973 Extensions *es;
975 memset(&req, 0, sizeof(req));
977 if (digest == NULL)
978 digest = _hx509_crypto_default_digest_alg;
980 ctx.req = &req.tbsRequest;
981 ctx.certs = pool;
982 ctx.digest = digest;
983 ctx.parent = NULL;
985 ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx);
986 hx509_cert_free(ctx.parent);
987 if (ret)
988 goto out;
990 if (nonce) {
991 req.tbsRequest.requestExtensions =
992 calloc(1, sizeof(*req.tbsRequest.requestExtensions));
993 if (req.tbsRequest.requestExtensions == NULL) {
994 ret = ENOMEM;
995 goto out;
998 es = req.tbsRequest.requestExtensions;
1000 es->val = calloc(es->len, sizeof(es->val[0]));
1001 if (es->val == NULL) {
1002 ret = ENOMEM;
1003 goto out;
1005 es->len = 1;
1007 ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID);
1008 if (ret) {
1009 free_OCSPRequest(&req);
1010 return ret;
1013 es->val[0].extnValue.data = malloc(10);
1014 if (es->val[0].extnValue.data == NULL) {
1015 ret = ENOMEM;
1016 goto out;
1018 es->val[0].extnValue.length = 10;
1020 ret = RAND_bytes(es->val[0].extnValue.data,
1021 es->val[0].extnValue.length);
1022 if (ret != 1) {
1023 ret = HX509_CRYPTO_INTERNAL_ERROR;
1024 goto out;
1026 ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1027 if (ret) {
1028 ret = ENOMEM;
1029 goto out;
1033 ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1034 &req, &size, ret);
1035 free_OCSPRequest(&req);
1036 if (ret)
1037 goto out;
1038 if (size != request->length)
1039 _hx509_abort("internal ASN.1 encoder error");
1041 return 0;
1043 out:
1044 free_OCSPRequest(&req);
1045 return ret;
1048 static char *
1049 printable_time(time_t t)
1051 static char s[128];
1052 strlcpy(s, ctime(&t)+ 4, sizeof(s));
1053 s[20] = 0;
1054 return s;
1058 * Print the OCSP reply stored in a file.
1060 * @param context a hx509 context
1061 * @param path path to a file with a OCSP reply
1062 * @param out the out FILE descriptor to print the reply on
1064 * @return An hx509 error code, see hx509_get_error_string().
1066 * @ingroup hx509_revoke
1070 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1072 struct revoke_ocsp ocsp;
1073 int ret, i;
1075 if (out == NULL)
1076 out = stdout;
1078 memset(&ocsp, 0, sizeof(ocsp));
1080 ocsp.path = strdup(path);
1081 if (ocsp.path == NULL)
1082 return ENOMEM;
1084 ret = load_ocsp(context, &ocsp);
1085 if (ret) {
1086 free_ocsp(&ocsp);
1087 return ret;
1090 fprintf(out, "signer: ");
1092 switch(ocsp.ocsp.tbsResponseData.responderID.element) {
1093 case choice_OCSPResponderID_byName: {
1094 hx509_name n;
1095 char *s;
1096 _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
1097 hx509_name_to_string(n, &s);
1098 hx509_name_free(&n);
1099 fprintf(out, " byName: %s\n", s);
1100 free(s);
1101 break;
1103 case choice_OCSPResponderID_byKey: {
1104 char *s;
1105 hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
1106 ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
1107 &s);
1108 fprintf(out, " byKey: %s\n", s);
1109 free(s);
1110 break;
1112 default:
1113 _hx509_abort("choice_OCSPResponderID unknown");
1114 break;
1117 fprintf(out, "producedAt: %s\n",
1118 printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1120 fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1122 for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1123 const char *status;
1124 switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1125 case choice_OCSPCertStatus_good:
1126 status = "good";
1127 break;
1128 case choice_OCSPCertStatus_revoked:
1129 status = "revoked";
1130 break;
1131 case choice_OCSPCertStatus_unknown:
1132 status = "unknown";
1133 break;
1134 default:
1135 status = "element unknown";
1138 fprintf(out, "\t%d. status: %s\n", i, status);
1140 fprintf(out, "\tthisUpdate: %s\n",
1141 printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1142 if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
1143 fprintf(out, "\tproducedAt: %s\n",
1144 printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1148 fprintf(out, "appended certs:\n");
1149 if (ocsp.certs)
1150 ret = hx509_certs_iter(context, ocsp.certs, hx509_ci_print_names, out);
1152 free_ocsp(&ocsp);
1153 return ret;
1157 * Verify that the certificate is part of the OCSP reply and it's not
1158 * expired. Doesn't verify signature the OCSP reply or it's done by a
1159 * authorized sender, that is assumed to be already done.
1161 * @param context a hx509 context
1162 * @param now the time right now, if 0, use the current time.
1163 * @param cert the certificate to verify
1164 * @param flags flags control the behavior
1165 * @param data pointer to the encode ocsp reply
1166 * @param length the length of the encode ocsp reply
1167 * @param expiration return the time the OCSP will expire and need to
1168 * be rechecked.
1170 * @return An hx509 error code, see hx509_get_error_string().
1172 * @ingroup hx509_verify
1176 hx509_ocsp_verify(hx509_context context,
1177 time_t now,
1178 hx509_cert cert,
1179 int flags,
1180 const void *data, size_t length,
1181 time_t *expiration)
1183 const Certificate *c = _hx509_get_cert(cert);
1184 OCSPBasicOCSPResponse basic;
1185 int ret, i;
1187 if (now == 0)
1188 now = time(NULL);
1190 *expiration = 0;
1192 ret = parse_ocsp_basic(data, length, &basic);
1193 if (ret) {
1194 hx509_set_error_string(context, 0, ret,
1195 "Failed to parse OCSP response");
1196 return ret;
1199 for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1201 ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1202 &c->tbsCertificate.serialNumber);
1203 if (ret != 0)
1204 continue;
1206 /* verify issuer hashes hash */
1207 ret = _hx509_verify_signature(context,
1208 NULL,
1209 &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1210 &c->tbsCertificate.issuer._save,
1211 &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1212 if (ret != 0)
1213 continue;
1215 switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1216 case choice_OCSPCertStatus_good:
1217 break;
1218 case choice_OCSPCertStatus_revoked:
1219 case choice_OCSPCertStatus_unknown:
1220 continue;
1223 /* don't allow the update to be in the future */
1224 if (basic.tbsResponseData.responses.val[i].thisUpdate >
1225 now + context->ocsp_time_diff)
1226 continue;
1228 /* don't allow the next update to be in the past */
1229 if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1230 if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1231 continue;
1232 *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1233 } else
1234 *expiration = now;
1236 free_OCSPBasicOCSPResponse(&basic);
1237 return 0;
1240 free_OCSPBasicOCSPResponse(&basic);
1243 hx509_name name;
1244 char *subject;
1246 ret = hx509_cert_get_subject(cert, &name);
1247 if (ret) {
1248 hx509_clear_error_string(context);
1249 goto out;
1251 ret = hx509_name_to_string(name, &subject);
1252 hx509_name_free(&name);
1253 if (ret) {
1254 hx509_clear_error_string(context);
1255 goto out;
1257 hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1258 "Certificate %s not in OCSP response "
1259 "or not good",
1260 subject);
1261 free(subject);
1263 out:
1264 return HX509_CERT_NOT_IN_OCSP;
1267 struct hx509_crl {
1268 hx509_certs revoked;
1269 time_t expire;
1273 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1275 * @param context a hx509 context.
1276 * @param crl return pointer to a newly allocated CRL context.
1278 * @return An hx509 error code, see hx509_get_error_string().
1280 * @ingroup hx509_verify
1284 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1286 int ret;
1288 *crl = calloc(1, sizeof(**crl));
1289 if (*crl == NULL) {
1290 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1291 return ENOMEM;
1294 ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1295 if (ret) {
1296 free(*crl);
1297 *crl = NULL;
1298 return ret;
1300 (*crl)->expire = 0;
1301 return ret;
1305 * Add revoked certificate to an CRL context.
1307 * @param context a hx509 context.
1308 * @param crl the CRL to add the revoked certificate to.
1309 * @param certs keyset of certificate to revoke.
1311 * @return An hx509 error code, see hx509_get_error_string().
1313 * @ingroup hx509_verify
1317 hx509_crl_add_revoked_certs(hx509_context context,
1318 hx509_crl crl,
1319 hx509_certs certs)
1321 return hx509_certs_merge(context, crl->revoked, certs);
1325 * Set the lifetime of a CRL context.
1327 * @param context a hx509 context.
1328 * @param crl a CRL context
1329 * @param delta delta time the certificate is valid, library adds the
1330 * current time to this.
1332 * @return An hx509 error code, see hx509_get_error_string().
1334 * @ingroup hx509_verify
1338 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1340 crl->expire = time(NULL) + delta;
1341 return 0;
1345 * Free a CRL context.
1347 * @param context a hx509 context.
1348 * @param crl a CRL context to free.
1350 * @ingroup hx509_verify
1353 void
1354 hx509_crl_free(hx509_context context, hx509_crl *crl)
1356 if (*crl == NULL)
1357 return;
1358 hx509_certs_free(&(*crl)->revoked);
1359 memset(*crl, 0, sizeof(**crl));
1360 free(*crl);
1361 *crl = NULL;
1364 static int
1365 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1367 TBSCRLCertList *c = ctx;
1368 unsigned int num;
1369 void *ptr;
1370 int ret;
1372 num = c->revokedCertificates->len;
1373 ptr = realloc(c->revokedCertificates->val,
1374 (num + 1) * sizeof(c->revokedCertificates->val[0]));
1375 if (ptr == NULL) {
1376 hx509_clear_error_string(context);
1377 return ENOMEM;
1379 c->revokedCertificates->val = ptr;
1381 ret = hx509_cert_get_serialnumber(cert,
1382 &c->revokedCertificates->val[num].userCertificate);
1383 if (ret) {
1384 hx509_clear_error_string(context);
1385 return ret;
1387 c->revokedCertificates->val[num].revocationDate.element =
1388 choice_Time_generalTime;
1389 c->revokedCertificates->val[num].revocationDate.u.generalTime =
1390 time(NULL) - 3600 * 24;
1391 c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1393 c->revokedCertificates->len++;
1395 return 0;
1399 * Sign a CRL and return an encode certificate.
1401 * @param context a hx509 context.
1402 * @param signer certificate to sign the CRL with
1403 * @param crl the CRL to sign
1404 * @param os return the signed and encoded CRL, free with
1405 * free_heim_octet_string()
1407 * @return An hx509 error code, see hx509_get_error_string().
1409 * @ingroup hx509_verify
1413 hx509_crl_sign(hx509_context context,
1414 hx509_cert signer,
1415 hx509_crl crl,
1416 heim_octet_string *os)
1418 const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1419 CRLCertificateList c;
1420 size_t size;
1421 int ret;
1422 hx509_private_key signerkey;
1424 memset(&c, 0, sizeof(c));
1426 signerkey = _hx509_cert_private_key(signer);
1427 if (signerkey == NULL) {
1428 ret = HX509_PRIVATE_KEY_MISSING;
1429 hx509_set_error_string(context, 0, ret,
1430 "Private key missing for CRL signing");
1431 return ret;
1434 c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1435 if (c.tbsCertList.version == NULL) {
1436 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1437 return ENOMEM;
1440 *c.tbsCertList.version = 1;
1442 ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1443 if (ret) {
1444 hx509_clear_error_string(context);
1445 goto out;
1448 ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1449 &c.tbsCertList.issuer);
1450 if (ret) {
1451 hx509_clear_error_string(context);
1452 goto out;
1455 c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1456 c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1458 c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1459 if (c.tbsCertList.nextUpdate == NULL) {
1460 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1461 ret = ENOMEM;
1462 goto out;
1466 time_t next = crl->expire;
1467 if (next == 0)
1468 next = time(NULL) + 24 * 3600 * 365;
1470 c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1471 c.tbsCertList.nextUpdate->u.generalTime = next;
1474 c.tbsCertList.revokedCertificates =
1475 calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1476 if (c.tbsCertList.revokedCertificates == NULL) {
1477 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1478 ret = ENOMEM;
1479 goto out;
1481 c.tbsCertList.crlExtensions = NULL;
1483 ret = hx509_certs_iter(context, crl->revoked, add_revoked, &c.tbsCertList);
1484 if (ret)
1485 goto out;
1487 /* if not revoked certs, remove OPTIONAL entry */
1488 if (c.tbsCertList.revokedCertificates->len == 0) {
1489 free(c.tbsCertList.revokedCertificates);
1490 c.tbsCertList.revokedCertificates = NULL;
1493 ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1494 &c.tbsCertList, &size, ret);
1495 if (ret) {
1496 hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1497 goto out;
1499 if (size != os->length)
1500 _hx509_abort("internal ASN.1 encoder error");
1503 ret = _hx509_create_signature_bitstring(context,
1504 signerkey,
1505 sigalg,
1507 &c.signatureAlgorithm,
1508 &c.signatureValue);
1509 free(os->data);
1511 ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1512 &c, &size, ret);
1513 free_CRLCertificateList(&c);
1514 if (ret) {
1515 hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1516 goto out;
1518 if (size != os->length)
1519 _hx509_abort("internal ASN.1 encoder error");
1521 return 0;
1523 out:
1524 free_CRLCertificateList(&c);
1525 return ret;