zcrypt_key_t * zcrypt_keystore_find_wrappingkey(spa_t *spa, uint64_t dsobj) { zcrypt_keystore_node_t *zk; zk = zcrypt_keystore_find_node(spa, dsobj, B_FALSE); if (zk == NULL) return (NULL); return (zk->skn_wrapkey); }
static void dsl_keychain_clone_phys(dsl_dataset_t *src, dsl_dir_t *dd, dmu_tx_t *tx, zcrypt_key_t *dwkey) { objset_t *mos = dd->dd_pool->dp_meta_objset; uint64_t keychain = dsl_dir_phys(dd)->dd_keychain_obj; caddr_t wrappedkey = NULL; size_t wkeylen = 0; zcrypt_keystore_node_t *kn; zcrypt_keychain_node_t *n; uint64_t newest_txg = dsl_dataset_phys(src)->ds_creation_txg; kn = zcrypt_keystore_find_node(dsl_dataset_get_spa(src), src->ds_object, B_FALSE); if (kn == NULL) { kn = zcrypt_keystore_find_node(dsl_dataset_get_spa(src), dsl_dir_phys(src->ds_dir)->dd_head_dataset_obj, B_FALSE); } ASSERT(kn != NULL); ASSERT(dwkey != NULL); /* * Walk the in memory AVL tree representation of the keychain * creating new keychain entries using our wrappingkey, stopping * when we reach keychain entries created after the snapshot we * are cloning from. */ mutex_enter(&kn->skn_lock); for (n = avl_first(&kn->skn_keychain); n != NULL && n->dkn_txg <= newest_txg; n = AVL_NEXT(&kn->skn_keychain, n)) { VERIFY(zcrypt_wrap_key(dwkey, n->dkn_key, &wrappedkey, &wkeylen, zio_crypt_select_wrap(dwkey->zk_crypt)) == 0); VERIFY(zap_update_uint64(mos, keychain, &n->dkn_txg, 1, 1, wkeylen, wrappedkey, tx) == 0); kmem_free(wrappedkey, wkeylen); } mutex_exit(&kn->skn_lock); }
/* * zcrypt_key_lookup * * This function looks up the key we need based on the bookmark. * It returns a reference to the key that the caller should NOT free. * The caller should use zcrypt_key_hold/release() * On failure it returns NULL; */ zcrypt_key_t * zcrypt_key_lookup(spa_t *spa, uint64_t objset, uint64_t txg) { zcrypt_keystore_node_t *skn; zcrypt_keychain_node_t *dkn; crypto_mechanism_t mech = { 0 }; zcrypt_key_t *key; #if _KERNEL printk("zcrypt_key_lookup enter\n"); #endif skn = zcrypt_keystore_find_node(spa, objset, B_FALSE); if (skn == NULL) return (NULL); /* ZIL writes use txg 0 but we want the latest key */ if (txg == 0) txg = -1UL; mutex_enter(&skn->skn_lock); dkn = zcrypt_keychain_find(skn->skn_keychain, txg); if (dkn == NULL) { mutex_exit(&skn->skn_lock); return (NULL); } key = dkn->dkn_key; if (key != NULL && !key->zk_ctx_tmpl_valid) { mech.cm_type = crypto_mech2id( zio_crypt_table[key->zk_crypt].ci_mechname); if (crypto_create_ctx_template(&mech, &key->zk_key, &key->zk_ctx_tmpl, KM_SLEEP) == CRYPTO_SUCCESS) key->zk_ctx_tmpl_valid = B_TRUE; } if (key != NULL && !key->zk_mac_ctx_tmpl_valid) { mech.cm_type = crypto_mech2id(SUN_CKM_SHA256_HMAC_GENERAL); if (crypto_create_ctx_template(&mech, &key->zk_mackey, &key->zk_mac_ctx_tmpl, KM_SLEEP) == CRYPTO_SUCCESS) key->zk_mac_ctx_tmpl_valid = B_TRUE; } mutex_exit(&skn->skn_lock); #if _KERNEL printk("zcrypt_key_lookup exit: key %p\n", key); #endif return (key); }
zfs_crypt_key_status_t dsl_dataset_keystatus(dsl_dataset_t *ds, boolean_t dp_config_rwlock_held) { /* * Sneaky way of determining if this is an encrypted dataset * by looking for a keychain obj so we can avoid calling * dsl_prop_get_ds and all the locking issues that can entail * given when we can be called. */ if (ds == NULL) return (ZFS_CRYPT_KEY_UNAVAILABLE); if (ds->ds_dir != NULL && dsl_dir_phys(ds->ds_dir) != NULL && dsl_dir_phys(ds->ds_dir)->dd_keychain_obj == 0) { return (ZFS_CRYPT_KEY_NONE); } if (zcrypt_keystore_find_node(dsl_dataset_get_spa(ds), ds->ds_object, dp_config_rwlock_held)) { return (ZFS_CRYPT_KEY_AVAILABLE); } return (ZFS_CRYPT_KEY_UNAVAILABLE); }
/* * dsl_crypto_key_change * * The old key must already be present in memory since the user interface * doesn't provide away to prompt or retrieve the old key. */ static void dsl_crypto_key_change_sync(void *arg1, dmu_tx_t *tx) { struct wkey_change_arg *ca = arg1; dsl_dataset_t *ds = ca->ca_ds; size_t wkeylen; char *wkeybuf = NULL; zcrypt_key_t *txgkey; zap_cursor_t zc; zap_attribute_t za; objset_t *mos; uint64_t keychain_zapobj; spa_t *spa; zcrypt_keystore_node_t *zkn; ASSERT(RRW_WRITE_HELD(&ds->ds_dir->dd_pool->dp_config_rwlock)); mos = ds->ds_dir->dd_pool->dp_meta_objset; keychain_zapobj = dsl_dir_phys(ds->ds_dir)->dd_keychain_obj; /* * To allow for the case were the keychains of child datasets * are not loaded (ie an explicit 'zfs key -u tank/fs/sub' had * been done some time before doing 'zfs key -c tank/fs') we itterate * over the zap objects on disk rather than copying from the * in memory keystore node. */ for (zap_cursor_init(&zc, mos, keychain_zapobj); zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { wkeylen = za.za_num_integers; wkeybuf = kmem_alloc(wkeylen, KM_SLEEP); VERIFY(zap_lookup_uint64(mos, keychain_zapobj, (uint64_t *)za.za_name, 1, 1, wkeylen, wkeybuf) == 0); VERIFY(zcrypt_unwrap_key(ca->ca_old_key, ds->ds_objset->os_crypt, wkeybuf, wkeylen, &txgkey) == 0); kmem_free(wkeybuf, wkeylen); VERIFY(zcrypt_wrap_key(ca->ca_new_key, txgkey, &wkeybuf, &wkeylen, zio_crypt_select_wrap(ds->ds_objset->os_crypt)) == 0); zcrypt_key_free(txgkey); VERIFY(zap_update_uint64(mos, keychain_zapobj, (uint64_t *)za.za_name, 1, 1, wkeylen, wkeybuf, tx) == 0); kmem_free(wkeybuf, wkeylen); } zap_cursor_fini(&zc); spa = dsl_dataset_get_spa(ds); /* * If the wrapping key is loaded switch the in memory copy now. */ zkn = zcrypt_keystore_find_node(spa, ds->ds_object, B_FALSE); if (zkn != NULL) { zcrypt_key_free(zkn->skn_wrapkey); zkn->skn_wrapkey = zcrypt_key_copy(ca->ca_new_key); } spa_history_log_internal(spa, "key change", tx, "succeeded dataset = %llu", ds->ds_object); }
int dsl_crypto_key_new(const char *dsname) { dsl_dataset_t *ds; objset_t *os; zcrypt_keystore_node_t *skn; spa_t *spa; struct knarg arg; int error; dsl_pool_t *dp; void *cookie; error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); if ((error = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (error); } if (dsl_dataset_is_snapshot(ds)) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOTSUP); } if ((error = dmu_objset_from_ds(ds, &os)) != 0) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); } if (os->os_crypt == ZIO_CRYPT_OFF) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOTSUP); } ASSERT(os->os_crypt != ZIO_CRYPT_INHERIT); /* * Need the keychain and wrapping key to already be available. */ spa = dsl_dataset_get_spa(ds); skn = zcrypt_keystore_find_node(spa, ds->ds_object, B_FALSE); if (skn == NULL) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOENT); } ASSERT(ds != NULL); ASSERT(ds->ds_objset != NULL); //zil_suspend_dmu_sync(dmu_objset_zil(os)); zil_suspend(dsname, &cookie); arg.kn_skn = skn; arg.kn_txgkey = zcrypt_key_gen(os->os_crypt); arg.kn_ds = ds; zcrypt_key_hold(skn->skn_wrapkey, FTAG); VERIFY(zcrypt_wrap_key(skn->skn_wrapkey, arg.kn_txgkey, &arg.kn_wkeybuf, &arg.kn_wkeylen, zio_crypt_select_wrap(os->os_crypt)) == 0); error = dsl_sync_task(spa->spa_name, dsl_crypto_key_new_check, dsl_crypto_key_new_sync, &arg, 1, ZFS_SPACE_CHECK_NONE); kmem_free(arg.kn_wkeybuf, arg.kn_wkeylen); zcrypt_key_release(skn->skn_wrapkey, FTAG); //zil_resume_dmu_sync(dmu_objset_zil(os)); zil_resume(os); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); if (error) zcrypt_key_free(arg.kn_txgkey); return (error); }
int dsl_crypto_key_load(const char *dsname, zcrypt_key_t *wrappingkey) { dsl_dataset_t *ds; uint64_t crypt; int error; dsl_pool_t *dp; error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); if ((error = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (error); } /* * This is key load not key change so if ds->ds_key is already * set we fail. */ if (zcrypt_keystore_find_node(dsl_dataset_get_spa(ds), ds->ds_object, B_FALSE) != NULL) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (EEXIST); } /* * Find out what size of key we expect. * * For now the wrapping key size (and type) matches the size * of the dataset key, this may not always be the case * (particularly if we ever support wrapping dataset keys * with asymmetric keys (eg RSA)). * * When alternate wrapping keys are added it maybe done using * a index property. */ rrw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER, FTAG); error = dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &crypt, NULL/*, DSL_PROP_GET_EFFECTIVE*/); rrw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock, FTAG); if (error != 0) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); } if (crypt == ZIO_CRYPT_OFF) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOTSUP); } ASSERT(crypt != ZIO_CRYPT_INHERIT); error = dsl_keychain_load(ds, crypt, wrappingkey); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); }