disk/cryptodisk: Add the "erase secrets" function

This commit adds the grub_cryptodisk_erasesecrets() function to wipe
master keys from all cryptodisks. This function is EFI-only.

Since there is no easy way to "force unmount" a given encrypted disk,
this function renders all mounted cryptodisks unusable. An attempt to
read them will return garbage.

This is why this function must be used in "no way back" conditions.

Currently, it is used when unloading the cryptodisk module and when
performing the "exit" command (it is often used to switch to the next
EFI application). This function is not called when performing the
"chainloader" command, because the callee may return to GRUB. For this
reason, users are encouraged to use "exit" instead of "chainloader" to
execute third-party boot applications.

This function does not guarantee that all secrets are wiped from RAM.
Console output, chunks from disk read requests and other may remain.

This function does not clear the IV prefix and rekey key for geli disks.

Also, this commit adds the relevant documentation improvements.

Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Maxim Suhanov 2025-03-04 14:02:25 +03:00 committed by Daniel Kiper
parent 23ec4535f4
commit 301b4ef25a
4 changed files with 46 additions and 0 deletions

View File

@ -6788,6 +6788,11 @@ namespace in addition to the cryptodisk namespace.
Support for plain encryption mode (plain dm-crypt) is provided via separate Support for plain encryption mode (plain dm-crypt) is provided via separate
@command{@pxref{plainmount}} command. @command{@pxref{plainmount}} command.
On the EFI platform, GRUB tries to erase master keys from memory when the cryptodisk
module is unloaded or the command @command{exit} is executed. All secrets remain in
memory when the command @command{chainloader} is issued, because execution can
return to GRUB on the EFI platform.
@end deffn @end deffn
@node cutmem @node cutmem
@ -9406,6 +9411,7 @@ USB support provides benefits similar to ATA (for USB disks) or AT (for USB
keyboards). In addition it allows USBserial. keyboards). In addition it allows USBserial.
Chainloading refers to the ability to load another bootloader through the same protocol Chainloading refers to the ability to load another bootloader through the same protocol
and on some platforms, like EFI, allow that bootloader to return to the GRUB.
Hints allow faster disk discovery by already knowing in advance which is the disk in Hints allow faster disk discovery by already knowing in advance which is the disk in
question. On some platforms hints are correct unless you move the disk between boots. question. On some platforms hints are correct unless you move the disk between boots.

View File

@ -29,6 +29,10 @@
#include <grub/command.h> #include <grub/command.h>
#include <grub/i18n.h> #include <grub/i18n.h>
#ifdef GRUB_MACHINE_EFI
#include <grub/cryptodisk.h>
#endif
GRUB_MOD_LICENSE ("GPLv3+"); GRUB_MOD_LICENSE ("GPLv3+");
/* cat FILE */ /* cat FILE */
@ -187,6 +191,13 @@ grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)), int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused))) char *argv[] __attribute__ ((unused)))
{ {
#ifdef GRUB_MACHINE_EFI
/*
* The "exit" command is often used to launch the next boot application.
* So, erase the secrets.
*/
grub_cryptodisk_erasesecrets ();
#endif
grub_exit (); grub_exit ();
/* Not reached. */ /* Not reached. */
} }

View File

@ -1856,6 +1856,31 @@ grub_cryptodisk_challenge_password (void)
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
void
grub_cryptodisk_erasesecrets (void)
{
grub_cryptodisk_t i;
grub_uint8_t *buf;
buf = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN);
if (buf == NULL)
grub_fatal ("grub_cryptodisk_erasesecrets: cannot allocate memory");
for (i = cryptodisk_list; i != NULL; i = i->next)
if (grub_cryptodisk_setkey (i, buf, i->keysize))
grub_fatal ("grub_cryptodisk_erasesecrets: cannot erase secrets for %s", i->source);
else
grub_printf ("Erased crypto secrets for %s\n", i->source);
/*
* Unfortunately, there is no way to "force unmount" a given disk, it may
* have mounted "child" disks as well, e.g., an LVM volume. So, this
* function MUST be called when there is no way back, e.g., when exiting.
* Otherwise, subsequent read calls for a cryptodisk will return garbage.
*/
grub_free (buf);
}
#endif /* GRUB_MACHINE_EFI */ #endif /* GRUB_MACHINE_EFI */
struct grub_procfs_entry luks_script = struct grub_procfs_entry luks_script =
@ -1880,6 +1905,9 @@ GRUB_MOD_INIT (cryptodisk)
GRUB_MOD_FINI (cryptodisk) GRUB_MOD_FINI (cryptodisk)
{ {
#ifdef GRUB_MACHINE_EFI
grub_cryptodisk_erasesecrets ();
#endif
grub_disk_dev_unregister (&grub_cryptodisk_dev); grub_disk_dev_unregister (&grub_cryptodisk_dev);
cryptodisk_cleanup (); cryptodisk_cleanup ();
grub_unregister_extcmd (cmd); grub_unregister_extcmd (cmd);

View File

@ -205,5 +205,6 @@ grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk);
#ifdef GRUB_MACHINE_EFI #ifdef GRUB_MACHINE_EFI
grub_err_t grub_cryptodisk_challenge_password (void); grub_err_t grub_cryptodisk_challenge_password (void);
void grub_cryptodisk_erasesecrets (void);
#endif #endif
#endif #endif