/man/man3/*.3
/man/man8/*.8
/samples/swtpm-create-user-config-files
-/samples/swtpm_localca
/samples/swtpm-localca
-/samples/swtpm-localca.2inst
/samples/swtpm-localca.conf
-/samples/swtpm_localca_conf.h
/samples/swtpm_setup.conf
/src/selinux/*.pp.bz2
/src/selinux/swtpm.pp
/src/swtpm_bios/swtpm_bios
/src/swtpm_cert/swtpm_cert
/src/swtpm_ioctl/swtpm_ioctl
+/src/swtpm_localca/swtpm_localca
+/src/swtpm_localca/swtpm_localca_conf.h
src/swtpm_setup/swtpm_setup
src/swtpm_setup/swtpm_setup_conf.h
/test-driver
debian/swtpm-tools.postinst \
swtpm.spec \
samples/Makefile \
- samples/swtpm-localca.2inst \
samples/swtpm-localca.conf \
samples/swtpm-create-user-config-files \
- samples/swtpm_localca_conf.h \
samples/swtpm_setup.conf \
include/Makefile \
include/swtpm/Makefile \
src/swtpm_bios/Makefile \
src/swtpm_cert/Makefile \
src/swtpm_ioctl/Makefile \
+ src/swtpm_localca/Makefile \
+ src/swtpm_localca/swtpm_localca_conf.h \
src/swtpm_setup/Makefile \
src/swtpm_setup/swtpm_setup_conf.h \
src/utils/Makefile \
samplesconf_SCRIPTS = \
swtpm-create-tpmca \
- swtpm-create-user-config-files
+ swtpm-create-user-config-files \
+ swtpm-localca
samplessysconf_DATA = \
swtpm-localca.conf \
swtpm-localca.options \
swtpm_setup.conf
-noinst_HEADERS = \
- swtpm_localca.h \
- swtpm_localca_utils.h
-
-bin_PROGRAMS = \
- swtpm_localca
-
$(top_builddir)/src/utils/libswtpm_utils.la:
$(MAKE) -C$(dir $@)
-swtpm_localca_DEPENDENCIES = \
- $(top_builddir)/src/utils/libswtpm_utils.la
-
-swtpm_localca_SOURCES = \
- swtpm_localca.c \
- swtpm_localca_utils.c
-
-swtpm_localca_LDADD = \
- $(top_builddir)/src/utils/libswtpm_utils.la
-
-swtpm_localca_LDFLAGS = \
- -L$(top_builddir)/src/utils -lswtpm_utils \
- $(HARDENING_LDFLAGS) \
- $(GLIB_LIBS)
-
-swtpm_localca_CFLAGS = \
- -I$(top_srcdir)/src/utils \
- $(HARDENING_CFLAGS) \
- $(GLIB_CFLAGS)
-
install-data-local:
$(MKDIR_P) $(DESTDIR)$(localstatedir)/lib/swtpm-localca
if test -z $(DESTDIR); then \
chmod 0750 $(DESTDIR)$(localstatedir)/lib/swtpm-localca || true; \
fi
-install-exec-local:
- $(MKDIR_P) $(DESTDIR)$(samplesconfdir)
- $(INSTALL_SCRIPT) swtpm-localca.2inst $(DESTDIR)$(samplesconfdir)/swtpm-localca
-
-uninstall-local:
- rm -f $(DESTDIR)$(samplesconfdir)/swtpm-localca
-
EXTRA_DIST= \
swtpm-create-tpmca \
swtpm-create-user-config-files \
+++ /dev/null
-#!/usr/bin/env sh
-
-@BINDIR@/swtpm_localca "$@"
-
-exit $?
#!/usr/bin/env sh
-swtpm_localca_exe="$(dirname "$0")/swtpm_localca"
-if ! [ -x "$swtpm_localca_exe" ]; then
- swtpm_localca_exe=@BINDIR@/swtpm_localca
-fi
-
-$swtpm_localca_exe "$@"
+@BINDIR@/swtpm_localca "$@"
exit $?
+++ /dev/null
-/* 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 <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <pwd.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <glib.h>
-
-#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) {
- 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;
- static const 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 : "<unknown>");
- 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 : "<unknown>");
- 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 : "<unknown>");
- 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 : "<unknown>");
- 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 {
- 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 : "<unknown>");
- goto error;
- }
-
- 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 : "<unknown>");
- 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 : "<unknown>");
- 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);
-}
+++ /dev/null
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * swtpm_localca.h: Header file
- *
- * Author: Stefan Berger, stefanb@linux.ibm.com
- *
- * Copyright (c) IBM Corporation, 2021
- */
-
-#ifndef SWTPM_LOCALCA_H
-#define SWTPM_LOCALCA_H
-
-#include <glib.h>
-
-extern gchar *gl_LOGFILE;
-
-#endif /* SWTPM_LOCALCA_H */
+++ /dev/null
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * swtpm_setup_conf.h: Compile-time constants
- *
- * Author: Stefan Berger, stefanb@linux.ibm.com
- *
- * Copyright (c) IBM Corporation, 2021
- */
-
-#ifndef SWTPM_LOCALCA_CONF_H
-#define SWTPM_LOCALCA_CONF_H
-
-#define SYSCONFDIR "@SYSCONFDIR@"
-
-#endif /* SWTPM_LOCALCA_CONF_H */
+++ /dev/null
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * swtpm_localca_utils.c: Utility functions
- *
- * Author: Stefan Berger, stefanb@linux.ibm.com
- *
- * Copyright (c) IBM Corporation, 2021
- */
-
-#include "config.h"
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <regex.h>
-#include <errno.h>
-#include <sys/file.h>
-
-#include "swtpm_utils.h"
-#include "swtpm_localca.h"
-#include "swtpm_localca_utils.h"
-
-/* Create a directory pat (and all its predecessors) if it doesn't exist */
-int makedir(const char *dirname, const char *purpose)
-{
- struct stat statbuf;
-
- if (stat(dirname, &statbuf) != 0) {
- logit(gl_LOGFILE, "Creating swtpm-localca dir '%s'.\n", dirname);
- if (g_mkdir_with_parents(dirname, S_IRWXU | S_IRWXG | S_IXGRP | S_IRGRP) == -1) {
- logerr(gl_LOGFILE, "Could not create directory for '%s': %s\n",
- purpose, strerror(errno));
- return 1;
- }
- }
- return 0;
-}
-
-/* Get a configuration value given its name */
-gchar *get_config_value(gchar **config_file_lines, const gchar *configname, const gchar *fallback)
-{
- g_autofree gchar *regex = g_strdup_printf("^%s[[:space:]]*=[[:space:]]*([^#\n]*).*", configname);
- gchar *result = NULL;
- regex_t preg;
- size_t idx;
- regmatch_t pmatch[2];
-
- if (regcomp(&preg, regex, REG_EXTENDED) != 0) {
- logerr(gl_LOGFILE, "Internal error: Could not compile regex\n");
- goto error;
- }
-
- for (idx = 0; config_file_lines[idx] != NULL; idx++) {
- const gchar *line = config_file_lines[idx];
- if (regexec(&preg, line, 2, pmatch, 0) == 0) {
- result = g_strndup(&line[pmatch[1].rm_so],
- pmatch[1].rm_eo - pmatch[1].rm_so);
- /* coverity: g_strchmop modifies in-place */
- result = g_strchomp(result);
- break;
- }
- }
- regfree(&preg);
-
-error:
- if (result == NULL)
- result = g_strdup(fallback);
- //printf("Found match for %s: |%s|\n", configname, result);
-
- return result;
-}
-
-/* Extract all environment variables from the config file and add them to
- * the given environent.
- * Environment variable lines must start with 'env:' and must not contain
- * trailing spaces or a comment starting with '#'
- */
-int get_config_envvars(gchar **config_file_lines, gchar ***env)
-{
- const char *regex = "^env:([a-zA-Z_][a-zA-Z_0-9]*)[[:space:]]*=[[:space:]]*([^\n]*)";
- regex_t preg;
- size_t idx;
- regmatch_t pmatch[3];
-
- if (regcomp(&preg, regex, REG_EXTENDED) != 0) {
- logerr(gl_LOGFILE, "Internal error: Could not compile regex\n");
- return 1;
- }
-
- for (idx = 0; config_file_lines[idx] != NULL; idx++) {
- const gchar *line = config_file_lines[idx];
- if (regexec(&preg, line, 3, pmatch, 0) == 0) {
- g_autofree gchar *key = NULL, *value = NULL;
-
- key = g_strndup(&line[pmatch[1].rm_so],
- pmatch[1].rm_eo - pmatch[1].rm_so);
- value = g_strndup(&line[pmatch[2].rm_so],
- pmatch[2].rm_eo - pmatch[2].rm_so);
- *env = g_environ_setenv(*env, key, value, TRUE);
- }
- }
-
- regfree(&preg);
-
- return 0;
-}
-
-/* flock a file; the file descriptor for the file to unlock later on is returned */
-int lock_file(const gchar *lockfile)
-{
- int lockfd;
- mode_t mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
-
- lockfd = open(lockfile, O_RDWR | O_CREAT, mode);
- if (lockfd < 0) {
- logerr(gl_LOGFILE, "Could not open lockfile %s: %s\n", lockfile, strerror(errno));
- return -1;
- }
-
- if (flock(lockfd, LOCK_EX) < 0) {
- logerr(gl_LOGFILE, "Could not lock file %s: %s\n", lockfile, strerror(errno));
- close(lockfd);
- return -1;
- }
- return lockfd;
-}
-
-/* unlock a file previously locked using lock_file */
-void unlock_file(int lockfd) {
- if (lockfd >= 0) {
- flock(lockfd, LOCK_UN);
- close(lockfd);
- }
-}
+++ /dev/null
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * swtpm_localca_utils.h: Header file for swtpm_localca_utils.c
- *
- * Author: Stefan Berger, stefanb@linux.ibm.com
- *
- * Copyright (c) IBM Corporation, 2021
- */
-
-#ifndef SWTPM_LOCALCA_UTILS_H
-#define SWTPM_LOCALCA_UTILS_H
-
-#include <glib.h>
-
-gchar *get_config_value(gchar **config_file_lines, const gchar *varname, const gchar *fallback);
-int get_config_envvars(gchar **config_file_lines, gchar ***env);
-
-int makedir(const char *dirname, const char *purpose);
-
-int lock_file(const gchar *lockfile);
-void unlock_file(int lockfd);
-
-#endif /* SWTPM_LOCALCA_UTILS_H */
swtpm_bios \
swtpm_cert \
swtpm_ioctl \
+ swtpm_localca \
swtpm_setup
if WITH_SELINUX
--- /dev/null
+#
+# src/swtpm_localca/Makefile.am
+#
+# For the license, see the LICENSE file in the root directory.
+#
+
+bin_PROGRAMS = \
+ swtpm_localca
+
+noinst_HEADERS = \
+ swtpm_localca.h \
+ swtpm_localca_utils.h
+
+swtpm_localca_CFLAGS = \
+ -I$(top_srcdir)/src/utils \
+ $(GLIB_CFLAGS) \
+ $(HARDENING_CFLAGS)
+
+swtpm_localca_DEPENDENCIES = \
+ $(top_builddir)/src/utils/libswtpm_utils.la
+
+swtpm_localca_LDADD = \
+ $(top_builddir)/src/utils/libswtpm_utils.la
+
+swtpm_localca_LDFLAGS = \
+ -L$(top_builddir)/src/utils -lswtpm_utils \
+ $(GLIB_LIBS) \
+ $(HARDENING_LDFLAGS)
+
+swtpm_localca_SOURCES = \
+ swtpm_localca.c \
+ swtpm_localca_utils.c
--- /dev/null
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+
+#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) {
+ 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;
+ static const 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 : "<unknown>");
+ 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 : "<unknown>");
+ 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 : "<unknown>");
+ 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 : "<unknown>");
+ 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 {
+ 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 : "<unknown>");
+ goto error;
+ }
+
+ 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 : "<unknown>");
+ 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 : "<unknown>");
+ 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);
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * swtpm_localca.h: Header file
+ *
+ * Author: Stefan Berger, stefanb@linux.ibm.com
+ *
+ * Copyright (c) IBM Corporation, 2021
+ */
+
+#ifndef SWTPM_LOCALCA_H
+#define SWTPM_LOCALCA_H
+
+#include <glib.h>
+
+extern gchar *gl_LOGFILE;
+
+#endif /* SWTPM_LOCALCA_H */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * swtpm_setup_conf.h: Compile-time constants
+ *
+ * Author: Stefan Berger, stefanb@linux.ibm.com
+ *
+ * Copyright (c) IBM Corporation, 2021
+ */
+
+#ifndef SWTPM_LOCALCA_CONF_H
+#define SWTPM_LOCALCA_CONF_H
+
+#define SYSCONFDIR "@SYSCONFDIR@"
+
+#endif /* SWTPM_LOCALCA_CONF_H */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * swtpm_localca_utils.c: Utility functions
+ *
+ * Author: Stefan Berger, stefanb@linux.ibm.com
+ *
+ * Copyright (c) IBM Corporation, 2021
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <errno.h>
+#include <sys/file.h>
+
+#include "swtpm_utils.h"
+#include "swtpm_localca.h"
+#include "swtpm_localca_utils.h"
+
+/* Create a directory pat (and all its predecessors) if it doesn't exist */
+int makedir(const char *dirname, const char *purpose)
+{
+ struct stat statbuf;
+
+ if (stat(dirname, &statbuf) != 0) {
+ logit(gl_LOGFILE, "Creating swtpm-localca dir '%s'.\n", dirname);
+ if (g_mkdir_with_parents(dirname, S_IRWXU | S_IRWXG | S_IXGRP | S_IRGRP) == -1) {
+ logerr(gl_LOGFILE, "Could not create directory for '%s': %s\n",
+ purpose, strerror(errno));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Get a configuration value given its name */
+gchar *get_config_value(gchar **config_file_lines, const gchar *configname, const gchar *fallback)
+{
+ g_autofree gchar *regex = g_strdup_printf("^%s[[:space:]]*=[[:space:]]*([^#\n]*).*", configname);
+ gchar *result = NULL;
+ regex_t preg;
+ size_t idx;
+ regmatch_t pmatch[2];
+
+ if (regcomp(&preg, regex, REG_EXTENDED) != 0) {
+ logerr(gl_LOGFILE, "Internal error: Could not compile regex\n");
+ goto error;
+ }
+
+ for (idx = 0; config_file_lines[idx] != NULL; idx++) {
+ const gchar *line = config_file_lines[idx];
+ if (regexec(&preg, line, 2, pmatch, 0) == 0) {
+ result = g_strndup(&line[pmatch[1].rm_so],
+ pmatch[1].rm_eo - pmatch[1].rm_so);
+ /* coverity: g_strchmop modifies in-place */
+ result = g_strchomp(result);
+ break;
+ }
+ }
+ regfree(&preg);
+
+error:
+ if (result == NULL)
+ result = g_strdup(fallback);
+ //printf("Found match for %s: |%s|\n", configname, result);
+
+ return result;
+}
+
+/* Extract all environment variables from the config file and add them to
+ * the given environent.
+ * Environment variable lines must start with 'env:' and must not contain
+ * trailing spaces or a comment starting with '#'
+ */
+int get_config_envvars(gchar **config_file_lines, gchar ***env)
+{
+ const char *regex = "^env:([a-zA-Z_][a-zA-Z_0-9]*)[[:space:]]*=[[:space:]]*([^\n]*)";
+ regex_t preg;
+ size_t idx;
+ regmatch_t pmatch[3];
+
+ if (regcomp(&preg, regex, REG_EXTENDED) != 0) {
+ logerr(gl_LOGFILE, "Internal error: Could not compile regex\n");
+ return 1;
+ }
+
+ for (idx = 0; config_file_lines[idx] != NULL; idx++) {
+ const gchar *line = config_file_lines[idx];
+ if (regexec(&preg, line, 3, pmatch, 0) == 0) {
+ g_autofree gchar *key = NULL, *value = NULL;
+
+ key = g_strndup(&line[pmatch[1].rm_so],
+ pmatch[1].rm_eo - pmatch[1].rm_so);
+ value = g_strndup(&line[pmatch[2].rm_so],
+ pmatch[2].rm_eo - pmatch[2].rm_so);
+ *env = g_environ_setenv(*env, key, value, TRUE);
+ }
+ }
+
+ regfree(&preg);
+
+ return 0;
+}
+
+/* flock a file; the file descriptor for the file to unlock later on is returned */
+int lock_file(const gchar *lockfile)
+{
+ int lockfd;
+ mode_t mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
+
+ lockfd = open(lockfile, O_RDWR | O_CREAT, mode);
+ if (lockfd < 0) {
+ logerr(gl_LOGFILE, "Could not open lockfile %s: %s\n", lockfile, strerror(errno));
+ return -1;
+ }
+
+ if (flock(lockfd, LOCK_EX) < 0) {
+ logerr(gl_LOGFILE, "Could not lock file %s: %s\n", lockfile, strerror(errno));
+ close(lockfd);
+ return -1;
+ }
+ return lockfd;
+}
+
+/* unlock a file previously locked using lock_file */
+void unlock_file(int lockfd) {
+ if (lockfd >= 0) {
+ flock(lockfd, LOCK_UN);
+ close(lockfd);
+ }
+}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * swtpm_localca_utils.h: Header file for swtpm_localca_utils.c
+ *
+ * Author: Stefan Berger, stefanb@linux.ibm.com
+ *
+ * Copyright (c) IBM Corporation, 2021
+ */
+
+#ifndef SWTPM_LOCALCA_UTILS_H
+#define SWTPM_LOCALCA_UTILS_H
+
+#include <glib.h>
+
+gchar *get_config_value(gchar **config_file_lines, const gchar *varname, const gchar *fallback);
+int get_config_envvars(gchar **config_file_lines, gchar ***env);
+
+int makedir(const char *dirname, const char *purpose);
+
+int lock_file(const gchar *lockfile);
+void unlock_file(int lockfd);
+
+#endif /* SWTPM_LOCALCA_UTILS_H */
test_swtpm_cert \
test_swtpm_setup_create_cert \
test_tpm2_parameters \
- test_tpm2_samples_swtpm_localca \
- test_tpm2_samples_swtpm_localca_pkcs11 \
test_tpm2_swtpm_cert \
test_tpm2_swtpm_cert_ecc \
+ test_tpm2_swtpm_localca \
+ test_tpm2_swtpm_localca_pkcs11 \
test_tpm2_swtpm_setup_create_cert
if HAVE_TCSD
TESTS += \
SWTPM_SETUP=${ROOT}/src/swtpm_setup/swtpm_setup
SWTPM_CREATE_TPMCA=${SRCDIR}/samples/swtpm-create-tpmca
-SWTPM_LOCALCA=${ROOT}/samples/swtpm-localca
+SWTPM_LOCALCA=${ROOT}/src/swtpm_localca/swtpm_localca
SWTPM=${ROOT}/src/swtpm/swtpm
SWTPM_IOCTL=${ROOT}/src/swtpm_ioctl/swtpm_ioctl
source ${TESTDIR}/common
-SWTPM_LOCALCA=${ROOT}/samples/swtpm-localca
+SWTPM_LOCALCA=${ROOT}/src/swtpm_localca/swtpm_localca
workdir=$(mktemp -d)
SWTPM_SETUP=${ROOT}/src/swtpm_setup/swtpm_setup
SWTPM_CREATE_TPMCA=${SRCDIR}/samples/swtpm-create-tpmca
-SWTPM_LOCALCA=${SRCDIR}/samples/swtpm-localca
+SWTPM_LOCALCA=${ROOT}/src/swtpm_localca/swtpm_localca
SWTPM=${ROOT}/src/swtpm/swtpm
SWTPM_IOCTL=${ROOT}/src/swtpm_ioctl/swtpm_ioctl
+++ /dev/null
-#!/usr/bin/env bash
-
-# For the license, see the LICENSE file in the root directory.
-#set -x
-
-TOPBUILD=${abs_top_builddir:-$(dirname "$0")/..}
-TOPSRC=${abs_top_srcdir:-$(dirname "$0")/..}
-TESTDIR=${abs_top_testdir:-$(dirname "$0")}
-
-SWTPM_LOCALCA=${TOPBUILD}/samples/swtpm-localca
-
-workdir=$(mktemp -d "/tmp/path with spaces.XXXXXX")
-
-ek="80" # 2048 bit key must have highest bit set
-for ((i = 1; i < 256; i++)); do
- ek="${ek}$(printf "%02x" $i)"
-done
-
-SIGNINGKEY=${workdir}/signingkey.pem
-ISSUERCERT=${workdir}/issuercert.pem
-CERTSERIAL=${workdir}/certserial
-
-PATH=${TOPBUILD}/src/swtpm_cert:$PATH
-
-source ${TESTDIR}/common
-
-trap "cleanup" SIGTERM EXIT
-
-function cleanup()
-{
- rm -rf "${workdir}"
-}
-
-cat <<_EOF_ > "${workdir}/swtpm-localca.conf"
-statedir=${workdir}
-signingkey = ${SIGNINGKEY}
-issuercert = ${ISSUERCERT}
-certserial = ${CERTSERIAL}
-signingkey_password = password
-_EOF_
-
-cat <<_EOF_ > "${workdir}/swtpm-localca.options"
---tpm-manufacturer IBM
---tpm-model swtpm-libtpms
---tpm-version 2
---platform-manufacturer Fedora
---platform-version 2.1
---platform-model QEMU
-_EOF_
-
-# the following contains the test parameters and
-# expected key usage
-for testparams in \
- "--allow-signing|Digital signature" \
- "--allow-signing --decryption|Digital signature,Key encipherment" \
- "--decryption|Key encipherment" \
- "|Key encipherment";
-do
- params=$(echo ${testparams} | cut -d"|" -f1)
- usage=$(echo ${testparams} | cut -d"|" -f2)
-
- ${SWTPM_LOCALCA} \
- --type ek \
- --ek "${ek}" \
- --dir "${workdir}" \
- --vmid test \
- --tpm2 \
- --configfile "${workdir}/swtpm-localca.conf" \
- --optsfile "${workdir}/swtpm-localca.options" \
- --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0 \
- ${params}
- if [ $? -ne 0 ]; then
- echo "Error: Test with parameters '$params' failed."
- exit 1
- fi
-
- # Signing key should always be password protected
- if [ -z "$(grep "ENCRYPTED PRIVATE KEY" "${SIGNINGKEY}")" ]; then
- echo "Error: Signing key is not password protected."
- exit 1
- fi
-
- # For the root CA's key we flip the password protection
- if [ -n "${SWTPM_ROOTCA_PASSWORD}" ] ;then
- if [ -z "$(grep "ENCRYPTED PRIVATE KEY" "${workdir}/swtpm-localca-rootca-privkey.pem")" ]; then
- echo "Error: Root CA's private key is not password protected."
- exit 1
- fi
- unset SWTPM_ROOTCA_PASSWORD
- else
- if [ -n "$(grep "ENCRYPTED PRIVATE KEY" "${workdir}/swtpm-localca-rootca-privkey.pem")" ]; then
- echo "Error: Root CA's private key is password protected but should not be."
- exit 1
- fi
- export SWTPM_ROOTCA_PASSWORD=xyz
- fi
-
- if [ ! -r "${workdir}/ek.cert" ]; then
- echo "Error: ${workdir}/ek.cert was not created."
- exit 1
- fi
-
- OIFS="$IFS"
- IFS=","
-
- for u in $usage; do
- echo $u
- if [ -z "$(${CERTTOOL} -i \
- --inder --infile "${workdir}/ek.cert" | \
- grep "Key Usage" -A2 | \
- grep "$u")" ]; then
- echo "Error: Could not find key usage $u in key created " \
- "with $params."
- else
- echo "Found '$u'"
- fi
- done
-
- IFS="$OIFS"
-
- ${CERTTOOL} \
- -i \
- --inder --infile "${workdir}/ek.cert" \
- --outfile "${workdir}/ek.pem"
-
- ${CERTTOOL} \
- --verify \
- --load-ca-certificate "${ISSUERCERT}" \
- --infile "${workdir}/ek.pem"
- if [ $? -ne 0 ]; then
- echo "Error: Could not verify certificate chain."
- exit 1
- fi
-
- # Delete all keys to have CA re-created
- rm -rf "${workdir}"/*.pem
-done
-
-echo "Test 1: OK"
-echo
-
-#A few tests with odd vm Ids
-for vmid in \
- 's p a c e|s p a c e' \
- '$(ls)>foo|$(ls)\>foo' \
- '`ls`&; #12|`ls`&\; #12' \
- 'foo>&1<&2;$(ls)|foo\>&1\<&2\;$(ls)' \
- "'*|'*" \
- '"*|\"*' \
- ':$$|:$$' \
- '${t}[]|${t}[]';
-do
- in=$(echo "$vmid" | cut -d"|" -f1)
- exp=$(echo "$vmid" | cut -d"|" -f2)
-
- ${SWTPM_LOCALCA} \
- --type ek \
- --ek "${ek}" \
- --dir "${workdir}" \
- --vmid "$in" \
- --tpm2 \
- --configfile "${workdir}/swtpm-localca.conf" \
- --optsfile "${workdir}/swtpm-localca.options" \
- --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0 \
- ${params} &>/dev/null
- if [ $? -ne 0 ]; then
- echo "Error: Test with parameters '$params' failed."
- exit 1
- fi
-
- if [ ! -r "${workdir}/ek.cert" ]; then
- echo "Error: ${workdir}/ek.cert was not created."
- exit 1
- fi
-
- ac=$(${CERTTOOL} -i --inder --infile "${workdir}/ek.cert" | \
- sed -n "s/.*Subject: CN=\(.*\)$/\1/p")
- if [ "$ac" != "$exp" ]; then
- echo "Error: unexpected subject string"
- echo "actual : $ac"
- echo "expected : $exp"
- else
- echo "Pass: $ac"
- fi
-done
-
-echo "Test 2: OK"
-
-exit 0
+++ /dev/null
-#!/usr/bin/env bash
-
-# For the license, see the LICENSE file in the root directory.
-#set -x
-
-TOPBUILD=${abs_top_builddir:-$(dirname "$0")/..}
-TOPSRC=${abs_top_srcdir:-$(dirname "$0")/..}
-TESTDIR=${abs_top_testdir:-$(dirname "$0")}
-
-SWTPM_LOCALCA=${TOPBUILD}/samples/swtpm-localca
-
-workdir=$(mktemp -d)
-if [ $? -ne 0 ]; then
- exit 1
-fi
-
-# Have softhsm_setup use 'workdir'.
-export SOFTHSM_SETUP_CONFIGDIR="${workdir}"
-
-# Since we will be using the pkcs11 module as well via certtool
-# we have to set the environment variable to point to its config file.
-# This has to be the same as for swtpm_setup sets it.
-export SOFTHSM2_CONF="${workdir}"/softhsm2.conf
-
-ek="80" # 2048 bit key must have highest bit set
-for ((i = 1; i < 256; i++)); do
- ek="${ek}$(printf "%02x" $i)"
-done
-
-SIGNINGKEY=${workdir}/signingkey.pem
-ISSUERCERT=${workdir}/issuercert.pem
-CERTSERIAL=${workdir}/certserial
-
-PATH=${TOPBUILD}/src/swtpm_cert:$PATH
-
-source ${TESTDIR}/common
-
-trap "cleanup" SIGTERM EXIT
-
-function cleanup()
-{
- rm -rf ${workdir}
- ${TESTDIR}/softhsm_setup teardown
-}
-
-unset GNUTLS_PIN
-export PIN="abcdef"
-
-# Generate the PKCS11 token and key; it uses env. variable 'PIN'
-msg=$(${TESTDIR}/softhsm_setup setup 2>&1)
-if [ $? -ne 0 ]; then
- echo -e "Could not setup softhsm:\n${msg}"
- echo "softhsm needs to be v2.3.0 or greater and pkcs11 correctly configured"
- exit 77
-fi
-pkcs11uri=$(echo ${msg} | sed -n 's|^keyuri: \(.*\)|\1|p')
-
-# Now we need to create the root CA ...
-template=${workdir}/template
-
-cakey=${workdir}/swtpm-localca-rootca-privkey.pem
-cacert=${workdir}/swtpm-localca-rootca-cert.pem
-
-# first the private key
-msg=$(${CERTTOOL} \
- --generate-privkey \
- --outfile ${cakey} \
- ${passparam} \
- 2>&1)
-if [ $? -ne 0 ]; then
- echo "Could not create root-CA key ${cakey}."
- echo "${msg}"
- exit 1
-fi
-chmod 640 ${cakey}
-
-# now the self-signed certificate
-cat <<_EOF_ >${template}
-cn=swtpm-localca-rootca
-ca
-cert_signing_key
-expiration_days = 3650
-_EOF_
-
-msg=$(${CERTTOOL} \
- --generate-self-signed \
- --template ${template} \
- --outfile ${cacert} \
- --load-privkey ${cakey} \
- 2>&1)
-if [ $? -ne 0 ]; then
- echo "Could not create root CA."
- echo "${msg}"
- exit 1
-fi
-
-# And now create the intermediate CA with the pkcs11 URI key
-
-pubkey=${workdir}/swtpm-localca-interm-pubkey.pem
-
-msg=$(GNUTLS_PIN=${PIN} ${CERTTOOL} \
- --load-privkey ${pkcs11uri} \
- --pubkey-info \
- --outfile ${pubkey})
-if [ $? -ne 0 ]; then
- echo "Could not get public key for pkcs11 uri key ($pkcs11uri}."
- echo "${msg}"
- exit 1
-fi
-
-cat <<_EOF_ > ${template}
-cn=swtpm-localca
-ca
-cert_signing_key
-expiration_days = 3650
-_EOF_
-
-msg=$(GNUTLS_PIN=${PIN} ${CERTTOOL} \
- --generate-certificate \
- --template ${template} \
- --outfile ${ISSUERCERT} \
- --load-ca-privkey ${cakey} \
- --load-ca-certificate ${cacert} \
- --load-privkey ${pkcs11uri} \
- --load-pubkey ${pubkey} \
- 2>&1)
-if [ $? -ne 0 ]; then
- echo "Could not create intermediate CA"
- echo "${msg}"
- exit 1
-fi
-
-echo -n 1 > ${CERTSERIAL}
-
-# Now we can create the config files
-cat <<_EOF_ > ${workdir}/swtpm-localca.conf
-statedir = ${workdir}
-signingkey = $(echo ${pkcs11uri} | sed 's|;|\\;|g')
-issuercert = ${ISSUERCERT}
-certserial = ${CERTSERIAL}
-SWTPM_PKCS11_PIN = ${PIN}
-_EOF_
-
-cat <<_EOF_ > ${workdir}/swtpm-localca.options
---tpm-manufacturer IBM
---tpm-model swtpm-libtpms
---tpm-version 2
---platform-manufacturer Fedora
---platform-version 2.1
---platform-model QEMU
-_EOF_
-
-# the following contains the test parameters and
-# expected key usage
-for testparams in \
- "--allow-signing|Digital signature" \
- "--allow-signing --decryption|Digital signature,Key encipherment" \
- "--decryption|Key encipherment" \
- "|Key encipherment";
-do
- params=$(echo ${testparams} | cut -d"|" -f1)
- usage=$(echo ${testparams} | cut -d"|" -f2)
-
- msg=$(${SWTPM_LOCALCA} \
- --type ek \
- --ek ${ek} \
- --dir ${workdir} \
- --vmid test \
- --tpm2 \
- --configfile ${workdir}/swtpm-localca.conf \
- --optsfile ${workdir}/swtpm-localca.options \
- --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0 \
- ${params} 2>&1)
- if [ $? -ne 0 ]; then
- echo "Error: Test with parameters '$params' failed."
- echo "${msg}"
- if [[ "${msg}" =~ The\ requested\ PKCS\ #11\ object\ is\ not\ available ]]; then
- # could be related to i386 executables on x86_64 host and
- # libsofthsm.so only available for x86_64...
- exit 77
- fi
- exit 1
- fi
-
- if [ ! -r ${workdir}/ek.cert ]; then
- echo "${msg}"
- echo "Error: ${workdir}/ek.cert was not created."
- exit 1
- fi
-
- OIFS="$IFS"
- IFS=","
-
- for u in $usage; do
- if [ -z "$(${CERTTOOL} -i \
- --inder --infile ${workdir}/ek.cert | \
- grep "Key Usage" -A2 | \
- grep "$u")" ]; then
- echo "Error: Could not find key usage $u in key created " \
- "with $params."
- else
- echo "Found '$u'"
- fi
- done
-
- IFS="$OIFS"
-
- ${CERTTOOL} \
- -i \
- --inder --infile ${workdir}/ek.cert \
- --outfile ${workdir}/ek.pem
-
- GNUTLS_PIN=${PIN} ${CERTTOOL} \
- --verify \
- --load-ca-certificate ${ISSUERCERT} \
- --infile ${workdir}/ek.pem
- if [ $? -ne 0 ]; then
- echo "Error: Could not verify certificate chain."
- exit 1
- fi
-done
-
-exit 0
--- /dev/null
+#!/usr/bin/env bash
+
+# For the license, see the LICENSE file in the root directory.
+#set -x
+
+TOPBUILD=${abs_top_builddir:-$(dirname "$0")/..}
+TOPSRC=${abs_top_srcdir:-$(dirname "$0")/..}
+TESTDIR=${abs_top_testdir:-$(dirname "$0")}
+
+SWTPM_LOCALCA=${TOPBUILD}/src/swtpm_localca/swtpm_localca
+
+workdir=$(mktemp -d "/tmp/path with spaces.XXXXXX")
+
+ek="80" # 2048 bit key must have highest bit set
+for ((i = 1; i < 256; i++)); do
+ ek="${ek}$(printf "%02x" $i)"
+done
+
+SIGNINGKEY=${workdir}/signingkey.pem
+ISSUERCERT=${workdir}/issuercert.pem
+CERTSERIAL=${workdir}/certserial
+
+PATH=${TOPBUILD}/src/swtpm_cert:$PATH
+
+source ${TESTDIR}/common
+
+trap "cleanup" SIGTERM EXIT
+
+function cleanup()
+{
+ rm -rf "${workdir}"
+}
+
+cat <<_EOF_ > "${workdir}/swtpm-localca.conf"
+statedir=${workdir}
+signingkey = ${SIGNINGKEY}
+issuercert = ${ISSUERCERT}
+certserial = ${CERTSERIAL}
+signingkey_password = password
+_EOF_
+
+cat <<_EOF_ > "${workdir}/swtpm-localca.options"
+--tpm-manufacturer IBM
+--tpm-model swtpm-libtpms
+--tpm-version 2
+--platform-manufacturer Fedora
+--platform-version 2.1
+--platform-model QEMU
+_EOF_
+
+# the following contains the test parameters and
+# expected key usage
+for testparams in \
+ "--allow-signing|Digital signature" \
+ "--allow-signing --decryption|Digital signature,Key encipherment" \
+ "--decryption|Key encipherment" \
+ "|Key encipherment";
+do
+ params=$(echo ${testparams} | cut -d"|" -f1)
+ usage=$(echo ${testparams} | cut -d"|" -f2)
+
+ ${SWTPM_LOCALCA} \
+ --type ek \
+ --ek "${ek}" \
+ --dir "${workdir}" \
+ --vmid test \
+ --tpm2 \
+ --configfile "${workdir}/swtpm-localca.conf" \
+ --optsfile "${workdir}/swtpm-localca.options" \
+ --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0 \
+ ${params}
+ if [ $? -ne 0 ]; then
+ echo "Error: Test with parameters '$params' failed."
+ exit 1
+ fi
+
+ # Signing key should always be password protected
+ if [ -z "$(grep "ENCRYPTED PRIVATE KEY" "${SIGNINGKEY}")" ]; then
+ echo "Error: Signing key is not password protected."
+ exit 1
+ fi
+
+ # For the root CA's key we flip the password protection
+ if [ -n "${SWTPM_ROOTCA_PASSWORD}" ] ;then
+ if [ -z "$(grep "ENCRYPTED PRIVATE KEY" "${workdir}/swtpm-localca-rootca-privkey.pem")" ]; then
+ echo "Error: Root CA's private key is not password protected."
+ exit 1
+ fi
+ unset SWTPM_ROOTCA_PASSWORD
+ else
+ if [ -n "$(grep "ENCRYPTED PRIVATE KEY" "${workdir}/swtpm-localca-rootca-privkey.pem")" ]; then
+ echo "Error: Root CA's private key is password protected but should not be."
+ exit 1
+ fi
+ export SWTPM_ROOTCA_PASSWORD=xyz
+ fi
+
+ if [ ! -r "${workdir}/ek.cert" ]; then
+ echo "Error: ${workdir}/ek.cert was not created."
+ exit 1
+ fi
+
+ OIFS="$IFS"
+ IFS=","
+
+ for u in $usage; do
+ echo $u
+ if [ -z "$(${CERTTOOL} -i \
+ --inder --infile "${workdir}/ek.cert" | \
+ grep "Key Usage" -A2 | \
+ grep "$u")" ]; then
+ echo "Error: Could not find key usage $u in key created " \
+ "with $params."
+ else
+ echo "Found '$u'"
+ fi
+ done
+
+ IFS="$OIFS"
+
+ ${CERTTOOL} \
+ -i \
+ --inder --infile "${workdir}/ek.cert" \
+ --outfile "${workdir}/ek.pem"
+
+ ${CERTTOOL} \
+ --verify \
+ --load-ca-certificate "${ISSUERCERT}" \
+ --infile "${workdir}/ek.pem"
+ if [ $? -ne 0 ]; then
+ echo "Error: Could not verify certificate chain."
+ exit 1
+ fi
+
+ # Delete all keys to have CA re-created
+ rm -rf "${workdir}"/*.pem
+done
+
+echo "Test 1: OK"
+echo
+
+#A few tests with odd vm Ids
+for vmid in \
+ 's p a c e|s p a c e' \
+ '$(ls)>foo|$(ls)\>foo' \
+ '`ls`&; #12|`ls`&\; #12' \
+ 'foo>&1<&2;$(ls)|foo\>&1\<&2\;$(ls)' \
+ "'*|'*" \
+ '"*|\"*' \
+ ':$$|:$$' \
+ '${t}[]|${t}[]';
+do
+ in=$(echo "$vmid" | cut -d"|" -f1)
+ exp=$(echo "$vmid" | cut -d"|" -f2)
+
+ ${SWTPM_LOCALCA} \
+ --type ek \
+ --ek "${ek}" \
+ --dir "${workdir}" \
+ --vmid "$in" \
+ --tpm2 \
+ --configfile "${workdir}/swtpm-localca.conf" \
+ --optsfile "${workdir}/swtpm-localca.options" \
+ --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0 \
+ ${params} &>/dev/null
+ if [ $? -ne 0 ]; then
+ echo "Error: Test with parameters '$params' failed."
+ exit 1
+ fi
+
+ if [ ! -r "${workdir}/ek.cert" ]; then
+ echo "Error: ${workdir}/ek.cert was not created."
+ exit 1
+ fi
+
+ ac=$(${CERTTOOL} -i --inder --infile "${workdir}/ek.cert" | \
+ sed -n "s/.*Subject: CN=\(.*\)$/\1/p")
+ if [ "$ac" != "$exp" ]; then
+ echo "Error: unexpected subject string"
+ echo "actual : $ac"
+ echo "expected : $exp"
+ else
+ echo "Pass: $ac"
+ fi
+done
+
+echo "Test 2: OK"
+
+exit 0
--- /dev/null
+#!/usr/bin/env bash
+
+# For the license, see the LICENSE file in the root directory.
+#set -x
+
+TOPBUILD=${abs_top_builddir:-$(dirname "$0")/..}
+TOPSRC=${abs_top_srcdir:-$(dirname "$0")/..}
+TESTDIR=${abs_top_testdir:-$(dirname "$0")}
+
+SWTPM_LOCALCA=${TOPBUILD}/src/swtpm_localca/swtpm_localca
+
+workdir=$(mktemp -d)
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+# Have softhsm_setup use 'workdir'.
+export SOFTHSM_SETUP_CONFIGDIR="${workdir}"
+
+# Since we will be using the pkcs11 module as well via certtool
+# we have to set the environment variable to point to its config file.
+# This has to be the same as for swtpm_setup sets it.
+export SOFTHSM2_CONF="${workdir}"/softhsm2.conf
+
+ek="80" # 2048 bit key must have highest bit set
+for ((i = 1; i < 256; i++)); do
+ ek="${ek}$(printf "%02x" $i)"
+done
+
+SIGNINGKEY=${workdir}/signingkey.pem
+ISSUERCERT=${workdir}/issuercert.pem
+CERTSERIAL=${workdir}/certserial
+
+PATH=${TOPBUILD}/src/swtpm_cert:$PATH
+
+source ${TESTDIR}/common
+
+trap "cleanup" SIGTERM EXIT
+
+function cleanup()
+{
+ rm -rf ${workdir}
+ ${TESTDIR}/softhsm_setup teardown
+}
+
+unset GNUTLS_PIN
+export PIN="abcdef"
+
+# Generate the PKCS11 token and key; it uses env. variable 'PIN'
+msg=$(${TESTDIR}/softhsm_setup setup 2>&1)
+if [ $? -ne 0 ]; then
+ echo -e "Could not setup softhsm:\n${msg}"
+ echo "softhsm needs to be v2.3.0 or greater and pkcs11 correctly configured"
+ exit 77
+fi
+pkcs11uri=$(echo ${msg} | sed -n 's|^keyuri: \(.*\)|\1|p')
+
+# Now we need to create the root CA ...
+template=${workdir}/template
+
+cakey=${workdir}/swtpm-localca-rootca-privkey.pem
+cacert=${workdir}/swtpm-localca-rootca-cert.pem
+
+# first the private key
+msg=$(${CERTTOOL} \
+ --generate-privkey \
+ --outfile ${cakey} \
+ ${passparam} \
+ 2>&1)
+if [ $? -ne 0 ]; then
+ echo "Could not create root-CA key ${cakey}."
+ echo "${msg}"
+ exit 1
+fi
+chmod 640 ${cakey}
+
+# now the self-signed certificate
+cat <<_EOF_ >${template}
+cn=swtpm-localca-rootca
+ca
+cert_signing_key
+expiration_days = 3650
+_EOF_
+
+msg=$(${CERTTOOL} \
+ --generate-self-signed \
+ --template ${template} \
+ --outfile ${cacert} \
+ --load-privkey ${cakey} \
+ 2>&1)
+if [ $? -ne 0 ]; then
+ echo "Could not create root CA."
+ echo "${msg}"
+ exit 1
+fi
+
+# And now create the intermediate CA with the pkcs11 URI key
+
+pubkey=${workdir}/swtpm-localca-interm-pubkey.pem
+
+msg=$(GNUTLS_PIN=${PIN} ${CERTTOOL} \
+ --load-privkey ${pkcs11uri} \
+ --pubkey-info \
+ --outfile ${pubkey})
+if [ $? -ne 0 ]; then
+ echo "Could not get public key for pkcs11 uri key ($pkcs11uri}."
+ echo "${msg}"
+ exit 1
+fi
+
+cat <<_EOF_ > ${template}
+cn=swtpm-localca
+ca
+cert_signing_key
+expiration_days = 3650
+_EOF_
+
+msg=$(GNUTLS_PIN=${PIN} ${CERTTOOL} \
+ --generate-certificate \
+ --template ${template} \
+ --outfile ${ISSUERCERT} \
+ --load-ca-privkey ${cakey} \
+ --load-ca-certificate ${cacert} \
+ --load-privkey ${pkcs11uri} \
+ --load-pubkey ${pubkey} \
+ 2>&1)
+if [ $? -ne 0 ]; then
+ echo "Could not create intermediate CA"
+ echo "${msg}"
+ exit 1
+fi
+
+echo -n 1 > ${CERTSERIAL}
+
+# Now we can create the config files
+cat <<_EOF_ > ${workdir}/swtpm-localca.conf
+statedir = ${workdir}
+signingkey = $(echo ${pkcs11uri} | sed 's|;|\\;|g')
+issuercert = ${ISSUERCERT}
+certserial = ${CERTSERIAL}
+SWTPM_PKCS11_PIN = ${PIN}
+_EOF_
+
+cat <<_EOF_ > ${workdir}/swtpm-localca.options
+--tpm-manufacturer IBM
+--tpm-model swtpm-libtpms
+--tpm-version 2
+--platform-manufacturer Fedora
+--platform-version 2.1
+--platform-model QEMU
+_EOF_
+
+# the following contains the test parameters and
+# expected key usage
+for testparams in \
+ "--allow-signing|Digital signature" \
+ "--allow-signing --decryption|Digital signature,Key encipherment" \
+ "--decryption|Key encipherment" \
+ "|Key encipherment";
+do
+ params=$(echo ${testparams} | cut -d"|" -f1)
+ usage=$(echo ${testparams} | cut -d"|" -f2)
+
+ msg=$(${SWTPM_LOCALCA} \
+ --type ek \
+ --ek ${ek} \
+ --dir ${workdir} \
+ --vmid test \
+ --tpm2 \
+ --configfile ${workdir}/swtpm-localca.conf \
+ --optsfile ${workdir}/swtpm-localca.options \
+ --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0 \
+ ${params} 2>&1)
+ if [ $? -ne 0 ]; then
+ echo "Error: Test with parameters '$params' failed."
+ echo "${msg}"
+ if [[ "${msg}" =~ The\ requested\ PKCS\ #11\ object\ is\ not\ available ]]; then
+ # could be related to i386 executables on x86_64 host and
+ # libsofthsm.so only available for x86_64...
+ exit 77
+ fi
+ exit 1
+ fi
+
+ if [ ! -r ${workdir}/ek.cert ]; then
+ echo "${msg}"
+ echo "Error: ${workdir}/ek.cert was not created."
+ exit 1
+ fi
+
+ OIFS="$IFS"
+ IFS=","
+
+ for u in $usage; do
+ if [ -z "$(${CERTTOOL} -i \
+ --inder --infile ${workdir}/ek.cert | \
+ grep "Key Usage" -A2 | \
+ grep "$u")" ]; then
+ echo "Error: Could not find key usage $u in key created " \
+ "with $params."
+ else
+ echo "Found '$u'"
+ fi
+ done
+
+ IFS="$OIFS"
+
+ ${CERTTOOL} \
+ -i \
+ --inder --infile ${workdir}/ek.cert \
+ --outfile ${workdir}/ek.pem
+
+ GNUTLS_PIN=${PIN} ${CERTTOOL} \
+ --verify \
+ --load-ca-certificate ${ISSUERCERT} \
+ --infile ${workdir}/ek.pem
+ if [ $? -ne 0 ]; then
+ echo "Error: Could not verify certificate chain."
+ exit 1
+ fi
+done
+
+exit 0
source ${TESTDIR}/common
-SWTPM_LOCALCA=${TOPBUILD}/samples/swtpm-localca
+SWTPM_LOCALCA=${TOPBUILD}/src/swtpm_localca/swtpm_localca
workdir=$(mktemp -d "/tmp/path with spaces.XXXXXX")