I managed to get fucking tcsd to use swtpm

This commit is contained in:
наб 2020-10-19 00:32:42 +02:00
parent 65c2384582
commit 1cc8ddaa63
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
4 changed files with 148 additions and 4 deletions

View File

@ -5,6 +5,7 @@ packages:
- clang
- pkg-config
- libtss2-dev
- libtspi-dev
- ronn
tasks:
- get-zfs: |

View File

@ -23,7 +23,7 @@
include configMakefile
LDDLLS := rt $(OS_LD_LIBS)
LDDLLS := rt tspi $(OS_LD_LIBS)
PKGS := libzfs libzfs_core tss2-esys tss2-rc
LDAR := $(LNCXXAR) $(foreach l,,-L$(BLDDIR)$(l)) $(foreach dll,$(LDDLLS),-l$(dll)) $(shell pkg-config --libs $(PKGS))
INCAR := $(foreach l,$(foreach l,,$(l)/include),-isystemext/$(l)) $(foreach l,,-isystem$(BLDDIR)$(l)/include) $(shell pkg-config --cflags $(PKGS))

View File

@ -14,7 +14,7 @@ Plus it's a pretty good annoyed sigh onomatopoeia.
### Building
You'll need `pkg-config`, `ronn`, `libzfslinux-dev`, `libtss2-dev`, and `make` should hopefully Just Work™ if you have a C++17-capable compiler.
You'll need `pkg-config`, `ronn`, `libzfslinux-dev`, `libtss2-dev`, `libtspi-dev`, and `make` should hopefully Just Work™ if you have a C++17-capable compiler.
The output binaries are trimmed of extraneous dependencies, so they're all just libc + libzfs and friends + TPM back-end.
### Installation
@ -50,14 +50,29 @@ See the [repository README](//debian.nabijaczleweli.xyz/README) for more informa
Build [`swtpm`](//github.com/stefanberger/swtpm), then prepare and run it:
```sh
swtpm_setup --tpmstate tpm-state --tpm2 --createek --display --logfile /dev/stdout --overwrite
swtpm socket --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --tpm2 --tpmstate dir=tpm-state --flags not-need-init --log level=10
swtpm_setup --tpmstate tpm2-state --tpm2 --createek --display --logfile /dev/stdout --overwrite
swtpm socket --server type=tcp,port=2321 --ctrl type=tcp,port=2322 --tpm2 --tpmstate dir=tpm2-state --flags not-need-init --log level=10
```
If your platform has a TPM, switch to `swtpm` by default:
```
ln -s /usr/lib/i386-linux-gnu/libtss2-tcti-{swtpm,default}.so
```
#### TPM1.x
Build [`swtpm`](//github.com/stefanberger/swtpm), then prepare and run it and
([hopefully](https://github.com/stefanberger/swtpm/issues/5#issuecomment-210607890)) [TrouSerS](//sourceforge.net/projects/trousers), as `root`/`tpm`:
```sh
swtpm_setup --tpmstate tpm1x-state --createek --display --logfile /dev/stdout --overwrite
swtpm cuse -n tpm --tpmstate dir=tpm1x-state --seccomp action=none --log level=10,file=/dev/fd/4 4>&1
swtpm_ioctl -i /dev/tpm
TPM_DEVICE=/dev/tpm swtpm_bios
tcsd -f
swtpm_ioctl -s /dev/tpm # to shut down, apparently
```
If your platform has a TPM, occupy it first by running `exec 100<>/dev/tpm0` or equivalent. `tcsd` looks at `/dev/tpm0` before `/dev/tpm`.
## Reporting bugs

View File

@ -0,0 +1,128 @@
/* SPDX-License-Identifier: MIT */
#include <libzfs.h>
// #include <sys/zio_crypt.h>
#define WRAPPING_KEY_LEN 32
#include <stdio.h>
#include <tss/platform.h>
#include <tss/tspi.h>
#include <tss/tss_structs.h>
#include <tss/tss_typedef.h>
#include <trousers/trousers.h>
#include "../fd.hpp"
#include "../main.hpp"
#include "../parse.hpp"
#include "../tpm2.hpp"
#include "../zfs.hpp"
#define THIS_BACKEND "TPM1.X"
int main(int argc, char ** argv) {
const char * backup{};
return do_main(
argc, argv, "b:", "[-b backup-file]", [&](auto) { backup = optarg; },
[&](auto dataset) {
REQUIRE_KEY_LOADED(dataset);
// TSS_RESULT Tspi_Context_Create(TSS_HCONTEXT* phContext);
// TSS_RESULT Tspi_Context_Connect(TSS_HCONTEXT hLocalContext, UNICODE* wszDestination);
// TSS_RESULT Tspi_Context_GetTpmObject(TSS_HCONTEXT hContext, TSS_HTPM* phTPM);
//
// TSS_RESULT Tspi_TPM_GetRandom(TSS_HTPM hTPM, UINT32 size, BYTE** random);
//
//
// TSS_RESULT Tspi_Data_Seal(TSS_HENCDATA hEncData, TSS_HKEY hEncKey,
// UINT32 ulDataLength, BYTE* rgbDataToSeal,
// TSS_HPCRS hPcrComposite);
//
//
// TSS_RESULT Tspi_Context_Close(TSS_HCONTEXT hLocalContext);
TSS_HCONTEXT ctx{};
fprintf(stderr, "Tspi_Context_Create() = %s\n", Trspi_Error_String(Tspi_Context_Create(&ctx)));
fprintf(stderr, "Tspi_Context_Connect() = %s\n", Trspi_Error_String(Tspi_Context_Connect(ctx, nullptr)));
TSS_HTPM tpm_h{};
fprintf(stderr, "Tspi_Context_GetTpmObject() = %s\n", Trspi_Error_String(Tspi_Context_GetTpmObject(ctx, &tpm_h)));
quickscope_wrapper ctx_deleter{[&] { fprintf(stderr, "Tspi_Context_Close() = %s\n", Trspi_Error_String(Tspi_Context_Close(ctx))); }};
return 0;
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
char *previous_backend{}, *previous_handle_s{};
TRY_MAIN(lookup_userprop(dataset, PROPNAME_BACKEND, previous_backend));
TRY_MAIN(lookup_userprop(dataset, PROPNAME_KEY, previous_handle_s));
if(!!previous_backend ^ !!previous_handle_s)
fprintf(stderr, "Inconsistent tzpfms metadata for %s: back-end is %s, but handle is %s?\n", zfs_get_name(dataset), previous_backend,
previous_handle_s);
else if(previous_backend && previous_handle_s) {
if(strcmp(previous_backend, THIS_BACKEND))
fprintf(stderr,
"Dataset %s was encrypted with tzpfms back-end %s before, but we are %s. You will have to free handle %s for back-end %s manually!\n",
zfs_get_name(dataset), previous_backend, THIS_BACKEND, previous_handle_s, previous_backend);
else {
TPMI_DH_PERSISTENT previous_handle{};
if(parse_int(previous_handle_s, previous_handle))
fprintf(stderr, "Couldn't parse previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c %s\" or equivalent!\n",
zfs_get_name(dataset), previous_handle_s);
else {
if(tpm2_free_persistent(tpm2_ctx, tpm2_session, previous_handle))
fprintf(stderr,
"Couldn't free previous persistent handle for dataset %s. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n",
zfs_get_name(dataset), previous_handle);
}
}
}
uint8_t wrap_key[WRAPPING_KEY_LEN];
TPMI_DH_PERSISTENT persistent_handle{};
TRY_MAIN(tpm2_generate_rand(tpm2_ctx, wrap_key, sizeof(wrap_key)));
if(backup)
TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400));
TRY_MAIN(tpm2_seal(tpm2_ctx, tpm2_session, persistent_handle, tpm2_creation_metadata(zfs_get_name(dataset)), wrap_key, sizeof(wrap_key)));
bool ok = false; // Try to free the persistent handle if we're unsuccessful in actually using it later on
quickscope_wrapper persistent_clearer{[&] {
if(!ok && tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle))
fprintf(stderr, "Couldn't free persistent handle. You might need to run \"tpm2_evictcontrol -c 0x%X\" or equivalent!\n", persistent_handle);
if(!ok && clear_key_props(dataset)) // Sync with zfs-tpm2-clear-key
fprintf(stderr, "You might need to run \"zfs inherit %s %s\" and \"zfs inherit %s %s\"!\n", PROPNAME_BACKEND, zfs_get_name(dataset), PROPNAME_KEY,
zfs_get_name(dataset));
}};
TRY_MAIN(set_key_props(dataset, THIS_BACKEND, persistent_handle));
/// zfs_crypto_rewrap() with "prompt" reads from stdin, but not if it's a TTY;
/// this user-proofs the set-up, and means we don't have to touch the filesysten:
/// instead, get an FD, write the raw key data there, dup() it onto stdin,
/// let libzfs read it, then restore stdin
int key_fd;
TRY_MAIN(filled_fd(key_fd, wrap_key, WRAPPING_KEY_LEN));
quickscope_wrapper key_fd_deleter{[=] { close(key_fd); }};
TRY_MAIN(with_stdin_at(key_fd, [&] {
if(zfs_crypto_rewrap(dataset, TRY_PTR("get rewrap args", rewrap_args()), B_FALSE))
return __LINE__; // Error printed by libzfs
else
printf("Key for %s changed\n", zfs_get_name(dataset));
return 0;
}));
ok = true;
return 0;
}));
return 0;
});
}