1/*	$NetBSD: revoke.c,v 1.2 2011/04/14 18:22:35 elric Exp $	*/
2
3/*
4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36/**
37 * @page page_revoke Revocation methods
38 *
39 * There are two revocation method for PKIX/X.509: CRL and OCSP.
40 * Revocation is needed if the private key is lost and
41 * stolen. Depending on how picky you are, you might want to make
42 * revocation for destroyed private keys too (smartcard broken), but
43 * that should not be a problem.
44 *
45 * CRL is a list of certifiates that have expired.
46 *
47 * OCSP is an online checking method where the requestor sends a list
48 * of certificates to the OCSP server to return a signed reply if they
49 * are valid or not. Some services sends a OCSP reply as part of the
50 * hand-shake to make the revoktion decision simpler/faster for the
51 * client.
52 */
53
54#include "hx_locl.h"
55
56struct revoke_crl {
57    char *path;
58    time_t last_modfied;
59    CRLCertificateList crl;
60    int verified;
61    int failed_verify;
62};
63
64struct revoke_ocsp {
65    char *path;
66    time_t last_modfied;
67    OCSPBasicOCSPResponse ocsp;
68    hx509_certs certs;
69    hx509_cert signer;
70};
71
72
73struct hx509_revoke_ctx_data {
74    unsigned int ref;
75    struct {
76	struct revoke_crl *val;
77	size_t len;
78    } crls;
79    struct {
80	struct revoke_ocsp *val;
81	size_t len;
82    } ocsps;
83};
84
85/**
86 * Allocate a revokation context. Free with hx509_revoke_free().
87 *
88 * @param context A hx509 context.
89 * @param ctx returns a newly allocated revokation context.
90 *
91 * @return An hx509 error code, see hx509_get_error_string().
92 *
93 * @ingroup hx509_revoke
94 */
95
96int
97hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
98{
99    *ctx = calloc(1, sizeof(**ctx));
100    if (*ctx == NULL)
101	return ENOMEM;
102
103    (*ctx)->ref = 1;
104    (*ctx)->crls.len = 0;
105    (*ctx)->crls.val = NULL;
106    (*ctx)->ocsps.len = 0;
107    (*ctx)->ocsps.val = NULL;
108
109    return 0;
110}
111
112hx509_revoke_ctx
113_hx509_revoke_ref(hx509_revoke_ctx ctx)
114{
115    if (ctx == NULL)
116	return NULL;
117    if (ctx->ref == 0)
118	_hx509_abort("revoke ctx refcount == 0 on ref");
119    ctx->ref++;
120    if (ctx->ref == UINT_MAX)
121	_hx509_abort("revoke ctx refcount == UINT_MAX on ref");
122    return ctx;
123}
124
125static void
126free_ocsp(struct revoke_ocsp *ocsp)
127{
128    free(ocsp->path);
129    free_OCSPBasicOCSPResponse(&ocsp->ocsp);
130    hx509_certs_free(&ocsp->certs);
131    hx509_cert_free(ocsp->signer);
132}
133
134/**
135 * Free a hx509 revokation context.
136 *
137 * @param ctx context to be freed
138 *
139 * @ingroup hx509_revoke
140 */
141
142void
143hx509_revoke_free(hx509_revoke_ctx *ctx)
144{
145    size_t i ;
146
147    if (ctx == NULL || *ctx == NULL)
148	return;
149
150    if ((*ctx)->ref == 0)
151	_hx509_abort("revoke ctx refcount == 0 on free");
152    if (--(*ctx)->ref > 0)
153	return;
154
155    for (i = 0; i < (*ctx)->crls.len; i++) {
156	free((*ctx)->crls.val[i].path);
157	free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
158    }
159
160    for (i = 0; i < (*ctx)->ocsps.len; i++)
161	free_ocsp(&(*ctx)->ocsps.val[i]);
162    free((*ctx)->ocsps.val);
163
164    free((*ctx)->crls.val);
165
166    memset(*ctx, 0, sizeof(**ctx));
167    free(*ctx);
168    *ctx = NULL;
169}
170
171static int
172verify_ocsp(hx509_context context,
173	    struct revoke_ocsp *ocsp,
174	    time_t time_now,
175	    hx509_certs certs,
176	    hx509_cert parent)
177{
178    hx509_cert signer = NULL;
179    hx509_query q;
180    int ret;
181
182    _hx509_query_clear(&q);
183
184    /*
185     * Need to match on issuer too in case there are two CA that have
186     * issued the same name to a certificate. One example of this is
187     * the www.openvalidation.org test's ocsp validator.
188     */
189
190    q.match = HX509_QUERY_MATCH_ISSUER_NAME;
191    q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
192
193    switch(ocsp->ocsp.tbsResponseData.responderID.element) {
194    case choice_OCSPResponderID_byName:
195	q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
196	q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
197	break;
198    case choice_OCSPResponderID_byKey:
199	q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
200	q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
201	break;
202    }
203
204    ret = hx509_certs_find(context, certs, &q, &signer);
205    if (ret && ocsp->certs)
206	ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
207    if (ret)
208	goto out;
209
210    /*
211     * If signer certificate isn't the CA certificate, lets check the
212     * it is the CA that signed the signer certificate and the OCSP EKU
213     * is set.
214     */
215    if (hx509_cert_cmp(signer, parent) != 0) {
216	Certificate *p = _hx509_get_cert(parent);
217	Certificate *s = _hx509_get_cert(signer);
218
219	ret = _hx509_cert_is_parent_cmp(s, p, 0);
220	if (ret != 0) {
221	    ret = HX509_PARENT_NOT_CA;
222	    hx509_set_error_string(context, 0, ret, "Revoke OCSP signer is "
223				   "doesn't have CA as signer certificate");
224	    goto out;
225	}
226
227	ret = _hx509_verify_signature_bitstring(context,
228						parent,
229						&s->signatureAlgorithm,
230						&s->tbsCertificate._save,
231						&s->signatureValue);
232	if (ret) {
233	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
234				   "OCSP signer signature invalid");
235	    goto out;
236	}
237
238	ret = hx509_cert_check_eku(context, signer,
239				   &asn1_oid_id_pkix_kp_OCSPSigning, 0);
240	if (ret)
241	    goto out;
242    }
243
244    ret = _hx509_verify_signature_bitstring(context,
245					    signer,
246					    &ocsp->ocsp.signatureAlgorithm,
247					    &ocsp->ocsp.tbsResponseData._save,
248					    &ocsp->ocsp.signature);
249    if (ret) {
250	hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
251			       "OCSP signature invalid");
252	goto out;
253    }
254
255    ocsp->signer = signer;
256    signer = NULL;
257out:
258    if (signer)
259	hx509_cert_free(signer);
260
261    return ret;
262}
263
264/*
265 *
266 */
267
268static int
269parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
270{
271    OCSPResponse resp;
272    size_t size;
273    int ret;
274
275    memset(basic, 0, sizeof(*basic));
276
277    ret = decode_OCSPResponse(data, length, &resp, &size);
278    if (ret)
279	return ret;
280    if (length != size) {
281	free_OCSPResponse(&resp);
282	return ASN1_EXTRA_DATA;
283    }
284
285    switch (resp.responseStatus) {
286    case successful:
287	break;
288    default:
289	free_OCSPResponse(&resp);
290	return HX509_REVOKE_WRONG_DATA;
291    }
292
293    if (resp.responseBytes == NULL) {
294	free_OCSPResponse(&resp);
295	return EINVAL;
296    }
297
298    ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
299			   &asn1_oid_id_pkix_ocsp_basic);
300    if (ret != 0) {
301	free_OCSPResponse(&resp);
302	return HX509_REVOKE_WRONG_DATA;
303    }
304
305    ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
306				       resp.responseBytes->response.length,
307				       basic,
308				       &size);
309    if (ret) {
310	free_OCSPResponse(&resp);
311	return ret;
312    }
313    if (size != resp.responseBytes->response.length) {
314	free_OCSPResponse(&resp);
315	free_OCSPBasicOCSPResponse(basic);
316	return ASN1_EXTRA_DATA;
317    }
318    free_OCSPResponse(&resp);
319
320    return 0;
321}
322
323/*
324 *
325 */
326
327static int
328load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
329{
330    OCSPBasicOCSPResponse basic;
331    hx509_certs certs = NULL;
332    size_t length;
333    struct stat sb;
334    void *data;
335    int ret;
336
337    ret = rk_undumpdata(ocsp->path, &data, &length);
338    if (ret)
339	return ret;
340
341    ret = stat(ocsp->path, &sb);
342    if (ret)
343	return errno;
344
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;
351    }
352
353    if (basic.certs) {
354	int i;
355
356	ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
357			       NULL, &certs);
358	if (ret) {
359	    free_OCSPBasicOCSPResponse(&basic);
360	    return ret;
361	}
362
363	for (i = 0; i < basic.certs->len; i++) {
364	    hx509_cert c;
365
366	    ret = hx509_cert_init(context, &basic.certs->val[i], &c);
367	    if (ret)
368		continue;
369
370	    ret = hx509_certs_add(context, certs, c);
371	    hx509_cert_free(c);
372	    if (ret)
373		continue;
374	}
375    }
376
377    ocsp->last_modfied = sb.st_mtime;
378
379    free_OCSPBasicOCSPResponse(&ocsp->ocsp);
380    hx509_certs_free(&ocsp->certs);
381    hx509_cert_free(ocsp->signer);
382
383    ocsp->ocsp = basic;
384    ocsp->certs = certs;
385    ocsp->signer = NULL;
386
387    return 0;
388}
389
390/**
391 * Add a OCSP file to the revokation context.
392 *
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.
396 *
397 * @return An hx509 error code, see hx509_get_error_string().
398 *
399 * @ingroup hx509_revoke
400 */
401
402int
403hx509_revoke_add_ocsp(hx509_context context,
404		      hx509_revoke_ctx ctx,
405		      const char *path)
406{
407    void *data;
408    int ret;
409    size_t i;
410
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;
415    }
416
417    path += 5;
418
419    for (i = 0; i < ctx->ocsps.len; i++) {
420	if (strcmp(ctx->ocsps.val[0].path, path) == 0)
421	    return 0;
422    }
423
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;
429    }
430
431    ctx->ocsps.val = data;
432
433    memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
434	   sizeof(ctx->ocsps.val[0]));
435
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;
440    }
441
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;
446    }
447    ctx->ocsps.len++;
448
449    return ret;
450}
451
452/*
453 *
454 */
455
456static int
457verify_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)
463{
464    hx509_cert signer;
465    hx509_query q;
466    time_t t;
467    int ret;
468
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;
474    }
475
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;
480    }
481
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;
487    }
488
489    _hx509_query_clear(&q);
490
491    /*
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.
494     */
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;
501
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;
507	}
508    }
509
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;
519    }
520
521    /*
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.
526     */
527    while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
528	hx509_cert crl_parent;
529
530	_hx509_query_clear(&q);
531
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;
535
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;
541	}
542
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 revoke "
554				   "status of CRL signer");
555	    goto out;
556	}
557    }
558
559out:
560    hx509_cert_free(signer);
561
562    return ret;
563}
564
565static int
566load_crl(const char *path, time_t *t, CRLCertificateList *crl)
567{
568    size_t length, size;
569    struct stat sb;
570    void *data;
571    int ret;
572
573    memset(crl, 0, sizeof(*crl));
574
575    ret = rk_undumpdata(path, &data, &length);
576    if (ret)
577	return ret;
578
579    ret = stat(path, &sb);
580    if (ret)
581	return errno;
582
583    *t = sb.st_mtime;
584
585    ret = decode_CRLCertificateList(data, length, crl, &size);
586    rk_xfree(data);
587    if (ret)
588	return ret;
589
590    /* check signature is aligned */
591    if (crl->signatureValue.length & 7) {
592	free_CRLCertificateList(crl);
593	return HX509_CRYPTO_SIG_INVALID_FORMAT;
594    }
595    return 0;
596}
597
598/**
599 * Add a CRL file to the revokation context.
600 *
601 * @param context hx509 context
602 * @param ctx hx509 revokation context
603 * @param path path to file that is going to be added to the context.
604 *
605 * @return An hx509 error code, see hx509_get_error_string().
606 *
607 * @ingroup hx509_revoke
608 */
609
610int
611hx509_revoke_add_crl(hx509_context context,
612		     hx509_revoke_ctx ctx,
613		     const char *path)
614{
615    void *data;
616    size_t i;
617    int ret;
618
619    if (strncmp(path, "FILE:", 5) != 0) {
620	hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
621			       "unsupport type in %s", path);
622	return HX509_UNSUPPORTED_OPERATION;
623    }
624
625
626    path += 5;
627
628    for (i = 0; i < ctx->crls.len; i++) {
629	if (strcmp(ctx->crls.val[0].path, path) == 0)
630	    return 0;
631    }
632
633    data = realloc(ctx->crls.val,
634		   (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
635    if (data == NULL) {
636	hx509_clear_error_string(context);
637	return ENOMEM;
638    }
639    ctx->crls.val = data;
640
641    memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
642
643    ctx->crls.val[ctx->crls.len].path = strdup(path);
644    if (ctx->crls.val[ctx->crls.len].path == NULL) {
645	hx509_clear_error_string(context);
646	return ENOMEM;
647    }
648
649    ret = load_crl(path,
650		   &ctx->crls.val[ctx->crls.len].last_modfied,
651		   &ctx->crls.val[ctx->crls.len].crl);
652    if (ret) {
653	free(ctx->crls.val[ctx->crls.len].path);
654	return ret;
655    }
656
657    ctx->crls.len++;
658
659    return ret;
660}
661
662/**
663 * Check that a certificate is not expired according to a revokation
664 * context. Also need the parent certificte to the check OCSP
665 * parent identifier.
666 *
667 * @param context hx509 context
668 * @param ctx hx509 revokation context
669 * @param certs
670 * @param now
671 * @param cert
672 * @param parent_cert
673 *
674 * @return An hx509 error code, see hx509_get_error_string().
675 *
676 * @ingroup hx509_revoke
677 */
678
679
680int
681hx509_revoke_verify(hx509_context context,
682		    hx509_revoke_ctx ctx,
683		    hx509_certs certs,
684		    time_t now,
685		    hx509_cert cert,
686		    hx509_cert parent_cert)
687{
688    const Certificate *c = _hx509_get_cert(cert);
689    const Certificate *p = _hx509_get_cert(parent_cert);
690    unsigned long i, j, k;
691    int ret;
692
693    hx509_clear_error_string(context);
694
695    for (i = 0; i < ctx->ocsps.len; i++) {
696	struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
697	struct stat sb;
698
699	/* check this ocsp apply to this cert */
700
701	/* check if there is a newer version of the file */
702	ret = stat(ocsp->path, &sb);
703	if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
704	    ret = load_ocsp(context, ocsp);
705	    if (ret)
706		continue;
707	}
708
709	/* verify signature in ocsp if not already done */
710	if (ocsp->signer == NULL) {
711	    ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
712	    if (ret)
713		continue;
714	}
715
716	for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
717	    heim_octet_string os;
718
719	    ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
720				   &c->tbsCertificate.serialNumber);
721	    if (ret != 0)
722		continue;
723
724	    /* verify issuer hashes hash */
725	    ret = _hx509_verify_signature(context,
726					  NULL,
727					  &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
728					  &c->tbsCertificate.issuer._save,
729					  &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
730	    if (ret != 0)
731		continue;
732
733	    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
734	    os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
735
736	    ret = _hx509_verify_signature(context,
737					  NULL,
738					  &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
739					  &os,
740					  &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
741	    if (ret != 0)
742		continue;
743
744	    switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
745	    case choice_OCSPCertStatus_good:
746		break;
747	    case choice_OCSPCertStatus_revoked:
748		hx509_set_error_string(context, 0,
749				       HX509_CERT_REVOKED,
750				       "Certificate revoked by issuer in OCSP");
751		return HX509_CERT_REVOKED;
752	    case choice_OCSPCertStatus_unknown:
753		continue;
754	    }
755
756	    /* don't allow the update to be in the future */
757	    if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
758		now + context->ocsp_time_diff)
759		continue;
760
761	    /* don't allow the next update to be in the past */
762	    if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
763		if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
764		    continue;
765	    } else
766		/* Should force a refetch, but can we ? */;
767
768	    return 0;
769	}
770    }
771
772    for (i = 0; i < ctx->crls.len; i++) {
773	struct revoke_crl *crl = &ctx->crls.val[i];
774	struct stat sb;
775	int diff;
776
777	/* check if cert.issuer == crls.val[i].crl.issuer */
778	ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
779			      &crl->crl.tbsCertList.issuer, &diff);
780	if (ret || diff)
781	    continue;
782
783	ret = stat(crl->path, &sb);
784	if (ret == 0 && crl->last_modfied != sb.st_mtime) {
785	    CRLCertificateList cl;
786
787	    ret = load_crl(crl->path, &crl->last_modfied, &cl);
788	    if (ret == 0) {
789		free_CRLCertificateList(&crl->crl);
790		crl->crl = cl;
791		crl->verified = 0;
792		crl->failed_verify = 0;
793	    }
794	}
795	if (crl->failed_verify)
796	    continue;
797
798	/* verify signature in crl if not already done */
799	if (crl->verified == 0) {
800	    ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
801	    if (ret) {
802		crl->failed_verify = 1;
803		continue;
804	    }
805	    crl->verified = 1;
806	}
807
808	if (crl->crl.tbsCertList.crlExtensions) {
809	    for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
810		if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
811		    hx509_set_error_string(context, 0,
812					   HX509_CRL_UNKNOWN_EXTENSION,
813					   "Unknown CRL extension");
814		    return HX509_CRL_UNKNOWN_EXTENSION;
815		}
816	    }
817	}
818
819	if (crl->crl.tbsCertList.revokedCertificates == NULL)
820	    return 0;
821
822	/* check if cert is in crl */
823	for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
824	    time_t t;
825
826	    ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
827				       &c->tbsCertificate.serialNumber);
828	    if (ret != 0)
829		continue;
830
831	    t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
832	    if (t > now)
833		continue;
834
835	    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
836		for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
837		    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
838			return HX509_CRL_UNKNOWN_EXTENSION;
839
840	    hx509_set_error_string(context, 0,
841				   HX509_CERT_REVOKED,
842				   "Certificate revoked by issuer in CRL");
843	    return HX509_CERT_REVOKED;
844	}
845
846	return 0;
847    }
848
849
850    if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
851	return 0;
852    hx509_set_error_string(context, HX509_ERROR_APPEND,
853			   HX509_REVOKE_STATUS_MISSING,
854			   "No revoke status found for "
855			   "certificates");
856    return HX509_REVOKE_STATUS_MISSING;
857}
858
859struct ocsp_add_ctx {
860    OCSPTBSRequest *req;
861    hx509_certs certs;
862    const AlgorithmIdentifier *digest;
863    hx509_cert parent;
864};
865
866static int
867add_to_req(hx509_context context, void *ptr, hx509_cert cert)
868{
869    struct ocsp_add_ctx *ctx = ptr;
870    OCSPInnerRequest *one;
871    hx509_cert parent = NULL;
872    Certificate *p, *c = _hx509_get_cert(cert);
873    heim_octet_string os;
874    int ret;
875    hx509_query q;
876    void *d;
877
878    d = realloc(ctx->req->requestList.val,
879		sizeof(ctx->req->requestList.val[0]) *
880		(ctx->req->requestList.len + 1));
881    if (d == NULL)
882	return ENOMEM;
883    ctx->req->requestList.val = d;
884
885    one = &ctx->req->requestList.val[ctx->req->requestList.len];
886    memset(one, 0, sizeof(*one));
887
888    _hx509_query_clear(&q);
889
890    q.match |= HX509_QUERY_FIND_ISSUER_CERT;
891    q.subject = c;
892
893    ret = hx509_certs_find(context, ctx->certs, &q, &parent);
894    if (ret)
895	goto out;
896
897    if (ctx->parent) {
898	if (hx509_cert_cmp(ctx->parent, parent) != 0) {
899	    ret = HX509_REVOKE_NOT_SAME_PARENT;
900	    hx509_set_error_string(context, 0, ret,
901				   "Not same parent certifate as "
902				   "last certificate in request");
903	    goto out;
904	}
905    } else
906	ctx->parent = hx509_cert_ref(parent);
907
908    p = _hx509_get_cert(parent);
909
910    ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
911    if (ret)
912	goto out;
913
914    ret = _hx509_create_signature(context,
915				  NULL,
916				  &one->reqCert.hashAlgorithm,
917				  &c->tbsCertificate.issuer._save,
918				  NULL,
919				  &one->reqCert.issuerNameHash);
920    if (ret)
921	goto out;
922
923    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
924    os.length =
925	p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
926
927    ret = _hx509_create_signature(context,
928				  NULL,
929				  &one->reqCert.hashAlgorithm,
930				  &os,
931				  NULL,
932				  &one->reqCert.issuerKeyHash);
933    if (ret)
934	goto out;
935
936    ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
937				       &one->reqCert.serialNumber);
938    if (ret)
939	goto out;
940
941    ctx->req->requestList.len++;
942out:
943    hx509_cert_free(parent);
944    if (ret) {
945	free_OCSPInnerRequest(one);
946	memset(one, 0, sizeof(*one));
947    }
948
949    return ret;
950}
951
952/**
953 * Create an OCSP request for a set of certificates.
954 *
955 * @param context a hx509 context
956 * @param reqcerts list of certificates to request ocsp data for
957 * @param pool certificate pool to use when signing
958 * @param signer certificate to use to sign the request
959 * @param digest the signing algorithm in the request, if NULL use the
960 * default signature algorithm,
961 * @param request the encoded request, free with free_heim_octet_string().
962 * @param nonce nonce in the request, free with free_heim_octet_string().
963 *
964 * @return An hx509 error code, see hx509_get_error_string().
965 *
966 * @ingroup hx509_revoke
967 */
968
969int
970hx509_ocsp_request(hx509_context context,
971		   hx509_certs reqcerts,
972		   hx509_certs pool,
973		   hx509_cert signer,
974		   const AlgorithmIdentifier *digest,
975		   heim_octet_string *request,
976		   heim_octet_string *nonce)
977{
978    OCSPRequest req;
979    size_t size;
980    int ret;
981    struct ocsp_add_ctx ctx;
982    Extensions *es;
983
984    memset(&req, 0, sizeof(req));
985
986    if (digest == NULL)
987	digest = _hx509_crypto_default_digest_alg;
988
989    ctx.req = &req.tbsRequest;
990    ctx.certs = pool;
991    ctx.digest = digest;
992    ctx.parent = NULL;
993
994    ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
995    hx509_cert_free(ctx.parent);
996    if (ret)
997	goto out;
998
999    if (nonce) {
1000	req.tbsRequest.requestExtensions =
1001	    calloc(1, sizeof(*req.tbsRequest.requestExtensions));
1002	if (req.tbsRequest.requestExtensions == NULL) {
1003	    ret = ENOMEM;
1004	    goto out;
1005	}
1006
1007	es = req.tbsRequest.requestExtensions;
1008
1009	es->val = calloc(es->len, sizeof(es->val[0]));
1010	if (es->val == NULL) {
1011	    ret = ENOMEM;
1012	    goto out;
1013	}
1014	es->len = 1;
1015	ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
1016	if (ret) {
1017	    free_OCSPRequest(&req);
1018	    return ret;
1019	}
1020
1021	es->val[0].extnValue.data = malloc(10);
1022	if (es->val[0].extnValue.data == NULL) {
1023	    ret = ENOMEM;
1024	    goto out;
1025	}
1026	es->val[0].extnValue.length = 10;
1027
1028	ret = RAND_bytes(es->val[0].extnValue.data,
1029			 es->val[0].extnValue.length);
1030	if (ret != 1) {
1031	    ret = HX509_CRYPTO_INTERNAL_ERROR;
1032	    goto out;
1033	}
1034	ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1035	if (ret) {
1036	    ret = ENOMEM;
1037	    goto out;
1038	}
1039    }
1040
1041    ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1042		       &req, &size, ret);
1043    free_OCSPRequest(&req);
1044    if (ret)
1045	goto out;
1046    if (size != request->length)
1047	_hx509_abort("internal ASN.1 encoder error");
1048
1049    return 0;
1050
1051out:
1052    free_OCSPRequest(&req);
1053    return ret;
1054}
1055
1056static char *
1057printable_time(time_t t)
1058{
1059    static char s[128];
1060    char *p;
1061    if ((p = ctime(&t)) == NULL)
1062       strlcpy(s, "?", sizeof(s));
1063    else {
1064       strlcpy(s, p + 4, sizeof(s));
1065       s[20] = 0;
1066    }
1067    return s;
1068}
1069
1070/**
1071 * Print the OCSP reply stored in a file.
1072 *
1073 * @param context a hx509 context
1074 * @param path path to a file with a OCSP reply
1075 * @param out the out FILE descriptor to print the reply on
1076 *
1077 * @return An hx509 error code, see hx509_get_error_string().
1078 *
1079 * @ingroup hx509_revoke
1080 */
1081
1082int
1083hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1084{
1085    struct revoke_ocsp ocsp;
1086    int ret, i;
1087
1088    if (out == NULL)
1089	out = stdout;
1090
1091    memset(&ocsp, 0, sizeof(ocsp));
1092
1093    ocsp.path = strdup(path);
1094    if (ocsp.path == NULL)
1095	return ENOMEM;
1096
1097    ret = load_ocsp(context, &ocsp);
1098    if (ret) {
1099	free_ocsp(&ocsp);
1100	return ret;
1101    }
1102
1103    fprintf(out, "signer: ");
1104
1105    switch(ocsp.ocsp.tbsResponseData.responderID.element) {
1106    case choice_OCSPResponderID_byName: {
1107	hx509_name n;
1108	char *s;
1109	_hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
1110	hx509_name_to_string(n, &s);
1111	hx509_name_free(&n);
1112	fprintf(out, " byName: %s\n", s);
1113	free(s);
1114	break;
1115    }
1116    case choice_OCSPResponderID_byKey: {
1117	char *s;
1118	hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
1119		   ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
1120		   &s);
1121	fprintf(out, " byKey: %s\n", s);
1122	free(s);
1123	break;
1124    }
1125    default:
1126	_hx509_abort("choice_OCSPResponderID unknown");
1127	break;
1128    }
1129
1130    fprintf(out, "producedAt: %s\n",
1131	    printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1132
1133    fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1134
1135    for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1136	const char *status;
1137	switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1138	case choice_OCSPCertStatus_good:
1139	    status = "good";
1140	    break;
1141	case choice_OCSPCertStatus_revoked:
1142	    status = "revoked";
1143	    break;
1144	case choice_OCSPCertStatus_unknown:
1145	    status = "unknown";
1146	    break;
1147	default:
1148	    status = "element unknown";
1149	}
1150
1151	fprintf(out, "\t%d. status: %s\n", i, status);
1152
1153	fprintf(out, "\tthisUpdate: %s\n",
1154		printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1155	if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
1156	    fprintf(out, "\tproducedAt: %s\n",
1157		    printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1158
1159    }
1160
1161    fprintf(out, "appended certs:\n");
1162    if (ocsp.certs)
1163	ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);
1164
1165    free_ocsp(&ocsp);
1166    return ret;
1167}
1168
1169/**
1170 * Verify that the certificate is part of the OCSP reply and it's not
1171 * expired. Doesn't verify signature the OCSP reply or it's done by a
1172 * authorized sender, that is assumed to be already done.
1173 *
1174 * @param context a hx509 context
1175 * @param now the time right now, if 0, use the current time.
1176 * @param cert the certificate to verify
1177 * @param flags flags control the behavior
1178 * @param data pointer to the encode ocsp reply
1179 * @param length the length of the encode ocsp reply
1180 * @param expiration return the time the OCSP will expire and need to
1181 * be rechecked.
1182 *
1183 * @return An hx509 error code, see hx509_get_error_string().
1184 *
1185 * @ingroup hx509_verify
1186 */
1187
1188int
1189hx509_ocsp_verify(hx509_context context,
1190		  time_t now,
1191		  hx509_cert cert,
1192		  int flags,
1193		  const void *data, size_t length,
1194		  time_t *expiration)
1195{
1196    const Certificate *c = _hx509_get_cert(cert);
1197    OCSPBasicOCSPResponse basic;
1198    int ret, i;
1199
1200    if (now == 0)
1201	now = time(NULL);
1202
1203    *expiration = 0;
1204
1205    ret = parse_ocsp_basic(data, length, &basic);
1206    if (ret) {
1207	hx509_set_error_string(context, 0, ret,
1208			       "Failed to parse OCSP response");
1209	return ret;
1210    }
1211
1212    for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1213
1214	ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1215			       &c->tbsCertificate.serialNumber);
1216	if (ret != 0)
1217	    continue;
1218
1219	/* verify issuer hashes hash */
1220	ret = _hx509_verify_signature(context,
1221				      NULL,
1222				      &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1223				      &c->tbsCertificate.issuer._save,
1224				      &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1225	if (ret != 0)
1226	    continue;
1227
1228	switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1229	case choice_OCSPCertStatus_good:
1230	    break;
1231	case choice_OCSPCertStatus_revoked:
1232	case choice_OCSPCertStatus_unknown:
1233	    continue;
1234	}
1235
1236	/* don't allow the update to be in the future */
1237	if (basic.tbsResponseData.responses.val[i].thisUpdate >
1238	    now + context->ocsp_time_diff)
1239	    continue;
1240
1241	/* don't allow the next update to be in the past */
1242	if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1243	    if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1244		continue;
1245	    *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1246	} else
1247	    *expiration = now;
1248
1249	free_OCSPBasicOCSPResponse(&basic);
1250	return 0;
1251    }
1252
1253    free_OCSPBasicOCSPResponse(&basic);
1254
1255    {
1256	hx509_name name;
1257	char *subject;
1258
1259	ret = hx509_cert_get_subject(cert, &name);
1260	if (ret) {
1261	    hx509_clear_error_string(context);
1262	    goto out;
1263	}
1264	ret = hx509_name_to_string(name, &subject);
1265	hx509_name_free(&name);
1266	if (ret) {
1267	    hx509_clear_error_string(context);
1268	    goto out;
1269	}
1270	hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1271			       "Certificate %s not in OCSP response "
1272			       "or not good",
1273			       subject);
1274	free(subject);
1275    }
1276out:
1277    return HX509_CERT_NOT_IN_OCSP;
1278}
1279
1280struct hx509_crl {
1281    hx509_certs revoked;
1282    time_t expire;
1283};
1284
1285/**
1286 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1287 *
1288 * @param context a hx509 context.
1289 * @param crl return pointer to a newly allocated CRL context.
1290 *
1291 * @return An hx509 error code, see hx509_get_error_string().
1292 *
1293 * @ingroup hx509_verify
1294 */
1295
1296int
1297hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1298{
1299    int ret;
1300
1301    *crl = calloc(1, sizeof(**crl));
1302    if (*crl == NULL) {
1303	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1304	return ENOMEM;
1305    }
1306
1307    ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1308    if (ret) {
1309	free(*crl);
1310	*crl = NULL;
1311	return ret;
1312    }
1313    (*crl)->expire = 0;
1314    return ret;
1315}
1316
1317/**
1318 * Add revoked certificate to an CRL context.
1319 *
1320 * @param context a hx509 context.
1321 * @param crl the CRL to add the revoked certificate to.
1322 * @param certs keyset of certificate to revoke.
1323 *
1324 * @return An hx509 error code, see hx509_get_error_string().
1325 *
1326 * @ingroup hx509_verify
1327 */
1328
1329int
1330hx509_crl_add_revoked_certs(hx509_context context,
1331			    hx509_crl crl,
1332			    hx509_certs certs)
1333{
1334    return hx509_certs_merge(context, crl->revoked, certs);
1335}
1336
1337/**
1338 * Set the lifetime of a CRL context.
1339 *
1340 * @param context a hx509 context.
1341 * @param crl a CRL context
1342 * @param delta delta time the certificate is valid, library adds the
1343 * current time to this.
1344 *
1345 * @return An hx509 error code, see hx509_get_error_string().
1346 *
1347 * @ingroup hx509_verify
1348 */
1349
1350int
1351hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1352{
1353    crl->expire = time(NULL) + delta;
1354    return 0;
1355}
1356
1357/**
1358 * Free a CRL context.
1359 *
1360 * @param context a hx509 context.
1361 * @param crl a CRL context to free.
1362 *
1363 * @ingroup hx509_verify
1364 */
1365
1366void
1367hx509_crl_free(hx509_context context, hx509_crl *crl)
1368{
1369    if (*crl == NULL)
1370	return;
1371    hx509_certs_free(&(*crl)->revoked);
1372    memset(*crl, 0, sizeof(**crl));
1373    free(*crl);
1374    *crl = NULL;
1375}
1376
1377static int
1378add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1379{
1380    TBSCRLCertList *c = ctx;
1381    unsigned int num;
1382    void *ptr;
1383    int ret;
1384
1385    num = c->revokedCertificates->len;
1386    ptr = realloc(c->revokedCertificates->val,
1387		  (num + 1) * sizeof(c->revokedCertificates->val[0]));
1388    if (ptr == NULL) {
1389	hx509_clear_error_string(context);
1390	return ENOMEM;
1391    }
1392    c->revokedCertificates->val = ptr;
1393
1394    ret = hx509_cert_get_serialnumber(cert,
1395				      &c->revokedCertificates->val[num].userCertificate);
1396    if (ret) {
1397	hx509_clear_error_string(context);
1398	return ret;
1399    }
1400    c->revokedCertificates->val[num].revocationDate.element =
1401	choice_Time_generalTime;
1402    c->revokedCertificates->val[num].revocationDate.u.generalTime =
1403	time(NULL) - 3600 * 24;
1404    c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1405
1406    c->revokedCertificates->len++;
1407
1408    return 0;
1409}
1410
1411/**
1412 * Sign a CRL and return an encode certificate.
1413 *
1414 * @param context a hx509 context.
1415 * @param signer certificate to sign the CRL with
1416 * @param crl the CRL to sign
1417 * @param os return the signed and encoded CRL, free with
1418 * free_heim_octet_string()
1419 *
1420 * @return An hx509 error code, see hx509_get_error_string().
1421 *
1422 * @ingroup hx509_verify
1423 */
1424
1425int
1426hx509_crl_sign(hx509_context context,
1427	       hx509_cert signer,
1428	       hx509_crl crl,
1429	       heim_octet_string *os)
1430{
1431    const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1432    CRLCertificateList c;
1433    size_t size;
1434    int ret;
1435    hx509_private_key signerkey;
1436
1437    memset(&c, 0, sizeof(c));
1438
1439    signerkey = _hx509_cert_private_key(signer);
1440    if (signerkey == NULL) {
1441	ret = HX509_PRIVATE_KEY_MISSING;
1442	hx509_set_error_string(context, 0, ret,
1443			       "Private key missing for CRL signing");
1444	return ret;
1445    }
1446
1447    c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1448    if (c.tbsCertList.version == NULL) {
1449	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1450	return ENOMEM;
1451    }
1452
1453    *c.tbsCertList.version = 1;
1454
1455    ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1456    if (ret) {
1457	hx509_clear_error_string(context);
1458	goto out;
1459    }
1460
1461    ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1462		    &c.tbsCertList.issuer);
1463    if (ret) {
1464	hx509_clear_error_string(context);
1465	goto out;
1466    }
1467
1468    c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1469    c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1470
1471    c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1472    if (c.tbsCertList.nextUpdate == NULL) {
1473	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1474	ret = ENOMEM;
1475	goto out;
1476    }
1477
1478    {
1479	time_t next = crl->expire;
1480	if (next == 0)
1481	    next = time(NULL) + 24 * 3600 * 365;
1482
1483	c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1484	c.tbsCertList.nextUpdate->u.generalTime = next;
1485    }
1486
1487    c.tbsCertList.revokedCertificates =
1488	calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1489    if (c.tbsCertList.revokedCertificates == NULL) {
1490	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1491	ret = ENOMEM;
1492	goto out;
1493    }
1494    c.tbsCertList.crlExtensions = NULL;
1495
1496    ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
1497    if (ret)
1498	goto out;
1499
1500    /* if not revoked certs, remove OPTIONAL entry */
1501    if (c.tbsCertList.revokedCertificates->len == 0) {
1502	free(c.tbsCertList.revokedCertificates);
1503	c.tbsCertList.revokedCertificates = NULL;
1504    }
1505
1506    ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1507		       &c.tbsCertList, &size, ret);
1508    if (ret) {
1509	hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1510	goto out;
1511    }
1512    if (size != os->length)
1513	_hx509_abort("internal ASN.1 encoder error");
1514
1515
1516    ret = _hx509_create_signature_bitstring(context,
1517					    signerkey,
1518					    sigalg,
1519					    os,
1520					    &c.signatureAlgorithm,
1521					    &c.signatureValue);
1522    free(os->data);
1523    if (ret) {
1524	hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
1525	goto out;
1526    }
1527
1528    ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1529		       &c, &size, ret);
1530    if (ret) {
1531	hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1532	goto out;
1533    }
1534    if (size != os->length)
1535	_hx509_abort("internal ASN.1 encoder error");
1536
1537    free_CRLCertificateList(&c);
1538
1539    return 0;
1540
1541out:
1542    free_CRLCertificateList(&c);
1543    return ret;
1544}
1545