xref: /freebsd/crypto/heimdal/lib/hx509/revoke.c (revision 9a14aa017b21c292740c00ee098195cd46642730)
1 /*
2  * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
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.
16  *
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.
20  *
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.
32  */
33 
34 /**
35  * @page page_revoke Revocation methods
36  *
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.
42  *
43  * CRL is a list of certifiates that have expired.
44  *
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.
50  */
51 
52 #include "hx_locl.h"
53 RCSID("$Id: revoke.c 22275 2007-12-11 11:02:11Z lha $");
54 
55 struct revoke_crl {
56     char *path;
57     time_t last_modfied;
58     CRLCertificateList crl;
59     int verified;
60     int failed_verify;
61 };
62 
63 struct revoke_ocsp {
64     char *path;
65     time_t last_modfied;
66     OCSPBasicOCSPResponse ocsp;
67     hx509_certs certs;
68     hx509_cert signer;
69 };
70 
71 
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;
82 };
83 
84 /**
85  * Allocate a revokation context. Free with hx509_revoke_free().
86  *
87  * @param context A hx509 context.
88  * @param ctx returns a newly allocated revokation context.
89  *
90  * @return An hx509 error code, see hx509_get_error_string().
91  *
92  * @ingroup hx509_revoke
93  */
94 
95 int
96 hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
97 {
98     *ctx = calloc(1, sizeof(**ctx));
99     if (*ctx == NULL)
100 	return ENOMEM;
101 
102     (*ctx)->ref = 1;
103     (*ctx)->crls.len = 0;
104     (*ctx)->crls.val = NULL;
105     (*ctx)->ocsps.len = 0;
106     (*ctx)->ocsps.val = NULL;
107 
108     return 0;
109 }
110 
111 hx509_revoke_ctx
112 _hx509_revoke_ref(hx509_revoke_ctx ctx)
113 {
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;
122 }
123 
124 static void
125 free_ocsp(struct revoke_ocsp *ocsp)
126 {
127     free(ocsp->path);
128     free_OCSPBasicOCSPResponse(&ocsp->ocsp);
129     hx509_certs_free(&ocsp->certs);
130     hx509_cert_free(ocsp->signer);
131 }
132 
133 /**
134  * Free a hx509 revokation context.
135  *
136  * @param ctx context to be freed
137  *
138  * @ingroup hx509_revoke
139  */
140 
141 void
142 hx509_revoke_free(hx509_revoke_ctx *ctx)
143 {
144     size_t i ;
145 
146     if (ctx == NULL || *ctx == NULL)
147 	return;
148 
149     if ((*ctx)->ref <= 0)
150 	_hx509_abort("revoke ctx refcount <= 0 on free");
151     if (--(*ctx)->ref > 0)
152 	return;
153 
154     for (i = 0; i < (*ctx)->crls.len; i++) {
155 	free((*ctx)->crls.val[i].path);
156 	free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
157     }
158 
159     for (i = 0; i < (*ctx)->ocsps.len; i++)
160 	free_ocsp(&(*ctx)->ocsps.val[i]);
161     free((*ctx)->ocsps.val);
162 
163     free((*ctx)->crls.val);
164 
165     memset(*ctx, 0, sizeof(**ctx));
166     free(*ctx);
167     *ctx = NULL;
168 }
169 
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)
176 {
177     hx509_cert signer = NULL;
178     hx509_query q;
179     int ret;
180 
181     _hx509_query_clear(&q);
182 
183     /*
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.
187      */
188 
189     q.match = HX509_QUERY_MATCH_ISSUER_NAME;
190     q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
191 
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;
201     }
202 
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;
208 
209     /*
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.
213      */
214     if (hx509_cert_cmp(signer, parent) != 0) {
215 	Certificate *p = _hx509_get_cert(parent);
216 	Certificate *s = _hx509_get_cert(signer);
217 
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;
224 	}
225 
226 	ret = _hx509_verify_signature_bitstring(context,
227 						p,
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;
235 	}
236 
237 	ret = hx509_cert_check_eku(context, signer,
238 				   oid_id_pkix_kp_OCSPSigning(), 0);
239 	if (ret)
240 	    goto out;
241     }
242 
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;
252     }
253 
254     ocsp->signer = signer;
255     signer = NULL;
256 out:
257     if (signer)
258 	hx509_cert_free(signer);
259 
260     return ret;
261 }
262 
263 /*
264  *
265  */
266 
267 static int
268 parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
269 {
270     OCSPResponse resp;
271     size_t size;
272     int ret;
273 
274     memset(basic, 0, sizeof(*basic));
275 
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;
282     }
283 
284     switch (resp.responseStatus) {
285     case successful:
286 	break;
287     default:
288 	free_OCSPResponse(&resp);
289 	return HX509_REVOKE_WRONG_DATA;
290     }
291 
292     if (resp.responseBytes == NULL) {
293 	free_OCSPResponse(&resp);
294 	return EINVAL;
295     }
296 
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;
302     }
303 
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;
311     }
312     if (size != resp.responseBytes->response.length) {
313 	free_OCSPResponse(&resp);
314 	free_OCSPBasicOCSPResponse(basic);
315 	return ASN1_EXTRA_DATA;
316     }
317     free_OCSPResponse(&resp);
318 
319     return 0;
320 }
321 
322 /*
323  *
324  */
325 
326 static int
327 load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
328 {
329     OCSPBasicOCSPResponse basic;
330     hx509_certs certs = NULL;
331     size_t length;
332     struct stat sb;
333     void *data;
334     int ret;
335 
336     ret = _hx509_map_file(ocsp->path, &data, &length, &sb);
337     if (ret)
338 	return ret;
339 
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;
346     }
347 
348     if (basic.certs) {
349 	int i;
350 
351 	ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
352 			       NULL, &certs);
353 	if (ret) {
354 	    free_OCSPBasicOCSPResponse(&basic);
355 	    return ret;
356 	}
357 
358 	for (i = 0; i < basic.certs->len; i++) {
359 	    hx509_cert c;
360 
361 	    ret = hx509_cert_init(context, &basic.certs->val[i], &c);
362 	    if (ret)
363 		continue;
364 
365 	    ret = hx509_certs_add(context, certs, c);
366 	    hx509_cert_free(c);
367 	    if (ret)
368 		continue;
369 	}
370     }
371 
372     ocsp->last_modfied = sb.st_mtime;
373 
374     free_OCSPBasicOCSPResponse(&ocsp->ocsp);
375     hx509_certs_free(&ocsp->certs);
376     hx509_cert_free(ocsp->signer);
377 
378     ocsp->ocsp = basic;
379     ocsp->certs = certs;
380     ocsp->signer = NULL;
381 
382     return 0;
383 }
384 
385 /**
386  * Add a OCSP file to the revokation context.
387  *
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.
391  *
392  * @return An hx509 error code, see hx509_get_error_string().
393  *
394  * @ingroup hx509_revoke
395  */
396 
397 int
398 hx509_revoke_add_ocsp(hx509_context context,
399 		      hx509_revoke_ctx ctx,
400 		      const char *path)
401 {
402     void *data;
403     int ret;
404     size_t i;
405 
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;
410     }
411 
412     path += 5;
413 
414     for (i = 0; i < ctx->ocsps.len; i++) {
415 	if (strcmp(ctx->ocsps.val[0].path, path) == 0)
416 	    return 0;
417     }
418 
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;
424     }
425 
426     ctx->ocsps.val = data;
427 
428     memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
429 	   sizeof(ctx->ocsps.val[0]));
430 
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;
435     }
436 
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;
441     }
442     ctx->ocsps.len++;
443 
444     return ret;
445 }
446 
447 /*
448  *
449  */
450 
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)
458 {
459     hx509_cert signer;
460     hx509_query q;
461     time_t t;
462     int ret;
463 
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;
469     }
470 
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;
475     }
476 
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;
482     }
483 
484     _hx509_query_clear(&q);
485 
486     /*
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.
489      */
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;
496 
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;
502 	}
503     }
504 
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;
514     }
515 
516     /*
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.
521      */
522     while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
523 	hx509_cert crl_parent;
524 
525 	_hx509_query_clear(&q);
526 
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;
530 
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;
536 	}
537 
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;
551 	}
552     }
553 
554 out:
555     hx509_cert_free(signer);
556 
557     return ret;
558 }
559 
560 static int
561 load_crl(const char *path, time_t *t, CRLCertificateList *crl)
562 {
563     size_t length, size;
564     struct stat sb;
565     void *data;
566     int ret;
567 
568     memset(crl, 0, sizeof(*crl));
569 
570     ret = _hx509_map_file(path, &data, &length, &sb);
571     if (ret)
572 	return ret;
573 
574     *t = sb.st_mtime;
575 
576     ret = decode_CRLCertificateList(data, length, crl, &size);
577     _hx509_unmap_file(data, length);
578     if (ret)
579 	return ret;
580 
581     /* check signature is aligned */
582     if (crl->signatureValue.length & 7) {
583 	free_CRLCertificateList(crl);
584 	return HX509_CRYPTO_SIG_INVALID_FORMAT;
585     }
586     return 0;
587 }
588 
589 /**
590  * Add a CRL file to the revokation context.
591  *
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.
595  *
596  * @return An hx509 error code, see hx509_get_error_string().
597  *
598  * @ingroup hx509_revoke
599  */
600 
601 int
602 hx509_revoke_add_crl(hx509_context context,
603 		     hx509_revoke_ctx ctx,
604 		     const char *path)
605 {
606     void *data;
607     size_t i;
608     int ret;
609 
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;
614     }
615 
616 
617     path += 5;
618 
619     for (i = 0; i < ctx->crls.len; i++) {
620 	if (strcmp(ctx->crls.val[0].path, path) == 0)
621 	    return 0;
622     }
623 
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;
629     }
630     ctx->crls.val = data;
631 
632     memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
633 
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;
638     }
639 
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;
646     }
647 
648     ctx->crls.len++;
649 
650     return ret;
651 }
652 
653 /**
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.
657  *
658  * @param context hx509 context
659  * @param ctx hx509 revokation context
660  * @param certs
661  * @param now
662  * @param cert
663  * @param parent_cert
664  *
665  * @return An hx509 error code, see hx509_get_error_string().
666  *
667  * @ingroup hx509_revoke
668  */
669 
670 
671 int
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)
678 {
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;
683 
684     hx509_clear_error_string(context);
685 
686     for (i = 0; i < ctx->ocsps.len; i++) {
687 	struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
688 	struct stat sb;
689 
690 	/* check this ocsp apply to this cert */
691 
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;
698 	}
699 
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;
705 	}
706 
707 	for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
708 	    heim_octet_string os;
709 
710 	    ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
711 				   &c->tbsCertificate.serialNumber);
712 	    if (ret != 0)
713 		continue;
714 
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;
723 
724 	    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
725 	    os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
726 
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;
734 
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;
745 	    }
746 
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;
751 
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 ? */;
758 
759 	    return 0;
760 	}
761     }
762 
763     for (i = 0; i < ctx->crls.len; i++) {
764 	struct revoke_crl *crl = &ctx->crls.val[i];
765 	struct stat sb;
766 
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;
772 
773 	ret = stat(crl->path, &sb);
774 	if (ret == 0 && crl->last_modfied != sb.st_mtime) {
775 	    CRLCertificateList cl;
776 
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;
783 	    }
784 	}
785 	if (crl->failed_verify)
786 	    continue;
787 
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;
794 	    }
795 	    crl->verified = 1;
796 	}
797 
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;
805 		}
806 	    }
807 	}
808 
809 	if (crl->crl.tbsCertList.revokedCertificates == NULL)
810 	    return 0;
811 
812 	/* check if cert is in crl */
813 	for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
814 	    time_t t;
815 
816 	    ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
817 				       &c->tbsCertificate.serialNumber);
818 	    if (ret != 0)
819 		continue;
820 
821 	    t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
822 	    if (t > now)
823 		continue;
824 
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;
829 
830 	    hx509_set_error_string(context, 0,
831 				   HX509_CERT_REVOKED,
832 				   "Certificate revoked by issuer in CRL");
833 	    return HX509_CERT_REVOKED;
834 	}
835 
836 	return 0;
837     }
838 
839 
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;
847 }
848 
849 struct ocsp_add_ctx {
850     OCSPTBSRequest *req;
851     hx509_certs certs;
852     const AlgorithmIdentifier *digest;
853     hx509_cert parent;
854 };
855 
856 static int
857 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
858 {
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;
867 
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;
874 
875     one = &ctx->req->requestList.val[ctx->req->requestList.len];
876     memset(one, 0, sizeof(*one));
877 
878     _hx509_query_clear(&q);
879 
880     q.match |= HX509_QUERY_FIND_ISSUER_CERT;
881     q.subject = c;
882 
883     ret = hx509_certs_find(context, ctx->certs, &q, &parent);
884     if (ret)
885 	goto out;
886 
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;
894 	}
895     } else
896 	ctx->parent = hx509_cert_ref(parent);
897 
898     p = _hx509_get_cert(parent);
899 
900     ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
901     if (ret)
902 	goto out;
903 
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;
912 
913     os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
914     os.length =
915 	p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
916 
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;
925 
926     ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
927 				       &one->reqCert.serialNumber);
928     if (ret)
929 	goto out;
930 
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));
937     }
938 
939     return ret;
940 }
941 
942 /**
943  * Create an OCSP request for a set of certificates.
944  *
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().
953  *
954  * @return An hx509 error code, see hx509_get_error_string().
955  *
956  * @ingroup hx509_revoke
957  */
958 
959 int
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)
967 {
968     OCSPRequest req;
969     size_t size;
970     int ret;
971     struct ocsp_add_ctx ctx;
972     Extensions *es;
973 
974     memset(&req, 0, sizeof(req));
975 
976     if (digest == NULL)
977 	digest = _hx509_crypto_default_digest_alg;
978 
979     ctx.req = &req.tbsRequest;
980     ctx.certs = pool;
981     ctx.digest = digest;
982     ctx.parent = NULL;
983 
984     ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx);
985     hx509_cert_free(ctx.parent);
986     if (ret)
987 	goto out;
988 
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;
995 	}
996 
997 	es = req.tbsRequest.requestExtensions;
998 
999 	es->val = calloc(es->len, sizeof(es->val[0]));
1000 	if (es->val == NULL) {
1001 	    ret = ENOMEM;
1002 	    goto out;
1003 	}
1004 	es->len = 1;
1005 
1006 	ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID);
1007 	if (ret) {
1008 	    free_OCSPRequest(&req);
1009 	    return ret;
1010 	}
1011 
1012 	es->val[0].extnValue.data = malloc(10);
1013 	if (es->val[0].extnValue.data == NULL) {
1014 	    ret = ENOMEM;
1015 	    goto out;
1016 	}
1017 	es->val[0].extnValue.length = 10;
1018 
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;
1024 	}
1025 	ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1026 	if (ret) {
1027 	    ret = ENOMEM;
1028 	    goto out;
1029 	}
1030     }
1031 
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");
1039 
1040     return 0;
1041 
1042 out:
1043     free_OCSPRequest(&req);
1044     return ret;
1045 }
1046 
1047 static char *
1048 printable_time(time_t t)
1049 {
1050     static char s[128];
1051     strlcpy(s, ctime(&t)+ 4, sizeof(s));
1052     s[20] = 0;
1053     return s;
1054 }
1055 
1056 /**
1057  * Print the OCSP reply stored in a file.
1058  *
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
1062  *
1063  * @return An hx509 error code, see hx509_get_error_string().
1064  *
1065  * @ingroup hx509_revoke
1066  */
1067 
1068 int
1069 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1070 {
1071     struct revoke_ocsp ocsp;
1072     int ret, i;
1073 
1074     if (out == NULL)
1075 	out = stdout;
1076 
1077     memset(&ocsp, 0, sizeof(ocsp));
1078 
1079     ocsp.path = strdup(path);
1080     if (ocsp.path == NULL)
1081 	return ENOMEM;
1082 
1083     ret = load_ocsp(context, &ocsp);
1084     if (ret) {
1085 	free_ocsp(&ocsp);
1086 	return ret;
1087     }
1088 
1089     fprintf(out, "signer: ");
1090 
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;
1101     }
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;
1110     }
1111     default:
1112 	_hx509_abort("choice_OCSPResponderID unknown");
1113 	break;
1114     }
1115 
1116     fprintf(out, "producedAt: %s\n",
1117 	    printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1118 
1119     fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1120 
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";
1135 	}
1136 
1137 	fprintf(out, "\t%d. status: %s\n", i, status);
1138 
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));
1144 
1145     }
1146 
1147     fprintf(out, "appended certs:\n");
1148     if (ocsp.certs)
1149 	ret = hx509_certs_iter(context, ocsp.certs, hx509_ci_print_names, out);
1150 
1151     free_ocsp(&ocsp);
1152     return ret;
1153 }
1154 
1155 /**
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.
1159  *
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.
1168  *
1169  * @return An hx509 error code, see hx509_get_error_string().
1170  *
1171  * @ingroup hx509_verify
1172  */
1173 
1174 int
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)
1181 {
1182     const Certificate *c = _hx509_get_cert(cert);
1183     OCSPBasicOCSPResponse basic;
1184     int ret, i;
1185 
1186     if (now == 0)
1187 	now = time(NULL);
1188 
1189     *expiration = 0;
1190 
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;
1196     }
1197 
1198     for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1199 
1200 	ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1201 			       &c->tbsCertificate.serialNumber);
1202 	if (ret != 0)
1203 	    continue;
1204 
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;
1213 
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;
1220 	}
1221 
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;
1226 
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;
1234 
1235 	free_OCSPBasicOCSPResponse(&basic);
1236 	return 0;
1237     }
1238 
1239     free_OCSPBasicOCSPResponse(&basic);
1240 
1241     {
1242 	hx509_name name;
1243 	char *subject;
1244 
1245 	ret = hx509_cert_get_subject(cert, &name);
1246 	if (ret) {
1247 	    hx509_clear_error_string(context);
1248 	    goto out;
1249 	}
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;
1255 	}
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);
1261     }
1262 out:
1263     return HX509_CERT_NOT_IN_OCSP;
1264 }
1265 
1266 struct hx509_crl {
1267     hx509_certs revoked;
1268     time_t expire;
1269 };
1270 
1271 /**
1272  * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1273  *
1274  * @param context a hx509 context.
1275  * @param crl return pointer to a newly allocated CRL context.
1276  *
1277  * @return An hx509 error code, see hx509_get_error_string().
1278  *
1279  * @ingroup hx509_verify
1280  */
1281 
1282 int
1283 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1284 {
1285     int ret;
1286 
1287     *crl = calloc(1, sizeof(**crl));
1288     if (*crl == NULL) {
1289 	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1290 	return ENOMEM;
1291     }
1292 
1293     ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1294     if (ret) {
1295 	free(*crl);
1296 	*crl = NULL;
1297 	return ret;
1298     }
1299     (*crl)->expire = 0;
1300     return ret;
1301 }
1302 
1303 /**
1304  * Add revoked certificate to an CRL context.
1305  *
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.
1309  *
1310  * @return An hx509 error code, see hx509_get_error_string().
1311  *
1312  * @ingroup hx509_verify
1313  */
1314 
1315 int
1316 hx509_crl_add_revoked_certs(hx509_context context,
1317 			    hx509_crl crl,
1318 			    hx509_certs certs)
1319 {
1320     return hx509_certs_merge(context, crl->revoked, certs);
1321 }
1322 
1323 /**
1324  * Set the lifetime of a CRL context.
1325  *
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.
1330  *
1331  * @return An hx509 error code, see hx509_get_error_string().
1332  *
1333  * @ingroup hx509_verify
1334  */
1335 
1336 int
1337 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1338 {
1339     crl->expire = time(NULL) + delta;
1340     return 0;
1341 }
1342 
1343 /**
1344  * Free a CRL context.
1345  *
1346  * @param context a hx509 context.
1347  * @param crl a CRL context to free.
1348  *
1349  * @ingroup hx509_verify
1350  */
1351 
1352 void
1353 hx509_crl_free(hx509_context context, hx509_crl *crl)
1354 {
1355     if (*crl == NULL)
1356 	return;
1357     hx509_certs_free(&(*crl)->revoked);
1358     memset(*crl, 0, sizeof(**crl));
1359     free(*crl);
1360     *crl = NULL;
1361 }
1362 
1363 static int
1364 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1365 {
1366     TBSCRLCertList *c = ctx;
1367     unsigned int num;
1368     void *ptr;
1369     int ret;
1370 
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;
1377     }
1378     c->revokedCertificates->val = ptr;
1379 
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;
1385     }
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;
1391 
1392     c->revokedCertificates->len++;
1393 
1394     return 0;
1395 }
1396 
1397 /**
1398  * Sign a CRL and return an encode certificate.
1399  *
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()
1405  *
1406  * @return An hx509 error code, see hx509_get_error_string().
1407  *
1408  * @ingroup hx509_verify
1409  */
1410 
1411 int
1412 hx509_crl_sign(hx509_context context,
1413 	       hx509_cert signer,
1414 	       hx509_crl crl,
1415 	       heim_octet_string *os)
1416 {
1417     const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1418     CRLCertificateList c;
1419     size_t size;
1420     int ret;
1421     hx509_private_key signerkey;
1422 
1423     memset(&c, 0, sizeof(c));
1424 
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;
1431     }
1432 
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;
1437     }
1438 
1439     *c.tbsCertList.version = 1;
1440 
1441     ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1442     if (ret) {
1443 	hx509_clear_error_string(context);
1444 	goto out;
1445     }
1446 
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;
1452     }
1453 
1454     c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1455     c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1456 
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;
1462     }
1463 
1464     {
1465 	time_t next = crl->expire;
1466 	if (next == 0)
1467 	    next = time(NULL) + 24 * 3600 * 365;
1468 
1469 	c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1470 	c.tbsCertList.nextUpdate->u.generalTime = next;
1471     }
1472 
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;
1479     }
1480     c.tbsCertList.crlExtensions = NULL;
1481 
1482     ret = hx509_certs_iter(context, crl->revoked, add_revoked, &c.tbsCertList);
1483     if (ret)
1484 	goto out;
1485 
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;
1490     }
1491 
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;
1497     }
1498     if (size != os->length)
1499 	_hx509_abort("internal ASN.1 encoder error");
1500 
1501 
1502     ret = _hx509_create_signature_bitstring(context,
1503 					    signerkey,
1504 					    sigalg,
1505 					    os,
1506 					    &c.signatureAlgorithm,
1507 					    &c.signatureValue);
1508     free(os->data);
1509 
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;
1516     }
1517     if (size != os->length)
1518 	_hx509_abort("internal ASN.1 encoder error");
1519 
1520     return 0;
1521 
1522 out:
1523     free_CRLCertificateList(&c);
1524     return ret;
1525 }
1526