diff --git a/src/bin/rewrap.cpp b/src/bin/rewrap.cpp index 65a4ab6..fc8e5ea 100644 --- a/src/bin/rewrap.cpp +++ b/src/bin/rewrap.cpp @@ -5,22 +5,20 @@ // #include #define WRAPPING_KEY_LEN 32 -#include - -#include -#include -#include - #include -#include #include "../fd.hpp" #include "../main.hpp" +#include "../parse.hpp" +#include "../tpm2.hpp" #include "../zfs.hpp" // ./src/swtpm/swtpm socket --server port=2321 --ctrl type=tcp,port=2322 --tpm2 +#define THIS_BACKEND "TPM2" + + template struct slice_iter { T * data; @@ -54,7 +52,6 @@ int main(int argc, char ** argv) { } - ESYS_CONTEXT * tpm2_ctx{}; // https://software.intel.com/content/www/us/en/develop/articles/code-sample-protecting-secret-data-and-keys-using-intel-platform-trust-technology.html // tssstartup // tpm2_createprimary -Q --hierarchy=o --key-context=prim.ctx @@ -66,225 +63,68 @@ int main(int argc, char ** argv) { // // tpm2_unseal -Q --object-context=0x81000000 - - // https://trustedcomputinggroup.org/wp-content/uploads/TSS_ESAPI_v1p00_r05_pubrev.pdf - // mainly "3.4. The ESAPI Session" and "3.5. ESAPI Use Model" - // https://tpm2-tss.readthedocs.io/en/latest/group___e_s_y_s___c_o_n_t_e_x_t.html - fprintf(stderr, "Esys_Initialize() = %s\n", Tss2_RC_Decode(Esys_Initialize(&tpm2_ctx, nullptr, nullptr))); - quickscope_wrapper tpm2_ctx_deleter{[&] { Esys_Finalize(&tpm2_ctx); }}; - - fprintf(stderr, "Esys_Startup() = %s\n", Tss2_RC_Decode(Esys_Startup(tpm2_ctx, TPM2_SU_CLEAR))); - - - ESYS_TR tpm2_session; - quickscope_wrapper tpm2_session_deleter{ - [&] { fprintf(stderr, "Esys_FlushContext(tpm2_session) = %s\n", Tss2_RC_Decode(Esys_FlushContext(tpm2_ctx, tpm2_session))); }}; - - { - // https://github.com/tpm2-software/tpm2-tss/blob/master/test/integration/esys-create-session-auth.int.c#L218 - TPMT_SYM_DEF session_key{}; - session_key.algorithm = TPM2_ALG_AES; - session_key.keyBits.aes = 128; - session_key.mode.aes = TPM2_ALG_CFB; - fprintf(stderr, "Esys_StartAuthSession() = %s\n", - Tss2_RC_Decode(Esys_StartAuthSession(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, nullptr, TPM2_SE_HMAC, - &session_key, TPM2_ALG_SHA512, &tpm2_session))); - } - - - uint8_t wrap_key[WRAPPING_KEY_LEN]; - { - TPM2B_DIGEST * rand{}; - fprintf(stderr, "Esys_GetRandom() = %s\n", - Tss2_RC_Decode(Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, WRAPPING_KEY_LEN, &rand))); - quickscope_wrapper rand_deleter{[=] { Esys_Free(rand); }}; - if(rand->size != WRAPPING_KEY_LEN) - fprintf(stderr, "Wrong random size: %d\n", rand->size); - memcpy(wrap_key, rand->buffer, WRAPPING_KEY_LEN); - } - - ESYS_TR primary_handle = ESYS_TR_NONE; - quickscope_wrapper primary_handle_deleter{ - [&] { fprintf(stderr, "Esys_FlushContext(primary_handle) = %s\n", Tss2_RC_Decode(Esys_FlushContext(tpm2_ctx, primary_handle))); }}; - - TPM2B_DATA metadata{}; - const auto now = time(nullptr); - const auto now_tm = localtime(&now); - metadata.size = snprintf((char *)metadata.buffer, sizeof(metadata.buffer), "%s %d-%02d-%02dT%02d:%02d:%02d %s", zfs_get_name(dataset), // - now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, // - TZPFMS_VERSION) + - 1; - metadata.size = metadata.size > sizeof(metadata.buffer) ? sizeof(metadata.buffer) : metadata.size; - fprintf(stderr, "%d/%zu: \"%s\"\n", metadata.size, sizeof(metadata.buffer), metadata.buffer); - - { - const TPM2B_SENSITIVE_CREATE primary_sens{}; - - // Adapted from tpm2-tss-3.0.1/test/integration/esys-create-primary-hmac.int.c - TPM2B_PUBLIC pub{}; - pub.publicArea.type = TPM2_ALG_RSA; - pub.publicArea.nameAlg = TPM2_ALG_SHA1; - pub.publicArea.objectAttributes = TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_RESTRICTED | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM | - TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN; - pub.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM2_ALG_AES; - pub.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; - pub.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM2_ALG_CFB; - pub.publicArea.parameters.rsaDetail.scheme.scheme = TPM2_ALG_NULL; - pub.publicArea.parameters.rsaDetail.keyBits = 2048; - pub.publicArea.parameters.rsaDetail.exponent = 0; - - const TPML_PCR_SELECTION pcrs{}; - - TPM2B_PUBLIC * public_ret{}; - TPM2B_CREATION_DATA * creation_data{}; - TPM2B_DIGEST * creation_hash{}; - TPMT_TK_CREATION * creation_ticket{}; - fprintf(stderr, "Esys_CreatePrimary() = %s\n", - Tss2_RC_Decode(Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata, &pcrs, - &primary_handle, &public_ret, &creation_data, &creation_hash, &creation_ticket))); - quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }}; - quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }}; - quickscope_wrapper creation_data_deleter{[=] { Esys_Free(creation_data); }}; - quickscope_wrapper public_ret_deleter{[=] { Esys_Free(public_ret); }}; - - // TSS2_RC Esys_CertifyCreation ( ESYS_CONTEXT * esysContext, - // ESYS_TR signHandle, - // ESYS_TR objectHandle, - // ESYS_TR shandle1, - // ESYS_TR shandle2, - // ESYS_TR shandle3, - // const TPM2B_DATA * qualifyingData, - // const TPM2B_DIGEST * creationHash, - // const TPMT_SIG_SCHEME * inScheme, - // const TPMT_TK_CREATION * creationTicket, - // TPM2B_ATTEST ** certifyInfo, - // TPMT_SIGNATURE ** signature - // ) - } - - - TPM2B_PRIVATE * sealant_private{}; - TPM2B_PUBLIC * sealant_public{}; - quickscope_wrapper sealant_public_deleter{[=] { Esys_Free(sealant_public); }}; - quickscope_wrapper sealant_private_deleter{[=] { Esys_Free(sealant_private); }}; - - /// This is the object with the actual sealed data in it - { - TPM2B_SENSITIVE_CREATE secret_sens{}; - secret_sens.sensitive.data.size = sizeof(wrap_key); - memcpy(secret_sens.sensitive.data.buffer, wrap_key, secret_sens.sensitive.data.size); - - // Same args as tpm2-tools' tpm2_create(1) - TPM2B_PUBLIC pub{}; - pub.publicArea.type = TPM2_ALG_KEYEDHASH; - pub.publicArea.nameAlg = TPM2_ALG_SHA256; - pub.publicArea.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_USERWITHAUTH; - pub.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL; - - const TPML_PCR_SELECTION pcrs{}; - - TPM2B_CREATION_DATA * creation_data{}; - TPM2B_DIGEST * creation_hash{}; - TPMT_TK_CREATION * creation_ticket{}; - fprintf(stderr, "Esys_Create() = %s\n", - Tss2_RC_Decode(Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs, - &sealant_private, &sealant_public, &creation_data, &creation_hash, &creation_ticket))); - quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }}; - quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }}; - quickscope_wrapper creation_data_deleter{[=] { Esys_Free(creation_data); }}; - } - - ESYS_TR sealed_handle = ESYS_TR_NONE; - quickscope_wrapper sealed_handle_deleter{ - [&] { fprintf(stderr, "Esys_FlushContext(sealed_handle) = %s\n", Tss2_RC_Decode(Esys_FlushContext(tpm2_ctx, sealed_handle))); }}; - - /// Load the keyedhash/sealed object into a transient handle - { - fprintf( - stderr, "Esys_Load() = %s\n", - Tss2_RC_Decode(Esys_Load(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, sealant_private, sealant_public, &sealed_handle))); - } - - TPMI_DH_PERSISTENT persistent_handle{}; - - /// Find lowest unused persistent handle - { - TPMS_CAPABILITY_DATA * cap; // TODO: check for more data? - fprintf(stderr, "Esys_GetCapability() = %s\n", - Tss2_RC_Decode(Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST, - TPM2_MAX_CAP_HANDLES, nullptr, &cap))); - quickscope_wrapper cap_deleter{[=] { Esys_Free(cap); }}; - - switch(cap->data.handles.count) { - case 0: - persistent_handle = TPM2_PERSISTENT_FIRST; - break; - case TPM2_MAX_CAP_HANDLES: - break; - default: - for(TPM2_HC i = TPM2_PERSISTENT_FIRST; i <= TPM2_PERSISTENT_LAST && i <= TPM2_PLATFORM_PERSISTENT; ++i) - if(std::find(std::begin(cap->data.handles.handle), std::end(cap->data.handles.handle), i) == std::end(cap->data.handles.handle)) { - persistent_handle = i; - break; - } + 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)); + 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); + } + } } - if(!persistent_handle) - fprintf(stderr, "All %zu persistent handles allocated! We're fucked!\n", TPM2_MAX_CAP_HANDLES); - } + uint8_t wrap_key[WRAPPING_KEY_LEN]; + TPMI_DH_PERSISTENT persistent_handle{}; - fprintf(stderr, "0x%x\n", 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)); - /// Persist the loaded handle in the TPM — this will make it available as $persistent_handle until we explicitly evict it back to the transient store - { - // Can't be flushed (tpm:parameter(1):value is out of range or is not correct for the context), plus, that's kinda the point - ESYS_TR new_handle; - fprintf(stderr, "Esys_EvictControl() = %s\n", - Tss2_RC_Decode( - Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, sealed_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, persistent_handle, &new_handle))); - } + 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); + }}; - /// Free the persistent slot - /*{ - // Neither of these are flushable (tpm:parameter(1):value is out of range or is not correct for the context) - ESYS_TR pandle; - fprintf(stderr, "Esys_TR_FromTPMPublic() = %s\n", - Tss2_RC_Decode(Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle - 1, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle))); + TRY_MAIN(set_key_props(dataset, THIS_BACKEND, persistent_handle)); - ESYS_TR new_handle; - fprintf(stderr, "Esys_EvictControl() = %s\n", - Tss2_RC_Decode(Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_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); }}; - // uint8_t wrap_key[WRAPPING_KEY_LEN]; - // TRY_MAIN(read_exact("/dev/random", wrap_key, sizeof(wrap_key))); - if(backup) - TRY_MAIN(write_exact(backup, wrap_key, sizeof(wrap_key), 0400)); + 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)); - char persistent_handle_s[2 + sizeof(persistent_handle) * 2 + 1]; - if(auto written = snprintf(persistent_handle_s, sizeof(persistent_handle_s), "0x%02X", persistent_handle); - written < 0 || written >= static_cast(sizeof(persistent_handle_s))) - fprintf(stderr, "oops, truncated: %d/%zu\n", written, sizeof(persistent_handle_s)); - TRY_MAIN(zfs_prop_set(dataset, "xyz.nabijaczleweli:tzpfms.key", persistent_handle_s)); - - - /// 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; })); diff --git a/src/bin/unlock.cpp b/src/bin/unlock.cpp index 6f06e66..022f755 100644 --- a/src/bin/unlock.cpp +++ b/src/bin/unlock.cpp @@ -2,59 +2,19 @@ #include +// #include +#define WRAPPING_KEY_LEN 32 #include #include "../fd.hpp" #include "../main.hpp" +#include "../parse.hpp" +#include "../tpm2.hpp" #include "../zfs.hpp" -static int hex_nibble(char c) { - switch(c) { - case '0': - return 0x0; - case '1': - return 0x1; - case '2': - return 0x2; - case '3': - return 0x3; - case '4': - return 0x4; - case '5': - return 0x5; - case '6': - return 0x6; - case '7': - return 0x7; - case '8': - return 0x8; - case '9': - return 0x9; - case 'A': - case 'a': - return 0xA; - case 'B': - case 'b': - return 0xB; - case 'C': - case 'c': - return 0xC; - case 'D': - case 'd': - return 0xD; - case 'E': - case 'e': - return 0xE; - case 'F': - case 'f': - return 0xF; - default: - fprintf(stderr, "Character %c (0x%02X) not hex?\n", c, static_cast(c)); - return 0; - } -} +#define THIS_BACKEND "TPM2" int main(int argc, char ** argv) { @@ -62,23 +22,41 @@ int main(int argc, char ** argv) { return do_main( argc, argv, "n", [&](auto) { noop = B_TRUE; }, [&](auto dataset) { - char * stored_key_s{}; - TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), "xyz.nabijaczleweli:tzpfms.key", stored_key_s)); - errno = EEXIST; - TRY_PTR("find encryption key prop", stored_key_s); + char *backend{}, *handle_s{}; + TRY_MAIN(lookup_userprop(zfs_get_user_props(dataset), PROPNAME_BACKEND, backend)); + fprintf(stderr, "backend=%s\n", backend); - auto stored_key_len = strlen(stored_key_s) / 2; - auto stored_key = static_cast(TRY_PTR("stored_key", alloca(stored_key_len))); - { - auto cur = stored_key_s; - for(auto kcur = stored_key; kcur < stored_key + stored_key_len; ++kcur) { - *kcur = (hex_nibble(cur[0]) << 4) | hex_nibble(cur[1]); - cur += 2; - } + 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__; + } + + + uint8_t wrap_key[WRAPPING_KEY_LEN]; + TRY_MAIN(with_tpm2_session([&](auto tpm2_ctx, auto tpm2_session) { + TRY_MAIN(tpm2_unseal(tpm2_ctx, tpm2_session, handle, wrap_key, sizeof(wrap_key))); + return 0; + })); + + int key_fd; - TRY_MAIN(filled_fd(key_fd, (void *)stored_key, stored_key_len)); + TRY_MAIN(filled_fd(key_fd, (void *)wrap_key, sizeof(wrap_key))); quickscope_wrapper key_fd_deleter{[=] { close(key_fd); }}; @@ -86,7 +64,7 @@ int main(int argc, char ** argv) { if(zfs_crypto_load_key(dataset, noop, nullptr)) return __LINE__; // Error printed by libzfs else - printf("Key for %s loaded\n", zfs_get_name(dataset)); + printf("Key for %s %s\n", zfs_get_name(dataset), noop ? "OK" : "loaded"); return 0; })); diff --git a/src/common.hpp b/src/common.hpp index 7addb8e..d4b5ed9 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -9,17 +9,17 @@ #include -#define TRY_GENERIC(what, cond_pre, cond_post, err_src, err_ret, ...) \ - ({ \ - auto _try_ret = (__VA_ARGS__); \ - if(cond_pre _try_ret cond_post) { \ - if constexpr(what != nullptr) \ - fprintf(stderr, "Couldn't %s: %s\n", static_cast(what), strerror(err_src)); \ - return err_ret; \ - } \ - _try_ret; \ +#define TRY_GENERIC(what, cond_pre, cond_post, err_src, err_ret, strerr, ...) \ + ({ \ + auto _try_ret = (__VA_ARGS__); \ + if(cond_pre _try_ret cond_post) { \ + if constexpr(what != nullptr) \ + fprintf(stderr, "Couldn't %s: %s\n", static_cast(what), strerr(err_src)); \ + return err_ret; \ + } \ + _try_ret; \ }) -#define TRY(what, ...) TRY_GENERIC(what, , == -1, errno, __LINE__, __VA_ARGS__) +#define TRY(what, ...) TRY_GENERIC(what, , == -1, errno, __LINE__, strerror, __VA_ARGS__) template diff --git a/src/fd.cpp b/src/fd.cpp index e83560d..5ce1095 100644 --- a/src/fd.cpp +++ b/src/fd.cpp @@ -44,7 +44,7 @@ int read_exact(const char * path, void * data, size_t len) { int write_exact(const char * path, const void * data, size_t len, mode_t mode) { - auto outfd = TRY("open output file", open(path, O_WRONLY | O_CREAT | O_EXCL, mode)); + auto outfd = TRY("create output file", open(path, O_WRONLY | O_CREAT | O_EXCL, mode)); quickscope_wrapper infd_deleter{[=] { close(outfd); }}; while(len) { diff --git a/src/main.hpp b/src/main.hpp index a80ee4d..f9adddb 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -10,7 +10,7 @@ #include -#define TRY_PTR(what, ...) TRY_GENERIC(what, !, , errno, __LINE__, __VA_ARGS__) +#define TRY_PTR(what, ...) TRY_GENERIC(what, !, , errno, __LINE__, strerror, __VA_ARGS__) #define TRY_MAIN(...) \ do { \ if(auto _try_ret = (__VA_ARGS__)) \ diff --git a/src/tpm2.cpp b/src/tpm2.cpp index a9c8216..ea34389 100644 --- a/src/tpm2.cpp +++ b/src/tpm2.cpp @@ -26,7 +26,7 @@ TPM2B_DATA tpm2_creation_metadata(const char * dataset_name) { int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) { TPM2B_DIGEST * rand{}; - TRY_TPM2("Esys_GetRandom()", Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, length, &rand)); + TRY_TPM2("get random data from TPM", Esys_GetRandom(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, length, &rand)); quickscope_wrapper rand_deleter{[=] { Esys_Free(rand); }}; if(rand->size != length) { @@ -41,7 +41,7 @@ int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length) { static int tpm2_find_unused_persistent_non_platform(ESYS_CONTEXT * tpm2_ctx, TPMI_DH_PERSISTENT & persistent_handle) { TPMS_CAPABILITY_DATA * cap; // TODO: check for more data? - TRY_TPM2("Esys_GetCapability()", Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST, + TRY_TPM2("Read used persistent TPM handles", Esys_GetCapability(tpm2_ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_CAP_HANDLES, TPM2_PERSISTENT_FIRST, TPM2_MAX_CAP_HANDLES, nullptr, &cap)); quickscope_wrapper cap_deleter{[=] { Esys_Free(cap); }}; @@ -94,7 +94,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT TPM2B_CREATION_DATA * creation_data{}; TPM2B_DIGEST * creation_hash{}; TPMT_TK_CREATION * creation_ticket{}; - TRY_TPM2("Esys_CreatePrimary()", Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata, + TRY_TPM2("create primary encryption key", Esys_CreatePrimary(tpm2_ctx, ESYS_TR_RH_OWNER, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &primary_sens, &pub, &metadata, &pcrs, &primary_handle, &public_ret, &creation_data, &creation_hash, &creation_ticket)); quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }}; quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }}; @@ -140,7 +140,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT TPM2B_CREATION_DATA * creation_data{}; TPM2B_DIGEST * creation_hash{}; TPMT_TK_CREATION * creation_ticket{}; - TRY_TPM2("Esys_Create()", Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs, + TRY_TPM2("create key seal", Esys_Create(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &secret_sens, &pub, &metadata, &pcrs, &sealant_private, &sealant_public, &creation_data, &creation_hash, &creation_ticket)); quickscope_wrapper creation_ticket_deleter{[=] { Esys_Free(creation_ticket); }}; quickscope_wrapper creation_hash_deleter{[=] { Esys_Free(creation_hash); }}; @@ -151,7 +151,7 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT quickscope_wrapper sealed_handle_deleter{[&] { Esys_FlushContext(tpm2_ctx, sealed_handle); }}; /// Load the sealed object (keyedhash) into a transient handle - TRY_TPM2("Esys_Load()", Esys_Load(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, sealant_private, sealant_public, &sealed_handle)); + TRY_TPM2("load key seal", Esys_Load(tpm2_ctx, primary_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, sealant_private, sealant_public, &sealed_handle)); /// Find lowest unused persistent handle TRY_MAIN(tpm2_find_unused_persistent_non_platform(tpm2_ctx, persistent_handle)); @@ -160,20 +160,37 @@ int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT { // Can't be flushed (tpm:parameter(1):value is out of range or is not correct for the context), plus, that's kinda the point ESYS_TR new_handle; - TRY_TPM2("Esys_EvictControl()", + TRY_TPM2("persist key seal", Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, sealed_handle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, persistent_handle, &new_handle)); } return 0; } +int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, void * data, size_t data_len) { + // Entirely fake and not flushable (tpm:parameter(1):value is out of range or is not correct for the context) + ESYS_TR pandle; + TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle)); + + TPM2B_SENSITIVE_DATA * unsealed{}; + TRY_TPM2("unseal data", Esys_Unseal(tpm2_ctx, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, &unsealed)); + quickscope_wrapper unsealed_deleter{[=] { Esys_Free(unsealed); }}; + + if(unsealed->size != data_len) { + fprintf(stderr, "Unsealed data has wrong length %u, expected %zu!\n", unsealed->size, data_len); + return __LINE__; + } + memcpy(data, unsealed->buffer, data_len); + return 0; +} + int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle) { // Neither of these are flushable (tpm:parameter(1):value is out of range or is not correct for the context) ESYS_TR pandle; - TRY_TPM2("Esys_TR_FromTPMPublic()", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle)); + TRY_TPM2("convert persistent handle to object", Esys_TR_FromTPMPublic(tpm2_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &pandle)); ESYS_TR new_handle; - TRY_TPM2("Esys_EvictControl()", Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle)); + TRY_TPM2("unpersist object", Esys_EvictControl(tpm2_ctx, ESYS_TR_RH_OWNER, pandle, tpm2_session, ESYS_TR_NONE, ESYS_TR_NONE, 0, &new_handle)); return 0; } diff --git a/src/tpm2.hpp b/src/tpm2.hpp index 0053571..9fa7b47 100644 --- a/src/tpm2.hpp +++ b/src/tpm2.hpp @@ -46,4 +46,5 @@ extern TPM2B_DATA tpm2_creation_metadata(const char * dataset_name); extern int tpm2_generate_rand(ESYS_CONTEXT * tpm2_ctx, void * into, size_t length); extern int tpm2_seal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT & persistent_handle, const TPM2B_DATA & metadata, void * data, size_t data_len); +extern int tpm2_unseal(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle, void * data, size_t data_len); extern int tpm2_free_persistent(ESYS_CONTEXT * tpm2_ctx, ESYS_TR tpm2_session, TPMI_DH_PERSISTENT persistent_handle); diff --git a/src/zfs.cpp b/src/zfs.cpp index 6da224d..02bfa21 100644 --- a/src/zfs.cpp +++ b/src/zfs.cpp @@ -9,7 +9,7 @@ // Funxion statics pull in libc++'s __cxa_guard_acquire() static nvlist_t * rrargs{}; -static quickscope_wrapper ret_deleter{[] { nvlist_free(rrargs); }}; +static quickscope_wrapper rrargs_deleter{[] { nvlist_free(rrargs); }}; nvlist_t * rewrap_args() { if(!rrargs) if(auto err = @@ -22,8 +22,8 @@ nvlist_t * rewrap_args() { }(); err && rrargs) { nvlist_free(rrargs); - rrargs = nullptr; - errno = err; + rrargs = nullptr; + errno = err; } return rrargs; @@ -48,3 +48,24 @@ int lookup_userprop(nvlist_t * from, const char * name, char *& out) { TRY_LOOKUP("look up user property value", nvlist_lookup_string(vs, "value", &out)); return 0; } + + +int set_key_props(zfs_handle_t * on, const char * backend, uint32_t handle) { + char handle_s[2 + sizeof(handle) * 2 + 1]; + if(auto written = snprintf(handle_s, sizeof(handle_s), "0x%02X", handle); written < 0 || written >= static_cast(sizeof(handle_s))) { + fprintf(stderr, "Truncated handle name? %d/%zu\n", written, sizeof(handle_s)); + return __LINE__; + } + + + nvlist_t * props{}; + quickscope_wrapper props_deleter{[&] { nvlist_free(props); }}; + + TRY_NVL("allocate key nvlist", nvlist_alloc(&props, NV_UNIQUE_NAME, 0)); + TRY_NVL("add back-end to key nvlist", nvlist_add_string(props, PROPNAME_BACKEND, backend)); + TRY_NVL("add handle to key nvlist", nvlist_add_string(props, PROPNAME_KEY, handle_s)); + + TRY("set tzpfms.{backend,key}", zfs_prop_set_list(on, props)); + + return 0; +} diff --git a/src/zfs.hpp b/src/zfs.hpp index 56fca26..dc6f504 100644 --- a/src/zfs.hpp +++ b/src/zfs.hpp @@ -4,14 +4,18 @@ #pragma once +#include #include -// #include // #include //// #include // #define WRAPPING_KEY_LEN 32 -#define TRY_NVL(what, ...) TRY_GENERIC(what, , , _try_ret, _try_ret, __VA_ARGS__) +#define TRY_NVL(what, ...) TRY_GENERIC(what, , , _try_ret, _try_ret, strerror, __VA_ARGS__) + + +#define PROPNAME_BACKEND "xyz.nabijaczleweli:tzpfms.backend" +#define PROPNAME_KEY "xyz.nabijaczleweli:tzpfms.key" /// Static nvlist with {keyformat=raw, keylocation=prompt} @@ -20,4 +24,7 @@ extern nvlist_t * 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(nvlist_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);