Finalise zfs-tpm2-clear-key. Add manpages

This commit is contained in:
наб 2020-10-18 03:22:56 +02:00
parent 0cf16ed2a2
commit a007176d65
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
15 changed files with 224 additions and 41 deletions

View File

@ -31,7 +31,7 @@ VERAR := $(foreach l,TZPFMS,-D$(l)_VERSION='$($(l)_VERSION)')
BINARY_SOURCES := $(sort $(wildcard $(SRCDIR)bin/*.cpp $(SRCDIR)bin/**/*.cpp))
COMMON_SOURCES := $(filter-out $(BINARY_SOURCES),$(sort $(wildcard $(SRCDIR)*.cpp $(SRCDIR)**/*.cpp $(SRCDIR)**/**/*.cpp $(SRCDIR)**/**/**/*.cpp)))
# TEST_SOURCES := $(sort $(wildcard $(TSTDIR)*.cpp $(TSTDIR)**/*.cpp $(TSTDIR)**/**/*.cpp $(TSTDIR)**/**/**/*.cpp))
MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md $(MANDIR)**/*.md))
MANPAGE_SOURCES := $(sort $(wildcard $(MANDIR)*.md.pp))
.PHONY : all clean build build-test man
@ -48,17 +48,17 @@ clean :
build : $(subst $(SRCDIR)bin/,$(OUTDIR),$(subst .cpp,$(EXE),$(BINARY_SOURCES)))
#build-test : $(OUTDIR)tzpfms-test$(EXE)
man : $(subst $(MANDIR),$(OUTDIR)man/,$(MANPAGE_SOURCES))
man : $(OUTDIR)man/index.txt
#$(OUTDIR)tzpfms-test$(EXE) : $(subst $(TSTDIR),$(BLDDIR)test/,$(subst .cpp,$(OBJ),$(TEST_SOURCES))) $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(filter-out $(SRCDIR)main.cpp,$(SOURCES)))) $(patsubst ext/fmt/src/%.cc,$(BLDDIR)fmt/obj/%$(OBJ),$(wildcard ext/fmt/src/*.cc))
# $(CXX) $(CXXAR) -o$@ $^ $(PIC) $(LDAR)
$(subst $(MANDIR),$(OUTDIR)man/,$(MANPAGE_SOURCES)) : $(MANDIR)index.txt $(MANPAGE_SOURCES)
@rm -rf $(dir $@) && mkdir -p $(dir $@)
cp $^ $(dir $@)
$(RONN) $@
$(RONN) -f $@
$(OUTDIR)man/index.txt : $(MANDIR)index.txt $(patsubst $(MANDIR)%.pp,$(OUTDIR)man/%,$(MANPAGE_SOURCES))
@mkdir -p $(dir $@)
cp $< $(dir $@)
$(RONN) --organization="tzpfms developers" $(filter-out $<,$^)
$(RONN) --organization="tzpfms developers" -f $(filter-out $<,$^)
$(OUTDIR)%$(EXE) : $(subst $(SRCDIR),$(OBJDIR),$(subst .cpp,$(OBJ),$(SRCDIR)bin/%.cpp $(COMMON_SOURCES)))
@ -73,3 +73,7 @@ $(OBJDIR)%$(OBJ) : $(SRCDIR)%.cpp
$(BLDDIR)test/%$(OBJ) : $(TSTDIR)%.cpp
@mkdir -p $(dir $@)
$(CXX) $(CXXAR) $(INCAR) -I$(SRCDIR) $(VERAR) -c -o$@ $^
$(OUTDIR)man/%.md : $(MANDIR)%.md.pp $(sort $(wildcard $(MANDIR)*.h))
@mkdir -p $(dir $@)
$(AWK) '/^#include/ {gsub("\"", "", $$2); while((getline inc < ("$(dir $<)" $$2)) == 1) print inc; next} {print}' $< > $@

View File

@ -52,7 +52,6 @@ TZPFMS_VERSION := "0.0.0-$(shell git rev-list HEAD --count)"
INCCMAKEAR := CXXFLAGS="$(INCCXXAR)"
LNCMAKEAR := LDFLAGS="$(LNCXXAR)"
LDD ?= ldd
AWK ?= awk
RONN ?= ronn
OBJ := .o

17
man/backend-tpm2.h Normal file
View File

@ -0,0 +1,17 @@
## TPM2 back-end configuration
### Environment variables
* `TSS2_LOG`=:
Any of: *NONE*, *ERROR*, *WARNING*, *INFO*, *DEBUG*, *TRACE*. Default: *WARNING*.
### TPM selection
The library `libtss2-tcti-default.so` can be linked to any of the `libtss2-tcti-*.so` libraries to select the default,
otherwise `/dev/tpmrm0`, then `/dev/tpm0`, then `localhost:2321` will be tried, in order (see ESYS_CONTEXT(3)).
### See also
The tpm2-tss git repository at <https://github.com/tpm2-software/tpm2-tss> and the documentation at <https://tpm2-tss.readthedocs.io>.
The TPM 2.0 specifications, mainly at &lt;<https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf>&gt; and related pages.

16
man/common.h Normal file
View File

@ -0,0 +1,16 @@
## AUTHOR
Written by наб &lt;<nabijaczleweli@nabijaczleweli.xyz>&gt;
## SPECIAL THANKS
To all who support further development, in particular:
* ThePhD
* Embark Studios
## REPORTING BUGS
&lt;<https://todo.sr.ht/~nabijaczleweli/tzpfms>&gt;
&lt;<mailto:~nabijaczleweli/tzpfms@lists.sr.ht>&gt;, archived at &lt;<https://lists.sr.ht/~nabijaczleweli/tzpfms>&gt;

8
man/index.txt Normal file
View File

@ -0,0 +1,8 @@
zfs-tpm2-change-key(8) zfs-tpm2-change-key.8.ronn
zfs-tpm2-load-key(8) zfs-tpm2-load-key.8.ronn
zfs-tpm2-clear-key(8) zfs-tpm2-clear-key.8.ronn
zfs(8) https://manpages.debian.org/bullseye/zfsutils-linux/zfs.8.en.html
tpm2_unseal(1) https://manpages.debian.org/bullseye/tpm2-tools/tpm2_unseal.1.en.html
ESYS_CONTEXT(3) https://www.mankier.com/3/ESYS_CONTEXT

View File

@ -0,0 +1,56 @@
zfs-tpm2-change-key(8) -- change ZFS dataset key to one stored on the TPM
=========================================================================
## SYNOPSIS
`zfs-tpm2-change-key` [-b file] <dataset>
## DESCRIPTION
To normalise `dataset`, zfs-tpm2-change-key(8) will open its encryption root in its stead.
zfs-tpm2-change-key(8) will *never* create or destroy encryption roots; use **zfs(8) change-key** for that.
First, a connection is made to the TPM, which *must* be TPM-2.0-compatible.
If `dataset` was previously encrypted with tzpfms and the *TPM2* back-end was used, the previous key will be freed from the TPM.
Otherwise, or in case of an error, data required for manual intervention will be printed to the standard error stream.
Next, a new wrapping key is be generated on the TPM, optionally backed up (see [OPTIONS][]),
and sealed to a persistent object on the TPM under the owner hierarchy.
The following properties are set on `dataset`:
* `xyz.nabijaczleweli:tzpfms.backend`=`TPM2`
* `xyz.nabijaczleweli:tzpfms.key`=*(ID of persistent object)*
`tzpfms.backend` identifies this dataset for work with *TPM2*-back-ended tzpfms tools
(namely zfs-tpm2-change-key(8), zfs-tpm2-load-key(8), and zfs-tpm2-clear-key(8)).
`tzpfms.key` is an integer representing the sealed object;
if needed, it can be passed to **tpm2_unseal(1) -c ${tzpfms.key}** or equivalent for back-up (see [OPTIONS][]).
If you have a sealed key you can access with that or equivalent tool and set both of these properties, it will funxion seamlessly.
Finally, the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=raw dataset** is performed with the new key.
If an error occurred, best effort is made to clean up the persistent object and properties,
or to issue a note for manual intervention into the standard error stream.
A final verification should be made by running **zfs-tpm2-load-key(8) -n dataset**.
If that command succeeds, all is well,
but otherwise the dataset can be manually rolled back to a password with **zfs-tpm2-clear-key(8) dataset** (or, if that fails to work, **zfs(8) change-key -o keyformat=passphrase dataset**), and you are hereby asked to report a bug, please.
**zfs-tpm2-clear-key(8) dataset** can be used to free the TPM persistent object and go back to using a password.
## OPTIONS
* `-b` *file*:
Save a back-up of the key to *file*, which must not exist beforehand.
This back-up **must** be stored securely, off-site.
In case of a catastrophic event, the key can be loaded by running **zfs(8) load-key dataset < backup-file**.
#include "backend-tpm2.h"
#include "common.h"
## SEE ALSO
&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;

View File

@ -0,0 +1,24 @@
zfs-tpm2-clear-key(8) -- rewrap ZFS dataset key in passsword and clear tzpfms TPM2 metadata
===========================================================================================
## SYNOPSIS
`zfs-tpm2-clear-key` <dataset>
## DESCRIPTION
zfs-tpm2-clear-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM2* will:
1. perform the equivalent of **zfs(8) change-key -o keylocation=prompt -o keyformat=passphrase dataset**,
2. free the sealed key previously used to encrypt `dataset`,
3. remove the `xyz.nabijaczleweli:tzpfms.{backend,key}` properties from `dataset`.
See zfs-tpm2-change-key(8) for a detailed description.
#include "backend-tpm2.h"
#include "common.h"
## SEE ALSO
&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;

View File

@ -0,0 +1,25 @@
zfs-tpm2-load-key(8) -- load tzpfms TPM2-encrypted ZFS dataset key
==================================================================
## SYNOPSIS
`zfs-tpm2-load-key` [-n] <dataset>
## DESCRIPTION
zfs-tpm2-load-key(8), after verifying that `dataset` was encrypted with tzpfms backend *TPM2* will unseal the key and load it into `dataset`.
See zfs-tpm2-change-key(8) for a detailed description.
## OPTIONS
* `-n`:
Do a no-op/dry run, can be used even if the key is already loaded. Equivalent to **zfs(8) load-key**'s `-n` option.
#include "backend-tpm2.h"
#include "common.h"
## SEE ALSO
&lt;<https://git.sr.ht/~nabijaczleweli/tzpfms>&gt;

View File

@ -62,8 +62,8 @@ int main(int argc, char ** argv) {
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
char *previous_backend{}, *previous_handle_s{};
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, previous_backend));
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_KEY, 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);
@ -98,6 +98,9 @@ int main(int argc, char ** argv) {
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));

View File

@ -6,19 +6,32 @@
#include <stdio.h>
#include "../main.hpp"
#include "../tpm2.hpp"
#include "../zfs.hpp"
#define THIS_BACKEND "TPM2"
int main(int argc, char ** argv) {
return do_main(
argc, argv, "", [&](auto) {},
[&](auto dataset) {
REQUIRE_KEY_LOADED(dataset);
TPMI_DH_PERSISTENT persistent_handle{};
TRY_MAIN(parse_key_props(dataset, THIS_BACKEND, persistent_handle));
if(zfs_crypto_rewrap(dataset, TRY_PTR("get clear rewrap args", clear_rewrap_args()), B_FALSE))
return __LINE__; // Error printed by libzfs
if(clear_key_props(dataset)) {
TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) {
TRY_MAIN(tpm2_free_persistent(tpm2_ctx, tpm2_session, persistent_handle));
return 0;
}));
if(clear_key_props(dataset)) { // Sync with zfs-tpm2-change-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));
return __LINE__;

View File

@ -9,7 +9,6 @@
#include "../fd.hpp"
#include "../main.hpp"
#include "../parse.hpp"
#include "../tpm2.hpp"
#include "../zfs.hpp"
@ -22,29 +21,8 @@ int main(int argc, char ** argv) {
return do_main(
argc, argv, "n", [&](auto) { noop = B_TRUE; },
[&](auto dataset) {
char *backend{}, *handle_s{};
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, backend));
if(!backend) {
fprintf(stderr, "Dataset %s not encrypted with tzpfms!\n", zfs_get_name(dataset));
return __LINE__;
}
if(strcmp(backend, THIS_BACKEND)) {
fprintf(stderr, "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n", zfs_get_name(dataset), backend, THIS_BACKEND);
return __LINE__;
}
TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_KEY, handle_s));
if(!handle_s) {
fprintf(stderr, "Dataset %s missing key data.\n", zfs_get_name(dataset));
return __LINE__;
}
TPMI_DH_PERSISTENT handle{};
if(parse_int(handle_s, handle)) {
fprintf(stderr, "Dataset %s's handle %s not valid.\n", zfs_get_name(dataset), handle_s);
return __LINE__;
}
TRY_MAIN(parse_key_props(dataset, THIS_BACKEND, handle));
uint8_t wrap_key[WRAPPING_KEY_LEN];

View File

@ -6,6 +6,7 @@
#include <charconv>
#include <stdio.h>
#include <string.h>
template <class T>

View File

@ -6,6 +6,8 @@
#include "common.hpp"
#include <libzfs.h>
#include <tss2/tss2_common.h>
#include <tss2/tss2_esys.h>
#include <tss2/tss2_rc.h>

View File

@ -3,9 +3,13 @@
#include "zfs.hpp"
#include "common.hpp"
#include "main.hpp"
#include "parse.hpp"
#include <libzfs.h>
#include <string.h>
// Funxion statics pull in libc++'s __cxa_guard_acquire()
static nvlist_t * rrargs{};
@ -59,14 +63,19 @@ nvlist_t * clear_rewrap_args() {
TRY_NVL(what, _try_retl); \
})
// TODO: how does this interact with nested datasets?
int lookup_userprop(nvlist_t * from, const char * name, char *& out) {
int lookup_userprop(zfs_handle_t * in, const char * name, char *& out) {
// xyz.nabijaczleweli:tzpfms.key:
// value: '76B0286BEB3FAF57536C47D9A2BAD38157FD522A75A59E72867BBFD6AF167395'
// source: 'owo/enc'
nvlist_t * vs{};
TRY_LOOKUP("look up user property", nvlist_lookup_nvlist(from, name, &vs));
TRY_LOOKUP("look up user property", nvlist_lookup_nvlist(zfs_get_user_props(in), name, &vs));
char * source{};
TRY_LOOKUP("look up user property source", nvlist_lookup_string(vs, "source", &source));
if(!source || strcmp(source, zfs_get_name(in)))
return 0;
TRY_LOOKUP("look up user property value", nvlist_lookup_string(vs, "value", &out));
return 0;
}
@ -98,3 +107,31 @@ int clear_key_props(zfs_handle_t * from) {
TRY("delete tzpfms.key", zfs_prop_inherit(from, PROPNAME_KEY, B_FALSE));
return 0;
}
int parse_key_props(zfs_handle_t * in, const char * our_backend, uint32_t & handle) {
char *backend{}, *handle_s{};
TRY_MAIN(lookup_userprop(in, PROPNAME_BACKEND, backend));
if(!backend) {
fprintf(stderr, "Dataset %s not encrypted with tzpfms!\n", zfs_get_name(in));
return __LINE__;
}
if(strcmp(backend, our_backend)) {
fprintf(stderr, "Dataset %s encrypted with tzpfms back-end %s, but we are %s.\n", zfs_get_name(in), backend, our_backend);
return __LINE__;
}
TRY_MAIN(lookup_userprop(in, PROPNAME_KEY, handle_s));
if(!handle_s) {
fprintf(stderr, "Dataset %s missing key data.\n", zfs_get_name(in));
return __LINE__;
}
if(parse_int(handle_s, handle)) {
fprintf(stderr, "Dataset %s's handle %s not valid.\n", zfs_get_name(in), handle_s);
return __LINE__;
}
return 0;
}

View File

@ -6,9 +6,6 @@
#include <libzfs.h>
#include <sys/nvpair.h>
// #include <sys/fs/zfs.h>
//// #include <sys/zio_crypt.h>
// #define WRAPPING_KEY_LEN 32
#define TRY_NVL(what, ...) TRY_GENERIC(what, , , _try_ret, _try_ret, strerror, __VA_ARGS__)
@ -36,10 +33,13 @@ extern nvlist_t * clear_rewrap_args();
/// Extract user property name from ZFS property list from to out.
///
/// Returns success but does not touch out on not found.
extern int lookup_userprop(nvlist_t * from, const char * name, char *& out);
extern int lookup_userprop(zfs_handle_t * from, const char * name, char *& out);
/// Set required decoding props on the dataset
extern int set_key_props(zfs_handle_t * on, const char * backend, uint32_t handle);
/// Remove decoding props from the dataset
extern int clear_key_props(zfs_handle_t * from);
/// Read in decoding props from the dataset
extern int parse_key_props(zfs_handle_t * in, const char * our_backend, uint32_t & handle);