From 2cdc899567c4db45ab0a3a5eae2aa91faa7cff92 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 14:44:29 +0100 Subject: [PATCH] ZFS crypto key adding --- grub-core/fs/zfs/zfs.c | 114 +++++++++++++++++++++++-------------- grub-core/fs/zfs/zfsinfo.c | 67 ++++++++++++++++++++++ include/grub/zfs/zfs.h | 2 + util/grub-fstest.c | 26 +++++++++ 4 files changed, 165 insertions(+), 44 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 9da4d70e2..4008d17f4 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -1,6 +1,6 @@ /* * 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. * * 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]; }; +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); 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; }; +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 zlib_decompress (void *s, void *d, 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) { 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; + struct grub_zfs_wrap_key *wrap_key; if (elemsize != 1 || nelem != sizeof (*key)) { @@ -2766,51 +2786,57 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, else 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); + if (!cipher) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + err = grub_crypto_cipher_set_key (cipher, + (const grub_uint8_t *) wrap_key->key, + keylen); + if (err) + { + grub_errno = GRUB_ERR_NONE; + continue; + } - cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); - if (!cipher) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - err = grub_crypto_cipher_set_key (cipher, wrapping_key, keylen); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } + err = grub_ccm_decrypt (cipher, decrypted, key->unknown_purpose_key, 32, + mac, key->unknown_purpose_nonce, 2, 16); + if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) + != 0)) + { + grub_dprintf ("zfs", "key loading failed\n"); + grub_errno = GRUB_ERR_NONE; + continue; + } - err = grub_ccm_decrypt (cipher, decrypted, key->unknown_purpose_key, 32, - mac, key->unknown_purpose_nonce, 2, 16); - if (err || grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) != 0) - { - grub_dprintf ("zfs", "key loading failed\n"); - grub_errno = GRUB_ERR_NONE; + err = grub_ccm_decrypt (cipher, decrypted, key->enc_key, keylen, mac, + key->enc_nonce, 2, 16); + if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0) + { + grub_dprintf ("zfs", "key loading failed\n"); + grub_errno = GRUB_ERR_NONE; + continue; + } + subvol->cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); + if (!subvol->cipher) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + err = grub_crypto_cipher_set_key (subvol->cipher, decrypted, keylen); + if (err) + { + grub_errno = GRUB_ERR_NONE; + continue; + } return 0; } - - err = grub_ccm_decrypt (cipher, decrypted, key->enc_key, keylen, mac, - key->enc_nonce, 2, 16); - if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0) - { - grub_dprintf ("zfs", "key loading failed\n"); - grub_errno = GRUB_ERR_NONE; - return 0; - } - subvol->cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); - if (!subvol->cipher) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - err = grub_crypto_cipher_set_key (subvol->cipher, decrypted, keylen); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - return 0; } diff --git a/grub-core/fs/zfs/zfsinfo.c b/grub-core/fs/zfs/zfsinfo.c index 8c073fab5..dfc238d11 100644 --- a/grub-core/fs/zfs/zfsinfo.c +++ b/grub-core/fs/zfs/zfsinfo.c @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -389,14 +391,78 @@ grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc, 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_extcmd_t cmd_key; GRUB_MOD_INIT (zfsinfo) { cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo, "zfsinfo 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, "zfs-bootfs FILESYSTEM [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_bootfs); + grub_unregister_extcmd (cmd_key); } diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h index d7029903a..62b72776e 100644 --- a/include/grub/zfs/zfs.h +++ b/include/grub/zfs/zfs.h @@ -121,5 +121,7 @@ char *grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, grub_size_t index); int grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, 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 */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 996d71c3a..0d0801187 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -438,6 +439,7 @@ static struct argp_option options[] = { {"diskcount", 'c', "N", 0, N_("N input files."), 2}, {"debug", 'd', "S", 0, N_("Set debug environment variable."), 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}, {"uncompress", 'u', NULL, OPTION_ARG_OPTIONAL, N_("Uncompress data."), 2}, {0, 0, 0, 0, 0, 0} @@ -462,6 +464,30 @@ argp_parser (int key, char *arg, struct argp_state *state) root = arg; 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': mount_crypt = 1; return 0;