s4:torture: Adapt KDC canon test to Heimdal upstream changes
[Samba.git] / source4 / heimdal / lib / hx509 / revoke.c
blobc2f2e00cc297793e1084efed8a9353de8eec6e88
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 certificates 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"
54 struct revoke_crl {
55 char *path;
56 time_t last_modfied;
57 CRLCertificateList crl;
58 int verified;
59 int failed_verify;
62 struct revoke_ocsp {
63 char *path;
64 time_t last_modfied;
65 OCSPBasicOCSPResponse ocsp;
66 hx509_certs certs;
67 hx509_cert signer;
71 struct hx509_revoke_ctx_data {
72 unsigned int ref;
73 struct {
74 struct revoke_crl *val;
75 size_t len;
76 } crls;
77 struct {
78 struct revoke_ocsp *val;
79 size_t len;
80 } ocsps;
83 /**
84 * Allocate a revokation context. Free with hx509_revoke_free().
86 * @param context A hx509 context.
87 * @param ctx returns a newly allocated revokation context.
89 * @return An hx509 error code, see hx509_get_error_string().
91 * @ingroup hx509_revoke
94 HX509_LIB_FUNCTION int HX509_LIB_CALL
95 hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
97 *ctx = calloc(1, sizeof(**ctx));
98 if (*ctx == NULL)
99 return ENOMEM;
101 (*ctx)->ref = 1;
102 (*ctx)->crls.len = 0;
103 (*ctx)->crls.val = NULL;
104 (*ctx)->ocsps.len = 0;
105 (*ctx)->ocsps.val = NULL;
107 return 0;
110 HX509_LIB_FUNCTION hx509_revoke_ctx HX509_LIB_CALL
111 _hx509_revoke_ref(hx509_revoke_ctx ctx)
113 if (ctx == NULL)
114 return NULL;
115 if (ctx->ref == 0)
116 _hx509_abort("revoke ctx refcount == 0 on ref");
117 ctx->ref++;
118 if (ctx->ref == UINT_MAX)
119 _hx509_abort("revoke ctx refcount == UINT_MAX on ref");
120 return ctx;
123 static void
124 free_ocsp(struct revoke_ocsp *ocsp)
126 free(ocsp->path);
127 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
128 hx509_certs_free(&ocsp->certs);
129 hx509_cert_free(ocsp->signer);
133 * Free a hx509 revokation context.
135 * @param ctx context to be freed
137 * @ingroup hx509_revoke
140 HX509_LIB_FUNCTION void HX509_LIB_CALL
141 hx509_revoke_free(hx509_revoke_ctx *ctx)
143 size_t i ;
145 if (ctx == NULL || *ctx == NULL)
146 return;
148 if ((*ctx)->ref == 0)
149 _hx509_abort("revoke ctx refcount == 0 on free");
150 if (--(*ctx)->ref > 0)
151 return;
153 for (i = 0; i < (*ctx)->crls.len; i++) {
154 free((*ctx)->crls.val[i].path);
155 free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
158 for (i = 0; i < (*ctx)->ocsps.len; i++)
159 free_ocsp(&(*ctx)->ocsps.val[i]);
160 free((*ctx)->ocsps.val);
162 free((*ctx)->crls.val);
164 memset(*ctx, 0, sizeof(**ctx));
165 free(*ctx);
166 *ctx = NULL;
169 static int
170 verify_ocsp(hx509_context context,
171 struct revoke_ocsp *ocsp,
172 time_t time_now,
173 hx509_certs certs,
174 hx509_cert parent)
176 hx509_cert signer = NULL;
177 hx509_query q;
178 int ret;
180 _hx509_query_clear(&q);
183 * Need to match on issuer too in case there are two CA that have
184 * issued the same name to a certificate. One example of this is
185 * the www.openvalidation.org test's ocsp validator.
188 q.match = HX509_QUERY_MATCH_ISSUER_NAME;
189 q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
191 switch(ocsp->ocsp.tbsResponseData.responderID.element) {
192 case choice_OCSPResponderID_byName:
193 q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
194 q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
195 break;
196 case choice_OCSPResponderID_byKey:
197 q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
198 q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
199 break;
202 ret = hx509_certs_find(context, certs, &q, &signer);
203 if (ret && ocsp->certs)
204 ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
205 if (ret)
206 goto out;
209 * If signer certificate isn't the CA certificate, lets check the
210 * it is the CA that signed the signer certificate and the OCSP EKU
211 * is set.
213 if (hx509_cert_cmp(signer, parent) != 0) {
214 Certificate *p = _hx509_get_cert(parent);
215 Certificate *s = _hx509_get_cert(signer);
217 ret = _hx509_cert_is_parent_cmp(s, p, 0);
218 if (ret != 0) {
219 ret = HX509_PARENT_NOT_CA;
220 hx509_set_error_string(context, 0, ret, "Revoke OCSP signer "
221 "doesn't have CA as signer certificate");
222 goto out;
225 ret = _hx509_verify_signature_bitstring(context,
226 parent,
227 &s->signatureAlgorithm,
228 &s->tbsCertificate._save,
229 &s->signatureValue);
230 if (ret) {
231 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
232 "OCSP signer signature invalid");
233 goto out;
236 ret = hx509_cert_check_eku(context, signer,
237 &asn1_oid_id_pkix_kp_OCSPSigning, 0);
238 if (ret)
239 goto out;
242 ret = _hx509_verify_signature_bitstring(context,
243 signer,
244 &ocsp->ocsp.signatureAlgorithm,
245 &ocsp->ocsp.tbsResponseData._save,
246 &ocsp->ocsp.signature);
247 if (ret) {
248 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
249 "OCSP signature invalid");
250 goto out;
253 ocsp->signer = signer;
254 signer = NULL;
255 out:
256 if (signer)
257 hx509_cert_free(signer);
259 return ret;
266 static int
267 parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
269 OCSPResponse resp;
270 size_t size;
271 int ret;
273 memset(basic, 0, sizeof(*basic));
275 ret = decode_OCSPResponse(data, length, &resp, &size);
276 if (ret)
277 return ret;
278 if (length != size) {
279 free_OCSPResponse(&resp);
280 return ASN1_EXTRA_DATA;
283 switch (resp.responseStatus) {
284 case successful:
285 break;
286 default:
287 free_OCSPResponse(&resp);
288 return HX509_REVOKE_WRONG_DATA;
291 if (resp.responseBytes == NULL) {
292 free_OCSPResponse(&resp);
293 return EINVAL;
296 ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
297 &asn1_oid_id_pkix_ocsp_basic);
298 if (ret != 0) {
299 free_OCSPResponse(&resp);
300 return HX509_REVOKE_WRONG_DATA;
303 ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
304 resp.responseBytes->response.length,
305 basic,
306 &size);
307 if (ret) {
308 free_OCSPResponse(&resp);
309 return ret;
311 if (size != resp.responseBytes->response.length) {
312 free_OCSPResponse(&resp);
313 free_OCSPBasicOCSPResponse(basic);
314 return ASN1_EXTRA_DATA;
316 free_OCSPResponse(&resp);
318 return 0;
325 static int
326 load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
328 OCSPBasicOCSPResponse basic;
329 hx509_certs certs = NULL;
330 size_t length;
331 struct stat sb;
332 void *data;
333 int ret;
335 ret = rk_undumpdata(ocsp->path, &data, &length);
336 if (ret)
337 return ret;
339 ret = stat(ocsp->path, &sb);
340 if (ret) {
341 rk_xfree(data);
342 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 c = hx509_cert_init(context, &basic.certs->val[i], NULL);
367 if (c == NULL)
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
402 HX509_LIB_FUNCTION int HX509_LIB_CALL
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 revocation "
554 "status of CRL signer");
555 goto out;
559 out:
560 hx509_cert_free(signer);
562 return ret;
565 static int
566 crl_parser(hx509_context context, const char *type,
567 const hx509_pem_header *header,
568 const void *data, size_t len, void *ctx)
570 CRLCertificateList *crl = (CRLCertificateList *)ctx;
571 size_t size;
572 int ret;
574 if (strcasecmp("X509 CRL", type) != 0)
575 return HX509_CRYPTO_SIG_INVALID_FORMAT;
577 ret = decode_CRLCertificateList(data, len, crl, &size);
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;
587 return 0;
590 static int
591 load_crl(hx509_context context, const char *path, time_t *t, CRLCertificateList *crl)
593 struct stat sb;
594 size_t length;
595 void *data;
596 FILE *f;
597 int ret;
599 memset(crl, 0, sizeof(*crl));
601 ret = stat(path, &sb);
602 if (ret)
603 return errno;
605 *t = sb.st_mtime;
607 if ((f = fopen(path, "r")) == NULL)
608 return errno;
610 rk_cloexec_file(f);
612 ret = hx509_pem_read(context, f, crl_parser, crl);
613 fclose(f);
615 if (ret == HX509_PARSING_KEY_FAILED) {
617 ret = rk_undumpdata(path, &data, &length);
618 if (ret)
619 return ret;
621 ret = crl_parser(context, "X509 CRL", NULL, data, length, crl);
622 rk_xfree(data);
624 return ret;
628 * Add a CRL file to the revokation context.
630 * @param context hx509 context
631 * @param ctx hx509 revokation context
632 * @param path path to file that is going to be added to the context.
634 * @return An hx509 error code, see hx509_get_error_string().
636 * @ingroup hx509_revoke
639 HX509_LIB_FUNCTION int HX509_LIB_CALL
640 hx509_revoke_add_crl(hx509_context context,
641 hx509_revoke_ctx ctx,
642 const char *path)
644 void *data;
645 size_t i;
646 int ret;
648 if (strncmp(path, "FILE:", 5) != 0) {
649 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
650 "unsupported type in %s", path);
651 return HX509_UNSUPPORTED_OPERATION;
655 path += 5;
657 for (i = 0; i < ctx->crls.len; i++) {
658 if (strcmp(ctx->crls.val[i].path, path) == 0)
659 return 0;
662 data = realloc(ctx->crls.val,
663 (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
664 if (data == NULL) {
665 hx509_clear_error_string(context);
666 return ENOMEM;
668 ctx->crls.val = data;
670 memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
672 ctx->crls.val[ctx->crls.len].path = strdup(path);
673 if (ctx->crls.val[ctx->crls.len].path == NULL) {
674 hx509_clear_error_string(context);
675 return ENOMEM;
678 ret = load_crl(context,
679 path,
680 &ctx->crls.val[ctx->crls.len].last_modfied,
681 &ctx->crls.val[ctx->crls.len].crl);
682 if (ret) {
683 free(ctx->crls.val[ctx->crls.len].path);
684 return ret;
687 ctx->crls.len++;
689 return ret;
693 * Check that a certificate is not expired according to a revokation
694 * context. Also need the parent certificte to the check OCSP
695 * parent identifier.
697 * @param context hx509 context
698 * @param ctx hx509 revokation context
699 * @param certs
700 * @param now
701 * @param cert
702 * @param parent_cert
704 * @return An hx509 error code, see hx509_get_error_string().
706 * @ingroup hx509_revoke
709 HX509_LIB_FUNCTION int HX509_LIB_CALL
710 hx509_revoke_verify(hx509_context context,
711 hx509_revoke_ctx ctx,
712 hx509_certs certs,
713 time_t now,
714 hx509_cert cert,
715 hx509_cert parent_cert)
717 const Certificate *c = _hx509_get_cert(cert);
718 const Certificate *p = _hx509_get_cert(parent_cert);
719 unsigned long i, j, k;
720 int ret;
722 hx509_clear_error_string(context);
724 for (i = 0; i < ctx->ocsps.len; i++) {
725 struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
726 struct stat sb;
728 /* check this ocsp apply to this cert */
730 /* check if there is a newer version of the file */
731 ret = stat(ocsp->path, &sb);
732 if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
733 ret = load_ocsp(context, ocsp);
734 if (ret)
735 continue;
738 /* verify signature in ocsp if not already done */
739 if (ocsp->signer == NULL) {
740 ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
741 if (ret)
742 continue;
745 for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
746 heim_octet_string os;
748 ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
749 &c->tbsCertificate.serialNumber);
750 if (ret != 0)
751 continue;
753 /* verify issuer hashes hash */
754 ret = _hx509_verify_signature(context,
755 NULL,
756 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
757 &c->tbsCertificate.issuer._save,
758 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
759 if (ret != 0)
760 continue;
762 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
763 os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
765 ret = _hx509_verify_signature(context,
766 NULL,
767 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
768 &os,
769 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
770 if (ret != 0)
771 continue;
773 switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
774 case choice_OCSPCertStatus_good:
775 break;
776 case choice_OCSPCertStatus_revoked:
777 hx509_set_error_string(context, 0,
778 HX509_CERT_REVOKED,
779 "Certificate revoked by issuer in OCSP");
780 return HX509_CERT_REVOKED;
781 case choice_OCSPCertStatus_unknown:
782 continue;
785 /* don't allow the update to be in the future */
786 if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
787 now + context->ocsp_time_diff)
788 continue;
790 /* don't allow the next update to be in the past */
791 if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
792 if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
793 continue;
794 } /* else should force a refetch, but can we ? */
796 return 0;
800 for (i = 0; i < ctx->crls.len; i++) {
801 struct revoke_crl *crl = &ctx->crls.val[i];
802 struct stat sb;
803 int diff;
805 /* check if cert.issuer == crls.val[i].crl.issuer */
806 ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
807 &crl->crl.tbsCertList.issuer, &diff);
808 if (ret || diff)
809 continue;
811 ret = stat(crl->path, &sb);
812 if (ret == 0 && crl->last_modfied != sb.st_mtime) {
813 CRLCertificateList cl;
815 ret = load_crl(context, crl->path, &crl->last_modfied, &cl);
816 if (ret == 0) {
817 free_CRLCertificateList(&crl->crl);
818 crl->crl = cl;
819 crl->verified = 0;
820 crl->failed_verify = 0;
823 if (crl->failed_verify)
824 continue;
826 /* verify signature in crl if not already done */
827 if (crl->verified == 0) {
828 ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
829 if (ret) {
830 crl->failed_verify = 1;
831 continue;
833 crl->verified = 1;
836 if (crl->crl.tbsCertList.crlExtensions) {
837 for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
838 if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
839 hx509_set_error_string(context, 0,
840 HX509_CRL_UNKNOWN_EXTENSION,
841 "Unknown CRL extension");
842 return HX509_CRL_UNKNOWN_EXTENSION;
847 if (crl->crl.tbsCertList.revokedCertificates == NULL)
848 return 0;
850 /* check if cert is in crl */
851 for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
852 time_t t;
854 ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
855 &c->tbsCertificate.serialNumber);
856 if (ret != 0)
857 continue;
859 t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
860 if (t > now)
861 continue;
863 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
864 for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
865 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
866 return HX509_CRL_UNKNOWN_EXTENSION;
868 hx509_set_error_string(context, 0,
869 HX509_CERT_REVOKED,
870 "Certificate revoked by issuer in CRL");
871 return HX509_CERT_REVOKED;
874 return 0;
878 if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
879 return 0;
880 hx509_set_error_string(context, HX509_ERROR_APPEND,
881 HX509_REVOKE_STATUS_MISSING,
882 "No revocation status found for certificates");
883 return HX509_REVOKE_STATUS_MISSING;
886 struct ocsp_add_ctx {
887 OCSPTBSRequest *req;
888 hx509_certs certs;
889 const AlgorithmIdentifier *digest;
890 hx509_cert parent;
893 static int HX509_LIB_CALL
894 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
896 struct ocsp_add_ctx *ctx = ptr;
897 OCSPInnerRequest *one;
898 hx509_cert parent = NULL;
899 Certificate *p, *c = _hx509_get_cert(cert);
900 heim_octet_string os;
901 int ret;
902 hx509_query q;
903 void *d;
905 d = realloc(ctx->req->requestList.val,
906 sizeof(ctx->req->requestList.val[0]) *
907 (ctx->req->requestList.len + 1));
908 if (d == NULL)
909 return ENOMEM;
910 ctx->req->requestList.val = d;
912 one = &ctx->req->requestList.val[ctx->req->requestList.len];
913 memset(one, 0, sizeof(*one));
915 _hx509_query_clear(&q);
917 q.match |= HX509_QUERY_FIND_ISSUER_CERT;
918 q.subject = c;
920 ret = hx509_certs_find(context, ctx->certs, &q, &parent);
921 if (ret)
922 goto out;
924 if (ctx->parent) {
925 if (hx509_cert_cmp(ctx->parent, parent) != 0) {
926 ret = HX509_REVOKE_NOT_SAME_PARENT;
927 hx509_set_error_string(context, 0, ret,
928 "Not same parent certifate as "
929 "last certificate in request");
930 goto out;
932 } else
933 ctx->parent = hx509_cert_ref(parent);
935 p = _hx509_get_cert(parent);
937 ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
938 if (ret)
939 goto out;
941 ret = _hx509_create_signature(context,
942 NULL,
943 &one->reqCert.hashAlgorithm,
944 &c->tbsCertificate.issuer._save,
945 NULL,
946 &one->reqCert.issuerNameHash);
947 if (ret)
948 goto out;
950 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
951 os.length =
952 p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
954 ret = _hx509_create_signature(context,
955 NULL,
956 &one->reqCert.hashAlgorithm,
957 &os,
958 NULL,
959 &one->reqCert.issuerKeyHash);
960 if (ret)
961 goto out;
963 ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
964 &one->reqCert.serialNumber);
965 if (ret)
966 goto out;
968 ctx->req->requestList.len++;
969 out:
970 hx509_cert_free(parent);
971 if (ret) {
972 free_OCSPInnerRequest(one);
973 memset(one, 0, sizeof(*one));
976 return ret;
980 * Create an OCSP request for a set of certificates.
982 * @param context a hx509 context
983 * @param reqcerts list of certificates to request ocsp data for
984 * @param pool certificate pool to use when signing
985 * @param signer certificate to use to sign the request
986 * @param digest the signing algorithm in the request, if NULL use the
987 * default signature algorithm,
988 * @param request the encoded request, free with free_heim_octet_string().
989 * @param nonce nonce in the request, free with free_heim_octet_string().
991 * @return An hx509 error code, see hx509_get_error_string().
993 * @ingroup hx509_revoke
996 HX509_LIB_FUNCTION int HX509_LIB_CALL
997 hx509_ocsp_request(hx509_context context,
998 hx509_certs reqcerts,
999 hx509_certs pool,
1000 hx509_cert signer,
1001 const AlgorithmIdentifier *digest,
1002 heim_octet_string *request,
1003 heim_octet_string *nonce)
1005 OCSPRequest req;
1006 size_t size;
1007 int ret;
1008 struct ocsp_add_ctx ctx;
1009 Extensions *es;
1011 memset(&req, 0, sizeof(req));
1013 if (digest == NULL)
1014 digest = _hx509_crypto_default_digest_alg;
1016 ctx.req = &req.tbsRequest;
1017 ctx.certs = pool;
1018 ctx.digest = digest;
1019 ctx.parent = NULL;
1021 ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
1022 hx509_cert_free(ctx.parent);
1023 if (ret)
1024 goto out;
1026 if (nonce) {
1027 req.tbsRequest.requestExtensions =
1028 calloc(1, sizeof(*req.tbsRequest.requestExtensions));
1029 if (req.tbsRequest.requestExtensions == NULL) {
1030 ret = ENOMEM;
1031 goto out;
1034 es = req.tbsRequest.requestExtensions;
1036 es->val = calloc(es->len, sizeof(es->val[0]));
1037 if (es->val == NULL) {
1038 ret = ENOMEM;
1039 goto out;
1041 es->len = 1;
1042 ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
1043 if (ret) {
1044 free_OCSPRequest(&req);
1045 return ret;
1048 es->val[0].extnValue.data = malloc(10);
1049 if (es->val[0].extnValue.data == NULL) {
1050 ret = ENOMEM;
1051 goto out;
1053 es->val[0].extnValue.length = 10;
1055 ret = RAND_bytes(es->val[0].extnValue.data,
1056 es->val[0].extnValue.length);
1057 if (ret != 1) {
1058 ret = HX509_CRYPTO_INTERNAL_ERROR;
1059 goto out;
1061 ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1062 if (ret) {
1063 ret = ENOMEM;
1064 goto out;
1068 ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1069 &req, &size, ret);
1070 free_OCSPRequest(&req);
1071 if (ret)
1072 goto out;
1073 if (size != request->length)
1074 _hx509_abort("internal ASN.1 encoder error");
1076 return 0;
1078 out:
1079 free_OCSPRequest(&req);
1080 return ret;
1083 static char *
1084 printable_time(time_t t)
1086 static char s[128];
1087 char *p;
1088 if ((p = ctime(&t)) == NULL)
1089 strlcpy(s, "?", sizeof(s));
1090 else {
1091 strlcpy(s, p + 4, sizeof(s));
1092 s[20] = 0;
1094 return s;
1101 static int
1102 print_ocsp(hx509_context context, struct revoke_ocsp *ocsp, FILE *out)
1104 int ret = 0;
1105 size_t i;
1107 fprintf(out, "signer: ");
1109 switch(ocsp->ocsp.tbsResponseData.responderID.element) {
1110 case choice_OCSPResponderID_byName: {
1111 hx509_name n;
1112 char *s;
1113 _hx509_name_from_Name(&ocsp->ocsp.tbsResponseData.responderID.u.byName, &n);
1114 hx509_name_to_string(n, &s);
1115 hx509_name_free(&n);
1116 fprintf(out, " byName: %s\n", s);
1117 free(s);
1118 break;
1120 case choice_OCSPResponderID_byKey: {
1121 char *s;
1122 hex_encode(ocsp->ocsp.tbsResponseData.responderID.u.byKey.data,
1123 ocsp->ocsp.tbsResponseData.responderID.u.byKey.length,
1124 &s);
1125 fprintf(out, " byKey: %s\n", s);
1126 free(s);
1127 break;
1129 default:
1130 _hx509_abort("choice_OCSPResponderID unknown");
1131 break;
1134 fprintf(out, "producedAt: %s\n",
1135 printable_time(ocsp->ocsp.tbsResponseData.producedAt));
1137 fprintf(out, "replies: %d\n", ocsp->ocsp.tbsResponseData.responses.len);
1139 for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) {
1140 const char *status;
1141 switch (ocsp->ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1142 case choice_OCSPCertStatus_good:
1143 status = "good";
1144 break;
1145 case choice_OCSPCertStatus_revoked:
1146 status = "revoked";
1147 break;
1148 case choice_OCSPCertStatus_unknown:
1149 status = "unknown";
1150 break;
1151 default:
1152 status = "element unknown";
1155 fprintf(out, "\t%llu. status: %s\n", (unsigned long long)i, status);
1157 fprintf(out, "\tthisUpdate: %s\n",
1158 printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
1159 if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate)
1160 fprintf(out, "\tproducedAt: %s\n",
1161 printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
1165 fprintf(out, "appended certs:\n");
1166 if (ocsp->certs)
1167 ret = hx509_certs_iter_f(context, ocsp->certs, hx509_ci_print_names, out);
1169 return ret;
1172 static int
1173 print_crl(hx509_context context, struct revoke_crl *crl, FILE *out)
1176 hx509_name n;
1177 char *s;
1178 _hx509_name_from_Name(&crl->crl.tbsCertList.issuer, &n);
1179 hx509_name_to_string(n, &s);
1180 hx509_name_free(&n);
1181 fprintf(out, " issuer: %s\n", s);
1182 free(s);
1185 fprintf(out, " thisUpdate: %s\n",
1186 printable_time(_hx509_Time2time_t(&crl->crl.tbsCertList.thisUpdate)));
1188 return 0;
1196 HX509_LIB_FUNCTION int HX509_LIB_CALL
1197 hx509_revoke_print(hx509_context context,
1198 hx509_revoke_ctx ctx,
1199 FILE *out)
1201 int saved_ret = 0, ret;
1202 size_t n;
1204 for (n = 0; n < ctx->ocsps.len; n++) {
1205 struct revoke_ocsp *ocsp = &ctx->ocsps.val[n];
1207 fprintf(out, "OCSP %s\n", ocsp->path);
1209 ret = print_ocsp(context, ocsp, out);
1210 if (ret) {
1211 fprintf(out, "failure printing OCSP: %d\n", ret);
1212 saved_ret = ret;
1216 for (n = 0; n < ctx->crls.len; n++) {
1217 struct revoke_crl *crl = &ctx->crls.val[n];
1219 fprintf(out, "CRL %s\n", crl->path);
1221 ret = print_crl(context, crl, out);
1222 if (ret) {
1223 fprintf(out, "failure printing CRL: %d\n", ret);
1224 saved_ret = ret;
1227 return saved_ret;
1232 * Print the OCSP reply stored in a file.
1234 * @param context a hx509 context
1235 * @param path path to a file with a OCSP reply
1236 * @param out the out FILE descriptor to print the reply on
1238 * @return An hx509 error code, see hx509_get_error_string().
1240 * @ingroup hx509_revoke
1243 HX509_LIB_FUNCTION int HX509_LIB_CALL
1244 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1246 struct revoke_ocsp ocsp;
1247 int ret;
1249 if (out == NULL)
1250 out = stdout;
1252 memset(&ocsp, 0, sizeof(ocsp));
1254 ocsp.path = strdup(path);
1255 if (ocsp.path == NULL)
1256 return ENOMEM;
1258 ret = load_ocsp(context, &ocsp);
1259 if (ret) {
1260 free_ocsp(&ocsp);
1261 return ret;
1264 ret = print_ocsp(context, &ocsp, out);
1266 free_ocsp(&ocsp);
1267 return ret;
1271 * Verify that the certificate is part of the OCSP reply and it's not
1272 * expired. Doesn't verify signature the OCSP reply or it's done by a
1273 * authorized sender, that is assumed to be already done.
1275 * @param context a hx509 context
1276 * @param now the time right now, if 0, use the current time.
1277 * @param cert the certificate to verify
1278 * @param flags flags control the behavior
1279 * @param data pointer to the encode ocsp reply
1280 * @param length the length of the encode ocsp reply
1281 * @param expiration return the time the OCSP will expire and need to
1282 * be rechecked.
1284 * @return An hx509 error code, see hx509_get_error_string().
1286 * @ingroup hx509_verify
1289 HX509_LIB_FUNCTION int HX509_LIB_CALL
1290 hx509_ocsp_verify(hx509_context context,
1291 time_t now,
1292 hx509_cert cert,
1293 int flags,
1294 const void *data, size_t length,
1295 time_t *expiration)
1297 const Certificate *c = _hx509_get_cert(cert);
1298 OCSPBasicOCSPResponse basic;
1299 int ret;
1300 size_t i;
1302 if (now == 0)
1303 now = time(NULL);
1305 *expiration = 0;
1307 ret = parse_ocsp_basic(data, length, &basic);
1308 if (ret) {
1309 hx509_set_error_string(context, 0, ret,
1310 "Failed to parse OCSP response");
1311 return ret;
1314 for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1316 ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1317 &c->tbsCertificate.serialNumber);
1318 if (ret != 0)
1319 continue;
1321 /* verify issuer hashes hash */
1322 ret = _hx509_verify_signature(context,
1323 NULL,
1324 &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1325 &c->tbsCertificate.issuer._save,
1326 &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1327 if (ret != 0)
1328 continue;
1330 switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1331 case choice_OCSPCertStatus_good:
1332 break;
1333 case choice_OCSPCertStatus_revoked:
1334 case choice_OCSPCertStatus_unknown:
1335 continue;
1338 /* don't allow the update to be in the future */
1339 if (basic.tbsResponseData.responses.val[i].thisUpdate >
1340 now + context->ocsp_time_diff)
1341 continue;
1343 /* don't allow the next update to be in the past */
1344 if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1345 if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1346 continue;
1347 *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1348 } else
1349 *expiration = now;
1351 free_OCSPBasicOCSPResponse(&basic);
1352 return 0;
1355 free_OCSPBasicOCSPResponse(&basic);
1358 hx509_name name;
1359 char *subject;
1361 ret = hx509_cert_get_subject(cert, &name);
1362 if (ret) {
1363 hx509_clear_error_string(context);
1364 goto out;
1366 ret = hx509_name_to_string(name, &subject);
1367 hx509_name_free(&name);
1368 if (ret) {
1369 hx509_clear_error_string(context);
1370 goto out;
1372 hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1373 "Certificate %s not in OCSP response "
1374 "or not good",
1375 subject);
1376 free(subject);
1378 out:
1379 return HX509_CERT_NOT_IN_OCSP;
1382 struct hx509_crl {
1383 hx509_certs revoked;
1384 time_t expire;
1388 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1390 * @param context a hx509 context.
1391 * @param crl return pointer to a newly allocated CRL context.
1393 * @return An hx509 error code, see hx509_get_error_string().
1395 * @ingroup hx509_verify
1398 HX509_LIB_FUNCTION int HX509_LIB_CALL
1399 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1401 int ret;
1403 *crl = calloc(1, sizeof(**crl));
1404 if (*crl == NULL) {
1405 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1406 return ENOMEM;
1409 ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1410 if (ret) {
1411 free(*crl);
1412 *crl = NULL;
1413 return ret;
1415 (*crl)->expire = 0;
1416 return ret;
1420 * Add revoked certificate to an CRL context.
1422 * @param context a hx509 context.
1423 * @param crl the CRL to add the revoked certificate to.
1424 * @param certs keyset of certificate to revoke.
1426 * @return An hx509 error code, see hx509_get_error_string().
1428 * @ingroup hx509_verify
1431 HX509_LIB_FUNCTION int HX509_LIB_CALL
1432 hx509_crl_add_revoked_certs(hx509_context context,
1433 hx509_crl crl,
1434 hx509_certs certs)
1436 return hx509_certs_merge(context, crl->revoked, certs);
1440 * Set the lifetime of a CRL context.
1442 * @param context a hx509 context.
1443 * @param crl a CRL context
1444 * @param delta delta time the certificate is valid, library adds the
1445 * current time to this.
1447 * @return An hx509 error code, see hx509_get_error_string().
1449 * @ingroup hx509_verify
1452 HX509_LIB_FUNCTION int HX509_LIB_CALL
1453 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1455 crl->expire = time(NULL) + delta;
1456 return 0;
1460 * Free a CRL context.
1462 * @param context a hx509 context.
1463 * @param crl a CRL context to free.
1465 * @ingroup hx509_verify
1468 HX509_LIB_FUNCTION void HX509_LIB_CALL
1469 hx509_crl_free(hx509_context context, hx509_crl *crl)
1471 if (*crl == NULL)
1472 return;
1473 hx509_certs_free(&(*crl)->revoked);
1474 memset(*crl, 0, sizeof(**crl));
1475 free(*crl);
1476 *crl = NULL;
1479 static int HX509_LIB_CALL
1480 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1482 TBSCRLCertList *c = ctx;
1483 unsigned int num;
1484 void *ptr;
1485 int ret;
1487 num = c->revokedCertificates->len;
1488 ptr = realloc(c->revokedCertificates->val,
1489 (num + 1) * sizeof(c->revokedCertificates->val[0]));
1490 if (ptr == NULL) {
1491 hx509_clear_error_string(context);
1492 return ENOMEM;
1494 c->revokedCertificates->val = ptr;
1496 ret = hx509_cert_get_serialnumber(cert,
1497 &c->revokedCertificates->val[num].userCertificate);
1498 if (ret) {
1499 hx509_clear_error_string(context);
1500 return ret;
1502 c->revokedCertificates->val[num].revocationDate.element =
1503 choice_Time_generalTime;
1504 c->revokedCertificates->val[num].revocationDate.u.generalTime =
1505 time(NULL) - 3600 * 24;
1506 c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1508 c->revokedCertificates->len++;
1510 return 0;
1514 * Sign a CRL and return an encode certificate.
1516 * @param context a hx509 context.
1517 * @param signer certificate to sign the CRL with
1518 * @param crl the CRL to sign
1519 * @param os return the signed and encoded CRL, free with
1520 * free_heim_octet_string()
1522 * @return An hx509 error code, see hx509_get_error_string().
1524 * @ingroup hx509_verify
1527 HX509_LIB_FUNCTION int HX509_LIB_CALL
1528 hx509_crl_sign(hx509_context context,
1529 hx509_cert signer,
1530 hx509_crl crl,
1531 heim_octet_string *os)
1533 const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1534 CRLCertificateList c;
1535 size_t size;
1536 int ret;
1537 hx509_private_key signerkey;
1539 memset(&c, 0, sizeof(c));
1541 signerkey = _hx509_cert_private_key(signer);
1542 if (signerkey == NULL) {
1543 ret = HX509_PRIVATE_KEY_MISSING;
1544 hx509_set_error_string(context, 0, ret,
1545 "Private key missing for CRL signing");
1546 return ret;
1549 c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1550 if (c.tbsCertList.version == NULL) {
1551 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1552 return ENOMEM;
1555 *c.tbsCertList.version = 1;
1557 ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1558 if (ret) {
1559 hx509_clear_error_string(context);
1560 goto out;
1563 ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1564 &c.tbsCertList.issuer);
1565 if (ret) {
1566 hx509_clear_error_string(context);
1567 goto out;
1570 c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1571 c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1573 c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1574 if (c.tbsCertList.nextUpdate == NULL) {
1575 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1576 ret = ENOMEM;
1577 goto out;
1581 time_t next = crl->expire;
1582 if (next == 0)
1583 next = time(NULL) + 24 * 3600 * 365;
1585 c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1586 c.tbsCertList.nextUpdate->u.generalTime = next;
1589 c.tbsCertList.revokedCertificates =
1590 calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1591 if (c.tbsCertList.revokedCertificates == NULL) {
1592 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1593 ret = ENOMEM;
1594 goto out;
1596 c.tbsCertList.crlExtensions = NULL;
1598 ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
1599 if (ret)
1600 goto out;
1602 /* if not revoked certs, remove OPTIONAL entry */
1603 if (c.tbsCertList.revokedCertificates->len == 0) {
1604 free(c.tbsCertList.revokedCertificates);
1605 c.tbsCertList.revokedCertificates = NULL;
1608 ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1609 &c.tbsCertList, &size, ret);
1610 if (ret) {
1611 hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1612 goto out;
1614 if (size != os->length)
1615 _hx509_abort("internal ASN.1 encoder error");
1618 ret = _hx509_create_signature_bitstring(context,
1619 signerkey,
1620 sigalg,
1622 &c.signatureAlgorithm,
1623 &c.signatureValue);
1624 free(os->data);
1625 if (ret) {
1626 hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
1627 goto out;
1630 ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1631 &c, &size, ret);
1632 if (ret) {
1633 hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1634 goto out;
1636 if (size != os->length)
1637 _hx509_abort("internal ASN.1 encoder error");
1639 free_CRLCertificateList(&c);
1641 return 0;
1643 out:
1644 free_CRLCertificateList(&c);
1645 return ret;