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