cryptodisk: Support key protectors

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>
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
This commit is contained in:
Hernan Gatta authored and Daniel Kiper committed 2024-11-28 21:50:55 +01:00
1 parent 48e230c317
commit ad0c52784a
4 files changed
+212 -78

No files matched your search

+1
View File
@@ -40,6 +40,7 @@ library = {
common = grub-core/disk/luks.c; common = grub-core/disk/luks.c;
common = grub-core/disk/luks2.c; common = grub-core/disk/luks2.c;
common = grub-core/disk/geli.c; common = grub-core/disk/geli.c;
common = grub-core/disk/key_protector.c;
common = grub-core/disk/cryptodisk.c; common = grub-core/disk/cryptodisk.c;
common = grub-core/disk/AFSplitter.c; common = grub-core/disk/AFSplitter.c;
common = grub-core/lib/pbkdf2.c; common = grub-core/lib/pbkdf2.c;
+7 -5
View File
@@ -6683,16 +6683,18 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum}
@node cryptomount @node cryptomount
@subsection 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, Setup access to encrypted device. A passphrase will be requested interactively,
if neither the @option{-p} nor @option{-k} options are given. The option 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). @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 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, 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 respectively, of the key data in the given key file. Besides the keyfile,
be used to supply cryptomount backends with an alternative header file (aka the key can be stored in a key protector, and option @option{-P} configures
detached header). Not all backends have headers nor support alternative header specific key protector, e.g. tpm2, to retrieve the key from.
files (currently only LUKS1 and LUKS2 support them). 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 Argument @var{device} configures specific grub device
(@pxref{Naming convention}); option @option{-u} @var{uuid} configures device (@pxref{Naming convention}); option @option{-u} @var{uuid} configures device
with specified @var{uuid}; option @option{-a} configures all detected encrypted with specified @var{uuid}; option @option{-a} configures all detected encrypted
+188 -73
View File
@@ -27,6 +27,7 @@
#include <grub/file.h> #include <grub/file.h>
#include <grub/procfs.h> #include <grub/procfs.h>
#include <grub/partition.h> #include <grub/partition.h>
#include <grub/key_protector.h>
#ifdef GRUB_UTIL #ifdef GRUB_UTIL
#include <grub/emu/hostdisk.h> #include <grub/emu/hostdisk.h>
@@ -45,7 +46,8 @@ enum
OPTION_KEYFILE, OPTION_KEYFILE,
OPTION_KEYFILE_OFFSET, OPTION_KEYFILE_OFFSET,
OPTION_KEYFILE_SIZE, OPTION_KEYFILE_SIZE,
OPTION_HEADER OPTION_HEADER,
OPTION_PROTECTOR
}; };
static const struct grub_arg_option options[] = 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-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}, {"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}, {"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} {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_err_t ret = GRUB_ERR_NONE;
grub_cryptodisk_t dev; grub_cryptodisk_t dev;
grub_cryptodisk_dev_t cr; grub_cryptodisk_dev_t cr;
int i;
struct cryptodisk_read_hook_ctx read_hook_data = {0}; struct cryptodisk_read_hook_ctx read_hook_data = {0};
int askpass = 0; int askpass = 0;
char *part = NULL; char *part = NULL;
@@ -1114,79 +1119,151 @@ grub_cryptodisk_scan_device_real (const char *name,
goto error_no_close; goto error_no_close;
if (!dev) if (!dev)
continue; 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;
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;
}
cargs->key_len = grub_strlen ((char *) cargs->key_data);
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");
if (dev == NULL)
{
grub_error (GRUB_ERR_BAD_MODULE,
"no cryptodisk module can handle this device");
goto error_no_close;
}
if (cargs->protectors)
{
for (i = 0; cargs->protectors[i]; i++)
{
if (cargs->key_cache[i].invalid)
continue;
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;
}
}
cargs->key_data = cargs->key_cache[i].key;
cargs->key_len = cargs->key_cache[i].key_len;
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;
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;
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;
}
cargs->key_len = grub_strlen ((char *) cargs->key_data);
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; goto cleanup;
error: error:
@@ -1298,6 +1375,20 @@ grub_cryptodisk_scan_device (const char *name,
return ret; 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 static grub_err_t
grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) 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) if (grub_cryptodisk_list == NULL)
return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded"); 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 */ if (state[OPTION_PASSWORD].set) /* password */
{ {
cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg; 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; 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 */ if (state[OPTION_UUID].set) /* uuid */
{ {
int found_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]); dev = grub_cryptodisk_get_by_uuid (args[0]);
if (dev) if (dev)
{ {
grub_cryptodisk_clear_key_cache (&cargs);
grub_dprintf ("cryptodisk", grub_dprintf ("cryptodisk",
"already mounted as crypto%lu\n", dev->id); "already mounted as crypto%lu\n", dev->id);
return GRUB_ERR_NONE; 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.check_boot = state[OPTION_BOOT].set;
cargs.search_uuid = args[0]; cargs.search_uuid = args[0];
found_uuid = grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); found_uuid = grub_device_iterate (&grub_cryptodisk_scan_device, &cargs);
grub_cryptodisk_clear_key_cache (&cargs);
if (found_uuid) if (found_uuid)
return GRUB_ERR_NONE; 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; cargs.check_boot = state[OPTION_BOOT].set;
grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); grub_device_iterate (&grub_cryptodisk_scan_device, &cargs);
grub_cryptodisk_clear_key_cache (&cargs);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
else else
@@ -1460,6 +1571,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
disk = grub_disk_open (diskname); disk = grub_disk_open (diskname);
if (!disk) if (!disk)
{ {
grub_cryptodisk_clear_key_cache (&cargs);
if (disklast) if (disklast)
*disklast = ')'; *disklast = ')';
return grub_errno; 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_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id);
grub_disk_close (disk); grub_disk_close (disk);
grub_cryptodisk_clear_key_cache (&cargs);
if (disklast) if (disklast)
*disklast = ')'; *disklast = ')';
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
dev = grub_cryptodisk_scan_device_real (diskname, disk, &cargs); dev = grub_cryptodisk_scan_device_real (diskname, disk, &cargs);
grub_cryptodisk_clear_key_cache (&cargs);
grub_disk_close (disk); grub_disk_close (disk);
if (disklast) if (disklast)
@@ -1629,6 +1743,7 @@ GRUB_MOD_INIT (cryptodisk)
cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0,
N_("[ [-p password] | [-k keyfile" N_("[ [-p password] | [-k keyfile"
" [-O keyoffset] [-S keysize] ] ] [-H file]" " [-O keyoffset] [-S keysize] ] ] [-H file]"
" [-P protector [-P protector ...]]"
" <SOURCE|-u UUID|-a|-b>"), " <SOURCE|-u UUID|-a|-b>"),
N_("Mount a crypto device."), options); N_("Mount a crypto device."), options);
grub_procfs_register ("luks_script", &luks_script); grub_procfs_register ("luks_script", &luks_script);
+16
View File
@@ -70,6 +70,18 @@ typedef gcry_err_code_t
(*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev, (*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev,
grub_uint64_t zoneno); 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 struct grub_cryptomount_args
{ {
/* scan: Flag to indicate that only bootable volumes should be decrypted */ /* 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 */ /* recover_key: Length of key_data */
grub_size_t key_len; grub_size_t key_len;
grub_file_t hdr_file; 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; typedef struct grub_cryptomount_args *grub_cryptomount_args_t;