From a37376e72a3c2e4d449d308e38f571a84f732550 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 12 Sep 2010 01:07:41 +0200 Subject: [PATCH] legacy_password implementation --- grub-core/commands/legacycfg.c | 193 ++++++++++++++++++++++++++++++++- grub-core/commands/password.c | 23 ++-- grub-core/lib/legacy_parse.c | 20 +++- include/grub/normal.h | 3 + 4 files changed, 226 insertions(+), 13 deletions(-) diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c index 6c0caad98..f8e91b876 100644 --- a/grub-core/commands/legacycfg.c +++ b/grub-core/commands/legacycfg.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2010 Free Software Foundation, Inc. + * Copyright (C) 2000, 2001, 2010 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 @@ -28,6 +28,8 @@ #include #include #include +#include +#include static grub_err_t legacy_file (const char *filename) @@ -351,7 +353,7 @@ grub_cmd_legacy_initrd (struct grub_command *mycmd __attribute__ ((unused)), static grub_err_t grub_cmd_legacy_color (struct grub_command *mycmd __attribute__ ((unused)), - int argc, char **args) + int argc, char **args) { if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "color required"); @@ -382,8 +384,188 @@ grub_cmd_legacy_color (struct grub_command *mycmd __attribute__ ((unused)), return grub_errno; } +static grub_err_t +check_password_deny (const char *user __attribute__ ((unused)), + const char *entered __attribute__ ((unused)), + void *password __attribute__ ((unused))) +{ + return GRUB_ACCESS_DENIED; +} + +#define MD5_HASHLEN 16 + +struct legacy_md5_password +{ + grub_uint8_t *salt; + int saltlen; + grub_uint8_t hash[MD5_HASHLEN]; +}; + +static int +check_password_md5_real (const char *entered, + struct legacy_md5_password *pw) +{ + int enteredlen = grub_strlen (entered); + unsigned char alt_result[MD5_HASHLEN]; + unsigned char *digest; + grub_uint8_t ctx[GRUB_MD_MD5->contextsize]; + int i; + + GRUB_MD_MD5->init (ctx); + GRUB_MD_MD5->write (ctx, entered, enteredlen); + GRUB_MD_MD5->write (ctx, pw->salt + 3, pw->saltlen - 3); + GRUB_MD_MD5->write (ctx, entered, enteredlen); + digest = GRUB_MD_MD5->read (ctx); + GRUB_MD_MD5->final (ctx); + memcpy (alt_result, digest, MD5_HASHLEN); + + GRUB_MD_MD5->init (ctx); + GRUB_MD_MD5->write (ctx, entered, enteredlen); + GRUB_MD_MD5->write (ctx, pw->salt, pw->saltlen); /* include the $1$ header */ + for (i = enteredlen; i > 16; i -= 16) + GRUB_MD_MD5->write (ctx, alt_result, 16); + GRUB_MD_MD5->write (ctx, alt_result, i); + + for (i = enteredlen; i > 0; i >>= 1) + GRUB_MD_MD5->write (ctx, entered + ((i & 1) ? enteredlen : 0), 1); + digest = GRUB_MD_MD5->read (ctx); + GRUB_MD_MD5->final (ctx); + + for (i = 0; i < 1000; i++) + { + memcpy (alt_result, digest, 16); + + GRUB_MD_MD5->init (ctx); + if ((i & 1) != 0) + GRUB_MD_MD5->write (ctx, entered, enteredlen); + else + GRUB_MD_MD5->write (ctx, alt_result, 16); + + if (i % 3 != 0) + GRUB_MD_MD5->write (ctx, pw->salt + 3, pw->saltlen - 3); + + if (i % 7 != 0) + GRUB_MD_MD5->write (ctx, entered, enteredlen); + + if ((i & 1) != 0) + GRUB_MD_MD5->write (ctx, alt_result, 16); + else + GRUB_MD_MD5->write (ctx, entered, enteredlen); + digest = GRUB_MD_MD5->read (ctx); + GRUB_MD_MD5->final (ctx); + } + + return (grub_crypto_memcmp (digest, pw->hash, MD5_HASHLEN) == 0); +} + +static grub_err_t +check_password_md5 (const char *user, + const char *entered, + void *password) +{ + if (!check_password_md5_real (entered, password)) + return GRUB_ACCESS_DENIED; + + grub_auth_authenticate (user); + + return GRUB_ERR_NONE; +} + +static inline int +ib64t (char c) +{ + if (c == '.') + return 0; + if (c == '/') + return 1; + if (c >= '0' && c <= '9') + return c - '0' + 2; + if (c >= 'A' && c <= 'Z') + return c - 'A' + 12; + if (c >= 'a' && c <= 'z') + return c - 'a' + 38; + return -1; +} + +static grub_err_t +grub_cmd_legacy_password (struct grub_command *mycmd __attribute__ ((unused)), + int argc, char **args) +{ + const char *salt, *saltend; + const char *p; + struct legacy_md5_password *pw = NULL; + int i; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "arguments expected"); + if (args[0][0] != '-' || args[0][1] != '-') + return grub_normal_set_password ("legacy", args[0]); + if (grub_memcmp (args[0], "--md5", sizeof ("--md5")) != 0) + goto fail; + if (argc == 1) + goto fail; + if (grub_strlen(args[1]) <= 3) + goto fail; + salt = args[1]; + saltend = grub_strchr (salt + 3, '$'); + if (!saltend) + goto fail; + pw = grub_malloc (sizeof (*pw)); + if (!pw) + goto fail; + + p = saltend + 1; + for (i = 0; i < 5; i++) + { + int n; + grub_uint32_t w = 0; + + for (n = 0; n < 4; n++) + { + int ww = ib64t(*p++); + if (ww == -1) + goto fail; + w |= ww << (n * 6); + } + pw->hash[i == 4 ? 5 : 12+i] = w & 0xff; + pw->hash[6+i] = (w >> 8) & 0xff; + pw->hash[i] = (w >> 16) & 0xff; + } + { + int n; + grub_uint32_t w = 0; + for (n = 0; n < 2; n++) + { + int ww = ib64t(*p++); + if (ww == -1) + goto fail; + w |= ww << (6 * n); + } + if (w >= 0x100) + goto fail; + pw->hash[11] = w; + } + + pw->saltlen = saltend - salt; + pw->salt = (grub_uint8_t *) grub_strndup (salt, pw->saltlen); + if (!pw->salt) + goto fail; + + return grub_auth_register_authentication ("legacy", check_password_md5, pw); + + fail: + grub_free (pw); + /* This is to imitate minor difference between grub-legacy in GRUB2. + If 2 password commands are executed in a row and second one fails + on GRUB2 the password of first one is used, whereas in grub-legacy + authenthication is denied. In case of no password command was executed + early both versions deny any access. */ + return grub_auth_register_authentication ("legacy", check_password_deny, + NULL); +} + static grub_command_t cmd_source, cmd_configfile, cmd_kernel, cmd_initrd; -static grub_command_t cmd_color; +static grub_command_t cmd_color, cmd_password; GRUB_MOD_INIT(legacycfg) { @@ -407,6 +589,10 @@ GRUB_MOD_INIT(legacycfg) grub_cmd_legacy_color, N_("NORMAL [HIGHLIGHT]"), N_("Simulate grub-legacy color command")); + cmd_password = grub_register_command ("legacy_password", + grub_cmd_legacy_password, + N_("[--md5] PASSWD [FILE]"), + N_("Simulate grub-legacy password command")); } GRUB_MOD_FINI(legacycfg) @@ -416,4 +602,5 @@ GRUB_MOD_FINI(legacycfg) grub_unregister_command (cmd_kernel); grub_unregister_command (cmd_initrd); grub_unregister_command (cmd_color); + grub_unregister_command (cmd_password); } diff --git a/grub-core/commands/password.c b/grub-core/commands/password.c index 04285254e..db5951cbb 100644 --- a/grub-core/commands/password.c +++ b/grub-core/commands/password.c @@ -40,26 +40,22 @@ check_password (const char *user, const char *entered, return GRUB_ERR_NONE; } -static grub_err_t -grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) +grub_err_t +grub_normal_set_password (const char *user, const char *password) { grub_err_t err; char *pass; int copylen; - if (argc != 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments expected"); - pass = grub_zalloc (GRUB_AUTH_MAX_PASSLEN); if (!pass) return grub_errno; - copylen = grub_strlen (args[1]); + copylen = grub_strlen (password); if (copylen >= GRUB_AUTH_MAX_PASSLEN) copylen = GRUB_AUTH_MAX_PASSLEN - 1; - grub_memcpy (pass, args[1], copylen); + grub_memcpy (pass, password, copylen); - err = grub_auth_register_authentication (args[0], check_password, pass); + err = grub_auth_register_authentication (user, check_password, pass); if (err) { grub_free (pass); @@ -69,6 +65,15 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), return GRUB_ERR_NONE; } +static grub_err_t +grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments expected"); + return grub_normal_set_password (args[0], args[1]); +} + static grub_command_t cmd; GRUB_MOD_INIT(password) diff --git a/grub-core/lib/legacy_parse.c b/grub-core/lib/legacy_parse.c index 585c91b22..34ebd19c5 100644 --- a/grub-core/lib/legacy_parse.c +++ b/grub-core/lib/legacy_parse.c @@ -32,6 +32,7 @@ struct legacy_command TYPE_FORCE_OPTION, TYPE_NOAPM_OPTION, TYPE_TYPE_OR_NOMEM_OPTION, + TYPE_OPTION, TYPE_FILE, TYPE_FILE_NO_CONSUME, TYPE_PARTITION, @@ -159,7 +160,21 @@ struct legacy_command legacy_commands[] = /* partnew unsupported. */ {"parttype", "parttool '%s' type=%s\n", 2, {TYPE_PARTITION, TYPE_INT}, 0, "PART TYPE", "Change the type of the partition PART to TYPE."}, - /* password unsupported. */ /* NUL_TERMINATE */ + /* FIXME: support config file reloading. */ + /* FIXME: support usage in menuentry. */ + {"password", "if [ \"$superusers\" = "" ]; then superusers=legacy; fi; " + "legacy_password %s '%s' %s", 3, {TYPE_OPTION, TYPE_VERBATIM, + TYPE_FILE}, FLAG_IGNORE_REST, + "[--md5] PASSWD [FILE]", + "If used in the first section of a menu file, disable all" + " interactive editing control (menu entry editor and" + " command line). If the password PASSWD is entered, it loads the" + " FILE as a new config file and restarts the GRUB Stage 2. If you" + " omit the argument FILE, then GRUB just unlocks privileged" + " instructions. You can also use it in the script section, in" + " which case it will ask for the password, before continuing." + " The option --md5 tells GRUB that PASSWD is encrypted with" + " md5crypt."}, /* pause unsupported. */ /* rarp unsupported. */ {"read", "read_dword %s\n", 1, {TYPE_INT}, 0, "ADDR", @@ -323,6 +338,8 @@ is_option (enum arg_type opt, const char *curarg, grub_size_t len) || check_option (curarg, "--type=biglinux", len) || check_option (curarg, "--type=multiboot", len) || check_option (curarg, "--no-mem-option", len); + case TYPE_OPTION: + return (len >= 2 && curarg[0] == '-' && curarg[1] == '-'); default: return 0; } @@ -453,6 +470,7 @@ grub_legacy_parse (const char *buf, char **entryname) case TYPE_FORCE_OPTION: case TYPE_NOAPM_OPTION: case TYPE_TYPE_OR_NOMEM_OPTION: + case TYPE_OPTION: if (is_option (legacy_commands[cmdnum].argt[i], curarg, curarglen)) { args[j++] = grub_strndup (curarg, curarglen); diff --git a/include/grub/normal.h b/include/grub/normal.h index df7f70142..417560d9f 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -120,4 +120,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *users, const char *hotkey, const char *prefix, const char *sourcecode); +grub_err_t +grub_normal_set_password (const char *user, const char *password); + #endif /* ! GRUB_NORMAL_HEADER */