/* * Holds the pool while the objset is held. Therefore only one objset * can be held at a time. */ int dmu_objset_hold(const char *name, void *tag, objset_t **osp) { dsl_pool_t *dp; dsl_dataset_t *ds; int err; err = dsl_pool_hold(name, tag, &dp); if (err != 0) return (err); err = dsl_dataset_hold(dp, name, tag, &ds); if (err != 0) { dsl_pool_rele(dp, tag); return (err); } err = dmu_objset_from_ds(ds, osp); if (err != 0) { dsl_dataset_rele(ds, tag); dsl_pool_rele(dp, tag); } return (err); }
int dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) { dsl_pool_t *dp; dsl_dataset_t *ds; int error; error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); error = dsl_dataset_hold(dp, dsname, FTAG, &ds); if (error == 0) { error = dsl_deleg_access_impl(ds, perm, cr); dsl_dataset_rele(ds, FTAG); } dsl_pool_rele(dp, FTAG); return (error); }
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); }
static int dsl_crypto_key_change_find(const char *dsname, void *arg) { struct wkey_change_arg *ca = arg; struct kcnode *kcn; dsl_dataset_t *ds; objset_t *os; uint64_t crypt; char caource[MAXNAMELEN]; char setpoint[MAXNAMELEN]; int err; dsl_pool_t *dp; err = dsl_pool_hold(dsname, FTAG, &dp); if (err != 0) return (err); kcn = kmem_alloc(sizeof (struct kcnode), KM_SLEEP); if ((err = dsl_dataset_hold(dp, dsname, kcn, &ds)) != 0) { kmem_free(kcn, sizeof (struct kcnode)); dsl_pool_rele(dp, FTAG); return (err); } if ((err = dmu_objset_from_ds(ds, &os)) != 0) { dsl_dataset_rele(ds, kcn); dsl_pool_rele(dp, FTAG); kmem_free(kcn, sizeof (struct kcnode)); return (err); } /* * Check that this child dataset of ca->parent * is actually inheriting keysource from ca->parent and * not somewhere else (eg local, or some other dataset). */ rrw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER, FTAG); VERIFY(dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &crypt, NULL/*, DSL_PROP_GET_EFFECTIVE*/) == 0); VERIFY(dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), 1, sizeof (caource), &caource, setpoint/*, DSL_PROP_GET_EFFECTIVE*/) == 0); rrw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock, FTAG); if (crypt == ZIO_CRYPT_OFF || ((strcmp(ca->ca_parent, setpoint) != 0 && strcmp(ca->ca_parent, dsname) != 0))) { dsl_dataset_rele(ds, kcn); dsl_pool_rele(dp, FTAG); kmem_free(kcn, sizeof (struct kcnode)); return (0); } //dsl_sync_task_create(ca->ca_dstg, dsl_crypto_key_change_check, // dsl_crypto_key_change_sync, ds, arg, 1); ca->ca_ds = ds; err = dsl_sync_task(dsname, dsl_crypto_key_change_check, dsl_crypto_key_change_sync, arg, 1, ZFS_SPACE_CHECK_NONE); kcn->kc_ds = ds; list_insert_tail(&ca->ca_nodes, kcn); return (0); }
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_inherit(const char *dsname) { char keysource[MAXNAMELEN]; char setpoint[MAXNAMELEN]; dsl_dataset_t *ids; int error; zcrypt_key_t *wrappingkey; zfs_crypt_key_status_t keystatus; spa_t *spa; dsl_pool_t *dp; /* * Try inheriting the wrapping key from our parent */ error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); error = dsl_dataset_keystatus_byname(dp, dsname, &keystatus); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } if (keystatus == ZFS_CRYPT_KEY_NONE) { dsl_pool_rele(dp, FTAG); return (0); } if (keystatus == ZFS_CRYPT_KEY_AVAILABLE) { dsl_pool_rele(dp, FTAG); return (EEXIST); } error = dsl_prop_get(dsname, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), 1, sizeof (keysource), &keysource, setpoint); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } if (strcmp(setpoint, dsname) == 0) { dsl_pool_rele(dp, FTAG); return (ENOENT); } error = dsl_dataset_hold(dp, setpoint, FTAG, &ids); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } spa = dsl_dataset_get_spa(ids); wrappingkey = zcrypt_key_copy(zcrypt_keystore_find_wrappingkey(spa, ids->ds_object)); dsl_dataset_rele(ids, FTAG); dsl_pool_rele(dp, FTAG); if (wrappingkey == NULL) return (ENOENT); error = dsl_crypto_key_load(dsname, wrappingkey); 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); }
/* * dsl_crypto_key_unload * * Remove the key from the in memory keystore. * * First we have to remove the minor node for a ZVOL or unmount * the filesystem. This is so that we flush all pending IO for it to disk * so we won't need to encrypt anything with this key. Anything in flight * should already have a lock on the keys it needs. * We can't assume that userland has already successfully unmounted the * dataset though in many cases it will have. * * If the key can't be removed return the failure back to our caller. */ int dsl_crypto_key_unload(const char *dsname) { dsl_dataset_t *ds; objset_t *os; int error; spa_t *spa; dsl_pool_t *dp; #ifdef _KERNEL dmu_objset_type_t os_type; //vfs_t *vfsp; struct vfsmount *vfsp; #endif /* _KERNEL */ error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); /* XXX - should we use own_exclusive() here? */ if ((error = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (error); } if ((error = dmu_objset_from_ds(ds, &os)) != 0) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); } #ifdef _KERNEL /* * Make sure that the device node has gone for ZVOLs * and that filesystems are umounted. */ #if 0 // FIXME os_type = dmu_objset_type(os); if (os_type == DMU_OST_ZVOL) { error = zvol_remove_minor(dsname); if (error == ENXIO) error = 0; } else if (os_type == DMU_OST_ZFS) { vfsp = zfs_get_vfs(dsname); if (vfsp != NULL) { error = vn_vfswlock(vfsp->vfs_vnodecovered); VFS_RELE(vfsp); if (error == 0) error = dounmount(vfsp, 0, CRED()); } } if (error != 0) { dsl_dataset_rele(ds, FTAG); return (error); } #endif #endif /* _KERNEL */ /* * Make sure all dbufs are synced. * * It is essential for encrypted datasets to ensure that * there is no further pending IO before removing the key. */ if (dmu_objset_is_dirty(os, 0)) // FIXME, 0? txg_wait_synced(dmu_objset_pool(os), 0); dmu_objset_evict_dbufs(os); spa = dsl_dataset_get_spa(ds); error = zcrypt_keystore_remove(spa, ds->ds_object); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); }
/* * Find all 'allow' permissions from a given point and then continue * traversing up to the root. * * This function constructs an nvlist of nvlists. * each setpoint is an nvlist composed of an nvlist of an nvlist * of the individual * users/groups/everyone/create * permissions. * * The nvlist will look like this. * * { source fsname -> { whokeys { permissions,...}, ...}} * * The fsname nvpairs will be arranged in a bottom up order. For example, * if we have the following structure a/b/c then the nvpairs for the fsnames * will be ordered a/b/c, a/b, a. */ int dsl_deleg_get(const char *ddname, nvlist_t **nvp) { dsl_dir_t *dd, *startdd; dsl_pool_t *dp; int error; objset_t *mos; zap_cursor_t *basezc, *zc; zap_attribute_t *baseza, *za; char *source; error = dsl_pool_hold(ddname, FTAG, &dp); if (error != 0) return (error); error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } dp = startdd->dd_pool; mos = dp->dp_meta_objset; zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); basezc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); baseza = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); source = kmem_alloc(MAXNAMELEN + strlen(MOS_DIR_NAME) + 1, KM_SLEEP); VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); for (dd = startdd; dd != NULL; dd = dd->dd_parent) { nvlist_t *sp_nvp; uint64_t n; if (dd->dd_phys->dd_deleg_zapobj == 0 || zap_count(mos, dd->dd_phys->dd_deleg_zapobj, &n) != 0 || n == 0) continue; sp_nvp = fnvlist_alloc(); for (zap_cursor_init(basezc, mos, dd->dd_phys->dd_deleg_zapobj); zap_cursor_retrieve(basezc, baseza) == 0; zap_cursor_advance(basezc)) { nvlist_t *perms_nvp; ASSERT(baseza->za_integer_length == 8); ASSERT(baseza->za_num_integers == 1); perms_nvp = fnvlist_alloc(); for (zap_cursor_init(zc, mos, baseza->za_first_integer); zap_cursor_retrieve(zc, za) == 0; zap_cursor_advance(zc)) { fnvlist_add_boolean(perms_nvp, za->za_name); } zap_cursor_fini(zc); fnvlist_add_nvlist(sp_nvp, baseza->za_name, perms_nvp); fnvlist_free(perms_nvp); } zap_cursor_fini(basezc); dsl_dir_name(dd, source); fnvlist_add_nvlist(*nvp, source, sp_nvp); nvlist_free(sp_nvp); } kmem_free(source, MAXNAMELEN + strlen(MOS_DIR_NAME) + 1); kmem_free(baseza, sizeof (zap_attribute_t)); kmem_free(basezc, sizeof (zap_cursor_t)); kmem_free(za, sizeof (zap_attribute_t)); kmem_free(zc, sizeof (zap_cursor_t)); dsl_dir_rele(startdd, FTAG); dsl_pool_rele(dp, FTAG); return (0); }
/* * Find all 'allow' permissions from a given point and then continue * traversing up to the root. * * This function constructs an nvlist of nvlists. * each setpoint is an nvlist composed of an nvlist of an nvlist * of the individual * users/groups/everyone/create * permissions. * * The nvlist will look like this. * * { source fsname -> { whokeys { permissions,...}, ...}} * * The fsname nvpairs will be arranged in a bottom up order. For example, * if we have the following structure a/b/c then the nvpairs for the fsnames * will be ordered a/b/c, a/b, a. */ int dsl_deleg_get(const char *ddname, nvlist_t **nvp) { dsl_dir_t *dd, *startdd; dsl_pool_t *dp; int error; objset_t *mos; error = dsl_pool_hold(ddname, FTAG, &dp); if (error != 0) return (error); error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } dp = startdd->dd_pool; mos = dp->dp_meta_objset; VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); for (dd = startdd; dd != NULL; dd = dd->dd_parent) { zap_cursor_t basezc; zap_attribute_t baseza; nvlist_t *sp_nvp; uint64_t n; char source[ZFS_MAX_DATASET_NAME_LEN]; if (dsl_dir_phys(dd)->dd_deleg_zapobj == 0 || zap_count(mos, dsl_dir_phys(dd)->dd_deleg_zapobj, &n) != 0 || n == 0) continue; sp_nvp = fnvlist_alloc(); for (zap_cursor_init(&basezc, mos, dsl_dir_phys(dd)->dd_deleg_zapobj); zap_cursor_retrieve(&basezc, &baseza) == 0; zap_cursor_advance(&basezc)) { zap_cursor_t zc; zap_attribute_t za; nvlist_t *perms_nvp; ASSERT(baseza.za_integer_length == 8); ASSERT(baseza.za_num_integers == 1); perms_nvp = fnvlist_alloc(); for (zap_cursor_init(&zc, mos, baseza.za_first_integer); zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { fnvlist_add_boolean(perms_nvp, za.za_name); } zap_cursor_fini(&zc); fnvlist_add_nvlist(sp_nvp, baseza.za_name, perms_nvp); fnvlist_free(perms_nvp); } zap_cursor_fini(&basezc); dsl_dir_name(dd, source); fnvlist_add_nvlist(*nvp, source, sp_nvp); nvlist_free(sp_nvp); } dsl_dir_rele(startdd, FTAG); dsl_pool_rele(dp, FTAG); return (0); }