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>
371 lines
8.5 KiB
C
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 ();
|
|
}
|