1 /* $NetBSD: revoke.c,v 1.5 2023/06/19 21:41:44 christos 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 rk_xfree(data);
344 return errno;
345 }
346
347 ret = parse_ocsp_basic(data, length, &basic);
348 rk_xfree(data);
349 if (ret) {
350 hx509_set_error_string(context, 0, ret,
351 "Failed to parse OCSP response");
352 return ret;
353 }
354
355 if (basic.certs) {
356 size_t i;
357
358 ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
359 NULL, &certs);
360 if (ret) {
361 free_OCSPBasicOCSPResponse(&basic);
362 return ret;
363 }
364
365 for (i = 0; i < basic.certs->len; i++) {
366 hx509_cert c;
367
368 c = hx509_cert_init(context, &basic.certs->val[i], NULL);
369 if (c == NULL)
370 continue;
371
372 ret = hx509_certs_add(context, certs, c);
373 hx509_cert_free(c);
374 if (ret)
375 continue;
376 }
377 }
378
379 ocsp->last_modfied = sb.st_mtime;
380
381 free_OCSPBasicOCSPResponse(&ocsp->ocsp);
382 hx509_certs_free(&ocsp->certs);
383 hx509_cert_free(ocsp->signer);
384
385 ocsp->ocsp = basic;
386 ocsp->certs = certs;
387 ocsp->signer = NULL;
388
389 return 0;
390 }
391
392 /**
393 * Add a OCSP file to the revokation context.
394 *
395 * @param context hx509 context
396 * @param ctx hx509 revokation context
397 * @param path path to file that is going to be added to the context.
398 *
399 * @return An hx509 error code, see hx509_get_error_string().
400 *
401 * @ingroup hx509_revoke
402 */
403
404 int
hx509_revoke_add_ocsp(hx509_context context,hx509_revoke_ctx ctx,const char * path)405 hx509_revoke_add_ocsp(hx509_context context,
406 hx509_revoke_ctx ctx,
407 const char *path)
408 {
409 void *data;
410 int ret;
411 size_t i;
412
413 if (strncmp(path, "FILE:", 5) != 0) {
414 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
415 "unsupport type in %s", path);
416 return HX509_UNSUPPORTED_OPERATION;
417 }
418
419 path += 5;
420
421 for (i = 0; i < ctx->ocsps.len; i++) {
422 if (strcmp(ctx->ocsps.val[0].path, path) == 0)
423 return 0;
424 }
425
426 data = realloc(ctx->ocsps.val,
427 (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
428 if (data == NULL) {
429 hx509_clear_error_string(context);
430 return ENOMEM;
431 }
432
433 ctx->ocsps.val = data;
434
435 memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
436 sizeof(ctx->ocsps.val[0]));
437
438 ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
439 if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
440 hx509_clear_error_string(context);
441 return ENOMEM;
442 }
443
444 ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
445 if (ret) {
446 free(ctx->ocsps.val[ctx->ocsps.len].path);
447 return ret;
448 }
449 ctx->ocsps.len++;
450
451 return ret;
452 }
453
454 /*
455 *
456 */
457
458 static int
verify_crl(hx509_context context,hx509_revoke_ctx ctx,CRLCertificateList * crl,time_t time_now,hx509_certs certs,hx509_cert parent)459 verify_crl(hx509_context context,
460 hx509_revoke_ctx ctx,
461 CRLCertificateList *crl,
462 time_t time_now,
463 hx509_certs certs,
464 hx509_cert parent)
465 {
466 hx509_cert signer;
467 hx509_query q;
468 time_t t;
469 int ret;
470
471 t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
472 if (t > time_now) {
473 hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
474 "CRL used before time");
475 return HX509_CRL_USED_BEFORE_TIME;
476 }
477
478 if (crl->tbsCertList.nextUpdate == NULL) {
479 hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
480 "CRL missing nextUpdate");
481 return HX509_CRL_INVALID_FORMAT;
482 }
483
484 t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
485 if (t < time_now) {
486 hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
487 "CRL used after time");
488 return HX509_CRL_USED_AFTER_TIME;
489 }
490
491 _hx509_query_clear(&q);
492
493 /*
494 * If it's the signer have CRLSIGN bit set, use that as the signer
495 * cert for the certificate, otherwise, search for a certificate.
496 */
497 if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
498 signer = hx509_cert_ref(parent);
499 } else {
500 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
501 q.match |= HX509_QUERY_KU_CRLSIGN;
502 q.subject_name = &crl->tbsCertList.issuer;
503
504 ret = hx509_certs_find(context, certs, &q, &signer);
505 if (ret) {
506 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
507 "Failed to find certificate for CRL");
508 return ret;
509 }
510 }
511
512 ret = _hx509_verify_signature_bitstring(context,
513 signer,
514 &crl->signatureAlgorithm,
515 &crl->tbsCertList._save,
516 &crl->signatureValue);
517 if (ret) {
518 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
519 "CRL signature invalid");
520 goto out;
521 }
522
523 /*
524 * If signer is not CA cert, need to check revoke status of this
525 * CRL signing cert too, this include all parent CRL signer cert
526 * up to the root *sigh*, assume root at least hve CERTSIGN flag
527 * set.
528 */
529 while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
530 hx509_cert crl_parent;
531
532 _hx509_query_clear(&q);
533
534 q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
535 q.match |= HX509_QUERY_KU_CRLSIGN;
536 q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
537
538 ret = hx509_certs_find(context, certs, &q, &crl_parent);
539 if (ret) {
540 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
541 "Failed to find parent of CRL signer");
542 goto out;
543 }
544
545 ret = hx509_revoke_verify(context,
546 ctx,
547 certs,
548 time_now,
549 signer,
550 crl_parent);
551 hx509_cert_free(signer);
552 signer = crl_parent;
553 if (ret) {
554 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
555 "Failed to verify revoke "
556 "status of CRL signer");
557 goto out;
558 }
559 }
560
561 out:
562 hx509_cert_free(signer);
563
564 return ret;
565 }
566
567 static int
crl_parser(hx509_context context,const char * type,const hx509_pem_header * header,const void * data,size_t len,void * ctx)568 crl_parser(hx509_context context, const char *type,
569 const hx509_pem_header *header,
570 const void *data, size_t len, void *ctx)
571 {
572 CRLCertificateList *crl = (CRLCertificateList *)ctx;
573 size_t size;
574 int ret;
575
576 if (strcasecmp("X509 CRL", type) != 0)
577 return HX509_CRYPTO_SIG_INVALID_FORMAT;
578
579 ret = decode_CRLCertificateList(data, len, crl, &size);
580 if (ret)
581 return ret;
582
583 /* check signature is aligned */
584 if (crl->signatureValue.length & 7) {
585 free_CRLCertificateList(crl);
586 return HX509_CRYPTO_SIG_INVALID_FORMAT;
587 }
588
589 return 0;
590 }
591
592 static int
load_crl(hx509_context context,const char * path,time_t * t,CRLCertificateList * crl)593 load_crl(hx509_context context, const char *path, time_t *t, CRLCertificateList *crl)
594 {
595 struct stat sb;
596 size_t length;
597 void *data;
598 FILE *f;
599 int ret;
600
601 memset(crl, 0, sizeof(*crl));
602
603 ret = stat(path, &sb);
604 if (ret)
605 return errno;
606
607 *t = sb.st_mtime;
608
609 if ((f = fopen(path, "r")) == NULL)
610 return errno;
611
612 rk_cloexec_file(f);
613
614 ret = hx509_pem_read(context, f, crl_parser, crl);
615 fclose(f);
616
617 if (ret == HX509_PARSING_KEY_FAILED) {
618
619 ret = rk_undumpdata(path, &data, &length);
620 if (ret)
621 return ret;
622
623 ret = crl_parser(context, "X509 CRL", NULL, data, length, crl);
624 rk_xfree(data);
625 }
626 return ret;
627 }
628
629 /**
630 * Add a CRL file to the revokation context.
631 *
632 * @param context hx509 context
633 * @param ctx hx509 revokation context
634 * @param path path to file that is going to be added to the context.
635 *
636 * @return An hx509 error code, see hx509_get_error_string().
637 *
638 * @ingroup hx509_revoke
639 */
640
641 int
hx509_revoke_add_crl(hx509_context context,hx509_revoke_ctx ctx,const char * path)642 hx509_revoke_add_crl(hx509_context context,
643 hx509_revoke_ctx ctx,
644 const char *path)
645 {
646 void *data;
647 size_t i;
648 int ret;
649
650 if (strncmp(path, "FILE:", 5) != 0) {
651 hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
652 "unsupport type in %s", path);
653 return HX509_UNSUPPORTED_OPERATION;
654 }
655
656
657 path += 5;
658
659 for (i = 0; i < ctx->crls.len; i++) {
660 if (strcmp(ctx->crls.val[i].path, path) == 0)
661 return 0;
662 }
663
664 data = realloc(ctx->crls.val,
665 (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
666 if (data == NULL) {
667 hx509_clear_error_string(context);
668 return ENOMEM;
669 }
670 ctx->crls.val = data;
671
672 memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
673
674 ctx->crls.val[ctx->crls.len].path = strdup(path);
675 if (ctx->crls.val[ctx->crls.len].path == NULL) {
676 hx509_clear_error_string(context);
677 return ENOMEM;
678 }
679
680 ret = load_crl(context,
681 path,
682 &ctx->crls.val[ctx->crls.len].last_modfied,
683 &ctx->crls.val[ctx->crls.len].crl);
684 if (ret) {
685 free(ctx->crls.val[ctx->crls.len].path);
686 return ret;
687 }
688
689 ctx->crls.len++;
690
691 return ret;
692 }
693
694 /**
695 * Check that a certificate is not expired according to a revokation
696 * context. Also need the parent certificte to the check OCSP
697 * parent identifier.
698 *
699 * @param context hx509 context
700 * @param ctx hx509 revokation context
701 * @param certs
702 * @param now
703 * @param cert
704 * @param parent_cert
705 *
706 * @return An hx509 error code, see hx509_get_error_string().
707 *
708 * @ingroup hx509_revoke
709 */
710
711 int
hx509_revoke_verify(hx509_context context,hx509_revoke_ctx ctx,hx509_certs certs,time_t now,hx509_cert cert,hx509_cert parent_cert)712 hx509_revoke_verify(hx509_context context,
713 hx509_revoke_ctx ctx,
714 hx509_certs certs,
715 time_t now,
716 hx509_cert cert,
717 hx509_cert parent_cert)
718 {
719 const Certificate *c = _hx509_get_cert(cert);
720 const Certificate *p = _hx509_get_cert(parent_cert);
721 unsigned long i, j, k;
722 int ret;
723
724 hx509_clear_error_string(context);
725
726 for (i = 0; i < ctx->ocsps.len; i++) {
727 struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
728 struct stat sb;
729
730 /* check this ocsp apply to this cert */
731
732 /* check if there is a newer version of the file */
733 ret = stat(ocsp->path, &sb);
734 if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
735 ret = load_ocsp(context, ocsp);
736 if (ret)
737 continue;
738 }
739
740 /* verify signature in ocsp if not already done */
741 if (ocsp->signer == NULL) {
742 ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
743 if (ret)
744 continue;
745 }
746
747 for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
748 heim_octet_string os;
749
750 ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
751 &c->tbsCertificate.serialNumber);
752 if (ret != 0)
753 continue;
754
755 /* verify issuer hashes hash */
756 ret = _hx509_verify_signature(context,
757 NULL,
758 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
759 &c->tbsCertificate.issuer._save,
760 &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
761 if (ret != 0)
762 continue;
763
764 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
765 os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
766
767 ret = _hx509_verify_signature(context,
768 NULL,
769 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
770 &os,
771 &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
772 if (ret != 0)
773 continue;
774
775 switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
776 case choice_OCSPCertStatus_good:
777 break;
778 case choice_OCSPCertStatus_revoked:
779 hx509_set_error_string(context, 0,
780 HX509_CERT_REVOKED,
781 "Certificate revoked by issuer in OCSP");
782 return HX509_CERT_REVOKED;
783 case choice_OCSPCertStatus_unknown:
784 continue;
785 }
786
787 /* don't allow the update to be in the future */
788 if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
789 now + context->ocsp_time_diff)
790 continue;
791
792 /* don't allow the next update to be in the past */
793 if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
794 if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
795 continue;
796 } /* else should force a refetch, but can we ? */
797
798 return 0;
799 }
800 }
801
802 for (i = 0; i < ctx->crls.len; i++) {
803 struct revoke_crl *crl = &ctx->crls.val[i];
804 struct stat sb;
805 int diff;
806
807 /* check if cert.issuer == crls.val[i].crl.issuer */
808 ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
809 &crl->crl.tbsCertList.issuer, &diff);
810 if (ret || diff)
811 continue;
812
813 ret = stat(crl->path, &sb);
814 if (ret == 0 && crl->last_modfied != sb.st_mtime) {
815 CRLCertificateList cl;
816
817 ret = load_crl(context, crl->path, &crl->last_modfied, &cl);
818 if (ret == 0) {
819 free_CRLCertificateList(&crl->crl);
820 crl->crl = cl;
821 crl->verified = 0;
822 crl->failed_verify = 0;
823 }
824 }
825 if (crl->failed_verify)
826 continue;
827
828 /* verify signature in crl if not already done */
829 if (crl->verified == 0) {
830 ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
831 if (ret) {
832 crl->failed_verify = 1;
833 continue;
834 }
835 crl->verified = 1;
836 }
837
838 if (crl->crl.tbsCertList.crlExtensions) {
839 for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
840 if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
841 hx509_set_error_string(context, 0,
842 HX509_CRL_UNKNOWN_EXTENSION,
843 "Unknown CRL extension");
844 return HX509_CRL_UNKNOWN_EXTENSION;
845 }
846 }
847 }
848
849 if (crl->crl.tbsCertList.revokedCertificates == NULL)
850 return 0;
851
852 /* check if cert is in crl */
853 for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
854 time_t t;
855
856 ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
857 &c->tbsCertificate.serialNumber);
858 if (ret != 0)
859 continue;
860
861 t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
862 if (t > now)
863 continue;
864
865 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
866 for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
867 if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
868 return HX509_CRL_UNKNOWN_EXTENSION;
869
870 hx509_set_error_string(context, 0,
871 HX509_CERT_REVOKED,
872 "Certificate revoked by issuer in CRL");
873 return HX509_CERT_REVOKED;
874 }
875
876 return 0;
877 }
878
879
880 if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
881 return 0;
882 hx509_set_error_string(context, HX509_ERROR_APPEND,
883 HX509_REVOKE_STATUS_MISSING,
884 "No revoke status found for "
885 "certificates");
886 return HX509_REVOKE_STATUS_MISSING;
887 }
888
889 struct ocsp_add_ctx {
890 OCSPTBSRequest *req;
891 hx509_certs certs;
892 const AlgorithmIdentifier *digest;
893 hx509_cert parent;
894 };
895
896 static int
add_to_req(hx509_context context,void * ptr,hx509_cert cert)897 add_to_req(hx509_context context, void *ptr, hx509_cert cert)
898 {
899 struct ocsp_add_ctx *ctx = ptr;
900 OCSPInnerRequest *one;
901 hx509_cert parent = NULL;
902 Certificate *p, *c = _hx509_get_cert(cert);
903 heim_octet_string os;
904 int ret;
905 hx509_query q;
906 void *d;
907
908 d = realloc(ctx->req->requestList.val,
909 sizeof(ctx->req->requestList.val[0]) *
910 (ctx->req->requestList.len + 1));
911 if (d == NULL)
912 return ENOMEM;
913 ctx->req->requestList.val = d;
914
915 one = &ctx->req->requestList.val[ctx->req->requestList.len];
916 memset(one, 0, sizeof(*one));
917
918 _hx509_query_clear(&q);
919
920 q.match |= HX509_QUERY_FIND_ISSUER_CERT;
921 q.subject = c;
922
923 ret = hx509_certs_find(context, ctx->certs, &q, &parent);
924 if (ret)
925 goto out;
926
927 if (ctx->parent) {
928 if (hx509_cert_cmp(ctx->parent, parent) != 0) {
929 ret = HX509_REVOKE_NOT_SAME_PARENT;
930 hx509_set_error_string(context, 0, ret,
931 "Not same parent certifate as "
932 "last certificate in request");
933 goto out;
934 }
935 } else
936 ctx->parent = hx509_cert_ref(parent);
937
938 p = _hx509_get_cert(parent);
939
940 ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
941 if (ret)
942 goto out;
943
944 ret = _hx509_create_signature(context,
945 NULL,
946 &one->reqCert.hashAlgorithm,
947 &c->tbsCertificate.issuer._save,
948 NULL,
949 &one->reqCert.issuerNameHash);
950 if (ret)
951 goto out;
952
953 os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
954 os.length =
955 p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
956
957 ret = _hx509_create_signature(context,
958 NULL,
959 &one->reqCert.hashAlgorithm,
960 &os,
961 NULL,
962 &one->reqCert.issuerKeyHash);
963 if (ret)
964 goto out;
965
966 ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
967 &one->reqCert.serialNumber);
968 if (ret)
969 goto out;
970
971 ctx->req->requestList.len++;
972 out:
973 hx509_cert_free(parent);
974 if (ret) {
975 free_OCSPInnerRequest(one);
976 memset(one, 0, sizeof(*one));
977 }
978
979 return ret;
980 }
981
982 /**
983 * Create an OCSP request for a set of certificates.
984 *
985 * @param context a hx509 context
986 * @param reqcerts list of certificates to request ocsp data for
987 * @param pool certificate pool to use when signing
988 * @param signer certificate to use to sign the request
989 * @param digest the signing algorithm in the request, if NULL use the
990 * default signature algorithm,
991 * @param request the encoded request, free with free_heim_octet_string().
992 * @param nonce nonce in the request, free with free_heim_octet_string().
993 *
994 * @return An hx509 error code, see hx509_get_error_string().
995 *
996 * @ingroup hx509_revoke
997 */
998
999 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)1000 hx509_ocsp_request(hx509_context context,
1001 hx509_certs reqcerts,
1002 hx509_certs pool,
1003 hx509_cert signer,
1004 const AlgorithmIdentifier *digest,
1005 heim_octet_string *request,
1006 heim_octet_string *nonce)
1007 {
1008 OCSPRequest req;
1009 size_t size;
1010 int ret;
1011 struct ocsp_add_ctx ctx;
1012 Extensions *es;
1013
1014 memset(&req, 0, sizeof(req));
1015
1016 if (digest == NULL)
1017 digest = _hx509_crypto_default_digest_alg;
1018
1019 ctx.req = &req.tbsRequest;
1020 ctx.certs = pool;
1021 ctx.digest = digest;
1022 ctx.parent = NULL;
1023
1024 ret = hx509_certs_iter_f(context, reqcerts, add_to_req, &ctx);
1025 hx509_cert_free(ctx.parent);
1026 if (ret)
1027 goto out;
1028
1029 if (nonce) {
1030 req.tbsRequest.requestExtensions =
1031 calloc(1, sizeof(*req.tbsRequest.requestExtensions));
1032 if (req.tbsRequest.requestExtensions == NULL) {
1033 ret = ENOMEM;
1034 goto out;
1035 }
1036
1037 es = req.tbsRequest.requestExtensions;
1038
1039 es->val = calloc(es->len, sizeof(es->val[0]));
1040 if (es->val == NULL) {
1041 ret = ENOMEM;
1042 goto out;
1043 }
1044 es->len = 1;
1045 ret = der_copy_oid(&asn1_oid_id_pkix_ocsp_nonce, &es->val[0].extnID);
1046 if (ret) {
1047 free_OCSPRequest(&req);
1048 return ret;
1049 }
1050
1051 es->val[0].extnValue.data = malloc(10);
1052 if (es->val[0].extnValue.data == NULL) {
1053 ret = ENOMEM;
1054 goto out;
1055 }
1056 es->val[0].extnValue.length = 10;
1057
1058 ret = RAND_bytes(es->val[0].extnValue.data,
1059 es->val[0].extnValue.length);
1060 if (ret != 1) {
1061 ret = HX509_CRYPTO_INTERNAL_ERROR;
1062 goto out;
1063 }
1064 ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
1065 if (ret) {
1066 ret = ENOMEM;
1067 goto out;
1068 }
1069 }
1070
1071 ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
1072 &req, &size, ret);
1073 free_OCSPRequest(&req);
1074 if (ret)
1075 goto out;
1076 if (size != request->length)
1077 _hx509_abort("internal ASN.1 encoder error");
1078
1079 return 0;
1080
1081 out:
1082 free_OCSPRequest(&req);
1083 return ret;
1084 }
1085
1086 static char *
printable_time(time_t t)1087 printable_time(time_t t)
1088 {
1089 static char s[128];
1090 char *p;
1091 if ((p = ctime(&t)) == NULL)
1092 strlcpy(s, "?", sizeof(s));
1093 else {
1094 strlcpy(s, p + 4, sizeof(s));
1095 s[20] = 0;
1096 }
1097 return s;
1098 }
1099
1100 /*
1101 *
1102 */
1103
1104 static int
print_ocsp(hx509_context context,struct revoke_ocsp * ocsp,FILE * out)1105 print_ocsp(hx509_context context, struct revoke_ocsp *ocsp, FILE *out)
1106 {
1107 int ret = 0;
1108 size_t i;
1109
1110 fprintf(out, "signer: ");
1111
1112 switch(ocsp->ocsp.tbsResponseData.responderID.element) {
1113 case choice_OCSPResponderID_byName: {
1114 hx509_name n;
1115 char *s;
1116 _hx509_name_from_Name(&ocsp->ocsp.tbsResponseData.responderID.u.byName, &n);
1117 hx509_name_to_string(n, &s);
1118 hx509_name_free(&n);
1119 fprintf(out, " byName: %s\n", s);
1120 free(s);
1121 break;
1122 }
1123 case choice_OCSPResponderID_byKey: {
1124 char *s;
1125 hex_encode(ocsp->ocsp.tbsResponseData.responderID.u.byKey.data,
1126 ocsp->ocsp.tbsResponseData.responderID.u.byKey.length,
1127 &s);
1128 fprintf(out, " byKey: %s\n", s);
1129 free(s);
1130 break;
1131 }
1132 default:
1133 _hx509_abort("choice_OCSPResponderID unknown");
1134 break;
1135 }
1136
1137 fprintf(out, "producedAt: %s\n",
1138 printable_time(ocsp->ocsp.tbsResponseData.producedAt));
1139
1140 fprintf(out, "replies: %d\n", ocsp->ocsp.tbsResponseData.responses.len);
1141
1142 for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) {
1143 const char *status;
1144 switch (ocsp->ocsp.tbsResponseData.responses.val[i].certStatus.element) {
1145 case choice_OCSPCertStatus_good:
1146 status = "good";
1147 break;
1148 case choice_OCSPCertStatus_revoked:
1149 status = "revoked";
1150 break;
1151 case choice_OCSPCertStatus_unknown:
1152 status = "unknown";
1153 break;
1154 default:
1155 status = "element unknown";
1156 }
1157
1158 fprintf(out, "\t%llu. status: %s\n", (unsigned long long)i, status);
1159
1160 fprintf(out, "\tthisUpdate: %s\n",
1161 printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
1162 if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate)
1163 fprintf(out, "\tproducedAt: %s\n",
1164 printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
1165
1166 }
1167
1168 fprintf(out, "appended certs:\n");
1169 if (ocsp->certs)
1170 ret = hx509_certs_iter_f(context, ocsp->certs, hx509_ci_print_names, out);
1171
1172 return ret;
1173 }
1174
1175 static int
print_crl(hx509_context context,struct revoke_crl * crl,FILE * out)1176 print_crl(hx509_context context, struct revoke_crl *crl, FILE *out)
1177 {
1178 {
1179 hx509_name n;
1180 char *s;
1181 _hx509_name_from_Name(&crl->crl.tbsCertList.issuer, &n);
1182 hx509_name_to_string(n, &s);
1183 hx509_name_free(&n);
1184 fprintf(out, " issuer: %s\n", s);
1185 free(s);
1186 }
1187
1188 fprintf(out, " thisUpdate: %s\n",
1189 printable_time(_hx509_Time2time_t(&crl->crl.tbsCertList.thisUpdate)));
1190
1191 return 0;
1192 }
1193
1194
1195 /*
1196 *
1197 */
1198
1199 int
hx509_revoke_print(hx509_context context,hx509_revoke_ctx ctx,FILE * out)1200 hx509_revoke_print(hx509_context context,
1201 hx509_revoke_ctx ctx,
1202 FILE *out)
1203 {
1204 int saved_ret = 0, ret;
1205 size_t n;
1206
1207 for (n = 0; n < ctx->ocsps.len; n++) {
1208 struct revoke_ocsp *ocsp = &ctx->ocsps.val[n];
1209
1210 fprintf(out, "OCSP %s\n", ocsp->path);
1211
1212 ret = print_ocsp(context, ocsp, out);
1213 if (ret) {
1214 fprintf(out, "failure printing OCSP: %d\n", ret);
1215 saved_ret = ret;
1216 }
1217 }
1218
1219 for (n = 0; n < ctx->crls.len; n++) {
1220 struct revoke_crl *crl = &ctx->crls.val[n];
1221
1222 fprintf(out, "CRL %s\n", crl->path);
1223
1224 ret = print_crl(context, crl, out);
1225 if (ret) {
1226 fprintf(out, "failure printing CRL: %d\n", ret);
1227 saved_ret = ret;
1228 }
1229 }
1230 return saved_ret;
1231
1232 }
1233
1234 /**
1235 * Print the OCSP reply stored in a file.
1236 *
1237 * @param context a hx509 context
1238 * @param path path to a file with a OCSP reply
1239 * @param out the out FILE descriptor to print the reply on
1240 *
1241 * @return An hx509 error code, see hx509_get_error_string().
1242 *
1243 * @ingroup hx509_revoke
1244 */
1245
1246 int
hx509_revoke_ocsp_print(hx509_context context,const char * path,FILE * out)1247 hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
1248 {
1249 struct revoke_ocsp ocsp;
1250 int ret;
1251
1252 if (out == NULL)
1253 out = stdout;
1254
1255 memset(&ocsp, 0, sizeof(ocsp));
1256
1257 ocsp.path = strdup(path);
1258 if (ocsp.path == NULL)
1259 return ENOMEM;
1260
1261 ret = load_ocsp(context, &ocsp);
1262 if (ret) {
1263 free_ocsp(&ocsp);
1264 return ret;
1265 }
1266
1267 ret = print_ocsp(context, &ocsp, out);
1268
1269 free_ocsp(&ocsp);
1270 return ret;
1271 }
1272
1273 /**
1274 * Verify that the certificate is part of the OCSP reply and it's not
1275 * expired. Doesn't verify signature the OCSP reply or it's done by a
1276 * authorized sender, that is assumed to be already done.
1277 *
1278 * @param context a hx509 context
1279 * @param now the time right now, if 0, use the current time.
1280 * @param cert the certificate to verify
1281 * @param flags flags control the behavior
1282 * @param data pointer to the encode ocsp reply
1283 * @param length the length of the encode ocsp reply
1284 * @param expiration return the time the OCSP will expire and need to
1285 * be rechecked.
1286 *
1287 * @return An hx509 error code, see hx509_get_error_string().
1288 *
1289 * @ingroup hx509_verify
1290 */
1291
1292 int
hx509_ocsp_verify(hx509_context context,time_t now,hx509_cert cert,int flags,const void * data,size_t length,time_t * expiration)1293 hx509_ocsp_verify(hx509_context context,
1294 time_t now,
1295 hx509_cert cert,
1296 int flags,
1297 const void *data, size_t length,
1298 time_t *expiration)
1299 {
1300 const Certificate *c = _hx509_get_cert(cert);
1301 OCSPBasicOCSPResponse basic;
1302 int ret;
1303 size_t i;
1304
1305 if (now == 0)
1306 now = time(NULL);
1307
1308 *expiration = 0;
1309
1310 ret = parse_ocsp_basic(data, length, &basic);
1311 if (ret) {
1312 hx509_set_error_string(context, 0, ret,
1313 "Failed to parse OCSP response");
1314 return ret;
1315 }
1316
1317 for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
1318
1319 ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
1320 &c->tbsCertificate.serialNumber);
1321 if (ret != 0)
1322 continue;
1323
1324 /* verify issuer hashes hash */
1325 ret = _hx509_verify_signature(context,
1326 NULL,
1327 &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
1328 &c->tbsCertificate.issuer._save,
1329 &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
1330 if (ret != 0)
1331 continue;
1332
1333 switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
1334 case choice_OCSPCertStatus_good:
1335 break;
1336 case choice_OCSPCertStatus_revoked:
1337 case choice_OCSPCertStatus_unknown:
1338 continue;
1339 }
1340
1341 /* don't allow the update to be in the future */
1342 if (basic.tbsResponseData.responses.val[i].thisUpdate >
1343 now + context->ocsp_time_diff)
1344 continue;
1345
1346 /* don't allow the next update to be in the past */
1347 if (basic.tbsResponseData.responses.val[i].nextUpdate) {
1348 if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
1349 continue;
1350 *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
1351 } else
1352 *expiration = now;
1353
1354 free_OCSPBasicOCSPResponse(&basic);
1355 return 0;
1356 }
1357
1358 free_OCSPBasicOCSPResponse(&basic);
1359
1360 {
1361 hx509_name name;
1362 char *subject;
1363
1364 ret = hx509_cert_get_subject(cert, &name);
1365 if (ret) {
1366 hx509_clear_error_string(context);
1367 goto out;
1368 }
1369 ret = hx509_name_to_string(name, &subject);
1370 hx509_name_free(&name);
1371 if (ret) {
1372 hx509_clear_error_string(context);
1373 goto out;
1374 }
1375 hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
1376 "Certificate %s not in OCSP response "
1377 "or not good",
1378 subject);
1379 free(subject);
1380 }
1381 out:
1382 return HX509_CERT_NOT_IN_OCSP;
1383 }
1384
1385 struct hx509_crl {
1386 hx509_certs revoked;
1387 time_t expire;
1388 };
1389
1390 /**
1391 * Create a CRL context. Use hx509_crl_free() to free the CRL context.
1392 *
1393 * @param context a hx509 context.
1394 * @param crl return pointer to a newly allocated CRL context.
1395 *
1396 * @return An hx509 error code, see hx509_get_error_string().
1397 *
1398 * @ingroup hx509_verify
1399 */
1400
1401 int
hx509_crl_alloc(hx509_context context,hx509_crl * crl)1402 hx509_crl_alloc(hx509_context context, hx509_crl *crl)
1403 {
1404 int ret;
1405
1406 *crl = calloc(1, sizeof(**crl));
1407 if (*crl == NULL) {
1408 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1409 return ENOMEM;
1410 }
1411
1412 ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
1413 if (ret) {
1414 free(*crl);
1415 *crl = NULL;
1416 return ret;
1417 }
1418 (*crl)->expire = 0;
1419 return ret;
1420 }
1421
1422 /**
1423 * Add revoked certificate to an CRL context.
1424 *
1425 * @param context a hx509 context.
1426 * @param crl the CRL to add the revoked certificate to.
1427 * @param certs keyset of certificate to revoke.
1428 *
1429 * @return An hx509 error code, see hx509_get_error_string().
1430 *
1431 * @ingroup hx509_verify
1432 */
1433
1434 int
hx509_crl_add_revoked_certs(hx509_context context,hx509_crl crl,hx509_certs certs)1435 hx509_crl_add_revoked_certs(hx509_context context,
1436 hx509_crl crl,
1437 hx509_certs certs)
1438 {
1439 return hx509_certs_merge(context, crl->revoked, certs);
1440 }
1441
1442 /**
1443 * Set the lifetime of a CRL context.
1444 *
1445 * @param context a hx509 context.
1446 * @param crl a CRL context
1447 * @param delta delta time the certificate is valid, library adds the
1448 * current time to this.
1449 *
1450 * @return An hx509 error code, see hx509_get_error_string().
1451 *
1452 * @ingroup hx509_verify
1453 */
1454
1455 int
hx509_crl_lifetime(hx509_context context,hx509_crl crl,int delta)1456 hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
1457 {
1458 crl->expire = time(NULL) + delta;
1459 return 0;
1460 }
1461
1462 /**
1463 * Free a CRL context.
1464 *
1465 * @param context a hx509 context.
1466 * @param crl a CRL context to free.
1467 *
1468 * @ingroup hx509_verify
1469 */
1470
1471 void
hx509_crl_free(hx509_context context,hx509_crl * crl)1472 hx509_crl_free(hx509_context context, hx509_crl *crl)
1473 {
1474 if (*crl == NULL)
1475 return;
1476 hx509_certs_free(&(*crl)->revoked);
1477 memset(*crl, 0, sizeof(**crl));
1478 free(*crl);
1479 *crl = NULL;
1480 }
1481
1482 static int
add_revoked(hx509_context context,void * ctx,hx509_cert cert)1483 add_revoked(hx509_context context, void *ctx, hx509_cert cert)
1484 {
1485 TBSCRLCertList *c = ctx;
1486 unsigned int num;
1487 void *ptr;
1488 int ret;
1489
1490 num = c->revokedCertificates->len;
1491 ptr = realloc(c->revokedCertificates->val,
1492 (num + 1) * sizeof(c->revokedCertificates->val[0]));
1493 if (ptr == NULL) {
1494 hx509_clear_error_string(context);
1495 return ENOMEM;
1496 }
1497 c->revokedCertificates->val = ptr;
1498
1499 ret = hx509_cert_get_serialnumber(cert,
1500 &c->revokedCertificates->val[num].userCertificate);
1501 if (ret) {
1502 hx509_clear_error_string(context);
1503 return ret;
1504 }
1505 c->revokedCertificates->val[num].revocationDate.element =
1506 choice_Time_generalTime;
1507 c->revokedCertificates->val[num].revocationDate.u.generalTime =
1508 time(NULL) - 3600 * 24;
1509 c->revokedCertificates->val[num].crlEntryExtensions = NULL;
1510
1511 c->revokedCertificates->len++;
1512
1513 return 0;
1514 }
1515
1516 /**
1517 * Sign a CRL and return an encode certificate.
1518 *
1519 * @param context a hx509 context.
1520 * @param signer certificate to sign the CRL with
1521 * @param crl the CRL to sign
1522 * @param os return the signed and encoded CRL, free with
1523 * free_heim_octet_string()
1524 *
1525 * @return An hx509 error code, see hx509_get_error_string().
1526 *
1527 * @ingroup hx509_verify
1528 */
1529
1530 int
hx509_crl_sign(hx509_context context,hx509_cert signer,hx509_crl crl,heim_octet_string * os)1531 hx509_crl_sign(hx509_context context,
1532 hx509_cert signer,
1533 hx509_crl crl,
1534 heim_octet_string *os)
1535 {
1536 const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
1537 CRLCertificateList c;
1538 size_t size;
1539 int ret;
1540 hx509_private_key signerkey;
1541
1542 memset(&c, 0, sizeof(c));
1543
1544 signerkey = _hx509_cert_private_key(signer);
1545 if (signerkey == NULL) {
1546 ret = HX509_PRIVATE_KEY_MISSING;
1547 hx509_set_error_string(context, 0, ret,
1548 "Private key missing for CRL signing");
1549 return ret;
1550 }
1551
1552 c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
1553 if (c.tbsCertList.version == NULL) {
1554 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1555 return ENOMEM;
1556 }
1557
1558 *c.tbsCertList.version = 1;
1559
1560 ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
1561 if (ret) {
1562 hx509_clear_error_string(context);
1563 goto out;
1564 }
1565
1566 ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
1567 &c.tbsCertList.issuer);
1568 if (ret) {
1569 hx509_clear_error_string(context);
1570 goto out;
1571 }
1572
1573 c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
1574 c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
1575
1576 c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
1577 if (c.tbsCertList.nextUpdate == NULL) {
1578 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1579 ret = ENOMEM;
1580 goto out;
1581 }
1582
1583 {
1584 time_t next = crl->expire;
1585 if (next == 0)
1586 next = time(NULL) + 24 * 3600 * 365;
1587
1588 c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
1589 c.tbsCertList.nextUpdate->u.generalTime = next;
1590 }
1591
1592 c.tbsCertList.revokedCertificates =
1593 calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
1594 if (c.tbsCertList.revokedCertificates == NULL) {
1595 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
1596 ret = ENOMEM;
1597 goto out;
1598 }
1599 c.tbsCertList.crlExtensions = NULL;
1600
1601 ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList);
1602 if (ret)
1603 goto out;
1604
1605 /* if not revoked certs, remove OPTIONAL entry */
1606 if (c.tbsCertList.revokedCertificates->len == 0) {
1607 free(c.tbsCertList.revokedCertificates);
1608 c.tbsCertList.revokedCertificates = NULL;
1609 }
1610
1611 ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
1612 &c.tbsCertList, &size, ret);
1613 if (ret) {
1614 hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
1615 goto out;
1616 }
1617 if (size != os->length)
1618 _hx509_abort("internal ASN.1 encoder error");
1619
1620
1621 ret = _hx509_create_signature_bitstring(context,
1622 signerkey,
1623 sigalg,
1624 os,
1625 &c.signatureAlgorithm,
1626 &c.signatureValue);
1627 free(os->data);
1628 if (ret) {
1629 hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
1630 goto out;
1631 }
1632
1633 ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
1634 &c, &size, ret);
1635 if (ret) {
1636 hx509_set_error_string(context, 0, ret, "failed to encode CRL");
1637 goto out;
1638 }
1639 if (size != os->length)
1640 _hx509_abort("internal ASN.1 encoder error");
1641
1642 free_CRLCertificateList(&c);
1643
1644 return 0;
1645
1646 out:
1647 free_CRLCertificateList(&c);
1648 return ret;
1649 }
1650