From 2b8a668dbbbacee4ecc165857fb2ab4c34beca89 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sat, 21 Apr 2018 21:44:31 -0400 Subject: [PATCH] swtpm: Implement CMD_GETINFO to retrieve TPM specification info We quetry the swtpm for TPM specification info that goes into the certificate for the EK. Update the test cases that now see more capabilties being returned by the swtpm. Signed-off-by: Stefan Berger --- include/swtpm/tpm_ioctl.h | 28 ++++++++++++++++++++++++ src/swtpm/ctrlchannel.c | 45 ++++++++++++++++++++++++++++++++++++++- src/swtpm/cuse_tpm.c | 45 +++++++++++++++++++++++++++++++++++++++ tests/test_clientfds.py | 2 +- tests/test_ctrlchannel | 8 +++---- tests/test_ctrlchannel4 | 2 +- 6 files changed, 123 insertions(+), 7 deletions(-) diff --git a/include/swtpm/tpm_ioctl.h b/include/swtpm/tpm_ioctl.h index bad8601..d6196d4 100644 --- a/include/swtpm/tpm_ioctl.h +++ b/include/swtpm/tpm_ioctl.h @@ -191,6 +191,30 @@ struct ptm_setbuffersize { } u; }; +#define PTM_GETINFO_SIZE (3 * 1024) +/* + * PTM_GET_INFO: Get info about the TPM implementation (from libtpms) + * + * This request allows to indirectly call TPMLIB_GetInfo(flags) and + * retrieve information from libtpms. + * Only one transaction is currently necessary for returning results + * to a client. Therefore, totlength and length will be the same if + * offset is 0. + */ +struct ptm_getinfo { + union { + struct { + uint64_t flags; + uint32_t offset; /* offset from where to read */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t totlength; + uint32_t length; + char buffer[PTM_GETINFO_SIZE]; + } resp; /* response */ + } u; +}; typedef uint64_t ptm_cap; typedef struct ptm_est ptm_est; @@ -202,6 +226,7 @@ typedef struct ptm_getstate ptm_getstate; typedef struct ptm_setstate ptm_setstate; typedef struct ptm_getconfig ptm_getconfig; typedef struct ptm_setbuffersize ptm_setbuffersize; +typedef struct ptm_getinfo ptm_getinfo; /* capability flags returned by PTM_GET_CAPABILITY */ #define PTM_CAP_INIT (1) @@ -218,6 +243,7 @@ typedef struct ptm_setbuffersize ptm_setbuffersize; #define PTM_CAP_GET_CONFIG (1 << 11) #define PTM_CAP_SET_DATAFD (1 << 12) #define PTM_CAP_SET_BUFFERSIZE (1 << 13) +#define PTM_CAP_GET_INFO (1 << 14) enum { PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), @@ -237,6 +263,7 @@ enum { PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), PTM_SET_DATAFD = _IOR('P', 15, ptm_res), PTM_SET_BUFFERSIZE = _IOWR('P', 16, ptm_setbuffersize), + PTM_GET_INFO = _IOWR('P', 17, ptm_getinfo), }; /* @@ -267,6 +294,7 @@ enum { CMD_GET_CONFIG, /* 0x0f */ CMD_SET_DATAFD, /* 0x10 */ CMD_SET_BUFFERSIZE, /* 0x11 */ + CMD_GET_INFO, /* 0x12 */ }; #endif /* _TPM_IOCTL_H */ diff --git a/src/swtpm/ctrlchannel.c b/src/swtpm/ctrlchannel.c index 7b9aeb4..1303fb3 100644 --- a/src/swtpm/ctrlchannel.c +++ b/src/swtpm/ctrlchannel.c @@ -475,11 +475,16 @@ int ctrlchannel_process_fd(int fd, ptm_setstate *pss; ptm_loc *pl; ptm_setbuffersize *psbs; + ptm_getinfo *pgi; size_t out_len = 0; TPM_RESULT res; uint32_t remain; uint32_t buffersize, maxsize, minsize; + uint64_t info_flags; + uint32_t offset; + char *info_data = NULL; + size_t length; if (fd < 0) return -1; @@ -515,7 +520,8 @@ int ctrlchannel_process_fd(int fd, #ifndef __CYGWIN__ PTM_CAP_SET_DATAFD | #endif - PTM_CAP_SET_BUFFERSIZE + PTM_CAP_SET_BUFFERSIZE | + PTM_CAP_GET_INFO ); out_len = sizeof(*ptm_caps); @@ -795,6 +801,37 @@ int ctrlchannel_process_fd(int fd, break; + case CMD_GET_INFO: + pgi = (ptm_getinfo *)input.body; + if (n < (ssize_t)sizeof(pgi->u.req)) /* rw */ + goto err_bad_input; + + info_flags = be64toh(pgi->u.req.flags); + + info_data = TPMLIB_GetInfo(info_flags); + if (!info_data) + goto err_memory; + + offset = be32toh(pgi->u.req.offset); + if (offset >= strlen(info_data)) { + free(info_data); + goto err_bad_input; + } + + length = min(strlen(info_data) + 1 - offset, + sizeof(pgi->u.resp.buffer)); + + pgi = (ptm_getinfo *)&output.body; + pgi->u.resp.tpm_result = htobe32(0); + pgi->u.resp.totlength = htobe32(strlen(info_data) + 1); + pgi->u.resp.length = htobe32(length); + strncpy(pgi->u.resp.buffer, &info_data[offset], length); + free(info_data); + + out_len = offsetof(ptm_getinfo, u.resp.buffer) + length; + + break; + default: logprintf(STDERR_FILENO, "Error: Unknown command: 0x%08x\n", be32toh(input.cmd)); @@ -840,6 +877,12 @@ err_io: goto send_resp; +err_memory: + *res_p = htobe32(TPM_SIZE); + out_len = sizeof(ptm_res); + + goto send_resp; + err_socket: close(fd); diff --git a/src/swtpm/cuse_tpm.c b/src/swtpm/cuse_tpm.c index 967c6d5..e8e79b2 100644 --- a/src/swtpm/cuse_tpm.c +++ b/src/swtpm/cuse_tpm.c @@ -1209,6 +1209,39 @@ static void ptm_ioctl(fuse_req_t req, int cmd, void *arg, } break; + case PTM_GET_INFO: + if (out_bufsz != sizeof(ptm_getinfo)) { + struct iovec iov = { arg, sizeof(uint32_t) }; + fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0); + } else { + ptm_getinfo *in_pgi = (ptm_getinfo *)in_buf; + ptm_getinfo out_pgi; + char *info_data; + uint32_t length, offset; + + info_data = TPMLIB_GetInfo(in_pgi->u.req.flags); + if (!info_data) + goto error_memory; + + offset = in_pgi->u.req.offset; + if (offset >= strlen(info_data)) { + free(info_data); + goto error_bad_input; + } + + length = min(strlen(info_data) + 1 - offset, + sizeof(out_pgi.u.resp.buffer)); + + out_pgi.u.resp.tpm_result = 0; + out_pgi.u.resp.totlength = strlen(info_data) + 1; + out_pgi.u.resp.length = length; + strncpy(out_pgi.u.resp.buffer, &info_data[offset], length); + free(info_data); + + fuse_reply_ioctl(req, 0, &out_pgi, sizeof(out_pgi)); + } + break; + default: fuse_reply_err(req, EINVAL); } @@ -1225,11 +1258,23 @@ cleanup: return; +error_bad_input: + res = TPM_BAD_PARAMETER; + fuse_reply_ioctl(req, 0, &res, sizeof(res)); + + goto cleanup; + error_running: error_not_running: res = TPM_BAD_ORDINAL; fuse_reply_ioctl(req, 0, &res, sizeof(res)); + goto cleanup; + +error_memory: + res = TPM_SIZE; + fuse_reply_ioctl(req, 0, &res, sizeof(res)); + goto cleanup; } diff --git a/tests/test_clientfds.py b/tests/test_clientfds.py index 7dbbfce..289da09 100755 --- a/tests/test_clientfds.py +++ b/tests/test_clientfds.py @@ -64,7 +64,7 @@ def test_get_caps(): # test get capabilities # CMD_GET_CAPABILITY = 0x00 00 00 01 cmd_get_caps = bytearray([0x00, 0x00, 0x00, 0x01]) - expected_caps = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff]) + expected_caps = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff]) def toString(arr): return ' '.join('{:02x}'.format(x) for x in arr) diff --git a/tests/test_ctrlchannel b/tests/test_ctrlchannel index b0a2d88..41328ec 100755 --- a/tests/test_ctrlchannel +++ b/tests/test_ctrlchannel @@ -58,9 +58,9 @@ PID="$(cat $PID_FILE)" # Get the capability bits: CMD_GET_CAPABILITY = 0x00 00 00 01 res="$(swtpm_ctrl_tx ${SWTPM_INTERFACE} '\x00\x00\x00\x01')" if [[ "$(uname -s)" =~ (Linux|OpenBSD) ]]; then - exp=" 00 00 00 00 00 00 3f ff" + exp=" 00 00 00 00 00 00 7f ff" else - exp=" 00 00 00 00 00 00 2f ff" + exp=" 00 00 00 00 00 00 6f ff" fi if [ "$res" != "$exp" ]; then echo "Error: Unexpected response from CMD_GET_CAPABILITY:" @@ -173,9 +173,9 @@ swtpm_open_cmddev ${SWTPM_INTERFACE} 100 # Get the capability bits: CMD_GET_CAPABILITY = 0x00 00 00 01 res="$(swtpm_ctrl_tx ${SWTPM_INTERFACE} '\x00\x00\x00\x01')" if [[ "$(uname -s)" =~ (Linux|OpenBSD) ]]; then - exp=" 00 00 00 00 00 00 3f ff" + exp=" 00 00 00 00 00 00 7f ff" else - exp=" 00 00 00 00 00 00 2f ff" + exp=" 00 00 00 00 00 00 6f ff" fi if [ "$res" != "$exp" ]; then echo "Error: Socket TPM: Unexpected response from CMD_GET_CAPABILITY:" diff --git a/tests/test_ctrlchannel4 b/tests/test_ctrlchannel4 index 65fa6d3..ffbcde7 100755 --- a/tests/test_ctrlchannel4 +++ b/tests/test_ctrlchannel4 @@ -45,7 +45,7 @@ PID="$(cat $PID_FILE)" # Get the capability bits: CMD_GET_CAPABILITY = 0x00 00 00 01 res="$(swtpm_ctrl_tx ${SWTPM_INTERFACE} '\x00\x00\x00\x01')" -exp=" 00 00 00 00 00 00 3f ff" +exp=" 00 00 00 00 00 00 7f ff" if [ "$res" != "$exp" ]; then echo "Error: Unexpected response from CMD_GET_CAPABILITY:" echo " actual : $res"