void zcrypt_keystore_fini(spa_t *spa) { void *cookie; avl_tree_t *tree; zcrypt_keystore_node_t *node; if (spa->spa_keystore == NULL) return; rw_enter(&spa->spa_keystore->sk_lock, RW_WRITER); /* * Note we don't bother with the refcnt of the keys in here * because this function can't return failure so we just need to * destroy everything. */ cookie = NULL; tree = &spa->spa_keystore->sk_dslkeys; while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) { mutex_enter(&node->skn_lock); (void) zcrypt_keychain_fini(node->skn_keychain); zcrypt_key_free(node->skn_wrapkey); mutex_exit(&node->skn_lock); bzero(node, sizeof (zcrypt_keystore_node_t)); kmem_free(node, sizeof (zcrypt_keystore_node_t)); } avl_destroy(tree); rw_exit(&spa->spa_keystore->sk_lock); rw_destroy(&spa->spa_keystore->sk_lock); kmem_free(spa->spa_keystore, sizeof (zcrypt_keystore_t)); spa->spa_keystore = NULL; }
static int zcrypt_keychain_fini(avl_tree_t keychain) { void *cookie = NULL; zcrypt_keychain_node_t *node = NULL; #if 0 while (AVL_NEXT(&keychain, node) != NULL) { if (!refcount_is_zero(&node->dkn_key->zk_refcnt)) return (EBUSY); } #endif while ((node = avl_destroy_nodes(&keychain, &cookie)) != NULL) { zcrypt_key_free(node->dkn_key); kmem_free(node, sizeof (zcrypt_keychain_node_t)); } avl_destroy(&keychain); return (0); }
int zcrypt_keystore_remove(spa_t *spa, uint64_t dsobj) { zcrypt_keystore_node_t zk_tofind; zcrypt_keystore_node_t *zk; avl_tree_t *tree; int err = 0; ASSERT(spa->spa_keystore != NULL); zk_tofind.skn_os = dsobj; tree = &spa->spa_keystore->sk_dslkeys; rw_enter(&spa->spa_keystore->sk_lock, RW_WRITER); zk = avl_find(tree, &zk_tofind, NULL); if (zk == NULL) { goto out; } mutex_enter(&zk->skn_lock); err = zcrypt_keychain_fini(zk->skn_keychain); if (err != 0) { mutex_exit(&zk->skn_lock); goto out; } zcrypt_key_free(zk->skn_wrapkey); zk->skn_wrapkey = NULL; mutex_exit(&zk->skn_lock); mutex_destroy(&zk->skn_lock); avl_remove(tree, zk); kmem_free(zk, sizeof (zcrypt_keystore_node_t)); out: rw_exit(&spa->spa_keystore->sk_lock); return (err); }
int dsl_crypto_key_change(char *dsname, zcrypt_key_t *newkey, nvlist_t *props) { struct wkey_change_arg *ca; struct kcnode *kcn; dsl_dataset_t *ds; dsl_props_arg_t pa; spa_t *spa; int err; //dsl_sync_task_group_t *dstg; zcrypt_key_t *oldkey; dsl_pool_t *dp; ASSERT(newkey != NULL); ASSERT(dsname != NULL); err = dsl_pool_hold(dsname, FTAG, &dp); if (err != 0) return (err); if ((err = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (err); } /* * Take the spa lock here so that new datasets can't get * created below us while we are doing a wrapping key change. * This is to avoid them being created with the wrong inherited * wrapping key. */ err = spa_open(dsname, &spa, FTAG); if (err) return (err); oldkey = zcrypt_key_copy(zcrypt_keystore_find_wrappingkey(spa, ds->ds_object)); if (oldkey == NULL) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); spa_close(spa, FTAG); return (ENOENT); } ca = kmem_alloc(sizeof (struct wkey_change_arg), KM_SLEEP); ca->ca_new_key = newkey; ca->ca_old_key = oldkey; ca->ca_parent = dsname; ca->ca_props = props; list_create(&ca->ca_nodes, sizeof (struct kcnode), offsetof(struct kcnode, kc_node)); zcrypt_key_hold(ca->ca_old_key, FTAG); zcrypt_key_hold(ca->ca_new_key, FTAG); //ca->ca_ds = dsl_sync_task_group_create(spa_get_dsl(spa)); err = dmu_objset_find(dsname, dsl_crypto_key_change_find, ca, DS_FIND_CHILDREN); /* * If this is the "top" dataset in this keychange it gets * the keysource and salt properties updated. */ pa.pa_props = props; pa.pa_source = ZPROP_SRC_LOCAL; //pa.pa_flags = 0; //pa.pa_zone = curzone; //dsl_sync_task_create(ca->ca_dstg, NULL, dsl_props_set_sync, ds, &pa, 2); dsl_props_set(dsname, ZPROP_SRC_LOCAL, props); //if (err == 0) //err = dsl_sync_task_group_wait(dstg); while ((kcn = list_head(&ca->ca_nodes))) { list_remove(&ca->ca_nodes, kcn); dsl_dataset_rele(kcn->kc_ds, kcn); kmem_free(kcn, sizeof (struct kcnode)); } //dsl_sync_task_group_destroy(ca->ca_dstg); /* * We are finished so release and free both the old and new keys. * We can free even the new key because everyone got a copy of it * not a reference to this one. */ zcrypt_key_release(ca->ca_old_key, FTAG); zcrypt_key_free(ca->ca_old_key); zcrypt_key_release(ca->ca_new_key, FTAG); zcrypt_key_free(ca->ca_new_key); kmem_free(ca, sizeof (struct wkey_change_arg)); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); spa_close(spa, FTAG); return (err); }
/* * 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); }
/* * dsl_crypto_key_clone * * Our caller (dmu_objset_create_sync) must have a lock on the dataset. */ int dsl_crypto_key_clone(dsl_dir_t *dd, dsl_dataset_phys_t *dsphys, uint64_t dsobj, dsl_dataset_t *clone_origin, dsl_crypto_ctx_t *ctx, dmu_tx_t *tx) { zcrypt_key_t *txgkey; zcrypt_key_t *wrappingkey = NULL; dsl_dataset_t *origin; uint64_t crypt; spa_t *spa = dd->dd_pool->dp_spa; int error = 0; ASSERT(ctx != NULL); if (BP_IS_HOLE(&dsphys->ds_bp)) { origin = ctx->dcc_origin_ds; } else { origin = clone_origin; } ASSERT(origin != NULL); /* * Need to look at the value of crypt on the origin snapshot * since it is sticky for encryption. */ VERIFY(dsl_prop_get_ds(origin, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &crypt,/* DSL_PROP_GET_EFFECTIVE,*/ NULL) == 0); if (crypt == ZIO_CRYPT_OFF) { return (0); } wrappingkey = ctx->dcc_wrap_key; dsl_keychain_create_obj(dd, tx); dsl_keychain_clone_phys(origin, dd, tx, wrappingkey); if (ctx->dcc_salt != 0) { objset_t *mos = dd->dd_pool->dp_meta_objset; uint64_t props_zapobj = dsl_dir_phys(dd)->dd_props_zapobj; error = zap_update(mos, props_zapobj, zfs_prop_to_name(ZFS_PROP_SALT), 8, 1, (void *)&ctx->dcc_salt, tx); } if (ctx->dcc_clone_newkey) { caddr_t wkeybuf; size_t wkeylen; /* * Generate a new key and add it to the keychain * to be valid from this txg onwards. */ zcrypt_key_hold(wrappingkey, FTAG); txgkey = zcrypt_key_gen(crypt); VERIFY(zcrypt_wrap_key(wrappingkey, txgkey, &wkeybuf, &wkeylen, zio_crypt_select_wrap(crypt)) == 0); zcrypt_key_release(wrappingkey, FTAG); dsl_keychain_set_key(dd, tx, wkeybuf, wkeylen, dsphys->ds_creation_time); kmem_free(wkeybuf, wkeylen); zcrypt_key_free(txgkey); spa_history_log_internal(spa, "key create", tx, "rekey succeeded dataset = %llu from dataset = %llu", dsobj, clone_origin->ds_object); } else { spa_history_log_internal(spa, "key create", tx, "succeeded dataset = %llu from dataset = %llu", dsobj, clone_origin->ds_object); } if (wrappingkey != NULL) { error = dsl_keychain_load_dd(dd, dsobj, crypt, wrappingkey); } return (error); }
/* * dsl_crypto_key_gen - Generate dataset key * * Generate a new key for this dataset based on its encryption property type. * Store the key as a usable zcrypt_key_t in the in memory keystore and * put the wrapped version of it in the on disk keychain. * * returns 0 on success */ int dsl_crypto_key_create(dsl_dir_t *dd, dsl_dataset_phys_t *dsphys, uint64_t dsobj, dsl_crypto_ctx_t *ctx, dmu_tx_t *tx) { int error = -1; #ifdef DEBUG zcrypt_key_t *debugkey; #endif /* DEBUG */ zcrypt_key_t *wrappingkey, *dslkey; caddr_t wkeybuf = NULL; size_t wkeylen = 0; uint64_t crypt; spa_t *spa = dd->dd_pool->dp_spa; zcrypt_keystore_node_t *skn; if (ctx == NULL) { return (0); } crypt = ctx->dcc_crypt; if (crypt == ZIO_CRYPT_OFF || crypt == ZIO_CRYPT_INHERIT) { return (0); } crypt = zio_crypt_select(crypt, ZIO_CRYPT_ON_VALUE); wrappingkey = ctx->dcc_wrap_key; skn = zcrypt_keystore_insert(spa, dsobj, wrappingkey); if (skn == NULL) { error = ENOKEY; goto out; } dslkey = zcrypt_key_gen(crypt); zcrypt_key_hold(wrappingkey, FTAG); error = zcrypt_wrap_key(wrappingkey, dslkey, &wkeybuf, &wkeylen, zio_crypt_select_wrap(crypt)); if (error != 0) { zcrypt_key_free(dslkey); spa_history_log_internal(spa, "key create", tx, "failed dataset = %llu unable to wrap key", dsobj, error); goto out; } dsl_keychain_create_obj(dd, tx); dsl_keychain_set_key(dd, tx, wkeybuf, wkeylen, dsphys->ds_creation_time); zcrypt_keychain_insert(&skn->skn_keychain, dsphys->ds_creation_txg, dslkey); if (ctx->dcc_salt != 0) { objset_t *mos = dd->dd_pool->dp_meta_objset; uint64_t props_zapobj = dsl_dir_phys(dd)->dd_props_zapobj; error = zap_update(mos, props_zapobj, zfs_prop_to_name(ZFS_PROP_SALT), 8, 1, (void *)&ctx->dcc_salt, tx); } if (error == 0) spa_history_log_internal(spa, "key create", tx, "succeeded dataset = %llu", dsobj); #ifdef DEBUG ASSERT3U(zcrypt_unwrap_key(wrappingkey, crypt, wkeybuf, wkeylen, &debugkey), ==, 0); zcrypt_key_compare(dslkey, debugkey); zcrypt_key_free(debugkey); #endif /* DEBUG */ out: zcrypt_key_release(wrappingkey, FTAG); if (wkeylen > 0) { kmem_free(wkeybuf, wkeylen); } return (error); }
/* * zcrypt_unwrap_key * * Using the provided wrapping key unwrap the key into a zcrypt_key_t. * * Allocates a zcrypt_key_t using kmem_alloc(), caller should free * using zcrypt_key_free(). * * returns 0 on success */ int zcrypt_unwrap_key(zcrypt_key_t *wk, uint64_t crypt, caddr_t wkeybuf, size_t wkeylen, zcrypt_key_t **zck) { crypto_mechanism_t wmech; crypto_data_t wkey_cdt, ptkey_cdt; zcrypt_key_t *tmpzck; caddr_t uwrapkey, uwrapmac; size_t uwrapkeylen, uwrapmaclen; size_t keylen; int ret; zcrypt_key_phys_t *wkeyphys = (zcrypt_key_phys_t *)wkeybuf; uint64_t wcrypt; ASSERT(wkeybuf != NULL); ASSERT(wkeylen != 0); /* * We maybe unwrapping a key of a smaller length than the wrapping * key so unwrapbuflen and keylen need to take that into account. * * The incoming wkey also has the iv stored at the start. */ wcrypt = wkeyphys->zkp_crypt; ASSERT3U(wcrypt, <, ZIO_CRYPT_WRAP_FUNCTIONS); wmech.cm_type = crypto_mech2id( zio_crypt_wrap_table[wcrypt].cwi_mechname); if (wmech.cm_type == CRYPTO_MECH_INVALID) { return (EINVAL); } keylen = zio_crypt_table[crypt].ci_keylen; if (wcrypt == ZIO_CRYPT_WRAP_AES_CCM) { CK_AES_CCM_PARAMS *ccmp; ccmp = kmem_zalloc(sizeof (CK_AES_CCM_PARAMS), KM_SLEEP); ccmp->ulNonceSize = zio_crypt_wrap_table[wcrypt].cwi_ivlen; ccmp->nonce = (uchar_t *)wkeyphys->zkp_kiv; ccmp->ulMACSize = zio_crypt_wrap_table[wcrypt].cwi_maclen; ccmp->ulDataSize = keylen + ccmp->ulMACSize; wmech.cm_param = (char *)ccmp; wmech.cm_param_len = sizeof (CK_AES_CCM_PARAMS); } else if (wcrypt == ZIO_CRYPT_WRAP_AES_GCM) { CK_AES_GCM_PARAMS *gcmp; gcmp = kmem_zalloc(sizeof (CK_AES_GCM_PARAMS), KM_SLEEP); gcmp->ulIvLen = zio_crypt_wrap_table[wcrypt].cwi_ivlen; gcmp->pIv = (uchar_t *)wkeyphys->zkp_kiv; gcmp->ulTagBits = zio_crypt_wrap_table[wcrypt].cwi_maclen * 8; wmech.cm_param = (char *)gcmp; wmech.cm_param_len = sizeof (CK_AES_GCM_PARAMS); } else { ASSERT(0); } uwrapkeylen = keylen + zio_crypt_wrap_table[wcrypt].cwi_maclen; uwrapkey = kmem_zalloc(uwrapkeylen, KM_SLEEP); SET_CRYPTO_DATA(ptkey_cdt, uwrapkey, uwrapkeylen); SET_CRYPTO_DATA(wkey_cdt, (char *)wkeyphys->zkp_key, keylen + zio_crypt_wrap_table[wcrypt].cwi_maclen); ret = crypto_decrypt(&wmech, &wkey_cdt, &wk->zk_key, NULL, &ptkey_cdt, NULL); kmem_free(wmech.cm_param, wmech.cm_param_len); if (ret != CRYPTO_SUCCESS) { kmem_free(uwrapkey, uwrapkeylen); zck = NULL; return (ret); } tmpzck = zcrypt_key_allocate(); tmpzck->zk_key.ck_format = CRYPTO_KEY_RAW; tmpzck->zk_key.ck_data = kmem_alloc(keylen, KM_SLEEP); tmpzck->zk_key.ck_length = keylen * 8; tmpzck->zk_crypt = crypt; bcopy(uwrapkey, tmpzck->zk_key.ck_data, keylen); kmem_free(uwrapkey, uwrapkeylen); /* Now the HMAC-SHA256 key which we know is 32 bytes */ keylen = 32; if (wcrypt == ZIO_CRYPT_WRAP_AES_CCM) { CK_AES_CCM_PARAMS *ccmp; ccmp = kmem_zalloc(sizeof (CK_AES_CCM_PARAMS), KM_SLEEP); ccmp->ulNonceSize = zio_crypt_wrap_table[wcrypt].cwi_ivlen; ccmp->nonce = (uchar_t *)wkeyphys->zkp_miv; ccmp->ulMACSize = zio_crypt_wrap_table[wcrypt].cwi_maclen; ccmp->ulDataSize = keylen + ccmp->ulMACSize; wmech.cm_param = (char *)ccmp; wmech.cm_param_len = sizeof (CK_AES_CCM_PARAMS); } else if (wcrypt == ZIO_CRYPT_WRAP_AES_GCM) { CK_AES_GCM_PARAMS *gcmp; gcmp = kmem_zalloc(sizeof (CK_AES_GCM_PARAMS), KM_SLEEP); gcmp->ulIvLen = zio_crypt_wrap_table[wcrypt].cwi_ivlen; gcmp->pIv = (uchar_t *)wkeyphys->zkp_miv; gcmp->ulTagBits = zio_crypt_wrap_table[wcrypt].cwi_maclen * 8; wmech.cm_param = (char *)gcmp; wmech.cm_param_len = sizeof (CK_AES_GCM_PARAMS); } else { ASSERT(0); } uwrapmaclen = keylen + zio_crypt_wrap_table[wcrypt].cwi_maclen; uwrapmac = kmem_zalloc(uwrapmaclen, KM_SLEEP); SET_CRYPTO_DATA(ptkey_cdt, uwrapmac, uwrapmaclen); SET_CRYPTO_DATA(wkey_cdt, (char *)wkeyphys->zkp_mackey, keylen + zio_crypt_wrap_table[wcrypt].cwi_maclen); ret = crypto_decrypt(&wmech, &wkey_cdt, &wk->zk_key, NULL, &ptkey_cdt, NULL); kmem_free(wmech.cm_param, wmech.cm_param_len); if (ret != CRYPTO_SUCCESS) { zcrypt_key_free(tmpzck); kmem_free(uwrapmac, uwrapmaclen); zck = NULL; return (ret); } tmpzck->zk_mackey.ck_format = CRYPTO_KEY_RAW; tmpzck->zk_mackey.ck_data = kmem_alloc(keylen, KM_SLEEP); tmpzck->zk_mackey.ck_length = keylen * 8; bcopy(uwrapmac, tmpzck->zk_mackey.ck_data, keylen); kmem_free(uwrapmac, uwrapmaclen); *zck = tmpzck; return (0); }