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