From 90d00b832a2fcab9424fff1859bb2b9b4be01a2a Mon Sep 17 00:00:00 2001
From: James Bottomley <jejb@linux.ibm.com>
Date: Mon, 11 Nov 2024 14:20:23 +0800
Subject: [PATCH 1/2] cryptodisk: add OS provided secret support

cherry-picked from https://mail.gnu.org/archive/html/grub-devel/2022-02/msg00065.html.

Make use of the new OS provided secrets API so that if the new '-s'
option is passed in we try to extract the secret from the API rather
than prompting for it.

The primary consumer of this is AMD SEV, which has been programmed to
provide an injectable secret to the encrypted virtual machine.  OVMF
provides the secret area and passes it into the EFI Configuration
Tables.  The grub EFI layer pulls the secret out and primes the
secrets API with it.  The upshot of all of this is that a SEV
protected VM can do an encrypted boot with a protected boot secret.

[ hly: The patch from James Bottomley can not work properly, fix it
  when backport the patch. ]

Signed-off-by: James Bottomley <jejb@linux.ibm.com>
Signed-off-by: hanliyang <hanliyang@hygon.cn>
---
 grub-core/disk/cryptodisk.c | 62 +++++++++++++++++++++++++++++++++++--
 include/grub/cryptodisk.h   | 14 +++++++++
 2 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 8bc559b..4d8deba 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -48,7 +48,8 @@ enum
     OPTION_KEYFILE_OFFSET,
     OPTION_KEYFILE_SIZE,
     OPTION_HEADER,
-    OPTION_PROTECTOR
+    OPTION_PROTECTOR,
+    OPTION_SECRET
   };
 
 static const struct grub_arg_option options[] =
@@ -64,6 +65,7 @@ static const struct grub_arg_option options[] =
     {"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},
+    {"secret", 's', 0, N_("Get secret passphrase from named module and optional identifier"), 0, 0},
     {0, 0, 0, 0, 0, 0}
   };
 
@@ -1026,6 +1028,9 @@ grub_util_cryptodisk_get_uuid (grub_disk_t disk)
 
 #endif
 
+/* variable to hold the list of secret providers */
+static struct grub_secret_entry *secret_providers;
+
 static void
 cryptodisk_close (grub_cryptodisk_t dev)
 {
@@ -1260,6 +1265,18 @@ grub_cryptodisk_scan_device_real (const char *name,
   return dev;
 }
 
+void
+grub_cryptodisk_add_secret_provider (struct grub_secret_entry *e)
+{
+  grub_list_push(GRUB_AS_LIST_P (&secret_providers), GRUB_AS_LIST (e));
+}
+
+void
+grub_cryptodisk_remove_secret_provider (struct grub_secret_entry *e)
+{
+  grub_list_remove (GRUB_AS_LIST (e));
+}
+
 #ifdef GRUB_UTIL
 #include <grub/util/misc.h>
 grub_err_t
@@ -1376,7 +1393,7 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
   struct grub_arg_list *state = ctxt->state;
   struct grub_cryptomount_args cargs = {0};
 
-  if (argc < 1 && !state[OPTION_ALL].set && !state[OPTION_BOOT].set)
+  if (argc < 1 && !state[OPTION_ALL].set && !state[OPTION_BOOT].set && !state[OPTION_SECRET].set)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
 
   if (grub_cryptodisk_list == NULL)
@@ -1386,6 +1403,11 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
     return grub_error (GRUB_ERR_BAD_ARGUMENT,
 		       "a password and a key protector cannot both be set");
 
+  if (state[OPTION_SECRET].set &&
+      (state[OPTION_PASSWORD].set || state[OPTION_PROTECTOR].set)) /* secret module and password/key protector */
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+		       "a secret module and a password/key protector cannot both be set");
+
   if (state[OPTION_PASSWORD].set) /* password */
     {
       cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg;
@@ -1527,6 +1549,40 @@ grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args)
       grub_cryptodisk_clear_key_cache (&cargs);
       return GRUB_ERR_NONE;
     }
+  else if (state[OPTION_SECRET].set) /* secret module */
+    {
+      struct grub_secret_entry *se = NULL;
+      grub_err_t rc;
+
+      if (argc < 1)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "secret module must be specified");
+#ifndef GRUB_UTIL
+      grub_dl_load (args[0]);
+#endif
+      se = grub_named_list_find (GRUB_AS_NAMED_LIST (secret_providers), args[0]);
+      if (se == NULL)
+        return grub_error (GRUB_ERR_INVALID_COMMAND, "No secret provider is found");
+
+      rc = se->get (args[1], &cargs.key_data);
+      if (rc)
+        return rc;
+
+      cargs.key_len = grub_strlen((char *) cargs.key_data);
+
+      cargs.check_boot = state[OPTION_BOOT].set;
+      cargs.search_uuid = NULL;
+
+      grub_device_iterate (&grub_cryptodisk_scan_device, &cargs);
+
+      if (state[OPTION_SECRET].set)
+        {
+          rc = se->put (args[1], 1, &cargs.key_data);
+        }
+
+      grub_cryptodisk_clear_key_cache (&cargs);
+
+      return rc;
+    }
   else
     {
       grub_disk_t disk;
@@ -1721,7 +1777,7 @@ GRUB_MOD_INIT (cryptodisk)
 			      N_("[ [-p password] | [-k keyfile"
 				 " [-O keyoffset] [-S keysize] ] ] [-H file]"
 				 " [-P protector [-P protector ...]]"
-				 " <SOURCE|-u UUID|-a|-b>"),
+				 " <SOURCE|-u UUID|-a|-b|-s MOD [ID]>"),
 			      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 0b41e24..d0804a7 100644
--- a/include/grub/cryptodisk.h
+++ b/include/grub/cryptodisk.h
@@ -203,4 +203,18 @@ grub_util_get_geli_uuid (const char *dev);
 grub_cryptodisk_t grub_cryptodisk_get_by_uuid (const char *uuid);
 grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk);
 
+struct grub_secret_entry {
+  /* as named list */
+  struct grub_secret_entry *next;
+  struct grub_secret_entry **prev;
+  const char *name;
+
+  /* additional entries */
+  grub_err_t (*get) (const char *arg, grub_uint8_t **secret);
+  grub_err_t (*put) (const char *arg, int have_it, grub_uint8_t **secret);
+};
+
+void grub_cryptodisk_add_secret_provider (struct grub_secret_entry *e);
+void grub_cryptodisk_remove_secret_provider (struct grub_secret_entry *e);
+
 #endif
-- 
2.25.1

