diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 3558bd8bb..0e9869d3f 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -205,7 +205,8 @@ grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher, const grub_uint32_t *expected_mac, grub_zfs_endian_t endian) = NULL; grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key, - grub_size_t keysize) = NULL; + grub_size_t keysize, + grub_uint64_t salt) = NULL; static grub_err_t zlib_decompress (void *s, void *d, @@ -1500,7 +1501,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, if (encrypted) { if (!grub_zfs_decrypt) - err = grub_error (GRUB_ERR_BAD_FS, "zfscrypto module not loaded"); + err = grub_error (GRUB_ERR_BAD_FS, "zfscrypt module not loaded"); else err = grub_zfs_decrypt (data->subvol.cipher, &(bp)->blk_dva[encrypted], compbuf, psize, ((grub_uint32_t *) &zc + 5), @@ -2657,8 +2658,10 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, const char *ptr_at, *filename; grub_uint64_t headobj; grub_uint64_t keychainobj; + grub_uint64_t salt; grub_err_t err; + auto int NESTED_FUNC_ATTR iterate_zap_key (const char *name, const void *val_in, grub_size_t nelem, @@ -2675,7 +2678,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, return 0; } - subvol->cipher = grub_zfs_load_key (val_in, nelem); + subvol->cipher = grub_zfs_load_key (val_in, nelem, salt); return 0; } @@ -2745,7 +2748,32 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian); if (grub_zfs_load_key && keychainobj) { - dnode_end_t keychain_dn; + dnode_end_t keychain_dn, props_dn; + grub_uint64_t propsobj; + propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian); + + err = dnode_get (&(data->mos), propsobj, DMU_OT_DSL_PROPS, + &props_dn, data); + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + + err = zap_lookup (&props_dn, "salt", &salt, data, 0); + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + err = 0; + grub_errno = 0; + salt = 0; + } + if (err) + { + grub_dprintf ("zfs", "failed here\n"); + return err; + } + err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN, &keychain_dn, data); if (err) diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c index 251538041..d8c7181f0 100644 --- a/grub-core/fs/zfs/zfscrypt.c +++ b/grub-core/fs/zfs/zfscrypt.c @@ -64,19 +64,27 @@ struct grub_zfs_key struct grub_zfs_wrap_key { struct grub_zfs_wrap_key *next; - grub_uint64_t key[GRUB_ZFS_MAX_KEYLEN / 8]; + grub_size_t keylen; + int is_passphrase; + grub_uint64_t key[0]; }; static struct grub_zfs_wrap_key *zfs_wrap_keys; grub_err_t -grub_zfs_add_key (grub_uint8_t *key_in) +grub_zfs_add_key (grub_uint8_t *key_in, + grub_size_t keylen, + int passphrase) { struct grub_zfs_wrap_key *key; - key = grub_malloc (sizeof (*key)); + if (!passphrase && keylen > 32) + keylen = 32; + key = grub_malloc (sizeof (*key) + keylen); if (!key) return grub_errno; - grub_memcpy (key->key, key_in, GRUB_ZFS_MAX_KEYLEN); + key->is_passphrase = passphrase; + key->keylen = keylen; + grub_memcpy (key->key, key_in, keylen); key->next = zfs_wrap_keys; zfs_wrap_keys = key; return GRUB_ERR_NONE; @@ -168,7 +176,8 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, void *nonce, static grub_crypto_cipher_handle_t grub_zfs_load_key_real (const struct grub_zfs_key *key, - grub_size_t keysize) + grub_size_t keysize, + grub_uint64_t salt) { unsigned keylen; struct grub_zfs_wrap_key *wrap_key; @@ -192,15 +201,25 @@ grub_zfs_load_key_real (const struct grub_zfs_key *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]; + grub_uint8_t decrypted[32], mac[32], wrap_key_real[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, + grub_memset (wrap_key_real, 0, sizeof (wrap_key_real)); + if (!wrap_key->is_passphrase) + grub_memcpy(wrap_key_real, wrap_key->key, + wrap_key->keylen < keylen ? wrap_key->keylen : keylen); + else + grub_crypto_pbkdf2 (GRUB_MD_SHA1, + (const grub_uint8_t *) wrap_key->key, + wrap_key->keylen, + (const grub_uint8_t *) &salt, sizeof (salt), + 1000, wrap_key_real, keylen); + + err = grub_crypto_cipher_set_key (cipher, wrap_key_real, keylen); if (err) { @@ -268,25 +287,19 @@ grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, char **args) if (real_size < 0) return grub_errno; } - if (ctxt->state[0].set - || (argc > 0 && !ctxt->state[1].set && !ctxt->state[2].set)) + else { - 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; + grub_printf ("Enter ZFS password: "); + if (!grub_password_get ((char *) buf, 1023)) + return grub_errno; + real_size = grub_strlen ((char *) buf); } 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++) + for (i = 0; i < real_size / 2; i++) { char c1 = grub_tolower (buf[2 * i]) - '0'; char c2 = grub_tolower (buf[2 * i + 1]) - '0'; @@ -296,12 +309,16 @@ grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, char **args) c2 += '0' - 'a' + 10; buf[i] = (c1 << 4) | c2; } - err = grub_zfs_add_key (buf); + err = grub_zfs_add_key (buf, real_size / 2, 0); if (err) return err; return GRUB_ERR_NONE; } - return GRUB_ERR_NONE; + + return grub_zfs_add_key (buf, real_size, + ctxt->state[2].set + || (argc == 0 && !ctxt->state[0].set + && !ctxt->state[1].set)); } static grub_extcmd_t cmd_key; diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h index 6c280bfe2..db8e915bf 100644 --- a/include/grub/zfs/zfs.h +++ b/include/grub/zfs/zfs.h @@ -129,8 +129,10 @@ 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 +grub_err_t +grub_zfs_add_key (grub_uint8_t *key_in, + grub_size_t keylen, + int passphrase); extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher, void *nonce, @@ -141,7 +143,8 @@ extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher, struct grub_zfs_key; extern grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key, - grub_size_t keysize); + grub_size_t keysize, + grub_uint64_t salt); diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 0d0801187..f90755e86 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -439,7 +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}, + {"zfs-key", 'K', N_("FILE|prompt"), 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} @@ -465,26 +465,34 @@ argp_parser (int key, char *arg, struct argp_state *state) return 0; case 'K': + if (strcmp (arg, "prompt") == 0) + { + char buf[1024]; + grub_printf ("Enter ZFS password: "); + if (grub_password_get (buf, 1023)) + { + grub_zfs_add_key ((grub_uint8_t *) buf, grub_strlen (buf), 1); + } + } + else { FILE *f; ssize_t real_size; - grub_uint8_t buf[GRUB_ZFS_MAX_KEYLEN]; + grub_uint8_t buf[1024]; 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); + real_size = fread (buf, 1, 1024, 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); + grub_zfs_add_key (buf, real_size, 0); } return 0;