/* SPDX-License-Identifier: BSD-3-Clause */ /* * swtpm_localca.c: A tool for creating TPM 1.2 and TPM 2 certificates localy or using pkcs11 * * Author: Stefan Berger, stefanb@linux.ibm.com * * Copyright (c) IBM Corporation, 2021 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "swtpm_utils.h" #include "swtpm_localca_conf.h" #include "swtpm_localca_utils.h" #define SETUP_TPM2_F 1 /* for TPM 2 EK */ #define ALLOW_SIGNING_F 2 #define DECRYPTION_F 4 /* Default logging goes to stderr */ gchar *gl_LOGFILE = NULL; #define LOCALCA_OPTIONS "swtpm-localca.options" #define LOCALCA_CONFIG "swtpm-localca.conf" #if defined __APPLE__ # define CERTTOOL_NAME "gnutls-certtool" #else # define CERTTOOL_NAME "certtool" #endif /* initialize the path of the options and config files */ static int init(gchar **options_file, gchar **config_file) { const char *xch = getenv("XDG_CONFIG_HOME"); const char *home = getenv("HOME"); char path[PATH_MAX]; const char *p = NULL; int ret = 0; if (xch != NULL && (p = pathjoin(path, sizeof(path), xch, LOCALCA_OPTIONS, NULL)) != NULL && access(p, R_OK) == 0) { /* p is good */ } else if (home != NULL && (p = pathjoin(path, sizeof(path), home, ".config", LOCALCA_OPTIONS)) != NULL && access(p, R_OK) == 0) { /* p is good */ } else { p = pathjoin(path, sizeof(path), G_DIR_SEPARATOR_S, SYSCONFDIR, LOCALCA_OPTIONS); } *options_file = g_strdup(p); if (xch != NULL && (p = pathjoin(path, sizeof(path), xch, LOCALCA_CONFIG, NULL)) != NULL && access(p, R_OK) == 0) { /* p is good */ } else if (home != NULL && (p = pathjoin(path, sizeof(path), home, ".config", LOCALCA_CONFIG)) != NULL && access(p, R_OK) == 0) { /* p is good */ } else { p = pathjoin(path, sizeof(path), G_DIR_SEPARATOR_S, SYSCONFDIR, LOCALCA_CONFIG); } *config_file = g_strdup(p); return ret; } /* Run the certtool command line prepared in cmd. Display error message * in case of failure and also display the keyfile if something goes wrong. */ static int run_certtool(gchar **cmd, gchar **env, const char *msg, gchar *keyfile) { g_autofree gchar *standard_error = NULL; gint exit_status; GError *error = NULL; gboolean success; success = g_spawn_sync(NULL, cmd, env, G_SPAWN_STDOUT_TO_DEV_NULL, NULL, NULL, NULL, &standard_error, &exit_status, &error); if (!success || exit_status != 0) { logerr(gl_LOGFILE, "%s" , msg); if (keyfile) logerr(gl_LOGFILE, " %s:", keyfile); if (!success) { logerr(gl_LOGFILE, "%s\n", error->message); g_error_free(error); } else { logerr(gl_LOGFILE, "%s\n", standard_error); } return 1; } return 0; } /* Create a root CA key and cert and a local CA key and cert. The latter will be * used for signing the TPM certs. */ static int create_localca_cert(const gchar *lockfile, const gchar *statedir, const gchar *signkey, const gchar *signkey_password, const gchar *issuercert) { int lockfd; int ret = 1; struct stat statbuf; int template1_file_fd = -1; int template2_file_fd = -1; g_autofree gchar *template1_file = NULL; g_autofree gchar *template2_file = NULL; gchar **certtool_env = NULL; lockfd = lock_file(lockfile); if (lockfd < 0) return 1; if (stat(statedir, &statbuf) != 0) { if (makedir(statedir, "statedir") != 0) goto error; } if (access(signkey, R_OK) != 0 || access(issuercert, R_OK) != 0) { g_autofree gchar *directory = g_path_get_dirname(signkey); g_autofree gchar *cakey = g_strjoin(G_DIR_SEPARATOR_S, directory, "swtpm-localca-rootca-privkey.pem", NULL); g_autofree gchar *cacert = g_strjoin(G_DIR_SEPARATOR_S, directory, "swtpm-localca-rootca-cert.pem", NULL); const gchar *swtpm_rootca_password = g_getenv("SWTPM_ROOTCA_PASSWORD"); g_autofree gchar *certtool = g_find_program_in_path(CERTTOOL_NAME); g_autofree gchar **cmd = NULL; g_autofree gchar *fc = NULL; const char *filecontent; if (certtool == NULL) { logerr(gl_LOGFILE, "Could not find %s in PATH.\n", CERTTOOL_NAME); goto error; } /* generate the root-CA's private key */ cmd = concat_arrays(cmd, (gchar*[]){ (gchar *)certtool, "--generate-privkey", "--outfile", cakey, NULL }, TRUE); if (swtpm_rootca_password != NULL) cmd = concat_arrays(cmd, (gchar*[]){ "--password", (gchar *)swtpm_rootca_password, NULL }, TRUE); if (run_certtool(cmd, certtool_env, "Could not create root-CA key", cakey)) goto error; if (chmod(cakey, S_IRUSR | S_IWUSR | S_IRGRP) != 0) { logerr(gl_LOGFILE, "Could not chmod %s: %s\n", cakey, strerror(errno)); goto error; } certtool_env = g_environ_setenv(NULL, "PATH", g_getenv("PATH"), TRUE); /* create the root-CA's cert */ filecontent = "cn=swtpm-localca-rootca\n" "ca\n" "cert_signing_key\n" "expiration_days = 3600\n"; template1_file_fd = write_to_tempfile(&template1_file, (const unsigned char *)filecontent, strlen(filecontent)); if (template1_file_fd < 0) goto error; g_free(cmd); cmd = concat_arrays(NULL, (gchar *[]) { certtool, "--generate-self-signed", "--template", template1_file, "--outfile", cacert, "--load-privkey", cakey, NULL }, FALSE); if (swtpm_rootca_password != NULL) certtool_env = g_environ_setenv(certtool_env, "GNUTLS_PIN", swtpm_rootca_password, TRUE); if (run_certtool(cmd, certtool_env, "Could not create root-CA:", NULL)) goto error; g_free(cmd); /* create the intermediate CA's key */ cmd = concat_arrays(NULL, (gchar *[]) { certtool, "--generate-privkey", "--outfile", (gchar *)signkey, NULL }, FALSE); if (signkey_password != NULL) cmd = concat_arrays(cmd, (gchar *[]){ "--password", (gchar *)signkey_password, NULL}, TRUE); if (run_certtool(cmd, certtool_env, "Could not create local-CA key", cakey)) goto error; if (chmod(signkey, S_IRUSR | S_IWUSR | S_IRGRP) != 0) { logerr(gl_LOGFILE, "Could not chmod %s: %s\n", signkey, strerror(errno)); goto error; } filecontent = "cn=swtpm-localca\n" "ca\n" "cert_signing_key\n" "expiration_days = 3600\n"; if (swtpm_rootca_password != NULL && signkey_password != NULL) fc = g_strdup_printf("%spassword = %s\n", filecontent, swtpm_rootca_password); else fc = g_strdup(filecontent); template2_file_fd = write_to_tempfile(&template2_file, (const unsigned char *)fc, strlen(fc)); if (template2_file_fd < 0) goto error; g_free(cmd); cmd = concat_arrays(NULL, (gchar *[]) { certtool, "--generate-certificate", "--template", template2_file, "--outfile", (gchar *)issuercert, "--load-privkey", (gchar *)signkey, "--load-ca-privkey", cakey, "--load-ca-certificate", cacert, NULL }, FALSE); if (signkey_password != NULL) certtool_env = g_environ_setenv(certtool_env, "GNUTLS_PIN", signkey_password, TRUE); else if (swtpm_rootca_password != NULL) certtool_env = g_environ_setenv(certtool_env, "GNUTLS_PIN", swtpm_rootca_password, TRUE); if (run_certtool(cmd, certtool_env, "Could not create local-CA:", NULL)) goto error; } ret = 0; error: if (template1_file_fd >= 0) close(template1_file_fd); if (template1_file != NULL) unlink(template1_file); if (template2_file_fd >= 0) close(template2_file_fd); if (template2_file != NULL) unlink(template2_file); g_strfreev(certtool_env); unlock_file(lockfd); return ret; } /* Extract the ECC parameters from a string like x=12,y=34,id=secp384r1. * This function returns 1 on error, 2 if the ECC parameters could be extracted * and 0 if no parameters could be extracted (likely a modulus). */ static gboolean extract_ecc_params(const gchar *ekparams, gchar **ecc_x, gchar **ecc_y, gchar **ecc_curveid) { regmatch_t pmatch[5]; regex_t preg; int ret; if (regcomp(&preg, "x=([0-9A-Fa-f]+),y=([0-9A-Fa-f]+)(,id=([^,]+))?", REG_EXTENDED) != 0) { logerr(gl_LOGFILE, "Internal error: Could not compile regex\n"); return 1; } ret = 0; if (regexec(&preg, ekparams, 5, pmatch, 0) == 0) { *ecc_x = g_strndup(&ekparams[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so); *ecc_y = g_strndup(&ekparams[pmatch[2].rm_so], pmatch[2].rm_eo - pmatch[2].rm_so); if (pmatch[4].rm_so > 0 && pmatch[4].rm_eo > 0) *ecc_curveid = g_strndup(&ekparams[pmatch[4].rm_so], pmatch[4].rm_eo - pmatch[4].rm_so); ret = 2; } regfree(&preg); return ret; } /* Get the next serial number from the certserial file; if it contains * a non-numeric content start over with serial number '1'. */ static int get_next_serial(const gchar *certserial, const gchar *lockfile, gchar **serial_str) { g_autofree gchar *buffer = NULL; size_t buffer_len; unsigned long long serial, serial_n; char *endptr = NULL; int lockfd; int ret = 1; lockfd = lock_file(lockfile); if (lockfd < 0) return 1; if (access(certserial, R_OK) != 0) write_file(certserial, (unsigned char *)"1", 1); if (read_file(certserial, &buffer, &buffer_len) != 0) goto error; if (buffer_len > 0) { serial = strtoull(buffer, &endptr, 10); if (*endptr == '\0') { serial_n = serial + 1; } else { serial_n = 1; } } else { serial_n = 1; } *serial_str = g_strdup_printf("%llu", serial_n); write_file(certserial, (unsigned char *)*serial_str, strlen(*serial_str)); ret = 0; error: unlock_file(lockfd); return ret; } /* Create a TPM 1.2 or TPM 2 EK or platform cert */ static int create_cert(unsigned long flags, const gchar *typ, const gchar *directory, gchar *ekparams, const gchar *vmid, gchar **tpm_spec_params, gchar **tpm_attr_params, const gchar *signkey, const gchar *signkey_password, const gchar *issuercert, const gchar *parentkey_password, gchar **swtpm_cert_env, const gchar *certserial, const gchar *lockfile, const gchar *optsfile) { gchar ** optsfile_lines = NULL; g_autofree gchar **options = NULL; g_autofree gchar **keyparams = NULL; g_autofree gchar **cmd = NULL; g_autofree gchar *subject = NULL; g_autofree gchar *ecc_x = NULL; g_autofree gchar *ecc_y = NULL; g_autofree gchar *ecc_curveid = NULL; g_autofree gchar *certfile = NULL; g_autofree gchar *serial_str = NULL; gchar **to_free = NULL; gchar **split; const char *certtype; int signkey_pwd_fd = -1; int parentkey_pwd_fd = -1; g_autofree gchar *signkey_pwd_file = NULL; g_autofree gchar *signkey_pwd_file_param = NULL; g_autofree gchar *parentkey_pwd_file = NULL; g_autofree gchar *parentkey_pwd_file_param = NULL; gboolean success; g_autofree gchar *standard_output = NULL; g_autofree gchar *standard_error = NULL; g_autofree gchar *swtpm_cert_path = NULL; GError *error = NULL; gint exit_status; int ret = 1; size_t i, j; swtpm_cert_path = g_find_program_in_path("swtpm_cert"); if (swtpm_cert_path == NULL) { logerr(gl_LOGFILE, "Could not find swtpm_cert in PATH.\n"); return 1; } if (get_next_serial(certserial, lockfile, &serial_str) != 0) return 1; /* try to read the optsfile */ read_file_lines(optsfile, &optsfile_lines); /* split each line from the optsfile and add the stripped parameters to options */ for (i = 0; optsfile_lines != NULL && optsfile_lines[i] != NULL; i++) { gchar *chomped = g_strchomp(optsfile_lines[i]); if (strlen(chomped) == 0) continue; split = g_strsplit(chomped, " ", -1); for (j = 0; split[j] != NULL; j++) { chomped = g_strchomp(split[j]); if (strlen(chomped) > 0) { gchar *to_add = g_strdup(chomped); options = concat_arrays(options, (gchar *[]){to_add, NULL}, TRUE); /* need to collect this also to free later on */ to_free = concat_arrays(to_free, (gchar *[]){to_add, NULL}, TRUE); } } g_strfreev(split); } if (vmid != NULL) subject = g_strdup_printf("CN=%s", vmid); else subject = g_strdup("CN=unknown"); if (flags & SETUP_TPM2_F) options = concat_arrays(options, (gchar *[]){"--tpm2", NULL}, TRUE); else options = concat_arrays(options, (gchar *[]){"--add-header", NULL}, TRUE); if (strcmp(typ, "ek") == 0) { if (flags & ALLOW_SIGNING_F) options = concat_arrays(options, (gchar *[]){"--allow-signing", NULL}, TRUE); if (flags & DECRYPTION_F) options = concat_arrays(options, (gchar *[]){"--decryption", NULL}, TRUE); } switch (extract_ecc_params(ekparams, &ecc_x, &ecc_y, &ecc_curveid)) { case 1: goto error; case 2: keyparams = concat_arrays((gchar *[]){ "--ecc-x", ecc_x, "--ecc-y", ecc_y, NULL }, NULL, FALSE); if (ecc_curveid != NULL) keyparams = concat_arrays(keyparams, (gchar *[]){ "--ecc-curveid", ecc_curveid, NULL }, TRUE); break; case 0: keyparams = concat_arrays((gchar *[]){ "--modulus", ekparams, NULL}, NULL, FALSE); break; } cmd = concat_arrays((gchar *[]){ swtpm_cert_path, "--subject", subject, NULL }, options, FALSE); if (signkey_password != NULL) { signkey_pwd_fd = write_to_tempfile(&signkey_pwd_file, (unsigned char *)signkey_password, strlen(signkey_password)); if (signkey_pwd_fd < 0) goto error; signkey_pwd_file_param = g_strdup_printf("file:%s", signkey_pwd_file); cmd = concat_arrays(cmd, (gchar*[]){"--signkey-pwd", signkey_pwd_file_param, NULL}, TRUE); } if (parentkey_password != NULL) { parentkey_pwd_fd = write_to_tempfile(&parentkey_pwd_file, (unsigned char *)parentkey_password, strlen(parentkey_password)); if (parentkey_pwd_fd < 0) goto error; parentkey_pwd_file_param = g_strdup_printf("file:%s", parentkey_pwd_file); cmd = concat_arrays(cmd, (gchar*[]){"--parentkey-pwd", parentkey_pwd_file_param, NULL}, TRUE); } if (strcmp(typ, "ek") == 0) cmd = concat_arrays(cmd, tpm_spec_params, TRUE); cmd = concat_arrays(cmd, tpm_attr_params, TRUE); if (strcmp(typ, "platform") == 0) { certfile = g_strjoin(G_DIR_SEPARATOR_S, directory, "platform.cert", NULL); cmd = concat_arrays(cmd, (gchar *[]){ "--type", "platform", "--out-cert", certfile, NULL}, TRUE); } else { certfile = g_strjoin(G_DIR_SEPARATOR_S, directory, "ek.cert", NULL); cmd = concat_arrays(cmd, (gchar *[]){ "--out-cert", certfile, NULL }, TRUE); } cmd = concat_arrays(cmd, keyparams, TRUE); cmd = concat_arrays(cmd, (gchar *[]){ "--signkey", (gchar *)signkey, "--issuercert", (gchar *)issuercert, "--days", "3600", "--serial", (gchar *)serial_str, NULL }, TRUE); if (strcmp(typ, "ek") == 0) certtype = "EK"; else certtype = "platform"; #if 0 { g_autofree gchar *join = g_strjoinv(" ", cmd); fprintf(stderr, "Starting: %s\n", join); } #endif success = g_spawn_sync(NULL, cmd, swtpm_cert_env, G_SPAWN_DEFAULT, NULL, NULL, &standard_output, &standard_error, &exit_status, &error); if (!success) { logerr(gl_LOGFILE, "Could not run swtpm_cert: %s\n", error); g_error_free(error); goto error; } if (exit_status != 0) { logerr(gl_LOGFILE, "Could not create %s certificate locally\n", certtype); logerr(gl_LOGFILE, "%s\n", standard_error); goto error; } logit(gl_LOGFILE, "Successfully created %s certificate locally.\n", certtype); ret = 0; error: g_strfreev(optsfile_lines); g_strfreev(to_free); if (signkey_pwd_fd >= 0) close(signkey_pwd_fd); if (signkey_pwd_file) unlink(signkey_pwd_file); if (parentkey_pwd_fd >= 0) close(parentkey_pwd_fd); if (parentkey_pwd_file) unlink(parentkey_pwd_file); return ret; } static void usage(const char *prgname) { printf( "Usage: %s [options]\n" "\n" "The following options are supported:\n" "\n" "--type type The type of certificate to create: 'ek' or 'platform'\n" "--ek key-param The modulus of an RSA key or x=...,y=,... for an EC key\n" "--dir directory The directory to write the resulting certificate into\n" "--vmid vmid The ID of the virtual machine\n" "--optsfile file A file containing options to pass to swtpm_cert\n" "--configfile file A file containing configuration parameters for directory,\n" " signing key and password and certificate to use\n" "--logfile file A file to write a log into\n" "--tpm-spec-family s The implemented spec family, e.g., '2.0'\n" "--tpm-spec-revision i The spec revision of the TPM as integer; e.g., 146\n" "--tpm-spec-level i The spec level of the TPM; must be an integer; e.g. 0\n" "--tpm-manufacturer s The manufacturer of the TPM; e.g., id:00001014\n" "--tpm-model s The model of the TPM; e.g., 'swtpm'\n" "--tpm-version i The (firmware) version of the TPM; e.g., id:20160511\n" "--tpm2 Generate a certificate for a TPM 2\n" "--allow-signing The TPM 2's EK can be used for signing\n" "--decryption The TPM 2's EK can be used for decryption\n" "--help, -h, -? Display this help screen and exit\n" "\n" "\n" "The following environment variables are supported:\n" "\n" "SWTPM_ROOTCA_PASSWORD The root CA's private key password\n" "\n", prgname); } int main(int argc, char *argv[]) { int opt, option_index = 0; const static struct option long_options[] = { {"type", required_argument, NULL, 't'}, {"ek", required_argument, NULL, 'e'}, {"dir", required_argument, NULL, 'd'}, {"vmid", required_argument, NULL, 'v'}, {"optsfile", required_argument, NULL, 'o'}, {"configfile", required_argument, NULL, 'c'}, {"logfile", required_argument, NULL, 'l'}, {"tpm-spec-family", required_argument, NULL, 'f'}, {"tpm-spec-revision", required_argument, NULL, 'r'}, {"tpm-spec-level", required_argument, NULL, '1'}, {"tpm-manufacturer", required_argument, NULL, 'a'}, {"tpm-model", required_argument, NULL, 'm'}, {"tpm-version", required_argument, NULL, 's'}, {"tpm2", no_argument, NULL, '2'}, {"allow-signing", no_argument, NULL, 'i'}, {"decryption", no_argument, NULL, 'y'}, {"help", no_argument, NULL, 'h'}, }; g_autofree gchar *default_options_file = NULL; g_autofree gchar *default_config_file = NULL; g_autofree gchar *optsfile = NULL; g_autofree gchar *configfile = NULL; unsigned long flags = 0; g_autofree gchar *typ =g_strdup(""); g_autofree gchar *ekparams = g_strdup(""); g_autofree gchar *directory = g_strdup("."); /* default to current directory */ g_autofree gchar *vmid = NULL; g_autofree gchar *lockfile = NULL; g_autofree gchar *statedir = NULL; g_autofree gchar *signkey = NULL; g_autofree gchar *signkey_password = NULL; g_autofree gchar *parentkey_password = NULL; g_autofree gchar *issuercert = NULL; g_autofree gchar *certserial = NULL; gchar **tpm_spec_params = NULL; gchar **tpm_attr_params = NULL; gchar **config_file_lines = NULL; gchar **swtpm_cert_env = NULL; const struct passwd *curr_user; struct stat statbuf; int ret = 1; if (init(&default_options_file, &default_config_file) < 0) goto error; optsfile = g_strdup(default_options_file); configfile = g_strdup(default_config_file); while ((opt = getopt_long(argc, argv, "h?", long_options, &option_index)) != -1) { switch (opt) { case 't': /* --type */ g_free(typ); typ = g_strdup(optarg); break; case 'e': /* --ek */ g_free(ekparams); ekparams = g_strdup(optarg); break; case 'd': /* --dir */ g_free(directory); directory = g_strdup(optarg); break; case 'v': /* --vmid */ g_free(vmid); vmid = g_strdup(optarg); break; case 'o': /* --optsfile */ g_free(optsfile); optsfile = g_strdup(optarg); break; case 'c': /* --configfile */ g_free(configfile); configfile = g_strdup(optarg); break; case 'l': /* --logfile */ g_free(gl_LOGFILE); gl_LOGFILE = g_strdup(optarg); break; case 'f': /* --tpm-spec-family */ case 'r': /* --tpm-spec-revision */ case '1': /* --tpm-spec-level */ tpm_spec_params = concat_arrays(tpm_spec_params, (gchar *[]) { g_strdup_printf("--%s", long_options[option_index].name), g_strdup(optarg), NULL }, TRUE); break; case 'a': /* --tpm-manufacturer */ case 'm': /* --tpm-model */ case 's': /* --tpm-version */ tpm_attr_params = concat_arrays(tpm_attr_params, (gchar *[]) { g_strdup_printf("--%s", long_options[option_index].name), g_strdup(optarg), NULL }, TRUE); break; case '2': /* --tpm2 */ flags |= SETUP_TPM2_F; break; case 'i': /* --allow-signing */ flags |= ALLOW_SIGNING_F; break; case 'y': /* --decryption */ flags |= DECRYPTION_F; break; case '?': case 'h': /* --help */ usage(argv[0]); ret = 0; goto out; default: fprintf(stderr, "Unknown option code %d\n", opt); usage(argv[0]); goto error; } } curr_user = getpwuid(getuid()); if (gl_LOGFILE != NULL) { FILE *tmpfile; if (stat(gl_LOGFILE, &statbuf) == 0 && (statbuf.st_mode & S_IFMT) == S_IFLNK) { fprintf(stderr, "Logfile must not be a symlink.\n"); goto error; } tmpfile = fopen(gl_LOGFILE, "a"); // do not truncate if (tmpfile == NULL) { fprintf(stderr, "Cannot write to logfile %s.\n", gl_LOGFILE); goto error; } fclose(tmpfile); } if (access(optsfile, R_OK) != 0) { logerr(gl_LOGFILE, "Need read rights on options file %s for user %s.\n", optsfile, curr_user ? curr_user->pw_name : ""); goto error; } if (access(configfile, R_OK) != 0) { logerr(gl_LOGFILE, "Need read rights on config file %s for user %s.\n", configfile, curr_user ? curr_user->pw_name : ""); goto error; } if (read_file_lines(configfile, &config_file_lines) != 0) goto error; statedir = get_config_value(config_file_lines, "statedir", NULL); if (statedir == NULL) { logerr(gl_LOGFILE, "Missing 'statedir' config value in config file %s.\n", configfile); goto error; } if (makedir(statedir, "statedir") != 0) goto error; if (access(statedir, W_OK | R_OK) != 0) { logerr(gl_LOGFILE, "Need read/write rights on statedir %s for user %s.\n", statedir, curr_user ? curr_user->pw_name : ""); goto error; } lockfile = g_strjoin(G_DIR_SEPARATOR_S, statedir, ".lock.swtpm-localca", NULL); if (stat(lockfile, &statbuf) == 0 && access(lockfile, W_OK | R_OK) != 0) { logerr(gl_LOGFILE, "Need read/write rights on %s for user %s.\n", lockfile, curr_user ? curr_user->pw_name : ""); goto error; } signkey = get_config_value(config_file_lines, "signingkey", NULL); if (signkey == NULL) { logerr(gl_LOGFILE, "Missing 'signingkey' config value in config file %s.\n", configfile); goto error; } if (!g_str_has_prefix(signkey, "tpmkey:file=") && !g_str_has_prefix(signkey, "tpmkey:uuid=") && !g_str_has_prefix(signkey, "pkcs11:")) { g_autofree gchar *d = g_path_get_dirname(signkey); if (makedir(d, "signkey") != 0) goto error; } signkey_password = get_config_value(config_file_lines, "signingkey_password", NULL); parentkey_password = get_config_value(config_file_lines, "parentkey_password", NULL); issuercert = get_config_value(config_file_lines, "issuercert", NULL); if (issuercert == NULL) { logerr(gl_LOGFILE, "Missing 'issuercert' config value in config file %s.\n", configfile); goto error; } { g_autofree gchar *d = g_path_get_dirname(issuercert); if (makedir(d, "issuercert") != 0) goto error; } swtpm_cert_env = g_get_environ(); // TPM keys are GNUTLS URIs... if (g_str_has_prefix(signkey, "tpmkey:file=") || g_str_has_prefix(signkey, "tpmkey:uuid=")) { gchar *tss_tcsd_hostname = get_config_value(config_file_lines, "TSS_TCSD_HOSTNAME", "localhost"); gchar *tss_tcsd_port = get_config_value(config_file_lines, "TSS_TCSD_PORT", "30003"); swtpm_cert_env = g_environ_setenv(swtpm_cert_env, "TSS_TCSD_HOSTNAME", tss_tcsd_hostname, TRUE); swtpm_cert_env = g_environ_setenv(swtpm_cert_env, "TSS_TCSD_PORT", tss_tcsd_port, TRUE); logit(gl_LOGFILE, "CA uses a GnuTLS TPM key; using TSS_TCSD_HOSTNAME=%s " \ "TSS_TCSD_PORT=%s\n", tss_tcsd_hostname, tss_tcsd_port); } else if (g_str_has_prefix(signkey, "pkcs11:")) { gchar *tmp = str_replace(signkey, "\\;", ";"); /* historical reasons ... */ g_free(signkey); signkey = tmp; if (signkey_password != NULL) { swtpm_cert_env = g_environ_setenv(swtpm_cert_env, "SWTPM_PKCS11_PIN", g_strdup(signkey_password), TRUE); logit(gl_LOGFILE, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n"); } else { g_autofree gchar *swtpm_pkcs11_pin = NULL; swtpm_pkcs11_pin = get_config_value(config_file_lines, "SWTPM_PKCS11_PIN", "swtpm-tpmca"); swtpm_cert_env = g_environ_setenv(swtpm_cert_env, "SWTPM_PKCS11_PIN", swtpm_pkcs11_pin, TRUE); logit(gl_LOGFILE, "CA uses a PKCS#11 key; using SWTPM_PKCS11_PIN\n"); } ret = get_config_envvars(config_file_lines, &swtpm_cert_env); if (ret != 0) goto error; } else { int create_certs = 0; /* create certificate if either the signing key or issuer cert are missing */ if (access(signkey, R_OK) != 0) { if (stat(signkey, &statbuf) == 0) { logerr(gl_LOGFILE, "Need read rights on signing key %s for user %s.\n", signkey, curr_user ? curr_user->pw_name : ""); goto error; } create_certs = 1; } if (access(issuercert, R_OK) != 0) { if (stat(issuercert, &statbuf) == 0) { logerr(gl_LOGFILE, "Need read rights on issuer certificate %s for user %s.\n", issuercert, curr_user ? curr_user->pw_name : ""); goto error; } create_certs = 1; } if (create_certs) { logit(gl_LOGFILE, "Creating root CA and a local CA's signing key and issuer cert.\n"); if (create_localca_cert(lockfile, statedir, signkey, signkey_password, issuercert) != 0) { logerr(gl_LOGFILE, "Error creating local CA's signing key and cert.\n"); goto error; } if (access(signkey, R_OK) != 0) { logerr(gl_LOGFILE, "Need read rights on signing key %s for user %s.\n", signkey, curr_user ? curr_user->pw_name : ""); goto error; } } } if (access(issuercert, R_OK) != 0) { logerr(gl_LOGFILE, "Need read rights on issuer certificate %s for user %s.\n", issuercert, curr_user ? curr_user->pw_name : ""); goto error; } { g_autofree gchar *d = NULL; g_autofree gchar *p = g_strjoin(G_DIR_SEPARATOR_S, statedir, "certserial", NULL); certserial = get_config_value(config_file_lines, "certserial", p); d = g_path_get_dirname(certserial); if (makedir(d, "certserial") != 0) goto error; } ret = create_cert(flags, typ, directory, ekparams, vmid, tpm_spec_params, tpm_attr_params, signkey, signkey_password, issuercert, parentkey_password, swtpm_cert_env, certserial, lockfile, optsfile); out: error: g_strfreev(tpm_attr_params); g_strfreev(tpm_spec_params); exit(ret); }