uboot/common/autoboot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2000
   4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   5 */
   6
   7#include <common.h>
   8#include <autoboot.h>
   9#include <bootretry.h>
  10#include <cli.h>
  11#include <command.h>
  12#include <console.h>
  13#include <env.h>
  14#include <fdtdec.h>
  15#include <hash.h>
  16#include <log.h>
  17#include <malloc.h>
  18#include <memalign.h>
  19#include <menu.h>
  20#include <post.h>
  21#include <time.h>
  22#include <asm/global_data.h>
  23#include <linux/delay.h>
  24#include <u-boot/sha256.h>
  25#include <bootcount.h>
  26#include <crypt.h>
  27#include <dm/ofnode.h>
  28
  29DECLARE_GLOBAL_DATA_PTR;
  30
  31#define DELAY_STOP_STR_MAX_LENGTH 64
  32
  33#ifndef DEBUG_BOOTKEYS
  34#define DEBUG_BOOTKEYS 0
  35#endif
  36#define debug_bootkeys(fmt, args...)            \
  37        debug_cond(DEBUG_BOOTKEYS, fmt, ##args)
  38
  39/* Stored value of bootdelay, used by autoboot_command() */
  40static int stored_bootdelay;
  41static int menukey;
  42
  43#if !defined(CONFIG_AUTOBOOT_STOP_STR_CRYPT)
  44#define CONFIG_AUTOBOOT_STOP_STR_CRYPT ""
  45#endif
  46#if !defined(CONFIG_AUTOBOOT_STOP_STR_SHA256)
  47#define CONFIG_AUTOBOOT_STOP_STR_SHA256 ""
  48#endif
  49
  50#ifdef CONFIG_AUTOBOOT_USE_MENUKEY
  51#define AUTOBOOT_MENUKEY CONFIG_AUTOBOOT_MENUKEY
  52#else
  53#define AUTOBOOT_MENUKEY 0
  54#endif
  55
  56/**
  57 * passwd_abort_crypt() - check for a crypt-style hashed key sequence to abort booting
  58 *
  59 * This checks for the user entering a password within a given time.
  60 *
  61 * The entered password is hashed via one of the crypt-style hash methods
  62 * and compared to the pre-defined value from either
  63 *   the environment variable "bootstopkeycrypt"
  64 * or
  65 *   the config value CONFIG_AUTOBOOT_STOP_STR_CRYPT
  66 *
  67 * In case the config value CONFIG_AUTOBOOT_NEVER_TIMEOUT has been enabled
  68 * this function never times out if the user presses the <Enter> key
  69 * before starting to enter the password.
  70 *
  71 * @etime: Timeout value ticks (stop when get_ticks() reachs this)
  72 * Return: 0 if autoboot should continue, 1 if it should stop
  73 */
  74static int passwd_abort_crypt(uint64_t etime)
  75{
  76        const char *crypt_env_str = env_get("bootstopkeycrypt");
  77        char presskey[DELAY_STOP_STR_MAX_LENGTH];
  78        u_int presskey_len = 0;
  79        int abort = 0;
  80        int never_timeout = 0;
  81        int err;
  82
  83        if (IS_ENABLED(CONFIG_AUTOBOOT_STOP_STR_ENABLE) && !crypt_env_str)
  84                crypt_env_str = CONFIG_AUTOBOOT_STOP_STR_CRYPT;
  85
  86        if (!crypt_env_str)
  87                return 0;
  88
  89        /* We expect the stop-string to be newline-terminated */
  90        do {
  91                if (tstc()) {
  92                        /* Check for input string overflow */
  93                        if (presskey_len >= sizeof(presskey))
  94                                return 0;
  95
  96                        presskey[presskey_len] = getchar();
  97
  98                        if ((presskey[presskey_len] == '\r') ||
  99                            (presskey[presskey_len] == '\n')) {
 100                                if (IS_ENABLED(CONFIG_AUTOBOOT_NEVER_TIMEOUT) &&
 101                                    !presskey_len) {
 102                                        never_timeout = 1;
 103                                        continue;
 104                                }
 105                                presskey[presskey_len] = '\0';
 106                                err = crypt_compare(crypt_env_str, presskey,
 107                                                    &abort);
 108                                if (err)
 109                                        debug_bootkeys(
 110                                                "crypt_compare() failed with: %s\n",
 111                                                errno_str(err));
 112                                /* you had one chance */
 113                                break;
 114                        } else {
 115                                presskey_len++;
 116                        }
 117                }
 118        } while (never_timeout || get_ticks() <= etime);
 119
 120        return abort;
 121}
 122
 123/*
 124 * Use a "constant-length" time compare function for this
 125 * hash compare:
 126 *
 127 * https://crackstation.net/hashing-security.htm
 128 */
 129static int slow_equals(u8 *a, u8 *b, int len)
 130{
 131        int diff = 0;
 132        int i;
 133
 134        for (i = 0; i < len; i++)
 135                diff |= a[i] ^ b[i];
 136
 137        return diff == 0;
 138}
 139
 140/**
 141 * passwd_abort_sha256() - check for a hashed key sequence to abort booting
 142 *
 143 * This checks for the user entering a SHA256 hash within a given time.
 144 *
 145 * @etime: Timeout value ticks (stop when get_ticks() reachs this)
 146 * Return: 0 if autoboot should continue, 1 if it should stop
 147 */
 148static int passwd_abort_sha256(uint64_t etime)
 149{
 150        const char *sha_env_str = env_get("bootstopkeysha256");
 151        u8 sha_env[SHA256_SUM_LEN];
 152        u8 *sha;
 153        char *presskey;
 154        char *c;
 155        const char *algo_name = "sha256";
 156        u_int presskey_len = 0;
 157        int abort = 0;
 158        int size = sizeof(sha);
 159        int ret;
 160
 161        if (sha_env_str == NULL)
 162                sha_env_str = CONFIG_AUTOBOOT_STOP_STR_SHA256;
 163
 164        presskey = malloc_cache_aligned(DELAY_STOP_STR_MAX_LENGTH);
 165        c = strstr(sha_env_str, ":");
 166        if (c && (c - sha_env_str < DELAY_STOP_STR_MAX_LENGTH)) {
 167                /* preload presskey with salt */
 168                memcpy(presskey, sha_env_str, c - sha_env_str);
 169                presskey_len = c - sha_env_str;
 170                sha_env_str = c + 1;
 171        }
 172        /*
 173         * Generate the binary value from the environment hash value
 174         * so that we can compare this value with the computed hash
 175         * from the user input
 176         */
 177        ret = hash_parse_string(algo_name, sha_env_str, sha_env);
 178        if (ret) {
 179                printf("Hash %s not supported!\n", algo_name);
 180                return 0;
 181        }
 182
 183        sha = malloc_cache_aligned(SHA256_SUM_LEN);
 184        size = SHA256_SUM_LEN;
 185        /*
 186         * We don't know how long the stop-string is, so we need to
 187         * generate the sha256 hash upon each input character and
 188         * compare the value with the one saved in the environment
 189         */
 190        do {
 191                if (tstc()) {
 192                        /* Check for input string overflow */
 193                        if (presskey_len >= DELAY_STOP_STR_MAX_LENGTH) {
 194                                free(presskey);
 195                                free(sha);
 196                                return 0;
 197                        }
 198
 199                        presskey[presskey_len++] = getchar();
 200
 201                        /* Calculate sha256 upon each new char */
 202                        hash_block(algo_name, (const void *)presskey,
 203                                   presskey_len, sha, &size);
 204
 205                        /* And check if sha matches saved value in env */
 206                        if (slow_equals(sha, sha_env, SHA256_SUM_LEN))
 207                                abort = 1;
 208                }
 209        } while (!abort && get_ticks() <= etime);
 210
 211        free(presskey);
 212        free(sha);
 213        return abort;
 214}
 215
 216/**
 217 * passwd_abort_key() - check for a key sequence to aborted booting
 218 *
 219 * This checks for the user entering a string within a given time.
 220 *
 221 * @etime: Timeout value ticks (stop when get_ticks() reachs this)
 222 * Return: 0 if autoboot should continue, 1 if it should stop
 223 */
 224static int passwd_abort_key(uint64_t etime)
 225{
 226        int abort = 0;
 227        struct {
 228                char *str;
 229                u_int len;
 230                int retry;
 231        }
 232        delaykey[] = {
 233                { .str = env_get("bootdelaykey"),  .retry = 1 },
 234                { .str = env_get("bootstopkey"),   .retry = 0 },
 235        };
 236
 237        char presskey[DELAY_STOP_STR_MAX_LENGTH];
 238        int presskey_len = 0;
 239        int presskey_max = 0;
 240        int i;
 241
 242#  ifdef CONFIG_AUTOBOOT_DELAY_STR
 243        if (delaykey[0].str == NULL)
 244                delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR;
 245#  endif
 246#  ifdef CONFIG_AUTOBOOT_STOP_STR
 247        if (delaykey[1].str == NULL)
 248                delaykey[1].str = CONFIG_AUTOBOOT_STOP_STR;
 249#  endif
 250
 251        for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
 252                delaykey[i].len = delaykey[i].str == NULL ?
 253                                    0 : strlen(delaykey[i].str);
 254                delaykey[i].len = delaykey[i].len > DELAY_STOP_STR_MAX_LENGTH ?
 255                                    DELAY_STOP_STR_MAX_LENGTH : delaykey[i].len;
 256
 257                presskey_max = presskey_max > delaykey[i].len ?
 258                                    presskey_max : delaykey[i].len;
 259
 260                debug_bootkeys("%s key:<%s>\n",
 261                               delaykey[i].retry ? "delay" : "stop",
 262                               delaykey[i].str ? delaykey[i].str : "NULL");
 263        }
 264
 265        /* In order to keep up with incoming data, check timeout only
 266         * when catch up.
 267         */
 268        do {
 269                if (tstc()) {
 270                        if (presskey_len < presskey_max) {
 271                                presskey[presskey_len++] = getchar();
 272                        } else {
 273                                for (i = 0; i < presskey_max - 1; i++)
 274                                        presskey[i] = presskey[i + 1];
 275
 276                                presskey[i] = getchar();
 277                        }
 278                }
 279
 280                for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i++) {
 281                        if (delaykey[i].len > 0 &&
 282                            presskey_len >= delaykey[i].len &&
 283                                memcmp(presskey + presskey_len -
 284                                        delaykey[i].len, delaykey[i].str,
 285                                        delaykey[i].len) == 0) {
 286                                        debug_bootkeys("got %skey\n",
 287                                                delaykey[i].retry ? "delay" :
 288                                                "stop");
 289
 290                                /* don't retry auto boot */
 291                                if (!delaykey[i].retry)
 292                                        bootretry_dont_retry();
 293                                abort = 1;
 294                        }
 295                }
 296        } while (!abort && get_ticks() <= etime);
 297
 298        return abort;
 299}
 300
 301/**
 302 * flush_stdin() - drops all pending characters from stdin
 303 */
 304static void flush_stdin(void)
 305{
 306        while (tstc())
 307                (void)getchar();
 308}
 309
 310/**
 311 * fallback_to_sha256() - check whether we should fall back to sha256
 312 *                        password checking
 313 *
 314 * This checks for the environment variable `bootstopusesha256` in case
 315 * sha256-fallback has been enabled via the config setting
 316 * `AUTOBOOT_SHA256_FALLBACK`.
 317 *
 318 * Return: `false` if we must not fall-back, `true` if plain sha256 should be tried
 319 */
 320static bool fallback_to_sha256(void)
 321{
 322        if (IS_ENABLED(CONFIG_AUTOBOOT_SHA256_FALLBACK))
 323                return env_get_yesno("bootstopusesha256") == 1;
 324        else if (IS_ENABLED(CONFIG_CRYPT_PW))
 325                return false;
 326        else
 327                return true;
 328}
 329
 330/***************************************************************************
 331 * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
 332 * returns: 0 -  no key string, allow autoboot 1 - got key string, abort
 333 */
 334static int abortboot_key_sequence(int bootdelay)
 335{
 336        int abort;
 337        uint64_t etime = endtick(bootdelay);
 338
 339        if (IS_ENABLED(CONFIG_AUTOBOOT_FLUSH_STDIN))
 340                flush_stdin();
 341#  ifdef CONFIG_AUTOBOOT_PROMPT
 342        /*
 343         * CONFIG_AUTOBOOT_PROMPT includes the %d for all boards.
 344         * To print the bootdelay value upon bootup.
 345         */
 346        printf(CONFIG_AUTOBOOT_PROMPT, bootdelay);
 347#  endif
 348
 349        if (IS_ENABLED(CONFIG_AUTOBOOT_ENCRYPTION)) {
 350                if (IS_ENABLED(CONFIG_CRYPT_PW) && !fallback_to_sha256())
 351                        abort = passwd_abort_crypt(etime);
 352                else
 353                        abort = passwd_abort_sha256(etime);
 354        } else {
 355                abort = passwd_abort_key(etime);
 356        }
 357        if (!abort)
 358                debug_bootkeys("key timeout\n");
 359
 360        return abort;
 361}
 362
 363static int abortboot_single_key(int bootdelay)
 364{
 365        int abort = 0;
 366        unsigned long ts;
 367
 368        printf("Hit any key to stop autoboot: %2d ", bootdelay);
 369
 370        /*
 371         * Check if key already pressed
 372         */
 373        if (tstc()) {   /* we got a key press   */
 374                getchar();      /* consume input        */
 375                puts("\b\b\b 0");
 376                abort = 1;      /* don't auto boot      */
 377        }
 378
 379        while ((bootdelay > 0) && (!abort)) {
 380                --bootdelay;
 381                /* delay 1000 ms */
 382                ts = get_timer(0);
 383                do {
 384                        if (tstc()) {   /* we got a key press   */
 385                                int key;
 386
 387                                abort  = 1;     /* don't auto boot      */
 388                                bootdelay = 0;  /* no more delay        */
 389                                key = getchar();/* consume input        */
 390                                if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY))
 391                                        menukey = key;
 392                                break;
 393                        }
 394                        udelay(10000);
 395                } while (!abort && get_timer(ts) < 1000);
 396
 397                printf("\b\b\b%2d ", bootdelay);
 398        }
 399
 400        putc('\n');
 401
 402        return abort;
 403}
 404
 405static int abortboot(int bootdelay)
 406{
 407        int abort = 0;
 408
 409        if (bootdelay >= 0) {
 410                if (autoboot_keyed())
 411                        abort = abortboot_key_sequence(bootdelay);
 412                else
 413                        abort = abortboot_single_key(bootdelay);
 414        }
 415
 416        if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && abort)
 417                gd->flags &= ~GD_FLG_SILENT;
 418
 419        return abort;
 420}
 421
 422static void process_fdt_options(const void *blob)
 423{
 424#ifdef CONFIG_SYS_TEXT_BASE
 425        ulong addr;
 426
 427        /* Add an env variable to point to a kernel payload, if available */
 428        addr = ofnode_conf_read_int("kernel-offset", 0);
 429        if (addr)
 430                env_set_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
 431
 432        /* Add an env variable to point to a root disk, if available */
 433        addr = ofnode_conf_read_int("rootdisk-offset", 0);
 434        if (addr)
 435                env_set_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr));
 436#endif /* CONFIG_SYS_TEXT_BASE */
 437}
 438
 439const char *bootdelay_process(void)
 440{
 441        char *s;
 442        int bootdelay;
 443
 444        bootcount_inc();
 445
 446        s = env_get("bootdelay");
 447        bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
 448
 449        /*
 450         * Does it really make sense that the devicetree overrides the user
 451         * setting? It is possibly helpful for security since the device tree
 452         * may be signed whereas the environment is often loaded from storage.
 453         */
 454        if (IS_ENABLED(CONFIG_OF_CONTROL))
 455                bootdelay = ofnode_conf_read_int("bootdelay", bootdelay);
 456
 457        debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
 458
 459        if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW))
 460                bootdelay = menu_show(bootdelay);
 461        bootretry_init_cmd_timeout();
 462
 463#ifdef CONFIG_POST
 464        if (gd->flags & GD_FLG_POSTFAIL) {
 465                s = env_get("failbootcmd");
 466        } else
 467#endif /* CONFIG_POST */
 468        if (bootcount_error())
 469                s = env_get("altbootcmd");
 470        else
 471                s = env_get("bootcmd");
 472
 473        if (IS_ENABLED(CONFIG_OF_CONTROL))
 474                process_fdt_options(gd->fdt_blob);
 475        stored_bootdelay = bootdelay;
 476
 477        return s;
 478}
 479
 480void autoboot_command(const char *s)
 481{
 482        debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
 483
 484        if (s && (stored_bootdelay == -2 ||
 485                 (stored_bootdelay != -1 && !abortboot(stored_bootdelay)))) {
 486                bool lock;
 487                int prev;
 488
 489                lock = autoboot_keyed() &&
 490                        !IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC);
 491                if (lock)
 492                        prev = disable_ctrlc(1); /* disable Ctrl-C checking */
 493
 494                run_command_list(s, -1, 0);
 495
 496                if (lock)
 497                        disable_ctrlc(prev);    /* restore Ctrl-C checking */
 498        }
 499
 500        if (IS_ENABLED(CONFIG_AUTOBOOT_USE_MENUKEY) &&
 501            menukey == AUTOBOOT_MENUKEY) {
 502                s = env_get("menucmd");
 503                if (s)
 504                        run_command_list(s, -1, 0);
 505        }
 506}
 507