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    default:
280	free_OCSPResponse(&resp);
281	return HX509_REVOKE_WRONG_DATA;
282    }
283
284    if (resp.responseBytes == NULL) {
285	free_OCSPResponse(&resp);
286	return EINVAL;
287    }
288
289    ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
290			   &asn1_oid_id_pkix_ocsp_basic);
291    if (ret != 0) {
292	free_OCSPResponse(&resp);
293	return HX509_REVOKE_WRONG_DATA;
294    }
295
296    ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
297				       resp.responseBytes->response.length,
298				       basic,
299				       &size);
300    if (ret) {
301	free_OCSPResponse(&resp);
302	return ret;
303    }
304    if (size != resp.responseBytes->response.length) {
305	free_OCSPResponse(&resp);
306	free_OCSPBasicOCSPResponse(basic);
307	return ASN1_EXTRA_DATA;
308    }
309    free_OCSPResponse(&resp);
310
311    return 0;
312}
313
314/*
315 *
316 */
317
318static int
319load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
320{
321    OCSPBasicOCSPResponse basic;
322    hx509_certs certs = NULL;
323    size_t length;
324    struct stat sb;
325    void *data;
326    int ret;
327
328    ret = rk_undumpdata(ocsp->path, &data, &length);
329    if (ret)
330	return ret;
331
332    ret = stat(ocsp->path, &sb);
333    if (ret)
334	return errno;
335
336    ret = parse_ocsp_basic(data, length, &basic);
337    rk_xfree(data);
338    if (ret) {
339	hx509_set_error_string(context, 0, ret,
340			       "Failed to parse OCSP response");
341	return ret;
342    }
343
344    if (basic.certs) {
345	size_t i;
346
347	ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
348			       NULL, &certs);
349	if (ret) {
350	    free_OCSPBasicOCSPResponse(&basic);
351	    return ret;
352	}
353
354	for (i = 0; i < basic.certs->len; i++) {
355	    hx509_cert c;
356
357	    ret = hx509_cert_init(context, &basic.certs->val[i], &c);
358	    if (ret)
359		continue;
360
361	    ret = hx509_certs_add(context, certs, c);
362	    hx509_cert_free(c);
363	    if (ret)
364		continue;
365	}
366    }
367
368    ocsp->last_modfied = sb.st_mtime;
369
370    free_OCSPBasicOCSPResponse(&ocsp->ocsp);
371    hx509_certs_free(&ocsp->certs);
372    hx509_cert_free(ocsp->signer);
373
374    ocsp->ocsp = basic;
375    ocsp->certs = certs;
376    ocsp->signer = NULL;
377
378    return 0;
379}
380
381/**
382 * Add a OCSP file to the revokation context.
383 *
384 * @param context hx509 context
385 * @param ctx hx509 revokation context
386 * @param path path to file that is going to be added to the context.
387 *
388 * @return An hx509 error code, see hx509_get_error_string().
389 *
390 * @ingroup hx509_revoke
391 */
392
393int
394hx509_revoke_add_ocsp(hx509_context context,
395		      hx509_revoke_ctx ctx,
396		      const char *path)
397{
398    void *data;
399    int ret;
400    size_t i;
401
402    if (strncmp(path, "FILE:", 5) != 0) {
403	hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
404			       "unsupport type in %s", path);
405	return HX509_UNSUPPORTED_OPERATION;
406    }
407
408    path += 5;
409
410    for (i = 0; i < ctx->ocsps.len; i++) {
411	if (strcmp(ctx->ocsps.val[0].path, path) == 0)
412	    return 0;
413    }
414
415    data = realloc(ctx->ocsps.val,
416		   (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
417    if (data == NULL) {
418	hx509_clear_error_string(context);
419	return ENOMEM;
420    }
421
422    ctx->ocsps.val = data;
423
424    memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
425	   sizeof(ctx->ocsps.val[0]));
426
427    ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
428    if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
429	hx509_clear_error_string(context);
430	return ENOMEM;
431    }
432
433    ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
434    if (ret) {
435	free(ctx->ocsps.val[ctx->ocsps.len].path);
436	return ret;
437    }
438    ctx->ocsps.len++;
439
440    return ret;
441}
442
443/*
444 *
445 */
446
447static int
448verify_crl(hx509_context context,
449	   hx509_revoke_ctx ctx,
450	   CRLCertificateList *crl,
451	   time_t time_now,
452	   hx509_certs certs,
453	   hx509_cert parent)
454{
455    hx509_cert signer;
456    hx509_query q;
457    time_t t;
458    int ret;
459
460    t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
461    if (t > time_now) {
462	hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
463			       "CRL used before time");
464	return HX509_CRL_USED_BEFORE_TIME;
465    }
466
467    if (crl->tbsCertList.nextUpdate == NULL) {
468	hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
469			       "CRL missing nextUpdate");
470	return HX509_CRL_INVALID_FORMAT;
471    }
472
473    t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
474    if (t < time_now) {
475	hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
476			       "CRL used after time");
477	return HX509_CRL_USED_AFTER_TIME;
478    }
479
480    _hx509_query_clear(&q);
481
482    /*
483     * If it's the signer have CRLSIGN bit set, use that as the signer
484     * cert for the certificate, otherwise, search for a certificate.
485     */
486    if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
487	signer = hx509_cert_ref(parent);
488    } else {
489	q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
490	q.match |= HX509_QUERY_KU_CRLSIGN;
491	q.subject_name = &crl->tbsCertList.issuer;
492
493	ret = hx509_certs_find(context, certs, &q, &signer);
494	if (ret) {
495	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
496				   "Failed to find certificate for CRL");
497	    return ret;
498	}
499    }
500
501    ret = _hx509_verify_signature_bitstring(context,
502					    signer,
503					    &crl->signatureAlgorithm,
504					    &crl->tbsCertList._save,
505					    &crl->signatureValue);
506    if (ret) {
507	hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
508			       "CRL signature invalid");
509	goto out;
510    }
511
512    /*
513     * If signer is not CA cert, need to check revoke status of this
514     * CRL signing cert too, this include all parent CRL signer cert
515     * up to the root *sigh*, assume root at least hve CERTSIGN flag
516     * set.
517     */
518    while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
519	hx509_cert crl_parent;
520
521	_hx509_query_clear(&q);
522
523	q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
524	q.match |= HX509_QUERY_KU_CRLSIGN;
525	q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
526
527	ret = hx509_certs_find(context, certs, &q, &crl_parent);
528	if (ret) {
529	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
530				   "Failed to find parent of CRL signer");
531	    goto out;
532	}
533
534	ret = hx509_revoke_verify(context,
535				  ctx,
536				  certs,
537				  time_now,
538				  signer,
539				  crl_parent);
540	hx509_cert_free(signer);
541	signer = crl_parent;
542	if (ret) {
543	    hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
544				   "Failed to verify revoke "
545				   "status of CRL signer");
546	    goto out;
547	}
548    }
549
550out:
551    hx509_cert_free(signer);
552
553    return ret;
554}
555
556static int
557load_crl(const char *path, time_t *t, CRLCertificateList *crl)
558{
559    size_t length, size;
560    struct stat sb;
561    void *data;
562    int ret;
563
564    memset(crl, 0, sizeof(*crl));
565
566    ret = rk_undumpdata(path, &data, &length);
567    if (ret)
568	return ret;
569
570    ret = stat(path, &sb);
571    if (ret)
572	return errno;
573
574    *t = sb.st_mtime;
575
576    ret = decode_CRLCertificateList(data, length, crl, &size);
577    rk_xfree(data);
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 invalid_choice_OCSPCertStatus:
739	    case choice_OCSPCertStatus_revoked:
740		hx509_set_error_string(context, 0,
741				       HX509_CERT_REVOKED,
742				       "Certificate revoked by issuer in OCSP");
743		return HX509_CERT_REVOKED;
744	    case choice_OCSPCertStatus_unknown:
745		continue;
746	    }
747
748	    /* don't allow the update to be in the future */
749	    if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
750		now + context->ocsp_time_diff)
751		continue;
752
753	    /* don't allow the next update to be in the past */
754	    if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
755		if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
756		    continue;
757	    } /* else 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	int diff;
767
768	/* check if cert.issuer == crls.val[i].crl.issuer */
769	ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
770			      &crl->crl.tbsCertList.issuer, &diff);
771	if (ret || diff)
772	    continue;
773
774	ret = stat(crl->path, &sb);
775	if (ret == 0 && crl->last_modfied != sb.st_mtime) {
776	    CRLCertificateList cl;
777
778	    ret = load_crl(crl->path, &crl->last_modfied, &cl);
779	    if (ret == 0) {
780		free_CRLCertificateList(&crl->crl);
781		crl->crl = cl;
782		crl->verified = 0;
783		crl->failed_verify = 0;
784	    }
785	}
786	if (crl->failed_verify)
787	    continue;
788
789	/* verify signature in crl if not already done */
790	if (crl->verified == 0) {
791	    ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
792	    if (ret) {
793		crl->failed_verify = 1;
794		continue;
795	    }
796	    crl->verified = 1;
797	}
798
799	if (crl->crl.tbsCertList.crlExtensions) {
800	    for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
801		if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
802		    hx509_set_error_string(context, 0,
803					   HX509_CRL_UNKNOWN_EXTENSION,
804					   "Unknown CRL extension");
805		    return HX509_CRL_UNKNOWN_EXTENSION;
806		}
807	    }
808	}
809
810	if (crl->crl.tbsCertList.revokedCertificates == NULL)
811	    return 0;
812
813	/* check if cert is in crl */
814	for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
815	    time_t t;
816
817	    ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
818				       &c->tbsCertificate.serialNumber);
819	    if (ret != 0)
820		continue;
821
822	    t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
823	    if (t > now)
824		continue;
825
826	    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
827		for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
828		    if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
829			return HX509_CRL_UNKNOWN_EXTENSION;
830
831	    hx509_set_error_string(context, 0,
832				   HX509_CERT_REVOKED,
833				   "Certificate revoked by issuer in CRL");
834	    return HX509_CERT_REVOKED;
835	}
836
837	return 0;
838    }
839
840
841    if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
842	return 0;
843    hx509_set_error_string(context, HX509_ERROR_APPEND,
844			   HX509_REVOKE_STATUS_MISSING,
845			   "No revoke status found for "
846			   "certificates");
847    return HX509_REVOKE_STATUS_MISSING;
848}
849
850struct ocsp_add_ctx {
851    OCSPTBSRequest *req;
852    hx509_certs certs;
853    const AlgorithmIdentifier *digest;
854    hx509_cert parent;
855};
856
857static int
858add_to_req(hx509_context context, void *ptr, hx509_cert cert)
859{
860    struct ocsp_add_ctx *ctx = ptr;
861    OCSPInnerRequest *one;
862    hx509_cert parent = NULL;
863    Certificate *p, *c = _hx509_get_cert(cert);
864    heim_octet_string os;
865    int ret;
866    hx509_query q;
867    void *d;
868
869    d = realloc(ctx->req->requestList.val,
870		sizeof(ctx->req->requestList.val[0]) *
871		(ctx->req->requestList.len + 1));
872    if (d == NULL)
873	return ENOMEM;
874    ctx->req->requestList.val = d;
875
876    one = &ctx->req->requestList.val[ctx->req->requestList.len];
877    memset(one, 0, sizeof(*one));
878
879    _hx509_query_clear(&q);
880
881    q.match |= HX509_QUERY_FIND_ISSUER_CERT;
882    q.subject = c;
883
884    ret = hx509_certs_find(context, ctx->certs, &q, &parent);
885    if (ret)
886	goto out;
887
888    if (ctx->parent) {
889	if (hx509_cert_cmp(ctx->parent, parent) != 0) {
890	    ret = HX509_REVOKE_NOT_SAME_PARENT;
891	    hx509_set_error_string(context, 0, ret,
892				   "Not same parent certifate as "
893				   "last certificate in request");
894	    goto out;
895	}
896    } else
897	ctx->parent = hx509_cert_ref(parent);
898
899    p = _hx509_get_cert(parent);
900
901    ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
902    if (ret)
903	goto out;
904
905    ret = _hx509_create_signature(context,
906				  NULL,
907				  &one->reqCert.hashAlgorithm,
908				  &c->tbsCertificate.issuer._save,
909				  NULL,
910				  &one->reqCert.issuerNameHash);
911    if (ret)
912	goto out;
913
914    os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
915    os.length =
916	p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
917
918    ret = _hx509_create_signature(context,
919				  NULL,
920				  &one->reqCert.hashAlgorithm,
921				  &os,
922				  NULL,
923				  &one->reqCert.issuerKeyHash);
924    if (ret)
925	goto out;
926
927    ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
928				       &one->reqCert.serialNumber);
929    if (ret)
930	goto out;
931
932    ctx->req->requestList.len++;
933out:
934    hx509_cert_free(parent);
935    if (ret) {
936	free_OCSPInnerRequest(one);
937	memset(one, 0, sizeof(*one));
938    }
939
940    return ret;
941}
942
943/**
944 * Create an OCSP request for a set of certificates.
945 *
946 * @param context a hx509 context
947 * @param reqcerts list of certificates to request ocsp data for
948 * @param pool certificate pool to use when signing
949 * @param signer certificate to use to sign the request
950 * @param digest the signing algorithm in the request, if NULL use the
951 * default signature algorithm,
952 * @param request the encoded request, free with free_heim_octet_string().
953 * @param nonce nonce in the request, free with free_heim_octet_string().
954 *
955 * @return An hx509 error code, see hx509_get_error_string().
956 *
957 * @ingroup hx509_revoke
958 */
959
960int
961hx509_ocsp_request(hx509_context context,
962		   hx509_certs reqcerts,
963		   hx509_certs pool,
964		   hx509_cert signer,
965		   const AlgorithmIdentifier *digest,
966		   heim_octet_string *request,
967		   heim_octet_string *nonce)
968{
969    OCSPRequest req;
970    size_t size;
971    int ret;
972    struct ocsp_add_ctx ctx;
973    Extensions *es;
974
975    memset(&req, 0, sizeof(req));
976
977    if (digest == NULL)
978	digest = _hx509_crypto_default_digest_alg;
979
980    ctx.req = &req.tbsRequest;
981    ctx.certs = pool;
982    ctx.digest = digest;
983    ctx.parent = NULL;
984
985    ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
986    hx509_cert_free(ctx.parent);
987    if (ret)
988	goto out;
989
990    if (nonce) {
991	req.tbsRequest.requestExtensions =
992	    calloc(1, sizeof(*req.tbsRequest.requestExtensions));
993	if (req.tbsRequest.requestExtensions == NULL) {
994	    ret = ENOMEM;
995	    goto out;
996	}
997
998	es = req.tbsRequest.requestExtensions;
999
1000	es->val = calloc(es->len, sizeof(es->val[0]));
1001	if (es->val == NULL) {
1002	    ret = ENOMEM;
1003	    goto out;
1004	}
1005	es->len = 1;
1006	ret = der_copy_oid(&asn1_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 = CCRandomCopyBytes(kCCRandomDefault,
1020				es->val[0].extnValue.data,
1021				es->val[0].extnValue.length);
1022	if (ret) {
1023	    ret = HX509_CRYPTO_INTERNAL_ERROR;
1024	    goto out;
1025	}
1026	ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1027	if (ret) {
1028	    ret = ENOMEM;
1029	    goto out;
1030	}
1031    }
1032
1033    ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1034		       &req, &size, ret);
1035    free_OCSPRequest(&req);
1036    if (ret)
1037	goto out;
1038    if (size != request->length)
1039	_hx509_abort("internal ASN.1 encoder error");
1040
1041    return 0;
1042
1043out:
1044    free_OCSPRequest(&req);
1045    return ret;
1046}
1047
1048static char *
1049printable_time(time_t t)
1050{
1051    static char s[128];
1052    char *p;
1053    if ((p = ctime(&t)) == NULL)
1054       strlcpy(s, "?", sizeof(s));
1055    else {
1056       strlcpy(s, p + 4, sizeof(s));
1057       s[20] = 0;
1058    }
1059    return s;
1060}
1061
1062/**
1063 * Print the OCSP reply stored in a file.
1064 *
1065 * @param context a hx509 context
1066 * @param path path to a file with a OCSP reply
1067 * @param out the out FILE descriptor to print the reply on
1068 *
1069 * @return An hx509 error code, see hx509_get_error_string().
1070 *
1071 * @ingroup hx509_revoke
1072 */
1073
1074int
1075hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1076{
1077    struct revoke_ocsp ocsp;
1078    int ret;
1079    size_t i;
1080
1081    if (out == NULL)
1082	out = stdout;
1083
1084    memset(&ocsp, 0, sizeof(ocsp));
1085
1086    ocsp.path = strdup(path);
1087    if (ocsp.path == NULL)
1088	return ENOMEM;
1089
1090    ret = load_ocsp(context, &ocsp);
1091    if (ret) {
1092	free_ocsp(&ocsp);
1093	return ret;
1094    }
1095
1096    fprintf(out, "signer: ");
1097
1098    switch(ocsp.ocsp.tbsResponseData.responderID.element) {
1099    case choice_OCSPResponderID_byName: {
1100	hx509_name n;
1101	char *s;
1102	hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
1103	hx509_name_to_string(n, &s);
1104	hx509_name_free(&n);
1105	fprintf(out, " byName: %s\n", s);
1106	free(s);
1107	break;
1108    }
1109    case choice_OCSPResponderID_byKey: {
1110	char *s;
1111	hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
1112		   ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
1113		   &s);
1114	fprintf(out, " byKey: %s\n", s);
1115	free(s);
1116	break;
1117    }
1118    default:
1119	_hx509_abort("choice_OCSPResponderID unknown");
1120	break;
1121    }
1122
1123    fprintf(out, "producedAt: %s\n",
1124	    printable_time(ocsp.ocsp.tbsResponseData.producedAt));
1125
1126    fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
1127
1128    for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
1129	const char *status;
1130	switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1131	case choice_OCSPCertStatus_good:
1132	    status = "good";
1133	    break;
1134	case choice_OCSPCertStatus_revoked:
1135	    status = "revoked";
1136	    break;
1137	case choice_OCSPCertStatus_unknown:
1138	    status = "unknown";
1139	    break;
1140	default:
1141	    status = "element unknown";
1142	}
1143
1144	fprintf(out, "\t%zu. status: %s\n", i, status);
1145
1146	fprintf(out, "\tthisUpdate: %s\n",
1147		printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1148	if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
1149	    fprintf(out, "\tproducedAt: %s\n",
1150		    printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
1151
1152    }
1153
1154    fprintf(out, "appended certs:\n");
1155    if (ocsp.certs)
1156	ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);
1157
1158    free_ocsp(&ocsp);
1159    return ret;
1160}
1161
1162/**
1163 * Verify that the certificate is part of the OCSP reply and it's not
1164 * expired. Doesn't verify signature the OCSP reply or it's done by a
1165 * authorized sender, that is assumed to be already done.
1166 *
1167 * @param context a hx509 context
1168 * @param now the time right now, if 0, use the current time.
1169 * @param cert the certificate to verify
1170 * @param flags flags control the behavior
1171 * @param data pointer to the encode ocsp reply
1172 * @param length the length of the encode ocsp reply
1173 * @param expiration return the time the OCSP will expire and need to
1174 * be rechecked.
1175 *
1176 * @return An hx509 error code, see hx509_get_error_string().
1177 *
1178 * @ingroup hx509_verify
1179 */
1180
1181int
1182hx509_ocsp_verify(hx509_context context,
1183		  time_t now,
1184		  hx509_cert cert,
1185		  int flags,
1186		  const void *data, size_t length,
1187		  time_t *expiration)
1188{
1189    const Certificate *c = _hx509_get_cert(cert);
1190    OCSPBasicOCSPResponse basic;
1191    int ret;
1192    size_t i;
1193
1194    if (now == 0)
1195	now = time(NULL);
1196
1197    *expiration = 0;
1198
1199    ret = parse_ocsp_basic(data, length, &basic);
1200    if (ret) {
1201	hx509_set_error_string(context, 0, ret,
1202			       "Failed to parse OCSP response");
1203	return ret;
1204    }
1205
1206    for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1207
1208	ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1209			       &c->tbsCertificate.serialNumber);
1210	if (ret != 0)
1211	    continue;
1212
1213	/* verify issuer hashes hash */
1214	ret = _hx509_verify_signature(context,
1215				      NULL,
1216				      &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1217				      &c->tbsCertificate.issuer._save,
1218				      &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1219	if (ret != 0)
1220	    continue;
1221
1222	switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1223	case choice_OCSPCertStatus_good:
1224	    break;
1225	case choice_OCSPCertStatus_revoked:
1226	case choice_OCSPCertStatus_unknown:
1227	case invalid_choice_OCSPCertStatus:
1228	    continue;
1229	}
1230
1231	/* don't allow the update to be in the future */
1232	if (basic.tbsResponseData.responses.val[i].thisUpdate >
1233	    now + context->ocsp_time_diff)
1234	    continue;
1235
1236	/* don't allow the next update to be in the past */
1237	if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1238	    if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1239		continue;
1240	    *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1241	} else
1242	    *expiration = now;
1243
1244	free_OCSPBasicOCSPResponse(&basic);
1245	return 0;
1246    }
1247
1248    free_OCSPBasicOCSPResponse(&basic);
1249
1250    {
1251	hx509_name name;
1252	char *subject;
1253
1254	ret = hx509_cert_get_subject(cert, &name);
1255	if (ret) {
1256	    hx509_clear_error_string(context);
1257	    goto out;
1258	}
1259	ret = hx509_name_to_string(name, &subject);
1260	hx509_name_free(&name);
1261	if (ret) {
1262	    hx509_clear_error_string(context);
1263	    goto out;
1264	}
1265	hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1266			       "Certificate %s not in OCSP response "
1267			       "or not good",
1268			       subject);
1269	free(subject);
1270    }
1271out:
1272    return HX509_CERT_NOT_IN_OCSP;
1273}
1274
1275struct hx509_crl {
1276    hx509_certs revoked;
1277    time_t expire;
1278};
1279
1280/**
1281 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1282 *
1283 * @param context a hx509 context.
1284 * @param crl return pointer to a newly allocated CRL context.
1285 *
1286 * @return An hx509 error code, see hx509_get_error_string().
1287 *
1288 * @ingroup hx509_verify
1289 */
1290
1291int
1292hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1293{
1294    int ret;
1295
1296    *crl = calloc(1, sizeof(**crl));
1297    if (*crl == NULL) {
1298	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1299	return ENOMEM;
1300    }
1301
1302    ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1303    if (ret) {
1304	free(*crl);
1305	*crl = NULL;
1306	return ret;
1307    }
1308    (*crl)->expire = 0;
1309    return ret;
1310}
1311
1312/**
1313 * Add revoked certificate to an CRL context.
1314 *
1315 * @param context a hx509 context.
1316 * @param crl the CRL to add the revoked certificate to.
1317 * @param certs keyset of certificate to revoke.
1318 *
1319 * @return An hx509 error code, see hx509_get_error_string().
1320 *
1321 * @ingroup hx509_verify
1322 */
1323
1324int
1325hx509_crl_add_revoked_certs(hx509_context context,
1326			    hx509_crl crl,
1327			    hx509_certs certs)
1328{
1329    return hx509_certs_merge(context, crl->revoked, certs);
1330}
1331
1332/**
1333 * Set the lifetime of a CRL context.
1334 *
1335 * @param context a hx509 context.
1336 * @param crl a CRL context
1337 * @param delta delta time the certificate is valid, library adds the
1338 * current time to this.
1339 *
1340 * @return An hx509 error code, see hx509_get_error_string().
1341 *
1342 * @ingroup hx509_verify
1343 */
1344
1345int
1346hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1347{
1348    crl->expire = time(NULL) + delta;
1349    return 0;
1350}
1351
1352/**
1353 * Free a CRL context.
1354 *
1355 * @param context a hx509 context.
1356 * @param crl a CRL context to free.
1357 *
1358 * @ingroup hx509_verify
1359 */
1360
1361void
1362hx509_crl_free(hx509_context context, hx509_crl *crl)
1363{
1364    if (*crl == NULL)
1365	return;
1366    hx509_certs_free(&(*crl)->revoked);
1367    memset(*crl, 0, sizeof(**crl));
1368    free(*crl);
1369    *crl = NULL;
1370}
1371
1372static int
1373add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1374{
1375    TBSCRLCertList *c = ctx;
1376    unsigned int num;
1377    void *ptr;
1378    int ret;
1379
1380    num = c->revokedCertificates->len;
1381    ptr = realloc(c->revokedCertificates->val,
1382		  (num + 1) * sizeof(c->revokedCertificates->val[0]));
1383    if (ptr == NULL) {
1384	hx509_clear_error_string(context);
1385	return ENOMEM;
1386    }
1387    c->revokedCertificates->val = ptr;
1388
1389    ret = hx509_cert_get_serialnumber(cert,
1390				      &c->revokedCertificates->val[num].userCertificate);
1391    if (ret) {
1392	hx509_clear_error_string(context);
1393	return ret;
1394    }
1395    c->revokedCertificates->val[num].revocationDate.element =
1396	choice_Time_generalTime;
1397    c->revokedCertificates->val[num].revocationDate.u.generalTime =
1398	time(NULL) - 3600 * 24;
1399    c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1400
1401    c->revokedCertificates->len++;
1402
1403    return 0;
1404}
1405
1406/**
1407 * Sign a CRL and return an encode certificate.
1408 *
1409 * @param context a hx509 context.
1410 * @param signer certificate to sign the CRL with
1411 * @param crl the CRL to sign
1412 * @param os return the signed and encoded CRL, free with
1413 * free_heim_octet_string()
1414 *
1415 * @return An hx509 error code, see hx509_get_error_string().
1416 *
1417 * @ingroup hx509_verify
1418 */
1419
1420int
1421hx509_crl_sign(hx509_context context,
1422	       hx509_cert signer,
1423	       hx509_crl crl,
1424	       heim_octet_string *os)
1425{
1426    const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1427    CRLCertificateList c;
1428    size_t size;
1429    int ret;
1430    hx509_private_key signerkey;
1431
1432    memset(&c, 0, sizeof(c));
1433
1434    signerkey = _hx509_cert_private_key(signer);
1435    if (signerkey == NULL) {
1436	ret = HX509_PRIVATE_KEY_MISSING;
1437	hx509_set_error_string(context, 0, ret,
1438			       "Private key missing for CRL signing");
1439	return ret;
1440    }
1441
1442    c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1443    if (c.tbsCertList.version == NULL) {
1444	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1445	return ENOMEM;
1446    }
1447
1448    *c.tbsCertList.version = 1;
1449
1450    ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1451    if (ret) {
1452	hx509_clear_error_string(context);
1453	goto out;
1454    }
1455
1456    ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1457		    &c.tbsCertList.issuer);
1458    if (ret) {
1459	hx509_clear_error_string(context);
1460	goto out;
1461    }
1462
1463    c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1464    c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1465
1466    c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1467    if (c.tbsCertList.nextUpdate == NULL) {
1468	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1469	ret = ENOMEM;
1470	goto out;
1471    }
1472
1473    {
1474	time_t next = crl->expire;
1475	if (next == 0)
1476	    next = time(NULL) + 24 * 3600 * 365;
1477
1478	c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1479	c.tbsCertList.nextUpdate->u.generalTime = next;
1480    }
1481
1482    c.tbsCertList.revokedCertificates =
1483	calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1484    if (c.tbsCertList.revokedCertificates == NULL) {
1485	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1486	ret = ENOMEM;
1487	goto out;
1488    }
1489    c.tbsCertList.crlExtensions = NULL;
1490
1491    ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
1492    if (ret)
1493	goto out;
1494
1495    /* if not revoked certs, remove OPTIONAL entry */
1496    if (c.tbsCertList.revokedCertificates->len == 0) {
1497	free(c.tbsCertList.revokedCertificates);
1498	c.tbsCertList.revokedCertificates = NULL;
1499    }
1500
1501    ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1502		       &c.tbsCertList, &size, ret);
1503    if (ret) {
1504	hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1505	goto out;
1506    }
1507    if (size != os->length)
1508	_hx509_abort("internal ASN.1 encoder error");
1509
1510
1511    ret = _hx509_create_signature_bitstring(context,
1512					    signerkey,
1513					    sigalg,
1514					    os,
1515					    &c.signatureAlgorithm,
1516					    &c.signatureValue);
1517    free(os->data);
1518    if (ret) {
1519	hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
1520	goto out;
1521    }
1522
1523    ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1524		       &c, &size, ret);
1525    if (ret) {
1526	hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1527	goto out;
1528    }
1529    if (size != os->length)
1530	_hx509_abort("internal ASN.1 encoder error");
1531
1532    free_CRLCertificateList(&c);
1533
1534    return 0;
1535
1536out:
1537    free_CRLCertificateList(&c);
1538    return ret;
1539}
1540