diff --git a/src/swtpm_setup/Makefile.am b/src/swtpm_setup/Makefile.am index c0f916b3d..d8ed7d50b 100644 --- a/src/swtpm_setup/Makefile.am +++ b/src/swtpm_setup/Makefile.am @@ -37,7 +37,8 @@ swtpm_setup_LDFLAGS = \ $(HARDENING_LDFLAGS) \ $(GLIB_LIBS) \ $(JSON_GLIB_LIBS) \ - $(LIBCRYPTO_LIBS) + $(LIBCRYPTO_LIBS) \ + $(GNUTLS_LIBS) swtpm_setup_CFLAGS = \ -I$(top_builddir)/include \ @@ -49,7 +50,8 @@ swtpm_setup_CFLAGS = \ $(CFLAGS) \ $(HARDENING_CFLAGS) \ $(GLIB_CFLAGS) \ - $(JSON_GLIB_CFLAGS) + $(JSON_GLIB_CFLAGS) \ + $(GNUTLS_CFLAGS) EXTRA_DIST = \ README diff --git a/src/swtpm_setup/swtpm.c b/src/swtpm_setup/swtpm.c index d99a9ab40..4165294ea 100644 --- a/src/swtpm_setup/swtpm.c +++ b/src/swtpm_setup/swtpm.c @@ -1617,6 +1617,36 @@ static int swtpm_tpm2_write_idevid_cert_nvram(struct swtpm *self, gboolean lock_ lock_nvram, "", "IDevID certificate"); } +static int swtpm_tpm2_get_capability(struct swtpm *self, uint32_t cap, uint32_t prop, + uint32_t *res) +{ + struct tpm2_get_capability_req { + struct tpm_req_header hdr; + uint32_t cap; + uint32_t prop; + uint32_t count; + } __attribute__((packed)) req = { + .hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_NO_SESSIONS, sizeof(req), TPM2_CC_GETCAPABILITY), + .cap = htobe32(cap), + .prop = htobe32(prop), + .count = htobe32(1), + }; + unsigned char tpmresp[27]; + size_t tpmresp_len = sizeof(tpmresp); + uint32_t val; + int ret; + + ret = transfer(self, &req, sizeof(req), "TPM2_GetCapability", FALSE, + tpmresp, &tpmresp_len, TPM2_DURATION_SHORT); + if (ret != 0) + return 1; + + memcpy(&val, &tpmresp[23], sizeof(val)); + *res = be32toh(val); + + return 0; +} + static const struct swtpm2_ops swtpm_tpm2_ops = { .shutdown = swtpm_tpm2_shutdown, .create_iak = swtpm_tpm2_create_iak, @@ -1629,6 +1659,7 @@ static const struct swtpm2_ops swtpm_tpm2_ops = { .write_platform_cert_nvram = swtpm_tpm2_write_platform_cert_nvram, .write_iak_cert_nvram = swtpm_tpm2_write_iak_cert_nvram, .write_idevid_cert_nvram = swtpm_tpm2_write_idevid_cert_nvram, + .get_capability = swtpm_tpm2_get_capability, }; /* diff --git a/src/swtpm_setup/swtpm.h b/src/swtpm_setup/swtpm.h index d1ce7cfcf..0136f1619 100644 --- a/src/swtpm_setup/swtpm.h +++ b/src/swtpm_setup/swtpm.h @@ -62,6 +62,7 @@ struct swtpm2_ops { const unsigned char *data, size_t data_len); int (*write_idevid_cert_nvram)(struct swtpm *self, gboolean lock_nvram, const unsigned char *data, size_t data_len); + int (*get_capability)(struct swtpm *self, uint32_t cap, uint32_t prop, uint32_t *res); }; /* common structure for swtpm object */ diff --git a/src/swtpm_setup/swtpm_setup.c b/src/swtpm_setup/swtpm_setup.c index e9586c572..1d9397f53 100644 --- a/src/swtpm_setup/swtpm_setup.c +++ b/src/swtpm_setup/swtpm_setup.c @@ -32,9 +32,12 @@ #include +#include + #include "swtpm.h" #include "swtpm_conf.h" #include "swtpm_utils.h" +#include "swtpm_setup.h" #include "swtpm_setup_utils.h" #include @@ -369,6 +372,52 @@ static int read_certificate_file(const gchar *certsdir, const gchar *filename, return read_file(*certfile, filecontent, filecontent_len); } +/* data extracted from EK certificate */ +struct ek_certificate_data { + unsigned char id[64]; + size_t id_len; + unsigned char serial[20]; + size_t serial_len; +}; + +static int tpm2_extract_certificate_data(gchar *certdata, size_t certdata_len, + struct ek_certificate_data *ecd) +{ + gnutls_x509_crt_t cert; + gnutls_datum_t data = { + .data = (unsigned char *)certdata, + .size = certdata_len, + }; + int err; + int ret = 1; + + if ((err = gnutls_x509_crt_init(&cert)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_init() failed: %s\n", + gnutls_strerror(err)); + return 1; + } + if ((err = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_DER)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_import() failed: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + if ((err = gnutls_x509_crt_get_authority_key_id(cert, ecd->id, &ecd->id_len, NULL)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_get_authority_key_id() failed: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + if ((err = gnutls_x509_crt_get_serial(cert, ecd->serial, &ecd->serial_len)) < 0) { + logerr(gl_LOGFILE, "gnutls_x509_crt_get_serial() failed: %s\n", + gnutls_strerror(err)); + goto cleanup; + } + ret = 0; + +cleanup: + gnutls_x509_crt_deinit(cert); + return ret; +} + /* * Read the certificate from the file where swtpm_cert left it. * Write the file into the TPM's NVRAM and, if the user wants it, @@ -378,7 +427,8 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, const struct flag_to_certfile *ftc, unsigned int rsa_keysize, struct swtpm2 *swtpm2, const gchar *user_certsdir, const gchar *key_type, - const gchar *key_description) + const gchar *key_description, + struct ek_certificate_data *ecd) { g_autofree gchar *filecontent = NULL; g_autofree gchar *certfile = NULL; @@ -391,6 +441,12 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, if (ret != 0) goto error_unlink; + if (ecd) { + ret = tpm2_extract_certificate_data(filecontent, filecontent_len, ecd); + if (ret != 0) + goto error_unlink; + } + if (ftc->flag == SETUP_IAK_F) { ret = swtpm2->ops->write_iak_cert_nvram(&swtpm2->swtpm, !!(flags & SETUP_LOCK_NVRAM_F), @@ -428,9 +484,11 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir, static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file, const gchar *certsdir, const gchar *vmid, unsigned int rsa_keysize, struct swtpm2 *swtpm2, - const gchar *user_certsdir) + const gchar *user_certsdir, + struct ek_certificate_data *ecd) { g_autofree gchar *key_params = NULL; + struct ek_certificate_data *ecd_dup; const char *key_description; unsigned long cert_flags; const gchar *key_type; @@ -457,11 +515,19 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file for (idx = 0; flags_to_certfiles[idx].filename; idx++) { if (cert_flags & flags_to_certfiles[idx].flag) { - key_type = flags_to_certfiles[idx].flag & SETUP_EK_CERT_F ? "ek" : ""; + + ecd_dup = NULL; + if (flags_to_certfiles[idx].flag & SETUP_EK_CERT_F) { + key_type = "ek"; + if (rsa_keysize) + ecd_dup = ecd; + } else { + key_type = ""; + } ret = tpm2_persist_certificate(flags, certsdir, &flags_to_certfiles[idx], rsa_keysize, swtpm2, user_certsdir, - key_type, key_description); + key_type, key_description, ecd_dup); if (ret) return 1; } @@ -475,28 +541,56 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file static int tpm2_create_eks_and_certs(unsigned long flags, const gchar *config_file, const gchar *certsdir, const gchar *vmid, unsigned int rsa_keysize, struct swtpm2 *swtpm2, - const gchar *user_certsdir) + const gchar *user_certsdir, + struct ek_certificate_data *ecd) { int ret; /* 1st key will be RSA */ flags = flags & ~SETUP_TPM2_ECC_F; ret = tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, - user_certsdir); + user_certsdir, ecd); if (ret != 0) return 1; /* 2nd key will be an ECC; no more platform cert */ flags = (flags & ~SETUP_PLATFORM_CERT_F) | SETUP_TPM2_ECC_F; return tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, - user_certsdir); + user_certsdir, NULL); +} + +static gchar *tpm2_create_tpm_serial_num(struct swtpm2 *swtpm2, const struct ek_certificate_data *ecd) +{ + struct swtpm *swtpm = &swtpm2->swtpm; + g_autofree gchar *cert_ser = NULL; + g_autofree gchar *ca_akid = NULL; + uint32_t res; + char code[sizeof(res) + 1]; + size_t i; + int ret; + + ret = swtpm2->ops->get_capability(swtpm, TPM_CAP_TPM_PROPERTIES, TPM_PT_MANUFACTURER, &res); + if (ret != 0) { + logerr(gl_LOGFILE, "TPM_GetCapability failed\n"); + return NULL; + } + + ca_akid = print_as_hex(ecd->id, ecd->id_len); + cert_ser = print_as_hex(ecd->serial, ecd->serial_len); + for (i = 0; i < sizeof(res); i++) + code[i] = res >> (8 * (3 - i)); + code[4] = 0; + + return g_strdup_printf("%s:%s:%s", code, ca_akid, cert_ser); } /* Create the IAK and cert */ static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *config_file, const gchar *certsdir, const char *vmid, - struct swtpm2 *swtpm2, const gchar *user_certsdir) + struct swtpm2 *swtpm2, const gchar *user_certsdir, + const struct ek_certificate_data *ecd) { + g_autofree gchar *tpm_serial_num = NULL; g_autofree gchar *key_params = NULL; const char *key_description; const char *key_type = NULL; @@ -509,6 +603,8 @@ static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *co if (!cert_flags) return 0; + tpm_serial_num = tpm2_create_tpm_serial_num(swtpm2, ecd); + for (idx = 0; flags_to_certfiles[idx].filename; idx++) { if (cert_flags & flags_to_certfiles[idx].flag) { @@ -525,13 +621,14 @@ static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *co return 1; ret = call_create_certs(flags, flags_to_certfiles[idx].flag, config_file, - certsdir, key_params, vmid, "TEST", &swtpm2->swtpm); + certsdir, key_params, vmid, tpm_serial_num, + &swtpm2->swtpm); if (ret != 0) return 1; ret = tpm2_persist_certificate(flags, certsdir, &flags_to_certfiles[idx], 0, swtpm2, user_certsdir, - key_type, key_description); + key_type, key_description, NULL); if (ret) return 1; } @@ -605,6 +702,10 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf unsigned int rsa_keysize, const gchar *certsdir, const gchar *user_certsdir) { + struct ek_certificate_data ecd = { + .id_len = sizeof(ecd.id), + .serial_len = sizeof(ecd.serial), + }; struct swtpm2 *swtpm2; struct swtpm *swtpm; int ret; @@ -629,12 +730,12 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf } ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2, - user_certsdir); + user_certsdir, &ecd); if (ret != 0) goto destroy; ret = tpm2_create_iak_idevid_and_certs(flags, config_file, certsdir, vmid, - swtpm2, user_certsdir); + swtpm2, user_certsdir, &ecd); if (ret != 0) goto destroy; } diff --git a/src/swtpm_setup/swtpm_setup.h b/src/swtpm_setup/swtpm_setup.h index 913b36f96..f78383779 100644 --- a/src/swtpm_setup/swtpm_setup.h +++ b/src/swtpm_setup/swtpm_setup.h @@ -14,4 +14,7 @@ extern gchar *gl_LOGFILE; +#define TPM_CAP_TPM_PROPERTIES 6 +#define TPM_PT_MANUFACTURER 0x105 + #endif /* SWTPM_SETUP_H */ diff --git a/tests/create_certs.sh b/tests/create_certs.sh index 9a87666b1..e96eedb63 100755 --- a/tests/create_certs.sh +++ b/tests/create_certs.sh @@ -2,8 +2,29 @@ #echo $@ +ek_cert=\ +MIID9TCCAl2gAwIBAgICBL4wDQYJKoZIhvcNAQELBQAwGDEWMBQGA1UEAxMNc3d0cG0tbG9jYWxj\ +YTAgFw0yMzA4MjIwMTM2MDdaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4GA1UEAxMHdW5rbm93bjCC\ +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJSKqmO3O1KnFQFXENX+z8kcL0eIFW8xVhPW\ +eCJrChwRil36KaxJHPf5aqzWkMuZnCHI2U5susPq7BAeCCPXaOb3pwbs3tgEC5eGAEApv6Y+5Yep\ +ia8chBGI58q3CuKL/HcToNnzT1MwwTtq4dCzav9BcmR/tkh9fSUvjOwZyPIDktgvMXAkLFzqhAx3\ +8TKSqdhjWgbUAr8fGsAUGm1bvepphEpycjCfftdC6/GnGEbHsDWjBS944k3sHWD7Aik/VV9wEHLT\ +EIN8r3rKdj5nzqUrGRFqSupN5e25YRhKxS1SB9wFOiOYoevsPR7Bh3/5MdZd33zhnTbrA9f0RDxu\ +qjUCAwEAAaOBzDCByTAQBgNVHSUECTAHBgVngQUIATBSBgNVHREBAf8ESDBGpEQwQjEWMBQGBWeB\ +BQIBDAtpZDowMDAwMTAxNDEQMA4GBWeBBQICDAVzd3RwbTEWMBQGBWeBBQIDDAtpZDoyMDE5MTAy\ +MzAMBgNVHRMBAf8EAjAAMCIGA1UdCQQbMBkwFwYFZ4EFAhAxDjAMDAMyLjACAQACAgCkMB8GA1Ud\ +IwQYMBaAFMFMlKkwREqQv5MU2eVVkxUZIOP0MA4GA1UdDwEB/wQEAwIFIDANBgkqhkiG9w0BAQsF\ +AAOCAYEAihjxS4PmR5xO7jAYsF5hqeMvEh5+wH+OWjkj9oH+a8N6oFlxUJeQgLLFgK+Jyq4zt0kz\ +tdAaFX0XrWfPH/s1AQdwtUzqOHdSHWcBmfPV+MlMCtz1HjfC5GGKmCHPgwDRLiowwzsWyKFRPKlu\ +UtmtP0ukRTbzGa/j3GpBSnIn7l2yTrnXZ6XXeZ/gvHghzyp02aGJ2Ei873X57zOuFmz1z++WwXRN\ +ipRoAjga57NAz/f1RceJuF+zA8aAX7GY2dcvDCVpU1yoBsWt9gXtZ/4ZO400fbwnxz3zVLJEXgpR\ +jd+XbUUxsGMWqWZ3qEApbrkWjS77TXkDmOqK8Nh92mZvLSMHBJa/mzWFJBPpu+MCSPbO9kAhfB5W\ +F2ynlGuQfeBue4ju5PmcID3xs2FbCItWyj8bJhuA2QQDYmUrSnqQJ9zNLj7ibbq7hDWsaeko65/E\ +HYBXBvksWO4cdoR7F9pcuyhsJDMU7jyGAo0RKuRkUrGnN2Aja4GKSXTilXTCeq/5 + + main() { - local typ ek dir vmid + local typ ek dir vmid tpm2=0 while [ $# -ne 0 ]; do #echo $1 @@ -25,6 +46,7 @@ main() { vmid="$1" ;; --tpm2) + tpm2=1 ;; esac shift @@ -32,7 +54,12 @@ main() { case "$typ" in ek) - echo -n "ek" > ${dir}/ek.cert + # ek cert must be parseable for a TPM 2 + if [ "${tpm2}" -ne 0 ]; then + base64 -d <<< "${ek_cert}" > ${dir}/ek.cert + else + echo -n "ek" > ${dir}/ek.cert + fi ;; platform) echo -n "platform" > ${dir}/platform.cert diff --git a/tests/test_tpm2_swtpm_setup_create_cert b/tests/test_tpm2_swtpm_setup_create_cert index f445598de..40472f78c 100755 --- a/tests/test_tpm2_swtpm_setup_create_cert +++ b/tests/test_tpm2_swtpm_setup_create_cert @@ -19,6 +19,8 @@ CERTSERIAL=${workdir}/certserial USER_CERTSDIR=${workdir}/mycerts mkdir -p "${USER_CERTSDIR}" +vmid="test" + PATH=${TOPBUILD}/src/swtpm_bios:$PATH trap "cleanup" SIGTERM EXIT @@ -199,6 +201,7 @@ for pwdfile in "" "${PWDFILE}"; do --logfile "${workdir}/logfile" \ --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ --overwrite \ + --vmid "${vmid}" \ --write-ek-cert-files "${workdir}" \ ${pwdfile:+--pwdfile "${pwdfile}"}; then echo "Error: Could not run $SWTPM_SETUP." @@ -237,6 +240,29 @@ for pwdfile in "" "${PWDFILE}"; do $CERTTOOL --inder --infile "${certfile}" -i exit 1 fi + # IAK and IDevID need serialNumber to match :: + if [[ ${certfile} =~ (iak|idevid) ]]; then + cert_vmid=$($CERTTOOL --inder --infile "${certfile}" -i \ + | sed -n 's/.*Subject:.*serialNumber=//p') + if [ "${cert_vmid}" != "${vmid}" ]; then + echo "Error: The serialNumber in the cert is wrong" + echo "expected: ${vmid}" + echo "actual : ${cert_vmid}" + exit 1 + fi + # The serial number starts at the 12th ASCII char + serial=$($CERTTOOL --inder --infile "${certfile}" -i \ + | sed -n "s/.*otherName ASCII: [[:print:]]\{11\}//p") + if [ -z "$serial" ] || \ + [ "${serial}" != \ + "$(echo "${serial}" | \ + sed -n 's/\([[:print:]^:]*\):\([[:xdigit:]^:]*\):\([[:xdigit:]^:]*\)/\1:\2:\3/p')" ]; + then + echo "Error: serial Number does not seem to be properly formatted" + echo " serial: ${serial}" + exit 1 + fi + fi done swtpm_setup_reconfigure "${workdir}" "${pwdfile}"