From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.90_1) id 1nEspz-0004hY-AA for mharc-grub-devel@gnu.org; Tue, 01 Feb 2022 08:04:48 -0500 Received: from eggs.gnu.org ([209.51.188.92]:57230) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nEsog-0004bg-F6 for grub-devel@gnu.org; Tue, 01 Feb 2022 08:03:45 -0500 Received: from linux.microsoft.com ([13.77.154.182]:46396) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nEsoX-0005QN-NG for grub-devel@gnu.org; Tue, 01 Feb 2022 08:03:21 -0500 Received: from linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net (linux.microsoft.com [13.77.154.182]) by linux.microsoft.com (Postfix) with ESMTPSA id CF9AF20B8016; Tue, 1 Feb 2022 05:03:13 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com CF9AF20B8016 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1643720593; bh=2fqeMuEEf2ObMIAZ9kZHyRXdaBo1DYZEETTpMEJgpZk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nrr2AIjXGBow4NuQlaxnIf0TkD0gu69G2W8e3ygySpNX5YIiZ9a15QYUq9kUzPN6E TK0WXG0CnJUT2DVljWGl5KFAbwJFMULn8WejX7YyGcL8ORuWnKJJC9UnRJjuY4nWCt a7gAKuYBtc3HZSpuDW226rV9Rh6xdCiEGlzs4HPc= From: Hernan Gatta To: grub-devel@gnu.org Cc: shkhisti@microsoft.com, jaskaran.khurana@microsoft.com, christopher.co@microsoft.com, daniel.mihai@microsoft.com, rharwood@redhat.com, jaredz@redhat.com, development@efficientek.com, jejb@linux.ibm.com Subject: [PATCH v2 4/5] cryptodisk: Support key protectors Date: Tue, 1 Feb 2022 05:02:56 -0800 Message-Id: <1643720577-22911-5-git-send-email-hegatta@linux.microsoft.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1643720577-22911-1-git-send-email-hegatta@linux.microsoft.com> References: <1643720577-22911-1-git-send-email-hegatta@linux.microsoft.com> Received-SPF: pass client-ip=13.77.154.182; envelope-from=hegatta@linux.microsoft.com; helo=linux.microsoft.com X-Spam_score_int: -197 X-Spam_score: -19.8 X-Spam_bar: ------------------- X-Spam_report: (-19.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, ENV_AND_HDR_SPF_MATCH=-0.5, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, USER_IN_DEF_DKIM_WL=-7.5, USER_IN_DEF_SPF_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 01 Feb 2022 13:04:21 -0000 Add a new parameter to cryptomount to support the key protectors framework: -k. 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: --- Makefile.util.def | 1 + grub-core/disk/cryptodisk.c | 166 ++++++++++++++++++++++++++++++++++++-------- include/grub/cryptodisk.h | 14 ++++ 3 files changed, 151 insertions(+), 30 deletions(-) diff --git a/Makefile.util.def b/Makefile.util.def index f8b356c..39b53b3 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -35,6 +35,7 @@ library = { common = grub-core/kern/list.c; common = grub-core/kern/misc.c; common = grub-core/kern/partition.c; + common = grub-core/kern/protectors.c; common = grub-core/lib/crypto.c; common = grub-core/lib/json/json.c; common = grub-core/disk/luks.c; diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 4970973..00c4477 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef GRUB_UTIL #include @@ -42,6 +43,8 @@ static const struct grub_arg_option options[] = {"all", 'a', 0, N_("Mount all."), 0, 0}, {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0}, {"password", 'p', 0, N_("Password to open volumes."), 0, ARG_TYPE_STRING}, + {"protector", 'k', GRUB_ARG_OPTION_REPEATABLE, + N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; @@ -1000,7 +1003,8 @@ grub_cryptodisk_scan_device_real (const char *name, { grub_err_t ret = GRUB_ERR_NONE; grub_cryptodisk_t dev; - grub_cryptodisk_dev_t cr; + grub_cryptodisk_dev_t cr, crd = NULL; + int i; int askpass = 0; char *part = NULL; @@ -1016,39 +1020,108 @@ grub_cryptodisk_scan_device_real (const char *name, return NULL; if (!dev) continue; + crd = cr; + } - if (!cargs->key_len) - { - /* Get the passphrase from the user, if no key data. */ - askpass = 1; - 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); - - cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); - if (cargs->key_data == NULL) - return NULL; - - 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); - } + if (!dev) + { + grub_error (GRUB_ERR_BAD_MODULE, + "no cryptodisk module can handle this device"); + return NULL; + } - ret = cr->recover_key (source, dev, cargs); - if (ret != GRUB_ERR_NONE) + if (cargs->protectors) + { + for (i = 0; cargs->protectors[i]; i++) + { + if (cargs->key_cache[i].invalid) + continue; + + if (!cargs->key_cache[i].key) + { + ret = grub_key_protector_recover_key (cargs->protectors[i], + &cargs->key_cache[i].key, + &cargs->key_cache[i].key_len); + if (ret) + { + 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 = crd->recover_key (source, dev, cargs); + if (ret) + { + 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 + { + grub_cryptodisk_insert (dev, name, source); + 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; + } - grub_cryptodisk_insert (dev, name, source); + if (!cargs->key_len) + { + /* Get the passphrase from the user, if no key data. */ + askpass = 1; + 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); + + cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + if (cargs->key_data == NULL) + goto error; + + 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 = crd->recover_key (source, dev, cargs); + if (ret != GRUB_ERR_NONE) + goto error; + + grub_cryptodisk_insert (dev, name, source); - goto cleanup; - } - grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); goto cleanup; error: @@ -1155,6 +1228,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) + 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) { @@ -1167,12 +1254,25 @@ 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[3].set && state[4].set) /* password and key protector */ + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a password and a key protector cannot both be set"); + if (state[3].set) /* password */ { cargs.key_data = (grub_uint8_t *) state[3].arg; cargs.key_len = grub_strlen (state[3].arg); } + if (state[4].set) /* key protector(s) */ + { + cargs.key_cache = grub_zalloc (state[4].set * sizeof (*cargs.key_cache)); + if (!cargs.key_cache) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no memory for key protector key cache"); + cargs.protectors = state[4].args; + } + if (state[0].set) /* uuid */ { int found_uuid; @@ -1181,6 +1281,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; @@ -1189,6 +1290,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) cargs.check_boot = state[2].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; @@ -1208,6 +1310,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) { cargs.check_boot = state[2].set; grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); return GRUB_ERR_NONE; } else @@ -1231,6 +1334,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; @@ -1241,12 +1345,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) @@ -1385,7 +1491,7 @@ GRUB_MOD_INIT (cryptodisk) { grub_disk_dev_register (&grub_cryptodisk_dev); cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, - N_("[-p password] "), + N_("[-p password] [-k protector [-k protector ...]] "), 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 c6524c9..b556498 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -67,6 +67,16 @@ 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. */ + int invalid; +}; + struct grub_cryptomount_args { /* scan: Flag to indicate that only bootable volumes should be decrypted */ @@ -77,6 +87,10 @@ struct grub_cryptomount_args grub_uint8_t *key_data; /* recover_key: Length of key_data */ grub_size_t key_len; + /* 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; -- 1.8.3.1