ZFS crypto support.

* Makefile.util.def (libgrubmods): Add grub-core/fs/zfs/zfscrypt.c.
	* grub-core/Makefile.core.def (zfscrypt): New module.
	* grub-core/fs/zfs/zfs.c (subvolume): New structure.
	(grub_zfs_data): Replace mdn with subvol. Put case_insensitivity inside
	it. All users updated.
	(grub_zfs_decrypt): New var.
	(grub_zfs_load_key): Likewise.
	(zio_checksum_functions): Add SHA256+MAC.
	(zio_checksum_verify): Handle incomplete comparison due to MAC.
	(zio_read): Handle encrypted blocks.
	(zap_verify): Remove incorrect check.
	(fzap_iterate): Handle non-standard fzap.
	(zap_iterate): Likewise.
	(zap_iterate_u64): New function.
	(dnode_get_fullpath): Load keys.
	* grub-core/fs/zfs/zfscrypt.c: New file.
	* grub-core/lib/crypto.c (grub_crypto_cipher_close): Removed.
	(grub_crypto_ecb_encrypt): Make input const.
	* include/grub/crypto.h (grub_crypto_cipher_close): Inline.
	(grub_crypto_ecb_encrypt): Make input const.
	(GRUB_CIPHER_AES): New macro.
	* include/grub/zfs/dmu.h (dmu_object_type): Add DMU_OT_DSL_KEYCHAIN.
	* include/grub/zfs/dsl_dir.h (dsl_dir_phys): Add keychain.
	* include/grub/zfs/spa.h (grub_zfs_endian): Moved from here ...
	* include/grub/zfs/zfs.h (grub_zfs_endian): ... here. Added GURB_ZFS_
	prefix. All users updated.
	(grub_zfs_add_key): New proto.
	(grub_zfs_decrypt): Likewise.
	(grub_zfs_load_key): Likewise.
	* include/grub/zfs/zio.h (zio_checksum): Add SHA256+MAC.
	* util/grub-fstest.c (options): Add -K option.
	(argp_parser): Likewise.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-11-06 21:44:48 +01:00
commit 3084ede4c7
13 changed files with 950 additions and 127 deletions

View file

@ -1,3 +1,40 @@
2011-11-06 Vladimir Serbinenko <phcoder@gmail.com>
ZFS crypto support.
* Makefile.util.def (libgrubmods): Add grub-core/fs/zfs/zfscrypt.c.
* grub-core/Makefile.core.def (zfscrypt): New module.
* grub-core/fs/zfs/zfs.c (subvolume): New structure.
(grub_zfs_data): Replace mdn with subvol. Put case_insensitivity inside
it. All users updated.
(grub_zfs_decrypt): New var.
(grub_zfs_load_key): Likewise.
(zio_checksum_functions): Add SHA256+MAC.
(zio_checksum_verify): Handle incomplete comparison due to MAC.
(zio_read): Handle encrypted blocks.
(zap_verify): Remove incorrect check.
(fzap_iterate): Handle non-standard fzap.
(zap_iterate): Likewise.
(zap_iterate_u64): New function.
(dnode_get_fullpath): Load keys.
* grub-core/fs/zfs/zfscrypt.c: New file.
* grub-core/lib/crypto.c (grub_crypto_cipher_close): Removed.
(grub_crypto_ecb_encrypt): Make input const.
* include/grub/crypto.h (grub_crypto_cipher_close): Inline.
(grub_crypto_ecb_encrypt): Make input const.
(GRUB_CIPHER_AES): New macro.
* include/grub/zfs/dmu.h (dmu_object_type): Add DMU_OT_DSL_KEYCHAIN.
* include/grub/zfs/dsl_dir.h (dsl_dir_phys): Add keychain.
* include/grub/zfs/spa.h (grub_zfs_endian): Moved from here ...
* include/grub/zfs/zfs.h (grub_zfs_endian): ... here. Added GURB_ZFS_
prefix. All users updated.
(grub_zfs_add_key): New proto.
(grub_zfs_decrypt): Likewise.
(grub_zfs_load_key): Likewise.
* include/grub/zfs/zio.h (zio_checksum): Add SHA256+MAC.
* util/grub-fstest.c (options): Add -K option.
(argp_parser): Likewise.
2011-11-05 Vladimir Serbinenko <phcoder@gmail.com>
Support zle compression on ZFS.

View file

@ -82,6 +82,7 @@ library = {
common = grub-core/fs/ufs2.c;
common = grub-core/fs/ufs.c;
common = grub-core/fs/xfs.c;
common = grub-core/fs/zfs/zfscrypt.c;
common = grub-core/fs/zfs/zfs.c;
common = grub-core/fs/zfs/zfsinfo.c;
common = grub-core/fs/zfs/zfs_lzjb.c;

View file

@ -1116,6 +1116,11 @@ module = {
common = fs/zfs/zfs_fletcher.c;
};
module = {
name = zfscrypt;
common = fs/zfs/zfscrypt.c;
};
module = {
name = zfsinfo;
common = fs/zfs/zfsinfo.c;

View file

@ -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
@ -52,6 +52,7 @@
#include <grub/zfs/dsl_dir.h>
#include <grub/zfs/dsl_dataset.h>
#include <grub/deflate.h>
#include <grub/crypto.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -161,6 +162,20 @@ struct grub_zfs_device_desc
int original;
};
struct subvolume
{
dnode_end_t mdn;
grub_uint64_t obj;
grub_uint64_t case_insensitive;
grub_size_t nkeys;
struct
{
grub_crypto_cipher_handle_t cipher;
grub_uint64_t txg;
grub_uint64_t algo;
} *keyring;
};
struct grub_zfs_data
{
/* cache for a file block of the currently zfs_open()-ed file */
@ -176,8 +191,8 @@ struct grub_zfs_data
grub_zfs_endian_t dnode_endian;
dnode_end_t mos;
dnode_end_t mdn;
dnode_end_t dnode;
struct subvolume subvol;
struct grub_zfs_device_desc *devices_attached;
unsigned n_devices_attached;
@ -190,6 +205,17 @@ struct grub_zfs_data
grub_uint64_t guid;
};
grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
grub_uint64_t algo,
void *nonce,
char *buf, grub_size_t size,
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,
grub_uint64_t salt,
grub_uint64_t algo) = NULL;
static grub_err_t
zlib_decompress (void *s, void *d,
grub_size_t slen, grub_size_t dlen)
@ -292,6 +318,7 @@ static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
{fletcher_4, 1, 0, "fletcher4"},
{zio_checksum_SHA256, 1, 0, "SHA256"},
{NULL, 0, 0, "zilog2"},
{zio_checksum_SHA256, 1, 0, "SHA256+MAC"},
};
/*
@ -302,7 +329,8 @@ static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
*/
static grub_err_t
zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
grub_zfs_endian_t endian, char *buf, grub_size_t size)
grub_zfs_endian_t endian,
char *buf, grub_size_t size)
{
zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
@ -326,10 +354,8 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
else
ci->ci_func (buf, size, endian, &actual_cksum);
if ((actual_cksum.zc_word[0] != zc.zc_word[0])
|| (actual_cksum.zc_word[1] != zc.zc_word[1])
|| (actual_cksum.zc_word[2] != zc.zc_word[2])
|| (actual_cksum.zc_word[3] != zc.zc_word[3]))
if (grub_memcmp (&actual_cksum, &zc,
checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20) != 0)
{
grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name);
grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n",
@ -362,14 +388,16 @@ static int
vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2)
{
grub_zfs_endian_t ub1_endian, ub2_endian;
if (grub_zfs_to_cpu64 (ub1->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC)
ub1_endian = LITTLE_ENDIAN;
if (grub_zfs_to_cpu64 (ub1->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
== UBERBLOCK_MAGIC)
ub1_endian = GRUB_ZFS_LITTLE_ENDIAN;
else
ub1_endian = BIG_ENDIAN;
if (grub_zfs_to_cpu64 (ub2->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC)
ub2_endian = LITTLE_ENDIAN;
ub1_endian = GRUB_ZFS_BIG_ENDIAN;
if (grub_zfs_to_cpu64 (ub2->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
== UBERBLOCK_MAGIC)
ub2_endian = GRUB_ZFS_LITTLE_ENDIAN;
else
ub2_endian = BIG_ENDIAN;
ub2_endian = GRUB_ZFS_BIG_ENDIAN;
if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian)
< grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
@ -401,20 +429,23 @@ uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset)
{
uberblock_t *uber = &ub->ubp_uberblock;
grub_err_t err;
grub_zfs_endian_t endian = UNKNOWN_ENDIAN;
grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN;
zio_cksum_t zc;
if (grub_zfs_to_cpu64 (uber->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC
&& grub_zfs_to_cpu64 (uber->ub_version, LITTLE_ENDIAN) > 0
&& grub_zfs_to_cpu64 (uber->ub_version, LITTLE_ENDIAN) <= SPA_VERSION)
endian = LITTLE_ENDIAN;
if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
== UBERBLOCK_MAGIC
&& grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0
&& grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN)
<= SPA_VERSION)
endian = GRUB_ZFS_LITTLE_ENDIAN;
if (grub_zfs_to_cpu64 (uber->ub_magic, BIG_ENDIAN) == UBERBLOCK_MAGIC
&& grub_zfs_to_cpu64 (uber->ub_version, BIG_ENDIAN) > 0
&& grub_zfs_to_cpu64 (uber->ub_version, BIG_ENDIAN) <= SPA_VERSION)
endian = BIG_ENDIAN;
if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC
&& grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0
&& grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN)
<= SPA_VERSION)
endian = GRUB_ZFS_BIG_ENDIAN;
if (endian == UNKNOWN_ENDIAN)
if (endian == GRUB_ZFS_UNKNOWN_ENDIAN)
return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic");
grub_memset (&zc, 0, sizeof (zc));
@ -1335,7 +1366,7 @@ zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
zio_gb = grub_malloc (SPA_GANGBLOCKSIZE);
if (!zio_gb)
return grub_errno;
grub_dprintf ("zfs", endian == LITTLE_ENDIAN ? "little-endian gang\n"
grub_dprintf ("zfs", endian == GRUB_ZFS_LITTLE_ENDIAN ? "little-endian gang\n"
:"big-endian gang\n");
err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE);
@ -1419,7 +1450,7 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf,
grub_size_t *size, struct grub_zfs_data *data)
{
grub_size_t lsize, psize;
unsigned int comp;
unsigned int comp, encrypted;
char *compbuf = NULL;
grub_err_t err;
zio_cksum_t zc = bp->blk_cksum;
@ -1429,6 +1460,7 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf,
checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff;
comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff;
encrypted = ((grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 60) & 3);
lsize = (BP_IS_HOLE(bp) ? 0 :
(((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1)
<< SPA_MINBLOCKSHIFT));
@ -1447,7 +1479,8 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf,
if (comp != ZIO_COMPRESS_OFF)
{
compbuf = grub_malloc (psize);
/* It's not really necessary to align to 16, just for safety. */
compbuf = grub_malloc (ALIGN_UP (psize, 16));
if (! compbuf)
return grub_errno;
}
@ -1462,8 +1495,10 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf,
*buf = NULL;
return err;
}
grub_memset (compbuf, 0, ALIGN_UP (psize, 16) - psize);
err = zio_checksum_verify (zc, checksum, endian, compbuf, psize);
err = zio_checksum_verify (zc, checksum, endian,
compbuf, psize);
if (err)
{
grub_dprintf ("zfs", "incorrect checksum\n");
@ -1472,6 +1507,51 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf,
return err;
}
if (encrypted)
{
if (!grub_zfs_decrypt)
err = grub_error (GRUB_ERR_BAD_FS, "zfscrypt module not loaded");
else
{
unsigned i, besti = 0;
grub_uint64_t bestval = 0;
for (i = 0; i < data->subvol.nkeys; i++)
if (data->subvol.keyring[i].txg <= grub_zfs_to_cpu64 (bp->blk_birth,
endian)
&& data->subvol.keyring[i].txg > bestval)
{
besti = i;
bestval = data->subvol.keyring[i].txg;
}
if (bestval == 0)
{
grub_free (compbuf);
*buf = NULL;
grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
grub_zfs_to_cpu64 (bp->blk_birth,
endian));
return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
}
grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
", %p) for txg %" PRIxGRUB_UINT64_T "\n",
besti, data->subvol.keyring[besti].txg,
data->subvol.keyring[besti].cipher,
grub_zfs_to_cpu64 (bp->blk_birth,
endian));
err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher,
data->subvol.keyring[besti].algo,
&(bp)->blk_dva[encrypted],
compbuf, psize, ((grub_uint32_t *) &zc + 5),
endian);
}
if (err)
{
grub_free (compbuf);
*buf = NULL;
return err;
}
}
if (comp != ZIO_COMPRESS_OFF)
{
*buf = grub_malloc (lsize);
@ -1572,7 +1652,7 @@ dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
*/
static grub_err_t
mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
int objsize, char *name, grub_uint64_t * value,
int objsize, const char *name, grub_uint64_t * value,
int case_insensitive)
{
int i, chunks;
@ -1799,9 +1879,6 @@ zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian)
if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC)
return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic");
if (zap->zap_flags != 0)
return grub_error (GRUB_ERR_BAD_FS, "bad ZAP flags");
if (zap->zap_salt == 0)
return grub_error (GRUB_ERR_BAD_FS, "bad ZAP salt");
@ -1854,8 +1931,12 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
/* XXX */
static int
fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
int NESTED_FUNC_ATTR (*hook) (const char *name,
grub_uint64_t val),
grub_size_t name_elem_length,
int NESTED_FUNC_ATTR (*hook) (const void *name,
grub_size_t name_length,
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize),
struct grub_zfs_data *data)
{
zap_leaf_phys_t *l;
@ -1918,9 +1999,9 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS (blksft); chunk++)
{
char *buf;
struct zap_leaf_array *la;
struct zap_leaf_entry *le;
grub_uint64_t val;
char *val;
grub_size_t val_length;
le = ZAP_LEAF_ENTRY (l, blksft, chunk);
/* Verify the chunk entry */
@ -1928,36 +2009,42 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
continue;
buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian)
+ 1);
* name_elem_length + 1);
if (zap_leaf_array_get (l, endian, blksft,
grub_zfs_to_cpu16 (le->le_name_chunk,
endian),
grub_zfs_to_cpu16 (le->le_name_length,
endian), buf))
endian)
* name_elem_length, buf))
{
grub_free (buf);
continue;
}
buf[le->le_name_length] = 0;
buf[le->le_name_length * name_elem_length] = 0;
if (le->le_int_size != 8
|| grub_zfs_to_cpu16 (le->le_value_length, endian) != 1)
continue;
/* get the uint64_t property value */
la = &ZAP_LEAF_CHUNK (l, blksft,
val_length = ((int) le->le_value_length
* (int) le->le_int_size);
val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian));
if (zap_leaf_array_get (l, endian, blksft,
grub_zfs_to_cpu16 (le->le_value_chunk,
endian)).l_array;
val = grub_be_to_cpu64 (la->la_array64);
if (hook (buf, val))
endian),
val_length, val))
{
grub_free (buf);
grub_free (val);
continue;
}
if (hook (buf, le->le_name_length,
val, le->le_value_length, le->le_int_size))
return 1;
grub_free (buf);
grub_free (val);
}
}
return 0;
}
/*
* Read in the data of a zap object and find the value for a matching
* property name.
@ -2009,8 +2096,9 @@ zap_lookup (dnode_end_t * zap_dnode, char *name, grub_uint64_t * val,
}
static int
zap_iterate (dnode_end_t * zap_dnode,
int NESTED_FUNC_ATTR (*hook) (const char *name, grub_uint64_t val),
zap_iterate_u64 (dnode_end_t * zap_dnode,
int NESTED_FUNC_ATTR (*hook) (const char *name,
grub_uint64_t val),
struct grub_zfs_data *data)
{
grub_uint64_t block_type;
@ -2020,6 +2108,23 @@ zap_iterate (dnode_end_t * zap_dnode,
int ret;
grub_zfs_endian_t endian;
auto int NESTED_FUNC_ATTR transform (const void *name,
grub_size_t namelen,
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize);
int NESTED_FUNC_ATTR transform (const void *name,
grub_size_t namelen __attribute__ ((unused)),
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize)
{
if (elemsize != sizeof (grub_uint64_t) || nelem != 1)
return 0;
return hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in));
}
/* Read in the first block of the zap object data. */
size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
@ -2040,7 +2145,50 @@ zap_iterate (dnode_end_t * zap_dnode,
{
grub_dprintf ("zfs", "fat zap\n");
/* this is a fat zap */
ret = fzap_iterate (zap_dnode, zapbuf, hook, data);
ret = fzap_iterate (zap_dnode, zapbuf, 1, transform, data);
grub_free (zapbuf);
return ret;
}
grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
return 0;
}
static int
zap_iterate (dnode_end_t * zap_dnode,
grub_size_t nameelemlen,
int NESTED_FUNC_ATTR (*hook) (const void *name,
grub_size_t namelen,
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize),
struct grub_zfs_data *data)
{
grub_uint64_t block_type;
int size;
void *zapbuf;
grub_err_t err;
int ret;
grub_zfs_endian_t endian;
/* Read in the first block of the zap object data. */
size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
if (err)
return 0;
block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
grub_dprintf ("zfs", "zap iterate\n");
if (block_type == ZBT_MICRO)
{
grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected");
return 0;
}
if (block_type == ZBT_HEADER)
{
grub_dprintf ("zfs", "fat zap\n");
/* this is a fat zap */
ret = fzap_iterate (zap_dnode, zapbuf, nameelemlen, hook, data);
grub_free (zapbuf);
return ret;
}
@ -2124,10 +2272,10 @@ dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
*
*/
static grub_err_t
dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
struct grub_zfs_data *data, int *case_insensitive)
dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
struct grub_zfs_data *data)
{
grub_uint64_t objnum, version, insensitivity;
grub_uint64_t objnum, version;
char *cname, ch;
grub_err_t err = GRUB_ERR_NONE;
char *path, *path_buf;
@ -2144,7 +2292,7 @@ dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
dn_new->next = 0;
dnode_path = root = dn_new;
err = dnode_get (mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE,
err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE,
&(dnode_path->dn), data);
if (err)
{
@ -2166,15 +2314,14 @@ dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "too new ZPL version");
}
err = zap_lookup (&(dnode_path->dn), "casesensitivity", &insensitivity,
err = zap_lookup (&(dnode_path->dn), "casesensitivity",
&subvol->case_insensitive,
data, 0);
if (err == GRUB_ERR_FILE_NOT_FOUND)
{
grub_errno = GRUB_ERR_NONE;
insensitivity = 0;
subvol->case_insensitive = 0;
}
if (case_insensitive)
*case_insensitive = insensitivity;
err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data, 0);
if (err)
@ -2183,7 +2330,7 @@ dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
return err;
}
err = dnode_get (mdn, objnum, 0, &(dnode_path->dn), data);
err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data);
if (err)
{
grub_free (dn_new);
@ -2237,7 +2384,8 @@ dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
grub_free (path_buf);
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
}
err = zap_lookup (&(dnode_path->dn), cname, &objnum, data, insensitivity);
err = zap_lookup (&(dnode_path->dn), cname, &objnum,
data, subvol->case_insensitive);
if (err)
break;
@ -2251,7 +2399,7 @@ dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn,
dnode_path = dn_new;
objnum = ZFS_DIRENT_OBJ (objnum);
err = dnode_get (mdn, objnum, 0, &(dnode_path->dn), data);
err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data);
if (err)
break;
@ -2545,14 +2693,65 @@ make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
}
static grub_err_t
dnode_get_fullpath (const char *fullpath, dnode_end_t * mdn,
grub_uint64_t *mdnobj, dnode_end_t * dn, int *isfs,
struct grub_zfs_data *data, int *case_insensitive)
dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
dnode_end_t * dn, int *isfs,
struct grub_zfs_data *data)
{
char *fsname, *snapname;
const char *ptr_at, *filename;
grub_uint64_t headobj;
grub_uint64_t keychainobj;
grub_uint64_t salt;
grub_err_t err;
int keyn = 0;
auto int NESTED_FUNC_ATTR count_zap_keys (const void *name,
grub_size_t namelen,
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize);
int NESTED_FUNC_ATTR count_zap_keys (const void *name __attribute__ ((unused)),
grub_size_t namelen __attribute__ ((unused)),
const void *val_in __attribute__ ((unused)),
grub_size_t nelem __attribute__ ((unused)),
grub_size_t elemsize __attribute__ ((unused)))
{
subvol->nkeys++;
return 0;
}
auto int NESTED_FUNC_ATTR load_zap_key (const void *name,
grub_size_t namelen,
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize);
int NESTED_FUNC_ATTR load_zap_key (const void *name,
grub_size_t namelen,
const void *val_in,
grub_size_t nelem,
grub_size_t elemsize)
{
if (namelen != 1)
{
grub_dprintf ("zfs", "Unexpected key index size %" PRIuGRUB_SIZE "\n",
namelen);
return 0;
}
if (elemsize != 1)
{
grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n",
elemsize);
return 0;
}
subvol->keyring[keyn].txg = grub_be_to_cpu64 (*(grub_uint64_t *) name);
subvol->keyring[keyn].algo = grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
subvol->keyring[keyn].cipher = grub_zfs_load_key (val_in, nelem, salt,
subvol->keyring[keyn].algo);
keyn++;
return 0;
}
ptr_at = grub_strchr (fullpath, '@');
if (! ptr_at)
@ -2605,29 +2804,80 @@ dnode_get_fullpath (const char *fullpath, dnode_end_t * mdn,
headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian);
grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data);
err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &subvol->mdn,
data);
if (err)
{
grub_free (fsname);
grub_free (snapname);
return err;
}
grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
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, 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)
{
grub_free (fsname);
grub_free (snapname);
return err;
}
subvol->nkeys = 0;
zap_iterate (&keychain_dn, 8, count_zap_keys, data);
subvol->keyring = grub_zalloc (subvol->nkeys * sizeof (subvol->keyring[0]));
if (!subvol->keyring)
{
grub_free (fsname);
grub_free (snapname);
return err;
}
zap_iterate (&keychain_dn, 8, load_zap_key, data);
}
if (snapname)
{
grub_uint64_t snapobj;
snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_snapnames_zapobj, mdn->endian);
snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian);
err = dnode_get (&(data->mos), snapobj,
DMU_OT_DSL_DS_SNAP_MAP, mdn, data);
DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data);
if (!err)
err = zap_lookup (mdn, snapname, &headobj, data, 0);
err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0);
if (!err)
err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data);
err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET,
&subvol->mdn, data);
if (err)
{
grub_free (fsname);
@ -2636,12 +2886,11 @@ dnode_get_fullpath (const char *fullpath, dnode_end_t * mdn,
}
}
if (mdnobj)
*mdnobj = headobj;
subvol->obj = headobj;
make_mdn (mdn, data);
make_mdn (&subvol->mdn, data);
grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
if (*isfs)
{
@ -2649,7 +2898,7 @@ dnode_get_fullpath (const char *fullpath, dnode_end_t * mdn,
grub_free (snapname);
return GRUB_ERR_NONE;
}
err = dnode_get_path (mdn, filename, dn, data, case_insensitive);
err = dnode_get_path (subvol, filename, dn, data);
grub_free (fsname);
grub_free (snapname);
return err;
@ -2918,6 +3167,9 @@ zfs_unmount (struct grub_zfs_data *data)
grub_free (data->dnode_buf);
grub_free (data->dnode_mdn);
grub_free (data->file_buf);
for (i = 0; i < data->subvol.nkeys; i++)
grub_crypto_cipher_close (data->subvol.keyring[i].cipher);
grub_free (data->subvol.keyring);
grub_free (data);
}
@ -2933,7 +3185,7 @@ zfs_mount (grub_device_t dev)
grub_err_t err;
objset_phys_t *osp = 0;
grub_size_t ospsize;
grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN;
grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
uberblock_t *ub;
if (! dev->disk)
@ -2964,8 +3216,8 @@ zfs_mount (grub_device_t dev)
ub = &(data->current_uberblock);
ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic,
LITTLE_ENDIAN) == UBERBLOCK_MAGIC
? LITTLE_ENDIAN : BIG_ENDIAN);
GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC
? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
err = zio_read (&ub->ub_rootbp, ub_endian,
(void **) &osp, &ospsize, data);
@ -3054,7 +3306,7 @@ static grub_err_t
zfs_mtime (grub_device_t device, grub_int32_t *mt)
{
struct grub_zfs_data *data;
grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN;
grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
uberblock_t *ub;
*mt = 0;
@ -3065,8 +3317,8 @@ zfs_mtime (grub_device_t device, grub_int32_t *mt)
ub = &(data->current_uberblock);
ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic,
LITTLE_ENDIAN) == UBERBLOCK_MAGIC
? LITTLE_ENDIAN : BIG_ENDIAN);
GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC
? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
*mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian);
zfs_unmount (data);
@ -3088,8 +3340,8 @@ grub_zfs_open (struct grub_file *file, const char *fsfilename)
if (! data)
return grub_errno;
err = dnode_get_fullpath (fsfilename, &(data->mdn), 0,
&(data->dnode), &isfs, data, NULL);
err = dnode_get_fullpath (fsfilename, &(data->subvol),
&(data->dnode), &isfs, data);
if (err)
{
zfs_unmount (data);
@ -3252,8 +3504,9 @@ grub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename,
if (! data)
return grub_errno;
err = dnode_get_fullpath (fsfilename, &(data->mdn), mdnobj,
&(data->dnode), &isfs, data, NULL);
err = dnode_get_fullpath (fsfilename, &(data->subvol),
&(data->dnode), &isfs, data);
*mdnobj = data->subvol.obj;
zfs_unmount (data);
return err;
}
@ -3348,7 +3601,6 @@ grub_zfs_dir (grub_device_t device, const char *path,
struct grub_zfs_data *data;
grub_err_t err;
int isfs;
int case_insensitive = 0;
auto int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val);
auto int NESTED_FUNC_ATTR iterate_zap_fs (const char *name,
grub_uint64_t val);
@ -3361,7 +3613,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
dnode_end_t dn;
grub_memset (&info, 0, sizeof (info));
dnode_get (&(data->mdn), val, 0, &dn, data);
dnode_get (&(data->subvol.mdn), val, 0, &dn, data);
if (dn.dn.dn_bonustype == DMU_OT_SA)
{
@ -3393,7 +3645,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
info.mtimeset = 1;
info.mtime = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
info.case_insensitive = case_insensitive;
info.case_insensitive = data->subvol.case_insensitive;
}
if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
@ -3448,8 +3700,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
data = zfs_mount (device);
if (! data)
return grub_errno;
err = dnode_get_fullpath (path, &(data->mdn), 0, &(data->dnode), &isfs, data,
&case_insensitive);
err = dnode_get_fullpath (path, &(data->subvol), &(data->dnode), &isfs, data);
if (err)
{
zfs_unmount (data);
@ -3475,7 +3726,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
return err;
}
zap_iterate (&dn, iterate_zap_fs, data);
zap_iterate_u64 (&dn, iterate_zap_fs, data);
err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data);
if (err)
@ -3494,7 +3745,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
return err;
}
zap_iterate (&dn, iterate_zap_snap, data);
zap_iterate_u64 (&dn, iterate_zap_snap, data);
}
else
{
@ -3503,7 +3754,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
zfs_unmount (data);
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
}
zap_iterate (&(data->dnode), iterate_zap, data);
zap_iterate_u64 (&(data->dnode), iterate_zap, data);
}
zfs_unmount (data);
return grub_errno;

466
grub-core/fs/zfs/zfscrypt.c Normal file
View file

@ -0,0 +1,466 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/err.h>
#include <grub/file.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/disk.h>
#include <grub/partition.h>
#include <grub/dl.h>
#include <grub/types.h>
#include <grub/zfs/zfs.h>
#include <grub/zfs/zio.h>
#include <grub/zfs/dnode.h>
#include <grub/zfs/uberblock_impl.h>
#include <grub/zfs/vdev_impl.h>
#include <grub/zfs/zio_checksum.h>
#include <grub/zfs/zap_impl.h>
#include <grub/zfs/zap_leaf.h>
#include <grub/zfs/zfs_znode.h>
#include <grub/zfs/dmu.h>
#include <grub/zfs/dmu_objset.h>
#include <grub/zfs/sa_impl.h>
#include <grub/zfs/dsl_dir.h>
#include <grub/zfs/dsl_dataset.h>
#include <grub/crypto.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
GRUB_MOD_LICENSE ("GPLv3+");
enum grub_zfs_algo
{
GRUB_ZFS_ALGO_CCM,
GRUB_ZFS_ALGO_GCM,
};
struct grub_zfs_key
{
grub_uint64_t algo;
grub_uint8_t enc_nonce[13];
grub_uint8_t unused[3];
grub_uint8_t enc_key[48];
grub_uint8_t unknown_purpose_nonce[13];
grub_uint8_t unused2[3];
grub_uint8_t unknown_purpose_key[48];
};
struct grub_zfs_wrap_key
{
struct grub_zfs_wrap_key *next;
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_size_t keylen,
int passphrase)
{
struct grub_zfs_wrap_key *key;
if (!passphrase && keylen > 32)
keylen = 32;
key = grub_malloc (sizeof (*key) + keylen);
if (!key)
return grub_errno;
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;
}
static grub_err_t
grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher,
grub_uint8_t *out, const grub_uint8_t *in,
grub_size_t psize,
void *mac_out, const void *nonce,
unsigned l, unsigned m)
{
grub_uint8_t iv[16];
grub_uint8_t mul[16];
grub_uint32_t mac[4];
unsigned i, j;
grub_err_t err;
grub_memcpy (iv + 1, nonce, 15 - l);
iv[0] = (l - 1) | (((m-2) / 2) << 3);
for (j = 0; j < l; j++)
iv[15 - j] = psize >> (8 * j);
err = grub_crypto_ecb_encrypt (cipher, mac, iv, 16);
if (err)
return err;
iv[0] = l - 1;
for (i = 0; i < (psize + 15) / 16; i++)
{
grub_size_t csize;
csize = 16;
if (csize > psize - 16 * i)
csize = psize - 16 * i;
for (j = 0; j < l; j++)
iv[15 - j] = (i + 1) >> (8 * j);
err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16);
if (err)
return err;
grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize);
grub_crypto_xor (mac, mac, out + 16 * i, csize);
err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16);
if (err)
return err;
}
for (j = 0; j < l; j++)
iv[15 - j] = 0;
err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16);
if (err)
return err;
if (mac_out)
grub_crypto_xor (mac_out, mac, mul, m);
return GRUB_ERR_NONE;
}
static void
grub_gcm_mul_x (grub_uint8_t *a)
{
int i;
int c = 0, d = 0;
for (i = 0; i < 16; i++)
{
c = a[i] & 0x1;
a[i] = (a[i] >> 1) | (d << 7);
d = c;
}
if (d)
a[0] ^= 0xe1;
}
static void
grub_gcm_mul (grub_uint8_t *a, const grub_uint8_t *b)
{
grub_uint8_t res[16], bs[16];
int i;
grub_memcpy (bs, b, 16);
grub_memset (res, 0, 16);
for (i = 0; i < 128; i++)
{
if ((a[i / 8] << (i % 8)) & 0x80)
grub_crypto_xor (res, res, bs, 16);
grub_gcm_mul_x (bs);
}
grub_memcpy (a, res, 16);
}
static grub_err_t
grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher,
grub_uint8_t *out, const grub_uint8_t *in,
grub_size_t psize,
void *mac_out, const void *nonce,
unsigned nonce_len, unsigned m)
{
grub_uint8_t iv[16];
grub_uint8_t mul[16];
grub_uint8_t mac[16], h[16], mac_xor[16];
unsigned i, j;
grub_err_t err;
grub_memset (mac, 0, sizeof (mac));
err = grub_crypto_ecb_encrypt (cipher, h, mac, 16);
if (err)
return err;
if (nonce_len == 12)
{
grub_memcpy (iv, nonce, 12);
iv[12] = 0;
iv[13] = 0;
iv[14] = 0;
iv[15] = 1;
}
else
{
grub_memset (iv, 0, sizeof (iv));
grub_memcpy (iv, nonce, nonce_len);
grub_gcm_mul (iv, h);
iv[15] ^= nonce_len * 8;
grub_gcm_mul (iv, h);
}
err = grub_crypto_ecb_encrypt (cipher, mac_xor, iv, 16);
if (err)
return err;
for (i = 0; i < (psize + 15) / 16; i++)
{
grub_size_t csize;
csize = 16;
if (csize > psize - 16 * i)
csize = psize - 16 * i;
for (j = 0; j < 4; j++)
{
iv[15 - j]++;
if (iv[15 - j] != 0)
break;
}
grub_crypto_xor (mac, mac, in + 16 * i, csize);
grub_gcm_mul (mac, h);
err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16);
if (err)
return err;
grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize);
}
for (j = 0; j < 8; j++)
mac[15 - j] ^= ((psize * 8) >> (8 * j));
grub_gcm_mul (mac, h);
if (mac_out)
grub_crypto_xor (mac_out, mac, mac_xor, m);
return GRUB_ERR_NONE;
}
static grub_err_t
algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo,
grub_uint8_t *out, const grub_uint8_t *in,
grub_size_t psize,
void *mac_out, const void *nonce,
unsigned l, unsigned m)
{
switch (algo)
{
case 0:
return grub_ccm_decrypt (cipher, out, in, psize, mac_out, nonce, l, m);
case 1:
return grub_gcm_decrypt (cipher, out, in, psize, mac_out, nonce,
15 - l, m);
default:
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "algorithm %"
PRIuGRUB_UINT64_T " is not supported yet", algo);
}
}
static grub_err_t
grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher,
grub_uint64_t algo,
void *nonce,
char *buf, grub_size_t size,
const grub_uint32_t *expected_mac,
grub_zfs_endian_t endian)
{
grub_uint32_t mac[4];
unsigned i;
grub_uint32_t sw[4];
grub_err_t err;
grub_memcpy (sw, nonce, 16);
for (i = 0; i < 4; i++)
sw[i] = grub_cpu_to_be32 (grub_zfs_to_cpu32 (sw[i], endian));
if (!cipher)
return grub_error (GRUB_ERR_ACCESS_DENIED,
"no decryption key available");;
err = algo_decrypt (cipher, algo,
(grub_uint8_t *) buf,
(grub_uint8_t *) buf,
size, mac,
sw + 1, 3, 12);
if (err)
return err;
for (i = 0; i < 3; i++)
if (grub_zfs_to_cpu32 (expected_mac[i], endian)
!= grub_be_to_cpu32 (mac[i]))
return grub_error (GRUB_ERR_BAD_FS, "MAC verification failed");
return GRUB_ERR_NONE;
}
static grub_crypto_cipher_handle_t
grub_zfs_load_key_real (const struct grub_zfs_key *key,
grub_size_t keysize,
grub_uint64_t salt,
grub_uint64_t algo)
{
unsigned keylen;
struct grub_zfs_wrap_key *wrap_key;
grub_crypto_cipher_handle_t ret = NULL;
grub_err_t err;
if (keysize != sizeof (*key))
{
grub_dprintf ("zfs", "Unexpected key size %" PRIuGRUB_SIZE "\n", keysize);
return 0;
}
if (grub_memcmp (key->enc_key + 32, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)
== 0)
keylen = 16;
else if (grub_memcmp (key->enc_key + 40, "\0\0\0\0\0\0\0\0", 8) == 0)
keylen = 24;
else
keylen = 32;
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], wrap_key_real[32];
cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES);
if (!cipher)
{
grub_errno = GRUB_ERR_NONE;
return 0;
}
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)
{
grub_errno = GRUB_ERR_NONE;
continue;
}
err = algo_decrypt (cipher, algo, 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 = algo_decrypt (cipher, algo, 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;
}
ret = grub_crypto_cipher_open (GRUB_CIPHER_AES);
if (!ret)
{
grub_errno = GRUB_ERR_NONE;
continue;
}
err = grub_crypto_cipher_set_key (ret, decrypted, keylen);
if (err)
{
grub_errno = GRUB_ERR_NONE;
grub_crypto_cipher_close (ret);
continue;
}
return ret;
}
return NULL;
}
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;
}
else
{
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;
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';
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, real_size / 2, 0);
if (err)
return err;
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;
GRUB_MOD_INIT(zfscrypto)
{
grub_zfs_decrypt = grub_zfs_decrypt_real;
grub_zfs_load_key = grub_zfs_load_key_real;
cmd_key = grub_register_extcmd ("zfskey", grub_cmd_zfs_key, 0,
"zfskey [-h|-p|-r] [FILE]",
"Import ZFS wrapping key stored in FILE.",
options);
}
GRUB_MOD_FINI(zfscrypto)
{
grub_zfs_decrypt = 0;
grub_zfs_load_key = 0;
grub_unregister_extcmd (cmd_key);
}

View file

@ -169,14 +169,6 @@ grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher,
return cipher->cipher->setkey (cipher->ctx, key, keylen);
}
void
grub_crypto_cipher_close (grub_crypto_cipher_handle_t cipher)
{
grub_free (cipher);
}
void
grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size)
{
@ -210,9 +202,10 @@ grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher,
gcry_err_code_t
grub_crypto_ecb_encrypt (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->encrypt)
return GPG_ERR_NOT_SUPPORTED;
if (size % cipher->cipher->blocksize != 0)

View file

@ -26,6 +26,7 @@
#include <grub/symbol.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/mm.h>
typedef enum
{
@ -191,8 +192,11 @@ grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher,
const unsigned char *key,
unsigned keylen);
void
grub_crypto_cipher_close (grub_crypto_cipher_handle_t cipher);
static inline void
grub_crypto_cipher_close (grub_crypto_cipher_handle_t cipher)
{
grub_free (cipher);
}
void
grub_crypto_xor (void *out, const void *in1, const void *in2, grub_size_t size);
@ -203,7 +207,7 @@ grub_crypto_ecb_decrypt (grub_crypto_cipher_handle_t cipher,
gcry_err_code_t
grub_crypto_ecb_encrypt (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_cbc_encrypt (grub_crypto_cipher_handle_t cipher,
void *out, void *in, grub_size_t size,
@ -251,11 +255,13 @@ extern gcry_md_spec_t _gcry_digest_spec_sha1;
extern gcry_md_spec_t _gcry_digest_spec_sha256;
extern gcry_md_spec_t _gcry_digest_spec_sha512;
extern gcry_md_spec_t _gcry_digest_spec_crc32;
extern gcry_cipher_spec_t _gcry_cipher_spec_aes;
#define GRUB_MD_MD5 ((const gcry_md_spec_t *) &_gcry_digest_spec_md5)
#define GRUB_MD_SHA1 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha1)
#define GRUB_MD_SHA256 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha256)
#define GRUB_MD_SHA512 ((const gcry_md_spec_t *) &_gcry_digest_spec_sha512)
#define GRUB_MD_CRC32 ((const gcry_md_spec_t *) &_gcry_digest_spec_crc32)
#define GRUB_CIPHER_AES ((const gcry_cipher_spec_t *) &_gcry_cipher_spec_aes)
/* Implement PKCS#5 PBKDF2 as per RFC 2898. The PRF to use is HMAC variant
of digest supplied by MD. Inputs are the password P of length PLEN,

View file

@ -88,6 +88,7 @@ typedef enum dmu_object_type {
DMU_OT_SA_MASTER_NODE, /* ZAP */
DMU_OT_SA_ATTR_REGISTRATION, /* ZAP */
DMU_OT_SA_ATTR_LAYOUTS, /* ZAP */
DMU_OT_DSL_KEYCHAIN = 54,
DMU_OT_NUMTYPES
} dmu_object_type_t;

View file

@ -42,7 +42,9 @@ typedef struct dsl_dir_phys {
grub_uint64_t dd_reserved;
grub_uint64_t dd_props_zapobj;
grub_uint64_t dd_deleg_zapobj; /* dataset permissions */
grub_uint64_t dd_pad[20]; /* pad out to 256 bytes for good measure */
grub_uint64_t unused[7];
grub_uint64_t keychain;
grub_uint64_t unused2[12];
} dsl_dir_phys_t;
#endif /* _SYS_DSL_DIR_H */

View file

@ -20,26 +20,24 @@
#ifndef GRUB_ZFS_SPA_HEADER
#define GRUB_ZFS_SPA_HEADER 1
typedef enum grub_zfs_endian
{
UNKNOWN_ENDIAN = -2,
LITTLE_ENDIAN = -1,
BIG_ENDIAN = 0
} grub_zfs_endian_t;
#define grub_zfs_to_cpu16(x,a) (((a) == BIG_ENDIAN) ? grub_be_to_cpu16(x) \
#define grub_zfs_to_cpu16(x,a) (((a) == GRUB_ZFS_BIG_ENDIAN) ? \
grub_be_to_cpu16(x) \
: grub_le_to_cpu16(x))
#define grub_cpu_to_zfs16(x,a) (((a) == BIG_ENDIAN) ? grub_cpu_to_be16(x) \
#define grub_cpu_to_zfs16(x,a) (((a) == GRUB_ZFS_BIG_ENDIAN) ? \
grub_cpu_to_be16(x) \
: grub_cpu_to_le16(x))
#define grub_zfs_to_cpu32(x,a) (((a) == BIG_ENDIAN) ? grub_be_to_cpu32(x) \
#define grub_zfs_to_cpu32(x,a) (((a) == GRUB_ZFS_BIG_ENDIAN) ? \
grub_be_to_cpu32(x) \
: grub_le_to_cpu32(x))
#define grub_cpu_to_zfs32(x,a) (((a) == BIG_ENDIAN) ? grub_cpu_to_be32(x) \
#define grub_cpu_to_zfs32(x,a) (((a) == GRUB_ZFS_BIG_ENDIAN) ? \
grub_cpu_to_be32(x) \
: grub_cpu_to_le32(x))
#define grub_zfs_to_cpu64(x,a) (((a) == BIG_ENDIAN) ? grub_be_to_cpu64(x) \
#define grub_zfs_to_cpu64(x,a) (((a) == GRUB_ZFS_BIG_ENDIAN) \
? grub_be_to_cpu64(x) \
: grub_le_to_cpu64(x))
#define grub_cpu_to_zfs64(x,a) (((a) == BIG_ENDIAN) ? grub_cpu_to_be64(x) \
#define grub_cpu_to_zfs64(x,a) (((a) == GRUB_ZFS_BIG_ENDIAN) ? grub_cpu_to_be64(x) \
: grub_cpu_to_le64(x))
/*

View file

@ -24,6 +24,14 @@
#include <grub/err.h>
#include <grub/disk.h>
#include <grub/crypto.h>
typedef enum grub_zfs_endian
{
GRUB_ZFS_UNKNOWN_ENDIAN = -2,
GRUB_ZFS_LITTLE_ENDIAN = -1,
GRUB_ZFS_BIG_ENDIAN = 0
} grub_zfs_endian_t;
/*
* On-disk version number.
@ -121,5 +129,25 @@ 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,
grub_size_t keylen,
int passphrase);
extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
grub_uint64_t algo,
void *nonce,
char *buf, grub_size_t size,
const grub_uint32_t *expected_mac,
grub_zfs_endian_t endian);
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_uint64_t salt,
grub_uint64_t algo);
#endif /* ! GRUB_ZFS_HEADER */

View file

@ -65,6 +65,7 @@ enum zio_checksum {
ZIO_CHECKSUM_FLETCHER_4,
ZIO_CHECKSUM_SHA256,
ZIO_CHECKSUM_ZILOG2,
ZIO_CHECKSUM_SHA256_MAC,
ZIO_CHECKSUM_FUNCTIONS
};

View file

@ -33,6 +33,7 @@
#include <grub/crypto.h>
#include <grub/command.h>
#include <grub/i18n.h>
#include <grub/zfs/zfs.h>
#include <stdio.h>
#include <unistd.h>
@ -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", '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}
@ -462,6 +464,38 @@ argp_parser (int key, char *arg, struct argp_state *state)
root = arg;
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[1024];
f = fopen (arg, "rb");
if (!f)
{
printf ("Error loading file %s: %s\n", arg, strerror (errno));
return 0;
}
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;
}
grub_zfs_add_key (buf, real_size, 0);
}
return 0;
case 'C':
mount_crypt = 1;
return 0;