hcrypto: Add X25519
[heimdal.git] / kdc / ca.c
blobea4183ba967eb3ffa0f487e4f1220e9d8675b3dd
1 /*
2 * Copyright (c) 2019 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "kdc_locl.h"
35 #include <hex.h>
36 #include <rfc2459_asn1.h>
37 #include <hx509.h>
38 #include <hx509_err.h>
40 #include <stdarg.h>
43 * This file implements a singular utility function `kdc_issue_certificate()'
44 * for certificate issuance for kx509 and bx509, which takes a principal name,
45 * an `hx509_request' resulting from parsing a CSR and possibly adding
46 * SAN/EKU/KU extensions, the start/end times of request's authentication
47 * method, and whether to include a full certificate chain in the result.
50 typedef enum {
51 CERT_NOTSUP = 0,
52 CERT_CLIENT = 1,
53 CERT_SERVER = 2,
54 CERT_MIXED = 3
55 } cert_type;
57 static void
58 frees(char **s)
60 free(*s);
61 *s = NULL;
64 static krb5_error_code
65 count_sans(hx509_request req, size_t *n)
67 size_t i;
68 char *s = NULL;
69 int ret = 0;
71 *n = 0;
72 for (i = 0; ret == 0; i++) {
73 hx509_san_type san_type;
75 frees(&s);
76 ret = hx509_request_get_san(req, i, &san_type, &s);
77 if (ret)
78 break;
79 switch (san_type) {
80 case HX509_SAN_TYPE_DNSNAME:
81 case HX509_SAN_TYPE_EMAIL:
82 case HX509_SAN_TYPE_XMPP:
83 case HX509_SAN_TYPE_PKINIT:
84 case HX509_SAN_TYPE_MS_UPN:
85 (*n)++;
86 break;
87 default:
88 ret = ENOTSUP;
90 frees(&s);
92 return ret == HX509_NO_ITEM ? 0 : ret;
95 static int
96 has_sans(hx509_request req)
98 hx509_san_type san_type;
99 char *s = NULL;
100 int ret = hx509_request_get_san(req, 0, &san_type, &s);
102 frees(&s);
103 return ret == HX509_NO_ITEM ? 0 : 1;
106 static cert_type
107 characterize_cprinc(krb5_context context,
108 krb5_principal cprinc)
110 unsigned int ncomp = krb5_principal_get_num_comp(context, cprinc);
111 const char *comp1 = krb5_principal_get_comp_string(context, cprinc, 1);
113 switch (ncomp) {
114 case 1:
115 return CERT_CLIENT;
116 case 2:
117 if (strchr(comp1, '.') == NULL)
118 return CERT_CLIENT;
119 return CERT_SERVER;
120 case 3:
121 if (strchr(comp1, '.'))
122 return CERT_SERVER;
123 return CERT_NOTSUP;
124 default:
125 return CERT_NOTSUP;
129 /* Characterize request as client or server cert req */
130 static cert_type
131 characterize(krb5_context context,
132 krb5_principal cprinc,
133 hx509_request req)
135 krb5_error_code ret = 0;
136 cert_type res = CERT_NOTSUP;
137 size_t i;
138 char *s = NULL;
139 int want_ekus = 0;
141 if (!has_sans(req))
142 return characterize_cprinc(context, cprinc);
144 for (i = 0; ret == 0; i++) {
145 heim_oid oid;
147 frees(&s);
148 ret = hx509_request_get_eku(req, i, &s);
149 if (ret)
150 break;
152 want_ekus = 1;
153 ret = der_parse_heim_oid(s, ".", &oid);
154 if (ret)
155 break;
157 * If the client wants only a server certificate, then we'll be
158 * willing to issue one that may be longer-lived than the client's
159 * ticket/token.
161 * There may be other server EKUs, but these are the ones we know
162 * of.
164 if (der_heim_oid_cmp(&asn1_oid_id_pkix_kp_serverAuth, &oid) &&
165 der_heim_oid_cmp(&asn1_oid_id_pkix_kp_OCSPSigning, &oid) &&
166 der_heim_oid_cmp(&asn1_oid_id_pkix_kp_secureShellServer, &oid))
167 res |= CERT_CLIENT;
168 else
169 res |= CERT_SERVER;
170 der_free_oid(&oid);
172 frees(&s);
173 if (ret == HX509_NO_ITEM)
174 ret = 0;
176 for (i = 0; ret == 0; i++) {
177 hx509_san_type san_type;
179 frees(&s);
180 ret = hx509_request_get_san(req, i, &san_type, &s);
181 if (ret)
182 break;
183 switch (san_type) {
184 case HX509_SAN_TYPE_DNSNAME:
185 if (!want_ekus)
186 res |= CERT_SERVER;
187 break;
188 case HX509_SAN_TYPE_EMAIL:
189 case HX509_SAN_TYPE_XMPP:
190 case HX509_SAN_TYPE_PKINIT:
191 case HX509_SAN_TYPE_MS_UPN:
192 if (!want_ekus)
193 res |= CERT_CLIENT;
194 break;
195 default:
196 ret = ENOTSUP;
198 if (ret)
199 break;
201 frees(&s);
202 if (ret == HX509_NO_ITEM)
203 ret = 0;
204 return ret ? CERT_NOTSUP : res;
208 * Get a configuration sub-tree for kx509 based on what's being requested and
209 * by whom.
211 * We have a number of cases:
213 * - default certificate (no CSR used, or no certificate extensions requested)
214 * - for client principals
215 * - for service principals
216 * - client certificate requested (CSR used and client-y SANs/EKUs requested)
217 * - server certificate requested (CSR used and server-y SANs/EKUs requested)
218 * - mixed client/server certificate requested (...)
220 static const krb5_config_binding *
221 get_cf(krb5_context context,
222 krb5_kdc_configuration *config,
223 hx509_request req,
224 krb5_principal cprinc)
226 krb5_error_code ret;
227 const krb5_config_binding *cf = NULL;
228 unsigned int ncomp = krb5_principal_get_num_comp(context, cprinc);
229 const char *realm = krb5_principal_get_realm(context, cprinc);
230 const char *comp0 = krb5_principal_get_comp_string(context, cprinc, 0);
231 const char *comp1 = krb5_principal_get_comp_string(context, cprinc, 1);
232 const char *label = NULL;
233 const char *svc = NULL;
234 const char *def = NULL;
235 cert_type certtype = CERT_NOTSUP;
236 size_t nsans = 0;
238 if (ncomp == 0) {
239 kdc_log(context, config, 5, "Client principal has no components!");
240 krb5_set_error_message(context, ENOTSUP,
241 "Client principal has no components!");
242 return NULL;
245 if ((ret = count_sans(req, &nsans)) ||
246 (certtype = characterize(context, cprinc, req)) == CERT_NOTSUP) {
247 kdc_log(context, config, 5, "Could not characterize CSR");
248 krb5_set_error_message(context, ret, "Could not characterize CSR");
249 return NULL;
252 if (nsans) {
253 def = "custom";
254 /* Client requested some certificate extension, a SAN or EKU */
255 switch (certtype) {
256 case CERT_MIXED: label = "mixed"; break;
257 case CERT_CLIENT: label = "client"; break;
258 case CERT_SERVER: label = "server"; break;
259 default: return NULL;
261 } else {
262 def = "default";
263 /* Default certificate desired */
264 if (ncomp == 1) {
265 label = "user";
266 } else if (ncomp == 2 && strcmp(comp1, "root") == 0) {
267 label = "root_user";
268 } else if (ncomp == 2 && strcmp(comp1, "admin") == 0) {
269 label = "admin_user";
270 } else if (strchr(comp1, '.')) {
271 label = "hostbased_service";
272 svc = comp0;
273 } else {
274 label = "other";
278 if (strcmp(config->app, "kdc") == 0)
279 cf = krb5_config_get_list(context, NULL, config->app, "realms", realm,
280 "kx509", label, svc, NULL);
281 else
282 cf = krb5_config_get_list(context, NULL, config->app, "realms", realm,
283 label, svc, NULL);
284 if (cf == NULL) {
285 kdc_log(context, config, 3,
286 "No %s configuration for %s %s certificates [%s] realm "
287 "-> %s -> kx509 -> %s%s%s",
288 strcmp(config->app, "bx509") == 0 ? "bx509" : "kx509",
289 def, label, config->app, realm, label,
290 svc ? " -> " : "", svc ? svc : "");
291 krb5_set_error_message(context, KRB5KDC_ERR_POLICY,
292 "No %s configuration for %s %s certificates [%s] realm "
293 "-> %s -> kx509 -> %s%s%s",
294 strcmp(config->app, "bx509") == 0 ? "bx509" : "kx509",
295 def, label, config->app, realm, label,
296 svc ? " -> " : "", svc ? svc : "");
298 return cf;
302 * Find and set a certificate template using a configuration sub-tree
303 * appropriate to the requesting principal.
305 * This allows for the specification of the following in configuration:
307 * - certificates as templates, with ${var} tokens in subjectName attribute
308 * values that will be expanded later
309 * - a plain string with ${var} tokens to use as the subjectName
310 * - EKUs
311 * - whether to include a PKINIT SAN
313 static krb5_error_code
314 set_template(krb5_context context,
315 krb5_kdc_configuration *config,
316 const krb5_config_binding *cf,
317 hx509_ca_tbs tbs)
319 krb5_error_code ret = 0;
320 const char *cert_template = NULL;
321 const char *subj_name = NULL;
322 char **ekus = NULL;
324 if (cf == NULL)
325 return KRB5KDC_ERR_POLICY; /* Can't happen */
327 cert_template = krb5_config_get_string(context, cf, "template_cert", NULL);
328 subj_name = krb5_config_get_string(context, cf, "subject_name", NULL);
329 ekus = krb5_config_get_strings(context, cf, "ekus", NULL);
331 if (cert_template) {
332 hx509_certs certs;
333 hx509_cert template;
335 ret = hx509_certs_init(context->hx509ctx, cert_template, 0,
336 NULL, &certs);
337 if (ret == 0)
338 ret = hx509_get_one_cert(context->hx509ctx, certs, &template);
339 hx509_certs_free(&certs);
340 if (ret) {
341 kdc_log(context, config, 1,
342 "Failed to load certificate template from %s",
343 cert_template);
344 krb5_set_error_message(context, KRB5KDC_ERR_POLICY,
345 "Failed to load certificate template from "
346 "%s", cert_template);
347 return ret;
351 * Only take the subjectName, the keyUsage, and EKUs from the template
352 * certificate.
354 ret = hx509_ca_tbs_set_template(context->hx509ctx, tbs,
355 HX509_CA_TEMPLATE_SUBJECT |
356 HX509_CA_TEMPLATE_KU |
357 HX509_CA_TEMPLATE_EKU,
358 template);
359 hx509_cert_free(template);
360 if (ret)
361 return ret;
364 if (subj_name) {
365 hx509_name dn = NULL;
367 ret = hx509_parse_name(context->hx509ctx, subj_name, &dn);
368 if (ret == 0)
369 ret = hx509_ca_tbs_set_subject(context->hx509ctx, tbs, dn);
370 hx509_name_free(&dn);
371 if (ret)
372 return ret;
375 if (cert_template == NULL && subj_name == NULL) {
376 hx509_name dn = NULL;
378 ret = hx509_empty_name(context->hx509ctx, &dn);
379 if (ret == 0)
380 ret = hx509_ca_tbs_set_subject(context->hx509ctx, tbs, dn);
381 hx509_name_free(&dn);
382 if (ret)
383 return ret;
386 if (ekus) {
387 size_t i;
389 for (i = 0; ret == 0 && ekus[i]; i++) {
390 heim_oid oid = { 0, 0 };
392 if ((ret = der_find_or_parse_heim_oid(ekus[i], ".", &oid)) == 0)
393 ret = hx509_ca_tbs_add_eku(context->hx509ctx, tbs, &oid);
394 der_free_oid(&oid);
396 krb5_config_free_strings(ekus);
400 * XXX A KeyUsage template would be nice, but it needs some smarts to
401 * remove, e.g., encipherOnly, decipherOnly, keyEncipherment, if the SPKI
402 * algorithm does not support encryption. The same logic should be added
403 * to hx509_ca_tbs_set_template()'s HX509_CA_TEMPLATE_KU functionality.
405 return ret;
409 * Find and set a certificate template, set "variables" in `env', and add add
410 * default SANs/EKUs as appropriate.
412 * TODO:
413 * - lookup a template for the client principal in its HDB entry
414 * - lookup subjectName, SANs for a principal in its HDB entry
415 * - lookup a host-based client principal's HDB entry and add its canonical
416 * name / aliases as dNSName SANs
417 * (this would have to be if requested by the client, perhaps)
419 static krb5_error_code
420 set_tbs(krb5_context context,
421 krb5_kdc_configuration *config,
422 const krb5_config_binding *cf,
423 hx509_request req,
424 krb5_principal cprinc,
425 hx509_env *env,
426 hx509_ca_tbs tbs)
428 krb5_error_code ret;
429 unsigned int ncomp = krb5_principal_get_num_comp(context, cprinc);
430 const char *realm = krb5_principal_get_realm(context, cprinc);
431 const char *comp0 = krb5_principal_get_comp_string(context, cprinc, 0);
432 const char *comp1 = krb5_principal_get_comp_string(context, cprinc, 1);
433 const char *comp2 = krb5_principal_get_comp_string(context, cprinc, 2);
434 char *princ_no_realm = NULL;
435 char *princ = NULL;
437 ret = krb5_unparse_name_flags(context, cprinc, 0, &princ);
438 if (ret == 0)
439 ret = krb5_unparse_name_flags(context, cprinc,
440 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
441 &princ_no_realm);
442 if (ret == 0)
443 ret = hx509_env_add(context->hx509ctx, env,
444 "principal-name-without-realm", princ_no_realm);
445 if (ret == 0)
446 ret = hx509_env_add(context->hx509ctx, env, "principal-name", princ);
447 if (ret == 0)
448 ret = hx509_env_add(context->hx509ctx, env, "principal-name-realm",
449 realm);
451 /* Populate requested certificate extensions from CSR/CSRPlus if allowed */
452 ret = hx509_ca_tbs_set_from_csr(context->hx509ctx, tbs, req);
453 if (ret == 0)
454 ret = set_template(context, config, cf, tbs);
457 * Optionally add PKINIT SAN.
459 * Adding an id-pkinit-san means the client can use the certificate to
460 * initiate PKINIT. That might seem odd, but it enables a sort of PKIX
461 * credential delegation by allowing forwarded Kerberos tickets to be
462 * used to acquire PKIX credentials. Thus this can work:
464 * PKIX (w/ HW token) -> Kerberos ->
465 * PKIX (w/ softtoken) -> Kerberos ->
466 * PKIX (w/ softtoken) -> Kerberos ->
467 * ...
469 * Note that we may not have added the PKINIT EKU -- that depends on the
470 * template, and host-based service templates might well not include it.
472 if (ret == 0 && !has_sans(req) &&
473 krb5_config_get_bool_default(context, cf, FALSE, "include_pkinit_san",
474 NULL)) {
475 ret = hx509_ca_tbs_add_san_pkinit(context->hx509ctx, tbs, princ);
478 if (ret)
479 goto out;
481 if (ncomp == 1) {
482 const char *email_domain;
484 ret = hx509_env_add(context->hx509ctx, env, "principal-component0",
485 princ_no_realm);
488 * If configured, include an rfc822Name that's just the client's
489 * principal name sans realm @ configured email domain.
491 if (ret == 0 && !has_sans(req) &&
492 (email_domain = krb5_config_get_string(context, cf, "email_domain",
493 NULL))) {
494 char *email;
496 if (asprintf(&email, "%s@%s", princ_no_realm, email_domain) == -1 ||
497 email == NULL)
498 goto enomem;
499 ret = hx509_ca_tbs_add_san_rfc822name(context->hx509ctx, tbs, email);
500 free(email);
502 } else if (ncomp == 2 || ncomp == 3) {
504 * 2- and 3-component principal name.
506 * We do not have a reliable name-type indicator. If the second
507 * component has a '.' in it then we'll assume that the name is a
508 * host-based (2-component) or domain-based (3-component) service
509 * principal name. Else we'll assume it's a two-component admin-style
510 * username.
513 ret = hx509_env_add(context->hx509ctx, env, "principal-component0",
514 comp0);
515 if (ret == 0)
516 ret = hx509_env_add(context->hx509ctx, env, "principal-component1",
517 comp1);
518 if (ret == 0 && ncomp == 3)
519 ret = hx509_env_add(context->hx509ctx, env, "principal-component2",
520 comp2);
521 if (ret == 0 && strchr(comp1, '.')) {
522 /* Looks like host-based or domain-based service */
523 ret = hx509_env_add(context->hx509ctx, env,
524 "principal-service-name", comp0);
525 if (ret == 0)
526 ret = hx509_env_add(context->hx509ctx, env, "principal-host-name", comp1);
527 if (ret == 0 && ncomp == 3)
528 ret = hx509_env_add(context->hx509ctx, env, "principal-domain-name", comp2);
529 if (ret == 0 && !has_sans(req) &&
530 krb5_config_get_bool_default(context, cf, FALSE,
531 "include_dnsname_san", NULL)) {
532 ret = hx509_ca_tbs_add_san_hostname(context->hx509ctx, tbs, comp1);
535 } else {
536 kdc_log(context, config, 5, "kx509/bx509 client %s has too many "
537 "components!", princ);
538 krb5_set_error_message(context, ret = KRB5KDC_ERR_POLICY,
539 "kx509/bx509 client %s has too many "
540 "components!", princ);
543 out:
544 if (ret == ENOMEM)
545 goto enomem;
546 krb5_xfree(princ_no_realm);
547 krb5_xfree(princ);
548 return ret;
550 enomem:
551 kdc_log(context, config, 0,
552 "Could not set up TBSCertificate: Out of memory");
553 ret = krb5_enomem(context);
554 goto out;
557 static krb5_error_code
558 tbs_set_times(krb5_context context,
559 const krb5_config_binding *cf,
560 krb5_times *auth_times,
561 time_t req_life,
562 hx509_ca_tbs tbs)
564 time_t now = time(NULL);
565 time_t endtime = auth_times->endtime;
566 time_t starttime = auth_times->starttime ?
567 auth_times->starttime : now - 5 * 60;
568 time_t fudge =
569 krb5_config_get_time_default(context, cf, 5 * 24 * 3600,
570 "force_cert_lifetime", NULL);
571 time_t clamp =
572 krb5_config_get_time_default(context, cf, 0, "max_cert_lifetime",
573 NULL);
575 if (fudge && now + fudge > endtime)
576 endtime = now + fudge;
578 if (req_life && req_life < endtime - now)
579 endtime = now + req_life;
581 if (clamp && clamp < endtime - now)
582 endtime = now + clamp;
584 hx509_ca_tbs_set_notAfter(context->hx509ctx, tbs, endtime);
585 hx509_ca_tbs_set_notBefore(context->hx509ctx, tbs, starttime);
586 return 0;
590 * Build a certifate for `principal' and its CSR.
592 krb5_error_code
593 kdc_issue_certificate(krb5_context context,
594 krb5_kdc_configuration *config,
595 hx509_request req,
596 krb5_principal cprinc,
597 krb5_times *auth_times,
598 int send_chain,
599 hx509_certs *out)
601 const krb5_config_binding *cf;
602 krb5_error_code ret;
603 const char *ca;
604 hx509_ca_tbs tbs = NULL;
605 hx509_certs chain = NULL;
606 hx509_cert signer = NULL;
607 hx509_cert cert = NULL;
608 hx509_env env = NULL;
609 KeyUsage ku;
611 *out = NULL;
612 /* Force KU */
613 ku = int2KeyUsage(0);
614 ku.digitalSignature = 1;
615 hx509_request_authorize_ku(req, ku);
617 /* Get configuration */
618 if ((cf = get_cf(context, config, req, cprinc)) == NULL)
619 return KRB5KDC_ERR_POLICY;
620 if ((ca = krb5_config_get_string(context, cf, "ca", NULL)) == NULL) {
621 kdc_log(context, config, 3, "No kx509 CA issuer credential specified");
622 krb5_set_error_message(context, ret = KRB5KDC_ERR_POLICY,
623 "No kx509 CA issuer credential specified");
624 return ret;
627 ret = hx509_ca_tbs_init(context->hx509ctx, &tbs);
628 if (ret) {
629 kdc_log(context, config, 0,
630 "Failed to create certificate: Out of memory");
631 return ret;
634 /* Lookup a template and set things in `env' and `tbs' as appropriate */
635 if (ret == 0)
636 ret = set_tbs(context, config, cf, req, cprinc, &env, tbs);
638 /* Populate generic template "env" variables */
641 * The `tbs' and `env' are now complete as to naming and EKUs.
643 * We check that the `tbs' is not name-less, after which all remaining
644 * failures here will not be policy failures. So we also log the intent to
645 * issue a certificate now.
647 if (ret == 0 && hx509_name_is_null_p(hx509_ca_tbs_get_name(tbs)) &&
648 !has_sans(req)) {
649 kdc_log(context, config, 3,
650 "Not issuing certificate because it would have no names");
651 krb5_set_error_message(context, ret = KRB5KDC_ERR_POLICY,
652 "Not issuing certificate because it "
653 "would have no names");
655 if (ret)
656 goto out;
659 * Still to be done below:
661 * - set certificate spki
662 * - set certificate validity
663 * - expand variables in certificate subject name template
664 * - sign certificate
665 * - encode certificate and chain
668 /* Load the issuer certificate and private key */
670 hx509_certs certs;
671 hx509_query *q;
673 ret = hx509_certs_init(context->hx509ctx, ca, 0, NULL, &certs);
674 if (ret) {
675 kdc_log(context, config, 1,
676 "Failed to load CA certificate and private key %s", ca);
677 krb5_set_error_message(context, ret, "Failed to load CA "
678 "certificate and private key %s", ca);
679 goto out;
681 ret = hx509_query_alloc(context->hx509ctx, &q);
682 if (ret) {
683 hx509_certs_free(&certs);
684 goto out;
687 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
688 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
690 ret = hx509_certs_find(context->hx509ctx, certs, q, &signer);
691 hx509_query_free(context->hx509ctx, q);
692 hx509_certs_free(&certs);
693 if (ret) {
694 kdc_log(context, config, 1,
695 "Failed to find a CA certificate in %s", ca);
696 krb5_set_error_message(context, ret,
697 "Failed to find a CA certificate in %s",
698 ca);
699 goto out;
703 /* Populate the subject public key in the TBS context */
705 SubjectPublicKeyInfo spki;
707 ret = hx509_request_get_SubjectPublicKeyInfo(context->hx509ctx,
708 req, &spki);
709 if (ret == 0)
710 ret = hx509_ca_tbs_set_spki(context->hx509ctx, tbs, &spki);
711 free_SubjectPublicKeyInfo(&spki);
712 if (ret)
713 goto out;
716 /* Work out cert expiration */
717 if (ret == 0)
718 ret = tbs_set_times(context, cf, auth_times, 0 /* XXX req_life */, tbs);
720 /* Expand the subjectName template in the TBS using the env */
721 if (ret == 0)
722 ret = hx509_ca_tbs_subject_expand(context->hx509ctx, tbs, env);
723 hx509_env_free(&env);
725 /* All done with the TBS, sign/issue the certificate */
726 ret = hx509_ca_sign(context->hx509ctx, tbs, signer, &cert);
727 if (ret)
728 goto out;
731 * Gather the certificate and chain into a MEMORY store, being careful not
732 * to include private keys in the chain.
734 * We could have specified a separate configuration parameter for an hx509
735 * store meant to have only the chain and no private keys, but expecting
736 * the full chain in the issuer credential store and copying only the certs
737 * (but not the private keys) is safer and easier to configure.
739 ret = hx509_certs_init(context->hx509ctx, "MEMORY:certs",
740 HX509_CERTS_NO_PRIVATE_KEYS, NULL, out);
741 if (ret == 0)
742 ret = hx509_certs_add(context->hx509ctx, *out, cert);
743 if (ret == 0 && send_chain) {
744 ret = hx509_certs_init(context->hx509ctx, ca,
745 HX509_CERTS_NO_PRIVATE_KEYS, NULL, &chain);
746 if (ret == 0)
747 ret = hx509_certs_merge(context->hx509ctx, *out, chain);
750 out:
751 hx509_certs_free(&chain);
752 if (env)
753 hx509_env_free(&env);
754 if (tbs)
755 hx509_ca_tbs_free(&tbs);
756 if (cert)
757 hx509_cert_free(cert);
758 if (signer)
759 hx509_cert_free(signer);
760 if (ret)
761 hx509_certs_free(out);
762 return ret;