]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
legacy_password implementation
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 11 Sep 2010 23:07:41 +0000 (01:07 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 11 Sep 2010 23:07:41 +0000 (01:07 +0200)
grub-core/commands/legacycfg.c
grub-core/commands/password.c
grub-core/lib/legacy_parse.c
include/grub/normal.h

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