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/Makefile.in b/Makefile.in
index 46b380cd5..b28b660da 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -169,7 +169,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) > $@ \
@@ -193,6 +193,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 0e048797e..7bb2f0ae5 100644
--- a/commands/password.c
+++ b/commands/password.c
@@ -17,6 +17,7 @@
*/
#include
+#include
#include
#include
#include
@@ -27,17 +28,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);
@@ -51,13 +45,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 85c0e9ce4..c43c4bf37 100644
--- a/commands/xnu_uuid.c
+++ b/commands/xnu_uuid.c
@@ -31,12 +31,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. */
@@ -44,311 +39,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 2c383f387..bbc564c80 100644
--- a/conf/any-emu.rmk
+++ b/conf/any-emu.rmk
@@ -24,10 +24,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/term.c \
+ normal/menu_text.c normal/crypto.c normal/term.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 \
@@ -45,7 +45,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 02d57f0a3..83168557d 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -543,7 +543,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/term.c
+ normal/misc.c normal/crypto.c normal/term.c
normal_mod_CFLAGS = $(COMMON_CFLAGS)
normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
@@ -643,3 +643,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 823442941..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 de71bb588..cc801bc67 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 d629338e6..33c30b22a 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);
+
void grub_set_more (int onoff);
#ifdef GRUB_UTIL
diff --git a/lib/crypto.c b/lib/crypto.c
new file mode 100644
index 000000000..021560d6c
--- /dev/null
+++ b/lib/crypto.c
@@ -0,0 +1,447 @@
+/*
+ * 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;
+}
+
+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;
+}
+
+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 42098e682..0dab9e09e 100644
--- a/normal/main.c
+++ b/normal/main.c
@@ -441,6 +441,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 ()