1 /* SPDX-License-Identifier: BSD-3-Clause */
3 * swtpm_localca.c: A tool for creating TPM 1.2 and TPM 2 certificates localy or using pkcs11
5 * Author: Stefan Berger, stefanb@linux.ibm.com
7 * Copyright (c) IBM Corporation, 2021
23 #include <sys/types.h>
27 #include "swtpm_utils.h"
28 #include "swtpm_localca_conf.h"
29 #include "swtpm_localca_utils.h"
31 #define SETUP_TPM2_F 1
33 #define ALLOW_SIGNING_F 2
34 #define DECRYPTION_F 4
36 /* Default logging goes to stderr */
37 gchar
*gl_LOGFILE
= NULL
;
39 #define LOCALCA_OPTIONS "swtpm-localca.options"
40 #define LOCALCA_CONFIG "swtpm-localca.conf"
43 # define CERTTOOL_NAME "gnutls-certtool"
45 # define CERTTOOL_NAME "certtool"
48 /* initialize the path of the options and config files */
49 static int init(gchar
**options_file
, gchar
**config_file
)
51 const gchar
*configdir
= g_get_user_config_dir();
53 *options_file
= g_build_filename(configdir
, LOCALCA_OPTIONS
, NULL
);
54 if (access(*options_file
, R_OK
) != 0) {
55 g_free(*options_file
);
56 *options_file
= g_build_filename(SYSCONFDIR
, LOCALCA_OPTIONS
, NULL
);
59 *config_file
= g_build_filename(configdir
, LOCALCA_CONFIG
, NULL
);
60 if (access(*config_file
, R_OK
) != 0) {
62 *config_file
= g_build_filename(SYSCONFDIR
, LOCALCA_CONFIG
, NULL
);
68 /* Run the certtool command line prepared in cmd. Display error message
69 * in case of failure and also display the keyfile if something goes wrong.
71 static int run_certtool(gchar
**cmd
, gchar
**env
, const char *msg
, gchar
*keyfile
)
73 g_autofree gchar
*standard_error
= NULL
;
78 success
= g_spawn_sync(NULL
, cmd
, env
, G_SPAWN_STDOUT_TO_DEV_NULL
, NULL
, NULL
,
79 NULL
, &standard_error
, &exit_status
, &error
);
80 if (!success
|| exit_status
!= 0) {
81 logerr(gl_LOGFILE
, "%s" , msg
);
83 logerr(gl_LOGFILE
, " %s:", keyfile
);
85 logerr(gl_LOGFILE
, "%s\n", error
->message
);
88 logerr(gl_LOGFILE
, "%s\n", standard_error
);
95 /* Create a root CA key and cert and a local CA key and cert. The latter will be
96 * used for signing the TPM certs.
98 static int create_localca_cert(const gchar
*lockfile
, const gchar
*statedir
,
99 const gchar
*signkey
, const gchar
*signkey_password
,
100 const gchar
*issuercert
)
105 int template1_file_fd
= -1;
106 int template2_file_fd
= -1;
107 g_autofree gchar
*template1_file
= NULL
;
108 g_autofree gchar
*template2_file
= NULL
;
109 gchar
**certtool_env
= NULL
;
111 lockfd
= lock_file(lockfile
);
115 if (stat(statedir
, &statbuf
) != 0) {
116 if (makedir(statedir
, "statedir") != 0)
120 if (access(signkey
, R_OK
) != 0) {
121 g_autofree gchar
*directory
= g_path_get_dirname(signkey
);
122 g_autofree gchar
*cakey
= g_strjoin(G_DIR_SEPARATOR_S
, directory
, "swtpm-localca-rootca-privkey.pem", NULL
);
123 g_autofree gchar
*cacert
= g_strjoin(G_DIR_SEPARATOR_S
, directory
, "swtpm-localca-rootca-cert.pem", NULL
);
124 const gchar
*swtpm_rootca_password
= g_getenv("SWTPM_ROOTCA_PASSWORD");
125 g_autofree gchar
*certtool
= g_find_program_in_path(CERTTOOL_NAME
);
126 g_autofree gchar
**cmd
= NULL
;
127 g_autofree gchar
*fc
= NULL
;
128 const char *filecontent
;
130 if (certtool
== NULL
) {
131 logerr(gl_LOGFILE
, "Could not find %s in PATH.\n", CERTTOOL_NAME
);
135 /* generate the root-CA's private key */
136 cmd
= concat_arrays(cmd
, (gchar
*[]){
137 (gchar
*)certtool
, "--generate-privkey", "--outfile", cakey
, NULL
139 if (swtpm_rootca_password
!= NULL
)
140 cmd
= concat_arrays(cmd
, (gchar
*[]){
141 "--password", (gchar
*)swtpm_rootca_password
, NULL
143 if (run_certtool(cmd
, certtool_env
, "Could not create root-CA key", cakey
))
146 if (chmod(cakey
, S_IRUSR
| S_IWUSR
| S_IRGRP
) != 0) {
147 logerr(gl_LOGFILE
, "Could not chmod %s: %s\n", cakey
, strerror(errno
));
151 certtool_env
= g_environ_setenv(NULL
, "PATH", g_getenv("PATH"), TRUE
);
153 /* create the root-CA's cert */
154 filecontent
= "cn=swtpm-localca-rootca\n"
157 "expiration_days = -1\n";
158 template1_file_fd
= write_to_tempfile(&template1_file
,
159 (const unsigned char *)filecontent
, strlen(filecontent
));
160 if (template1_file_fd
< 0)
164 cmd
= concat_arrays(NULL
,
167 "--generate-self-signed",
168 "--template", template1_file
,
170 "--load-privkey", cakey
,
173 if (swtpm_rootca_password
!= NULL
)
174 certtool_env
= g_environ_setenv(certtool_env
, "GNUTLS_PIN", swtpm_rootca_password
, TRUE
);
176 if (run_certtool(cmd
, certtool_env
, "Could not create root-CA:", NULL
))
181 /* create the intermediate CA's key */
182 cmd
= concat_arrays(NULL
,
184 certtool
, "--generate-privkey", "--outfile", (gchar
*)signkey
, NULL
186 if (signkey_password
!= NULL
)
187 cmd
= concat_arrays(cmd
, (gchar
*[]){
188 "--password", (gchar
*)signkey_password
, NULL
},
190 if (run_certtool(cmd
, certtool_env
, "Could not create local-CA key", cakey
))
193 if (chmod(signkey
, S_IRUSR
| S_IWUSR
| S_IRGRP
) != 0) {
194 logerr(gl_LOGFILE
, "Could not chmod %s: %s\n", signkey
, strerror(errno
));
198 filecontent
= "cn=swtpm-localca\n"
201 "expiration_days = -1\n";
202 if (swtpm_rootca_password
!= NULL
&& signkey_password
!= NULL
)
203 fc
= g_strdup_printf("%spassword = %s\n", filecontent
, swtpm_rootca_password
);
205 fc
= g_strdup(filecontent
);
207 template2_file_fd
= write_to_tempfile(&template2_file
,
208 (const unsigned char *)fc
, strlen(fc
));
209 if (template2_file_fd
< 0)
213 cmd
= concat_arrays(NULL
,
216 "--generate-certificate",
217 "--template", template2_file
,
218 "--outfile", (gchar
*)issuercert
,
219 "--load-privkey", (gchar
*)signkey
,
220 "--load-ca-privkey", cakey
,
221 "--load-ca-certificate", cacert
,
224 if (signkey_password
!= NULL
)
225 certtool_env
= g_environ_setenv(certtool_env
, "GNUTLS_PIN", signkey_password
, TRUE
);
226 else if (swtpm_rootca_password
!= NULL
)
227 certtool_env
= g_environ_setenv(certtool_env
, "GNUTLS_PIN", swtpm_rootca_password
, TRUE
);
229 if (run_certtool(cmd
, certtool_env
, "Could not create local-CA:", NULL
))
236 if (template1_file_fd
>= 0)
237 close(template1_file_fd
);
238 if (template1_file
!= NULL
)
239 unlink(template1_file
);
241 if (template2_file_fd
>= 0)
242 close(template2_file_fd
);
243 if (template2_file
!= NULL
)
244 unlink(template2_file
);
245 g_strfreev(certtool_env
);
252 /* Extract the ECC parameters from a string like x=12,y=34,id=secp384r1.
253 * This function returns 1 on error, 2 if the ECC parameters could be extracted
254 * and 0 if no parameters could be extracted (likely a modulus).
256 static gboolean
extract_ecc_params(const gchar
*ekparams
, gchar
**ecc_x
, gchar
**ecc_y
, gchar
**ecc_curveid
)
258 regmatch_t pmatch
[5];
262 if (regcomp(&preg
, "x=([0-9A-Fa-f]+),y=([0-9A-Fa-f]+)(,id=([^,]+))?",
263 REG_EXTENDED
) != 0) {
264 logerr(gl_LOGFILE
, "Internal error: Could not compile regex\n");
269 if (regexec(&preg
, ekparams
, 5, pmatch
, 0) == 0) {
270 *ecc_x
= g_strndup(&ekparams
[pmatch
[1].rm_so
],
271 pmatch
[1].rm_eo
- pmatch
[1].rm_so
);
272 *ecc_y
= g_strndup(&ekparams
[pmatch
[2].rm_so
],
273 pmatch
[2].rm_eo
- pmatch
[2].rm_so
);
274 if (pmatch
[4].rm_so
> 0 && pmatch
[4].rm_eo
> 0)
275 *ecc_curveid
= g_strndup(&ekparams
[pmatch
[4].rm_so
],
276 pmatch
[4].rm_eo
- pmatch
[4].rm_so
);
285 /* Get the next serial number from the certserial file; if it contains
286 * a non-numeric content start over with serial number '1'.
288 static int get_next_serial(const gchar
*certserial
, const gchar
*lockfile
,
291 g_autofree gchar
*buffer
= NULL
;
293 unsigned long long serial
, serial_n
;
298 lockfd
= lock_file(lockfile
);
302 if (access(certserial
, R_OK
) != 0)
303 write_file(certserial
, (unsigned char *)"1", 1);
304 if (read_file(certserial
, &buffer
, &buffer_len
) != 0)
307 if (buffer_len
> 0) {
308 serial
= strtoull(buffer
, &endptr
, 10);
309 if (*endptr
== '\0') {
310 serial_n
= serial
+ 1;
317 *serial_str
= g_strdup_printf("%llu", serial_n
);
318 write_file(certserial
, (unsigned char *)*serial_str
, strlen(*serial_str
));
327 /* Create a TPM 1.2 or TPM 2 EK or platform cert */
328 static int create_cert(unsigned long flags
, const gchar
*typ
, const gchar
*directory
,
329 gchar
*ekparams
, const gchar
*vmid
, gchar
**tpm_spec_params
,
330 gchar
**tpm_attr_params
, const gchar
*signkey
,
331 const gchar
*signkey_password
, const gchar
*issuercert
,
332 const gchar
*parentkey_password
, gchar
**swtpm_cert_env
,
333 const gchar
*certserial
, const gchar
*lockfile
,
334 const gchar
*optsfile
)
336 gchar
** optsfile_lines
= NULL
;
337 g_autofree gchar
**options
= NULL
;
338 g_autofree gchar
**keyparams
= NULL
;
339 g_autofree gchar
**cmd
= NULL
;
340 g_autofree gchar
*subject
= NULL
;
341 g_autofree gchar
*ecc_x
= NULL
;
342 g_autofree gchar
*ecc_y
= NULL
;
343 g_autofree gchar
*ecc_curveid
= NULL
;
344 g_autofree gchar
*certfile
= NULL
;
345 g_autofree gchar
*serial_str
= NULL
;
346 gchar
**to_free
= NULL
;
348 const char *certtype
;
349 int signkey_pwd_fd
= -1;
350 int parentkey_pwd_fd
= -1;
351 g_autofree gchar
*signkey_pwd_file
= NULL
;
352 g_autofree gchar
*signkey_pwd_file_param
= NULL
;
353 g_autofree gchar
*parentkey_pwd_file
= NULL
;
354 g_autofree gchar
*parentkey_pwd_file_param
= NULL
;
356 g_autofree gchar
*standard_output
= NULL
;
357 g_autofree gchar
*standard_error
= NULL
;
358 g_autofree gchar
*swtpm_cert_path
= NULL
;
359 GError
*error
= NULL
;
364 swtpm_cert_path
= g_find_program_in_path("swtpm_cert");
365 if (swtpm_cert_path
== NULL
) {
366 logerr(gl_LOGFILE
, "Could not find swtpm_cert in PATH.\n");
370 if (get_next_serial(certserial
, lockfile
, &serial_str
) != 0)
373 /* try to read the optsfile */
374 read_file_lines(optsfile
, &optsfile_lines
);
376 /* split each line from the optsfile and add the stripped parameters to options */
377 for (i
= 0; optsfile_lines
!= NULL
&& optsfile_lines
[i
] != NULL
; i
++) {
378 gchar
*chomped
= g_strchomp(optsfile_lines
[i
]);
379 if (strlen(chomped
) == 0)
382 split
= g_strsplit(chomped
, " ", -1);
383 for (j
= 0; split
[j
] != NULL
; j
++) {
384 chomped
= g_strchomp(split
[j
]);
385 if (strlen(chomped
) > 0) {
386 gchar
*to_add
= g_strdup(chomped
);
387 options
= concat_arrays(options
, (gchar
*[]){to_add
, NULL
}, TRUE
);
388 /* need to collect this also to free later on */
389 to_free
= concat_arrays(to_free
, (gchar
*[]){to_add
, NULL
}, TRUE
);
396 subject
= g_strdup_printf("CN=%s", vmid
);
398 subject
= g_strdup("CN=unknown");
400 if (flags
& SETUP_TPM2_F
)
401 options
= concat_arrays(options
, (gchar
*[]){"--tpm2", NULL
}, TRUE
);
403 options
= concat_arrays(options
, (gchar
*[]){"--add-header", NULL
}, TRUE
);
405 if (strcmp(typ
, "ek") == 0) {
406 if (flags
& ALLOW_SIGNING_F
)
407 options
= concat_arrays(options
, (gchar
*[]){"--allow-signing", NULL
}, TRUE
);
408 if (flags
& DECRYPTION_F
)
409 options
= concat_arrays(options
, (gchar
*[]){"--decryption", NULL
}, TRUE
);
412 switch (extract_ecc_params(ekparams
, &ecc_x
, &ecc_y
, &ecc_curveid
)) {
416 keyparams
= concat_arrays((gchar
*[]){
422 if (ecc_curveid
!= NULL
)
423 keyparams
= concat_arrays(keyparams
,
425 "--ecc-curveid", ecc_curveid
,
430 keyparams
= concat_arrays((gchar
*[]){
431 "--modulus", ekparams
,
437 cmd
= concat_arrays((gchar
*[]){
438 swtpm_cert_path
, "--subject", subject
, NULL
441 if (signkey_password
!= NULL
) {
442 signkey_pwd_fd
= write_to_tempfile(&signkey_pwd_file
,
443 (unsigned char *)signkey_password
, strlen(signkey_password
));
444 if (signkey_pwd_fd
< 0)
447 signkey_pwd_file_param
= g_strdup_printf("file:%s", signkey_pwd_file
);
448 cmd
= concat_arrays(cmd
, (gchar
*[]){"--signkey-pwd", signkey_pwd_file_param
, NULL
}, TRUE
);
450 if (parentkey_password
!= NULL
) {
451 parentkey_pwd_fd
= write_to_tempfile(&parentkey_pwd_file
,
452 (unsigned char *)parentkey_password
, strlen(parentkey_password
));
453 if (parentkey_pwd_fd
< 0)
456 parentkey_pwd_file_param
= g_strdup_printf("file:%s", parentkey_pwd_file
);
457 cmd
= concat_arrays(cmd
, (gchar
*[]){"--parentkey-pwd", parentkey_pwd_file_param
, NULL
}, TRUE
);
460 if (strcmp(typ
, "ek") == 0)
461 cmd
= concat_arrays(cmd
, tpm_spec_params
, TRUE
);
463 cmd
= concat_arrays(cmd
, tpm_attr_params
, TRUE
);
465 if (strcmp(typ
, "platform") == 0) {
466 certfile
= g_strjoin(G_DIR_SEPARATOR_S
, directory
, "platform.cert", NULL
);
467 cmd
= concat_arrays(cmd
,
469 "--type", "platform",
470 "--out-cert", certfile
,
474 certfile
= g_strjoin(G_DIR_SEPARATOR_S
, directory
, "ek.cert", NULL
);
475 cmd
= concat_arrays(cmd
,
477 "--out-cert", certfile
,
482 cmd
= concat_arrays(cmd
, keyparams
, TRUE
);
483 cmd
= concat_arrays(cmd
, (gchar
*[]){
484 "--signkey", (gchar
*)signkey
,
485 "--issuercert", (gchar
*)issuercert
,
487 "--serial", (gchar
*)serial_str
,
491 if (strcmp(typ
, "ek") == 0)
494 certtype
= "platform";
497 g_autofree gchar
*join
= g_strjoinv(" ", cmd
);
498 fprintf(stderr
, "Starting: %s\n", join
);
501 success
= g_spawn_sync(NULL
, cmd
, swtpm_cert_env
, G_SPAWN_DEFAULT
, NULL
, NULL
,
502 &standard_output
, &standard_error
, &exit_status
, &error
);
504 logerr(gl_LOGFILE
, "Could not run swtpm_cert: %s\n", error
);
508 if (exit_status
!= 0) {
509 logerr(gl_LOGFILE
, "Could not create %s certificate locally\n", certtype
);
510 logerr(gl_LOGFILE
, "%s\n", standard_error
);
514 logit(gl_LOGFILE
, "Successfully created %s certificate locally.\n", certtype
);
518 g_strfreev(optsfile_lines
);
521 if (signkey_pwd_fd
>= 0)
522 close(signkey_pwd_fd
);
523 if (signkey_pwd_file
)
524 unlink(signkey_pwd_file
);
526 if (parentkey_pwd_fd
>= 0)
527 close(parentkey_pwd_fd
);
528 if (parentkey_pwd_file
)
529 unlink(parentkey_pwd_file
);
534 static void usage(const char *prgname
)
537 "Usage: %s [options]\n"
539 "The following options are supported:\n"
541 "--type type The type of certificate to create: 'ek' or 'platform'\n"
542 "--ek key-param The modulus of an RSA key or x=...,y=,... for an EC key\n"
543 "--dir directory The directory to write the resulting certificate into\n"
544 "--vmid vmid The ID of the virtual machine\n"
545 "--optsfile file A file containing options to pass to swtpm_cert\n"
546 "--configfile file A file containing configuration parameters for directory,\n"
547 " signing key and password and certificate to use\n"
548 "--logfile file A file to write a log into\n"
549 "--tpm-spec-family s The implemented spec family, e.g., '2.0'\n"
550 "--tpm-spec-revision i The spec revision of the TPM as integer; e.g., 146\n"
551 "--tpm-spec-level i The spec level of the TPM; must be an integer; e.g. 0\n"
552 "--tpm-manufacturer s The manufacturer of the TPM; e.g., id:00001014\n"
553 "--tpm-model s The model of the TPM; e.g., 'swtpm'\n"
554 "--tpm-version i The (firmware) version of the TPM; e.g., id:20160511\n"
555 "--tpm2 Generate a certificate for a TPM 2\n"
556 "--allow-signing The TPM 2's EK can be used for signing\n"
557 "--decryption The TPM 2's EK can be used for decryption\n"
558 "--help, -h Display this help screen and exit\n"
561 "The following environment variables are supported:\n"
563 "SWTPM_ROOTCA_PASSWORD The root CA's private key password\n"
567 int main(int argc
, char *argv
[])
569 int opt
, option_index
= 0;
570 static const struct option long_options
[] = {
571 {"type", required_argument
, NULL
, 't'},
572 {"ek", required_argument
, NULL
, 'e'},
573 {"dir", required_argument
, NULL
, 'd'},
574 {"vmid", required_argument
, NULL
, 'v'},
575 {"optsfile", required_argument
, NULL
, 'o'},
576 {"configfile", required_argument
, NULL
, 'c'},
577 {"logfile", required_argument
, NULL
, 'l'},
578 {"tpm-spec-family", required_argument
, NULL
, 'f'},
579 {"tpm-spec-revision", required_argument
, NULL
, 'r'},
580 {"tpm-spec-level", required_argument
, NULL
, '1'},
581 {"tpm-manufacturer", required_argument
, NULL
, 'a'},
582 {"tpm-model", required_argument
, NULL
, 'm'},
583 {"tpm-version", required_argument
, NULL
, 's'},
584 {"tpm2", no_argument
, NULL
, '2'},
585 {"allow-signing", no_argument
, NULL
, 'i'},
586 {"decryption", no_argument
, NULL
, 'y'},
587 {"help", no_argument
, NULL
, 'h'},
589 g_autofree gchar
*default_options_file
= NULL
;
590 g_autofree gchar
*default_config_file
= NULL
;
591 g_autofree gchar
*optsfile
= NULL
;
592 g_autofree gchar
*configfile
= NULL
;
593 unsigned long flags
= 0;
594 g_autofree gchar
*typ
=g_strdup("");
595 g_autofree gchar
*ekparams
= g_strdup("");
596 g_autofree gchar
*directory
= g_strdup("."); /* default to current directory */
597 g_autofree gchar
*vmid
= NULL
;
598 g_autofree gchar
*lockfile
= NULL
;
599 g_autofree gchar
*statedir
= NULL
;
600 g_autofree gchar
*signkey
= NULL
;
601 g_autofree gchar
*signkey_password
= NULL
;
602 g_autofree gchar
*parentkey_password
= NULL
;
603 g_autofree gchar
*issuercert
= NULL
;
604 g_autofree gchar
*certserial
= NULL
;
605 gchar
**tpm_spec_params
= NULL
;
606 gchar
**tpm_attr_params
= NULL
;
607 gchar
**config_file_lines
= NULL
;
608 gchar
**swtpm_cert_env
= NULL
;
609 const struct passwd
*curr_user
;
613 if (init(&default_options_file
, &default_config_file
) < 0)
615 optsfile
= g_strdup(default_options_file
);
616 configfile
= g_strdup(default_config_file
);
618 while ((opt
= getopt_long(argc
, argv
, "h?",
619 long_options
, &option_index
)) != -1) {
621 case 't': /* --type */
623 typ
= g_strdup(optarg
);
627 ekparams
= g_strdup(optarg
);
629 case 'd': /* --dir */
631 directory
= g_strdup(optarg
);
633 case 'v': /* --vmid */
635 vmid
= g_strdup(optarg
);
636 vmid_replacechars(vmid
);
638 case 'o': /* --optsfile */
640 optsfile
= g_strdup(optarg
);
642 case 'c': /* --configfile */
644 configfile
= g_strdup(optarg
);
646 case 'l': /* --logfile */
648 gl_LOGFILE
= g_strdup(optarg
);
650 case 'f': /* --tpm-spec-family */
651 case 'r': /* --tpm-spec-revision */
652 case '1': /* --tpm-spec-level */
653 tpm_spec_params
= concat_arrays(tpm_spec_params
,
655 g_strdup_printf("--%s", long_options
[option_index
].name
), g_strdup(optarg
), NULL
658 case 'a': /* --tpm-manufacturer */
659 case 'm': /* --tpm-model */
660 case 's': /* --tpm-version */
661 tpm_attr_params
= concat_arrays(tpm_attr_params
,
663 g_strdup_printf("--%s", long_options
[option_index
].name
), g_strdup(optarg
), NULL
666 case '2': /* --tpm2 */
667 flags
|= SETUP_TPM2_F
;
669 case 'i': /* --allow-signing */
670 flags
|= ALLOW_SIGNING_F
;
672 case 'y': /* --decryption */
673 flags
|= DECRYPTION_F
;
676 case 'h': /* --help */
682 fprintf(stderr
, "Unknown option code %d\n", opt
);
688 curr_user
= getpwuid(getuid());
690 if (gl_LOGFILE
!= NULL
) {
693 if (stat(gl_LOGFILE
, &statbuf
) == 0 &&
694 (statbuf
.st_mode
& S_IFMT
) == S_IFLNK
) {
695 fprintf(stderr
, "Logfile must not be a symlink.\n");
698 tmpfile
= fopen(gl_LOGFILE
, "a"); // do not truncate
699 if (tmpfile
== NULL
) {
700 fprintf(stderr
, "Cannot write to logfile %s.\n", gl_LOGFILE
);
706 if (access(optsfile
, R_OK
) != 0) {
707 logerr(gl_LOGFILE
, "Need read rights on options file %s for user %s.\n",
708 optsfile
, curr_user
? curr_user
->pw_name
: "<unknown>");
712 if (access(configfile
, R_OK
) != 0) {
713 logerr(gl_LOGFILE
, "Need read rights on config file %s for user %s.\n",
714 configfile
, curr_user
? curr_user
->pw_name
: "<unknown>");
718 if (read_file_lines(configfile
, &config_file_lines
) != 0)
721 statedir
= get_config_value(config_file_lines
, "statedir", NULL
);
722 if (statedir
== NULL
) {
723 logerr(gl_LOGFILE
, "Missing 'statedir' config value in config file %s.\n", configfile
);
726 if (makedir(statedir
, "statedir") != 0)
728 if (access(statedir
, W_OK
| R_OK
) != 0) {
729 logerr(gl_LOGFILE
, "Need read/write rights on statedir %s for user %s.\n",
730 statedir
, curr_user
? curr_user
->pw_name
: "<unknown>");
734 lockfile
= g_strjoin(G_DIR_SEPARATOR_S
, statedir
, ".lock.swtpm-localca", NULL
);
735 if (stat(lockfile
, &statbuf
) == 0 &&
736 access(lockfile
, W_OK
| R_OK
) != 0) {
737 logerr(gl_LOGFILE
, "Need read/write rights on %s for user %s.\n",
738 lockfile
, curr_user
? curr_user
->pw_name
: "<unknown>");
742 signkey
= get_config_value(config_file_lines
, "signingkey", NULL
);
743 if (signkey
== NULL
) {
744 logerr(gl_LOGFILE
, "Missing 'signingkey' config value in config file %s.\n",
749 if (!g_str_has_prefix(signkey
, "tpmkey:file=") &&
750 !g_str_has_prefix(signkey
, "tpmkey:uuid=") &&
751 !g_str_has_prefix(signkey
, "pkcs11:")) {
752 g_autofree gchar
*d
= g_path_get_dirname(signkey
);
753 if (makedir(d
, "signkey") != 0)
757 signkey_password
= get_config_value(config_file_lines
, "signingkey_password", NULL
);
758 parentkey_password
= get_config_value(config_file_lines
, "parentkey_password", NULL
);
760 issuercert
= get_config_value(config_file_lines
, "issuercert", NULL
);
761 if (issuercert
== NULL
) {
762 logerr(gl_LOGFILE
, "Missing 'issuercert' config value in config file %s.\n", configfile
);
766 g_autofree gchar
*d
= g_path_get_dirname(issuercert
);
767 if (makedir(d
, "issuercert") != 0)
771 swtpm_cert_env
= g_get_environ();
773 // TPM keys are GNUTLS URIs...
774 if (g_str_has_prefix(signkey
, "tpmkey:file=") || g_str_has_prefix(signkey
, "tpmkey:uuid=")) {
775 g_autofree gchar
*tss_tcsd_hostname
= NULL
;
776 g_autofree gchar
*tss_tcsd_port
= NULL
;
778 tss_tcsd_hostname
= get_config_value(config_file_lines
,
779 "TSS_TCSD_HOSTNAME", "localhost");
780 tss_tcsd_port
= get_config_value(config_file_lines
,
781 "TSS_TCSD_PORT", "30003");
782 swtpm_cert_env
= g_environ_setenv(swtpm_cert_env
,
783 "TSS_TCSD_HOSTNAME", tss_tcsd_hostname
, TRUE
);
784 swtpm_cert_env
= g_environ_setenv(swtpm_cert_env
,
785 "TSS_TCSD_PORT", tss_tcsd_port
, TRUE
);
787 logit(gl_LOGFILE
, "CA uses a GnuTLS TPM key; using TSS_TCSD_HOSTNAME=%s " \
788 "TSS_TCSD_PORT=%s\n", tss_tcsd_hostname
, tss_tcsd_port
);
789 } else if (g_str_has_prefix(signkey
, "pkcs11:")) {
790 gchar
*tmp
= str_replace(signkey
, "\\;", ";"); /* historical reasons ... */
794 if (signkey_password
!= NULL
) {
795 swtpm_cert_env
= g_environ_setenv(swtpm_cert_env
,
796 "SWTPM_PKCS11_PIN", g_strdup(signkey_password
), TRUE
);
797 logit(gl_LOGFILE
, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n");
799 g_autofree gchar
*swtpm_pkcs11_pin
= NULL
;
801 swtpm_pkcs11_pin
= get_config_value(config_file_lines
,
802 "SWTPM_PKCS11_PIN", "swtpm-tpmca");
803 swtpm_cert_env
= g_environ_setenv(swtpm_cert_env
,
804 "SWTPM_PKCS11_PIN", swtpm_pkcs11_pin
, TRUE
);
805 logit(gl_LOGFILE
, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n");
807 ret
= get_config_envvars(config_file_lines
, &swtpm_cert_env
);
811 if (access(signkey
, R_OK
) != 0) {
812 if (stat(signkey
, &statbuf
) == 0) {
813 logerr(gl_LOGFILE
, "Need read rights on signing key %s for user %s.\n",
814 signkey
, curr_user
? curr_user
->pw_name
: "<unknown>");
818 logit(gl_LOGFILE
, "Creating root CA and a local CA's signing key and issuer cert.\n");
819 if (create_localca_cert(lockfile
, statedir
, signkey
, signkey_password
,
821 logerr(gl_LOGFILE
, "Error creating local CA's signing key and cert.\n");
825 if (access(signkey
, R_OK
) != 0) {
826 logerr(gl_LOGFILE
, "Need read rights on signing key %s for user %s.\n",
827 signkey
, curr_user
? curr_user
->pw_name
: "<unknown>");
833 if (access(issuercert
, R_OK
) != 0) {
834 logerr(gl_LOGFILE
, "Need read rights on issuer certificate %s for user %s.\n",
835 issuercert
, curr_user
? curr_user
->pw_name
: "<unknown>");
840 g_autofree gchar
*d
= NULL
;
841 g_autofree gchar
*p
= g_strjoin(G_DIR_SEPARATOR_S
, statedir
, "certserial", NULL
);
843 certserial
= get_config_value(config_file_lines
, "certserial", p
);
844 d
= g_path_get_dirname(certserial
);
845 if (makedir(d
, "certserial") != 0)
849 ret
= create_cert(flags
, typ
, directory
, ekparams
, vmid
, tpm_spec_params
, tpm_attr_params
,
850 signkey
, signkey_password
, issuercert
, parentkey_password
, swtpm_cert_env
,
851 certserial
, lockfile
, optsfile
);
855 g_strfreev(config_file_lines
);
856 g_strfreev(swtpm_cert_env
);
857 g_strfreev(tpm_attr_params
);
858 g_strfreev(tpm_spec_params
);