swtpm: Implement CMD_LOCK_STORAGE to lock storage

Implement CMD_LOCK_STORAGE / PTM_LOCK_STORAGE for a user to be able to
lock the storage of the storage backend (if supported) after its lock
has been released for example when the 'savestate' blob was received
while the TPM state was migrated.

Also adjust test case and extend man pages.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
This commit is contained in:
Stefan Berger 2022-08-23 16:07:24 -04:00 committed by Stefan Berger
parent e821754bb6
commit 6fbb219db9
9 changed files with 135 additions and 10 deletions

View File

@ -230,6 +230,20 @@ struct ptm_getinfo {
#define SWTPM_INFO_TPMSPECIFICATION ((uint64_t)1 << 0)
#define SWTPM_INFO_TPMATTRIBUTES ((uint64_t)1 << 1)
/*
* PTM_LOCK_STORAGE: Lock the storage and retry n times
*/
struct ptm_lockstorage {
union {
struct {
uint32_t retries; /* number of retries */
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* reponse */
} u;
};
typedef uint64_t ptm_cap;
typedef struct ptm_est ptm_est;
typedef struct ptm_reset_est ptm_reset_est;
@ -241,6 +255,7 @@ typedef struct ptm_setstate ptm_setstate;
typedef struct ptm_getconfig ptm_getconfig;
typedef struct ptm_setbuffersize ptm_setbuffersize;
typedef struct ptm_getinfo ptm_getinfo;
typedef struct ptm_lockstorage ptm_lockstorage;
/* capability flags returned by PTM_GET_CAPABILITY */
#define PTM_CAP_INIT (1)
@ -259,6 +274,7 @@ typedef struct ptm_getinfo ptm_getinfo;
#define PTM_CAP_SET_BUFFERSIZE (1 << 13)
#define PTM_CAP_GET_INFO (1 << 14)
#define PTM_CAP_SEND_COMMAND_HEADER (1 << 15)
#define PTM_CAP_LOCK_STORAGE (1 << 16)
#ifndef _WIN32
enum {
@ -280,6 +296,7 @@ enum {
PTM_SET_DATAFD = _IOR('P', 15, ptm_res),
PTM_SET_BUFFERSIZE = _IOWR('P', 16, ptm_setbuffersize),
PTM_GET_INFO = _IOWR('P', 17, ptm_getinfo),
PTM_LOCK_STORAGE = _IOWR('P', 18, ptm_lockstorage),
};
#endif
@ -312,6 +329,7 @@ enum {
CMD_SET_DATAFD, /* 0x10 */
CMD_SET_BUFFERSIZE, /* 0x11 */
CMD_GET_INFO, /* 0x12 */
CMD_LOCK_STORAGE, /* 0x13 */
};
#endif /* _TPM_IOCTL_H_ */

View File

@ -91,10 +91,14 @@ The PTM_GET_CONFIG ioctl or CMD_GET_CONFIG command is supported.
The CMD_SET_DATAFD command is supported. This command only applies to UnixIO
and there is no support for PTM_SET_DATAFD.
=item B<PTM_SET_BUFFERSIZE (since v0.1)>
=item B<PTM_CAP_SET_BUFFERSIZE (since v0.1)>
The PTM_SET_BUFFERSIZE ioctl or CMD_SET_BUFFERSIZE command is supported.
=item B<PTM_CAP_LOCK_STORAGE (since v0.8)>
The PTM_LOCK_STORAGE ioctl or CMD_LOCK_STORAGE command is supported.
=back
=item B<PTM_INIT / CMD_INIT, ptm_init>
@ -398,6 +402,28 @@ buffer sizes are returned in the response.
The buffersize can only be changed when the TPM is stopped. The
currently used buffersize can be read at any time.
=item B<CMD_LOCK_STORAGE, ptm_lockstorage>
Lock the storage and retry a given number of times with 10ms delay in between.
Locking the storage may be necessary to do after the state of the TPM has been
migrated out and the lock on the storage has been released when the 'savestate'
blob was received and now the storage should be locked again.
The ptm_lockstorage data structure looks as follows:
struct ptm_lockstorage {
union {
struct {
uint32_t retries; /* number of retries */
} req; /* request */
struct {
ptm_res tpm_result;
} resp; /* reponse */
} u;
};
A TPM result code is returned in the tpm_result field.
=back
=head1 SEE ALSO

View File

@ -119,6 +119,12 @@ about the specification the TPM implementation followed. The flag
I<TPMLIB_INFO_TPMATTRIBUTES>, which has the value 2, returns information
about the manufacturer, model, and version of the TPM.
=item B<--lock-storage E<lt>retriesE<gt>>
Lock the storage and retry a given number of times with 10ms delay in between.
Locking the storage may be necessary to do after the state of the TPM has been
migrated out and the lock on the storage has been released when the 'savestate'
blob was received and now the storage should be locked again.
=back

View File

@ -456,7 +456,8 @@ static uint64_t get_ptm_caps_supported(TPMLIB_TPMVersion tpmversion)
| PTM_CAP_SET_DATAFD
#endif
| PTM_CAP_SET_BUFFERSIZE
| PTM_CAP_GET_INFO;
| PTM_CAP_GET_INFO
| PTM_CAP_LOCK_STORAGE;
if (tpmversion == TPMLIB_TPM_VERSION_2)
caps |= PTM_CAP_SEND_COMMAND_HEADER;
@ -520,6 +521,7 @@ int ctrlchannel_process_fd(int fd,
ptm_loc *pl;
ptm_setbuffersize *psbs;
ptm_getinfo *pgi, _pgi;
ptm_lockstorage *pls;
size_t out_len = 0;
TPM_RESULT res;
@ -881,6 +883,24 @@ int ctrlchannel_process_fd(int fd,
break;
case CMD_LOCK_STORAGE:
if (n < (ssize_t)sizeof(pls->u.req)) /* rw */
goto err_bad_input;
pls = (ptm_lockstorage *)input.body;
mlp->locking_retries = be32toh(pls->u.req.retries);
pls = (ptm_lockstorage *)&output.body;
out_len = sizeof(pls->u.resp);
if (!mainloop_ensure_locked_storage(mlp))
pls->u.resp.tpm_result = htobe32(TPM_FAIL);
else
pls->u.resp.tpm_result = htobe32(TPM_SUCCESS);
break;
default:
logprintf(STDERR_FILENO,
"Error: Unknown command: 0x%08x\n", be32toh(input.cmd));

View File

@ -1088,6 +1088,7 @@ static void ptm_ioctl(fuse_req_t req, int cmd, void *arg,
case PTM_CANCEL_TPM_CMD:
case PTM_GET_CONFIG:
case PTM_SET_BUFFERSIZE:
case PTM_LOCK_STORAGE:
/* no need to wait */
break;
case PTM_INIT:
@ -1129,7 +1130,8 @@ static void ptm_ioctl(fuse_req_t req, int cmd, void *arg,
| PTM_CAP_STOP
| PTM_CAP_GET_CONFIG
| PTM_CAP_SET_BUFFERSIZE
| PTM_CAP_GET_INFO;
| PTM_CAP_GET_INFO
| PTM_CAP_LOCK_STORAGE;
break;
case TPMLIB_TPM_VERSION_1_2:
ptm_caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN
@ -1144,7 +1146,8 @@ static void ptm_ioctl(fuse_req_t req, int cmd, void *arg,
| PTM_CAP_STOP
| PTM_CAP_GET_CONFIG
| PTM_CAP_SET_BUFFERSIZE
| PTM_CAP_GET_INFO;
| PTM_CAP_GET_INFO
| PTM_CAP_LOCK_STORAGE;
break;
}
fuse_reply_ioctl(req, 0, &ptm_caps, sizeof(ptm_caps));
@ -1433,6 +1436,25 @@ static void ptm_ioctl(fuse_req_t req, int cmd, void *arg,
}
break;
case PTM_LOCK_STORAGE:
if (out_bufsz != sizeof(ptm_lockstorage)) {
struct iovec iov = { arg, sizeof(uint32_t) };
fuse_reply_ioctl_retry(req, &iov, 1, NULL, 0);
} else {
ptm_lockstorage *in_pls = (ptm_lockstorage *)in_buf;
ptm_lockstorage out_pls;
g_locking_retries = in_pls->u.req.retries;
if (!ensure_locked_storage())
out_pls.u.resp.tpm_result = TPM_FAIL;
else
out_pls.u.resp.tpm_result = TPM_SUCCESS;
fuse_reply_ioctl(req, 0, &out_pls, sizeof(out_pls));
}
break;
default:
fuse_reply_err(req, EINVAL);
}

View File

@ -899,6 +899,8 @@ static void usage(const char *prgname)
" size; get minimum and maximum supported sizes\n"
"--info <flags> : get TPM implementation specific information;\n"
" flags must be an integer value\n"
"--lock-storage <n> : lock the storage after it was unlocked; retry\n"
" n times with 10ms delay in between\n"
"--version : display version and exit\n"
"--help : display help screen and exit\n"
"\n"
@ -917,6 +919,7 @@ int main(int argc, char *argv[])
ptm_getconfig cfg;
ptm_setbuffersize psbs;
ptm_getinfo pgi;
ptm_lockstorage pls;
char *tmp;
size_t buffersize = 0;
static struct option long_options[] = {
@ -939,6 +942,7 @@ int main(int argc, char *argv[])
{"load", required_argument, NULL, 'L'},
{"version", no_argument, NULL, 'V'},
{"info", required_argument, NULL, 'I'},
{"lock-storage", required_argument, NULL, 'o'},
{"help", no_argument, NULL, 'H'},
{NULL, 0, NULL, 0},
};
@ -950,6 +954,7 @@ int main(int argc, char *argv[])
unsigned int locality = 0;
unsigned int tpmbuffersize = 0;
unsigned int tcp_port = 0;
unsigned int retries;
bool is_chardev;
unsigned long int info_flags = 0;
char *endptr = NULL;
@ -1042,6 +1047,14 @@ int main(int argc, char *argv[])
goto exit;
}
break;
case 'o':
command = argv[optind - 2];
if (sscanf(argv[optind - 1], "%u", &retries) != 1) {
fprintf(stderr, "Could not parse lock retries from %s.\n",
argv[optind - 1]);
goto exit;
}
break;
case 'V':
versioninfo();
ret = EXIT_SUCCESS;
@ -1307,6 +1320,26 @@ int main(int argc, char *argv[])
goto exit;
}
printf("%s\n", pgi.u.resp.buffer);
} else if (!strcmp(command, "--lock-storage")) {
memset(&pls, 0, sizeof(pls));
pls.u.req.retries = htodev32(is_chardev, 0);
n = ctrlcmd(fd, PTM_LOCK_STORAGE, &pls,
sizeof(pls.u.req), sizeof(pls.u.resp));
if (n < 0) {
fprintf(stderr,
"Could not execute PTM_LOCK_STORAGE: "
"%s\n", strerror(errno));
goto exit;
}
res = devtoh32(is_chardev, pls.u.resp.tpm_result);
if (devtoh32(is_chardev, res) != 0) {
fprintf(stderr,
"TPM result from PTM_LOCK_STORAGE: 0x%x\n",
devtoh32(is_chardev, res));
goto exit;
}
} else {
usage(argv[0]);
goto exit;

View File

@ -70,7 +70,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, 0x7f, 0xff])
expected_caps = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff])
def toString(arr):
return ' '.join('{:02x}'.format(x) for x in arr)

View File

@ -99,9 +99,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|FreeBSD|NetBSD|Darwin|DragonFly) ]]; then
exp=" 00 00 00 00 00 00 7f ff"
exp=" 00 00 00 00 00 01 7f ff"
else
exp=" 00 00 00 00 00 00 6f ff"
exp=" 00 00 00 00 00 01 6f ff"
fi
if [ "$res" != "$exp" ]; then
echo "Error: Unexpected response from CMD_GET_CAPABILITY:"
@ -236,9 +236,9 @@ validate_pidfile $PID $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|FreeBSD|NetBSD|Darwin|DragonFly) ]]; then
exp=" 00 00 00 00 00 00 7f ff"
exp=" 00 00 00 00 00 01 7f ff"
else
exp=" 00 00 00 00 00 00 6f ff"
exp=" 00 00 00 00 00 01 6f ff"
fi
if [ "$res" != "$exp" ]; then
echo "Error: Socket TPM: Unexpected response from CMD_GET_CAPABILITY:"

View File

@ -49,7 +49,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 7f ff"
exp=" 00 00 00 00 00 01 7f ff"
if [ "$res" != "$exp" ]; then
echo "Error: Unexpected response from CMD_GET_CAPABILITY:"
echo " actual : $res"