~nabijaczleweli/tzpfms

1cc8ddaa63b5edc1a47d9bcbd3bcfa6ac49d9227 — наб a month ago 65c2384
I managed to get fucking tcsd to use swtpm
4 files changed, 148 insertions(+), 4 deletions(-)

M .build.yml
M Makefile
M README.md
A src/bin/zfs-tpm1x-change-key.cpp
M .build.yml => .build.yml +1 -0
@@ 5,6 5,7 @@ packages:
  - clang
  - pkg-config
  - libtss2-dev
  - libtspi-dev
  - ronn
tasks:
  - get-zfs: |

M Makefile => Makefile +1 -1
@@ 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))

M README.md => README.md +18 -3
@@ 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


A src/bin/zfs-tpm1x-change-key.cpp => src/bin/zfs-tpm1x-change-key.cpp +128 -0
@@ 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;
	    });
}