]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
geli support
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 23 Apr 2011 22:00:29 +0000 (00:00 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 23 Apr 2011 22:00:29 +0000 (00:00 +0200)
Makefile.util.def
grub-core/Makefile.core.def
grub-core/disk/cryptodisk.c [new file with mode: 0644]
grub-core/disk/geli.c [new file with mode: 0644]
grub-core/disk/luks.c
grub-core/lib/crypto.c
include/grub/crypto.h
include/grub/cryptodisk.h [new file with mode: 0644]
util/grub-fstest.c

index ffa7a794f78bd85720ea475c9f3154274ee0915d..fe9e8c03614076c3d0f58cf3139d7d0886dac77f 100644 (file)
@@ -23,6 +23,7 @@ library = {
   common = grub-core/kern/partition.c;
   common = grub-core/lib/crypto.c;
   common = grub-core/disk/luks.c;
+  common = grub-core/disk/geli.c;
   common = grub-core/disk/cryptodisk.c;
   common = grub-core/disk/AFSplitter.c;
   common = grub-core/lib/pbkdf2.c;
index c55fff13a231949125a9a116f2c56817800abd15..52add9f1b49d8c6b798208f2826a7e2f8a6049b3 100644 (file)
@@ -779,6 +779,11 @@ module = {
   common = disk/AFSplitter.c;
 };
 
+module = {
+  name = geli;
+  common = disk/geli.c;
+};
+
 module = {
   name = lvm;
   common = disk/lvm.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
new file mode 100644 (file)
index 0000000..7f3b60b
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2007,2010,2011  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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/cryptodisk.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/dl.h>
+
+#ifdef GRUB_UTIL
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <grub/emu/hostdisk.h>
+#include <unistd.h>
+#include <string.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is:  */
+#define GF_POLYNOM 0x87
+#define GF_PER_SECTOR (GRUB_DISK_SECTOR_SIZE / GRUB_CRYPTODISK_GF_BYTES)
+
+static grub_cryptodisk_t cryptodisk_list = NULL;
+static grub_uint8_t n = 0;
+
+static void
+gf_mul_x (grub_uint8_t *g)
+{
+  int over = 0, over2 = 0;
+  int j;
+
+  for (j = 0; j < GRUB_CRYPTODISK_GF_BYTES; j++)
+    {
+      over2 = !!(g[j] & 0x80);
+      g[j] <<= 1;
+      g[j] |= over;
+      over = over2;
+    }
+  if (over)
+    g[0] ^= GF_POLYNOM;
+}
+
+
+static void
+gf_mul_x_be (grub_uint8_t *g)
+{
+  int over = 0, over2 = 0;
+  int j;
+
+  for (j = GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--)
+    {
+      over2 = !!(g[j] & 0x80);
+      g[j] <<= 1;
+      g[j] |= over;
+      over = over2;
+    }
+  if (over)
+    g[GRUB_CRYPTODISK_GF_BYTES - 1] ^= GF_POLYNOM;
+}
+
+static void
+gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b)
+{
+  int i;
+  grub_uint8_t t[GRUB_CRYPTODISK_GF_BYTES];
+  grub_memset (o, 0, GRUB_CRYPTODISK_GF_BYTES);
+  grub_memcpy (t, b, GRUB_CRYPTODISK_GF_BYTES);
+  for (i = 0; i < GRUB_CRYPTODISK_GF_SIZE; i++)
+    {
+      if (((a[GRUB_CRYPTODISK_GF_BYTES - i / 8 - 1] >> (i % 8))) & 1)
+       grub_crypto_xor (o, o, t, GRUB_CRYPTODISK_GF_BYTES);
+      gf_mul_x_be (t);
+    }
+}
+
+static gcry_err_code_t
+grub_crypto_pcbc_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_crypto_xor (iv, ivt, outptr, cipher->cipher->blocksize);
+    }
+  return GPG_ERR_NO_ERROR;
+}
+
+struct lrw_sector
+{
+  grub_uint8_t low[GRUB_CRYPTODISK_GF_BYTES];
+  grub_uint8_t high[GRUB_CRYPTODISK_GF_BYTES];
+  grub_uint8_t low_byte, low_byte_c;
+};
+
+static void
+generate_lrw_sector (struct lrw_sector *sec,
+                    const struct grub_cryptodisk *dev,
+                    const grub_uint8_t *iv)
+{
+  grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES];
+  grub_uint16_t c;
+  int j;
+  grub_memcpy (idx, iv, GRUB_CRYPTODISK_GF_BYTES);
+  sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1] & (GF_PER_SECTOR - 1));
+  sec->low_byte_c = (((GF_PER_SECTOR - 1) & ~sec->low_byte) + 1);
+  idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR - 1);
+  gf_mul_be (sec->low, dev->lrw_key, idx);
+  if (!sec->low_byte)
+    return;
+
+  c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR;
+  if (c & 0x100)
+    {
+      for (j = GRUB_CRYPTODISK_GF_BYTES - 2; j >= 0; j--)
+       {
+         idx[j]++;
+         if (idx[j] != 0)
+           break;
+       }
+    }
+  idx[GRUB_CRYPTODISK_GF_BYTES - 1] = c;
+  gf_mul_be (sec->high, dev->lrw_key, idx);
+}
+
+static void __attribute__ ((unused))
+lrw_xor (const struct lrw_sector *sec,
+        const struct grub_cryptodisk *dev,
+        grub_uint8_t *b)
+{
+  int i;
+
+  for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; i += GRUB_CRYPTODISK_GF_BYTES)
+    grub_crypto_xor (b + i, b + i, sec->low, GRUB_CRYPTODISK_GF_BYTES);
+  grub_crypto_xor (b, b, dev->lrw_precalc + GRUB_CRYPTODISK_GF_BYTES * sec->low_byte,
+                  sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES);
+  if (!sec->low_byte)
+    return;
+
+  for (i = sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES;
+       i < GRUB_DISK_SECTOR_SIZE; i += GRUB_CRYPTODISK_GF_BYTES)
+    grub_crypto_xor (b + i, b + i, sec->high, GRUB_CRYPTODISK_GF_BYTES);
+  grub_crypto_xor (b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES,
+                  b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES,
+                  dev->lrw_precalc, sec->low_byte * GRUB_CRYPTODISK_GF_BYTES);
+}
+
+gcry_err_code_t
+grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev,
+                        grub_uint8_t * data, grub_size_t len,
+                        grub_disk_addr_t sector)
+{
+  grub_size_t i;
+  gcry_err_code_t err;
+
+  /* The only mode without IV.  */
+  if (dev->mode == GRUB_CRYPTODISK_MODE_ECB)
+    return grub_crypto_ecb_decrypt (dev->cipher, data, data, len);
+
+  for (i = 0; i < len; i += GRUB_DISK_SECTOR_SIZE)
+    {
+      grub_size_t sz = ((dev->cipher->cipher->blocksize
+                        + sizeof (grub_uint32_t) - 1)
+                       / sizeof (grub_uint32_t));
+      grub_uint32_t iv[sz];
+
+      grub_memset (iv, 0, sz * sizeof (iv[0]));
+      switch (dev->mode_iv)
+       {
+       case GRUB_CRYPTODISK_MODE_IV_NULL:
+         break;
+       case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH:
+         {
+           grub_uint64_t tmp;
+           grub_uint64_t ctx[(dev->iv_hash->contextsize + 7) / 8];
+           tmp = grub_cpu_to_le64 (sector << GRUB_DISK_SECTOR_BITS);
+           dev->iv_hash->init (ctx);
+           dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len);
+           dev->iv_hash->write (ctx, &tmp, sizeof (tmp));
+           dev->iv_hash->final (ctx);
+
+           grub_memcpy (iv, dev->iv_hash->read (ctx), sizeof (iv));
+         }
+         break;
+       case GRUB_CRYPTODISK_MODE_IV_PLAIN64:
+         iv[1] = grub_cpu_to_le32 (sector >> 32);
+       case GRUB_CRYPTODISK_MODE_IV_PLAIN:
+         iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF);
+         break;
+       case GRUB_CRYPTODISK_MODE_IV_BENBI:
+         {
+           grub_uint64_t num = (sector << dev->benbi_log) + 1;
+           iv[sz - 2] = grub_cpu_to_be32 (num >> 32);
+           iv[sz - 1] = grub_cpu_to_be32 (num & 0xFFFFFFFF);
+         }
+         break;
+       case GRUB_CRYPTODISK_MODE_IV_ESSIV:
+         iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF);
+         err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv,
+                                        dev->cipher->cipher->blocksize);
+         if (err)
+           return err;
+       }
+
+      switch (dev->mode)
+       {
+       case GRUB_CRYPTODISK_MODE_CBC:
+         err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i,
+                                        GRUB_DISK_SECTOR_SIZE, iv);
+         if (err)
+           return err;
+         break;
+
+       case GRUB_CRYPTODISK_MODE_PCBC:
+         err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i,
+                                         GRUB_DISK_SECTOR_SIZE, iv);
+         if (err)
+           return err;
+         break;
+       case GRUB_CRYPTODISK_MODE_XTS:
+         {
+           int j;
+           err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv,
+                                          dev->cipher->cipher->blocksize);
+           if (err)
+             return err;
+           
+           for (j = 0; j < GRUB_DISK_SECTOR_SIZE;
+                j += dev->cipher->cipher->blocksize)
+             {
+               grub_crypto_xor (data + i + j, data + i + j, iv,
+                                dev->cipher->cipher->blocksize);
+               err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, 
+                                              data + i + j,
+                                              dev->cipher->cipher->blocksize);
+               if (err)
+                 return err;
+               grub_crypto_xor (data + i + j, data + i + j, iv,
+                                dev->cipher->cipher->blocksize);
+               gf_mul_x ((grub_uint8_t *) iv);
+             }
+         }
+         break;
+       case GRUB_CRYPTODISK_MODE_LRW:
+         {
+           struct lrw_sector sec;
+
+           generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv);
+           lrw_xor (&sec, dev, data + i);
+
+           err = grub_crypto_ecb_decrypt (dev->cipher, data + i, 
+                                          data + i, GRUB_DISK_SECTOR_SIZE);
+           if (err)
+             return err;
+           lrw_xor (&sec, dev, data + i);
+         }
+         break;
+       default:
+         return GPG_ERR_NOT_IMPLEMENTED;
+       }
+      sector++;
+    }
+  return GPG_ERR_NO_ERROR;
+}
+
+gcry_err_code_t
+grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize)
+{
+  gcry_err_code_t err;
+  int real_keysize;
+
+  real_keysize = keysize;
+  if (dev->mode == GRUB_CRYPTODISK_MODE_XTS)
+    real_keysize /= 2;
+  if (dev->mode == GRUB_CRYPTODISK_MODE_LRW)
+    real_keysize -= dev->cipher->cipher->blocksize;
+       
+  /* Set the PBKDF2 output as the cipher key.  */
+  err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize);
+  if (err)
+    return err;
+
+  /* Configure ESSIV if necessary.  */
+  if (dev->mode_iv == GRUB_CRYPTODISK_MODE_IV_ESSIV)
+    {
+      grub_size_t essiv_keysize = dev->essiv_hash->mdlen;
+      grub_uint8_t hashed_key[essiv_keysize];
+
+      grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize);
+      err = grub_crypto_cipher_set_key (dev->essiv_cipher,
+                                       hashed_key, essiv_keysize);
+      if (err)
+       return err;
+    }
+  if (dev->mode == GRUB_CRYPTODISK_MODE_XTS)
+    {
+      err = grub_crypto_cipher_set_key (dev->secondary_cipher,
+                                       key + real_keysize,
+                                       keysize / 2);
+      if (err)
+       return err;
+    }
+
+  if (dev->mode == GRUB_CRYPTODISK_MODE_LRW)
+    {
+      int i;
+      grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES];
+
+      grub_free (dev->lrw_precalc);
+      grub_memcpy (dev->lrw_key, key + real_keysize,
+                  dev->cipher->cipher->blocksize);
+      dev->lrw_precalc = grub_malloc (GRUB_DISK_SECTOR_SIZE);
+      if (!dev->lrw_precalc)
+       return GPG_ERR_OUT_OF_MEMORY;
+      grub_memset (idx, 0, GRUB_CRYPTODISK_GF_BYTES);
+      for (i = 0; i < GRUB_DISK_SECTOR_SIZE;
+          i += GRUB_CRYPTODISK_GF_BYTES)
+       {
+         idx[GRUB_CRYPTODISK_GF_BYTES - 1] = i / GRUB_CRYPTODISK_GF_BYTES;
+         gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key);
+       }
+    }
+  return GPG_ERR_NO_ERROR;
+}
+
+static int
+grub_cryptodisk_iterate (int (*hook) (const char *name),
+                  grub_disk_pull_t pull)
+{
+  grub_cryptodisk_t i;
+
+  if (pull != GRUB_DISK_PULL_NONE)
+    return 0;
+
+  for (i = cryptodisk_list; i != NULL; i = i->next)
+    {
+      char buf[30];
+      grub_snprintf (buf, sizeof (buf), "crypto%lu", i->id);
+      if (hook (buf))
+       return 1;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cryptodisk_open (const char *name, grub_disk_t disk,
+               grub_disk_pull_t pull __attribute__ ((unused)))
+{
+  grub_cryptodisk_t dev;
+
+  if (grub_memcmp (name, "crypto", sizeof ("crypto") - 1) != 0)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device");
+
+  if (grub_memcmp (name, "cryptouuid/", sizeof ("cryptouuid/") - 1) == 0)
+    {
+      for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
+       if (grub_strcasecmp (name + sizeof ("cryptouuid/") - 1, dev->uuid) == 0)
+         break;
+    }
+  else
+    {
+      unsigned long id = grub_strtoul (name + sizeof ("crypto") - 1, 0, 0);
+      if (grub_errno)
+       return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device");
+      /* Search for requested device in the list of CRYPTODISK devices.  */
+      for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
+       if (dev->id == id)
+         break;
+    }
+  if (!dev)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device");
+
+#ifdef GRUB_UTIL
+  if (dev->cheat)
+    {
+      if (dev->cheat_fd == -1)
+       dev->cheat_fd = open (dev->cheat, O_RDONLY);
+      if (dev->cheat_fd == -1)
+       return grub_error (GRUB_ERR_IO, "couldn't open %s: %s",
+                          dev->cheat, strerror (errno));
+    }
+#endif
+
+  if (!dev->source_disk)
+    {
+      grub_dprintf ("cryptodisk", "Opening device %s\n", name);
+      /* Try to open the source disk and populate the requested disk.  */
+      dev->source_disk = grub_disk_open (dev->source);
+      if (!dev->source_disk)
+       return grub_errno;
+    }
+
+  disk->data = dev;
+  disk->total_sectors = dev->total_length;
+  disk->id = dev->id;
+  dev->ref++;
+  return GRUB_ERR_NONE;
+}
+
+static void
+grub_cryptodisk_close (grub_disk_t disk)
+{
+  grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
+  grub_dprintf ("cryptodisk", "Closing disk\n");
+
+  dev->ref--;
+
+  if (dev->ref != 0)
+    return;
+#ifdef GRUB_UTIL
+  if (dev->cheat)
+    {
+      close (dev->cheat_fd);
+      dev->cheat_fd = -1;
+    }
+#endif
+  grub_disk_close (dev->source_disk);
+  dev->source_disk = NULL;
+}
+
+static grub_err_t
+grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector,
+               grub_size_t size, char *buf)
+{
+  grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
+  grub_err_t err;
+  gcry_err_code_t gcry_err;
+
+#ifdef GRUB_UTIL
+  if (dev->cheat)
+    {
+      err = grub_util_fd_sector_seek (dev->cheat_fd, dev->cheat, sector);
+      if (err)
+       return err;
+      if (grub_util_fd_read (dev->cheat_fd, buf, size << GRUB_DISK_SECTOR_BITS)
+         != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+       return grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'",
+                          dev->cheat);
+      return GRUB_ERR_NONE;
+    }
+#endif
+
+  grub_dprintf ("cryptodisk",
+               "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%"
+               PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n",
+               size, sector, dev->offset);
+
+  err = grub_disk_read (dev->source_disk, sector + dev->offset, 0,
+                       size << GRUB_DISK_SECTOR_BITS, buf);
+  if (err)
+    {
+      grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err);
+      return err;
+    }
+  gcry_err = grub_cryptodisk_decrypt (dev, (grub_uint8_t *) buf,
+                                     size << GRUB_DISK_SECTOR_BITS,
+                                     sector);
+  return grub_crypto_gcry_error (gcry_err);
+}
+
+static grub_err_t
+grub_cryptodisk_write (grub_disk_t disk __attribute ((unused)),
+                grub_disk_addr_t sector __attribute ((unused)),
+                grub_size_t size __attribute ((unused)),
+                const char *buf __attribute ((unused)))
+{
+  return GRUB_ERR_NOT_IMPLEMENTED_YET;
+}
+
+#ifdef GRUB_UTIL
+static grub_disk_memberlist_t
+grub_cryptodisk_memberlist (grub_disk_t disk)
+{
+  grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
+  grub_disk_memberlist_t list = NULL;
+
+  list = grub_malloc (sizeof (*list));
+  if (list)
+    {
+      list->disk = dev->source_disk;
+      list->next = NULL;
+    }
+
+  return list;
+}
+#endif
+
+static void
+cryptodisk_cleanup (void)
+{
+  grub_cryptodisk_t dev = cryptodisk_list;
+  grub_cryptodisk_t tmp;
+
+  while (dev != NULL)
+    {
+      grub_free (dev->source);
+      grub_free (dev->cipher);
+      grub_free (dev->secondary_cipher);
+      grub_free (dev->essiv_cipher);
+      tmp = dev->next;
+      grub_free (dev);
+      dev = tmp;
+    }
+}
+
+grub_err_t
+grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name,
+                       grub_disk_t source)
+{
+  newdev->source = grub_strdup (name);
+  if (!newdev->source)
+    {
+      grub_free (newdev);
+      return grub_errno;
+    }
+
+  newdev->id = n++;
+  newdev->source_id = source->id;
+  newdev->source_dev_id = source->dev->id;
+  newdev->next = cryptodisk_list;
+  cryptodisk_list = newdev;
+
+  return GRUB_ERR_NONE;
+}
+
+grub_cryptodisk_t
+grub_cryptodisk_get_by_uuid (const char *uuid)
+{
+  grub_cryptodisk_t dev;
+  for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
+    if (grub_strcasecmp (dev->uuid, uuid) == 0)
+      return dev;
+  return NULL;
+}
+
+grub_cryptodisk_t
+grub_cryptodisk_get_by_source_disk (grub_disk_t disk)
+{
+  grub_cryptodisk_t dev;
+  for (dev = cryptodisk_list; dev != NULL; dev = dev->next)
+    if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id)
+      return dev;
+  return NULL;
+}
+
+#ifdef GRUB_UTIL
+grub_err_t
+grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name,
+                             grub_disk_t source, const char *cheat)
+{
+  newdev->cheat = grub_strdup (cheat);
+  newdev->source = grub_strdup (name);
+  if (!newdev->source || !newdev->cheat)
+    {
+      grub_free (newdev->source);
+      grub_free (newdev->cheat);
+      return grub_errno;
+    }
+
+  newdev->cheat_fd = -1;
+  newdev->source_id = source->id;
+  newdev->source_dev_id = source->dev->id;
+  newdev->id = n++;
+  newdev->next = cryptodisk_list;
+  cryptodisk_list = newdev;
+
+  return GRUB_ERR_NONE;
+}
+
+void
+grub_util_cryptodisk_print_abstraction (grub_disk_t disk)
+{
+  grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
+
+  grub_printf ("luks ");
+
+  if (dev->cipher)
+    grub_printf ("%s ", dev->cipher->cipher->modname);
+  if (dev->secondary_cipher)
+    grub_printf ("%s ", dev->secondary_cipher->cipher->modname);
+  if (dev->essiv_cipher)
+    grub_printf ("%s ", dev->essiv_cipher->cipher->modname);
+  if (dev->hash)
+    grub_printf ("%s ", dev->hash->modname);
+  if (dev->essiv_hash)
+    grub_printf ("%s ", dev->essiv_hash->modname);
+  if (dev->iv_hash)
+    grub_printf ("%s ", dev->iv_hash->modname);
+}
+#endif
+
+static struct grub_disk_dev grub_cryptodisk_dev = {
+  .name = "cryptodisk",
+  .id = GRUB_DISK_DEVICE_CRYPTODISK_ID,
+  .iterate = grub_cryptodisk_iterate,
+  .open = grub_cryptodisk_open,
+  .close = grub_cryptodisk_close,
+  .read = grub_cryptodisk_read,
+  .write = grub_cryptodisk_write,
+#ifdef GRUB_UTIL
+  .memberlist = grub_cryptodisk_memberlist,
+#endif
+  .next = 0
+};
+
+GRUB_MOD_INIT (cryptodisk)
+{
+  grub_disk_dev_register (&grub_cryptodisk_dev);
+}
+
+GRUB_MOD_FINI (cryptodisk)
+{
+  grub_disk_dev_unregister (&grub_cryptodisk_dev);
+  cryptodisk_cleanup ();
+}
diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c
new file mode 100644 (file)
index 0000000..6af1de7
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2007,2010,2011  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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file is loosely based on FreeBSD geli implementation
+   (but no code was directly copied). FreeBSD geli is distributed under
+   following terms:  */
+/*-
+ * Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <grub/cryptodisk.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/err.h>
+#include <grub/disk.h>
+#include <grub/crypto.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_geli_key
+{
+  grub_uint8_t iv_key[64];
+  grub_uint8_t cipher_key[64];
+  grub_uint8_t hmac[64];
+} __attribute__ ((packed));
+
+struct grub_geli_phdr
+{
+  grub_uint8_t magic[16];
+#define GELI_MAGIC "GEOM::ELI"
+  grub_uint32_t version;
+  grub_uint32_t unused1;
+  grub_uint16_t alg;
+  grub_uint16_t keylen;
+  grub_uint16_t unused3[7];
+  grub_uint8_t keys_used;
+  grub_uint32_t niter;
+  grub_uint8_t salt[64];
+  struct grub_geli_key keys[2];
+} __attribute__ ((packed));
+
+const char *algorithms[] = {
+  [0x0b] = "aes",
+};
+
+#define MAX_PASSPHRASE 256
+
+static const struct grub_arg_option options[] =
+  {
+    {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0},
+    {"all", 'a', 0, N_("Mount all."), 0, 0},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+static int check_uuid, have_it;
+static char *search_uuid;
+
+static grub_cryptodisk_t
+configure_ciphers (const struct grub_geli_phdr *header)
+{
+  grub_cryptodisk_t newdev;
+  grub_crypto_cipher_handle_t cipher = NULL;
+  const struct gcry_cipher_spec *ciph;
+  const char *ciphername = NULL;
+  const gcry_md_spec_t *hash = NULL, *iv_hash = NULL;
+
+  /* Look for GELI magic sequence.  */
+  if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC))
+      || grub_le_to_cpu32 (header->version) != 3)
+    {
+      grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]);
+      return NULL;
+    }
+
+#if 0
+  optr = uuid;
+  for (iptr = header->uuid; iptr < &header->uuid[ARRAY_SIZE (header->uuid)];
+       iptr++)
+    {
+      if (*iptr != '-')
+       *optr++ = *iptr;
+    }
+  *optr = 0;
+
+  if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0)
+    {
+      grub_dprintf ("luks", "%s != %s", uuid, search_uuid);
+      return NULL;
+    }
+#endif
+
+  if (grub_le_to_cpu16 (header->alg) >= ARRAY_SIZE (algorithms)
+      || algorithms[grub_le_to_cpu16 (header->alg)] == NULL)
+    {
+      grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown",
+                 grub_le_to_cpu16 (header->alg));
+      return NULL;
+    }
+
+  ciphername = algorithms[grub_le_to_cpu16 (header->alg)];
+  ciph = grub_crypto_lookup_cipher_by_name (ciphername);
+  if (!ciph)
+    {
+      grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available",
+                 ciphername);
+      return NULL;
+    }
+
+  /* Configure the cipher used for the bulk data.  */
+  cipher = grub_crypto_cipher_open (ciph);
+  if (!cipher)
+    return NULL;
+
+  if (grub_le_to_cpu16 (header->keylen) > 1024)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d",
+                 grub_le_to_cpu16 (header->keylen));
+      return NULL;
+    }
+
+  hash = grub_crypto_lookup_md_by_name ("sha512");
+  if (!hash)
+    {
+      grub_crypto_cipher_close (cipher);
+      grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash",
+                 "sha512");
+      return NULL;
+    }
+
+  iv_hash = grub_crypto_lookup_md_by_name ("sha256");
+  if (!hash)
+    {
+      grub_crypto_cipher_close (cipher);
+      grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash",
+                 "sha512");
+      return NULL;
+    }
+
+  newdev = grub_zalloc (sizeof (struct grub_cryptodisk));
+  if (!newdev)
+    return NULL;
+  newdev->cipher = cipher;
+  newdev->offset = 0;
+  newdev->source_disk = NULL;
+  newdev->benbi_log = 0;
+  newdev->mode = GRUB_CRYPTODISK_MODE_CBC;
+  newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH;
+  newdev->secondary_cipher = NULL;
+  newdev->essiv_cipher = NULL;
+  newdev->essiv_hash = NULL;
+  newdev->hash = hash;
+  newdev->iv_hash = iv_hash;
+#if 0
+  grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid));
+#endif
+  return newdev;
+}
+
+static grub_err_t
+recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header,
+            const char *name, grub_disk_t source __attribute__ ((unused)))
+{
+  grub_size_t keysize = grub_le_to_cpu16 (header->keylen) / 8;
+  grub_uint8_t digest[dev->hash->mdlen];
+  grub_uint8_t geomkey[dev->hash->mdlen];
+  grub_uint8_t verify_key[dev->hash->mdlen];
+  grub_uint8_t pbkdf_key[64];
+  grub_uint8_t zero[dev->cipher->cipher->blocksize];
+  char passphrase[MAX_PASSPHRASE] = "";
+  unsigned i;
+  gcry_err_code_t gcry_err;
+
+  grub_memset (zero, 0, sizeof (zero));
+
+  grub_printf ("Attempting to decrypt master key...\n");
+
+  /* Get the passphrase from the user.  */
+  grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid);
+  if (!grub_password_get (passphrase, MAX_PASSPHRASE))
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied");
+
+      /* Calculate the PBKDF2 of the user supplied passphrase.  */
+  gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase,
+                                grub_strlen (passphrase),
+                                header->salt,
+                                sizeof (header->salt),
+                                grub_le_to_cpu32 (header->niter),
+                                pbkdf_key, sizeof (pbkdf_key));
+
+  if (gcry_err)
+    return grub_crypto_gcry_error (gcry_err);
+
+  gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key,
+                                     sizeof (pbkdf_key), geomkey);
+  if (gcry_err)
+    return grub_crypto_gcry_error (gcry_err);
+
+  gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
+                                     sizeof (geomkey), "\1", 1, digest);
+  if (gcry_err)
+    return grub_crypto_gcry_error (gcry_err);
+
+  gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
+                                     sizeof (geomkey), "\0", 1, verify_key);
+  if (gcry_err)
+    return grub_crypto_gcry_error (gcry_err);
+
+  grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize);
+
+  /* Try to recover master key from each active keyslot.  */
+  for (i = 0; i < ARRAY_SIZE (header->keys); i++)
+    {
+      struct grub_geli_key candidate_key;
+      grub_uint8_t key_hmac[dev->hash->mdlen];
+
+      /* Check if keyslot is enabled.  */
+      if (! (header->keys_used & (1 << i)))
+         continue;
+
+      grub_dprintf ("geli", "Trying keyslot %d\n", i);
+
+      gcry_err = grub_crypto_cipher_set_key (dev->cipher,
+                                            digest, keysize);
+      if (gcry_err)
+       return grub_crypto_gcry_error (gcry_err);
+
+      gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key,
+                                         &header->keys[i],
+                                         sizeof (candidate_key),
+                                         zero);
+      if (gcry_err)
+       return grub_crypto_gcry_error (gcry_err);
+
+      gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key,
+                                         sizeof (verify_key), 
+                                         &candidate_key,
+                                         (sizeof (candidate_key)
+                                          - sizeof (candidate_key.hmac)),
+                                         key_hmac);
+      if (gcry_err)
+       return grub_crypto_gcry_error (gcry_err);
+
+      if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0)
+       continue;
+      grub_printf ("Slot %d opened\n", i);
+
+      /* Set the master key.  */
+      gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key,
+                                        keysize); 
+      if (gcry_err)
+       return grub_crypto_gcry_error (gcry_err);
+
+      dev->iv_prefix_len = sizeof (candidate_key.iv_key);
+      grub_memcpy (dev->iv_prefix, candidate_key.iv_key,
+                  sizeof (candidate_key.iv_key));
+
+      COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key));
+
+      return GRUB_ERR_NONE;
+    }
+
+  return GRUB_ACCESS_DENIED;
+}
+
+static void
+close (grub_cryptodisk_t luks)
+{
+  grub_crypto_cipher_close (luks->cipher);
+  grub_crypto_cipher_close (luks->secondary_cipher);
+  grub_crypto_cipher_close (luks->essiv_cipher);
+  grub_free (luks);
+}
+
+static grub_err_t
+grub_geli_scan_device_real (const char *name, grub_disk_t source)
+{
+  grub_err_t err;
+  struct grub_geli_phdr header;
+  grub_cryptodisk_t newdev, dev;
+  grub_disk_addr_t sector;
+
+  grub_dprintf ("geli", "scanning %s\n", source->name);
+  dev = grub_cryptodisk_get_by_source_disk (source);
+
+  if (dev)
+    return GRUB_ERR_NONE;
+
+  sector = grub_disk_get_size (source);
+  if (sector == GRUB_DISK_SIZE_UNKNOWN)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli");
+
+  /* Read the LUKS header.  */
+  err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
+  if (err)
+    return err;
+
+  newdev = configure_ciphers (&header);
+  if (!newdev)
+    return grub_errno;
+
+  newdev->total_length = grub_disk_get_size (source) - 1;
+
+  err = recover_key (newdev, &header, name, source);
+  if (err)
+    {
+      close (newdev);
+      return err;
+    }
+
+  grub_cryptodisk_insert (newdev, name, source);
+
+  have_it = 1;
+
+  return GRUB_ERR_NONE;
+}
+
+#ifdef GRUB_UTIL
+grub_err_t
+grub_geli_cheat_mount (const char *sourcedev, const char *cheat)
+{
+  grub_err_t err;
+  struct grub_geli_phdr header;
+  grub_cryptodisk_t newdev, dev;
+  grub_disk_t source;
+  grub_disk_addr_t sector;
+
+  /* Try to open disk.  */
+  source = grub_disk_open (sourcedev);
+  if (!source)
+    return grub_errno;
+
+  dev = grub_cryptodisk_get_by_source_disk (source);
+
+  if (dev)
+    {
+      grub_disk_close (source);        
+      return GRUB_ERR_NONE;
+    }
+
+  sector = grub_disk_get_size (source);
+  if (sector == GRUB_DISK_SIZE_UNKNOWN)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli");
+
+  /* Read the LUKS header.  */
+  err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
+  if (err)
+    return err;
+
+  newdev = configure_ciphers (&header);
+  if (!newdev)
+    {
+      grub_disk_close (source);
+      return grub_errno;
+    }
+
+  newdev->total_length = grub_disk_get_size (source) - 1;
+
+  err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat);
+  grub_disk_close (source);
+  if (err)
+    grub_free (newdev);
+
+  return err;
+}
+#endif
+
+static int
+grub_geli_scan_device (const char *name)
+{
+  grub_err_t err;
+  grub_disk_t source;
+
+  /* Try to open disk.  */
+  source = grub_disk_open (name);
+  if (!source)
+    return grub_errno;
+
+  err = grub_geli_scan_device_real (name, source);
+
+  grub_disk_close (source);
+  
+  if (err)
+    grub_print_error ();
+  return have_it && check_uuid ? 0 : 1;
+}
+
+#ifdef GRUB_UTIL
+
+void
+grub_util_geli_print_uuid (grub_disk_t disk)
+{
+  grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
+  grub_printf ("%s ", dev->uuid);
+}
+#endif
+
+static grub_err_t
+grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args)
+{
+  struct grub_arg_list *state = ctxt->state;
+
+  if (argc < 1 && !state[1].set)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
+
+  have_it = 0;
+  if (state[0].set)
+    {
+      grub_cryptodisk_t dev;
+
+      dev = grub_cryptodisk_get_by_uuid (args[0]);
+      if (dev)
+       {
+         grub_dprintf ("luks", "already mounted as crypto%lu\n", dev->id);
+         return GRUB_ERR_NONE;
+       }
+
+      check_uuid = 1;
+      search_uuid = args[0];
+      grub_device_iterate (&grub_geli_scan_device);
+      search_uuid = NULL;
+
+      if (!have_it)
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found");
+      return GRUB_ERR_NONE;
+    }
+  else if (state[1].set)
+    {
+      check_uuid = 0;
+      search_uuid = NULL;
+      grub_device_iterate (&grub_geli_scan_device);
+      search_uuid = NULL;
+      return GRUB_ERR_NONE;
+    }
+  else
+    {
+      grub_err_t err;
+      grub_disk_t disk;
+      grub_cryptodisk_t dev;
+
+      check_uuid = 0;
+      search_uuid = NULL;
+      disk = grub_disk_open (args[0]);
+      if (!disk)
+       return grub_errno;
+
+      dev = grub_cryptodisk_get_by_source_disk (disk);
+      if (dev)
+       {
+         grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id);
+         grub_disk_close (disk);
+         return GRUB_ERR_NONE;
+       }
+
+      err = grub_geli_scan_device_real (args[0], disk);
+
+      grub_disk_close (disk);
+
+      return err;
+    }
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT (geli)
+{
+  cmd = grub_register_extcmd ("gelimount", grub_cmd_gelimount, 0,
+                             N_("SOURCE|-u UUID|-a"),
+                             N_("Mount a GELI device."), options);
+}
+
+GRUB_MOD_FINI (geli)
+{
+  grub_unregister_extcmd (cmd);
+}
index 361beb756270457db42786edd3f971bbda2dcfb3..0763da01101733d84eaeb778dda2dcdfaa4386b0 100644 (file)
@@ -454,6 +454,8 @@ grub_luks_scan_device_real (const char *name, grub_disk_t source)
   if (!newdev)
     return grub_errno;
 
+  newdev->total_length = grub_disk_get_size (source) - newdev->offset;
+
   err = luks_recover_key (newdev, &header, name, source);
   if (err)
     {
@@ -502,6 +504,8 @@ grub_luks_cheat_mount (const char *sourcedev, const char *cheat)
       return grub_errno;
     }
 
+  newdev->total_length = grub_disk_get_size (source) - newdev->offset;
+
   err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat);
   grub_disk_close (source);
   if (err)
index 2f172ebf80608db191f83b7477cdc4a1c6e9c770..e6f55062bae9e2491ae13747c21d3ce6377dbd3a 100644 (file)
@@ -193,9 +193,10 @@ 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)
+                        void *out, const void *in, grub_size_t size)
 {
-  grub_uint8_t *inptr, *outptr, *end;
+  const grub_uint8_t *inptr;
+  grub_uint8_t *outptr, *end;
   if (!cipher->cipher->decrypt)
     return GPG_ERR_NOT_SUPPORTED;
   if (size % cipher->cipher->blocksize != 0)
@@ -249,10 +250,11 @@ grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher,
 
 gcry_err_code_t
 grub_crypto_cbc_decrypt (grub_crypto_cipher_handle_t cipher,
-                        void *out, void *in, grub_size_t size,
+                        void *out, const void *in, grub_size_t size,
                         void *iv)
 {
-  grub_uint8_t *inptr, *outptr, *end;
+  const grub_uint8_t *inptr;
+  grub_uint8_t *outptr, *end;
   grub_uint8_t ivt[cipher->cipher->blocksize];
   if (!cipher->cipher->decrypt)
     return GPG_ERR_NOT_SUPPORTED;
index 62ed2015cba644bdd4728cf011492959ca59fce1..ab82da862530dc77c0a12d049f65ab7d67c0793a 100644 (file)
@@ -199,7 +199,7 @@ 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);
+                        void *out, const void *in, grub_size_t size);
 
 gcry_err_code_t
 grub_crypto_ecb_encrypt (grub_crypto_cipher_handle_t cipher,
@@ -210,7 +210,7 @@ grub_crypto_cbc_encrypt (grub_crypto_cipher_handle_t cipher,
                         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 *out, const void *in, grub_size_t size,
                         void *iv);
 void 
 grub_cipher_register (gcry_cipher_spec_t *cipher);
diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h
new file mode 100644 (file)
index 0000000..284e599
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2004,2005,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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_CRYPTODISK_HEADER
+#define GRUB_CRYPTODISK_HEADER 1
+
+#include <grub/disk.h>
+#include <grub/crypto.h>
+
+typedef enum
+  {
+    GRUB_CRYPTODISK_MODE_ECB,
+    GRUB_CRYPTODISK_MODE_CBC,
+    GRUB_CRYPTODISK_MODE_PCBC,
+    GRUB_CRYPTODISK_MODE_XTS,
+    GRUB_CRYPTODISK_MODE_LRW
+  } grub_cryptodisk_mode_t;
+
+typedef enum
+  {
+    GRUB_CRYPTODISK_MODE_IV_NULL,
+    GRUB_CRYPTODISK_MODE_IV_PLAIN,
+    GRUB_CRYPTODISK_MODE_IV_PLAIN64,
+    GRUB_CRYPTODISK_MODE_IV_ESSIV,
+    GRUB_CRYPTODISK_MODE_IV_BENBI,
+    GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH
+  } grub_cryptodisk_mode_iv_t;
+
+#define GRUB_CRYPTODISK_MAX_UUID_LENGTH 63
+
+#define GRUB_CRYPTODISK_GF_SIZE 128
+#define GRUB_CRYPTODISK_GF_BYTES (GRUB_CRYPTODISK_GF_SIZE / 8)
+
+struct grub_cryptodisk
+{
+  char *source;
+  grub_disk_addr_t offset;
+  grub_disk_addr_t total_length;
+  grub_disk_t source_disk;
+  int ref;
+  grub_crypto_cipher_handle_t cipher;
+  grub_crypto_cipher_handle_t secondary_cipher;
+  grub_crypto_cipher_handle_t essiv_cipher;
+  const gcry_md_spec_t *essiv_hash, *hash, *iv_hash;
+  grub_cryptodisk_mode_t mode;
+  grub_cryptodisk_mode_iv_t mode_iv;
+  int benbi_log;
+  unsigned long id, source_id;
+  enum grub_disk_dev_id source_dev_id;
+  char uuid[GRUB_CRYPTODISK_MAX_UUID_LENGTH + 1];
+  grub_uint8_t lrw_key[GRUB_CRYPTODISK_GF_BYTES];
+  grub_uint8_t *lrw_precalc;
+  grub_uint8_t iv_prefix[64];
+  grub_size_t iv_prefix_len;
+#ifdef GRUB_UTIL
+  char *cheat;
+  int cheat_fd;
+#endif
+  struct grub_cryptodisk *next;
+};
+typedef struct grub_cryptodisk *grub_cryptodisk_t;
+
+gcry_err_code_t
+grub_cryptodisk_setkey (grub_cryptodisk_t dev,
+                       grub_uint8_t *key, grub_size_t keysize);
+gcry_err_code_t
+grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev,
+                        grub_uint8_t * data, grub_size_t len,
+                        grub_disk_addr_t sector);
+grub_err_t
+grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name,
+                       grub_disk_t source);
+#ifdef GRUB_UTIL
+grub_err_t
+grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name,
+                             grub_disk_t source, const char *cheat);
+void
+grub_util_cryptodisk_print_abstraction (grub_disk_t disk);
+#endif
+
+grub_cryptodisk_t grub_cryptodisk_get_by_uuid (const char *uuid);
+grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk);
+
+#endif
index 2adb2331d4dd63818b40c5e37434eebd7c464672..48a5be1ca5179ccef47892e38a6839d4491c70fe 100644 (file)
@@ -308,8 +308,12 @@ fstest (int n, char **args)
   {
     char *argv[2] = { "-a", NULL};
     if (mount_crypt)
-      if (execute_command ("luksmount", 1, argv))
-       grub_util_error (_("luksmount command fails: %s"), grub_errmsg);
+      {
+       if (execute_command ("luksmount", 1, argv))
+         grub_util_error (_("luksmount command fails: %s"), grub_errmsg);
+       if (execute_command ("gelimount", 1, argv))
+         grub_util_error (_("gelimount command fails: %s"), grub_errmsg);
+      }
   }
 
   grub_lvm_fini ();