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