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
54struct revoke_crl {
55    char *path;
56    time_t last_modfied;
57    CRLCertificateList crl;
58    int verified;
59    int failed_verify;
60};
61
62struct revoke_ocsp {
63    char *path;
64    time_t last_modfied;
65    OCSPBasicOCSPResponse ocsp;
66    hx509_certs certs;
67    hx509_cert signer;
68};
69
70
71struct hx509_revoke_ctx_data {
72    struct heim_base_uniq base;
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
83static void
84free_ocsp(struct revoke_ocsp *ocsp)
85{
86    free(ocsp->path);
87    free_OCSPBasicOCSPResponse(&ocsp->ocsp);
88    hx509_certs_free(&ocsp->certs);
89    hx509_cert_free(ocsp->signer);
90}
91
92static void
93revoke_free(void *ptr)
94{
95    hx509_revoke_ctx ctx = ptr;
96    size_t i;
97
98    for (i = 0; i < ctx->crls.len; i++) {
99	free(ctx->crls.val[i].path);
100	free_CRLCertificateList(&ctx->crls.val[i].crl);
101    }
102
103    for (i = 0; i < ctx->ocsps.len; i++)
104	free_ocsp(&ctx->ocsps.val[i]);
105    free(ctx->ocsps.val);
106
107    free(ctx->crls.val);
108}
109
110
111/**
112 * Allocate a revokation context. Free with hx509_revoke_free().
113 *
114 * @param context A hx509 context.
115 * @param ctx returns a newly allocated revokation context.
116 *
117 * @return An hx509 error code, see hx509_get_error_string().
118 *
119 * @ingroup hx509_revoke
120 */
121
122int
123hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
124{
125    *ctx = heim_uniq_alloc(sizeof(**ctx), "hx509-revoke", revoke_free);
126    if (*ctx == NULL)
127	return ENOMEM;
128
129    (*ctx)->crls.len = 0;
130    (*ctx)->crls.val = NULL;
131    (*ctx)->ocsps.len = 0;
132    (*ctx)->ocsps.val = NULL;
133
134    return 0;
135}
136
137hx509_revoke_ctx
138_hx509_revoke_ref(hx509_revoke_ctx ctx)
139{
140    return heim_retain(ctx);
141}
142
143/**
144 * Free a hx509 revokation context.
145 *
146 * @param ctx context to be freed
147 *
148 * @ingroup hx509_revoke
149 */
150
151void
152hx509_revoke_free(hx509_revoke_ctx *ctx)
153{
154    if (ctx == NULL)
155	return;
156    heim_release(*ctx);
157    *ctx = NULL;
158}
159
160static int
161verify_ocsp(hx509_context context,
162	    struct revoke_ocsp *ocsp,
163	    time_t time_now,
164	    hx509_certs certs,
165	    hx509_cert parent)
166{
167    hx509_cert signer = NULL;
168    hx509_query q;
169    int ret;
170
171    _hx509_query_clear(&q);
172
173    /*
174     * Need to match on issuer too in case there are two CA that have
175     * issued the same name to a certificate. One example of this is
176     * the www.openvalidation.org test's ocsp validator.
177     */
178
179    q.match = HX509_QUERY_MATCH_ISSUER_NAME;
180    q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
181
182    switch(ocsp->ocsp.tbsResponseData.responderID.element) {
183    case choice_OCSPResponderID_byName:
184	q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
185	q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
186	break;
187    case choice_OCSPResponderID_byKey:
188	q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
189	q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
190	break;
191    case invalid_choice_OCSPResponderID:
192	return HX509_CERT_NOT_FOUND;
193    }
194
195    ret = hx509_certs_find(context, certs, &q, &signer);
196    if (ret && ocsp->certs)
197	ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
198    if (ret)
199	goto out;
200
201    /*
202     * If signer certificate isn't the CA certificate, lets check the
203     * it is the CA that signed the signer certificate and the OCSP EKU
204     * is set.
205     */
206    if (hx509_cert_cmp(signer, parent) != 0) {
207	Certificate *p = _hx509_get_cert(parent);
208	Certificate *s = _hx509_get_cert(signer);
209
210	ret = _hx509_cert_is_parent_cmp(s, p, 0);
211	if (ret != 0) {
212	    ret = HX509_PARENT_NOT_CA;
213	    hx509_set_error_string(context, 0, ret, "Revoke OCSP signer is "
214				   "doesn't have CA as signer certificate");
215	    goto out;
216	}
217
218	ret = _hx509_verify_signature_bitstring(context,
219						parent,
220						&s->signatureAlgorithm,
221						&s->tbsCertificate._save,
222						&s->signatureValue);
223	if (ret) {
224	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
225				   "OCSP signer signature invalid");
226	    goto out;
227	}
228
229	ret = hx509_cert_check_eku(context, signer,
230				   &asn1_oid_id_pkix_kp_OCSPSigning, 0);
231	if (ret)
232	    goto out;
233    }
234
235    ret = _hx509_verify_signature_bitstring(context,
236					    signer,
237					    &ocsp->ocsp.signatureAlgorithm,
238					    &ocsp->ocsp.tbsResponseData._save,
239					    &ocsp->ocsp.signature);
240    if (ret) {
241	hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
242			       "OCSP signature invalid");
243	goto out;
244    }
245
246    ocsp->signer = signer;
247    signer = NULL;
248out:
249    if (signer)
250	hx509_cert_free(signer);
251
252    return ret;
253}
254
255/*
256 *
257 */
258
259static int
260parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
261{
262    OCSPResponse resp;
263    size_t size;
264    int ret;
265
266    memset(basic, 0, sizeof(*basic));
267
268    ret = decode_OCSPResponse(data, length, &resp, &size);
269    if (ret)
270	return ret;
271    if (length != size) {
272	free_OCSPResponse(&resp);
273	return ASN1_EXTRA_DATA;
274    }
275
276    switch (resp.responseStatus) {
277    case successful:
278	break;
279    case internalError:
280    case malformedRequest:
281    case sigRequired:
282    case tryLater:
283    case unauthorized:
284	free_OCSPResponse(&resp);
285	return HX509_REVOKE_WRONG_DATA;
286    }
287
288    if (resp.responseBytes == NULL) {
289	free_OCSPResponse(&resp);
290	return EINVAL;
291    }
292
293    ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
294			   &asn1_oid_id_pkix_ocsp_basic);
295    if (ret != 0) {
296	free_OCSPResponse(&resp);
297	return HX509_REVOKE_WRONG_DATA;
298    }
299
300    ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
301				       resp.responseBytes->response.length,
302				       basic,
303				       &size);
304    if (ret) {
305	free_OCSPResponse(&resp);
306	return ret;
307    }
308    if (size != resp.responseBytes->response.length) {
309	free_OCSPResponse(&resp);
310	free_OCSPBasicOCSPResponse(basic);
311	return ASN1_EXTRA_DATA;
312    }
313    free_OCSPResponse(&resp);
314
315    return 0;
316}
317
318/*
319 *
320 */
321
322static int
323load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
324{
325    OCSPBasicOCSPResponse basic;
326    hx509_certs certs = NULL;
327    size_t length;
328    struct stat sb;
329    void *data;
330    int ret;
331
332    ret = rk_undumpdata(ocsp->path, &data, &length);
333    if (ret)
334	return ret;
335
336    ret = stat(ocsp->path, &sb);
337    if (ret)
338	return errno;
339
340    ret = parse_ocsp_basic(data, length, &basic);
341    rk_xfree(data);
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	size_t 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
397int
398hx509_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
451static int
452verify_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					    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
554out:
555    hx509_cert_free(signer);
556
557    return ret;
558}
559
560static int
561load_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 = rk_undumpdata(path, &data, &length);
571    if (ret)
572	return ret;
573
574    ret = stat(path, &sb);
575    if (ret)
576	return errno;
577
578    *t = sb.st_mtime;
579
580    ret = decode_CRLCertificateList(data, length, crl, &size);
581    rk_xfree(data);
582    if (ret)
583	return ret;
584
585    /* check signature is aligned */
586    if (crl->signatureValue.length & 7) {
587	free_CRLCertificateList(crl);
588	return HX509_CRYPTO_SIG_INVALID_FORMAT;
589    }
590    return 0;
591}
592
593/**
594 * Add a CRL file to the revokation context.
595 *
596 * @param context hx509 context
597 * @param ctx hx509 revokation context
598 * @param path path to file that is going to be added to the context.
599 *
600 * @return An hx509 error code, see hx509_get_error_string().
601 *
602 * @ingroup hx509_revoke
603 */
604
605int
606hx509_revoke_add_crl(hx509_context context,
607		     hx509_revoke_ctx ctx,
608		     const char *path)
609{
610    void *data;
611    size_t i;
612    int ret;
613
614    if (strncmp(path, "FILE:", 5) != 0) {
615	hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
616			       "unsupport type in %s", path);
617	return HX509_UNSUPPORTED_OPERATION;
618    }
619
620
621    path += 5;
622
623    for (i = 0; i < ctx->crls.len; i++) {
624	if (strcmp(ctx->crls.val[0].path, path) == 0)
625	    return 0;
626    }
627
628    data = realloc(ctx->crls.val,
629		   (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
630    if (data == NULL) {
631	hx509_clear_error_string(context);
632	return ENOMEM;
633    }
634    ctx->crls.val = data;
635
636    memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
637
638    ctx->crls.val[ctx->crls.len].path = strdup(path);
639    if (ctx->crls.val[ctx->crls.len].path == NULL) {
640	hx509_clear_error_string(context);
641	return ENOMEM;
642    }
643
644    ret = load_crl(path,
645		   &ctx->crls.val[ctx->crls.len].last_modfied,
646		   &ctx->crls.val[ctx->crls.len].crl);
647    if (ret) {
648	free(ctx->crls.val[ctx->crls.len].path);
649	return ret;
650    }
651
652    ctx->crls.len++;
653
654    return ret;
655}
656
657/**
658 * Check that a certificate is not expired according to a revokation
659 * context. Also need the parent certificte to the check OCSP
660 * parent identifier.
661 *
662 * @param context hx509 context
663 * @param ctx hx509 revokation context
664 * @param certs certifiate pool t use
665 * @param now time now, 0 for current time
666 * @param cert cert to check
667 * @param parent_cert parent certificate
668 *
669 * @return An hx509 error code, see hx509_get_error_string().
670 *
671 * @ingroup hx509_revoke
672 */
673
674
675int
676hx509_revoke_verify(hx509_context context,
677		    hx509_revoke_ctx ctx,
678		    hx509_certs certs,
679		    time_t now,
680		    hx509_cert cert,
681		    hx509_cert parent_cert)
682{
683    const Certificate *c = _hx509_get_cert(cert);
684    const Certificate *p = _hx509_get_cert(parent_cert);
685    unsigned long i, j, k;
686    int ret;
687
688    hx509_clear_error_string(context);
689
690    for (i = 0; i < ctx->ocsps.len; i++) {
691	struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
692	struct stat sb;
693
694	/* check this ocsp apply to this cert */
695
696	/* check if there is a newer version of the file */
697	ret = stat(ocsp->path, &sb);
698	if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
699	    ret = load_ocsp(context, ocsp);
700	    if (ret)
701		continue;
702	}
703
704	/* verify signature in ocsp if not already done */
705	if (ocsp->signer == NULL) {
706	    ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
707	    if (ret)
708		continue;
709	}
710
711	for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
712	    heim_octet_string os;
713
714	    ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
715				   &c->tbsCertificate.serialNumber);
716	    if (ret != 0)
717		continue;
718
719	    /* verify issuer hashes hash */
720	    ret = _hx509_verify_signature(context,
721					  NULL,
722					  &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
723					  &c->tbsCertificate.issuer._save,
724					  &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
725	    if (ret != 0)
726		continue;
727
728	    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
729	    os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
730
731	    ret = _hx509_verify_signature(context,
732					  NULL,
733					  &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
734					  &os,
735					  &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
736	    if (ret != 0)
737		continue;
738
739	    switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
740	    case choice_OCSPCertStatus_good:
741		break;
742	    case invalid_choice_OCSPCertStatus:
743	    case choice_OCSPCertStatus_revoked:
744		hx509_set_error_string(context, 0,
745				       HX509_CERT_REVOKED,
746				       "Certificate revoked by issuer in OCSP");
747		return HX509_CERT_REVOKED;
748	    case choice_OCSPCertStatus_unknown:
749		continue;
750	    }
751
752	    /* don't allow the update to be in the future */
753	    if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
754		now + context->ocsp_time_diff)
755		continue;
756
757	    /* don't allow the next update to be in the past */
758	    if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
759		if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
760		    continue;
761	    } /* else should force a refetch, but can we ? */
762
763	    return 0;
764	}
765    }
766
767    for (i = 0; i < ctx->crls.len; i++) {
768	struct revoke_crl *crl = &ctx->crls.val[i];
769	struct stat sb;
770	int diff;
771
772	/* check if cert.issuer == crls.val[i].crl.issuer */
773	ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
774			      &crl->crl.tbsCertList.issuer, &diff);
775	if (ret || diff)
776	    continue;
777
778	ret = stat(crl->path, &sb);
779	if (ret == 0 && crl->last_modfied != sb.st_mtime) {
780	    CRLCertificateList cl;
781
782	    ret = load_crl(crl->path, &crl->last_modfied, &cl);
783	    if (ret == 0) {
784		free_CRLCertificateList(&crl->crl);
785		crl->crl = cl;
786		crl->verified = 0;
787		crl->failed_verify = 0;
788	    }
789	}
790	if (crl->failed_verify)
791	    continue;
792
793	/* verify signature in crl if not already done */
794	if (crl->verified == 0) {
795	    ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
796	    if (ret) {
797		crl->failed_verify = 1;
798		continue;
799	    }
800	    crl->verified = 1;
801	}
802
803	if (crl->crl.tbsCertList.crlExtensions) {
804	    for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
805		if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
806		    hx509_set_error_string(context, 0,
807					   HX509_CRL_UNKNOWN_EXTENSION,
808					   "Unknown CRL extension");
809		    return HX509_CRL_UNKNOWN_EXTENSION;
810		}
811	    }
812	}
813
814	if (crl->crl.tbsCertList.revokedCertificates == NULL)
815	    return 0;
816
817	/* check if cert is in crl */
818	for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
819	    time_t t;
820
821	    ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
822				       &c->tbsCertificate.serialNumber);
823	    if (ret != 0)
824		continue;
825
826	    t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
827	    if (t > now)
828		continue;
829
830	    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
831		for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
832		    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
833			return HX509_CRL_UNKNOWN_EXTENSION;
834
835	    hx509_set_error_string(context, 0,
836				   HX509_CERT_REVOKED,
837				   "Certificate revoked by issuer in CRL");
838	    return HX509_CERT_REVOKED;
839	}
840
841	return 0;
842    }
843
844
845    if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
846	return 0;
847    hx509_set_error_string(context, HX509_ERROR_APPEND,
848			   HX509_REVOKE_STATUS_MISSING,
849			   "No revoke status found for "
850			   "certificates");
851    return HX509_REVOKE_STATUS_MISSING;
852}
853
854struct ocsp_add_ctx {
855    OCSPTBSRequest *req;
856    hx509_certs certs;
857    const AlgorithmIdentifier *digest;
858    hx509_cert parent;
859};
860
861static int
862add_to_req(hx509_context context, void *ptr, hx509_cert cert)
863{
864    struct ocsp_add_ctx *ctx = ptr;
865    OCSPInnerRequest *one;
866    hx509_cert parent = NULL;
867    Certificate *p, *c = _hx509_get_cert(cert);
868    heim_octet_string os;
869    int ret;
870    hx509_query q;
871    void *d;
872
873    d = realloc(ctx->req->requestList.val,
874		sizeof(ctx->req->requestList.val[0]) *
875		(ctx->req->requestList.len + 1));
876    if (d == NULL)
877	return ENOMEM;
878    ctx->req->requestList.val = d;
879
880    one = &ctx->req->requestList.val[ctx->req->requestList.len];
881    memset(one, 0, sizeof(*one));
882
883    _hx509_query_clear(&q);
884
885    q.match |= HX509_QUERY_FIND_ISSUER_CERT;
886    q.subject = c;
887
888    ret = hx509_certs_find(context, ctx->certs, &q, &parent);
889    if (ret)
890	goto out;
891
892    if (ctx->parent) {
893	if (hx509_cert_cmp(ctx->parent, parent) != 0) {
894	    ret = HX509_REVOKE_NOT_SAME_PARENT;
895	    hx509_set_error_string(context, 0, ret,
896				   "Not same parent certifate as "
897				   "last certificate in request");
898	    goto out;
899	}
900    } else
901	ctx->parent = hx509_cert_ref(parent);
902
903    p = _hx509_get_cert(parent);
904
905    ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
906    if (ret)
907	goto out;
908
909    ret = _hx509_create_signature(context,
910				  NULL,
911				  &one->reqCert.hashAlgorithm,
912				  &c->tbsCertificate.issuer._save,
913				  NULL,
914				  &one->reqCert.issuerNameHash);
915    if (ret)
916	goto out;
917
918    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
919    os.length =
920	p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
921
922    ret = _hx509_create_signature(context,
923				  NULL,
924				  &one->reqCert.hashAlgorithm,
925				  &os,
926				  NULL,
927				  &one->reqCert.issuerKeyHash);
928    if (ret)
929	goto out;
930
931    ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
932				       &one->reqCert.serialNumber);
933    if (ret)
934	goto out;
935
936    ctx->req->requestList.len++;
937out:
938    hx509_cert_free(parent);
939    if (ret) {
940	free_OCSPInnerRequest(one);
941	memset(one, 0, sizeof(*one));
942    }
943
944    return ret;
945}
946
947/**
948 * Create an OCSP request for a set of certificates.
949 *
950 * @param context a hx509 context
951 * @param reqcerts list of certificates to request ocsp data for
952 * @param pool certificate pool to use when signing
953 * @param signer certificate to use to sign the request
954 * @param digest the signing algorithm in the request, if NULL use the
955 * default signature algorithm,
956 * @param request the encoded request, free with free_heim_octet_string().
957 * @param nonce nonce in the request, free with free_heim_octet_string().
958 *
959 * @return An hx509 error code, see hx509_get_error_string().
960 *
961 * @ingroup hx509_revoke
962 */
963
964int
965hx509_ocsp_request(hx509_context context,
966		   hx509_certs reqcerts,
967		   hx509_certs pool,
968		   hx509_cert signer,
969		   const AlgorithmIdentifier *digest,
970		   heim_octet_string *request,
971		   heim_octet_string *nonce)
972{
973    OCSPRequest req;
974    size_t size = 0;
975    int ret;
976    struct ocsp_add_ctx ctx;
977    Extensions *es;
978
979    memset(&req, 0, sizeof(req));
980
981    if (digest == NULL)
982	digest = _hx509_crypto_default_digest_alg;
983
984    ctx.req = &req.tbsRequest;
985    ctx.certs = pool;
986    ctx.digest = digest;
987    ctx.parent = NULL;
988
989    ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
990    hx509_cert_free(ctx.parent);
991    if (ret)
992	goto out;
993
994    if (nonce) {
995	req.tbsRequest.requestExtensions =
996	    calloc(1, sizeof(*req.tbsRequest.requestExtensions));
997	if (req.tbsRequest.requestExtensions == NULL) {
998	    ret = ENOMEM;
999	    goto out;
1000	}
1001
1002	es = req.tbsRequest.requestExtensions;
1003
1004	es->len = 1;
1005	es->val = calloc(es->len, sizeof(es->val[0]));
1006	if (es->val == NULL) {
1007	    es->len = 0;
1008	    ret = ENOMEM;
1009	    goto out;
1010	}
1011	ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
1012	if (ret) {
1013	    free_OCSPRequest(&req);
1014	    return ret;
1015	}
1016
1017	es->val[0].extnValue.data = malloc(10);
1018	if (es->val[0].extnValue.data == NULL) {
1019	    ret = ENOMEM;
1020	    goto out;
1021	}
1022	es->val[0].extnValue.length = 10;
1023
1024	ret = CCRandomCopyBytes(kCCRandomDefault,
1025				es->val[0].extnValue.data,
1026				es->val[0].extnValue.length);
1027	if (ret) {
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
1048out:
1049    free_OCSPRequest(&req);
1050    return ret;
1051}
1052
1053static char *
1054printable_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
1079int
1080hx509_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    case invalid_choice_OCSPResponderID:
1124	_hx509_abort("choice_OCSPResponderID unknown");
1125    }
1126
1127    fprintf(out, "producedAt: %s\n",
1128	    printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1129
1130    fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1131
1132    for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1133	const char *status;
1134	switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1135	case choice_OCSPCertStatus_good:
1136	    status = "good";
1137	    break;
1138	case choice_OCSPCertStatus_revoked:
1139	    status = "revoked";
1140	    break;
1141	case choice_OCSPCertStatus_unknown:
1142	    status = "unknown";
1143	    break;
1144	case invalid_choice_OCSPCertStatus:
1145	    status = "element unknown";
1146	    break;
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
1186int
1187hx509_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	case invalid_choice_OCSPCertStatus:
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 = 0;
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