Gary Lin afddba0127 tpm2_key_protector: Support PCR capping
To prevent a sealed key from being unsealed again, a common and
straightforward method is to "cap" the key by extending the associated
PCRs. When the PCRs associated with the sealed key are extended, TPM will
be unable to unseal the key, as the PCR values required for unsealing no
longer match, effectively rendering the key unusable until the next
system boot or a state where the PCRs are reset to their expected values.

To cap a specific set of PCRs, simply append the argument '-c pcr_list'
to the tpm2_key_protector command. Upon successfully unsealing the key,
the TPM2 key protector will then invoke tpm2_protector_cap_pcrs(). This
function extends the selected PCRs with an EV_SEPARATOR event,
effectively "capping" them. Consequently, the associated key cannot be
unsealed in any subsequent attempts until these PCRs are reset to their
original, pre-capped state, typically occurring upon the next system
boot.

Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2025-10-11 15:43:58 +02:00

602 lines
14 KiB
C

/* main.c - the normal mode main routine */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2005,2006,2007,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/net.h>
#include <grub/normal.h>
#include <grub/dl.h>
#include <grub/menu.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/term.h>
#include <grub/env.h>
#include <grub/parser.h>
#include <grub/reader.h>
#include <grub/menu_viewer.h>
#include <grub/auth.h>
#include <grub/i18n.h>
#include <grub/charset.h>
#include <grub/script_sh.h>
#include <grub/bufio.h>
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_DEFAULT_HISTORY_SIZE 50
static int nested_level = 0;
int grub_normal_exit_level = 0;
void
grub_normal_free_menu (grub_menu_t menu)
{
grub_menu_entry_t entry = menu->entry_list;
while (entry)
{
grub_menu_entry_t next_entry = entry->next;
grub_size_t i;
if (entry->classes)
{
struct grub_menu_entry_class *class;
for (class = entry->classes; class; class = class->next)
grub_free (class->name);
grub_free (entry->classes);
}
if (entry->args)
{
for (i = 0; entry->args[i]; i++)
grub_free (entry->args[i]);
grub_free (entry->args);
}
if (entry->blsuki)
{
entry->blsuki->visible = 0;
}
grub_free ((void *) entry->id);
grub_free ((void *) entry->users);
grub_free ((void *) entry->title);
grub_free ((void *) entry->sourcecode);
grub_free (entry);
entry = next_entry;
}
grub_free (menu);
grub_env_unset_menu ();
}
/* Helper for read_config_file. */
static grub_err_t
read_config_file_getline (char **line, int cont __attribute__ ((unused)),
void *data)
{
grub_file_t file = data;
while (1)
{
char *buf;
*line = buf = grub_file_getline (file);
if (! buf)
return grub_errno;
if (buf[0] == '#')
grub_free (*line);
else
break;
}
return GRUB_ERR_NONE;
}
static grub_menu_t
read_config_file (const char *config)
{
grub_file_t rawfile, file;
char *old_file = 0, *old_dir = 0;
char *config_dir, *ptr = 0;
const char *ctmp;
grub_menu_t newmenu;
newmenu = grub_env_get_menu ();
if (! newmenu)
{
newmenu = grub_zalloc (sizeof (*newmenu));
if (! newmenu)
return 0;
grub_env_set_menu (newmenu);
}
/* Try to open the config file. */
rawfile = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
if (! rawfile)
return 0;
file = grub_bufio_open (rawfile, 0);
if (! file)
{
grub_file_close (rawfile);
return 0;
}
ctmp = grub_env_get ("config_file");
if (ctmp)
old_file = grub_strdup (ctmp);
ctmp = grub_env_get ("config_directory");
if (ctmp)
old_dir = grub_strdup (ctmp);
if (*config == '(')
{
grub_env_set ("config_file", config);
config_dir = grub_strdup (config);
}
else
{
/* $root is guranteed to be defined, otherwise open above would fail */
config_dir = grub_xasprintf ("(%s)%s", grub_env_get ("root"), config);
if (config_dir)
grub_env_set ("config_file", config_dir);
}
if (config_dir)
{
ptr = grub_strrchr (config_dir, '/');
if (ptr)
*ptr = 0;
grub_env_set ("config_directory", config_dir);
grub_free (config_dir);
}
grub_env_export ("config_file");
grub_env_export ("config_directory");
while (1)
{
char *line;
/* Print an error, if any. */
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
if ((read_config_file_getline (&line, 0, file)) || (! line))
break;
grub_normal_parse_line (line, read_config_file_getline, file);
grub_free (line);
}
if (old_file)
grub_env_set ("config_file", old_file);
else
grub_env_unset ("config_file");
if (old_dir)
grub_env_set ("config_directory", old_dir);
else
grub_env_unset ("config_directory");
grub_free (old_file);
grub_free (old_dir);
grub_file_close (file);
return newmenu;
}
/* Initialize the screen. */
void
grub_normal_init_page (struct grub_term_output *term,
int y)
{
grub_ssize_t msg_len;
int posx;
char *msg_formatted;
grub_uint32_t *unicode_msg;
grub_uint32_t *last_position;
grub_term_cls (term);
msg_formatted = grub_xasprintf (_("GNU GRUB version %s"), PACKAGE_VERSION);
if (!msg_formatted)
return;
msg_len = grub_utf8_to_ucs4_alloc (msg_formatted,
&unicode_msg, &last_position);
grub_free (msg_formatted);
if (msg_len < 0)
{
return;
}
posx = grub_getstringwidth (unicode_msg, last_position, term);
posx = ((int) grub_term_width (term) - posx) / 2;
if (posx < 0)
posx = 0;
grub_term_gotoxy (term, (struct grub_term_coordinate) { posx, y });
grub_print_ucs4 (unicode_msg, last_position, 0, 0, term);
grub_putcode ('\n', term);
grub_putcode ('\n', term);
grub_free (unicode_msg);
}
static void
read_lists (const char *val)
{
if (! grub_no_modules)
{
read_command_list (val);
read_fs_list (val);
read_crypto_list (val);
read_terminal_list (val);
}
grub_gettext_reread_prefix (val);
}
static char *
read_lists_hook (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
read_lists (val);
return val ? grub_strdup (val) : NULL;
}
/* Read the config file CONFIG and execute the menu interface or
the command line interface if BATCH is false. */
void
grub_normal_execute (const char *config, int nested, int batch)
{
grub_menu_t menu = 0;
const char *prefix;
if (! nested)
{
prefix = grub_env_get ("prefix");
read_lists (prefix);
grub_register_variable_hook ("prefix", NULL, read_lists_hook);
}
grub_boot_time ("Executing config file");
if (config)
{
menu = read_config_file (config);
/* Ignore any error. */
grub_errno = GRUB_ERR_NONE;
}
grub_boot_time ("Executed config file");
if (! batch)
{
if (menu && menu->size)
{
grub_boot_time ("Entering menu");
grub_show_menu (menu, nested, 0);
if (nested)
grub_normal_free_menu (menu);
}
}
}
/* This starts the normal mode. */
void
grub_enter_normal_mode (const char *config)
{
grub_boot_time ("Entering normal mode");
nested_level++;
grub_normal_execute (config, 0, 0);
grub_boot_time ("Entering shell");
grub_cmdline_run (0, 1);
nested_level--;
if (grub_normal_exit_level)
grub_normal_exit_level--;
grub_boot_time ("Exiting normal mode");
}
/* Enter normal mode from rescue mode. */
static grub_err_t
grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
if (argc == 0)
{
/* Guess the config filename. It is necessary to make CONFIG static,
so that it won't get broken by longjmp. */
char *config;
const char *prefix;
prefix = grub_env_get ("prefix");
if (prefix)
{
grub_size_t config_len;
int disable_net_search = 0;
const char *net_search_cfg;
config_len = grub_strlen (prefix) +
sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
config = grub_malloc (config_len);
if (!config)
goto quit;
grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
net_search_cfg = grub_env_get ("feature_net_search_cfg");
if (net_search_cfg && net_search_cfg[0] == 'n')
disable_net_search = 1;
if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 &&
!disable_net_search)
grub_net_search_config_file (config, config_len);
grub_enter_normal_mode (config);
grub_free (config);
}
else
grub_enter_normal_mode (0);
}
else
grub_enter_normal_mode (argv[0]);
quit:
return 0;
}
/* Exit from normal mode to rescue mode. */
static grub_err_t
grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
if (nested_level <= grub_normal_exit_level)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment");
grub_normal_exit_level++;
return GRUB_ERR_NONE;
}
static grub_err_t
grub_normal_reader_init (int nested)
{
struct grub_term_output *term;
const char *msg_esc = _("ESC at any time exits.");
char *msg_formatted;
msg_formatted = grub_xasprintf (_("Minimal BASH-like line editing is supported. For "
"the first word, TAB lists possible command completions. Anywhere "
"else TAB lists possible device or file completions. To enable "
"less(1)-like paging, \"set pager=1\". %s"),
nested ? msg_esc : "");
if (!msg_formatted)
return grub_errno;
FOR_ACTIVE_TERM_OUTPUTS(term)
{
grub_normal_init_page (term, 1);
grub_term_setcursor (term, 1);
if (grub_term_width (term) > 3 + STANDARD_MARGIN + 20)
grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
else
grub_print_message_indented (msg_formatted, 0, 0, term);
grub_putcode ('\n', term);
grub_putcode ('\n', term);
grub_putcode ('\n', term);
}
grub_free (msg_formatted);
return 0;
}
static grub_err_t
grub_normal_read_line_real (char **line, int cont, int nested)
{
const char *prompt;
if (cont)
/* TRANSLATORS: it's command line prompt. */
prompt = _(">");
else
/* TRANSLATORS: it's command line prompt. */
prompt = _("grub>");
if (!prompt)
return grub_errno;
while (1)
{
*line = grub_cmdline_get (prompt);
if (*line)
return 0;
if (cont || nested)
{
grub_free (*line);
*line = 0;
return grub_errno;
}
}
}
static grub_err_t
grub_normal_read_line (char **line, int cont,
void *data __attribute__ ((unused)))
{
return grub_normal_read_line_real (line, cont, 0);
}
void
grub_cmdline_run (int nested, int force_auth)
{
grub_err_t err = GRUB_ERR_NONE;
do
{
err = grub_auth_check_authentication (NULL);
}
while (err && force_auth);
if (err == GRUB_ERR_NONE)
err = grub_auth_check_cli_access ();
if (err)
{
grub_print_error ();
grub_wait_after_message ();
grub_errno = GRUB_ERR_NONE;
return;
}
grub_normal_reader_init (nested);
while (1)
{
char *line = NULL;
if (grub_normal_exit_level)
break;
/* Print an error, if any. */
grub_print_error ();
grub_errno = GRUB_ERR_NONE;
grub_normal_read_line_real (&line, 0, nested);
if (! line)
break;
grub_normal_parse_line (line, grub_normal_read_line, NULL);
grub_free (line);
}
}
static char *
grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)),
const char *val)
{
grub_set_more ((*val == '1'));
return grub_strdup (val);
}
/* clear */
static grub_err_t
grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
grub_cls ();
return 0;
}
static grub_command_t cmd_clear;
static void (*grub_xputs_saved) (const char *str);
static const char *features[] = {
"feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint",
"feature_default_font_path", "feature_all_video_module",
"feature_menuentry_id", "feature_menuentry_options", "feature_200_final",
"feature_nativedisk_cmd", "feature_timeout_style",
"feature_search_cryptodisk_only", "feature_tpm2_cap_pcrs"
};
GRUB_MOD_INIT(normal)
{
unsigned i;
grub_boot_time ("Preparing normal module");
/* Previously many modules depended on gzio. Be nice to user and load it. */
grub_dl_load ("gzio");
grub_errno = 0;
grub_normal_auth_init ();
grub_context_init ();
grub_script_init ();
grub_menu_init ();
grub_xputs_saved = grub_xputs;
grub_xputs = grub_xputs_normal;
/* Normal mode shouldn't be unloaded. */
if (mod)
grub_dl_ref (mod);
cmd_clear =
grub_register_command ("clear", grub_mini_cmd_clear,
0, N_("Clear the screen."));
grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
grub_register_variable_hook ("pager", 0, grub_env_write_pager);
grub_env_export ("pager");
/* Register a command "normal" for the rescue mode. */
grub_register_command ("normal", grub_cmd_normal,
0, N_("Enter normal mode."));
grub_register_command ("normal_exit", grub_cmd_normal_exit,
0, N_("Exit from normal mode."));
/* Reload terminal colors when these variables are written to. */
grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
/* Preserve hooks after context changes. */
grub_env_export ("color_normal");
grub_env_export ("color_highlight");
/* Set default color names. */
grub_env_set ("color_normal", "light-gray/black");
grub_env_set ("color_highlight", "black/light-gray");
for (i = 0; i < ARRAY_SIZE (features); i++)
{
grub_env_set (features[i], "y");
grub_env_export (features[i]);
}
grub_env_set ("grub_cpu", GRUB_TARGET_CPU);
grub_env_export ("grub_cpu");
grub_env_set ("grub_platform", GRUB_PLATFORM);
grub_env_export ("grub_platform");
grub_boot_time ("Normal module prepared");
}
GRUB_MOD_FINI(normal)
{
grub_context_fini ();
grub_script_fini ();
grub_menu_fini ();
grub_normal_auth_fini ();
grub_xputs = grub_xputs_saved;
grub_set_history (0);
grub_register_variable_hook ("pager", NULL, NULL);
grub_register_variable_hook ("color_normal", NULL, NULL);
grub_register_variable_hook ("color_highlight", NULL, NULL);
grub_fs_autoload_hook = 0;
grub_unregister_command (cmd_clear);
}