grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2 10/11] cryptodisk: Support key protectors


From: Glenn Washburn
Subject: Re: [PATCH v2 10/11] cryptodisk: Support key protectors
Date: Fri, 24 Mar 2023 03:21:57 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.8.0

On 3/22/23 08:10, Gary Lin wrote:
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>

I'm wondering, why Michael's SOB is needed here? Did I miss somewhere on the list where he contributed to this patch?

Signed-off-by: Gary Lin <glin@suse.com>
---
  Makefile.util.def           |   1 +
  grub-core/disk/cryptodisk.c | 175 +++++++++++++++++++++++++++++-------
  include/grub/cryptodisk.h   |  14 +++
  3 files changed, 158 insertions(+), 32 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index c3b7df96d..7d8db722e 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 34b67a705..c5974399e 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -26,6 +26,7 @@
  #include <grub/file.h>
  #include <grub/procfs.h>
  #include <grub/partition.h>
+#include <grub/protector.h>
#ifdef GRUB_UTIL
  #include <grub/emu/hostdisk.h>
@@ -44,7 +45,8 @@ enum
      OPTION_KEYFILE,
      OPTION_KEYFILE_OFFSET,
      OPTION_KEYFILE_SIZE,
-    OPTION_HEADER
+    OPTION_HEADER,
+    OPTION_PROTECTOR
    };
static const struct grub_arg_option options[] =
@@ -58,6 +60,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}
    };
@@ -1060,7 +1064,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;
    struct cryptodisk_read_hook_ctx read_hook_data = {0};
    int askpass = 0;
    char *part = NULL;
@@ -1113,41 +1118,113 @@ grub_cryptodisk_scan_device_real (const char *name,
        goto error_no_close;
      if (!dev)
        continue;
+    crd = cr;

Why can't we just use cr below and not introduce crd?

+    break;
+  }
- 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_no_close;
-
-       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");

Every 8 spaces should be converted to a tab character. Same for all instances below too (and probably in other patches, though not in the ones importing foreign libs, like libtasn).

+      goto error_no_close;
+    }
- ret = cr->recover_key (source, dev, cargs);
-    if (ret != GRUB_ERR_NONE)
-      goto error;
+  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)

Daniel will say this should be "ret != GRUB_ERR_NONE", and same for similar uses elsewhere.

+                {
+                  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
+           {
+             ret = grub_cryptodisk_insert (dev, name, source);
+             if (ret != GRUB_ERR_NONE)
+               goto error;
+             goto cleanup;
+           }
+        }
- ret = grub_cryptodisk_insert (dev, name, source);
-    if (ret != GRUB_ERR_NONE)
+      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)
+    {
+      /* 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;
+
+  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:
@@ -1258,6 +1335,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)

Daniel also prefers "cargs->key_cache == NULL" for NULL checks on pointers.

+    return;
+
+  for (i = 0; cargs->protectors[i]; i++)

Maybe good to be defensive and check that cargs->protectors is not NULL.

+    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)
  {
@@ -1270,6 +1361,10 @@ 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_PASSWORD].set) /* password */
      {
        cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg;
@@ -1362,6 +1457,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)
+        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;
@@ -1370,6 +1474,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;
@@ -1378,6 +1483,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;
@@ -1397,6 +1503,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
@@ -1420,6 +1527,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;
@@ -1430,12 +1538,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)
@@ -1576,6 +1686,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..ce18df883 100644
--- a/include/grub/cryptodisk.h
+++ b/include/grub/cryptodisk.h
@@ -70,6 +70,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. */

Not a properly formatted multiline comment[1].

Overall this looks okay, even if I'm still not in love with the key protectors user interface (but like the idea).

Glenn

[1] https://www.gnu.org/software/grub/manual/grub-dev/grub-dev.html#Multi_002dLine-Comments

+  int invalid;
+};
+
  struct grub_cryptomount_args
  {
    /* scan: Flag to indicate that only bootable volumes should be decrypted */
@@ -81,6 +91,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;




reply via email to

[Prev in Thread] Current Thread [Next in Thread]