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:
parent
48e230c317
commit
ad0c52784a
@ -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;
|
||||
|
||||
@ -6683,16 +6683,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
|
||||
|
||||
@ -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;
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user