xref: /freebsd/crypto/heimdal/lib/hx509/revoke.c (revision 3078531de10dcae44b253a35125c949ff4235284)
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 
54 struct revoke_crl {
55     char *path;
56     time_t last_modfied;
57     CRLCertificateList crl;
58     int verified;
59     int failed_verify;
60 };
61 
62 struct revoke_ocsp {
63     char *path;
64     time_t last_modfied;
65     OCSPBasicOCSPResponse ocsp;
66     hx509_certs certs;
67     hx509_cert signer;
68 };
69 
70 
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;
81 };
82 
83 /**
84  * Allocate a revokation context. Free with hx509_revoke_free().
85  *
86  * @param context A hx509 context.
87  * @param ctx returns a newly allocated revokation context.
88  *
89  * @return An hx509 error code, see hx509_get_error_string().
90  *
91  * @ingroup hx509_revoke
92  */
93 
94 int
95 hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
96 {
97     *ctx = calloc(1, sizeof(**ctx));
98     if (*ctx == NULL)
99 	return ENOMEM;
100 
101     (*ctx)->ref = 1;
102     (*ctx)->crls.len = 0;
103     (*ctx)->crls.val = NULL;
104     (*ctx)->ocsps.len = 0;
105     (*ctx)->ocsps.val = NULL;
106 
107     return 0;
108 }
109 
110 hx509_revoke_ctx
111 _hx509_revoke_ref(hx509_revoke_ctx ctx)
112 {
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;
121 }
122 
123 static void
124 free_ocsp(struct revoke_ocsp *ocsp)
125 {
126     free(ocsp->path);
127     free_OCSPBasicOCSPResponse(&ocsp->ocsp);
128     hx509_certs_free(&ocsp->certs);
129     hx509_cert_free(ocsp->signer);
130 }
131 
132 /**
133  * Free a hx509 revokation context.
134  *
135  * @param ctx context to be freed
136  *
137  * @ingroup hx509_revoke
138  */
139 
140 void
141 hx509_revoke_free(hx509_revoke_ctx *ctx)
142 {
143     size_t i ;
144 
145     if (ctx == NULL || *ctx == NULL)
146 	return;
147 
148     if ((*ctx)->ref == 0)
149 	_hx509_abort("revoke ctx refcount == 0 on free");
150     if (--(*ctx)->ref > 0)
151 	return;
152 
153     for (i = 0; i < (*ctx)->crls.len; i++) {
154 	free((*ctx)->crls.val[i].path);
155 	free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
156     }
157 
158     for (i = 0; i < (*ctx)->ocsps.len; i++)
159 	free_ocsp(&(*ctx)->ocsps.val[i]);
160     free((*ctx)->ocsps.val);
161 
162     free((*ctx)->crls.val);
163 
164     memset(*ctx, 0, sizeof(**ctx));
165     free(*ctx);
166     *ctx = NULL;
167 }
168 
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)
175 {
176     hx509_cert signer = NULL;
177     hx509_query q;
178     int ret;
179 
180     _hx509_query_clear(&q);
181 
182     /*
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.
186      */
187 
188     q.match = HX509_QUERY_MATCH_ISSUER_NAME;
189     q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
190 
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;
200     }
201 
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;
207 
208     /*
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.
212      */
213     if (hx509_cert_cmp(signer, parent) != 0) {
214 	Certificate *p = _hx509_get_cert(parent);
215 	Certificate *s = _hx509_get_cert(signer);
216 
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 is "
221 				   "doesn't have CA as signer certificate");
222 	    goto out;
223 	}
224 
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;
234 	}
235 
236 	ret = hx509_cert_check_eku(context, signer,
237 				   &asn1_oid_id_pkix_kp_OCSPSigning, 0);
238 	if (ret)
239 	    goto out;
240     }
241 
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;
251     }
252 
253     ocsp->signer = signer;
254     signer = NULL;
255 out:
256     if (signer)
257 	hx509_cert_free(signer);
258 
259     return ret;
260 }
261 
262 /*
263  *
264  */
265 
266 static int
267 parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
268 {
269     OCSPResponse resp;
270     size_t size;
271     int ret;
272 
273     memset(basic, 0, sizeof(*basic));
274 
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;
281     }
282 
283     switch (resp.responseStatus) {
284     case successful:
285 	break;
286     default:
287 	free_OCSPResponse(&resp);
288 	return HX509_REVOKE_WRONG_DATA;
289     }
290 
291     if (resp.responseBytes == NULL) {
292 	free_OCSPResponse(&resp);
293 	return EINVAL;
294     }
295 
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;
301     }
302 
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;
310     }
311     if (size != resp.responseBytes->response.length) {
312 	free_OCSPResponse(&resp);
313 	free_OCSPBasicOCSPResponse(basic);
314 	return ASN1_EXTRA_DATA;
315     }
316     free_OCSPResponse(&resp);
317 
318     return 0;
319 }
320 
321 /*
322  *
323  */
324 
325 static int
326 load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
327 {
328     OCSPBasicOCSPResponse basic;
329     hx509_certs certs = NULL;
330     size_t length;
331     struct stat sb;
332     void *data;
333     int ret;
334 
335     ret = rk_undumpdata(ocsp->path, &data, &length);
336     if (ret)
337 	return ret;
338 
339     ret = stat(ocsp->path, &sb);
340     if (ret)
341 	return errno;
342 
343     ret = parse_ocsp_basic(data, length, &basic);
344     rk_xfree(data);
345     if (ret) {
346 	hx509_set_error_string(context, 0, ret,
347 			       "Failed to parse OCSP response");
348 	return ret;
349     }
350 
351     if (basic.certs) {
352 	size_t i;
353 
354 	ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
355 			       NULL, &certs);
356 	if (ret) {
357 	    free_OCSPBasicOCSPResponse(&basic);
358 	    return ret;
359 	}
360 
361 	for (i = 0; i < basic.certs->len; i++) {
362 	    hx509_cert c;
363 
364 	    ret = hx509_cert_init(context, &basic.certs->val[i], &c);
365 	    if (ret)
366 		continue;
367 
368 	    ret = hx509_certs_add(context, certs, c);
369 	    hx509_cert_free(c);
370 	    if (ret)
371 		continue;
372 	}
373     }
374 
375     ocsp->last_modfied = sb.st_mtime;
376 
377     free_OCSPBasicOCSPResponse(&ocsp->ocsp);
378     hx509_certs_free(&ocsp->certs);
379     hx509_cert_free(ocsp->signer);
380 
381     ocsp->ocsp = basic;
382     ocsp->certs = certs;
383     ocsp->signer = NULL;
384 
385     return 0;
386 }
387 
388 /**
389  * Add a OCSP file to the revokation context.
390  *
391  * @param context hx509 context
392  * @param ctx hx509 revokation context
393  * @param path path to file that is going to be added to the context.
394  *
395  * @return An hx509 error code, see hx509_get_error_string().
396  *
397  * @ingroup hx509_revoke
398  */
399 
400 int
401 hx509_revoke_add_ocsp(hx509_context context,
402 		      hx509_revoke_ctx ctx,
403 		      const char *path)
404 {
405     void *data;
406     int ret;
407     size_t i;
408 
409     if (strncmp(path, "FILE:", 5) != 0) {
410 	hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
411 			       "unsupport type in %s", path);
412 	return HX509_UNSUPPORTED_OPERATION;
413     }
414 
415     path += 5;
416 
417     for (i = 0; i < ctx->ocsps.len; i++) {
418 	if (strcmp(ctx->ocsps.val[0].path, path) == 0)
419 	    return 0;
420     }
421 
422     data = realloc(ctx->ocsps.val,
423 		   (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
424     if (data == NULL) {
425 	hx509_clear_error_string(context);
426 	return ENOMEM;
427     }
428 
429     ctx->ocsps.val = data;
430 
431     memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
432 	   sizeof(ctx->ocsps.val[0]));
433 
434     ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
435     if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
436 	hx509_clear_error_string(context);
437 	return ENOMEM;
438     }
439 
440     ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
441     if (ret) {
442 	free(ctx->ocsps.val[ctx->ocsps.len].path);
443 	return ret;
444     }
445     ctx->ocsps.len++;
446 
447     return ret;
448 }
449 
450 /*
451  *
452  */
453 
454 static int
455 verify_crl(hx509_context context,
456 	   hx509_revoke_ctx ctx,
457 	   CRLCertificateList *crl,
458 	   time_t time_now,
459 	   hx509_certs certs,
460 	   hx509_cert parent)
461 {
462     hx509_cert signer;
463     hx509_query q;
464     time_t t;
465     int ret;
466 
467     t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
468     if (t > time_now) {
469 	hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
470 			       "CRL used before time");
471 	return HX509_CRL_USED_BEFORE_TIME;
472     }
473 
474     if (crl->tbsCertList.nextUpdate == NULL) {
475 	hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
476 			       "CRL missing nextUpdate");
477 	return HX509_CRL_INVALID_FORMAT;
478     }
479 
480     t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
481     if (t < time_now) {
482 	hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
483 			       "CRL used after time");
484 	return HX509_CRL_USED_AFTER_TIME;
485     }
486 
487     _hx509_query_clear(&q);
488 
489     /*
490      * If it's the signer have CRLSIGN bit set, use that as the signer
491      * cert for the certificate, otherwise, search for a certificate.
492      */
493     if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
494 	signer = hx509_cert_ref(parent);
495     } else {
496 	q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
497 	q.match |= HX509_QUERY_KU_CRLSIGN;
498 	q.subject_name = &crl->tbsCertList.issuer;
499 
500 	ret = hx509_certs_find(context, certs, &q, &signer);
501 	if (ret) {
502 	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
503 				   "Failed to find certificate for CRL");
504 	    return ret;
505 	}
506     }
507 
508     ret = _hx509_verify_signature_bitstring(context,
509 					    signer,
510 					    &crl->signatureAlgorithm,
511 					    &crl->tbsCertList._save,
512 					    &crl->signatureValue);
513     if (ret) {
514 	hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
515 			       "CRL signature invalid");
516 	goto out;
517     }
518 
519     /*
520      * If signer is not CA cert, need to check revoke status of this
521      * CRL signing cert too, this include all parent CRL signer cert
522      * up to the root *sigh*, assume root at least hve CERTSIGN flag
523      * set.
524      */
525     while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
526 	hx509_cert crl_parent;
527 
528 	_hx509_query_clear(&q);
529 
530 	q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
531 	q.match |= HX509_QUERY_KU_CRLSIGN;
532 	q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
533 
534 	ret = hx509_certs_find(context, certs, &q, &crl_parent);
535 	if (ret) {
536 	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
537 				   "Failed to find parent of CRL signer");
538 	    goto out;
539 	}
540 
541 	ret = hx509_revoke_verify(context,
542 				  ctx,
543 				  certs,
544 				  time_now,
545 				  signer,
546 				  crl_parent);
547 	hx509_cert_free(signer);
548 	signer = crl_parent;
549 	if (ret) {
550 	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
551 				   "Failed to verify revoke "
552 				   "status of CRL signer");
553 	    goto out;
554 	}
555     }
556 
557 out:
558     hx509_cert_free(signer);
559 
560     return ret;
561 }
562 
563 static int
564 load_crl(const char *path, time_t *t, CRLCertificateList *crl)
565 {
566     size_t length, size;
567     struct stat sb;
568     void *data;
569     int ret;
570 
571     memset(crl, 0, sizeof(*crl));
572 
573     ret = rk_undumpdata(path, &data, &length);
574     if (ret)
575 	return ret;
576 
577     ret = stat(path, &sb);
578     if (ret)
579 	return errno;
580 
581     *t = sb.st_mtime;
582 
583     ret = decode_CRLCertificateList(data, length, crl, &size);
584     rk_xfree(data);
585     if (ret)
586 	return ret;
587 
588     /* check signature is aligned */
589     if (crl->signatureValue.length & 7) {
590 	free_CRLCertificateList(crl);
591 	return HX509_CRYPTO_SIG_INVALID_FORMAT;
592     }
593     return 0;
594 }
595 
596 /**
597  * Add a CRL file to the revokation context.
598  *
599  * @param context hx509 context
600  * @param ctx hx509 revokation context
601  * @param path path to file that is going to be added to the context.
602  *
603  * @return An hx509 error code, see hx509_get_error_string().
604  *
605  * @ingroup hx509_revoke
606  */
607 
608 int
609 hx509_revoke_add_crl(hx509_context context,
610 		     hx509_revoke_ctx ctx,
611 		     const char *path)
612 {
613     void *data;
614     size_t i;
615     int ret;
616 
617     if (strncmp(path, "FILE:", 5) != 0) {
618 	hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
619 			       "unsupport type in %s", path);
620 	return HX509_UNSUPPORTED_OPERATION;
621     }
622 
623 
624     path += 5;
625 
626     for (i = 0; i < ctx->crls.len; i++) {
627 	if (strcmp(ctx->crls.val[0].path, path) == 0)
628 	    return 0;
629     }
630 
631     data = realloc(ctx->crls.val,
632 		   (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
633     if (data == NULL) {
634 	hx509_clear_error_string(context);
635 	return ENOMEM;
636     }
637     ctx->crls.val = data;
638 
639     memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
640 
641     ctx->crls.val[ctx->crls.len].path = strdup(path);
642     if (ctx->crls.val[ctx->crls.len].path == NULL) {
643 	hx509_clear_error_string(context);
644 	return ENOMEM;
645     }
646 
647     ret = load_crl(path,
648 		   &ctx->crls.val[ctx->crls.len].last_modfied,
649 		   &ctx->crls.val[ctx->crls.len].crl);
650     if (ret) {
651 	free(ctx->crls.val[ctx->crls.len].path);
652 	return ret;
653     }
654 
655     ctx->crls.len++;
656 
657     return ret;
658 }
659 
660 /**
661  * Check that a certificate is not expired according to a revokation
662  * context. Also need the parent certificte to the check OCSP
663  * parent identifier.
664  *
665  * @param context hx509 context
666  * @param ctx hx509 revokation context
667  * @param certs
668  * @param now
669  * @param cert
670  * @param parent_cert
671  *
672  * @return An hx509 error code, see hx509_get_error_string().
673  *
674  * @ingroup hx509_revoke
675  */
676 
677 
678 int
679 hx509_revoke_verify(hx509_context context,
680 		    hx509_revoke_ctx ctx,
681 		    hx509_certs certs,
682 		    time_t now,
683 		    hx509_cert cert,
684 		    hx509_cert parent_cert)
685 {
686     const Certificate *c = _hx509_get_cert(cert);
687     const Certificate *p = _hx509_get_cert(parent_cert);
688     unsigned long i, j, k;
689     int ret;
690 
691     hx509_clear_error_string(context);
692 
693     for (i = 0; i < ctx->ocsps.len; i++) {
694 	struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
695 	struct stat sb;
696 
697 	/* check this ocsp apply to this cert */
698 
699 	/* check if there is a newer version of the file */
700 	ret = stat(ocsp->path, &sb);
701 	if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
702 	    ret = load_ocsp(context, ocsp);
703 	    if (ret)
704 		continue;
705 	}
706 
707 	/* verify signature in ocsp if not already done */
708 	if (ocsp->signer == NULL) {
709 	    ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
710 	    if (ret)
711 		continue;
712 	}
713 
714 	for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
715 	    heim_octet_string os;
716 
717 	    ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
718 				   &c->tbsCertificate.serialNumber);
719 	    if (ret != 0)
720 		continue;
721 
722 	    /* verify issuer hashes hash */
723 	    ret = _hx509_verify_signature(context,
724 					  NULL,
725 					  &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
726 					  &c->tbsCertificate.issuer._save,
727 					  &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
728 	    if (ret != 0)
729 		continue;
730 
731 	    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
732 	    os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
733 
734 	    ret = _hx509_verify_signature(context,
735 					  NULL,
736 					  &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
737 					  &os,
738 					  &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
739 	    if (ret != 0)
740 		continue;
741 
742 	    switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
743 	    case choice_OCSPCertStatus_good:
744 		break;
745 	    case choice_OCSPCertStatus_revoked:
746 		hx509_set_error_string(context, 0,
747 				       HX509_CERT_REVOKED,
748 				       "Certificate revoked by issuer in OCSP");
749 		return HX509_CERT_REVOKED;
750 	    case choice_OCSPCertStatus_unknown:
751 		continue;
752 	    }
753 
754 	    /* don't allow the update to be in the future */
755 	    if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
756 		now + context->ocsp_time_diff)
757 		continue;
758 
759 	    /* don't allow the next update to be in the past */
760 	    if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
761 		if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
762 		    continue;
763 	    } /* else should force a refetch, but can we ? */
764 
765 	    return 0;
766 	}
767     }
768 
769     for (i = 0; i < ctx->crls.len; i++) {
770 	struct revoke_crl *crl = &ctx->crls.val[i];
771 	struct stat sb;
772 	int diff;
773 
774 	/* check if cert.issuer == crls.val[i].crl.issuer */
775 	ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
776 			      &crl->crl.tbsCertList.issuer, &diff);
777 	if (ret || diff)
778 	    continue;
779 
780 	ret = stat(crl->path, &sb);
781 	if (ret == 0 && crl->last_modfied != sb.st_mtime) {
782 	    CRLCertificateList cl;
783 
784 	    ret = load_crl(crl->path, &crl->last_modfied, &cl);
785 	    if (ret == 0) {
786 		free_CRLCertificateList(&crl->crl);
787 		crl->crl = cl;
788 		crl->verified = 0;
789 		crl->failed_verify = 0;
790 	    }
791 	}
792 	if (crl->failed_verify)
793 	    continue;
794 
795 	/* verify signature in crl if not already done */
796 	if (crl->verified == 0) {
797 	    ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
798 	    if (ret) {
799 		crl->failed_verify = 1;
800 		continue;
801 	    }
802 	    crl->verified = 1;
803 	}
804 
805 	if (crl->crl.tbsCertList.crlExtensions) {
806 	    for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
807 		if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
808 		    hx509_set_error_string(context, 0,
809 					   HX509_CRL_UNKNOWN_EXTENSION,
810 					   "Unknown CRL extension");
811 		    return HX509_CRL_UNKNOWN_EXTENSION;
812 		}
813 	    }
814 	}
815 
816 	if (crl->crl.tbsCertList.revokedCertificates == NULL)
817 	    return 0;
818 
819 	/* check if cert is in crl */
820 	for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
821 	    time_t t;
822 
823 	    ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
824 				       &c->tbsCertificate.serialNumber);
825 	    if (ret != 0)
826 		continue;
827 
828 	    t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
829 	    if (t > now)
830 		continue;
831 
832 	    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
833 		for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
834 		    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
835 			return HX509_CRL_UNKNOWN_EXTENSION;
836 
837 	    hx509_set_error_string(context, 0,
838 				   HX509_CERT_REVOKED,
839 				   "Certificate revoked by issuer in CRL");
840 	    return HX509_CERT_REVOKED;
841 	}
842 
843 	return 0;
844     }
845 
846 
847     if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
848 	return 0;
849     hx509_set_error_string(context, HX509_ERROR_APPEND,
850 			   HX509_REVOKE_STATUS_MISSING,
851 			   "No revoke status found for "
852 			   "certificates");
853     return HX509_REVOKE_STATUS_MISSING;
854 }
855 
856 struct ocsp_add_ctx {
857     OCSPTBSRequest *req;
858     hx509_certs certs;
859     const AlgorithmIdentifier *digest;
860     hx509_cert parent;
861 };
862 
863 static int
864 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
865 {
866     struct ocsp_add_ctx *ctx = ptr;
867     OCSPInnerRequest *one;
868     hx509_cert parent = NULL;
869     Certificate *p, *c = _hx509_get_cert(cert);
870     heim_octet_string os;
871     int ret;
872     hx509_query q;
873     void *d;
874 
875     d = realloc(ctx->req->requestList.val,
876 		sizeof(ctx->req->requestList.val[0]) *
877 		(ctx->req->requestList.len + 1));
878     if (d == NULL)
879 	return ENOMEM;
880     ctx->req->requestList.val = d;
881 
882     one = &ctx->req->requestList.val[ctx->req->requestList.len];
883     memset(one, 0, sizeof(*one));
884 
885     _hx509_query_clear(&q);
886 
887     q.match |= HX509_QUERY_FIND_ISSUER_CERT;
888     q.subject = c;
889 
890     ret = hx509_certs_find(context, ctx->certs, &q, &parent);
891     if (ret)
892 	goto out;
893 
894     if (ctx->parent) {
895 	if (hx509_cert_cmp(ctx->parent, parent) != 0) {
896 	    ret = HX509_REVOKE_NOT_SAME_PARENT;
897 	    hx509_set_error_string(context, 0, ret,
898 				   "Not same parent certifate as "
899 				   "last certificate in request");
900 	    goto out;
901 	}
902     } else
903 	ctx->parent = hx509_cert_ref(parent);
904 
905     p = _hx509_get_cert(parent);
906 
907     ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
908     if (ret)
909 	goto out;
910 
911     ret = _hx509_create_signature(context,
912 				  NULL,
913 				  &one->reqCert.hashAlgorithm,
914 				  &c->tbsCertificate.issuer._save,
915 				  NULL,
916 				  &one->reqCert.issuerNameHash);
917     if (ret)
918 	goto out;
919 
920     os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
921     os.length =
922 	p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
923 
924     ret = _hx509_create_signature(context,
925 				  NULL,
926 				  &one->reqCert.hashAlgorithm,
927 				  &os,
928 				  NULL,
929 				  &one->reqCert.issuerKeyHash);
930     if (ret)
931 	goto out;
932 
933     ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
934 				       &one->reqCert.serialNumber);
935     if (ret)
936 	goto out;
937 
938     ctx->req->requestList.len++;
939 out:
940     hx509_cert_free(parent);
941     if (ret) {
942 	free_OCSPInnerRequest(one);
943 	memset(one, 0, sizeof(*one));
944     }
945 
946     return ret;
947 }
948 
949 /**
950  * Create an OCSP request for a set of certificates.
951  *
952  * @param context a hx509 context
953  * @param reqcerts list of certificates to request ocsp data for
954  * @param pool certificate pool to use when signing
955  * @param signer certificate to use to sign the request
956  * @param digest the signing algorithm in the request, if NULL use the
957  * default signature algorithm,
958  * @param request the encoded request, free with free_heim_octet_string().
959  * @param nonce nonce in the request, free with free_heim_octet_string().
960  *
961  * @return An hx509 error code, see hx509_get_error_string().
962  *
963  * @ingroup hx509_revoke
964  */
965 
966 int
967 hx509_ocsp_request(hx509_context context,
968 		   hx509_certs reqcerts,
969 		   hx509_certs pool,
970 		   hx509_cert signer,
971 		   const AlgorithmIdentifier *digest,
972 		   heim_octet_string *request,
973 		   heim_octet_string *nonce)
974 {
975     OCSPRequest req;
976     size_t size;
977     int ret;
978     struct ocsp_add_ctx ctx;
979     Extensions *es;
980 
981     memset(&req, 0, sizeof(req));
982 
983     if (digest == NULL)
984 	digest = _hx509_crypto_default_digest_alg;
985 
986     ctx.req = &req.tbsRequest;
987     ctx.certs = pool;
988     ctx.digest = digest;
989     ctx.parent = NULL;
990 
991     ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
992     hx509_cert_free(ctx.parent);
993     if (ret)
994 	goto out;
995 
996     if (nonce) {
997 	req.tbsRequest.requestExtensions =
998 	    calloc(1, sizeof(*req.tbsRequest.requestExtensions));
999 	if (req.tbsRequest.requestExtensions == NULL) {
1000 	    ret = ENOMEM;
1001 	    goto out;
1002 	}
1003 
1004 	es = req.tbsRequest.requestExtensions;
1005 
1006 	es->val = calloc(es->len, sizeof(es->val[0]));
1007 	if (es->val == NULL) {
1008 	    ret = ENOMEM;
1009 	    goto out;
1010 	}
1011 	es->len = 1;
1012 	ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
1013 	if (ret) {
1014 	    free_OCSPRequest(&req);
1015 	    return ret;
1016 	}
1017 
1018 	es->val[0].extnValue.data = malloc(10);
1019 	if (es->val[0].extnValue.data == NULL) {
1020 	    ret = ENOMEM;
1021 	    goto out;
1022 	}
1023 	es->val[0].extnValue.length = 10;
1024 
1025 	ret = RAND_bytes(es->val[0].extnValue.data,
1026 			 es->val[0].extnValue.length);
1027 	if (ret != 1) {
1028 	    ret = HX509_CRYPTO_INTERNAL_ERROR;
1029 	    goto out;
1030 	}
1031 	ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1032 	if (ret) {
1033 	    ret = ENOMEM;
1034 	    goto out;
1035 	}
1036     }
1037 
1038     ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1039 		       &req, &size, ret);
1040     free_OCSPRequest(&req);
1041     if (ret)
1042 	goto out;
1043     if (size != request->length)
1044 	_hx509_abort("internal ASN.1 encoder error");
1045 
1046     return 0;
1047 
1048 out:
1049     free_OCSPRequest(&req);
1050     return ret;
1051 }
1052 
1053 static char *
1054 printable_time(time_t t)
1055 {
1056     static char s[128];
1057     char *p;
1058     if ((p = ctime(&t)) == NULL)
1059        strlcpy(s, "?", sizeof(s));
1060     else {
1061        strlcpy(s, p + 4, sizeof(s));
1062        s[20] = 0;
1063     }
1064     return s;
1065 }
1066 
1067 /**
1068  * Print the OCSP reply stored in a file.
1069  *
1070  * @param context a hx509 context
1071  * @param path path to a file with a OCSP reply
1072  * @param out the out FILE descriptor to print the reply on
1073  *
1074  * @return An hx509 error code, see hx509_get_error_string().
1075  *
1076  * @ingroup hx509_revoke
1077  */
1078 
1079 int
1080 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1081 {
1082     struct revoke_ocsp ocsp;
1083     int ret;
1084     size_t i;
1085 
1086     if (out == NULL)
1087 	out = stdout;
1088 
1089     memset(&ocsp, 0, sizeof(ocsp));
1090 
1091     ocsp.path = strdup(path);
1092     if (ocsp.path == NULL)
1093 	return ENOMEM;
1094 
1095     ret = load_ocsp(context, &ocsp);
1096     if (ret) {
1097 	free_ocsp(&ocsp);
1098 	return ret;
1099     }
1100 
1101     fprintf(out, "signer: ");
1102 
1103     switch(ocsp.ocsp.tbsResponseData.responderID.element) {
1104     case choice_OCSPResponderID_byName: {
1105 	hx509_name n;
1106 	char *s;
1107 	_hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
1108 	hx509_name_to_string(n, &s);
1109 	hx509_name_free(&n);
1110 	fprintf(out, " byName: %s\n", s);
1111 	free(s);
1112 	break;
1113     }
1114     case choice_OCSPResponderID_byKey: {
1115 	char *s;
1116 	hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
1117 		   ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
1118 		   &s);
1119 	fprintf(out, " byKey: %s\n", s);
1120 	free(s);
1121 	break;
1122     }
1123     default:
1124 	_hx509_abort("choice_OCSPResponderID unknown");
1125 	break;
1126     }
1127 
1128     fprintf(out, "producedAt: %s\n",
1129 	    printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1130 
1131     fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1132 
1133     for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1134 	const char *status;
1135 	switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1136 	case choice_OCSPCertStatus_good:
1137 	    status = "good";
1138 	    break;
1139 	case choice_OCSPCertStatus_revoked:
1140 	    status = "revoked";
1141 	    break;
1142 	case choice_OCSPCertStatus_unknown:
1143 	    status = "unknown";
1144 	    break;
1145 	default:
1146 	    status = "element unknown";
1147 	}
1148 
1149 	fprintf(out, "\t%zu. status: %s\n", i, status);
1150 
1151 	fprintf(out, "\tthisUpdate: %s\n",
1152 		printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1153 	if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
1154 	    fprintf(out, "\tproducedAt: %s\n",
1155 		    printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1156 
1157     }
1158 
1159     fprintf(out, "appended certs:\n");
1160     if (ocsp.certs)
1161 	ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);
1162 
1163     free_ocsp(&ocsp);
1164     return ret;
1165 }
1166 
1167 /**
1168  * Verify that the certificate is part of the OCSP reply and it's not
1169  * expired. Doesn't verify signature the OCSP reply or it's done by a
1170  * authorized sender, that is assumed to be already done.
1171  *
1172  * @param context a hx509 context
1173  * @param now the time right now, if 0, use the current time.
1174  * @param cert the certificate to verify
1175  * @param flags flags control the behavior
1176  * @param data pointer to the encode ocsp reply
1177  * @param length the length of the encode ocsp reply
1178  * @param expiration return the time the OCSP will expire and need to
1179  * be rechecked.
1180  *
1181  * @return An hx509 error code, see hx509_get_error_string().
1182  *
1183  * @ingroup hx509_verify
1184  */
1185 
1186 int
1187 hx509_ocsp_verify(hx509_context context,
1188 		  time_t now,
1189 		  hx509_cert cert,
1190 		  int flags,
1191 		  const void *data, size_t length,
1192 		  time_t *expiration)
1193 {
1194     const Certificate *c = _hx509_get_cert(cert);
1195     OCSPBasicOCSPResponse basic;
1196     int ret;
1197     size_t i;
1198 
1199     if (now == 0)
1200 	now = time(NULL);
1201 
1202     *expiration = 0;
1203 
1204     ret = parse_ocsp_basic(data, length, &basic);
1205     if (ret) {
1206 	hx509_set_error_string(context, 0, ret,
1207 			       "Failed to parse OCSP response");
1208 	return ret;
1209     }
1210 
1211     for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1212 
1213 	ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1214 			       &c->tbsCertificate.serialNumber);
1215 	if (ret != 0)
1216 	    continue;
1217 
1218 	/* verify issuer hashes hash */
1219 	ret = _hx509_verify_signature(context,
1220 				      NULL,
1221 				      &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1222 				      &c->tbsCertificate.issuer._save,
1223 				      &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1224 	if (ret != 0)
1225 	    continue;
1226 
1227 	switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1228 	case choice_OCSPCertStatus_good:
1229 	    break;
1230 	case choice_OCSPCertStatus_revoked:
1231 	case choice_OCSPCertStatus_unknown:
1232 	    continue;
1233 	}
1234 
1235 	/* don't allow the update to be in the future */
1236 	if (basic.tbsResponseData.responses.val[i].thisUpdate >
1237 	    now + context->ocsp_time_diff)
1238 	    continue;
1239 
1240 	/* don't allow the next update to be in the past */
1241 	if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1242 	    if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1243 		continue;
1244 	    *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1245 	} else
1246 	    *expiration = now;
1247 
1248 	free_OCSPBasicOCSPResponse(&basic);
1249 	return 0;
1250     }
1251 
1252     free_OCSPBasicOCSPResponse(&basic);
1253 
1254     {
1255 	hx509_name name;
1256 	char *subject;
1257 
1258 	ret = hx509_cert_get_subject(cert, &name);
1259 	if (ret) {
1260 	    hx509_clear_error_string(context);
1261 	    goto out;
1262 	}
1263 	ret = hx509_name_to_string(name, &subject);
1264 	hx509_name_free(&name);
1265 	if (ret) {
1266 	    hx509_clear_error_string(context);
1267 	    goto out;
1268 	}
1269 	hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1270 			       "Certificate %s not in OCSP response "
1271 			       "or not good",
1272 			       subject);
1273 	free(subject);
1274     }
1275 out:
1276     return HX509_CERT_NOT_IN_OCSP;
1277 }
1278 
1279 struct hx509_crl {
1280     hx509_certs revoked;
1281     time_t expire;
1282 };
1283 
1284 /**
1285  * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1286  *
1287  * @param context a hx509 context.
1288  * @param crl return pointer to a newly allocated CRL context.
1289  *
1290  * @return An hx509 error code, see hx509_get_error_string().
1291  *
1292  * @ingroup hx509_verify
1293  */
1294 
1295 int
1296 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1297 {
1298     int ret;
1299 
1300     *crl = calloc(1, sizeof(**crl));
1301     if (*crl == NULL) {
1302 	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1303 	return ENOMEM;
1304     }
1305 
1306     ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1307     if (ret) {
1308 	free(*crl);
1309 	*crl = NULL;
1310 	return ret;
1311     }
1312     (*crl)->expire = 0;
1313     return ret;
1314 }
1315 
1316 /**
1317  * Add revoked certificate to an CRL context.
1318  *
1319  * @param context a hx509 context.
1320  * @param crl the CRL to add the revoked certificate to.
1321  * @param certs keyset of certificate to revoke.
1322  *
1323  * @return An hx509 error code, see hx509_get_error_string().
1324  *
1325  * @ingroup hx509_verify
1326  */
1327 
1328 int
1329 hx509_crl_add_revoked_certs(hx509_context context,
1330 			    hx509_crl crl,
1331 			    hx509_certs certs)
1332 {
1333     return hx509_certs_merge(context, crl->revoked, certs);
1334 }
1335 
1336 /**
1337  * Set the lifetime of a CRL context.
1338  *
1339  * @param context a hx509 context.
1340  * @param crl a CRL context
1341  * @param delta delta time the certificate is valid, library adds the
1342  * current time to this.
1343  *
1344  * @return An hx509 error code, see hx509_get_error_string().
1345  *
1346  * @ingroup hx509_verify
1347  */
1348 
1349 int
1350 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1351 {
1352     crl->expire = time(NULL) + delta;
1353     return 0;
1354 }
1355 
1356 /**
1357  * Free a CRL context.
1358  *
1359  * @param context a hx509 context.
1360  * @param crl a CRL context to free.
1361  *
1362  * @ingroup hx509_verify
1363  */
1364 
1365 void
1366 hx509_crl_free(hx509_context context, hx509_crl *crl)
1367 {
1368     if (*crl == NULL)
1369 	return;
1370     hx509_certs_free(&(*crl)->revoked);
1371     memset(*crl, 0, sizeof(**crl));
1372     free(*crl);
1373     *crl = NULL;
1374 }
1375 
1376 static int
1377 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1378 {
1379     TBSCRLCertList *c = ctx;
1380     unsigned int num;
1381     void *ptr;
1382     int ret;
1383 
1384     num = c->revokedCertificates->len;
1385     ptr = realloc(c->revokedCertificates->val,
1386 		  (num + 1) * sizeof(c->revokedCertificates->val[0]));
1387     if (ptr == NULL) {
1388 	hx509_clear_error_string(context);
1389 	return ENOMEM;
1390     }
1391     c->revokedCertificates->val = ptr;
1392 
1393     ret = hx509_cert_get_serialnumber(cert,
1394 				      &c->revokedCertificates->val[num].userCertificate);
1395     if (ret) {
1396 	hx509_clear_error_string(context);
1397 	return ret;
1398     }
1399     c->revokedCertificates->val[num].revocationDate.element =
1400 	choice_Time_generalTime;
1401     c->revokedCertificates->val[num].revocationDate.u.generalTime =
1402 	time(NULL) - 3600 * 24;
1403     c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1404 
1405     c->revokedCertificates->len++;
1406 
1407     return 0;
1408 }
1409 
1410 /**
1411  * Sign a CRL and return an encode certificate.
1412  *
1413  * @param context a hx509 context.
1414  * @param signer certificate to sign the CRL with
1415  * @param crl the CRL to sign
1416  * @param os return the signed and encoded CRL, free with
1417  * free_heim_octet_string()
1418  *
1419  * @return An hx509 error code, see hx509_get_error_string().
1420  *
1421  * @ingroup hx509_verify
1422  */
1423 
1424 int
1425 hx509_crl_sign(hx509_context context,
1426 	       hx509_cert signer,
1427 	       hx509_crl crl,
1428 	       heim_octet_string *os)
1429 {
1430     const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1431     CRLCertificateList c;
1432     size_t size;
1433     int ret;
1434     hx509_private_key signerkey;
1435 
1436     memset(&c, 0, sizeof(c));
1437 
1438     signerkey = _hx509_cert_private_key(signer);
1439     if (signerkey == NULL) {
1440 	ret = HX509_PRIVATE_KEY_MISSING;
1441 	hx509_set_error_string(context, 0, ret,
1442 			       "Private key missing for CRL signing");
1443 	return ret;
1444     }
1445 
1446     c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1447     if (c.tbsCertList.version == NULL) {
1448 	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1449 	return ENOMEM;
1450     }
1451 
1452     *c.tbsCertList.version = 1;
1453 
1454     ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1455     if (ret) {
1456 	hx509_clear_error_string(context);
1457 	goto out;
1458     }
1459 
1460     ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1461 		    &c.tbsCertList.issuer);
1462     if (ret) {
1463 	hx509_clear_error_string(context);
1464 	goto out;
1465     }
1466 
1467     c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1468     c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1469 
1470     c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1471     if (c.tbsCertList.nextUpdate == NULL) {
1472 	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1473 	ret = ENOMEM;
1474 	goto out;
1475     }
1476 
1477     {
1478 	time_t next = crl->expire;
1479 	if (next == 0)
1480 	    next = time(NULL) + 24 * 3600 * 365;
1481 
1482 	c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1483 	c.tbsCertList.nextUpdate->u.generalTime = next;
1484     }
1485 
1486     c.tbsCertList.revokedCertificates =
1487 	calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1488     if (c.tbsCertList.revokedCertificates == NULL) {
1489 	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1490 	ret = ENOMEM;
1491 	goto out;
1492     }
1493     c.tbsCertList.crlExtensions = NULL;
1494 
1495     ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
1496     if (ret)
1497 	goto out;
1498 
1499     /* if not revoked certs, remove OPTIONAL entry */
1500     if (c.tbsCertList.revokedCertificates->len == 0) {
1501 	free(c.tbsCertList.revokedCertificates);
1502 	c.tbsCertList.revokedCertificates = NULL;
1503     }
1504 
1505     ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1506 		       &c.tbsCertList, &size, ret);
1507     if (ret) {
1508 	hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1509 	goto out;
1510     }
1511     if (size != os->length)
1512 	_hx509_abort("internal ASN.1 encoder error");
1513 
1514 
1515     ret = _hx509_create_signature_bitstring(context,
1516 					    signerkey,
1517 					    sigalg,
1518 					    os,
1519 					    &c.signatureAlgorithm,
1520 					    &c.signatureValue);
1521     free(os->data);
1522     if (ret) {
1523 	hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
1524 	goto out;
1525     }
1526 
1527     ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1528 		       &c, &size, ret);
1529     if (ret) {
1530 	hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1531 	goto out;
1532     }
1533     if (size != os->length)
1534 	_hx509_abort("internal ASN.1 encoder error");
1535 
1536     free_CRLCertificateList(&c);
1537 
1538     return 0;
1539 
1540 out:
1541     free_CRLCertificateList(&c);
1542     return ret;
1543 }
1544