From 3f551e1dc1f59adcd84ced3c95555205b3bd3c5b Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sat, 5 Oct 2024 14:41:42 -0400 Subject: [PATCH] swtpm: Implement --print-info to run TPMLIB_GetInfo with flags Implement --print-info that takes a number as argument and uses this number as flags to call TPMLIB_GetInfo with. Display the JSON string and exit. Extend the man page and update other parts where swtpm_ioctl is not necessary anymore to use. Extend a test case to also check that swtpm now returns the same result as swtpm_ioctl does. Append cmdarg-print-info to printed out capabilties. Adjust test cases. (Expect 'profiles' to always be part of capabilties JSON.) Signed-off-by: Stefan Berger --- man/man8/swtpm.pod | 58 +++++++++++++++++++++++------ src/swtpm/capabilities.c | 3 +- src/swtpm/cuse_tpm.c | 27 +++++++++++++- src/swtpm/swtpm.c | 24 +++++++++++- src/swtpm/swtpm_chardev.c | 26 ++++++++++++- tests/_test_print_capabilities | 8 ++-- tests/_test_tpm2_print_capabilities | 8 ++-- tests/test_tpm2_swtpm_setup_profile | 14 +++++++ 8 files changed, 144 insertions(+), 24 deletions(-) diff --git a/man/man8/swtpm.pod b/man/man8/swtpm.pod index dfa59ffac..abd735c9e 100644 --- a/man/man8/swtpm.pod +++ b/man/man8/swtpm.pod @@ -336,7 +336,8 @@ may contain the following: "rsa-keysize-3072", "cmdarg-profile", "cmdarg-print-profiles", - "profile-opt-remove-disabled" + "profile-opt-remove-disabled", + "cmdarg-print-info", ], "version": "0.7.0" } @@ -413,18 +414,22 @@ rsa-keysize verbs is shown then only RSA 2048 bit keys are supported. =item B (since v0.10) -The option <--profile> is supported to set a profile for a TPM 2 with either +The option I<--profile> is supported to set a profile for a TPM 2 with either one of the option parameters I, I, I, or I. =item B (since v0.10) -The option <--print-profiles> is supported. +The option I<--print-profiles> is supported. -=item B +=item B (since v0.10) The I<--profile> option supports the I option parameter. +=item B (since v0.10) + +The option I<--print-info> is supported. + =back =item B<--print-states> (since v0.7) @@ -528,10 +533,9 @@ require a certain StateFormatLevel, it is recommended to omit the StateFormatLevel field from the profile. To see the list of algorithms that are supported and can be disabled, one -may use I as follows. A swtpm instance is assumed to be -listening for control commands on port 2322: +may use I as follows. - $ swtpm_ioctl --tcp :2322 --info 0x08 | jq + $ swtpm socket --tpmstate dir=./ --tpm2 --print-info 0x08 | jq { "RuntimeAlgorithms": { "Implemented": "rsa,rsa-min-size=1024,tdes,tdes-min-size=128,sha1,hmac,aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,sha512,null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,sm2,ecschnorr,ecmqv,kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-min-size=192,ecc-nist,ecc-bn,ecc-nist-p192,ecc-nist-p224,ecc-nist-p256,ecc-nist-p384,ecc-nist-p521,ecc-bn-p256,ecc-bn-p638,ecc-sm2-p256,symcipher,camellia,camellia-min-size=128,cmac,ctr,ofb,cbc,cfb,ecb", @@ -543,7 +547,7 @@ listening for control commands on port 2322: To see the list of supported commands: - $ swtpm_ioctl --tcp :2322 --info 0x10 | jq + $ swtpm socket --tpmstate dir=./ --tpm2 --print-info 0x10 | jq { "RuntimeCommands": { "Implemented": "0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x197,0x199-0x19c", @@ -555,7 +559,7 @@ To see the list of supported commands: To see the list of supported attributes: - $swtpm_ioctl --tcp :2322 --info 0x80 | jq + $ swtpm socket --tpmstate dir=./ --tpm2 --print-info 0x80 | jq { "RuntimeAttributes": { "Implemented": "no-unpadded-encryption,no-sha1-signing,no-sha1-verification,no-sha1-hmac-creation,no-sha1-hmac-verification,no-sha1-hmac,fips-host", @@ -570,7 +574,7 @@ entry, which is similar to the "Algorithms" and "Commands" entries. To see the list of available profiles: - $ swtpm_ioctl --tcp :2322 --info 0x40 | jq + $ swtpm socket --tpm2 --print-info 0x40 | jq { "AvailableProfiles": [ { @@ -597,7 +601,8 @@ To see the list of available profiles: ] } -To see the current active profile: +To see the current active profile querying swtpm listening for control +message on port 2322: $ swtpm_ioctl --tcp :2322 --info 0x20 | jq { @@ -619,6 +624,37 @@ none of them relies on disabled commands or algorithms. Display the profiles supported by libtpms. Use with I<--tpm2> option. +=item B<--print-info> (since v0.10) + +Display information about the TPM from libtpms TPMLIB_GetInfo call and exit. +Use the I<--tpm2> option for information about TPM 2. If the I<--tpmstate> +option is also provided then the output will show information about the profile +as well. Note that with this option a TPM state may be created if none existed +before. + +The following values can be provided. All of the values can be +or'ed (or added) together to get information about all of them in one query. + +=over 2 + +=item * 0x1: information about the specification the TPM implementation followed + +=item * 0x2: information about the manufacturer, model and version of the TPM + +=item * 0x4: lists supported RSA and Camellia key sizes + +=item * 0x8: describes supported and enabled algorithms + +=item * 0x10: describes supported and enabled commands + +=item * 0x20: describes the active profile + +=item * 0x40: lists all built-in profiles + +=item * 0x80: describes supported attributes + +=back + =item B<-h|--help> Display usage info. diff --git a/src/swtpm/capabilities.c b/src/swtpm/capabilities.c index 5369690c5..2740c1447 100644 --- a/src/swtpm/capabilities.c +++ b/src/swtpm/capabilities.c @@ -270,7 +270,7 @@ int capabilities_print_json(bool cusetpm, TPMLIB_TPMVersion tpmversion) "{ " "\"type\": \"swtpm\", " "\"features\": [ " - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" " ], " "\"profiles\": { %s}, " "\"version\": \"" VERSION "\" " @@ -293,6 +293,7 @@ int capabilities_print_json(bool cusetpm, TPMLIB_TPMVersion tpmversion) true ? ", \"cmdarg-profile\"" : "", true ? ", \"cmdarg-print-profiles\"" : "", true ? ", \"profile-opt-remove-disabled\"" : "", + true ? ", \"cmdarg-print-info\"" : "", profiles ? profiles : "" ); diff --git a/src/swtpm/cuse_tpm.c b/src/swtpm/cuse_tpm.c index eb4da8d61..ace0f8355 100644 --- a/src/swtpm/cuse_tpm.c +++ b/src/swtpm/cuse_tpm.c @@ -284,7 +284,9 @@ static const char *usage = " algorithms first\n" "--print-profiles\n" " : print all profiles supported by libtpms\n" -"-h|--help : display this help screen and terminate\n" +"--print-info \n" +" : print information about the TPM and profiles and exit\n" +"-h|--help : display this help screen and terminate\n" "\n"; static TPM_RESULT @@ -1594,6 +1596,7 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac { #endif int opt, longindex = 0; + enum TPMLIB_InfoFlags infoflags = 0; static struct option longopts[] = { {"maj" , required_argument, 0, 'M'}, {"min" , required_argument, 0, 'm'}, @@ -1619,16 +1622,19 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac {"print-states" , no_argument, 0, 'e'}, {"profile" , required_argument, 0, 'I'}, {"print-profiles", no_argument, 0, 'N'}, + {"print-info" , required_argument, 0, 'x'}, {NULL , 0 , 0, 0 }, }; struct cuse_info cinfo; struct cuse_param param = { .startupType = _TPM_ST_NONE, }; + g_autofree gchar *jsoninfo = NULL; const char *devname = NULL; char *cinfo_argv[1] = { 0 }; unsigned int num; const char *uri = NULL; + char *end_ptr = NULL; int n, tpmfd; char path[PATH_MAX]; int ret = 0; @@ -1753,6 +1759,16 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac case 'N': /* --print-profiles */ printprofiles = true; break; + case 'x': /* --print-info */ + errno = 0; + infoflags = strtoul(optarg, &end_ptr, 0); + if (infoflags != (unsigned int)infoflags || errno || end_ptr[0] != '\0') { + logprintf(STDERR_FILENO, + "Cannot parse info value '%s'.\n", optarg); + ret = -1; + goto exit; + } + break; case 'v': /* version */ fprintf(stdout, "TPM emulator CUSE interface version %d.%d.%d, " "Copyright (c) 2014-2015 IBM Corp.\n", @@ -1898,7 +1914,7 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac g_mutex_lock(FILE_OPS_LOCK); - if (!need_init_cmd) { + if (!need_init_cmd || (infoflags && tpmstate_get_backend_uri())) { if (tpm_start(0, tpmversion, g_json_profile, &res) < 0) { ret = -1; goto err_unlock; @@ -1907,6 +1923,13 @@ int swtpm_cuse_main(int argc, char **argv, const char *prgname, const char *ifac SWTPM_G_FREE(g_json_profile); } + if (infoflags) { + /* returns more information with tpmstate active */ + jsoninfo = TPMLIB_GetInfo(infoflags); + printf("%s\n", jsoninfo); + goto err_unlock; + } + if (param.startupType != _TPM_ST_NONE) { if (ptm_send_startup(param.startupType, tpmversion) < 0) { ret = -1; diff --git a/src/swtpm/swtpm.c b/src/swtpm/swtpm.c index 04cfba944..224b8641c 100644 --- a/src/swtpm/swtpm.c +++ b/src/swtpm/swtpm.c @@ -205,6 +205,8 @@ static void usage(FILE *file, const char *prgname, const char *iface) " algorithms first\n" "--print-profiles\n" " : print all profiles supported by libtpms\n" + "--print-info \n" + " : print information about the TPM and profiles and exit\n" "-h|--help : display this help screen and terminate\n" "\n", prgname, iface); @@ -239,6 +241,7 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface) .incoming_migration = false, .storage_locked = false, }; + g_autofree gchar *jsoninfo = NULL; struct server *server = NULL; unsigned long val; char *end_ptr; @@ -266,6 +269,7 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface) bool printstates = false; bool printprofiles = false; bool tpm_running = false; + enum TPMLIB_InfoFlags infoflags = 0; static struct option longopts[] = { {"daemon" , no_argument, 0, 'd'}, {"help" , no_argument, 0, 'h'}, @@ -293,6 +297,7 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface) {"print-states", no_argument, 0, 'e'}, {"profile" , required_argument, 0, 'I'}, {"print-profiles", no_argument, 0, 'N'}, + {"print-info", required_argument, 0, 'x'}, {NULL , 0 , 0, 0 }, }; @@ -447,6 +452,16 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface) printprofiles = true; break; + case 'x': /* --print-info */ + errno = 0; + infoflags = strtoul(optarg, &end_ptr, 0); + if (infoflags != (unsigned int)infoflags || errno || end_ptr[0] != '\0') { + logprintf(STDERR_FILENO, + "Cannot parse info value '%s'.\n", optarg); + exit(EXIT_FAILURE); + } + break; + default: usage(stderr, prgname, iface); exit(EXIT_FAILURE); @@ -565,7 +580,7 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface) if ((rc = tpmlib_register_callbacks(&callbacks))) goto error_no_tpm; - if (!need_init_cmd) { + if (!need_init_cmd || (infoflags && tpmstate_get_backend_uri())) { mlp.storage_locked = !mlp.incoming_migration; if ((rc = tpmlib_start(0, mlp.tpmversion, mlp.storage_locked, @@ -575,6 +590,13 @@ int swtpm_main(int argc, char **argv, const char *prgname, const char *iface) SWTPM_G_FREE(mlp.json_profile); } + if (infoflags) { + /* returns more information with tpmstate active */ + jsoninfo = TPMLIB_GetInfo(infoflags); + printf("%s\n", jsoninfo); + goto error_no_sighandlers; + } + if (install_sighandlers(notify_fd, sigterm_handler) < 0) goto error_no_sighandlers; diff --git a/src/swtpm/swtpm_chardev.c b/src/swtpm/swtpm_chardev.c index 49568c118..e4efb5aad 100644 --- a/src/swtpm/swtpm_chardev.c +++ b/src/swtpm/swtpm_chardev.c @@ -226,6 +226,8 @@ static void usage(FILE *file, const char *prgname, const char *iface) " algorithms first\n" "--print-profiles\n" " : print all profiles supported by libtpms\n" + "--print-info \n" + " : print information about the TPM and profiles and exit\n" "-h|--help : display this help screen and terminate\n" "\n", prgname, iface); @@ -297,6 +299,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i .incoming_migration = false, .storage_locked = false, }; + g_autofree gchar *jsoninfo = NULL; unsigned long val; char *end_ptr; char *keydata = NULL; @@ -324,6 +327,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i bool printstates = false; bool printprofiles = false; bool tpm_running = false; + enum TPMLIB_InfoFlags infoflags = 0; static struct option longopts[] = { {"daemon" , no_argument, 0, 'd'}, {"help" , no_argument, 0, 'h'}, @@ -352,6 +356,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i {"print-states", no_argument, 0, 'e'}, {"profile" , required_argument, 0, 'I'}, {"print-profiles", no_argument, 0, 'N'}, + {"print-info", required_argument, 0, 'x'}, {NULL , 0 , 0, 0 }, }; @@ -497,6 +502,18 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i printprofiles = true; break; + case 'x': /* --print-info */ + errno = 0; + infoflags = strtoul(optarg, &end_ptr, 0); + if (infoflags != (unsigned int)infoflags || errno || end_ptr[0] != '\0') { + logprintf(STDERR_FILENO, + "Cannot parse info value '%s'.\n", optarg); + exit(EXIT_FAILURE); + } + if (mlp.fd < 0) + mlp.fd = open("/dev/zero", O_RDWR); + break; + default: usage(stderr, prgname, iface); exit(EXIT_FAILURE); @@ -613,7 +630,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i if ((rc = tpmlib_register_callbacks(&callbacks))) goto error_no_tpm; - if (!need_init_cmd) { + if (!need_init_cmd || (infoflags && tpmstate_get_backend_uri())) { mlp.storage_locked = !mlp.incoming_migration; if ((rc = tpmlib_start(0, mlp.tpmversion, mlp.storage_locked, @@ -623,6 +640,13 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i SWTPM_G_FREE(mlp.json_profile); } + if (infoflags) { + /* returns more information with tpmstate active */ + jsoninfo = TPMLIB_GetInfo(infoflags); + printf("%s\n", jsoninfo); + goto error_no_sighandlers; + } + if (install_sighandlers(notify_fd, sigterm_handler) < 0) goto error_no_sighandlers; diff --git a/tests/_test_print_capabilities b/tests/_test_print_capabilities index b17a0eac2..82f2ebb18 100755 --- a/tests/_test_print_capabilities +++ b/tests/_test_print_capabilities @@ -29,8 +29,8 @@ exp='\{ "type": "swtpm", '\ '"flags-opt-disable-auto-shutdown", "ctrl-opt-terminate", '${seccomp}'"cmdarg-key-fd", '\ '"cmdarg-pwd-fd", "cmdarg-print-states", "cmdarg-chroot", "cmdarg-migration", '\ '"nvram-backend-dir", "nvram-backend-file", "cmdarg-profile", '\ -'"cmdarg-print-profiles", "profile-opt-remove-disabled" \],'\ -'( "profiles": \{ \},)? '\ +'"cmdarg-print-profiles", "profile-opt-remove-disabled", "cmdarg-print-info" \], '\ +'"profiles": \{ \}, '\ '"version": "[^"]*" \}' if ! [[ ${msg} =~ ${exp} ]]; then echo "Unexpected response from ${SWTPM_IFACE} TPM to --print-capabilities:" @@ -53,8 +53,8 @@ exp='\{ "type": "swtpm_setup", '\ '"tpm12-not-need-root", "cmdarg-write-ek-cert-files", "cmdarg-create-config-files", '\ '"cmdarg-reconfigure-pcr-banks"'\ '(, "tpm2-rsa-keysize-2048")?(, "tpm2-rsa-keysize-3072")?, "cmdarg-profile", '\ -'"cmdarg-profile-remove-disabled" \],'\ -'( "profiles": \[ [^]]*\],)? '\ +'"cmdarg-profile-remove-disabled" \], '\ +'"profiles": \[ [^]]*\], '\ '"version": "[^"]*" \}' if ! [[ ${msg} =~ ${exp} ]]; then echo "Unexpected response from ${SWTPM_SETUP} to --print-capabilities:" diff --git a/tests/_test_tpm2_print_capabilities b/tests/_test_tpm2_print_capabilities index 2b4eb341b..2ce5baa49 100755 --- a/tests/_test_tpm2_print_capabilities +++ b/tests/_test_tpm2_print_capabilities @@ -31,8 +31,8 @@ exp='\{ "type": "swtpm", '\ '"cmdarg-pwd-fd", "cmdarg-print-states", "cmdarg-chroot", "cmdarg-migration", '\ '"nvram-backend-dir", "nvram-backend-file"'\ '(, "rsa-keysize-1024")?(, "rsa-keysize-2048")?(, "rsa-keysize-3072")?, "cmdarg-profile", '\ -'"cmdarg-print-profiles", "profile-opt-remove-disabled" \],'\ -'( "profiles": \{ "names": \[ [^]]*\], "algorithms": \{ [^\}]*\}, "commands": \{ [^\}]*\} },)? '\ +'"cmdarg-print-profiles", "profile-opt-remove-disabled", "cmdarg-print-info" \], '\ +'"profiles": \{ "names": \[ [^]]*\], "algorithms": \{ [^\}]*\}, "commands": \{ [^\}]*\} }, '\ '"version": "[^"]*" \}' if ! [[ ${msg} =~ ${exp} ]]; then echo "Unexpected response from ${SWTPM_IFACE} TPM to --print-capabilities:" @@ -54,8 +54,8 @@ exp='\{ "type": "swtpm_setup", '\ '"features": \[( "tpm-1.2",)? "tpm-2.0", "cmdarg-keyfile-fd", "cmdarg-pwdfile-fd", '\ '"tpm12-not-need-root", "cmdarg-write-ek-cert-files", "cmdarg-create-config-files", '\ '"cmdarg-reconfigure-pcr-banks"(, "tpm2-rsa-keysize-2048")?(, "tpm2-rsa-keysize-3072")?, '\ -'"cmdarg-profile", "cmdarg-profile-remove-disabled" \],'\ -'( "profiles": \[ [^]]*\],)? '\ +'"cmdarg-profile", "cmdarg-profile-remove-disabled" \], '\ +'"profiles": \[ [^]]*\], '\ '"version": "[^"]*" \}' if ! [[ ${msg} =~ ${exp} ]]; then echo "Unexpected response from ${SWTPM_SETUP} to --print-capabilities:" diff --git a/tests/test_tpm2_swtpm_setup_profile b/tests/test_tpm2_swtpm_setup_profile index 421b87e15..75c472916 100755 --- a/tests/test_tpm2_swtpm_setup_profile +++ b/tests/test_tpm2_swtpm_setup_profile @@ -128,6 +128,20 @@ test_swtpm_setup_profile() echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore." exit 1 fi + + # Expecting same results from swtpm directly + if [ -n "${exp_response}" ]; then + response=$(${SWTPM_EXE} socket \ + --tpmstate "dir=${workdir}" \ + --tpm2 \ + --print-info 0x20) + if ! [[ "${response}" =~ ${exp_response} ]]; then + echo "Error: Response does not match expected response regular expression (swtpm)" + echo "Actual : ${response}" + echo "Expected : ${exp_response}" + exit 1 + fi + fi } # Get available profiles and algorithms that are implemented and those that can be disabled