ZFS crypto key adding

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-11-06 14:44:29 +01:00
parent 1bc7cc1b4d
commit 2cdc899567
4 changed files with 165 additions and 44 deletions

View file

@ -1,6 +1,6 @@
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010 Free Software Foundation, Inc. * Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc.
* Copyright 2010 Sun Microsystems, Inc. * Copyright 2010 Sun Microsystems, Inc.
* *
* GRUB is free software; you can redistribute it and/or modify * GRUB is free software; you can redistribute it and/or modify
@ -142,6 +142,12 @@ struct grub_zfs_key
grub_uint8_t unknown_purpose_key[48]; grub_uint8_t unknown_purpose_key[48];
}; };
struct grub_zfs_wrap_key
{
struct grub_zfs_wrap_key *next;
grub_uint64_t key[GRUB_ZFS_MAX_KEYLEN / 8];
};
extern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t); extern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t);
typedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start, typedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start,
@ -216,6 +222,21 @@ struct grub_zfs_data
grub_uint64_t guid; grub_uint64_t guid;
}; };
static struct grub_zfs_wrap_key *zfs_wrap_keys;
grub_err_t
grub_zfs_add_key (grub_uint8_t *key_in)
{
struct grub_zfs_wrap_key *key;
key = grub_malloc (sizeof (*key));
if (!key)
return grub_errno;
grub_memcpy (key->key, key_in, GRUB_ZFS_MAX_KEYLEN);
key->next = zfs_wrap_keys;
zfs_wrap_keys = key;
return GRUB_ERR_NONE;
}
static grub_err_t static grub_err_t
zlib_decompress (void *s, void *d, zlib_decompress (void *s, void *d,
grub_size_t slen, grub_size_t dlen) grub_size_t slen, grub_size_t dlen)
@ -2747,9 +2768,8 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
grub_size_t elemsize) grub_size_t elemsize)
{ {
const struct grub_zfs_key *key = val_in; const struct grub_zfs_key *key = val_in;
grub_crypto_cipher_handle_t cipher;
grub_uint8_t wrapping_key[32], decrypted[32], mac[32];
unsigned keylen; unsigned keylen;
struct grub_zfs_wrap_key *wrap_key;
if (elemsize != 1 || nelem != sizeof (*key)) if (elemsize != 1 || nelem != sizeof (*key))
{ {
@ -2766,28 +2786,33 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
else else
keylen = 32; keylen = 32;
grub_memset (wrapping_key, 0, sizeof (wrapping_key)); for (wrap_key = zfs_wrap_keys; wrap_key; wrap_key = wrap_key->next)
{
grub_crypto_cipher_handle_t cipher;
grub_uint8_t decrypted[32], mac[32];
cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
if (!cipher) if (!cipher)
{ {
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
return 0; return 0;
} }
err = grub_crypto_cipher_set_key (cipher, wrapping_key, keylen); err = grub_crypto_cipher_set_key (cipher,
(const grub_uint8_t *) wrap_key->key,
keylen);
if (err) if (err)
{ {
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
return 0; continue;
} }
err = grub_ccm_decrypt (cipher, decrypted, key->unknown_purpose_key, 32, err = grub_ccm_decrypt (cipher, decrypted, key->unknown_purpose_key, 32,
mac, key->unknown_purpose_nonce, 2, 16); mac, key->unknown_purpose_nonce, 2, 16);
if (err || grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) != 0) if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16)
!= 0))
{ {
grub_dprintf ("zfs", "key loading failed\n"); grub_dprintf ("zfs", "key loading failed\n");
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
return 0; continue;
} }
err = grub_ccm_decrypt (cipher, decrypted, key->enc_key, keylen, mac, err = grub_ccm_decrypt (cipher, decrypted, key->enc_key, keylen, mac,
@ -2796,21 +2821,22 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
{ {
grub_dprintf ("zfs", "key loading failed\n"); grub_dprintf ("zfs", "key loading failed\n");
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
return 0; continue;
} }
subvol->cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); subvol->cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
if (!subvol->cipher) if (!subvol->cipher)
{ {
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
return 0; continue;
} }
err = grub_crypto_cipher_set_key (subvol->cipher, decrypted, keylen); err = grub_crypto_cipher_set_key (subvol->cipher, decrypted, keylen);
if (err) if (err)
{ {
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
continue;
}
return 0; return 0;
} }
return 0; return 0;
} }

View file

@ -21,10 +21,12 @@
#include <grub/device.h> #include <grub/device.h>
#include <grub/file.h> #include <grub/file.h>
#include <grub/command.h> #include <grub/command.h>
#include <grub/extcmd.h>
#include <grub/misc.h> #include <grub/misc.h>
#include <grub/mm.h> #include <grub/mm.h>
#include <grub/dl.h> #include <grub/dl.h>
#include <grub/env.h> #include <grub/env.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+"); GRUB_MOD_LICENSE ("GPLv3+");
@ -389,14 +391,78 @@ grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc,
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
static const struct grub_arg_option options[] =
{
{"raw", 'r', 0, N_("Assume input is raw."), 0, 0},
{"hex", 'h', 0, N_("Assume input is hex."), 0, 0},
{"passphrase", 'p', 0, N_("Assume input is passphrase."), 0, 0},
{0, 0, 0, 0, 0, 0}
};
static grub_err_t
grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, char **args)
{
grub_uint8_t buf[1024];
grub_ssize_t real_size;
if (argc > 0)
{
grub_file_t file;
file = grub_file_open (args[0]);
if (!file)
return grub_errno;
real_size = grub_file_read (file, buf, 1024);
if (real_size < 0)
return grub_errno;
}
if (ctxt->state[0].set
|| (argc > 0 && !ctxt->state[1].set && !ctxt->state[2].set))
{
grub_err_t err;
if (real_size < GRUB_ZFS_MAX_KEYLEN)
grub_memset (buf + real_size, 0, GRUB_ZFS_MAX_KEYLEN - real_size);
err = grub_zfs_add_key (buf);
if (err)
return err;
return GRUB_ERR_NONE;
}
if (ctxt->state[1].set)
{
int i;
grub_err_t err;
if (real_size < 2 * GRUB_ZFS_MAX_KEYLEN)
grub_memset (buf + real_size, '0', 2 * GRUB_ZFS_MAX_KEYLEN - real_size);
for (i = 0; i < GRUB_ZFS_MAX_KEYLEN; i++)
{
char c1 = grub_tolower (buf[2 * i]) - '0';
char c2 = grub_tolower (buf[2 * i + 1]) - '0';
if (c1 > 9)
c1 += '0' - 'a' + 10;
if (c2 > 9)
c2 += '0' - 'a' + 10;
buf[i] = (c1 << 4) | c2;
}
err = grub_zfs_add_key (buf);
if (err)
return err;
return GRUB_ERR_NONE;
}
return GRUB_ERR_NONE;
}
static grub_command_t cmd_info, cmd_bootfs; static grub_command_t cmd_info, cmd_bootfs;
static grub_extcmd_t cmd_key;
GRUB_MOD_INIT (zfsinfo) GRUB_MOD_INIT (zfsinfo)
{ {
cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo, cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo,
"zfsinfo DEVICE", "zfsinfo DEVICE",
"Print ZFS info about DEVICE."); "Print ZFS info about DEVICE.");
cmd_key = grub_register_extcmd ("zfskey", grub_cmd_zfs_key, 0,
"zfskey [-h|-p|-r] [FILE]",
"Import ZFS wrapping key stored in FILE.",
options);
cmd_bootfs = grub_register_command ("zfs-bootfs", grub_cmd_zfs_bootfs, cmd_bootfs = grub_register_command ("zfs-bootfs", grub_cmd_zfs_bootfs,
"zfs-bootfs FILESYSTEM [VARIABLE]", "zfs-bootfs FILESYSTEM [VARIABLE]",
"Print ZFS-BOOTFSOBJ or set it to VARIABLE"); "Print ZFS-BOOTFSOBJ or set it to VARIABLE");
@ -406,4 +472,5 @@ GRUB_MOD_FINI (zfsinfo)
{ {
grub_unregister_command (cmd_info); grub_unregister_command (cmd_info);
grub_unregister_command (cmd_bootfs); grub_unregister_command (cmd_bootfs);
grub_unregister_extcmd (cmd_key);
} }

View file

@ -121,5 +121,7 @@ char *grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist,
grub_size_t index); grub_size_t index);
int grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, int grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist,
const char *name); const char *name);
grub_err_t grub_zfs_add_key (grub_uint8_t *key_in);
#define GRUB_ZFS_MAX_KEYLEN 32
#endif /* ! GRUB_ZFS_HEADER */ #endif /* ! GRUB_ZFS_HEADER */

View file

@ -33,6 +33,7 @@
#include <grub/crypto.h> #include <grub/crypto.h>
#include <grub/command.h> #include <grub/command.h>
#include <grub/i18n.h> #include <grub/i18n.h>
#include <grub/zfs/zfs.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -438,6 +439,7 @@ static struct argp_option options[] = {
{"diskcount", 'c', "N", 0, N_("N input files."), 2}, {"diskcount", 'c', "N", 0, N_("N input files."), 2},
{"debug", 'd', "S", 0, N_("Set debug environment variable."), 2}, {"debug", 'd', "S", 0, N_("Set debug environment variable."), 2},
{"crypto", 'C', NULL, OPTION_ARG_OPTIONAL, N_("Mount crypto devices."), 2}, {"crypto", 'C', NULL, OPTION_ARG_OPTIONAL, N_("Mount crypto devices."), 2},
{"zfs-key-file", 'K', N_("KEY_FILENAME"), 0, N_("Load zfs crypto key."), 2},
{"verbose", 'v', NULL, OPTION_ARG_OPTIONAL, N_("Print verbose messages."), 2}, {"verbose", 'v', NULL, OPTION_ARG_OPTIONAL, N_("Print verbose messages."), 2},
{"uncompress", 'u', NULL, OPTION_ARG_OPTIONAL, N_("Uncompress data."), 2}, {"uncompress", 'u', NULL, OPTION_ARG_OPTIONAL, N_("Uncompress data."), 2},
{0, 0, 0, 0, 0, 0} {0, 0, 0, 0, 0, 0}
@ -462,6 +464,30 @@ argp_parser (int key, char *arg, struct argp_state *state)
root = arg; root = arg;
return 0; return 0;
case 'K':
{
FILE *f;
ssize_t real_size;
grub_uint8_t buf[GRUB_ZFS_MAX_KEYLEN];
f = fopen (arg, "rb");
if (!f)
{
printf ("Error loading file %s: %s\n", arg, strerror (errno));
return 0;
}
real_size = fread (buf, 1, GRUB_ZFS_MAX_KEYLEN, f);
if (real_size < 0)
{
printf ("Error loading file %s: %s\n", arg, strerror (errno));
fclose (f);
return 0;
}
if (real_size < GRUB_ZFS_MAX_KEYLEN)
grub_memset (buf + real_size, 0, GRUB_ZFS_MAX_KEYLEN - real_size);
grub_zfs_add_key (buf);
}
return 0;
case 'C': case 'C':
mount_crypt = 1; mount_crypt = 1;
return 0; return 0;