From d99b3726e591f125220fba02725bfe9fcbfb424b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 17:13:38 +0100 Subject: [PATCH] Support ZFS subvolumes with multiple keys --- grub-core/fs/zfs/zfs.c | 132 ++++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 27 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 0e9869d3f..6ab1f61a7 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -167,7 +167,12 @@ struct subvolume dnode_end_t mdn; grub_uint64_t obj; grub_uint64_t case_insensitive; - grub_crypto_cipher_handle_t cipher; + grub_size_t nkeys; + struct + { + grub_crypto_cipher_handle_t cipher; + grub_uint64_t txg; + } *keyring; }; struct grub_zfs_data @@ -1503,9 +1508,37 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, if (!grub_zfs_decrypt) 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), - endian); + { + 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, + &(bp)->blk_dva[encrypted], + compbuf, psize, ((grub_uint32_t *) &zc + 5), + endian); + } if (err) { grub_free (compbuf); @@ -1896,7 +1929,9 @@ 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_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), @@ -1971,18 +2006,19 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, if (le->le_type != ZAP_CHUNK_ENTRY) continue; - buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian) - + 1); + buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian) + * 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; val_length = ((int) le->le_value_length * (int) le->le_int_size); @@ -1997,7 +2033,8 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, continue; } - if (hook (buf, val, le->le_value_length, le->le_int_size)) + if (hook (buf, le->le_name_length, + val, le->le_value_length, le->le_int_size)) return 1; grub_free (buf); grub_free (val); @@ -2069,12 +2106,14 @@ zap_iterate_u64 (dnode_end_t * zap_dnode, int ret; grub_zfs_endian_t endian; - auto int NESTED_FUNC_ATTR transform (const char *name, + 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 char *name, + 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) @@ -2104,7 +2143,7 @@ zap_iterate_u64 (dnode_end_t * zap_dnode, { grub_dprintf ("zfs", "fat zap\n"); /* this is a fat zap */ - ret = fzap_iterate (zap_dnode, zapbuf, transform, data); + ret = fzap_iterate (zap_dnode, zapbuf, 1, transform, data); grub_free (zapbuf); return ret; } @@ -2114,7 +2153,9 @@ zap_iterate_u64 (dnode_end_t * zap_dnode, static int zap_iterate (dnode_end_t * zap_dnode, - int NESTED_FUNC_ATTR (*hook) (const char *name, + 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), @@ -2145,7 +2186,7 @@ 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, nameelemlen, hook, data); grub_free (zapbuf); return ret; } @@ -2660,17 +2701,41 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, grub_uint64_t keychainobj; grub_uint64_t salt; grub_err_t err; + int keyn = 0; - - auto int NESTED_FUNC_ATTR iterate_zap_key (const char *name, - const void *val_in, - grub_size_t nelem, - grub_size_t elemsize); - int NESTED_FUNC_ATTR iterate_zap_key (const char *name __attribute__ ((unused)), - const void *val_in, - grub_size_t nelem, - grub_size_t elemsize) + 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", @@ -2678,7 +2743,9 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, return 0; } - subvol->cipher = grub_zfs_load_key (val_in, nelem, salt); + subvol->keyring[keyn].txg = grub_be_to_cpu64 (*(grub_uint64_t *) name); + subvol->keyring[keyn].cipher = grub_zfs_load_key (val_in, nelem, salt); + keyn++; return 0; } @@ -2782,7 +2849,16 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, grub_free (snapname); return err; } - zap_iterate (&keychain_dn, iterate_zap_key, data); + 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) @@ -3087,7 +3163,9 @@ zfs_unmount (struct grub_zfs_data *data) grub_free (data->dnode_buf); grub_free (data->dnode_mdn); grub_free (data->file_buf); - grub_crypto_cipher_close (data->subvol.cipher); + 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); }