mirror of
https://github.com/u-boot/u-boot.git
synced 2024-12-13 06:43:29 +08:00
d199c3ab1c
The key-sequence based unlock mechanisms are sensitive to junk symbols that could have been sent to stdin and are still waiting to be retrieved. Enabling this option will read all symbols off stdin before displaying the autoboot prompt (and starting to read the password from stdin). Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de> Reviewed-by: Simon Glass <sjg@chromium.org>
481 lines
11 KiB
C
481 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2000
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <autoboot.h>
|
|
#include <bootretry.h>
|
|
#include <cli.h>
|
|
#include <command.h>
|
|
#include <console.h>
|
|
#include <env.h>
|
|
#include <fdtdec.h>
|
|
#include <hash.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <memalign.h>
|
|
#include <menu.h>
|
|
#include <post.h>
|
|
#include <time.h>
|
|
#include <asm/global_data.h>
|
|
#include <linux/delay.h>
|
|
#include <u-boot/sha256.h>
|
|
#include <bootcount.h>
|
|
#include <crypt.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#define DELAY_STOP_STR_MAX_LENGTH 64
|
|
|
|
#ifndef DEBUG_BOOTKEYS
|
|
#define DEBUG_BOOTKEYS 0
|
|
#endif
|
|
#define debug_bootkeys(fmt, args...) \
|
|
debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
|
|
|
|
/* Stored value of bootdelay, used by autoboot_command() */
|
|
static int stored_bootdelay;
|
|
static int menukey;
|
|
|
|
#if !defined(CONFIG_AUTOBOOT_STOP_STR_CRYPT)
|
|
#define CONFIG_AUTOBOOT_STOP_STR_CRYPT ""
|
|
#endif
|
|
#if !defined(CONFIG_AUTOBOOT_STOP_STR_SHA256)
|
|
#define CONFIG_AUTOBOOT_STOP_STR_SHA256 ""
|
|
#endif
|
|
|
|
#ifdef CONFIG_AUTOBOOT_USE_MENUKEY
|
|
#define AUTOBOOT_MENUKEY CONFIG_AUTOBOOT_MENUKEY
|
|
#else
|
|
#define AUTOBOOT_MENUKEY 0
|
|
#endif
|
|
|
|
/**
|
|
* passwd_abort_crypt() - check for a crypt-style hashed key sequence to abort booting
|
|
*
|
|
* This checks for the user entering a password within a given time.
|
|
*
|
|
* The entered password is hashed via one of the crypt-style hash methods
|
|
* and compared to the pre-defined value from either
|
|
* the environment variable "bootstopkeycrypt"
|
|
* or
|
|
* the config value CONFIG_AUTOBOOT_STOP_STR_CRYPT
|
|
*
|
|
* In case the config value CONFIG_AUTOBOOT_NEVER_TIMEOUT has been enabled
|
|
* this function never times out if the user presses the <Enter> key
|
|
* before starting to enter the password.
|
|
*
|
|
* @etime: Timeout value ticks (stop when get_ticks() reachs this)
|
|
* @return 0 if autoboot should continue, 1 if it should stop
|
|
*/
|
|
static int passwd_abort_crypt(uint64_t etime)
|
|
{
|
|
const char *crypt_env_str = env_get("bootstopkeycrypt");
|
|
char presskey[DELAY_STOP_STR_MAX_LENGTH];
|
|
u_int presskey_len = 0;
|
|
int abort = 0;
|
|
int never_timeout = 0;
|
|
int err;
|
|
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_STOP_STR_ENABLE) && !crypt_env_str)
|
|
crypt_env_str = CONFIG_AUTOBOOT_STOP_STR_CRYPT;
|
|
|
|
if (!crypt_env_str)
|
|
return 0;
|
|
|
|
/* We expect the stop-string to be newline-terminated */
|
|
do {
|
|
if (tstc()) {
|
|
/* Check for input string overflow */
|
|
if (presskey_len >= sizeof(presskey))
|
|
return 0;
|
|
|
|
presskey[presskey_len] = getchar();
|
|
|
|
if ((presskey[presskey_len] == '\r') ||
|
|
(presskey[presskey_len] == '\n')) {
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_NEVER_TIMEOUT) &&
|
|
!presskey_len) {
|
|
never_timeout = 1;
|
|
continue;
|
|
}
|
|
presskey[presskey_len] = '\0';
|
|
err = crypt_compare(crypt_env_str, presskey,
|
|
&abort);
|
|
if (err)
|
|
debug_bootkeys(
|
|
"crypt_compare() failed with: %s\n",
|
|
errno_str(err));
|
|
/* you had one chance */
|
|
break;
|
|
} else {
|
|
presskey_len++;
|
|
}
|
|
}
|
|
} while (never_timeout || get_ticks() <= etime);
|
|
|
|
return abort;
|
|
}
|
|
|
|
/*
|
|
* Use a "constant-length" time compare function for this
|
|
* hash compare:
|
|
*
|
|
* https://crackstation.net/hashing-security.htm
|
|
*/
|
|
static int slow_equals(u8 *a, u8 *b, int len)
|
|
{
|
|
int diff = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
diff |= a[i] ^ b[i];
|
|
|
|
return diff == 0;
|
|
}
|
|
|
|
/**
|
|
* passwd_abort_sha256() - check for a hashed key sequence to abort booting
|
|
*
|
|
* This checks for the user entering a SHA256 hash within a given time.
|
|
*
|
|
* @etime: Timeout value ticks (stop when get_ticks() reachs this)
|
|
* @return 0 if autoboot should continue, 1 if it should stop
|
|
*/
|
|
static int passwd_abort_sha256(uint64_t etime)
|
|
{
|
|
const char *sha_env_str = env_get("bootstopkeysha256");
|
|
u8 sha_env[SHA256_SUM_LEN];
|
|
u8 *sha;
|
|
char *presskey;
|
|
char *c;
|
|
const char *algo_name = "sha256";
|
|
u_int presskey_len = 0;
|
|
int abort = 0;
|
|
int size = sizeof(sha);
|
|
int ret;
|
|
|
|
if (sha_env_str == NULL)
|
|
sha_env_str = CONFIG_AUTOBOOT_STOP_STR_SHA256;
|
|
|
|
presskey = malloc_cache_aligned(DELAY_STOP_STR_MAX_LENGTH);
|
|
c = strstr(sha_env_str, ":");
|
|
if (c && (c - sha_env_str < DELAY_STOP_STR_MAX_LENGTH)) {
|
|
/* preload presskey with salt */
|
|
memcpy(presskey, sha_env_str, c - sha_env_str);
|
|
presskey_len = c - sha_env_str;
|
|
sha_env_str = c + 1;
|
|
}
|
|
/*
|
|
* Generate the binary value from the environment hash value
|
|
* so that we can compare this value with the computed hash
|
|
* from the user input
|
|
*/
|
|
ret = hash_parse_string(algo_name, sha_env_str, sha_env);
|
|
if (ret) {
|
|
printf("Hash %s not supported!\n", algo_name);
|
|
return 0;
|
|
}
|
|
|
|
sha = malloc_cache_aligned(SHA256_SUM_LEN);
|
|
size = SHA256_SUM_LEN;
|
|
/*
|
|
* We don't know how long the stop-string is, so we need to
|
|
* generate the sha256 hash upon each input character and
|
|
* compare the value with the one saved in the environment
|
|
*/
|
|
do {
|
|
if (tstc()) {
|
|
/* Check for input string overflow */
|
|
if (presskey_len >= DELAY_STOP_STR_MAX_LENGTH) {
|
|
free(presskey);
|
|
free(sha);
|
|
return 0;
|
|
}
|
|
|
|
presskey[presskey_len++] = getchar();
|
|
|
|
/* Calculate sha256 upon each new char */
|
|
hash_block(algo_name, (const void *)presskey,
|
|
presskey_len, sha, &size);
|
|
|
|
/* And check if sha matches saved value in env */
|
|
if (slow_equals(sha, sha_env, SHA256_SUM_LEN))
|
|
abort = 1;
|
|
}
|
|
} while (!abort && get_ticks() <= etime);
|
|
|
|
free(presskey);
|
|
free(sha);
|
|
return abort;
|
|
}
|
|
|
|
/**
|
|
* passwd_abort_key() - check for a key sequence to aborted booting
|
|
*
|
|
* This checks for the user entering a string within a given time.
|
|
*
|
|
* @etime: Timeout value ticks (stop when get_ticks() reachs this)
|
|
* @return 0 if autoboot should continue, 1 if it should stop
|
|
*/
|
|
static int passwd_abort_key(uint64_t etime)
|
|
{
|
|
int abort = 0;
|
|
struct {
|
|
char *str;
|
|
u_int len;
|
|
int retry;
|
|
}
|
|
delaykey[] = {
|
|
{ .str = env_get("bootdelaykey"), .retry = 1 },
|
|
{ .str = env_get("bootstopkey"), .retry = 0 },
|
|
};
|
|
|
|
char presskey[DELAY_STOP_STR_MAX_LENGTH];
|
|
int presskey_len = 0;
|
|
int presskey_max = 0;
|
|
int i;
|
|
|
|
# ifdef CONFIG_AUTOBOOT_DELAY_STR
|
|
if (delaykey[0].str == NULL)
|
|
delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
|
|
# endif
|
|
# ifdef CONFIG_AUTOBOOT_STOP_STR
|
|
if (delaykey[1].str == NULL)
|
|
delaykey[1].str = CONFIG_AUTOBOOT_STOP_STR;
|
|
# endif
|
|
|
|
for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
|
|
delaykey[i].len = delaykey[i].str == NULL ?
|
|
0 : strlen(delaykey[i].str);
|
|
delaykey[i].len = delaykey[i].len > DELAY_STOP_STR_MAX_LENGTH ?
|
|
DELAY_STOP_STR_MAX_LENGTH : delaykey[i].len;
|
|
|
|
presskey_max = presskey_max > delaykey[i].len ?
|
|
presskey_max : delaykey[i].len;
|
|
|
|
debug_bootkeys("%s key:<%s>\n",
|
|
delaykey[i].retry ? "delay" : "stop",
|
|
delaykey[i].str ? delaykey[i].str : "NULL");
|
|
}
|
|
|
|
/* In order to keep up with incoming data, check timeout only
|
|
* when catch up.
|
|
*/
|
|
do {
|
|
if (tstc()) {
|
|
if (presskey_len < presskey_max) {
|
|
presskey[presskey_len++] = getchar();
|
|
} else {
|
|
for (i = 0; i < presskey_max - 1; i++)
|
|
presskey[i] = presskey[i + 1];
|
|
|
|
presskey[i] = getchar();
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
|
|
if (delaykey[i].len > 0 &&
|
|
presskey_len >= delaykey[i].len &&
|
|
memcmp(presskey + presskey_len -
|
|
delaykey[i].len, delaykey[i].str,
|
|
delaykey[i].len) == 0) {
|
|
debug_bootkeys("got %skey\n",
|
|
delaykey[i].retry ? "delay" :
|
|
"stop");
|
|
|
|
/* don't retry auto boot */
|
|
if (!delaykey[i].retry)
|
|
bootretry_dont_retry();
|
|
abort = 1;
|
|
}
|
|
}
|
|
} while (!abort && get_ticks() <= etime);
|
|
|
|
return abort;
|
|
}
|
|
|
|
/**
|
|
* flush_stdin() - drops all pending characters from stdin
|
|
*/
|
|
static void flush_stdin(void)
|
|
{
|
|
while (tstc())
|
|
(void)getchar();
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Watch for 'delay' seconds for autoboot stop or autoboot delay string.
|
|
* returns: 0 - no key string, allow autoboot 1 - got key string, abort
|
|
*/
|
|
static int abortboot_key_sequence(int bootdelay)
|
|
{
|
|
int abort;
|
|
uint64_t etime = endtick(bootdelay);
|
|
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_FLUSH_STDIN))
|
|
flush_stdin();
|
|
# ifdef CONFIG_AUTOBOOT_PROMPT
|
|
/*
|
|
* CONFIG_AUTOBOOT_PROMPT includes the %d for all boards.
|
|
* To print the bootdelay value upon bootup.
|
|
*/
|
|
printf(CONFIG_AUTOBOOT_PROMPT, bootdelay);
|
|
# endif
|
|
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_ENCRYPTION)) {
|
|
if (IS_ENABLED(CONFIG_CRYPT_PW))
|
|
abort = passwd_abort_crypt(etime);
|
|
else
|
|
abort = passwd_abort_sha256(etime);
|
|
} else {
|
|
abort = passwd_abort_key(etime);
|
|
}
|
|
if (!abort)
|
|
debug_bootkeys("key timeout\n");
|
|
|
|
return abort;
|
|
}
|
|
|
|
static int abortboot_single_key(int bootdelay)
|
|
{
|
|
int abort = 0;
|
|
unsigned long ts;
|
|
|
|
printf("Hit any key to stop autoboot: %2d ", bootdelay);
|
|
|
|
/*
|
|
* Check if key already pressed
|
|
*/
|
|
if (tstc()) { /* we got a key press */
|
|
getchar(); /* consume input */
|
|
puts("\b\b\b 0");
|
|
abort = 1; /* don't auto boot */
|
|
}
|
|
|
|
while ((bootdelay > 0) && (!abort)) {
|
|
--bootdelay;
|
|
/* delay 1000 ms */
|
|
ts = get_timer(0);
|
|
do {
|
|
if (tstc()) { /* we got a key press */
|
|
int key;
|
|
|
|
abort = 1; /* don't auto boot */
|
|
bootdelay = 0; /* no more delay */
|
|
key = getchar();/* consume input */
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY))
|
|
menukey = key;
|
|
break;
|
|
}
|
|
udelay(10000);
|
|
} while (!abort && get_timer(ts) < 1000);
|
|
|
|
printf("\b\b\b%2d ", bootdelay);
|
|
}
|
|
|
|
putc('\n');
|
|
|
|
return abort;
|
|
}
|
|
|
|
static int abortboot(int bootdelay)
|
|
{
|
|
int abort = 0;
|
|
|
|
if (bootdelay >= 0) {
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED))
|
|
abort = abortboot_key_sequence(bootdelay);
|
|
else
|
|
abort = abortboot_single_key(bootdelay);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && abort)
|
|
gd->flags &= ~GD_FLG_SILENT;
|
|
|
|
return abort;
|
|
}
|
|
|
|
static void process_fdt_options(const void *blob)
|
|
{
|
|
#ifdef CONFIG_SYS_TEXT_BASE
|
|
ulong addr;
|
|
|
|
/* Add an env variable to point to a kernel payload, if available */
|
|
addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0);
|
|
if (addr)
|
|
env_set_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
|
|
|
|
/* Add an env variable to point to a root disk, if available */
|
|
addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0);
|
|
if (addr)
|
|
env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
|
|
#endif /* CONFIG_SYS_TEXT_BASE */
|
|
}
|
|
|
|
const char *bootdelay_process(void)
|
|
{
|
|
char *s;
|
|
int bootdelay;
|
|
|
|
bootcount_inc();
|
|
|
|
s = env_get("bootdelay");
|
|
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
|
|
|
|
if (IS_ENABLED(CONFIG_OF_CONTROL))
|
|
bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
|
|
bootdelay);
|
|
|
|
debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
|
|
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW))
|
|
bootdelay = menu_show(bootdelay);
|
|
bootretry_init_cmd_timeout();
|
|
|
|
#ifdef CONFIG_POST
|
|
if (gd->flags & GD_FLG_POSTFAIL) {
|
|
s = env_get("failbootcmd");
|
|
} else
|
|
#endif /* CONFIG_POST */
|
|
if (bootcount_error())
|
|
s = env_get("altbootcmd");
|
|
else
|
|
s = env_get("bootcmd");
|
|
|
|
if (IS_ENABLED(CONFIG_OF_CONTROL))
|
|
process_fdt_options(gd->fdt_blob);
|
|
stored_bootdelay = bootdelay;
|
|
|
|
return s;
|
|
}
|
|
|
|
void autoboot_command(const char *s)
|
|
{
|
|
debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
|
|
|
|
if (s && (stored_bootdelay == -2 ||
|
|
(stored_bootdelay != -1 && !abortboot(stored_bootdelay)))) {
|
|
bool lock;
|
|
int prev;
|
|
|
|
lock = IS_ENABLED(CONFIG_AUTOBOOT_KEYED) &&
|
|
!IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC);
|
|
if (lock)
|
|
prev = disable_ctrlc(1); /* disable Ctrl-C checking */
|
|
|
|
run_command_list(s, -1, 0);
|
|
|
|
if (lock)
|
|
disable_ctrlc(prev); /* restore Ctrl-C checking */
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY) &&
|
|
menukey == AUTOBOOT_MENUKEY) {
|
|
s = env_get("menucmd");
|
|
if (s)
|
|
run_command_list(s, -1, 0);
|
|
}
|
|
} |