diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 1aaf03ac52..2f2873452a 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -174,6 +174,11 @@ pcrs PCR + + systemd-analyze + OPTIONS + srk >FILE + @@ -926,6 +931,16 @@ NR NAME SHA256 23 application-support 0000000000000000000000000000000000000000000000000000000000000000 + + + <command>systemd-analyze srk > <replaceable>FILE</replaceable></command> + + This command reads the Storage Root Key (SRK) from the TPM2 device, and writes it in marshalled + TPM2B_PUBLIC format to stdout. Example: + + systemd-analyze srk > srk.tpm2b_public + + diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index b40d202233..8fd885cb26 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -444,15 +444,21 @@ enrollment is calculated using the provided TPM2 key. This is useful in situations where the TPM2 security chip is not available at the time of enrollment. - The key, in most cases, should be the Storage Root Key (SRK) from the TPM2 security chip. If a - key from a different handle (not the SRK) is used, you must specify its handle index using + The key, in most cases, should be the Storage Root Key (SRK) from a local TPM2 security + chip. If a key from a different handle (not the SRK) is used, you must specify its handle index using . - You may use tpm2-tss tools to get the SRK from the TPM2 security chip with tpm2_readpublic1, - for example: + The + systemd-tpm2-setup.service8 + service writes the SRK to /run/systemd/tpm2-srk-public-key.tpm2b_public + automatically during boot, in the correct format. - tpm2_readpublic -c 0x81000001 -o srk.pub + Alternatively, you may use systemd-analyze srk to retrieve the SRK from the + TPM2 security chip explicitly. See + systemd-analyze1 + for details. Example: + + systemd-analyze srk > srk.tpm2b_public diff --git a/man/systemd-tpm2-setup.service.xml b/man/systemd-tpm2-setup.service.xml index c20fb1a140..8c138950c7 100644 --- a/man/systemd-tpm2-setup.service.xml +++ b/man/systemd-tpm2-setup.service.xml @@ -37,13 +37,15 @@ The services will store the public key of the SRK key pair in a PEM file in /run/systemd/tpm2-srk-public-key.pem and - /var/lib/systemd/tpm2-srk-public-key.pem. + /var/lib/systemd/tpm2-srk-public-key.pem. It will also store it in TPM2B_PUBLIC + format in /run/systemd/tpm2-srk-public-key.tpm2_public and + /var/lib/systemd/tpm2-srk-public-key.tpm2b_public. systemd-tpm2-setup-early.service runs very early at boot (possibly in the - initrd), and writes the SRK public key to /run/systemd/tpm2-srk-public-key.pem (as + initrd), and writes the SRK public key to /run/systemd/tpm2-srk-public-key.* (as /var/ is generally not accessible this early yet), while systemd-tpm2-setup.service runs during a later boot phase and saves the public key - to /var/lib/systemd/tpm2-srk-public-key.pem. + to /var/lib/systemd/tpm2-srk-public-key.*. @@ -52,16 +54,18 @@ /run/systemd/tpm2-srk-public-key.pem + /run/systemd/tpm2-srk-public-key.tpm2b_public - The SRK public key in PEM format, written during early boot. + The SRK public key in PEM and TPM2B_PUBLIC format, written during early boot. /var/lib/systemd/tpm2-srk-public-key.pem + /var/lib/systemd/tpm2-srk-public-key.tpm2_public - The SRK public key in PEM format, written during later boot (once + The SRK public key in PEM and TPM2B_PUBLIC format, written during later boot (once /var/ is available). diff --git a/src/analyze/analyze-srk.c b/src/analyze/analyze-srk.c new file mode 100644 index 0000000000..3138246225 --- /dev/null +++ b/src/analyze/analyze-srk.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-srk.h" +#include "tpm2-util.h" + +int verb_srk(int argc, char *argv[], void *userdata) { +#if HAVE_TPM2 + _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL; + _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL; + int r; + + r = tpm2_context_new(/* device= */ NULL, &c); + if (r < 0) + return log_error_errno(r, "Failed to create TPM2 context: %m"); + + r = tpm2_get_srk( + c, + /* session= */ NULL, + &public, + /* ret_name= */ NULL, + /* ret_qname= */ NULL, + /* ret_handle= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to get SRK: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No SRK stored so far."); + + _cleanup_free_ void *marshalled = NULL; + size_t marshalled_size = 0; + r = tpm2_marshal_public(public, &marshalled, &marshalled_size); + if (r < 0) + return log_error_errno(r, "Failed to marshal SRK: %m"); + + if (isatty(STDOUT_FILENO)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Refusing to write binary data to TTY, please redirect output to file."); + + if (fwrite(marshalled, 1, marshalled_size, stdout) != marshalled_size) + return log_error_errno(errno, "Failed to write SRK to stdout: %m"); + + fflush(stdout); + + return EXIT_SUCCESS; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support not available."); +#endif +} diff --git a/src/analyze/analyze-srk.h b/src/analyze/analyze-srk.h new file mode 100644 index 0000000000..26028354f8 --- /dev/null +++ b/src/analyze/analyze-srk.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_srk(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 1b96e8b69a..d2be144f4f 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -32,6 +32,7 @@ #include "analyze-plot.h" #include "analyze-security.h" #include "analyze-service-watchdogs.h" +#include "analyze-srk.h" #include "analyze-syscall-filter.h" #include "analyze-time.h" #include "analyze-time-data.h" @@ -236,6 +237,7 @@ static int help(int argc, char *argv[], void *userdata) { " malloc [D-BUS SERVICE...] Dump malloc stats of a D-Bus service\n" " fdstore SERVICE... Show file descriptor store contents of service\n" " pcrs [PCR...] Show TPM2 PCRs and their names\n" + " srk > FILE Write TPM2 SRK to stdout\n" "\nOptions:\n" " --recursive-errors=MODE Control which units are verified\n" " --offline=BOOL Perform a security review on unit file(s)\n" @@ -646,6 +648,7 @@ static int run(int argc, char *argv[]) { { "fdstore", 2, VERB_ANY, 0, verb_fdstore }, { "image-policy", 2, 2, 0, verb_image_policy }, { "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs }, + { "srk", VERB_ANY, 1, 0, verb_srk }, {} }; diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 1a9b68f6d2..a50544730b 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -21,6 +21,7 @@ systemd_analyze_sources = files( 'analyze-plot.c', 'analyze-security.c', 'analyze-service-watchdogs.c', + 'analyze-srk.c', 'analyze-syscall-filter.c', 'analyze-time.c', 'analyze-time-data.c', diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index f3712c8496..dcf26a6c03 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -2413,7 +2413,7 @@ static int tpm2_unmarshal_private(const void *data, size_t size, TPM2B_PRIVATE * return 0; } -static int tpm2_marshal_public(const TPM2B_PUBLIC *public, void **ret, size_t *ret_size) { +int tpm2_marshal_public(const TPM2B_PUBLIC *public, void **ret, size_t *ret_size) { size_t max_size = sizeof(*public), blob_size = 0; _cleanup_free_ void *blob = NULL; TSS2_RC rc; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index e867976369..505e0a8226 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -126,6 +126,7 @@ int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_P int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private); int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle); int tpm2_load(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, Tpm2Handle **ret_handle); +int tpm2_marshal_public(const TPM2B_PUBLIC *public, void **ret, size_t *ret_size); int tpm2_marshal_nv_public(const TPM2B_NV_PUBLIC *nv_public, void **ret, size_t *ret_size); int tpm2_unmarshal_nv_public(const void *data, size_t size, TPM2B_NV_PUBLIC *ret_nv_public); int tpm2_marshal_blob(const TPM2B_PUBLIC *public, const TPM2B_PRIVATE *private, const TPM2B_ENCRYPTED_SECRET *seed, void **ret_blob, size_t *ret_blob_size); diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c index 2f23fb162d..be34d166d7 100644 --- a/src/tpm2-setup/tpm2-setup.c +++ b/src/tpm2-setup/tpm2-setup.c @@ -24,6 +24,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); #define TPM2_SRK_PEM_PERSISTENT_PATH "/var/lib/systemd/tpm2-srk-public-key.pem" #define TPM2_SRK_PEM_RUNTIME_PATH "/run/systemd/tpm2-srk-public-key.pem" +#define TPM2_SRK_TPM2B_PUBLIC_PERSISTENT_PATH "/var/lib/systemd/tpm2-srk-public-key.tpm2b_public" +#define TPM2_SRK_TPM2B_PUBLIC_RUNTIME_PATH "/run/systemd/tpm2-srk-public-key.tpm2b_public" + static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL; int r; @@ -111,7 +114,8 @@ static int parse_argv(int argc, char *argv[]) { } struct public_key_data { - EVP_PKEY *pkey; + EVP_PKEY *pkey; /* as OpenSSL object */ + TPM2B_PUBLIC *public; /* in TPM2 format */ void *fingerprint; size_t fingerprint_size; char *fingerprint_hex; @@ -125,6 +129,10 @@ static void public_key_data_done(struct public_key_data *d) { EVP_PKEY_free(d->pkey); d->pkey = NULL; } + if (d->public) { + Esys_Freep(&d->public); + d->public = NULL; + } d->fingerprint = mfree(d->fingerprint); d->fingerprint_size = 0; d->fingerprint_hex = mfree(d->fingerprint_hex); @@ -192,7 +200,6 @@ static int load_public_key_disk(const char *path, struct public_key_data *ret) { static int load_public_key_tpm2(struct public_key_data *ret) { _cleanup_(public_key_data_done) struct public_key_data data = {}; _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL; - _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL; int r; assert(ret); @@ -204,7 +211,7 @@ static int load_public_key_tpm2(struct public_key_data *ret) { r = tpm2_get_or_create_srk( c, /* session= */ NULL, - &public, + &data.public, /* ret_name= */ NULL, /* ret_qname= */ NULL, NULL); @@ -215,7 +222,7 @@ static int load_public_key_tpm2(struct public_key_data *ret) { else log_info("SRK already stored in the TPM."); - r = tpm2_tpm2b_public_to_openssl_pkey(public, &data.pkey); + r = tpm2_tpm2b_public_to_openssl_pkey(data.public, &data.pkey); if (r < 0) return log_error_errno(r, "Failed to convert TPM2 SRK public key to OpenSSL public key: %m"); @@ -300,28 +307,64 @@ static int run(int argc, char *argv[]) { public_key_data_done(&persistent_key); } - const char *path = arg_early ? TPM2_SRK_PEM_RUNTIME_PATH : TPM2_SRK_PEM_PERSISTENT_PATH; - - (void) mkdir_parents(path, 0755); + const char *pem_path = arg_early ? TPM2_SRK_PEM_RUNTIME_PATH : TPM2_SRK_PEM_PERSISTENT_PATH; + (void) mkdir_parents(pem_path, 0755); /* Write out public key (note that we only do that as a help to the user, we don't make use of this ever */ _cleanup_(unlink_and_freep) char *t = NULL; _cleanup_fclose_ FILE *f = NULL; - r = fopen_tmpfile_linkable(path, O_WRONLY, &t, &f); + r = fopen_tmpfile_linkable(pem_path, O_WRONLY, &t, &f); if (r < 0) - return log_error_errno(r, "Failed to open SRK public key file '%s' for writing: %m", path); + return log_error_errno(r, "Failed to open SRK public key file '%s' for writing: %m", pem_path); if (PEM_write_PUBKEY(f, tpm2_key.pkey) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK public key file '%s'.", path); + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write SRK public key file '%s'.", pem_path); if (fchmod(fileno(f), 0444) < 0) - return log_error_errno(errno, "Failed to adjust access mode of SRK public key file '%s' to 0444: %m", path); + return log_error_errno(errno, "Failed to adjust access mode of SRK public key file '%s' to 0444: %m", pem_path); - r = flink_tmpfile(f, t, path, LINK_TMPFILE_SYNC|LINK_TMPFILE_REPLACE); + r = fflush_and_check(f); if (r < 0) - return log_error_errno(r, "Failed to move SRK public key file to '%s': %m", path); + return log_error_errno(r, "Failed to sync SRK key to disk: %m"); - log_info("SRK public key saved to '%s'.", path); + r = flink_tmpfile(f, t, pem_path, LINK_TMPFILE_SYNC|LINK_TMPFILE_REPLACE); + if (r < 0) + return log_error_errno(r, "Failed to move SRK public key file to '%s': %m", pem_path); + + f = safe_fclose(f); + t = mfree(t); + + log_info("SRK public key saved to '%s' in PEM format.", pem_path); + + const char *tpm2b_public_path = arg_early ? TPM2_SRK_TPM2B_PUBLIC_RUNTIME_PATH : TPM2_SRK_TPM2B_PUBLIC_PERSISTENT_PATH; + (void) mkdir_parents(tpm2b_public_path, 0755); + + /* Now also write this out in TPM2B_PUBLIC format */ + r = fopen_tmpfile_linkable(tpm2b_public_path, O_WRONLY, &t, &f); + if (r < 0) + return log_error_errno(r, "Failed to open SRK public key file '%s' for writing: %m", tpm2b_public_path); + + _cleanup_free_ void *marshalled = NULL; + size_t marshalled_size = 0; + r = tpm2_marshal_public(tpm2_key.public, &marshalled, &marshalled_size); + if (r < 0) + return log_error_errno(r, "Failed to marshal TPM2_PUBLIC key."); + + if (fwrite(marshalled, 1, marshalled_size, f) != marshalled_size) + return log_error_errno(errno, "Failed to write SRK public key file '%s'.", tpm2b_public_path); + + if (fchmod(fileno(f), 0444) < 0) + return log_error_errno(errno, "Failed to adjust access mode of SRK public key file '%s' to 0444: %m", tpm2b_public_path); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to sync SRK key to disk: %m"); + + r = flink_tmpfile(f, t, tpm2b_public_path, LINK_TMPFILE_SYNC|LINK_TMPFILE_REPLACE); + if (r < 0) + return log_error_errno(r, "Failed to move SRK public key file to '%s': %m", tpm2b_public_path); + + log_info("SRK public key saved to '%s' in TPM2B_PUBLIC format.", tpm2b_public_path); return 0; } diff --git a/test/units/testsuite-70.cryptsetup.sh b/test/units/testsuite-70.cryptsetup.sh index a1c6035e45..1db425dfe8 100755 --- a/test/units/testsuite-70.cryptsetup.sh +++ b/test/units/testsuite-70.cryptsetup.sh @@ -135,6 +135,12 @@ if tpm_has_pcr sha256 12; then tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) tpm2_readpublic -c 0x81000001 -o /tmp/srk.pub + systemd-analyze srk > /tmp/srk2.pub + cmp /tmp/srk.pub /tmp/srk2.pub + if [ -f /run/systemd/tpm2-srk-public-key.tpm2b_public ] ; then + cmp /tmp/srk.pub /run/systemd/tpm2-srk-public-key.tpm2b_public + fi + PASSWORD=passphrase systemd-cryptenroll --tpm2-device-key=/tmp/srk.pub --tpm2-pcrs="12:sha256=$CURRENT_PCR_VALUE" "$IMAGE" systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 systemd-cryptsetup detach test-volume