diff --git a/.bzrignore b/.bzrignore index 56e3516b5..8cdc9d12d 100644 --- a/.bzrignore +++ b/.bzrignore @@ -35,6 +35,7 @@ grub_fstest_init.c grub_fstest_init.h grub-install grub-mk* +grub-pbkdf2 grub-pe2elf grub-probe grub_probe_init.c diff --git a/ChangeLog b/ChangeLog index 59f4e41f2..1a271fbb5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,54 @@ +2010-01-07 Vladimir Serbinenko + + Merge crypto branch. + + * Makefile.in (pkglib_DATA): Add crypto.lst. + (crypto.lst): New target. + * commands/hashsum.c: New file. + * commands/password.c (check_password): Use grub_crypto_memcmp. + * commands/password_pbkdf2.c: New file. + * commands/xnu_uuid.c: Remove MD5. Use GRUB_MD_MD5. + * conf/any-emu.rmk (grub_emu_SOURCES): Add lib/crypto.c, + normal/crypto.c and lib/libgcrypt-grub/cipher/md5.c. + (grub_emu_CFLAGS): Add -Wno-missing-field-initializers -Wno-error + -I$(srcdir)/lib/libgcrypt_wrap. + * conf/common.rmk (normal_mod_SOURCES): Add normal/crypto.c. + (pkglib_MODULES): Add crypto.mod, hashsum.mod, pbkdf2.mod and + password_pbkdf2.mod. + (crypto_mod_SOURCES): New variable. + (crypto_mod_CFLAGS): Likewise. + (crypto_mod_LDFLAGS): Likewise. + (hashsum_mod_SOURCES): New variable. + (hashsum_mod_CFLAGS): Likewise. + (hashsum_mod_LDFLAGS): Likewise. + (pbkdf2_mod_SOURCES): New variable. + (pbkdf2_mod_CFLAGS): Likewise. + (pbkdf2_mod_LDFLAGS): Likewise. + (password_pbkdf2_mod_SOURCES): New variable. + (password_pbkdf2_mod_CFLAGS): Likewise. + (password_pbkdf2_mod_LDFLAGS): Likewise. + (bin_UTILITIES): Add grub-mkpasswd-pbkdf2. + (grub_mkpasswd_pbkdf2_SOURCES): New variable. + (grub_mkpasswd_pbkdf2_CFLAGS): Likewise. + Include conf/gcry.rmk. + * include/grub/auth.h: Rewritten. + * include/grub/crypto.h: New file. + * include/grub/disk.h (grub_disk_dev_id): Add GRUB_DISK_DEVICE_LUKS_ID. + * include/grub/normal.h (read_crypto_list): New prototype. + * lib/crypto.c: New file. + * lib/libgcrypt_wrap/cipher_wrap.h: Likewise. + * lib/pbkdf2.c: Likewise. + * normal/auth.c (grub_auth_strcmp): Removed. + (grub_iswordseparator): Likewise. + (grub_auth_strword): Likewise. + (is_authenticated): Use grub_strword. + (grub_auth_check_authentication): Use grub_strcmp, grub_password_get + and grub_strword. Pass entered password to authentication callback. + * normal/crypto.c: New file. + * normal/main.c: Call read_crypto_list. + * util/grub-mkpasswd-pbkdf2.c: New file. + * util/import_gcry.py: Generate crypto.lst. Add hash blocklen. + 2010-01-06 Vladimir Serbinenko Fix descent and ascent calculation. diff --git a/Makefile.in b/Makefile.in index 927db2cee..ffa8716ed 100644 --- a/Makefile.in +++ b/Makefile.in @@ -174,7 +174,7 @@ endif ### General targets. CLEANFILES += $(pkglib_DATA) $(pkgdata_DATA) po/*.mo -pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst video.lst +pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst video.lst crypto.lst moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep.awk cat $(DEFSYMFILES) /dev/null \ | $(AWK) -f $(srcdir)/genmoddep.awk $(UNDSYMFILES) > $@ \ @@ -198,6 +198,9 @@ parttool.lst: $(PARTTOOLFILES) video.lst: $(VIDEOFILES) cat $^ /dev/null | sort | uniq > $@ +crypto.lst: lib/libgcrypt-grub/cipher/crypto.lst + cp $^ $@ + ifneq (true, $(MAKEINFO)) info_INFOS += docs/grub.info endif diff --git a/commands/hashsum.c b/commands/hashsum.c new file mode 100644 index 000000000..60e17447b --- /dev/null +++ b/commands/hashsum.c @@ -0,0 +1,278 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct grub_arg_option options[] = { + {"hash", 'h', 0, "Specify hash to use.", "HASH", ARG_TYPE_STRING}, + {"check", 'c', 0, "Check hash list file.", "FILE", ARG_TYPE_STRING}, + {"prefix", 'p', 0, "Base directory for hash list.", "DIRECTORY", + ARG_TYPE_STRING}, + {"keep-going", 'k', 0, "Don't stop after first error.", 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +struct { const char *name; const char *hashname; } aliases[] = + { + {"sha256sum", "sha256"}, + {"sha512sum", "sha512"}, + {"md5sum", "md5"}, + }; + +static inline int +hextoval (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static grub_err_t +hash_file (grub_file_t file, const gcry_md_spec_t *hash, void *result) +{ + grub_uint8_t context[hash->contextsize]; + char *readbuf[4096]; + + grub_memset (context, 0, sizeof (context)); + hash->init (context); + while (1) + { + grub_ssize_t r; + r = grub_file_read (file, readbuf, sizeof (readbuf)); + if (r < 0) + return grub_errno; + if (r == 0) + break; + hash->write (context, readbuf, r); + } + hash->final (context); + grub_memcpy (result, hash->read (context), hash->mdlen); + + return GRUB_ERR_NONE; +} + +static grub_err_t +check_list (const gcry_md_spec_t *hash, const char *hashfilename, + const char *prefix, int keep) +{ + grub_file_t hashlist, file; + char *buf = NULL; + grub_uint8_t expected[hash->mdlen]; + grub_uint8_t actual[hash->mdlen]; + grub_err_t err; + unsigned i; + unsigned unread = 0, mismatch = 0; + + hashlist = grub_file_open (hashfilename); + if (!hashlist) + return grub_errno; + + while (grub_free (buf), (buf = grub_file_getline (hashlist))) + { + const char *p = buf; + for (i = 0; i < hash->mdlen; i++) + { + int high, low; + high = hextoval (*p++); + low = hextoval (*p++); + if (high < 0 || low < 0) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list"); + expected[i] = (high << 4) | low; + } + if (*p++ != ' ' || *p++ != ' ') + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list"); + if (prefix) + { + char *filename; + + filename = grub_malloc (grub_strlen (prefix) + + grub_strlen (p) + 2); + if (!filename) + return grub_errno; + grub_sprintf (filename, "%s/%s", prefix, p); + file = grub_file_open (filename); + grub_free (filename); + } + else + file = grub_file_open (p); + if (!file) + { + grub_file_close (hashlist); + grub_free (buf); + return grub_errno; + } + err = hash_file (file, hash, actual); + grub_file_close (file); + if (err) + { + grub_printf ("%s: READ ERROR\n", p); + if (!keep) + { + grub_file_close (hashlist); + grub_free (buf); + return err; + } + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + unread++; + continue; + } + if (grub_crypto_memcmp (expected, actual, hash->mdlen) != 0) + { + grub_printf ("%s: HASH MISMATCH\n", p); + if (!keep) + { + grub_file_close (hashlist); + grub_free (buf); + return grub_error (GRUB_ERR_TEST_FAILURE, + "hash of '%s' mismatches", p); + } + mismatch++; + continue; + } + grub_printf ("%s: OK\n", p); + } + if (mismatch || unread) + return grub_error (GRUB_ERR_TEST_FAILURE, + "%d files couldn't be read and hash " + "of %d files mismatches", unread, mismatch); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_hashsum (struct grub_extcmd *cmd, + int argc, char **args) +{ + struct grub_arg_list *state = cmd->state; + const char *hashname = NULL; + const char *prefix = NULL; + const gcry_md_spec_t *hash; + unsigned i; + int keep = state[3].set; + unsigned unread = 0; + + for (i = 0; i < ARRAY_SIZE (aliases); i++) + if (grub_strcmp (cmd->cmd->name, aliases[i].name) == 0) + hashname = aliases[i].hashname; + if (state[0].set) + hashname = state[0].arg; + + if (!hashname) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no hash specified"); + + hash = grub_crypto_lookup_md_by_name (hashname); + if (!hash) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown hash"); + + if (state[2].set) + prefix = state[2].arg; + + if (state[1].set) + { + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "--check is incompatible with file list"); + return check_list (hash, state[1].arg, prefix, keep); + } + + for (i = 0; i < (unsigned) argc; i++) + { + grub_uint8_t result[hash->mdlen]; + grub_file_t file; + grub_err_t err; + unsigned j; + file = grub_file_open (args[i]); + if (!file) + { + if (!keep) + return grub_errno; + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + unread++; + continue; + } + err = hash_file (file, hash, result); + grub_file_close (file); + if (err) + { + if (!keep) + return err; + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + unread++; + continue; + } + for (j = 0; j < hash->mdlen; j++) + grub_printf ("%02x", result[j]); + grub_printf (" %s\n", args[i]); + } + + if (unread) + return grub_error (GRUB_ERR_TEST_FAILURE, "%d files couldn't be read.", + unread); + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd, cmd_md5, cmd_sha256, cmd_sha512; + +GRUB_MOD_INIT(hashsum) +{ + cmd = grub_register_extcmd ("hashsum", grub_cmd_hashsum, + GRUB_COMMAND_FLAG_BOTH, + "hashsum -h HASH [-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]", + "Compute or check hash checksum.", + options); + cmd_md5 = grub_register_extcmd ("md5sum", grub_cmd_hashsum, + GRUB_COMMAND_FLAG_BOTH, + "md5sum [-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]", + "Compute or check hash checksum.", + options); + cmd_sha256 = grub_register_extcmd ("sha256sum", grub_cmd_hashsum, + GRUB_COMMAND_FLAG_BOTH, + "sha256sum [-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]", + "Compute or check hash checksum.", + options); + cmd_sha512 = grub_register_extcmd ("sha512sum", grub_cmd_hashsum, + GRUB_COMMAND_FLAG_BOTH, + "sha512sum [-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]", + "Compute or check hash checksum.", + options); +} + +GRUB_MOD_FINI(hashsum) +{ + grub_unregister_extcmd (cmd); + grub_unregister_extcmd (cmd_md5); + grub_unregister_extcmd (cmd_sha256); + grub_unregister_extcmd (cmd_sha512); +} diff --git a/commands/password.c b/commands/password.c index 2869ffc53..04285254e 100644 --- a/commands/password.c +++ b/commands/password.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -28,17 +29,10 @@ static grub_dl_t my_mod; static grub_err_t -check_password (const char *user, +check_password (const char *user, const char *entered, void *password) { - char entered[1024]; - - grub_memset (entered, 0, sizeof (entered)); - - if (!GRUB_GET_PASSWORD (entered, sizeof (entered) - 1)) - return GRUB_ACCESS_DENIED; - - if (grub_auth_strcmp (entered, password) != 0) + if (grub_crypto_memcmp (entered, password, GRUB_AUTH_MAX_PASSLEN) != 0) return GRUB_ACCESS_DENIED; grub_auth_authenticate (user); @@ -52,13 +46,18 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), { grub_err_t err; char *pass; + int copylen; if (argc != 2) return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments expected"); - pass = grub_strdup (args[1]); + pass = grub_zalloc (GRUB_AUTH_MAX_PASSLEN); if (!pass) return grub_errno; + copylen = grub_strlen (args[1]); + if (copylen >= GRUB_AUTH_MAX_PASSLEN) + copylen = GRUB_AUTH_MAX_PASSLEN - 1; + grub_memcpy (pass, args[1], copylen); err = grub_auth_register_authentication (args[0], check_password, pass); if (err) diff --git a/commands/password_pbkdf2.c b/commands/password_pbkdf2.c new file mode 100644 index 000000000..51c8ea794 --- /dev/null +++ b/commands/password_pbkdf2.c @@ -0,0 +1,196 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_dl_t my_mod; + +struct pbkdf2_password +{ + grub_uint8_t *salt; + grub_size_t saltlen; + unsigned int c; + grub_uint8_t *expected; + grub_size_t buflen; +}; + +static grub_err_t +check_password (const char *user, const char *entered, void *pin) +{ + grub_uint8_t *buf; + struct pbkdf2_password *pass = pin; + gcry_err_code_t err; + + buf = grub_malloc (pass->buflen); + if (!buf) + return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY); + + err = grub_crypto_pbkdf2 (GRUB_MD_SHA512, (grub_uint8_t *) entered, + grub_strlen (entered), + pass->salt, pass->saltlen, pass->c, + buf, pass->buflen); + if (err) + { + grub_free (buf); + return grub_crypto_gcry_error (err); + } + + if (grub_crypto_memcmp (buf, pass->expected, pass->buflen) != 0) + return GRUB_ACCESS_DENIED; + + grub_auth_authenticate (user); + + return GRUB_ERR_NONE; +} + +static inline int +hex2val (char hex) +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + if ('a' <= hex && hex <= 'f') + return hex - 'a' + 10; + if ('A' <= hex && hex <= 'F') + return hex - 'A' + 10; + return -1; +} + +static grub_err_t +grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_err_t err; + char *ptr, *ptr2; + grub_uint8_t *ptro; + struct pbkdf2_password *pass; + + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Two arguments expected."); + + if (grub_memcmp (args[1], "grub.pbkdf2.sha512.", + sizeof ("grub.pbkdf2.sha512.") - 1) != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Incorrect PBKDF2 password."); + + ptr = args[1] + sizeof ("grub.pbkdf2.sha512.") - 1; + + pass = grub_malloc (sizeof (*pass)); + if (!pass) + return grub_errno; + + pass->c = grub_strtoul (ptr, &ptr, 0); + if (*ptr != '.') + { + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Incorrect PBKDF2 password."); + } + ptr++; + + ptr2 = grub_strchr (ptr, '.'); + if (!ptr2 || ((ptr2 - ptr) & 1) || grub_strlen (ptr2 + 1) & 1) + { + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Incorrect PBKDF2 password."); + } + + pass->saltlen = (ptr2 - ptr) >> 1; + pass->buflen = grub_strlen (ptr2 + 1) >> 1; + ptro = pass->salt = grub_malloc (pass->saltlen); + if (!ptro) + { + grub_free (pass); + return grub_errno; + } + while (ptr < ptr2) + { + int hex1, hex2; + hex1 = hex2val (*ptr); + ptr++; + hex2 = hex2val (*ptr); + ptr++; + if (hex1 < 0 || hex2 < 0) + { + grub_free (pass->salt); + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Incorrect PBKDF2 password."); + } + + *ptro = (hex1 << 4) | hex2; + ptro++; + } + + ptro = pass->expected = grub_malloc (pass->buflen); + if (!ptro) + { + grub_free (pass->salt); + grub_free (pass); + return grub_errno; + } + ptr = ptr2 + 1; + ptr2 += grub_strlen (ptr2); + while (ptr < ptr2) + { + int hex1, hex2; + hex1 = hex2val (*ptr); + ptr++; + hex2 = hex2val (*ptr); + ptr++; + if (hex1 < 0 || hex2 < 0) + { + grub_free (pass->expected); + grub_free (pass->salt); + grub_free (pass); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Incorrect PBKDF2 password."); + } + + *ptro = (hex1 << 4) | hex2; + ptro++; + } + + err = grub_auth_register_authentication (args[0], check_password, pass); + if (err) + { + grub_free (pass); + return err; + } + grub_dl_ref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(password_pbkdf2) +{ + my_mod = mod; + cmd = grub_register_command ("password_pbkdf2", grub_cmd_password, + "password_pbkdf2 USER PBKDF2_PASSWORD", + "Set user password (PBKDF2). "); +} + +GRUB_MOD_FINI(password_pbkdf2) +{ + grub_unregister_command (cmd); +} diff --git a/commands/xnu_uuid.c b/commands/xnu_uuid.c index b94dc0dc7..b6f2b2604 100644 --- a/commands/xnu_uuid.c +++ b/commands/xnu_uuid.c @@ -32,12 +32,7 @@ #include #include #include - -struct tohash -{ - grub_uint8_t prefix[16]; - grub_uint64_t serial; -} __attribute__ ((packed)); +#include /* This prefix is used by xnu and boot-132 to hash together with volume serial. */ @@ -45,311 +40,27 @@ static grub_uint8_t hash_prefix[16] = {0xB3, 0xE2, 0x0F, 0x39, 0xF2, 0x92, 0x11, 0xD6, 0x97, 0xA4, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC}; -#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) -#define ror(x,n) ( ((x) >> (n)) | ((x) << (32-(n))) ) - -typedef struct { - grub_uint32_t A,B,C,D; /* chaining variables */ - grub_uint32_t nblocks; - grub_uint8_t buf[64]; - int count; -} MD5_CONTEXT; - -static void -md5_init( void *context ) -{ - MD5_CONTEXT *ctx = context; - - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - - ctx->nblocks = 0; - ctx->count = 0; -} - -/* These are the four functions used in the four steps of the MD5 algorithm - and defined in the RFC 1321. The first function is a little bit optimized - (as found in Colin Plumbs public domain implementation). */ -/* #define FF(b, c, d) ((b & c) | (~b & d)) */ -#define FF(b, c, d) (d ^ (b & (c ^ d))) -#define FG(b, c, d) FF (d, b, c) -#define FH(b, c, d) (b ^ c ^ d) -#define FI(b, c, d) (c ^ (b | ~d)) - - -/**************** - * transform n*64 grub_uint8_ts - */ -static void -transform ( MD5_CONTEXT *ctx, const unsigned char *data ) -{ - grub_uint32_t correct_words[16]; - register grub_uint32_t A = ctx->A; - register grub_uint32_t B = ctx->B; - register grub_uint32_t C = ctx->C; - register grub_uint32_t D = ctx->D; - grub_uint32_t *cwp = correct_words; - -#ifdef GRUB_CPU_WORDS_BIGENDIAN - { - int i; - const grub_uint32_t *p = (const grub_uint32_t *) data; - - for (i = 0; i < 16; i++) - correct_words[i] = grub_le_to_cpu32 (p[i]); - } -#else - grub_memcpy (correct_words, data, 64); -#endif - -#define OP(a, b, c, d, s, T) \ - do \ - { \ - a += FF (b, c, d) + (*cwp++) + T; \ - a = rol(a, s); \ - a += b; \ - } \ - while (0) - - /* Before we start, one word about the strange constants. - They are defined in RFC 1321 as - - T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 - */ - - /* Round 1. */ - OP (A, B, C, D, 7, 0xd76aa478); - OP (D, A, B, C, 12, 0xe8c7b756); - OP (C, D, A, B, 17, 0x242070db); - OP (B, C, D, A, 22, 0xc1bdceee); - OP (A, B, C, D, 7, 0xf57c0faf); - OP (D, A, B, C, 12, 0x4787c62a); - OP (C, D, A, B, 17, 0xa8304613); - OP (B, C, D, A, 22, 0xfd469501); - OP (A, B, C, D, 7, 0x698098d8); - OP (D, A, B, C, 12, 0x8b44f7af); - OP (C, D, A, B, 17, 0xffff5bb1); - OP (B, C, D, A, 22, 0x895cd7be); - OP (A, B, C, D, 7, 0x6b901122); - OP (D, A, B, C, 12, 0xfd987193); - OP (C, D, A, B, 17, 0xa679438e); - OP (B, C, D, A, 22, 0x49b40821); - -#undef OP -#define OP(f, a, b, c, d, k, s, T) \ - do \ - { \ - a += f (b, c, d) + correct_words[k] + T; \ - a = rol(a, s); \ - a += b; \ - } \ - while (0) - - /* Round 2. */ - OP (FG, A, B, C, D, 1, 5, 0xf61e2562); - OP (FG, D, A, B, C, 6, 9, 0xc040b340); - OP (FG, C, D, A, B, 11, 14, 0x265e5a51); - OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); - OP (FG, A, B, C, D, 5, 5, 0xd62f105d); - OP (FG, D, A, B, C, 10, 9, 0x02441453); - OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); - OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); - OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); - OP (FG, D, A, B, C, 14, 9, 0xc33707d6); - OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); - OP (FG, B, C, D, A, 8, 20, 0x455a14ed); - OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); - OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); - OP (FG, C, D, A, B, 7, 14, 0x676f02d9); - OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); - - /* Round 3. */ - OP (FH, A, B, C, D, 5, 4, 0xfffa3942); - OP (FH, D, A, B, C, 8, 11, 0x8771f681); - OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); - OP (FH, B, C, D, A, 14, 23, 0xfde5380c); - OP (FH, A, B, C, D, 1, 4, 0xa4beea44); - OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); - OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); - OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); - OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); - OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); - OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); - OP (FH, B, C, D, A, 6, 23, 0x04881d05); - OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); - OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); - OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); - OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); - - /* Round 4. */ - OP (FI, A, B, C, D, 0, 6, 0xf4292244); - OP (FI, D, A, B, C, 7, 10, 0x432aff97); - OP (FI, C, D, A, B, 14, 15, 0xab9423a7); - OP (FI, B, C, D, A, 5, 21, 0xfc93a039); - OP (FI, A, B, C, D, 12, 6, 0x655b59c3); - OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); - OP (FI, C, D, A, B, 10, 15, 0xffeff47d); - OP (FI, B, C, D, A, 1, 21, 0x85845dd1); - OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); - OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); - OP (FI, C, D, A, B, 6, 15, 0xa3014314); - OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); - OP (FI, A, B, C, D, 4, 6, 0xf7537e82); - OP (FI, D, A, B, C, 11, 10, 0xbd3af235); - OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); - OP (FI, B, C, D, A, 9, 21, 0xeb86d391); - - /* Put checksum in context given as argument. */ - ctx->A += A; - ctx->B += B; - ctx->C += C; - ctx->D += D; -} - -/* The routine updates the message-digest context to - * account for the presence of each of the characters inBuf[0..inLen-1] - * in the message whose digest is being computed. - */ -static void -md5_write( void *context, const void *inbuf_arg , grub_size_t inlen) -{ - const unsigned char *inbuf = inbuf_arg; - MD5_CONTEXT *hd = context; - - if( hd->count == 64 ) /* flush the buffer */ - { - transform( hd, hd->buf ); - // _gcry_burn_stack (80+6*sizeof(void*)); - hd->count = 0; - hd->nblocks++; - } - if( !inbuf ) - return; - - if( hd->count ) - { - for( ; inlen && hd->count < 64; inlen-- ) - hd->buf[hd->count++] = *inbuf++; - md5_write( hd, NULL, 0 ); - if( !inlen ) - return; - } - // _gcry_burn_stack (80+6*sizeof(void*)); - - while( inlen >= 64 ) - { - transform( hd, inbuf ); - hd->count = 0; - hd->nblocks++; - inlen -= 64; - inbuf += 64; - } - for( ; inlen && hd->count < 64; inlen-- ) - hd->buf[hd->count++] = *inbuf++; - -} - - - -/* The routine final terminates the message-digest computation and - * ends with the desired message digest in mdContext->digest[0...15]. - * The handle is prepared for a new MD5 cycle. - * Returns 16 grub_uint8_ts representing the digest. - */ -static void -md5_final( void *context) -{ - MD5_CONTEXT *hd = context; - grub_uint32_t t, msb, lsb; - grub_uint32_t *p; - - md5_write(hd, NULL, 0); /* flush */; - - t = hd->nblocks; - /* multiply by 64 to make a grub_uint8_t count */ - lsb = t << 6; - msb = t >> 26; - /* add the count */ - t = lsb; - if( (lsb += hd->count) < t ) - msb++; - /* multiply by 8 to make a bit count */ - t = lsb; - lsb <<= 3; - msb <<= 3; - msb |= t >> 29; - - if( hd->count < 56 ) /* enough room */ - { - hd->buf[hd->count++] = 0x80; /* pad */ - while( hd->count < 56 ) - hd->buf[hd->count++] = 0; /* pad */ - } - else /* need one extra block */ - { - hd->buf[hd->count++] = 0x80; /* pad character */ - while( hd->count < 64 ) - hd->buf[hd->count++] = 0; - md5_write(hd, NULL, 0); /* flush */; - grub_memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ - } - /* append the 64 bit count */ - hd->buf[56] = lsb ; - hd->buf[57] = lsb >> 8; - hd->buf[58] = lsb >> 16; - hd->buf[59] = lsb >> 24; - hd->buf[60] = msb ; - hd->buf[61] = msb >> 8; - hd->buf[62] = msb >> 16; - hd->buf[63] = msb >> 24; - transform( hd, hd->buf ); - // _gcry_burn_stack (80+6*sizeof(void*)); - - p = (grub_uint32_t *) hd->buf; -#define X(a) do { *p = grub_le_to_cpu32 (hd->a); p++; } while (0) - X(A); - X(B); - X(C); - X(D); -#undef X - -} - -/** - * GRUB2 Crypto Interface - * Written by Michael Gorven - */ -static grub_err_t -md5 (const char *in, grub_size_t insize, char *out) -{ - MD5_CONTEXT hd; - - md5_init (&hd); - md5_write (&hd, in, insize); - md5_final (&hd); - grub_memcpy (out, hd.buf, 16); - - return GRUB_ERR_NONE; -} - static grub_err_t grub_cmd_xnu_uuid (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { - struct tohash hashme; - grub_uint8_t xnu_uuid[16]; + grub_uint64_t serial; + grub_uint8_t *xnu_uuid; char uuid_string[sizeof ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]; char *ptr; + grub_uint8_t ctx[GRUB_MD_MD5->contextsize]; if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "UUID required"); - hashme.serial = grub_cpu_to_be64 (grub_strtoull (args[0], 0, 16)); - grub_memcpy (hashme.prefix, hash_prefix, sizeof (hashme.prefix)); + serial = grub_cpu_to_be64 (grub_strtoull (args[0], 0, 16)); + + GRUB_MD_MD5->init (&ctx); + GRUB_MD_MD5->write (&ctx, hash_prefix, sizeof (hash_prefix)); + GRUB_MD_MD5->write (&ctx, &serial, sizeof (serial)); + GRUB_MD_MD5->final (&ctx); + xnu_uuid = GRUB_MD_MD5->read (&ctx); - md5 ((char *) &hashme, sizeof (hashme), (char *) xnu_uuid); grub_sprintf (uuid_string, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", (unsigned int) xnu_uuid[0], (unsigned int) xnu_uuid[1], diff --git a/conf/any-emu.rmk b/conf/any-emu.rmk index c7b2e014c..82eef8270 100644 --- a/conf/any-emu.rmk +++ b/conf/any-emu.rmk @@ -26,10 +26,10 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ kern/partition.c kern/reader.c kern/term.c \ kern/rescue_reader.c kern/rescue_parser.c \ lib/arg.c normal/cmdline.c normal/datetime.c normal/misc.c \ - normal/handler.c normal/auth.c normal/autofs.c \ + normal/handler.c normal/auth.c lib/crypto.c normal/autofs.c \ normal/completion.c normal/main.c normal/color.c \ normal/menu.c normal/menu_entry.c normal/menu_viewer.c \ - normal/menu_text.c \ + normal/menu_text.c normal/crypto.c \ script/main.c script/execute.c script/function.c \ script/lexer.c script/script.c grub_script.tab.c \ partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \ @@ -47,7 +47,10 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ commands/parttool.c parttool/msdospart.c \ + lib/libgcrypt-grub/cipher/md5.c \ grub_emu_init.c gnulib/progname.c +grub_emu_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap + ifeq ($(target_cpu), i386) grub_emu_SOURCES += commands/i386/cpuid.c diff --git a/conf/common.rmk b/conf/common.rmk index 4fd005777..c5e4fcecf 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -550,7 +550,7 @@ normal_mod_SOURCES = normal/main.c normal/cmdline.c normal/dyncmd.c \ normal/auth.c normal/autofs.c normal/handler.c \ normal/color.c normal/completion.c normal/datetime.c normal/menu.c \ normal/menu_entry.c normal/menu_text.c normal/menu_viewer.c \ - normal/misc.c + normal/misc.c normal/crypto.c normal_mod_CFLAGS = $(COMMON_CFLAGS) normal_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -650,3 +650,30 @@ pkglib_MODULES += charset.mod charset_mod_SOURCES = lib/charset.c charset_mod_CFLAGS = $(COMMON_CFLAGS) charset_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += crypto.mod +crypto_mod_SOURCES = lib/crypto.c +crypto_mod_CFLAGS = $(COMMON_CFLAGS) +crypto_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += hashsum.mod +hashsum_mod_SOURCES = commands/hashsum.c +hashsum_mod_CFLAGS = $(COMMON_CFLAGS) +hashsum_mod_LDFLAGS = $(COMMON_LDFLAGS) + +pkglib_MODULES += pbkdf2.mod +pbkdf2_mod_SOURCES = lib/pbkdf2.c +pbkdf2_mod_CFLAGS = $(COMMON_CFLAGS) +pbkdf2_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For password_pbkdf2.mod. +pkglib_MODULES += password_pbkdf2.mod +password_pbkdf2_mod_SOURCES = commands/password_pbkdf2.c +password_pbkdf2_mod_CFLAGS = $(COMMON_CFLAGS) +password_pbkdf2_mod_LDFLAGS = $(COMMON_LDFLAGS) + +bin_UTILITIES += grub-mkpasswd-pbkdf2 +grub_mkpasswd_pbkdf2_SOURCES = gnulib/progname.c util/grub-mkpasswd-pbkdf2.c lib/crypto.c lib/libgcrypt-grub/cipher/sha512.c lib/pbkdf2.c util/misc.c kern/err.c +grub_mkpasswd_pbkdf2_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap -DGRUB_MKPASSWD=1 + +include $(srcdir)/conf/gcry.mk diff --git a/include/grub/auth.h b/include/grub/auth.h index 99f9d7260..747334451 100644 --- a/include/grub/auth.h +++ b/include/grub/auth.h @@ -15,24 +15,15 @@ * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ -#ifndef GRUB_AURH_HEADER +#ifndef GRUB_AUTH_HEADER #define GRUB_AUTH_HEADER 1 #include -#include +#include -/* Macros for indistinguishibility. */ -#define GRUB_ACCESS_DENIED grub_error (GRUB_ERR_ACCESS_DENIED, "access denied") -#define GRUB_GET_PASSWORD(string, len) grub_cmdline_get (N_("Enter password:"), \ - string, len, \ - '*', 0, 0) +#define GRUB_AUTH_MAX_PASSLEN 1024 -/* Like strcmp but untimeable. Accepts NULL as second argument. */ -int grub_auth_strcmp (const char *user_input, const char *template); -/* Like strcmp but untimeable and ignores commas in needle. */ -int grub_auth_strword (const char *haystack, const char *needle); - -typedef grub_err_t (*grub_auth_callback_t) (const char*, void *); +typedef grub_err_t (*grub_auth_callback_t) (const char *, const char *, void *); grub_err_t grub_auth_register_authentication (const char *user, grub_auth_callback_t callback, diff --git a/include/grub/crypto.h b/include/grub/crypto.h new file mode 100644 index 000000000..48b52ee65 --- /dev/null +++ b/include/grub/crypto.h @@ -0,0 +1,275 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 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 . + */ + +/* Contains elements based on gcrypt-module.h and gcrypt.h.in. + If it's changed please update this file. */ + +#ifndef GRUB_CRYPTO_HEADER +#define GRUB_CRYPTO_HEADER 1 + +#include +#include +#include + +typedef enum + { + GPG_ERR_NO_ERROR, + GPG_ERR_BAD_MPI, + GPG_ERR_BAD_SECKEY, + GPG_ERR_BAD_SIGNATURE, + GPG_ERR_CIPHER_ALGO, + GPG_ERR_CONFLICT, + GPG_ERR_DECRYPT_FAILED, + GPG_ERR_DIGEST_ALGO, + GPG_ERR_GENERAL, + GPG_ERR_INTERNAL, + GPG_ERR_INV_ARG, + GPG_ERR_INV_CIPHER_MODE, + GPG_ERR_INV_FLAG, + GPG_ERR_INV_KEYLEN, + GPG_ERR_INV_OBJ, + GPG_ERR_INV_OP, + GPG_ERR_INV_SEXP, + GPG_ERR_INV_VALUE, + GPG_ERR_MISSING_VALUE, + GPG_ERR_NO_ENCRYPTION_SCHEME, + GPG_ERR_NO_OBJ, + GPG_ERR_NO_PRIME, + GPG_ERR_NO_SIGNATURE_SCHEME, + GPG_ERR_NOT_FOUND, + GPG_ERR_NOT_IMPLEMENTED, + GPG_ERR_NOT_SUPPORTED, + GPG_ERROR_CFLAGS, + GPG_ERR_PUBKEY_ALGO, + GPG_ERR_SELFTEST_FAILED, + GPG_ERR_TOO_SHORT, + GPG_ERR_UNSUPPORTED, + GPG_ERR_WEAK_KEY, + GPG_ERR_WRONG_KEY_USAGE, + GPG_ERR_WRONG_PUBKEY_ALGO, + GPG_ERR_OUT_OF_MEMORY + } gcry_err_code_t; +#define gpg_err_code_t gcry_err_code_t +#define gpg_error_t gcry_err_code_t + +enum gcry_cipher_modes + { + GCRY_CIPHER_MODE_NONE = 0, /* Not yet specified. */ + GCRY_CIPHER_MODE_ECB = 1, /* Electronic codebook. */ + GCRY_CIPHER_MODE_CFB = 2, /* Cipher feedback. */ + GCRY_CIPHER_MODE_CBC = 3, /* Cipher block chaining. */ + GCRY_CIPHER_MODE_STREAM = 4, /* Used with stream ciphers. */ + GCRY_CIPHER_MODE_OFB = 5, /* Outer feedback. */ + GCRY_CIPHER_MODE_CTR = 6 /* Counter. */ + }; + +/* Type for the cipher_setkey function. */ +typedef gcry_err_code_t (*gcry_cipher_setkey_t) (void *c, + const unsigned char *key, + unsigned keylen); + +/* Type for the cipher_encrypt function. */ +typedef void (*gcry_cipher_encrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf); + +/* Type for the cipher_decrypt function. */ +typedef void (*gcry_cipher_decrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf); + +/* Type for the cipher_stencrypt function. */ +typedef void (*gcry_cipher_stencrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf, + unsigned int n); + +/* Type for the cipher_stdecrypt function. */ +typedef void (*gcry_cipher_stdecrypt_t) (void *c, + unsigned char *outbuf, + const unsigned char *inbuf, + unsigned int n); + +typedef struct gcry_cipher_oid_spec +{ + const char *oid; + int mode; +} gcry_cipher_oid_spec_t; + +/* Module specification structure for ciphers. */ +typedef struct gcry_cipher_spec +{ + const char *name; + const char **aliases; + gcry_cipher_oid_spec_t *oids; + grub_size_t blocksize; + grub_size_t keylen; + grub_size_t contextsize; + gcry_cipher_setkey_t setkey; + gcry_cipher_encrypt_t encrypt; + gcry_cipher_decrypt_t decrypt; + gcry_cipher_stencrypt_t stencrypt; + gcry_cipher_stdecrypt_t stdecrypt; + struct gcry_cipher_spec *next; +} gcry_cipher_spec_t; + +/* Type for the md_init function. */ +typedef void (*gcry_md_init_t) (void *c); + +/* Type for the md_write function. */ +typedef void (*gcry_md_write_t) (void *c, const void *buf, grub_size_t nbytes); + +/* Type for the md_final function. */ +typedef void (*gcry_md_final_t) (void *c); + +/* Type for the md_read function. */ +typedef unsigned char *(*gcry_md_read_t) (void *c); + +typedef struct gcry_md_oid_spec +{ + const char *oidstring; +} gcry_md_oid_spec_t; + +/* Module specification structure for message digests. */ +typedef struct gcry_md_spec +{ + const char *name; + unsigned char *asnoid; + int asnlen; + gcry_md_oid_spec_t *oids; + grub_size_t mdlen; + gcry_md_init_t init; + gcry_md_write_t write; + gcry_md_final_t final; + gcry_md_read_t read; + grub_size_t contextsize; /* allocate this amount of context */ + /* Block size, needed for HMAC. */ + grub_size_t blocksize; + struct gcry_md_spec *next; +} gcry_md_spec_t; + +struct grub_crypto_cipher_handle +{ + const struct gcry_cipher_spec *cipher; + char ctx[0]; +}; + +typedef struct grub_crypto_cipher_handle *grub_crypto_cipher_handle_t; + +struct grub_crypto_hmac_handle; + +const gcry_cipher_spec_t * +grub_crypto_lookup_cipher_by_name (const char *name); + +grub_crypto_cipher_handle_t +grub_crypto_cipher_open (const struct gcry_cipher_spec *cipher); + +gcry_err_code_t +grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher, + const unsigned char *key, + unsigned keylen); + +void +grub_crypto_cipher_close (grub_crypto_cipher_handle_t cipher); + +void +grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size); + +gcry_err_code_t +grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size); + +gcry_err_code_t +grub_crypto_ecb_encrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size); +gcry_err_code_t +grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size, + void *iv_in); +gcry_err_code_t +grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size, + void *iv); +void +grub_cipher_register (gcry_cipher_spec_t *cipher); +void +grub_cipher_unregister (gcry_cipher_spec_t *cipher); +void +grub_md_register (gcry_md_spec_t *digest); +void +grub_md_unregister (gcry_md_spec_t *cipher); +void +grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, + grub_size_t inlen); +const gcry_md_spec_t * +grub_crypto_lookup_md_by_name (const char *name); + +grub_err_t +grub_crypto_gcry_error (gcry_err_code_t in); + +void grub_burn_stack (grub_size_t size); + +struct grub_crypto_hmac_handle * +grub_crypto_hmac_init (const struct gcry_md_spec *md, + const void *key, grub_size_t keylen); +void +grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data, + grub_size_t datalen); +gcry_err_code_t +grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out); + +gcry_err_code_t +grub_crypto_hmac_buffer (const struct gcry_md_spec *md, + const void *key, grub_size_t keylen, + void *data, grub_size_t datalen, void *out); + +extern gcry_md_spec_t _gcry_digest_spec_md5; +extern gcry_md_spec_t _gcry_digest_spec_sha1; +extern gcry_md_spec_t _gcry_digest_spec_sha256; +extern gcry_md_spec_t _gcry_digest_spec_sha512; +#define GRUB_MD_MD5 ((const gcry_md_spec_t *) &_gcry_digest_spec_md5) +#define GRUB_MD_SHA1 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha1) +#define GRUB_MD_SHA256 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha256) +#define GRUB_MD_SHA512 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha512) + +/* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant + of digest supplied by MD. Inputs are the password P of length PLEN, + the salt S of length SLEN, the iteration counter C (> 0), and the + desired derived output length DKLEN. Output buffer is DK which + must have room for at least DKLEN octets. The output buffer will + be filled with the derived data. */ +gcry_err_code_t +grub_crypto_pbkdf2 (const struct gcry_md_spec *md, + const grub_uint8_t *P, grub_size_t Plen, + const grub_uint8_t *S, grub_size_t Slen, + unsigned int c, + grub_uint8_t *DK, grub_size_t dkLen); + +int +grub_crypto_memcmp (const void *a, const void *b, grub_size_t n); + +int +grub_password_get (char buf[], unsigned buf_size); + +/* For indistinguishibility. */ +#define GRUB_ACCESS_DENIED grub_error (GRUB_ERR_ACCESS_DENIED, "Access denied.") + +extern void (*grub_crypto_autoload_hook) (const char *name); + +#endif diff --git a/include/grub/disk.h b/include/grub/disk.h index 6efbfe58a..e60b1f3de 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -42,6 +42,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_PXE_ID, GRUB_DISK_DEVICE_SCSI_ID, GRUB_DISK_DEVICE_FILE_ID, + GRUB_DISK_DEVICE_LUKS_ID }; struct grub_disk; diff --git a/include/grub/normal.h b/include/grub/normal.h index e55553de5..66b427bcb 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -95,6 +95,8 @@ void read_command_list (void); /* Defined in `autofs.c'. */ void read_fs_list (void); +void read_crypto_list (void); + #ifdef GRUB_UTIL void grub_normal_init (void); diff --git a/lib/crypto.c b/lib/crypto.c new file mode 100644 index 000000000..d11f0994f --- /dev/null +++ b/lib/crypto.c @@ -0,0 +1,453 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 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 . + */ + +#include +#include +#include +#include + +struct grub_crypto_hmac_handle +{ + const struct gcry_md_spec *md; + void *ctx; + void *opad; +}; + +static gcry_cipher_spec_t *grub_ciphers = NULL; +static gcry_md_spec_t *grub_digests = NULL; + +void (*grub_crypto_autoload_hook) (const char *name) = NULL; + +/* Based on libgcrypt-1.4.4/src/misc.c. */ +void +grub_burn_stack (grub_size_t size) +{ + char buf[64]; + + grub_memset (buf, 0, sizeof (buf)); + if (size > sizeof (buf)) + grub_burn_stack (size - sizeof (buf)); +} + + +void +grub_cipher_register (gcry_cipher_spec_t *cipher) +{ + cipher->next = grub_ciphers; + grub_ciphers = cipher; +} + +void +grub_cipher_unregister (gcry_cipher_spec_t *cipher) +{ + gcry_cipher_spec_t **ciph; + for (ciph = &grub_ciphers; *ciph; ciph = &((*ciph)->next)) + if (*ciph == cipher) + { + *ciph = (*ciph)->next; + break; + } +} + +void +grub_md_register (gcry_md_spec_t *digest) +{ + digest->next = grub_digests; + grub_digests = digest; +} + +void +grub_md_unregister (gcry_md_spec_t *cipher) +{ + gcry_md_spec_t **ciph; + for (ciph = &grub_digests; *ciph; ciph = &((*ciph)->next)) + if (*ciph == cipher) + { + *ciph = (*ciph)->next; + break; + } +} + +void +grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, + grub_size_t inlen) +{ + grub_uint8_t ctx[hash->contextsize]; + hash->init (&ctx); + hash->write (&ctx, in, inlen); + hash->final (&ctx); + grub_memcpy (out, hash->read (&ctx), hash->mdlen); +} + +const gcry_md_spec_t * +grub_crypto_lookup_md_by_name (const char *name) +{ + const gcry_md_spec_t *md; + int first = 1; + while (1) + { + for (md = grub_digests; md; md = md->next) + if (grub_strcasecmp (name, md->name) == 0) + return md; + if (grub_crypto_autoload_hook && first) + grub_crypto_autoload_hook (name); + else + return NULL; + first = 0; + } +} + +const gcry_cipher_spec_t * +grub_crypto_lookup_cipher_by_name (const char *name) +{ + const gcry_cipher_spec_t *ciph; + int first = 1; + while (1) + { + for (ciph = grub_ciphers; ciph; ciph = ciph->next) + { + const char **alias; + if (grub_strcasecmp (name, ciph->name) == 0) + return ciph; + if (!ciph->aliases) + continue; + for (alias = ciph->aliases; *alias; alias++) + if (grub_strcasecmp (name, *alias) == 0) + return ciph; + } + if (grub_crypto_autoload_hook && first) + grub_crypto_autoload_hook (name); + else + return NULL; + first = 0; + } +} + + +grub_crypto_cipher_handle_t +grub_crypto_cipher_open (const struct gcry_cipher_spec *cipher) +{ + grub_crypto_cipher_handle_t ret; + ret = grub_malloc (sizeof (*ret) + cipher->contextsize); + if (!ret) + return NULL; + ret->cipher = cipher; + return ret; +} + +gcry_err_code_t +grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher, + const unsigned char *key, + unsigned keylen) +{ + return cipher->cipher->setkey (cipher->ctx, key, keylen); +} + + +void +grub_crypto_cipher_close (grub_crypto_cipher_handle_t cipher) +{ + grub_free (cipher); +} + + +void +grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size) +{ + const grub_uint8_t *in1ptr = in1, *in2ptr = in2; + grub_uint8_t *outptr = out; + while (size--) + { + *outptr = *in1ptr ^ *in2ptr; + in1ptr++; + in2ptr++; + outptr++; + } +} + +gcry_err_code_t +grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size) +{ + grub_uint8_t *inptr, *outptr, *end; + if (!cipher->cipher->decrypt) + return GPG_ERR_NOT_SUPPORTED; + if (size % cipher->cipher->blocksize != 0) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + cipher->cipher->decrypt (cipher->ctx, outptr, inptr); + return GPG_ERR_NO_ERROR; +} + +gcry_err_code_t +grub_crypto_ecb_encrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size) +{ + grub_uint8_t *inptr, *outptr, *end; + if (!cipher->cipher->encrypt) + return GPG_ERR_NOT_SUPPORTED; + if (size % cipher->cipher->blocksize != 0) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + cipher->cipher->encrypt (cipher->ctx, outptr, inptr); + return GPG_ERR_NO_ERROR; +} + +gcry_err_code_t +grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size, + void *iv_in) +{ + grub_uint8_t *inptr, *outptr, *end; + void *iv; + if (!cipher->cipher->decrypt) + return GPG_ERR_NOT_SUPPORTED; + if (size % cipher->cipher->blocksize != 0) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + iv = iv_in; + for (inptr = in, outptr = out; inptr < end; + inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + { + grub_crypto_xor (outptr, inptr, iv, cipher->cipher->blocksize); + cipher->cipher->encrypt (cipher->ctx, outptr, outptr); + iv = outptr; + } + grub_memcpy (iv_in, iv, cipher->cipher->blocksize); + return GPG_ERR_NO_ERROR; +} + +gcry_err_code_t +grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher, + void *out, void *in, grub_size_t size, + void *iv) +{ + grub_uint8_t *inptr, *outptr, *end; + grub_uint8_t ivt[cipher->cipher->blocksize]; + if (!cipher->cipher->decrypt) + return GPG_ERR_NOT_SUPPORTED; + if (size % cipher->cipher->blocksize != 0) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + { + grub_memcpy (ivt, inptr, cipher->cipher->blocksize); + cipher->cipher->decrypt (cipher->ctx, outptr, inptr); + grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize); + grub_memcpy (iv, ivt, cipher->cipher->blocksize); + } + return GPG_ERR_NO_ERROR; +} + +/* Based on gcry/cipher/md.c. */ +struct grub_crypto_hmac_handle * +grub_crypto_hmac_init (const struct gcry_md_spec *md, + const void *key, grub_size_t keylen) +{ + grub_uint8_t *helpkey = NULL; + grub_uint8_t *ipad = NULL, *opad = NULL; + void *ctx = NULL; + struct grub_crypto_hmac_handle *ret = NULL; + unsigned i; + + if (md->mdlen > md->blocksize) + return NULL; + + ctx = grub_malloc (md->contextsize); + if (!ctx) + goto err; + + if ( keylen > md->blocksize ) + { + helpkey = grub_malloc (md->mdlen); + if (!helpkey) + goto err; + grub_crypto_hash (md, helpkey, key, keylen); + + key = helpkey; + keylen = md->mdlen; + } + + ipad = grub_zalloc (md->blocksize); + if (!ipad) + goto err; + + opad = grub_zalloc (md->blocksize); + if (!opad) + goto err; + + grub_memcpy ( ipad, key, keylen ); + grub_memcpy ( opad, key, keylen ); + for (i=0; i < md->blocksize; i++ ) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + grub_free (helpkey); + helpkey = NULL; + + md->init (ctx); + + md->write (ctx, ipad, md->blocksize); /* inner pad */ + grub_memset (ipad, 0, md->blocksize); + grub_free (ipad); + ipad = NULL; + + ret = grub_malloc (sizeof (*ret)); + if (!ret) + goto err; + + ret->md = md; + ret->ctx = ctx; + ret->opad = opad; + + return ret; + + err: + grub_free (helpkey); + grub_free (ctx); + grub_free (ipad); + grub_free (opad); + return NULL; +} + +void +grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data, + grub_size_t datalen) +{ + hnd->md->write (hnd->ctx, data, datalen); +} + +gcry_err_code_t +grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out) +{ + grub_uint8_t *p; + grub_uint8_t *ctx2; + + ctx2 = grub_malloc (hnd->md->contextsize); + if (!ctx2) + return GPG_ERR_OUT_OF_MEMORY; + + hnd->md->final (hnd->ctx); + hnd->md->read (hnd->ctx); + p = hnd->md->read (hnd->ctx); + + hnd->md->init (ctx2); + hnd->md->write (ctx2, hnd->opad, hnd->md->blocksize); + hnd->md->write (ctx2, p, hnd->md->mdlen); + hnd->md->final (ctx2); + grub_memset (hnd->opad, 0, hnd->md->blocksize); + grub_free (hnd->opad); + grub_memset (hnd->ctx, 0, hnd->md->contextsize); + grub_free (hnd->ctx); + + grub_memcpy (out, hnd->md->read (ctx2), hnd->md->mdlen); + grub_memset (ctx2, 0, hnd->md->contextsize); + grub_free (ctx2); + + grub_memset (hnd, 0, sizeof (*hnd)); + grub_free (hnd); + + return GPG_ERR_NO_ERROR; +} + +gcry_err_code_t +grub_crypto_hmac_buffer (const struct gcry_md_spec *md, + const void *key, grub_size_t keylen, + void *data, grub_size_t datalen, void *out) +{ + struct grub_crypto_hmac_handle *hnd; + + hnd = grub_crypto_hmac_init (md, key, keylen); + if (!hnd) + return GPG_ERR_OUT_OF_MEMORY; + + grub_crypto_hmac_write (hnd, data, datalen); + return grub_crypto_hmac_fini (hnd, out); +} + + +grub_err_t +grub_crypto_gcry_error (gcry_err_code_t in) +{ + if (in == GPG_ERR_NO_ERROR) + return GRUB_ERR_NONE; + return GRUB_ACCESS_DENIED; +} + +int +grub_crypto_memcmp (const void *a, const void *b, grub_size_t n) +{ + register grub_size_t counter = 0; + const grub_uint8_t *pa, *pb; + + for (pa = a, pb = b; n; pa++, pb++, n--) + { + if (*pa != *pb) + counter++; + } + + return !!counter; +} + +#ifndef GRUB_MKPASSWD +int +grub_password_get (char buf[], unsigned buf_size) +{ + unsigned cur_len = 0; + int key; + + while (1) + { + key = GRUB_TERM_ASCII_CHAR (grub_getkey ()); + if (key == '\n' || key == '\r') + break; + + if (key == '\e') + { + cur_len = 0; + break; + } + + if (key == '\b') + { + cur_len--; + continue; + } + + if (!grub_isprint (key)) + continue; + + if (cur_len + 2 < buf_size) + buf[cur_len++] = key; + } + + grub_memset (buf + cur_len, 0, buf_size - cur_len); + + grub_putchar ('\n'); + grub_refresh (); + + return (key != '\e'); +} +#endif diff --git a/lib/libgcrypt_wrap/cipher_wrap.h b/lib/libgcrypt_wrap/cipher_wrap.h new file mode 100644 index 000000000..e05f0cda9 --- /dev/null +++ b/lib/libgcrypt_wrap/cipher_wrap.h @@ -0,0 +1,86 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifndef GRUB_GCRY_WRAP_HEADER +#define GRUB_GCRY_WRAP_HEADER 1 + +#include +#include +#include +#include +#include + +#define __GNU_LIBRARY__ + +#define DIM ARRAY_SIZE + +typedef grub_uint64_t u64; +typedef grub_uint32_t u32; +typedef grub_uint16_t u16; +typedef grub_uint8_t byte; +typedef grub_size_t size_t; + +#define U64_C(c) (c ## ULL) + +#define _gcry_burn_stack grub_burn_stack +#define log_error(fmt, args...) grub_dprintf ("crypto", fmt, ## args) + + +#define PUBKEY_FLAG_NO_BLINDING (1 << 0) + +#define CIPHER_INFO_NO_WEAK_KEY 1 + +#define HAVE_U64_TYPEDEF 1 + +typedef union { + int a; + short b; + char c[1]; + long d; +#ifdef HAVE_U64_TYPEDEF + u64 e; +#endif + float f; + double g; +} PROPERLY_ALIGNED_TYPE; + +#define gcry_assert(x) grub_assert_real(__FILE__, __LINE__, x) + +static inline void +grub_assert_real (const char *file, int line, int cond) +{ + if (!cond) + grub_fatal ("Assertion failed at %s:%d\n", file, line); +} + +/* Selftests are in separate modules. */ +static inline char * +selftest (void) +{ + return NULL; +} + +static inline int +fips_mode (void) +{ + return 0; +} + +#define memset grub_memset + +#endif diff --git a/lib/pbkdf2.c b/lib/pbkdf2.c new file mode 100644 index 000000000..083446ab9 --- /dev/null +++ b/lib/pbkdf2.c @@ -0,0 +1,102 @@ +/* gc-pbkdf2-sha1.c --- Password-Based Key Derivation Function a'la PKCS#5 + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc. + + This program 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 2, or (at your option) + any later version. + + This program 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 this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. */ +/* Imported from gnulib. */ + +#include +#include +#include + +/* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant + of digest supplied by MD. Inputs are the password P of length PLEN, + the salt S of length SLEN, the iteration counter C (> 0), and the + desired derived output length DKLEN. Output buffer is DK which + must have room for at least DKLEN octets. The output buffer will + be filled with the derived data. */ +gcry_err_code_t +grub_crypto_pbkdf2 (const struct gcry_md_spec *md, + const grub_uint8_t *P, grub_size_t Plen, + const grub_uint8_t *S, grub_size_t Slen, + unsigned int c, + grub_uint8_t *DK, grub_size_t dkLen) +{ + unsigned int hLen = md->mdlen; + grub_uint8_t U[md->mdlen]; + grub_uint8_t T[md->mdlen]; + unsigned int u; + unsigned int l; + unsigned int r; + unsigned int i; + unsigned int k; + gcry_err_code_t rc; + grub_uint8_t *tmp; + grub_size_t tmplen = Slen + 4; + + if (c == 0) + return GPG_ERR_INV_ARG; + + if (dkLen == 0) + return GPG_ERR_INV_ARG; + + if (dkLen > 4294967295U) + return GPG_ERR_INV_ARG; + + l = ((dkLen - 1) / hLen) + 1; + r = dkLen - (l - 1) * hLen; + + tmp = grub_malloc (tmplen); + if (tmp == NULL) + return GPG_ERR_OUT_OF_MEMORY; + + grub_memcpy (tmp, S, Slen); + + for (i = 1; i <= l; i++) + { + grub_memset (T, 0, hLen); + + for (u = 1; u <= c; u++) + { + if (u == 1) + { + tmp[Slen + 0] = (i & 0xff000000) >> 24; + tmp[Slen + 1] = (i & 0x00ff0000) >> 16; + tmp[Slen + 2] = (i & 0x0000ff00) >> 8; + tmp[Slen + 3] = (i & 0x000000ff) >> 0; + + rc = grub_crypto_hmac_buffer (md, P, Plen, tmp, tmplen, U); + } + else + rc = grub_crypto_hmac_buffer (md, P, Plen, U, hLen, U); + + if (rc != GPG_ERR_NO_ERROR) + { + grub_free (tmp); + return rc; + } + + for (k = 0; k < hLen; k++) + T[k] ^= U[k]; + } + + grub_memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen); + } + + grub_free (tmp); + + return GPG_ERR_NO_ERROR; +} diff --git a/normal/auth.c b/normal/auth.c index 54107fe01..240a77aee 100644 --- a/normal/auth.c +++ b/normal/auth.c @@ -36,58 +36,6 @@ struct grub_auth_user struct grub_auth_user *users = NULL; -int -grub_auth_strcmp (const char *s1, const char *s2) -{ - int ret; - grub_uint64_t end; - - end = grub_get_time_ms () + 100; - ret = grub_strcmp (s1, s2); - - /* This prevents an attacker from deriving information about the - password from the time it took to execute this function. */ - while (grub_get_time_ms () < end); - - return ret; -} - -static int -grub_iswordseparator (int c) -{ - return (grub_isspace (c) || c == ',' || c == ';' || c == '|' || c == '&'); -} - -int -grub_auth_strword (const char *haystack, const char *needle) -{ - const char *n_pos = needle; - int found = 0; - - while (grub_iswordseparator (*haystack)) - haystack++; - - while (*haystack) - { - int ok = 1; - /* Crawl both the needle and the haystack word we're on. */ - while(*haystack && !grub_iswordseparator (*haystack)) - { - if (*haystack == *n_pos && ok) - n_pos++; - else - ok = 0; - - haystack++; - } - - if (ok) - found = 1; - } - - return found; -} - grub_err_t grub_auth_register_authentication (const char *user, grub_auth_callback_t callback, @@ -194,8 +142,8 @@ is_authenticated (const char *userlist) return 0; name = ((struct grub_auth_user *) item)->name; - return (userlist && grub_auth_strword (userlist, name)) - || grub_auth_strword (superusers, name); + return (userlist && grub_strword (userlist, name)) + || grub_strword (superusers, name); } superusers = grub_env_get ("superusers"); @@ -213,11 +161,12 @@ grub_auth_check_authentication (const char *userlist) struct grub_auth_user *cur = NULL; grub_err_t err; static unsigned long punishment_delay = 1; + char entered[GRUB_AUTH_MAX_PASSLEN]; auto int hook (grub_list_t item); int hook (grub_list_t item) { - if (grub_auth_strcmp (login, ((struct grub_auth_user *) item)->name) == 0) + if (grub_strcmp (login, ((struct grub_auth_user *) item)->name) == 0) cur = (struct grub_auth_user *) item; return 0; } @@ -242,22 +191,17 @@ grub_auth_check_authentication (const char *userlist) 0, 0, 0)) goto access_denied; + grub_printf ("Enter password: "); + + if (!grub_password_get (entered, GRUB_AUTH_MAX_PASSLEN)) + goto access_denied; + grub_list_iterate (GRUB_AS_LIST (users), hook); if (!cur || ! cur->callback) - { - grub_list_iterate (GRUB_AS_LIST (users), hook_any); + goto access_denied; - /* No users present at all. */ - if (!cur) - goto access_denied; - - /* Display any of available authentication schemes. */ - err = cur->callback (login, 0); - - goto access_denied; - } - err = cur->callback (login, cur->arg); + err = cur->callback (login, entered, cur->arg); if (is_authenticated (userlist)) { punishment_delay = 1; diff --git a/normal/crypto.c b/normal/crypto.c new file mode 100644 index 000000000..67330272a --- /dev/null +++ b/normal/crypto.c @@ -0,0 +1,153 @@ +/* crypto.c - support crypto autoload */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include + +struct load_spec +{ + struct load_spec *next; + char *name; + char *modname; +}; + +struct load_spec *crypto_specs = NULL; + +static void +grub_crypto_autoload (const char *name) +{ + struct load_spec *cur; + grub_dl_t mod; + + for (cur = crypto_specs; cur; cur = cur->next) + if (grub_strcasecmp (name, cur->name) == 0) + { + mod = grub_dl_load (cur->modname); + if (mod) + grub_dl_ref (mod); + grub_errno = GRUB_ERR_NONE; + } +} + +static void +grub_crypto_spec_free (void) +{ + struct load_spec *cur, *next; + for (cur = crypto_specs; cur; cur = next) + { + next = cur->next; + grub_free (cur->name); + grub_free (cur->modname); + grub_free (cur); + } + crypto_specs = NULL; +} + + +/* Read the file crypto.lst for auto-loading. */ +void +read_crypto_list (void) +{ + const char *prefix; + char *filename; + grub_file_t file; + char *buf = NULL; + + prefix = grub_env_get ("prefix"); + if (!prefix) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + filename = grub_malloc (grub_strlen (prefix) + sizeof ("/crypto.lst")); + if (!filename) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + grub_sprintf (filename, "%s/crypto.lst", prefix); + file = grub_file_open (filename); + if (!file) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + /* Override previous commands.lst. */ + grub_crypto_spec_free (); + + for (;; grub_free (buf)) + { + char *p, *name; + struct load_spec *cur; + + buf = grub_file_getline (file); + + if (! buf) + break; + + name = buf; + + p = grub_strchr (name, ':'); + if (! p) + continue; + + *p = '\0'; + while (*++p == ' ') + ; + + cur = grub_malloc (sizeof (*cur)); + if (!cur) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + cur->name = grub_strdup (name); + if (! name) + { + grub_errno = GRUB_ERR_NONE; + grub_free (cur); + continue; + } + + cur->modname = grub_strdup (p); + if (! cur->modname) + { + grub_errno = GRUB_ERR_NONE; + grub_free (cur); + grub_free (cur->name); + continue; + } + cur->next = crypto_specs; + crypto_specs = cur; + } + + grub_file_close (file); + + grub_errno = GRUB_ERR_NONE; + + grub_crypto_autoload_hook = grub_crypto_autoload; +} diff --git a/normal/main.c b/normal/main.c index 23de7e238..568cd158d 100644 --- a/normal/main.c +++ b/normal/main.c @@ -428,6 +428,7 @@ grub_normal_execute (const char *config, int nested, int batch) read_command_list (); read_fs_list (); read_handler_list (); + read_crypto_list (); grub_command_execute ("parser.grub", 0, 0); reader_nested = nested; diff --git a/util/grub-mkpasswd-pbkdf2.c b/util/grub-mkpasswd-pbkdf2.c new file mode 100644 index 000000000..2f7c5efaa --- /dev/null +++ b/util/grub-mkpasswd-pbkdf2.c @@ -0,0 +1,342 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1992-1999,2001,2003,2004,2005,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 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "progname.h" + +/* Few functions to make crypto happy. */ +void * +grub_memmove (void *dest, const void *src, grub_size_t n) +{ + return memmove (dest, src, n); +} + +void * +grub_memset (void *s, int c, grub_size_t n) +{ + return memset (s, c, n); +} + +int +grub_vprintf (const char *fmt, va_list args) +{ + return vprintf (fmt, args); +} + +int +grub_vsprintf (char *str, const char *fmt, va_list args) +{ + return vsprintf (str, fmt, args); +} + +void +grub_abort (void) +{ + abort (); +} + +static struct option options[] = + { + {"iteration_count", required_argument, 0, 'c'}, + {"buflen", required_argument, 0, 'l'}, + {"saltlen", required_argument, 0, 's'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + }; + +static void +usage (int status) +{ + if (status) + fprintf (stderr, "Try ``grub-scrypt --help'' for more information.\n"); + else + printf ("\ +Usage: grub-scrypt [OPTIONS]\n\ +\nOptions:\n\ + -c number, --iteration-count=number Number of PBKDF2 iterations\n\ + -l number, --buflen=number Length of generated hash\n\ + -s number, --salt=number Length of salt\n\ +\n\ +Report bugs to <%s>.\n", PACKAGE_BUGREPORT); + + exit (status); +} + +static void +hexify (char *hex, grub_uint8_t *bin, grub_size_t n) +{ + while (n--) + { + if (((*bin & 0xf0) >> 4) < 10) + *hex = ((*bin & 0xf0) >> 4) + '0'; + else + *hex = ((*bin & 0xf0) >> 4) + 'A' - 10; + hex++; + + if ((*bin & 0xf) < 10) + *hex = (*bin & 0xf) + '0'; + else + *hex = (*bin & 0xf) + 'A' - 10; + hex++; + bin++; + } + *hex = 0; +} + +int +main (int argc, char *argv[]) +{ + unsigned int c = 10000, buflen = 64, saltlen = 64; + char *pass1, *pass2; + char *bufhex, *salthex; + gcry_err_code_t gcry_err; + grub_uint8_t *buf, *salt; + ssize_t nr; + FILE *in, *out; + struct termios s, t; + int tty_changed; + + set_program_name (argv[0]); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + /* Check for options. */ + while (1) + { + int c = getopt_long (argc, argv, "c:l:s:hvV", options, 0); + + if (c == -1) + break; + + switch (c) + { + case 'c': + c = strtoul (optarg, NULL, 0); + break; + + case 'l': + buflen = strtoul (optarg, NULL, 0); + break; + + case 's': + saltlen = strtoul (optarg, NULL, 0); + break; + + case 'h': + usage (0); + return 0; + + case 'V': + printf ("%s (%s) %s\n", program_name, + PACKAGE_NAME, PACKAGE_VERSION); + return 0; + + default: + usage (1); + return 1; + } + } + + bufhex = malloc (buflen * 2 + 1); + if (!bufhex) + grub_util_error ("Out of memory"); + buf = malloc (buflen); + if (!buf) + { + free (bufhex); + grub_util_error ("Out of memory"); + } + + salt = malloc (saltlen); + if (!salt) + { + free (bufhex); + free (buf); + grub_util_error ("Out of memory"); + } + salthex = malloc (saltlen * 2 + 1); + if (!salthex) + { + free (salt); + free (bufhex); + free (buf); + grub_util_error ("Out of memory"); + } + + /* Disable echoing. Based on glibc. */ + in = fopen ("/dev/tty", "w+c"); + if (in == NULL) + { + in = stdin; + out = stderr; + } + else + out = in; + + if (tcgetattr (fileno (in), &t) == 0) + { + /* Save the old one. */ + s = t; + /* Tricky, tricky. */ + t.c_lflag &= ~(ECHO|ISIG); + tty_changed = (tcsetattr (fileno (in), TCSAFLUSH, &t) == 0); + } + else + tty_changed = 0; + + printf ("Enter password: "); + pass1 = NULL; + { + grub_size_t n; + nr = getline (&pass1, &n, stdin); + } + if (nr < 0 || !pass1) + { + free (buf); + free (bufhex); + free (salthex); + free (salt); + /* Restore the original setting. */ + if (tty_changed) + (void) tcsetattr (fileno (in), TCSAFLUSH, &s); + grub_util_error ("Failure to read password"); + } + if (nr >= 1 && pass1[nr-1] == '\n') + pass1[nr-1] = 0; + + printf ("\nReenter password: "); + pass2 = NULL; + { + grub_size_t n; + nr = getline (&pass2, &n, stdin); + } + /* Restore the original setting. */ + if (tty_changed) + (void) tcsetattr (fileno (in), TCSAFLUSH, &s); + printf ("\n"); + + if (nr < 0 || !pass2) + { + memset (pass1, 0, strlen (pass1)); + free (pass1); + free (buf); + free (bufhex); + free (salthex); + free (salt); + grub_util_error ("Failure to read password"); + } + if (nr >= 1 && pass2[nr-1] == '\n') + pass2[nr-1] = 0; + + if (strcmp (pass1, pass2) != 0) + { + memset (pass1, 0, strlen (pass1)); + memset (pass2, 0, strlen (pass2)); + free (pass1); + free (pass2); + free (buf); + free (bufhex); + free (salthex); + free (salt); + grub_util_error ("Passwords don't match"); + } + memset (pass2, 0, strlen (pass2)); + free (pass2); + +#if ! defined (__linux__) && ! defined (__FreeBSD__) + printf ("WARNING: your random generator isn't known to be secure\n"); +#endif + + { + FILE *f; + size_t rd; + f = fopen ("/dev/random", "rb"); + if (!f) + { + memset (pass1, 0, strlen (pass1)); + free (pass1); + free (buf); + free (bufhex); + free (salthex); + free (salt); + fclose (f); + grub_util_error ("Couldn't retrieve random data for salt"); + } + rd = fread (salt, 1, saltlen, f); + if (rd != saltlen) + { + fclose (f); + memset (pass1, 0, strlen (pass1)); + free (pass1); + free (buf); + free (bufhex); + free (salthex); + free (salt); + fclose (f); + grub_util_error ("Couldn't retrieve random data for salt"); + } + fclose (f); + } + + gcry_err = grub_crypto_pbkdf2 (GRUB_MD_SHA512, + (grub_uint8_t *) pass1, strlen (pass1), + salt, saltlen, + c, buf, buflen); + memset (pass1, 0, strlen (pass1)); + free (pass1); + + if (gcry_err) + { + memset (buf, 0, buflen); + memset (bufhex, 0, 2 * buflen); + free (buf); + free (bufhex); + memset (salt, 0, saltlen); + memset (salthex, 0, 2 * saltlen); + free (salt); + free (salthex); + grub_util_error ("Cryptographic error number %d", gcry_err); + } + + hexify (bufhex, buf, buflen); + hexify (salthex, salt, saltlen); + + printf ("Your PBKDF2 is grub.pbkdf2.sha512.%d.%s.%s\n", c, salthex, bufhex); + memset (buf, 0, buflen); + memset (bufhex, 0, 2 * buflen); + free (buf); + free (bufhex); + memset (salt, 0, saltlen); + memset (salthex, 0, 2 * saltlen); + free (salt); + free (salthex); + + return 0; +} diff --git a/util/import_gcry.py b/util/import_gcry.py index 698e6cffe..d71924d53 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -61,12 +61,30 @@ mdblocksizes = {"_gcry_digest_spec_crc32" : 64, "_gcry_digest_spec_tiger" : 64, "_gcry_digest_spec_whirlpool" : 64} +cryptolist = open (os.path.join (cipher_dir_out, "crypto.lst"), "w") + +# rijndael is the only cipher using aliases. So no need for mangling, just +# hardcode it +cryptolist.write ("RIJNDAEL: gcry_rijndael\n"); +cryptolist.write ("RIJNDAEL192: gcry_rijndael\n"); +cryptolist.write ("RIJNDAEL256: gcry_rijndael\n"); +cryptolist.write ("AES128: gcry_rijndael\n"); +cryptolist.write ("AES-128: gcry_rijndael\n"); +cryptolist.write ("AES-192: gcry_rijndael\n"); +cryptolist.write ("AES-256: gcry_rijndael\n"); + for cipher_file in cipher_files: infile = os.path.join (cipher_dir_in, cipher_file) outfile = os.path.join (cipher_dir_out, cipher_file) if cipher_file == "ChangeLog": continue chlognew = " * %s" % cipher_file + if re.match ("(Manifest|Makefile\.am|ac\.c|cipher\.c|hash-common\.c|hmac-tests\.c|md\.c|pubkey\.c)$", cipher_file): + chlog = "%s%s: Removed\n" % (chlog, chlognew) + continue + # Autogenerated files. Not even worth mentionning in ChangeLog + if re.match ("Makefile\.in$", cipher_file): + continue nch = False if re.match (".*\.[ch]$", cipher_file): isc = re.match (".*\.c$", cipher_file) @@ -80,8 +98,21 @@ for cipher_file in cipher_files: skip = False skip2 = False ismd = False + iscryptostart = False iscomma = False + isglue = False + skip_statement = False + if isc: + modname = cipher_file [0:len(cipher_file) - 2] + if re.match (".*-glue$", modname): + modname = modname.replace ("-glue", "") + isglue = True + modname = "gcry_%s" % modname for line in f: + if skip_statement: + if not re.search (";", line) is None: + skip_statement = False + continue if skip: if line[0] == "}": skip = False @@ -90,6 +121,12 @@ for cipher_file in cipher_files: if not re.search (" *};", line) is None: skip2 = False continue + if iscryptostart: + s = re.search (" *\"([A-Z0-9_a-z]*)\"", line) + if not s is None: + sg = s.groups()[0] + cryptolist.write (("%s: %s\n") % (sg, modname)) + iscryptostart = False if ismd: if not re.search (" *};", line) is None: if not mdblocksizes.has_key (mdname): @@ -100,10 +137,22 @@ for cipher_file in cipher_files: fw.write (" .blocksize = %s\n" % mdblocksizes [mdname]) ismd = False iscomma = not re.search (",$", line) is None + # Used only for selftests. + m = re.match ("(static byte|static unsigned char) (weak_keys_chksum)\[[0-9]*\] =", line) + if not m is None: + skip = True + fname = m.groups ()[1] + chmsg = "(%s): Removed." % fname + if nch: + chlognew = "%s\n %s" % (chlognew, chmsg) + else: + chlognew = "%s %s" % (chlognew, chmsg) + nch = True + continue if hold: hold = False # We're optimising for size. - if not re.match ("(run_selftests|selftest|_gcry_aes_c.._..c|_gcry_[a-z0-9]*_hash_buffer)", line) is None: + if not re.match ("(run_selftests|selftest|_gcry_aes_c.._..c|_gcry_[a-z0-9]*_hash_buffer|tripledes_set2keys|do_tripledes_set_extra_info)", line) is None: skip = True fname = re.match ("[a-zA-Z0-9_]*", line).group () chmsg = "(%s): Removed." % fname @@ -127,16 +176,20 @@ for cipher_file in cipher_files: continue m = re.match ("gcry_cipher_spec_t", line) if isc and not m is None: + assert (not iscryptostart) ciphername = line [len ("gcry_cipher_spec_t"):].strip () ciphername = re.match("[a-zA-Z0-9_]*",ciphername).group () ciphernames.append (ciphername) + iscryptostart = True m = re.match ("gcry_md_spec_t", line) if isc and not m is None: assert (not ismd) + assert (not iscryptostart) mdname = line [len ("gcry_md_spec_t"):].strip () mdname = re.match("[a-zA-Z0-9_]*",mdname).group () mdnames.append (mdname) ismd = True + iscryptostart = True m = re.match ("static const char \*selftest.*;$", line) if not m is None: fname = line[len ("static const char \*"):] @@ -148,11 +201,18 @@ for cipher_file in cipher_files: chlognew = "%s %s" % (chlognew, chmsg) nch = True continue - m = re.match ("(static const char( |)\*|static gpg_err_code_t|void)$", line) + m = re.match ("(static const char( |)\*|static gpg_err_code_t|void|static int|static gcry_err_code_t)$", line) if not m is None: hold = True holdline = line continue + m = re.match ("static int tripledes_set2keys \(.*\);", line) + if not m is None: + continue + m = re.match ("static int tripledes_set2keys \(", line) + if not m is None: + skip_statement = True + continue m = re.match ("cipher_extra_spec_t", line) if isc and not m is None: skip2 = True @@ -179,14 +239,11 @@ for cipher_file in cipher_files: continue fw.write (line) if len (ciphernames) > 0 or len (mdnames) > 0: - modname = cipher_file [0:len(cipher_file) - 2] - if re.match (".*-glue$", modname): - modfiles = "libgcrypt-grub/cipher/%s libgcrypt-grub/cipher/%s" \ + if isglue: + modfiles = "lib/libgcrypt-grub/cipher/%s lib/libgcrypt-grub/cipher/%s" \ % (cipher_file, cipher_file.replace ("-glue.c", ".c")) - modname = modname.replace ("-glue", "") else: - modfiles = "libgcrypt-grub/cipher/%s" % cipher_file - modname = "gcry_%s" % modname + modfiles = "lib/libgcrypt-grub/cipher/%s" % cipher_file chmsg = "(GRUB_MOD_INIT(%s)): New function\n" % modname if nch: chlognew = "%s\n %s" % (chlognew, chmsg) @@ -220,7 +277,7 @@ for cipher_file in cipher_files: conf.write ("pkglib_MODULES += %s.mod\n" % modname) conf.write ("%s_mod_SOURCES = %s\n" %\ (modname, modfiles)) - conf.write ("%s_mod_CFLAGS = $(COMMON_CFLAGS) -Wno-missing-field-initializers -Wno-error\n" % modname) + conf.write ("%s_mod_CFLAGS = $(COMMON_CFLAGS) -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap\n" % modname) conf.write ("%s_mod_LDFLAGS = $(COMMON_LDFLAGS)\n\n" % modname) elif isc and cipher_file != "camellia.c": print ("WARNING: C file isn't a module: %s" % cipher_file) @@ -229,26 +286,22 @@ for cipher_file in cipher_files: if nch: chlog = "%s%s\n" % (chlog, chlognew) continue - if re.match ("(Manifest|Makefile\.am)$", cipher_file): - chlog = "%s%sRemoved\n" % (chlog, chlognew) - continue - # Autogenerated files. Not even worth mentionning in ChangeLog - if re.match ("Makefile\.in$", cipher_file): - chlog = "%s%sRemoved\n" % (chlog, chlognew) - continue chlog = "%s%sSkipped unknown file\n" % (chlog, chlognew) print ("WARNING: unknown file %s" % cipher_file) +cryptolist.close () +chlog = "%s * crypto.lst: New file.\n" % chlog + outfile = os.path.join (cipher_dir_out, "types.h") fw=open (outfile, "w") fw.write ("#include \n") -fw.write ("#include \n") +fw.write ("#include \n") chlog = "%s * types.h: New file.\n" % chlog fw.close () outfile = os.path.join (cipher_dir_out, "memory.h") fw=open (outfile, "w") -fw.write ("#include \n") +fw.write ("#include \n") chlog = "%s * memory.h: New file.\n" % chlog fw.close () @@ -256,13 +309,13 @@ fw.close () outfile = os.path.join (cipher_dir_out, "cipher.h") fw=open (outfile, "w") fw.write ("#include \n") -fw.write ("#include \n") +fw.write ("#include \n") chlog = "%s * cipher.h: Likewise.\n" % chlog fw.close () outfile = os.path.join (cipher_dir_out, "g10lib.h") fw=open (outfile, "w") -fw.write ("#include \n") +fw.write ("#include \n") chlog = "%s * g10lib.h: Likewise.\n" % chlog fw.close ()