From 1bc7cc1b4d850e2ed4d0562798a41f411623c1e1 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 13:18:27 +0100 Subject: [PATCH 1/7] First part of zfs-crypto. CCM support with 0-filled keys --- grub-core/fs/zfs/zfs.c | 422 ++++++++++++++++++++++++++++++------- grub-core/lib/crypto.c | 5 +- include/grub/crypto.h | 4 +- include/grub/zfs/dmu.h | 1 + include/grub/zfs/dsl_dir.h | 4 +- include/grub/zfs/zio.h | 1 + 6 files changed, 360 insertions(+), 77 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 9658dea0d..9da4d70e2 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -52,6 +52,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -124,6 +125,23 @@ static grub_dl_t my_mod; #define NBBY 8 #endif +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]; +}; + 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, @@ -161,6 +179,14 @@ struct grub_zfs_device_desc int original; }; +struct subvolume +{ + dnode_end_t mdn; + grub_uint64_t obj; + grub_uint64_t case_insensitive; + grub_crypto_cipher_handle_t cipher; +}; + struct grub_zfs_data { /* cache for a file block of the currently zfs_open()-ed file */ @@ -176,8 +202,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; @@ -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]; @@ -312,7 +340,7 @@ zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, { grub_dprintf ("zfs", "unknown checksum function %d\n", checksum); return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unknown checksum function %d", checksum); + "unknown checksum function %d", checksum); } if (ci->ci_eck) @@ -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", @@ -1410,16 +1436,67 @@ zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, return err; } +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; +} + /* * Read in a block of data, verify its checksum, decompress if needed, * and put the uncompressed data in buf. */ static grub_err_t -zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf, +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 +1506,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 +1525,7 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf, if (comp != ZIO_COMPRESS_OFF) { - compbuf = grub_malloc (psize); + compbuf = grub_malloc (ALIGN_UP (psize, 16)); if (! compbuf) return grub_errno; } @@ -1462,8 +1540,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 +1552,45 @@ zio_read (blkptr_t * bp, grub_zfs_endian_t endian, void **buf, return err; } + if (encrypted) + { + grub_uint32_t mac[4]; + unsigned i; + grub_uint32_t sw[4]; + + grub_memcpy (sw, &(bp)->blk_dva[encrypted], 16); + for (i = 0; i < 4; i++) + sw[i] = grub_cpu_to_be32 (grub_zfs_to_cpu32 (sw[i], endian)); + + if (!data->subvol.cipher) + { + grub_free (compbuf); + *buf = NULL; + return grub_error (GRUB_ERR_ACCESS_DENIED, + "no decryption key available");; + } + err = grub_ccm_decrypt (data->subvol.cipher, + (grub_uint8_t *) compbuf, + (grub_uint8_t *) compbuf, + psize, mac, + sw + 1, 3, 12); + if (err) + { + grub_free (compbuf); + *buf = NULL; + return err; + } + + for (i = 0; i < 3; i++) + if (grub_zfs_to_cpu32 (((grub_uint32_t *) &zc + 5)[i], endian) + != grub_be_to_cpu32 (mac[i])) + { + grub_free (compbuf); + *buf = NULL; + return grub_error (GRUB_ERR_BAD_FS, "MAC verification failed"); + } + } + if (comp != ZIO_COMPRESS_OFF) { *buf = grub_malloc (lsize); @@ -1572,7 +1691,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,8 +1918,8 @@ 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_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,9 +1973,11 @@ 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), - struct grub_zfs_data *data) + int NESTED_FUNC_ATTR (*hook) (const char *name, + const void *val_in, + grub_size_t nelem, + grub_size_t elemsize), + struct grub_zfs_data *data) { zap_leaf_phys_t *l; void *l_in; @@ -1918,9 +2039,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 */ @@ -1940,24 +2061,28 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, } buf[le->le_name_length] = 0; - if (le->le_int_size != 8 - || grub_zfs_to_cpu16 (le->le_value_length, endian) != 1) - continue; + 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), + val_length, val)) + { + grub_free (buf); + grub_free (val); + continue; + } - /* get the uint64_t property value */ - la = &ZAP_LEAF_CHUNK (l, blksft, - grub_zfs_to_cpu16 (le->le_value_chunk, - endian)).l_array; - val = grub_be_to_cpu64 (la->la_array64); - if (hook (buf, val)) + if (hook (buf, 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. @@ -2008,9 +2133,68 @@ zap_lookup (dnode_end_t * zap_dnode, char *name, grub_uint64_t * val, return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); } +static int +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; + int size; + void *zapbuf; + grub_err_t err; + int ret; + grub_zfs_endian_t endian; + + auto int NESTED_FUNC_ATTR transform (const char *name, + const void *val_in, + grub_size_t nelem, + grub_size_t elemsize); + + int NESTED_FUNC_ATTR transform (const char *name, + 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); + 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_dprintf ("zfs", "micro zap\n"); + ret = mzap_iterate (zapbuf, endian, size, hook); + grub_free (zapbuf); + return ret; + } + else if (block_type == ZBT_HEADER) + { + grub_dprintf ("zfs", "fat zap\n"); + /* this is a fat zap */ + ret = fzap_iterate (zap_dnode, zapbuf, 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, - int NESTED_FUNC_ATTR (*hook) (const char *name, grub_uint64_t val), + int NESTED_FUNC_ATTR (*hook) (const char *name, + const void *val_in, + grub_size_t nelem, + grub_size_t elemsize), struct grub_zfs_data *data) { grub_uint64_t block_type; @@ -2031,12 +2215,10 @@ zap_iterate (dnode_end_t * zap_dnode, if (block_type == ZBT_MICRO) { - grub_dprintf ("zfs", "micro zap\n"); - ret = mzap_iterate (zapbuf, endian, size, hook); - grub_free (zapbuf); - return ret; + grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected"); + return 0; } - else if (block_type == ZBT_HEADER) + if (block_type == ZBT_HEADER) { grub_dprintf ("zfs", "fat zap\n"); /* this is a fat zap */ @@ -2124,10 +2306,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 +2326,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 +2348,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 +2364,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 +2418,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 +2433,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,15 +2727,93 @@ 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_err_t err; + 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) + { + 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; + + if (elemsize != 1 || nelem != sizeof (*key)) + { + grub_dprintf ("zfs", "Unexpected key length %" PRIuGRUB_SIZE + " x %" PRIuGRUB_SIZE "\n", nelem, elemsize); + 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; + + grub_memset (wrapping_key, 0, sizeof (wrapping_key)); + + 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; + 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; + } + ptr_at = grub_strchr (fullpath, '@'); if (! ptr_at) { @@ -2605,29 +2865,47 @@ 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 (keychainobj) + { + dnode_end_t keychain_dn; + err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN, + &keychain_dn, data); + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + zap_iterate (&keychain_dn, iterate_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 +2914,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 +2926,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; @@ -3088,8 +3365,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 +3529,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 +3626,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 +3638,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 +3670,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 +3725,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 +3751,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 +3770,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 +3779,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; diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index 8876cc326..f858be9c6 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -210,9 +210,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) diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 10368d99f..573893a3e 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -203,7 +203,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 +251,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, diff --git a/include/grub/zfs/dmu.h b/include/grub/zfs/dmu.h index bee317e8a..8fc6dc5b5 100644 --- a/include/grub/zfs/dmu.h +++ b/include/grub/zfs/dmu.h @@ -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; diff --git a/include/grub/zfs/dsl_dir.h b/include/grub/zfs/dsl_dir.h index 41d77c790..6542a77fe 100644 --- a/include/grub/zfs/dsl_dir.h +++ b/include/grub/zfs/dsl_dir.h @@ -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 */ diff --git a/include/grub/zfs/zio.h b/include/grub/zfs/zio.h index 29451593f..8b645c063 100644 --- a/include/grub/zfs/zio.h +++ b/include/grub/zfs/zio.h @@ -65,6 +65,7 @@ enum zio_checksum { ZIO_CHECKSUM_FLETCHER_4, ZIO_CHECKSUM_SHA256, ZIO_CHECKSUM_ZILOG2, + ZIO_CHECKSUM_SHA256_MAC, ZIO_CHECKSUM_FUNCTIONS }; From 2cdc899567c4db45ab0a3a5eae2aa91faa7cff92 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 14:44:29 +0100 Subject: [PATCH 2/7] 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; From f003a8c5e75df1c0b01511195e62f2afe317aa58 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 15:18:25 +0100 Subject: [PATCH 3/7] Move ZFS crypto to separate module --- Makefile.util.def | 1 + grub-core/Makefile.core.def | 5 + grub-core/fs/zfs/zfs.c | 255 ++++++---------------------- grub-core/fs/zfs/zfscrypt.c | 324 ++++++++++++++++++++++++++++++++++++ grub-core/fs/zfs/zfsinfo.c | 67 -------- grub-core/lib/crypto.c | 8 - include/grub/crypto.h | 8 +- include/grub/zfs/spa.h | 24 ++- include/grub/zfs/zfs.h | 21 +++ 9 files changed, 415 insertions(+), 298 deletions(-) create mode 100644 grub-core/fs/zfs/zfscrypt.c diff --git a/Makefile.util.def b/Makefile.util.def index 17894b1d2..15a309367 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -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; diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index b2e8e7aa2..9590188fb 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -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; diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 4008d17f4..3558bd8bb 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -125,29 +125,6 @@ static grub_dl_t my_mod; #define NBBY 8 #endif -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_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, @@ -222,20 +199,13 @@ 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; -} +grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher, + 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) = NULL; static grub_err_t zlib_decompress (void *s, void *d, @@ -409,14 +379,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)) @@ -448,20 +420,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)); @@ -1382,7 +1357,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); @@ -1457,57 +1432,6 @@ zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, return err; } -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; -} - /* * Read in a block of data, verify its checksum, decompress if needed, * and put the uncompressed data in buf. @@ -1575,41 +1499,18 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, if (encrypted) { - grub_uint32_t mac[4]; - unsigned i; - grub_uint32_t sw[4]; - - grub_memcpy (sw, &(bp)->blk_dva[encrypted], 16); - for (i = 0; i < 4; i++) - sw[i] = grub_cpu_to_be32 (grub_zfs_to_cpu32 (sw[i], endian)); - - if (!data->subvol.cipher) - { - grub_free (compbuf); - *buf = NULL; - return grub_error (GRUB_ERR_ACCESS_DENIED, - "no decryption key available");; - } - err = grub_ccm_decrypt (data->subvol.cipher, - (grub_uint8_t *) compbuf, - (grub_uint8_t *) compbuf, - psize, mac, - sw + 1, 3, 12); + if (!grub_zfs_decrypt) + err = grub_error (GRUB_ERR_BAD_FS, "zfscrypto module not loaded"); + else + err = grub_zfs_decrypt (data->subvol.cipher, &(bp)->blk_dva[encrypted], + compbuf, psize, ((grub_uint32_t *) &zc + 5), + endian); if (err) { grub_free (compbuf); *buf = NULL; return err; } - - for (i = 0; i < 3; i++) - if (grub_zfs_to_cpu32 (((grub_uint32_t *) &zc + 5)[i], endian) - != grub_be_to_cpu32 (mac[i])) - { - grub_free (compbuf); - *buf = NULL; - return grub_error (GRUB_ERR_BAD_FS, "MAC verification failed"); - } } if (comp != ZIO_COMPRESS_OFF) @@ -2767,76 +2668,14 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, grub_size_t nelem, grub_size_t elemsize) { - const struct grub_zfs_key *key = val_in; - unsigned keylen; - struct grub_zfs_wrap_key *wrap_key; - - if (elemsize != 1 || nelem != sizeof (*key)) + if (elemsize != 1) { - grub_dprintf ("zfs", "Unexpected key length %" PRIuGRUB_SIZE - " x %" PRIuGRUB_SIZE "\n", nelem, elemsize); + grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n", + elemsize); 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]; - 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; - } - - 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->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; - } + subvol->cipher = grub_zfs_load_key (val_in, nelem); return 0; } @@ -2904,7 +2743,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, 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 (keychainobj) + if (grub_zfs_load_key && keychainobj) { dnode_end_t keychain_dn; err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN, @@ -2918,7 +2757,6 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, zap_iterate (&keychain_dn, iterate_zap_key, data); } - if (snapname) { grub_uint64_t snapobj; @@ -3221,6 +3059,7 @@ 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); grub_free (data); } @@ -3236,7 +3075,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) @@ -3267,8 +3106,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); @@ -3357,7 +3196,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; @@ -3368,8 +3207,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); diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c new file mode 100644 index 000000000..251538041 --- /dev/null +++ b/grub-core/fs/zfs/zfscrypt.c @@ -0,0 +1,324 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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_uint64_t key[GRUB_ZFS_MAX_KEYLEN / 8]; +}; + +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 +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 grub_err_t +grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, 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 = grub_ccm_decrypt (cipher, + (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) +{ + 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]; + 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; + } + + 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->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; + } + 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_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); +} diff --git a/grub-core/fs/zfs/zfsinfo.c b/grub-core/fs/zfs/zfsinfo.c index dfc238d11..3ed2448b0 100644 --- a/grub-core/fs/zfs/zfsinfo.c +++ b/grub-core/fs/zfs/zfsinfo.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -391,78 +390,13 @@ 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"); @@ -472,5 +406,4 @@ GRUB_MOD_FINI (zfsinfo) { grub_unregister_command (cmd_info); grub_unregister_command (cmd_bootfs); - grub_unregister_extcmd (cmd_key); } diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index f858be9c6..4dec5c694 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -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) { diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 573893a3e..b8a5b3a22 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -26,6 +26,7 @@ #include #include #include +#include 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); diff --git a/include/grub/zfs/spa.h b/include/grub/zfs/spa.h index 22ee03b15..0e29fa44a 100644 --- a/include/grub/zfs/spa.h +++ b/include/grub/zfs/spa.h @@ -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)) /* diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h index 62b72776e..6c280bfe2 100644 --- a/include/grub/zfs/zfs.h +++ b/include/grub/zfs/zfs.h @@ -24,6 +24,14 @@ #include #include +#include + +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. @@ -124,4 +132,17 @@ int grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, grub_err_t grub_zfs_add_key (grub_uint8_t *key_in); #define GRUB_ZFS_MAX_KEYLEN 32 +extern grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher, + 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); + + + #endif /* ! GRUB_ZFS_HEADER */ From ed746949afec9a9ab8c231e3ed4e49cefbe7c4f6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 16:30:52 +0100 Subject: [PATCH 4/7] ZFS passphrase support --- grub-core/fs/zfs/zfs.c | 36 +++++++++++++++++++--- grub-core/fs/zfs/zfscrypt.c | 61 ++++++++++++++++++++++++------------- include/grub/zfs/zfs.h | 9 ++++-- util/grub-fstest.c | 20 ++++++++---- 4 files changed, 91 insertions(+), 35 deletions(-) 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; From d99b3726e591f125220fba02725bfe9fcbfb424b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 17:13:38 +0100 Subject: [PATCH 5/7] 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); } From bc1de0bc26ad95dd81b968fa75eaae92023bcb18 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 21:05:25 +0100 Subject: [PATCH 6/7] GCM support --- grub-core/fs/zfs/zfs.c | 10 ++- grub-core/fs/zfs/zfscrypt.c | 147 +++++++++++++++++++++++++++++++++--- include/grub/zfs/zfs.h | 4 +- 3 files changed, 147 insertions(+), 14 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index 6ab1f61a7..fcf32d9db 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -172,6 +172,7 @@ struct subvolume { grub_crypto_cipher_handle_t cipher; grub_uint64_t txg; + grub_uint64_t algo; } *keyring; }; @@ -205,13 +206,15 @@ struct grub_zfs_data }; 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) = NULL; + grub_uint64_t salt, + grub_uint64_t algo) = NULL; static grub_err_t zlib_decompress (void *s, void *d, @@ -1535,6 +1538,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, 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); @@ -2744,7 +2748,9 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, } subvol->keyring[keyn].txg = grub_be_to_cpu64 (*(grub_uint64_t *) name); - subvol->keyring[keyn].cipher = grub_zfs_load_key (val_in, nelem, salt); + 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; } diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c index d8c7181f0..39335a728 100644 --- a/grub-core/fs/zfs/zfscrypt.c +++ b/grub-core/fs/zfs/zfscrypt.c @@ -141,8 +141,132 @@ grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher, 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_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, void *nonce, +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) @@ -159,11 +283,11 @@ grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, void *nonce, if (!cipher) return grub_error (GRUB_ERR_ACCESS_DENIED, "no decryption key available");; - err = grub_ccm_decrypt (cipher, - (grub_uint8_t *) buf, - (grub_uint8_t *) buf, - size, mac, - sw + 1, 3, 12); + err = algo_decrypt (cipher, algo, + (grub_uint8_t *) buf, + (grub_uint8_t *) buf, + size, mac, + sw + 1, 3, 12); if (err) return err; @@ -177,7 +301,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_uint64_t salt) + grub_uint64_t salt, + grub_uint64_t algo) { unsigned keylen; struct grub_zfs_wrap_key *wrap_key; @@ -227,8 +352,8 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key, continue; } - err = grub_ccm_decrypt (cipher, decrypted, key->unknown_purpose_key, 32, - mac, key->unknown_purpose_nonce, 2, 16); + 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)) { @@ -237,8 +362,8 @@ grub_zfs_load_key_real (const struct grub_zfs_key *key, continue; } - err = grub_ccm_decrypt (cipher, decrypted, key->enc_key, keylen, mac, - key->enc_nonce, 2, 16); + 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"); diff --git a/include/grub/zfs/zfs.h b/include/grub/zfs/zfs.h index db8e915bf..e326c8b2f 100644 --- a/include/grub/zfs/zfs.h +++ b/include/grub/zfs/zfs.h @@ -135,6 +135,7 @@ grub_zfs_add_key (grub_uint8_t *key_in, 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, @@ -144,7 +145,8 @@ 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 salt, + grub_uint64_t algo); From ae9a20d973284a143e798e06d292c63a1b3a793e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sun, 6 Nov 2011 21:08:32 +0100 Subject: [PATCH 7/7] Small cleanup --- grub-core/fs/zfs/zfs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index fcf32d9db..4d9a474fb 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -1479,6 +1479,7 @@ zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, if (comp != ZIO_COMPRESS_OFF) { + /* It's not really necessary to align to 16, just for safety. */ compbuf = grub_malloc (ALIGN_UP (psize, 16)); if (! compbuf) return grub_errno; @@ -1878,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");