summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorTom Caputi <[email protected]>2017-11-08 14:12:59 -0500
committerBrian Behlendorf <[email protected]>2018-02-02 11:37:16 -0800
commitae76f45cda0e0857f99e53959cf71c7a5d66bd8b (patch)
treee1c3cabe0971272785a442d6fb627bf6ebd07148 /include
parent4c46b99d24a6e71b3c72462c11cb051d0930ad60 (diff)
Encryption Stability and On-Disk Format Fixes
The on-disk format for encrypted datasets protects not only the encrypted and authenticated blocks themselves, but also the order and interpretation of these blocks. In order to make this work while maintaining the ability to do raw sends, the indirect bps maintain a secure checksum of all the MACs in the block below it along with a few other fields that determine how the data is interpreted. Unfortunately, the current on-disk format erroneously includes some fields which are not portable and thus cannot support raw sends. It is not possible to easily work around this issue due to a separate and much smaller bug which causes indirect blocks for encrypted dnodes to not be compressed, which conflicts with the previous bug. In addition, the current code generates incompatible on-disk formats on big endian and little endian systems due to an issue with how block pointers are authenticated. Finally, raw send streams do not currently include dn_maxblkid when sending both the metadnode and normal dnodes which are needed in order to ensure that we are correctly maintaining the portable objset MAC. This patch zero's out the offending fields when computing the bp MAC and ensures that these MACs are always calculated in little endian order (regardless of the host system's byte order). This patch also registers an errata for the old on-disk format, which we detect by adding a "version" field to newly created DSL Crypto Keys. We allow datasets without a version (version 0) to only be mounted for read so that they can easily be migrated. We also now include dn_maxblkid in raw send streams to ensure the MAC can be maintained correctly. This patch also contains minor bug fixes and cleanups. Reviewed-by: Jorgen Lundman <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed by: Matthew Ahrens <[email protected]> Signed-off-by: Tom Caputi <[email protected]> Closes #6845 Closes #6864 Closes #7052
Diffstat (limited to 'include')
-rw-r--r--include/sys/dmu.h7
-rw-r--r--include/sys/dmu_objset.h1
-rw-r--r--include/sys/dnode.h8
-rw-r--r--include/sys/dsl_crypt.h3
-rw-r--r--include/sys/fs/zfs.h1
-rw-r--r--include/sys/zfs_ioctl.h4
-rw-r--r--include/sys/zio_crypt.h11
7 files changed, 30 insertions, 5 deletions
diff --git a/include/sys/dmu.h b/include/sys/dmu.h
index ffc070726..5553667c3 100644
--- a/include/sys/dmu.h
+++ b/include/sys/dmu.h
@@ -438,6 +438,13 @@ int dmu_object_set_blocksize(objset_t *os, uint64_t object, uint64_t size,
int ibs, dmu_tx_t *tx);
/*
+ * Manually set the maxblkid on a dnode. This will adjust nlevels accordingly
+ * to accommodate the change.
+ */
+int dmu_object_set_maxblkid(objset_t *os, uint64_t object, uint64_t maxblkid,
+ dmu_tx_t *tx);
+
+/*
* Set the checksum property on a dnode. The new checksum algorithm will
* apply to all newly written blocks; existing blocks will not be affected.
*/
diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h
index 11b8fc625..f3013ad13 100644
--- a/include/sys/dmu_objset.h
+++ b/include/sys/dmu_objset.h
@@ -217,6 +217,7 @@ boolean_t dmu_objset_userobjused_enabled(objset_t *os);
boolean_t dmu_objset_userobjspace_upgradable(objset_t *os);
void dmu_objset_userobjspace_upgrade(objset_t *os);
boolean_t dmu_objset_userobjspace_present(objset_t *os);
+boolean_t dmu_objset_incompatible_encryption_version(objset_t *os);
int dmu_fsname(const char *snapname, char *buf);
diff --git a/include/sys/dnode.h b/include/sys/dnode.h
index e5e39b18c..a2bef9d2c 100644
--- a/include/sys/dnode.h
+++ b/include/sys/dnode.h
@@ -221,6 +221,13 @@ typedef struct dnode_phys {
uint64_t dn_maxblkid; /* largest allocated block ID */
uint64_t dn_used; /* bytes (or sectors) of disk space */
+ /*
+ * Both dn_pad2 and dn_pad3 are protected by the block's MAC. This
+ * allows us to protect any fields that might be added here in the
+ * future. In either case, developers will want to check
+ * zio_crypt_init_uios_dnode() to ensure the new field is being
+ * protected properly.
+ */
uint64_t dn_pad3[4];
/*
@@ -301,6 +308,7 @@ struct dnode {
uint8_t dn_rm_spillblk[TXG_SIZE]; /* for removing spill blk */
uint16_t dn_next_bonuslen[TXG_SIZE];
uint32_t dn_next_blksz[TXG_SIZE]; /* next block size in bytes */
+ uint64_t dn_next_maxblkid[TXG_SIZE]; /* next maxblkid in bytes */
/* protected by dn_dbufs_mtx; declared here to fill 32-bit hole */
uint32_t dn_dbufs_count; /* count of dn_dbufs */
diff --git a/include/sys/dsl_crypt.h b/include/sys/dsl_crypt.h
index 6fb91f67d..d0c789035 100644
--- a/include/sys/dsl_crypt.h
+++ b/include/sys/dsl_crypt.h
@@ -39,7 +39,7 @@
#define DSL_CRYPTO_KEY_HMAC_KEY "DSL_CRYPTO_HMAC_KEY_1"
#define DSL_CRYPTO_KEY_ROOT_DDOBJ "DSL_CRYPTO_ROOT_DDOBJ"
#define DSL_CRYPTO_KEY_REFCOUNT "DSL_CRYPTO_REFCOUNT"
-
+#define DSL_CRYPTO_KEY_VERSION "DSL_CRYPTO_VERSION"
/*
* In-memory representation of a wrapping key. One of these structs will exist
@@ -169,6 +169,7 @@ int dsl_crypto_params_create_nvlist(dcp_cmd_t cmd, nvlist_t *props,
void dsl_crypto_params_free(dsl_crypto_params_t *dcp, boolean_t unload);
void dsl_dataset_crypt_stats(struct dsl_dataset *ds, nvlist_t *nv);
int dsl_crypto_can_set_keylocation(const char *dsname, const char *keylocation);
+boolean_t dsl_dir_incompatible_encryption_version(dsl_dir_t *dd);
void spa_keystore_init(spa_keystore_t *sk);
void spa_keystore_fini(spa_keystore_t *sk);
diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
index 6b1c3bb56..611279d6b 100644
--- a/include/sys/fs/zfs.h
+++ b/include/sys/fs/zfs.h
@@ -891,6 +891,7 @@ typedef enum zpool_errata {
ZPOOL_ERRATA_NONE,
ZPOOL_ERRATA_ZOL_2094_SCRUB,
ZPOOL_ERRATA_ZOL_2094_ASYNC_DESTROY,
+ ZPOOL_ERRATA_ZOL_6845_ENCRYPTION,
} zpool_errata_t;
/*
diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h
index 6924280c4..827f619d9 100644
--- a/include/sys/zfs_ioctl.h
+++ b/include/sys/zfs_ioctl.h
@@ -219,10 +219,12 @@ typedef struct dmu_replay_record {
uint8_t drr_flags;
uint32_t drr_raw_bonuslen;
uint64_t drr_toguid;
- /* only nonzero for raw streams */
+ /* only (possibly) nonzero for raw streams */
uint8_t drr_indblkshift;
uint8_t drr_nlevels;
uint8_t drr_nblkptr;
+ uint8_t drr_pad[5];
+ uint64_t drr_maxblkid;
/* bonus content follows */
} drr_object;
struct drr_freeobjects {
diff --git a/include/sys/zio_crypt.h b/include/sys/zio_crypt.h
index 9cf9a17c2..57b4c1e7c 100644
--- a/include/sys/zio_crypt.h
+++ b/include/sys/zio_crypt.h
@@ -36,6 +36,8 @@ struct zbookmark_phys;
#define MASTER_KEY_MAX_LEN 32
#define SHA512_HMAC_KEYLEN 64
+#define ZIO_CRYPT_KEY_CURRENT_VERSION 1ULL
+
typedef enum zio_crypt_type {
ZC_TYPE_NONE = 0,
ZC_TYPE_CCM,
@@ -64,6 +66,9 @@ typedef struct zio_crypt_key {
/* encryption algorithm */
uint64_t zk_crypt;
+ /* on-disk format version */
+ uint64_t zk_version;
+
/* GUID for uniquely identifying this key. Not encrypted on disk. */
uint64_t zk_guid;
@@ -104,9 +109,9 @@ int zio_crypt_key_get_salt(zio_crypt_key_t *key, uint8_t *salt_out);
int zio_crypt_key_wrap(crypto_key_t *cwkey, zio_crypt_key_t *key, uint8_t *iv,
uint8_t *mac, uint8_t *keydata_out, uint8_t *hmac_keydata_out);
-int zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint64_t guid,
- uint8_t *keydata, uint8_t *hmac_keydata, uint8_t *iv, uint8_t *mac,
- zio_crypt_key_t *key);
+int zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint64_t version,
+ uint64_t guid, uint8_t *keydata, uint8_t *hmac_keydata, uint8_t *iv,
+ uint8_t *mac, zio_crypt_key_t *key);
int zio_crypt_generate_iv(uint8_t *ivbuf);
int zio_crypt_generate_iv_salt_dedup(zio_crypt_key_t *key, uint8_t *data,
uint_t datalen, uint8_t *ivbuf, uint8_t *salt);