Michael Chang 13febd78db disk/cryptodisk: Require authentication after TPM unlock for CLI access
The GRUB may use TPM to verify the integrity of boot components and the
result can determine whether a previously sealed key can be released. If
everything checks out, showing nothing has been tampered with, the key
is released and GRUB unlocks the encrypted root partition for the next
stage of booting.

However, the liberal Command Line Interface (CLI) can be misused by
anyone in this case to access files in the encrypted partition one way
or another. Despite efforts to keep the CLI secure by preventing utility
command output from leaking file content, many techniques in the wild
could still be used to exploit the CLI, enabling attacks or learning
methods to attack. It's nearly impossible to account for all scenarios
where a hack could be applied.

Therefore, to mitigate potential misuse of the CLI after the root device
has been successfully unlocked via TPM, the user should be required to
authenticate using the LUKS password. This added layer of security
ensures that only authorized users can access the CLI reducing the risk
of exploitation or unauthorized access to the encrypted partition.

Fixes: CVE-2024-49504

Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2025-01-23 16:22:47 +01:00

371 lines
8.5 KiB
C

/* main.c - the kernel main routine */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2005,2006,2008,2009 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/kernel.h>
#include <grub/stack_protector.h>
#include <grub/misc.h>
#include <grub/symbol.h>
#include <grub/dl.h>
#include <grub/term.h>
#include <grub/file.h>
#include <grub/device.h>
#include <grub/env.h>
#include <grub/mm.h>
#include <grub/command.h>
#include <grub/reader.h>
#include <grub/parser.h>
#include <grub/verify.h>
#include <grub/types.h>
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/machine/memory.h>
#endif
static bool cli_disabled = false;
static bool cli_need_auth = false;
grub_addr_t
grub_modules_get_end (void)
{
struct grub_module_info *modinfo;
modinfo = (struct grub_module_info *) grub_modbase;
/* Check if there are any modules. */
if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC)
return grub_modbase;
return grub_modbase + modinfo->size;
}
/* Load all modules in core. */
static void
grub_load_modules (void)
{
struct grub_module_header *header;
FOR_MODULES (header)
{
/* Not an ELF module, skip. */
if (header->type != OBJ_TYPE_ELF)
continue;
if (! grub_dl_load_core ((char *) header + sizeof (struct grub_module_header),
(header->size - sizeof (struct grub_module_header))))
grub_fatal ("%s", grub_errmsg);
if (grub_errno)
grub_print_error ();
}
}
static char *load_config;
static void
grub_load_config (void)
{
struct grub_module_header *header;
FOR_MODULES (header)
{
/* Not an embedded config, skip. */
if (header->type != OBJ_TYPE_CONFIG)
continue;
load_config = grub_malloc (header->size - sizeof (struct grub_module_header) + 1);
if (!load_config)
{
grub_print_error ();
break;
}
grub_memcpy (load_config, (char *) header +
sizeof (struct grub_module_header),
header->size - sizeof (struct grub_module_header));
load_config[header->size - sizeof (struct grub_module_header)] = 0;
break;
}
}
/* Write hook for the environment variables of root. Remove surrounding
parentheses, if any. */
static char *
grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
/* XXX Is it better to check the existence of the device? */
grub_size_t len = grub_strlen (val);
if (val[0] == '(' && val[len - 1] == ')')
return grub_strndup (val + 1, len - 2);
return grub_strdup (val);
}
static void
grub_set_prefix_and_root (void)
{
char *device = NULL;
char *path = NULL;
char *fwdevice = NULL;
char *fwpath = NULL;
char *prefix = NULL;
struct grub_module_header *header;
FOR_MODULES (header)
if (header->type == OBJ_TYPE_PREFIX)
prefix = (char *) header + sizeof (struct grub_module_header);
grub_register_variable_hook ("root", 0, grub_env_write_root);
grub_machine_get_bootlocation (&fwdevice, &fwpath);
if (fwdevice)
{
char *cmdpath;
cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : "");
if (cmdpath)
{
grub_env_set ("cmdpath", cmdpath);
grub_env_export ("cmdpath");
grub_free (cmdpath);
}
}
if (prefix)
{
char *pptr = NULL;
if (prefix[0] == '(')
{
pptr = grub_strrchr (prefix, ')');
if (pptr)
{
device = grub_strndup (prefix + 1, pptr - prefix - 1);
pptr++;
}
}
if (!pptr)
pptr = prefix;
if (pptr[0])
path = grub_strdup (pptr);
}
if (!device && fwdevice)
device = fwdevice;
else if (fwdevice && (device[0] == ',' || !device[0]))
{
/* We have a partition, but still need to fill in the drive. */
char *comma, *new_device;
for (comma = fwdevice; *comma; )
{
if (comma[0] == '\\' && comma[1] == ',')
{
comma += 2;
continue;
}
if (*comma == ',')
break;
comma++;
}
if (*comma)
{
char *drive = grub_strndup (fwdevice, comma - fwdevice);
new_device = grub_xasprintf ("%s%s", drive, device);
grub_free (drive);
}
else
new_device = grub_xasprintf ("%s%s", fwdevice, device);
grub_free (fwdevice);
grub_free (device);
device = new_device;
}
else
grub_free (fwdevice);
if (fwpath && !path)
{
grub_size_t len = grub_strlen (fwpath);
while (len > 1 && fwpath[len - 1] == '/')
fwpath[--len] = 0;
if (len >= sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1
&& grub_memcmp (fwpath + len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1), GRUB_TARGET_CPU "-" GRUB_PLATFORM,
sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1) == 0)
fwpath[len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1)] = 0;
path = fwpath;
}
else
grub_free (fwpath);
if (device)
{
char *prefix_set;
prefix_set = grub_xasprintf ("(%s)%s", device, path ? : "");
if (prefix_set)
{
grub_env_set ("prefix", prefix_set);
grub_free (prefix_set);
}
grub_env_set ("root", device);
}
grub_free (device);
grub_free (path);
grub_print_error ();
}
/* Load the normal mode module and execute the normal mode if possible. */
static void
grub_load_normal_mode (void)
{
/* Load the module. */
grub_dl_load ("normal");
/* Print errors if any. */
grub_print_error ();
grub_errno = 0;
grub_command_execute ("normal", 0, 0);
}
bool
grub_is_cli_disabled (void)
{
return cli_disabled;
}
bool
grub_is_cli_need_auth (void)
{
return cli_need_auth;
}
void grub_cli_set_auth_needed (void)
{
cli_need_auth = true;
}
static void
check_is_cli_disabled (void)
{
struct grub_module_header *header;
header = 0;
FOR_MODULES (header)
{
if (header->type == OBJ_TYPE_DISABLE_CLI)
{
cli_disabled = true;
return;
}
}
}
static void
reclaim_module_space (void)
{
grub_addr_t modstart, modend;
if (!grub_modbase)
return;
#ifdef GRUB_MACHINE_PCBIOS
modstart = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR;
#else
modstart = grub_modbase;
#endif
modend = grub_modules_get_end ();
grub_modbase = 0;
#if GRUB_KERNEL_PRELOAD_SPACE_REUSABLE
grub_mm_init_region ((void *) modstart, modend - modstart);
#else
(void) modstart;
(void) modend;
#endif
}
/* The main routine. */
void __attribute__ ((noreturn))
grub_main (void)
{
#ifdef GRUB_STACK_PROTECTOR
/*
* This call should only be made from a function that does not return because
* functions that return will get instrumented to check that the stack cookie
* does not change and this call will change the stack cookie. Thus a stack
* guard failure will be triggered.
*/
grub_update_stack_guard ();
#endif
/* First of all, initialize the machine. */
grub_machine_init ();
grub_boot_time ("After machine init.");
/* This breaks flicker-free boot on EFI systems, so disable it there. */
#ifndef GRUB_MACHINE_EFI
/* Hello. */
grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
grub_printf ("Welcome to GRUB!\n\n");
grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
#endif
/* Init verifiers API. */
grub_verifiers_init ();
grub_load_config ();
grub_boot_time ("Before loading embedded modules.");
/* Load pre-loaded modules and free the space. */
grub_register_exported_symbols ();
#ifdef GRUB_LINKER_HAVE_INIT
grub_arch_dl_init_linker ();
#endif
grub_load_modules ();
grub_boot_time ("After loading embedded modules.");
/* Check if the CLI should be disabled */
check_is_cli_disabled ();
/* It is better to set the root device as soon as possible,
for convenience. */
grub_set_prefix_and_root ();
grub_env_export ("root");
grub_env_export ("prefix");
/* Reclaim space used for modules. */
reclaim_module_space ();
grub_boot_time ("After reclaiming module space.");
grub_register_core_commands ();
grub_boot_time ("Before execution of embedded config.");
if (load_config)
grub_parser_execute (load_config);
grub_boot_time ("After execution of embedded config. Attempt to go to normal mode");
grub_load_normal_mode ();
grub_rescue_run ();
}