aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--module/zfs/dmu_send.c17
-rw-r--r--module/zfs/dsl_crypt.c73
2 files changed, 72 insertions, 18 deletions
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c
index 4318a7815..cc6b97d53 100644
--- a/module/zfs/dmu_send.c
+++ b/module/zfs/dmu_send.c
@@ -2064,7 +2064,8 @@ dmu_recv_resume_begin_sync(void *arg, dmu_tx_t *tx)
dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT;
rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
- ASSERT(!BP_IS_HOLE(dsl_dataset_get_blkptr(ds)));
+ ASSERT(!BP_IS_HOLE(dsl_dataset_get_blkptr(ds)) ||
+ drba->drba_cookie->drc_raw);
rrw_exit(&ds->ds_bp_rwlock, FTAG);
drba->drba_cookie->drc_ds = ds;
@@ -2958,6 +2959,7 @@ receive_object_range(struct receive_writer_arg *rwa,
static void
dmu_recv_cleanup_ds(dmu_recv_cookie_t *drc)
{
+ dsl_dataset_t *ds = drc->drc_ds;
ds_hold_flags_t dsflags = (drc->drc_raw) ? 0 : DS_HOLD_FLAG_DECRYPT;
/*
@@ -2967,14 +2969,17 @@ dmu_recv_cleanup_ds(dmu_recv_cookie_t *drc)
* that the user accounting code will not attempt to do anything
* after we stopped receiving the dataset.
*/
- txg_wait_synced(drc->drc_ds->ds_dir->dd_pool, 0);
+ txg_wait_synced(ds->ds_dir->dd_pool, 0);
- if (drc->drc_resumable) {
- dsl_dataset_disown(drc->drc_ds, dsflags, dmu_recv_tag);
+ rrw_enter(&ds->ds_bp_rwlock, RW_READER, FTAG);
+ if (drc->drc_resumable && !BP_IS_HOLE(dsl_dataset_get_blkptr(ds))) {
+ rrw_exit(&ds->ds_bp_rwlock, FTAG);
+ dsl_dataset_disown(ds, dsflags, dmu_recv_tag);
} else {
char name[ZFS_MAX_DATASET_NAME_LEN];
- dsl_dataset_name(drc->drc_ds, name);
- dsl_dataset_disown(drc->drc_ds, dsflags, dmu_recv_tag);
+ rrw_exit(&ds->ds_bp_rwlock, FTAG);
+ dsl_dataset_name(ds, name);
+ dsl_dataset_disown(ds, dsflags, dmu_recv_tag);
(void) dsl_destroy_head(name);
}
}
diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c
index 3dcdde4d0..a71142682 100644
--- a/module/zfs/dsl_crypt.c
+++ b/module/zfs/dsl_crypt.c
@@ -723,6 +723,7 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp,
dsl_crypto_key_t *dck = NULL;
dsl_wrapping_key_t *wkey = dcp->cp_wkey;
dsl_pool_t *dp = NULL;
+ uint64_t keyformat, salt, iters;
/*
* We don't validate the wrapping key's keyformat, salt, or iters
@@ -757,8 +758,36 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp,
if (ret != 0)
goto error;
+ /* initialize the wkey encryption parameters from the DSL Crypto Key */
+ ret = zap_lookup(dp->dp_meta_objset, dd->dd_crypto_obj,
+ zfs_prop_to_name(ZFS_PROP_KEYFORMAT), 8, 1, &keyformat);
+ if (ret != 0)
+ goto error;
+
+ ret = zap_lookup(dp->dp_meta_objset, dd->dd_crypto_obj,
+ zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), 8, 1, &salt);
+ if (ret != 0)
+ goto error;
+
+ ret = zap_lookup(dp->dp_meta_objset, dd->dd_crypto_obj,
+ zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), 8, 1, &iters);
+ if (ret != 0)
+ goto error;
+
+ ASSERT3U(keyformat, <, ZFS_KEYFORMAT_FORMATS);
+ ASSERT3U(keyformat, !=, ZFS_KEYFORMAT_NONE);
+ IMPLY(keyformat == ZFS_KEYFORMAT_PASSPHRASE, iters != 0);
+ IMPLY(keyformat == ZFS_KEYFORMAT_PASSPHRASE, salt != 0);
+ IMPLY(keyformat != ZFS_KEYFORMAT_PASSPHRASE, iters == 0);
+ IMPLY(keyformat != ZFS_KEYFORMAT_PASSPHRASE, salt == 0);
+
+ wkey->wk_keyformat = keyformat;
+ wkey->wk_salt = salt;
+ wkey->wk_iters = iters;
+
/*
- * At this point we have verified the key. We can simply cleanup and
+ * At this point we have verified the wkey and confirmed that it can
+ * be used to decrypt a DSL Crypto Key. We can simply cleanup and
* return if this is all the user wanted to do.
*/
if (noop)
@@ -2176,6 +2205,7 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
uint64_t rddobj;
nvlist_t *nvl = NULL;
uint64_t dckobj = ds->ds_dir->dd_crypto_obj;
+ dsl_dir_t *rdd = NULL;
dsl_pool_t *dp = ds->ds_dir->dd_pool;
objset_t *mos = dp->dp_meta_objset;
uint64_t crypt = 0, guid = 0, format = 0, iters = 0, salt = 0;
@@ -2194,10 +2224,6 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
goto error;
/* lookup values from the DSL Crypto Key */
- ret = dsl_dir_get_encryption_root_ddobj(ds->ds_dir, &rddobj);
- if (ret != 0)
- goto error;
-
ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_CRYPTO_SUITE, 8, 1,
&crypt);
if (ret != 0)
@@ -2227,24 +2253,43 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
if (ret != 0)
goto error;
- /* lookup wrapping key properties */
- ret = zap_lookup(dp->dp_meta_objset, dckobj,
- zfs_prop_to_name(ZFS_PROP_KEYFORMAT), 8, 1, &format);
+ /*
+ * Lookup wrapping key properties. An early version of the code did
+ * not correctly add these values to the wrapping key or the DSL
+ * Crypto Key on disk for non encryption roots, so to be safe we
+ * always take the slightly circuitous route of looking it up from
+ * the encryption root's key.
+ */
+ ret = dsl_dir_get_encryption_root_ddobj(ds->ds_dir, &rddobj);
if (ret != 0)
goto error;
+ dsl_pool_config_enter(dp, FTAG);
+
+ ret = dsl_dir_hold_obj(dp, rddobj, NULL, FTAG, &rdd);
+ if (ret != 0)
+ goto error_unlock;
+
+ ret = zap_lookup(dp->dp_meta_objset, rdd->dd_crypto_obj,
+ zfs_prop_to_name(ZFS_PROP_KEYFORMAT), 8, 1, &format);
+ if (ret != 0)
+ goto error_unlock;
+
if (format == ZFS_KEYFORMAT_PASSPHRASE) {
- ret = zap_lookup(dp->dp_meta_objset, dckobj,
+ ret = zap_lookup(dp->dp_meta_objset, rdd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), 8, 1, &iters);
if (ret != 0)
- goto error;
+ goto error_unlock;
- ret = zap_lookup(dp->dp_meta_objset, dckobj,
+ ret = zap_lookup(dp->dp_meta_objset, rdd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), 8, 1, &salt);
if (ret != 0)
- goto error;
+ goto error_unlock;
}
+ dsl_dir_rele(rdd, FTAG);
+ dsl_pool_config_exit(dp, FTAG);
+
fnvlist_add_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE, crypt);
fnvlist_add_uint64(nvl, DSL_CRYPTO_KEY_GUID, guid);
VERIFY0(nvlist_add_uint8_array(nvl, DSL_CRYPTO_KEY_MASTER_KEY,
@@ -2270,7 +2315,11 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
*nvl_out = nvl;
return (0);
+error_unlock:
+ dsl_pool_config_exit(dp, FTAG);
error:
+ if (rdd != NULL)
+ dsl_dir_rele(rdd, FTAG);
nvlist_free(nvl);
*nvl_out = NULL;