summaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
authorTom Caputi <[email protected]>2018-02-21 15:31:03 -0500
committerBrian Behlendorf <[email protected]>2018-02-21 12:31:03 -0800
commitb0918402dc9a0f81dd52880fbd4e4f4f2133764b (patch)
tree08bd4a79281aae8155e36047c3421420d77bdbbd /module/zfs
parent4a385862b7a9c62f5ec46462e92db48c3c5ec7d9 (diff)
Raw receive should change key atomically
Currently, raw zfs sends transfer the encrypted master keys and objset_phys_t encryption parameters in the DRR_BEGIN payload of each send file. Both of these are processed as soon as they are read in dmu_recv_stream(), meaning that the new keys are set before the new snapshot is received. In addition to the fact that this changes the user's keys for the dataset earlier than they might expect, the keys were never reset to what they originally were in the event that the receive failed. This patch splits the processing into objset handling and key handling, the later of which is moved to dmu_recv_end() so that they key change can be done atomically. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Tom Caputi <[email protected]> Closes #7200
Diffstat (limited to 'module/zfs')
-rw-r--r--module/zfs/dmu_send.c34
-rw-r--r--module/zfs/dsl_crypt.c464
2 files changed, 280 insertions, 218 deletions
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c
index bbb319822..16dc13939 100644
--- a/module/zfs/dmu_send.c
+++ b/module/zfs/dmu_send.c
@@ -3787,11 +3787,20 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp,
if (err != 0)
goto out;
- err = dsl_crypto_recv_key(spa_name(ra->os->os_spa),
+ /*
+ * If this is a new dataset we set the key immediately.
+ * Otherwise we don't want to change the key until we
+ * are sure the rest of the receive succeeded so we stash
+ * the keynvl away until then.
+ */
+ err = dsl_crypto_recv_raw(spa_name(ra->os->os_spa),
drc->drc_ds->ds_object, drc->drc_drrb->drr_type,
- keynvl);
+ keynvl, drc->drc_newfs);
if (err != 0)
goto out;
+
+ if (!drc->drc_newfs)
+ drc->drc_keynvl = fnvlist_dup(keynvl);
}
if (featureflags & DMU_BACKUP_FEATURE_RESUMING) {
@@ -3908,6 +3917,7 @@ out:
* the inconsistent state.
*/
dmu_recv_cleanup_ds(drc);
+ nvlist_free(drc->drc_keynvl);
}
*voffp = ra->voff;
@@ -3965,6 +3975,15 @@ dmu_recv_end_check(void *arg, dmu_tx_t *tx)
return (error);
}
}
+ if (drc->drc_keynvl != NULL) {
+ error = dsl_crypto_recv_raw_key_check(drc->drc_ds,
+ drc->drc_keynvl, tx);
+ if (error != 0) {
+ dsl_dataset_rele(origin_head, FTAG);
+ return (error);
+ }
+ }
+
error = dsl_dataset_clone_swap_check_impl(drc->drc_ds,
origin_head, drc->drc_force, drc->drc_owner, tx);
if (error != 0) {
@@ -4021,8 +4040,14 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx)
dsl_dataset_rele(snap, FTAG);
}
}
- VERIFY3P(drc->drc_ds->ds_prev, ==,
- origin_head->ds_prev);
+ if (drc->drc_keynvl != NULL) {
+ dsl_crypto_recv_raw_key_sync(drc->drc_ds,
+ drc->drc_keynvl, tx);
+ nvlist_free(drc->drc_keynvl);
+ drc->drc_keynvl = NULL;
+ }
+
+ VERIFY3P(drc->drc_ds->ds_prev, ==, origin_head->ds_prev);
dsl_dataset_clone_swap_sync_impl(drc->drc_ds,
origin_head, tx);
@@ -4174,6 +4199,7 @@ dmu_recv_end(dmu_recv_cookie_t *drc, void *owner)
if (error != 0) {
dmu_recv_cleanup_ds(drc);
+ nvlist_free(drc->drc_keynvl);
} else if (drc->drc_guid_to_ds_map != NULL) {
(void) add_ds_to_guidmap(drc->drc_tofs, drc->drc_guid_to_ds_map,
drc->drc_newsnapobj, drc->drc_raw);
diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c
index 98a6f8cc8..9009b526b 100644
--- a/module/zfs/dsl_crypt.c
+++ b/module/zfs/dsl_crypt.c
@@ -1838,7 +1838,7 @@ dsl_dataset_create_crypt_sync(uint64_t dsobj, dsl_dir_t *dd,
/*
* A NULL dcp at this point indicates this is the origin dataset
* which does not have an objset to encrypt. Raw receives will handle
- * encryption seperately later. In both cases we can simply return.
+ * encryption separately later. In both cases we can simply return.
*/
if (dcp == NULL || dcp->cp_cmd == DCP_CMD_RAW_RECV)
return;
@@ -1889,187 +1889,63 @@ dsl_dataset_create_crypt_sync(uint64_t dsobj, dsl_dir_t *dd,
typedef struct dsl_crypto_recv_key_arg {
uint64_t dcrka_dsobj;
- nvlist_t *dcrka_nvl;
dmu_objset_type_t dcrka_ostype;
+ nvlist_t *dcrka_nvl;
+ boolean_t dcrka_do_key;
} dsl_crypto_recv_key_arg_t;
-int
-dsl_crypto_recv_key_check(void *arg, dmu_tx_t *tx)
+static int
+dsl_crypto_recv_raw_objset_check(dsl_dataset_t *ds, dmu_objset_type_t ostype,
+ nvlist_t *nvl, dmu_tx_t *tx)
{
int ret;
- objset_t *mos = tx->tx_pool->dp_meta_objset;
objset_t *os;
dnode_t *mdn;
- dsl_crypto_recv_key_arg_t *dcrka = arg;
- nvlist_t *nvl = dcrka->dcrka_nvl;
- dsl_dataset_t *ds = NULL;
uint8_t *buf = NULL;
uint_t len;
- uint64_t intval, guid, nlevels, blksz, ibs, nblkptr, maxblkid, version;
- boolean_t is_passphrase = B_FALSE;
-
- ret = dsl_dataset_hold_obj(tx->tx_pool, dcrka->dcrka_dsobj, FTAG, &ds);
- if (ret != 0)
- goto error;
-
- ASSERT(dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT);
-
- /*
- * Read and check all the encryption values from the nvlist. We need
- * all of the fields of a DSL Crypto Key, as well as a fully specified
- * wrapping key.
- */
- ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE, &intval);
- if (ret != 0 || intval >= ZIO_CRYPT_FUNCTIONS ||
- intval <= ZIO_CRYPT_OFF) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID, &intval);
- if (ret != 0) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- /*
- * If this is an incremental receive make sure the given key guid
- * matches the one we already have.
- */
- if (ds->ds_dir->dd_crypto_obj != 0) {
- ret = zap_lookup(mos, ds->ds_dir->dd_crypto_obj,
- DSL_CRYPTO_KEY_GUID, 8, 1, &guid);
- if (ret != 0)
- goto error;
-
- if (intval != guid) {
- ret = SET_ERROR(EACCES);
- goto error;
- }
- }
-
- ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MASTER_KEY,
- &buf, &len);
- if (ret != 0 || len != MASTER_KEY_MAX_LEN) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_HMAC_KEY,
- &buf, &len);
- if (ret != 0 || len != SHA512_HMAC_KEYLEN) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_IV, &buf, &len);
- if (ret != 0 || len != WRAPPING_IV_LEN) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
+ uint64_t intval, nlevels, blksz, ibs, nblkptr, maxblkid;
- ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MAC, &buf, &len);
- if (ret != 0 || len != WRAPPING_MAC_LEN) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- /*
- * We don't support receiving old on-disk formats. The version 0
- * implementation protected several fields in an objset that were
- * not always portable during a raw receive. As a result, we call
- * the old version an on-disk errata #3.
- */
- ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_VERSION, &version);
- if (ret != 0 || version != ZIO_CRYPT_KEY_CURRENT_VERSION) {
- ret = SET_ERROR(ENOTSUP);
- goto error;
- }
-
- ret = nvlist_lookup_uint8_array(nvl, "portable_mac", &buf, &len);
- if (ret != 0 || len != ZIO_OBJSET_MAC_LEN) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- ret = nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_KEYFORMAT),
- &intval);
- if (ret != 0 || intval >= ZFS_KEYFORMAT_FORMATS ||
- intval == ZFS_KEYFORMAT_NONE) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- is_passphrase = (intval == ZFS_KEYFORMAT_PASSPHRASE);
-
- /*
- * for raw receives we allow any number of pbkdf2iters since there
- * won't be a chance for the user to change it.
- */
- ret = nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS),
- &intval);
- if (ret != 0 || (is_passphrase == (intval == 0))) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
- ret = nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT),
- &intval);
- if (ret != 0 || (is_passphrase == (intval == 0))) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
+ if (ostype != DMU_OST_ZFS && ostype != DMU_OST_ZVOL)
+ return (SET_ERROR(EINVAL));
/* raw receives also need info about the structure of the metadnode */
- ret = nvlist_lookup_uint64(nvl, "mdn_checksum", &intval);
- if (ret != 0 || intval >= ZIO_CHECKSUM_LEGACY_FUNCTIONS) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
-
ret = nvlist_lookup_uint64(nvl, "mdn_compress", &intval);
- if (ret != 0 || intval >= ZIO_COMPRESS_LEGACY_FUNCTIONS) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
+ if (ret != 0 || intval >= ZIO_COMPRESS_LEGACY_FUNCTIONS)
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint64(nvl, "mdn_checksum", &intval);
+ if (ret != 0 || intval >= ZIO_CHECKSUM_LEGACY_FUNCTIONS)
+ return (SET_ERROR(EINVAL));
ret = nvlist_lookup_uint64(nvl, "mdn_nlevels", &nlevels);
- if (ret != 0 || nlevels > DN_MAX_LEVELS) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
+ if (ret != 0 || nlevels > DN_MAX_LEVELS)
+ return (SET_ERROR(EINVAL));
ret = nvlist_lookup_uint64(nvl, "mdn_blksz", &blksz);
- if (ret != 0 || blksz < SPA_MINBLOCKSIZE) {
- ret = SET_ERROR(EINVAL);
- goto error;
- } else if (blksz > spa_maxblocksize(tx->tx_pool->dp_spa)) {
- ret = SET_ERROR(ENOTSUP);
- goto error;
- }
+ if (ret != 0 || blksz < SPA_MINBLOCKSIZE)
+ return (SET_ERROR(EINVAL));
+ else if (blksz > spa_maxblocksize(tx->tx_pool->dp_spa))
+ return (SET_ERROR(ENOTSUP));
ret = nvlist_lookup_uint64(nvl, "mdn_indblkshift", &ibs);
- if (ret != 0 || ibs < DN_MIN_INDBLKSHIFT ||
- ibs > DN_MAX_INDBLKSHIFT) {
- ret = SET_ERROR(ENOTSUP);
- goto error;
- }
+ if (ret != 0 || ibs < DN_MIN_INDBLKSHIFT || ibs > DN_MAX_INDBLKSHIFT)
+ return (SET_ERROR(ENOTSUP));
ret = nvlist_lookup_uint64(nvl, "mdn_nblkptr", &nblkptr);
- if (ret != 0 || nblkptr != DN_MAX_NBLKPTR) {
- ret = SET_ERROR(ENOTSUP);
- goto error;
- }
+ if (ret != 0 || nblkptr != DN_MAX_NBLKPTR)
+ return (SET_ERROR(ENOTSUP));
ret = nvlist_lookup_uint64(nvl, "mdn_maxblkid", &maxblkid);
- if (ret != 0) {
- ret = SET_ERROR(EINVAL);
- goto error;
- }
+ if (ret != 0)
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint8_array(nvl, "portable_mac", &buf, &len);
+ if (ret != 0 || len != ZIO_OBJSET_MAC_LEN)
+ return (SET_ERROR(EINVAL));
ret = dmu_objset_from_ds(ds, &os);
if (ret != 0)
- goto error;
+ return (ret);
/*
* Useraccounting is not portable and must be done with the keys loaded.
@@ -2082,80 +1958,54 @@ dsl_crypto_recv_key_check(void *arg, dmu_tx_t *tx)
mdn = DMU_META_DNODE(os);
/*
- * If we already created the objset, make sure its unchangable
+ * If we already created the objset, make sure its unchangeable
* properties match the ones received in the nvlist.
*/
rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
if (!BP_IS_HOLE(dsl_dataset_get_blkptr(ds)) &&
(mdn->dn_nlevels != nlevels || mdn->dn_datablksz != blksz ||
mdn->dn_indblkshift != ibs || mdn->dn_nblkptr != nblkptr)) {
- ret = SET_ERROR(EINVAL);
- goto error;
+ rrw_exit(&ds->ds_bp_rwlock, FTAG);
+ return (SET_ERROR(EINVAL));
}
rrw_exit(&ds->ds_bp_rwlock, FTAG);
- dsl_dataset_rele(ds, FTAG);
return (0);
-
-error:
- if (ds != NULL)
- dsl_dataset_rele(ds, FTAG);
- return (ret);
}
static void
-dsl_crypto_recv_key_sync(void *arg, dmu_tx_t *tx)
+dsl_crypto_recv_raw_objset_sync(dsl_dataset_t *ds, dmu_objset_type_t ostype,
+ nvlist_t *nvl, dmu_tx_t *tx)
{
- dsl_crypto_recv_key_arg_t *dcrka = arg;
- uint64_t dsobj = dcrka->dcrka_dsobj;
- nvlist_t *nvl = dcrka->dcrka_nvl;
dsl_pool_t *dp = tx->tx_pool;
- objset_t *mos = dp->dp_meta_objset;
- dsl_dataset_t *ds;
objset_t *os;
dnode_t *mdn;
- uint8_t *keydata, *hmac_keydata, *iv, *mac, *portable_mac;
+ zio_t *zio;
+ uint8_t *portable_mac;
uint_t len;
- uint64_t rddobj, one = 1;
- uint64_t version = ZIO_CRYPT_KEY_CURRENT_VERSION;
- uint64_t crypt, guid, keyformat, iters, salt;
uint64_t compress, checksum, nlevels, blksz, ibs, maxblkid;
- char *keylocation = "prompt";
+ boolean_t newds = B_FALSE;
- VERIFY0(dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds));
VERIFY0(dmu_objset_from_ds(ds, &os));
mdn = DMU_META_DNODE(os);
- /* lookup the values we need to create the DSL Crypto Key and objset */
- crypt = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE);
- guid = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID);
- keyformat = fnvlist_lookup_uint64(nvl,
- zfs_prop_to_name(ZFS_PROP_KEYFORMAT));
- iters = fnvlist_lookup_uint64(nvl,
- zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS));
- salt = fnvlist_lookup_uint64(nvl,
- zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT));
- VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MASTER_KEY,
- &keydata, &len));
- VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_HMAC_KEY,
- &hmac_keydata, &len));
- VERIFY0(nvlist_lookup_uint8_array(nvl, "portable_mac", &portable_mac,
- &len));
- VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_IV, &iv, &len));
- VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MAC, &mac, &len));
+ /* fetch the values we need from the nvlist */
compress = fnvlist_lookup_uint64(nvl, "mdn_compress");
checksum = fnvlist_lookup_uint64(nvl, "mdn_checksum");
nlevels = fnvlist_lookup_uint64(nvl, "mdn_nlevels");
blksz = fnvlist_lookup_uint64(nvl, "mdn_blksz");
ibs = fnvlist_lookup_uint64(nvl, "mdn_indblkshift");
maxblkid = fnvlist_lookup_uint64(nvl, "mdn_maxblkid");
+ VERIFY0(nvlist_lookup_uint8_array(nvl, "portable_mac", &portable_mac,
+ &len));
/* if we haven't created an objset for the ds yet, do that now */
rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
if (BP_IS_HOLE(dsl_dataset_get_blkptr(ds))) {
(void) dmu_objset_create_impl_dnstats(dp->dp_spa, ds,
- dsl_dataset_get_blkptr(ds), dcrka->dcrka_ostype, nlevels,
- blksz, ibs, tx);
+ dsl_dataset_get_blkptr(ds), ostype, nlevels, blksz,
+ ibs, tx);
+ newds = B_TRUE;
}
rrw_exit(&ds->ds_bp_rwlock, FTAG);
@@ -2178,35 +2028,172 @@ dsl_crypto_recv_key_sync(void *arg, dmu_tx_t *tx)
dnode_new_blkid(mdn, maxblkid, tx, B_FALSE);
rw_exit(&mdn->dn_struct_rwlock);
- dsl_dataset_dirty(ds, tx);
+ /*
+ * We can't normally dirty the dataset in syncing context unless
+ * we are creating a new dataset. In this case, we perform a
+ * pseudo txg sync here instead.
+ */
+ if (newds) {
+ dsl_dataset_dirty(ds, tx);
+ } else {
+ zio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
+ dsl_dataset_sync(ds, zio, tx);
+ VERIFY0(zio_wait(zio));
+
+ /* dsl_dataset_sync_done will drop this reference. */
+ dmu_buf_add_ref(ds->ds_dbuf, ds);
+ dsl_dataset_sync_done(ds, tx);
+ }
+}
+
+int
+dsl_crypto_recv_raw_key_check(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
+{
+ int ret;
+ objset_t *mos = tx->tx_pool->dp_meta_objset;
+ uint8_t *buf = NULL;
+ uint_t len;
+ uint64_t intval, guid, version;
+ boolean_t is_passphrase = B_FALSE;
+
+ ASSERT(dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT);
+
+ /*
+ * Read and check all the encryption values from the nvlist. We need
+ * all of the fields of a DSL Crypto Key, as well as a fully specified
+ * wrapping key.
+ */
+ ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE, &intval);
+ if (ret != 0 || intval >= ZIO_CRYPT_FUNCTIONS ||
+ intval <= ZIO_CRYPT_OFF)
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID, &intval);
+ if (ret != 0)
+ return (SET_ERROR(EINVAL));
+
+ /*
+ * If this is an incremental receive make sure the given key guid
+ * matches the one we already have.
+ */
+ if (ds->ds_dir->dd_crypto_obj != 0) {
+ ret = zap_lookup(mos, ds->ds_dir->dd_crypto_obj,
+ DSL_CRYPTO_KEY_GUID, 8, 1, &guid);
+ if (ret != 0)
+ return (ret);
+ if (intval != guid)
+ return (SET_ERROR(EACCES));
+ }
+
+ ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MASTER_KEY,
+ &buf, &len);
+ if (ret != 0 || len != MASTER_KEY_MAX_LEN)
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_HMAC_KEY,
+ &buf, &len);
+ if (ret != 0 || len != SHA512_HMAC_KEYLEN)
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_IV, &buf, &len);
+ if (ret != 0 || len != WRAPPING_IV_LEN)
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MAC, &buf, &len);
+ if (ret != 0 || len != WRAPPING_MAC_LEN)
+ return (SET_ERROR(EINVAL));
+
+ /*
+ * We don't support receiving old on-disk formats. The version 0
+ * implementation protected several fields in an objset that were
+ * not always portable during a raw receive. As a result, we call
+ * the old version an on-disk errata #3.
+ */
+ ret = nvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_VERSION, &version);
+ if (ret != 0 || version != ZIO_CRYPT_KEY_CURRENT_VERSION)
+ return (SET_ERROR(ENOTSUP));
+
+ ret = nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_KEYFORMAT),
+ &intval);
+ if (ret != 0 || intval >= ZFS_KEYFORMAT_FORMATS ||
+ intval == ZFS_KEYFORMAT_NONE)
+ return (SET_ERROR(EINVAL));
+
+ is_passphrase = (intval == ZFS_KEYFORMAT_PASSPHRASE);
+
+ /*
+ * for raw receives we allow any number of pbkdf2iters since there
+ * won't be a chance for the user to change it.
+ */
+ ret = nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS),
+ &intval);
+ if (ret != 0 || (is_passphrase == (intval == 0)))
+ return (SET_ERROR(EINVAL));
+
+ ret = nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT),
+ &intval);
+ if (ret != 0 || (is_passphrase == (intval == 0)))
+ return (SET_ERROR(EINVAL));
+
+ return (0);
+}
+
+void
+dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
+{
+ dsl_pool_t *dp = tx->tx_pool;
+ objset_t *mos = dp->dp_meta_objset;
+ dsl_dir_t *dd = ds->ds_dir;
+ uint_t len;
+ uint64_t rddobj, one = 1;
+ uint8_t *keydata, *hmac_keydata, *iv, *mac;
+ uint64_t crypt, guid, keyformat, iters, salt;
+ uint64_t version = ZIO_CRYPT_KEY_CURRENT_VERSION;
+ char *keylocation = "prompt";
+
+ /* lookup the values we need to create the DSL Crypto Key */
+ crypt = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE);
+ guid = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID);
+ keyformat = fnvlist_lookup_uint64(nvl,
+ zfs_prop_to_name(ZFS_PROP_KEYFORMAT));
+ iters = fnvlist_lookup_uint64(nvl,
+ zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS));
+ salt = fnvlist_lookup_uint64(nvl,
+ zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT));
+ VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MASTER_KEY,
+ &keydata, &len));
+ VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_HMAC_KEY,
+ &hmac_keydata, &len));
+ VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_IV, &iv, &len));
+ VERIFY0(nvlist_lookup_uint8_array(nvl, DSL_CRYPTO_KEY_MAC, &mac, &len));
/* if this is a new dataset setup the DSL Crypto Key. */
- if (ds->ds_dir->dd_crypto_obj == 0) {
+ if (dd->dd_crypto_obj == 0) {
/* zapify the dsl dir so we can add the key object to it */
- dmu_buf_will_dirty(ds->ds_dir->dd_dbuf, tx);
- dsl_dir_zapify(ds->ds_dir, tx);
+ dmu_buf_will_dirty(dd->dd_dbuf, tx);
+ dsl_dir_zapify(dd, tx);
/* create the DSL Crypto Key on disk and activate the feature */
- ds->ds_dir->dd_crypto_obj = zap_create(mos,
+ dd->dd_crypto_obj = zap_create(mos,
DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
VERIFY0(zap_update(tx->tx_pool->dp_meta_objset,
- ds->ds_dir->dd_crypto_obj, DSL_CRYPTO_KEY_REFCOUNT,
+ dd->dd_crypto_obj, DSL_CRYPTO_KEY_REFCOUNT,
sizeof (uint64_t), 1, &one, tx));
VERIFY0(zap_update(tx->tx_pool->dp_meta_objset,
- ds->ds_dir->dd_crypto_obj, DSL_CRYPTO_KEY_VERSION,
+ dd->dd_crypto_obj, DSL_CRYPTO_KEY_VERSION,
sizeof (uint64_t), 1, &version, tx));
- dsl_dataset_activate_feature(dsobj, SPA_FEATURE_ENCRYPTION, tx);
+ dsl_dataset_activate_feature(ds->ds_object,
+ SPA_FEATURE_ENCRYPTION, tx);
ds->ds_feature_inuse[SPA_FEATURE_ENCRYPTION] = B_TRUE;
/* save the dd_crypto_obj on disk */
- VERIFY0(zap_add(mos, ds->ds_dir->dd_object,
- DD_FIELD_CRYPTO_KEY_OBJ, sizeof (uint64_t), 1,
- &ds->ds_dir->dd_crypto_obj, tx));
+ VERIFY0(zap_add(mos, dd->dd_object, DD_FIELD_CRYPTO_KEY_OBJ,
+ sizeof (uint64_t), 1, &dd->dd_crypto_obj, tx));
/*
* Set the keylocation to prompt by default. If keylocation
- * has been provided via the properties, this will be overriden
+ * has been provided via the properties, this will be overridden
* later.
*/
dsl_prop_set_sync_impl(ds,
@@ -2214,16 +2201,64 @@ dsl_crypto_recv_key_sync(void *arg, dmu_tx_t *tx)
ZPROP_SRC_LOCAL, 1, strlen(keylocation) + 1,
keylocation, tx);
- rddobj = ds->ds_dir->dd_object;
+ rddobj = dd->dd_object;
} else {
- VERIFY0(dsl_dir_get_encryption_root_ddobj(ds->ds_dir, &rddobj));
+ VERIFY0(dsl_dir_get_encryption_root_ddobj(dd, &rddobj));
}
/* sync the key data to the ZAP object on disk */
- dsl_crypto_key_sync_impl(mos, ds->ds_dir->dd_crypto_obj, crypt,
+ dsl_crypto_key_sync_impl(mos, dd->dd_crypto_obj, crypt,
rddobj, guid, iv, mac, keydata, hmac_keydata, keyformat, salt,
iters, tx);
+}
+int
+dsl_crypto_recv_key_check(void *arg, dmu_tx_t *tx)
+{
+ int ret;
+ dsl_crypto_recv_key_arg_t *dcrka = arg;
+ dsl_dataset_t *ds = NULL;
+
+ ret = dsl_dataset_hold_obj(tx->tx_pool, dcrka->dcrka_dsobj,
+ FTAG, &ds);
+ if (ret != 0)
+ goto error;
+
+ ret = dsl_crypto_recv_raw_objset_check(ds,
+ dcrka->dcrka_ostype, dcrka->dcrka_nvl, tx);
+ if (ret != 0)
+ goto error;
+
+ /*
+ * We run this check even if we won't be doing this part of
+ * the receive now so that we don't make the user wait until
+ * the receive finishes to fail.
+ */
+ ret = dsl_crypto_recv_raw_key_check(ds, dcrka->dcrka_nvl, tx);
+ if (ret != 0)
+ goto error;
+
+ dsl_dataset_rele(ds, FTAG);
+ return (0);
+
+error:
+ if (ds != NULL)
+ dsl_dataset_rele(ds, FTAG);
+ return (ret);
+}
+
+void
+dsl_crypto_recv_key_sync(void *arg, dmu_tx_t *tx)
+{
+ dsl_crypto_recv_key_arg_t *dcrka = arg;
+ dsl_dataset_t *ds;
+
+ VERIFY0(dsl_dataset_hold_obj(tx->tx_pool, dcrka->dcrka_dsobj,
+ FTAG, &ds));
+ dsl_crypto_recv_raw_objset_sync(ds, dcrka->dcrka_ostype,
+ dcrka->dcrka_nvl, tx);
+ if (dcrka->dcrka_do_key)
+ dsl_crypto_recv_raw_key_sync(ds, dcrka->dcrka_nvl, tx);
dsl_dataset_rele(ds, FTAG);
}
@@ -2233,14 +2268,15 @@ dsl_crypto_recv_key_sync(void *arg, dmu_tx_t *tx)
* without wrapping it.
*/
int
-dsl_crypto_recv_key(const char *poolname, uint64_t dsobj,
- dmu_objset_type_t ostype, nvlist_t *nvl)
+dsl_crypto_recv_raw(const char *poolname, uint64_t dsobj,
+ dmu_objset_type_t ostype, nvlist_t *nvl, boolean_t do_key)
{
dsl_crypto_recv_key_arg_t dcrka;
dcrka.dcrka_dsobj = dsobj;
- dcrka.dcrka_nvl = nvl;
dcrka.dcrka_ostype = ostype;
+ dcrka.dcrka_nvl = nvl;
+ dcrka.dcrka_do_key = do_key;
return (dsl_sync_task(poolname, dsl_crypto_recv_key_check,
dsl_crypto_recv_key_sync, &dcrka, 1, ZFS_SPACE_CHECK_NORMAL));