[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v23 24/33] cryptodisk: Support key protectors
From: |
Gary Lin |
Subject: |
[PATCH v23 24/33] cryptodisk: Support key protectors |
Date: |
Fri, 15 Nov 2024 15:34:52 +0800 |
From: Hernan Gatta <hegatta@linux.microsoft.com>
Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.
Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Glenn Washburn <development@efficientek.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
Makefile.util.def | 1 +
docs/grub.texi | 12 +-
grub-core/disk/cryptodisk.c | 243 ++++++++++++++++++++++++++----------
include/grub/cryptodisk.h | 16 +++
4 files changed, 203 insertions(+), 69 deletions(-)
diff --git a/Makefile.util.def b/Makefile.util.def
index fe70cf9bd..fb82f59a0 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
common = grub-core/disk/luks.c;
common = grub-core/disk/luks2.c;
common = grub-core/disk/geli.c;
+ common = grub-core/disk/key_protector.c;
common = grub-core/disk/cryptodisk.c;
common = grub-core/disk/AFSplitter.c;
common = grub-core/lib/pbkdf2.c;
diff --git a/docs/grub.texi b/docs/grub.texi
index 78d47f467..19937f1c9 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6684,16 +6684,18 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See
command @command{hashsum}
@node cryptomount
@subsection cryptomount
-@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile
[@option{-O} keyoffset] [@option{-S} keysize] ] ] [@option{-H} file]
device|@option{-u} uuid|@option{-a}|@option{-b}
+@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile
[@option{-O} keyoffset] [@option{-S} keysize] ] | [@option{-P} protector] ]
[@option{-H} file] device|@option{-u} uuid|@option{-a}|@option{-b}
Setup access to encrypted device. A passphrase will be requested interactively,
if neither the @option{-p} nor @option{-k} options are given. The option
@option{-p} can be used to supply a passphrase (useful for scripts).
Alternatively the @option{-k} option can be used to supply a keyfile with
options @option{-O} and @option{-S} optionally supplying the offset and size,
-respectively, of the key data in the given key file. The @option{-H} options
can
-be used to supply cryptomount backends with an alternative header file (aka
-detached header). Not all backends have headers nor support alternative header
-files (currently only LUKS1 and LUKS2 support them).
+respectively, of the key data in the given key file. Besides the keyfile,
+the key can be stored in a key protector, and option @option{-P} configures
+specific key protector, e.g. tpm2, to retrieve the key from.
+The @option{-H} options can be used to supply cryptomount backends with an
+alternative header file (aka detached header). Not all backends have headers
+nor support alternative header files (currently only LUKS1 and LUKS2 support
them).
Argument @var{device} configures specific grub device
(@pxref{Naming convention}); option @option{-u} @var{uuid} configures device
with specified @var{uuid}; option @option{-a} configures all detected encrypted
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 5e1eb2743..6f7394942 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -27,6 +27,7 @@
#include <grub/file.h>
#include <grub/procfs.h>
#include <grub/partition.h>
+#include <grub/key_protector.h>
#ifdef GRUB_UTIL
#include <grub/emu/hostdisk.h>
@@ -45,7 +46,8 @@ enum
OPTION_KEYFILE,
OPTION_KEYFILE_OFFSET,
OPTION_KEYFILE_SIZE,
- OPTION_HEADER
+ OPTION_HEADER,
+ OPTION_PROTECTOR
};
static const struct grub_arg_option options[] =
@@ -59,6 +61,8 @@ static const struct grub_arg_option options[] =
{"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT},
{"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0,
ARG_TYPE_INT},
{"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+ {"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
{0, 0, 0, 0, 0, 0}
};
@@ -1062,6 +1066,7 @@ grub_cryptodisk_scan_device_real (const char *name,
grub_err_t ret = GRUB_ERR_NONE;
grub_cryptodisk_t dev;
grub_cryptodisk_dev_t cr;
+ int i;
struct cryptodisk_read_hook_ctx read_hook_data = {0};
int askpass = 0;
char *part = NULL;
@@ -1114,79 +1119,151 @@ grub_cryptodisk_scan_device_real (const char *name,
goto error_no_close;
if (!dev)
continue;
+ break;
+ }
- if (cargs->key_len)
- {
- ret = cr->recover_key (source, dev, cargs);
- if (ret != GRUB_ERR_NONE)
- goto error;
- }
- else
- {
- /* Get the passphrase from the user, if no key data. */
- unsigned long tries = 3;
- const char *tries_env;
+ if (dev == NULL)
+ {
+ grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+ goto error_no_close;
+ }
- askpass = 1;
- cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
- if (cargs->key_data == NULL)
- goto error_no_close;
+ if (cargs->protectors)
+ {
+ for (i = 0; cargs->protectors[i]; i++)
+ {
+ if (cargs->key_cache[i].invalid)
+ continue;
- tries_env = grub_env_get ("cryptodisk_passphrase_tries");
- if (tries_env != NULL && tries_env[0] != '\0')
- {
- unsigned long tries_env_val;
- const char *p;
+ if (cargs->key_cache[i].key == NULL)
+ {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+ &cargs->key_cache[i].key,
+
&cargs->key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+ {
+ if (grub_errno)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ grub_dprintf ("cryptodisk",
+ "failed to recover a key from key protector "
+ "%s, will not try it again for any other "
+ "disks, if any, during this invocation of "
+ "cryptomount\n",
+ cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+ }
+ }
- tries_env_val = grub_strtoul (tries_env, &p, 0);
- if (*p == '\0' && tries_env_val != ~0UL)
- tries = tries_env_val;
- else
- grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'.
Defaulting to %lu.\n"),
- tries_env,
- tries);
- }
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
- for (; tries > 0; tries--)
- {
- part = grub_partition_get_name (source->partition);
- grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "),
source->name,
- source->partition != NULL ? "," : "",
- part != NULL ? part : N_("UNKNOWN"),
- dev->uuid);
- grub_free (part);
-
- if (!grub_password_get ((char *) cargs->key_data,
GRUB_CRYPTODISK_MAX_PASSPHRASE))
- {
- grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied");
+ ret = cr->recover_key (source, dev, cargs);
+ if (ret != GRUB_ERR_NONE)
+ {
+ part = grub_partition_get_name (source->partition);
+ grub_dprintf ("cryptodisk",
+ "recovered a key from key protector %s but it "
+ "failed to unlock %s%s%s (%s)\n",
+ cargs->protectors[i], source->name,
+ source->partition != NULL ? "," : "",
+ part != NULL ? part : N_("UNKNOWN"), dev->uuid);
+ grub_free (part);
+ continue;
+ }
+ else
+ {
+ ret = grub_cryptodisk_insert (dev, name, source);
+ if (ret != GRUB_ERR_NONE)
goto error;
- }
- cargs->key_len = grub_strlen ((char *) cargs->key_data);
+ goto cleanup;
+ }
+ }
+
+ part = grub_partition_get_name (source->partition);
+ grub_error (GRUB_ERR_ACCESS_DENIED,
+ N_("no key protector provided a usable key for %s%s%s (%s)"),
+ source->name, source->partition != NULL ? "," : "",
+ part != NULL ? part : N_("UNKNOWN"), dev->uuid);
+ grub_free (part);
+ goto error;
+ }
+
+ if (cargs->key_len)
+ {
+ ret = cr->recover_key (source, dev, cargs);
+ if (ret != GRUB_ERR_NONE)
+ goto error;
+ }
+ else
+ {
+ /* Get the passphrase from the user, if no key data. */
+ unsigned long tries = 3;
+ const char *tries_env;
- ret = cr->recover_key (source, dev, cargs);
- if (ret == GRUB_ERR_NONE)
- break;
- if (ret != GRUB_ERR_ACCESS_DENIED || tries == 1)
+ askpass = 1;
+ cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
+ if (cargs->key_data == NULL)
+ goto error_no_close;
+
+ tries_env = grub_env_get ("cryptodisk_passphrase_tries");
+ if (tries_env != NULL && tries_env[0] != '\0')
+ {
+ unsigned long tries_env_val;
+ const char *p;
+
+ tries_env_val = grub_strtoul (tries_env, &p, 0);
+ if (*p == '\0' && tries_env_val != ~0UL)
+ tries = tries_env_val;
+ else
+ grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'.
Defaulting to %lu.\n"),
+ tries_env,
+ tries);
+ }
+
+ for (; tries > 0; tries--)
+ {
+ part = grub_partition_get_name (source->partition);
+ grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
+ source->partition != NULL ? "," : "",
+ part != NULL ? part : N_("UNKNOWN"),
+ dev->uuid);
+ grub_free (part);
+
+ if (!grub_password_get ((char *) cargs->key_data,
GRUB_CRYPTODISK_MAX_PASSPHRASE))
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied");
goto error;
- grub_puts_ (N_("Invalid passphrase."));
-
- /*
- * Since recover_key() calls a function that returns grub_errno,
- * a leftover error value from a previously rejected passphrase
- * will trigger a phantom failure. We therefore clear it before
- * trying a new passphrase.
- */
- grub_errno = GRUB_ERR_NONE;
- }
- }
+ }
+ cargs->key_len = grub_strlen ((char *) cargs->key_data);
- ret = grub_cryptodisk_insert (dev, name, source);
- if (ret != GRUB_ERR_NONE)
- goto error;
+ ret = cr->recover_key (source, dev, cargs);
+ if (ret == GRUB_ERR_NONE)
+ break;
+ if (ret != GRUB_ERR_ACCESS_DENIED || tries == 1)
+ goto error;
+ grub_puts_ (N_("Invalid passphrase."));
+
+ /*
+ * Since recover_key() calls a function that returns grub_errno,
+ * a leftover error value from a previously rejected passphrase
+ * will trigger a phantom failure. We therefore clear it before
+ * trying a new passphrase.
+ */
+ grub_errno = GRUB_ERR_NONE;
+ }
+ }
+
+ ret = grub_cryptodisk_insert (dev, name, source);
+ if (ret != GRUB_ERR_NONE)
+ goto error;
- goto cleanup;
- }
- grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this
device");
goto cleanup;
error:
@@ -1298,6 +1375,20 @@ grub_cryptodisk_scan_device (const char *name,
return ret;
}
+static void
+grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs)
+{
+ int i;
+
+ if (cargs->key_cache == NULL || cargs->protectors == NULL)
+ return;
+
+ for (i = 0; cargs->protectors[i]; i++)
+ grub_free (cargs->key_cache[i].key);
+
+ grub_free (cargs->key_cache);
+}
+
static grub_err_t
grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
{
@@ -1310,6 +1401,14 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
if (grub_cryptodisk_list == NULL)
return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded");
+ if (state[OPTION_PASSWORD].set && state[OPTION_PROTECTOR].set) /* password
and key protector */
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a password and a key protector cannot both be set");
+
+ if (state[OPTION_KEYFILE].set && state[OPTION_PROTECTOR].set) /* key file
and key protector */
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "a key file and a key protector cannot both be set");
+
if (state[OPTION_PASSWORD].set) /* password */
{
cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg;
@@ -1402,6 +1501,15 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
return grub_errno;
}
+ if (state[OPTION_PROTECTOR].set) /* key protector(s) */
+ {
+ cargs.key_cache = grub_zalloc (state[OPTION_PROTECTOR].set * sizeof
(*cargs.key_cache));
+ if (cargs.key_cache == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "no memory for key protector key cache");
+ cargs.protectors = state[OPTION_PROTECTOR].args;
+ }
+
if (state[OPTION_UUID].set) /* uuid */
{
int found_uuid;
@@ -1410,6 +1518,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
dev = grub_cryptodisk_get_by_uuid (args[0]);
if (dev)
{
+ grub_cryptodisk_clear_key_cache (&cargs);
grub_dprintf ("cryptodisk",
"already mounted as crypto%lu\n", dev->id);
return GRUB_ERR_NONE;
@@ -1418,6 +1527,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
cargs.check_boot = state[OPTION_BOOT].set;
cargs.search_uuid = args[0];
found_uuid = grub_device_iterate (&grub_cryptodisk_scan_device, &cargs);
+ grub_cryptodisk_clear_key_cache (&cargs);
if (found_uuid)
return GRUB_ERR_NONE;
@@ -1437,6 +1547,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
{
cargs.check_boot = state[OPTION_BOOT].set;
grub_device_iterate (&grub_cryptodisk_scan_device, &cargs);
+ grub_cryptodisk_clear_key_cache (&cargs);
return GRUB_ERR_NONE;
}
else
@@ -1460,6 +1571,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
disk = grub_disk_open (diskname);
if (!disk)
{
+ grub_cryptodisk_clear_key_cache (&cargs);
if (disklast)
*disklast = ')';
return grub_errno;
@@ -1470,12 +1582,14 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int
argc, char **args)
{
grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n",
dev->id);
grub_disk_close (disk);
+ grub_cryptodisk_clear_key_cache (&cargs);
if (disklast)
*disklast = ')';
return GRUB_ERR_NONE;
}
dev = grub_cryptodisk_scan_device_real (diskname, disk, &cargs);
+ grub_cryptodisk_clear_key_cache (&cargs);
grub_disk_close (disk);
if (disklast)
@@ -1629,6 +1743,7 @@ GRUB_MOD_INIT (cryptodisk)
cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
N_("[ [-p password] | [-k keyfile"
" [-O keyoffset] [-S keysize] ] ] [-H file]"
+ " [-P protector [-P protector ...]]"
" <SOURCE|-u UUID|-a|-b>"),
N_("Mount a crypto device."), options);
grub_procfs_register ("luks_script", &luks_script);
diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h
index d94df68b6..59b461e7a 100644
--- a/include/grub/cryptodisk.h
+++ b/include/grub/cryptodisk.h
@@ -70,6 +70,18 @@ typedef gcry_err_code_t
(*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev,
grub_uint64_t zoneno);
+struct grub_cryptomount_cached_key
+{
+ grub_uint8_t *key;
+ grub_size_t key_len;
+
+ /*
+ * The key protector associated with this cache entry failed, so avoid it
+ * even if the cached entry (an instance of this structure) is empty.
+ */
+ bool invalid;
+};
+
struct grub_cryptomount_args
{
/* scan: Flag to indicate that only bootable volumes should be decrypted */
@@ -81,6 +93,10 @@ struct grub_cryptomount_args
/* recover_key: Length of key_data */
grub_size_t key_len;
grub_file_t hdr_file;
+ /* recover_key: Names of the key protectors to use (NULL-terminated) */
+ char **protectors;
+ /* recover_key: Key cache to avoid invoking the same key protector twice */
+ struct grub_cryptomount_cached_key *key_cache;
};
typedef struct grub_cryptomount_args *grub_cryptomount_args_t;
--
2.43.0
- [PATCH v23 17/33] asn1_test: test module for libtasn1, (continued)
- [PATCH v23 17/33] asn1_test: test module for libtasn1, Gary Lin, 2024/11/15
- [PATCH v23 18/33] libtasn1: Add the documentation, Gary Lin, 2024/11/15
- [PATCH v23 19/33] key_protector: Add key protectors framework, Gary Lin, 2024/11/15
- [PATCH v23 20/33] tss2: Add TPM2 buffer handling functions, Gary Lin, 2024/11/15
- [PATCH v23 21/33] tss2: Add TPM2 types and Marshal/Unmarshal functions, Gary Lin, 2024/11/15
- [PATCH v23 25/33] util/grub-protect: Add new tool, Gary Lin, 2024/11/15
- [PATCH v23 28/33] cryptodisk: Fallback to passphrase, Gary Lin, 2024/11/15
- [PATCH v23 26/33] tpm2_key_protector: Support authorized policy, Gary Lin, 2024/11/15
- [PATCH v23 27/33] tpm2_key_protector: Implement NV index, Gary Lin, 2024/11/15
- [PATCH v23 22/33] tss2: Add TPM2 Software Stack (TSS2) support, Gary Lin, 2024/11/15
- [PATCH v23 24/33] cryptodisk: Support key protectors,
Gary Lin <=
- [PATCH v23 32/33] tests: Add tpm2_key_protector_test, Gary Lin, 2024/11/15
- [PATCH v23 31/33] tpm2_key_protector: Add grub-emu support, Gary Lin, 2024/11/15
- [PATCH v23 23/33] key_protector: Add TPM2 Key Protector, Gary Lin, 2024/11/15
- [PATCH v23 29/33] cryptodisk: wipe out the cached keys from protectors, Gary Lin, 2024/11/15
- [PATCH v23 30/33] diskfilter: look up cryptodisk devices first, Gary Lin, 2024/11/15
- [PATCH v23 33/33] docs: Document TPM2 key protector, Gary Lin, 2024/11/15
- Re: [PATCH v23 00/33] Automatic Disk Unlock with TPM2, Stefan Berger, 2024/11/19