From 89b4e7acb5b82b7f17b6622a9a91c5f49e40dfee Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Thu, 12 May 2016 11:00:08 -0400 Subject: [PATCH 01/10] Native data and metadata encryption for zfs --- cmd/zdb/zdb.c | 55 +- cmd/zdb/zdb_il.c | 11 +- cmd/zed/agents/zfs_mod.c | 2 +- cmd/zfs/zfs_main.c | 111 +- cmd/zinject/translate.c | 4 +- cmd/zpool/zpool_main.c | 49 +- cmd/ztest/ztest.c | 41 +- configure.ac | 1 + include/libuutil.h | 4 +- include/libzfs.h | 19 +- include/libzfs_core.h | 5 +- include/sys/Makefile.am | 2 + include/sys/arc.h | 41 +- include/sys/arc_impl.h | 43 +- include/sys/dbuf.h | 1 + include/sys/ddt.h | 12 +- include/sys/dmu.h | 58 +- include/sys/dmu_objset.h | 6 +- include/sys/dmu_traverse.h | 1 + include/sys/dsl_crypt.h | 181 ++ include/sys/dsl_dataset.h | 18 +- include/sys/dsl_deleg.h | 2 + include/sys/dsl_dir.h | 3 + include/sys/dsl_pool.h | 4 +- include/sys/fs/zfs.h | 14 + include/sys/spa.h | 36 +- include/sys/spa_checksum.h | 4 + include/sys/spa_impl.h | 2 + include/sys/zil.h | 6 +- include/sys/zio.h | 50 +- include/sys/zio_crypt.h | 172 ++ include/sys/zio_impl.h | 50 +- include/zfeature_common.h | 1 + include/zfs_deleg.h | 2 + lib/libzfs/Makefile.am | 1 + lib/libzfs/libzfs_crypto.c | 1359 ++++++++++++++ lib/libzfs/libzfs_dataset.c | 75 +- lib/libzfs/libzfs_mount.c | 25 +- lib/libzfs/libzfs_pool.c | 14 +- lib/libzfs/libzfs_sendrecv.c | 20 + lib/libzfs/libzfs_util.c | 2 + lib/libzfs_core/libzfs_core.c | 34 +- lib/libzpool/Makefile.am | 2 + man/man5/zpool-features.5 | 48 + man/man8/zfs.8 | 82 + man/man8/zpool.8 | 23 +- module/zcommon/zfs_deleg.c | 2 + module/zcommon/zfs_prop.c | 38 +- module/zfs/Makefile.in | 2 + module/zfs/arc.c | 1197 ++++++++++--- module/zfs/bptree.c | 3 +- module/zfs/dbuf.c | 52 +- module/zfs/ddt.c | 22 +- module/zfs/dmu.c | 189 +- module/zfs/dmu_objset.c | 112 +- module/zfs/dmu_send.c | 87 +- module/zfs/dmu_traverse.c | 25 +- module/zfs/dnode.c | 5 +- module/zfs/dsl_crypt.c | 1591 +++++++++++++++++ module/zfs/dsl_dataset.c | 200 ++- module/zfs/dsl_destroy.c | 12 +- module/zfs/dsl_dir.c | 39 +- module/zfs/dsl_pool.c | 19 +- module/zfs/dsl_scan.c | 14 +- module/zfs/spa.c | 37 +- module/zfs/spa_history.c | 7 +- module/zfs/zfeature.c | 4 +- module/zfs/zfeature_common.c | 11 + module/zfs/zfs_acl.c | 2 +- module/zfs/zfs_ioctl.c | 176 +- module/zfs/zfs_vfsops.c | 2 +- module/zfs/zil.c | 64 +- module/zfs/zio.c | 216 ++- module/zfs/zio_checksum.c | 42 +- module/zfs/zio_crypt.c | 1374 ++++++++++++++ module/zfs/zvol.c | 26 +- module/zpios/pios.c | 4 +- tests/runfiles/linux.run | 15 +- .../tests/functional/cli_root/Makefile.am | 1 + .../cli_root/zfs_create/Makefile.am | 3 +- .../zfs_create/zfs_create_encrypted.ksh | 121 ++ .../functional/cli_root/zfs_key/Makefile.am | 12 + .../functional/cli_root/zfs_key/cleanup.ksh | 34 + .../tests/functional/cli_root/zfs_key/key.cfg | 36 + .../functional/cli_root/zfs_key/setup.ksh | 36 + .../cli_root/zfs_key/zfs_key_change_neg.ksh | 81 + .../cli_root/zfs_key/zfs_key_change_pos.ksh | 73 + .../cli_root/zfs_key/zfs_key_common.kshlib | 53 + .../cli_root/zfs_key/zfs_key_load_neg.ksh | 62 + .../cli_root/zfs_key/zfs_key_load_pos.ksh | 62 + .../cli_root/zfs_key/zfs_key_unload_neg.ksh | 67 + .../cli_root/zfs_key/zfs_key_unload_pos.ksh | 61 + .../functional/cli_root/zfs_mount/Makefile.am | 1 + .../zfs_mount/zfs_mount_encrypted.ksh | 70 + .../cli_root/zfs_receive/Makefile.am | 4 +- .../zfs_receive/zfs_receive_encrypted_neg.ksh | 74 + .../zfs_receive/zfs_receive_encrypted_pos.ksh | 85 + .../functional/cli_root/zfs_send/Makefile.am | 4 +- .../zfs_send/zfs_send_encrypted_neg.ksh | 71 + .../zfs_send/zfs_send_encrypted_pos.ksh | 67 + .../cli_root/zpool_create/Makefile.am | 1 + .../zpool_create/zpool_create_encrypted.ksh | 111 ++ .../cli_root/zpool_get/zpool_get.cfg | 4 +- 103 files changed, 8788 insertions(+), 691 deletions(-) create mode 100644 include/sys/dsl_crypt.h create mode 100644 include/sys/zio_crypt.h create mode 100644 lib/libzfs/libzfs_crypto.c create mode 100644 module/zfs/dsl_crypt.c create mode 100644 module/zfs/zio_crypt.c create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/Makefile.am create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/cleanup.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/key.cfg create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/setup.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh create mode 100644 tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index 7768bb39abf3..5034ffbfc957 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -1887,6 +1888,7 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) dmu_buf_t *db = NULL; dmu_object_info_t doi; dnode_t *dn; + boolean_t dnode_held = B_FALSE; void *bonus = NULL; size_t bsize = 0; char iblk[32], dblk[32], lsize[32], asize[32], fill[32], dnsize[32]; @@ -1903,16 +1905,33 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) if (object == 0) { dn = DMU_META_DNODE(os); + dmu_object_info_from_dnode(dn, &doi); } else { - error = dmu_bonus_hold(os, object, FTAG, &db); + /* + * Encrypted datasets will have sensitive bonus buffers + * encrypted. Therefore we cannot hold the bonus buffer and + * must get the dnode itself instead. + */ + error = dmu_object_info(os, object, &doi); if (error) - fatal("dmu_bonus_hold(%llu) failed, errno %u", - object, error); - bonus = db->db_data; - bsize = db->db_size; - dn = DB_DNODE((dmu_buf_impl_t *)db); + fatal("dmu_object_info() failed, errno %u", error); + + if (os->os_encrypted && + DMU_OT_IS_ENCRYPTED(doi.doi_bonus_type)) { + error = dnode_hold(os, object, FTAG, &dn); + if (error) + fatal("dnode_hold() failed, errno %u", error); + dnode_held = B_TRUE; + } else { + error = dmu_bonus_hold(os, object, FTAG, &db); + if (error) + fatal("dmu_bonus_hold(%llu) failed, errno %u", + object, error); + bonus = db->db_data; + bsize = db->db_size; + dn = DB_DNODE((dmu_buf_impl_t *)db); + } } - dmu_object_info_from_dnode(dn, &doi); zdb_nicenum(doi.doi_metadata_block_size, iblk); zdb_nicenum(doi.doi_data_block_size, dblk); @@ -1959,8 +1978,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) (void) printf("\tdnode maxblkid: %llu\n", (longlong_t)dn->dn_phys->dn_maxblkid); - object_viewer[ZDB_OT_TYPE(doi.doi_bonus_type)](os, object, - bonus, bsize); + if (!dnode_held) { + object_viewer[ZDB_OT_TYPE(doi.doi_bonus_type)](os, + object, bonus, bsize); + } else { + (void) printf("\t\t(bonus encrypted)\n"); + } + object_viewer[ZDB_OT_TYPE(doi.doi_type)](os, object, NULL, 0); *print_header = 1; } @@ -2003,6 +2027,8 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) if (db != NULL) dmu_buf_rele(db, FTAG); + if (dnode_held) + dnode_rele(dn, FTAG); } static char *objset_types[DMU_OST_NUMTYPES] = { @@ -2299,7 +2325,7 @@ dump_one_dir(const char *dsname, void *arg) objset_t *os; spa_feature_t f; - error = dmu_objset_own(dsname, DMU_OST_ANY, B_TRUE, FTAG, &os); + error = dmu_objset_own(dsname, DMU_OST_ANY, B_TRUE, B_FALSE, FTAG, &os); if (error) { (void) printf("Could not open %s, error %d\n", dsname, error); return (0); @@ -2747,7 +2773,8 @@ dump_block_stats(spa_t *spa) zdb_cb_t zcb; zdb_blkstats_t *zb, *tzb; uint64_t norm_alloc, norm_space, total_alloc, total_found; - int flags = TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_HARD; + int flags = TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | + TRAVERSE_NO_DECRYPT | TRAVERSE_HARD; boolean_t leaks = B_FALSE; int e, c; bp_embedded_type_t i; @@ -3052,8 +3079,8 @@ dump_simulated_ddt(spa_t *spa) spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER); - (void) traverse_pool(spa, 0, TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, - zdb_ddt_add_cb, &t); + (void) traverse_pool(spa, 0, TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | + TRAVERSE_NO_DECRYPT, zdb_ddt_add_cb, &t); spa_config_exit(spa, SCL_CONFIG, FTAG); @@ -3875,7 +3902,7 @@ main(int argc, char **argv) } } else { error = dmu_objset_own(target, DMU_OST_ANY, - B_TRUE, FTAG, &os); + B_TRUE, B_FALSE, FTAG, &os); } } nvlist_free(policy); diff --git a/cmd/zdb/zdb_il.c b/cmd/zdb/zdb_il.c index 190bfee86ae5..c79e61e69889 100644 --- a/cmd/zdb/zdb_il.c +++ b/cmd/zdb/zdb_il.c @@ -311,8 +311,13 @@ print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg) (u_longlong_t)lr->lrc_txg, (u_longlong_t)lr->lrc_seq); - if (txtype && verbose >= 3) - zil_rec_info[txtype].zri_print(zilog, txtype, lr); + if (txtype && verbose >= 3) { + if (!zilog->zl_os->os_encrypted) { + zil_rec_info[txtype].zri_print(zilog, txtype, lr); + } else { + (void) printf("%s(encrypted)\n", prefix); + } + } zil_rec_info[txtype].zri_count++; zil_rec_info[0].zri_count++; @@ -399,7 +404,7 @@ dump_intent_log(zilog_t *zilog) if (verbose >= 2) { (void) printf("\n"); (void) zil_parse(zilog, print_log_block, print_log_record, NULL, - zh->zh_claim_txg); + zh->zh_claim_txg, B_FALSE); print_log_stats(verbose); } } diff --git a/cmd/zed/agents/zfs_mod.c b/cmd/zed/agents/zfs_mod.c index d297ab29401f..dacbfd57f4b0 100644 --- a/cmd/zed/agents/zfs_mod.c +++ b/cmd/zed/agents/zfs_mod.c @@ -506,7 +506,7 @@ zfs_enable_ds(void *arg) assert(pool->uap_enable_tid = pthread_self()); - (void) zpool_enable_datasets(pool->uap_zhp, NULL, 0); + (void) zpool_enable_datasets(pool->uap_zhp, NULL, 0, B_FALSE); zpool_close(pool->uap_zhp); pool->uap_zhp = NULL; diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index c31f1b5d2714..e97f8e6858ae 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -103,6 +103,7 @@ static int zfs_do_holds(int argc, char **argv); static int zfs_do_release(int argc, char **argv); static int zfs_do_diff(int argc, char **argv); static int zfs_do_bookmark(int argc, char **argv); +static int zfs_do_key(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -150,6 +151,7 @@ typedef enum { HELP_RELEASE, HELP_DIFF, HELP_BOOKMARK, + HELP_KEY, } zfs_help_t; typedef struct zfs_command { @@ -203,6 +205,7 @@ static zfs_command_t command_table[] = { { "holds", zfs_do_holds, HELP_HOLDS }, { "release", zfs_do_release, HELP_RELEASE }, { "diff", zfs_do_diff, HELP_DIFF }, + { "key", zfs_do_key, HELP_KEY }, }; #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) @@ -321,6 +324,10 @@ get_usage(zfs_help_t idx) "[snapshot|filesystem]\n")); case HELP_BOOKMARK: return (gettext("\tbookmark \n")); + case HELP_KEY: + return (gettext("\tkey [-lu] \n" + "\tkey -c [-o keysource=] [-o pbkfd2iters=] " + "\n")); } abort(); @@ -874,7 +881,7 @@ zfs_do_create(int argc, char **argv) (void) snprintf(msg, sizeof (msg), gettext("cannot create '%s'"), argv[0]); if (props && (real_props = zfs_valid_proplist(g_zfs, type, - props, 0, NULL, zpool_handle, msg)) == NULL) { + props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) { zpool_close(zpool_handle); goto error; } @@ -4145,6 +4152,8 @@ zfs_do_receive(int argc, char **argv) #define ZFS_DELEG_PERM_RELEASE "release" #define ZFS_DELEG_PERM_DIFF "diff" #define ZFS_DELEG_PERM_BOOKMARK "bookmark" +#define ZFS_DELEG_PERM_LOAD_KEY "keyuse" +#define ZFS_DELEG_PERM_CHANGE_KEY "keychange" #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE @@ -4165,6 +4174,8 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK }, + { ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY }, + { ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY }, { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, @@ -4738,6 +4749,12 @@ deleg_perm_comment(zfs_deleg_note_t note) case ZFS_DELEG_NOTE_SNAPSHOT: str = gettext(""); break; + case ZFS_DELEG_NOTE_LOAD_KEY: + str = gettext("Allows loading or unloading an encryption key"); + break; + case ZFS_DELEG_NOTE_CHANGE_KEY: + str = gettext("Allows changing or adding an encryption key"); + break; /* * case ZFS_DELEG_NOTE_VSCAN: * str = gettext(""); @@ -6914,6 +6931,98 @@ zfs_do_bookmark(int argc, char **argv) return (-1); } +static int +zfs_do_key(int argc, char **argv) +{ + int c, ret = -1; + boolean_t load = B_FALSE, unload = B_FALSE, rewrap = B_FALSE; + zfs_handle_t *zhp = NULL; + nvlist_t *props = fnvlist_alloc(); + + while ((c = getopt(argc, argv, "ulco:")) != -1) { + switch (c) { + case 'u': + if (ret == 0) { + (void) fprintf(stderr, gettext( + "multiple actions specified\n")); + usage(B_FALSE); + } + unload = B_TRUE; + ret = 0; + break; + case 'l': + if (ret == 0) { + (void) fprintf(stderr, gettext( + "multiple actions specified\n")); + usage(B_FALSE); + } + load = B_TRUE; + ret = 0; + break; + case 'c': + if (ret == 0) { + (void) fprintf(stderr, gettext( + "multiple actions specified\n")); + usage(B_FALSE); + } + rewrap = B_TRUE; + ret = 0; + break; + case 'o': + if (parseprop(props, optarg) != 0) + return (1); + break; + default: + (void) fprintf(stderr, + gettext("invalid option '%c'\n"), optopt); + usage(B_FALSE); + } + } + + if (ret) { + (void) fprintf(stderr, + gettext("No action specified\n")); + usage(B_FALSE); + } + + if (!rewrap && !nvlist_empty(props)) { + (void) fprintf(stderr, + gettext("Properties not allowed for specified command\n")); + usage(B_FALSE); + } + + if (argc < 3) { + (void) fprintf(stderr, gettext("Too few arguments\n")); + usage(B_FALSE); + } + + zhp = zfs_open(g_zfs, argv[argc - 1], + ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME); + if (zhp == NULL) + usage(B_FALSE); + + if (load) + ret = zfs_crypto_load_key(zhp); + else if (unload) + ret = zfs_crypto_unload_key(zhp); + else + ret = zfs_crypto_rewrap(zhp, props); + + if (ret) + goto error; + + nvlist_free(props); + zfs_close(zhp); + return (0); + +error: + if (props) + nvlist_free(props); + if (zhp) + zfs_close(zhp); + return (-1); +} + int main(int argc, char **argv) { diff --git a/cmd/zinject/translate.c b/cmd/zinject/translate.c index 00a0712900e5..dbc4193b2d90 100644 --- a/cmd/zinject/translate.c +++ b/cmd/zinject/translate.c @@ -179,7 +179,7 @@ object_from_path(const char *dataset, const char *path, struct stat64 *statbuf, */ sync(); - err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, FTAG, &os); + err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, B_TRUE, FTAG, &os); if (err != 0) { (void) fprintf(stderr, "cannot open dataset '%s': %s\n", dataset, strerror(err)); @@ -267,7 +267,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, * size. */ if ((err = dmu_objset_own(dataset, DMU_OST_ANY, - B_TRUE, FTAG, &os)) != 0) { + B_TRUE, B_TRUE, FTAG, &os)) != 0) { (void) fprintf(stderr, "cannot open dataset '%s': %s\n", dataset, strerror(err)); goto out; diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index f4873cf196a6..70bba3e53505 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -303,12 +303,13 @@ get_usage(zpool_help_t idx) { return (gettext("\thistory [-il] [] ...\n")); case HELP_IMPORT: return (gettext("\timport [-d dir] [-D]\n" - "\timport [-d dir | -c cachefile] [-F [-n]] \n" + "\timport [-d dir | -c cachefile] [-F [-n]] [-l] " + "\n" "\timport [-o mntopts] [-o property=value] ... \n" - "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] " + "\t [-d dir | -c cachefile] [-D] [-l] [-f] [-m] [-N] " "[-R root] [-F [-n]] -a\n" "\timport [-o mntopts] [-o property=value] ... \n" - "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] " + "\t [-d dir | -c cachefile] [-D] [-l] [-f] [-m] [-N] " "[-R root] [-F [-n]]\n" "\t [newpool]\n")); case HELP_IOSTAT: @@ -349,7 +350,7 @@ get_usage(zpool_help_t idx) { case HELP_SET: return (gettext("\tset \n")); case HELP_SPLIT: - return (gettext("\tsplit [-gLnP] [-R altroot] [-o mntopts]\n" + return (gettext("\tsplit [-gLnPl] [-R altroot] [-o mntopts]\n" "\t [-o property=value] " "[ ...]\n")); case HELP_REGUID: @@ -2147,7 +2148,8 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && !(flags & ZFS_IMPORT_ONLY) && - zpool_enable_datasets(zhp, mntopts, 0) != 0) { + zpool_enable_datasets(zhp, mntopts, 0, + !!(flags & ZFS_IMPORT_LOAD_KEYS)) != 0) { zpool_close(zhp); return (1); } @@ -2158,9 +2160,9 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, /* * zpool import [-d dir] [-D] - * import [-o mntopts] [-o prop=value] ... [-R root] [-D] + * import [-o mntopts] [-o prop=value] ... [-R root] [-D] [-l] * [-d dir | -c cachefile] [-f] -a - * import [-o mntopts] [-o prop=value] ... [-R root] [-D] + * import [-o mntopts] [-o prop=value] ... [-R root] [-D] [-l] * [-d dir | -c cachefile] [-f] [-n] [-F] [newpool] * * -c Read pool information from a cachefile instead of searching @@ -2195,6 +2197,8 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, * * -a Import all pools found. * + * -l Load encryption keys while importing. + * * -o Set property=value and/or temporary mount options (without '='). * * -s Scan using the default search path, the libblkid cache will @@ -2236,7 +2240,7 @@ zpool_do_import(int argc, char **argv) char *endptr; /* check options */ - while ((c = getopt(argc, argv, ":aCc:d:DEfFmnNo:R:stT:VX")) != -1) { + while ((c = getopt(argc, argv, ":aCc:d:DEfFlmnNo:R:stT:VX")) != -1) { switch (c) { case 'a': do_all = B_TRUE; @@ -2266,6 +2270,9 @@ zpool_do_import(int argc, char **argv) case 'F': do_rewind = B_TRUE; break; + case 'l': + flags |= ZFS_IMPORT_LOAD_KEYS; + break; case 'm': flags |= ZFS_IMPORT_MISSING_LOG; break; @@ -2340,6 +2347,17 @@ zpool_do_import(int argc, char **argv) usage(B_FALSE); } + if ((flags & ZFS_IMPORT_LOAD_KEYS) && (flags & ZFS_IMPORT_ONLY)) { + (void) fprintf(stderr, gettext("-l is incompatible with -N\n")); + usage(B_FALSE); + } + + if ((flags & ZFS_IMPORT_LOAD_KEYS) && !do_all && argc == 0) { + (void) fprintf(stderr, gettext("-l is only meaningful during " + "an import\n")); + usage(B_FALSE); + } + if ((dryrun || xtreme_rewind) && !do_rewind) { (void) fprintf(stderr, gettext("-n or -X only meaningful with -F\n")); @@ -4955,6 +4973,7 @@ zpool_do_detach(int argc, char **argv) * -o Set property=value, or set mount options. * -P Display full path for vdev name. * -R Mount the split-off pool under an alternate root. + * -l Load encryption keys while importing. * * Splits the named pool and gives it the new pool name. Devices to be split * off may be listed, provided that no more than one device is specified @@ -4971,6 +4990,7 @@ zpool_do_split(int argc, char **argv) char *srcpool, *newpool, *propval; char *mntopts = NULL; splitflags_t flags; + boolean_t loadkeys = B_FALSE; int c, ret = 0; zpool_handle_t *zhp; nvlist_t *config, *props = NULL; @@ -4980,7 +5000,7 @@ zpool_do_split(int argc, char **argv) flags.name_flags = 0; /* check options */ - while ((c = getopt(argc, argv, ":gLR:no:P")) != -1) { + while ((c = getopt(argc, argv, ":gLR:lno:P")) != -1) { switch (c) { case 'g': flags.name_flags |= VDEV_NAME_GUID; @@ -4997,6 +5017,9 @@ zpool_do_split(int argc, char **argv) usage(B_FALSE); } break; + case 'l': + loadkeys = B_TRUE; + break; case 'n': flags.dryrun = B_TRUE; break; @@ -5035,6 +5058,12 @@ zpool_do_split(int argc, char **argv) usage(B_FALSE); } + if (!flags.import && loadkeys) { + (void) fprintf(stderr, gettext("loading keys is only " + "valid when importing the pool\n")); + usage(B_FALSE); + } + argc -= optind; argv += optind; @@ -5088,7 +5117,7 @@ zpool_do_split(int argc, char **argv) return (1); } if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && - zpool_enable_datasets(zhp, mntopts, 0) != 0) { + zpool_enable_datasets(zhp, mntopts, 0, loadkeys) != 0) { ret = 1; (void) fprintf(stderr, gettext("Split was successful, but " "the datasets could not all be mounted\n")); diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c index 062d501d51ca..8f3dfd6e3bb1 100644 --- a/cmd/ztest/ztest.c +++ b/cmd/ztest/ztest.c @@ -2624,7 +2624,7 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id) */ nvroot = make_vdev_root("/dev/bogus", NULL, NULL, 0, 0, 0, 0, 0, 1); VERIFY3U(ENOENT, ==, - spa_create("ztest_bad_file", nvroot, NULL, NULL)); + spa_create("ztest_bad_file", nvroot, NULL, NULL, NULL)); nvlist_free(nvroot); /* @@ -2632,7 +2632,7 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id) */ nvroot = make_vdev_root("/dev/bogus", NULL, NULL, 0, 0, 0, 0, 2, 1); VERIFY3U(ENOENT, ==, - spa_create("ztest_bad_mirror", nvroot, NULL, NULL)); + spa_create("ztest_bad_mirror", nvroot, NULL, NULL, NULL)); nvlist_free(nvroot); /* @@ -2641,7 +2641,8 @@ ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id) */ (void) rw_rdlock(&ztest_name_lock); nvroot = make_vdev_root("/dev/bogus", NULL, NULL, 0, 0, 0, 0, 0, 1); - VERIFY3U(EEXIST, ==, spa_create(zo->zo_pool, nvroot, NULL, NULL)); + VERIFY3U(EEXIST, ==, + spa_create(zo->zo_pool, nvroot, NULL, NULL, NULL)); nvlist_free(nvroot); VERIFY3U(0, ==, spa_open(zo->zo_pool, &spa, FTAG)); VERIFY3U(EBUSY, ==, spa_destroy(zo->zo_pool)); @@ -2699,7 +2700,7 @@ ztest_spa_upgrade(ztest_ds_t *zd, uint64_t id) props = fnvlist_alloc(); fnvlist_add_uint64(props, zpool_prop_to_name(ZPOOL_PROP_VERSION), version); - VERIFY3S(spa_create(name, nvroot, props, NULL), ==, 0); + VERIFY3S(spa_create(name, nvroot, props, NULL, NULL), ==, 0); fnvlist_free(nvroot); fnvlist_free(props); @@ -3462,7 +3463,7 @@ static int ztest_dataset_create(char *dsname) { uint64_t zilset = ztest_random(100); - int err = dmu_objset_create(dsname, DMU_OST_OTHER, 0, + int err = dmu_objset_create(dsname, DMU_OST_OTHER, 0, NULL, ztest_objset_create_cb, NULL); if (err || zilset < 80) @@ -3485,7 +3486,7 @@ ztest_objset_destroy_cb(const char *name, void *arg) /* * Verify that the dataset contains a directory object. */ - VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, FTAG, &os)); + VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, B_TRUE, FTAG, &os)); error = dmu_object_info(os, ZTEST_DIROBJ, &doi); if (error != ENOENT) { /* We could have crashed in the middle of destroying it */ @@ -3566,7 +3567,8 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) * (invoked from ztest_objset_destroy_cb()) should just throw it away. */ if (ztest_random(2) == 0 && - dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os) == 0) { + dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, + B_TRUE, FTAG, &os) == 0) { ztest_zd_init(zdtmp, NULL, os); zil_replay(os, zdtmp, ztest_replay_vector); ztest_zd_fini(zdtmp); @@ -3584,7 +3586,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) /* * Verify that the destroyed dataset is no longer in the namespace. */ - VERIFY3U(ENOENT, ==, dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, + VERIFY3U(ENOENT, ==, dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, B_TRUE, FTAG, &os)); /* @@ -3599,7 +3601,8 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) fatal(0, "dmu_objset_create(%s) = %d", name, error); } - VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os)); + VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE, + FTAG, &os)); ztest_zd_init(zdtmp, NULL, os); @@ -3623,7 +3626,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) * Verify that we cannot create an existing dataset. */ VERIFY3U(EEXIST, ==, - dmu_objset_create(name, DMU_OST_OTHER, 0, NULL, NULL)); + dmu_objset_create(name, DMU_OST_OTHER, 0, NULL, NULL, NULL)); /* * Verify that we can hold an objset that is also owned. @@ -3635,7 +3638,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) * Verify that we cannot own an objset that is already owned. */ VERIFY3U(EBUSY, ==, - dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os2)); + dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE, FTAG, &os2)); zil_close(zilog); dmu_objset_disown(os, FTAG); @@ -3756,7 +3759,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id) fatal(0, "dmu_take_snapshot(%s) = %d", snap1name, error); } - error = dmu_objset_clone(clone1name, snap1name); + error = dmu_objset_clone(clone1name, snap1name, NULL); if (error) { if (error == ENOSPC) { ztest_record_enospc(FTAG); @@ -3783,7 +3786,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id) fatal(0, "dmu_open_snapshot(%s) = %d", snap3name, error); } - error = dmu_objset_clone(clone2name, snap3name); + error = dmu_objset_clone(clone2name, snap3name, NULL); if (error) { if (error == ENOSPC) { ztest_record_enospc(FTAG); @@ -3792,7 +3795,8 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id) fatal(0, "dmu_objset_create(%s) = %d", clone2name, error); } - error = dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, FTAG, &os); + error = dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, B_TRUE, + FTAG, &os); if (error) fatal(0, "dmu_objset_own(%s) = %d", snap2name, error); error = dsl_dataset_promote(clone2name, NULL); @@ -5122,7 +5126,7 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id) fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error); } - error = dmu_objset_clone(clonename, fullname); + error = dmu_objset_clone(clonename, fullname, NULL); if (error) { if (error == ENOSPC) { ztest_record_enospc("dmu_objset_clone"); @@ -6127,7 +6131,7 @@ ztest_dataset_open(int d) } ASSERT(error == 0 || error == EEXIST); - VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, zd, &os)); + VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE, zd, &os)); (void) rw_unlock(&ztest_name_lock); ztest_zd_init(zd, ZTEST_GET_SHARED_DS(d), os); @@ -6220,7 +6224,7 @@ ztest_run(ztest_shared_t *zs) ztest_spa = spa; VERIFY0(dmu_objset_own(ztest_opts.zo_pool, - DMU_OST_ANY, B_TRUE, FTAG, &os)); + DMU_OST_ANY, B_TRUE, B_TRUE, FTAG, &os)); zs->zs_guid = dmu_objset_fsid_guid(os); dmu_objset_disown(os, FTAG); @@ -6531,7 +6535,8 @@ ztest_init(ztest_shared_t *zs) VERIFY3U(0, ==, nvlist_add_uint64(props, buf, 0)); free(buf); } - VERIFY3U(0, ==, spa_create(ztest_opts.zo_pool, nvroot, props, NULL)); + VERIFY3U(0, ==, + spa_create(ztest_opts.zo_pool, nvroot, props, NULL, NULL)); nvlist_free(nvroot); nvlist_free(props); diff --git a/configure.ac b/configure.ac index 20a67dc4f089..54d64e10ddd4 100644 --- a/configure.ac +++ b/configure.ac @@ -190,6 +190,7 @@ AC_CONFIG_FILES([ tests/zfs-tests/tests/functional/cli_root/zfs_destroy/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_get/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_inherit/Makefile + tests/zfs-tests/tests/functional/cli_root/zfs_key/Makefile tests/zfs-tests/tests/functional/cli_root/zfs/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile tests/zfs-tests/tests/functional/cli_root/zfs_promote/Makefile diff --git a/include/libuutil.h b/include/libuutil.h index 667542446672..6c132fe5736b 100644 --- a/include/libuutil.h +++ b/include/libuutil.h @@ -242,7 +242,7 @@ void uu_list_pool_destroy(uu_list_pool_t *); * usage: * * foo_t *a; - * a = malloc(sizeof(*a)); + * a = malloc(sizeof (*a)); * uu_list_node_init(a, &a->foo_list, pool); * ... * uu_list_node_fini(a, &a->foo_list, pool); @@ -345,7 +345,7 @@ void uu_avl_pool_destroy(uu_avl_pool_t *); * usage: * * foo_t *a; - * a = malloc(sizeof(*a)); + * a = malloc(sizeof (*a)); * uu_avl_node_init(a, &a->foo_avl, pool); * ... * uu_avl_node_fini(a, &a->foo_avl, pool); diff --git a/include/libzfs.h b/include/libzfs.h index d74bf6b10f72..84efd730c5ef 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -146,6 +146,7 @@ typedef enum zfs_error { EZFS_DIFF, /* general failure of zfs diff */ EZFS_DIFFDATA, /* bad zfs diff data */ EZFS_POOLREADONLY, /* pool is in read-only mode */ + EZFS_CRYPTOFAILED, /* failed to setup encryption */ EZFS_UNKNOWN } zfs_error_t; @@ -462,8 +463,8 @@ extern uint64_t zfs_prop_default_numeric(zfs_prop_t); extern const char *zfs_prop_column_name(zfs_prop_t); extern boolean_t zfs_prop_align_right(zfs_prop_t); -extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t, - nvlist_t *, uint64_t, zfs_handle_t *, zpool_handle_t *, const char *); +extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t, nvlist_t *, + uint64_t, zfs_handle_t *, zpool_handle_t *, boolean_t, const char *); extern const char *zfs_prop_to_name(zfs_prop_t); extern int zfs_prop_set(zfs_handle_t *, const char *, const char *); @@ -493,6 +494,17 @@ extern nvlist_t *zfs_get_user_props(zfs_handle_t *); extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *); extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *); +/* + * zfs encryption management + */ +extern int zfs_crypto_create(libzfs_handle_t *, char *, nvlist_t *, nvlist_t *, + nvlist_t **); +extern int zfs_crypto_clone(libzfs_handle_t *, zfs_handle_t *, char *, + nvlist_t *, nvlist_t **); +extern int zfs_crypto_load_key(zfs_handle_t *); +extern int zfs_crypto_unload_key(zfs_handle_t *); +extern int zfs_crypto_rewrap(zfs_handle_t *, nvlist_t *); + typedef struct zprop_list { int pl_prop; char *pl_user_prop; @@ -818,7 +830,8 @@ int zfs_smb_acl_rename(libzfs_handle_t *, char *, char *, char *, char *); * Enable and disable datasets within a pool by mounting/unmounting and * sharing/unsharing them. */ -extern int zpool_enable_datasets(zpool_handle_t *, const char *, int); +extern int zpool_enable_datasets(zpool_handle_t *, const char *, int, + boolean_t); extern int zpool_disable_datasets(zpool_handle_t *, boolean_t); /* diff --git a/include/libzfs_core.h b/include/libzfs_core.h index bc0f115bbfdf..3422e3d03b4b 100644 --- a/include/libzfs_core.h +++ b/include/libzfs_core.h @@ -39,12 +39,13 @@ int libzfs_core_init(void); void libzfs_core_fini(void); int lzc_snapshot(nvlist_t *, nvlist_t *, nvlist_t **); -int lzc_create(const char *, dmu_objset_type_t, nvlist_t *); -int lzc_clone(const char *, const char *, nvlist_t *); +int lzc_create(const char *, dmu_objset_type_t, nvlist_t *, nvlist_t *); +int lzc_clone(const char *, const char *, nvlist_t *, nvlist_t *); int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **); int lzc_bookmark(nvlist_t *, nvlist_t **); int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **); int lzc_destroy_bookmarks(nvlist_t *, nvlist_t **); +int lzc_key(const char *, uint64_t, nvlist_t *, nvlist_t *); int lzc_snaprange_space(const char *, const char *, uint64_t *); diff --git a/include/sys/Makefile.am b/include/sys/Makefile.am index 956643801c66..81ece208e106 100644 --- a/include/sys/Makefile.am +++ b/include/sys/Makefile.am @@ -27,6 +27,7 @@ COMMON_H = \ $(top_srcdir)/include/sys/dsl_deleg.h \ $(top_srcdir)/include/sys/dsl_destroy.h \ $(top_srcdir)/include/sys/dsl_dir.h \ + $(top_srcdir)/include/sys/dsl_crypt.h \ $(top_srcdir)/include/sys/dsl_pool.h \ $(top_srcdir)/include/sys/dsl_prop.h \ $(top_srcdir)/include/sys/dsl_scan.h \ @@ -108,6 +109,7 @@ COMMON_H = \ $(top_srcdir)/include/sys/zil_impl.h \ $(top_srcdir)/include/sys/zio_checksum.h \ $(top_srcdir)/include/sys/zio_compress.h \ + $(top_srcdir)/include/sys/zio_crypt.h \ $(top_srcdir)/include/sys/zio.h \ $(top_srcdir)/include/sys/zio_impl.h \ $(top_srcdir)/include/sys/zio_priority.h \ diff --git a/include/sys/arc.h b/include/sys/arc.h index e1422d2e1051..d15996d49b8b 100644 --- a/include/sys/arc.h +++ b/include/sys/arc.h @@ -60,15 +60,24 @@ _NOTE(CONSTCOND) } while (0) typedef struct arc_buf_hdr arc_buf_hdr_t; typedef struct arc_buf arc_buf_t; typedef struct arc_prune arc_prune_t; -typedef void arc_done_func_t(zio_t *zio, arc_buf_t *buf, void *private); + +/* + * With the advent of encrypted data in the ARC it is now possible for + * legitimate errors to arise while transforming data into its desired format. + * As a result the "error" parameter of arc_read_done_func_t is used to indicate + * transform errors even if there is no assoicated zio. + */ +typedef void arc_read_done_func_t(zio_t *zio, int error, arc_buf_t *buf, + void *private); +typedef void arc_write_done_func_t(zio_t *zio, arc_buf_t *buf, void *private); typedef void arc_prune_func_t(int64_t bytes, void *private); /* Shared module parameters */ extern int zfs_arc_average_blocksize; /* generic arc_done_func_t's which you can use */ -arc_done_func_t arc_bcopy_func; -arc_done_func_t arc_getbuf_func; +arc_read_done_func_t arc_bcopy_func; +arc_read_done_func_t arc_getbuf_func; extern int zfs_arc_num_sublists_per_state; @@ -112,20 +121,21 @@ typedef enum arc_flags ARC_FLAG_L2_WRITING = 1 << 11, /* write in progress */ ARC_FLAG_L2_EVICTED = 1 << 12, /* evicted during I/O */ ARC_FLAG_L2_WRITE_HEAD = 1 << 13, /* head of write list */ + ARC_FLAG_ENCRYPT = 1 << 14, /* encrypted on disk */ /* indicates that the buffer contains metadata (otherwise, data) */ - ARC_FLAG_BUFC_METADATA = 1 << 14, + ARC_FLAG_BUFC_METADATA = 1 << 15, /* Flags specifying whether optional hdr struct fields are defined */ - ARC_FLAG_HAS_L1HDR = 1 << 15, - ARC_FLAG_HAS_L2HDR = 1 << 16, + ARC_FLAG_HAS_L1HDR = 1 << 16, + ARC_FLAG_HAS_L2HDR = 1 << 17, /* * Indicates the arc_buf_hdr_t's b_pdata matches the on-disk data. * This allows the l2arc to use the blkptr's checksum to verify * the data without having to store the checksum in the hdr. */ - ARC_FLAG_COMPRESSED_ARC = 1 << 17, - ARC_FLAG_SHARED_DATA = 1 << 18, + ARC_FLAG_COMPRESSED_ARC = 1 << 18, + ARC_FLAG_SHARED_DATA = 1 << 19, /* * The arc buffer's compression mode is stored in the top 7 bits of the @@ -144,7 +154,8 @@ typedef enum arc_flags typedef enum arc_buf_flags { ARC_BUF_FLAG_SHARED = 1 << 0, - ARC_BUF_FLAG_COMPRESSED = 1 << 1 + ARC_BUF_FLAG_COMPRESSED = 1 << 1, + ARC_BUF_FLAG_ENCRYPTED = 1 << 2 } arc_buf_flags_t; struct arc_buf { @@ -208,8 +219,10 @@ typedef struct arc_buf_info { void arc_space_consume(uint64_t space, arc_space_type_t type); void arc_space_return(uint64_t space, arc_space_type_t type); boolean_t arc_is_metadata(arc_buf_t *buf); +boolean_t arc_is_encrypted(arc_buf_t *buf); enum zio_compress arc_get_compression(arc_buf_t *buf); -int arc_decompress(arc_buf_t *buf); +int arc_untransform(arc_buf_t *buf, spa_t *spa, uint64_t dsobj, + boolean_t in_place); arc_buf_t *arc_alloc_buf(spa_t *spa, void *tag, arc_buf_contents_t type, int32_t size); arc_buf_t *arc_alloc_compressed_buf(spa_t *spa, void *tag, @@ -233,12 +246,12 @@ int arc_referenced(arc_buf_t *buf); #endif int arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, - arc_done_func_t *done, void *private, zio_priority_t priority, int flags, - arc_flags_t *arc_flags, const zbookmark_phys_t *zb); + arc_read_done_func_t *done, void *private, zio_priority_t priority, + int flags, arc_flags_t *arc_flags, const zbookmark_phys_t *zb); zio_t *arc_write(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp, arc_buf_t *buf, boolean_t l2arc, const zio_prop_t *zp, - arc_done_func_t *ready, arc_done_func_t *child_ready, - arc_done_func_t *physdone, arc_done_func_t *done, + arc_write_done_func_t *ready, arc_write_done_func_t *child_ready, + arc_write_done_func_t *physdone, arc_write_done_func_t *done, void *private, zio_priority_t priority, int zio_flags, const zbookmark_phys_t *zb); diff --git a/include/sys/arc_impl.h b/include/sys/arc_impl.h index 9aca8e1d248e..4bea583425aa 100644 --- a/include/sys/arc_impl.h +++ b/include/sys/arc_impl.h @@ -29,6 +29,7 @@ #define _SYS_ARC_IMPL_H #include +#include #ifdef __cplusplus extern "C" { @@ -90,8 +91,9 @@ typedef struct arc_callback arc_callback_t; struct arc_callback { void *acb_private; - arc_done_func_t *acb_done; + arc_read_done_func_t *acb_done; arc_buf_t *acb_buf; + boolean_t acb_encrypted; boolean_t acb_compressed; zio_t *acb_zio_dummy; arc_callback_t *acb_next; @@ -100,12 +102,12 @@ struct arc_callback { typedef struct arc_write_callback arc_write_callback_t; struct arc_write_callback { - void *awcb_private; - arc_done_func_t *awcb_ready; - arc_done_func_t *awcb_children_ready; - arc_done_func_t *awcb_physdone; - arc_done_func_t *awcb_done; - arc_buf_t *awcb_buf; + void *awcb_private; + arc_write_done_func_t *awcb_ready; + arc_write_done_func_t *awcb_children_ready; + arc_write_done_func_t *awcb_physdone; + arc_write_done_func_t *awcb_done; + arc_buf_t *awcb_buf; }; /* @@ -169,6 +171,28 @@ typedef struct l1arc_buf_hdr { abd_t *b_pabd; } l1arc_buf_hdr_t; +/* + * Encrypted blocks will need to be stored encrypted on the L2ARC + * disk as they appear in the main pool. In order for this to work we + * need to pass around the encryption parameters so they can be used + * to write data to the L2ARC. This struct is only defined in the + * arc_buf_hdr_t if the L1 header is defined and the has the + * ARC_FLAG_ENCRYPT flag set. + */ +typedef struct arc_buf_hdr_crypt { + abd_t *b_rabd; /* raw encrypted data */ + dmu_object_type_t b_ot; /* object type */ + uint32_t b_ebufcnt; /* count of encrypted buffers */ + + /* dsobj for looking up encryption key for l2arc encryption */ + uint64_t b_dsobj; + + /* encryption parameters */ + uint8_t b_salt[DATA_SALT_LEN]; + uint8_t b_iv[DATA_IV_LEN]; + uint8_t b_mac[DATA_MAC_LEN]; +} arc_buf_hdr_crypt_t; + typedef struct l2arc_dev { vdev_t *l2ad_vdev; /* vdev */ spa_t *l2ad_spa; /* spa */ @@ -237,6 +261,11 @@ struct arc_buf_hdr { l2arc_buf_hdr_t b_l2hdr; /* L1ARC fields. Undefined when in l2arc_only state */ l1arc_buf_hdr_t b_l1hdr; + /* + * Encryption parameters. Defined only when ARC_FLAG_ENCRYPT + * is set and the L1 header exists. + */ + arc_buf_hdr_crypt_t b_crypt_hdr; }; #ifdef __cplusplus } diff --git a/include/sys/dbuf.h b/include/sys/dbuf.h index 6262f012e7ab..77c715242dfd 100644 --- a/include/sys/dbuf.h +++ b/include/sys/dbuf.h @@ -54,6 +54,7 @@ extern "C" { #define DB_RF_NOPREFETCH (1 << 3) #define DB_RF_NEVERWAIT (1 << 4) #define DB_RF_CACHED (1 << 5) +#define DB_RF_NO_DECRYPT (1 << 6) /* * The simplified state transition diagram for dbufs looks like: diff --git a/include/sys/ddt.h b/include/sys/ddt.h index 667795f967f7..75a51f43f5cb 100644 --- a/include/sys/ddt.h +++ b/include/sys/ddt.h @@ -69,7 +69,7 @@ typedef struct ddt_key { /* * Encoded with logical & physical size, and compression, as follows: * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 0 | 0 | 0 | comp | PSIZE | LSIZE | + * | 0 | 0 | 0 |E| comp| PSIZE | LSIZE | * +-------+-------+-------+-------+-------+-------+-------+-------+ */ uint64_t ddk_prop; @@ -85,11 +85,17 @@ typedef struct ddt_key { #define DDK_SET_PSIZE(ddk, x) \ BF64_SET_SB((ddk)->ddk_prop, 16, 16, SPA_MINBLOCKSHIFT, 1, x) -#define DDK_GET_COMPRESS(ddk) BF64_GET((ddk)->ddk_prop, 32, 8) -#define DDK_SET_COMPRESS(ddk, x) BF64_SET((ddk)->ddk_prop, 32, 8, x) +#define DDK_GET_COMPRESS(ddk) BF64_GET((ddk)->ddk_prop, 32, 7) +#define DDK_SET_COMPRESS(ddk, x) BF64_SET((ddk)->ddk_prop, 32, 7, x) + +#define DDK_GET_ENCRYPTED(ddk) BF64_GET((ddk)->ddk_prop, 39, 1) +#define DDK_SET_ENCRYPTED(ddk, x) BF64_SET((ddk)->ddk_prop, 39, 1, x) #define DDT_KEY_WORDS (sizeof (ddt_key_t) / sizeof (uint64_t)) +#define DDE_GET_NDVAS(dde) (DDK_GET_ENCRYPTED(&dde->dde_key) \ + ? SPA_DVAS_PER_BP : SPA_DVAS_PER_BP - 1) + typedef struct ddt_phys { dva_t ddp_dva[SPA_DVAS_PER_BP]; uint64_t ddp_refcnt; diff --git a/include/sys/dmu.h b/include/sys/dmu.h index 9d61d94ee02b..7ecba73bdb0f 100644 --- a/include/sys/dmu.h +++ b/include/sys/dmu.h @@ -71,6 +71,7 @@ struct nvlist; struct arc_buf; struct zio_prop; struct sa_handle; +struct dsl_crypto_params; typedef struct objset objset_t; typedef struct dmu_tx dmu_tx_t; @@ -100,16 +101,18 @@ typedef enum dmu_object_byteswap { #define DMU_OT_NEWTYPE 0x80 #define DMU_OT_METADATA 0x40 -#define DMU_OT_BYTESWAP_MASK 0x3f +#define DMU_OT_ENCRYPTED 0x20 +#define DMU_OT_BYTESWAP_MASK 0x1f /* * Defines a uint8_t object type. Object types specify if the data * in the object is metadata (boolean) and how to byteswap the data * (dmu_object_byteswap_t). */ -#define DMU_OT(byteswap, metadata) \ +#define DMU_OT(byteswap, metadata, encrypted) \ (DMU_OT_NEWTYPE | \ ((metadata) ? DMU_OT_METADATA : 0) | \ + ((encrypted) ? DMU_OT_ENCRYPTED : 0) | \ ((byteswap) & DMU_OT_BYTESWAP_MASK)) #define DMU_OT_IS_VALID(ot) (((ot) & DMU_OT_NEWTYPE) ? \ @@ -120,6 +123,10 @@ typedef enum dmu_object_byteswap { ((ot) & DMU_OT_METADATA) : \ dmu_ot[(int)(ot)].ot_metadata) +#define DMU_OT_IS_ENCRYPTED(ot) (((ot) & DMU_OT_NEWTYPE) ? \ + ((ot) & DMU_OT_ENCRYPTED) : \ + dmu_ot[(int)(ot)].ot_encrypt) + /* * These object types use bp_fill != 1 for their L0 bp's. Therefore they can't * have their data embedded (i.e. use a BP_IS_EMBEDDED() bp), because bp_fill @@ -215,16 +222,27 @@ typedef enum dmu_object_type { /* * Names for valid types declared with DMU_OT(). */ - DMU_OTN_UINT8_DATA = DMU_OT(DMU_BSWAP_UINT8, B_FALSE), - DMU_OTN_UINT8_METADATA = DMU_OT(DMU_BSWAP_UINT8, B_TRUE), - DMU_OTN_UINT16_DATA = DMU_OT(DMU_BSWAP_UINT16, B_FALSE), - DMU_OTN_UINT16_METADATA = DMU_OT(DMU_BSWAP_UINT16, B_TRUE), - DMU_OTN_UINT32_DATA = DMU_OT(DMU_BSWAP_UINT32, B_FALSE), - DMU_OTN_UINT32_METADATA = DMU_OT(DMU_BSWAP_UINT32, B_TRUE), - DMU_OTN_UINT64_DATA = DMU_OT(DMU_BSWAP_UINT64, B_FALSE), - DMU_OTN_UINT64_METADATA = DMU_OT(DMU_BSWAP_UINT64, B_TRUE), - DMU_OTN_ZAP_DATA = DMU_OT(DMU_BSWAP_ZAP, B_FALSE), - DMU_OTN_ZAP_METADATA = DMU_OT(DMU_BSWAP_ZAP, B_TRUE), + DMU_OTN_UINT8_DATA = DMU_OT(DMU_BSWAP_UINT8, B_FALSE, B_FALSE), + DMU_OTN_UINT8_METADATA = DMU_OT(DMU_BSWAP_UINT8, B_TRUE, B_FALSE), + DMU_OTN_UINT16_DATA = DMU_OT(DMU_BSWAP_UINT16, B_FALSE, B_FALSE), + DMU_OTN_UINT16_METADATA = DMU_OT(DMU_BSWAP_UINT16, B_TRUE, B_FALSE), + DMU_OTN_UINT32_DATA = DMU_OT(DMU_BSWAP_UINT32, B_FALSE, B_FALSE), + DMU_OTN_UINT32_METADATA = DMU_OT(DMU_BSWAP_UINT32, B_TRUE, B_FALSE), + DMU_OTN_UINT64_DATA = DMU_OT(DMU_BSWAP_UINT64, B_FALSE, B_FALSE), + DMU_OTN_UINT64_METADATA = DMU_OT(DMU_BSWAP_UINT64, B_TRUE, B_FALSE), + DMU_OTN_ZAP_DATA = DMU_OT(DMU_BSWAP_ZAP, B_FALSE, B_FALSE), + DMU_OTN_ZAP_METADATA = DMU_OT(DMU_BSWAP_ZAP, B_TRUE, B_FALSE), + + DMU_OTN_UINT8_ENC_DATA = DMU_OT(DMU_BSWAP_UINT8, B_FALSE, B_TRUE), + DMU_OTN_UINT8_ENC_METADATA = DMU_OT(DMU_BSWAP_UINT8, B_TRUE, B_TRUE), + DMU_OTN_UINT16_ENC_DATA = DMU_OT(DMU_BSWAP_UINT16, B_FALSE, B_TRUE), + DMU_OTN_UINT16_ENC_METADATA = DMU_OT(DMU_BSWAP_UINT16, B_TRUE, B_TRUE), + DMU_OTN_UINT32_ENC_DATA = DMU_OT(DMU_BSWAP_UINT32, B_FALSE, B_TRUE), + DMU_OTN_UINT32_ENC_METADATA = DMU_OT(DMU_BSWAP_UINT32, B_TRUE, B_TRUE), + DMU_OTN_UINT64_ENC_DATA = DMU_OT(DMU_BSWAP_UINT64, B_FALSE, B_TRUE), + DMU_OTN_UINT64_ENC_METADATA = DMU_OT(DMU_BSWAP_UINT64, B_TRUE, B_TRUE), + DMU_OTN_ZAP_ENC_DATA = DMU_OT(DMU_BSWAP_ZAP, B_FALSE, B_TRUE), + DMU_OTN_ZAP_ENC_METADATA = DMU_OT(DMU_BSWAP_ZAP, B_TRUE, B_TRUE), } dmu_object_type_t; typedef enum txg_how { @@ -267,20 +285,23 @@ void zfs_znode_byteswap(void *buf, size_t size); */ #define DMU_BONUS_BLKID (-1ULL) #define DMU_SPILL_BLKID (-2ULL) + /* * Public routines to create, destroy, open, and close objsets. */ int dmu_objset_hold(const char *name, void *tag, objset_t **osp); int dmu_objset_own(const char *name, dmu_objset_type_t type, - boolean_t readonly, void *tag, objset_t **osp); + boolean_t readonly, boolean_t key_required, void *tag, objset_t **osp); void dmu_objset_rele(objset_t *os, void *tag); void dmu_objset_disown(objset_t *os, void *tag); int dmu_objset_open_ds(struct dsl_dataset *ds, objset_t **osp); void dmu_objset_evict_dbufs(objset_t *os); int dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags, - void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg); -int dmu_objset_clone(const char *name, const char *origin); + struct dsl_crypto_params *dcp, void (*func)(objset_t *os, void *arg, + cred_t *cr, dmu_tx_t *tx), void *arg); +int dmu_objset_clone(const char *name, const char *origin, + struct dsl_crypto_params *dcp); int dsl_destroy_snapshots_nvl(struct nvlist *snaps, boolean_t defer, struct nvlist *errlist); int dmu_objset_snapshot_one(const char *fsname, const char *snapname); @@ -431,7 +452,11 @@ dmu_write_embedded(objset_t *os, uint64_t object, uint64_t offset, #define WP_SPILL 0x4 void dmu_write_policy(objset_t *os, struct dnode *dn, int level, int wp, - enum zio_compress compress_override, struct zio_prop *zp); + struct zio_prop *zp); +void dmu_write_policy_override_compress(struct zio_prop *zp, + enum zio_compress compress); +void dmu_write_policy_override_encrypt(struct zio_prop *zp, + enum zio_compress compress, uint8_t *salt, uint8_t *iv, uint8_t *mac); /* * The bonus data is accessed more or less like a regular buffer. * You must dmu_bonus_hold() to get the buffer, which will give you a @@ -788,6 +813,7 @@ typedef void (*const arc_byteswap_func_t)(void *buf, size_t size); typedef struct dmu_object_type_info { dmu_object_byteswap_t ot_byteswap; boolean_t ot_metadata; + boolean_t ot_encrypt; char *ot_name; } dmu_object_type_info_t; diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h index ed3cbf498909..4253efa7fde3 100644 --- a/include/sys/dmu_objset.h +++ b/include/sys/dmu_objset.h @@ -96,6 +96,7 @@ struct objset { enum zio_compress os_compress; uint8_t os_copies; enum zio_checksum os_dedup_checksum; + boolean_t os_encrypted; boolean_t os_dedup_verify; zfs_logbias_op_t os_logbias; zfs_cache_type_t os_primary_cache; @@ -151,9 +152,10 @@ struct objset { /* called from zpl */ int dmu_objset_hold(const char *name, void *tag, objset_t **osp); int dmu_objset_own(const char *name, dmu_objset_type_t type, - boolean_t readonly, void *tag, objset_t **osp); + boolean_t readonly, boolean_t key_required, void *tag, objset_t **osp); int dmu_objset_own_obj(struct dsl_pool *dp, uint64_t obj, - dmu_objset_type_t type, boolean_t readonly, void *tag, objset_t **osp); + dmu_objset_type_t type, boolean_t readonly, boolean_t key_required, + void *tag, objset_t **osp); void dmu_objset_refresh_ownership(objset_t *os, void *tag); void dmu_objset_rele(objset_t *os, void *tag); void dmu_objset_disown(objset_t *os, void *tag); diff --git a/include/sys/dmu_traverse.h b/include/sys/dmu_traverse.h index c010edd440d9..7dc07f6bba6e 100644 --- a/include/sys/dmu_traverse.h +++ b/include/sys/dmu_traverse.h @@ -48,6 +48,7 @@ typedef int (blkptr_cb_t)(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, #define TRAVERSE_PREFETCH_DATA (1<<3) #define TRAVERSE_PREFETCH (TRAVERSE_PREFETCH_METADATA | TRAVERSE_PREFETCH_DATA) #define TRAVERSE_HARD (1<<4) +#define TRAVERSE_NO_DECRYPT (1<<5) /* Special traverse error return value to indicate skipping of children */ #define TRAVERSE_VISIT_NO_CHILDREN -1 diff --git a/include/sys/dsl_crypt.h b/include/sys/dsl_crypt.h new file mode 100644 index 000000000000..6c2d92a28933 --- /dev/null +++ b/include/sys/dsl_crypt.h @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Datto, Inc. All rights reserved. + */ + +#ifndef _SYS_DSL_CRYPT_H +#define _SYS_DSL_CRYPT_H + +#include +#include +#include +#include +#include + +/* forward declarations */ +struct dsl_dataset; + +typedef enum zfs_keystatus { + ZFS_KEYSTATUS_NONE = 0, + ZFS_KEYSTATUS_UNAVAILABLE, + ZFS_KEYSTATUS_AVAILABLE, +} zfs_keystatus_t; + +/* in memory representation of a wrapping key */ +typedef struct dsl_wrapping_key { + /* link into the keystore's tree of wrapping keys */ + avl_node_t wk_avl_link; + + /* actual wrapping key */ + crypto_key_t wk_key; + + /* refcount of number of dsl_crypto_key_t's holding this struct */ + refcount_t wk_refcnt; + + /* dsl directory object that owns this wrapping key */ + uint64_t wk_ddobj; +} dsl_wrapping_key_t; + +/* structure for passing around encryption params from userspace */ +typedef struct dsl_crypto_params { + /* command to be executed */ + zfs_ioc_crypto_cmd_t cp_cmd; + + /* the encryption algorithm */ + uint64_t cp_crypt; + + /* the salt, if the keysource is of type passphrase */ + uint64_t cp_salt; + + /* the pbkdf2 iterations, if the keysource is of type passphrase */ + uint64_t cp_iters; + + /* keysource property string */ + const char *cp_keysource; + + /* the wrapping key */ + dsl_wrapping_key_t *cp_wkey; +} dsl_crypto_params_t; + +/* in-memory representation of an encryption key for a dataset */ +typedef struct dsl_crypto_key { + /* avl node for linking into the keystore */ + avl_node_t dck_avl_link; + + /* refcount of dsl_key_mapping_t's holding this key */ + refcount_t dck_refcnt; + + /* master key used to derive encryption keys */ + zio_crypt_key_t dck_key; + + /* wrapping key for syncing this structure to disk */ + dsl_wrapping_key_t *dck_wkey; + + /* on-disk object id */ + uint64_t dck_obj; +} dsl_crypto_key_t; + +/* + * In memory mapping of a dataset to a DSL Crypto Key. This is used + * to look up the corresponding dsl_crypto_key_t from the zio layer + * for performing data encryption and decryption. + */ +typedef struct dsl_key_mapping { + /* avl node for linking into the keystore */ + avl_node_t km_avl_link; + + /* refcount of how many users are depending on this mapping */ + refcount_t km_refcnt; + + /* dataset this crypto key belongs to (index) */ + uint64_t km_dsobj; + + /* crypto key (value) of this record */ + dsl_crypto_key_t *km_key; +} dsl_key_mapping_t; + +/* in memory structure for holding all wrapping and dsl keys */ +typedef struct spa_keystore { + /* lock for protecting sk_dsl_keys */ + krwlock_t sk_dk_lock; + + /* tree of all dsl_crypto_key_t's */ + avl_tree_t sk_dsl_keys; + + /* lock for protecting sk_key_mappings */ + krwlock_t sk_km_lock; + + /* tree of all dsl_key_mapping_t's, indexed by dsobj */ + avl_tree_t sk_key_mappings; + + /* lock for protecting the wrapping keys tree */ + krwlock_t sk_wkeys_lock; + + /* tree of all wrapping keys, indexed by ddobj */ + avl_tree_t sk_wkeys; +} spa_keystore_t; + +void dsl_wrapping_key_hold(dsl_wrapping_key_t *wkey, void *tag); +void dsl_wrapping_key_rele(dsl_wrapping_key_t *wkey, void *tag); +void dsl_wrapping_key_free(dsl_wrapping_key_t *wkey); +int dsl_wrapping_key_create(uint8_t *wkeydata, dsl_wrapping_key_t **wkey_out); + +int dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, + dsl_crypto_params_t **dcp_out); +void dsl_crypto_params_free(dsl_crypto_params_t *dcp, boolean_t unload); + +void spa_keystore_init(spa_keystore_t *sk); +void spa_keystore_fini(spa_keystore_t *sk); +zfs_keystatus_t dsl_dataset_keystore_keystatus(struct dsl_dataset *ds); + +int spa_keystore_wkey_hold_ddobj(spa_t *spa, uint64_t ddobj, void *tag, + dsl_wrapping_key_t **wkey_out); +int spa_keystore_dsl_key_hold_dd(spa_t *spa, dsl_dir_t *dd, void *tag, + dsl_crypto_key_t **dck_out); +void spa_keystore_dsl_key_rele(spa_t *spa, dsl_crypto_key_t *dck, void *tag); +int spa_keystore_load_wkey_impl(spa_t *spa, dsl_wrapping_key_t *wkey); +int spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp); +int spa_keystore_unload_wkey_impl(spa_t *spa, uint64_t ddobj); +int spa_keystore_unload_wkey(const char *dsname); +int spa_keystore_create_mapping(spa_t *spa, struct dsl_dataset *ds, void *tag); +int spa_keystore_remove_mapping(spa_t *spa, struct dsl_dataset *ds, void *tag); +int spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag, + dsl_crypto_key_t **dck_out); + +int spa_keystore_rewrap(const char *dsname, dsl_crypto_params_t *dcp); +int dmu_objset_create_encryption_check(dsl_dir_t *pdd, + dsl_crypto_params_t *dcp); +int dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, + dsl_crypto_params_t *dcp); +uint64_t dsl_crypto_key_create_sync(uint64_t crypt, dsl_wrapping_key_t *wkey, + dmu_tx_t *tx); +uint64_t dsl_crypto_key_clone_sync(dsl_dir_t *orig_dd, + dsl_wrapping_key_t *wkey, dmu_tx_t *tx); +void dsl_crypto_key_destroy_sync(uint64_t dckobj, dmu_tx_t *tx); + +int spa_crypt_get_salt(spa_t *spa, uint64_t dsobj, uint8_t *salt); +int spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, + blkptr_t *bp, uint64_t txgid, uint_t datalen, abd_t *pabd, abd_t *cabd, + uint8_t *iv, uint8_t *mac, uint8_t *salt); + +#endif diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index a0ef3bc9cae4..37835f3b1a37 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #ifdef __cplusplus @@ -47,6 +48,7 @@ extern "C" { struct dsl_dataset; struct dsl_dir; struct dsl_pool; +struct dsl_crypto_params; #define DS_FLAG_INCONSISTENT (1ULL<<0) #define DS_IS_INCONSISTENT(ds) \ @@ -250,19 +252,25 @@ boolean_t dsl_dataset_try_add_ref(struct dsl_pool *dp, dsl_dataset_t *ds, int dsl_dataset_hold_obj(struct dsl_pool *dp, uint64_t dsobj, void *tag, dsl_dataset_t **); void dsl_dataset_rele(dsl_dataset_t *ds, void *tag); -int dsl_dataset_own(struct dsl_pool *dp, const char *name, +int dsl_dataset_hold_crypt(struct dsl_pool *dp, const char *name, void *tag, + dsl_dataset_t **dsp); +int dsl_dataset_hold_crypt_obj(struct dsl_pool *dp, uint64_t dsobj, void *tag, dsl_dataset_t **dsp); +void dsl_dataset_rele_crypt(dsl_dataset_t *ds, void *tag); +int dsl_dataset_own(struct dsl_pool *dp, const char *name, + void *tag, boolean_t key_required, dsl_dataset_t **dsp); int dsl_dataset_own_obj(struct dsl_pool *dp, uint64_t dsobj, - void *tag, dsl_dataset_t **dsp); + void *tag, boolean_t key_required, dsl_dataset_t **dsp); void dsl_dataset_disown(dsl_dataset_t *ds, void *tag); void dsl_dataset_name(dsl_dataset_t *ds, char *name); -boolean_t dsl_dataset_tryown(dsl_dataset_t *ds, void *tag); int dsl_dataset_namelen(dsl_dataset_t *ds); boolean_t dsl_dataset_has_owner(dsl_dataset_t *ds); +int dsl_dataset_tryown(dsl_dataset_t *ds, void *tag, boolean_t key_required); uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname, - dsl_dataset_t *origin, uint64_t flags, cred_t *, dmu_tx_t *); + dsl_dataset_t *origin, uint64_t flags, cred_t *, + struct dsl_crypto_params *, dmu_tx_t *); uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, - uint64_t flags, dmu_tx_t *tx); + struct dsl_crypto_params *dcp, uint64_t flags, dmu_tx_t *tx); int dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors); int dsl_dataset_promote(const char *name, char *conflsnap); int dsl_dataset_rename_snapshot(const char *fsname, diff --git a/include/sys/dsl_deleg.h b/include/sys/dsl_deleg.h index d399d1da973b..77b57295ec63 100644 --- a/include/sys/dsl_deleg.h +++ b/include/sys/dsl_deleg.h @@ -61,6 +61,8 @@ extern "C" { #define ZFS_DELEG_PERM_RELEASE "release" #define ZFS_DELEG_PERM_DIFF "diff" #define ZFS_DELEG_PERM_BOOKMARK "bookmark" +#define ZFS_DELEG_PERM_LOAD_KEY "keyuse" +#define ZFS_DELEG_PERM_CHANGE_KEY "keychange" /* * Note: the names of properties that are marked delegatable are also diff --git a/include/sys/dsl_dir.h b/include/sys/dsl_dir.h index fb299684c424..64610504d1c1 100644 --- a/include/sys/dsl_dir.h +++ b/include/sys/dsl_dir.h @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -47,6 +48,7 @@ struct dsl_dataset; #define DD_FIELD_FILESYSTEM_COUNT "com.joyent:filesystem_count" #define DD_FIELD_SNAPSHOT_COUNT "com.joyent:snapshot_count" +#define DD_FIELD_CRYPTO_KEY_OBJ "com.datto:dsl_crypto_obj" typedef enum dd_used { DD_USED_HEAD, @@ -89,6 +91,7 @@ struct dsl_dir { /* These are immutable; no lock needed: */ uint64_t dd_object; + uint64_t dd_crypto_obj; dsl_pool_t *dd_pool; /* Stable until user eviction; no lock needed: */ diff --git a/include/sys/dsl_pool.h b/include/sys/dsl_pool.h index 48b12e8eb134..28414cbecad4 100644 --- a/include/sys/dsl_pool.h +++ b/include/sys/dsl_pool.h @@ -50,6 +50,7 @@ struct dsl_dataset; struct dsl_pool; struct dmu_tx; struct dsl_scan; +struct dsl_crypto_params; extern unsigned long zfs_dirty_data_max; extern unsigned long zfs_dirty_data_max_max; @@ -138,7 +139,8 @@ typedef struct dsl_pool { int dsl_pool_init(spa_t *spa, uint64_t txg, dsl_pool_t **dpp); int dsl_pool_open(dsl_pool_t *dp); void dsl_pool_close(dsl_pool_t *dp); -dsl_pool_t *dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg); +dsl_pool_t *dsl_pool_create(spa_t *spa, nvlist_t *zplprops, + struct dsl_crypto_params *dcp, uint64_t txg); void dsl_pool_sync(dsl_pool_t *dp, uint64_t txg); void dsl_pool_sync_done(dsl_pool_t *dp, uint64_t txg); int dsl_pool_sync_context(dsl_pool_t *dp); diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 45b6d4981bca..fa2c5d16e4a0 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -163,6 +163,11 @@ typedef enum { ZFS_PROP_OVERLAY, ZFS_PROP_PREV_SNAP, ZFS_PROP_RECEIVE_RESUME_TOKEN, + ZFS_PROP_ENCRYPTION, + ZFS_PROP_SALT, + ZFS_PROP_PBKDF2_ITERS, + ZFS_PROP_KEYSOURCE, + ZFS_PROP_KEYSTATUS, ZFS_NUM_PROPS } zfs_prop_t; @@ -272,6 +277,7 @@ uint64_t zfs_prop_default_numeric(zfs_prop_t); boolean_t zfs_prop_readonly(zfs_prop_t); boolean_t zfs_prop_inheritable(zfs_prop_t); boolean_t zfs_prop_setonce(zfs_prop_t); +boolean_t zfs_prop_encryption_key_param(zfs_prop_t prop); const char *zfs_prop_to_name(zfs_prop_t); zfs_prop_t zfs_name_to_prop(const char *); boolean_t zfs_prop_user(const char *); @@ -1011,6 +1017,7 @@ typedef enum zfs_ioc { ZFS_IOC_GET_BOOKMARKS, ZFS_IOC_DESTROY_BOOKMARKS, ZFS_IOC_RECV_NEW, + ZFS_IOC_KEY, /* * Linux - 3/64 numbers reserved. @@ -1075,6 +1082,12 @@ typedef enum { #define ZPOOL_HIST_DSNAME "dsname" #define ZPOOL_HIST_DSID "dsid" +/* + * Special nvlist name that will not have its args recorded in the pool's + * history log. + */ +#define ZPOOL_HIDDEN_ARGS "hidden_args" + /* * Flags for ZFS_IOC_VDEV_SET_STATE */ @@ -1093,6 +1106,7 @@ typedef enum { #define ZFS_IMPORT_MISSING_LOG 0x4 #define ZFS_IMPORT_ONLY 0x8 #define ZFS_IMPORT_TEMP_NAME 0x10 +#define ZFS_IMPORT_LOAD_KEYS 0x20 /* * Sysevent payload members. ZFS will generate the following sysevents with the diff --git a/include/sys/spa.h b/include/sys/spa.h index 58520118e73b..94f76d47563c 100644 --- a/include/sys/spa.h +++ b/include/sys/spa.h @@ -60,6 +60,7 @@ typedef struct zbookmark_phys zbookmark_phys_t; struct dsl_pool; struct dsl_dataset; +struct dsl_crypto_params; /* * General-purpose 32-bit and 64-bit bitfield encodings. @@ -208,7 +209,7 @@ typedef struct zio_cksum_salt { * G gang block indicator * B byteorder (endianness) * D dedup - * X encryption (on version 30, which is not supported) + * X encryption * E blkptr_t contains embedded data (see below) * lvl level of indirection * type DMU object type @@ -254,7 +255,7 @@ typedef struct zio_cksum_salt { * payload contains the embedded data * B (byteorder) byteorder (endianness) * D (dedup) padding (set to zero) - * X encryption (set to zero; see above) + * X encryption (set to zero) * E (embedded) set to one * lvl indirection level * type DMU object type @@ -397,6 +398,9 @@ _NOTE(CONSTCOND) } while (0) #define BP_GET_LEVEL(bp) BF64_GET((bp)->blk_prop, 56, 5) #define BP_SET_LEVEL(bp, x) BF64_SET((bp)->blk_prop, 56, 5, x) +#define BP_IS_ENCRYPTED(bp) BF64_GET((bp)->blk_prop, 61, 1) +#define BP_SET_ENCRYPTED(bp, x) BF64_SET((bp)->blk_prop, 61, 1, x) + #define BP_GET_DEDUP(bp) BF64_GET((bp)->blk_prop, 62, 1) #define BP_SET_DEDUP(bp, x) BF64_SET((bp)->blk_prop, 62, 1, x) @@ -414,7 +418,20 @@ _NOTE(CONSTCOND) } while (0) (bp)->blk_phys_birth = ((logical) == (physical) ? 0 : (physical)); \ } -#define BP_GET_FILL(bp) (BP_IS_EMBEDDED(bp) ? 1 : (bp)->blk_fill) +#define BP_GET_FILL(bp) \ + ((BP_IS_ENCRYPTED(bp)) ? BF64_GET((bp)->blk_fill, 0, 32) : \ + ((BP_IS_EMBEDDED(bp)) ? 1 : (bp)->blk_fill)) + +#define BP_SET_FILL(bp, fill) \ +{ \ + if (BP_IS_ENCRYPTED(bp)) \ + BF64_SET((bp)->blk_fill, 0, 32, fill); \ + else \ + (bp)->blk_fill = fill; \ +} + +#define BP_GET_IV2(bp) BF64_GET((bp)->blk_fill, 32, 32) +#define BP_SET_IV2(bp, iv2) BF64_SET((bp)->blk_fill, 32, 32, iv2); #define BP_IS_METADATA(bp) \ (BP_GET_LEVEL(bp) > 0 || DMU_OT_IS_METADATA(BP_GET_TYPE(bp))) @@ -423,7 +440,7 @@ _NOTE(CONSTCOND) } while (0) (BP_IS_EMBEDDED(bp) ? 0 : \ DVA_GET_ASIZE(&(bp)->blk_dva[0]) + \ DVA_GET_ASIZE(&(bp)->blk_dva[1]) + \ - DVA_GET_ASIZE(&(bp)->blk_dva[2])) + (DVA_GET_ASIZE(&(bp)->blk_dva[2]) * !BP_IS_ENCRYPTED(bp))) #define BP_GET_UCSIZE(bp) \ (BP_IS_METADATA(bp) ? BP_GET_PSIZE(bp) : BP_GET_LSIZE(bp)) @@ -432,13 +449,13 @@ _NOTE(CONSTCOND) } while (0) (BP_IS_EMBEDDED(bp) ? 0 : \ !!DVA_GET_ASIZE(&(bp)->blk_dva[0]) + \ !!DVA_GET_ASIZE(&(bp)->blk_dva[1]) + \ - !!DVA_GET_ASIZE(&(bp)->blk_dva[2])) + (!!DVA_GET_ASIZE(&(bp)->blk_dva[2]) * !BP_IS_ENCRYPTED(bp))) #define BP_COUNT_GANG(bp) \ (BP_IS_EMBEDDED(bp) ? 0 : \ (DVA_GET_GANG(&(bp)->blk_dva[0]) + \ DVA_GET_GANG(&(bp)->blk_dva[1]) + \ - DVA_GET_GANG(&(bp)->blk_dva[2]))) + (DVA_GET_GANG(&(bp)->blk_dva[2]) * !BP_IS_ENCRYPTED(bp)))) #define DVA_EQUAL(dva1, dva2) \ ((dva1)->dva_word[1] == (dva2)->dva_word[1] && \ @@ -544,13 +561,14 @@ _NOTE(CONSTCOND) } while (0) DVA_GET_ASIZE(&bp->blk_dva[1]) / 2) \ copies--; \ len += func(buf + len, size - len, \ - "[L%llu %s] %s %s %s %s %s %s%c" \ + "[L%llu %s] %s %s %s %s %s %s %s%c" \ "size=%llxL/%llxP birth=%lluL/%lluP fill=%llu%c" \ "cksum=%llx:%llx:%llx:%llx", \ (u_longlong_t)BP_GET_LEVEL(bp), \ type, \ checksum, \ compress, \ + BP_IS_ENCRYPTED(bp) ? "encrypted" : "unencrypted", \ BP_GET_BYTEORDER(bp) == 0 ? "BE" : "LE", \ BP_IS_GANG(bp) ? "gang" : "contiguous", \ BP_GET_DEDUP(bp) ? "dedup" : "unique", \ @@ -584,8 +602,8 @@ extern int spa_open_rewind(const char *pool, spa_t **, void *tag, nvlist_t *policy, nvlist_t **config); extern int spa_get_stats(const char *pool, nvlist_t **config, char *altroot, size_t buflen); -extern int spa_create(const char *pool, nvlist_t *config, nvlist_t *props, - nvlist_t *zplprops); +extern int spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, + nvlist_t *zplprops, struct dsl_crypto_params *dcp); extern int spa_import(char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags); extern nvlist_t *spa_tryimport(nvlist_t *tryconfig); diff --git a/include/sys/spa_checksum.h b/include/sys/spa_checksum.h index b87990105a71..2c2cb2590ed5 100644 --- a/include/sys/spa_checksum.h +++ b/include/sys/spa_checksum.h @@ -47,6 +47,10 @@ typedef struct zio_cksum { (zcp)->zc_word[3] = w3; \ } +#define ZIO_CHECKSUM_MAC_EQUAL(zc1, zc2) \ + (0 == (((zc1).zc_word[0] - (zc2).zc_word[0]) | \ + ((zc1).zc_word[1] - (zc2).zc_word[1]))) + #define ZIO_CHECKSUM_EQUAL(zc1, zc2) \ (0 == (((zc1).zc_word[0] - (zc2).zc_word[0]) | \ ((zc1).zc_word[1] - (zc2).zc_word[1]) | \ diff --git a/include/sys/spa_impl.h b/include/sys/spa_impl.h index b807645373c9..12ef57184f37 100644 --- a/include/sys/spa_impl.h +++ b/include/sys/spa_impl.h @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -269,6 +270,7 @@ struct spa { spa_avz_action_t spa_avz_action; /* destroy/rebuild AVZ? */ uint64_t spa_errata; /* errata issues detected */ spa_stats_t spa_stats; /* assorted spa statistics */ + spa_keystore_t spa_keystore; /* loaded crypto keys */ hrtime_t spa_ccw_fail_time; /* Conf cache write fail time */ taskq_t *spa_zvol_taskq; /* Taskq for minor management */ diff --git a/include/sys/zil.h b/include/sys/zil.h index ed0810aa1a9e..e9ab58993f83 100644 --- a/include/sys/zil.h +++ b/include/sys/zil.h @@ -32,6 +32,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -86,7 +87,7 @@ typedef struct zil_header { * number passed in the blk_cksum field of the blkptr_t */ typedef struct zil_chain { - uint64_t zc_pad; + uint64_t zc_mac; /* mac for encryption */ blkptr_t zc_next_blk; /* next block in chain */ uint64_t zc_nused; /* bytes in log block used */ zio_eck_t zc_eck; /* block trailer */ @@ -458,7 +459,8 @@ typedef int (*const zil_replay_func_t)(void *, char *, boolean_t); typedef int zil_get_data_t(void *arg, lr_write_t *lr, char *dbuf, zio_t *zio); extern int zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func, - zil_parse_lr_func_t *parse_lr_func, void *arg, uint64_t txg); + zil_parse_lr_func_t *parse_lr_func, void *arg, uint64_t txg, + boolean_t decrypt); extern void zil_init(void); extern void zil_fini(void); diff --git a/include/sys/zio.h b/include/sys/zio.h index 6c5153dcff65..7ea7e1b310aa 100644 --- a/include/sys/zio.h +++ b/include/sys/zio.h @@ -102,6 +102,30 @@ enum zio_checksum { #define ZIO_DEDUPCHECKSUM ZIO_CHECKSUM_SHA256 #define ZIO_DEDUPDITTO_MIN 100 +/* supported encryption algorithms */ +enum zio_encrypt { + ZIO_CRYPT_INHERIT = 0, + ZIO_CRYPT_ON, + ZIO_CRYPT_OFF, + ZIO_CRYPT_AES_128_CCM, + ZIO_CRYPT_AES_192_CCM, + ZIO_CRYPT_AES_256_CCM, + ZIO_CRYPT_AES_128_GCM, + ZIO_CRYPT_AES_192_GCM, + ZIO_CRYPT_AES_256_GCM, + ZIO_CRYPT_FUNCTIONS +}; + +#define ZIO_CRYPT_ON_VALUE ZIO_CRYPT_AES_256_CCM +#define ZIO_CRYPT_DEFAULT ZIO_CRYPT_OFF + +/* macros defining encryption lengths */ +#define MAX_MASTER_KEY_LEN 32 +#define DATA_IV_LEN 12 +#define DATA_SALT_LEN 8 +#define DATA_MAC_LEN 16 +#define ZIL_MAC_LEN 8 + /* * The number of "legacy" compression functions which can be set on individual * objects. @@ -179,17 +203,19 @@ enum zio_flag { ZIO_FLAG_DONT_PROPAGATE = 1 << 20, ZIO_FLAG_IO_BYPASS = 1 << 21, ZIO_FLAG_IO_REWRITE = 1 << 22, - ZIO_FLAG_RAW = 1 << 23, - ZIO_FLAG_GANG_CHILD = 1 << 24, - ZIO_FLAG_DDT_CHILD = 1 << 25, - ZIO_FLAG_GODFATHER = 1 << 26, - ZIO_FLAG_NOPWRITE = 1 << 27, - ZIO_FLAG_REEXECUTED = 1 << 28, - ZIO_FLAG_DELEGATED = 1 << 29, - ZIO_FLAG_FASTWRITE = 1 << 30 + ZIO_FLAG_RAW_COMPRESS = 1 << 23, + ZIO_FLAG_RAW_ENCRYPT = 1 << 24, + ZIO_FLAG_GANG_CHILD = 1 << 25, + ZIO_FLAG_DDT_CHILD = 1 << 26, + ZIO_FLAG_GODFATHER = 1 << 27, + ZIO_FLAG_NOPWRITE = 1 << 28, + ZIO_FLAG_REEXECUTED = 1 << 29, + ZIO_FLAG_DELEGATED = 1 << 30, + ZIO_FLAG_FASTWRITE = 1 << 31, }; #define ZIO_FLAG_MUSTSUCCEED 0 +#define ZIO_FLAG_RAW (ZIO_FLAG_RAW_COMPRESS | ZIO_FLAG_RAW_ENCRYPT) #define ZIO_DDT_CHILD_FLAGS(zio) \ (((zio)->io_flags & ZIO_FLAG_DDT_INHERIT) | \ @@ -291,6 +317,10 @@ typedef struct zio_prop { boolean_t zp_dedup; boolean_t zp_dedup_verify; boolean_t zp_nopwrite; + boolean_t zp_encrypt; + uint8_t zp_salt[DATA_SALT_LEN]; + uint8_t zp_iv[DATA_IV_LEN]; + uint8_t zp_mac[DATA_MAC_LEN]; } zio_prop_t; typedef struct zio_cksum_report zio_cksum_report_t; @@ -496,8 +526,8 @@ extern zio_t *zio_write_phys(zio_t *pio, vdev_t *vd, uint64_t offset, extern zio_t *zio_free_sync(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp, enum zio_flag flags); -extern int zio_alloc_zil(spa_t *spa, uint64_t txg, blkptr_t *new_bp, - uint64_t size, boolean_t use_slog); +extern int zio_alloc_zil(spa_t *spa, objset_t *os, uint64_t txg, + blkptr_t *new_bp, uint64_t size, boolean_t use_slog); extern void zio_free_zil(spa_t *spa, uint64_t txg, blkptr_t *bp); extern void zio_flush(zio_t *zio, vdev_t *vd); extern void zio_shrink(zio_t *zio, uint64_t size); diff --git a/include/sys/zio_crypt.h b/include/sys/zio_crypt.h new file mode 100644 index 000000000000..2c8d98fe8c9a --- /dev/null +++ b/include/sys/zio_crypt.h @@ -0,0 +1,172 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Datto, Inc. All rights reserved. + */ + +#ifndef _SYS_ZIO_CRYPT_H +#define _SYS_ZIO_CRYPT_H + +#include +#include +#include +#include +#include +#include + +/* forward declarations */ +struct zbookmark_phys; + +#define WRAPPING_KEY_LEN 32 +#define WRAPPING_IV_LEN DATA_IV_LEN +#define WRAPPING_MAC_LEN 16 + +#define SHA1_DIGEST_LEN 20 +#define SHA_256_DIGEST_LEN 32 +#define HMAC_SHA256_KEYLEN 32 + +#define L2ARC_DEFAULT_CRYPT ZIO_CRYPT_AES_256_CCM + +#define ZIO_NO_ENCRYPTION_NEEDED -1 + +/* + * After encrypting many blocks with the same salt we may start to run + * up against the theoretical limits of how much data can securely be + * encrypted a single key using the supported encryption modes. To + * counteract this we generate a new salt after writing + * ZIO_CRYPT_MAX_SALT_USAGE blocks of data, tracked by zk_salt_count. + * The current value was chosen because it is approximately the number + * of blocks that would have to be written in order to acheive a + * 1 / 1 trillion chance of having an IV collision. Developers looking to + * change this number should make sure they take into account the + * birthday problem in regards to IV generation and the limits of what the + * underlying mode can actually handle. + */ +#define ZIO_CRYPT_MAX_SALT_USAGE 400000000 + +/* utility macros */ +#define BITS_TO_BYTES(x) (((x) + 7) >> 3) +#define BYTES_TO_BITS(x) (x << 3) + +/* supported commands for zfs_ioc_crypto() */ +typedef enum zfs_ioc_crypto_cmd { + ZFS_IOC_KEY_CMD_NONE = 0, + ZFS_IOC_KEY_LOAD_KEY, + ZFS_IOC_KEY_UNLOAD_KEY, + ZFS_IOC_KEY_REWRAP, +} zfs_ioc_crypto_cmd_t; + +typedef enum zio_crypt_type { + ZC_TYPE_NONE = 0, + ZC_TYPE_CCM, + ZC_TYPE_GCM +} zio_crypt_type_t; + +/* table of supported crypto algorithms, modes and keylengths. */ +typedef struct zio_crypt_info { + /* mechanism name, needed by ICP */ + crypto_mech_name_t ci_mechname; + + /* cipher mode type (GCM, CCM) */ + zio_crypt_type_t ci_crypt_type; + + /* length of the encryption key */ + size_t ci_keylen; + + /* human-readable name of the encryption alforithm */ + char *ci_name; +} zio_crypt_info_t; + +extern zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS]; + +/* ZAP entry keys for DSL Encryption Keys stored on disk */ +#define DSL_CRYPTO_KEY_CRYPT "DSL_CRYPTO_CRYPT" +#define DSL_CRYPTO_KEY_IV "DSL_CRYPTO_IV" +#define DSL_CRYPTO_KEY_MAC "DSL_CRYPTO_MAC" +#define DSL_CRYPTO_KEY_MASTER_BUF "DSL_CRYPTO_MASTER" +#define DSL_CRYPTO_KEY_HMAC_KEY_BUF "DSL_CRYPTO_HMAC_KEY" + +/* in memory representation of an unwrapped key that is loaded into memory */ +typedef struct zio_crypt_key { + /* encryption algorithm */ + uint64_t zk_crypt; + + /* buffer for master key */ + uint8_t zk_master_keydata[MAX_MASTER_KEY_LEN]; + + /* buffer for hmac key */ + uint8_t zk_hmac_keydata[HMAC_SHA256_KEYLEN]; + + /* buffer for currrent encryption key derived from master key */ + uint8_t zk_current_keydata[MAX_MASTER_KEY_LEN]; + + /* current 64 bit salt for deriving an encryption key */ + uint8_t zk_salt[DATA_SALT_LEN]; + + /* count of how many times the current salt has been used */ + uint64_t zk_salt_count; + + /* illumos crypto api current encryption key */ + crypto_key_t zk_current_key; + + /* template of current encryption key for illumos crypto api */ + crypto_ctx_template_t zk_current_tmpl; + + /* illumos crypto api current hmac key */ + crypto_key_t zk_hmac_key; + + /* template of hmac key for illumos crypto api */ + crypto_ctx_template_t zk_hmac_tmpl; + + /* lock for changing the salt and dependant values */ + krwlock_t zk_salt_lock; +} zio_crypt_key_t; + +void zio_crypt_key_destroy(zio_crypt_key_t *key); +int zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key); +int zio_crypt_key_get_salt(zio_crypt_key_t *key, uint8_t *salt_out); + +int zio_crypt_key_wrap(crypto_key_t *cwkey, zio_crypt_key_t *key, uint8_t *iv, + uint8_t *mac, uint8_t *keydata_out, uint8_t *hmac_keydata_out); +int zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint8_t *keydata, + uint8_t *hmac_keydata, uint8_t *iv, uint8_t *mac, zio_crypt_key_t *key); +int zio_crypt_generate_iv(uint8_t *ivbuf); +int zio_crypt_generate_iv_salt_dedup(zio_crypt_key_t *key, uint8_t *data, + uint_t datalen, uint8_t *ivbuf, uint8_t *salt); + +void zio_crypt_encode_params_bp(blkptr_t *bp, uint8_t *salt, uint8_t *iv); +void zio_crypt_decode_params_bp(const blkptr_t *bp, uint8_t *salt, uint8_t *iv); +void zio_crypt_encode_mac_bp(blkptr_t *bp, uint8_t *mac); +void zio_crypt_decode_mac_bp(const blkptr_t *bp, uint8_t *mac); +void zio_crypt_encode_mac_zil(const void *data, uint8_t *mac); +void zio_crypt_decode_mac_zil(const void *data, uint8_t *mac); +void zio_crypt_derive_zil_iv(const void *data, uint8_t *iv, uint8_t *iv_out); +void zio_crypt_copy_dnode_bonus(abd_t *src_abd, uint8_t *dst, uint_t datalen); + +int zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, + dmu_object_type_t ot, uint8_t *iv, uint8_t *mac, uint_t datalen, + uint8_t *plainbuf, uint8_t *cipherbuf); +int zio_do_crypt_abd(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, + dmu_object_type_t ot, uint8_t *iv, uint8_t *mac, uint_t datalen, + abd_t *pabd, abd_t *cabd); + +#endif diff --git a/include/sys/zio_impl.h b/include/sys/zio_impl.h index 4d56e906666f..7475b7d736cd 100644 --- a/include/sys/zio_impl.h +++ b/include/sys/zio_impl.h @@ -96,6 +96,16 @@ extern "C" { * physical I/O. The nop write feature can handle writes in either * syncing or open context (i.e. zil writes) and as a result is mutually * exclusive with dedup. + * + * Encryption: + * Encryption is handled by the ZIO_STAGE_ENCRYPT stage. If the data is to be + * encrypted, this stage determines how the encryption metadata is stored in + * the bp. Decryption is performed during ZIO_STAGE_READ_BP_INIT as a transform + * callback. Encryption is also mutually exclusive with nopwrite, because + * encrypted blocks with the same plaintext will not have matching ciphertexts. + * For dedup blocks we deterministically generate the IV by performing a + * SHA256-HMAC of the plaintext, so we can actually still do dedup. See the + * block comment in zio_crypt.c for details. */ /* @@ -110,32 +120,33 @@ enum zio_stage { ZIO_STAGE_ISSUE_ASYNC = 1 << 4, /* RWF-- */ ZIO_STAGE_WRITE_COMPRESS = 1 << 5, /* -W--- */ - ZIO_STAGE_CHECKSUM_GENERATE = 1 << 6, /* -W--- */ + ZIO_STAGE_ENCRYPT = 1 << 6, /* -W--- */ + ZIO_STAGE_CHECKSUM_GENERATE = 1 << 7, /* -W--- */ - ZIO_STAGE_NOP_WRITE = 1 << 7, /* -W--- */ + ZIO_STAGE_NOP_WRITE = 1 << 8, /* -W--- */ - ZIO_STAGE_DDT_READ_START = 1 << 8, /* R---- */ - ZIO_STAGE_DDT_READ_DONE = 1 << 9, /* R---- */ - ZIO_STAGE_DDT_WRITE = 1 << 10, /* -W--- */ - ZIO_STAGE_DDT_FREE = 1 << 11, /* --F-- */ + ZIO_STAGE_DDT_READ_START = 1 << 9, /* R---- */ + ZIO_STAGE_DDT_READ_DONE = 1 << 10, /* R---- */ + ZIO_STAGE_DDT_WRITE = 1 << 11, /* -W--- */ + ZIO_STAGE_DDT_FREE = 1 << 12, /* --F-- */ - ZIO_STAGE_GANG_ASSEMBLE = 1 << 12, /* RWFC- */ - ZIO_STAGE_GANG_ISSUE = 1 << 13, /* RWFC- */ + ZIO_STAGE_GANG_ASSEMBLE = 1 << 13, /* RWFC- */ + ZIO_STAGE_GANG_ISSUE = 1 << 14, /* RWFC- */ - ZIO_STAGE_DVA_THROTTLE = 1 << 14, /* -W--- */ - ZIO_STAGE_DVA_ALLOCATE = 1 << 15, /* -W--- */ - ZIO_STAGE_DVA_FREE = 1 << 16, /* --F-- */ - ZIO_STAGE_DVA_CLAIM = 1 << 17, /* ---C- */ + ZIO_STAGE_DVA_THROTTLE = 1 << 15, /* -W--- */ + ZIO_STAGE_DVA_ALLOCATE = 1 << 16, /* -W--- */ + ZIO_STAGE_DVA_FREE = 1 << 17, /* --F-- */ + ZIO_STAGE_DVA_CLAIM = 1 << 18, /* ---C- */ - ZIO_STAGE_READY = 1 << 18, /* RWFCI */ + ZIO_STAGE_READY = 1 << 19, /* RWFCI */ - ZIO_STAGE_VDEV_IO_START = 1 << 19, /* RW--I */ - ZIO_STAGE_VDEV_IO_DONE = 1 << 20, /* RW--I */ - ZIO_STAGE_VDEV_IO_ASSESS = 1 << 21, /* RW--I */ + ZIO_STAGE_VDEV_IO_START = 1 << 20, /* RW--I */ + ZIO_STAGE_VDEV_IO_DONE = 1 << 21, /* RW--I */ + ZIO_STAGE_VDEV_IO_ASSESS = 1 << 22, /* RW--I */ - ZIO_STAGE_CHECKSUM_VERIFY = 1 << 22, /* R---- */ + ZIO_STAGE_CHECKSUM_VERIFY = 1 << 23, /* R---- */ - ZIO_STAGE_DONE = 1 << 23 /* RWFCI */ + ZIO_STAGE_DONE = 1 << 24 /* RWFCI */ }; #define ZIO_INTERLOCK_STAGES \ @@ -187,12 +198,14 @@ enum zio_stage { #define ZIO_REWRITE_PIPELINE \ (ZIO_WRITE_COMMON_STAGES | \ ZIO_STAGE_WRITE_COMPRESS | \ + ZIO_STAGE_ENCRYPT | \ ZIO_STAGE_WRITE_BP_INIT) #define ZIO_WRITE_PIPELINE \ (ZIO_WRITE_COMMON_STAGES | \ ZIO_STAGE_WRITE_BP_INIT | \ ZIO_STAGE_WRITE_COMPRESS | \ + ZIO_STAGE_ENCRYPT | \ ZIO_STAGE_DVA_THROTTLE | \ ZIO_STAGE_DVA_ALLOCATE) @@ -207,6 +220,7 @@ enum zio_stage { ZIO_STAGE_WRITE_BP_INIT | \ ZIO_STAGE_ISSUE_ASYNC | \ ZIO_STAGE_WRITE_COMPRESS | \ + ZIO_STAGE_ENCRYPT | \ ZIO_STAGE_CHECKSUM_GENERATE | \ ZIO_STAGE_DDT_WRITE) diff --git a/include/zfeature_common.h b/include/zfeature_common.h index acf76381b7c0..5fb708471c22 100644 --- a/include/zfeature_common.h +++ b/include/zfeature_common.h @@ -55,6 +55,7 @@ typedef enum spa_feature { SPA_FEATURE_SKEIN, SPA_FEATURE_EDONR, SPA_FEATURE_USEROBJ_ACCOUNTING, + SPA_FEATURE_ENCRYPTION, SPA_FEATURES } spa_feature_t; diff --git a/include/zfs_deleg.h b/include/zfs_deleg.h index 95db9921f574..deab01131bc4 100644 --- a/include/zfs_deleg.h +++ b/include/zfs_deleg.h @@ -71,6 +71,8 @@ typedef enum { ZFS_DELEG_NOTE_RELEASE, ZFS_DELEG_NOTE_DIFF, ZFS_DELEG_NOTE_BOOKMARK, + ZFS_DELEG_NOTE_LOAD_KEY, + ZFS_DELEG_NOTE_CHANGE_KEY, ZFS_DELEG_NOTE_NONE } zfs_deleg_note_t; diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am index f1260ea7195d..703011d27584 100644 --- a/lib/libzfs/Makefile.am +++ b/lib/libzfs/Makefile.am @@ -12,6 +12,7 @@ lib_LTLIBRARIES = libzfs.la USER_C = \ libzfs_changelist.c \ libzfs_config.c \ + libzfs_crypto.c \ libzfs_dataset.c \ libzfs_diff.c \ libzfs_fru.c \ diff --git a/lib/libzfs/libzfs_crypto.c b/lib/libzfs/libzfs_crypto.c new file mode 100644 index 000000000000..82b0a7e499f2 --- /dev/null +++ b/lib/libzfs/libzfs_crypto.c @@ -0,0 +1,1359 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016, Datto, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libzfs_impl.h" +#include "zfeature_common.h" + +/* + * User keys are used to decrypt the master encyrption keys of a dataset. This + * indirection allows a user to change his / her access key without having to + * re-encrypt the entire dataset. User keys can be provided in one of several + * ways. Raw keys are simlply given to the kernel as is. Similarly, hex keys + * are converted to binary and passed into the kernel. Password based keys are + * a bit more complicated. Passwords alone do not provide suitable entropy for + * encryption and may be too short or too long to be used. In order to derive + * a more appropriate key we use a PBKDF2 function. This function is designed + * to take a (relatively) long time to calculate in order to discourage + * attackers from guessing from a list of common passwords. PBKDF2 requires + * 2 additional parameters. The first is the number of iterations to run, which + * will ultimately decide how long it takes to derive the resulting key from + * the password. The second parameter is a salt that is randomly generated for + * each datasset. The salt is used to "tweak" PBKDF2 such that a group of + * attackers cannot reasonably generate a table of commonly known passwords to + * their output keys and expect it work for all past and future PBKDF2 users. + * We store the salt as a hidden property of the dataset (although it is + * technically ok if the salt is known to the attacker). + */ + +typedef enum key_format { + KEY_FORMAT_NONE = 0, + KEY_FORMAT_RAW, + KEY_FORMAT_HEX, + KEY_FORMAT_PASSPHRASE +} key_format_t; + +typedef enum key_locator { + KEY_LOCATOR_NONE, + KEY_LOCATOR_PROMPT, + KEY_LOCATOR_URI +} key_locator_t; + +#define MIN_PASSPHRASE_LEN 8 +#define MAX_PASSPHRASE_LEN 64 +#define DEFAULT_PBKDF2_ITERATIONS 100000 +#define MIN_PBKDF2_ITERATIONS 10000 + +static int caught_interrupt; + +boolean_t +zfs_prop_encryption_key_param(zfs_prop_t prop) +{ + return (prop == ZFS_PROP_SALT || prop == ZFS_PROP_PBKDF2_ITERS || + prop == ZFS_PROP_KEYSOURCE); +} + +static int +parse_format(key_format_t *format, char *s, int len) +{ + if (strncmp("raw", s, len) == 0 && len == 3) + *format = KEY_FORMAT_RAW; + else if (strncmp("hex", s, len) == 0 && len == 3) + *format = KEY_FORMAT_HEX; + else if (strncmp("passphrase", s, len) == 0 && len == 10) + *format = KEY_FORMAT_PASSPHRASE; + else + return (EINVAL); + + return (0); +} + +static int +parse_locator(key_locator_t *locator, char *s, int len, char **uri) +{ + if (len == 6 && strncmp("prompt", s, 6) == 0) { + *locator = KEY_LOCATOR_PROMPT; + return (0); + } + + /* uri can currently only be an absolut file path */ + if (len > 8 && strncmp("file:///", s, 8) == 0) { + *locator = KEY_LOCATOR_URI; + *uri = s; + return (0); + } + + return (EINVAL); +} + +static int +keysource_prop_parser(char *keysource, key_format_t *format, + key_locator_t *locator, char **uri) +{ + int len, ret; + int keysource_len = strlen(keysource); + char *s = keysource; + + *format = KEY_FORMAT_NONE; + *locator = KEY_LOCATOR_NONE; + + if (keysource_len > ZPOOL_MAXPROPLEN) + return (EINVAL); + + for (len = 0; len < keysource_len; len++) + if (s[len] == ',') + break; + + /* If we are at the end of the key property, there is a problem */ + if (len == keysource_len) + return (EINVAL); + + ret = parse_format(format, s, len); + if (ret) + return (ret); + + s = s + len + 1; + len = keysource_len - len - 1; + ret = parse_locator(locator, s, len, uri); + + return (ret); +} + +static int +hex_key_to_raw(char *hex, int hexlen, uint8_t *out) +{ + int ret, i; + unsigned int c; + + for (i = 0; i < hexlen; i += 2) { + if (!isxdigit(hex[i]) || !isxdigit(hex[i + 1])) { + ret = EINVAL; + goto error; + } + + ret = sscanf(&hex[i], "%02x", &c); + if (ret != 1) { + ret = EINVAL; + goto error; + } + + out[i / 2] = c; + } + + return (0); + +error: + return (ret); +} + + +static void +catch_signal(int sig) +{ + caught_interrupt = sig; +} + +static char * +get_format_prompt_string(key_format_t format) +{ + switch (format) { + case KEY_FORMAT_RAW: + return ("raw key"); + case KEY_FORMAT_HEX: + return ("hex key"); + case KEY_FORMAT_PASSPHRASE: + return ("passphrase"); + default: + /* shouldn't happen */ + return (NULL); + } +} + +static int +get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, + uint8_t **buf, boolean_t again, size_t *len_out) +{ + int ret = 0, bytes; + size_t buflen = 0; + struct termios old_term, new_term; + struct sigaction act, osigint, osigtstp; + + *len_out = 0; + + if (isatty(fileno(fd))) { + /* + * handle SIGINT and ignore SIGSTP. This is necessary to + * restore the state of the terminal. + */ + caught_interrupt = 0; + act.sa_flags = 0; + (void) sigemptyset(&act.sa_mask); + act.sa_handler = catch_signal; + + (void) sigaction(SIGINT, &act, &osigint); + act.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &act, &osigtstp); + + /* prompt for the passphrase */ + if (fsname) { + (void) printf("%s %s for '%s': ", + (!again) ? "Enter" : "Renter", + get_format_prompt_string(format), fsname); + } else { + (void) printf("%s %s: ", + (!again) ? "Enter" : "Renter", + get_format_prompt_string(format)); + + } + (void) fflush(stdout); + + /* disable the terminal echo for passphrase input */ + (void) tcgetattr(fileno(fd), &old_term); + + new_term = old_term; + new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + + ret = tcsetattr(fileno(fd), TCSAFLUSH, &new_term); + if (ret) { + ret = errno; + errno = 0; + goto out; + } + } + + /* read the key material */ + bytes = getline((char **)buf, &buflen, fd); + if (bytes < 0) { + ret = errno; + errno = 0; + goto out; + } + + /* trim the ending newline if it exists */ + if ((*buf)[bytes - 1] == '\n') { + (*buf)[bytes - 1] = '\0'; + bytes--; + } + + *len_out = bytes; + +out: + if (isatty(fileno(fd))) { + /* reset the teminal */ + (void) tcsetattr(fileno(fd), TCSAFLUSH, &old_term); + (void) sigaction(SIGINT, &osigint, NULL); + (void) sigaction(SIGTSTP, &osigtstp, NULL); + + /* if we caught a signal, re-throw it now */ + if (caught_interrupt != 0) { + (void) kill(getpid(), caught_interrupt); + } + + /* print the newline that was not echo'ed */ + printf("\n"); + } + + return (ret); + +} + +static int +get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, + key_locator_t locator, char *uri, const char *fsname, uint8_t **km_out, + size_t *kmlen_out) +{ + int ret, i; + FILE *fd = NULL; + uint8_t *km = NULL, *km2 = NULL; + size_t kmlen, kmlen2; + + /* open the appropriate file descriptor */ + switch (locator) { + case KEY_LOCATOR_PROMPT: + fd = stdin; + break; + case KEY_LOCATOR_URI: + fd = fopen(&uri[7], "r"); + if (!fd) { + ret = errno; + errno = 0; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to open key material file")); + goto error; + } + break; + default: + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Invalid key locator.")); + goto error; + } + + /* fetch the key material into the buffer */ + ret = get_key_material_raw(fd, fsname, format, &km, B_FALSE, &kmlen); + if (ret) + goto error; + + /* do basic validation of the key material */ + switch (format) { + case KEY_FORMAT_RAW: + /* verify the key length is correct */ + if (kmlen < WRAPPING_KEY_LEN) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Raw key too short (expected %u)."), + WRAPPING_KEY_LEN); + goto error; + } + + if (kmlen > WRAPPING_KEY_LEN) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Raw key too long (expected %u)."), + WRAPPING_KEY_LEN); + goto error; + } + break; + case KEY_FORMAT_HEX: + /* verify the key length is correct */ + if (kmlen < WRAPPING_KEY_LEN * 2) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Hex key too short (expected %u)."), + WRAPPING_KEY_LEN * 2); + goto error; + } + + if (kmlen > WRAPPING_KEY_LEN * 2) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Hex key too long (expected %u)."), + WRAPPING_KEY_LEN * 2); + goto error; + } + + /* check for invalid hex digits */ + for (i = 0; i < WRAPPING_KEY_LEN * 2; i++) { + if (!isxdigit((char)km[i])) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Invalid hex character detected.")); + goto error; + } + } + break; + case KEY_FORMAT_PASSPHRASE: + /* verify the length is correct */ + if (kmlen > MAX_PASSPHRASE_LEN) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Passphrase too long (max 64).")); + goto error; + } + + if (kmlen < MIN_PASSPHRASE_LEN) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Passphrase too short (min 8).")); + goto error; + } + break; + default: + /* can't happen */ + break; + } + + if (do_verify && isatty(fileno(fd))) { + ret = get_key_material_raw(fd, fsname, format, &km2, + B_TRUE, &kmlen2); + if (ret) + goto error; + + if (kmlen2 != kmlen || + (strncmp((char *)km, (char *)km2, kmlen) != 0)) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Passphrases do not match.")); + goto error; + } + } + + if (fd != stdin) + fclose(fd); + + if (km2) + free(km2); + + *km_out = km; + *kmlen_out = kmlen; + return (0); + +error: + if (km) + free(km); + + if (km2) + free(km2); + + if (fd && fd != stdin) + fclose(fd); + + *km_out = NULL; + *kmlen_out = 0; + return (ret); +} + +static int +pbkdf2(uint8_t *passphrase, size_t passphraselen, uint8_t *salt, + size_t saltlen, uint64_t iterations, uint8_t *output, + size_t outputlen) +{ + int ret; + uint64_t iter; + uint32_t blockptr, i; + uint16_t hmac_key_len; + uint8_t *hmac_key; + uint8_t block[SHA1_DIGEST_LEN * 2]; + uint8_t *hmacresult = block + SHA1_DIGEST_LEN; + crypto_mechanism_t mech; + crypto_key_t key; + crypto_data_t in_data, out_data; + crypto_ctx_template_t tmpl = NULL; + + /* initialize output */ + memset(output, 0, outputlen); + + /* initialize icp for use */ + thread_init(); + icp_init(); + + /* HMAC key size is max(sizeof(uint32_t) + salt len, sha 256 len) */ + if (saltlen > SHA1_DIGEST_LEN) { + hmac_key_len = saltlen + sizeof (uint32_t); + } else { + hmac_key_len = SHA1_DIGEST_LEN; + } + + hmac_key = calloc(hmac_key_len, 1); + if (!hmac_key) { + ret = ENOMEM; + goto error; + } + + /* initialize sha 256 hmac mechanism */ + mech.cm_type = crypto_mech2id(SUN_CKM_SHA1_HMAC); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + /* initialize passphrase as a crypto key */ + key.ck_format = CRYPTO_KEY_RAW; + key.ck_length = BYTES_TO_BITS(passphraselen); + key.ck_data = passphrase; + + /* + * initialize crypto data for the input data. length will change + * after the first iteration, so we will initialize it in the loop. + */ + in_data.cd_format = CRYPTO_DATA_RAW; + in_data.cd_offset = 0; + in_data.cd_raw.iov_base = (char *)hmac_key; + + /* initialize crypto data for the output data */ + out_data.cd_format = CRYPTO_DATA_RAW; + out_data.cd_offset = 0; + out_data.cd_length = SHA1_DIGEST_LEN; + out_data.cd_raw.iov_base = (char *)hmacresult; + out_data.cd_raw.iov_len = SHA1_DIGEST_LEN; + + /* initialize the context template */ + ret = crypto_create_ctx_template(&mech, &key, &tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) { + ret = EIO; + goto error; + } + + /* main loop */ + for (blockptr = 0; blockptr < outputlen; blockptr += SHA1_DIGEST_LEN) { + + /* + * for the first iteration, the HMAC key is the user-provided + * salt concatenated with the block index (1-indexed) + */ + i = htobe32(1 + (blockptr / SHA1_DIGEST_LEN)); + memmove(hmac_key, salt, saltlen); + memmove(hmac_key + saltlen, (uint8_t *)(&i), sizeof (uint32_t)); + + /* block initializes to zeroes (no XOR) */ + memset(block, 0, SHA1_DIGEST_LEN); + + for (iter = 0; iter < iterations; iter++) { + if (iter > 0) { + in_data.cd_length = SHA1_DIGEST_LEN; + in_data.cd_raw.iov_len = SHA1_DIGEST_LEN; + } else { + in_data.cd_length = saltlen + sizeof (uint32_t); + in_data.cd_raw.iov_len = + saltlen + sizeof (uint32_t); + } + + ret = crypto_mac(&mech, &in_data, &key, tmpl, + &out_data, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = EIO; + goto error; + } + + /* HMAC key now becomes the output of this iteration */ + memmove(hmac_key, hmacresult, SHA1_DIGEST_LEN); + + /* XOR this iteration's result with the current block */ + for (i = 0; i < SHA1_DIGEST_LEN; i++) { + block[i] ^= hmacresult[i]; + } + } + + /* + * compute length of this block, make sure we don't write + * beyond the end of the output, truncating if necessary + */ + if (blockptr + SHA1_DIGEST_LEN > outputlen) { + memmove(output + blockptr, block, outputlen - blockptr); + } else { + memmove(output + blockptr, block, SHA1_DIGEST_LEN); + } + } + + crypto_destroy_ctx_template(tmpl); + free(hmac_key); + icp_fini(); + thread_fini(); + + return (0); + +error: + crypto_destroy_ctx_template(tmpl); + if (hmac_key) + free(hmac_key); + icp_fini(); + thread_fini(); + + return (ret); +} + +static int +derive_key(libzfs_handle_t *hdl, key_format_t format, uint64_t iters, + uint8_t *key_material, size_t key_material_len, uint64_t salt, + uint8_t **key_out) +{ + int ret; + uint8_t *key; + + *key_out = NULL; + + key = zfs_alloc(hdl, WRAPPING_KEY_LEN); + if (!key) + return (ENOMEM); + + switch (format) { + case KEY_FORMAT_RAW: + bcopy(key_material, key, WRAPPING_KEY_LEN); + break; + case KEY_FORMAT_HEX: + ret = hex_key_to_raw((char *)key_material, + WRAPPING_KEY_LEN * 2, key); + if (ret) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Invalid hex key provided.")); + goto error; + } + break; + case KEY_FORMAT_PASSPHRASE: + salt = LE_64(salt); + ret = pbkdf2(key_material, strlen((char *)key_material), + ((uint8_t *)&salt), sizeof (uint64_t), iters, + key, WRAPPING_KEY_LEN); + if (ret) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to generate key from passphrase.")); + goto error; + } + break; + default: + ret = EINVAL; + goto error; + } + + *key_out = key; + return (0); + +error: + free(key); + + *key_out = NULL; + return (ret); +} + +static boolean_t +encryption_feature_is_enabled(zpool_handle_t *zph) +{ + nvlist_t *features; + uint64_t feat_refcount; + + /* check that features can be enabled */ + if (zpool_get_prop_int(zph, ZPOOL_PROP_VERSION, NULL) + < SPA_VERSION_FEATURES) + return (B_FALSE); + + /* check for crypto feature */ + features = zpool_get_features(zph); + if (!features || nvlist_lookup_uint64(features, + spa_feature_table[SPA_FEATURE_ENCRYPTION].fi_guid, + &feat_refcount) != 0) + return (B_FALSE); + + return (B_TRUE); +} + +static int +populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, + zfs_handle_t *zhp, char *keysource, boolean_t new_ks, nvlist_t *props, + nvlist_t *hidden_args) +{ + int ret; + uint64_t iters, salt = 0; + key_format_t keyformat; + key_locator_t keylocator; + uint8_t *key_material = NULL; + size_t key_material_len = 0; + uint8_t *key_data = NULL; + char *uri; + const char *fsname = (zhp) ? zfs_get_name(zhp) : NULL; + + /* Parse the keysource */ + ret = keysource_prop_parser(keysource, &keyformat, &keylocator, &uri); + if (ret) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid keysource.")); + goto error; + } + + /* get key material from keysource */ + ret = get_key_material(hdl, B_TRUE, keyformat, keylocator, uri, + fsname, &key_material, &key_material_len); + if (ret) + goto error; + + /* passphrase formats require a salt and pbkdf2 iters property */ + if (keyformat == KEY_FORMAT_PASSPHRASE) { + /* always generate a new salt */ + random_init(); + ret = random_get_bytes((uint8_t *)&salt, sizeof (uint64_t)); + if (ret) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to generate salt.")); + goto error; + } + random_fini(); + + ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_SALT), + salt); + if (ret) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to add salt to properties.")); + goto error; + } + + /* + * If we are not changing the keysource we use the number of + * iterations we already have. If the user specifies a number + * validate that it is above the mimimum. + */ + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); + if (!ret && iters < MIN_PBKDF2_ITERATIONS) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Minimum pbkdf2 iterations is %u."), + MIN_PBKDF2_ITERATIONS); + goto error; + } else if (!ret && !new_ks) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Setting pbkdf2 iterations requires " + "specifying keysource.")); + goto error; + } else if (ret == ENOENT && new_ks) { + iters = DEFAULT_PBKDF2_ITERATIONS; + ret = nvlist_add_uint64(props, + zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), iters); + if (ret) + goto error; + } else if (ret == ENOENT) { + iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS); + } else if (ret) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to get pbkdf2 iterations.")); + goto error; + } + } + + /* derive a key from the key material */ + ret = derive_key(hdl, keyformat, iters, key_material, key_material_len, + salt, &key_data); + if (ret) + goto error; + + /* add the derived key to the properties list */ + ret = nvlist_add_uint8_array(hidden_args, "wkeydata", key_data, + WRAPPING_KEY_LEN); + if (ret) + goto error; + + free(key_material); + free(key_data); + + return (0); + +error: + if (key_material) + free(key_material); + if (key_data) + free(key_data); + return (ret); +} + +int +zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, + nvlist_t *pool_props, nvlist_t **hidden_args) +{ + int ret; + char errbuf[1024]; + uint64_t crypt = 0, pcrypt = 0; + char *keysource = NULL; + zfs_handle_t *pzhp = NULL; + nvlist_t *ha = NULL; + boolean_t local_crypt = B_TRUE; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "Encryption create error")); + + /* lookup crypt from props */ + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); + if (ret) + local_crypt = B_FALSE; + + /* lookup keysource from props */ + ret = nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); + if (ret) + keysource = NULL; + + if (parent_name) { + /* get a reference to parent dataset */ + pzhp = make_dataset_handle(hdl, parent_name); + if (pzhp == NULL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to lookup parent.")); + return (ENOENT); + } + + /* Lookup parent's crypt */ + pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); + + /* Check for encryption feature */ + if (!encryption_feature_is_enabled(pzhp->zpool_hdl)) { + if (!local_crypt && !keysource) { + ret = 0; + goto error; + } + + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Encryption feature not enabled.")); + goto error; + } + } else { + if (!nvlist_exists(pool_props, "feature@encryption") && + local_crypt) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Encryption feature not enabled.")); + goto error; + } + + pcrypt = ZIO_CRYPT_OFF; + } + + /* Check for encryption being explicitly truned off */ + if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Invalid encryption value. Dataset must be encrypted.")); + goto error; + } + + /* Get inherited the encryption property if we don't have it locally */ + if (!local_crypt) + crypt = pcrypt; + + /* + * At this point crypt should be the actual encryption value. + * Return if encryption is off + */ + if (crypt == ZIO_CRYPT_OFF) { + if (keysource) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Encryption required to set keysource.")); + goto error; + } + + ret = 0; + goto error; + } + + /* + * If the parent doesn't have a keysource to inherit + * we need one provided + */ + if (pcrypt == ZIO_CRYPT_OFF && !keysource) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Keysource required.")); + goto error; + } + + /* + * If a local keysource is provided, this dataset will + * be a new encryption root. populate encryption params + */ + if (keysource) { + ha = fnvlist_alloc(); + + ret = populate_create_encryption_params_nvlists(hdl, NULL, + keysource, B_TRUE, props, ha); + if (ret) + goto error; + } + + if (pzhp) + zfs_close(pzhp); + + *hidden_args = ha; + return (0); + +error: + if (pzhp) + zfs_close(pzhp); + if (ha) + nvlist_free(ha); + + *hidden_args = NULL; + return (ret); +} + +int +zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, + char *parent_name, nvlist_t *props, nvlist_t **hidden_args) +{ + int ret; + char errbuf[1024]; + char *keysource = NULL; + nvlist_t *ha = NULL; + zfs_handle_t *pzhp = NULL; + uint64_t crypt, pcrypt, ocrypt, okey_status; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "Encryption clone error")); + + /* get a reference to parent dataset, should never be null */ + pzhp = make_dataset_handle(hdl, parent_name); + if (pzhp == NULL) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Failed to lookup parent.")); + return (ENOENT); + } + + /* Lookup parent's crypt */ + pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); + ocrypt = zfs_prop_get_int(origin_zhp, ZFS_PROP_ENCRYPTION); + + /* lookup keysource from props */ + ret = nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); + if (ret) + keysource = NULL; + + /* crypt should not be set */ + ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + &crypt); + if (ret == 0) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Encryption may not be specified during cloning.")); + goto out; + } + + /* all children of encrypted parents must be encrypted */ + if (pcrypt != ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Cannot create unencrypted clone as child " + "of encrypted parent.")); + goto out; + } + + /* + * if neither parent nor the origin is encrypted check to make + * sure no encryption parameters are set + */ + if (pcrypt == ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) { + if (keysource) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Encryption properties may not be set " + "for an unencrypted clone.")); + goto out; + } + + ret = 0; + goto out; + } + + /* + * by this point this dataset will be encrypted. The origin's + * wrapping key must be loaded + */ + okey_status = zfs_prop_get_int(origin_zhp, ZFS_PROP_KEYSTATUS); + if (okey_status != ZFS_KEYSTATUS_AVAILABLE) { + ret = EACCES; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Origin wrapping key must be loaded.")); + goto out; + } + + /* + * if the parent doesn't have a keysource to inherit we need + * one provided for us + */ + if (pcrypt == ZIO_CRYPT_OFF && !keysource) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Keysource required.")); + goto out; + } + + /* prepare the keysource if needed */ + if (keysource) { + ha = fnvlist_alloc(); + + ret = populate_create_encryption_params_nvlists(hdl, NULL, + keysource, B_TRUE, props, ha); + if (ret) + goto out; + } + + zfs_close(pzhp); + + *hidden_args = ha; + return (0); + +out: + if (pzhp) + zfs_close(pzhp); + if (ha) + nvlist_free(ha); + + *hidden_args = NULL; + return (ret); +} + +int +zfs_crypto_load_key(zfs_handle_t *zhp) +{ + int ret; + char errbuf[1024]; + uint64_t crypt, keystatus, iters = 0, salt = 0; + char keysource[MAXNAMELEN]; + char keysource_src[MAXNAMELEN]; + key_format_t format; + key_locator_t locator; + char *uri; + uint8_t *key_material = NULL, *key_data = NULL; + size_t key_material_len; + nvlist_t *crypto_args = NULL; + zprop_source_t keysource_srctype; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "Key load error")); + + if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Encryption feature not enabled.")); + ret = EINVAL; + goto error; + } + + /* fetch relevent info from the dataset properties */ + crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); + if (crypt == ZIO_CRYPT_OFF) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Encryption not enabled for this dataset.")); + ret = EINVAL; + goto error; + } + + /* check that we are loading for an encryption root */ + ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, + sizeof (keysource), &keysource_srctype, keysource_src, + sizeof (keysource_src), B_TRUE); + if (ret) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Failed to get existing keysource property.")); + goto error; + } else if (keysource_srctype == ZPROP_SRC_INHERITED) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Keys must be loaded for encryption root '%s'."), + keysource_src); + ret = EINVAL; + goto error; + } + + /* check that the key is unloaded */ + keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); + if (keystatus == ZFS_KEYSTATUS_AVAILABLE) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Key already loaded.")); + ret = EEXIST; + goto error; + } + + /* parse the keysource. This shoudln't fail */ + ret = keysource_prop_parser(keysource, &format, &locator, &uri); + if (ret) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Invalid keysource property.")); + ret = EINVAL; + goto error; + } + + /* get key material from keysource */ + ret = get_key_material(zhp->zfs_hdl, B_FALSE, format, locator, uri, + zfs_get_name(zhp), &key_material, &key_material_len); + if (ret) + goto error; + + /* passphrase formats require a salt and pbkdf2_iters property */ + if (format == KEY_FORMAT_PASSPHRASE) { + salt = zfs_prop_get_int(zhp, ZFS_PROP_SALT); + iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS); + } + + /* derive a key from the key material */ + ret = derive_key(zhp->zfs_hdl, format, iters, key_material, + key_material_len, salt, &key_data); + if (ret) + goto error; + + /* put the key in an nvlist and pass to the ioctl */ + crypto_args = fnvlist_alloc(); + + ret = nvlist_add_uint8_array(crypto_args, "wkeydata", key_data, + WRAPPING_KEY_LEN); + if (ret) + goto error; + + ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_LOAD_KEY, NULL, + crypto_args); + if (ret) { + switch (ret) { + case EINVAL: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Invalid parameters provided.")); + break; + case EACCES: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Incorrect key provided.")); + break; + case EEXIST: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Key is already loaded.")); + break; + case EBUSY: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Dataset is busy.")); + break; + } + zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); + } + + nvlist_free(crypto_args); + free(key_material); + free(key_data); + + return (ret); + +error: + zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); + if (key_material) + free(key_material); + if (key_data) + free(key_data); + if (crypto_args) + nvlist_free(crypto_args); + + return (ret); +} + +int +zfs_crypto_unload_key(zfs_handle_t *zhp) +{ + int ret; + char errbuf[1024]; + char keysource[MAXNAMELEN]; + char keysource_src[MAXNAMELEN]; + uint64_t crypt, keystatus; + zprop_source_t keysource_srctype; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "Key unload error")); + + if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Encryption feature not enabled.")); + ret = EINVAL; + goto error; + } + + /* fetch relevent info from the dataset properties */ + crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); + if (crypt == ZIO_CRYPT_OFF) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Encryption not enabled.")); + ret = EINVAL; + goto error; + } + + /* check that we are loading for an encryption root */ + ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, + sizeof (keysource), &keysource_srctype, keysource_src, + sizeof (keysource_src), B_TRUE); + if (ret) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Failed to get existing keysource property.")); + goto error; + } else if (keysource_srctype == ZPROP_SRC_INHERITED) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Keys must be unloaded for encryption root '%s'."), + keysource_src); + ret = EINVAL; + goto error; + } + + /* check that the key is loaded */ + keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); + if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Key already unloaded.")); + ret = ENOENT; + goto error; + } + + /* call the ioctl */ + ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_UNLOAD_KEY, NULL, NULL); + + if (ret) { + switch (ret) { + case ENOENT: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Key is not currently loaded.")); + break; + case EBUSY: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Dataset is busy.")); + break; + } + zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); + } + + return (ret); + +error: + zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); + return (ret); +} + +static int +zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props, + nvlist_t **props_out, char *errbuf) +{ + int ret; + nvpair_t *elem = NULL; + char *strval = NULL; + uint64_t intval = 0; + zfs_prop_t prop; + nvlist_t *new_props = NULL; + + new_props = fnvlist_alloc(); + + /* + * loop through all provided properties, we should only have + * keysource and pbkdf2iters. + */ + while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { + const char *propname = nvpair_name(elem); + prop = zfs_name_to_prop(propname); + + switch (prop) { + case ZFS_PROP_PBKDF2_ITERS: + case ZFS_PROP_KEYSOURCE: + ret = zprop_parse_value(zhp->zfs_hdl, elem, prop, + zhp->zfs_type, new_props, &strval, &intval, + errbuf); + if (ret) + goto error; + break; + default: + ret = EINVAL; + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Only keysource and pbkdf2iters may " + "be set with this command.")); + goto error; + } + } + + *props_out = new_props; + return (0); + +error: + nvlist_free(new_props); + *props_out = NULL; + return (ret); +} + +int +zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) +{ + int ret; + char errbuf[1024]; + nvlist_t *crypto_args = NULL; + uint64_t crypt; + char prop_keysource[MAXNAMELEN]; + char *keysource; + boolean_t keysource_exists = B_TRUE; + nvlist_t *props = NULL; + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "Key rewrap error")); + + if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Encryption feature not enabled.")); + ret = EINVAL; + goto error; + } + + /* get crypt from dataset */ + crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); + if (crypt == ZIO_CRYPT_OFF) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Encryption not enabled.")); + ret = EINVAL; + goto error; + } + + ret = zfs_crypto_verify_rewrap_nvlist(zhp, raw_props, &props, errbuf); + if (ret) + goto error; + + /* load keysource from dataset if not specified */ + ret = nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), + &keysource); + if (ret == ENOENT) { + keysource_exists = B_FALSE; + ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, prop_keysource, + sizeof (prop_keysource), NULL, NULL, 0, B_TRUE); + if (ret) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Failed to get existing keysource property.")); + goto error; + } + keysource = prop_keysource; + } else if (ret) { + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Failed to find keysource.")); + goto error; + } + + /* populate an nvlist with the encryption params */ + crypto_args = fnvlist_alloc(); + + ret = populate_create_encryption_params_nvlists(zhp->zfs_hdl, zhp, + keysource, keysource_exists, props, crypto_args); + if (ret) + goto error; + + /* call the ioctl */ + ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_REWRAP, props, + crypto_args); + if (ret) { + switch (ret) { + case EINVAL: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Invalid properties for key change.")); + break; + case EACCES: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "Key is not currently loaded.")); + break; + } + zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); + } + + nvlist_free(props); + nvlist_free(crypto_args); + + return (ret); + +error: + if (props) + nvlist_free(props); + + if (crypto_args) + nvlist_free(crypto_args); + + zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); + return (ret); +} diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 72d86590e8b3..6e64717c113c 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -863,7 +863,7 @@ zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop) nvlist_t * zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl, - const char *errbuf) + boolean_t key_params_ok, const char *errbuf) { nvpair_t *elem; uint64_t intval; @@ -1022,7 +1022,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, } if (zfs_prop_readonly(prop) && - (!zfs_prop_setonce(prop) || zhp != NULL)) { + !(zfs_prop_setonce(prop) && zhp == NULL) && + !(zfs_prop_encryption_key_param(prop) && key_params_ok)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' is readonly"), propname); @@ -1342,6 +1343,27 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, break; } } + + /* check encryption properties */ + if (zhp != NULL) { + int64_t crypt = zfs_prop_get_int(zhp, + ZFS_PROP_ENCRYPTION); + + switch (prop) { + case ZFS_PROP_COPIES: + if (crypt != ZIO_CRYPT_OFF && intval > 2) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "encrypted datasets cannot have " + "3 copies")); + (void) zfs_error(hdl, EZFS_BADPROP, + errbuf); + goto error; + } + break; + default: + break; + } + } } /* @@ -1589,7 +1611,7 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props) if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props, zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl, - errbuf)) == NULL) + B_FALSE, errbuf)) == NULL) goto error; /* @@ -3222,9 +3244,11 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, int ret; uint64_t size = 0; uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); - char errbuf[1024]; uint64_t zoned; dmu_objset_type_t ost; + nvlist_t *hidden_args = NULL; + char errbuf[1024]; + char parent[ZFS_MAX_DATASET_NAME_LEN]; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot create '%s'"), path); @@ -3267,7 +3291,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, zpool_handle_t *zpool_handle = zpool_open(hdl, pool_path); if (props && (props = zfs_valid_proplist(hdl, type, props, - zoned, NULL, zpool_handle, errbuf)) == 0) { + zoned, NULL, zpool_handle, B_TRUE, errbuf)) == 0) { zpool_close(zpool_handle); return (-1); } @@ -3319,15 +3343,19 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, } } + (void) parent_name(path, parent, sizeof (parent)); + if (zfs_crypto_create(hdl, parent, props, NULL, &hidden_args) != 0) { + nvlist_free(props); + return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); + } + /* create the dataset */ - ret = lzc_create(path, ost, props); + ret = lzc_create(path, ost, props, hidden_args); nvlist_free(props); + nvlist_free(hidden_args); /* check for failure */ if (ret != 0) { - char parent[ZFS_MAX_DATASET_NAME_LEN]; - (void) parent_name(path, parent, sizeof (parent)); - switch (errno) { case ENOENT: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -3344,6 +3372,13 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, "pool must be upgraded to set this " "property or value")); return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); + + case EACCES: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "encryption root's key is not loaded " + "or provided")); + return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); + #ifdef _ILP32 case EOVERFLOW: /* @@ -3509,6 +3544,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) char parent[ZFS_MAX_DATASET_NAME_LEN]; int ret; char errbuf[1024]; + nvlist_t *hidden_args = NULL; libzfs_handle_t *hdl = zhp->zfs_hdl; uint64_t zoned; @@ -3537,12 +3573,16 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) type = ZFS_TYPE_FILESYSTEM; } if ((props = zfs_valid_proplist(hdl, type, props, zoned, - zhp, zhp->zpool_hdl, errbuf)) == NULL) + zhp, zhp->zpool_hdl, B_TRUE, errbuf)) == NULL) return (-1); } - ret = lzc_clone(target, zhp->zfs_name, props); + if (zfs_crypto_clone(hdl, zhp, parent, props, &hidden_args) != 0) + return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); + + ret = lzc_clone(target, zhp->zfs_name, props, hidden_args); nvlist_free(props); + nvlist_free(hidden_args); if (ret != 0) { switch (errno) { @@ -3567,6 +3607,12 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET, errbuf)); + case EACCES: + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "required encryption key not loaded or provided")); + return (zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, + errbuf)); + default: return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); @@ -3696,7 +3742,7 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props) if (props != NULL && (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT, - props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) { + props, B_FALSE, NULL, zpool_hdl, B_FALSE, errbuf)) == NULL) { zpool_close(zpool_hdl); return (-1); } @@ -4063,6 +4109,11 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive, "a child dataset already has a snapshot " "with the new name")); (void) zfs_error(hdl, EZFS_EXISTS, errbuf); + } else if (errno == EACCES) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Only encryption roots may be moved. " + "Please set a local keysource.")); + (void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf); } else { (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); } diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c index a2bb471b32d1..b2374048a8e3 100644 --- a/lib/libzfs/libzfs_mount.c +++ b/lib/libzfs/libzfs_mount.c @@ -74,6 +74,7 @@ #include #include #include +#include #include @@ -398,6 +399,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) char mntopts[MNT_LINE_MAX]; char overlay[ZFS_MAXPROPLEN]; libzfs_handle_t *hdl = zhp->zfs_hdl; + uint64_t keystatus; int remount = 0, rc; if (options == NULL) { @@ -434,6 +436,20 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags) mountpoint)); } + /* + * If the filesystem is an encryption root the key must be + * loaded in order to mount. If it isn't, we ask for the key now. + * During a mount, it is possible that a parent key may be loaded + * without updating this zhp. Just in case, we refresh the properties. + */ + zfs_refresh_properties(zhp); + keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); + if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) { + rc = zfs_crypto_load_key(zhp); + if (rc) + return (rc); + } + /* * Append zfsutil option so the mount helper allow the mount */ @@ -1123,11 +1139,13 @@ libzfs_dataset_cmp(const void *a, const void *b) */ #pragma weak zpool_mount_datasets = zpool_enable_datasets int -zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) +zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags, + boolean_t loadkeys) { get_all_cb_t cb = { 0 }; libzfs_handle_t *hdl = zhp->zpool_hdl; zfs_handle_t *zfsp; + uint64_t keystatus; int i, ret = -1; int *good; @@ -1156,6 +1174,11 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags) ret = 0; for (i = 0; i < cb.cb_used; i++) { + keystatus = + zfs_prop_get_int(cb.cb_handles[i], ZFS_PROP_KEYSTATUS); + if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE && !loadkeys) + continue; + if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0) ret = -1; else diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index ace2205a8275..ee6969f67739 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -1185,6 +1185,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, zfs_cmd_t zc = {"\0"}; nvlist_t *zc_fsprops = NULL; nvlist_t *zc_props = NULL; + nvlist_t *hidden_args = NULL; char msg[1024]; int ret = -1; @@ -1215,17 +1216,26 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, strcmp(zonestr, "on") == 0); if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM, - fsprops, zoned, NULL, NULL, msg)) == NULL) { + fsprops, zoned, NULL, NULL, B_TRUE, msg)) == NULL) { goto create_failed; } if (!zc_props && (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) { goto create_failed; } + if (zfs_crypto_create(hdl, NULL, zc_fsprops, props, + &hidden_args) != 0) { + zfs_error(hdl, EZFS_CRYPTOFAILED, msg); + goto create_failed; + } if (nvlist_add_nvlist(zc_props, ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) { goto create_failed; } + if (hidden_args != NULL && nvlist_add_nvlist(zc_props, + ZPOOL_HIDDEN_ARGS, hidden_args) != 0) { + goto create_failed; + } } if (zc_props && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0) @@ -1238,6 +1248,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, zcmd_free_nvlists(&zc); nvlist_free(zc_props); nvlist_free(zc_fsprops); + nvlist_free(hidden_args); switch (errno) { case EBUSY: @@ -1306,6 +1317,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, zcmd_free_nvlists(&zc); nvlist_free(zc_props); nvlist_free(zc_fsprops); + nvlist_free(hidden_args); return (ret); } diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index b140abe35e1c..67b65d0f95b3 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -1064,6 +1064,11 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj, "not an earlier snapshot from the same fs")); return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf)); + case EACCES: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "source key must be loaded")); + return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); + case ENOENT: if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_SNAPSHOT)) { @@ -1706,6 +1711,11 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd, switch (error) { case 0: return (0); + case EACCES: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "source key must be loaded")); + return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); + case EXDEV: case ENOENT: case EDQUOT: @@ -2054,6 +2064,11 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, } return (zfs_error(hdl, EZFS_NOENT, errbuf)); + case EACCES: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "dataset key must be loaded")); + return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); + case EBUSY: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "target is busy; if a filesystem, " @@ -3533,6 +3548,11 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, "since most recent snapshot"), name); (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf); break; + case EACCES: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "inheritted key must be loaded")); + (void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf); + break; case EEXIST: cp = strchr(destsnap, '@'); if (newfs) { diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index a9c8374f19e4..9b6a83f72b85 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -256,6 +256,8 @@ libzfs_error_description(libzfs_handle_t *hdl) return (dgettext(TEXT_DOMAIN, "invalid diff data")); case EZFS_POOLREADONLY: return (dgettext(TEXT_DOMAIN, "pool is read-only")); + case EZFS_CRYPTOFAILED: + return (dgettext(TEXT_DOMAIN, "encryption failure")); case EZFS_UNKNOWN: return (dgettext(TEXT_DOMAIN, "unknown error")); default: diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c index 9be34d34b334..6682df42914c 100644 --- a/lib/libzfs_core/libzfs_core.c +++ b/lib/libzfs_core/libzfs_core.c @@ -170,27 +170,32 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name, } int -lzc_create(const char *fsname, dmu_objset_type_t type, nvlist_t *props) +lzc_create(const char *fsname, dmu_objset_type_t type, nvlist_t *props, + nvlist_t *hidden_args) { int error; nvlist_t *args = fnvlist_alloc(); fnvlist_add_int32(args, "type", type); if (props != NULL) fnvlist_add_nvlist(args, "props", props); + if (hidden_args != NULL) + fnvlist_add_nvlist(args, ZPOOL_HIDDEN_ARGS, hidden_args); error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL); nvlist_free(args); return (error); } int -lzc_clone(const char *fsname, const char *origin, - nvlist_t *props) +lzc_clone(const char *fsname, const char *origin, nvlist_t *props, + nvlist_t *hidden_args) { int error; nvlist_t *args = fnvlist_alloc(); fnvlist_add_string(args, "origin", origin); if (props != NULL) fnvlist_add_nvlist(args, "props", props); + if (hidden_args != NULL) + fnvlist_add_nvlist(args, ZPOOL_HIDDEN_ARGS, hidden_args); error = lzc_ioctl(ZFS_IOC_CLONE, fsname, args, NULL); nvlist_free(args); return (error); @@ -926,3 +931,26 @@ lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist) return (error); } + +/* + * Performs key management functions + * + * crypto_cmd should be a value from zfs_ioc_crypto_cmd_t. If the command + * specifies to load or change a wrapping key, the key should be specified in + * the hidden_args nvlist so that it is not logged + */ +int +lzc_key(const char *fsname, uint64_t crypto_cmd, nvlist_t *args, + nvlist_t *hidden_args) +{ + int error; + nvlist_t *ioc_args = fnvlist_alloc(); + fnvlist_add_uint64(ioc_args, "crypto_cmd", crypto_cmd); + if (args != NULL) + fnvlist_add_nvlist(ioc_args, "args", args); + if (hidden_args != NULL) + fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args); + error = lzc_ioctl(ZFS_IOC_KEY, fsname, ioc_args, NULL); + nvlist_free(ioc_args); + return (error); +} diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 40c460284329..7a161c5442f9 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -59,6 +59,7 @@ KERNEL_C = \ dsl_deadlist.c \ dsl_deleg.c \ dsl_dir.c \ + dsl_crypt.c \ dsl_pool.c \ dsl_prop.c \ dsl_scan.c \ @@ -126,6 +127,7 @@ KERNEL_C = \ zio.c \ zio_checksum.c \ zio_compress.c \ + zio_crypt.c \ zio_inject.c \ zle.c \ zrlock.c diff --git a/man/man5/zpool-features.5 b/man/man5/zpool-features.5 index ccc7ab47e34c..b15eb1a1af53 100644 --- a/man/man5/zpool-features.5 +++ b/man/man5/zpool-features.5 @@ -563,6 +563,8 @@ Booting off of pools using \fBedonr\fR is \fBNOT\fR supported -- any attempt to enable \fBedonr\fR on a root pool will fail with an error. +.RE + .sp .ne 2 .na @@ -589,5 +591,51 @@ files. .RE +.sp +.ne 2 +.na +\fB\fBencryption\fR\fR +.ad +.RS 4n +.TS +l l . +GUID com.datto:encryption +READ\-ONLY COMPATIBLE no +DEPENDENCIES extensible_dataset +.TE + +This feature enables the creation of natively encrypted datasets. ZFS will +encrypt all user data including file and zvol data, file attributes, ACLs, +permission bits, directory listings, and FUID mappings. ZFS will not encrypt +metadata related to the pool structure, including dataset names, dataset +hierarchy, file size, file holes, and dedup tables. Key rotation is managed +internally by the ZFS kernel module and changing the user's key does not +require re-encrypting the entire dataset. Datasets can be scrubbed, resilvered, +moved, and deleted without the encryption keys being loaded (see the zfs key +subcommand for more info). + +Encryption changes the behavior of a few ZFS operations. Encryption is applied +after compression so compression ratios are preserved. Normally checksums in +ZFS are 256 bits long, but for encrypted data the checksum is 128 bits of the +user-chosen checksum and 128 bits of MAC from the encryption suite, which +provides additional protection against maliciously altered data. Deduplication +is still possible with encryption enabled but for security, datasets will only +dedup against themselves, their snapshots, and their clones. Encrypted data +cannot be embedded via the \fIembedded_data\fR feature. + +There are a few limitations on encrypted datasets. Encrypted datasets may not +have \fIcopies=3\fR since the implementation stores some encryption metadata +where the third copy would normally be. Since compression is applied before +encryption datasets may be vulnerable to a CRIME-like attack if applications +accessing the data allow for it. Deduplication with encryption will leak +information about which blocks are equivalent in a dataset and will incur an +extra CPU cost per block written. + +This feature becomes \fBactive\fR when an encrypted dataset is created and will +be returned to the \fBenabled\fR state when all datasets that use this feature +are destroyed. + +.RE + .SH "SEE ALSO" \fBzpool\fR(8) diff --git a/man/man8/zfs.8 b/man/man8/zfs.8 index d5eeed2fccd9..166931614ed7 100644 --- a/man/man8/zfs.8 +++ b/man/man8/zfs.8 @@ -485,6 +485,17 @@ dataset tree. This value is only available when a \fBfilesystem_limit\fR has been set somewhere in the tree under which the dataset resides. .RE +.sp +.ne 2 +.mk +.na +\fB\fBkeystatus\fR +.ad +.sp .6 +.RS 4n +Indicates if an encryption key is currently loaded into ZFS. The possible values are \fBnone\fR, \fBavailable\fR, and \fBunavaliable\fR. See \fBzfs key -l\fR and \fBzfs key -u\fR. +.RE + .sp .ne 2 .na @@ -981,6 +992,35 @@ This property can also be referred to by its shortened column name, \fBdnsize\fR. .RE +.sp +.ne 2 +.na +\fB\fBencryption\fR=\fBoff\fR | \fBon\fR | \fBaes-128-ccm\fR | \fBaes-192-ccm\fR | \fBaes-256-ccm\fR | \fBaes-128-gcm\fR | \fBaes-192-gcm\fR | \fBaes-256-gcm\fR +.ad +.sp .6 +.RS 4n +Controls the encryption cipher suite (block cipher, key length, and mode) used for this dataset. Requires the encryption feature to be enabled on the pool. Requires a keysource to be set at dataset creation time. +.sp +Selecting \fBencryption\fR=\fBon\fR when creating a dataset indicates that the current default encryption suite will be selected, which is currently \fBaes-256-ccm\fR. In order to provide consistent data protection, encryption must be specified at dataset creation time and it cannot be changed afterwards. +.sp +The \fBencryption\fR property is inherited by child datasets by default. All inheriting datasets will be accessible when the root key is mounted. A child dataset may also set encryption locally, which will indicate that it should have a separate key and \fBkeysource\fR from the parent. Regardless, all children of an encrypted dataset must also be encrypted. +.sp +For more details and caveats about encryption see \fBzpool-features\fR. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fBkeysource\fR=<\fBraw\fR | \fBhex\fR | \fBpassphrase\fR>,<\fBprompt\fR | \fBfile://\fR\fI> +.ad +.sp .6 +.RS 4n +Controls what format the user's encryption key will be provided as and where it should be loaded from. This property is only set when the dataset is encrypted. +.sp +Raw keys and hex keys must be 32 bytes long (regardless of the chosen encryption suite) and must be randomly generated. Passphrases must be between 8 and 64 bytes long and will be processed through PBKDF2 before being used (see the \fBpbkdf2iters\fR property). Even though the encryption suite cannot be changed after dataset creation, the keysource can be with \fBzfs key -c\fR. If prompt is selected as the key location ZFS will ask for the key at the command prompt when it is required (\fBzfs mount\fR and \fBzfs key -l\fR). If \fBfile\fR is selected, the key will be loaded from the specified absolute file path. The key may also be passed in via STDIN, but users should be careful not to place keys which should be kept secret on the command line. +.RE + .sp .ne 2 .mk @@ -1053,6 +1093,16 @@ The values \fBon\fR and \fBoff\fR are equivalent to the \fBnbmand\fR and \fBnonb This property is not used on Linux. .RE +.sp +.ne 2 +.na +\fB\fBpbkdf2iters\fR=\fIiterations\fR +.ad +.sp .6 +.RS 4n +Controls the number of PBKDF2 iterations that a \fBpassphrase\fR encryption key should be run through when processing it into an encryption key. This property is only defined when encryption is enabled and a \fBpassphrase\fR key is selected. The goal of PBKDF2 is to significantly increase the computational difficulty needed to brute force a user's passphrase. This is accomplished by forcing the attacker to run each passphrase through a computationally expensive hashing function many times before they arrive at the resulting key. A user who actually knows the passphrase will only have to pay this cost once. As CPUs become better at processing, this number should be raised to ensure that a brute force attack is still not possible. The current default (if not otherwise specified) is \fB100000\fR This may be changed with \fBzfs key -c\fR. +.RE + .sp .ne 2 .na @@ -3402,6 +3452,38 @@ Give more parsable tab-separated output, without header lines and without arrows Display the path's inode change time as the first column of output. .RE +.RE + +.sp +.ne 2 +.na +\fB\fBzfs key -l\fR \fIfilesystem\fR | \fIvolume\fR\fR +.ad +.sp .6 +.RS 4n +Loads a key into ZFS so that the dataset and all of its children that inherit the \fBencryption\fR property may be accessed. The key will be expected in the format and location specified by the \fBkeysource\fR property. Loading a key alone will not automatically mount the dataset. If that functionality is desired, \fBzfs mount\fR will ask for the key and mount the dataset. When a key is loaded the \fBkeystatus\fR property will be set to available. +.RE + +.sp +.ne 2 +.na +\fB\fBzfs key -u\fR \fIfilesystem\fR | \fIvolume\fR\fR +.ad +.sp .6 +.RS 4n +Unloads a key from ZFS, removing the ability to access the dataset and all of its children that inherit the \fBencryption\fR property. This requires that the dataset is not currently open or mounted. When a key is unloaded the \fBkeystatus\fR property will be set to \fBunavailable\fR. +.RE + +.sp +.ne 2 +.na +\fB\fBzfs key -c\fR [\fB-o\fR \fIkeysource\fR=\fIkeysource\fR] [\fB-o\fR \fIpbkdf2iters\fR=\fIiterations\fR] \fIfilesystem\fR | \fIvolume\fR\fR +.ad +.sp .6 +.RS 4n +Allows a user to change the encryption key used to access a dataset. This command requires that the existing key for the dataset is already loaded into ZFS. This command may also be used to change the \fBpbkdf2iters\fR and / or \fBkeysource\fR properties as needed. If the dataset was previously inheriting the \fBencryption\fR property when this command is run it will now be locally set, indicating that this dataset must have its key loaded separately from the parent. +.RE + .SH EXAMPLES .LP \fBExample 1 \fRCreating a ZFS File System Hierarchy diff --git a/man/man8/zpool.8 b/man/man8/zpool.8 index 883d1173973f..bde00a02c602 100644 --- a/man/man8/zpool.8 +++ b/man/man8/zpool.8 @@ -1222,7 +1222,7 @@ Lists destroyed pools only. .sp .ne 2 .na -\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-N\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] [\fB-s\fR] \fB-a\fR\fR +\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR] [\fB-f\fR] [\fB-l\fR] [\fB-m\fR] [\fB-N\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] [\fB-s\fR] \fB-a\fR\fR .ad .sp .6 .RS 4n @@ -1290,6 +1290,15 @@ Forces import, even if the pool appears to be potentially active. Recovery mode for a non-importable pool. Attempt to return the pool to an importable state by discarding the last few transactions. Not all damaged pools can be recovered by using this option. If successful, the data from the discarded transactions is irretrievably lost. This option is ignored if the pool is importable or already imported. .RE +.sp +.ne 2 +.na +\fB\fB-l\fR\fR +.ad +.RS 21n +Indicates that the zpool command will request encryption keys for all encrypted datasets it attempts to mount as it is bringing the pool online. This is equivalent to running \fBzfs mount\fR on each encrypted dataset immediately after the pool is imported. If any datasets have a \fBprompt\fR keysource this command will block waiting for the key to be entered. Otherwise, encrypted datasets will be left unavailable until the keys are loaded. +.RE + .sp .ne 2 .na @@ -1369,7 +1378,7 @@ Scan using the default search path, the libblkid cache will not be consulted. A .sp .ne 2 .na -\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] [\fB-t\fR]] [\fB-s\fR] \fIpool\fR | \fIid\fR [\fInewpool\fR]\fR +\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR] [\fB-f\fR] [\fB-l\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] [\fB-t\fR]] [\fB-s\fR] \fIpool\fR | \fIid\fR [\fInewpool\fR]\fR .ad .sp .6 .RS 4n @@ -1446,6 +1455,16 @@ Forces import, even if the pool appears to be potentially active. Recovery mode for a non-importable pool. Attempt to return the pool to an importable state by discarding the last few transactions. Not all damaged pools can be recovered by using this option. If successful, the data from the discarded transactions is irretrievably lost. This option is ignored if the pool is importable or already imported. .RE +.sp +.ne 2 +.na +\fB\fB-l\fR\fR +.ad +.sp .6 +.RS 4n +Indicates that the zpool command will request encryption keys for all encrypted datasets it attempts to mount as it is bringing the pool online. This is equivalent to running \fBzfs mount\fR on each encrypted dataset immediately after the pool is imported. If any datasets have a \fBprompt\fR keysource this command will block waiting for the key to be entered. Otherwise, encrypted datasets will be left unavailable until the keys are loaded. +.RE + .sp .ne 2 .na diff --git a/module/zcommon/zfs_deleg.c b/module/zcommon/zfs_deleg.c index 647a24e5ff1f..eac6f555578d 100644 --- a/module/zcommon/zfs_deleg.c +++ b/module/zcommon/zfs_deleg.c @@ -68,6 +68,8 @@ zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = { {ZFS_DELEG_PERM_GROUPOBJUSED}, {ZFS_DELEG_PERM_HOLD}, {ZFS_DELEG_PERM_RELEASE}, + {ZFS_DELEG_PERM_LOAD_KEY}, + {ZFS_DELEG_PERM_CHANGE_KEY}, {NULL} }; diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 06e4854e4620..767516be66f1 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "zfs_prop.h" #include "zfs_deleg.h" @@ -118,6 +119,18 @@ zfs_prop_init(void) { NULL } }; + static zprop_index_t crypto_table[] = { + { "on", ZIO_CRYPT_ON }, + { "off", ZIO_CRYPT_OFF }, + { "aes-128-ccm", ZIO_CRYPT_AES_128_CCM }, + { "aes-192-ccm", ZIO_CRYPT_AES_192_CCM }, + { "aes-256-ccm", ZIO_CRYPT_AES_256_CCM }, + { "aes-128-gcm", ZIO_CRYPT_AES_128_GCM }, + { "aes-192-gcm", ZIO_CRYPT_AES_192_GCM }, + { "aes-256-gcm", ZIO_CRYPT_AES_256_GCM }, + { NULL } + }; + static zprop_index_t snapdir_table[] = { { "hidden", ZFS_SNAPDIR_HIDDEN }, { "visible", ZFS_SNAPDIR_VISIBLE }, @@ -192,6 +205,13 @@ zfs_prop_init(void) { NULL } }; + static zprop_index_t keystatus_table[] = { + { "none", ZFS_KEYSTATUS_NONE}, + { "unavailable", ZFS_KEYSTATUS_UNAVAILABLE}, + { "available", ZFS_KEYSTATUS_AVAILABLE}, + { NULL } + }; + static zprop_index_t logbias_table[] = { { "latency", ZFS_LOGBIAS_LATENCY }, { "throughput", ZFS_LOGBIAS_THROUGHPUT }, @@ -268,6 +288,11 @@ zfs_prop_init(void) ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", "COMPRESS", compress_table); + zprop_register_index(ZFS_PROP_ENCRYPTION, "encryption", + ZIO_CRYPT_DEFAULT, PROP_ONETIME, ZFS_TYPE_DATASET, + "on | off | aes-128-ccm | aes-192-ccm | aes-256-ccm | " + "aes-128-gcm | aes-192-gcm | aes-256-gcm", "ENCRYPTION", + crypto_table); zprop_register_index(ZFS_PROP_SNAPDIR, "snapdir", ZFS_SNAPDIR_HIDDEN, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "hidden | visible", "SNAPDIR", snapdir_table); @@ -337,12 +362,16 @@ zfs_prop_init(void) PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "on | off | noauto", "CANMOUNT", canmount_table); - /* readonly index (boolean) properties */ + /* readonly index properties */ zprop_register_index(ZFS_PROP_MOUNTED, "mounted", 0, PROP_READONLY, ZFS_TYPE_FILESYSTEM, "yes | no", "MOUNTED", boolean_table); zprop_register_index(ZFS_PROP_DEFER_DESTROY, "defer_destroy", 0, PROP_READONLY, ZFS_TYPE_SNAPSHOT, "yes | no", "DEFER_DESTROY", boolean_table); + zprop_register_index(ZFS_PROP_KEYSTATUS, "keystatus", + ZFS_KEYSTATUS_NONE, PROP_READONLY, ZFS_TYPE_DATASET, + "none | unavailable | available", + "KEYSTATUS", keystatus_table); /* set once index properties */ zprop_register_index(ZFS_PROP_NORMALIZE, "normalization", 0, @@ -395,6 +424,9 @@ zfs_prop_init(void) "receive_resume_token", NULL, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "", "RESUMETOK"); + zprop_register_string(ZFS_PROP_KEYSOURCE, "keysource", + "none", PROP_ONETIME, ZFS_TYPE_DATASET, + ",", "KEYSOURCE"); /* readonly number properties */ zprop_register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY, @@ -432,6 +464,8 @@ zfs_prop_init(void) PROP_READONLY, ZFS_TYPE_DATASET, "", "LUSED"); zprop_register_number(ZFS_PROP_LOGICALREFERENCED, "logicalreferenced", 0, PROP_READONLY, ZFS_TYPE_DATASET, "", "LREFER"); + zprop_register_number(ZFS_PROP_PBKDF2_ITERS, "pbkdf2iters", + 0, PROP_ONETIME, ZFS_TYPE_DATASET, "", "PBKDF2ITERS"); /* default number properties */ zprop_register_number(ZFS_PROP_QUOTA, "quota", 0, PROP_DEFAULT, @@ -489,6 +523,8 @@ zfs_prop_init(void) PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET, "INCONSISTENT"); zprop_register_hidden(ZFS_PROP_PREV_SNAP, "prevsnap", PROP_TYPE_STRING, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "PREVSNAP"); + zprop_register_hidden(ZFS_PROP_SALT, "salt", + PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET, "SALT"); /* * Property to be removed once libbe is integrated diff --git a/module/zfs/Makefile.in b/module/zfs/Makefile.in index 6712b9b3c04b..edf2812ab70e 100644 --- a/module/zfs/Makefile.in +++ b/module/zfs/Makefile.in @@ -33,6 +33,7 @@ $(MODULE)-objs += dsl_deadlist.o $(MODULE)-objs += dsl_deleg.o $(MODULE)-objs += dsl_bookmark.o $(MODULE)-objs += dsl_dir.o +$(MODULE)-objs += dsl_crypt.o $(MODULE)-objs += dsl_pool.o $(MODULE)-objs += dsl_prop.o $(MODULE)-objs += dsl_scan.o @@ -102,6 +103,7 @@ $(MODULE)-objs += zil.o $(MODULE)-objs += zio.o $(MODULE)-objs += zio_checksum.o $(MODULE)-objs += zio_compress.o +$(MODULE)-objs += zio_crypt.o $(MODULE)-objs += zio_inject.o $(MODULE)-objs += zle.o $(MODULE)-objs += zpl_ctldir.o diff --git a/module/zfs/arc.c b/module/zfs/arc.c index 2ce8cf628f1d..a0021a898c94 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -258,6 +258,21 @@ * ARC is disabled, then the L2ARC's block must be transformed to look * like the physical block in the main data pool before comparing the * checksum and determining its validity. + * + * The L1ARC has a slightly different system for storing encrypted data. + * Raw (encrypted + possibly compressed) data has a few subtle differences from + * data that is just compressed. The biggest difference is that it is not always + * possible to decrypt encrypted data (or visa versa) if the keys aren't loaded. + * The other difference is that encryption cannot be treated as a suggestion. + * If a caller would prefer compressed data, but they actually wind up with + * uncompressed data the worst thing that could happen is there might be a + * performance hit. If the caller requests encrypted data, however, we must be + * sure they actually get it or else secret information could be leaked. Raw + * data is stored in hdr->b_crypt_hdr.b_rabd. An encrypted header, therefore, + * may have both an encrypted version and a decrypted version of its data at + * once. When a caller needs a raw arc_buf_t, it is allocated and the data is + * copied out of this header. To avoid complications with b_pabd, raw buffers + * cannot be shared. */ #include @@ -274,6 +289,7 @@ #include #include #include +#include #ifdef _KERNEL #include #include @@ -647,6 +663,7 @@ typedef struct arc_stats { kstat_named_t arcstat_demand_hit_predictive_prefetch; kstat_named_t arcstat_need_free; kstat_named_t arcstat_sys_free; + kstat_named_t arcstat_raw_size; } arc_stats_t; static arc_stats_t arc_stats = { @@ -741,7 +758,8 @@ static arc_stats_t arc_stats = { { "sync_wait_for_async", KSTAT_DATA_UINT64 }, { "demand_hit_predictive_prefetch", KSTAT_DATA_UINT64 }, { "arc_need_free", KSTAT_DATA_UINT64 }, - { "arc_sys_free", KSTAT_DATA_UINT64 } + { "arc_sys_free", KSTAT_DATA_UINT64 }, + { "arc_raw_size", KSTAT_DATA_UINT64 } }; #define ARCSTAT(stat) (arc_stats.stat.value.ui64) @@ -817,6 +835,8 @@ static arc_state_t *arc_l2c_only; #define arc_need_free ARCSTAT(arcstat_need_free) /* bytes to be freed */ #define arc_sys_free ARCSTAT(arcstat_sys_free) /* target system free bytes */ +/* encrypted + compressed size of entire arc */ +#define arc_raw_size ARCSTAT(arcstat_raw_size) /* compressed size of entire arc */ #define arc_compressed_size ARCSTAT(arcstat_compressed_size) /* uncompressed size of entire arc */ @@ -846,6 +866,7 @@ static taskq_t *arc_prune_taskq; #define HDR_L2_WRITING(hdr) ((hdr)->b_flags & ARC_FLAG_L2_WRITING) #define HDR_L2_EVICTED(hdr) ((hdr)->b_flags & ARC_FLAG_L2_EVICTED) #define HDR_L2_WRITE_HEAD(hdr) ((hdr)->b_flags & ARC_FLAG_L2_WRITE_HEAD) +#define HDR_ENCRYPT(hdr) ((hdr)->b_flags & ARC_FLAG_ENCRYPT) #define HDR_SHARED_DATA(hdr) ((hdr)->b_flags & ARC_FLAG_SHARED_DATA) #define HDR_ISTYPE_METADATA(hdr) \ @@ -854,6 +875,9 @@ static taskq_t *arc_prune_taskq; #define HDR_HAS_L1HDR(hdr) ((hdr)->b_flags & ARC_FLAG_HAS_L1HDR) #define HDR_HAS_L2HDR(hdr) ((hdr)->b_flags & ARC_FLAG_HAS_L2HDR) +#define HDR_HAS_RABD(hdr) \ + (HDR_HAS_L1HDR(hdr) && HDR_ENCRYPT(hdr) && \ + (hdr)->b_crypt_hdr.b_rabd != NULL) /* For storing compression mode in b_flags */ #define HDR_COMPRESS_OFFSET (highbit64(ARC_FLAG_COMPRESS_0) - 1) @@ -866,12 +890,14 @@ static taskq_t *arc_prune_taskq; #define ARC_BUF_LAST(buf) ((buf)->b_next == NULL) #define ARC_BUF_SHARED(buf) ((buf)->b_flags & ARC_BUF_FLAG_SHARED) #define ARC_BUF_COMPRESSED(buf) ((buf)->b_flags & ARC_BUF_FLAG_COMPRESSED) +#define ARC_BUF_ENCRYPTED(buf) ((buf)->b_flags & ARC_BUF_FLAG_ENCRYPTED) /* * Other sizes */ -#define HDR_FULL_SIZE ((int64_t)sizeof (arc_buf_hdr_t)) +#define HDR_FULL_CRYPT_SIZE ((int64_t)sizeof (arc_buf_hdr_t)) +#define HDR_FULL_SIZE ((int64_t)offsetof(arc_buf_hdr_t, b_crypt_hdr)) #define HDR_L2ONLY_SIZE ((int64_t)offsetof(arc_buf_hdr_t, b_l1hdr)) /* @@ -968,6 +994,13 @@ typedef struct l2arc_data_free { list_node_t l2df_list_node; } l2arc_data_free_t; +typedef enum arc_fill_flags { + ARC_FILL_LOCKED = 1 << 0, + ARC_FILL_COMPRESSED = 1 << 1, + ARC_FILL_ENCRYPTED = 1 << 2, + ARC_FILL_IN_PLACE = 1 << 3 +} arc_fill_flags_t; + static kmutex_t l2arc_feed_thr_lock; static kcondvar_t l2arc_feed_thr_cv; static uint8_t l2arc_thread_exit; @@ -978,8 +1011,8 @@ static void arc_get_data_impl(arc_buf_hdr_t *, uint64_t, void *); static void arc_free_data_abd(arc_buf_hdr_t *, abd_t *, uint64_t, void *); static void arc_free_data_buf(arc_buf_hdr_t *, void *, uint64_t, void *); static void arc_free_data_impl(arc_buf_hdr_t *hdr, uint64_t size, void *tag); -static void arc_hdr_free_pabd(arc_buf_hdr_t *); -static void arc_hdr_alloc_pabd(arc_buf_hdr_t *); +static void arc_hdr_free_abd(arc_buf_hdr_t *, boolean_t); +static void arc_hdr_alloc_abd(arc_buf_hdr_t *, boolean_t); static void arc_access(arc_buf_hdr_t *, kmutex_t *); static boolean_t arc_is_overflowing(void); static void arc_buf_watch(arc_buf_t *); @@ -1131,7 +1164,9 @@ buf_hash_remove(arc_buf_hdr_t *hdr) /* * Global data structures and functions for the buf kmem cache. */ + static kmem_cache_t *hdr_full_cache; +static kmem_cache_t *hdr_full_crypt_cache; static kmem_cache_t *hdr_l2only_cache; static kmem_cache_t *buf_cache; @@ -1154,6 +1189,7 @@ buf_fini(void) for (i = 0; i < BUF_LOCKS; i++) mutex_destroy(&buf_hash_table.ht_locks[i].ht_lock); kmem_cache_destroy(hdr_full_cache); + kmem_cache_destroy(hdr_full_crypt_cache); kmem_cache_destroy(hdr_l2only_cache); kmem_cache_destroy(buf_cache); } @@ -1180,6 +1216,19 @@ hdr_full_cons(void *vbuf, void *unused, int kmflag) return (0); } +/* ARGSUSED */ +static int +hdr_full_crypt_cons(void *vbuf, void *unused, int kmflag) +{ + arc_buf_hdr_t *hdr = vbuf; + + hdr_full_cons(vbuf, unused, kmflag); + bzero(&hdr->b_crypt_hdr, HDR_FULL_CRYPT_SIZE - HDR_FULL_SIZE); + arc_space_consume(HDR_FULL_CRYPT_SIZE - HDR_FULL_SIZE, ARC_SPACE_HDRS); + + return (0); +} + /* ARGSUSED */ static int hdr_l2only_cons(void *vbuf, void *unused, int kmflag) @@ -1223,6 +1272,14 @@ hdr_full_dest(void *vbuf, void *unused) arc_space_return(HDR_FULL_SIZE, ARC_SPACE_HDRS); } +/* ARGSUSED */ +static void +hdr_full_crypt_dest(void *vbuf, void *unused) +{ + hdr_full_dest(vbuf, unused); + arc_space_return(HDR_FULL_CRYPT_SIZE - HDR_FULL_SIZE, ARC_SPACE_HDRS); +} + /* ARGSUSED */ static void hdr_l2only_dest(void *vbuf, void *unused) @@ -1295,6 +1352,9 @@ buf_init(void) hdr_full_cache = kmem_cache_create("arc_buf_hdr_t_full", HDR_FULL_SIZE, 0, hdr_full_cons, hdr_full_dest, hdr_recl, NULL, NULL, 0); + hdr_full_crypt_cache = kmem_cache_create("arc_buf_hdr_t_full_crypt", + HDR_FULL_CRYPT_SIZE, 0, hdr_full_crypt_cons, hdr_full_crypt_dest, + hdr_recl, NULL, NULL, 0); hdr_l2only_cache = kmem_cache_create("arc_buf_hdr_t_l2only", HDR_L2ONLY_SIZE, 0, hdr_l2only_cons, hdr_l2only_dest, hdr_recl, NULL, NULL, 0); @@ -1331,6 +1391,12 @@ arc_buf_lsize(arc_buf_t *buf) return (HDR_GET_LSIZE(buf->b_hdr)); } +boolean_t +arc_is_encrypted(arc_buf_t *buf) +{ + return (ARC_BUF_ENCRYPTED(buf) != 0); +} + enum zio_compress arc_get_compression(arc_buf_t *buf) { @@ -1338,6 +1404,17 @@ arc_get_compression(arc_buf_t *buf) HDR_GET_COMPRESS(buf->b_hdr) : ZIO_COMPRESS_OFF); } +/* + * Returns the compression that should be used for compressed buffers. If + * ARC compression is disabled, returns ZIO_CRYPT_OFF. + */ +static inline enum zio_compress +arc_hdr_get_compress(arc_buf_hdr_t *hdr) +{ + return (HDR_COMPRESSION_ENABLED(hdr) ? + HDR_GET_COMPRESS(hdr) : ZIO_COMPRESS_OFF); +} + static inline boolean_t arc_buf_is_shared(arc_buf_t *buf) { @@ -1361,6 +1438,7 @@ static inline void arc_cksum_free(arc_buf_hdr_t *hdr) { ASSERT(HDR_HAS_L1HDR(hdr)); + mutex_enter(&hdr->b_l1hdr.b_freeze_lock); if (hdr->b_l1hdr.b_freeze_cksum != NULL) { kmem_free(hdr->b_l1hdr.b_freeze_cksum, sizeof (zio_cksum_t)); @@ -1401,57 +1479,17 @@ arc_cksum_verify(arc_buf_t *buf) mutex_exit(&hdr->b_l1hdr.b_freeze_lock); } +/* + * This function makes the assumption that data stored in the L2ARC + * will be transformed exactly as it is in the main pool. Because of + * this we can verify the checksum against the reading process's bp. + */ static boolean_t arc_cksum_is_equal(arc_buf_hdr_t *hdr, zio_t *zio) { - enum zio_compress compress = BP_GET_COMPRESS(zio->io_bp); - boolean_t valid_cksum; - ASSERT(!BP_IS_EMBEDDED(zio->io_bp)); VERIFY3U(BP_GET_PSIZE(zio->io_bp), ==, HDR_GET_PSIZE(hdr)); - /* - * We rely on the blkptr's checksum to determine if the block - * is valid or not. When compressed arc is enabled, the l2arc - * writes the block to the l2arc just as it appears in the pool. - * This allows us to use the blkptr's checksum to validate the - * data that we just read off of the l2arc without having to store - * a separate checksum in the arc_buf_hdr_t. However, if compressed - * arc is disabled, then the data written to the l2arc is always - * uncompressed and won't match the block as it exists in the main - * pool. When this is the case, we must first compress it if it is - * compressed on the main pool before we can validate the checksum. - */ - if (!HDR_COMPRESSION_ENABLED(hdr) && compress != ZIO_COMPRESS_OFF) { - uint64_t lsize; - uint64_t csize; - void *cbuf; - ASSERT3U(HDR_GET_COMPRESS(hdr), ==, ZIO_COMPRESS_OFF); - - cbuf = zio_buf_alloc(HDR_GET_PSIZE(hdr)); - lsize = HDR_GET_LSIZE(hdr); - csize = zio_compress_data(compress, zio->io_abd, cbuf, lsize); - - ASSERT3U(csize, <=, HDR_GET_PSIZE(hdr)); - if (csize < HDR_GET_PSIZE(hdr)) { - /* - * Compressed blocks are always a multiple of the - * smallest ashift in the pool. Ideally, we would - * like to round up the csize to the next - * spa_min_ashift but that value may have changed - * since the block was last written. Instead, - * we rely on the fact that the hdr's psize - * was set to the psize of the block when it was - * last written. We set the csize to that value - * and zero out any part that should not contain - * data. - */ - bzero((char *)cbuf + csize, HDR_GET_PSIZE(hdr) - csize); - csize = HDR_GET_PSIZE(hdr); - } - zio_push_transform(zio, cbuf, csize, HDR_GET_PSIZE(hdr), NULL); - } - /* * Block pointers always store the checksum for the logical data. * If the block pointer has the gang bit set, then the checksum @@ -1465,11 +1503,9 @@ arc_cksum_is_equal(arc_buf_hdr_t *hdr, zio_t *zio) * generated using the correct checksum algorithm and accounts for the * logical I/O size and not just a gang fragment. */ - valid_cksum = (zio_checksum_error_impl(zio->io_spa, zio->io_bp, + return (zio_checksum_error_impl(zio->io_spa, zio->io_bp, BP_GET_CHECKSUM(zio->io_bp), zio->io_abd, zio->io_size, zio->io_offset, NULL) == 0); - zio_pop_transforms(zio); - return (valid_cksum); } /* @@ -1497,6 +1533,7 @@ arc_cksum_compute(arc_buf_t *buf) return; } + ASSERT(!ARC_BUF_ENCRYPTED(buf)); ASSERT(!ARC_BUF_COMPRESSED(buf)); hdr->b_l1hdr.b_freeze_cksum = kmem_alloc(sizeof (zio_cksum_t), KM_SLEEP); @@ -1659,15 +1696,14 @@ arc_hdr_set_compress(arc_buf_hdr_t *hdr, enum zio_compress cmp) */ if (!zfs_compressed_arc_enabled || HDR_GET_PSIZE(hdr) == 0) { arc_hdr_clear_flags(hdr, ARC_FLAG_COMPRESSED_ARC); - HDR_SET_COMPRESS(hdr, ZIO_COMPRESS_OFF); ASSERT(!HDR_COMPRESSION_ENABLED(hdr)); - ASSERT3U(HDR_GET_COMPRESS(hdr), ==, ZIO_COMPRESS_OFF); } else { arc_hdr_set_flags(hdr, ARC_FLAG_COMPRESSED_ARC); - HDR_SET_COMPRESS(hdr, cmp); - ASSERT3U(HDR_GET_COMPRESS(hdr), ==, cmp); ASSERT(HDR_COMPRESSION_ENABLED(hdr)); } + + HDR_SET_COMPRESS(hdr, cmp); + ASSERT3U(HDR_GET_COMPRESS(hdr), ==, cmp); } /* @@ -1708,6 +1744,113 @@ arc_buf_try_copy_decompressed_data(arc_buf_t *buf) return (copied); } +/* + * Return the size of the block, b_pabd, that is stored in the arc_buf_hdr_t. + */ +static uint64_t +arc_hdr_size(arc_buf_hdr_t *hdr) +{ + uint64_t size; + + if (arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF && + HDR_GET_PSIZE(hdr) > 0) { + size = HDR_GET_PSIZE(hdr); + } else { + ASSERT3U(HDR_GET_LSIZE(hdr), !=, 0); + size = HDR_GET_LSIZE(hdr); + } + return (size); +} + +/* + * This function will take a header that only has raw encrypted data in + * b_crypt_hdr.b_rabd and decrypts it into a new buffer which is stored in + * b_l1hdr.b_pabd. If designated on the header, this function will also + * decompress the data as well. + */ +static int +arc_hdr_decrypt(arc_buf_hdr_t *hdr, kmutex_t *hash_lock, spa_t *spa, + uint64_t dsobj) +{ + int ret; + dsl_crypto_key_t *dck = NULL; + abd_t *cabd = NULL; + void *tmp = NULL; + + ASSERT(HDR_HAS_RABD(hdr)); + ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + + if (hash_lock) + mutex_enter(hash_lock); + + arc_hdr_alloc_abd(hdr, B_FALSE); + + /* + * We must be careful to use the passed-in dsobj value here and + * not the value in b_dsobj. b_dsobj is meant to be a best guess for + * the L2ARC, which has the luxury of being able to fail without real + * consequences (the data simply won't make it to the L2ARC). In + * reality, the dsobj stored in the header may belong to a dataset + * that has been unmounted or otherwise disowned, meaning the key + * won't be accessible via that dsobj anymore. + */ + ret = spa_keystore_lookup_key(spa, dsobj, FTAG, &dck); + if (ret) { + ret = SET_ERROR(EACCES); + goto error; + } + + ret = zio_do_crypt_abd(B_FALSE, &dck->dck_key, + hdr->b_crypt_hdr.b_salt, hdr->b_crypt_hdr.b_ot, + hdr->b_crypt_hdr.b_iv, hdr->b_crypt_hdr.b_mac, + HDR_GET_PSIZE(hdr), hdr->b_l1hdr.b_pabd, + hdr->b_crypt_hdr.b_rabd); + if (ret && ret != ZIO_NO_ENCRYPTION_NEEDED) + goto error; + + if (HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF && + !HDR_COMPRESSION_ENABLED(hdr)) { + /* + * We want to make sure that we are correctly honoring the + * zfs_abd_scatter_enabled setting, so we allocate an abd here + * and then loan a buffer from it, rather than allocating a + * linear buffer and wrapping it in an abd later. + */ + cabd = arc_get_data_abd(hdr, arc_hdr_size(hdr), hdr); + tmp = abd_borrow_buf(cabd, arc_hdr_size(hdr)); + + ret = zio_decompress_data(HDR_GET_COMPRESS(hdr), + hdr->b_l1hdr.b_pabd, tmp, HDR_GET_PSIZE(hdr), + HDR_GET_LSIZE(hdr)); + if (ret) { + abd_return_buf(cabd, tmp, arc_hdr_size(hdr)); + goto error; + } + + abd_return_buf_copy(cabd, tmp, arc_hdr_size(hdr)); + arc_free_data_abd(hdr, hdr->b_l1hdr.b_pabd, + arc_hdr_size(hdr), hdr); + hdr->b_l1hdr.b_pabd = cabd; + } + + spa_keystore_dsl_key_rele(spa, dck, FTAG); + + if (hash_lock) + mutex_exit(hash_lock); + + return (0); + +error: + arc_hdr_free_abd(hdr, B_FALSE); + if (dck) + spa_keystore_dsl_key_rele(spa, dck, FTAG); + if (cabd) + arc_free_data_buf(hdr, cabd, arc_hdr_size(hdr), hdr); + if (hash_lock) + mutex_exit(hash_lock); + return (ret); +} + /* * Given a buf that has a data buffer attached to it, this function will * efficiently fill the buf with data of the specified compression setting from @@ -1722,18 +1865,75 @@ arc_buf_try_copy_decompressed_data(arc_buf_t *buf) * the correct-sized data buffer. */ static int -arc_buf_fill(arc_buf_t *buf, boolean_t compressed) +arc_buf_fill(arc_buf_t *buf, spa_t *spa, uint64_t dsobj, arc_fill_flags_t flags) { + int error; arc_buf_hdr_t *hdr = buf->b_hdr; - boolean_t hdr_compressed = (HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF); + boolean_t hdr_compressed = + (arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF); + boolean_t compressed = (flags & ARC_FILL_COMPRESSED) != 0; + boolean_t encrypted = (flags & ARC_FILL_ENCRYPTED) != 0; dmu_object_byteswap_t bswap = hdr->b_l1hdr.b_byteswap; + kmutex_t *hash_lock = (flags & ARC_FILL_LOCKED) ? NULL : HDR_LOCK(hdr); ASSERT3P(buf->b_data, !=, NULL); - IMPLY(compressed, hdr_compressed); + IMPLY(compressed, hdr_compressed || ARC_BUF_ENCRYPTED(buf)); IMPLY(compressed, ARC_BUF_COMPRESSED(buf)); + IMPLY(encrypted, HDR_ENCRYPT(hdr)); + IMPLY(encrypted, ARC_BUF_ENCRYPTED(buf)); + IMPLY(encrypted, ARC_BUF_COMPRESSED(buf)); + IMPLY(encrypted, !ARC_BUF_SHARED(buf)); + IMPLY((flags & ARC_FILL_IN_PLACE), ARC_BUF_ENCRYPTED(buf)); + IMPLY((flags & ARC_FILL_IN_PLACE), HDR_ENCRYPT(hdr)); + IMPLY((flags & ARC_FILL_IN_PLACE), + hdr->b_crypt_hdr.b_ot == DMU_OT_DNODE); + + if (encrypted) { + ASSERT(HDR_HAS_RABD(hdr)); + abd_copy_to_buf(buf->b_data, hdr->b_crypt_hdr.b_rabd, + HDR_GET_PSIZE(hdr)); + goto byteswap; + } else if (HDR_HAS_RABD(hdr) && hdr->b_l1hdr.b_pabd == NULL) { + /* + * If we only have the encrypted version of the data, but the + * unencrypted version was requested we take this opportunity + * to store the decrypted version in the header for future use. + */ + error = arc_hdr_decrypt(hdr, hash_lock, spa, dsobj); + if (error) + return (error); + } if (hdr_compressed == compressed) { - if (!arc_buf_is_shared(buf)) { + /* + * There is a special case here for dnode blocks which are + * decrypting their bonus buffers. These blocks may request to + * be decrypted in-place. This is necessary because there may + * be many dnodes pointing into this buffer and there is + * currently no method to synchronize replacing the backing + * b_data buffer and updating all of the pointers. If the need + * arises for other types to be decrypted in-place, they must + * add handling here as well. + */ + if (ARC_BUF_ENCRYPTED(buf) && + hdr->b_crypt_hdr.b_ot == DMU_OT_DNODE && + (flags & ARC_FILL_IN_PLACE)) { + ASSERT(!arc_buf_is_shared(buf)); + ASSERT(!compressed); + ASSERT(!encrypted); + zio_crypt_copy_dnode_bonus(hdr->b_l1hdr.b_pabd, + buf->b_data, arc_buf_size(buf)); + buf->b_flags &= ~ARC_BUF_FLAG_ENCRYPTED; + buf->b_flags &= ~ARC_BUF_FLAG_COMPRESSED; + + if (hash_lock) { + mutex_enter(hash_lock); + hdr->b_crypt_hdr.b_ebufcnt -= 1; + mutex_exit(hash_lock); + } else { + hdr->b_crypt_hdr.b_ebufcnt -= 1; + } + } else if (!arc_buf_is_shared(buf)) { abd_copy_to_buf(buf->b_data, hdr->b_l1hdr.b_pabd, arc_buf_size(buf)); } @@ -1785,7 +1985,7 @@ arc_buf_fill(arc_buf_t *buf, boolean_t compressed) ASSERT3P(hdr->b_l1hdr.b_freeze_cksum, !=, NULL); return (0); } else { - int error = zio_decompress_data(HDR_GET_COMPRESS(hdr), + error = zio_decompress_data(HDR_GET_COMPRESS(hdr), hdr->b_l1hdr.b_pabd, buf->b_data, HDR_GET_PSIZE(hdr), HDR_GET_LSIZE(hdr)); @@ -1796,13 +1996,14 @@ arc_buf_fill(arc_buf_t *buf, boolean_t compressed) if (error != 0) { zfs_dbgmsg( "hdr %p, compress %d, psize %d, lsize %d", - hdr, HDR_GET_COMPRESS(hdr), + hdr, arc_hdr_get_compress(hdr), HDR_GET_PSIZE(hdr), HDR_GET_LSIZE(hdr)); return (SET_ERROR(EIO)); } } } +byteswap: /* Byteswap the buf's data if necessary */ if (bswap != DMU_BSWAP_NUMFUNCS) { ASSERT(!HDR_SHARED_DATA(hdr)); @@ -1817,27 +2018,10 @@ arc_buf_fill(arc_buf_t *buf, boolean_t compressed) } int -arc_decompress(arc_buf_t *buf) +arc_untransform(arc_buf_t *buf, spa_t *spa, uint64_t dsobj, boolean_t in_place) { - return (arc_buf_fill(buf, B_FALSE)); -} - -/* - * Return the size of the block, b_pabd, that is stored in the arc_buf_hdr_t. - */ -static uint64_t -arc_hdr_size(arc_buf_hdr_t *hdr) -{ - uint64_t size; - - if (HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF && - HDR_GET_PSIZE(hdr) > 0) { - size = HDR_GET_PSIZE(hdr); - } else { - ASSERT3U(HDR_GET_LSIZE(hdr), !=, 0); - size = HDR_GET_LSIZE(hdr); - } - return (size); + return (arc_buf_fill(buf, spa, dsobj, + (in_place) ? ARC_FILL_IN_PLACE : 0)); } /* @@ -1857,6 +2041,7 @@ arc_evictable_space_increment(arc_buf_hdr_t *hdr, arc_state_t *state) ASSERT0(hdr->b_l1hdr.b_bufcnt); ASSERT3P(hdr->b_l1hdr.b_buf, ==, NULL); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); (void) refcount_add_many(&state->arcs_esize[type], HDR_GET_LSIZE(hdr), hdr); return; @@ -1867,6 +2052,11 @@ arc_evictable_space_increment(arc_buf_hdr_t *hdr, arc_state_t *state) (void) refcount_add_many(&state->arcs_esize[type], arc_hdr_size(hdr), hdr); } + if (HDR_HAS_RABD(hdr)) { + (void) refcount_add_many(&state->arcs_esize[type], + HDR_GET_PSIZE(hdr), hdr); + } + for (buf = hdr->b_l1hdr.b_buf; buf != NULL; buf = buf->b_next) { if (arc_buf_is_shared(buf)) continue; @@ -1892,6 +2082,7 @@ arc_evictable_space_decrement(arc_buf_hdr_t *hdr, arc_state_t *state) ASSERT0(hdr->b_l1hdr.b_bufcnt); ASSERT3P(hdr->b_l1hdr.b_buf, ==, NULL); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); (void) refcount_remove_many(&state->arcs_esize[type], HDR_GET_LSIZE(hdr), hdr); return; @@ -1902,6 +2093,11 @@ arc_evictable_space_decrement(arc_buf_hdr_t *hdr, arc_state_t *state) (void) refcount_remove_many(&state->arcs_esize[type], arc_hdr_size(hdr), hdr); } + if (HDR_HAS_RABD(hdr)) { + (void) refcount_remove_many(&state->arcs_esize[type], + HDR_GET_PSIZE(hdr), hdr); + } + for (buf = hdr->b_l1hdr.b_buf; buf != NULL; buf = buf->b_next) { if (arc_buf_is_shared(buf)) continue; @@ -2045,7 +2241,8 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *hdr, old_state = hdr->b_l1hdr.b_state; refcnt = refcount_count(&hdr->b_l1hdr.b_refcnt); bufcnt = hdr->b_l1hdr.b_bufcnt; - update_old = (bufcnt > 0 || hdr->b_l1hdr.b_pabd != NULL); + update_old = (bufcnt > 0 || hdr->b_l1hdr.b_pabd != NULL || + HDR_HAS_RABD(hdr)); } else { old_state = arc_l2c_only; refcnt = 0; @@ -2115,6 +2312,7 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *hdr, (void) refcount_add_many(&new_state->arcs_size, HDR_GET_LSIZE(hdr), hdr); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); } else { arc_buf_t *buf; uint32_t buffers = 0; @@ -2147,8 +2345,11 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *hdr, if (hdr->b_l1hdr.b_pabd != NULL) { (void) refcount_add_many(&new_state->arcs_size, arc_hdr_size(hdr), hdr); - } else { - ASSERT(GHOST_STATE(old_state)); + } + + if (HDR_HAS_RABD(hdr)) { + (void) refcount_add_many(&new_state->arcs_size, + HDR_GET_PSIZE(hdr), hdr); } } } @@ -2158,6 +2359,7 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *hdr, if (GHOST_STATE(old_state)) { ASSERT0(bufcnt); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); /* * When moving a header off of a ghost state, @@ -2198,9 +2400,20 @@ arc_change_state(arc_state_t *new_state, arc_buf_hdr_t *hdr, buf); } ASSERT3U(bufcnt, ==, buffers); - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); - (void) refcount_remove_many( - &old_state->arcs_size, arc_hdr_size(hdr), hdr); + ASSERT(hdr->b_l1hdr.b_pabd != NULL || + HDR_HAS_RABD(hdr)); + + if (hdr->b_l1hdr.b_pabd != NULL) { + (void) refcount_remove_many( + &old_state->arcs_size, arc_hdr_size(hdr), + hdr); + } + + if (HDR_HAS_RABD(hdr)) { + (void) refcount_remove_many( + &old_state->arcs_size, HDR_GET_PSIZE(hdr), + hdr); + } } } @@ -2304,10 +2517,11 @@ arc_can_share(arc_buf_hdr_t *hdr, arc_buf_t *buf) boolean_t hdr_compressed, buf_compressed; /* * The criteria for sharing a hdr's data are: - * 1. the hdr's compression matches the buf's compression - * 2. the hdr doesn't need to be byteswapped - * 3. the hdr isn't already being shared - * 4. the buf is either compressed or it is the last buf in the hdr list + * 1. the buffer is not encrypted + * 2. the hdr's compression matches the buf's compression + * 3. the hdr doesn't need to be byteswapped + * 4. the hdr isn't already being shared + * 5. the buf is either compressed or it is the last buf in the hdr list * * Criterion #4 maintains the invariant that shared uncompressed * bufs must be the final buf in the hdr's b_buf list. Reading this, you @@ -2324,9 +2538,10 @@ arc_can_share(arc_buf_hdr_t *hdr, arc_buf_t *buf) * sharing if the new buf isn't the first to be added. */ ASSERT3P(buf->b_hdr, ==, hdr); - hdr_compressed = HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF; + hdr_compressed = arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF; buf_compressed = ARC_BUF_COMPRESSED(buf) != 0; - return (buf_compressed == hdr_compressed && + return (!ARC_BUF_ENCRYPTED(buf) && + buf_compressed == hdr_compressed && hdr->b_l1hdr.b_byteswap == DMU_BSWAP_NUMFUNCS && !HDR_SHARED_DATA(hdr) && (ARC_BUF_LAST(buf) || ARC_BUF_COMPRESSED(buf))); @@ -2338,11 +2553,12 @@ arc_can_share(arc_buf_hdr_t *hdr, arc_buf_t *buf) * copy was made successfully, or an error code otherwise. */ static int -arc_buf_alloc_impl(arc_buf_hdr_t *hdr, void *tag, boolean_t compressed, - boolean_t fill, arc_buf_t **ret) +arc_buf_alloc_impl(arc_buf_hdr_t *hdr, spa_t *spa, uint64_t dsobj, void *tag, + boolean_t encrypted, boolean_t compressed, boolean_t fill, arc_buf_t **ret) { arc_buf_t *buf; boolean_t can_share; + arc_fill_flags_t flags = ARC_FILL_LOCKED; ASSERT(HDR_HAS_L1HDR(hdr)); ASSERT3U(HDR_GET_LSIZE(hdr), >, 0); @@ -2350,6 +2566,7 @@ arc_buf_alloc_impl(arc_buf_hdr_t *hdr, void *tag, boolean_t compressed, hdr->b_type == ARC_BUFC_METADATA); ASSERT3P(ret, !=, NULL); ASSERT3P(*ret, ==, NULL); + IMPLY(encrypted, compressed); hdr->b_l1hdr.b_mru_hits = 0; hdr->b_l1hdr.b_mru_ghost_hits = 0; @@ -2373,18 +2590,19 @@ arc_buf_alloc_impl(arc_buf_hdr_t *hdr, void *tag, boolean_t compressed, /* * Only honor requests for compressed bufs if the hdr is actually - * compressed. + * compressed. This must be overriden if the buffer is encrypted since + * encrypted buffers cannot be decompressed. */ - if (compressed && HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF) + if (encrypted && HDR_ENCRYPT(hdr)) { buf->b_flags |= ARC_BUF_FLAG_COMPRESSED; - - /* - * Although the ARC should handle it correctly, levels above the ARC - * should prevent us from having multiple compressed bufs off the same - * hdr. To ensure we notice it if this behavior changes, we assert this - * here the best we can. - */ - IMPLY(ARC_BUF_COMPRESSED(buf), !HDR_SHARED_DATA(hdr)); + buf->b_flags |= ARC_BUF_FLAG_ENCRYPTED; + flags |= ARC_FILL_COMPRESSED | ARC_FILL_ENCRYPTED; + } else if (compressed && + arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF) { + ASSERT(!encrypted); + buf->b_flags |= ARC_BUF_FLAG_COMPRESSED; + flags |= ARC_FILL_COMPRESSED; + } /* * If the hdr's data can be shared then we share the data buffer and @@ -2400,7 +2618,7 @@ arc_buf_alloc_impl(arc_buf_hdr_t *hdr, void *tag, boolean_t compressed, * need to be ABD-aware. */ can_share = arc_can_share(hdr, buf) && !HDR_L2_WRITING(hdr) && - abd_is_linear(hdr->b_l1hdr.b_pabd); + hdr->b_l1hdr.b_pabd != NULL && abd_is_linear(hdr->b_l1hdr.b_pabd); /* Set up b_data and sharing */ if (can_share) { @@ -2416,13 +2634,15 @@ arc_buf_alloc_impl(arc_buf_hdr_t *hdr, void *tag, boolean_t compressed, hdr->b_l1hdr.b_buf = buf; hdr->b_l1hdr.b_bufcnt += 1; + if (encrypted) + hdr->b_crypt_hdr.b_ebufcnt += 1; /* * If the user wants the data from the hdr, we need to either copy or * decompress the data. */ if (fill) { - return (arc_buf_fill(buf, ARC_BUF_COMPRESSED(buf) != 0)); + return (arc_buf_fill(buf, spa, dsobj, flags)); } return (0); @@ -2502,11 +2722,11 @@ l2arc_free_abd_on_write(abd_t *abd, size_t size, arc_buf_contents_t type) } static void -arc_hdr_free_on_write(arc_buf_hdr_t *hdr) +arc_hdr_free_on_write(arc_buf_hdr_t *hdr, boolean_t free_rdata) { arc_state_t *state = hdr->b_l1hdr.b_state; arc_buf_contents_t type = arc_buf_type(hdr); - uint64_t size = arc_hdr_size(hdr); + uint64_t size = (free_rdata) ? HDR_GET_PSIZE(hdr) : arc_hdr_size(hdr); /* protected by hash lock, if in the hash table */ if (multilist_link_active(&hdr->b_l1hdr.b_arc_node)) { @@ -2518,7 +2738,11 @@ arc_hdr_free_on_write(arc_buf_hdr_t *hdr) } (void) refcount_remove_many(&state->arcs_size, size, hdr); - l2arc_free_abd_on_write(hdr->b_l1hdr.b_pabd, size, type); + if (free_rdata) { + l2arc_free_abd_on_write(hdr->b_crypt_hdr.b_rabd, size, type); + } else { + l2arc_free_abd_on_write(hdr->b_l1hdr.b_pabd, size, type); + } } /* @@ -2531,6 +2755,7 @@ arc_share_buf(arc_buf_hdr_t *hdr, arc_buf_t *buf) { ASSERT(arc_can_share(hdr, buf)); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!ARC_BUF_ENCRYPTED(buf)); ASSERT(MUTEX_HELD(HDR_LOCK(hdr)) || HDR_EMPTY(hdr)); /* @@ -2632,6 +2857,7 @@ arc_buf_destroy_impl(arc_buf_t *buf) { arc_buf_t *lastbuf; arc_buf_hdr_t *hdr = buf->b_hdr; + uint32_t ebufcnt = 0; /* * Free up the data associated with the buf but only if we're not @@ -2659,6 +2885,18 @@ arc_buf_destroy_impl(arc_buf_t *buf) ASSERT(hdr->b_l1hdr.b_bufcnt > 0); hdr->b_l1hdr.b_bufcnt -= 1; + + if (ARC_BUF_ENCRYPTED(buf)) + hdr->b_crypt_hdr.b_ebufcnt -= 1; + + /* + * if we have no more encrypted buffers and we've already + * gotten a copy of the decrypted data we can free b_rabd to + * save some space. + */ + if (hdr->b_crypt_hdr.b_ebufcnt == 0 && HDR_HAS_RABD(hdr) && + hdr->b_l1hdr.b_pabd != NULL) + arc_hdr_free_abd(hdr, B_TRUE); } lastbuf = arc_buf_remove(hdr, buf); @@ -2673,16 +2911,17 @@ arc_buf_destroy_impl(arc_buf_t *buf) * There is an equivalent case for compressed bufs, but since * they aren't guaranteed to be the last buf in the list and * that is an exceedingly rare case, we just allow that space be - * wasted temporarily. + * wasted temporarily. We must also be careful not to share + * encrypted buffers, since they cannot be shared. */ - if (lastbuf != NULL) { + if (lastbuf != NULL && !ARC_BUF_ENCRYPTED(lastbuf)) { /* Only one buf can be shared at once */ VERIFY(!arc_buf_is_shared(lastbuf)); /* hdr is uncompressed so can't have compressed buf */ VERIFY(!ARC_BUF_COMPRESSED(lastbuf)); ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); - arc_hdr_free_pabd(hdr); + arc_hdr_free_abd(hdr, B_FALSE); /* * We must setup a new shared block between the @@ -2703,10 +2942,13 @@ arc_buf_destroy_impl(arc_buf_t *buf) */ ASSERT3P(lastbuf, !=, NULL); ASSERT(arc_buf_is_shared(lastbuf) || - HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF); + arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF); } - if (hdr->b_l1hdr.b_bufcnt == 0) + if (HDR_ENCRYPT(hdr)) + ebufcnt = hdr->b_crypt_hdr.b_ebufcnt; + + if (hdr->b_l1hdr.b_bufcnt - ebufcnt == 0) arc_cksum_free(hdr); /* clean up the buf */ @@ -2715,26 +2957,43 @@ arc_buf_destroy_impl(arc_buf_t *buf) } static void -arc_hdr_alloc_pabd(arc_buf_hdr_t *hdr) +arc_hdr_alloc_abd(arc_buf_hdr_t *hdr, boolean_t alloc_rdata) { + uint64_t size; + ASSERT3U(HDR_GET_LSIZE(hdr), >, 0); ASSERT(HDR_HAS_L1HDR(hdr)); - ASSERT(!HDR_SHARED_DATA(hdr)); + ASSERT(!HDR_SHARED_DATA(hdr) || alloc_rdata); + IMPLY(alloc_rdata, HDR_ENCRYPT(hdr)); - ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); - hdr->b_l1hdr.b_pabd = arc_get_data_abd(hdr, arc_hdr_size(hdr), hdr); - hdr->b_l1hdr.b_byteswap = DMU_BSWAP_NUMFUNCS; - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); + if (hdr->b_l1hdr.b_pabd == NULL && !HDR_HAS_RABD(hdr)) + hdr->b_l1hdr.b_byteswap = DMU_BSWAP_NUMFUNCS; - ARCSTAT_INCR(arcstat_compressed_size, arc_hdr_size(hdr)); + if (alloc_rdata) { + size = HDR_GET_PSIZE(hdr); + ASSERT3P(hdr->b_crypt_hdr.b_rabd, ==, NULL); + hdr->b_crypt_hdr.b_rabd = arc_get_data_abd(hdr, size, hdr); + ASSERT3P(hdr->b_crypt_hdr.b_rabd, !=, NULL); + ARCSTAT_INCR(arcstat_raw_size, size); + } else { + size = arc_hdr_size(hdr); + ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + hdr->b_l1hdr.b_pabd = arc_get_data_abd(hdr, size, hdr); + ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); + } + + ARCSTAT_INCR(arcstat_compressed_size, size); ARCSTAT_INCR(arcstat_uncompressed_size, HDR_GET_LSIZE(hdr)); } static void -arc_hdr_free_pabd(arc_buf_hdr_t *hdr) +arc_hdr_free_abd(arc_buf_hdr_t *hdr, boolean_t free_rdata) { + uint64_t size = (free_rdata) ? HDR_GET_PSIZE(hdr) : arc_hdr_size(hdr); + ASSERT(HDR_HAS_L1HDR(hdr)); - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); + ASSERT(hdr->b_l1hdr.b_pabd != NULL || HDR_HAS_RABD(hdr)); + IMPLY(free_rdata, HDR_HAS_RABD(hdr)); /* * If the hdr is currently being written to the l2arc then @@ -2743,28 +3002,42 @@ arc_hdr_free_pabd(arc_buf_hdr_t *hdr) * writing it to the l2arc device. */ if (HDR_L2_WRITING(hdr)) { - arc_hdr_free_on_write(hdr); + arc_hdr_free_on_write(hdr, free_rdata); ARCSTAT_BUMP(arcstat_l2_free_on_write); + } else if (free_rdata) { + arc_free_data_abd(hdr, hdr->b_crypt_hdr.b_rabd, size, hdr); } else { - arc_free_data_abd(hdr, hdr->b_l1hdr.b_pabd, - arc_hdr_size(hdr), hdr); + arc_free_data_abd(hdr, hdr->b_l1hdr.b_pabd, size, hdr); } - hdr->b_l1hdr.b_pabd = NULL; - hdr->b_l1hdr.b_byteswap = DMU_BSWAP_NUMFUNCS; - ARCSTAT_INCR(arcstat_compressed_size, -arc_hdr_size(hdr)); + if (free_rdata) { + hdr->b_crypt_hdr.b_rabd = NULL; + ARCSTAT_INCR(arcstat_raw_size, -size); + } else { + hdr->b_l1hdr.b_pabd = NULL; + } + + if (hdr->b_l1hdr.b_pabd == NULL && !HDR_HAS_RABD(hdr)) + hdr->b_l1hdr.b_byteswap = DMU_BSWAP_NUMFUNCS; + + ARCSTAT_INCR(arcstat_compressed_size, -size); ARCSTAT_INCR(arcstat_uncompressed_size, -HDR_GET_LSIZE(hdr)); } static arc_buf_hdr_t * arc_hdr_alloc(uint64_t spa, int32_t psize, int32_t lsize, - enum zio_compress compression_type, arc_buf_contents_t type) + boolean_t encrypted, enum zio_compress compression_type, + arc_buf_contents_t type, boolean_t alloc_rdata) { arc_buf_hdr_t *hdr; VERIFY(type == ARC_BUFC_DATA || type == ARC_BUFC_METADATA); + if (encrypted) { + hdr = kmem_cache_alloc(hdr_full_crypt_cache, KM_PUSHPAGE); + } else { + hdr = kmem_cache_alloc(hdr_full_cache, KM_PUSHPAGE); + } - hdr = kmem_cache_alloc(hdr_full_cache, KM_PUSHPAGE); ASSERT(HDR_EMPTY(hdr)); ASSERT3P(hdr->b_l1hdr.b_freeze_cksum, ==, NULL); HDR_SET_PSIZE(hdr, psize); @@ -2774,6 +3047,8 @@ arc_hdr_alloc(uint64_t spa, int32_t psize, int32_t lsize, hdr->b_flags = 0; arc_hdr_set_flags(hdr, arc_bufc_to_flags(type) | ARC_FLAG_HAS_L1HDR); arc_hdr_set_compress(hdr, compression_type); + if (encrypted) + arc_hdr_set_flags(hdr, ARC_FLAG_ENCRYPT); hdr->b_l1hdr.b_state = arc_anon; hdr->b_l1hdr.b_arc_access = 0; @@ -2785,7 +3060,7 @@ arc_hdr_alloc(uint64_t spa, int32_t psize, int32_t lsize, * the compressed or uncompressed data depending on the block * it references and compressed arc enablement. */ - arc_hdr_alloc_pabd(hdr); + arc_hdr_alloc_abd(hdr, alloc_rdata); ASSERT(refcount_is_zero(&hdr->b_l1hdr.b_refcnt)); return (hdr); @@ -2808,6 +3083,16 @@ arc_hdr_realloc(arc_buf_hdr_t *hdr, kmem_cache_t *old, kmem_cache_t *new) ASSERT((old == hdr_full_cache && new == hdr_l2only_cache) || (old == hdr_l2only_cache && new == hdr_full_cache)); + /* + * if the caller wanted a new full header and the header is to be + * encrypted we will actually allocate the from the full crypt + * cache instead. The same applies to freeing from the old cache. + */ + if (HDR_ENCRYPT(hdr) && new == hdr_full_cache) + new = hdr_full_crypt_cache; + if (HDR_ENCRYPT(hdr) && old == hdr_full_cache) + old = hdr_full_crypt_cache; + nhdr = kmem_cache_alloc(new, KM_PUSHPAGE); ASSERT(MUTEX_HELD(HDR_LOCK(hdr))); @@ -2815,7 +3100,7 @@ arc_hdr_realloc(arc_buf_hdr_t *hdr, kmem_cache_t *old, kmem_cache_t *new) bcopy(hdr, nhdr, HDR_L2ONLY_SIZE); - if (new == hdr_full_cache) { + if (new == hdr_full_cache || new == hdr_full_crypt_cache) { arc_hdr_set_flags(nhdr, ARC_FLAG_HAS_L1HDR); /* * arc_access and arc_change_state need to be aware that a @@ -2826,6 +3111,7 @@ arc_hdr_realloc(arc_buf_hdr_t *hdr, kmem_cache_t *old, kmem_cache_t *new) /* Verify previous threads set to NULL before freeing */ ASSERT3P(nhdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); } else { ASSERT3P(hdr->b_l1hdr.b_buf, ==, NULL); ASSERT0(hdr->b_l1hdr.b_bufcnt); @@ -2848,6 +3134,7 @@ arc_hdr_realloc(arc_buf_hdr_t *hdr, kmem_cache_t *old, kmem_cache_t *new) */ VERIFY(!HDR_L2_WRITING(hdr)); VERIFY3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); arc_hdr_clear_flags(nhdr, ARC_FLAG_HAS_L1HDR); } @@ -2889,6 +3176,65 @@ arc_hdr_realloc(arc_buf_hdr_t *hdr, kmem_cache_t *old, kmem_cache_t *new) return (nhdr); } +/* + * This function allows an L1 header to be reallocated as a crypt + * header and vice versa. If going to a crypt header, the new fields + * will be zeroed out. + */ +static arc_buf_hdr_t * +arc_hdr_realloc_crypt(arc_buf_hdr_t *hdr, boolean_t encrypt) +{ + arc_buf_hdr_t *nhdr; + arc_buf_t *buf; + kmem_cache_t *ncache, *ocache; + + ASSERT(HDR_HAS_L1HDR(hdr)); + ASSERT3U(!!HDR_ENCRYPT(hdr), !=, encrypt); + ASSERT3P(hdr->b_l1hdr.b_state, ==, arc_anon); + ASSERT(!multilist_link_active(&hdr->b_l1hdr.b_arc_node)); + + if (encrypt) { + ncache = hdr_full_crypt_cache; + ocache = hdr_full_cache; + } else { + ncache = hdr_full_cache; + ocache = hdr_full_crypt_cache; + } + + nhdr = kmem_cache_alloc(ncache, KM_PUSHPAGE); + bcopy(hdr, nhdr, HDR_L2ONLY_SIZE); + nhdr->b_l1hdr.b_freeze_cksum = hdr->b_l1hdr.b_freeze_cksum; + + nhdr->b_l1hdr.b_buf = hdr->b_l1hdr.b_buf; + for (buf = nhdr->b_l1hdr.b_buf; buf != NULL; buf = buf->b_next) { + buf->b_hdr = nhdr; + } + + nhdr->b_l1hdr.b_bufcnt = hdr->b_l1hdr.b_bufcnt; + nhdr->b_l1hdr.b_byteswap = hdr->b_l1hdr.b_byteswap; + nhdr->b_l1hdr.b_state = hdr->b_l1hdr.b_state; + nhdr->b_l1hdr.b_arc_access = hdr->b_l1hdr.b_arc_access; + nhdr->b_l1hdr.b_mru_hits = hdr->b_l1hdr.b_mru_hits; + nhdr->b_l1hdr.b_mru_ghost_hits = hdr->b_l1hdr.b_mru_ghost_hits; + nhdr->b_l1hdr.b_mfu_hits = hdr->b_l1hdr.b_mfu_hits; + nhdr->b_l1hdr.b_mfu_ghost_hits = hdr->b_l1hdr.b_mfu_ghost_hits; + nhdr->b_l1hdr.b_l2_hits = hdr->b_l1hdr.b_l2_hits; + refcount_transfer(&nhdr->b_l1hdr.b_refcnt, &hdr->b_l1hdr.b_refcnt); + nhdr->b_l1hdr.b_acb = hdr->b_l1hdr.b_acb; + nhdr->b_l1hdr.b_pabd = hdr->b_l1hdr.b_pabd; + + if (encrypt) { + arc_hdr_set_flags(nhdr, ARC_FLAG_ENCRYPT); + } else { + arc_hdr_clear_flags(nhdr, ARC_FLAG_ENCRYPT); + } + + buf_discard_identity(hdr); + kmem_cache_free(ocache, hdr); + + return (nhdr); +} + /* * Allocate a new arc_buf_hdr_t and arc_buf_t and return the buf to the caller. * The buf is returned thawed since we expect the consumer to modify it. @@ -2898,11 +3244,12 @@ arc_alloc_buf(spa_t *spa, void *tag, arc_buf_contents_t type, int32_t size) { arc_buf_t *buf; arc_buf_hdr_t *hdr = arc_hdr_alloc(spa_load_guid(spa), size, size, - ZIO_COMPRESS_OFF, type); + B_FALSE, ZIO_COMPRESS_OFF, type, B_FALSE); ASSERT(!MUTEX_HELD(HDR_LOCK(hdr))); buf = NULL; - VERIFY0(arc_buf_alloc_impl(hdr, tag, B_FALSE, B_FALSE, &buf)); + VERIFY0(arc_buf_alloc_impl(hdr, spa, 0, tag, B_FALSE, + B_FALSE, B_FALSE, &buf)); arc_buf_thaw(buf); return (buf); @@ -2923,12 +3270,13 @@ arc_alloc_compressed_buf(spa_t *spa, void *tag, uint64_t psize, uint64_t lsize, ASSERT(compression_type > ZIO_COMPRESS_OFF); ASSERT(compression_type < ZIO_COMPRESS_FUNCTIONS); - hdr = arc_hdr_alloc(spa_load_guid(spa), psize, lsize, - compression_type, ARC_BUFC_DATA); + hdr = arc_hdr_alloc(spa_load_guid(spa), psize, lsize, B_FALSE, + compression_type, ARC_BUFC_DATA, B_FALSE); ASSERT(!MUTEX_HELD(HDR_LOCK(hdr))); buf = NULL; - VERIFY0(arc_buf_alloc_impl(hdr, tag, B_TRUE, B_FALSE, &buf)); + VERIFY0(arc_buf_alloc_impl(hdr, spa, 0, tag, B_FALSE, + B_TRUE, B_FALSE, &buf)); arc_buf_thaw(buf); ASSERT3P(hdr->b_l1hdr.b_freeze_cksum, ==, NULL); @@ -2940,7 +3288,7 @@ arc_alloc_compressed_buf(spa_t *spa, void *tag, uint64_t psize, uint64_t lsize, * buf and the hdr. */ ASSERT(!abd_is_linear(hdr->b_l1hdr.b_pabd)); - arc_hdr_free_pabd(hdr); + arc_hdr_free_abd(hdr, B_FALSE); arc_share_buf(hdr, buf); } @@ -3012,15 +3360,25 @@ arc_hdr_destroy(arc_buf_hdr_t *hdr) while (hdr->b_l1hdr.b_buf != NULL) arc_buf_destroy_impl(hdr->b_l1hdr.b_buf); - if (hdr->b_l1hdr.b_pabd != NULL) - arc_hdr_free_pabd(hdr); + if (hdr->b_l1hdr.b_pabd != NULL) { + arc_hdr_free_abd(hdr, B_FALSE); + } + + if (HDR_HAS_RABD(hdr)) { + arc_hdr_free_abd(hdr, B_TRUE); + } } ASSERT3P(hdr->b_hash_next, ==, NULL); if (HDR_HAS_L1HDR(hdr)) { ASSERT(!multilist_link_active(&hdr->b_l1hdr.b_arc_node)); ASSERT3P(hdr->b_l1hdr.b_acb, ==, NULL); - kmem_cache_free(hdr_full_cache, hdr); + + if (!HDR_ENCRYPT(hdr)) { + kmem_cache_free(hdr_full_cache, hdr); + } else { + kmem_cache_free(hdr_full_crypt_cache, hdr); + } } else { kmem_cache_free(hdr_l2only_cache, hdr); } @@ -3097,6 +3455,7 @@ arc_evict_hdr(arc_buf_hdr_t *hdr, kmutex_t *hash_lock) if (HDR_HAS_L2HDR(hdr)) { ASSERT(hdr->b_l1hdr.b_pabd == NULL); + ASSERT(!HDR_HAS_RABD(hdr)); /* * This buffer is cached on the 2nd Level ARC; * don't destroy the header. @@ -3163,7 +3522,11 @@ arc_evict_hdr(arc_buf_hdr_t *hdr, kmutex_t *hash_lock) * This ensures that the accounting is updated correctly * in arc_free_data_impl(). */ - arc_hdr_free_pabd(hdr); + if (hdr->b_l1hdr.b_pabd != NULL) + arc_hdr_free_abd(hdr, B_FALSE); + + if (HDR_HAS_RABD(hdr)) + arc_hdr_free_abd(hdr, B_TRUE); arc_change_state(evicted_state, hdr, hash_lock); ASSERT(HDR_IN_HASH_TABLE(hdr)); @@ -4834,22 +5197,22 @@ arc_access(arc_buf_hdr_t *hdr, kmutex_t *hash_lock) } } -/* a generic arc_done_func_t which you can use */ +/* a generic arc_read_done_func_t which you can use */ /* ARGSUSED */ void -arc_bcopy_func(zio_t *zio, arc_buf_t *buf, void *arg) +arc_bcopy_func(zio_t *zio, int error, arc_buf_t *buf, void *arg) { - if (zio == NULL || zio->io_error == 0) + if (error == 0 && (zio == NULL || zio->io_error == 0)) bcopy(buf->b_data, arg, arc_buf_size(buf)); arc_buf_destroy(buf, arg); } -/* a generic arc_done_func_t */ +/* a generic arc_read_done_func_t */ void -arc_getbuf_func(zio_t *zio, arc_buf_t *buf, void *arg) +arc_getbuf_func(zio_t *zio, int error, arc_buf_t *buf, void *arg) { arc_buf_t **bufp = arg; - if (zio && zio->io_error) { + if (error == 0 && zio && zio->io_error) { arc_buf_destroy(buf, arg); *bufp = NULL; } else { @@ -4863,20 +5226,22 @@ arc_hdr_verify(arc_buf_hdr_t *hdr, blkptr_t *bp) { if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp)) { ASSERT3U(HDR_GET_PSIZE(hdr), ==, 0); - ASSERT3U(HDR_GET_COMPRESS(hdr), ==, ZIO_COMPRESS_OFF); + ASSERT3U(arc_hdr_get_compress(hdr), ==, ZIO_COMPRESS_OFF); } else { if (HDR_COMPRESSION_ENABLED(hdr)) { - ASSERT3U(HDR_GET_COMPRESS(hdr), ==, + ASSERT3U(arc_hdr_get_compress(hdr), ==, BP_GET_COMPRESS(bp)); } ASSERT3U(HDR_GET_LSIZE(hdr), ==, BP_GET_LSIZE(bp)); ASSERT3U(HDR_GET_PSIZE(hdr), ==, BP_GET_PSIZE(bp)); + ASSERT3U(!!HDR_ENCRYPT(hdr), ==, BP_IS_ENCRYPTED(bp)); } } static void arc_read_done(zio_t *zio) { + blkptr_t *bp = zio->io_bp; arc_buf_hdr_t *hdr = zio->io_private; kmutex_t *hash_lock = NULL; arc_callback_t *callback_list; @@ -4909,6 +5274,29 @@ arc_read_done(zio_t *zio) ASSERT3P(hash_lock, !=, NULL); } + if (BP_IS_ENCRYPTED(bp)) { + uint8_t tmpiv[DATA_IV_LEN]; + void *tmpbuf; + + hdr->b_crypt_hdr.b_ot = BP_GET_TYPE(bp); + hdr->b_crypt_hdr.b_dsobj = zio->io_bookmark.zb_objset; + zio_crypt_decode_params_bp(bp, hdr->b_crypt_hdr.b_salt, tmpiv); + + if (BP_GET_TYPE(bp) == DMU_OT_INTENT_LOG) { + tmpbuf = abd_borrow_buf_copy(zio->io_abd, + sizeof (zil_chain_t)); + zio_crypt_decode_mac_zil(tmpbuf, + hdr->b_crypt_hdr.b_mac); + zio_crypt_derive_zil_iv(tmpbuf, tmpiv, + hdr->b_crypt_hdr.b_iv); + abd_return_buf(zio->io_abd, tmpbuf, + sizeof (zil_chain_t)); + } else { + zio_crypt_decode_mac_bp(bp, hdr->b_crypt_hdr.b_mac); + bcopy(tmpiv, hdr->b_crypt_hdr.b_iv, DATA_IV_LEN); + } + } + if (no_zio_error) { /* byteswap if necessary */ if (BP_SHOULD_BYTESWAP(zio->io_bp)) { @@ -4955,8 +5343,10 @@ arc_read_done(zio_t *zio) callback_cnt++; - error = arc_buf_alloc_impl(hdr, acb->acb_private, - acb->acb_compressed, no_zio_error, &acb->acb_buf); + error = arc_buf_alloc_impl(hdr, zio->io_spa, + zio->io_bookmark.zb_objset, acb->acb_private, + acb->acb_encrypted, acb->acb_compressed, no_zio_error, + &acb->acb_buf); if (no_zio_error) { zio->io_error = error; } @@ -4965,8 +5355,7 @@ arc_read_done(zio_t *zio) arc_hdr_clear_flags(hdr, ARC_FLAG_IO_IN_PROGRESS); if (callback_cnt == 0) { ASSERT(HDR_PREFETCH(hdr)); - ASSERT0(hdr->b_l1hdr.b_bufcnt); - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); + ASSERT(hdr->b_l1hdr.b_pabd != NULL || HDR_HAS_RABD(hdr)); } ASSERT(refcount_is_zero(&hdr->b_l1hdr.b_refcnt) || @@ -5006,7 +5395,7 @@ arc_read_done(zio_t *zio) /* execute each callback and free its structure */ while ((acb = callback_list) != NULL) { if (acb->acb_done) - acb->acb_done(zio, acb->acb_buf, acb->acb_private); + acb->acb_done(zio, 0, acb->acb_buf, acb->acb_private); if (acb->acb_zio_dummy != NULL) { acb->acb_zio_dummy->io_error = zio->io_error; @@ -5040,15 +5429,17 @@ arc_read_done(zio_t *zio) * for readers of this block. */ int -arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, - void *private, zio_priority_t priority, int zio_flags, - arc_flags_t *arc_flags, const zbookmark_phys_t *zb) +arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, + arc_read_done_func_t *done, void *private, zio_priority_t priority, + int zio_flags, arc_flags_t *arc_flags, const zbookmark_phys_t *zb) { arc_buf_hdr_t *hdr = NULL; kmutex_t *hash_lock = NULL; zio_t *rzio; uint64_t guid = spa_load_guid(spa); - boolean_t compressed_read = (zio_flags & ZIO_FLAG_RAW) != 0; + boolean_t compressed_read = (zio_flags & ZIO_FLAG_RAW_COMPRESS) != 0; + boolean_t encrypted_read = BP_IS_ENCRYPTED(bp) && + (zio_flags & ZIO_FLAG_RAW_ENCRYPT) != 0; int rc = 0; ASSERT(!BP_IS_EMBEDDED(bp) || @@ -5063,7 +5454,14 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, hdr = buf_hash_find(guid, bp, &hash_lock); } - if (hdr != NULL && HDR_HAS_L1HDR(hdr) && hdr->b_l1hdr.b_pabd != NULL) { + /* + * Determine if we have an L1 cache hit or a cache miss. For simplicity + * we maintain encrypted data seperately from compressed / uncompressed + * data. If the user is requesting + */ + if (hdr != NULL && HDR_HAS_L1HDR(hdr) && + (hdr->b_l1hdr.b_pabd != NULL || HDR_HAS_RABD(hdr)) && + (!encrypted_read || !BP_IS_ENCRYPTED(bp) || HDR_HAS_RABD(hdr))) { arc_buf_t *buf = NULL; *arc_flags |= ARC_FLAG_CACHED; @@ -5149,8 +5547,9 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, ASSERT(!BP_IS_EMBEDDED(bp) || !BP_IS_HOLE(bp)); /* Get a buf with the desired data in it. */ - VERIFY0(arc_buf_alloc_impl(hdr, private, - compressed_read, B_TRUE, &buf)); + rc = arc_buf_alloc_impl(hdr, spa, zb->zb_objset, + private, encrypted_read, compressed_read, B_TRUE, + &buf); } else if (*arc_flags & ARC_FLAG_PREFETCH && refcount_count(&hdr->b_l1hdr.b_refcnt) == 0) { arc_hdr_set_flags(hdr, ARC_FLAG_PREFETCH); @@ -5166,7 +5565,7 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, data, metadata, hits); if (done) - done(NULL, buf, private); + done(NULL, rc, buf, private); } else { uint64_t lsize = BP_GET_LSIZE(bp); uint64_t psize = BP_GET_PSIZE(bp); @@ -5175,6 +5574,7 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, uint64_t addr = 0; boolean_t devw = B_FALSE; uint64_t size; + void *hdr_abd; /* * Gracefully handle a damaged logical block size as a @@ -5190,7 +5590,8 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, arc_buf_hdr_t *exists = NULL; arc_buf_contents_t type = BP_GET_BUFC_TYPE(bp); hdr = arc_hdr_alloc(spa_load_guid(spa), psize, lsize, - BP_GET_COMPRESS(bp), type); + BP_IS_ENCRYPTED(bp), BP_GET_COMPRESS(bp), type, + encrypted_read); if (!BP_IS_EMBEDDED(bp)) { hdr->b_dva = *BP_IDENTITY(bp); @@ -5206,26 +5607,42 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, } } else { /* - * This block is in the ghost cache. If it was L2-only - * (and thus didn't have an L1 hdr), we realloc the - * header to add an L1 hdr. + * This block is in the ghost cache or encrypted data + * was requested and we didn't have it. If it was + * L2-only (and thus didn't have an L1 hdr), + * we realloc the header to add an L1 hdr. */ if (!HDR_HAS_L1HDR(hdr)) { hdr = arc_hdr_realloc(hdr, hdr_l2only_cache, hdr_full_cache); } - ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); - ASSERT(GHOST_STATE(hdr->b_l1hdr.b_state)); - ASSERT(!HDR_IO_IN_PROGRESS(hdr)); - ASSERT(refcount_is_zero(&hdr->b_l1hdr.b_refcnt)); - ASSERT3P(hdr->b_l1hdr.b_buf, ==, NULL); - ASSERT3P(hdr->b_l1hdr.b_freeze_cksum, ==, NULL); + if (GHOST_STATE(hdr->b_l1hdr.b_state)) { + ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); + ASSERT(!HDR_IO_IN_PROGRESS(hdr)); + ASSERT0(refcount_count(&hdr->b_l1hdr.b_refcnt)); + ASSERT3P(hdr->b_l1hdr.b_buf, ==, NULL); + ASSERT3P(hdr->b_l1hdr.b_freeze_cksum, ==, NULL); + } else if (HDR_IO_IN_PROGRESS(hdr)) { + /* + * If this header already had an IO in progress + * and we are performing another IO to fetch + * encrypted data we must wait until the first + * IO completes so as not to confuse + * arc_read_done(). This should be very rare + * and so the performance impact shouldn't + * matter. + */ + cv_wait(&hdr->b_l1hdr.b_cv, hash_lock); + mutex_exit(hash_lock); + goto top; + } /* * This is a delicate dance that we play here. - * This hdr is in the ghost list so we access it - * to move it out of the ghost list before we + * This hdr might be in the ghost list so we access + * it to move it out of the ghost list before we * initiate the read. If it's a prefetch then * it won't have a callback so we'll remove the * reference that arc_buf_alloc_impl() created. We @@ -5233,19 +5650,22 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, * avoid hitting an assert in remove_reference(). */ arc_access(hdr, hash_lock); - arc_hdr_alloc_pabd(hdr); + arc_hdr_alloc_abd(hdr, encrypted_read); } - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); - size = arc_hdr_size(hdr); - /* - * If compression is enabled on the hdr, then will do - * RAW I/O and will store the compressed data in the hdr's - * data block. Otherwise, the hdr's data block will contain - * the uncompressed data. - */ - if (HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF) { + if (encrypted_read) { + ASSERT(HDR_HAS_RABD(hdr)); + size = HDR_GET_PSIZE(hdr); + hdr_abd = hdr->b_crypt_hdr.b_rabd; zio_flags |= ZIO_FLAG_RAW; + } else { + ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); + size = arc_hdr_size(hdr); + hdr_abd = hdr->b_l1hdr.b_pabd; + + if (arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF) { + zio_flags |= ZIO_FLAG_RAW_COMPRESS; + } } if (*arc_flags & ARC_FLAG_PREFETCH) @@ -5262,6 +5682,7 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, acb->acb_done = done; acb->acb_private = private; acb->acb_compressed = compressed_read; + acb->acb_encrypted = encrypted_read; ASSERT3P(hdr->b_l1hdr.b_acb, ==, NULL); hdr->b_l1hdr.b_acb = acb; @@ -5336,10 +5757,10 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, * Issue a null zio if the underlying buffer * was squashed to zero size by compression. */ - ASSERT3U(HDR_GET_COMPRESS(hdr), !=, + ASSERT3U(arc_hdr_get_compress(hdr), !=, ZIO_COMPRESS_EMPTY); rzio = zio_read_phys(pio, vd, addr, - size, hdr->b_l1hdr.b_pabd, + HDR_GET_PSIZE(hdr), hdr_abd, ZIO_CHECKSUM_OFF, l2arc_read_done, cb, priority, zio_flags | ZIO_FLAG_DONT_CACHE | @@ -5349,7 +5770,8 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, DTRACE_PROBE2(l2arc__read, vdev_t *, vd, zio_t *, rzio); - ARCSTAT_INCR(arcstat_l2_read_bytes, size); + ARCSTAT_INCR(arcstat_l2_read_bytes, + HDR_GET_PSIZE(hdr)); if (*arc_flags & ARC_FLAG_NOWAIT) { zio_nowait(rzio); @@ -5379,7 +5801,7 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done, } } - rzio = zio_read(pio, spa, bp, hdr->b_l1hdr.b_pabd, size, + rzio = zio_read(pio, spa, bp, hdr_abd, size, arc_read_done, hdr, priority, zio_flags, zb); if (*arc_flags & ARC_FLAG_WAIT) { @@ -5573,7 +5995,8 @@ arc_release(arc_buf_t *buf, void *tag) uint64_t spa = hdr->b_spa; uint64_t psize = HDR_GET_PSIZE(hdr); uint64_t lsize = HDR_GET_LSIZE(hdr); - enum zio_compress compress = HDR_GET_COMPRESS(hdr); + boolean_t encrypted = HDR_ENCRYPT(hdr); + enum zio_compress compress = arc_hdr_get_compress(hdr); arc_buf_contents_t type = arc_buf_type(hdr); arc_buf_t *lastbuf = NULL; VERIFY3U(hdr->b_type, ==, type); @@ -5619,7 +6042,7 @@ arc_release(arc_buf_t *buf, void *tag) if (arc_can_share(hdr, lastbuf)) { arc_share_buf(hdr, lastbuf); } else { - arc_hdr_alloc_pabd(hdr); + arc_hdr_alloc_abd(hdr, B_FALSE); abd_copy_from_buf(hdr->b_l1hdr.b_pabd, buf->b_data, psize); } @@ -5634,10 +6057,11 @@ arc_release(arc_buf_t *buf, void *tag) * if we have a compressed, shared buffer. */ ASSERT(arc_buf_is_shared(lastbuf) || - HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF); + arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF); ASSERT(!ARC_BUF_SHARED(buf)); } - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); + + ASSERT(hdr->b_l1hdr.b_pabd != NULL || HDR_HAS_RABD(hdr)); ASSERT3P(state, !=, arc_l2c_only); (void) refcount_remove_many(&state->arcs_size, @@ -5650,6 +6074,9 @@ arc_release(arc_buf_t *buf, void *tag) } hdr->b_l1hdr.b_bufcnt -= 1; + if (ARC_BUF_ENCRYPTED(buf)) + hdr->b_crypt_hdr.b_ebufcnt -= 1; + arc_cksum_verify(buf); arc_buf_unwatch(buf); @@ -5658,8 +6085,11 @@ arc_release(arc_buf_t *buf, void *tag) /* * Allocate a new hdr. The new hdr will contain a b_pabd * buffer which will be freed in arc_write(). + * Allocate a new hdr. The new hdr will contain a b_pabd + * or b_rabd buffer which will be freed in arc_write(). */ - nhdr = arc_hdr_alloc(spa, psize, lsize, compress, type); + nhdr = arc_hdr_alloc(spa, psize, lsize, encrypted, + compress, type, HDR_HAS_RABD(hdr)); ASSERT3P(nhdr->b_l1hdr.b_buf, ==, NULL); ASSERT0(nhdr->b_l1hdr.b_bufcnt); ASSERT0(refcount_count(&nhdr->b_l1hdr.b_refcnt)); @@ -5668,6 +6098,8 @@ arc_release(arc_buf_t *buf, void *tag) nhdr->b_l1hdr.b_buf = buf; nhdr->b_l1hdr.b_bufcnt = 1; + if (ARC_BUF_ENCRYPTED(buf)) + nhdr->b_crypt_hdr.b_ebufcnt = 1; nhdr->b_l1hdr.b_mru_hits = 0; nhdr->b_l1hdr.b_mru_ghost_hits = 0; nhdr->b_l1hdr.b_mfu_hits = 0; @@ -5730,7 +6162,8 @@ arc_write_ready(zio_t *zio) arc_write_callback_t *callback = zio->io_private; arc_buf_t *buf = callback->awcb_buf; arc_buf_hdr_t *hdr = buf->b_hdr; - uint64_t psize = BP_IS_HOLE(zio->io_bp) ? 0 : BP_GET_PSIZE(zio->io_bp); + blkptr_t *bp = zio->io_bp; + uint64_t psize = BP_IS_HOLE(bp) ? 0 : BP_GET_PSIZE(bp); enum zio_compress compress; fstrans_cookie_t cookie = spl_fstrans_mark(); @@ -5750,11 +6183,15 @@ arc_write_ready(zio_t *zio) if (arc_buf_is_shared(buf)) { arc_unshare_buf(hdr, buf); } else { - arc_hdr_free_pabd(hdr); + arc_hdr_free_abd(hdr, B_FALSE); } } + + if (HDR_HAS_RABD(hdr)) + arc_hdr_free_abd(hdr, B_TRUE); } ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); + ASSERT(!HDR_HAS_RABD(hdr)); ASSERT(!HDR_SHARED_DATA(hdr)); ASSERT(!arc_buf_is_shared(buf)); @@ -5766,18 +6203,34 @@ arc_write_ready(zio_t *zio) arc_cksum_compute(buf); arc_hdr_set_flags(hdr, ARC_FLAG_IO_IN_PROGRESS); - if (BP_IS_HOLE(zio->io_bp) || BP_IS_EMBEDDED(zio->io_bp)) { + if (BP_IS_ENCRYPTED(bp) != !!HDR_ENCRYPT(hdr)) + hdr = arc_hdr_realloc_crypt(hdr, BP_IS_ENCRYPTED(bp)); + + if (HDR_ENCRYPT(hdr)) { + /* ZIL blocks are written through zio_rewrite */ + ASSERT3U(BP_GET_TYPE(bp), !=, DMU_OT_INTENT_LOG); + + hdr->b_crypt_hdr.b_ot = BP_GET_TYPE(bp); + hdr->b_crypt_hdr.b_dsobj = zio->io_bookmark.zb_objset; + zio_crypt_decode_params_bp(bp, hdr->b_crypt_hdr.b_salt, + hdr->b_crypt_hdr.b_iv); + zio_crypt_decode_mac_bp(bp, hdr->b_crypt_hdr.b_mac); + } + + if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp)) { compress = ZIO_COMPRESS_OFF; } else { - ASSERT3U(HDR_GET_LSIZE(hdr), ==, BP_GET_LSIZE(zio->io_bp)); - compress = BP_GET_COMPRESS(zio->io_bp); + ASSERT3U(HDR_GET_LSIZE(hdr), ==, BP_GET_LSIZE(bp)); + compress = BP_GET_COMPRESS(bp); } HDR_SET_PSIZE(hdr, psize); arc_hdr_set_compress(hdr, compress); /* - * Fill the hdr with data. If the hdr is compressed, the data we want - * is available from the zio, otherwise we can take it from the buf. + * Fill the hdr with data. If the buffer is encrypted we have no choice + * but to copy the data into b_radb. If the hdr is compressed, the data + * we want is available from the zio, otherwise we can take it from + * the buf. * * We might be able to share the buf's data with the hdr here. However, * doing so would cause the ARC to be full of linear ABDs if we write a @@ -5787,23 +6240,28 @@ arc_write_ready(zio_t *zio) * written. Therefore, if they're allowed then we allocate one and copy * the data into it; otherwise, we share the data directly if we can. */ - if (zfs_abd_scatter_enabled || !arc_can_share(hdr, buf)) { - arc_hdr_alloc_pabd(hdr); - + if (ARC_BUF_ENCRYPTED(buf)) { + ASSERT(ARC_BUF_COMPRESSED(buf)); + arc_hdr_alloc_abd(hdr, B_TRUE); + abd_copy(hdr->b_crypt_hdr.b_rabd, zio->io_abd, psize); + } else if (zfs_abd_scatter_enabled || !arc_can_share(hdr, buf)) { /* * Ideally, we would always copy the io_abd into b_pabd, but the * user may have disabled compressed ARC, thus we must check the * hdr's compression setting rather than the io_bp's. */ - if (HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF) { - ASSERT3U(BP_GET_COMPRESS(zio->io_bp), !=, - ZIO_COMPRESS_OFF); + if (HDR_ENCRYPT(hdr)) { ASSERT3U(psize, >, 0); - + arc_hdr_alloc_abd(hdr, B_TRUE); + abd_copy(hdr->b_crypt_hdr.b_rabd, zio->io_abd, psize); + } else if (arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF && + !ARC_BUF_COMPRESSED(buf)) { + ASSERT3U(psize, >, 0); + arc_hdr_alloc_abd(hdr, B_FALSE); abd_copy(hdr->b_l1hdr.b_pabd, zio->io_abd, psize); } else { ASSERT3U(zio->io_orig_size, ==, arc_hdr_size(hdr)); - + arc_hdr_alloc_abd(hdr, B_FALSE); abd_copy_from_buf(hdr->b_l1hdr.b_pabd, buf->b_data, arc_buf_size(buf)); } @@ -5815,7 +6273,7 @@ arc_write_ready(zio_t *zio) arc_share_buf(hdr, buf); } - arc_hdr_verify(hdr, zio->io_bp); + arc_hdr_verify(hdr, bp); spl_fstrans_unmark(cookie); } @@ -5927,9 +6385,9 @@ arc_write_done(zio_t *zio) zio_t * arc_write(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp, arc_buf_t *buf, boolean_t l2arc, - const zio_prop_t *zp, arc_done_func_t *ready, - arc_done_func_t *children_ready, arc_done_func_t *physdone, - arc_done_func_t *done, void *private, zio_priority_t priority, + const zio_prop_t *zp, arc_write_done_func_t *ready, + arc_write_done_func_t *children_ready, arc_write_done_func_t *physdone, + arc_write_done_func_t *done, void *private, zio_priority_t priority, int zio_flags, const zbookmark_phys_t *zb) { arc_buf_hdr_t *hdr = buf->b_hdr; @@ -5944,9 +6402,13 @@ arc_write(zio_t *pio, spa_t *spa, uint64_t txg, ASSERT3U(hdr->b_l1hdr.b_bufcnt, >, 0); if (l2arc) arc_hdr_set_flags(hdr, ARC_FLAG_L2CACHE); - if (ARC_BUF_COMPRESSED(buf)) { - ASSERT3U(zp->zp_compress, !=, ZIO_COMPRESS_OFF); + + if (ARC_BUF_ENCRYPTED(buf)) { + ASSERT(ARC_BUF_COMPRESSED(buf)); zio_flags |= ZIO_FLAG_RAW; + } else if (ARC_BUF_COMPRESSED(buf)) { + ASSERT3U(zp->zp_compress, !=, ZIO_COMPRESS_OFF); + zio_flags |= ZIO_FLAG_RAW_COMPRESS; } callback = kmem_zalloc(sizeof (arc_write_callback_t), KM_SLEEP); callback->awcb_ready = ready; @@ -5970,11 +6432,16 @@ arc_write(zio_t *pio, spa_t *spa, uint64_t txg, if (arc_buf_is_shared(buf)) { arc_unshare_buf(hdr, buf); } else { - arc_hdr_free_pabd(hdr); + arc_hdr_free_abd(hdr, B_FALSE); } VERIFY3P(buf->b_data, !=, NULL); - arc_hdr_set_compress(hdr, ZIO_COMPRESS_OFF); } + + if (HDR_HAS_RABD(hdr)) + arc_hdr_free_abd(hdr, B_TRUE); + + arc_hdr_set_compress(hdr, ZIO_COMPRESS_OFF); + ASSERT(!arc_buf_is_shared(buf)); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); @@ -6976,10 +7443,12 @@ l2arc_write_done(zio_t *zio) static void l2arc_read_done(zio_t *zio) { + int tfm_error = 0; + spa_t *spa = zio->io_spa; l2arc_read_callback_t *cb; arc_buf_hdr_t *hdr; kmutex_t *hash_lock; - boolean_t valid_cksum; + boolean_t valid_cksum, using_rdata, needs_tfm; ASSERT3P(zio->io_vd, !=, NULL); ASSERT(zio->io_flags & ZIO_FLAG_DONT_PROPAGATE); @@ -7000,12 +7469,88 @@ l2arc_read_done(zio_t *zio) /* * Check this survived the L2ARC journey. */ - ASSERT3P(zio->io_abd, ==, hdr->b_l1hdr.b_pabd); + ASSERT(zio->io_abd == hdr->b_l1hdr.b_pabd || + (HDR_HAS_RABD(hdr) && zio->io_abd == hdr->b_crypt_hdr.b_rabd)); zio->io_bp_copy = cb->l2rcb_bp; /* XXX fix in L2ARC 2.0 */ zio->io_bp = &zio->io_bp_copy; /* XXX fix in L2ARC 2.0 */ + /* + * b_rabd should always match the data as it exists on disk if it is + * being used. Therefore if we are reading into b_rabd we do not + * attempt to transform the data. + */ valid_cksum = arc_cksum_is_equal(hdr, zio); - if (valid_cksum && zio->io_error == 0 && !HDR_L2_EVICTED(hdr)) { + using_rdata = + (HDR_HAS_RABD(hdr) && zio->io_abd == hdr->b_crypt_hdr.b_rabd); + needs_tfm = (valid_cksum && !using_rdata); + + /* Decrypt the data if it was encrypted in the L2ARC */ + if (needs_tfm && HDR_ENCRYPT(hdr)) { + dsl_crypto_key_t *dck; + blkptr_t *bp = zio->io_bp; + uint8_t salt[DATA_SALT_LEN]; + uint8_t iv[DATA_IV_LEN]; + uint8_t mac[DATA_MAC_LEN]; + abd_t *eabd = arc_get_data_abd(hdr, arc_hdr_size(hdr), hdr); + + /* + * ZIL data is never be written to the L2ARC, so we don't need + * special handling for its unique mac storage. + */ + ASSERT3U(BP_GET_TYPE(bp), !=, DMU_OT_INTENT_LOG); + + VERIFY0(spa_keystore_lookup_key(spa, cb->l2rcb_zb.zb_objset, + FTAG, &dck)); + + zio_crypt_decode_params_bp(bp, salt, iv); + zio_crypt_decode_mac_bp(bp, mac); + + tfm_error = zio_do_crypt_abd(B_FALSE, &dck->dck_key, salt, + BP_GET_TYPE(bp), iv, mac, HDR_GET_PSIZE(hdr), eabd, + hdr->b_l1hdr.b_pabd); + + spa_keystore_dsl_key_rele(spa, dck, FTAG); + + if (tfm_error == 0) { + arc_free_data_abd(hdr, hdr->b_l1hdr.b_pabd, + arc_hdr_size(hdr), hdr); + hdr->b_l1hdr.b_pabd = eabd; + zio->io_abd = eabd; + } else { + arc_free_data_abd(hdr, eabd, arc_hdr_size(hdr), hdr); + } + } + + /* + * If the L2ARC block was compressed, but ARC compression + * is disabled we decompress the data into a new buffer and + * replace the existing data. + */ + if (needs_tfm && tfm_error == 0 && + HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF && + !HDR_COMPRESSION_ENABLED(hdr)) { + abd_t *cabd = arc_get_data_abd(hdr, arc_hdr_size(hdr), hdr); + void *tmp = abd_borrow_buf(cabd, arc_hdr_size(hdr)); + + tfm_error = zio_decompress_data(HDR_GET_COMPRESS(hdr), + hdr->b_l1hdr.b_pabd, tmp, HDR_GET_PSIZE(hdr), + HDR_GET_LSIZE(hdr)); + + if (tfm_error == 0) { + abd_return_buf_copy(cabd, tmp, arc_hdr_size(hdr)); + arc_free_data_abd(hdr, hdr->b_l1hdr.b_pabd, + arc_hdr_size(hdr), hdr); + hdr->b_l1hdr.b_pabd = cabd; + zio->io_abd = cabd; + zio->io_size = HDR_GET_LSIZE(hdr); + } else { + abd_return_buf(cabd, tmp, arc_hdr_size(hdr)); + arc_free_data_abd(hdr, cabd, arc_hdr_size(hdr), hdr); + } + } + + if (valid_cksum && tfm_error == 0 && zio->io_error == 0 && + !HDR_L2_EVICTED(hdr)) { mutex_exit(hash_lock); zio->io_private = hdr; arc_read_done(zio); @@ -7020,7 +7565,7 @@ l2arc_read_done(zio_t *zio) } else { zio->io_error = SET_ERROR(EIO); } - if (!valid_cksum) + if (!valid_cksum || tfm_error != 0) ARCSTAT_BUMP(arcstat_l2_cksum_bad); /* @@ -7030,11 +7575,13 @@ l2arc_read_done(zio_t *zio) */ if (zio->io_waiter == NULL) { zio_t *pio = zio_unique_parent(zio); + void *abd = (using_rdata) ? + hdr->b_crypt_hdr.b_rabd : hdr->b_l1hdr.b_pabd; ASSERT(!pio || pio->io_child_type == ZIO_CHILD_LOGICAL); zio_nowait(zio_read(pio, zio->io_spa, zio->io_bp, - hdr->b_l1hdr.b_pabd, zio->io_size, arc_read_done, + abd, zio->io_size, arc_read_done, hdr, zio->io_priority, cb->l2rcb_flags, &cb->l2rcb_zb)); } @@ -7201,6 +7748,102 @@ l2arc_evict(l2arc_dev_t *dev, uint64_t distance, boolean_t all) mutex_exit(&dev->l2ad_mtx); } +static int +l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, abd_t **abd_out, + uint64_t *bsize_out, uint64_t *csize_out) +{ + int ret; + void *tmp = NULL; + abd_t *cabd = NULL, *eabd = NULL, *to_write = hdr->b_l1hdr.b_pabd; + enum zio_compress compress = HDR_GET_COMPRESS(hdr); + uint64_t bsize = arc_hdr_size(hdr); + uint64_t csize = bsize; + boolean_t ismd = HDR_ISTYPE_METADATA(hdr); + dsl_crypto_key_t *dck = NULL; + + ASSERT((HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF && + !HDR_COMPRESSION_ENABLED(hdr)) || + HDR_ENCRYPT(hdr) || + HDR_SHARED_DATA(hdr)); + + /* + * If this is just a shared buffer, we simply copy the data. Otherwise + * we apply the needed transforms so that the data matches what is + * on disk. + */ + if (!(compress != ZIO_COMPRESS_OFF && + !HDR_COMPRESSION_ENABLED(hdr)) && + !HDR_ENCRYPT(hdr)) { + to_write = abd_alloc_for_io(bsize, ismd); + abd_copy(to_write, hdr->b_l1hdr.b_pabd, bsize); + goto out; + } + + if (compress != ZIO_COMPRESS_OFF && !HDR_COMPRESSION_ENABLED(hdr)) { + cabd = abd_alloc_for_io(bsize, ismd); + tmp = abd_borrow_buf(cabd, bsize); + + csize = zio_compress_data(compress, to_write, tmp, bsize); + ASSERT3U(csize, <=, HDR_GET_PSIZE(hdr)); + if (csize < HDR_GET_PSIZE(hdr)) { + bzero((char *)tmp + csize, HDR_GET_PSIZE(hdr) - csize); + csize = HDR_GET_PSIZE(hdr); + } + abd_return_buf_copy(cabd, tmp, bsize); + + to_write = cabd; + } + + if (HDR_ENCRYPT(hdr)) { + eabd = abd_alloc_for_io(csize, ismd); + + /* + * If the dataset was disowned before the buffer + * made it to this point, the key to re-encrypt + * it won't be available. In this case we simply + * won't write the buffer to the L2ARC. + */ + ret = spa_keystore_lookup_key(spa, hdr->b_crypt_hdr.b_dsobj, + FTAG, &dck); + if (ret) + goto error; + + ret = zio_do_crypt_abd(B_TRUE, &dck->dck_key, + hdr->b_crypt_hdr.b_salt, hdr->b_crypt_hdr.b_ot, + hdr->b_crypt_hdr.b_iv, hdr->b_crypt_hdr.b_mac, + csize, to_write, eabd); + if (ret) + goto error; + + spa_keystore_dsl_key_rele(spa, dck, FTAG); + + if (to_write == cabd) + abd_free(cabd); + + to_write = eabd; + bsize = csize; + } + +out: + *bsize_out = bsize; + *csize_out = csize; + *abd_out = to_write; + return (0); + +error: + if (dck) + spa_keystore_dsl_key_rele(spa, dck, FTAG); + if (cabd) + abd_free(cabd); + if (eabd) + abd_free(eabd); + + *bsize_out = 0; + *csize_out = 0; + *abd_out = NULL; + return (ret); +} + /* * Find and write ARC buffers to the L2ARC device. * @@ -7257,8 +7900,8 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz) for (; hdr; hdr = hdr_prev) { kmutex_t *hash_lock; - uint64_t asize, size; - abd_t *to_write; + uint64_t asize, bsize, csize; + abd_t *to_write = NULL; if (arc_warm == B_FALSE) hdr_prev = multilist_sublist_next(mls, hdr); @@ -7293,6 +7936,61 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz) break; } + /* + * We rely on the L1 portion of the header below, so + * it's invalid for this header to have been evicted out + * of the ghost cache, prior to being written out. The + * ARC_FLAG_L2_WRITING bit ensures this won't happen. + */ + arc_hdr_set_flags(hdr, ARC_FLAG_L2_WRITING); + ASSERT(HDR_HAS_L1HDR(hdr)); + + ASSERT3U(HDR_GET_PSIZE(hdr), >, 0); + ASSERT(hdr->b_l1hdr.b_pabd != NULL || + HDR_HAS_RABD(hdr)); + ASSERT3U(arc_hdr_size(hdr), >, 0); + + /* + * If this header has b_rabd, we can use this since it + * must always match the data exactly as it exists on + * disk. Otherwise, the L2ARC can normally use the + * hdr's data, but if we're sharing data between the + * hdr and one of its bufs, L2ARC needs its own copy of + * the data so that the ZIO below can't race with the + * buf consumer. To ensure that this copy will be + * available for the lifetime of the ZIO and be cleaned + * up afterwards, we add it to the l2arc_free_on_write + * queue. If we need to apply any transforms to the + * data (compression, encryption) we will also need the + * extra buffer. + */ + if (HDR_HAS_RABD(hdr)) { + bsize = HDR_GET_PSIZE(hdr); + csize = bsize; + to_write = hdr->b_crypt_hdr.b_rabd; + } else if (!(!HDR_COMPRESSION_ENABLED(hdr) && + HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF) && + !HDR_ENCRYPT(hdr) && + !HDR_SHARED_DATA(hdr)) { + bsize = arc_hdr_size(hdr); + csize = bsize; + to_write = hdr->b_l1hdr.b_pabd; + } else { + int ret; + arc_buf_contents_t type = arc_buf_type(hdr); + + ret = l2arc_apply_transforms(spa, hdr, + &to_write, &bsize, &csize); + if (ret) { + arc_hdr_clear_flags(hdr, + ARC_FLAG_L2_WRITING); + mutex_exit(hash_lock); + continue; + } + + l2arc_free_abd_on_write(to_write, bsize, type); + } + if (pio == NULL) { /* * Insert a dummy header on the buflist so @@ -7315,48 +8013,17 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz) hdr->b_l2hdr.b_hits = 0; hdr->b_l2hdr.b_daddr = dev->l2ad_hand; - arc_hdr_set_flags(hdr, - ARC_FLAG_L2_WRITING | ARC_FLAG_HAS_L2HDR); + arc_hdr_set_flags(hdr, ARC_FLAG_HAS_L2HDR); mutex_enter(&dev->l2ad_mtx); list_insert_head(&dev->l2ad_buflist, hdr); mutex_exit(&dev->l2ad_mtx); - /* - * We rely on the L1 portion of the header below, so - * it's invalid for this header to have been evicted out - * of the ghost cache, prior to being written out. The - * ARC_FLAG_L2_WRITING bit ensures this won't happen. - */ - ASSERT(HDR_HAS_L1HDR(hdr)); - - ASSERT3U(HDR_GET_PSIZE(hdr), >, 0); - ASSERT3P(hdr->b_l1hdr.b_pabd, !=, NULL); - ASSERT3U(arc_hdr_size(hdr), >, 0); - size = arc_hdr_size(hdr); - - (void) refcount_add_many(&dev->l2ad_alloc, size, hdr); + (void) refcount_add_many(&dev->l2ad_alloc, + arc_hdr_size(hdr), hdr); - /* - * Normally the L2ARC can use the hdr's data, but if - * we're sharing data between the hdr and one of its - * bufs, L2ARC needs its own copy of the data so that - * the ZIO below can't race with the buf consumer. To - * ensure that this copy will be available for the - * lifetime of the ZIO and be cleaned up afterwards, we - * add it to the l2arc_free_on_write queue. - */ - if (!HDR_SHARED_DATA(hdr)) { - to_write = hdr->b_l1hdr.b_pabd; - } else { - to_write = abd_alloc_for_io(size, - HDR_ISTYPE_METADATA(hdr)); - abd_copy(to_write, hdr->b_l1hdr.b_pabd, size); - l2arc_free_abd_on_write(to_write, size, - arc_buf_type(hdr)); - } wzio = zio_write_phys(pio, dev->l2ad_vdev, - hdr->b_l2hdr.b_daddr, size, to_write, + hdr->b_l2hdr.b_daddr, csize, to_write, ZIO_CHECKSUM_OFF, NULL, hdr, ZIO_PRIORITY_ASYNC_WRITE, ZIO_FLAG_CANFAIL, B_FALSE); @@ -7365,11 +8032,11 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz) DTRACE_PROBE2(l2arc__write, vdev_t *, dev->l2ad_vdev, zio_t *, wzio); - write_asize += size; + write_asize += csize; /* * Keep the clock hand suitably device-aligned. */ - asize = vdev_psize_to_asize(dev->l2ad_vdev, size); + asize = vdev_psize_to_asize(dev->l2ad_vdev, csize); write_psize += asize; dev->l2ad_hand += asize; @@ -7734,7 +8401,7 @@ module_param(zfs_arc_average_blocksize, int, 0444); MODULE_PARM_DESC(zfs_arc_average_blocksize, "Target average block size"); module_param(zfs_compressed_arc_enabled, int, 0644); -MODULE_PARM_DESC(zfs_arc_average_blocksize, "Disable compressed arc buffers"); +MODULE_PARM_DESC(zfs_compressed_arc_enabled, "Disable compressed arc buffers"); module_param(zfs_arc_min_prefetch_lifespan, int, 0644); MODULE_PARM_DESC(zfs_arc_min_prefetch_lifespan, "Min life of prefetch block"); diff --git a/module/zfs/bptree.c b/module/zfs/bptree.c index c0edc9d768e8..b442ba6bfde8 100644 --- a/module/zfs/bptree.c +++ b/module/zfs/bptree.c @@ -212,7 +212,8 @@ bptree_iterate(objset_t *os, uint64_t obj, boolean_t free, bptree_itor_t func, err = 0; for (i = ba.ba_phys->bt_begin; i < ba.ba_phys->bt_end; i++) { bptree_entry_phys_t bte; - int flags = TRAVERSE_PREFETCH_METADATA | TRAVERSE_POST; + int flags = TRAVERSE_PREFETCH_METADATA | TRAVERSE_POST | + TRAVERSE_NO_DECRYPT; err = dmu_read(os, obj, i * sizeof (bte), sizeof (bte), &bte, DMU_READ_NO_PREFETCH); diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c index b7dfb858721a..92bae3f24715 100644 --- a/module/zfs/dbuf.c +++ b/module/zfs/dbuf.c @@ -961,7 +961,7 @@ dbuf_whichblock(const dnode_t *dn, const int64_t level, const uint64_t offset) } static void -dbuf_read_done(zio_t *zio, arc_buf_t *buf, void *vdb) +dbuf_read_done(zio_t *zio, int err, arc_buf_t *buf, void *vdb) { dmu_buf_impl_t *db = vdb; @@ -981,7 +981,7 @@ dbuf_read_done(zio_t *zio, arc_buf_t *buf, void *vdb) db->db_freed_in_flight = FALSE; dbuf_set_data(db, buf); db->db_state = DB_CACHED; - } else if (zio == NULL || zio->io_error == 0) { + } else if (err == 0 && (zio == NULL || zio->io_error == 0)) { dbuf_set_data(db, buf); db->db_state = DB_CACHED; } else { @@ -1000,7 +1000,7 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags) dnode_t *dn; zbookmark_phys_t zb; uint32_t aflags = ARC_FLAG_NOWAIT; - int err; + int err, zio_flags = 0; DB_DNODE_ENTER(db); dn = DB_DNODE(db); @@ -1018,6 +1018,19 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags) */ int bonuslen = MIN(dn->dn_bonuslen, dn->dn_phys->dn_bonuslen); int max_bonuslen = DN_SLOTS_TO_BONUSLEN(dn->dn_num_slots); + arc_buf_t *dn_buf = (dn->dn_dbuf) ? dn->dn_dbuf->db_buf : NULL; + + if (dn_buf != NULL && arc_is_encrypted(dn_buf) && + DMU_OT_IS_ENCRYPTED(dn->dn_bonustype) && + !(flags & DB_RF_NO_DECRYPT)) { + err = arc_untransform(dn_buf, dn->dn_objset->os_spa, + dmu_objset_id(dn->dn_objset), B_TRUE); + if (err != 0) { + DB_DNODE_EXIT(db); + mutex_exit(&db->db_mtx); + return (err); + } + } ASSERT3U(bonuslen, <=, db->db.db_size); db->db.db_data = kmem_alloc(max_bonuslen, KM_SLEEP); @@ -1087,9 +1100,13 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags) dbuf_add_ref(db, NULL); + zio_flags = (flags & DB_RF_CANFAIL) ? + ZIO_FLAG_CANFAIL : ZIO_FLAG_MUSTSUCCEED; + if ((flags & DB_RF_NO_DECRYPT) && BP_IS_ENCRYPTED(db->db_blkptr)) + zio_flags |= ZIO_FLAG_RAW; + err = arc_read(zio, db->db_objset->os_spa, db->db_blkptr, - dbuf_read_done, db, ZIO_PRIORITY_SYNC_READ, - (flags & DB_RF_CANFAIL) ? ZIO_FLAG_CANFAIL : ZIO_FLAG_MUSTSUCCEED, + dbuf_read_done, db, ZIO_PRIORITY_SYNC_READ, zio_flags, &aflags, &zb); return (err); @@ -1186,6 +1203,8 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags) mutex_enter(&db->db_mtx); if (db->db_state == DB_CACHED) { + spa_t *spa = dn->dn_objset->os_spa; + /* * If the arc buf is compressed, we need to decompress it to * read the data. This could happen during the "zfs receive" of @@ -1195,7 +1214,8 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags) arc_get_compression(db->db_buf) != ZIO_COMPRESS_OFF) { dbuf_fix_old_data(db, spa_syncing_txg(dmu_objset_spa(db->db_objset))); - err = arc_decompress(db->db_buf); + err = arc_untransform(db->db_buf, spa, + dmu_objset_id(db->db_objset), B_FALSE); dbuf_set_data(db, db->db_buf); } mutex_exit(&db->db_mtx); @@ -2457,7 +2477,7 @@ dbuf_issue_final_prefetch(dbuf_prefetch_arg_t *dpa, blkptr_t *bp) * prefetch if the next block down is our target. */ static void -dbuf_prefetch_indirect_done(zio_t *zio, arc_buf_t *abuf, void *private) +dbuf_prefetch_indirect_done(zio_t *zio, int err, arc_buf_t *abuf, void *private) { dbuf_prefetch_arg_t *dpa = private; uint64_t nextblkid; @@ -2479,7 +2499,7 @@ dbuf_prefetch_indirect_done(zio_t *zio, arc_buf_t *abuf, void *private) */ if (zio != NULL) { ASSERT3S(BP_GET_LEVEL(zio->io_bp), ==, dpa->dpa_curlevel); - if (zio->io_flags & ZIO_FLAG_RAW) { + if (zio->io_flags & ZIO_FLAG_RAW_COMPRESS) { ASSERT3U(BP_GET_PSIZE(zio->io_bp), ==, zio->io_size); } else { ASSERT3U(BP_GET_LSIZE(zio->io_bp), ==, zio->io_size); @@ -2504,7 +2524,7 @@ dbuf_prefetch_indirect_done(zio_t *zio, arc_buf_t *abuf, void *private) (dpa->dpa_epbs * (dpa->dpa_curlevel - dpa->dpa_zb.zb_level)); bp = ((blkptr_t *)abuf->b_data) + P2PHASE(nextblkid, 1ULL << dpa->dpa_epbs); - if (BP_IS_HOLE(bp) || (zio != NULL && zio->io_error != 0)) { + if (BP_IS_HOLE(bp) || err != 0 || (zio != NULL && zio->io_error != 0)) { kmem_free(dpa, sizeof (*dpa)); } else if (dpa->dpa_curlevel == dpa->dpa_zb.zb_level) { ASSERT3U(nextblkid, ==, dpa->dpa_zb.zb_blkid); @@ -3506,7 +3526,7 @@ dbuf_write_ready(zio_t *zio, arc_buf_t *buf, void *vdb) DB_DNODE_EXIT(db); if (!BP_IS_EMBEDDED(bp)) - bp->blk_fill = fill; + BP_SET_FILL(bp, fill); mutex_exit(&db->db_mtx); @@ -3785,9 +3805,13 @@ dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx) wp_flag = WP_SPILL; wp_flag |= (db->db_state == DB_NOFILL) ? WP_NOFILL : 0; - dmu_write_policy(os, dn, db->db_level, wp_flag, - (data != NULL && arc_get_compression(data) != ZIO_COMPRESS_OFF) ? - arc_get_compression(data) : ZIO_COMPRESS_INHERIT, &zp); + dmu_write_policy(os, dn, db->db_level, wp_flag, &zp); + + if (data != NULL && arc_get_compression(data) != ZIO_COMPRESS_OFF) { + dmu_write_policy_override_compress(&zp, + arc_get_compression(data)); + } + DB_DNODE_EXIT(db); /* @@ -3827,7 +3851,7 @@ dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx) ZIO_PRIORITY_ASYNC_WRITE, ZIO_FLAG_MUSTSUCCEED | ZIO_FLAG_NODATA, &zb); } else { - arc_done_func_t *children_ready_cb = NULL; + arc_write_done_func_t *children_ready_cb = NULL; ASSERT(arc_released(data)); /* diff --git a/module/zfs/ddt.c b/module/zfs/ddt.c index 75ab7f5b26e1..aabe5057e45a 100644 --- a/module/zfs/ddt.c +++ b/module/zfs/ddt.c @@ -269,6 +269,10 @@ ddt_bp_fill(const ddt_phys_t *ddp, blkptr_t *bp, uint64_t txg) BP_SET_BIRTH(bp, txg, ddp->ddp_phys_birth); } +/* + * The bp created via this function may be used for repairs and scrub, but it + * will be missing the salt / IV required to do a full decrypting read. + */ void ddt_bp_create(enum zio_checksum checksum, const ddt_key_t *ddk, const ddt_phys_t *ddp, blkptr_t *bp) @@ -279,11 +283,12 @@ ddt_bp_create(enum zio_checksum checksum, ddt_bp_fill(ddp, bp, ddp->ddp_phys_birth); bp->blk_cksum = ddk->ddk_cksum; - bp->blk_fill = 1; BP_SET_LSIZE(bp, DDK_GET_LSIZE(ddk)); BP_SET_PSIZE(bp, DDK_GET_PSIZE(ddk)); BP_SET_COMPRESS(bp, DDK_GET_COMPRESS(ddk)); + BP_SET_ENCRYPTED(bp, DDK_GET_ENCRYPTED(ddk)); + BP_SET_FILL(bp, 1); BP_SET_CHECKSUM(bp, checksum); BP_SET_TYPE(bp, DMU_OT_DEDUP); BP_SET_LEVEL(bp, 0); @@ -300,6 +305,7 @@ ddt_key_fill(ddt_key_t *ddk, const blkptr_t *bp) DDK_SET_LSIZE(ddk, BP_GET_LSIZE(bp)); DDK_SET_PSIZE(ddk, BP_GET_PSIZE(bp)); DDK_SET_COMPRESS(ddk, BP_GET_COMPRESS(bp)); + DDK_SET_ENCRYPTED(ddk, BP_IS_ENCRYPTED(bp)); } void @@ -389,7 +395,7 @@ ddt_stat_generate(ddt_t *ddt, ddt_entry_t *dde, ddt_stat_t *dds) if (ddp->ddp_phys_birth == 0) continue; - for (d = 0; d < SPA_DVAS_PER_BP; d++) + for (d = 0; d < DDE_GET_NDVAS(dde); d++) dsize += dva_get_dsize_sync(spa, &ddp->ddp_dva[d]); dds->dds_blocks += 1; @@ -562,6 +568,7 @@ ddt_ditto_copies_needed(ddt_t *ddt, ddt_entry_t *dde, ddt_phys_t *ddp_willref) uint64_t ditto = spa->spa_dedup_ditto; int total_copies = 0; int desired_copies = 0; + int copies_needed = 0; int p; for (p = DDT_PHYS_SINGLE; p <= DDT_PHYS_TRIPLE; p++) { @@ -588,7 +595,14 @@ ddt_ditto_copies_needed(ddt_t *ddt, ddt_entry_t *dde, ddt_phys_t *ddp_willref) if (total_refcnt >= ditto * ditto) desired_copies++; - return (MAX(desired_copies, total_copies) - total_copies); + copies_needed = MAX(desired_copies, total_copies) - total_copies; + + /* encrypted blocks store their IV in DVA[2] */ + if (DDK_GET_ENCRYPTED(&dde->dde_key) && + copies_needed >= SPA_DVAS_PER_BP) + copies_needed = SPA_DVAS_PER_BP - 1; + + return (copies_needed); } int @@ -599,7 +613,7 @@ ddt_ditto_copies_present(ddt_entry_t *dde) int copies = 0 - DVA_GET_GANG(dva); int d; - for (d = 0; d < SPA_DVAS_PER_BP; d++, dva++) + for (d = 0; d < DDE_GET_NDVAS(dde); d++, dva++) if (DVA_IS_VALID(dva)) copies++; diff --git a/module/zfs/dmu.c b/module/zfs/dmu.c index b5ddec2d9e8b..7d9e245b0f0c 100644 --- a/module/zfs/dmu.c +++ b/module/zfs/dmu.c @@ -59,60 +59,60 @@ int zfs_nopwrite_enabled = 1; const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = { - { DMU_BSWAP_UINT8, TRUE, "unallocated" }, - { DMU_BSWAP_ZAP, TRUE, "object directory" }, - { DMU_BSWAP_UINT64, TRUE, "object array" }, - { DMU_BSWAP_UINT8, TRUE, "packed nvlist" }, - { DMU_BSWAP_UINT64, TRUE, "packed nvlist size" }, - { DMU_BSWAP_UINT64, TRUE, "bpobj" }, - { DMU_BSWAP_UINT64, TRUE, "bpobj header" }, - { DMU_BSWAP_UINT64, TRUE, "SPA space map header" }, - { DMU_BSWAP_UINT64, TRUE, "SPA space map" }, - { DMU_BSWAP_UINT64, TRUE, "ZIL intent log" }, - { DMU_BSWAP_DNODE, TRUE, "DMU dnode" }, - { DMU_BSWAP_OBJSET, TRUE, "DMU objset" }, - { DMU_BSWAP_UINT64, TRUE, "DSL directory" }, - { DMU_BSWAP_ZAP, TRUE, "DSL directory child map"}, - { DMU_BSWAP_ZAP, TRUE, "DSL dataset snap map" }, - { DMU_BSWAP_ZAP, TRUE, "DSL props" }, - { DMU_BSWAP_UINT64, TRUE, "DSL dataset" }, - { DMU_BSWAP_ZNODE, TRUE, "ZFS znode" }, - { DMU_BSWAP_OLDACL, TRUE, "ZFS V0 ACL" }, - { DMU_BSWAP_UINT8, FALSE, "ZFS plain file" }, - { DMU_BSWAP_ZAP, TRUE, "ZFS directory" }, - { DMU_BSWAP_ZAP, TRUE, "ZFS master node" }, - { DMU_BSWAP_ZAP, TRUE, "ZFS delete queue" }, - { DMU_BSWAP_UINT8, FALSE, "zvol object" }, - { DMU_BSWAP_ZAP, TRUE, "zvol prop" }, - { DMU_BSWAP_UINT8, FALSE, "other uint8[]" }, - { DMU_BSWAP_UINT64, FALSE, "other uint64[]" }, - { DMU_BSWAP_ZAP, TRUE, "other ZAP" }, - { DMU_BSWAP_ZAP, TRUE, "persistent error log" }, - { DMU_BSWAP_UINT8, TRUE, "SPA history" }, - { DMU_BSWAP_UINT64, TRUE, "SPA history offsets" }, - { DMU_BSWAP_ZAP, TRUE, "Pool properties" }, - { DMU_BSWAP_ZAP, TRUE, "DSL permissions" }, - { DMU_BSWAP_ACL, TRUE, "ZFS ACL" }, - { DMU_BSWAP_UINT8, TRUE, "ZFS SYSACL" }, - { DMU_BSWAP_UINT8, TRUE, "FUID table" }, - { DMU_BSWAP_UINT64, TRUE, "FUID table size" }, - { DMU_BSWAP_ZAP, TRUE, "DSL dataset next clones"}, - { DMU_BSWAP_ZAP, TRUE, "scan work queue" }, - { DMU_BSWAP_ZAP, TRUE, "ZFS user/group used" }, - { DMU_BSWAP_ZAP, TRUE, "ZFS user/group quota" }, - { DMU_BSWAP_ZAP, TRUE, "snapshot refcount tags"}, - { DMU_BSWAP_ZAP, TRUE, "DDT ZAP algorithm" }, - { DMU_BSWAP_ZAP, TRUE, "DDT statistics" }, - { DMU_BSWAP_UINT8, TRUE, "System attributes" }, - { DMU_BSWAP_ZAP, TRUE, "SA master node" }, - { DMU_BSWAP_ZAP, TRUE, "SA attr registration" }, - { DMU_BSWAP_ZAP, TRUE, "SA attr layouts" }, - { DMU_BSWAP_ZAP, TRUE, "scan translations" }, - { DMU_BSWAP_UINT8, FALSE, "deduplicated block" }, - { DMU_BSWAP_ZAP, TRUE, "DSL deadlist map" }, - { DMU_BSWAP_UINT64, TRUE, "DSL deadlist map hdr" }, - { DMU_BSWAP_ZAP, TRUE, "DSL dir clones" }, - { DMU_BSWAP_UINT64, TRUE, "bpobj subobj" } + { DMU_BSWAP_UINT8, TRUE, FALSE, "unallocated" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "object directory" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "object array" }, + { DMU_BSWAP_UINT8, TRUE, FALSE, "packed nvlist" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "packed nvlist size" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "bpobj" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "bpobj header" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "SPA space map header" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "SPA space map" }, + { DMU_BSWAP_UINT64, TRUE, TRUE, "ZIL intent log" }, + { DMU_BSWAP_DNODE, TRUE, TRUE, "DMU dnode" }, + { DMU_BSWAP_OBJSET, TRUE, FALSE, "DMU objset" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "DSL directory" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL directory child map"}, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL dataset snap map" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL props" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "DSL dataset" }, + { DMU_BSWAP_ZNODE, TRUE, FALSE, "ZFS znode" }, + { DMU_BSWAP_OLDACL, TRUE, TRUE, "ZFS V0 ACL" }, + { DMU_BSWAP_UINT8, FALSE, TRUE, "ZFS plain file" }, + { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS directory" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "ZFS master node" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "ZFS delete queue" }, + { DMU_BSWAP_UINT8, FALSE, TRUE, "zvol object" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "zvol prop" }, + { DMU_BSWAP_UINT8, FALSE, TRUE, "other uint8[]" }, + { DMU_BSWAP_UINT64, FALSE, TRUE, "other uint64[]" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "other ZAP" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "persistent error log" }, + { DMU_BSWAP_UINT8, TRUE, FALSE, "SPA history" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "SPA history offsets" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "Pool properties" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL permissions" }, + { DMU_BSWAP_ACL, TRUE, TRUE, "ZFS ACL" }, + { DMU_BSWAP_UINT8, TRUE, TRUE, "ZFS SYSACL" }, + { DMU_BSWAP_UINT8, TRUE, TRUE, "FUID table" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "FUID table size" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL dataset next clones"}, + { DMU_BSWAP_ZAP, TRUE, FALSE, "scan work queue" }, + { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS user/group used" }, + { DMU_BSWAP_ZAP, TRUE, TRUE, "ZFS user/group quota" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "snapshot refcount tags"}, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DDT ZAP algorithm" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DDT statistics" }, + { DMU_BSWAP_UINT8, TRUE, TRUE, "System attributes" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "SA master node" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "SA attr registration" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "SA attr layouts" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "scan translations" }, + { DMU_BSWAP_UINT8, FALSE, FALSE, "deduplicated block" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL deadlist map" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "DSL deadlist map hdr" }, + { DMU_BSWAP_ZAP, TRUE, FALSE, "DSL dir clones" }, + { DMU_BSWAP_UINT64, TRUE, FALSE, "bpobj subobj" } }; const dmu_object_byteswap_info_t dmu_ot_byteswap[DMU_BSWAP_NUMFUNCS] = { @@ -588,7 +588,9 @@ dmu_buf_rele_array(dmu_buf_t **dbp_fake, int numbufs, void *tag) * the data starting at offset, and continuing to offset + len. * * Note that if the indirect blocks above the blocks being prefetched are not in - * cache, they will be asychronously read in. + * cache, they will be asychronously read in. Also, callers should make sure + * not to attempt to prefetch encrypted data without the relevant encryption key + * being loaded. */ void dmu_prefetch(objset_t *os, uint64_t object, int64_t level, uint64_t offset, @@ -1426,7 +1428,7 @@ dmu_sync_ready(zio_t *zio, arc_buf_t *buf, void *varg) BP_SET_LSIZE(bp, db->db_size); } else if (!BP_IS_EMBEDDED(bp)) { ASSERT(BP_GET_LEVEL(bp) == 0); - bp->blk_fill = 1; + BP_SET_FILL(bp, 1); } } } @@ -1594,8 +1596,7 @@ dmu_sync(zio_t *pio, uint64_t txg, dmu_sync_cb_t *done, zgd_t *zgd) DB_DNODE_ENTER(db); dn = DB_DNODE(db); - dmu_write_policy(os, dn, db->db_level, WP_DMU_SYNC, - ZIO_COMPRESS_INHERIT, &zp); + dmu_write_policy(os, dn, db->db_level, WP_DMU_SYNC, &zp); DB_DNODE_EXIT(db); /* @@ -1765,8 +1766,7 @@ int zfs_mdcomp_disable = 0; int zfs_redundant_metadata_most_ditto_level = 2; void -dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, - enum zio_compress override_compress, zio_prop_t *zp) +dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp) { dmu_object_type_t type = dn ? dn->dn_type : DMU_OT_OBJSET; boolean_t ismd = (level > 0 || DMU_OT_IS_METADATA(type) || @@ -1777,6 +1777,7 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, boolean_t dedup = B_FALSE; boolean_t nopwrite = B_FALSE; boolean_t dedup_verify = os->os_dedup_verify; + boolean_t encrypt = B_FALSE; int copies = os->os_copies; /* @@ -1864,23 +1865,75 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, compress != ZIO_COMPRESS_OFF && zfs_nopwrite_enabled); } - zp->zp_checksum = checksum; - /* - * If we're writing a pre-compressed buffer, the compression type we use - * must match the data. If it hasn't been compressed yet, then we should - * use the value dictated by the policies above. + * Encrypted objects override the checksum type with sha256-mac (which + * is dedupable). Encrypted, dedup'd ojects cannot use all available + * copies since we use the last one to store the IV. Encryption is also + * incompatible with nopwrite because encrypted checksums are not + * reproducible (unless dedup is on). */ - zp->zp_compress = override_compress != ZIO_COMPRESS_INHERIT - ? override_compress : compress; - ASSERT3U(zp->zp_compress, !=, ZIO_COMPRESS_INHERIT); + if (os->os_encrypted && DMU_OT_IS_ENCRYPTED(type) && + !(wp & WP_NOFILL) && level <= 0) { + encrypt = B_TRUE; + nopwrite = B_FALSE; + + if (copies >= SPA_DVAS_PER_BP) + copies = SPA_DVAS_PER_BP - 1; + if (type == DMU_OT_DNODE) + compress = ZIO_COMPRESS_EMPTY; + } + zp->zp_compress = compress; + zp->zp_checksum = checksum; zp->zp_type = (wp & WP_SPILL) ? dn->dn_bonustype : type; zp->zp_level = level; zp->zp_copies = MIN(copies, spa_max_replication(os->os_spa)); zp->zp_dedup = dedup; zp->zp_dedup_verify = dedup && dedup_verify; zp->zp_nopwrite = nopwrite; + zp->zp_encrypt = encrypt; + bzero(zp->zp_salt, DATA_SALT_LEN); + bzero(zp->zp_iv, DATA_IV_LEN); + bzero(zp->zp_mac, DATA_MAC_LEN); + + ASSERT(!(zp->zp_encrypt && zp->zp_copies >= 3)); +} + +/* + * If we're writing a pre-compressed buffer, the compression type we use + * must match the data. If it hasn't been compressed yet, then we should + * use the value dictated by the original policy. + */ +void +dmu_write_policy_override_compress(zio_prop_t *zp, enum zio_compress compress) +{ + ASSERT3U(compress, !=, ZIO_COMPRESS_INHERIT); + zp->zp_compress = compress; +} + +/* + * Raw encrypted data must pass a few values to the zio layer. The encryption + * parameters must be passsed in to the policy so that they can be written along + * with the block. In addition, raw encrypted writes must also be raw compressed + * since encryption is applied after compression, so we must set that field here + * as well. + */ +void +dmu_write_policy_override_encrypt(zio_prop_t *zp, enum zio_compress compress, + uint8_t *salt, uint8_t *iv, uint8_t *mac) +{ + ASSERT3U(compress, !=, ZIO_COMPRESS_INHERIT); + ASSERT3U(zp->zp_level, <=, 0); + + zp->zp_compress = compress; + zp->zp_nopwrite = B_FALSE; + zp->zp_encrypt = B_TRUE; + bcopy(salt, zp->zp_salt, DATA_SALT_LEN); + bcopy(iv, zp->zp_iv, DATA_IV_LEN); + bcopy(mac, zp->zp_mac, DATA_MAC_LEN); + + if (zp->zp_copies >= SPA_DVAS_PER_BP) + zp->zp_copies = SPA_DVAS_PER_BP - 1; } int diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index add4417ac339..23f68f1be657 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -188,6 +188,19 @@ compression_changed_cb(void *arg, uint64_t newval) ZIO_COMPRESS_ON); } +static void +encryption_changed_cb(void *arg, uint64_t newval) +{ + objset_t *os = arg; + + /* + * Inheritance and range checking should have been done by now. + */ + ASSERT(newval != ZIO_CRYPT_INHERIT && newval != ZIO_CRYPT_ON); + + os->os_encrypted = (newval != ZIO_CRYPT_OFF ? B_TRUE : B_FALSE); +} + static void copies_changed_cb(void *arg, uint64_t newval) { @@ -425,6 +438,11 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, zfs_prop_to_name(ZFS_PROP_SECONDARYCACHE), secondary_cache_changed_cb, os); } + if (err == 0) { + err = dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + encryption_changed_cb, os); + } if (!ds->ds_is_snapshot) { if (err == 0) { err = dsl_prop_register(ds, @@ -484,6 +502,7 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, /* It's the meta-objset. */ os->os_checksum = ZIO_CHECKSUM_FLETCHER_4; os->os_compress = ZIO_COMPRESS_ON; + os->os_encrypted = B_FALSE; os->os_copies = spa_max_replication(spa); os->os_dedup_checksum = ZIO_CHECKSUM_OFF; os->os_dedup_verify = B_FALSE; @@ -615,7 +634,7 @@ dmu_objset_own_impl(dsl_dataset_t *ds, dmu_objset_type_t type, */ int dmu_objset_own(const char *name, dmu_objset_type_t type, - boolean_t readonly, void *tag, objset_t **osp) + boolean_t readonly, boolean_t key_required, void *tag, objset_t **osp) { dsl_pool_t *dp; dsl_dataset_t *ds; @@ -624,7 +643,7 @@ dmu_objset_own(const char *name, dmu_objset_type_t type, err = dsl_pool_hold(name, FTAG, &dp); if (err != 0) return (err); - err = dsl_dataset_own(dp, name, tag, &ds); + err = dsl_dataset_own(dp, name, tag, key_required, &ds); if (err != 0) { dsl_pool_rele(dp, FTAG); return (err); @@ -640,12 +659,12 @@ dmu_objset_own(const char *name, dmu_objset_type_t type, int dmu_objset_own_obj(dsl_pool_t *dp, uint64_t obj, dmu_objset_type_t type, - boolean_t readonly, void *tag, objset_t **osp) + boolean_t readonly, boolean_t key_required, void *tag, objset_t **osp) { dsl_dataset_t *ds; int err; - err = dsl_dataset_own_obj(dp, obj, tag, &ds); + err = dsl_dataset_own_obj(dp, obj, tag, key_required, &ds); if (err != 0) return (err); @@ -676,6 +695,7 @@ dmu_objset_refresh_ownership(objset_t *os, void *tag) { dsl_pool_t *dp; dsl_dataset_t *ds, *newds; + boolean_t key_needed; char name[ZFS_MAX_DATASET_NAME_LEN]; ds = os->os_dsl_dataset; @@ -683,11 +703,18 @@ dmu_objset_refresh_ownership(objset_t *os, void *tag) VERIFY3P(ds->ds_owner, ==, tag); VERIFY(dsl_dataset_long_held(ds)); + if (os->os_encrypted && + (spa_keystore_lookup_key(os->os_spa, + os->os_dsl_dataset->ds_object, NULL, NULL) == 0)) + key_needed = B_TRUE; + else + key_needed = B_FALSE; + dsl_dataset_name(ds, name); dp = dmu_objset_pool(os); dsl_pool_config_enter(dp, FTAG); dmu_objset_disown(os, tag); - VERIFY0(dsl_dataset_own(dp, name, tag, &newds)); + VERIFY0(dsl_dataset_own(dp, name, tag, key_needed, &newds)); VERIFY3P(newds, ==, os->os_dsl_dataset); dsl_pool_config_exit(dp, FTAG); } @@ -895,6 +922,7 @@ typedef struct dmu_objset_create_arg { void *doca_userarg; dmu_objset_type_t doca_type; uint64_t doca_flags; + dsl_crypto_params_t *doca_dcp; } dmu_objset_create_arg_t; /*ARGSUSED*/ @@ -920,8 +948,16 @@ dmu_objset_create_check(void *arg, dmu_tx_t *tx) dsl_dir_rele(pdd, FTAG); return (SET_ERROR(EEXIST)); } + + error = dmu_objset_create_encryption_check(pdd, doca->doca_dcp); + if (error != 0) { + dsl_dir_rele(pdd, FTAG); + return (error); + } + error = dsl_fs_ss_limit_check(pdd, 1, ZFS_PROP_FILESYSTEM_LIMIT, NULL, doca->doca_cred); + dsl_dir_rele(pdd, FTAG); return (error); @@ -942,13 +978,25 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx) VERIFY0(dsl_dir_hold(dp, doca->doca_name, FTAG, &pdd, &tail)); obj = dsl_dataset_create_sync(pdd, tail, NULL, doca->doca_flags, - doca->doca_cred, tx); + doca->doca_cred, doca->doca_dcp, tx); VERIFY0(dsl_dataset_hold_obj(pdd->dd_pool, obj, FTAG, &ds)); bp = dsl_dataset_get_blkptr(ds); os = dmu_objset_create_impl(pdd->dd_pool->dp_spa, ds, bp, doca->doca_type, tx); + /* + * At this time, the objset is not owned and so it does not have its + * key mapping in the keystore. This mapping might be needed to create + * encrypted objects in the dataset. We create it here manually, but we + * cannot destroy it until after syncing is complete. Therefore any + * function that calls this must clean up the key mapping after syncing. + */ + if (os->os_encrypted) { + (void) spa_keystore_create_mapping(os->os_spa, + os->os_dsl_dataset, doca); + } + if (doca->doca_userfunc != NULL) { doca->doca_userfunc(os, doca->doca_userarg, doca->doca_cred, tx); @@ -963,8 +1011,11 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx) int dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags, - void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg) + dsl_crypto_params_t *dcp, void (*func)(objset_t *os, void *arg, + cred_t *cr, dmu_tx_t *tx), void *arg) { + int ret; + objset_t *os; dmu_objset_create_arg_t doca; doca.doca_name = name; @@ -973,16 +1024,33 @@ dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags, doca.doca_userfunc = func; doca.doca_userarg = arg; doca.doca_type = type; + doca.doca_dcp = dcp; - return (dsl_sync_task(name, + ret = dsl_sync_task(name, dmu_objset_create_check, dmu_objset_create_sync, &doca, - 5, ZFS_SPACE_CHECK_NORMAL)); + 5, ZFS_SPACE_CHECK_NORMAL); + if (ret) + return (ret); + + ret = dmu_objset_hold(name, FTAG, &os); + if (ret) + return (ret); + + /* See comment in dmu_objset_create_sync() for details */ + if (os->os_encrypted) + (void) spa_keystore_remove_mapping(os->os_spa, + os->os_dsl_dataset, &doca); + + dmu_objset_rele(os, FTAG); + + return (0); } typedef struct dmu_objset_clone_arg { const char *doca_clone; const char *doca_origin; cred_t *doca_cred; + dsl_crypto_params_t *doca_dcp; } dmu_objset_clone_arg_t; /*ARGSUSED*/ @@ -1016,7 +1084,6 @@ dmu_objset_clone_check(void *arg, dmu_tx_t *tx) dsl_dir_rele(pdd, FTAG); return (SET_ERROR(EDQUOT)); } - dsl_dir_rele(pdd, FTAG); error = dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin); if (error != 0) @@ -1027,7 +1094,17 @@ dmu_objset_clone_check(void *arg, dmu_tx_t *tx) dsl_dataset_rele(origin, FTAG); return (SET_ERROR(EINVAL)); } + + error = dmu_objset_clone_encryption_check(pdd, origin->ds_dir, + doca->doca_dcp); + if (error != 0) { + dsl_dataset_rele(origin, FTAG); + dsl_dir_rele(pdd, FTAG); + return (error); + } + dsl_dataset_rele(origin, FTAG); + dsl_dir_rele(pdd, FTAG); return (0); } @@ -1047,7 +1124,7 @@ dmu_objset_clone_sync(void *arg, dmu_tx_t *tx) VERIFY0(dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin)); obj = dsl_dataset_create_sync(pdd, tail, origin, 0, - doca->doca_cred, tx); + doca->doca_cred, doca->doca_dcp, tx); VERIFY0(dsl_dataset_hold_obj(pdd->dd_pool, obj, FTAG, &ds)); dsl_dataset_name(origin, namebuf); @@ -1060,13 +1137,15 @@ dmu_objset_clone_sync(void *arg, dmu_tx_t *tx) } int -dmu_objset_clone(const char *clone, const char *origin) +dmu_objset_clone(const char *clone, const char *origin, + dsl_crypto_params_t *dcp) { dmu_objset_clone_arg_t doca; doca.doca_clone = clone; doca.doca_origin = origin; doca.doca_cred = CRED(); + doca.doca_dcp = dcp; return (dsl_sync_task(clone, dmu_objset_clone_check, dmu_objset_clone_sync, &doca, @@ -1177,8 +1256,10 @@ dmu_objset_write_ready(zio_t *zio, arc_buf_t *abuf, void *arg) blkptr_t *bp = zio->io_bp; objset_t *os = arg; dnode_phys_t *dnp = &os->os_phys->os_meta_dnode; + uint64_t fill = 0; ASSERT(!BP_IS_EMBEDDED(bp)); + ASSERT(!BP_IS_ENCRYPTED(bp)); ASSERT3P(bp, ==, os->os_rootbp); ASSERT3U(BP_GET_TYPE(bp), ==, DMU_OT_OBJSET); ASSERT0(BP_GET_LEVEL(bp)); @@ -1189,9 +1270,10 @@ dmu_objset_write_ready(zio_t *zio, arc_buf_t *abuf, void *arg) * objects that are stored in the objset_phys_t -- the meta * dnode and user/group accounting objects). */ - bp->blk_fill = 0; for (i = 0; i < dnp->dn_nblkptr; i++) - bp->blk_fill += BP_GET_FILL(&dnp->dn_blkptr[i]); + fill += BP_GET_FILL(&dnp->dn_blkptr[i]); + + BP_SET_FILL(bp, fill); } /* ARGSUSED */ @@ -1248,7 +1330,7 @@ dmu_objset_sync(objset_t *os, zio_t *pio, dmu_tx_t *tx) ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID); arc_release(os->os_phys_buf, &os->os_phys_buf); - dmu_write_policy(os, NULL, 0, 0, ZIO_COMPRESS_INHERIT, &zp); + dmu_write_policy(os, NULL, 0, 0, &zp); zio = arc_write(pio, os->os_spa, tx->tx_txg, os->os_rootbp, os->os_phys_buf, DMU_OS_IS_L2CACHEABLE(os), diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index d66a9dca45d8..0b2d8b4424e5 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -336,10 +336,10 @@ dump_write(dmu_sendarg_t *dsp, dmu_object_type_t type, payload_size = drrw->drr_logical_size; } - if (bp == NULL || BP_IS_EMBEDDED(bp)) { + if (bp == NULL || BP_IS_EMBEDDED(bp) || BP_IS_ENCRYPTED(bp)) { /* * There's no pre-computed checksum for partial-block - * writes or embedded BP's, so (like + * writes, embedded BP's, or encrypted BP's, so (like * fletcher4-checkummed blocks) userland will have to * compute a dedup-capable checksum itself. */ @@ -729,7 +729,7 @@ do_dump(dmu_sendarg_t *dsa, struct send_block_record *data) zb->zb_blkid * blksz >= dsa->dsa_resume_offset)); if (request_compressed) - zioflags |= ZIO_FLAG_RAW; + zioflags |= ZIO_FLAG_RAW_COMPRESS; if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, ZIO_PRIORITY_ASYNC_READ, zioflags, @@ -1009,7 +1009,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, if (err != 0) return (err); - err = dsl_dataset_hold_obj(dp, tosnap, FTAG, &ds); + err = dsl_dataset_hold_crypt_obj(dp, tosnap, FTAG, &ds); if (err != 0) { dsl_pool_rele(dp, FTAG); return (err); @@ -1021,7 +1021,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, err = dsl_dataset_hold_obj(dp, fromsnap, FTAG, &fromds); if (err != 0) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); dsl_pool_rele(dp, FTAG); return (err); } @@ -1039,7 +1039,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE, embedok, large_block_ok, compressok, outfd, 0, 0, vp, off); } - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (err); } @@ -1066,10 +1066,10 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, * We are sending a filesystem or volume. Ensure * that it doesn't change by owning the dataset. */ - err = dsl_dataset_own(dp, tosnap, FTAG, &ds); + err = dsl_dataset_own(dp, tosnap, FTAG, B_TRUE, &ds); owned = B_TRUE; } else { - err = dsl_dataset_hold(dp, tosnap, FTAG, &ds); + err = dsl_dataset_hold_crypt(dp, tosnap, FTAG, &ds); } if (err != 0) { dsl_pool_rele(dp, FTAG); @@ -1109,7 +1109,11 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, err = dsl_bookmark_lookup(dp, fromsnap, ds, &zb); } if (err != 0) { - dsl_dataset_rele(ds, FTAG); + if (owned) + dsl_dataset_disown(ds, FTAG); + else + dsl_dataset_rele_crypt(ds, FTAG); + dsl_pool_rele(dp, FTAG); return (err); } @@ -1124,7 +1128,7 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, if (owned) dsl_dataset_disown(ds, FTAG); else - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (err); } @@ -1264,7 +1268,8 @@ dmu_send_estimate_from_txg(dsl_dataset_t *ds, uint64_t from_txg, * traverse the blocks of the snapshot with birth times after * from_txg, summing their uncompressed size */ - err = traverse_dataset(ds, from_txg, TRAVERSE_POST, + err = traverse_dataset(ds, from_txg, + TRAVERSE_POST | TRAVERSE_NO_DECRYPT, dmu_calculate_send_traversal, &size); if (err) @@ -1431,18 +1436,18 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_DNODE)) return (SET_ERROR(ENOTSUP)); - error = dsl_dataset_hold(dp, tofs, FTAG, &ds); + error = dsl_dataset_hold_crypt(dp, tofs, FTAG, &ds); if (error == 0) { /* target fs already exists; recv into temp clone */ /* Can't recv a clone into an existing fs */ if (flags & DRR_FLAG_CLONE || drba->drba_origin) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } error = recv_begin_check_existing_impl(drba, ds, fromguid); - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); } else if (error == ENOENT) { /* target fs does not exist; must be a full backup or clone */ char buf[ZFS_MAX_DATASET_NAME_LEN]; @@ -1467,7 +1472,7 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) /* Open the parent of tofs */ ASSERT3U(strlen(tofs), <, sizeof (buf)); (void) strlcpy(buf, tofs, strrchr(tofs, '/') - tofs + 1); - error = dsl_dataset_hold(dp, buf, FTAG, &ds); + error = dsl_dataset_hold_crypt(dp, buf, FTAG, &ds); if (error != 0) return (error); @@ -1479,14 +1484,14 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) error = dsl_fs_ss_limit_check(ds->ds_dir, 1, ZFS_PROP_FILESYSTEM_LIMIT, NULL, drba->drba_cred); if (error != 0) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (error); } error = dsl_fs_ss_limit_check(ds->ds_dir, 1, ZFS_PROP_SNAPSHOT_LIMIT, NULL, drba->drba_cred); if (error != 0) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (error); } @@ -1495,23 +1500,23 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) error = dsl_dataset_hold(dp, drba->drba_origin, FTAG, &origin); if (error != 0) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (error); } if (!origin->ds_is_snapshot) { dsl_dataset_rele(origin, FTAG); - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } if (dsl_dataset_phys(origin)->ds_guid != fromguid && fromguid != 0) { dsl_dataset_rele(origin, FTAG); - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(ENODEV)); } dsl_dataset_rele(origin, FTAG); } - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); error = 0; } return (error); @@ -1533,7 +1538,7 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) if (drrb->drr_flags & DRR_FLAG_CI_DATA) crflags |= DS_FLAG_CI_DATASET; - error = dsl_dataset_hold(dp, tofs, FTAG, &ds); + error = dsl_dataset_hold_crypt(dp, tofs, FTAG, &ds); if (error == 0) { /* create temporary clone */ dsl_dataset_t *snap = NULL; @@ -1542,10 +1547,10 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) drba->drba_snapobj, FTAG, &snap)); } dsobj = dsl_dataset_create_sync(ds->ds_dir, recv_clone_name, - snap, crflags, drba->drba_cred, tx); + snap, crflags, drba->drba_cred, NULL, tx); if (drba->drba_snapobj != 0) dsl_dataset_rele(snap, FTAG); - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); } else { dsl_dir_t *dd; const char *tail; @@ -1561,13 +1566,13 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) /* Create new dataset. */ dsobj = dsl_dataset_create_sync(dd, strrchr(tofs, '/') + 1, - origin, crflags, drba->drba_cred, tx); + origin, crflags, drba->drba_cred, NULL, tx); if (origin != NULL) dsl_dataset_rele(origin, FTAG); dsl_dir_rele(dd, FTAG); drba->drba_cookie->drc_newfs = B_TRUE; } - VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, &newds)); + VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, B_TRUE, &newds)); if (drba->drba_cookie->drc_resumable) { uint64_t one = 1; @@ -1667,28 +1672,28 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) (void) snprintf(recvname, sizeof (recvname), "%s/%s", tofs, recv_clone_name); - if (dsl_dataset_hold(dp, recvname, FTAG, &ds) != 0) { + if (dsl_dataset_hold_crypt(dp, recvname, FTAG, &ds) != 0) { /* %recv does not exist; continue in tofs */ - error = dsl_dataset_hold(dp, tofs, FTAG, &ds); + error = dsl_dataset_hold_crypt(dp, tofs, FTAG, &ds); if (error != 0) return (error); } /* check that ds is marked inconsistent */ if (!DS_IS_INCONSISTENT(ds)) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } /* check that there is resuming data, and that the toguid matches */ if (!dsl_dataset_is_zapified(ds)) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } error = zap_lookup(dp->dp_meta_objset, ds->ds_object, DS_FIELD_RESUME_TOGUID, sizeof (val), 1, &val); if (error != 0 || drrb->drr_toguid != val) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } @@ -1698,13 +1703,13 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) * fails) because it will be marked inconsistent. */ if (dsl_dataset_has_owner(ds)) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EBUSY)); } /* There should not be any snapshots of this fs yet. */ if (ds->ds_prev != NULL && ds->ds_prev->ds_dir == ds->ds_dir) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } @@ -1718,11 +1723,11 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) (void) zap_lookup(dp->dp_meta_objset, ds->ds_object, DS_FIELD_RESUME_FROMGUID, sizeof (val), 1, &val); if (drrb->drr_fromguid != val) { - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (SET_ERROR(EINVAL)); } - dsl_dataset_rele(ds, FTAG); + dsl_dataset_rele_crypt(ds, FTAG); return (0); } @@ -1753,7 +1758,7 @@ dmu_recv_resume_begin_sync(void *arg, dmu_tx_t *tx) dsobj = ds->ds_object; dsl_dataset_rele(ds, FTAG); - VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, &ds)); + VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, B_TRUE, &ds)); dmu_buf_will_dirty(ds->ds_dbuf, tx); dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT; @@ -3262,11 +3267,17 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx) } drc->drc_newsnapobj = dsl_dataset_phys(drc->drc_ds)->ds_prev_snap_obj; zvol_create_minors(dp->dp_spa, drc->drc_tofs, B_TRUE); + /* * Release the hold from dmu_recv_begin. This must be done before - * we return to open context, so that when we free the dataset's dnode, - * we can evict its bonus buffer. + * we return to open context, so that when we free the dataset's dnode + * we can evict its bonus buffer. Since the dataset may be destroyed + * at this point (and therefore won't have a valid pointer to the spa) + * we release the key mapping manually here while we do have a valid + * pointer. */ + (void) spa_keystore_remove_mapping(dmu_tx_pool(tx)->dp_spa, + drc->drc_ds, drc->drc_ds); dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); drc->drc_ds = NULL; } diff --git a/module/zfs/dmu_traverse.c b/module/zfs/dmu_traverse.c index c78228d74588..2fc6887dde6c 100644 --- a/module/zfs/dmu_traverse.c +++ b/module/zfs/dmu_traverse.c @@ -132,7 +132,7 @@ traverse_zil(traverse_data_t *td, zil_header_t *zh) zilog = zil_alloc(spa_get_dsl(td->td_spa)->dp_meta_objset, zh); (void) zil_parse(zilog, traverse_zil_block, traverse_zil_record, td, - claim_txg); + claim_txg, !(td->td_flags & TRAVERSE_NO_DECRYPT)); zil_free(zilog); } @@ -181,6 +181,7 @@ traverse_prefetch_metadata(traverse_data_t *td, const blkptr_t *bp, const zbookmark_phys_t *zb) { arc_flags_t flags = ARC_FLAG_NOWAIT | ARC_FLAG_PREFETCH; + int zio_flags = ZIO_FLAG_CANFAIL; if (!(td->td_flags & TRAVERSE_PREFETCH_METADATA)) return; @@ -196,8 +197,11 @@ traverse_prefetch_metadata(traverse_data_t *td, if (BP_GET_LEVEL(bp) == 0 && BP_GET_TYPE(bp) != DMU_OT_DNODE) return; + if ((td->td_flags & TRAVERSE_NO_DECRYPT) && BP_IS_ENCRYPTED(bp)) + zio_flags |= ZIO_FLAG_RAW; + (void) arc_read(NULL, td->td_spa, bp, NULL, NULL, - ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); + ZIO_PRIORITY_ASYNC_READ, zio_flags, &flags, zb); } static boolean_t @@ -324,14 +328,23 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp, } else if (BP_GET_TYPE(bp) == DMU_OT_DNODE) { uint32_t flags = ARC_FLAG_WAIT; + uint32_t zio_flags = ZIO_FLAG_CANFAIL; int32_t i; int32_t epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; dnode_phys_t *child_dnp; + /* + * dnode blocks might have their bonus buffers encrypted, so + * we must be careful to honor TRAVERSE_NO_DECRYPT + */ + if ((td->td_flags & TRAVERSE_NO_DECRYPT) && BP_IS_ENCRYPTED(bp)) + zio_flags |= ZIO_FLAG_RAW; + err = arc_read(NULL, td->td_spa, bp, arc_getbuf_func, &buf, - ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); + ZIO_PRIORITY_ASYNC_READ, zio_flags, &flags, zb); if (err != 0) goto post; + child_dnp = buf->b_data; for (i = 0; i < epb; i += child_dnp[i].dn_extra_slots + 1) { @@ -500,6 +513,7 @@ traverse_prefetcher(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) { prefetch_data_t *pfd = arg; + int zio_flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE; arc_flags_t aflags = ARC_FLAG_NOWAIT | ARC_FLAG_PREFETCH; ASSERT(pfd->pd_bytes_fetched >= 0); @@ -518,8 +532,11 @@ traverse_prefetcher(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, cv_broadcast(&pfd->pd_cv); mutex_exit(&pfd->pd_mtx); + if ((pfd->pd_flags & TRAVERSE_NO_DECRYPT) && BP_IS_ENCRYPTED(bp)) + zio_flags |= ZIO_FLAG_RAW; + (void) arc_read(NULL, spa, bp, NULL, NULL, ZIO_PRIORITY_ASYNC_READ, - ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE, &aflags, zb); + zio_flags, &aflags, zb); return (0); } diff --git a/module/zfs/dnode.c b/module/zfs/dnode.c index 45bb958cd406..848dcec5c69a 100644 --- a/module/zfs/dnode.c +++ b/module/zfs/dnode.c @@ -1250,7 +1250,7 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag, int slots, rw_exit(&mdn->dn_struct_rwlock); if (db == NULL) return (SET_ERROR(EIO)); - err = dbuf_read(db, NULL, DB_RF_CANFAIL); + err = dbuf_read(db, NULL, DB_RF_CANFAIL | DB_RF_NO_DECRYPT); if (err) { dbuf_rele(db, FTAG); return (err); @@ -2026,7 +2026,8 @@ dnode_next_offset_level(dnode_t *dn, int flags, uint64_t *offset, */ return (SET_ERROR(ESRCH)); } - error = dbuf_read(db, NULL, DB_RF_CANFAIL | DB_RF_HAVESTRUCT); + error = dbuf_read(db, NULL, + DB_RF_CANFAIL | DB_RF_HAVESTRUCT | DB_RF_NO_DECRYPT); if (error) { dbuf_rele(db, FTAG); return (error); diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c new file mode 100644 index 000000000000..6115a862e1ab --- /dev/null +++ b/module/zfs/dsl_crypt.c @@ -0,0 +1,1591 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Datto, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This file's primary purpose is for managing master encryption keys in + * memory and on disk. For more info on how these keys are used, see the + * block comment in zio_crypt.c. + * + * All master keys are stored encrypted on disk in the form of the DSL + * Crypto Key ZAP object. The binary key data in this object is always + * randomly generated and is encrypted with the user's secret key. This + * layer of indirection allows the user to change their key without + * needing to re-encrypt the entire dataset. The ZAP also holds on to the + * (non-encrypted) encryption algorithm identifier, IV, and MAC needed to + * safely decrypt the master key. For more info on the user's key see the + * block comment in libzfs_crypto.c + * + * In memory encryption keys are managed through the spa_keystore. The + * keystore consists of 3 AVL trees, which are as follows: + * + * The Wrapping Key Tree: + * The wrapping key (wkey) tree stores the user's keys that are fed into the + * kernel through 'zfs key -K' and related commands. Datasets inherit their + * parent's wkey, so they are refcounted. The wrapping keys remain in memory + * until they are explicitly unloaded (with "zfs key -u"). Unloading is only + * possible when no datasets are using them (refcount=0). + * + * The DSL Crypto Key Tree: + * The DSL Crypto Keys are the in-memory representation of decrypted master + * keys. They are used by the functions in zio_crypt.c to perform encryption + * and decryption. The decrypted master key bit patterns are shared between + * all datasets within a "clone family", but each clone may use a different + * wrapping key. As a result, we maintain one of these structs for each clone + * to allow us to manage the loading and unloading of each separately. + * Snapshots of a given dataset, however, will share a DSL Crypto Key, so they + * are also refcounted. Once the refcount on a key hits zero, it is immediately + * zeroed out and freed. + * + * The Crypto Key Mapping Tree: + * The zio layer needs to lookup master keys by their dataset object id. Since + * the DSL Crypto Keys can belong to multiple datasets, we maintain a tree of + * dsl_key_mapping_t's which essentially just map the dataset object id to its + * appropriate DSL Crypto Key. The management for creating and destroying these + * mappings hooks into the code for owning and disowning datasets. Usually, + * there will only be one active dataset owner, but there are times + * (particularly during dataset creation and destruction) when this may not be + * true or the dataset may not be initialized enough to own. As a result, this + * object is also refcounted. + */ + +void +dsl_wrapping_key_hold(dsl_wrapping_key_t *wkey, void *tag) +{ + (void) refcount_add(&wkey->wk_refcnt, tag); +} + +void +dsl_wrapping_key_rele(dsl_wrapping_key_t *wkey, void *tag) +{ + (void) refcount_remove(&wkey->wk_refcnt, tag); +} + +void +dsl_wrapping_key_free(dsl_wrapping_key_t *wkey) +{ + ASSERT0(refcount_count(&wkey->wk_refcnt)); + + if (wkey->wk_key.ck_data) { + bzero(wkey->wk_key.ck_data, + BITS_TO_BYTES(wkey->wk_key.ck_length)); + kmem_free(wkey->wk_key.ck_data, + BITS_TO_BYTES(wkey->wk_key.ck_length)); + } + + refcount_destroy(&wkey->wk_refcnt); + kmem_free(wkey, sizeof (dsl_wrapping_key_t)); +} + +int +dsl_wrapping_key_create(uint8_t *wkeydata, dsl_wrapping_key_t **wkey_out) +{ + int ret; + dsl_wrapping_key_t *wkey; + + /* allocate the wrapping key */ + wkey = kmem_alloc(sizeof (dsl_wrapping_key_t), KM_SLEEP); + if (!wkey) + return (SET_ERROR(ENOMEM)); + + /* allocate and initialize the underlying crypto key */ + wkey->wk_key.ck_data = kmem_alloc(WRAPPING_KEY_LEN, KM_SLEEP); + if (!wkey->wk_key.ck_data) { + ret = ENOMEM; + goto error; + } + + wkey->wk_key.ck_format = CRYPTO_KEY_RAW; + wkey->wk_key.ck_length = BYTES_TO_BITS(WRAPPING_KEY_LEN); + + /* copy the data */ + bcopy(wkeydata, wkey->wk_key.ck_data, WRAPPING_KEY_LEN); + + /* initialize the refcount */ + refcount_create(&wkey->wk_refcnt); + + *wkey_out = wkey; + return (0); + +error: + dsl_wrapping_key_free(wkey); + + *wkey_out = NULL; + return (ret); +} + +int +dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, + dsl_crypto_params_t **dcp_out) +{ + int ret; + dsl_crypto_params_t *dcp = NULL; + dsl_wrapping_key_t *wkey = NULL; + boolean_t crypt_exists = B_TRUE, wkeydata_exists = B_TRUE; + boolean_t keysource_exists = B_TRUE, salt_exists = B_TRUE; + boolean_t iters_exists = B_TRUE, cmd_exists = B_TRUE; + char *keysource = NULL; + uint64_t iters = 0, salt = 0, crypt = 0, cmd = ZFS_IOC_KEY_CMD_NONE; + uint8_t *wkeydata; + uint_t wkeydata_len; + + /* get relevant properties from the nvlist */ + if (props) { + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); + if (ret) + crypt_exists = B_FALSE; + + ret = nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); + if (ret) + keysource_exists = B_FALSE; + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_SALT), &salt); + if (ret) + salt_exists = B_FALSE; + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); + if (ret) + iters_exists = B_FALSE; + + ret = nvlist_lookup_uint64(props, "crypto_cmd", &cmd); + if (ret) + cmd_exists = B_FALSE; + } else { + crypt_exists = B_FALSE; + keysource_exists = B_FALSE; + salt_exists = B_FALSE; + iters_exists = B_FALSE; + cmd_exists = B_FALSE; + } + + if (crypto_args) { + ret = nvlist_lookup_uint8_array(crypto_args, "wkeydata", + &wkeydata, &wkeydata_len); + if (ret) + wkeydata_exists = B_FALSE; + } else { + wkeydata_exists = B_FALSE; + } + + /* no parameters are valid; results in inherited crypto settings */ + if (!crypt_exists && !keysource_exists && !wkeydata_exists && + !salt_exists && !cmd_exists && !iters_exists) { + *dcp_out = NULL; + return (0); + } + + dcp = kmem_alloc(sizeof (dsl_crypto_params_t), KM_SLEEP); + if (!dcp) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + /* check wrapping key length */ + if (wkeydata_exists && wkeydata_len != WRAPPING_KEY_LEN) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* specifying a keysource requires keydata */ + if (keysource_exists && !wkeydata_exists) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* remove crypto_cmd from props since it should not be used again */ + if (cmd_exists) + (void) nvlist_remove_all(props, "crypto_cmd"); + + /* if the user asked for the deault crypt, decide that now */ + if (crypt == ZIO_CRYPT_ON) { + crypt = ZIO_CRYPT_ON_VALUE; + + ret = nvlist_remove_all(props, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION)); + if (ret) + goto error; + ret = nvlist_add_uint64(props, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt); + if (ret) + goto error; + } + + /* create the wrapping key from the raw data */ + if (wkeydata_exists) { + /* create the wrapping key with the verified parameters */ + ret = dsl_wrapping_key_create(wkeydata, &wkey); + if (ret) + goto error; + } + + dcp->cp_cmd = cmd; + dcp->cp_crypt = crypt; + dcp->cp_salt = salt; + dcp->cp_iters = iters; + dcp->cp_keysource = keysource; + dcp->cp_wkey = wkey; + *dcp_out = dcp; + + return (0); + +error: + if (wkey) + dsl_wrapping_key_free(wkey); + if (dcp) + kmem_free(dcp, sizeof (dsl_crypto_params_t)); + + *dcp_out = NULL; + return (ret); +} + +void +dsl_crypto_params_free(dsl_crypto_params_t *dcp, boolean_t unload) +{ + if (!dcp) + return; + + if (unload) + dsl_wrapping_key_free(dcp->cp_wkey); + kmem_free(dcp, sizeof (dsl_crypto_params_t)); +} + +static int +spa_crypto_key_compare(const void *a, const void *b) +{ + const dsl_crypto_key_t *dcka = a; + const dsl_crypto_key_t *dckb = b; + + if (dcka->dck_obj < dckb->dck_obj) + return (-1); + if (dcka->dck_obj > dckb->dck_obj) + return (1); + return (0); +} + +static int +spa_key_mapping_compare(const void *a, const void *b) +{ + const dsl_key_mapping_t *kma = a; + const dsl_key_mapping_t *kmb = b; + + if (kma->km_dsobj < kmb->km_dsobj) + return (-1); + if (kma->km_dsobj > kmb->km_dsobj) + return (1); + return (0); +} + +static int +spa_wkey_compare(const void *a, const void *b) +{ + const dsl_wrapping_key_t *wka = a; + const dsl_wrapping_key_t *wkb = b; + + if (wka->wk_ddobj < wkb->wk_ddobj) + return (-1); + if (wka->wk_ddobj > wkb->wk_ddobj) + return (1); + return (0); +} + +void +spa_keystore_init(spa_keystore_t *sk) +{ + rw_init(&sk->sk_dk_lock, NULL, RW_DEFAULT, NULL); + rw_init(&sk->sk_km_lock, NULL, RW_DEFAULT, NULL); + rw_init(&sk->sk_wkeys_lock, NULL, RW_DEFAULT, NULL); + avl_create(&sk->sk_dsl_keys, spa_crypto_key_compare, + sizeof (dsl_crypto_key_t), + offsetof(dsl_crypto_key_t, dck_avl_link)); + avl_create(&sk->sk_key_mappings, spa_key_mapping_compare, + sizeof (dsl_key_mapping_t), + offsetof(dsl_key_mapping_t, km_avl_link)); + avl_create(&sk->sk_wkeys, spa_wkey_compare, sizeof (dsl_wrapping_key_t), + offsetof(dsl_wrapping_key_t, wk_avl_link)); +} + +void +spa_keystore_fini(spa_keystore_t *sk) +{ + dsl_wrapping_key_t *wkey; + void *cookie = NULL; + + ASSERT(avl_is_empty(&sk->sk_dsl_keys)); + ASSERT(avl_is_empty(&sk->sk_key_mappings)); + + while ((wkey = avl_destroy_nodes(&sk->sk_wkeys, &cookie)) != NULL) + dsl_wrapping_key_free(wkey); + + avl_destroy(&sk->sk_wkeys); + avl_destroy(&sk->sk_key_mappings); + avl_destroy(&sk->sk_dsl_keys); + rw_destroy(&sk->sk_wkeys_lock); + rw_destroy(&sk->sk_km_lock); + rw_destroy(&sk->sk_dk_lock); +} + +static int +dsl_dir_hold_keysource_source_dd(dsl_dir_t *dd, void *tag, + dsl_dir_t **inherit_dd_out) +{ + int ret; + dsl_dir_t *inherit_dd = NULL; + char keysource[MAXNAMELEN]; + char setpoint[MAXNAMELEN]; + + /* + * lookup dd's keysource property and find + * out where it was inherited from + */ + ret = dsl_prop_get_dd(dd, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), + 1, sizeof (keysource), keysource, setpoint, B_FALSE); + if (ret) + goto error; + + /* hold the dsl dir that we inherited the property from */ + ret = dsl_dir_hold(dd->dd_pool, setpoint, tag, &inherit_dd, NULL); + if (ret) + goto error; + + *inherit_dd_out = inherit_dd; + return (0); + +error: + *inherit_dd_out = NULL; + return (ret); +} + +zfs_keystatus_t +dsl_dataset_keystore_keystatus(dsl_dataset_t *ds) +{ + int ret; + dsl_wrapping_key_t *wkey; + + /* check if this dataset has a owns a dsl key */ + if (ds->ds_dir->dd_crypto_obj == 0) + return (ZFS_KEYSTATUS_NONE); + + /* lookup the wkey. if it doesn't exist the key is unavailable */ + ret = spa_keystore_wkey_hold_ddobj(ds->ds_dir->dd_pool->dp_spa, + ds->ds_dir->dd_object, FTAG, &wkey); + if (ret) + return (ZFS_KEYSTATUS_UNAVAILABLE); + + dsl_wrapping_key_rele(wkey, FTAG); + + return (ZFS_KEYSTATUS_AVAILABLE); +} + +static int +spa_keystore_wkey_hold_ddobj_impl(spa_t *spa, uint64_t ddobj, + void *tag, dsl_wrapping_key_t **wkey_out) +{ + int ret; + dsl_wrapping_key_t search_wkey; + dsl_wrapping_key_t *found_wkey; + + ASSERT(RW_LOCK_HELD(&spa->spa_keystore.sk_wkeys_lock)); + + /* init the search wrapping key */ + search_wkey.wk_ddobj = ddobj; + + /* lookup the wrapping key */ + found_wkey = avl_find(&spa->spa_keystore.sk_wkeys, &search_wkey, NULL); + if (!found_wkey) { + ret = SET_ERROR(ENOENT); + goto error; + } + + /* increment the refcount */ + dsl_wrapping_key_hold(found_wkey, tag); + + *wkey_out = found_wkey; + return (0); + +error: + *wkey_out = NULL; + return (ret); +} + +int +spa_keystore_wkey_hold_ddobj(spa_t *spa, uint64_t ddobj, void *tag, + dsl_wrapping_key_t **wkey_out) +{ + int ret; + dsl_pool_t *dp = spa_get_dsl(spa); + dsl_dir_t *dd = NULL, *inherit_dd = NULL; + dsl_wrapping_key_t *wkey; + boolean_t locked = B_FALSE; + + if (!RW_WRITE_HELD(&dp->dp_spa->spa_keystore.sk_wkeys_lock)) { + rw_enter(&spa->spa_keystore.sk_wkeys_lock, RW_READER); + locked = B_TRUE; + } + + /* + * There is a special case in zfs_create_fs() where the wrapping key + * is needed before the filesystem's properties are set. This is + * problematic because dsl_dir_hold_keysource_source_dd() uses the + * properties to determine where the wrapping key is inherited from. + * As a result, here we try to find a wrapping key for this dd before + * checking for wrapping key inheritance. + */ + ret = spa_keystore_wkey_hold_ddobj_impl(spa, ddobj, tag, &wkey); + if (ret == 0) { + if (locked) + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + + *wkey_out = wkey; + return (0); + } + + /* hold the dsl dir */ + ret = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd); + if (ret) + goto error; + + /* get the dd that the keysource property was inherited from */ + ret = dsl_dir_hold_keysource_source_dd(dd, FTAG, &inherit_dd); + if (ret) + goto error; + + /* lookup the wkey in the avl tree */ + ret = spa_keystore_wkey_hold_ddobj_impl(spa, inherit_dd->dd_object, + tag, &wkey); + if (ret) + goto error; + + /* unlock the wkey tree if we locked it */ + if (locked) + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + + dsl_dir_rele(inherit_dd, FTAG); + dsl_dir_rele(dd, FTAG); + + *wkey_out = wkey; + return (0); + +error: + if (locked) + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + if (inherit_dd) + dsl_dir_rele(inherit_dd, FTAG); + if (dd) + dsl_dir_rele(dd, FTAG); + + *wkey_out = NULL; + return (ret); +} + +static void +dsl_crypto_key_free(dsl_crypto_key_t *dck) +{ + ASSERT(refcount_count(&dck->dck_refcnt) == 0); + + /* destroy the zio_crypt_key_t */ + zio_crypt_key_destroy(&dck->dck_key); + + /* free the refcount, wrapping key, and lock */ + refcount_destroy(&dck->dck_refcnt); + if (dck->dck_wkey) + dsl_wrapping_key_rele(dck->dck_wkey, dck); + + /* free the key */ + kmem_free(dck, sizeof (dsl_crypto_key_t)); +} + +static void +dsl_crypto_key_rele(dsl_crypto_key_t *dck, void *tag) +{ + if (refcount_remove(&dck->dck_refcnt, tag) == 0) + dsl_crypto_key_free(dck); +} + +static int +dsl_crypto_key_open(objset_t *mos, dsl_wrapping_key_t *wkey, + uint64_t dckobj, void *tag, dsl_crypto_key_t **dck_out) +{ + int ret; + uint64_t crypt = 0; + uint8_t raw_keydata[MAX_MASTER_KEY_LEN]; + uint8_t raw_hmac_keydata[HMAC_SHA256_KEYLEN]; + uint8_t iv[WRAPPING_IV_LEN]; + uint8_t mac[WRAPPING_MAC_LEN]; + dsl_crypto_key_t *dck; + + /* allocate and initialize the key */ + dck = kmem_zalloc(sizeof (dsl_crypto_key_t), KM_SLEEP); + if (!dck) + return (SET_ERROR(ENOMEM)); + + /* fetch all of the values we need from the ZAP */ + ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_CRYPT, 8, 1, &crypt); + if (ret) + goto error; + + ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_MASTER_BUF, 1, + MAX_MASTER_KEY_LEN, raw_keydata); + if (ret) + goto error; + + ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_HMAC_KEY_BUF, 1, + HMAC_SHA256_KEYLEN, raw_hmac_keydata); + if (ret) + goto error; + + ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_IV, 1, WRAPPING_IV_LEN, + iv); + if (ret) + goto error; + + ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_MAC, 1, WRAPPING_MAC_LEN, + mac); + if (ret) + goto error; + + /* + * Unwrap the keys. If there is an error return EACCES to indicate + * an authentication failure. + */ + ret = zio_crypt_key_unwrap(&wkey->wk_key, crypt, raw_keydata, + raw_hmac_keydata, iv, mac, &dck->dck_key); + if (ret) { + ret = SET_ERROR(EACCES); + goto error; + } + + /* finish initializing the dsl_crypto_key_t */ + refcount_create(&dck->dck_refcnt); + dsl_wrapping_key_hold(wkey, dck); + dck->dck_wkey = wkey; + dck->dck_obj = dckobj; + refcount_add(&dck->dck_refcnt, tag); + + *dck_out = dck; + return (0); + +error: + if (dck) { + bzero(dck, sizeof (dsl_crypto_key_t)); + kmem_free(dck, sizeof (dsl_crypto_key_t)); + } + + *dck_out = NULL; + return (ret); +} + +static int +spa_keystore_dsl_key_hold_impl(spa_t *spa, uint64_t dckobj, void *tag, + dsl_crypto_key_t **dck_out) +{ + int ret; + dsl_crypto_key_t search_dck; + dsl_crypto_key_t *found_dck; + + ASSERT(RW_LOCK_HELD(&spa->spa_keystore.sk_dk_lock)); + + /* init the search key */ + search_dck.dck_obj = dckobj; + + /* find the matching key in the keystore */ + found_dck = avl_find(&spa->spa_keystore.sk_dsl_keys, &search_dck, NULL); + if (!found_dck) { + ret = SET_ERROR(ENOENT); + goto error; + } + + /* increment the refcount */ + refcount_add(&found_dck->dck_refcnt, tag); + + *dck_out = found_dck; + return (0); + +error: + *dck_out = NULL; + return (ret); +} + +int +spa_keystore_dsl_key_hold_dd(spa_t *spa, dsl_dir_t *dd, void *tag, + dsl_crypto_key_t **dck_out) +{ + int ret; + avl_index_t where; + dsl_crypto_key_t *dck = NULL; + dsl_wrapping_key_t *wkey = NULL; + uint64_t dckobj = dd->dd_crypto_obj; + + /* + * we need a write lock here because we might load a dsl key + * from disk if we don't have it in the keystore already. + * This could be a problem because this lock also allows the zio + * layer to access the keys, but this function should only be + * called during key loading, encrypted dataset mounting, encrypted + * dataset creation, etc. so this is probably ok. If it becomes a + * problem an RCU-like implementation could make sense here. + */ + rw_enter(&spa->spa_keystore.sk_dk_lock, RW_WRITER); + + /* lookup the key in the tree of currently loaded keys */ + ret = spa_keystore_dsl_key_hold_impl(spa, dckobj, tag, &dck); + if (!ret) { + rw_exit(&spa->spa_keystore.sk_dk_lock); + *dck_out = dck; + return (0); + } + + /* lookup the wrapping key from the keystore */ + ret = spa_keystore_wkey_hold_ddobj(spa, dd->dd_object, FTAG, &wkey); + if (ret) { + ret = SET_ERROR(EACCES); + goto error_unlock; + } + + /* read the key from disk */ + ret = dsl_crypto_key_open(spa_get_dsl(spa)->dp_meta_objset, wkey, + dckobj, tag, &dck); + if (ret) + goto error_unlock; + + /* + * add the key to the keystore (this should always succeed + * since we made sure it didn't exist before) + */ + avl_find(&spa->spa_keystore.sk_dsl_keys, dck, &where); + avl_insert(&spa->spa_keystore.sk_dsl_keys, dck, where); + + /* release the wrapping key (the dsl key now has a reference to it) */ + dsl_wrapping_key_rele(wkey, FTAG); + + rw_exit(&spa->spa_keystore.sk_dk_lock); + + *dck_out = dck; + return (0); + +error_unlock: + rw_exit(&spa->spa_keystore.sk_dk_lock); + if (wkey) + dsl_wrapping_key_rele(wkey, FTAG); + + *dck_out = NULL; + return (ret); +} + +void +spa_keystore_dsl_key_rele(spa_t *spa, dsl_crypto_key_t *dck, void *tag) +{ + rw_enter(&spa->spa_keystore.sk_dk_lock, RW_WRITER); + + if (refcount_remove(&dck->dck_refcnt, tag) == 0) { + avl_remove(&spa->spa_keystore.sk_dsl_keys, dck); + dsl_crypto_key_free(dck); + } + + rw_exit(&spa->spa_keystore.sk_dk_lock); +} + +int +spa_keystore_load_wkey_impl(spa_t *spa, dsl_wrapping_key_t *wkey) +{ + int ret; + avl_index_t where; + dsl_wrapping_key_t *found_wkey; + + rw_enter(&spa->spa_keystore.sk_wkeys_lock, RW_WRITER); + + /* insert the wrapping key into the keystore */ + found_wkey = avl_find(&spa->spa_keystore.sk_wkeys, wkey, &where); + if (found_wkey) { + ret = SET_ERROR(EEXIST); + goto error_unlock; + } + avl_insert(&spa->spa_keystore.sk_wkeys, wkey, where); + + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + + return (0); + +error_unlock: + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + return (ret); +} + +int +spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp) +{ + int ret; + dsl_dir_t *dd = NULL; + dsl_crypto_key_t *dck = NULL; + dsl_wrapping_key_t *wkey = dcp->cp_wkey; + dsl_pool_t *dp = NULL; + + if (!dcp || !dcp->cp_wkey) + return (SET_ERROR(EINVAL)); + if (dcp->cp_crypt || dcp->cp_keysource || dcp->cp_salt || + dcp->cp_cmd || dcp->cp_iters) + return (SET_ERROR(EINVAL)); + + ret = dsl_pool_hold(dsname, FTAG, &dp); + if (ret) + goto error; + + /* hold the dsl dir */ + ret = dsl_dir_hold(dp, dsname, FTAG, &dd, NULL); + if (ret) + goto error; + + /* initialize the wkey's ddobj */ + wkey->wk_ddobj = dd->dd_object; + + /* verify that the wkey is correct by opening its dsl key */ + ret = dsl_crypto_key_open(dp->dp_meta_objset, wkey, + dd->dd_crypto_obj, FTAG, &dck); + if (ret) + goto error; + + /* insert the wrapping key into the keystore */ + ret = spa_keystore_load_wkey_impl(dp->dp_spa, wkey); + if (ret) + goto error; + + dsl_crypto_key_rele(dck, FTAG); + dsl_dir_rele(dd, FTAG); + dsl_pool_rele(dp, FTAG); + + /* create any zvols under this ds */ + zvol_create_minors(dp->dp_spa, dsname, B_TRUE); + + return (0); + +error: + if (dck) + dsl_crypto_key_rele(dck, FTAG); + if (dd) + dsl_dir_rele(dd, FTAG); + if (dp) + dsl_pool_rele(dp, FTAG); + + return (ret); +} + +int +spa_keystore_unload_wkey_impl(spa_t *spa, uint64_t ddobj) +{ + int ret; + dsl_wrapping_key_t search_wkey; + dsl_wrapping_key_t *found_wkey; + + /* init the search wrapping key */ + search_wkey.wk_ddobj = ddobj; + + rw_enter(&spa->spa_keystore.sk_wkeys_lock, RW_WRITER); + + /* remove the wrapping key from the keystore */ + found_wkey = avl_find(&spa->spa_keystore.sk_wkeys, + &search_wkey, NULL); + if (!found_wkey) { + ret = SET_ERROR(ENOENT); + goto error_unlock; + } else if (refcount_count(&found_wkey->wk_refcnt) != 0) { + ret = SET_ERROR(EBUSY); + goto error_unlock; + } + avl_remove(&spa->spa_keystore.sk_wkeys, found_wkey); + + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + + /* free the wrapping key */ + dsl_wrapping_key_free(found_wkey); + + return (0); + +error_unlock: + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + return (ret); +} + +int +spa_keystore_unload_wkey(const char *dsname) +{ + int ret = 0; + dsl_dir_t *dd = NULL; + dsl_pool_t *dp = NULL; + + /* hold the dsl dir */ + ret = dsl_pool_hold(dsname, FTAG, &dp); + if (ret) + goto error; + + ret = dsl_dir_hold(dp, dsname, FTAG, &dd, NULL); + if (ret) + goto error; + + /* unload the wkey */ + ret = spa_keystore_unload_wkey_impl(dp->dp_spa, dd->dd_object); + if (ret) + goto error; + + dsl_dir_rele(dd, FTAG); + dsl_pool_rele(dp, FTAG); + + /* remove any zvols under this ds */ + zvol_remove_minors(dp->dp_spa, dsname, B_TRUE); + + return (0); + +error: + if (dd) + dsl_dir_rele(dd, FTAG); + if (dp) + dsl_pool_rele(dp, FTAG); + + return (ret); +} + +int +spa_keystore_create_mapping(spa_t *spa, dsl_dataset_t *ds, void *tag) +{ + int ret; + avl_index_t where; + dsl_key_mapping_t *km = NULL, *found_km; + boolean_t should_free = B_FALSE; + + /* allocate the mapping */ + km = kmem_alloc(sizeof (dsl_key_mapping_t), KM_SLEEP); + if (!km) + return (SET_ERROR(ENOMEM)); + + /* initialize the mapping */ + refcount_create(&km->km_refcnt); + + ret = spa_keystore_dsl_key_hold_dd(spa, ds->ds_dir, km, + &km->km_key); + if (ret) + goto error; + + km->km_dsobj = ds->ds_object; + + rw_enter(&spa->spa_keystore.sk_km_lock, RW_WRITER); + + /* + * If a mapping already exists, simply increment its refcount and + * cleanup the one we made. We want to allocate / free outside of + * the lock because this lock is also used by the zio layer to lookup + * key mappings. Otherwise, use the one we created. Normally, there will + * only be one active reference at a time (the objset owner), but there + * are times when there could be multiple async users. + */ + found_km = avl_find(&spa->spa_keystore.sk_key_mappings, km, &where); + if (found_km) { + should_free = B_TRUE; + refcount_add(&found_km->km_refcnt, tag); + } else { + refcount_add(&km->km_refcnt, tag); + avl_insert(&spa->spa_keystore.sk_key_mappings, km, where); + } + + rw_exit(&spa->spa_keystore.sk_km_lock); + + if (should_free) { + spa_keystore_dsl_key_rele(spa, km->km_key, km); + refcount_destroy(&km->km_refcnt); + kmem_free(km, sizeof (dsl_key_mapping_t)); + } + + return (0); + +error: + if (km->km_key) + spa_keystore_dsl_key_rele(spa, km->km_key, km); + + refcount_destroy(&km->km_refcnt); + kmem_free(km, sizeof (dsl_key_mapping_t)); + + return (ret); +} + +int +spa_keystore_remove_mapping(spa_t *spa, dsl_dataset_t *ds, void *tag) +{ + int ret; + dsl_key_mapping_t search_km; + dsl_key_mapping_t *found_km; + boolean_t should_free = B_FALSE; + + /* init the search key mapping */ + search_km.km_dsobj = ds->ds_object; + + rw_enter(&spa->spa_keystore.sk_km_lock, RW_WRITER); + + /* find the matching mapping */ + found_km = avl_find(&spa->spa_keystore.sk_key_mappings, + &search_km, NULL); + if (found_km == NULL) { + ret = SET_ERROR(ENOENT); + goto error_unlock; + } + + /* + * Decrement the refcount on the mapping and remove it from the tree if + * it is zero. Try to minimize time spent in this lock by deferring + * cleanup work. + */ + if (refcount_remove(&found_km->km_refcnt, tag) == 0) { + should_free = B_TRUE; + avl_remove(&spa->spa_keystore.sk_key_mappings, found_km); + } + + rw_exit(&spa->spa_keystore.sk_km_lock); + + /* destroy the key mapping */ + if (should_free) { + spa_keystore_dsl_key_rele(spa, found_km->km_key, found_km); + kmem_free(found_km, sizeof (dsl_key_mapping_t)); + } + + return (0); + +error_unlock: + rw_exit(&spa->spa_keystore.sk_km_lock); + return (ret); +} + +/* + * This function is primarily used by the zio layer to lookup DSL + * Crypto Keys for encryption. For this use case the key cannot be + * unloaded because it is held open by the owning dataset. There are some + * callers, however, that are not protected by the owning dataset (currently + * just the L2ARC). These callers may pass a tag which will prevent the key + * from vanishing while it is being used. Those callers must release the + * key with spa_keystore_dsl_key_rele(). + */ +int +spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag, + dsl_crypto_key_t **dck_out) +{ + int ret; + dsl_key_mapping_t search_km; + dsl_key_mapping_t *found_km; + + IMPLY(tag != NULL, dck_out != NULL); + + /* init the search key mapping */ + search_km.km_dsobj = dsobj; + + rw_enter(&spa->spa_keystore.sk_km_lock, RW_READER); + + /* remove the mapping from the tree */ + found_km = avl_find(&spa->spa_keystore.sk_key_mappings, &search_km, + NULL); + if (found_km == NULL) { + ret = SET_ERROR(ENOENT); + goto error_unlock; + } + + if (found_km && tag) + refcount_add(&found_km->km_key->dck_refcnt, tag); + + rw_exit(&spa->spa_keystore.sk_km_lock); + + if (dck_out) + *dck_out = found_km->km_key; + return (0); + +error_unlock: + rw_exit(&spa->spa_keystore.sk_km_lock); + + if (dck_out) + *dck_out = NULL; + return (ret); +} + +static void +dsl_crypto_key_sync(dsl_crypto_key_t *dck, dmu_tx_t *tx) +{ + uint64_t dckobj = dck->dck_obj; + zio_crypt_key_t *key = &dck->dck_key; + objset_t *mos = tx->tx_pool->dp_meta_objset; + uint8_t keydata[MAX_MASTER_KEY_LEN]; + uint8_t hmac_keydata[HMAC_SHA256_KEYLEN]; + uint8_t iv[WRAPPING_IV_LEN]; + uint8_t mac[WRAPPING_MAC_LEN]; + + ASSERT(dmu_tx_is_syncing(tx)); + ASSERT3U(key->zk_crypt, <, ZIO_CRYPT_FUNCTIONS); + + /* encrypt and store the keys along with the IV and MAC */ + VERIFY0(zio_crypt_key_wrap(&dck->dck_wkey->wk_key, key, iv, mac, + keydata, hmac_keydata)); + + /* update the ZAP with the obtained values */ + VERIFY0(zap_update(mos, dckobj, DSL_CRYPTO_KEY_CRYPT, 8, 1, + &key->zk_crypt, tx)); + + VERIFY0(zap_update(mos, dckobj, DSL_CRYPTO_KEY_IV, 1, WRAPPING_IV_LEN, + iv, tx)); + + VERIFY0(zap_update(mos, dckobj, DSL_CRYPTO_KEY_MAC, 1, WRAPPING_MAC_LEN, + mac, tx)); + + VERIFY0(zap_update(mos, dckobj, DSL_CRYPTO_KEY_MASTER_BUF, 1, + MAX_MASTER_KEY_LEN, keydata, tx)); + + VERIFY0(zap_update(mos, dckobj, DSL_CRYPTO_KEY_HMAC_KEY_BUF, 1, + HMAC_SHA256_KEYLEN, hmac_keydata, tx)); +} + +typedef struct spa_keystore_rewrap_args { + const char *skra_dsname; + dsl_crypto_params_t *skra_cp; +} spa_keystore_rewrap_args_t; + +static int +spa_keystore_rewrap_check(void *arg, dmu_tx_t *tx) +{ + int ret; + dsl_dir_t *dd; + dsl_crypto_key_t *dck = NULL; + dsl_pool_t *dp = dmu_tx_pool(tx); + spa_keystore_rewrap_args_t *skra = arg; + dsl_crypto_params_t *dcp = skra->skra_cp; + + if (!dcp || !dcp->cp_wkey) + return (SET_ERROR(EINVAL)); + if (dcp->cp_crypt != ZIO_CRYPT_INHERIT || dcp->cp_cmd) + return (SET_ERROR(EINVAL)); + if (dcp->cp_keysource && + strncmp(dcp->cp_keysource, "passphrase", 10) == 0 && + (!skra->skra_cp->cp_salt || !skra->skra_cp->cp_iters)) + return (SET_ERROR(EINVAL)); + + /* hold the dd */ + ret = dsl_dir_hold(dp, skra->skra_dsname, FTAG, &dd, NULL); + if (ret) + return (ret); + + /* check that this dd has a dsl key */ + if (dd->dd_crypto_obj == 0) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* make sure the dsl key is loaded / loadable */ + ret = spa_keystore_dsl_key_hold_dd(dp->dp_spa, dd, FTAG, &dck); + if (ret) + goto error; + + ASSERT(dck->dck_wkey != NULL); + + spa_keystore_dsl_key_rele(dp->dp_spa, dck, FTAG); + dsl_dir_rele(dd, FTAG); + + return (0); + +error: + if (dck) + spa_keystore_dsl_key_rele(dp->dp_spa, dck, FTAG); + dsl_dir_rele(dd, FTAG); + + return (ret); +} + + +static void +spa_keystore_rewrap_sync_impl(uint64_t root_ddobj, uint64_t ddobj, + dsl_wrapping_key_t *wkey, dmu_tx_t *tx) +{ + zap_cursor_t zc; + zap_attribute_t za; + dsl_pool_t *dp = dmu_tx_pool(tx); + dsl_dir_t *dd = NULL, *inherit_dd = NULL; + dsl_crypto_key_t *dck = NULL; + + ASSERT(RW_WRITE_HELD(&dp->dp_spa->spa_keystore.sk_wkeys_lock)); + + /* hold the dd */ + VERIFY0(dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd)); + + /* ignore hidden dsl dirs */ + if (dd->dd_myname[0] == '$' || dd->dd_myname[0] == '%') { + dsl_dir_rele(dd, FTAG); + return; + } + + /* hold the dd we inherited the keysource from */ + VERIFY0(dsl_dir_hold_keysource_source_dd(dd, FTAG, &inherit_dd)); + + /* stop recursing if this dsl dir didn't inherit from the root */ + if (inherit_dd->dd_object != root_ddobj) { + dsl_dir_rele(inherit_dd, FTAG); + dsl_dir_rele(dd, FTAG); + return; + } + + /* get the dsl_crypt_key_t for the current dsl dir */ + VERIFY0(spa_keystore_dsl_key_hold_dd(dp->dp_spa, dd, FTAG, &dck)); + + /* replace the wrapping key */ + dsl_wrapping_key_hold(wkey, dck); + dsl_wrapping_key_rele(dck->dck_wkey, dck); + dck->dck_wkey = wkey; + + /* sync the dsl key wrapped with the new wrapping key */ + dsl_crypto_key_sync(dck, tx); + + /* recurse into all children dsl dirs */ + for (zap_cursor_init(&zc, dp->dp_meta_objset, + dsl_dir_phys(dd)->dd_child_dir_zapobj); + zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_advance(&zc)) { + spa_keystore_rewrap_sync_impl(root_ddobj, za.za_first_integer, + wkey, tx); + } + zap_cursor_fini(&zc); + + spa_keystore_dsl_key_rele(dp->dp_spa, dck, FTAG); + dsl_dir_rele(inherit_dd, FTAG); + dsl_dir_rele(dd, FTAG); +} + +static void +spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) +{ + dsl_dataset_t *ds; + avl_index_t where; + dsl_pool_t *dp = dmu_tx_pool(tx); + spa_t *spa = dp->dp_spa; + spa_keystore_rewrap_args_t *skra = arg; + dsl_wrapping_key_t *wkey = skra->skra_cp->cp_wkey; + dsl_wrapping_key_t *found_wkey; + uint64_t crypt; + const char *keysource = skra->skra_cp->cp_keysource; + + /* create and initialize the wrapping key */ + VERIFY0(dsl_dataset_hold(dp, skra->skra_dsname, FTAG, &ds)); + ASSERT(!ds->ds_is_snapshot); + + /* set additional properties which can be sent along with this ioctl */ + if (keysource) + dsl_prop_set_sync_impl(ds, + zfs_prop_to_name(ZFS_PROP_KEYSOURCE), ZPROP_SRC_LOCAL, + 1, strlen(keysource) + 1, keysource, tx); + + if (skra->skra_cp->cp_iters) + dsl_prop_set_sync_impl(ds, + zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), + ZPROP_SRC_LOCAL, 8, 1, &skra->skra_cp->cp_iters, tx); + + dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_SALT), + ZPROP_SRC_LOCAL, 8, 1, &skra->skra_cp->cp_salt, tx); + + /* + * Rewrapping the dataset implies we are making this an encryption + * root. Set the encryption property locally now. + */ + VERIFY0(dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &crypt, NULL)); + dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + ZPROP_SRC_LOCAL, 8, 1, &crypt, tx); + + wkey->wk_ddobj = ds->ds_dir->dd_object; + + rw_enter(&spa->spa_keystore.sk_wkeys_lock, RW_WRITER); + + /* recurse through all children and rewrap their keys */ + spa_keystore_rewrap_sync_impl(ds->ds_dir->dd_object, + ds->ds_dir->dd_object, wkey, tx); + + /* + * All references to the old wkey should be released now (if it + * existed). Replace the wrapping key. + */ + found_wkey = avl_find(&spa->spa_keystore.sk_wkeys, wkey, NULL); + if (found_wkey) { + ASSERT0(refcount_count(&found_wkey->wk_refcnt)); + avl_remove(&spa->spa_keystore.sk_wkeys, found_wkey); + dsl_wrapping_key_free(found_wkey); + } + + avl_find(&spa->spa_keystore.sk_wkeys, wkey, &where); + avl_insert(&spa->spa_keystore.sk_wkeys, wkey, where); + + rw_exit(&spa->spa_keystore.sk_wkeys_lock); + + dsl_dataset_rele(ds, FTAG); +} + +int +spa_keystore_rewrap(const char *dsname, dsl_crypto_params_t *dcp) +{ + spa_keystore_rewrap_args_t skra; + + /* initialize the args struct */ + skra.skra_dsname = dsname; + skra.skra_cp = dcp; + + /* perform the actual work in syncing context */ + return (dsl_sync_task(dsname, spa_keystore_rewrap_check, + spa_keystore_rewrap_sync, &skra, 0, ZFS_SPACE_CHECK_NORMAL)); +} + +int +dmu_objset_create_encryption_check(dsl_dir_t *pdd, dsl_crypto_params_t *dcp) +{ + int ret; + dsl_wrapping_key_t *wkey = NULL; + uint64_t cmd = 0, salt = 0, iters = 0; + uint64_t pcrypt, crypt = ZIO_CRYPT_INHERIT; + const char *keysource = NULL; + + if (!spa_feature_is_enabled(pdd->dd_pool->dp_spa, + SPA_FEATURE_ENCRYPTION) && dcp) + return (SET_ERROR(EINVAL)); + + ret = dsl_prop_get_dd(pdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &pcrypt, NULL, B_FALSE); + if (ret) + return (ret); + + if (dcp) { + crypt = dcp->cp_crypt; + wkey = dcp->cp_wkey; + salt = dcp->cp_salt; + iters = dcp->cp_iters; + keysource = dcp->cp_keysource; + cmd = dcp->cp_cmd; + } + + if (crypt >= ZIO_CRYPT_FUNCTIONS) + return (SET_ERROR(EINVAL)); + if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) + return (SET_ERROR(EINVAL)); + if (crypt == ZIO_CRYPT_INHERIT && pcrypt == ZIO_CRYPT_OFF && + (salt || keysource || wkey || iters)) + return (SET_ERROR(EINVAL)); + if (crypt == ZIO_CRYPT_OFF && (salt || keysource || wkey || iters)) + return (SET_ERROR(EINVAL)); + if (crypt != ZIO_CRYPT_INHERIT && crypt != ZIO_CRYPT_OFF && + pcrypt == ZIO_CRYPT_OFF && (!keysource || !wkey)) + return (SET_ERROR(EINVAL)); + if (keysource && strncmp(keysource, "passphrase", 10) == 0 && + (!salt || !iters)) + if (cmd) + return (SET_ERROR(EINVAL)); + + if (!wkey && pcrypt != ZIO_CRYPT_OFF) { + ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, + pdd->dd_object, FTAG, &wkey); + if (ret) + return (SET_ERROR(EACCES)); + + dsl_wrapping_key_rele(wkey, FTAG); + } + + return (0); +} + +int +dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, + dsl_crypto_params_t *dcp) +{ + int ret; + dsl_wrapping_key_t *wkey = NULL; + uint64_t cmd = 0, salt = 0, iters = 0; + uint64_t pcrypt, ocrypt, crypt = ZIO_CRYPT_INHERIT; + const char *keysource = NULL; + + if (!spa_feature_is_enabled(pdd->dd_pool->dp_spa, + SPA_FEATURE_ENCRYPTION) && dcp) + return (SET_ERROR(EINVAL)); + + ret = dsl_prop_get_dd(pdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, + &pcrypt, NULL, B_FALSE); + if (ret) + return (ret); + + ret = dsl_prop_get_dd(odd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, + &ocrypt, NULL, B_FALSE); + if (ret) + return (ret); + + if (dcp) { + crypt = dcp->cp_crypt; + wkey = dcp->cp_wkey; + salt = dcp->cp_salt; + iters = dcp->cp_iters; + keysource = dcp->cp_keysource; + cmd = dcp->cp_cmd; + } + + if (crypt != ZIO_CRYPT_INHERIT) + return (SET_ERROR(EINVAL)); + if (pcrypt != ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) + return (SET_ERROR(EINVAL)); + if (pcrypt == ZIO_CRYPT_OFF && ocrypt != ZIO_CRYPT_OFF && + (!wkey || !keysource)) + return (SET_ERROR(EINVAL)); + if (keysource && strncmp(keysource, "passphrase", 10) == 0 && + (!salt || !iters)) + return (SET_ERROR(EINVAL)); + + /* origin wrapping key must be present, if it is encrypted */ + if (ocrypt != ZIO_CRYPT_OFF) { + ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, + odd->dd_object, FTAG, &wkey); + if (ret) + return (SET_ERROR(EACCES)); + + dsl_wrapping_key_rele(wkey, FTAG); + } + + /* parent's wrapping key must be present if a new one isn't specified */ + if (!wkey && pcrypt != ZIO_CRYPT_OFF) { + ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, + pdd->dd_object, FTAG, &wkey); + if (ret) + return (SET_ERROR(EACCES)); + + dsl_wrapping_key_rele(wkey, FTAG); + } + + return (0); +} + +uint64_t +dsl_crypto_key_create_sync(uint64_t crypt, dsl_wrapping_key_t *wkey, + dmu_tx_t *tx) +{ + dsl_crypto_key_t dck; + + ASSERT(dmu_tx_is_syncing(tx)); + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(crypt, >, ZIO_CRYPT_OFF); + + /* create the DSL Crypto Key ZAP object */ + dck.dck_obj = zap_create(tx->tx_pool->dp_meta_objset, + DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx); + + /* fill in the key (on the stack) and sync it to disk */ + dck.dck_wkey = wkey; + VERIFY0(zio_crypt_key_init(crypt, &dck.dck_key)); + + dsl_crypto_key_sync(&dck, tx); + + zio_crypt_key_destroy(&dck.dck_key); + bzero(&dck.dck_key, sizeof (zio_crypt_key_t)); + + /* increment the encryption feature count */ + spa_feature_incr(tx->tx_pool->dp_spa, SPA_FEATURE_ENCRYPTION, tx); + + return (dck.dck_obj); +} + +uint64_t +dsl_crypto_key_clone_sync(dsl_dir_t *orig_dd, dsl_wrapping_key_t *wkey, + dmu_tx_t *tx) +{ + dsl_pool_t *dp = tx->tx_pool; + dsl_crypto_key_t *orig_dck; + dsl_crypto_key_t dck; + + ASSERT(dmu_tx_is_syncing(tx)); + + /* get the key from the original dataset */ + VERIFY0(spa_keystore_dsl_key_hold_dd(dp->dp_spa, orig_dd, FTAG, + &orig_dck)); + + /* create the DSL Crypto Key ZAP object */ + dck.dck_obj = zap_create(dp->dp_meta_objset, DMU_OTN_ZAP_METADATA, + DMU_OT_NONE, 0, tx); + + /* assign the wrapping key temporarily */ + dck.dck_wkey = wkey; + + /* + * Fill in the temporary key with the original key's data. We only need + * to actually copy the fields that will be synced to disk, namely the + * master key, hmac key and crypt. + */ + bcopy(&orig_dck->dck_key.zk_master_keydata, + &dck.dck_key.zk_master_keydata, MAX_MASTER_KEY_LEN); + bcopy(&orig_dck->dck_key.zk_hmac_keydata, + &dck.dck_key.zk_hmac_keydata, HMAC_SHA256_KEYLEN); + + dck.dck_key.zk_crypt = orig_dck->dck_key.zk_crypt; + + /* sync the new key, wrapped with the new wrapping key */ + dsl_crypto_key_sync(&dck, tx); + bzero(&dck.dck_key, sizeof (zio_crypt_key_t)); + + /* increment the encryption feature count */ + spa_feature_incr(dp->dp_spa, SPA_FEATURE_ENCRYPTION, tx); + + spa_keystore_dsl_key_rele(dp->dp_spa, orig_dck, FTAG); + + return (dck.dck_obj); +} + +void +dsl_crypto_key_destroy_sync(uint64_t dckobj, dmu_tx_t *tx) +{ + /* destroy the DSL Crypto Key object */ + VERIFY0(zap_destroy(tx->tx_pool->dp_meta_objset, dckobj, tx)); + + /* decrement the feature count */ + spa_feature_decr(tx->tx_pool->dp_spa, SPA_FEATURE_ENCRYPTION, tx); +} + +int +spa_crypt_get_salt(spa_t *spa, uint64_t dsobj, uint8_t *salt) +{ + int ret; + dsl_crypto_key_t *dck; + + /* look up the key from the spa's keystore */ + ret = spa_keystore_lookup_key(spa, dsobj, NULL, &dck); + if (ret) { + ret = SET_ERROR(EACCES); + goto error; + } + + ret = zio_crypt_key_get_salt(&dck->dck_key, salt); + if (ret) + goto error; + + return (0); + +error: + return (ret); +} + +/* + * This function serve as a multiplexer for encryption and decryption of + * all blocks (except the L2ARC). For encryption, it will populate the IV, + * salt, MAC, and cabd (the ciphertext). On decryption it will simply use + * these fields to populate pabd (the plaintext). + */ +int +spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, + blkptr_t *bp, uint64_t txgid, uint_t datalen, abd_t *pabd, abd_t *cabd, + uint8_t *iv, uint8_t *mac, uint8_t *salt) +{ + int ret; + dmu_object_type_t ot = BP_GET_TYPE(bp); + dsl_crypto_key_t *dck; + uint8_t tmpiv[DATA_IV_LEN]; + uint8_t *plainbuf = NULL, *cipherbuf = NULL; + + ASSERT(spa_feature_is_active(spa, SPA_FEATURE_ENCRYPTION)); + ASSERT(!BP_IS_EMBEDDED(bp)); + ASSERT(BP_IS_ENCRYPTED(bp)); + + /* look up the key from the spa's keystore */ + ret = spa_keystore_lookup_key(spa, zb->zb_objset, NULL, &dck); + if (ret) { + ret = SET_ERROR(EACCES); + goto error; + } + + if (encrypt) { + plainbuf = abd_borrow_buf_copy(pabd, datalen); + cipherbuf = abd_borrow_buf(cabd, datalen); + } else { + plainbuf = abd_borrow_buf(pabd, datalen); + cipherbuf = abd_borrow_buf_copy(cabd, datalen); + } + + /* + * Both encryption and decryption functions need a salt for key + * generation and an IV. When encrypting a non-dedup block, we + * generate the salt and IV randomly to be stored by the caller. Dedup + * blocks perform a (more expensive) HMAC of the plaintext to obtain + * the salt and the IV. ZIL blocks have their salt and IV generated + * at allocation time in zio_alloc_zil(). On decryption, we simply use + * the provided values. + */ + if (encrypt && ot != DMU_OT_INTENT_LOG && !BP_GET_DEDUP(bp)) { + ret = zio_crypt_key_get_salt(&dck->dck_key, salt); + if (ret) + goto error; + + ret = zio_crypt_generate_iv(iv); + if (ret) + goto error; + } else if (encrypt && BP_GET_DEDUP(bp)) { + ret = zio_crypt_generate_iv_salt_dedup(&dck->dck_key, + plainbuf, datalen, iv, salt); + if (ret) + goto error; + } else if (ot == DMU_OT_INTENT_LOG) { + uint8_t *src = (encrypt) ? plainbuf : cipherbuf; + + zio_crypt_derive_zil_iv(src, iv, tmpiv); + iv = tmpiv; + } + + /* call lower level function to perform encryption / decryption */ + ret = zio_do_crypt_data(encrypt, &dck->dck_key, salt, ot, iv, mac, + datalen, plainbuf, cipherbuf); + if (ret) + goto error; + + if (encrypt) { + abd_return_buf(pabd, plainbuf, datalen); + abd_return_buf_copy(cabd, cipherbuf, datalen); + } else { + abd_return_buf_copy(pabd, plainbuf, datalen); + abd_return_buf(cabd, cipherbuf, datalen); + } + + return (0); + +error: + /* zero out any state we might have changed while encrypting */ + if (encrypt) { + bzero(salt, DATA_SALT_LEN); + bzero(iv, DATA_IV_LEN); + bzero(mac, (ot == DMU_OT_INTENT_LOG) ? + ZIL_MAC_LEN : DATA_MAC_LEN); + } + + if (plainbuf) + abd_return_buf(pabd, plainbuf, datalen); + if (cipherbuf) + abd_return_buf(cabd, cipherbuf, datalen); + + return (ret); +} diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 8abb510e8ed9..dc8d46fe7533 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -611,31 +611,106 @@ dsl_dataset_hold(dsl_pool_t *dp, const char *name, return (err); } +void +dsl_dataset_rele_crypt(dsl_dataset_t *ds, void *tag) +{ + ASSERT(ds->ds_objset != NULL); + + if (ds->ds_objset->os_encrypted) { + VERIFY0(spa_keystore_remove_mapping(ds->ds_objset->os_spa, + ds, ds)); + } + + dsl_dataset_rele(ds, tag); +} + int -dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, +dsl_dataset_hold_crypt(dsl_pool_t *dp, const char *name, void *tag, dsl_dataset_t **dsp) +{ + int err; + objset_t *os; + + err = dsl_dataset_hold(dp, name, tag, dsp); + if (err != 0) + return (err); + + err = dmu_objset_from_ds(*dsp, &os); + if (err != 0) + goto error; + + if (os->os_encrypted) { + err = spa_keystore_create_mapping(dp->dp_spa, *dsp, *dsp); + if (err != 0) + goto error; + } + + return (0); + +error: + dsl_dataset_rele(*dsp, tag); + *dsp = NULL; + return (err); +} + +int +dsl_dataset_hold_crypt_obj(dsl_pool_t *dp, uint64_t dsobj, + void *tag, dsl_dataset_t **dsp) +{ + int err; + objset_t *os; + + err = dsl_dataset_hold_obj(dp, dsobj, tag, dsp); + if (err != 0) + return (err); + + err = dmu_objset_from_ds(*dsp, &os); + if (err != 0) + goto error; + + if (os->os_encrypted) { + err = spa_keystore_create_mapping(dp->dp_spa, *dsp, *dsp); + if (err != 0) + goto error; + } + + return (0); + +error: + dsl_dataset_rele(*dsp, tag); + *dsp = NULL; + return (err); +} + +int +dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, + void *tag, boolean_t key_required, dsl_dataset_t **dsp) { int err = dsl_dataset_hold_obj(dp, dsobj, tag, dsp); if (err != 0) return (err); - if (!dsl_dataset_tryown(*dsp, tag)) { + + err = dsl_dataset_tryown(*dsp, tag, key_required); + if (err != 0) { dsl_dataset_rele(*dsp, tag); *dsp = NULL; - return (SET_ERROR(EBUSY)); + return (err); } return (0); } int dsl_dataset_own(dsl_pool_t *dp, const char *name, - void *tag, dsl_dataset_t **dsp) + void *tag, boolean_t key_required, dsl_dataset_t **dsp) { int err = dsl_dataset_hold(dp, name, tag, dsp); if (err != 0) return (err); - if (!dsl_dataset_tryown(*dsp, tag)) { + + err = dsl_dataset_tryown(*dsp, tag, key_required); + if (err != 0) { dsl_dataset_rele(*dsp, tag); - return (SET_ERROR(EBUSY)); + return (err); } return (0); } @@ -727,26 +802,41 @@ dsl_dataset_disown(dsl_dataset_t *ds, void *tag) ASSERT(ds->ds_dbuf != NULL); mutex_enter(&ds->ds_lock); + if (ds->ds_dir && ds->ds_dir->dd_crypto_obj) { + (void) spa_keystore_remove_mapping(ds->ds_dir->dd_pool->dp_spa, + ds, ds); + } ds->ds_owner = NULL; mutex_exit(&ds->ds_lock); dsl_dataset_long_rele(ds, tag); dsl_dataset_rele(ds, tag); } -boolean_t -dsl_dataset_tryown(dsl_dataset_t *ds, void *tag) +int +dsl_dataset_tryown(dsl_dataset_t *ds, void *tag, boolean_t key_required) { - boolean_t gotit = FALSE; + int ret = 0; + spa_t *spa = ds->ds_dir->dd_pool->dp_spa; + uint64_t dckobj = ds->ds_dir->dd_crypto_obj; ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); mutex_enter(&ds->ds_lock); if (ds->ds_owner == NULL && !DS_IS_INCONSISTENT(ds)) { + if (dckobj != 0 && key_required) { + ret = spa_keystore_create_mapping(spa, ds, ds); + if (ret) { + mutex_exit(&ds->ds_lock); + return (SET_ERROR(EACCES)); + } + } ds->ds_owner = tag; dsl_dataset_long_hold(ds, tag); - gotit = TRUE; + } else { + ret = SET_ERROR(EBUSY); } mutex_exit(&ds->ds_lock); - return (gotit); + + return (ret); } boolean_t @@ -789,12 +879,13 @@ dsl_dataset_deactivate_feature(uint64_t dsobj, spa_feature_t f, dmu_tx_t *tx) uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, - uint64_t flags, dmu_tx_t *tx) + dsl_crypto_params_t *dcp, uint64_t flags, dmu_tx_t *tx) { dsl_pool_t *dp = dd->dd_pool; dmu_buf_t *dbuf; dsl_dataset_phys_t *dsphys; - uint64_t dsobj; + uint64_t dsobj, crypt; + dsl_wrapping_key_t *wkey; objset_t *mos = dp->dp_meta_objset; if (origin == NULL) @@ -887,6 +978,80 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, } } + /* handle encryption */ + if (dcp == NULL) { + crypt = ZIO_CRYPT_INHERIT; + wkey = NULL; + } else { + crypt = dcp->cp_crypt; + wkey = dcp->cp_wkey; + } + + if (!dsl_dir_is_clone(dd)) { + if (crypt == ZIO_CRYPT_INHERIT && dd->dd_parent != NULL) { + VERIFY0(dsl_prop_get_dd(dd->dd_parent, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &crypt, NULL, B_FALSE)); + } else if (crypt == ZIO_CRYPT_INHERIT) { + crypt = ZIO_CRYPT_OFF; + } + + if (crypt == ZIO_CRYPT_OFF) + goto no_crypto; + + if (wkey == NULL) { + VERIFY0(spa_keystore_wkey_hold_ddobj(dp->dp_spa, + dd->dd_parent->dd_object, FTAG, &wkey)); + } else { + wkey->wk_ddobj = dd->dd_object; + } + + dsl_dir_zapify(dd, tx); + dd->dd_crypto_obj = dsl_crypto_key_create_sync(crypt, wkey, tx); + VERIFY0(zap_add(mos, dd->dd_object, DD_FIELD_CRYPTO_KEY_OBJ, + sizeof (uint64_t), 1, &dd->dd_crypto_obj, tx)); + + if (dcp == NULL || dcp->cp_wkey == NULL) { + dsl_wrapping_key_rele(wkey, FTAG); + } else { + VERIFY0(spa_keystore_load_wkey_impl(tx->tx_pool->dp_spa, + wkey)); + } + } else if (origin->ds_dir->dd_crypto_obj != 0) { + VERIFY0(dsl_prop_get_dd(origin->ds_dir, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, + &crypt, NULL, B_FALSE)); + + if (crypt == ZIO_CRYPT_OFF) + goto no_crypto; + + if (wkey == NULL) { + VERIFY0(spa_keystore_wkey_hold_ddobj(dp->dp_spa, + dd->dd_parent->dd_object, FTAG, &wkey)); + } else { + wkey->wk_ddobj = dd->dd_object; + } + + VERIFY0(zap_update(mos, + dsl_dir_phys(dd)->dd_props_zapobj, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &crypt, tx)); + + dsl_dir_zapify(dd, tx); + dd->dd_crypto_obj = dsl_crypto_key_clone_sync(origin->ds_dir, + wkey, tx); + VERIFY0(zap_add(mos, dd->dd_object, DD_FIELD_CRYPTO_KEY_OBJ, + sizeof (uint64_t), 1, &dd->dd_crypto_obj, tx)); + + if (dcp == NULL || dcp->cp_wkey == NULL) { + dsl_wrapping_key_rele(wkey, FTAG); + } else { + VERIFY0(spa_keystore_load_wkey_impl(tx->tx_pool->dp_spa, + wkey)); + } + } + +no_crypto: if (spa_version(dp->dp_spa) >= SPA_VERSION_UNIQUE_ACCURATE) dsphys->ds_flags |= DS_FLAG_UNIQUE_ACCURATE; @@ -910,7 +1075,8 @@ dsl_dataset_zero_zil(dsl_dataset_t *ds, dmu_tx_t *tx) uint64_t dsl_dataset_create_sync(dsl_dir_t *pdd, const char *lastname, - dsl_dataset_t *origin, uint64_t flags, cred_t *cr, dmu_tx_t *tx) + dsl_dataset_t *origin, uint64_t flags, cred_t *cr, + dsl_crypto_params_t *dcp, dmu_tx_t *tx) { dsl_pool_t *dp = pdd->dd_pool; uint64_t dsobj, ddobj; @@ -922,7 +1088,7 @@ dsl_dataset_create_sync(dsl_dir_t *pdd, const char *lastname, ddobj = dsl_dir_create_sync(dp, pdd, lastname, tx); VERIFY0(dsl_dir_hold_obj(dp, ddobj, lastname, FTAG, &dd)); - dsobj = dsl_dataset_create_sync_dd(dd, origin, + dsobj = dsl_dataset_create_sync_dd(dd, origin, dcp, flags & ~DS_CREATE_FLAG_NODIRTY, tx); dsl_deleg_set_create_perms(dd, tx, cr); @@ -1863,6 +2029,8 @@ dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv) ds->ds_userrefs); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY, DS_IS_DEFER_DESTROY(ds) ? 1 : 0); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_KEYSTATUS, + dsl_dataset_keystore_keystatus(ds)); if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { uint64_t written, comp, uncomp; @@ -2271,7 +2439,7 @@ dsl_dataset_rollback_sync(void *arg, dmu_tx_t *tx) fnvlist_add_string(ddra->ddra_result, "target", namebuf); cloneobj = dsl_dataset_create_sync(ds->ds_dir, "%rollback", - ds->ds_prev, DS_CREATE_FLAG_NODIRTY, kcred, tx); + ds->ds_prev, DS_CREATE_FLAG_NODIRTY, kcred, NULL, tx); VERIFY0(dsl_dataset_hold_obj(dp, cloneobj, FTAG, &clone)); diff --git a/module/zfs/dsl_destroy.c b/module/zfs/dsl_destroy.c index 716081ba3ac3..e0fd63744047 100644 --- a/module/zfs/dsl_destroy.c +++ b/module/zfs/dsl_destroy.c @@ -596,8 +596,8 @@ old_synchronous_dataset_destroy(dsl_dataset_t *ds, dmu_tx_t *tx) ka.ds = ds; ka.tx = tx; VERIFY0(traverse_dataset(ds, - dsl_dataset_phys(ds)->ds_prev_snap_txg, TRAVERSE_POST, - kill_blkptr, &ka)); + dsl_dataset_phys(ds)->ds_prev_snap_txg, TRAVERSE_POST | + TRAVERSE_NO_DECRYPT, kill_blkptr, &ka)); ASSERT(!DS_UNIQUE_IS_ACCURATE(ds) || dsl_dataset_phys(ds)->ds_unique_bytes == 0); } @@ -704,6 +704,11 @@ dsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx) for (t = 0; t < DD_USED_NUM; t++) ASSERT0(dsl_dir_phys(dd)->dd_used_breakdown[t]); + if (dd->dd_crypto_obj != 0) { + dsl_crypto_key_destroy_sync(dd->dd_crypto_obj, tx); + (void) spa_keystore_unload_wkey_impl(dp->dp_spa, dd->dd_object); + } + VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_child_dir_zapobj, tx)); VERIFY0(zap_destroy(mos, dsl_dir_phys(dd)->dd_props_zapobj, tx)); VERIFY0(dsl_deleg_destroy(mos, dsl_dir_phys(dd)->dd_deleg_zapobj, tx)); @@ -945,7 +950,8 @@ dsl_destroy_head(const char *name) * remove the objects from open context so that the txg sync * is not too long. */ - error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, FTAG, &os); + error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, B_TRUE, + FTAG, &os); if (error == 0) { uint64_t obj; uint64_t prev_snap_txg = diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index b21735235908..43b339da3e9d 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -159,6 +159,7 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, { dmu_buf_t *dbuf; dsl_dir_t *dd; + dmu_object_info_t doi; int err; ASSERT(dsl_pool_config_held(dp)); @@ -167,14 +168,11 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, if (err != 0) return (err); dd = dmu_buf_get_user(dbuf); -#ifdef ZFS_DEBUG - { - dmu_object_info_t doi; - dmu_object_info_from_db(dbuf, &doi); - ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_DSL_DIR); - ASSERT3U(doi.doi_bonus_size, >=, sizeof (dsl_dir_phys_t)); - } -#endif + + dmu_object_info_from_db(dbuf, &doi); + ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_DSL_DIR); + ASSERT3U(doi.doi_bonus_size, >=, sizeof (dsl_dir_phys_t)); + if (dd == NULL) { dsl_dir_t *winner; @@ -182,6 +180,15 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, dd->dd_object = ddobj; dd->dd_dbuf = dbuf; dd->dd_pool = dp; + + if (doi.doi_type == DMU_OTN_ZAP_METADATA && + zap_contains(dp->dp_meta_objset, ddobj, + DD_FIELD_CRYPTO_KEY_OBJ) == 0) { + VERIFY0(zap_lookup(dp->dp_meta_objset, + ddobj, DD_FIELD_CRYPTO_KEY_OBJ, + sizeof (uint64_t), 1, &dd->dd_crypto_obj)); + } + mutex_init(&dd->dd_lock, NULL, MUTEX_DEFAULT, NULL); dsl_prop_init(dd); @@ -917,6 +924,7 @@ dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds, const char *name, DMU_OT_DSL_DIR_CHILD_MAP, DMU_OT_NONE, 0, tx); if (spa_version(dp->dp_spa) >= SPA_VERSION_USED_BREAKDOWN) ddphys->dd_flags |= DD_FLAG_USED_BREAKDOWN; + dmu_buf_rele(dbuf, FTAG); return (ddobj); @@ -1802,6 +1810,21 @@ dsl_dir_rename_check(void *arg, dmu_tx_t *tx) dsl_dir_rele(dd, FTAG); return (err); } + + /* + * encrypted datasets can only be moved if they are + * an encryption root (locally set keysource). + */ + if (dd->dd_crypto_obj != 0) { + err = zap_contains(os, + dsl_dir_phys(dd)->dd_props_zapobj, + zfs_prop_to_name(ZFS_PROP_KEYSOURCE)); + if (err != 0) { + dsl_dir_rele(newparent, FTAG); + dsl_dir_rele(dd, FTAG); + return (SET_ERROR(EACCES)); + } + } } /* no rename into our descendant */ diff --git a/module/zfs/dsl_pool.c b/module/zfs/dsl_pool.c index 1a62fba2c6a4..313985e10f74 100644 --- a/module/zfs/dsl_pool.c +++ b/module/zfs/dsl_pool.c @@ -346,7 +346,8 @@ dsl_pool_close(dsl_pool_t *dp) } dsl_pool_t * -dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg) +dsl_pool_create(spa_t *spa, nvlist_t *zplprops, dsl_crypto_params_t *dcp, + uint64_t txg) { int err; dsl_pool_t *dp = dsl_pool_open_impl(spa, txg); @@ -360,6 +361,7 @@ dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg) /* create and open the MOS (meta-objset) */ dp->dp_meta_objset = dmu_objset_create_impl(spa, NULL, &dp->dp_meta_rootbp, DMU_OST_META, tx); + spa->spa_meta_objset = dp->dp_meta_objset; /* create the pool directory */ err = zap_create_claim(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT, @@ -397,8 +399,19 @@ dsl_pool_create(spa_t *spa, nvlist_t *zplprops, uint64_t txg) if (spa_version(spa) >= SPA_VERSION_DSL_SCRUB) dsl_pool_create_origin(dp, tx); + /* + * some features can get enabled when creating the root dataset, so we + * create the feature objects here. + */ + if (spa_version(spa) >= SPA_VERSION_FEATURES) + spa_feature_create_zap_objects(spa, tx); + + if (dcp && dcp->cp_crypt != ZIO_CRYPT_OFF && + dcp->cp_crypt != ZIO_CRYPT_INHERIT) + spa_feature_enable(spa, SPA_FEATURE_ENCRYPTION, tx); + /* create the root dataset */ - obj = dsl_dataset_create_sync_dd(dp->dp_root_dir, NULL, 0, tx); + obj = dsl_dataset_create_sync_dd(dp->dp_root_dir, NULL, dcp, 0, tx); /* create the root objset */ VERIFY0(dsl_dataset_hold_obj(dp, obj, FTAG, &ds)); @@ -840,7 +853,7 @@ dsl_pool_create_origin(dsl_pool_t *dp, dmu_tx_t *tx) /* create the origin dir, ds, & snap-ds */ dsobj = dsl_dataset_create_sync(dp->dp_root_dir, ORIGIN_DIR_NAME, - NULL, 0, kcred, tx); + NULL, 0, kcred, NULL, tx); VERIFY0(dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds)); dsl_dataset_snapshot_sync_impl(ds, ORIGIN_DIR_NAME, tx); VERIFY0(dsl_dataset_hold_obj(dp, dsl_dataset_phys(ds)->ds_prev_snap_obj, diff --git a/module/zfs/dsl_scan.c b/module/zfs/dsl_scan.c index c097a35ac1f3..75546482d445 100644 --- a/module/zfs/dsl_scan.c +++ b/module/zfs/dsl_scan.c @@ -591,7 +591,7 @@ dsl_scan_zil(dsl_pool_t *dp, zil_header_t *zh) zilog = zil_alloc(dp->dp_meta_objset, zh); (void) zil_parse(zilog, dsl_scan_zil_block, dsl_scan_zil_record, &zsa, - claim_txg); + claim_txg, B_TRUE); zil_free(zilog); } @@ -603,6 +603,7 @@ dsl_scan_prefetch(dsl_scan_t *scn, arc_buf_t *buf, blkptr_t *bp, { zbookmark_phys_t czb; arc_flags_t flags = ARC_FLAG_NOWAIT | ARC_FLAG_PREFETCH; + int zio_flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SCAN_THREAD; if (zfs_no_scrub_prefetch) return; @@ -611,11 +612,13 @@ dsl_scan_prefetch(dsl_scan_t *scn, arc_buf_t *buf, blkptr_t *bp, (BP_GET_LEVEL(bp) == 0 && BP_GET_TYPE(bp) != DMU_OT_DNODE)) return; + if (BP_IS_ENCRYPTED(bp)) + zio_flags |= ZIO_FLAG_RAW; + SET_BOOKMARK(&czb, objset, object, BP_GET_LEVEL(bp), blkid); (void) arc_read(scn->scn_zio_root, scn->scn_dp->dp_spa, bp, - NULL, NULL, ZIO_PRIORITY_ASYNC_READ, - ZIO_FLAG_CANFAIL | ZIO_FLAG_SCAN_THREAD, &flags, &czb); + NULL, NULL, ZIO_PRIORITY_ASYNC_READ, zio_flags, &flags, &czb); } static boolean_t @@ -701,6 +704,11 @@ dsl_scan_recurse(dsl_scan_t *scn, dsl_dataset_t *ds, dmu_objset_type_t ostype, int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; arc_buf_t *buf; + if (BP_IS_ENCRYPTED(bp)) { + ASSERT3U(BP_GET_COMPRESS(bp), ==, ZIO_COMPRESS_OFF); + zio_flags |= ZIO_FLAG_RAW; + } + err = arc_read(NULL, dp->dp_spa, bp, arc_getbuf_func, &buf, ZIO_PRIORITY_ASYNC_READ, zio_flags, &flags, zb); if (err) { diff --git a/module/zfs/spa.c b/module/zfs/spa.c index f3d821f79991..b344787c69ba 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -1150,6 +1150,8 @@ spa_activate(spa_t *spa, int mode) spa_error_entry_compare, sizeof (spa_error_entry_t), offsetof(spa_error_entry_t, se_avl)); + spa_keystore_init(&spa->spa_keystore); + /* * This taskq is used to perform zvol-minor-related tasks * asynchronously. This has several advantages, including easy @@ -1227,10 +1229,11 @@ spa_deactivate(spa_t *spa) * still have errors left in the queues. Empty them just in case. */ spa_errlog_drain(spa); - avl_destroy(&spa->spa_errlist_scrub); avl_destroy(&spa->spa_errlist_last); + spa_keystore_fini(&spa->spa_keystore); + spa->spa_state = POOL_STATE_UNINITIALIZED; mutex_enter(&spa->spa_proc_lock); @@ -2059,8 +2062,8 @@ spa_load_verify(spa_t *spa) if (spa_load_verify_metadata) { error = traverse_pool(spa, spa->spa_verify_min_txg, - TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, - spa_load_verify_cb, rio); + TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | + TRAVERSE_NO_DECRYPT, spa_load_verify_cb, rio); } (void) zio_wait(rio); @@ -3738,7 +3741,7 @@ spa_l2cache_drop(spa_t *spa) */ int spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, - nvlist_t *zplprops) + nvlist_t *zplprops, dsl_crypto_params_t *dcp) { spa_t *spa; char *altroot = NULL; @@ -3751,6 +3754,9 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, uint_t nspares, nl2cache; uint64_t version, obj; boolean_t has_features; + boolean_t has_encryption; + spa_feature_t feat; + char *feat_name; nvpair_t *elem; int c, i; char *poolname; @@ -3793,10 +3799,25 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, spa->spa_import_flags |= ZFS_IMPORT_TEMP_NAME; has_features = B_FALSE; + has_encryption = B_FALSE; for (elem = nvlist_next_nvpair(props, NULL); elem != NULL; elem = nvlist_next_nvpair(props, elem)) { - if (zpool_prop_feature(nvpair_name(elem))) + if (zpool_prop_feature(nvpair_name(elem))) { has_features = B_TRUE; + + feat_name = strchr(nvpair_name(elem), '@') + 1; + VERIFY0(zfeature_lookup_name(feat_name, &feat)); + if (feat == SPA_FEATURE_ENCRYPTION) + has_encryption = B_TRUE; + } + } + + if (!has_encryption && dcp && dcp->cp_crypt != ZIO_CRYPT_OFF && + dcp->cp_crypt != ZIO_CRYPT_INHERIT) { + spa_deactivate(spa); + spa_remove(spa); + mutex_exit(&spa_namespace_lock); + return (SET_ERROR(EINVAL)); } if (has_features || nvlist_lookup_uint64(props, @@ -3886,8 +3907,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, } spa->spa_is_initializing = B_TRUE; - spa->spa_dsl_pool = dp = dsl_pool_create(spa, zplprops, txg); - spa->spa_meta_objset = dp->dp_meta_objset; + spa->spa_dsl_pool = dp = dsl_pool_create(spa, zplprops, dcp, txg); spa->spa_is_initializing = B_FALSE; /* @@ -3912,9 +3932,6 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, cmn_err(CE_PANIC, "failed to add pool config"); } - if (spa_version(spa) >= SPA_VERSION_FEATURES) - spa_feature_create_zap_objects(spa, tx); - if (zap_add(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_CREATION_VERSION, sizeof (uint64_t), 1, &version, tx) != 0) { diff --git a/module/zfs/spa_history.c b/module/zfs/spa_history.c index 4ae74c28401a..edef47d56b54 100644 --- a/module/zfs/spa_history.c +++ b/module/zfs/spa_history.c @@ -302,11 +302,16 @@ spa_history_log_nvl(spa_t *spa, nvlist_t *nvl) { int err = 0; dmu_tx_t *tx; - nvlist_t *nvarg; + nvlist_t *nvarg, *in_nvl = NULL; if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY || !spa_writeable(spa)) return (SET_ERROR(EINVAL)); + err = nvlist_lookup_nvlist(nvl, ZPOOL_HIST_INPUT_NVL, &in_nvl); + if (err == 0) { + (void) nvlist_remove_all(in_nvl, ZPOOL_HIDDEN_ARGS); + } + tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir); err = dmu_tx_assign(tx, TXG_WAIT); if (err) { diff --git a/module/zfs/zfeature.c b/module/zfs/zfeature.c index bda9548293d0..f540f89c4251 100644 --- a/module/zfs/zfeature.c +++ b/module/zfs/zfeature.c @@ -423,8 +423,8 @@ spa_feature_create_zap_objects(spa_t *spa, dmu_tx_t *tx) * We create feature flags ZAP objects in two instances: during pool * creation and during pool upgrade. */ - ASSERT(dsl_pool_sync_context(spa_get_dsl(spa)) || (!spa->spa_sync_on && - tx->tx_txg == TXG_INITIAL)); + ASSERT((!spa->spa_sync_on && tx->tx_txg == TXG_INITIAL) || + dsl_pool_sync_context(spa_get_dsl(spa))); spa->spa_feat_for_read_obj = zap_create_link(spa->spa_meta_objset, DMU_OTN_ZAP_METADATA, DMU_POOL_DIRECTORY_OBJECT, diff --git a/module/zfs/zfeature_common.c b/module/zfs/zfeature_common.c index ccd65a7b74d9..bb9c1afc1689 100644 --- a/module/zfs/zfeature_common.c +++ b/module/zfs/zfeature_common.c @@ -296,4 +296,15 @@ zpool_feature_init(void) ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET, userobj_accounting_deps); } + + { + static const spa_feature_t encryption_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_ENCRYPTION, + "com.datto:encryption", "encryption", + "Support for dataset level encryption", + 0, encryption_deps); + } } diff --git a/module/zfs/zfs_acl.c b/module/zfs/zfs_acl.c index defb8f4483cb..9fdf5cbd5c34 100644 --- a/module/zfs/zfs_acl.c +++ b/module/zfs/zfs_acl.c @@ -2204,7 +2204,7 @@ zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode) * placed into the working_mode, giving the caller a mask of denied * accesses. Returns: * 0 if all AoI granted - * EACCESS if the denied mask is non-zero + * EACCES if the denied mask is non-zero * other error if abnormal failure (e.g., IO error) * * A secondary usage of the function is to determine if any of the diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 98ab745f8cf6..a81c20d78c64 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -31,6 +31,7 @@ * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2016 Actifio, Inc. All rights reserved. + * Copyright (c) 2016, Datto, Inc. All rights reserved. */ /* @@ -179,6 +180,7 @@ #include #include #include +#include #include #include @@ -1287,6 +1289,37 @@ zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) return (error); } +static int +zfs_secpolicy_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) +{ + int ret = 0; + uint64_t crypto_cmd; + + ret = nvlist_lookup_uint64(innvl, "crypto_cmd", &crypto_cmd); + if (ret) { + ret = SET_ERROR(EINVAL); + goto out; + } + + switch (crypto_cmd) { + case ZFS_IOC_KEY_LOAD_KEY: + case ZFS_IOC_KEY_UNLOAD_KEY: + ret = zfs_secpolicy_write_perms(zc->zc_name, + ZFS_DELEG_PERM_LOAD_KEY, cr); + break; + case ZFS_IOC_KEY_REWRAP: + ret = zfs_secpolicy_write_perms(zc->zc_name, + ZFS_DELEG_PERM_CHANGE_KEY, cr); + break; + default: + ret = SET_ERROR(EINVAL); + break; + } + +out: + return (ret); +} + /* * Returns the nvlist as specified by the user in the zfs_cmd_t. */ @@ -1460,6 +1493,7 @@ zfs_ioc_pool_create(zfs_cmd_t *zc) nvlist_t *config, *props = NULL; nvlist_t *rootprops = NULL; nvlist_t *zplprops = NULL; + dsl_crypto_params_t *dcp = NULL; if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, zc->zc_iflags, &config))) @@ -1474,6 +1508,7 @@ zfs_ioc_pool_create(zfs_cmd_t *zc) if (props) { nvlist_t *nvl = NULL; + nvlist_t *ha = NULL; uint64_t version = SPA_VERSION; (void) nvlist_lookup_uint64(props, @@ -1492,6 +1527,16 @@ zfs_ioc_pool_create(zfs_cmd_t *zc) } (void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS); } + + (void) nvlist_lookup_nvlist(props, ZPOOL_HIDDEN_ARGS, &ha); + error = dsl_crypto_params_create_nvlist(rootprops, ha, &dcp); + if (error != 0) { + nvlist_free(config); + nvlist_free(props); + return (error); + } + (void) nvlist_remove_all(props, ZPOOL_HIDDEN_ARGS); + VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0); error = zfs_fill_zplprops_root(version, rootprops, zplprops, NULL); @@ -1499,7 +1544,7 @@ zfs_ioc_pool_create(zfs_cmd_t *zc) goto pool_props_bad; } - error = spa_create(zc->zc_name, config, props, zplprops); + error = spa_create(zc->zc_name, config, props, zplprops, dcp); /* * Set the remaining root properties @@ -1513,6 +1558,7 @@ zfs_ioc_pool_create(zfs_cmd_t *zc) nvlist_free(zplprops); nvlist_free(config); nvlist_free(props); + dsl_crypto_params_free(dcp, !!error); return (error); } @@ -3119,6 +3165,7 @@ zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, * innvl: { * "type" -> dmu_objset_type_t (int32) * (optional) "props" -> { prop -> value } + * (optional) "hidden_args" -> { "wkeydata" -> value } * } * * outnvl: propname -> error code (int32) @@ -3129,15 +3176,18 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) int error = 0; zfs_creat_t zct = { 0 }; nvlist_t *nvprops = NULL; + nvlist_t *hidden_args = NULL; void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); int32_t type32; dmu_objset_type_t type; boolean_t is_insensitive = B_FALSE; + dsl_crypto_params_t *dcp = NULL; if (nvlist_lookup_int32(innvl, "type", &type32) != 0) return (SET_ERROR(EINVAL)); type = type32; (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); + (void) nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); switch (type) { case DMU_OST_ZFS: @@ -3203,9 +3253,17 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) } } + error = dsl_crypto_params_create_nvlist(nvprops, hidden_args, &dcp); + if (error != 0) { + nvlist_free(zct.zct_zplprops); + return (error); + } + error = dmu_objset_create(fsname, type, - is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct); + is_insensitive ? DS_FLAG_CI_DATASET : 0, dcp, cbfunc, &zct); + nvlist_free(zct.zct_zplprops); + dsl_crypto_params_free(dcp, !!error); /* * It would be nice to do this atomically. @@ -3240,6 +3298,7 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) * innvl: { * "origin" -> name of origin snapshot * (optional) "props" -> { prop -> value } + * (optional) "hidden_args" -> { "wkeydata" -> value } * } * * outputs: @@ -3250,11 +3309,14 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) { int error = 0; nvlist_t *nvprops = NULL; + nvlist_t *hidden_args = NULL; + dsl_crypto_params_t *dcp = NULL; char *origin_name; if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) return (SET_ERROR(EINVAL)); (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); + (void) nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); if (strchr(fsname, '@') || strchr(fsname, '%')) @@ -3262,10 +3324,15 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) if (dataset_namecheck(origin_name, NULL, NULL) != 0) return (SET_ERROR(EINVAL)); - error = dmu_objset_clone(fsname, origin_name); + + error = dsl_crypto_params_create_nvlist(nvprops, hidden_args, &dcp); if (error != 0) return (error); + error = dmu_objset_clone(fsname, origin_name, dcp); + + dsl_crypto_params_free(dcp, !!error); + /* * It would be nice to do this atomically. */ @@ -4555,7 +4622,8 @@ zfs_ioc_send(zfs_cmd_t *zc) if (error != 0) return (error); - error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); + error = dsl_dataset_hold_crypt_obj(dp, zc->zc_sendobj, + FTAG, &tosnap); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); @@ -4565,7 +4633,7 @@ zfs_ioc_send(zfs_cmd_t *zc) error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &fromsnap); if (error != 0) { - dsl_dataset_rele(tosnap, FTAG); + dsl_dataset_rele_crypt(tosnap, FTAG); dsl_pool_rele(dp, FTAG); return (error); } @@ -4576,7 +4644,7 @@ zfs_ioc_send(zfs_cmd_t *zc) if (fromsnap != NULL) dsl_dataset_rele(fromsnap, FTAG); - dsl_dataset_rele(tosnap, FTAG); + dsl_dataset_rele_crypt(tosnap, FTAG); dsl_pool_rele(dp, FTAG); } else { file_t *fp = getf(zc->zc_cookie); @@ -5668,6 +5736,98 @@ zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) return (error); } +static int +zfs_ioc_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) +{ + int ret = 0; + dsl_crypto_params_t *dcp = NULL; + uint64_t crypto_cmd; + nvlist_t *args, *hidden_args; + spa_t *spa; + + ret = spa_open(dsname, &spa, FTAG); + if (ret) + return (ret); + + if (!spa_feature_is_enabled(spa, SPA_FEATURE_ENCRYPTION)) { + spa_close(spa, FTAG); + return (SET_ERROR(EINVAL)); + } + + spa_close(spa, FTAG); + + if (strchr(dsname, '@') || strchr(dsname, '%')) { + ret = (SET_ERROR(EINVAL)); + goto error; + } + + ret = nvlist_lookup_uint64(innvl, "crypto_cmd", &crypto_cmd); + if (ret) { + ret = (SET_ERROR(EINVAL)); + goto error; + } + + switch (crypto_cmd) { + case ZFS_IOC_KEY_LOAD_KEY: + ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, + &hidden_args); + if (ret) { + ret = SET_ERROR(EINVAL); + goto error; + } + + ret = dsl_crypto_params_create_nvlist(NULL, hidden_args, &dcp); + if (ret) + goto error; + + ret = spa_keystore_load_wkey(dsname, dcp); + if (ret) + goto error; + + break; + case ZFS_IOC_KEY_UNLOAD_KEY: + ret = spa_keystore_unload_wkey(dsname); + if (ret) + goto error; + + break; + case ZFS_IOC_KEY_REWRAP: + ret = nvlist_lookup_nvlist(innvl, "args", &args); + if (ret) { + ret = SET_ERROR(EINVAL); + goto error; + } + + ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, + &hidden_args); + if (ret) { + ret = SET_ERROR(EINVAL); + goto error; + } + + ret = dsl_crypto_params_create_nvlist(args, hidden_args, &dcp); + if (ret) + goto error; + + ret = spa_keystore_rewrap(dsname, dcp); + if (ret) + goto error; + + break; + default: + ret = SET_ERROR(EINVAL); + goto error; + } + + dsl_crypto_params_free(dcp, B_FALSE); + + return (0); + +error: + dsl_crypto_params_free(dcp, B_TRUE); + return (ret); +} + static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST]; static void @@ -5839,6 +5999,9 @@ zfs_ioctl_init(void) zfs_ioctl_register("receive", ZFS_IOC_RECV_NEW, zfs_ioc_recv_new, zfs_secpolicy_recv_new, DATASET_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + zfs_ioctl_register("crypto", ZFS_IOC_KEY, + zfs_ioc_key, zfs_secpolicy_key, + DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE); /* IOCTLS that use the legacy function signature */ @@ -6266,7 +6429,6 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg) break; } - if (error == 0 && !(flag & FKIOCTL)) { cookie = spl_fstrans_mark(); error = vec->zvec_secpolicy(zc, innvl, CRED()); diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 3c76cfe6ff44..319a5b506478 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -752,7 +752,7 @@ zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp) * We claim to always be readonly so we can open snapshots; * other ZPL code will prevent us from writing to snapshots. */ - error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, zsb, &os); + error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, B_TRUE, zsb, &os); if (error) goto out_zmo; diff --git a/module/zfs/zil.c b/module/zfs/zil.c index 10ea12cd72a4..02a56ad59cad 100644 --- a/module/zfs/zil.c +++ b/module/zfs/zil.c @@ -195,8 +195,8 @@ zil_init_log_chain(zilog_t *zilog, blkptr_t *bp) * Read a log block and make sure it's valid. */ static int -zil_read_log_block(zilog_t *zilog, const blkptr_t *bp, blkptr_t *nbp, void *dst, - char **end) +zil_read_log_block(zilog_t *zilog, boolean_t decrypt, const blkptr_t *bp, + blkptr_t *nbp, void *dst, char **end) { enum zio_flag zio_flags = ZIO_FLAG_CANFAIL; arc_flags_t aflags = ARC_FLAG_WAIT; @@ -210,11 +210,14 @@ zil_read_log_block(zilog_t *zilog, const blkptr_t *bp, blkptr_t *nbp, void *dst, if (!(zilog->zl_header->zh_flags & ZIL_CLAIM_LR_SEQ_VALID)) zio_flags |= ZIO_FLAG_SPECULATIVE; + if (!decrypt) + zio_flags |= ZIO_FLAG_RAW; + SET_BOOKMARK(&zb, bp->blk_cksum.zc_word[ZIL_ZC_OBJSET], ZB_ZIL_OBJECT, ZB_ZIL_LEVEL, bp->blk_cksum.zc_word[ZIL_ZC_SEQ]); - error = arc_read(NULL, zilog->zl_spa, bp, arc_getbuf_func, &abuf, - ZIO_PRIORITY_SYNC_READ, zio_flags, &aflags, &zb); + error = arc_read(NULL, zilog->zl_spa, bp, arc_getbuf_func, + &abuf, ZIO_PRIORITY_SYNC_READ, zio_flags, &aflags, &zb); if (error == 0) { zio_cksum_t cksum = bp->blk_cksum; @@ -261,7 +264,8 @@ zil_read_log_block(zilog_t *zilog, const blkptr_t *bp, blkptr_t *nbp, void *dst, } } - arc_buf_destroy(abuf, &abuf); + if (abuf) + arc_buf_destroy(abuf, &abuf); } return (error); @@ -289,6 +293,14 @@ zil_read_log_data(zilog_t *zilog, const lr_write_t *lr, void *wbuf) if (zilog->zl_header->zh_claim_txg == 0) zio_flags |= ZIO_FLAG_SPECULATIVE | ZIO_FLAG_SCRUB; + /* + * If we are not using the resulting data, we are just checking that + * it hasn't been corrupted. We shouldn't try to decrypt it now since + * the keys may not be loaded. + */ + if (wbuf == NULL && BP_IS_ENCRYPTED(bp)) + zio_flags |= ZIO_FLAG_RAW; + SET_BOOKMARK(&zb, dmu_objset_id(zilog->zl_os), lr->lr_foid, ZB_ZIL_LEVEL, lr->lr_offset / BP_GET_LSIZE(bp)); @@ -309,7 +321,8 @@ zil_read_log_data(zilog_t *zilog, const lr_write_t *lr, void *wbuf) */ int zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func, - zil_parse_lr_func_t *parse_lr_func, void *arg, uint64_t txg) + zil_parse_lr_func_t *parse_lr_func, void *arg, uint64_t txg, + boolean_t decrypt) { const zil_header_t *zh = zilog->zl_header; boolean_t claimed = !!zh->zh_claim_txg; @@ -350,7 +363,9 @@ zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func, if (blk_seq > claim_blk_seq) break; - if ((error = parse_blk_func(zilog, &blk, arg, txg)) != 0) + + error = parse_blk_func(zilog, &blk, arg, txg); + if (error != 0) break; ASSERT3U(max_blk_seq, <, blk_seq); max_blk_seq = blk_seq; @@ -359,7 +374,8 @@ zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func, if (max_lr_seq == claim_lr_seq && max_blk_seq == claim_blk_seq) break; - error = zil_read_log_block(zilog, &blk, &next_blk, lrbuf, &end); + error = zil_read_log_block(zilog, decrypt, &blk, &next_blk, + lrbuf, &end); if (error != 0) break; @@ -369,7 +385,9 @@ zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func, ASSERT3U(reclen, >=, sizeof (lr_t)); if (lr->lrc_seq > claim_lr_seq) goto done; - if ((error = parse_lr_func(zilog, lr, arg, txg)) != 0) + + error = parse_lr_func(zilog, lr, arg, txg); + if (error != 0) goto done; ASSERT3U(max_lr_seq, <, lr->lrc_seq); max_lr_seq = lr->lrc_seq; @@ -425,9 +443,12 @@ zil_claim_log_record(zilog_t *zilog, lr_t *lrc, void *tx, uint64_t first_txg) * waited for all writes to be stable first), so it is semantically * correct to declare this the end of the log. */ - if (lr->lr_blkptr.blk_birth >= first_txg && - (error = zil_read_log_data(zilog, lr, NULL)) != 0) - return (error); + if (lr->lr_blkptr.blk_birth >= first_txg) { + error = zil_read_log_data(zilog, lr, NULL); + if (error) + return (error); + } + return (zil_claim_log_block(zilog, &lr->lr_blkptr, tx, first_txg)); } @@ -557,7 +578,7 @@ zil_create(zilog_t *zilog) BP_ZERO(&blk); } - error = zio_alloc_zil(zilog->zl_spa, txg, &blk, + error = zio_alloc_zil(zilog->zl_spa, zilog->zl_os, txg, &blk, ZIL_MIN_BLKSZ, B_TRUE); fastwrite = TRUE; @@ -651,7 +672,7 @@ zil_destroy_sync(zilog_t *zilog, dmu_tx_t *tx) { ASSERT(list_is_empty(&zilog->zl_lwb_list)); (void) zil_parse(zilog, zil_free_log_block, - zil_free_log_record, tx, zilog->zl_header->zh_claim_txg); + zil_free_log_record, tx, zilog->zl_header->zh_claim_txg, B_FALSE); } int @@ -665,7 +686,7 @@ zil_claim(dsl_pool_t *dp, dsl_dataset_t *ds, void *txarg) int error; error = dmu_objset_own_obj(dp, ds->ds_object, - DMU_OST_ANY, B_FALSE, FTAG, &os); + DMU_OST_ANY, B_FALSE, B_FALSE, FTAG, &os); if (error != 0) { /* * EBUSY indicates that the objset is inconsistent, in which @@ -701,7 +722,7 @@ zil_claim(dsl_pool_t *dp, dsl_dataset_t *ds, void *txarg) ASSERT3U(zh->zh_claim_txg, <=, first_txg); if (zh->zh_claim_txg == 0 && !BP_IS_HOLE(&zh->zh_log)) { (void) zil_parse(zilog, zil_claim_log_block, - zil_claim_log_record, tx, first_txg); + zil_claim_log_record, tx, first_txg, B_FALSE); zh->zh_claim_txg = first_txg; zh->zh_claim_blk_seq = zilog->zl_parse_blk_seq; zh->zh_claim_lr_seq = zilog->zl_parse_lr_seq; @@ -770,7 +791,8 @@ zil_check_log_chain(dsl_pool_t *dp, dsl_dataset_t *ds, void *tx) * which will update spa_max_claim_txg. See spa_load() for details. */ error = zil_parse(zilog, zil_claim_log_block, zil_claim_log_record, tx, - zilog->zl_header->zh_claim_txg ? -1ULL : spa_first_txg(os->os_spa)); + zilog->zl_header->zh_claim_txg ? -1ULL : spa_first_txg(os->os_spa), + B_FALSE); return ((error == ECKSUM || error == ENOENT) ? 0 : error); } @@ -1026,8 +1048,8 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) BP_ZERO(bp); use_slog = USE_SLOG(zilog); - error = zio_alloc_zil(spa, txg, bp, zil_blksz, - USE_SLOG(zilog)); + error = zio_alloc_zil(spa, zilog->zl_os, txg, bp, + zil_blksz, USE_SLOG(zilog)); if (use_slog) { ZIL_STAT_BUMP(zil_itx_metaslab_slog_count); ZIL_STAT_INCR(zil_itx_metaslab_slog_bytes, lwb->lwb_nused); @@ -1059,7 +1081,7 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) wsz = lwb->lwb_sz; } - zilc->zc_pad = 0; + zilc->zc_mac = 0; zilc->zc_nused = lwb->lwb_nused; zilc->zc_eck.zec_cksum = lwb->lwb_blk.blk_cksum; @@ -2203,7 +2225,7 @@ zil_replay(objset_t *os, void *arg, zil_replay_func_t replay_func[TX_MAX_TYPE]) zilog->zl_replay_time = ddi_get_lbolt(); ASSERT(zilog->zl_replay_blks == 0); (void) zil_parse(zilog, zil_incr_blks, zil_replay_log_record, &zr, - zh->zh_claim_txg); + zh->zh_claim_txg, B_TRUE); vmem_free(zr.zr_lr, 2 * SPA_MAXBLOCKSIZE); zil_destroy(zilog, B_FALSE); diff --git a/module/zfs/zio.c b/module/zfs/zio.c index a242c6bf62f3..fa2aaf766926 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -43,6 +43,7 @@ #include #include #include +#include /* * ========================================================================== @@ -362,7 +363,7 @@ zio_pop_transforms(zio_t *zio) /* * ========================================================================== - * I/O transform callbacks for subblocks and decompression + * I/O transform callbacks for subblocks, decompression, and decryption * ========================================================================== */ static void @@ -388,6 +389,41 @@ zio_decompress(zio_t *zio, abd_t *data, uint64_t size) } } +static void +zio_decrypt(zio_t *zio, abd_t *data, uint64_t size) +{ + int ret; + void *tmp; + blkptr_t *bp = zio->io_bp; + uint8_t salt[DATA_SALT_LEN]; + uint8_t iv[DATA_IV_LEN]; + uint8_t mac[DATA_MAC_LEN]; + + ASSERT(BP_IS_ENCRYPTED(bp)); + ASSERT3U(size, !=, 0); + + if (zio->io_error != 0) + return; + + zio_crypt_decode_params_bp(bp, salt, iv); + + if (BP_GET_TYPE(bp) == DMU_OT_INTENT_LOG) { + tmp = abd_borrow_buf_copy(zio->io_abd, sizeof (zil_chain_t)); + zio_crypt_decode_mac_zil(tmp, mac); + abd_return_buf(zio->io_abd, tmp, sizeof (zil_chain_t)); + } else { + zio_crypt_decode_mac_bp(bp, mac); + } + + ret = spa_do_crypt_abd(B_FALSE, zio->io_spa, &zio->io_bookmark, bp, + bp->blk_birth, size, data, zio->io_abd, iv, mac, salt); + if (ret == ZIO_NO_ENCRYPTION_NEEDED) { + ASSERT3U(BP_GET_TYPE(bp), ==, DMU_OT_INTENT_LOG); + } else if (ret) { + zio->io_error = ret; + } +} + /* * ========================================================================== * I/O parent/child relationships and pipeline interlocks @@ -584,7 +620,7 @@ zio_create(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp, ASSERT(!bp || !(flags & ZIO_FLAG_CONFIG_WRITER)); ASSERT(vd || stage == ZIO_STAGE_OPEN); - IMPLY(lsize != psize, (flags & ZIO_FLAG_RAW) != 0); + IMPLY(lsize != psize, (flags & ZIO_FLAG_RAW_COMPRESS) != 0); zio = kmem_cache_alloc(zio_cache, KM_SLEEP); bzero(zio, sizeof (zio_t)); @@ -820,9 +856,12 @@ zio_write(zio_t *pio, spa_t *spa, uint64_t txg, blkptr_t *bp, * Data can be NULL if we are going to call zio_write_override() to * provide the already-allocated BP. But we may need the data to * verify a dedup hit (if requested). In this case, don't try to - * dedup (just take the already-allocated BP verbatim). + * dedup (just take the already-allocated BP verbatim). Encrypted + * dedup blocks need data as well so we also disable dedup in this + * case. */ - if (data == NULL && zio->io_prop.zp_dedup_verify) { + if (data == NULL && + (zio->io_prop.zp_dedup_verify || zio->io_prop.zp_encrypt)) { zio->io_prop.zp_dedup = zio->io_prop.zp_dedup_verify = B_FALSE; } @@ -1162,16 +1201,22 @@ static int zio_read_bp_init(zio_t *zio) { blkptr_t *bp = zio->io_bp; + uint64_t psize = + BP_IS_EMBEDDED(bp) ? BPE_GET_PSIZE(bp) : BP_GET_PSIZE(bp); if (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF && zio->io_child_type == ZIO_CHILD_LOGICAL && - !(zio->io_flags & ZIO_FLAG_RAW)) { - uint64_t psize = - BP_IS_EMBEDDED(bp) ? BPE_GET_PSIZE(bp) : BP_GET_PSIZE(bp); + !(zio->io_flags & ZIO_FLAG_RAW_COMPRESS)) { zio_push_transform(zio, abd_alloc_sametype(zio->io_abd, psize), psize, psize, zio_decompress); } + if (BP_IS_ENCRYPTED(bp) && zio->io_child_type == ZIO_CHILD_LOGICAL && + !(zio->io_flags & ZIO_FLAG_RAW_ENCRYPT)) { + zio_push_transform(zio, abd_alloc_sametype(zio->io_abd, psize), + psize, psize, zio_decrypt); + } + if (BP_IS_EMBEDDED(bp) && BPE_GET_ETYPE(bp) == BP_EMBEDDED_TYPE_DATA) { int psize = BPE_GET_PSIZE(bp); void *data = abd_borrow_buf(zio->io_abd, psize); @@ -1198,7 +1243,6 @@ zio_read_bp_init(zio_t *zio) static int zio_write_bp_init(zio_t *zio) { - if (!IO_IS_ALLOCATING(zio)) return (ZIO_PIPELINE_CONTINUE); @@ -1237,7 +1281,8 @@ zio_write_bp_init(zio_t *zio) ASSERT((zio_checksum_table[zp->zp_checksum].ci_flags & ZCHECKSUM_FLAG_DEDUP) || zp->zp_dedup_verify); - if (BP_GET_CHECKSUM(bp) == zp->zp_checksum) { + if (BP_GET_CHECKSUM(bp) == zp->zp_checksum && + !zp->zp_encrypt) { BP_SET_DEDUP(bp, 1); zio->io_pipeline |= ZIO_STAGE_DDT_WRITE; return (ZIO_PIPELINE_CONTINUE); @@ -1266,8 +1311,6 @@ zio_write_compress(zio_t *zio) uint64_t psize = zio->io_size; int pass = 1; - EQUIV(lsize != psize, (zio->io_flags & ZIO_FLAG_RAW) != 0); - /* * If our children haven't all reached the ready stage, * wait for them and then repeat this pipeline stage. @@ -1317,13 +1360,15 @@ zio_write_compress(zio_t *zio) } /* If it's a compressed write that is not raw, compress the buffer. */ - if (compress != ZIO_COMPRESS_OFF && psize == lsize) { + if (compress != ZIO_COMPRESS_OFF && + !(zio->io_flags & ZIO_FLAG_RAW_COMPRESS)) { void *cbuf = zio_buf_alloc(lsize); psize = zio_compress_data(compress, zio->io_abd, cbuf, lsize); if (psize == 0 || psize == lsize) { compress = ZIO_COMPRESS_OFF; zio_buf_free(cbuf, lsize); - } else if (!zp->zp_dedup && psize <= BPE_PAYLOAD_SIZE && + } else if (!zp->zp_dedup && !zp->zp_encrypt && + psize <= BPE_PAYLOAD_SIZE && zp->zp_level == 0 && !DMU_OT_HAS_FILL(zp->zp_type) && spa_feature_is_enabled(spa, SPA_FEATURE_EMBEDDED_DATA)) { encode_embedded_bp_compressed(bp, @@ -2274,11 +2319,19 @@ zio_write_gang_block(zio_t *pio) uint64_t resid = pio->io_size; uint64_t lsize; int copies = gio->io_prop.zp_copies; - int gbh_copies = MIN(copies + 1, spa_max_replication(spa)); + int gbh_copies; zio_prop_t zp; int g, error; - int flags = METASLAB_HINTBP_FAVOR | METASLAB_GANG_HEADER; + + /* + * encrypted blocks need DVA[2] free so encrypted gang headers can't + * have a third copy. + */ + gbh_copies = MIN(copies + 1, spa_max_replication(spa)); + if (gio->io_prop.zp_encrypt && gbh_copies >= SPA_DVAS_PER_BP) + gbh_copies = SPA_DVAS_PER_BP - 1; + if (pio->io_flags & ZIO_FLAG_IO_ALLOCATING) { ASSERT(pio->io_priority == ZIO_PRIORITY_ASYNC_WRITE); ASSERT(!(pio->io_flags & ZIO_FLAG_NODATA)); @@ -2351,12 +2404,16 @@ zio_write_gang_block(zio_t *pio) zp.zp_checksum = gio->io_prop.zp_checksum; zp.zp_compress = ZIO_COMPRESS_OFF; + zp.zp_encrypt = gio->io_prop.zp_encrypt; zp.zp_type = DMU_OT_NONE; zp.zp_level = 0; zp.zp_copies = gio->io_prop.zp_copies; zp.zp_dedup = B_FALSE; zp.zp_dedup_verify = B_FALSE; zp.zp_nopwrite = B_FALSE; + bzero(zp.zp_salt, DATA_SALT_LEN); + bzero(zp.zp_iv, DATA_IV_LEN); + bzero(zp.zp_mac, DATA_MAC_LEN); cio = zio_write(zio, spa, txg, &gbh->zg_blkptr[g], abd_get_offset(pio->io_abd, pio->io_size - resid), lsize, @@ -2435,6 +2492,7 @@ zio_nop_write(zio_t *zio) if (BP_IS_HOLE(bp_orig) || !(zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_flags & ZCHECKSUM_FLAG_NOPWRITE) || + BP_IS_ENCRYPTED(bp) || BP_IS_ENCRYPTED(bp_orig) || BP_GET_CHECKSUM(bp) != BP_GET_CHECKSUM(bp_orig) || BP_GET_COMPRESS(bp) != BP_GET_COMPRESS(bp_orig) || BP_GET_DEDUP(bp) != BP_GET_DEDUP(bp_orig) || @@ -2584,7 +2642,7 @@ zio_ddt_collision(zio_t *zio, ddt_t *ddt, ddt_entry_t *dde) * pushed the I/O transforms. That's an important optimization * because otherwise we'd compress/encrypt all dmu_sync() data twice. * However, we should never get a raw, override zio so in these - * cases we can compare the io_data directly. This is useful because + * cases we can compare the io_abd directly. This is useful because * it allows us to do dedup verification even if we don't have access * to the original data (for instance, if the encryption keys aren't * loaded). @@ -3073,8 +3131,8 @@ zio_dva_unallocate(zio_t *zio, zio_gang_node_t *gn, blkptr_t *bp) * Try to allocate an intent log block. Return 0 on success, errno on failure. */ int -zio_alloc_zil(spa_t *spa, uint64_t txg, blkptr_t *new_bp, uint64_t size, - boolean_t use_slog) +zio_alloc_zil(spa_t *spa, objset_t *os, uint64_t txg, blkptr_t *new_bp, + uint64_t size, boolean_t use_slog) { int error = 1; @@ -3101,6 +3159,23 @@ zio_alloc_zil(spa_t *spa, uint64_t txg, blkptr_t *new_bp, uint64_t size, BP_SET_LEVEL(new_bp, 0); BP_SET_DEDUP(new_bp, 0); BP_SET_BYTEORDER(new_bp, ZFS_HOST_BYTEORDER); + + /* + * encrypted blocks will require an IV and salt. We generate + * these now since we will not be rewriting the bp at + * rewrite time. + */ + if (os->os_encrypted) { + uint8_t iv[DATA_IV_LEN]; + uint8_t salt[DATA_SALT_LEN]; + + BP_SET_ENCRYPTED(new_bp, B_TRUE); + VERIFY0(spa_crypt_get_salt(spa, + dmu_objset_id(os), salt)); + VERIFY0(zio_crypt_generate_iv(iv)); + + zio_crypt_encode_params_bp(new_bp, salt, iv); + } } return (error); @@ -3423,6 +3498,110 @@ zio_vdev_io_bypass(zio_t *zio) zio->io_stage = ZIO_STAGE_VDEV_IO_ASSESS >> 1; } +/* + * ========================================================================== + * Encrypt and store encryption parameters + * ========================================================================== + */ + + +/* + * This function is used for ZIO_STAGE_ENCRYPT. It is responsible for + * managing the storage of encryption parameters and passing them to the + * lower-level encryption functions. + */ +static int +zio_encrypt(zio_t *zio) +{ + int ret; + zio_prop_t *zp = &zio->io_prop; + spa_t *spa = zio->io_spa; + blkptr_t *bp = zio->io_bp; + uint64_t psize = BP_GET_PSIZE(bp); + dmu_object_type_t ot = BP_GET_TYPE(bp); + void *enc_buf = NULL; + abd_t *eabd = NULL; + uint8_t salt[DATA_SALT_LEN]; + uint8_t iv[DATA_IV_LEN]; + uint8_t mac[DATA_MAC_LEN]; + + /* the root zio already encrypted the data */ + if (zio->io_child_type == ZIO_CHILD_GANG) + return (ZIO_PIPELINE_CONTINUE); + + /* only ZIL blocks are re-encrypted on rewrite */ + if (!IO_IS_ALLOCATING(zio) && + BP_GET_TYPE(bp) != DMU_OT_INTENT_LOG) + return (ZIO_PIPELINE_CONTINUE); + + /* if we are doing raw encryption set the provided encryption params */ + if (zio->io_flags & ZIO_FLAG_RAW_ENCRYPT) { + ASSERT(zio->io_flags & ZIO_FLAG_RAW_COMPRESS); + ASSERT(zp->zp_encrypt); + BP_SET_ENCRYPTED(bp, B_TRUE); + zio_crypt_encode_params_bp(bp, zp->zp_salt, zp->zp_iv); + zio_crypt_encode_mac_bp(bp, zp->zp_mac); + return (ZIO_PIPELINE_CONTINUE); + } + + if (!(zp->zp_encrypt || BP_IS_ENCRYPTED(bp))) { + BP_SET_ENCRYPTED(bp, B_FALSE); + return (ZIO_PIPELINE_CONTINUE); + } + + ASSERT3U(psize, !=, 0); + ASSERT(spa_feature_is_active(spa, SPA_FEATURE_ENCRYPTION)); + ASSERT(BP_GET_LEVEL(bp) == 0 || ot == DMU_OT_INTENT_LOG); + + enc_buf = zio_buf_alloc(psize); + eabd = abd_get_from_buf(enc_buf, psize); + abd_take_ownership_of_buf(eabd, B_TRUE); + + /* + * For an explanation of what encryption parameters are stored + * where, see the block comment in zio_crypt.c. + */ + if (ot == DMU_OT_INTENT_LOG) { + zio_crypt_decode_params_bp(bp, salt, iv); + } else { + BP_SET_ENCRYPTED(bp, B_TRUE); + } + + /* Perform the encryption. This should not fail */ + ret = spa_do_crypt_abd(B_TRUE, spa, &zio->io_bookmark, bp, + zio->io_txg, psize, zio->io_abd, eabd, iv, mac, salt); + if (ret) { + /* + * Dnode blocks and ZIL blocks may not need encryption if there + * is no private data in the blocks. We cannot disable + * encryption on ZIL blocks since they have been preallocated, + * but we can do so with dnode blocks. This means that we can + * use a full checksum instead of leaving half blank for a + * MAC we won't end up using. + */ + ASSERT3U(ret, ==, ZIO_NO_ENCRYPTION_NEEDED); + abd_free(eabd); + + if (ot != DMU_OT_INTENT_LOG) { + ASSERT3U(ot, ==, DMU_OT_DNODE); + BP_SET_ENCRYPTED(bp, B_FALSE); + } + return (ZIO_PIPELINE_CONTINUE); + } + + /* encode encryption metadata into the bp */ + if (ot == DMU_OT_INTENT_LOG) { + zio_crypt_encode_mac_zil(enc_buf, mac); + } else { + zio_crypt_encode_params_bp(bp, salt, iv); + zio_crypt_encode_mac_bp(bp, mac); + } + + zio_push_transform(zio, eabd, psize, psize, NULL); + + return (ZIO_PIPELINE_CONTINUE); +} + /* * ========================================================================== * Generate and verify checksums @@ -4013,6 +4192,7 @@ static zio_pipe_stage_t *zio_pipeline[] = { zio_free_bp_init, zio_issue_async, zio_write_compress, + zio_encrypt, zio_checksum_generate, zio_nop_write, zio_ddt_read_start, diff --git a/module/zfs/zio_checksum.c b/module/zfs/zio_checksum.c index 37116f049748..52003a597abb 100644 --- a/module/zfs/zio_checksum.c +++ b/module/zfs/zio_checksum.c @@ -330,7 +330,26 @@ zio_checksum_compute(zio_t *zio, enum zio_checksum checksum, eck->zec_cksum = cksum; } else { ci->ci_func[0](abd, size, spa->spa_cksum_tmpls[checksum], - &bp->blk_cksum); + &cksum); + + if (BP_IS_ENCRYPTED(bp)) { + /* + * Weak checksums do not have their entropy spread + * evenly across the bits of the checksum. Therefore, + * when truncating a weak checksum we XOR the first + * 2 words with the last 2 so that we don't "lose" any + * entropy unnecessarily. + */ + if (!(ci->ci_flags & ZCHECKSUM_FLAG_DEDUP)) { + cksum.zc_word[0] ^= cksum.zc_word[2]; + cksum.zc_word[1] ^= cksum.zc_word[3]; + } + + bp->blk_cksum.zc_word[0] = cksum.zc_word[0]; + bp->blk_cksum.zc_word[1] = cksum.zc_word[1]; + } else { + bp->blk_cksum = cksum; + } } } @@ -341,6 +360,7 @@ zio_checksum_error_impl(spa_t *spa, blkptr_t *bp, enum zio_checksum checksum, zio_checksum_info_t *ci = &zio_checksum_table[checksum]; int byteswap; zio_cksum_t actual_cksum, expected_cksum; + boolean_t mac = (bp) ? BP_IS_ENCRYPTED(bp) : B_FALSE; if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL) return (SET_ERROR(EINVAL)); @@ -374,6 +394,7 @@ zio_checksum_error_impl(spa_t *spa, blkptr_t *bp, enum zio_checksum checksum, } size = P2ROUNDUP_TYPED(nused, ZIL_MIN_BLKSZ, uint64_t); + mac = B_FALSE; } else { eck = (zio_eck_t *)((char *)data + data_size) - 1; } @@ -420,8 +441,23 @@ zio_checksum_error_impl(spa_t *spa, blkptr_t *bp, enum zio_checksum checksum, info->zbc_has_cksum = 1; } - if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) - return (SET_ERROR(ECKSUM)); + /* + * MAC checksums are a special case since half of this checksum will + * actually be the encryption MAC. This will be verified by the + * decryption process, so we just check the real checksum now. + */ + if (mac) { + if (!(ci->ci_flags & ZCHECKSUM_FLAG_DEDUP)) { + actual_cksum.zc_word[0] ^= actual_cksum.zc_word[2]; + actual_cksum.zc_word[1] ^= actual_cksum.zc_word[3]; + } + + if (!ZIO_CHECKSUM_MAC_EQUAL(actual_cksum, expected_cksum)) + return (SET_ERROR(ECKSUM)); + } else { + if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) + return (SET_ERROR(ECKSUM)); + } return (0); } diff --git a/module/zfs/zio_crypt.c b/module/zfs/zio_crypt.c new file mode 100644 index 000000000000..a76595ec5e56 --- /dev/null +++ b/module/zfs/zio_crypt.c @@ -0,0 +1,1374 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2016, Datto, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +/* + * This file is responsible for handling all of the details of generating + * encryption parameters and performing encryption. + * + * BLOCK ENCRYPTION PARAMETERS: + * Encryption Algorithm (crypt): + * The encryption algorithm and mode we are going to use. We currently support + * AES-GCM and AES-CCM in 128, 192, and 256 bits. All encryption parameters are + * stored in little endian format (regardless of the host machine's byteorder). + * + * Plaintext: + * The unencrypted data that we want to encrypt + * + * Initialization Vector (IV): + * An initialization vector for the encryption algorithms. This is + * used to "tweak" the encryption algorithms so that equivalent blocks of + * data are encrypted into different ciphertext outputs. Different modes + * of encryption have different requirements for the IV. AES-GCM and AES-CCM + * require that an IV is never reused with the same encryption key. This + * value is stored unencrypted and must simply be provided to the decryption + * function. We use a 96 bit IV (as recommended by NIST). For non-dedup blocks + * we derive the IV randomly. The first 64 bits of the IV are stored in the + * second word of DVA[2] and the remaining 32 bits are stored in the upper 32 + * bits of blk_fill. For most object types this is safe because we only encrypt + * level 0 blocks which means that the fill count will be 1. For DMU_OT_DNODE + * blocks the fill count is instead used to indicate the number of allocated + * dnodes beneath the bp. The on-disk format supports at most 2^15 slots per + * L0 dnode block, because the maximum block size is 16MB (2^24). In either + * case, for level 0 blocks this number will still be smaller than UINT32_MAX + * so it is safe to store the IV in the top 32 bits of blk_fill, while leaving + * the bottom 32 bits of the fill count for the dnode code. + * + * Master key: + * This is the most important secret data of an encrypted dataset. It is used + * along with the salt to generate that actual encryption keys via HKDF. We + * do not use the master key to encrypt any data because there are theoretical + * limits on how much data can actually be safely encrypted with any encryption + * mode. The master key is stored encrypted on disk with the user's key. It's + * length is determined by the encryption algorithm. For details on how this is + * stored see the block comment in dsl_crypt.c + * + * Salt: + * Used as an input to the HKDF function, along with the master key. We use a + * 64 bit salt, stored unencrypted in the first word of DVA[2]. Any given salt + * can be used for encrypting many blocks, so we cache the current salt and the + * associated derived key in zio_crypt_t so we do not need to derive it again + * needlessly. + * + * Encryption Key: + * A secret binary key, generated from an HKDF function used to encrypt and + * decrypt data. + * + * Message Authenication Code (MAC) + * The MAC is an output of authenticated encryption modes such as AES-GCM and + * AES-CCM. Its purpose is to ensure that an attacker cannot modify encrypted + * data on disk and return garbage to the application. Effectively, it is a + * checksum that can not be reproduced by an attacker. We store the MAC in the + * second 128 bits of blk_cksum, leaving the first 128 bits for a truncated + * regular checksum of the ciphertext which can be used for scrubbing. + * + * ZIL ENCRYPTION: + * ZIL blocks have their bp written to disk ahead of the associated data, so we + * cannot store encryption paramaters there as we normally do. For these blocks + * the MAC is stored in the zil_chain_t header (in zc_mac) in a previously + * unused 8 bytes. The salt and IV are generated for the block on bp allocation. + * Since ZIL blocks are rewritten many times as new log records are added it is + * important that we do not reuse the IV with the same salt. To accomplish this + * we add in zc_nused from the zil_chain_t which should be incremented on each + * rewrite. + * + * CONSIDERATIONS FOR DEDUP: + * In order for dedup to work, blocks that we want to dedup with one another + * need to use the same IV and encryption key, so that they will have the same + * cyphertext. Normally, one should never reuse an IV with the same encryption + * key or else AES-GCM and AES-CCM can both actually leak the plaintext of both + * blocks. In this case, however, since we are using the same plaindata as + * well all that we end up with is a duplicate of the original data we already + * had. As a result, an attacker with read access to the raw disk will be able + * to tell which blocks are the same but this information is already given away + * by dedup anyway. In order to get the same IVs and encryption keys for + * equivalent blocks of data we use a HMAC of the plaindata. We use an HMAC + * here so there is never a reproducible checksum of the plaindata available + * to the attacker. The HMAC key is kept alongside the master key, encrypted + * on disk. The first 64 bits of the HMAC are used in place of the random salt, + * and the next 96 bits are used as the IV. + */ + +zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = { + {"", ZC_TYPE_NONE, 0, "inherit"}, + {"", ZC_TYPE_NONE, 0, "on"}, + {"", ZC_TYPE_NONE, 0, "off"}, + {SUN_CKM_AES_CCM, ZC_TYPE_CCM, 16, "aes-128-ccm"}, + {SUN_CKM_AES_CCM, ZC_TYPE_CCM, 24, "aes-192-ccm"}, + {SUN_CKM_AES_CCM, ZC_TYPE_CCM, 32, "aes-256-ccm"}, + {SUN_CKM_AES_GCM, ZC_TYPE_GCM, 16, "aes-128-gcm"}, + {SUN_CKM_AES_GCM, ZC_TYPE_GCM, 24, "aes-192-gcm"}, + {SUN_CKM_AES_GCM, ZC_TYPE_GCM, 32, "aes-256-gcm"} +}; + +static int +hkdf_sha256_extract(uint8_t *salt, uint_t salt_len, uint8_t *key_material, + uint_t km_len, uint8_t *out_buf) +{ + int ret; + crypto_mechanism_t mech; + crypto_key_t key; + crypto_data_t input_cd, output_cd; + + /* initialize sha 256 hmac mechanism */ + mech.cm_type = crypto_mech2id(SUN_CKM_SHA256_HMAC); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + /* initialize the salt as a crypto key */ + key.ck_format = CRYPTO_KEY_RAW; + key.ck_length = BYTES_TO_BITS(salt_len); + key.ck_data = salt; + + /* initialize crypto data for the input and output data */ + input_cd.cd_format = CRYPTO_DATA_RAW; + input_cd.cd_offset = 0; + input_cd.cd_length = km_len; + input_cd.cd_raw.iov_base = (char *)key_material; + input_cd.cd_raw.iov_len = km_len; + + output_cd.cd_format = CRYPTO_DATA_RAW; + output_cd.cd_offset = 0; + output_cd.cd_length = SHA_256_DIGEST_LEN; + output_cd.cd_raw.iov_base = (char *)out_buf; + output_cd.cd_raw.iov_len = SHA_256_DIGEST_LEN; + + ret = crypto_mac(&mech, &input_cd, &key, NULL, &output_cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + return (0); + +error: + return (ret); +} + +static int +hkdf_sha256_expand(uint8_t *extract_key, uint8_t *info, uint_t info_len, + uint8_t *out_buf, uint_t out_len) +{ + int ret; + crypto_mechanism_t mech; + crypto_context_t ctx; + crypto_key_t key; + crypto_data_t T_cd, info_cd, c_cd; + uint_t i, T_len = 0, pos = 0; + uint8_t c; + uint_t N = (out_len + SHA_256_DIGEST_LEN) / SHA_256_DIGEST_LEN; + uint8_t T[SHA_256_DIGEST_LEN]; + + if (N > 255) + return (SET_ERROR(EINVAL)); + + /* initialize sha 256 hmac mechanism */ + mech.cm_type = crypto_mech2id(SUN_CKM_SHA256_HMAC); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + /* initialize the salt as a crypto key */ + key.ck_format = CRYPTO_KEY_RAW; + key.ck_length = BYTES_TO_BITS(SHA_256_DIGEST_LEN); + key.ck_data = extract_key; + + /* initialize crypto data for the input and output data */ + T_cd.cd_format = CRYPTO_DATA_RAW; + T_cd.cd_offset = 0; + T_cd.cd_raw.iov_base = (char *)T; + + c_cd.cd_format = CRYPTO_DATA_RAW; + c_cd.cd_offset = 0; + c_cd.cd_length = 1; + c_cd.cd_raw.iov_base = (char *)&c; + c_cd.cd_raw.iov_len = 1; + + info_cd.cd_format = CRYPTO_DATA_RAW; + info_cd.cd_offset = 0; + info_cd.cd_length = info_len; + info_cd.cd_raw.iov_base = (char *)info; + info_cd.cd_raw.iov_len = info_len; + + for (i = 1; i <= N; i++) { + c = i; + + T_cd.cd_length = T_len; + T_cd.cd_raw.iov_len = T_len; + + ret = crypto_mac_init(&mech, &key, NULL, &ctx, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + ret = crypto_mac_update(ctx, &T_cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + ret = crypto_mac_update(ctx, &info_cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + ret = crypto_mac_update(ctx, &c_cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + T_len = SHA_256_DIGEST_LEN; + T_cd.cd_length = T_len; + T_cd.cd_raw.iov_len = T_len; + + ret = crypto_mac_final(ctx, &T_cd, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + bcopy(T, out_buf + pos, + (i != N) ? SHA_256_DIGEST_LEN : (out_len - pos)); + pos += SHA_256_DIGEST_LEN; + } + + return (0); + +error: + return (ret); +} + +/* + * HKDF is designed to be a relatively fast function for deriving keys from a + * master key + a salt. We use this function to generate new encryption keys + * so as to avoid hitting the cryptographic limits of the underlying + * encryption modes. Note that, for the sake of deriving encryption keys, the + * info parameter is called the "salt" everywhere else in the code. + */ +static int +hkdf_sha256(uint8_t *key_material, uint_t km_len, uint8_t *salt, + uint_t salt_len, uint8_t *info, uint_t info_len, uint8_t *output_key, + uint_t out_len) +{ + int ret; + uint8_t extract_key[SHA_256_DIGEST_LEN]; + + ret = hkdf_sha256_extract(salt, salt_len, key_material, km_len, + extract_key); + if (ret) + goto error; + + ret = hkdf_sha256_expand(extract_key, info, info_len, output_key, + out_len); + if (ret) + goto error; + + return (0); + +error: + return (ret); +} + +void +zio_crypt_key_destroy(zio_crypt_key_t *key) +{ + rw_destroy(&key->zk_salt_lock); + + /* free crypto templates */ + crypto_destroy_ctx_template(key->zk_current_tmpl); + crypto_destroy_ctx_template(key->zk_hmac_tmpl); + + /* zero out sensitive data */ + bzero(key, sizeof (zio_crypt_key_t)); +} + +int +zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key) +{ + int ret; + crypto_mechanism_t mech; + uint_t keydata_len; + + ASSERT(key != NULL); + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + + keydata_len = zio_crypt_table[crypt].ci_keylen; + + /* fill keydata buffers and salt with random data */ + ret = random_get_bytes(key->zk_master_keydata, keydata_len); + if (ret) + goto error; + + ret = random_get_bytes(key->zk_hmac_keydata, HMAC_SHA256_KEYLEN); + if (ret) + goto error; + + ret = random_get_bytes(key->zk_salt, DATA_SALT_LEN); + if (ret) + goto error; + + /* derive the current key from the master key */ + ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, + key->zk_salt, DATA_SALT_LEN, key->zk_current_keydata, keydata_len); + if (ret) + goto error; + + /* initialize keys for the ICP */ + key->zk_current_key.ck_format = CRYPTO_KEY_RAW; + key->zk_current_key.ck_data = key->zk_current_keydata; + key->zk_current_key.ck_length = BYTES_TO_BITS(keydata_len); + + key->zk_hmac_key.ck_format = CRYPTO_KEY_RAW; + key->zk_hmac_key.ck_data = &key->zk_hmac_key; + key->zk_hmac_key.ck_length = BYTES_TO_BITS(HMAC_SHA256_KEYLEN); + + /* + * Initialize the crypto templates. It's ok if this fails because + * this is just an optimization. + */ + mech.cm_type = crypto_mech2id(zio_crypt_table[crypt].ci_mechname); + ret = crypto_create_ctx_template(&mech, &key->zk_current_key, + &key->zk_current_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_current_tmpl = NULL; + + mech.cm_type = crypto_mech2id(SUN_CKM_SHA256_HMAC); + ret = crypto_create_ctx_template(&mech, &key->zk_hmac_key, + &key->zk_hmac_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_hmac_tmpl = NULL; + + key->zk_crypt = crypt; + key->zk_salt_count = 0; + rw_init(&key->zk_salt_lock, NULL, RW_DEFAULT, NULL); + + return (0); + +error: + zio_crypt_key_destroy(key); + return (ret); +} + +static int +zio_crypt_key_change_salt(zio_crypt_key_t *key) +{ + int ret; + uint8_t salt[DATA_SALT_LEN]; + crypto_mechanism_t mech; + uint_t keydata_len = zio_crypt_table[key->zk_crypt].ci_keylen; + + /* generate a new salt */ + ret = random_get_bytes(salt, DATA_SALT_LEN); + if (ret) + goto error; + + rw_enter(&key->zk_salt_lock, RW_WRITER); + + /* derive the current key from the master key and the new salt */ + ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, + salt, DATA_SALT_LEN, key->zk_current_keydata, keydata_len); + if (ret) + goto error_unlock; + + /* assign the salt and reset the usage count */ + bcopy(salt, key->zk_salt, DATA_SALT_LEN); + key->zk_salt_count = 0; + + /* destroy the old context template and create the new one */ + crypto_destroy_ctx_template(key->zk_current_tmpl); + ret = crypto_create_ctx_template(&mech, &key->zk_current_key, + &key->zk_current_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_current_tmpl = NULL; + + rw_exit(&key->zk_salt_lock); + + return (0); + +error_unlock: + rw_exit(&key->zk_salt_lock); +error: + return (ret); +} + +/* See comment above ZIO_CRYPT_MAX_SALT_USAGE definition for details */ +int +zio_crypt_key_get_salt(zio_crypt_key_t *key, uint8_t *salt) +{ + int ret; + boolean_t salt_change; + + rw_enter(&key->zk_salt_lock, RW_READER); + + bcopy(key->zk_salt, salt, DATA_SALT_LEN); + salt_change = (atomic_inc_64_nv(&key->zk_salt_count) == + ZIO_CRYPT_MAX_SALT_USAGE); + + rw_exit(&key->zk_salt_lock); + + if (salt_change) { + ret = zio_crypt_key_change_salt(key); + if (ret) + goto error; + } + + return (0); + +error: + return (ret); +} + +/* + * This function handles all encryption and decryption in zfs. When + * encrypting it expects puio to refernce the plaintext and cuio to + * have enough space for the ciphertext + room for a MAC. On decrypting + * it expects both puio and cuio to have enough room for a MAC, although + * the plaintext uio can be dsicarded afterwards. datalen should be the + * length of only the plaintext / ciphertext in either case. + */ +static int +zio_do_crypt_uio(boolean_t encrypt, uint64_t crypt, crypto_key_t *key, + crypto_ctx_template_t tmpl, uint8_t *ivbuf, uint_t datalen, + uio_t *puio, uio_t *cuio) +{ + int ret; + crypto_data_t plaindata, cipherdata; + CK_AES_CCM_PARAMS ccmp; + CK_AES_GCM_PARAMS gcmp; + crypto_mechanism_t mech; + zio_crypt_info_t crypt_info; + uint_t plain_full_len, maclen; + + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(key->ck_format, ==, CRYPTO_KEY_RAW); + + /* lookup the encryption info */ + crypt_info = zio_crypt_table[crypt]; + + /* the mac will always be the last iovec_t in the cipher uio */ + maclen = cuio->uio_iov[cuio->uio_iovcnt - 1].iov_len; + + ASSERT(maclen <= DATA_MAC_LEN); + + /* setup encryption mechanism (same as crypt) */ + mech.cm_type = crypto_mech2id(crypt_info.ci_mechname); + + /* plain length will include the MAC if we are decrypting */ + if (encrypt) { + plain_full_len = datalen; + } else { + plain_full_len = datalen + maclen; + } + + /* + * setup encryption params (currently only AES CCM and AES GCM + * are supported) + */ + if (crypt_info.ci_crypt_type == ZC_TYPE_CCM) { + ccmp.ulNonceSize = DATA_IV_LEN; + ccmp.ulAuthDataSize = 0; + ccmp.authData = NULL; + ccmp.ulMACSize = maclen; + ccmp.nonce = ivbuf; + ccmp.ulDataSize = plain_full_len; + + mech.cm_param = (char *)(&ccmp); + mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS); + } else { + gcmp.ulIvLen = DATA_IV_LEN; + gcmp.ulIvBits = BYTES_TO_BITS(DATA_IV_LEN); + gcmp.ulAADLen = 0; + gcmp.pAAD = NULL; + gcmp.ulTagBits = BYTES_TO_BITS(maclen); + gcmp.pIv = ivbuf; + + mech.cm_param = (char *)(&gcmp); + mech.cm_param_len = sizeof (CK_AES_GCM_PARAMS); + } + + /* populate the cipher and plain data structs. */ + plaindata.cd_format = CRYPTO_DATA_UIO; + plaindata.cd_offset = 0; + plaindata.cd_uio = puio; + plaindata.cd_miscdata = NULL; + plaindata.cd_length = plain_full_len; + + cipherdata.cd_format = CRYPTO_DATA_UIO; + cipherdata.cd_offset = 0; + cipherdata.cd_uio = cuio; + cipherdata.cd_miscdata = NULL; + cipherdata.cd_length = datalen + maclen; + + /* perform the actual encryption */ + if (encrypt) { + ret = crypto_encrypt(&mech, &plaindata, key, tmpl, &cipherdata, + NULL); + } else { + ret = crypto_decrypt(&mech, &cipherdata, key, tmpl, &plaindata, + NULL); + } + + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + return (0); + +error: + return (ret); +} + +int +zio_crypt_key_wrap(crypto_key_t *cwkey, zio_crypt_key_t *key, uint8_t *iv, + uint8_t *mac, uint8_t *keydata_out, uint8_t *hmac_keydata_out) +{ + int ret; + uio_t puio, cuio; + iovec_t plain_iovecs[2], cipher_iovecs[3]; + uint64_t crypt = key->zk_crypt; + uint_t enc_len, keydata_len; + + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(cwkey->ck_format, ==, CRYPTO_KEY_RAW); + + keydata_len = zio_crypt_table[crypt].ci_keylen; + + /* generate iv for wrapping the master and hmac key */ + ret = random_get_pseudo_bytes(iv, WRAPPING_IV_LEN); + if (ret) + goto error; + + /* initialize uio_ts */ + plain_iovecs[0].iov_base = key->zk_master_keydata; + plain_iovecs[0].iov_len = keydata_len; + plain_iovecs[1].iov_base = key->zk_hmac_keydata; + plain_iovecs[1].iov_len = HMAC_SHA256_KEYLEN; + + cipher_iovecs[0].iov_base = keydata_out; + cipher_iovecs[0].iov_len = keydata_len; + cipher_iovecs[1].iov_base = hmac_keydata_out; + cipher_iovecs[1].iov_len = HMAC_SHA256_KEYLEN; + cipher_iovecs[2].iov_base = mac; + cipher_iovecs[2].iov_len = WRAPPING_MAC_LEN; + + enc_len = zio_crypt_table[crypt].ci_keylen + HMAC_SHA256_KEYLEN; + puio.uio_iov = plain_iovecs; + puio.uio_iovcnt = 2; + puio.uio_segflg = UIO_SYSSPACE; + cuio.uio_iov = cipher_iovecs; + cuio.uio_iovcnt = 3; + cuio.uio_segflg = UIO_SYSSPACE; + + /* encrypt the keys and store the resulting ciphertext and mac */ + ret = zio_do_crypt_uio(B_TRUE, crypt, cwkey, NULL, iv, enc_len, + &puio, &cuio); + if (ret) + goto error; + + return (0); + +error: + return (ret); +} + +int +zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint8_t *keydata, + uint8_t *hmac_keydata, uint8_t *iv, uint8_t *mac, zio_crypt_key_t *key) +{ + int ret; + crypto_mechanism_t mech; + uio_t puio, cuio; + iovec_t plain_iovecs[3], cipher_iovecs[3]; + uint8_t outmac[WRAPPING_MAC_LEN]; + uint_t enc_len, keydata_len; + + ASSERT3U(crypt, <, ZIO_CRYPT_FUNCTIONS); + ASSERT3U(cwkey->ck_format, ==, CRYPTO_KEY_RAW); + + keydata_len = zio_crypt_table[crypt].ci_keylen; + + /* initialize uio_ts */ + plain_iovecs[0].iov_base = key->zk_master_keydata; + plain_iovecs[0].iov_len = keydata_len; + plain_iovecs[1].iov_base = key->zk_hmac_keydata; + plain_iovecs[1].iov_len = HMAC_SHA256_KEYLEN; + plain_iovecs[2].iov_base = outmac; + plain_iovecs[2].iov_len = WRAPPING_MAC_LEN; + + cipher_iovecs[0].iov_base = keydata; + cipher_iovecs[0].iov_len = keydata_len; + cipher_iovecs[1].iov_base = hmac_keydata; + cipher_iovecs[1].iov_len = HMAC_SHA256_KEYLEN; + cipher_iovecs[2].iov_base = mac; + cipher_iovecs[2].iov_len = WRAPPING_MAC_LEN; + + enc_len = keydata_len + HMAC_SHA256_KEYLEN; + puio.uio_iov = plain_iovecs; + puio.uio_segflg = UIO_SYSSPACE; + puio.uio_iovcnt = 3; + cuio.uio_iov = cipher_iovecs; + cuio.uio_iovcnt = 3; + cuio.uio_segflg = UIO_SYSSPACE; + + /* decrypt the keys and store the result in the output buffers */ + ret = zio_do_crypt_uio(B_FALSE, crypt, cwkey, NULL, iv, enc_len, + &puio, &cuio); + if (ret) + goto error; + + /* generate a fresh salt */ + ret = random_get_bytes(key->zk_salt, DATA_SALT_LEN); + if (ret) + goto error; + + /* derive the current key from the master key */ + ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, + key->zk_salt, DATA_SALT_LEN, key->zk_current_keydata, keydata_len); + if (ret) + goto error; + + /* initialize keys for ICP */ + key->zk_current_key.ck_format = CRYPTO_KEY_RAW; + key->zk_current_key.ck_data = key->zk_current_keydata; + key->zk_current_key.ck_length = BYTES_TO_BITS(keydata_len); + + key->zk_hmac_key.ck_format = CRYPTO_KEY_RAW; + key->zk_hmac_key.ck_data = key->zk_hmac_keydata; + key->zk_hmac_key.ck_length = BYTES_TO_BITS(HMAC_SHA256_KEYLEN); + + /* + * Initialize the crypto templates. It's ok if this fails because + * this is just an optimization. + */ + mech.cm_type = crypto_mech2id(zio_crypt_table[crypt].ci_mechname); + ret = crypto_create_ctx_template(&mech, &key->zk_current_key, + &key->zk_current_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_current_tmpl = NULL; + + mech.cm_type = crypto_mech2id(SUN_CKM_SHA256_HMAC); + ret = crypto_create_ctx_template(&mech, &key->zk_hmac_key, + &key->zk_hmac_tmpl, KM_SLEEP); + if (ret != CRYPTO_SUCCESS) + key->zk_hmac_tmpl = NULL; + + key->zk_crypt = crypt; + key->zk_salt_count = 0; + rw_init(&key->zk_salt_lock, NULL, RW_DEFAULT, NULL); + + return (0); + +error: + zio_crypt_key_destroy(key); + return (ret); +} + +int +zio_crypt_generate_iv(uint8_t *ivbuf) +{ + int ret; + + /* randomly generate the IV */ + ret = random_get_pseudo_bytes(ivbuf, DATA_IV_LEN); + if (ret) + goto error; + + return (0); + +error: + bzero(ivbuf, DATA_IV_LEN); + return (ret); +} + +int +zio_crypt_generate_iv_salt_dedup(zio_crypt_key_t *key, uint8_t *data, + uint_t datalen, uint8_t *ivbuf, uint8_t *salt) +{ + int ret; + crypto_mechanism_t mech; + crypto_data_t in_data, digest_data; + uint8_t digestbuf[SHA_256_DIGEST_LEN]; + + /* initialize sha256-hmac mechanism and crypto data */ + mech.cm_type = crypto_mech2id(SUN_CKM_SHA256_HMAC); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + /* initialize the crypto data */ + in_data.cd_format = CRYPTO_DATA_RAW; + in_data.cd_offset = 0; + in_data.cd_length = datalen; + in_data.cd_raw.iov_base = (char *)data; + in_data.cd_raw.iov_len = datalen; + + digest_data.cd_format = CRYPTO_DATA_RAW; + digest_data.cd_offset = 0; + digest_data.cd_length = SHA_256_DIGEST_LEN; + digest_data.cd_raw.iov_base = (char *)digestbuf; + digest_data.cd_raw.iov_len = SHA_256_DIGEST_LEN; + + /* generate the hmac */ + ret = crypto_mac(&mech, &in_data, &key->zk_hmac_key, key->zk_hmac_tmpl, + &digest_data, NULL); + if (ret != CRYPTO_SUCCESS) { + ret = SET_ERROR(EIO); + goto error; + } + + /* truncate and copy the digest into the output buffer */ + bcopy(digestbuf, salt, DATA_SALT_LEN); + bcopy(digestbuf + DATA_SALT_LEN, ivbuf, DATA_IV_LEN); + + return (0); + +error: + return (ret); +} + +void +zio_crypt_encode_params_bp(blkptr_t *bp, uint8_t *salt, uint8_t *iv) +{ + uint32_t *iv2 = (uint32_t *)(iv + sizeof (uint64_t)); + + ASSERT(BP_IS_ENCRYPTED(bp)); + bp->blk_dva[2].dva_word[0] = LE_64(*((uint64_t *)salt)); + + bp->blk_dva[2].dva_word[1] = LE_64(*((uint64_t *)iv)); + BP_SET_IV2(bp, LE_32(*iv2)); +} + +void +zio_crypt_decode_params_bp(const blkptr_t *bp, uint8_t *salt, uint8_t *iv) +{ + uint32_t *iv2 = (uint32_t *)(iv + sizeof (uint64_t)); + + ASSERT(BP_IS_ENCRYPTED(bp)); + *((uint64_t *)salt) = LE_64(bp->blk_dva[2].dva_word[0]); + + *((uint64_t *)iv) = LE_64(bp->blk_dva[2].dva_word[1]); + *((uint32_t *)iv2) = LE_32((uint32_t)BP_GET_IV2(bp)); +} + +void +zio_crypt_encode_mac_bp(blkptr_t *bp, uint8_t *mac) +{ + ASSERT(BP_IS_ENCRYPTED(bp)); + bp->blk_cksum.zc_word[2] = LE_64(((uint64_t *)mac)[0]); + bp->blk_cksum.zc_word[3] = LE_64(((uint64_t *)mac)[1]); +} + +void +zio_crypt_decode_mac_bp(const blkptr_t *bp, uint8_t *mac) +{ + ASSERT(BP_IS_ENCRYPTED(bp)); + ((uint64_t *)mac)[0] = LE_64(bp->blk_cksum.zc_word[2]); + ((uint64_t *)mac)[1] = LE_64(bp->blk_cksum.zc_word[3]); +} + +void +zio_crypt_encode_mac_zil(const void *data, uint8_t *mac) +{ + uint64_t *zc_mac = &((zil_chain_t *)data)->zc_mac; + *zc_mac = LE_64(*((uint64_t *)mac)); +} + +void +zio_crypt_decode_mac_zil(const void *data, uint8_t *mac) +{ + uint64_t *zc_mac = &((zil_chain_t *)data)->zc_mac; + *((uint64_t *)mac) = LE_64(*zc_mac); +} + +/* + * This function is modeled off of zio_crypt_init_uios_dnode(). This function, + * however, copies bonus buffers instead of parsing them into a uio_t. + */ +void +zio_crypt_copy_dnode_bonus(abd_t *src_abd, uint8_t *dst, uint_t datalen) +{ + uint_t i, crypt_len, max_dnp = datalen >> DNODE_SHIFT; + uint8_t *src, *bonus, *bonus_end, *dn_end; + dnode_phys_t *dnp, *sdnp, *ddnp; + + src = abd_borrow_buf_copy(src_abd, datalen); + + sdnp = (dnode_phys_t *)src; + ddnp = (dnode_phys_t *)dst; + + for (i = 0; i < max_dnp; i += sdnp[i].dn_extra_slots + 1) { + dnp = &sdnp[i]; + dn_end = (uint8_t *)(dnp + (dnp->dn_extra_slots + 1)); + if (dnp->dn_type != DMU_OT_NONE && + DMU_OT_IS_ENCRYPTED(dnp->dn_bonustype) && + dnp->dn_bonuslen != 0) { + bonus = (uint8_t *)DN_BONUS(dnp); + if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { + bonus_end = (uint8_t *)DN_SPILL_BLKPTR(dnp); + } else { + bonus_end = (uint8_t *)dn_end; + } + crypt_len = bonus_end - bonus; + bcopy(bonus, DN_BONUS(&ddnp[i]), crypt_len); + } + } + + abd_return_buf(src_abd, src, datalen); +} + +/* + * ZIL blocks are rewritten as new log entries are synced to + * disk. We generated the IV randomly when we allocated the + * block, but we cannot reuse this each time we do a rewrite. + * To combat this we add in zc_nused from the zil_chain_t. We + * only need the IV to be unique for each, not securely random + * so it is ok for us to just add it into the existing value. + */ +void +zio_crypt_derive_zil_iv(const void *data, uint8_t *iv, uint8_t *iv_out) +{ + uint64_t *iv_word; + zil_chain_t *zc = (zil_chain_t *)data; + + bcopy(iv, iv_out, DATA_IV_LEN); + iv_word = (uint64_t *)iv_out; + *iv_word = LE_64(LE_64(*iv_word) + zc->zc_nused); +} + +static void +zio_crypt_destroy_uio(uio_t *uio) +{ + if (uio->uio_iov) + kmem_free(uio->uio_iov, uio->uio_iovcnt * sizeof (iovec_t)); +} + +/* + * We do not check for the older zil chain because this feature was not + * available before the newer zil chain was introduced. The goal here + * is to encrypt everything except the blkptr_t of a lr_write_t and + * the zil_chain_t header. + */ +static int +zio_crypt_init_uios_zil(boolean_t encrypt, uint8_t *plainbuf, + uint8_t *cipherbuf, uint_t datalen, uio_t *puio, uio_t *cuio, + uint_t *enc_len) +{ + int ret; + uint_t nr_src, nr_dst, lr_len, crypt_len, nr_iovecs = 0, total_len = 0; + iovec_t *src_iovecs = NULL, *dst_iovecs = NULL; + uint8_t *src, *dst, *slrp, *dlrp, *end; + zil_chain_t *zilc; + lr_t *lr; + + /* if we are decrypting, the plainbuffer needs an extra iovec */ + if (encrypt) { + src = plainbuf; + dst = cipherbuf; + nr_src = 0; + nr_dst = 1; + } else { + src = cipherbuf; + dst = plainbuf; + nr_src = 1; + nr_dst = 0; + } + + /* find the start and end record of the log block */ + zilc = (zil_chain_t *)src; + end = src + zilc->zc_nused; + slrp = src + sizeof (zil_chain_t); + + /* calculate the number of encrypted iovecs we will need */ + for (; slrp < end; slrp += lr_len) { + lr = (lr_t *)slrp; + lr_len = lr->lrc_reclen; + + nr_iovecs++; + if (lr->lrc_txtype == TX_WRITE && + lr_len != sizeof (lr_write_t)) + nr_iovecs++; + } + + if (nr_iovecs == 0) { + *enc_len = 0; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ZIO_NO_ENCRYPTION_NEEDED); + } + + nr_src += nr_iovecs; + nr_dst += nr_iovecs; + + /* allocate the iovec arrays */ + src_iovecs = kmem_alloc(nr_src * sizeof (iovec_t), KM_SLEEP); + if (!src_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + dst_iovecs = kmem_alloc(nr_dst * sizeof (iovec_t), KM_SLEEP); + if (!dst_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + /* loop over records again, filling in iovecs */ + nr_iovecs = 0; + slrp = src + sizeof (zil_chain_t); + dlrp = dst + sizeof (zil_chain_t); + + for (; slrp < end; slrp += lr_len, dlrp += lr_len) { + lr = (lr_t *)slrp; + lr_len = lr->lrc_reclen; + + if (lr->lrc_txtype == TX_WRITE) { + bcopy(slrp, dlrp, sizeof (lr_t)); + crypt_len = sizeof (lr_write_t) - + sizeof (lr_t) - sizeof (blkptr_t); + src_iovecs[nr_iovecs].iov_base = slrp + sizeof (lr_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = dlrp + sizeof (lr_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + + /* copy the bp now since it will not be encrypted */ + bcopy(slrp + sizeof (lr_write_t) - sizeof (blkptr_t), + dlrp + sizeof (lr_write_t) - sizeof (blkptr_t), + sizeof (blkptr_t)); + nr_iovecs++; + total_len += crypt_len; + + if (lr_len != sizeof (lr_write_t)) { + crypt_len = lr_len - sizeof (lr_write_t); + src_iovecs[nr_iovecs].iov_base = + slrp + sizeof (lr_write_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = + dlrp + sizeof (lr_write_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + nr_iovecs++; + total_len += crypt_len; + } + } else { + bcopy(slrp, dlrp, sizeof (lr_t)); + crypt_len = lr_len - sizeof (lr_t); + src_iovecs[nr_iovecs].iov_base = slrp + sizeof (lr_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = dlrp + sizeof (lr_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + nr_iovecs++; + total_len += crypt_len; + } + } + + /* copy the plain zil header over */ + bcopy(src, dst, sizeof (zil_chain_t)); + + *enc_len = total_len; + + if (encrypt) { + puio->uio_iov = src_iovecs; + puio->uio_iovcnt = nr_src; + cuio->uio_iov = dst_iovecs; + cuio->uio_iovcnt = nr_dst; + } else { + puio->uio_iov = dst_iovecs; + puio->uio_iovcnt = nr_dst; + cuio->uio_iov = src_iovecs; + cuio->uio_iovcnt = nr_src; + } + + return (0); + +error: + if (src_iovecs) + kmem_free(src_iovecs, nr_src * sizeof (iovec_t)); + if (dst_iovecs) + kmem_free(dst_iovecs, nr_dst * sizeof (iovec_t)); + + *enc_len = 0; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ret); +} + +static int +zio_crypt_init_uios_dnode(boolean_t encrypt, uint8_t *plainbuf, + uint8_t *cipherbuf, uint_t datalen, uio_t *puio, uio_t *cuio, + uint_t *enc_len) +{ + int ret; + uint_t nr_src, nr_dst, crypt_len, total_len = 0, nr_iovecs = 0; + uint_t i, max_dnp = datalen >> DNODE_SHIFT; + iovec_t *src_iovecs = NULL, *dst_iovecs = NULL; + uint8_t *src, *dst, *bonus, *bonus_end, *dn_end; + dnode_phys_t *dnp, *sdnp, *ddnp; + + if (encrypt) { + src = plainbuf; + dst = cipherbuf; + nr_src = 0; + nr_dst = 1; + } else { + src = cipherbuf; + dst = plainbuf; + nr_src = 1; + nr_dst = 0; + } + + sdnp = (dnode_phys_t *)src; + ddnp = (dnode_phys_t *)dst; + + for (i = 0; i < max_dnp; i += sdnp[i].dn_extra_slots + 1) { + if (sdnp[i].dn_type != DMU_OT_NONE && + DMU_OT_IS_ENCRYPTED(sdnp[i].dn_bonustype) && + sdnp[i].dn_bonuslen != 0) { + nr_iovecs++; + } + } + + if (nr_iovecs == 0) { + *enc_len = 0; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ZIO_NO_ENCRYPTION_NEEDED); + } + + nr_src += nr_iovecs; + nr_dst += nr_iovecs; + + src_iovecs = kmem_alloc(nr_src * sizeof (iovec_t), KM_SLEEP); + if (!src_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + dst_iovecs = kmem_alloc(nr_dst * sizeof (iovec_t), KM_SLEEP); + if (!dst_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + nr_iovecs = 0; + + for (i = 0; i < max_dnp; i += sdnp[i].dn_extra_slots + 1) { + dnp = &sdnp[i]; + dn_end = (uint8_t *)(dnp + (dnp->dn_extra_slots + 1)); + if (dnp->dn_type != DMU_OT_NONE && + DMU_OT_IS_ENCRYPTED(dnp->dn_bonustype) && + dnp->dn_bonuslen != 0) { + bonus = (uint8_t *)DN_BONUS(dnp); + if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) { + bonus_end = (uint8_t *)DN_SPILL_BLKPTR(dnp); + } else { + bonus_end = (uint8_t *)dn_end; + } + crypt_len = bonus_end - bonus; + + bcopy(dnp, &ddnp[i], bonus - (uint8_t *)dnp); + src_iovecs[nr_iovecs].iov_base = bonus; + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = DN_BONUS(&ddnp[i]); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + + if (dnp->dn_flags & DNODE_FLAG_SPILL_BLKPTR) + bcopy(bonus_end, DN_SPILL_BLKPTR(&ddnp[i]), + sizeof (blkptr_t)); + + nr_iovecs++; + total_len += crypt_len; + } else { + bcopy(dnp, &ddnp[i], dn_end - (uint8_t *)dnp); + } + } + + *enc_len = total_len; + + if (encrypt) { + puio->uio_iov = src_iovecs; + puio->uio_iovcnt = nr_src; + cuio->uio_iov = dst_iovecs; + cuio->uio_iovcnt = nr_dst; + } else { + puio->uio_iov = dst_iovecs; + puio->uio_iovcnt = nr_dst; + cuio->uio_iov = src_iovecs; + cuio->uio_iovcnt = nr_src; + } + + return (0); + +error: + if (src_iovecs) + kmem_free(src_iovecs, nr_src * sizeof (iovec_t)); + if (dst_iovecs) + kmem_free(dst_iovecs, nr_dst * sizeof (iovec_t)); + + *enc_len = 0; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ret); +} + +static int +zio_crypt_init_uios_normal(boolean_t encrypt, uint8_t *plainbuf, + uint8_t *cipherbuf, uint_t datalen, uio_t *puio, uio_t *cuio, + uint_t *enc_len) +{ + int ret; + uint_t nr_plain = 1, nr_cipher = 2; + iovec_t *plain_iovecs = NULL, *cipher_iovecs = NULL; + + /* allocate the iovecs for the plain and cipher data */ + plain_iovecs = kmem_alloc(nr_plain * sizeof (iovec_t), + KM_SLEEP); + if (!plain_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + cipher_iovecs = kmem_alloc(nr_cipher * sizeof (iovec_t), + KM_SLEEP); + if (!cipher_iovecs) { + ret = SET_ERROR(ENOMEM); + goto error; + } + + plain_iovecs[0].iov_base = plainbuf; + plain_iovecs[0].iov_len = datalen; + cipher_iovecs[0].iov_base = cipherbuf; + cipher_iovecs[0].iov_len = datalen; + + *enc_len = datalen; + puio->uio_iov = plain_iovecs; + puio->uio_iovcnt = nr_plain; + cuio->uio_iov = cipher_iovecs; + cuio->uio_iovcnt = nr_cipher; + + return (0); + +error: + if (plain_iovecs) + kmem_free(plain_iovecs, nr_plain * sizeof (iovec_t)); + if (cipher_iovecs) + kmem_free(cipher_iovecs, nr_cipher * sizeof (iovec_t)); + + *enc_len = 0; + puio->uio_iov = NULL; + puio->uio_iovcnt = 0; + cuio->uio_iov = NULL; + cuio->uio_iovcnt = 0; + return (ret); +} + +static int +zio_crypt_init_uios(boolean_t encrypt, dmu_object_type_t ot, uint8_t *plainbuf, + uint8_t *cipherbuf, uint_t datalen, uint8_t *mac, uio_t *puio, uio_t *cuio, + uint_t *enc_len) +{ + int ret; + uint_t maclen; + iovec_t *mac_iov; + + ASSERT(DMU_OT_IS_ENCRYPTED(ot) || ot == DMU_OT_NONE); + + /* route to handler */ + switch (ot) { + case DMU_OT_INTENT_LOG: + ret = zio_crypt_init_uios_zil(encrypt, plainbuf, cipherbuf, + datalen, puio, cuio, enc_len); + maclen = ZIL_MAC_LEN; + break; + case DMU_OT_DNODE: + ret = zio_crypt_init_uios_dnode(encrypt, plainbuf, cipherbuf, + datalen, puio, cuio, enc_len); + maclen = DATA_MAC_LEN; + break; + default: + ret = zio_crypt_init_uios_normal(encrypt, plainbuf, cipherbuf, + datalen, puio, cuio, enc_len); + maclen = DATA_MAC_LEN; + break; + } + + /* return the error or ZIO_NO_ENCRYPTION_NEEDED to the caller */ + if (ret) + goto error; + + /* populate the uios */ + puio->uio_segflg = UIO_SYSSPACE; + cuio->uio_segflg = UIO_SYSSPACE; + + mac_iov = ((iovec_t *)&cuio->uio_iov[cuio->uio_iovcnt - 1]); + mac_iov->iov_base = mac; + mac_iov->iov_len = maclen; + + return (0); + +error: + return (ret); +} + +/* + * Primary encryption / decryption entrypoint for zio data. + */ +int +zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, + dmu_object_type_t ot, uint8_t *iv, uint8_t *mac, uint_t datalen, + uint8_t *plainbuf, uint8_t *cipherbuf) +{ + int ret; + boolean_t locked = B_FALSE; + uint64_t crypt = key->zk_crypt; + uint_t enc_len, keydata_len = zio_crypt_table[crypt].ci_keylen; + uio_t puio, cuio; + uint8_t enc_keydata[MAX_MASTER_KEY_LEN]; + crypto_key_t tmp_ckey, *ckey = NULL; + crypto_ctx_template_t tmpl; + + bzero(&puio, sizeof (uio_t)); + bzero(&cuio, sizeof (uio_t)); + + /* create uios for encryption */ + ret = zio_crypt_init_uios(encrypt, ot, plainbuf, cipherbuf, datalen, + mac, &puio, &cuio, &enc_len); + + /* return the error or ZIO_NO_ENCRYPTION_NEEDED to the caller */ + if (ret) + return (ret); + + /* + * If the needed key is the current one, just use it. Otherwise we + * need to generate a temporary one from the given salt + master key. + * If we are encrypting, we must return a copy of the current salt + * so that it can be stored in the blkptr_t. + */ + rw_enter(&key->zk_salt_lock, RW_READER); + locked = B_TRUE; + + if (bcmp(salt, key->zk_salt, DATA_SALT_LEN) == 0) { + ckey = &key->zk_current_key; + tmpl = key->zk_current_tmpl; + } else { + rw_exit(&key->zk_salt_lock); + locked = B_FALSE; + + ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, + salt, DATA_SALT_LEN, enc_keydata, keydata_len); + if (ret) + goto error; + + tmp_ckey.ck_format = CRYPTO_KEY_RAW; + tmp_ckey.ck_data = enc_keydata; + tmp_ckey.ck_length = BYTES_TO_BITS(keydata_len); + + ckey = &tmp_ckey; + tmpl = NULL; + } + + /* perform the encryption / decryption */ + ret = zio_do_crypt_uio(encrypt, key->zk_crypt, ckey, tmpl, iv, enc_len, + &puio, &cuio); + if (ret) + goto error; + + if (locked) { + rw_exit(&key->zk_salt_lock); + locked = B_FALSE; + } + + if (ckey == &tmp_ckey) + bzero(enc_keydata, keydata_len); + zio_crypt_destroy_uio(&puio); + zio_crypt_destroy_uio(&cuio); + + return (0); + +error: + if (locked) + rw_exit(&key->zk_salt_lock); + if (ckey == &tmp_ckey) + bzero(enc_keydata, keydata_len); + zio_crypt_destroy_uio(&puio); + zio_crypt_destroy_uio(&cuio); + + return (ret); +} + +/* + * Simple wrapper around zio_do_crypt_data() to work with abd's instead of + * linear buffers. + */ +int +zio_do_crypt_abd(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, + dmu_object_type_t ot, uint8_t *iv, uint8_t *mac, uint_t datalen, + abd_t *pabd, abd_t *cabd) +{ + int ret; + void *ptmp, *ctmp; + + if (encrypt) { + ptmp = abd_borrow_buf_copy(pabd, datalen); + ctmp = abd_borrow_buf(cabd, datalen); + } else { + ptmp = abd_borrow_buf(pabd, datalen); + ctmp = abd_borrow_buf_copy(cabd, datalen); + } + + ret = zio_do_crypt_data(encrypt, key, salt, ot, iv, mac, + datalen, ptmp, ctmp); + if (ret) + goto error; + + if (encrypt) { + abd_return_buf(pabd, ptmp, datalen); + abd_return_buf_copy(cabd, ctmp, datalen); + } else { + abd_return_buf_copy(pabd, ptmp, datalen); + abd_return_buf(cabd, ctmp, datalen); + } + + return (0); + +error: + abd_return_buf(pabd, ptmp, datalen); + abd_return_buf(cabd, ctmp, datalen); + return (ret); +} diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 10c963ca5956..7d8299d30f7a 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -364,7 +364,7 @@ zvol_set_volsize(const char *name, uint64_t volsize) zv = zvol_find_by_name(name); if (zv == NULL || zv->zv_objset == NULL) { - if ((error = dmu_objset_own(name, DMU_OST_ZVOL, B_FALSE, + if ((error = dmu_objset_own(name, DMU_OST_ZVOL, B_FALSE, B_TRUE, FTAG, &os)) != 0) { mutex_exit(&zvol_state_lock); return (SET_ERROR(error)); @@ -956,7 +956,7 @@ zvol_first_open(zvol_state_t *zv) uint64_t ro; /* lie and say we're read-only */ - error = dmu_objset_own(zv->zv_name, DMU_OST_ZVOL, 1, zvol_tag, &os); + error = dmu_objset_own(zv->zv_name, DMU_OST_ZVOL, 1, 1, zvol_tag, &os); if (error) return (SET_ERROR(-error)); @@ -1380,7 +1380,8 @@ zvol_create_minor_impl(const char *name) doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP); - error = dmu_objset_own(name, DMU_OST_ZVOL, B_TRUE, zvol_tag, &os); + error = dmu_objset_own(name, DMU_OST_ZVOL, B_TRUE, B_TRUE, + zvol_tag, &os); if (error) goto out_doi; @@ -1435,10 +1436,12 @@ zvol_create_minor_impl(const char *name) * When udev detects the addition of the device it will immediately * invoke blkid(8) to determine the type of content on the device. * Prefetching the blocks commonly scanned by blkid(8) will speed - * up this process. + * up this process. We cannot do this optimization for encrypted blocks + * because we are about to disown the objset, which will release the + * dsl_key_mapping_t. */ len = MIN(MAX(zvol_prefetch_bytes, 0), SPA_MAXBLOCKSIZE); - if (len > 0) { + if (len > 0 && !os->os_encrypted) { dmu_prefetch(os, ZVOL_OBJ, 0, 0, len, ZIO_PRIORITY_SYNC_READ); dmu_prefetch(os, ZVOL_OBJ, 0, volsize - len, len, ZIO_PRIORITY_SYNC_READ); @@ -1512,10 +1515,17 @@ zvol_prefetch_minors_impl(void *arg) char *dsname = job->name; objset_t *os = NULL; - job->error = dmu_objset_own(dsname, DMU_OST_ZVOL, B_TRUE, zvol_tag, - &os); + job->error = dmu_objset_own(dsname, DMU_OST_ZVOL, B_TRUE, B_TRUE, + zvol_tag, &os); if (job->error == 0) { - dmu_prefetch(os, ZVOL_OBJ, 0, 0, 0, ZIO_PRIORITY_SYNC_READ); + /* + * Encrypted volumes cannot be prefetched if ownership will + * be dropped. + */ + if (!os->os_encrypted) { + dmu_prefetch(os, ZVOL_OBJ, 0, 0, 0, + ZIO_PRIORITY_SYNC_READ); + } dmu_objset_disown(os, zvol_tag); } } diff --git a/module/zpios/pios.c b/module/zpios/pios.c index 297d35bba949..cc288df7673f 100644 --- a/module/zpios/pios.c +++ b/module/zpios/pios.c @@ -210,14 +210,14 @@ zpios_dmu_setup(run_args_t *run_args) t->start = zpios_timespec_now(); (void) snprintf(name, 32, "%s/id_%d", run_args->pool, run_args->id); - rc = dmu_objset_create(name, DMU_OST_OTHER, 0, NULL, NULL); + rc = dmu_objset_create(name, DMU_OST_OTHER, 0, NULL, NULL, NULL); if (rc) { zpios_print(run_args->file, "Error dmu_objset_create(%s, ...) " "failed: %d\n", name, rc); goto out; } - rc = dmu_objset_own(name, DMU_OST_OTHER, 0, zpios_tag, &os); + rc = dmu_objset_own(name, DMU_OST_OTHER, 0, 1, zpios_tag, &os); if (rc) { zpios_print(run_args->file, "Error dmu_objset_own(%s, ...) " "failed: %d\n", name, rc); diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 49e6a47d328e..67ce63a8da90 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -98,7 +98,7 @@ tests = ['zfs_create_001_pos', 'zfs_create_002_pos', 'zfs_create_003_pos', 'zfs_create_004_pos', 'zfs_create_005_pos', 'zfs_create_006_pos', 'zfs_create_007_pos', 'zfs_create_008_neg', 'zfs_create_009_neg', 'zfs_create_010_neg', 'zfs_create_011_pos', 'zfs_create_012_pos', - 'zfs_create_013_pos', 'zfs_create_014_pos'] + 'zfs_create_013_pos', 'zfs_create_014_pos', 'zfs_create_encrypted'] # DISABLED: # zfs_destroy_005_neg - busy mountpoint behavior @@ -120,6 +120,10 @@ tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos', [tests/functional/cli_root/zfs_inherit] tests = ['zfs_inherit_001_neg', 'zfs_inherit_002_neg', 'zfs_inherit_003_pos'] +[tests/functional/cli_root/zfs_key] +tests = ['zfs_key_unload_pos', 'zfs_key_unload_neg', 'zfs_key_load_pos', + 'zfs_key_load_neg', 'zfs_key_change_pos', 'zfs_key_change_neg'] + # DISABLED: # zfs_mount_006_pos - needs investigation # zfs_mount_007_pos - needs investigation @@ -128,7 +132,7 @@ tests = ['zfs_inherit_001_neg', 'zfs_inherit_002_neg', 'zfs_inherit_003_pos'] [tests/functional/cli_root/zfs_mount] tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos', 'zfs_mount_004_pos', 'zfs_mount_005_pos', 'zfs_mount_008_pos', - 'zfs_mount_010_neg', 'zfs_mount_011_neg'] + 'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_encrypted'] [tests/functional/cli_root/zfs_promote] tests = ['zfs_promote_001_pos', 'zfs_promote_002_pos', 'zfs_promote_003_pos', @@ -147,7 +151,8 @@ tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos', 'zfs_receive_005_neg', 'zfs_receive_006_pos', 'zfs_receive_007_neg', 'zfs_receive_008_pos', 'zfs_receive_009_neg', 'zfs_receive_010_pos', 'zfs_receive_011_pos', 'zfs_receive_012_pos', - 'zfs_receive_013_pos', 'zfs_receive_014_pos'] + 'zfs_receive_013_pos', 'zfs_receive_014_pos', 'zfs_receive_encrypted_pos', + 'zfs_receive_encrypted_neg'] [tests/functional/cli_root/zfs_rename] tests = ['zfs_rename_001_pos', 'zfs_rename_002_pos', 'zfs_rename_003_pos', @@ -168,7 +173,7 @@ tests = ['zfs_rollback_003_neg', 'zfs_rollback_004_neg'] [tests/functional/cli_root/zfs_send] tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos', 'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos', - 'zfs_send_007_pos'] + 'zfs_send_007_pos', 'zfs_send_encrypted_pos', 'zfs_send_encrypted_neg'] [tests/functional/cli_root/zfs_set] tests = ['cache_001_pos', 'cache_002_neg', 'canmount_001_pos', @@ -251,7 +256,7 @@ tests = [ 'zpool_create_009_neg', 'zpool_create_010_neg', 'zpool_create_017_neg', 'zpool_create_018_pos', 'zpool_create_019_pos', 'zpool_create_021_pos', 'zpool_create_022_pos', 'zpool_create_023_neg', - 'zpool_create_024_pos', + 'zpool_create_024_pos', 'zpool_create_encrypted', 'zpool_create_features_001_pos', 'zpool_create_features_002_pos', 'zpool_create_features_003_pos', 'zpool_create_features_004_neg', 'zpool_create_features_005_pos'] diff --git a/tests/zfs-tests/tests/functional/cli_root/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/Makefile.am index 5dc72de16be7..1df08ff0a7c8 100644 --- a/tests/zfs-tests/tests/functional/cli_root/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/Makefile.am @@ -11,6 +11,7 @@ SUBDIRS = \ zfs_destroy \ zfs_get \ zfs_inherit \ + zfs_key \ zfs_mount \ zfs_promote \ zfs_property \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am index 998c7dca5fb6..310e2d9853cc 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am @@ -18,4 +18,5 @@ dist_pkgdata_SCRIPTS = \ zfs_create_011_pos.ksh \ zfs_create_012_pos.ksh \ zfs_create_013_pos.ksh \ - zfs_create_014_pos.ksh + zfs_create_014_pos.ksh \ + zfs_create_encrypted.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh new file mode 100644 index 000000000000..ec5a02147151 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh @@ -0,0 +1,121 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib +. $STF_SUITE/tests/functional/cli_root/zfs_create/properties.kshlib + +# +# DESCRIPTION: +# 'zfs create' should be able to create an encrypted dataset with +# a valid encryption algorithm, keysource, and key. +# +# STRATEGY: +# 1. Create a filesystem for each encryption type +# 2. Create a filesystem for each keysource type +# 3. Verify that each filesystem has the correct properties set +# + +verify_runnable "both" + +function cleanup +{ + datasetexists $TESTPOOL/$TESTFS1 && \ + log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 +} + +log_onexit cleanup + +set -A ENCRYPTION_ALGS "encryption=on" \ + "encryption=aes-128-ccm" \ + "encryption=aes-192-ccm" \ + "encryption=aes-256-ccm" \ + "encryption=aes-128-gcm" \ + "encryption=aes-192-gcm" \ + "encryption=aes-256-gcm" + +set -A ENCRYPTION_PROPS "encryption=aes-256-ccm" \ + "encryption=aes-128-ccm" \ + "encryption=aes-192-ccm" \ + "encryption=aes-256-ccm" \ + "encryption=aes-128-gcm" \ + "encryption=aes-192-gcm" \ + "encryption=aes-256-gcm" + +set -A KEYSOURCE_TYPES "keysource=raw,prompt" \ + "keysource=hex,prompt" \ + "keysource=passphrase,prompt" + +set -A KEYSOURCES "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ + "abcdefgh" + +log_assert "'zfs create' should properly create encrypted datasets" + +typeset -i i=0 +while (( $i < ${#ENCRYPTION_ALGS[*]} )); do + log_must eval 'echo ${KEYSOURCES[0]} | \ + $ZFS create -o ${ENCRYPTION_ALGS[$i]} -o ${KEYSOURCE_TYPES[0]} \ + $TESTPOOL/$TESTFS1' + + datasetexists $TESTPOOL/$TESTFS1 || \ + log_fail "zfs create -o ${ENCRYPTION_ALGS[$i]} \ + -o ${KEYSOURCE_TYPES[0]} $TESTPOOL/$TESTFS1 fail." + + propertycheck $TESTPOOL/$TESTFS1 ${ENCRYPTION_PROPS[i]} || \ + log_fail "${ENCRYPTION_ALGS[i]} is failed to set." + propertycheck $TESTPOOL/$TESTFS1 ${KEYSOURCE_TYPES[0]} || \ + log_fail "${KEYSOURCE_TYPES[0]} is failed to set." + + log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 + (( i = i + 1 )) +done + +typeset -i j=0 +while (( $j < ${#KEYSOURCE_TYPES[*]} )); do + log_must eval 'echo ${KEYSOURCES[$j]} | \ + $ZFS create -o ${ENCRYPTION_ALGS[0]} -o ${KEYSOURCE_TYPES[$j]} \ + $TESTPOOL/$TESTFS1' + + datasetexists $TESTPOOL/$TESTFS1 || \ + log_fail "zfs create -o ${ENCRYPTION_ALGS[0]} \ + -o ${KEYSOURCE_TYPES[$j]} $TESTPOOL/$TESTFS1 fail." + + propertycheck $TESTPOOL/$TESTFS1 ${ENCRYPTION_PROPS[0]} || \ + log_fail "${ENCRYPTION_ALGS[0]} is failed to set." + propertycheck $TESTPOOL/$TESTFS1 ${KEYSOURCE_TYPES[j]} || \ + log_fail "${KEYSOURCE_TYPES[j]} is failed to set." + + log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 + (( j = j + 1 )) +done + +log_pass "'zfs create properly creates encrypted datasets" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_key/Makefile.am new file mode 100644 index 000000000000..4305adaefbf9 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/Makefile.am @@ -0,0 +1,12 @@ +pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cli_root/zfs_key +dist_pkgdata_SCRIPTS = \ + key.cfg \ + zfs_key_common.kshlib \ + setup.ksh \ + cleanup.ksh \ + zfs_key_unload_pos.ksh \ + zfs_key_unload_neg.ksh \ + zfs_key_load_pos.ksh \ + zfs_key_load_neg.ksh \ + zfs_key_change_pos.ksh \ + zfs_key_change_neg.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/cleanup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/cleanup.ksh new file mode 100644 index 000000000000..f6831770b1ff --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/cleanup.ksh @@ -0,0 +1,34 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +default_cleanup diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/key.cfg b/tests/zfs-tests/tests/functional/cli_root/zfs_key/key.cfg new file mode 100644 index 000000000000..2391f6641180 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/key.cfg @@ -0,0 +1,36 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +export PKEY="abcdefgh" +export BAD_PKEY="abcdefgz" +export SHORT_PKEY="abcd" +export HKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +export CRYPTDS="crypt" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/setup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/setup.ksh new file mode 100644 index 000000000000..ef0b6a818209 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/setup.ksh @@ -0,0 +1,36 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +DISK=${DISKS%% *} + +default_setup $DISK diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh new file mode 100644 index 000000000000..fae09d4f578c --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh @@ -0,0 +1,81 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs key -c' should not be able to change an unloaded +# wrapping key or allow a change to an invalid key +# +# STRATEGY: +# 1. Create an encrypted dataset and unmount it +# 2. Attempt to change the wrapping key to an invalid key +# 3. Verify that the old key still works for the encrypted system +# 4. Unload the wrapping key +# 5. Attempt to change the key and keysource to a valid key +# 6. Verify that the old key still works for the encrypted system +# 7. Attempt to change the key and keysource on an unencrypted filesystem +# + +verify_runnable "both" + +function cleanup +{ + destroy_default_encrypted_dataset +} + +log_onexit cleanup + +log_assert "'zfs key -c' should not change an unloaded wrapping key \ + or allow a change to an invalid key" + +create_default_encrypted_dataset +log_must $ZFS unmount $TESTPOOL/$CRYPTDS + +log_mustnot eval '$ECHO $SHORT_PKEY | \ + $ZFS key -c -o keysource=passphrase,prompt $TESTPOOL/$CRYPTDS' + +log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_must eval '$ECHO $PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +check_key_available $TESTPOOL/$CRYPTDS + +log_must $ZFS key -u $TESTPOOL/$CRYPTDS + +log_mustnot eval '$ECHO $HKEY | $ZFS key -c -o keysource=hex,prompt \ + $TESTPOOL/$CRYPTDS' +check_key_unavailable $TESTPOOL/$CRYPTDS + +log_mustnot eval '$ECHO $PKEY | $ZFS key -c -o keysource=passphrase,prompt \ + $TESTPOOL' + +log_pass "'zfs key -c' does not change an unloaded wrapping key \ + or allow a change to an invalid key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh new file mode 100644 index 000000000000..7a5dda0b7846 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh @@ -0,0 +1,73 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs key -c' should be able to change one loaded, valid +# wrapping key to another. +# +# STRATEGY: +# 1. Create an encrypted dataset and unmount it +# 2. Attempt to change the key and keysource +# 3. Verify that the old key cannot be loaded +# 4. Verify that the new can be loaded +# + +verify_runnable "both" + +function cleanup +{ + destroy_default_encrypted_dataset +} + +log_onexit cleanup + +log_assert "'zfs key -c' should properly change a valid wrapping key \ + to another" + +create_default_encrypted_dataset +log_must $ZFS unmount $TESTPOOL/$CRYPTDS + +log_must eval '$ECHO $HKEY | $ZFS key -c -o keysource=hex,prompt \ + $TESTPOOL/$CRYPTDS' + +check_key_available $TESTPOOL/$CRYPTDS + +log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_mustnot eval '$ECHO $PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +check_key_unavailable $TESTPOOL/$CRYPTDS + +log_must eval '$ECHO $HKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +check_key_available $TESTPOOL/$CRYPTDS + +log_pass "'zfs key -c' properly changes a valid wrapping key to another" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib new file mode 100644 index 000000000000..57c01838f7d2 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib @@ -0,0 +1,53 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/cli_root/zfs_key/key.cfg + +function create_default_encrypted_dataset +{ + log_must $ECHO $PKEY | $ZFS create -o encryption=on \ + -o keysource=passphrase,prompt $TESTPOOL/$CRYPTDS +} + +function destroy_default_encrypted_dataset +{ + log_must $ZFS destroy $TESTPOOL/$CRYPTDS +} + +function check_key_available +{ + typset dataset=$1 + log_must eval '$ZFS get keystatus $dataset | \ + $GREP "available" > /dev/null 2>&1' +} + +function check_key_unavailable +{ + typset dataset=$1 + log_must eval '$ZFS get keystatus $dataset | \ + $GREP "unavailable" > /dev/null 2>&1' +} diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh new file mode 100644 index 000000000000..8dd0e6728725 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh @@ -0,0 +1,62 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs key -l' should not load an invalid key into the ZFS keystore. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Unmount and unload the dataset's key +# 3. Attempt to load the key +# 4. Verify the key is loaded correctly +# + +verify_runnable "both" + +function cleanup +{ + destroy_default_encrypted_dataset +} + +log_onexit cleanup + +log_assert "'zfs key -l' should not load an invalid wrapping key" + +create_default_encrypted_dataset +log_must $ZFS unmount $TESTPOOL/$CRYPTDS +log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_mustnot eval '$ECHO $BAD_PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +check_key_unavailable $TESTPOOL/$CRYPTDS + +log_pass "'zfs key -l' does not load an invalid wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh new file mode 100644 index 000000000000..998d1ebc96b4 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh @@ -0,0 +1,62 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs key -l' should load a valid key into the ZFS keystore. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Unmount and unload the dataset's key +# 3. Attempt to load the key +# 4. Verify the key is loaded correctly +# + +verify_runnable "both" + +function cleanup +{ + destroy_default_encrypted_dataset +} + +log_onexit cleanup + +log_assert "'zfs key -l' should properly load a valid wrapping key" + +create_default_encrypted_dataset +log_must $ZFS unmount $TESTPOOL/$CRYPTDS +log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_must eval '$ECHO $PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +check_key_available $TESTPOOL/$CRYPTDS + +log_pass "'zfs key -l' properly loads a valid wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh new file mode 100644 index 000000000000..7c847a1cc35a --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh @@ -0,0 +1,67 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs key -u' should not unload a key from the ZFS keystore +# if the dataset is busy or not encrypted. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Attempt to unload the key +# 3. Verify the dataset was busy +# 4. Attempt to unload a key for a non encrypted dataset +# 5. Verify there was an error +# + +verify_runnable "both" + +function cleanup +{ + destroy_default_encrypted_dataset +} + +log_onexit cleanup + +log_assert "'zfs key -u' should not unload a wrapping key if the dataset \ + if the dataset is busy or not encrypted" + +create_default_encrypted_dataset + +log_mustnot $ZFS key -u $TESTPOOL/$CRYPTDS +check_key_available $TESTPOOL/$CRYPTDS + +log_mustnot $ZFS key -u $TESTPOOL + +log_pass "'zfs key -u' properly returns an error when unloading a wrapping \ + key from a busy or unencrypted dataset" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh new file mode 100644 index 000000000000..2cf29742277b --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh @@ -0,0 +1,61 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs key -u' will unload a key from the ZFS keystore. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Unmount the dataset +# 3. Unload the key +# 4. Verify the key is unloaded +# + +verify_runnable "both" + +function cleanup +{ + destroy_default_encrypted_dataset +} + +log_onexit cleanup + +log_assert "'zfs key -u' should properly unload a wrapping key" + +create_default_encrypted_dataset +log_must $ZFS unmount $TESTPOOL/$CRYPTDS +log_must $ZFS key -u $TESTPOOL/$CRYPTDS +check_key_unavailable $TESTPOOL/$CRYPTDS + +log_pass "'zfs key -u' properly unloads a wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile.am index c6ae99c6b095..1b968cb96e3d 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/Makefile.am @@ -15,4 +15,5 @@ dist_pkgdata_SCRIPTS = \ zfs_mount_009_neg.ksh \ zfs_mount_010_neg.ksh \ zfs_mount_011_neg.ksh \ + zfs_mount_encrypted.ksh \ zfs_mount_all_001_pos.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh new file mode 100644 index 000000000000..61d0a1358f14 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh @@ -0,0 +1,70 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib + +# +# DESCRIPTION: +# 'zfs mount' should accept a valid key as it mounts the filesystem. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Unmount and unload the dataset's key +# 3. Attempt to mount the dataset +# 4. Verify the key is loaded correctly +# + +verify_runnable "both" + +typeset CRYPTDS="cryptds" +typeset PASSKEY="abcdefgh" + +function cleanup +{ + datasetexists $TESTPOOL/$CRYPTDS && \ + log_must $ZFS destroy -f $TESTPOOL/$CRYPTDS +} + +log_onexit cleanup + +log_assert "'zfs mount' should properly load a valid wrapping key" + +log_must eval 'echo $PASSKEY | $ZFS create -o encryption=on \ + -o keysource=passphrase,prompt $TESTPOOL/$CRYPTDS' + +log_must $ZFS unmount $TESTPOOL/$CRYPTDS +log_must $ZFS key -u $TESTPOOL/$CRYPTDS + +log_must eval '$ECHO $PKEY | $ZFS mount $TESTPOOL/$CRYPTDS' +mounted $TESTPOOL/$CRYPTDS || \ + log_fail Filesystem $TESTPOOL/$TESTFS is unmounted + +log_pass "'zfs mount' properly loads a valid wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am index 564aab75698f..59ae0a4b8711 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am @@ -15,4 +15,6 @@ dist_pkgdata_SCRIPTS = \ zfs_receive_011_pos.ksh \ zfs_receive_012_pos.ksh \ zfs_receive_013_pos.ksh \ - zfs_receive_014_pos.ksh + zfs_receive_014_pos.ksh \ + zfs_receive_encrypted_pos.ksh \ + zfs_receive_encrypted_neg.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh new file mode 100644 index 000000000000..245772fae4ea --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh @@ -0,0 +1,74 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Verify 'zfs receive' fails when receiving to a dataset with unloaded keys. +# +# STRATEGY: +# 1. Create an unencrypted dataset and an encrypted dataset +# 2. Snapshot the unencrypted dataset +# 3. Unload the encrypted dataset's wrapping key +# 4. Verify that the receive operation fails without a loaded encryption key +# + +verify_runnable "both" + +function cleanup +{ + log_must $RM $streamfile + log_must $ZFS destroy -r $TESTPOOL/$TESTFS1 + log_must $ZFS destroy -r $TESTPOOL/$cryptds +} + +log_onexit cleanup + +log_assert "Verify 'zfs receive' fails when receiving to a dataset with \ + unloaded keys." + +typeset cryptds="crypt" +typeset passphrase="abcdefgh" +typeset streamfile=/var/tmp/streamfile.$$ + +log_must $ZFS create $TESTPOOL/$TESTFS1 +log_must $ZFS snapshot $TESTPOOL/$TESTFS1@snap +log_must eval "$ZFS send $TESTPOOL/$TESTFS1@snap > $streamfile" + +log_must eval "$ECHO $passphrase | \ + $ZFS create -o encryption=on -o keysource=passphrase,prompt \ + $TESTPOOL/$cryptds" +log_must $ZFS unmount $TESTPOOL/$cryptds +log_must $ZFS key -u $TESTPOOL/$cryptds +log_mustnot eval "$ZFS recv $TESTPOOL/$cryptds/recv < $streamfile" + +log_pass "'zfs receive' fails when receiving to a dataset with unloaded keys." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh new file mode 100644 index 000000000000..4268ac7700c8 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh @@ -0,0 +1,85 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Verify 'zfs receive' works with encrypted datasets. +# +# STRATEGY: +# 1. Create an unencrypted dataset with some data +# 2. Snapshot the unencrypted dataset +# 3. Create an encrypted dataset +# 4. Verify that the send stream is receivable as an encrypted child dataset +# 5. Verify that the data that was sent matches the original data +# + +verify_runnable "both" + +function cleanup +{ + log_must $RM $streamfile + log_must $ZFS destroy -r $TESTPOOL/$TESTFS1 + log_must $ZFS destroy -r $TESTPOOL/$cryptds +} + +log_onexit cleanup + +log_assert "Verify 'zfs receive' works with encrypted datasets." + +typeset cryptds="crypt" +typeset passphrase="abcdefgh" +typeset testfile="testfile" +typeset streamfile=/var/tmp/streamfile.$$ + +log_must $ZFS create $TESTPOOL/$TESTFS1 +send_mntpnt=$(get_prop mountpoint $TESTPOOL/$TESTFS1) || \ + log_fail "get_prop mountpoint $TESTPOOL/$TESTFS1" + +log_must $FILE_WRITE -o create -f $send_mntpnt/$testfile -b 4096 -c 5 -d 1 +log_must $ZFS snapshot $TESTPOOL/$TESTFS1@snap +log_must eval "$ZFS send $TESTPOOL/$TESTFS1@snap > $streamfile" + +log_must eval "$ECHO $passphrase | \ + $ZFS create -o encryption=on -o keysource=passphrase,prompt \ + $TESTPOOL/$cryptds" +log_must eval "$ZFS recv $TESTPOOL/$cryptds/recv < $streamfile" + +recv_mntpnt=$(get_prop mountpoint $TESTPOOL/$cryptds/recv) || \ + log_fail "get_prop mountpoint $TESTPOOL/$cryptds/recv" + +checksum1=$($SUM $send_mntpnt/$testfile | $AWK '{print $1}') +checksum2=$($SUM $recv_mntpnt/$testfile | $AWK '{print $1}') +[[ "$checksum1" != "$checksum2" ]] && \ + log_fail "Checksums differ ($checksum1 != $checksum2)" + +log_pass "'zfs receive' works with encrypted datasets." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_send/Makefile.am index 13faeab1d233..5e7b98b03ed3 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/Makefile.am @@ -9,4 +9,6 @@ dist_pkgdata_SCRIPTS = \ zfs_send_004_neg.ksh \ zfs_send_005_pos.ksh \ zfs_send_006_pos.ksh \ - zfs_send_007_pos.ksh + zfs_send_007_pos.ksh \ + zfs_send_encrypted_pos.ksh \ + zfs_send_encrypted_neg.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh new file mode 100644 index 000000000000..3b086e6e4b90 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh @@ -0,0 +1,71 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Verify 'zfs send' does not perform sends from encrypted datasets with +# unloaded keys. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Snapshot the dataset +# 3. Unload the dataset key +# 4. Verify sending the stream fails +# + +verify_runnable "both" + +function cleanup +{ + datasetexists $TESTPOOL/$cryptds && \ + log_must $ZFS destroy -r $TESTPOOL/$cryptds +} + +log_onexit cleanup + +log_assert "Verify 'zfs send' can perform sends from encrypted datasets with \ + unloaded keys." + +typeset cryptds="crypt" +typeset passphrase="abcdefgh" +typeset snap="$TESTPOOL/$cryptds@snap" + +log_must eval "$ECHO $passphrase | \ + $ZFS create -o encryption=on -o keysource=passphrase,prompt \ + $TESTPOOL/$cryptds" +log_must $ZFS snapshot $snap +log_must $ZFS unmount $TESTPOOL/$cryptds +log_must $ZFS key -u $TESTPOOL/$cryptds +log_mustnot eval "$ZFS send $snap > /dev/null" + +log_pass "'zfs send' cannot perform sends from encrypted datasets with unloaded keys." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh new file mode 100644 index 000000000000..2d055db2e1e2 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh @@ -0,0 +1,67 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Verify 'zfs send' can perform unencrypted sends from encrypted datasets. +# +# STRATEGY: +# 1. Create an encrypted dataset +# 2. Snapshot the dataset +# 3. Verify sending the stream succeeds +# + +verify_runnable "both" + +function cleanup +{ + datasetexists $TESTPOOL/$cryptds && \ + log_must $ZFS destroy -r $TESTPOOL/$cryptds +} + +log_onexit cleanup + +log_assert "Verify 'zfs send' can perform unencrypted sends from \ + encrypted datasets." + +typeset cryptds="crypt" +typeset passphrase="abcdefgh" +typeset snap="$TESTPOOL/$cryptds@snap" + +log_must eval "$ECHO $passphrase | \ + $ZFS create -o encryption=on -o keysource=passphrase,prompt \ + $TESTPOOL/$cryptds" +log_must $ZFS snapshot $snap +log_must eval "$ZFS send $snap > /dev/null" + +log_pass "'zfs send' can perform unencrypted sends from encrypted datasets." diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_create/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zpool_create/Makefile.am index 8cfc5bd00231..7df040bb828c 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_create/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_create/Makefile.am @@ -27,6 +27,7 @@ dist_pkgdata_SCRIPTS = \ zpool_create_022_pos.ksh \ zpool_create_023_neg.ksh \ zpool_create_024_pos.ksh \ + zpool_create_encrypted.ksh \ zpool_create_features_001_pos.ksh \ zpool_create_features_002_pos.ksh \ zpool_create_features_003_pos.ksh \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh new file mode 100644 index 000000000000..426777db191b --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh @@ -0,0 +1,111 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2016, Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib + +# +# DESCRIPTION: +# Create an encrypted pool +# +# STRATEGY: +# 1. Create a pool for each encryption type and verify that it is properly set +# 2. Create a pool for each keysource type and verify that it is properly set +# + +verify_runnable "global" + +function cleanup +{ + poolexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +set -A ENCRYPTION_ALGS "encryption=on" \ + "encryption=aes-128-ccm" \ + "encryption=aes-192-ccm" \ + "encryption=aes-256-ccm" \ + "encryption=aes-128-gcm" \ + "encryption=aes-192-gcm" \ + "encryption=aes-256-gcm" + +set -A ENCRYPTION_PROPS "encryption=aes-256-ccm" \ + "encryption=aes-128-ccm" \ + "encryption=aes-192-ccm" \ + "encryption=aes-256-ccm" \ + "encryption=aes-128-gcm" \ + "encryption=aes-192-gcm" \ + "encryption=aes-256-gcm" + +set -A KEYSOURCE_TYPES "keysource=raw,prompt" \ + "keysource=hex,prompt" \ + "keysource=passphrase,prompt" + +set -A KEYSOURCES "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ + "abcdefgh" + +log_assert "'zpool create' can create encrypted pools" + +typeset -i i=0 +while (( i < ${#ENCRYPTION_ALGS[*]} )); do + log_must eval "$ECHO ${KEYSOURCES[0]} | $ZPOOL create \ + -O ${ENCRYPTION_ALGS[i]} -O ${KEYSOURCE_TYPES[0]} \ + $TESTPOOL $DISKS" + + propertycheck $TESTPOOL ${ENCRYPTION_PROPS[i]} || \ + log_fail "${ENCRYPTION_ALGS[i]} is failed to set." + + propertycheck $TESTPOOL ${KEYSOURCE_TYPES[0]} || \ + log_fail "${KEYSOURCE_TYPES[0]} is failed to set." + + log_must $ZPOOL destroy $TESTPOOL + (( i = i + 1 )) +done + +typeset -i j=0 +while (( j < ${#KEYSOURCE_TYPES[*]} )); do + log_must eval "$ECHO ${KEYSOURCES[j]} | $ZPOOL create \ + -O ${ENCRYPTION_ALGS[0]} -O ${KEYSOURCE_TYPES[j]} \ + $TESTPOOL $DISKS" + + propertycheck $TESTPOOL ${ENCRYPTION_PROPS[0]} || \ + log_fail "${ENCRYPTION_ALGS[0]} is failed to set." + + propertycheck $TESTPOOL ${KEYSOURCE_TYPES[j]} || \ + log_fail "${KEYSOURCE_TYPES[j]} is failed to set." + + log_must $ZPOOL destroy $TESTPOOL + (( j = j + 1 )) +done + +log_pass "Creating encrypted pools works as expected." \ No newline at end of file diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg index 699229fef78d..cd6a7a40b25a 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg @@ -39,7 +39,7 @@ typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version" "feature@spacemap_histogram" "feature@enabled_txg" "feature@hole_birth" "feature@extensible_dataset" "feature@bookmarks" "feature@embedded_data" "feature@sha512" "feature@skein" "feature@edonr" - "feature@userobj_accounting") + "feature@userobj_accounting" "feature@encryption") else typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version" "bootfs" ""leaked" delegation" "autoreplace" "cachefile" "dedupditto" "dedupratio" @@ -48,5 +48,5 @@ typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version" "feature@lz4_compress" "feature@multi_vdev_crash_dump" "feature@spacemap_histogram" "feature@enabled_txg" "feature@hole_birth" "feature@extensible_dataset" "feature@bookmarks" "feature@sha512" - "feature@skein" "feature@edonr") + "feature@skein" "feature@edonr" "feature@encryption") fi From 2f7c9cb5e27c4feef62971d0c2f802735d60a4e8 Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Tue, 10 Jan 2017 23:44:20 -0500 Subject: [PATCH 02/10] added assert for sync-to-convergence, small man page fixes, removed ZIL-specific IV derivation --- cmd/zfs/zfs_main.c | 12 ++++++++++-- include/sys/zio_crypt.h | 1 - man/man8/zfs.8 | 18 ++++++++++++++++++ man/man8/zpool.8 | 4 ++-- module/zfs/arc.c | 11 ++++------- module/zfs/dsl_crypt.c | 6 ------ module/zfs/zio.c | 14 ++++++++++++-- module/zfs/zio_crypt.c | 19 ------------------- 8 files changed, 46 insertions(+), 39 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index e97f8e6858ae..3b465b80b06f 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -6991,8 +6991,16 @@ zfs_do_key(int argc, char **argv) usage(B_FALSE); } - if (argc < 3) { - (void) fprintf(stderr, gettext("Too few arguments\n")); + argc -= optind; + argv += optind; + + if (argc < 1) { + (void) fprintf(stderr, gettext("Missing dataset argument\n")); + usage(B_FALSE); + } + + if (argc > 1) { + (void) fprintf(stderr, gettext("Too many arguments\n")); usage(B_FALSE); } diff --git a/include/sys/zio_crypt.h b/include/sys/zio_crypt.h index 2c8d98fe8c9a..32e8ea041169 100644 --- a/include/sys/zio_crypt.h +++ b/include/sys/zio_crypt.h @@ -159,7 +159,6 @@ void zio_crypt_encode_mac_bp(blkptr_t *bp, uint8_t *mac); void zio_crypt_decode_mac_bp(const blkptr_t *bp, uint8_t *mac); void zio_crypt_encode_mac_zil(const void *data, uint8_t *mac); void zio_crypt_decode_mac_zil(const void *data, uint8_t *mac); -void zio_crypt_derive_zil_iv(const void *data, uint8_t *iv, uint8_t *iv_out); void zio_crypt_copy_dnode_bonus(abd_t *src_abd, uint8_t *dst, uint_t datalen); int zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, diff --git a/man/man8/zfs.8 b/man/man8/zfs.8 index 166931614ed7..d5caa1e66c58 100644 --- a/man/man8/zfs.8 +++ b/man/man8/zfs.8 @@ -268,6 +268,21 @@ zfs \- configures ZFS file systems .LP .nf \fBzfs\fR \fBdiff\fR [\fB-FHt\fR] \fIsnapshot\fR \fIsnapshot|filesystem\fR +.fi + +.LP +.nf +\fB\fBzfs key -l\fR \fIfilesystem\fR | \fIvolume\fR\fR +.fi + +.LP +.nf +\fB\fBzfs key -u\fR \fIfilesystem\fR | \fIvolume\fR\fR +.fi + +.LP +.nf +\fB\fBzfs key -c\fR [\fB-o\fR \fIkeysource\fR=\fIkeysource\fR] [\fB-o\fR \fIpbkdf2iters\fR=\fIiterations\fR] \fIfilesystem\fR | \fIvolume\fR\fR .SH DESCRIPTION .LP @@ -3206,6 +3221,9 @@ destroy subcommand Must also have the 'mount' ability diff subcommand Allows lookup of paths within a dataset given an object number, and the ability to create snapshots necessary to 'zfs diff'. +keyload subcommand Allows loading and unloading of encryption key. +keychange subcommand Allows changing anencryption key via + 'zfs key -c'. mount subcommand Allows mount/umount of ZFS datasets promote subcommand Must also have the 'mount' and 'promote' ability in the origin file system diff --git a/man/man8/zpool.8 b/man/man8/zpool.8 index bde00a02c602..b2a89e33f5eb 100644 --- a/man/man8/zpool.8 +++ b/man/man8/zpool.8 @@ -84,13 +84,13 @@ zpool \- configures ZFS storage pools .LP .nf \fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] - [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-N\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR] [\fB-X\fR\] [\fB-T\fR\]] [\fB-s\fR] \fB-a\fR + [\fB-D\fR] [\fB-f\fR] [\fB-l\fR] [\fB-m\fR] [\fB-N\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR] [\fB-X\fR\] [\fB-T\fR\]] [\fB-s\fR] \fB-a\fR .fi .LP .nf \fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] - [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR] [\fB-X\fR] [\fB-T\fR\]] [\fB-t\fR]] [\fB-s\fR] + [\fB-D\fR] [\fB-f\fR] [\fB-l\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR] [\fB-X\fR] [\fB-T\fR\]] [\fB-t\fR]] [\fB-s\fR] \fIpool\fR | \fIid\fR [\fInewpool\fR] .fi diff --git a/module/zfs/arc.c b/module/zfs/arc.c index a0021a898c94..3dc229afe942 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -5275,25 +5275,22 @@ arc_read_done(zio_t *zio) } if (BP_IS_ENCRYPTED(bp)) { - uint8_t tmpiv[DATA_IV_LEN]; - void *tmpbuf; - hdr->b_crypt_hdr.b_ot = BP_GET_TYPE(bp); hdr->b_crypt_hdr.b_dsobj = zio->io_bookmark.zb_objset; - zio_crypt_decode_params_bp(bp, hdr->b_crypt_hdr.b_salt, tmpiv); + zio_crypt_decode_params_bp(bp, hdr->b_crypt_hdr.b_salt, + hdr->b_crypt_hdr.b_iv); if (BP_GET_TYPE(bp) == DMU_OT_INTENT_LOG) { + void *tmpbuf; + tmpbuf = abd_borrow_buf_copy(zio->io_abd, sizeof (zil_chain_t)); zio_crypt_decode_mac_zil(tmpbuf, hdr->b_crypt_hdr.b_mac); - zio_crypt_derive_zil_iv(tmpbuf, tmpiv, - hdr->b_crypt_hdr.b_iv); abd_return_buf(zio->io_abd, tmpbuf, sizeof (zil_chain_t)); } else { zio_crypt_decode_mac_bp(bp, hdr->b_crypt_hdr.b_mac); - bcopy(tmpiv, hdr->b_crypt_hdr.b_iv, DATA_IV_LEN); } } diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index 6115a862e1ab..bbd83fefd58a 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -1506,7 +1506,6 @@ spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, int ret; dmu_object_type_t ot = BP_GET_TYPE(bp); dsl_crypto_key_t *dck; - uint8_t tmpiv[DATA_IV_LEN]; uint8_t *plainbuf = NULL, *cipherbuf = NULL; ASSERT(spa_feature_is_active(spa, SPA_FEATURE_ENCRYPTION)); @@ -1550,11 +1549,6 @@ spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, plainbuf, datalen, iv, salt); if (ret) goto error; - } else if (ot == DMU_OT_INTENT_LOG) { - uint8_t *src = (encrypt) ? plainbuf : cipherbuf; - - zio_crypt_derive_zil_iv(src, iv, tmpiv); - iv = tmpiv; } /* call lower level function to perform encryption / decryption */ diff --git a/module/zfs/zio.c b/module/zfs/zio.c index fa2aaf766926..ed8e4e389146 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -3549,9 +3549,19 @@ zio_encrypt(zio_t *zio) return (ZIO_PIPELINE_CONTINUE); } - ASSERT3U(psize, !=, 0); - ASSERT(spa_feature_is_active(spa, SPA_FEATURE_ENCRYPTION)); + /* + * Later passes of sync-to-convergence may decide to rewrite data + * in place to avoid more disk reallocations. This presents a problem + * for encryption because this consitutes rewriting the new data with + * the same encryption key and IV. However, this only applies to blocks + * in the MOS (particularly the spacemaps) and we do not encrypt the + * MOS. We assert that the zio is allocating or an intent log write + * to enforce this. + */ + ASSERT(IO_IS_ALLOCATING(zio) || ot == DMU_OT_INTENT_LOG); ASSERT(BP_GET_LEVEL(bp) == 0 || ot == DMU_OT_INTENT_LOG); + ASSERT(spa_feature_is_active(spa, SPA_FEATURE_ENCRYPTION)); + ASSERT3U(psize, !=, 0); enc_buf = zio_buf_alloc(psize); eabd = abd_get_from_buf(enc_buf, psize); diff --git a/module/zfs/zio_crypt.c b/module/zfs/zio_crypt.c index a76595ec5e56..192ddfd0dcca 100644 --- a/module/zfs/zio_crypt.c +++ b/module/zfs/zio_crypt.c @@ -846,25 +846,6 @@ zio_crypt_copy_dnode_bonus(abd_t *src_abd, uint8_t *dst, uint_t datalen) abd_return_buf(src_abd, src, datalen); } -/* - * ZIL blocks are rewritten as new log entries are synced to - * disk. We generated the IV randomly when we allocated the - * block, but we cannot reuse this each time we do a rewrite. - * To combat this we add in zc_nused from the zil_chain_t. We - * only need the IV to be unique for each, not securely random - * so it is ok for us to just add it into the existing value. - */ -void -zio_crypt_derive_zil_iv(const void *data, uint8_t *iv, uint8_t *iv_out) -{ - uint64_t *iv_word; - zil_chain_t *zc = (zil_chain_t *)data; - - bcopy(iv, iv_out, DATA_IV_LEN); - iv_word = (uint64_t *)iv_out; - *iv_word = LE_64(LE_64(*iv_word) + zc->zc_nused); -} - static void zio_crypt_destroy_uio(uio_t *uio) { From 2b761791ebb32d8641a826bf28623e31d0da6910 Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Tue, 17 Jan 2017 11:53:13 -0500 Subject: [PATCH 03/10] fix for small race condition in arc.c that would occasionally trigger assert --- module/zfs/arc.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/module/zfs/arc.c b/module/zfs/arc.c index 3dc229afe942..7412749a71ab 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -3204,12 +3204,6 @@ arc_hdr_realloc_crypt(arc_buf_hdr_t *hdr, boolean_t encrypt) nhdr = kmem_cache_alloc(ncache, KM_PUSHPAGE); bcopy(hdr, nhdr, HDR_L2ONLY_SIZE); nhdr->b_l1hdr.b_freeze_cksum = hdr->b_l1hdr.b_freeze_cksum; - - nhdr->b_l1hdr.b_buf = hdr->b_l1hdr.b_buf; - for (buf = nhdr->b_l1hdr.b_buf; buf != NULL; buf = buf->b_next) { - buf->b_hdr = nhdr; - } - nhdr->b_l1hdr.b_bufcnt = hdr->b_l1hdr.b_bufcnt; nhdr->b_l1hdr.b_byteswap = hdr->b_l1hdr.b_byteswap; nhdr->b_l1hdr.b_state = hdr->b_l1hdr.b_state; @@ -3219,9 +3213,25 @@ arc_hdr_realloc_crypt(arc_buf_hdr_t *hdr, boolean_t encrypt) nhdr->b_l1hdr.b_mfu_hits = hdr->b_l1hdr.b_mfu_hits; nhdr->b_l1hdr.b_mfu_ghost_hits = hdr->b_l1hdr.b_mfu_ghost_hits; nhdr->b_l1hdr.b_l2_hits = hdr->b_l1hdr.b_l2_hits; - refcount_transfer(&nhdr->b_l1hdr.b_refcnt, &hdr->b_l1hdr.b_refcnt); nhdr->b_l1hdr.b_acb = hdr->b_l1hdr.b_acb; nhdr->b_l1hdr.b_pabd = hdr->b_l1hdr.b_pabd; + nhdr->b_l1hdr.b_buf = hdr->b_l1hdr.b_buf; + + /* + * This refcount_add() exists only to ensure that the individual + * arc buffers always point to a header that is referenced, avoiding + * a small race condition that could trigger ASSERTs. + */ + (void) refcount_add(&nhdr->b_l1hdr.b_refcnt, FTAG); + + for (buf = nhdr->b_l1hdr.b_buf; buf != NULL; buf = buf->b_next) { + mutex_enter(&buf->b_evict_lock); + buf->b_hdr = nhdr; + mutex_exit(&buf->b_evict_lock); + } + + refcount_transfer(&nhdr->b_l1hdr.b_refcnt, &hdr->b_l1hdr.b_refcnt); + (void) refcount_remove(&nhdr->b_l1hdr.b_refcnt, FTAG); if (encrypt) { arc_hdr_set_flags(nhdr, ARC_FLAG_ENCRYPT); From 64fb3ce074c294585ea134895821833e747732ff Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Tue, 17 Jan 2017 12:07:20 -0500 Subject: [PATCH 04/10] reworked dataset holding / owning to work with flags --- cmd/zdb/zdb.c | 7 +- cmd/zinject/translate.c | 4 +- cmd/ztest/ztest.c | 14 ++-- include/sys/dmu.h | 2 +- include/sys/dmu_objset.h | 5 +- include/sys/dsl_dataset.h | 22 ++++--- module/zfs/dmu_objset.c | 48 ++++++++------ module/zfs/dmu_send.c | 79 +++++++++++++---------- module/zfs/dsl_dataset.c | 131 ++++++++++++++------------------------ module/zfs/dsl_destroy.c | 2 +- module/zfs/zfs_ioctl.c | 14 ++-- module/zfs/zfs_vfsops.c | 6 +- module/zfs/zil.c | 4 +- module/zfs/zvol.c | 10 +-- module/zpios/pios.c | 2 +- 15 files changed, 168 insertions(+), 182 deletions(-) diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index 5034ffbfc957..7e00dd3bb433 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -2340,7 +2340,7 @@ dump_one_dir(const char *dsname, void *arg) } dump_dir(os); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_FALSE, FTAG); fuid_table_destroy(); sa_loaded = B_FALSE; return (0); @@ -3945,7 +3945,10 @@ main(int argc, char **argv) zdb_read_block(argv[i], spa); } - (os != NULL) ? dmu_objset_disown(os, FTAG) : spa_close(spa, FTAG); + if (os != NULL) + dmu_objset_disown(os, B_FALSE, FTAG); + else + spa_close(spa, FTAG); fuid_table_destroy(); sa_loaded = B_FALSE; diff --git a/cmd/zinject/translate.c b/cmd/zinject/translate.c index dbc4193b2d90..1dd132c2997f 100644 --- a/cmd/zinject/translate.c +++ b/cmd/zinject/translate.c @@ -189,7 +189,7 @@ object_from_path(const char *dataset, const char *path, struct stat64 *statbuf, record->zi_objset = dmu_objset_id(os); record->zi_object = statbuf->st_ino; - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); return (0); } @@ -329,7 +329,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, dnode_rele(dn, FTAG); } if (os) - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); return (ret); } diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c index 8f3dfd6e3bb1..5c38de72cfbc 100644 --- a/cmd/ztest/ztest.c +++ b/cmd/ztest/ztest.c @@ -3494,7 +3494,7 @@ ztest_objset_destroy_cb(const char *name, void *arg) ASSERT3U(doi.doi_type, ==, DMU_OT_ZAP_OTHER); ASSERT3S(doi.doi_physical_blocks_512, >=, 0); } - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); /* * Destroy the dataset. @@ -3572,7 +3572,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) ztest_zd_init(zdtmp, NULL, os); zil_replay(os, zdtmp, ztest_replay_vector); ztest_zd_fini(zdtmp); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); } /* @@ -3641,7 +3641,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id) dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE, FTAG, &os2)); zil_close(zilog); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); ztest_zd_fini(zdtmp); out: (void) rw_unlock(&ztest_name_lock); @@ -3801,14 +3801,14 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id) fatal(0, "dmu_objset_own(%s) = %d", snap2name, error); error = dsl_dataset_promote(clone2name, NULL); if (error == ENOSPC) { - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); ztest_record_enospc(FTAG); goto out; } if (error != EBUSY) fatal(0, "dsl_dataset_promote(%s), %d, not EBUSY", clone2name, error); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); out: ztest_dsl_dataset_cleanup(osname, id); @@ -6172,7 +6172,7 @@ ztest_dataset_close(int d) ztest_ds_t *zd = &ztest_ds[d]; zil_close(zd->zd_zilog); - dmu_objset_disown(zd->zd_os, zd); + dmu_objset_disown(zd->zd_os, B_TRUE, zd); ztest_zd_fini(zd); } @@ -6226,7 +6226,7 @@ ztest_run(ztest_shared_t *zs) VERIFY0(dmu_objset_own(ztest_opts.zo_pool, DMU_OST_ANY, B_TRUE, B_TRUE, FTAG, &os)); zs->zs_guid = dmu_objset_fsid_guid(os); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); spa->spa_dedup_ditto = 2 * ZIO_DEDUPDITTO_MIN; diff --git a/include/sys/dmu.h b/include/sys/dmu.h index 7ecba73bdb0f..21ca47afe934 100644 --- a/include/sys/dmu.h +++ b/include/sys/dmu.h @@ -293,7 +293,7 @@ int dmu_objset_hold(const char *name, void *tag, objset_t **osp); int dmu_objset_own(const char *name, dmu_objset_type_t type, boolean_t readonly, boolean_t key_required, void *tag, objset_t **osp); void dmu_objset_rele(objset_t *os, void *tag); -void dmu_objset_disown(objset_t *os, void *tag); +void dmu_objset_disown(objset_t *os, boolean_t key_required, void *tag); int dmu_objset_open_ds(struct dsl_dataset *ds, objset_t **osp); void dmu_objset_evict_dbufs(objset_t *os); diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h index 4253efa7fde3..56fef9ac6dc1 100644 --- a/include/sys/dmu_objset.h +++ b/include/sys/dmu_objset.h @@ -156,9 +156,10 @@ int dmu_objset_own(const char *name, dmu_objset_type_t type, int dmu_objset_own_obj(struct dsl_pool *dp, uint64_t obj, dmu_objset_type_t type, boolean_t readonly, boolean_t key_required, void *tag, objset_t **osp); -void dmu_objset_refresh_ownership(objset_t *os, void *tag); +void dmu_objset_refresh_ownership(objset_t *os, boolean_t key_needed, + void *tag); void dmu_objset_rele(objset_t *os, void *tag); -void dmu_objset_disown(objset_t *os, void *tag); +void dmu_objset_disown(objset_t *os, boolean_t key_required, void *tag); int dmu_objset_from_ds(struct dsl_dataset *ds, objset_t **osp); void dmu_objset_stats(objset_t *os, nvlist_t *nv); diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index 37835f3b1a37..215184ef9af7 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -245,27 +245,29 @@ dsl_dataset_phys(dsl_dataset_t *ds) #define DS_UNIQUE_IS_ACCURATE(ds) \ ((dsl_dataset_phys(ds)->ds_flags & DS_FLAG_UNIQUE_ACCURATE) != 0) +#define DS_HOLD_FLAG_DECRYPT (1 << 0) + int dsl_dataset_hold(struct dsl_pool *dp, const char *name, void *tag, dsl_dataset_t **dsp); +int dsl_dataset_hold_flags(struct dsl_pool *dp, const char *name, int flags, + void *tag, dsl_dataset_t **dsp); boolean_t dsl_dataset_try_add_ref(struct dsl_pool *dp, dsl_dataset_t *ds, void *tag); int dsl_dataset_hold_obj(struct dsl_pool *dp, uint64_t dsobj, void *tag, dsl_dataset_t **); +int dsl_dataset_hold_obj_flags(struct dsl_pool *dp, uint64_t dsobj, int flags, + void *tag, dsl_dataset_t **); void dsl_dataset_rele(dsl_dataset_t *ds, void *tag); -int dsl_dataset_hold_crypt(struct dsl_pool *dp, const char *name, void *tag, - dsl_dataset_t **dsp); -int dsl_dataset_hold_crypt_obj(struct dsl_pool *dp, uint64_t dsobj, +void dsl_dataset_rele_flags(dsl_dataset_t *ds, int flags, void *tag); +int dsl_dataset_own(struct dsl_pool *dp, const char *name, int flags, + void *tag, dsl_dataset_t **dsp); +int dsl_dataset_own_obj(struct dsl_pool *dp, uint64_t dsobj, int flags, void *tag, dsl_dataset_t **dsp); -void dsl_dataset_rele_crypt(dsl_dataset_t *ds, void *tag); -int dsl_dataset_own(struct dsl_pool *dp, const char *name, - void *tag, boolean_t key_required, dsl_dataset_t **dsp); -int dsl_dataset_own_obj(struct dsl_pool *dp, uint64_t dsobj, - void *tag, boolean_t key_required, dsl_dataset_t **dsp); -void dsl_dataset_disown(dsl_dataset_t *ds, void *tag); +void dsl_dataset_disown(dsl_dataset_t *ds, int flags, void *tag); void dsl_dataset_name(dsl_dataset_t *ds, char *name); int dsl_dataset_namelen(dsl_dataset_t *ds); boolean_t dsl_dataset_has_owner(dsl_dataset_t *ds); -int dsl_dataset_tryown(dsl_dataset_t *ds, void *tag, boolean_t key_required); +int dsl_dataset_tryown(dsl_dataset_t *ds, int flags, void *tag); uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname, dsl_dataset_t *origin, uint64_t flags, cred_t *, struct dsl_crypto_params *, dmu_tx_t *); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index 23f68f1be657..715f0296403d 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -616,15 +616,13 @@ dmu_objset_own_impl(dsl_dataset_t *ds, dmu_objset_type_t type, err = dmu_objset_from_ds(ds, osp); if (err != 0) { - dsl_dataset_disown(ds, tag); + return (err); } else if (type != DMU_OST_ANY && type != (*osp)->os_phys->os_type) { - dsl_dataset_disown(ds, tag); return (SET_ERROR(EINVAL)); } else if (!readonly && dsl_dataset_is_snapshot(ds)) { - dsl_dataset_disown(ds, tag); return (SET_ERROR(EROFS)); } - return (err); + return (0); } /* @@ -639,19 +637,26 @@ dmu_objset_own(const char *name, dmu_objset_type_t type, dsl_pool_t *dp; dsl_dataset_t *ds; int err; + int flags = (key_required) ? DS_HOLD_FLAG_DECRYPT : 0; err = dsl_pool_hold(name, FTAG, &dp); if (err != 0) return (err); - err = dsl_dataset_own(dp, name, tag, key_required, &ds); + err = dsl_dataset_own(dp, name, flags, tag, &ds); if (err != 0) { dsl_pool_rele(dp, FTAG); return (err); } err = dmu_objset_own_impl(ds, type, readonly, tag, osp); + if (err != 0) { + dsl_dataset_disown(ds, flags, tag); + dsl_pool_rele(dp, FTAG); + return (err); + } + dsl_pool_rele(dp, FTAG); - if (err == 0 && dmu_objset_userobjspace_upgradable(*osp)) + if (dmu_objset_userobjspace_upgradable(*osp)) dmu_objset_userobjspace_upgrade(*osp); return (err); @@ -663,12 +668,19 @@ dmu_objset_own_obj(dsl_pool_t *dp, uint64_t obj, dmu_objset_type_t type, { dsl_dataset_t *ds; int err; + int flags = (key_required) ? DS_HOLD_FLAG_DECRYPT: 0; - err = dsl_dataset_own_obj(dp, obj, tag, key_required, &ds); + err = dsl_dataset_own_obj(dp, obj, flags, tag, &ds); if (err != 0) return (err); - return (dmu_objset_own_impl(ds, type, readonly, tag, osp)); + err = dmu_objset_own_impl(ds, type, readonly, tag, osp); + if (err != 0) { + dsl_dataset_disown(ds, flags, tag); + return (err); + } + + return (0); } void @@ -691,11 +703,10 @@ dmu_objset_rele(objset_t *os, void *tag) * same name so that it can be partially torn down and reconstructed. */ void -dmu_objset_refresh_ownership(objset_t *os, void *tag) +dmu_objset_refresh_ownership(objset_t *os, boolean_t key_needed, void *tag) { dsl_pool_t *dp; dsl_dataset_t *ds, *newds; - boolean_t key_needed; char name[ZFS_MAX_DATASET_NAME_LEN]; ds = os->os_dsl_dataset; @@ -703,30 +714,25 @@ dmu_objset_refresh_ownership(objset_t *os, void *tag) VERIFY3P(ds->ds_owner, ==, tag); VERIFY(dsl_dataset_long_held(ds)); - if (os->os_encrypted && - (spa_keystore_lookup_key(os->os_spa, - os->os_dsl_dataset->ds_object, NULL, NULL) == 0)) - key_needed = B_TRUE; - else - key_needed = B_FALSE; - dsl_dataset_name(ds, name); dp = dmu_objset_pool(os); dsl_pool_config_enter(dp, FTAG); - dmu_objset_disown(os, tag); - VERIFY0(dsl_dataset_own(dp, name, tag, key_needed, &newds)); + dmu_objset_disown(os, key_needed, tag); + VERIFY0(dsl_dataset_own(dp, name, + (key_needed) ? DS_HOLD_FLAG_DECRYPT : 0, tag, &newds)); VERIFY3P(newds, ==, os->os_dsl_dataset); dsl_pool_config_exit(dp, FTAG); } void -dmu_objset_disown(objset_t *os, void *tag) +dmu_objset_disown(objset_t *os, boolean_t key_needed, void *tag) { /* * Stop upgrading thread */ dmu_objset_upgrade_stop(os); - dsl_dataset_disown(os->os_dsl_dataset, tag); + dsl_dataset_disown(os->os_dsl_dataset, + (key_needed) ? DS_HOLD_FLAG_DECRYPT : 0, tag); } void diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index 0b2d8b4424e5..b4d008a54ad1 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -1003,13 +1003,14 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, dsl_pool_t *dp; dsl_dataset_t *ds; dsl_dataset_t *fromds = NULL; + int flags = DS_HOLD_FLAG_DECRYPT; int err; err = dsl_pool_hold(pool, FTAG, &dp); if (err != 0) return (err); - err = dsl_dataset_hold_crypt_obj(dp, tosnap, FTAG, &ds); + err = dsl_dataset_hold_obj_flags(dp, tosnap, flags, FTAG, &ds); if (err != 0) { dsl_pool_rele(dp, FTAG); return (err); @@ -1021,7 +1022,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, err = dsl_dataset_hold_obj(dp, fromsnap, FTAG, &fromds); if (err != 0) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); dsl_pool_rele(dp, FTAG); return (err); } @@ -1039,7 +1040,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap, err = dmu_send_impl(FTAG, dp, ds, NULL, B_FALSE, embedok, large_block_ok, compressok, outfd, 0, 0, vp, off); } - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (err); } @@ -1052,6 +1053,7 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, dsl_pool_t *dp; dsl_dataset_t *ds; int err; + int flags = DS_HOLD_FLAG_DECRYPT; boolean_t owned = B_FALSE; if (fromsnap != NULL && strpbrk(fromsnap, "@#") == NULL) @@ -1066,10 +1068,10 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, * We are sending a filesystem or volume. Ensure * that it doesn't change by owning the dataset. */ - err = dsl_dataset_own(dp, tosnap, FTAG, B_TRUE, &ds); + err = dsl_dataset_own(dp, tosnap, flags, FTAG, &ds); owned = B_TRUE; } else { - err = dsl_dataset_hold_crypt(dp, tosnap, FTAG, &ds); + err = dsl_dataset_hold_flags(dp, tosnap, flags, FTAG, &ds); } if (err != 0) { dsl_pool_rele(dp, FTAG); @@ -1110,9 +1112,9 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, } if (err != 0) { if (owned) - dsl_dataset_disown(ds, FTAG); + dsl_dataset_disown(ds, flags, FTAG); else - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); dsl_pool_rele(dp, FTAG); return (err); @@ -1126,9 +1128,10 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok, outfd, resumeobj, resumeoff, vp, off); } if (owned) - dsl_dataset_disown(ds, FTAG); + dsl_dataset_disown(ds, flags, FTAG); else - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); + return (err); } @@ -1381,6 +1384,7 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) struct drr_begin *drrb = drba->drba_cookie->drc_drrb; uint64_t fromguid = drrb->drr_fromguid; int flags = drrb->drr_flags; + int dsflags = DS_HOLD_FLAG_DECRYPT; int error; uint64_t featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); dsl_dataset_t *ds; @@ -1436,18 +1440,18 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_DNODE)) return (SET_ERROR(ENOTSUP)); - error = dsl_dataset_hold_crypt(dp, tofs, FTAG, &ds); + error = dsl_dataset_hold_flags(dp, tofs, dsflags, FTAG, &ds); if (error == 0) { /* target fs already exists; recv into temp clone */ /* Can't recv a clone into an existing fs */ if (flags & DRR_FLAG_CLONE || drba->drba_origin) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); return (SET_ERROR(EINVAL)); } error = recv_begin_check_existing_impl(drba, ds, fromguid); - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); } else if (error == ENOENT) { /* target fs does not exist; must be a full backup or clone */ char buf[ZFS_MAX_DATASET_NAME_LEN]; @@ -1472,7 +1476,7 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) /* Open the parent of tofs */ ASSERT3U(strlen(tofs), <, sizeof (buf)); (void) strlcpy(buf, tofs, strrchr(tofs, '/') - tofs + 1); - error = dsl_dataset_hold_crypt(dp, buf, FTAG, &ds); + error = dsl_dataset_hold_flags(dp, buf, dsflags, FTAG, &ds); if (error != 0) return (error); @@ -1484,14 +1488,14 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) error = dsl_fs_ss_limit_check(ds->ds_dir, 1, ZFS_PROP_FILESYSTEM_LIMIT, NULL, drba->drba_cred); if (error != 0) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); return (error); } error = dsl_fs_ss_limit_check(ds->ds_dir, 1, ZFS_PROP_SNAPSHOT_LIMIT, NULL, drba->drba_cred); if (error != 0) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); return (error); } @@ -1500,23 +1504,23 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) error = dsl_dataset_hold(dp, drba->drba_origin, FTAG, &origin); if (error != 0) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); return (error); } if (!origin->ds_is_snapshot) { dsl_dataset_rele(origin, FTAG); - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); return (SET_ERROR(EINVAL)); } if (dsl_dataset_phys(origin)->ds_guid != fromguid && fromguid != 0) { dsl_dataset_rele(origin, FTAG); - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); return (SET_ERROR(ENODEV)); } dsl_dataset_rele(origin, FTAG); } - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, dsflags, FTAG); error = 0; } return (error); @@ -1532,13 +1536,14 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) const char *tofs = drba->drba_cookie->drc_tofs; dsl_dataset_t *ds, *newds; uint64_t dsobj; + int flags = DS_HOLD_FLAG_DECRYPT; int error; uint64_t crflags = 0; if (drrb->drr_flags & DRR_FLAG_CI_DATA) crflags |= DS_FLAG_CI_DATASET; - error = dsl_dataset_hold_crypt(dp, tofs, FTAG, &ds); + error = dsl_dataset_hold_flags(dp, tofs, flags, FTAG, &ds); if (error == 0) { /* create temporary clone */ dsl_dataset_t *snap = NULL; @@ -1550,7 +1555,7 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) snap, crflags, drba->drba_cred, NULL, tx); if (drba->drba_snapobj != 0) dsl_dataset_rele(snap, FTAG); - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); } else { dsl_dir_t *dd; const char *tail; @@ -1572,7 +1577,7 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) dsl_dir_rele(dd, FTAG); drba->drba_cookie->drc_newfs = B_TRUE; } - VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, B_TRUE, &newds)); + VERIFY0(dsl_dataset_own_obj(dp, dsobj, flags, dmu_recv_tag, &newds)); if (drba->drba_cookie->drc_resumable) { uint64_t one = 1; @@ -1634,6 +1639,7 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) dsl_pool_t *dp = dmu_tx_pool(tx); struct drr_begin *drrb = drba->drba_cookie->drc_drrb; int error; + int flags = DS_HOLD_FLAG_DECRYPT; uint64_t featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); dsl_dataset_t *ds; const char *tofs = drba->drba_cookie->drc_tofs; @@ -1672,28 +1678,28 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) (void) snprintf(recvname, sizeof (recvname), "%s/%s", tofs, recv_clone_name); - if (dsl_dataset_hold_crypt(dp, recvname, FTAG, &ds) != 0) { + if (dsl_dataset_hold_flags(dp, recvname, flags, FTAG, &ds) != 0) { /* %recv does not exist; continue in tofs */ - error = dsl_dataset_hold_crypt(dp, tofs, FTAG, &ds); + error = dsl_dataset_hold_flags(dp, tofs, flags, FTAG, &ds); if (error != 0) return (error); } /* check that ds is marked inconsistent */ if (!DS_IS_INCONSISTENT(ds)) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (SET_ERROR(EINVAL)); } /* check that there is resuming data, and that the toguid matches */ if (!dsl_dataset_is_zapified(ds)) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (SET_ERROR(EINVAL)); } error = zap_lookup(dp->dp_meta_objset, ds->ds_object, DS_FIELD_RESUME_TOGUID, sizeof (val), 1, &val); if (error != 0 || drrb->drr_toguid != val) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (SET_ERROR(EINVAL)); } @@ -1703,13 +1709,13 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) * fails) because it will be marked inconsistent. */ if (dsl_dataset_has_owner(ds)) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (SET_ERROR(EBUSY)); } /* There should not be any snapshots of this fs yet. */ if (ds->ds_prev != NULL && ds->ds_prev->ds_dir == ds->ds_dir) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (SET_ERROR(EINVAL)); } @@ -1723,11 +1729,11 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) (void) zap_lookup(dp->dp_meta_objset, ds->ds_object, DS_FIELD_RESUME_FROMGUID, sizeof (val), 1, &val); if (drrb->drr_fromguid != val) { - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (SET_ERROR(EINVAL)); } - dsl_dataset_rele_crypt(ds, FTAG); + dsl_dataset_rele_flags(ds, flags, FTAG); return (0); } @@ -1758,7 +1764,8 @@ dmu_recv_resume_begin_sync(void *arg, dmu_tx_t *tx) dsobj = ds->ds_object; dsl_dataset_rele(ds, FTAG); - VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, B_TRUE, &ds)); + VERIFY0(dsl_dataset_own_obj(dp, dsobj, DS_HOLD_FLAG_DECRYPT, + dmu_recv_tag, &ds)); dmu_buf_will_dirty(ds->ds_dbuf, tx); dsl_dataset_phys(ds)->ds_flags |= DS_FLAG_INCONSISTENT; @@ -2459,14 +2466,16 @@ receive_free(struct receive_writer_arg *rwa, struct drr_free *drrf) static void dmu_recv_cleanup_ds(dmu_recv_cookie_t *drc) { + int flags = DS_HOLD_FLAG_DECRYPT; + if (drc->drc_resumable) { /* wait for our resume state to be written to disk */ txg_wait_synced(drc->drc_ds->ds_dir->dd_pool, 0); - dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); + dsl_dataset_disown(drc->drc_ds, flags, dmu_recv_tag); } else { char name[ZFS_MAX_DATASET_NAME_LEN]; dsl_dataset_name(drc->drc_ds, name); - dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); + dsl_dataset_disown(drc->drc_ds, flags, dmu_recv_tag); (void) dsl_destroy_head(name); } } @@ -3278,7 +3287,7 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx) */ (void) spa_keystore_remove_mapping(dmu_tx_pool(tx)->dp_spa, drc->drc_ds, drc->drc_ds); - dsl_dataset_disown(drc->drc_ds, dmu_recv_tag); + dsl_dataset_disown(drc->drc_ds, 0, dmu_recv_tag); drc->drc_ds = NULL; } diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index dc8d46fe7533..f46ac22fbc5c 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -406,7 +406,7 @@ dsl_dataset_try_add_ref(dsl_pool_t *dp, dsl_dataset_t *ds, void *tag) } int -dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag, +dsl_dataset_hold_obj_flags(dsl_pool_t *dp, uint64_t dsobj, int flags, void *tag, dsl_dataset_t **dsp) { objset_t *mos = dp->dp_meta_objset; @@ -556,11 +556,27 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag, spa_version(dp->dp_spa) < SPA_VERSION_ORIGIN || dp->dp_origin_snap == NULL || ds == dp->dp_origin_snap); *dsp = ds; + + if (flags & DS_HOLD_FLAG_DECRYPT) { + err = spa_keystore_create_mapping(dp->dp_spa, ds, ds); + if (err != 0) { + dsl_dataset_rele_flags(ds, flags, tag); + return (SET_ERROR(EACCES)); + } + } + return (0); } int -dsl_dataset_hold(dsl_pool_t *dp, const char *name, +dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag, + dsl_dataset_t **dsp) +{ + return (dsl_dataset_hold_obj_flags(dp, dsobj, 0, tag, dsp)); +} + +int +dsl_dataset_hold_flags(dsl_pool_t *dp, const char *name, int flags, void *tag, dsl_dataset_t **dsp) { dsl_dir_t *dd; @@ -576,7 +592,7 @@ dsl_dataset_hold(dsl_pool_t *dp, const char *name, ASSERT(dsl_pool_config_held(dp)); obj = dsl_dir_phys(dd)->dd_head_dataset_obj; if (obj != 0) - err = dsl_dataset_hold_obj(dp, obj, tag, &ds); + err = dsl_dataset_hold_obj_flags(dp, obj, flags, tag, &ds); else err = SET_ERROR(ENOENT); @@ -592,8 +608,10 @@ dsl_dataset_hold(dsl_pool_t *dp, const char *name, dprintf("looking for snapshot '%s'\n", snapname); err = dsl_dataset_snap_lookup(ds, snapname, &obj); - if (err == 0) - err = dsl_dataset_hold_obj(dp, obj, tag, &snap_ds); + if (err == 0) { + err = dsl_dataset_hold_obj_flags(dp, obj, flags, tag, + &snap_ds); + } dsl_dataset_rele(ds, tag); if (err == 0) { @@ -611,86 +629,22 @@ dsl_dataset_hold(dsl_pool_t *dp, const char *name, return (err); } -void -dsl_dataset_rele_crypt(dsl_dataset_t *ds, void *tag) -{ - ASSERT(ds->ds_objset != NULL); - - if (ds->ds_objset->os_encrypted) { - VERIFY0(spa_keystore_remove_mapping(ds->ds_objset->os_spa, - ds, ds)); - } - - dsl_dataset_rele(ds, tag); -} - -int -dsl_dataset_hold_crypt(dsl_pool_t *dp, const char *name, - void *tag, dsl_dataset_t **dsp) -{ - int err; - objset_t *os; - - err = dsl_dataset_hold(dp, name, tag, dsp); - if (err != 0) - return (err); - - err = dmu_objset_from_ds(*dsp, &os); - if (err != 0) - goto error; - - if (os->os_encrypted) { - err = spa_keystore_create_mapping(dp->dp_spa, *dsp, *dsp); - if (err != 0) - goto error; - } - - return (0); - -error: - dsl_dataset_rele(*dsp, tag); - *dsp = NULL; - return (err); -} - int -dsl_dataset_hold_crypt_obj(dsl_pool_t *dp, uint64_t dsobj, - void *tag, dsl_dataset_t **dsp) +dsl_dataset_hold(dsl_pool_t *dp, const char *name, void *tag, + dsl_dataset_t **dsp) { - int err; - objset_t *os; - - err = dsl_dataset_hold_obj(dp, dsobj, tag, dsp); - if (err != 0) - return (err); - - err = dmu_objset_from_ds(*dsp, &os); - if (err != 0) - goto error; - - if (os->os_encrypted) { - err = spa_keystore_create_mapping(dp->dp_spa, *dsp, *dsp); - if (err != 0) - goto error; - } - - return (0); - -error: - dsl_dataset_rele(*dsp, tag); - *dsp = NULL; - return (err); + return (dsl_dataset_hold_flags(dp, name, 0, tag, dsp)); } int -dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, - void *tag, boolean_t key_required, dsl_dataset_t **dsp) +dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, int flags, void *tag, + dsl_dataset_t **dsp) { int err = dsl_dataset_hold_obj(dp, dsobj, tag, dsp); if (err != 0) return (err); - err = dsl_dataset_tryown(*dsp, tag, key_required); + err = dsl_dataset_tryown(*dsp, flags, tag); if (err != 0) { dsl_dataset_rele(*dsp, tag); *dsp = NULL; @@ -700,14 +654,14 @@ dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, } int -dsl_dataset_own(dsl_pool_t *dp, const char *name, - void *tag, boolean_t key_required, dsl_dataset_t **dsp) +dsl_dataset_own(dsl_pool_t *dp, const char *name, int flags, void *tag, + dsl_dataset_t **dsp) { int err = dsl_dataset_hold(dp, name, tag, dsp); if (err != 0) return (err); - err = dsl_dataset_tryown(*dsp, tag, key_required); + err = dsl_dataset_tryown(*dsp, flags, tag); if (err != 0) { dsl_dataset_rele(*dsp, tag); return (err); @@ -790,19 +744,30 @@ dsl_dataset_namelen(dsl_dataset_t *ds) } void -dsl_dataset_rele(dsl_dataset_t *ds, void *tag) +dsl_dataset_rele_flags(dsl_dataset_t *ds, int flags, void *tag) { dmu_buf_rele(ds->ds_dbuf, tag); + + if (flags & DS_HOLD_FLAG_DECRYPT) { + (void) spa_keystore_remove_mapping(ds->ds_dir->dd_pool->dp_spa, + ds, ds); + } +} + +void +dsl_dataset_rele(dsl_dataset_t *ds, void *tag) +{ + dsl_dataset_rele_flags(ds, 0, tag); } void -dsl_dataset_disown(dsl_dataset_t *ds, void *tag) +dsl_dataset_disown(dsl_dataset_t *ds, int flags, void *tag) { ASSERT3P(ds->ds_owner, ==, tag); ASSERT(ds->ds_dbuf != NULL); mutex_enter(&ds->ds_lock); - if (ds->ds_dir && ds->ds_dir->dd_crypto_obj) { + if (flags & DS_HOLD_FLAG_DECRYPT) { (void) spa_keystore_remove_mapping(ds->ds_dir->dd_pool->dp_spa, ds, ds); } @@ -813,7 +778,7 @@ dsl_dataset_disown(dsl_dataset_t *ds, void *tag) } int -dsl_dataset_tryown(dsl_dataset_t *ds, void *tag, boolean_t key_required) +dsl_dataset_tryown(dsl_dataset_t *ds, int flags, void *tag) { int ret = 0; spa_t *spa = ds->ds_dir->dd_pool->dp_spa; @@ -822,7 +787,7 @@ dsl_dataset_tryown(dsl_dataset_t *ds, void *tag, boolean_t key_required) ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); mutex_enter(&ds->ds_lock); if (ds->ds_owner == NULL && !DS_IS_INCONSISTENT(ds)) { - if (dckobj != 0 && key_required) { + if (dckobj != 0 && (flags & DS_HOLD_FLAG_DECRYPT)) { ret = spa_keystore_create_mapping(spa, ds, ds); if (ret) { mutex_exit(&ds->ds_lock); diff --git a/module/zfs/dsl_destroy.c b/module/zfs/dsl_destroy.c index e0fd63744047..2bd6101b46f6 100644 --- a/module/zfs/dsl_destroy.c +++ b/module/zfs/dsl_destroy.c @@ -963,7 +963,7 @@ dsl_destroy_head(const char *name) (void) dmu_free_long_object(os, obj); /* sync out all frees */ txg_wait_synced(dmu_objset_pool(os), 0); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); } } diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index a81c20d78c64..973275f7a554 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -553,12 +553,12 @@ zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr) * Try to own the dataset; abort if there is any error, * (e.g., already mounted, in use, or other error). */ - error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, + error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE, B_TRUE, setsl_tag, &os); if (error != 0) return (SET_ERROR(EPERM)); - dmu_objset_disown(os, setsl_tag); + dmu_objset_disown(os, B_TRUE, setsl_tag); if (new_default) { needed_priv = PRIV_FILE_DOWNGRADE_SL; @@ -1481,7 +1481,7 @@ zfs_sb_rele(zfs_sb_t *zsb, void *tag) if (zsb->z_sb) { deactivate_super(zsb->z_sb); } else { - dmu_objset_disown(zsb->z_os, zsb); + dmu_objset_disown(zsb->z_os, B_TRUE, zsb); zfs_sb_free(zsb); } } @@ -4622,7 +4622,7 @@ zfs_ioc_send(zfs_cmd_t *zc) if (error != 0) return (error); - error = dsl_dataset_hold_crypt_obj(dp, zc->zc_sendobj, + error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &tosnap); if (error != 0) { dsl_pool_rele(dp, FTAG); @@ -4633,7 +4633,7 @@ zfs_ioc_send(zfs_cmd_t *zc) error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &fromsnap); if (error != 0) { - dsl_dataset_rele_crypt(tosnap, FTAG); + dsl_dataset_rele(tosnap, FTAG); dsl_pool_rele(dp, FTAG); return (error); } @@ -4644,7 +4644,7 @@ zfs_ioc_send(zfs_cmd_t *zc) if (fromsnap != NULL) dsl_dataset_rele(fromsnap, FTAG); - dsl_dataset_rele_crypt(tosnap, FTAG); + dsl_dataset_rele(tosnap, FTAG); dsl_pool_rele(dp, FTAG); } else { file_t *fp = getf(zc->zc_cookie); @@ -4999,7 +4999,7 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) error = zfs_suspend_fs(zsb); if (error == 0) { dmu_objset_refresh_ownership(zsb->z_os, - zsb); + B_TRUE, zsb); error = zfs_resume_fs(zsb, zc->zc_name); } } diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 319a5b506478..524a56e32014 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -893,7 +893,7 @@ zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp) return (0); out: - dmu_objset_disown(os, zsb); + dmu_objset_disown(os, B_TRUE, zsb); out_zmo: *zsbp = NULL; zfs_mntopts_free(zsb->z_mntopts); @@ -1481,7 +1481,7 @@ zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent) zsb->z_arc_prune = arc_add_prune_callback(zpl_prune_sb, sb); out: if (error) { - dmu_objset_disown(zsb->z_os, zsb); + dmu_objset_disown(zsb->z_os, B_TRUE, zsb); zfs_sb_free(zsb); /* * make sure we don't have dangling sb->s_fs_info which @@ -1563,7 +1563,7 @@ zfs_umount(struct super_block *sb) /* * Finally release the objset */ - dmu_objset_disown(os, zsb); + dmu_objset_disown(os, B_TRUE, zsb); } zfs_sb_free(zsb); diff --git a/module/zfs/zil.c b/module/zfs/zil.c index 02a56ad59cad..3960e955a168 100644 --- a/module/zfs/zil.c +++ b/module/zfs/zil.c @@ -708,7 +708,7 @@ zil_claim(dsl_pool_t *dp, dsl_dataset_t *ds, void *txarg) zio_free_zil(zilog->zl_spa, first_txg, &zh->zh_log); BP_ZERO(&zh->zh_log); dsl_dataset_dirty(dmu_objset_ds(os), tx); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_FALSE, FTAG); return (0); } @@ -733,7 +733,7 @@ zil_claim(dsl_pool_t *dp, dsl_dataset_t *ds, void *txarg) } ASSERT3U(first_txg, ==, (spa_last_synced_txg(zilog->zl_spa) + 1)); - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_FALSE, FTAG); return (0); } diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 7d8299d30f7a..9b1b08e82224 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -389,7 +389,7 @@ zvol_set_volsize(const char *name, uint64_t volsize) error = zvol_update_live_volsize(zv, volsize); out: if (owned) { - dmu_objset_disown(os, FTAG); + dmu_objset_disown(os, B_TRUE, FTAG); if (zv != NULL) zv->zv_objset = NULL; } @@ -989,7 +989,7 @@ zvol_first_open(zvol_state_t *zv) out_owned: if (error) { - dmu_objset_disown(os, zvol_tag); + dmu_objset_disown(os, 1, zvol_tag); zv->zv_objset = NULL; } @@ -1013,7 +1013,7 @@ zvol_last_close(zvol_state_t *zv) txg_wait_synced(dmu_objset_pool(zv->zv_objset), 0); (void) dmu_objset_evict_dbufs(zv->zv_objset); - dmu_objset_disown(zv->zv_objset, zvol_tag); + dmu_objset_disown(zv->zv_objset, 1, zvol_tag); zv->zv_objset = NULL; } @@ -1449,7 +1449,7 @@ zvol_create_minor_impl(const char *name) zv->zv_objset = NULL; out_dmu_objset_disown: - dmu_objset_disown(os, zvol_tag); + dmu_objset_disown(os, B_TRUE, zvol_tag); out_doi: kmem_free(doi, sizeof (dmu_object_info_t)); out: @@ -1526,7 +1526,7 @@ zvol_prefetch_minors_impl(void *arg) dmu_prefetch(os, ZVOL_OBJ, 0, 0, 0, ZIO_PRIORITY_SYNC_READ); } - dmu_objset_disown(os, zvol_tag); + dmu_objset_disown(os, B_TRUE, zvol_tag); } } diff --git a/module/zpios/pios.c b/module/zpios/pios.c index cc288df7673f..7ae885c9b660 100644 --- a/module/zpios/pios.c +++ b/module/zpios/pios.c @@ -429,7 +429,7 @@ zpios_remove_objset(run_args_t *run_args) } } - dmu_objset_disown(run_args->os, zpios_tag); + dmu_objset_disown(run_args->os, 1, zpios_tag); if (run_args->flags & DMU_REMOVE) { rc = dsl_destroy_head(name); From 4cb9b5c5850c43603a13d007653f21f9c4f9f7fc Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Tue, 17 Jan 2017 14:35:47 -0500 Subject: [PATCH 05/10] further cleanups for dataset owning code --- include/sys/dsl_dataset.h | 2 +- module/zfs/dsl_dataset.c | 45 +++++++++++++-------------------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index 215184ef9af7..4491c6320c7e 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -267,7 +267,7 @@ void dsl_dataset_disown(dsl_dataset_t *ds, int flags, void *tag); void dsl_dataset_name(dsl_dataset_t *ds, char *name); int dsl_dataset_namelen(dsl_dataset_t *ds); boolean_t dsl_dataset_has_owner(dsl_dataset_t *ds); -int dsl_dataset_tryown(dsl_dataset_t *ds, int flags, void *tag); +boolean_t dsl_dataset_tryown(dsl_dataset_t *ds, void *tag); uint64_t dsl_dataset_create_sync(dsl_dir_t *pds, const char *lastname, dsl_dataset_t *origin, uint64_t flags, cred_t *, struct dsl_crypto_params *, dmu_tx_t *); diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index f46ac22fbc5c..119c4190d341 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -557,10 +557,10 @@ dsl_dataset_hold_obj_flags(dsl_pool_t *dp, uint64_t dsobj, int flags, void *tag, dp->dp_origin_snap == NULL || ds == dp->dp_origin_snap); *dsp = ds; - if (flags & DS_HOLD_FLAG_DECRYPT) { + if (flags & DS_HOLD_FLAG_DECRYPT && ds->ds_dir->dd_crypto_obj != 0) { err = spa_keystore_create_mapping(dp->dp_spa, ds, ds); if (err != 0) { - dsl_dataset_rele_flags(ds, flags, tag); + dsl_dataset_rele(ds, tag); return (SET_ERROR(EACCES)); } } @@ -640,15 +640,13 @@ int dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, int flags, void *tag, dsl_dataset_t **dsp) { - int err = dsl_dataset_hold_obj(dp, dsobj, tag, dsp); + int err = dsl_dataset_hold_obj_flags(dp, dsobj, flags, tag, dsp); if (err != 0) return (err); - - err = dsl_dataset_tryown(*dsp, flags, tag); - if (err != 0) { - dsl_dataset_rele(*dsp, tag); + if (!dsl_dataset_tryown(*dsp, tag)) { + dsl_dataset_rele_flags(*dsp, flags, tag); *dsp = NULL; - return (err); + return (SET_ERROR(EBUSY)); } return (0); } @@ -657,14 +655,12 @@ int dsl_dataset_own(dsl_pool_t *dp, const char *name, int flags, void *tag, dsl_dataset_t **dsp) { - int err = dsl_dataset_hold(dp, name, tag, dsp); + int err = dsl_dataset_hold_flags(dp, name, flags, tag, dsp); if (err != 0) return (err); - - err = dsl_dataset_tryown(*dsp, flags, tag); - if (err != 0) { - dsl_dataset_rele(*dsp, tag); - return (err); + if (!dsl_dataset_tryown(*dsp, tag)) { + dsl_dataset_rele_flags(*dsp, flags, tag); + return (SET_ERROR(EBUSY)); } return (0); } @@ -777,31 +773,20 @@ dsl_dataset_disown(dsl_dataset_t *ds, int flags, void *tag) dsl_dataset_rele(ds, tag); } -int -dsl_dataset_tryown(dsl_dataset_t *ds, int flags, void *tag) +boolean_t +dsl_dataset_tryown(dsl_dataset_t *ds, void *tag) { - int ret = 0; - spa_t *spa = ds->ds_dir->dd_pool->dp_spa; - uint64_t dckobj = ds->ds_dir->dd_crypto_obj; + boolean_t gotit = FALSE; ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); mutex_enter(&ds->ds_lock); if (ds->ds_owner == NULL && !DS_IS_INCONSISTENT(ds)) { - if (dckobj != 0 && (flags & DS_HOLD_FLAG_DECRYPT)) { - ret = spa_keystore_create_mapping(spa, ds, ds); - if (ret) { - mutex_exit(&ds->ds_lock); - return (SET_ERROR(EACCES)); - } - } ds->ds_owner = tag; dsl_dataset_long_hold(ds, tag); - } else { - ret = SET_ERROR(EBUSY); + gotit = TRUE; } mutex_exit(&ds->ds_lock); - - return (ret); + return (gotit); } boolean_t From 18e675795765a4155a48489416c75b959f552f8b Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Wed, 18 Jan 2017 13:49:33 -0500 Subject: [PATCH 06/10] fixed dsl key leak introduced with new dataset holding code --- module/zfs/dsl_dataset.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 119c4190d341..2ab1e7c533a1 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -601,7 +601,7 @@ dsl_dataset_hold_flags(dsl_pool_t *dp, const char *name, int flags, dsl_dataset_t *snap_ds; if (*snapname++ != '@') { - dsl_dataset_rele(ds, tag); + dsl_dataset_rele_flags(ds, flags, tag); dsl_dir_rele(dd, FTAG); return (SET_ERROR(ENOENT)); } @@ -612,7 +612,7 @@ dsl_dataset_hold_flags(dsl_pool_t *dp, const char *name, int flags, err = dsl_dataset_hold_obj_flags(dp, obj, flags, tag, &snap_ds); } - dsl_dataset_rele(ds, tag); + dsl_dataset_rele_flags(ds, flags, tag); if (err == 0) { mutex_enter(&snap_ds->ds_lock); @@ -763,14 +763,10 @@ dsl_dataset_disown(dsl_dataset_t *ds, int flags, void *tag) ASSERT(ds->ds_dbuf != NULL); mutex_enter(&ds->ds_lock); - if (flags & DS_HOLD_FLAG_DECRYPT) { - (void) spa_keystore_remove_mapping(ds->ds_dir->dd_pool->dp_spa, - ds, ds); - } ds->ds_owner = NULL; mutex_exit(&ds->ds_lock); dsl_dataset_long_rele(ds, tag); - dsl_dataset_rele(ds, tag); + dsl_dataset_rele_flags(ds, flags, tag); } boolean_t From 891583e96667c515cee61ad56a1e8e17e48524f4 Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Wed, 18 Jan 2017 16:33:27 -0500 Subject: [PATCH 07/10] style and licensing updates --- cmd/zfs/zfs_main.c | 8 +- include/sys/dsl_crypt.h | 28 +++---- include/sys/dsl_dataset.h | 3 +- include/sys/zio_crypt.h | 22 ++--- lib/libzfs/libzfs_crypto.c | 131 ++++++++++++++--------------- module/zfs/arc.c | 30 +++---- module/zfs/dmu_objset.c | 4 +- module/zfs/dsl_crypt.c | 164 ++++++++++++++++++------------------- module/zfs/zfs_ioctl.c | 22 ++--- module/zfs/zil.c | 5 +- module/zfs/zio.c | 4 +- module/zfs/zio_crypt.c | 74 ++++++++--------- 12 files changed, 234 insertions(+), 261 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 3b465b80b06f..f969638e9433 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -6979,7 +6979,7 @@ zfs_do_key(int argc, char **argv) } } - if (ret) { + if (ret != 0) { (void) fprintf(stderr, gettext("No action specified\n")); usage(B_FALSE); @@ -7016,7 +7016,7 @@ zfs_do_key(int argc, char **argv) else ret = zfs_crypto_rewrap(zhp, props); - if (ret) + if (ret != 0) goto error; nvlist_free(props); @@ -7024,9 +7024,9 @@ zfs_do_key(int argc, char **argv) return (0); error: - if (props) + if (props != NULL) nvlist_free(props); - if (zhp) + if (zhp != NULL) zfs_close(zhp); return (-1); } diff --git a/include/sys/dsl_crypt.h b/include/sys/dsl_crypt.h index 6c2d92a28933..8b651fe97054 100644 --- a/include/sys/dsl_crypt.h +++ b/include/sys/dsl_crypt.h @@ -1,24 +1,18 @@ /* * CDDL HEADER START * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. * * CDDL HEADER END */ + /* * Copyright (c) 2016, Datto, Inc. All rights reserved. */ @@ -56,7 +50,11 @@ typedef struct dsl_wrapping_key { uint64_t wk_ddobj; } dsl_wrapping_key_t; -/* structure for passing around encryption params from userspace */ +/* + * This struct is a simple wrapper around all the parameters that are usually + * required to setup encryption. It exists so that all of the params can be + * passed around the kernel together for convenience + */ typedef struct dsl_crypto_params { /* command to be executed */ zfs_ioc_crypto_cmd_t cp_cmd; diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index 4491c6320c7e..d60285eeb01b 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -245,7 +245,8 @@ dsl_dataset_phys(dsl_dataset_t *ds) #define DS_UNIQUE_IS_ACCURATE(ds) \ ((dsl_dataset_phys(ds)->ds_flags & DS_FLAG_UNIQUE_ACCURATE) != 0) -#define DS_HOLD_FLAG_DECRYPT (1 << 0) +/* flags for holding the dataset */ +#define DS_HOLD_FLAG_DECRYPT (1 << 0) /* needs access encrypted data */ int dsl_dataset_hold(struct dsl_pool *dp, const char *name, void *tag, dsl_dataset_t **dsp); diff --git a/include/sys/zio_crypt.h b/include/sys/zio_crypt.h index 32e8ea041169..ccac97429cb6 100644 --- a/include/sys/zio_crypt.h +++ b/include/sys/zio_crypt.h @@ -1,24 +1,18 @@ /* * CDDL HEADER START * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. * * CDDL HEADER END */ + /* * Copyright (c) 2016, Datto, Inc. All rights reserved. */ diff --git a/lib/libzfs/libzfs_crypto.c b/lib/libzfs/libzfs_crypto.c index 82b0a7e499f2..2693a1c42773 100644 --- a/lib/libzfs/libzfs_crypto.c +++ b/lib/libzfs/libzfs_crypto.c @@ -1,21 +1,14 @@ /* * CDDL HEADER START * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. * * CDDL HEADER END */ @@ -140,7 +133,7 @@ keysource_prop_parser(char *keysource, key_format_t *format, return (EINVAL); ret = parse_format(format, s, len); - if (ret) + if (ret != 0) return (ret); s = s + len + 1; @@ -226,7 +219,7 @@ get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, (void) sigaction(SIGTSTP, &act, &osigtstp); /* prompt for the passphrase */ - if (fsname) { + if (fsname != NULL) { (void) printf("%s %s for '%s': ", (!again) ? "Enter" : "Renter", get_format_prompt_string(format), fsname); @@ -245,7 +238,7 @@ get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, new_term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); ret = tcsetattr(fileno(fd), TCSAFLUSH, &new_term); - if (ret) { + if (ret != 0) { ret = errno; errno = 0; goto out; @@ -322,7 +315,7 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, /* fetch the key material into the buffer */ ret = get_key_material_raw(fd, fsname, format, &km, B_FALSE, &kmlen); - if (ret) + if (ret != 0) goto error; /* do basic validation of the key material */ @@ -397,7 +390,7 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, if (do_verify && isatty(fileno(fd))) { ret = get_key_material_raw(fd, fsname, format, &km2, B_TRUE, &kmlen2); - if (ret) + if (ret != 0) goto error; if (kmlen2 != kmlen || @@ -412,7 +405,7 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, if (fd != stdin) fclose(fd); - if (km2) + if (km2 != NULL) free(km2); *km_out = km; @@ -420,10 +413,10 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, return (0); error: - if (km) + if (km != NULL) free(km); - if (km2) + if (km2 != NULL) free(km2); if (fd && fd != stdin) @@ -563,7 +556,7 @@ pbkdf2(uint8_t *passphrase, size_t passphraselen, uint8_t *salt, error: crypto_destroy_ctx_template(tmpl); - if (hmac_key) + if (hmac_key != NULL) free(hmac_key); icp_fini(); thread_fini(); @@ -592,7 +585,7 @@ derive_key(libzfs_handle_t *hdl, key_format_t format, uint64_t iters, case KEY_FORMAT_HEX: ret = hex_key_to_raw((char *)key_material, WRAPPING_KEY_LEN * 2, key); - if (ret) { + if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid hex key provided.")); goto error; @@ -603,7 +596,7 @@ derive_key(libzfs_handle_t *hdl, key_format_t format, uint64_t iters, ret = pbkdf2(key_material, strlen((char *)key_material), ((uint8_t *)&salt), sizeof (uint64_t), iters, key, WRAPPING_KEY_LEN); - if (ret) { + if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to generate key from passphrase.")); goto error; @@ -662,7 +655,7 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, /* Parse the keysource */ ret = keysource_prop_parser(keysource, &keyformat, &keylocator, &uri); - if (ret) { + if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid keysource.")); goto error; } @@ -670,7 +663,7 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, /* get key material from keysource */ ret = get_key_material(hdl, B_TRUE, keyformat, keylocator, uri, fsname, &key_material, &key_material_len); - if (ret) + if (ret != 0) goto error; /* passphrase formats require a salt and pbkdf2 iters property */ @@ -678,7 +671,7 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, /* always generate a new salt */ random_init(); ret = random_get_bytes((uint8_t *)&salt, sizeof (uint64_t)); - if (ret) { + if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to generate salt.")); goto error; @@ -687,7 +680,7 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_SALT), salt); - if (ret) { + if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to add salt to properties.")); goto error; @@ -717,11 +710,11 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, iters = DEFAULT_PBKDF2_ITERATIONS; ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), iters); - if (ret) + if (ret != 0) goto error; } else if (ret == ENOENT) { iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS); - } else if (ret) { + } else if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to get pbkdf2 iterations.")); goto error; @@ -731,13 +724,13 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, /* derive a key from the key material */ ret = derive_key(hdl, keyformat, iters, key_material, key_material_len, salt, &key_data); - if (ret) + if (ret != 0) goto error; /* add the derived key to the properties list */ ret = nvlist_add_uint8_array(hidden_args, "wkeydata", key_data, WRAPPING_KEY_LEN); - if (ret) + if (ret != 0) goto error; free(key_material); @@ -746,9 +739,9 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, return (0); error: - if (key_material) + if (key_material != NULL) free(key_material); - if (key_data) + if (key_data != NULL) free(key_data); return (ret); } @@ -771,16 +764,16 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, /* lookup crypt from props */ ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); - if (ret) + if (ret != 0) local_crypt = B_FALSE; /* lookup keysource from props */ ret = nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); - if (ret) + if (ret != 0) keysource = NULL; - if (parent_name) { + if (parent_name != NULL) { /* get a reference to parent dataset */ pzhp = make_dataset_handle(hdl, parent_name); if (pzhp == NULL) { @@ -833,7 +826,7 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, * Return if encryption is off */ if (crypt == ZIO_CRYPT_OFF) { - if (keysource) { + if (keysource != NULL) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption required to set keysource.")); @@ -859,25 +852,25 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, * If a local keysource is provided, this dataset will * be a new encryption root. populate encryption params */ - if (keysource) { + if (keysource != NULL) { ha = fnvlist_alloc(); ret = populate_create_encryption_params_nvlists(hdl, NULL, keysource, B_TRUE, props, ha); - if (ret) + if (ret != 0) goto error; } - if (pzhp) + if (pzhp != NULL) zfs_close(pzhp); *hidden_args = ha; return (0); error: - if (pzhp) + if (pzhp != NULL) zfs_close(pzhp); - if (ha) + if (ha != NULL) nvlist_free(ha); *hidden_args = NULL; @@ -913,7 +906,7 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, /* lookup keysource from props */ ret = nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); - if (ret) + if (ret != 0) keysource = NULL; /* crypt should not be set */ @@ -940,7 +933,7 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, * sure no encryption parameters are set */ if (pcrypt == ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) { - if (keysource) { + if (keysource != NULL) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption properties may not be set " @@ -976,12 +969,12 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, } /* prepare the keysource if needed */ - if (keysource) { + if (keysource != NULL) { ha = fnvlist_alloc(); ret = populate_create_encryption_params_nvlists(hdl, NULL, keysource, B_TRUE, props, ha); - if (ret) + if (ret != 0) goto out; } @@ -991,9 +984,9 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, return (0); out: - if (pzhp) + if (pzhp != NULL) zfs_close(pzhp); - if (ha) + if (ha != NULL) nvlist_free(ha); *hidden_args = NULL; @@ -1039,7 +1032,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, sizeof (keysource), &keysource_srctype, keysource_src, sizeof (keysource_src), B_TRUE); - if (ret) { + if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to get existing keysource property.")); goto error; @@ -1062,7 +1055,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) /* parse the keysource. This shoudln't fail */ ret = keysource_prop_parser(keysource, &format, &locator, &uri); - if (ret) { + if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Invalid keysource property.")); ret = EINVAL; @@ -1072,7 +1065,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) /* get key material from keysource */ ret = get_key_material(zhp->zfs_hdl, B_FALSE, format, locator, uri, zfs_get_name(zhp), &key_material, &key_material_len); - if (ret) + if (ret != 0) goto error; /* passphrase formats require a salt and pbkdf2_iters property */ @@ -1084,7 +1077,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) /* derive a key from the key material */ ret = derive_key(zhp->zfs_hdl, format, iters, key_material, key_material_len, salt, &key_data); - if (ret) + if (ret != 0) goto error; /* put the key in an nvlist and pass to the ioctl */ @@ -1092,12 +1085,12 @@ zfs_crypto_load_key(zfs_handle_t *zhp) ret = nvlist_add_uint8_array(crypto_args, "wkeydata", key_data, WRAPPING_KEY_LEN); - if (ret) + if (ret != 0) goto error; ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_LOAD_KEY, NULL, crypto_args); - if (ret) { + if (ret != 0) { switch (ret) { case EINVAL: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, @@ -1127,11 +1120,11 @@ zfs_crypto_load_key(zfs_handle_t *zhp) error: zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); - if (key_material) + if (key_material != NULL) free(key_material); - if (key_data) + if (key_data != NULL) free(key_data); - if (crypto_args) + if (crypto_args != NULL) nvlist_free(crypto_args); return (ret); @@ -1170,7 +1163,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, sizeof (keysource), &keysource_srctype, keysource_src, sizeof (keysource_src), B_TRUE); - if (ret) { + if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to get existing keysource property.")); goto error; @@ -1194,7 +1187,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) /* call the ioctl */ ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_UNLOAD_KEY, NULL, NULL); - if (ret) { + if (ret != 0) { switch (ret) { case ENOENT: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, @@ -1242,7 +1235,7 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props, ret = zprop_parse_value(zhp->zfs_hdl, elem, prop, zhp->zfs_type, new_props, &strval, &intval, errbuf); - if (ret) + if (ret != 0) goto error; break; default: @@ -1295,7 +1288,7 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) } ret = zfs_crypto_verify_rewrap_nvlist(zhp, raw_props, &props, errbuf); - if (ret) + if (ret != 0) goto error; /* load keysource from dataset if not specified */ @@ -1305,13 +1298,13 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) keysource_exists = B_FALSE; ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, prop_keysource, sizeof (prop_keysource), NULL, NULL, 0, B_TRUE); - if (ret) { + if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to get existing keysource property.")); goto error; } keysource = prop_keysource; - } else if (ret) { + } else if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Failed to find keysource.")); goto error; @@ -1322,13 +1315,13 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) ret = populate_create_encryption_params_nvlists(zhp->zfs_hdl, zhp, keysource, keysource_exists, props, crypto_args); - if (ret) + if (ret != 0) goto error; /* call the ioctl */ ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_REWRAP, props, crypto_args); - if (ret) { + if (ret != 0) { switch (ret) { case EINVAL: zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, @@ -1348,10 +1341,10 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) return (ret); error: - if (props) + if (props != NULL) nvlist_free(props); - if (crypto_args) + if (crypto_args != NULL) nvlist_free(crypto_args); zfs_error(zhp->zfs_hdl, EZFS_CRYPTOFAILED, errbuf); diff --git a/module/zfs/arc.c b/module/zfs/arc.c index 7412749a71ab..ae934a0a5dd2 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -1780,7 +1780,7 @@ arc_hdr_decrypt(arc_buf_hdr_t *hdr, kmutex_t *hash_lock, spa_t *spa, ASSERT(HDR_HAS_RABD(hdr)); ASSERT3P(hdr->b_l1hdr.b_pabd, ==, NULL); - if (hash_lock) + if (hash_lock != NULL) mutex_enter(hash_lock); arc_hdr_alloc_abd(hdr, B_FALSE); @@ -1795,7 +1795,7 @@ arc_hdr_decrypt(arc_buf_hdr_t *hdr, kmutex_t *hash_lock, spa_t *spa, * won't be accessible via that dsobj anymore. */ ret = spa_keystore_lookup_key(spa, dsobj, FTAG, &dck); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EACCES); goto error; } @@ -1822,7 +1822,7 @@ arc_hdr_decrypt(arc_buf_hdr_t *hdr, kmutex_t *hash_lock, spa_t *spa, ret = zio_decompress_data(HDR_GET_COMPRESS(hdr), hdr->b_l1hdr.b_pabd, tmp, HDR_GET_PSIZE(hdr), HDR_GET_LSIZE(hdr)); - if (ret) { + if (ret != 0) { abd_return_buf(cabd, tmp, arc_hdr_size(hdr)); goto error; } @@ -1835,18 +1835,18 @@ arc_hdr_decrypt(arc_buf_hdr_t *hdr, kmutex_t *hash_lock, spa_t *spa, spa_keystore_dsl_key_rele(spa, dck, FTAG); - if (hash_lock) + if (hash_lock != NULL) mutex_exit(hash_lock); return (0); error: arc_hdr_free_abd(hdr, B_FALSE); - if (dck) + if (dck != NULL) spa_keystore_dsl_key_rele(spa, dck, FTAG); - if (cabd) + if (cabd != NULL) arc_free_data_buf(hdr, cabd, arc_hdr_size(hdr), hdr); - if (hash_lock) + if (hash_lock != NULL) mutex_exit(hash_lock); return (ret); } @@ -1900,7 +1900,7 @@ arc_buf_fill(arc_buf_t *buf, spa_t *spa, uint64_t dsobj, arc_fill_flags_t flags) * to store the decrypted version in the header for future use. */ error = arc_hdr_decrypt(hdr, hash_lock, spa, dsobj); - if (error) + if (error != 0) return (error); } @@ -1926,7 +1926,7 @@ arc_buf_fill(arc_buf_t *buf, spa_t *spa, uint64_t dsobj, arc_fill_flags_t flags) buf->b_flags &= ~ARC_BUF_FLAG_ENCRYPTED; buf->b_flags &= ~ARC_BUF_FLAG_COMPRESSED; - if (hash_lock) { + if (hash_lock != NULL) { mutex_enter(hash_lock); hdr->b_crypt_hdr.b_ebufcnt -= 1; mutex_exit(hash_lock); @@ -7812,14 +7812,14 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, abd_t **abd_out, */ ret = spa_keystore_lookup_key(spa, hdr->b_crypt_hdr.b_dsobj, FTAG, &dck); - if (ret) + if (ret != 0) goto error; ret = zio_do_crypt_abd(B_TRUE, &dck->dck_key, hdr->b_crypt_hdr.b_salt, hdr->b_crypt_hdr.b_ot, hdr->b_crypt_hdr.b_iv, hdr->b_crypt_hdr.b_mac, csize, to_write, eabd); - if (ret) + if (ret != 0) goto error; spa_keystore_dsl_key_rele(spa, dck, FTAG); @@ -7838,11 +7838,11 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, abd_t **abd_out, return (0); error: - if (dck) + if (dck != NULL) spa_keystore_dsl_key_rele(spa, dck, FTAG); - if (cabd) + if (cabd != NULL) abd_free(cabd); - if (eabd) + if (eabd != NULL) abd_free(eabd); *bsize_out = 0; @@ -7988,7 +7988,7 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz) ret = l2arc_apply_transforms(spa, hdr, &to_write, &bsize, &csize); - if (ret) { + if (ret != 0) { arc_hdr_clear_flags(hdr, ARC_FLAG_L2_WRITING); mutex_exit(hash_lock); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index 715f0296403d..f27036afb839 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -1035,11 +1035,11 @@ dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags, ret = dsl_sync_task(name, dmu_objset_create_check, dmu_objset_create_sync, &doca, 5, ZFS_SPACE_CHECK_NORMAL); - if (ret) + if (ret != 0) return (ret); ret = dmu_objset_hold(name, FTAG, &os); - if (ret) + if (ret != 0) return (ret); /* See comment in dmu_objset_create_sync() for details */ diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index bbd83fefd58a..b16dd55b3844 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -1,24 +1,18 @@ /* * CDDL HEADER START * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. * * CDDL HEADER END */ + /* * Copyright (c) 2016, Datto, Inc. All rights reserved. */ @@ -160,29 +154,29 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, uint_t wkeydata_len; /* get relevant properties from the nvlist */ - if (props) { + if (props != NULL) { ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); - if (ret) + if (ret != 0) crypt_exists = B_FALSE; ret = nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); - if (ret) + if (ret != 0) keysource_exists = B_FALSE; ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_SALT), &salt); - if (ret) + if (ret != 0) salt_exists = B_FALSE; ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); - if (ret) + if (ret != 0) iters_exists = B_FALSE; ret = nvlist_lookup_uint64(props, "crypto_cmd", &cmd); - if (ret) + if (ret != 0) cmd_exists = B_FALSE; } else { crypt_exists = B_FALSE; @@ -192,10 +186,10 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, cmd_exists = B_FALSE; } - if (crypto_args) { + if (crypto_args != NULL) { ret = nvlist_lookup_uint8_array(crypto_args, "wkeydata", &wkeydata, &wkeydata_len); - if (ret) + if (ret != 0) wkeydata_exists = B_FALSE; } else { wkeydata_exists = B_FALSE; @@ -236,11 +230,11 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, ret = nvlist_remove_all(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION)); - if (ret) + if (ret != 0) goto error; ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt); - if (ret) + if (ret != 0) goto error; } @@ -248,7 +242,7 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, if (wkeydata_exists) { /* create the wrapping key with the verified parameters */ ret = dsl_wrapping_key_create(wkeydata, &wkey); - if (ret) + if (ret != 0) goto error; } @@ -263,9 +257,9 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, return (0); error: - if (wkey) + if (wkey != NULL) dsl_wrapping_key_free(wkey); - if (dcp) + if (dcp != NULL) kmem_free(dcp, sizeof (dsl_crypto_params_t)); *dcp_out = NULL; @@ -275,7 +269,7 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, void dsl_crypto_params_free(dsl_crypto_params_t *dcp, boolean_t unload) { - if (!dcp) + if (dcp == NULL) return; if (unload) @@ -373,12 +367,12 @@ dsl_dir_hold_keysource_source_dd(dsl_dir_t *dd, void *tag, */ ret = dsl_prop_get_dd(dd, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), 1, sizeof (keysource), keysource, setpoint, B_FALSE); - if (ret) + if (ret != 0) goto error; /* hold the dsl dir that we inherited the property from */ ret = dsl_dir_hold(dd->dd_pool, setpoint, tag, &inherit_dd, NULL); - if (ret) + if (ret != 0) goto error; *inherit_dd_out = inherit_dd; @@ -402,7 +396,7 @@ dsl_dataset_keystore_keystatus(dsl_dataset_t *ds) /* lookup the wkey. if it doesn't exist the key is unavailable */ ret = spa_keystore_wkey_hold_ddobj(ds->ds_dir->dd_pool->dp_spa, ds->ds_dir->dd_object, FTAG, &wkey); - if (ret) + if (ret != 0) return (ZFS_KEYSTATUS_UNAVAILABLE); dsl_wrapping_key_rele(wkey, FTAG); @@ -475,18 +469,18 @@ spa_keystore_wkey_hold_ddobj(spa_t *spa, uint64_t ddobj, void *tag, /* hold the dsl dir */ ret = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd); - if (ret) + if (ret != 0) goto error; /* get the dd that the keysource property was inherited from */ ret = dsl_dir_hold_keysource_source_dd(dd, FTAG, &inherit_dd); - if (ret) + if (ret != 0) goto error; /* lookup the wkey in the avl tree */ ret = spa_keystore_wkey_hold_ddobj_impl(spa, inherit_dd->dd_object, tag, &wkey); - if (ret) + if (ret != 0) goto error; /* unlock the wkey tree if we locked it */ @@ -502,9 +496,9 @@ spa_keystore_wkey_hold_ddobj(spa_t *spa, uint64_t ddobj, void *tag, error: if (locked) rw_exit(&spa->spa_keystore.sk_wkeys_lock); - if (inherit_dd) + if (inherit_dd != NULL) dsl_dir_rele(inherit_dd, FTAG); - if (dd) + if (dd != NULL) dsl_dir_rele(dd, FTAG); *wkey_out = NULL; @@ -554,27 +548,27 @@ dsl_crypto_key_open(objset_t *mos, dsl_wrapping_key_t *wkey, /* fetch all of the values we need from the ZAP */ ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_CRYPT, 8, 1, &crypt); - if (ret) + if (ret != 0) goto error; ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_MASTER_BUF, 1, MAX_MASTER_KEY_LEN, raw_keydata); - if (ret) + if (ret != 0) goto error; ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_HMAC_KEY_BUF, 1, HMAC_SHA256_KEYLEN, raw_hmac_keydata); - if (ret) + if (ret != 0) goto error; ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_IV, 1, WRAPPING_IV_LEN, iv); - if (ret) + if (ret != 0) goto error; ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_MAC, 1, WRAPPING_MAC_LEN, mac); - if (ret) + if (ret != 0) goto error; /* @@ -583,7 +577,7 @@ dsl_crypto_key_open(objset_t *mos, dsl_wrapping_key_t *wkey, */ ret = zio_crypt_key_unwrap(&wkey->wk_key, crypt, raw_keydata, raw_hmac_keydata, iv, mac, &dck->dck_key); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EACCES); goto error; } @@ -599,7 +593,7 @@ dsl_crypto_key_open(objset_t *mos, dsl_wrapping_key_t *wkey, return (0); error: - if (dck) { + if (dck != NULL) { bzero(dck, sizeof (dsl_crypto_key_t)); kmem_free(dck, sizeof (dsl_crypto_key_t)); } @@ -670,7 +664,7 @@ spa_keystore_dsl_key_hold_dd(spa_t *spa, dsl_dir_t *dd, void *tag, /* lookup the wrapping key from the keystore */ ret = spa_keystore_wkey_hold_ddobj(spa, dd->dd_object, FTAG, &wkey); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EACCES); goto error_unlock; } @@ -678,7 +672,7 @@ spa_keystore_dsl_key_hold_dd(spa_t *spa, dsl_dir_t *dd, void *tag, /* read the key from disk */ ret = dsl_crypto_key_open(spa_get_dsl(spa)->dp_meta_objset, wkey, dckobj, tag, &dck); - if (ret) + if (ret != 0) goto error_unlock; /* @@ -698,7 +692,7 @@ spa_keystore_dsl_key_hold_dd(spa_t *spa, dsl_dir_t *dd, void *tag, error_unlock: rw_exit(&spa->spa_keystore.sk_dk_lock); - if (wkey) + if (wkey != NULL) dsl_wrapping_key_rele(wkey, FTAG); *dck_out = NULL; @@ -729,7 +723,7 @@ spa_keystore_load_wkey_impl(spa_t *spa, dsl_wrapping_key_t *wkey) /* insert the wrapping key into the keystore */ found_wkey = avl_find(&spa->spa_keystore.sk_wkeys, wkey, &where); - if (found_wkey) { + if (found_wkey != NULL) { ret = SET_ERROR(EEXIST); goto error_unlock; } @@ -760,12 +754,12 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp) return (SET_ERROR(EINVAL)); ret = dsl_pool_hold(dsname, FTAG, &dp); - if (ret) + if (ret != 0) goto error; /* hold the dsl dir */ ret = dsl_dir_hold(dp, dsname, FTAG, &dd, NULL); - if (ret) + if (ret != 0) goto error; /* initialize the wkey's ddobj */ @@ -774,12 +768,12 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp) /* verify that the wkey is correct by opening its dsl key */ ret = dsl_crypto_key_open(dp->dp_meta_objset, wkey, dd->dd_crypto_obj, FTAG, &dck); - if (ret) + if (ret != 0) goto error; /* insert the wrapping key into the keystore */ ret = spa_keystore_load_wkey_impl(dp->dp_spa, wkey); - if (ret) + if (ret != 0) goto error; dsl_crypto_key_rele(dck, FTAG); @@ -792,11 +786,11 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp) return (0); error: - if (dck) + if (dck != NULL) dsl_crypto_key_rele(dck, FTAG); - if (dd) + if (dd != NULL) dsl_dir_rele(dd, FTAG); - if (dp) + if (dp != NULL) dsl_pool_rele(dp, FTAG); return (ret); @@ -847,16 +841,16 @@ spa_keystore_unload_wkey(const char *dsname) /* hold the dsl dir */ ret = dsl_pool_hold(dsname, FTAG, &dp); - if (ret) + if (ret != 0) goto error; ret = dsl_dir_hold(dp, dsname, FTAG, &dd, NULL); - if (ret) + if (ret != 0) goto error; /* unload the wkey */ ret = spa_keystore_unload_wkey_impl(dp->dp_spa, dd->dd_object); - if (ret) + if (ret != 0) goto error; dsl_dir_rele(dd, FTAG); @@ -868,9 +862,9 @@ spa_keystore_unload_wkey(const char *dsname) return (0); error: - if (dd) + if (dd != NULL) dsl_dir_rele(dd, FTAG); - if (dp) + if (dp != NULL) dsl_pool_rele(dp, FTAG); return (ret); @@ -894,7 +888,7 @@ spa_keystore_create_mapping(spa_t *spa, dsl_dataset_t *ds, void *tag) ret = spa_keystore_dsl_key_hold_dd(spa, ds->ds_dir, km, &km->km_key); - if (ret) + if (ret != 0) goto error; km->km_dsobj = ds->ds_object; @@ -910,7 +904,7 @@ spa_keystore_create_mapping(spa_t *spa, dsl_dataset_t *ds, void *tag) * are times when there could be multiple async users. */ found_km = avl_find(&spa->spa_keystore.sk_key_mappings, km, &where); - if (found_km) { + if (found_km != NULL) { should_free = B_TRUE; refcount_add(&found_km->km_refcnt, tag); } else { @@ -1021,14 +1015,14 @@ spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag, rw_exit(&spa->spa_keystore.sk_km_lock); - if (dck_out) + if (dck_out != NULL) *dck_out = found_km->km_key; return (0); error_unlock: rw_exit(&spa->spa_keystore.sk_km_lock); - if (dck_out) + if (dck_out != NULL) *dck_out = NULL; return (ret); } @@ -1094,7 +1088,7 @@ spa_keystore_rewrap_check(void *arg, dmu_tx_t *tx) /* hold the dd */ ret = dsl_dir_hold(dp, skra->skra_dsname, FTAG, &dd, NULL); - if (ret) + if (ret != 0) return (ret); /* check that this dd has a dsl key */ @@ -1105,7 +1099,7 @@ spa_keystore_rewrap_check(void *arg, dmu_tx_t *tx) /* make sure the dsl key is loaded / loadable */ ret = spa_keystore_dsl_key_hold_dd(dp->dp_spa, dd, FTAG, &dck); - if (ret) + if (ret != 0) goto error; ASSERT(dck->dck_wkey != NULL); @@ -1116,7 +1110,7 @@ spa_keystore_rewrap_check(void *arg, dmu_tx_t *tx) return (0); error: - if (dck) + if (dck != NULL) spa_keystore_dsl_key_rele(dp->dp_spa, dck, FTAG); dsl_dir_rele(dd, FTAG); @@ -1199,7 +1193,7 @@ spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) ASSERT(!ds->ds_is_snapshot); /* set additional properties which can be sent along with this ioctl */ - if (keysource) + if (keysource != NULL) dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), ZPROP_SRC_LOCAL, 1, strlen(keysource) + 1, keysource, tx); @@ -1234,7 +1228,7 @@ spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) * existed). Replace the wrapping key. */ found_wkey = avl_find(&spa->spa_keystore.sk_wkeys, wkey, NULL); - if (found_wkey) { + if (found_wkey != NULL) { ASSERT0(refcount_count(&found_wkey->wk_refcnt)); avl_remove(&spa->spa_keystore.sk_wkeys, found_wkey); dsl_wrapping_key_free(found_wkey); @@ -1277,10 +1271,10 @@ dmu_objset_create_encryption_check(dsl_dir_t *pdd, dsl_crypto_params_t *dcp) ret = dsl_prop_get_dd(pdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &pcrypt, NULL, B_FALSE); - if (ret) + if (ret != 0) return (ret); - if (dcp) { + if (dcp != NULL) { crypt = dcp->cp_crypt; wkey = dcp->cp_wkey; salt = dcp->cp_salt; @@ -1309,7 +1303,7 @@ dmu_objset_create_encryption_check(dsl_dir_t *pdd, dsl_crypto_params_t *dcp) if (!wkey && pcrypt != ZIO_CRYPT_OFF) { ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, pdd->dd_object, FTAG, &wkey); - if (ret) + if (ret != 0) return (SET_ERROR(EACCES)); dsl_wrapping_key_rele(wkey, FTAG); @@ -1334,15 +1328,15 @@ dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, ret = dsl_prop_get_dd(pdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &pcrypt, NULL, B_FALSE); - if (ret) + if (ret != 0) return (ret); ret = dsl_prop_get_dd(odd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &ocrypt, NULL, B_FALSE); - if (ret) + if (ret != 0) return (ret); - if (dcp) { + if (dcp != NULL) { crypt = dcp->cp_crypt; wkey = dcp->cp_wkey; salt = dcp->cp_salt; @@ -1366,7 +1360,7 @@ dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, if (ocrypt != ZIO_CRYPT_OFF) { ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, odd->dd_object, FTAG, &wkey); - if (ret) + if (ret != 0) return (SET_ERROR(EACCES)); dsl_wrapping_key_rele(wkey, FTAG); @@ -1376,7 +1370,7 @@ dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, if (!wkey && pcrypt != ZIO_CRYPT_OFF) { ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, pdd->dd_object, FTAG, &wkey); - if (ret) + if (ret != 0) return (SET_ERROR(EACCES)); dsl_wrapping_key_rele(wkey, FTAG); @@ -1477,13 +1471,13 @@ spa_crypt_get_salt(spa_t *spa, uint64_t dsobj, uint8_t *salt) /* look up the key from the spa's keystore */ ret = spa_keystore_lookup_key(spa, dsobj, NULL, &dck); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EACCES); goto error; } ret = zio_crypt_key_get_salt(&dck->dck_key, salt); - if (ret) + if (ret != 0) goto error; return (0); @@ -1514,7 +1508,7 @@ spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, /* look up the key from the spa's keystore */ ret = spa_keystore_lookup_key(spa, zb->zb_objset, NULL, &dck); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EACCES); goto error; } @@ -1538,23 +1532,23 @@ spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, */ if (encrypt && ot != DMU_OT_INTENT_LOG && !BP_GET_DEDUP(bp)) { ret = zio_crypt_key_get_salt(&dck->dck_key, salt); - if (ret) + if (ret != 0) goto error; ret = zio_crypt_generate_iv(iv); - if (ret) + if (ret != 0) goto error; } else if (encrypt && BP_GET_DEDUP(bp)) { ret = zio_crypt_generate_iv_salt_dedup(&dck->dck_key, plainbuf, datalen, iv, salt); - if (ret) + if (ret != 0) goto error; } /* call lower level function to perform encryption / decryption */ ret = zio_do_crypt_data(encrypt, &dck->dck_key, salt, ot, iv, mac, datalen, plainbuf, cipherbuf); - if (ret) + if (ret != 0) goto error; if (encrypt) { @@ -1576,9 +1570,9 @@ spa_do_crypt_abd(boolean_t encrypt, spa_t *spa, zbookmark_phys_t *zb, ZIL_MAC_LEN : DATA_MAC_LEN); } - if (plainbuf) + if (plainbuf != NULL) abd_return_buf(pabd, plainbuf, datalen); - if (cipherbuf) + if (cipherbuf != NULL) abd_return_buf(cabd, cipherbuf, datalen); return (ret); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 973275f7a554..23bbecb2c56c 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -1296,7 +1296,7 @@ zfs_secpolicy_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) uint64_t crypto_cmd; ret = nvlist_lookup_uint64(innvl, "crypto_cmd", &crypto_cmd); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EINVAL); goto out; } @@ -5746,7 +5746,7 @@ zfs_ioc_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) spa_t *spa; ret = spa_open(dsname, &spa, FTAG); - if (ret) + if (ret != 0) return (ret); if (!spa_feature_is_enabled(spa, SPA_FEATURE_ENCRYPTION)) { @@ -5762,7 +5762,7 @@ zfs_ioc_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) } ret = nvlist_lookup_uint64(innvl, "crypto_cmd", &crypto_cmd); - if (ret) { + if (ret != 0) { ret = (SET_ERROR(EINVAL)); goto error; } @@ -5771,46 +5771,46 @@ zfs_ioc_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) case ZFS_IOC_KEY_LOAD_KEY: ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EINVAL); goto error; } ret = dsl_crypto_params_create_nvlist(NULL, hidden_args, &dcp); - if (ret) + if (ret != 0) goto error; ret = spa_keystore_load_wkey(dsname, dcp); - if (ret) + if (ret != 0) goto error; break; case ZFS_IOC_KEY_UNLOAD_KEY: ret = spa_keystore_unload_wkey(dsname); - if (ret) + if (ret != 0) goto error; break; case ZFS_IOC_KEY_REWRAP: ret = nvlist_lookup_nvlist(innvl, "args", &args); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EINVAL); goto error; } ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); - if (ret) { + if (ret != 0) { ret = SET_ERROR(EINVAL); goto error; } ret = dsl_crypto_params_create_nvlist(args, hidden_args, &dcp); - if (ret) + if (ret != 0) goto error; ret = spa_keystore_rewrap(dsname, dcp); - if (ret) + if (ret != 0) goto error; break; diff --git a/module/zfs/zil.c b/module/zfs/zil.c index 3960e955a168..553a23d973ec 100644 --- a/module/zfs/zil.c +++ b/module/zfs/zil.c @@ -264,8 +264,7 @@ zil_read_log_block(zilog_t *zilog, boolean_t decrypt, const blkptr_t *bp, } } - if (abuf) - arc_buf_destroy(abuf, &abuf); + arc_buf_destroy(abuf, &abuf); } return (error); @@ -445,7 +444,7 @@ zil_claim_log_record(zilog_t *zilog, lr_t *lrc, void *tx, uint64_t first_txg) */ if (lr->lr_blkptr.blk_birth >= first_txg) { error = zil_read_log_data(zilog, lr, NULL); - if (error) + if (error != 0) return (error); } diff --git a/module/zfs/zio.c b/module/zfs/zio.c index ed8e4e389146..b1a398ca8b62 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -419,7 +419,7 @@ zio_decrypt(zio_t *zio, abd_t *data, uint64_t size) bp->blk_birth, size, data, zio->io_abd, iv, mac, salt); if (ret == ZIO_NO_ENCRYPTION_NEEDED) { ASSERT3U(BP_GET_TYPE(bp), ==, DMU_OT_INTENT_LOG); - } else if (ret) { + } else if (ret != 0) { zio->io_error = ret; } } @@ -3580,7 +3580,7 @@ zio_encrypt(zio_t *zio) /* Perform the encryption. This should not fail */ ret = spa_do_crypt_abd(B_TRUE, spa, &zio->io_bookmark, bp, zio->io_txg, psize, zio->io_abd, eabd, iv, mac, salt); - if (ret) { + if (ret != 0) { /* * Dnode blocks and ZIL blocks may not need encryption if there * is no private data in the blocks. We cannot disable diff --git a/module/zfs/zio_crypt.c b/module/zfs/zio_crypt.c index 192ddfd0dcca..296622d7ff58 100644 --- a/module/zfs/zio_crypt.c +++ b/module/zfs/zio_crypt.c @@ -1,24 +1,18 @@ /* * CDDL HEADER START * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. * * CDDL HEADER END */ + /* * Copyright (c) 2016, Datto, Inc. All rights reserved. */ @@ -285,12 +279,12 @@ hkdf_sha256(uint8_t *key_material, uint_t km_len, uint8_t *salt, ret = hkdf_sha256_extract(salt, salt_len, key_material, km_len, extract_key); - if (ret) + if (ret != 0) goto error; ret = hkdf_sha256_expand(extract_key, info, info_len, output_key, out_len); - if (ret) + if (ret != 0) goto error; return (0); @@ -326,21 +320,21 @@ zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key) /* fill keydata buffers and salt with random data */ ret = random_get_bytes(key->zk_master_keydata, keydata_len); - if (ret) + if (ret != 0) goto error; ret = random_get_bytes(key->zk_hmac_keydata, HMAC_SHA256_KEYLEN); - if (ret) + if (ret != 0) goto error; ret = random_get_bytes(key->zk_salt, DATA_SALT_LEN); - if (ret) + if (ret != 0) goto error; /* derive the current key from the master key */ ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, key->zk_salt, DATA_SALT_LEN, key->zk_current_keydata, keydata_len); - if (ret) + if (ret != 0) goto error; /* initialize keys for the ICP */ @@ -389,7 +383,7 @@ zio_crypt_key_change_salt(zio_crypt_key_t *key) /* generate a new salt */ ret = random_get_bytes(salt, DATA_SALT_LEN); - if (ret) + if (ret != 0) goto error; rw_enter(&key->zk_salt_lock, RW_WRITER); @@ -397,7 +391,7 @@ zio_crypt_key_change_salt(zio_crypt_key_t *key) /* derive the current key from the master key and the new salt */ ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, salt, DATA_SALT_LEN, key->zk_current_keydata, keydata_len); - if (ret) + if (ret != 0) goto error_unlock; /* assign the salt and reset the usage count */ @@ -438,7 +432,7 @@ zio_crypt_key_get_salt(zio_crypt_key_t *key, uint8_t *salt) if (salt_change) { ret = zio_crypt_key_change_salt(key); - if (ret) + if (ret != 0) goto error; } @@ -566,7 +560,7 @@ zio_crypt_key_wrap(crypto_key_t *cwkey, zio_crypt_key_t *key, uint8_t *iv, /* generate iv for wrapping the master and hmac key */ ret = random_get_pseudo_bytes(iv, WRAPPING_IV_LEN); - if (ret) + if (ret != 0) goto error; /* initialize uio_ts */ @@ -593,7 +587,7 @@ zio_crypt_key_wrap(crypto_key_t *cwkey, zio_crypt_key_t *key, uint8_t *iv, /* encrypt the keys and store the resulting ciphertext and mac */ ret = zio_do_crypt_uio(B_TRUE, crypt, cwkey, NULL, iv, enc_len, &puio, &cuio); - if (ret) + if (ret != 0) goto error; return (0); @@ -644,18 +638,18 @@ zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint8_t *keydata, /* decrypt the keys and store the result in the output buffers */ ret = zio_do_crypt_uio(B_FALSE, crypt, cwkey, NULL, iv, enc_len, &puio, &cuio); - if (ret) + if (ret != 0) goto error; /* generate a fresh salt */ ret = random_get_bytes(key->zk_salt, DATA_SALT_LEN); - if (ret) + if (ret != 0) goto error; /* derive the current key from the master key */ ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, key->zk_salt, DATA_SALT_LEN, key->zk_current_keydata, keydata_len); - if (ret) + if (ret != 0) goto error; /* initialize keys for ICP */ @@ -701,7 +695,7 @@ zio_crypt_generate_iv(uint8_t *ivbuf) /* randomly generate the IV */ ret = random_get_pseudo_bytes(ivbuf, DATA_IV_LEN); - if (ret) + if (ret != 0) goto error; return (0); @@ -993,9 +987,9 @@ zio_crypt_init_uios_zil(boolean_t encrypt, uint8_t *plainbuf, return (0); error: - if (src_iovecs) + if (src_iovecs != NULL) kmem_free(src_iovecs, nr_src * sizeof (iovec_t)); - if (dst_iovecs) + if (dst_iovecs != NULL) kmem_free(dst_iovecs, nr_dst * sizeof (iovec_t)); *enc_len = 0; @@ -1115,9 +1109,9 @@ zio_crypt_init_uios_dnode(boolean_t encrypt, uint8_t *plainbuf, return (0); error: - if (src_iovecs) + if (src_iovecs != NULL) kmem_free(src_iovecs, nr_src * sizeof (iovec_t)); - if (dst_iovecs) + if (dst_iovecs != NULL) kmem_free(dst_iovecs, nr_dst * sizeof (iovec_t)); *enc_len = 0; @@ -1166,9 +1160,9 @@ zio_crypt_init_uios_normal(boolean_t encrypt, uint8_t *plainbuf, return (0); error: - if (plain_iovecs) + if (plain_iovecs != NULL) kmem_free(plain_iovecs, nr_plain * sizeof (iovec_t)); - if (cipher_iovecs) + if (cipher_iovecs != NULL) kmem_free(cipher_iovecs, nr_cipher * sizeof (iovec_t)); *enc_len = 0; @@ -1210,7 +1204,7 @@ zio_crypt_init_uios(boolean_t encrypt, dmu_object_type_t ot, uint8_t *plainbuf, } /* return the error or ZIO_NO_ENCRYPTION_NEEDED to the caller */ - if (ret) + if (ret != 0) goto error; /* populate the uios */ @@ -1252,7 +1246,7 @@ zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, mac, &puio, &cuio, &enc_len); /* return the error or ZIO_NO_ENCRYPTION_NEEDED to the caller */ - if (ret) + if (ret != 0) return (ret); /* @@ -1273,7 +1267,7 @@ zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, ret = hkdf_sha256(key->zk_master_keydata, keydata_len, NULL, 0, salt, DATA_SALT_LEN, enc_keydata, keydata_len); - if (ret) + if (ret != 0) goto error; tmp_ckey.ck_format = CRYPTO_KEY_RAW; @@ -1287,7 +1281,7 @@ zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, /* perform the encryption / decryption */ ret = zio_do_crypt_uio(encrypt, key->zk_crypt, ckey, tmpl, iv, enc_len, &puio, &cuio); - if (ret) + if (ret != 0) goto error; if (locked) { @@ -1335,7 +1329,7 @@ zio_do_crypt_abd(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt, ret = zio_do_crypt_data(encrypt, key, salt, ot, iv, mac, datalen, ptmp, ctmp); - if (ret) + if (ret != 0) goto error; if (encrypt) { From 71434a257245ffedc1fd128cf2896a9ab23d8b6b Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Wed, 18 Jan 2017 16:51:09 -0500 Subject: [PATCH 08/10] several one-line fixes and name cleanups --- cmd/zinject/translate.c | 8 ++++---- include/sys/dsl_crypt.h | 2 +- module/zfs/dsl_crypt.c | 35 +++++++++++++++++++---------------- module/zfs/dsl_destroy.c | 4 ++-- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/cmd/zinject/translate.c b/cmd/zinject/translate.c index 1dd132c2997f..4b3169e889e4 100644 --- a/cmd/zinject/translate.c +++ b/cmd/zinject/translate.c @@ -179,7 +179,7 @@ object_from_path(const char *dataset, const char *path, struct stat64 *statbuf, */ sync(); - err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, B_TRUE, FTAG, &os); + err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, B_FALSE, FTAG, &os); if (err != 0) { (void) fprintf(stderr, "cannot open dataset '%s': %s\n", dataset, strerror(err)); @@ -189,7 +189,7 @@ object_from_path(const char *dataset, const char *path, struct stat64 *statbuf, record->zi_objset = dmu_objset_id(os); record->zi_object = statbuf->st_ino; - dmu_objset_disown(os, B_TRUE, FTAG); + dmu_objset_disown(os, B_FALSE, FTAG); return (0); } @@ -267,7 +267,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, * size. */ if ((err = dmu_objset_own(dataset, DMU_OST_ANY, - B_TRUE, B_TRUE, FTAG, &os)) != 0) { + B_TRUE, B_FALSE, FTAG, &os)) != 0) { (void) fprintf(stderr, "cannot open dataset '%s': %s\n", dataset, strerror(err)); goto out; @@ -329,7 +329,7 @@ calculate_range(const char *dataset, err_type_t type, int level, char *range, dnode_rele(dn, FTAG); } if (os) - dmu_objset_disown(os, B_TRUE, FTAG); + dmu_objset_disown(os, B_FALSE, FTAG); return (ret); } diff --git a/include/sys/dsl_crypt.h b/include/sys/dsl_crypt.h index 8b651fe97054..7f794f867219 100644 --- a/include/sys/dsl_crypt.h +++ b/include/sys/dsl_crypt.h @@ -60,7 +60,7 @@ typedef struct dsl_crypto_params { zfs_ioc_crypto_cmd_t cp_cmd; /* the encryption algorithm */ - uint64_t cp_crypt; + enum zio_encrypt cp_crypt; /* the salt, if the keysource is of type passphrase */ uint64_t cp_salt; diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index b16dd55b3844..9a2afa6e6b3b 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -1257,19 +1257,21 @@ spa_keystore_rewrap(const char *dsname, dsl_crypto_params_t *dcp) } int -dmu_objset_create_encryption_check(dsl_dir_t *pdd, dsl_crypto_params_t *dcp) +dmu_objset_create_encryption_check(dsl_dir_t *parentdd, + dsl_crypto_params_t *dcp) { int ret; dsl_wrapping_key_t *wkey = NULL; - uint64_t cmd = 0, salt = 0, iters = 0; + uint64_t salt = 0, iters = 0; + zfs_ioc_crypto_cmd_t cmd = ZFS_IOC_KEY_CMD_NONE; uint64_t pcrypt, crypt = ZIO_CRYPT_INHERIT; const char *keysource = NULL; - if (!spa_feature_is_enabled(pdd->dd_pool->dp_spa, + if (!spa_feature_is_enabled(parentdd->dd_pool->dp_spa, SPA_FEATURE_ENCRYPTION) && dcp) return (SET_ERROR(EINVAL)); - ret = dsl_prop_get_dd(pdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + ret = dsl_prop_get_dd(parentdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &pcrypt, NULL, B_FALSE); if (ret != 0) return (ret); @@ -1297,12 +1299,13 @@ dmu_objset_create_encryption_check(dsl_dir_t *pdd, dsl_crypto_params_t *dcp) return (SET_ERROR(EINVAL)); if (keysource && strncmp(keysource, "passphrase", 10) == 0 && (!salt || !iters)) + return (SET_ERROR(EINVAL)); if (cmd) return (SET_ERROR(EINVAL)); if (!wkey && pcrypt != ZIO_CRYPT_OFF) { - ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, - pdd->dd_object, FTAG, &wkey); + ret = spa_keystore_wkey_hold_ddobj(parentdd->dd_pool->dp_spa, + parentdd->dd_object, FTAG, &wkey); if (ret != 0) return (SET_ERROR(EACCES)); @@ -1313,7 +1316,7 @@ dmu_objset_create_encryption_check(dsl_dir_t *pdd, dsl_crypto_params_t *dcp) } int -dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, +dmu_objset_clone_encryption_check(dsl_dir_t *parentdd, dsl_dir_t *origindd, dsl_crypto_params_t *dcp) { int ret; @@ -1322,17 +1325,17 @@ dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, uint64_t pcrypt, ocrypt, crypt = ZIO_CRYPT_INHERIT; const char *keysource = NULL; - if (!spa_feature_is_enabled(pdd->dd_pool->dp_spa, + if (!spa_feature_is_enabled(parentdd->dd_pool->dp_spa, SPA_FEATURE_ENCRYPTION) && dcp) return (SET_ERROR(EINVAL)); - ret = dsl_prop_get_dd(pdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, - &pcrypt, NULL, B_FALSE); + ret = dsl_prop_get_dd(parentdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &pcrypt, NULL, B_FALSE); if (ret != 0) return (ret); - ret = dsl_prop_get_dd(odd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, - &ocrypt, NULL, B_FALSE); + ret = dsl_prop_get_dd(origindd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &ocrypt, NULL, B_FALSE); if (ret != 0) return (ret); @@ -1358,8 +1361,8 @@ dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, /* origin wrapping key must be present, if it is encrypted */ if (ocrypt != ZIO_CRYPT_OFF) { - ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, - odd->dd_object, FTAG, &wkey); + ret = spa_keystore_wkey_hold_ddobj(parentdd->dd_pool->dp_spa, + origindd->dd_object, FTAG, &wkey); if (ret != 0) return (SET_ERROR(EACCES)); @@ -1368,8 +1371,8 @@ dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, /* parent's wrapping key must be present if a new one isn't specified */ if (!wkey && pcrypt != ZIO_CRYPT_OFF) { - ret = spa_keystore_wkey_hold_ddobj(pdd->dd_pool->dp_spa, - pdd->dd_object, FTAG, &wkey); + ret = spa_keystore_wkey_hold_ddobj(parentdd->dd_pool->dp_spa, + parentdd->dd_object, FTAG, &wkey); if (ret != 0) return (SET_ERROR(EACCES)); diff --git a/module/zfs/dsl_destroy.c b/module/zfs/dsl_destroy.c index 2bd6101b46f6..25f5c721efbe 100644 --- a/module/zfs/dsl_destroy.c +++ b/module/zfs/dsl_destroy.c @@ -950,7 +950,7 @@ dsl_destroy_head(const char *name) * remove the objects from open context so that the txg sync * is not too long. */ - error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, B_TRUE, + error = dmu_objset_own(name, DMU_OST_ANY, B_FALSE, B_FALSE, FTAG, &os); if (error == 0) { uint64_t obj; @@ -963,7 +963,7 @@ dsl_destroy_head(const char *name) (void) dmu_free_long_object(os, obj); /* sync out all frees */ txg_wait_synced(dmu_objset_pool(os), 0); - dmu_objset_disown(os, B_TRUE, FTAG); + dmu_objset_disown(os, B_FALSE, FTAG); } } From 39b0a1181ef20dc65ec7fbe70c677b9429a9f463 Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Thu, 26 Jan 2017 11:16:11 -0500 Subject: [PATCH 09/10] first attempt at splitting keysource into keyformat and keylocation. needs testing. man pages / tests not updated --- cmd/zfs/zfs_main.c | 4 +- include/sys/dsl_crypt.h | 37 ++- include/sys/fs/zfs.h | 8 +- include/sys/zio_crypt.h | 1 + lib/libzfs/libzfs_crypto.c | 523 +++++++++++++++++------------------- lib/libzfs/libzfs_dataset.c | 31 ++- module/zcommon/zfs_prop.c | 52 +++- module/zfs/dmu_objset.c | 4 +- module/zfs/dsl_crypt.c | 507 +++++++++++++++++++++------------- module/zfs/dsl_dataset.c | 30 +-- module/zfs/dsl_dir.c | 4 +- module/zfs/zfs_ioctl.c | 16 +- 12 files changed, 724 insertions(+), 493 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index f969638e9433..16dabc403bce 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -326,8 +326,8 @@ get_usage(zfs_help_t idx) return (gettext("\tbookmark \n")); case HELP_KEY: return (gettext("\tkey [-lu] \n" - "\tkey -c [-o keysource=] [-o pbkfd2iters=] " - "\n")); + "\tkey -c [-o keyformat=] [-o keylocation=] " + "[-o pbkfd2iters=] \n")); } abort(); diff --git a/include/sys/dsl_crypt.h b/include/sys/dsl_crypt.h index 7f794f867219..cf64ea4b7ec6 100644 --- a/include/sys/dsl_crypt.h +++ b/include/sys/dsl_crypt.h @@ -29,12 +29,31 @@ /* forward declarations */ struct dsl_dataset; +/* enums used by both userspace and kernelspace */ typedef enum zfs_keystatus { ZFS_KEYSTATUS_NONE = 0, ZFS_KEYSTATUS_UNAVAILABLE, ZFS_KEYSTATUS_AVAILABLE, } zfs_keystatus_t; +typedef enum zfs_keyformat { + ZFS_KEYFORMAT_NONE = 0, + ZFS_KEYFORMAT_RAW, + ZFS_KEYFORMAT_HEX, + ZFS_KEYFORMAT_PASSPHRASE, + ZFS_KEYFORMAT_FORMATS +} zfs_keyformat_t; + +typedef enum zfs_key_location { + ZFS_KEYLOCATION_NONE, + ZFS_KEYLOCATION_PROMPT, + ZFS_KEYLOCATION_URI, + ZFS_KEYLOCATION_LOCATIONS +} zfs_keylocation_t; + +#define DEFAULT_PBKDF2_ITERATIONS 350000 +#define MIN_PBKDF2_ITERATIONS 100000 + /* in memory representation of a wrapping key */ typedef struct dsl_wrapping_key { /* link into the keystore's tree of wrapping keys */ @@ -62,14 +81,17 @@ typedef struct dsl_crypto_params { /* the encryption algorithm */ enum zio_encrypt cp_crypt; - /* the salt, if the keysource is of type passphrase */ + /* keyformat property enum */ + zfs_keyformat_t cp_keyformat; + + /* the pckdf2 salt, if the keyformat is of type passphrase */ uint64_t cp_salt; - /* the pbkdf2 iterations, if the keysource is of type passphrase */ + /* the pbkdf2 iterations, if the keyformat is of type passphrase */ uint64_t cp_iters; - /* keysource property string */ - const char *cp_keysource; + /* keylocation property string */ + char *cp_keylocation; /* the wrapping key */ dsl_wrapping_key_t *cp_wkey; @@ -141,6 +163,7 @@ int dsl_wrapping_key_create(uint8_t *wkeydata, dsl_wrapping_key_t **wkey_out); int dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, dsl_crypto_params_t **dcp_out); void dsl_crypto_params_free(dsl_crypto_params_t *dcp, boolean_t unload); +int dsl_crypto_can_set_keylocation(const char *dsname); void spa_keystore_init(spa_keystore_t *sk); void spa_keystore_fini(spa_keystore_t *sk); @@ -161,13 +184,11 @@ int spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag, dsl_crypto_key_t **dck_out); int spa_keystore_rewrap(const char *dsname, dsl_crypto_params_t *dcp); -int dmu_objset_create_encryption_check(dsl_dir_t *pdd, - dsl_crypto_params_t *dcp); -int dmu_objset_clone_encryption_check(dsl_dir_t *pdd, dsl_dir_t *odd, +int dmu_objset_create_crypt_check(dsl_dir_t *parentdd, dsl_dir_t *origindd, dsl_crypto_params_t *dcp); uint64_t dsl_crypto_key_create_sync(uint64_t crypt, dsl_wrapping_key_t *wkey, dmu_tx_t *tx); -uint64_t dsl_crypto_key_clone_sync(dsl_dir_t *orig_dd, +uint64_t dsl_crypto_key_clone_sync(dsl_dir_t *origindd, dsl_wrapping_key_t *wkey, dmu_tx_t *tx); void dsl_crypto_key_destroy_sync(uint64_t dckobj, dmu_tx_t *tx); diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index fa2c5d16e4a0..0923ff6fb46c 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -164,9 +164,10 @@ typedef enum { ZFS_PROP_PREV_SNAP, ZFS_PROP_RECEIVE_RESUME_TOKEN, ZFS_PROP_ENCRYPTION, - ZFS_PROP_SALT, + ZFS_PROP_KEYLOCATION, + ZFS_PROP_KEYFORMAT, + ZFS_PROP_PBKDF2_SALT, ZFS_PROP_PBKDF2_ITERS, - ZFS_PROP_KEYSOURCE, ZFS_PROP_KEYSTATUS, ZFS_NUM_PROPS } zfs_prop_t; @@ -277,7 +278,8 @@ uint64_t zfs_prop_default_numeric(zfs_prop_t); boolean_t zfs_prop_readonly(zfs_prop_t); boolean_t zfs_prop_inheritable(zfs_prop_t); boolean_t zfs_prop_setonce(zfs_prop_t); -boolean_t zfs_prop_encryption_key_param(zfs_prop_t prop); +boolean_t zfs_prop_encryption_key_param(zfs_prop_t); +boolean_t zfs_prop_valid_keylocation(const char *); const char *zfs_prop_to_name(zfs_prop_t); zfs_prop_t zfs_name_to_prop(const char *); boolean_t zfs_prop_user(const char *); diff --git a/include/sys/zio_crypt.h b/include/sys/zio_crypt.h index ccac97429cb6..f9675ba350ee 100644 --- a/include/sys/zio_crypt.h +++ b/include/sys/zio_crypt.h @@ -67,6 +67,7 @@ typedef enum zfs_ioc_crypto_cmd { ZFS_IOC_KEY_LOAD_KEY, ZFS_IOC_KEY_UNLOAD_KEY, ZFS_IOC_KEY_REWRAP, + ZFS_IOC_KEY_COMMANDS } zfs_ioc_crypto_cmd_t; typedef enum zio_crypt_type { diff --git a/lib/libzfs/libzfs_crypto.c b/lib/libzfs/libzfs_crypto.c index 2693a1c42773..89788b93c649 100644 --- a/lib/libzfs/libzfs_crypto.c +++ b/lib/libzfs/libzfs_crypto.c @@ -65,82 +65,18 @@ typedef enum key_locator { #define MIN_PASSPHRASE_LEN 8 #define MAX_PASSPHRASE_LEN 64 -#define DEFAULT_PBKDF2_ITERATIONS 100000 -#define MIN_PBKDF2_ITERATIONS 10000 static int caught_interrupt; -boolean_t -zfs_prop_encryption_key_param(zfs_prop_t prop) +static zfs_keylocation_t +zfs_prop_parse_keylocation(const char *str) { - return (prop == ZFS_PROP_SALT || prop == ZFS_PROP_PBKDF2_ITERS || - prop == ZFS_PROP_KEYSOURCE); -} - -static int -parse_format(key_format_t *format, char *s, int len) -{ - if (strncmp("raw", s, len) == 0 && len == 3) - *format = KEY_FORMAT_RAW; - else if (strncmp("hex", s, len) == 0 && len == 3) - *format = KEY_FORMAT_HEX; - else if (strncmp("passphrase", s, len) == 0 && len == 10) - *format = KEY_FORMAT_PASSPHRASE; - else - return (EINVAL); - - return (0); -} - -static int -parse_locator(key_locator_t *locator, char *s, int len, char **uri) -{ - if (len == 6 && strncmp("prompt", s, 6) == 0) { - *locator = KEY_LOCATOR_PROMPT; - return (0); - } - - /* uri can currently only be an absolut file path */ - if (len > 8 && strncmp("file:///", s, 8) == 0) { - *locator = KEY_LOCATOR_URI; - *uri = s; - return (0); - } - - return (EINVAL); -} - -static int -keysource_prop_parser(char *keysource, key_format_t *format, - key_locator_t *locator, char **uri) -{ - int len, ret; - int keysource_len = strlen(keysource); - char *s = keysource; - - *format = KEY_FORMAT_NONE; - *locator = KEY_LOCATOR_NONE; - - if (keysource_len > ZPOOL_MAXPROPLEN) - return (EINVAL); - - for (len = 0; len < keysource_len; len++) - if (s[len] == ',') - break; - - /* If we are at the end of the key property, there is a problem */ - if (len == keysource_len) - return (EINVAL); - - ret = parse_format(format, s, len); - if (ret != 0) - return (ret); - - s = s + len + 1; - len = keysource_len - len - 1; - ret = parse_locator(locator, s, len, uri); + if (strlen(str) == 6 && strncmp("prompt", str, 6) == 0) + return (ZFS_KEYLOCATION_PROMPT); + else if (strlen(str) > 8 && strncmp("file:///", str, 8) == 0) + return (ZFS_KEYLOCATION_URI); - return (ret); + return (ZFS_KEYLOCATION_NONE); } static int @@ -181,11 +117,11 @@ static char * get_format_prompt_string(key_format_t format) { switch (format) { - case KEY_FORMAT_RAW: + case ZFS_KEYFORMAT_RAW: return ("raw key"); - case KEY_FORMAT_HEX: + case ZFS_KEYFORMAT_HEX: return ("hex key"); - case KEY_FORMAT_PASSPHRASE: + case ZFS_KEYFORMAT_PASSPHRASE: return ("passphrase"); default: /* shouldn't happen */ @@ -194,8 +130,8 @@ get_format_prompt_string(key_format_t format) } static int -get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, - uint8_t **buf, boolean_t again, size_t *len_out) +get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat, + boolean_t again, uint8_t **buf, size_t *len_out) { int ret = 0, bytes; size_t buflen = 0; @@ -218,20 +154,20 @@ get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, act.sa_handler = SIG_IGN; (void) sigaction(SIGTSTP, &act, &osigtstp); - /* prompt for the passphrase */ + /* prompt for the key */ if (fsname != NULL) { (void) printf("%s %s for '%s': ", (!again) ? "Enter" : "Renter", - get_format_prompt_string(format), fsname); + get_format_prompt_string(keyformat), fsname); } else { (void) printf("%s %s: ", (!again) ? "Enter" : "Renter", - get_format_prompt_string(format)); + get_format_prompt_string(keyformat)); } (void) fflush(stdout); - /* disable the terminal echo for passphrase input */ + /* disable the terminal echo for key input */ (void) tcgetattr(fileno(fd), &old_term); new_term = old_term; @@ -246,11 +182,33 @@ get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, } /* read the key material */ - bytes = getline((char **)buf, &buflen, fd); - if (bytes < 0) { - ret = errno; - errno = 0; - goto out; + if (keyformat != ZFS_KEYFORMAT_RAW) { + bytes = getline((char **)buf, &buflen, fd); + if (bytes < 0) { + ret = errno; + errno = 0; + goto out; + } + } else { + /* + * Raw keys may have newline characters in them and so can't + * use getline(). Read 32 bytes directly instead. + */ + *buf = malloc(32 * sizeof (char)); + if (*buf == NULL) { + ret = ENOMEM; + goto out; + } + + bytes = fread(buf, 1, 32, fd); + if (bytes < 0) { + /* size errors are handled by the calling function */ + free(*buf); + *buf = NULL; + ret = errno; + errno = 0; + goto out; + } } /* trim the ending newline if it exists */ @@ -273,7 +231,7 @@ get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, (void) kill(getpid(), caught_interrupt); } - /* print the newline that was not echo'ed */ + /* print the newline that was not echo'd */ printf("\n"); } @@ -282,22 +240,26 @@ get_key_material_raw(FILE *fd, const char *fsname, key_format_t format, } static int -get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, - key_locator_t locator, char *uri, const char *fsname, uint8_t **km_out, - size_t *kmlen_out) +get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, + zfs_keyformat_t keyformat, char *keylocation, const char *fsname, + uint8_t **km_out, size_t *kmlen_out) { int ret, i; + zfs_keylocation_t keyloc; FILE *fd = NULL; uint8_t *km = NULL, *km2 = NULL; size_t kmlen, kmlen2; + /* verify and parse the keylocation */ + keyloc = zfs_prop_parse_keylocation(keylocation); + /* open the appropriate file descriptor */ - switch (locator) { - case KEY_LOCATOR_PROMPT: + switch (keyloc) { + case ZFS_KEYLOCATION_PROMPT: fd = stdin; break; - case KEY_LOCATOR_URI: - fd = fopen(&uri[7], "r"); + case ZFS_KEYLOCATION_URI: + fd = fopen(&keylocation[7], "r"); if (!fd) { ret = errno; errno = 0; @@ -309,18 +271,18 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, default: ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Invalid key locator.")); + "Invalid keylocation.")); goto error; } /* fetch the key material into the buffer */ - ret = get_key_material_raw(fd, fsname, format, &km, B_FALSE, &kmlen); + ret = get_key_material_raw(fd, fsname, keyformat, B_FALSE, &km, &kmlen); if (ret != 0) goto error; /* do basic validation of the key material */ - switch (format) { - case KEY_FORMAT_RAW: + switch (keyformat) { + case ZFS_KEYFORMAT_RAW: /* verify the key length is correct */ if (kmlen < WRAPPING_KEY_LEN) { ret = EINVAL; @@ -338,7 +300,7 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, goto error; } break; - case KEY_FORMAT_HEX: + case ZFS_KEYFORMAT_HEX: /* verify the key length is correct */ if (kmlen < WRAPPING_KEY_LEN * 2) { ret = EINVAL; @@ -366,7 +328,7 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, } } break; - case KEY_FORMAT_PASSPHRASE: + case ZFS_KEYFORMAT_PASSPHRASE: /* verify the length is correct */ if (kmlen > MAX_PASSPHRASE_LEN) { ret = EINVAL; @@ -388,16 +350,16 @@ get_key_material(libzfs_handle_t *hdl, boolean_t do_verify, key_format_t format, } if (do_verify && isatty(fileno(fd))) { - ret = get_key_material_raw(fd, fsname, format, &km2, - B_TRUE, &kmlen2); + ret = get_key_material_raw(fd, fsname, keyformat, B_TRUE, &km2, + &kmlen2); if (ret != 0) goto error; if (kmlen2 != kmlen || - (strncmp((char *)km, (char *)km2, kmlen) != 0)) { + (memcmp((char *)km, (char *)km2, kmlen) != 0)) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Passphrases do not match.")); + "Provided keys do not match.")); goto error; } } @@ -640,34 +602,24 @@ encryption_feature_is_enabled(zpool_handle_t *zph) static int populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, - zfs_handle_t *zhp, char *keysource, boolean_t new_ks, nvlist_t *props, - nvlist_t *hidden_args) + zfs_handle_t *zhp, zfs_keyformat_t keyformat, char *keylocation, + nvlist_t *props, nvlist_t *hidden_args) { int ret; uint64_t iters, salt = 0; - key_format_t keyformat; - key_locator_t keylocator; uint8_t *key_material = NULL; size_t key_material_len = 0; uint8_t *key_data = NULL; - char *uri; const char *fsname = (zhp) ? zfs_get_name(zhp) : NULL; - /* Parse the keysource */ - ret = keysource_prop_parser(keysource, &keyformat, &keylocator, &uri); - if (ret != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid keysource.")); - goto error; - } - - /* get key material from keysource */ - ret = get_key_material(hdl, B_TRUE, keyformat, keylocator, uri, - fsname, &key_material, &key_material_len); + /* get key material from keyformat and keylocation */ + ret = get_key_material(hdl, B_TRUE, keyformat, keylocation, fsname, + &key_material, &key_material_len); if (ret != 0) goto error; /* passphrase formats require a salt and pbkdf2 iters property */ - if (keyformat == KEY_FORMAT_PASSPHRASE) { + if (keyformat == ZFS_KEYFORMAT_PASSPHRASE) { /* always generate a new salt */ random_init(); ret = random_get_bytes((uint8_t *)&salt, sizeof (uint64_t)); @@ -678,8 +630,8 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, } random_fini(); - ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_SALT), - salt); + ret = nvlist_add_uint64(props, + zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), salt); if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to add salt to properties.")); @@ -687,33 +639,19 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, } /* - * If we are not changing the keysource we use the number of - * iterations we already have. If the user specifies a number - * validate that it is above the mimimum. + * If not otherwise specified, use the default number of + * pbkdf2 iterations. If specified, we have already checked + * that the given value is greater than MIN_PBKDF2_ITERATIONS + * during zfs_valid_proplist(). */ - ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); - if (!ret && iters < MIN_PBKDF2_ITERATIONS) { - ret = EINVAL; - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Minimum pbkdf2 iterations is %u."), - MIN_PBKDF2_ITERATIONS); - goto error; - } else if (!ret && !new_ks) { - ret = EINVAL; - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Setting pbkdf2 iterations requires " - "specifying keysource.")); - goto error; - } else if (ret == ENOENT && new_ks) { + if (ret == ENOENT) { iters = DEFAULT_PBKDF2_ITERATIONS; ret = nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), iters); if (ret != 0) goto error; - } else if (ret == ENOENT) { - iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS); } else if (ret != 0) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Failed to get pbkdf2 iterations.")); @@ -746,14 +684,45 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl, return (ret); } +static boolean_t +proplist_has_encryption_props(nvlist_t *props) +{ + int ret; + uint64_t intval; + char *strval; + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &intval); + if (ret == 0 && intval != ZIO_CRYPT_OFF) + return (B_TRUE); + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &intval); + if (ret == 0) + return (B_TRUE); + + ret = nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &strval); + if (ret == 0) + return (B_TRUE); + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &intval); + if (ret == 0) + return (B_TRUE); + + return (B_FALSE); +} + int zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, nvlist_t *pool_props, nvlist_t **hidden_args) { int ret; char errbuf[1024]; - uint64_t crypt = 0, pcrypt = 0; - char *keysource = NULL; + uint64_t crypt = ZIO_CRYPT_INHERIT, pcrypt = ZIO_CRYPT_INHERIT; + uint64_t keyformat = ZFS_KEYFORMAT_NONE; + char *keylocation = NULL; zfs_handle_t *pzhp = NULL; nvlist_t *ha = NULL; boolean_t local_crypt = B_TRUE; @@ -767,11 +736,11 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, if (ret != 0) local_crypt = B_FALSE; - /* lookup keysource from props */ - ret = nvlist_lookup_string(props, - zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); - if (ret != 0) - keysource = NULL; + /* lookup key location and format from props */ + (void) nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat); + (void) nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation); if (parent_name != NULL) { /* get a reference to parent dataset */ @@ -785,25 +754,29 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, /* Lookup parent's crypt */ pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); - /* Check for encryption feature */ + /* Params require the encryption feature */ if (!encryption_feature_is_enabled(pzhp->zpool_hdl)) { - if (!local_crypt && !keysource) { - ret = 0; - goto error; + if (proplist_has_encryption_props(props)) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Encryption feature not enabled.")); + goto out; } - ret = EINVAL; - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Encryption feature not enabled.")); - goto error; + ret = 0; + goto out; } } else { + /* + * special case for root dataset where encryption feature + * feature won't be on disk yet + */ if (!nvlist_exists(pool_props, "feature@encryption") && local_crypt) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); - goto error; + goto out; } pcrypt = ZIO_CRYPT_OFF; @@ -814,51 +787,56 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Invalid encryption value. Dataset must be encrypted.")); - goto error; + goto out; } - /* Get inherited the encryption property if we don't have it locally */ + /* Get the inherited encryption property if we don't have it locally */ if (!local_crypt) crypt = pcrypt; /* * At this point crypt should be the actual encryption value. - * Return if encryption is off + * Return 0 if encryption is off */ if (crypt == ZIO_CRYPT_OFF) { - if (keysource != NULL) { + if (proplist_has_encryption_props(props)) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Encryption required to set keysource.")); - goto error; + "Encryption must be turned on to set encryption " + "properties.")); + goto out; } ret = 0; - goto error; + goto out; } + /* default to prompt if no location is specified */ + if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) + keylocation = "prompt"; + /* - * If the parent doesn't have a keysource to inherit - * we need one provided + * If the parent doesn't have a keyformat to inherit + * we need one provided to us */ - if (pcrypt == ZIO_CRYPT_OFF && !keysource) { + if (pcrypt == ZIO_CRYPT_OFF && keyformat == ZFS_KEYFORMAT_NONE) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Keysource required.")); - goto error; + "Keyformat required.")); + goto out; } /* - * If a local keysource is provided, this dataset will - * be a new encryption root. populate encryption params + * If a local key format is provided, this dataset will be a new + * encryption root. Populate the encryption params. */ - if (keysource != NULL) { + if (keyformat != ZFS_KEYFORMAT_NONE) { ha = fnvlist_alloc(); ret = populate_create_encryption_params_nvlists(hdl, NULL, - keysource, B_TRUE, props, ha); + keyformat, keylocation, props, ha); if (ret != 0) - goto error; + goto out; } if (pzhp != NULL) @@ -867,7 +845,7 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, *hidden_args = ha; return (0); -error: +out: if (pzhp != NULL) zfs_close(pzhp); if (ha != NULL) @@ -883,7 +861,8 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, { int ret; char errbuf[1024]; - char *keysource = NULL; + uint64_t keyformat = ZFS_KEYFORMAT_NONE; + char *keylocation = NULL; nvlist_t *ha = NULL; zfs_handle_t *pzhp = NULL; uint64_t crypt, pcrypt, ocrypt, okey_status; @@ -891,7 +870,7 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Encryption clone error")); - /* get a reference to parent dataset, should never be null */ + /* get a reference to parent dataset, should never be NULL */ pzhp = make_dataset_handle(hdl, parent_name); if (pzhp == NULL) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -903,13 +882,13 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, pcrypt = zfs_prop_get_int(pzhp, ZFS_PROP_ENCRYPTION); ocrypt = zfs_prop_get_int(origin_zhp, ZFS_PROP_ENCRYPTION); - /* lookup keysource from props */ - ret = nvlist_lookup_string(props, - zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); - if (ret != 0) - keysource = NULL; + /* lookup keylocation from props */ + (void) nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat); + (void) nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation); - /* crypt should not be set */ + /* encryption should not be set since it must match the origin */ ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); if (ret == 0) { @@ -929,11 +908,11 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, } /* - * if neither parent nor the origin is encrypted check to make - * sure no encryption parameters are set + * If this dataset won't be encrypted check to ensure no encryption + * params were set and return. */ - if (pcrypt == ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) { - if (keysource != NULL) { + if (ocrypt == ZIO_CRYPT_OFF) { + if (proplist_has_encryption_props(props)) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Encryption properties may not be set " @@ -945,6 +924,10 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, goto out; } + /* default to prompt if no location is specified */ + if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) + keylocation = "prompt"; + /* * by this point this dataset will be encrypted. The origin's * wrapping key must be loaded @@ -958,22 +941,22 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, } /* - * if the parent doesn't have a keysource to inherit we need + * if the parent doesn't have a key format to inherit we need * one provided for us */ - if (pcrypt == ZIO_CRYPT_OFF && !keysource) { + if (pcrypt == ZIO_CRYPT_OFF && keyformat == ZFS_KEYFORMAT_NONE) { ret = EINVAL; zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Keysource required.")); + "Keyformat required.")); goto out; } - /* prepare the keysource if needed */ - if (keysource != NULL) { + /* prepare the key if needed */ + if (keyformat != ZFS_KEYFORMAT_NONE) { ha = fnvlist_alloc(); ret = populate_create_encryption_params_nvlists(hdl, NULL, - keysource, B_TRUE, props, ha); + keyformat, keylocation, props, ha); if (ret != 0) goto out; } @@ -998,16 +981,14 @@ zfs_crypto_load_key(zfs_handle_t *zhp) { int ret; char errbuf[1024]; - uint64_t crypt, keystatus, iters = 0, salt = 0; - char keysource[MAXNAMELEN]; - char keysource_src[MAXNAMELEN]; - key_format_t format; - key_locator_t locator; - char *uri; + uint64_t keystatus, iters = 0, salt = 0; + uint64_t keyformat; + char prop_keylocation[MAXNAMELEN]; + char keylocation_src[MAXNAMELEN]; uint8_t *key_material = NULL, *key_data = NULL; size_t key_material_len; nvlist_t *crypto_args = NULL; - zprop_source_t keysource_srctype; + zprop_source_t keylocation_srctype; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key load error")); @@ -1019,27 +1000,30 @@ zfs_crypto_load_key(zfs_handle_t *zhp) goto error; } - /* fetch relevent info from the dataset properties */ - crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); - if (crypt == ZIO_CRYPT_OFF) { + /* Fetch the keyformat. Check that the dataset is encrypted. */ + keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); + if (keyformat == ZFS_KEYFORMAT_NONE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Encryption not enabled for this dataset.")); ret = EINVAL; goto error; } - /* check that we are loading for an encryption root */ - ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, - sizeof (keysource), &keysource_srctype, keysource_src, - sizeof (keysource_src), B_TRUE); + /* + * Fetch the key location. Check that we are working with an + * encryption root. + */ + ret = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION, prop_keylocation, + sizeof (prop_keylocation), &keylocation_srctype, keylocation_src, + sizeof (keylocation_src), B_TRUE); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Failed to get existing keysource property.")); + "Failed to get keylocation property.")); goto error; - } else if (keysource_srctype == ZPROP_SRC_INHERITED) { + } else if (keylocation_srctype == ZPROP_SRC_INHERITED) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Keys must be loaded for encryption root '%s'."), - keysource_src); + keylocation_src); ret = EINVAL; goto error; } @@ -1053,29 +1037,21 @@ zfs_crypto_load_key(zfs_handle_t *zhp) goto error; } - /* parse the keysource. This shoudln't fail */ - ret = keysource_prop_parser(keysource, &format, &locator, &uri); - if (ret != 0) { - zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Invalid keysource property.")); - ret = EINVAL; - goto error; - } - - /* get key material from keysource */ - ret = get_key_material(zhp->zfs_hdl, B_FALSE, format, locator, uri, - zfs_get_name(zhp), &key_material, &key_material_len); + /* get key material from key format and location */ + ret = get_key_material(zhp->zfs_hdl, B_FALSE, keyformat, + prop_keylocation, zfs_get_name(zhp), &key_material, + &key_material_len); if (ret != 0) goto error; /* passphrase formats require a salt and pbkdf2_iters property */ - if (format == KEY_FORMAT_PASSPHRASE) { - salt = zfs_prop_get_int(zhp, ZFS_PROP_SALT); + if (keyformat == ZFS_KEYFORMAT_PASSPHRASE) { + salt = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_SALT); iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS); } /* derive a key from the key material */ - ret = derive_key(zhp->zfs_hdl, format, iters, key_material, + ret = derive_key(zhp->zfs_hdl, keyformat, iters, key_material, key_material_len, salt, &key_data); if (ret != 0) goto error; @@ -1135,10 +1111,10 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) { int ret; char errbuf[1024]; - char keysource[MAXNAMELEN]; - char keysource_src[MAXNAMELEN]; - uint64_t crypt, keystatus; - zprop_source_t keysource_srctype; + char prop_keylocation[MAXNAMELEN]; + char keylocation_src[MAXNAMELEN]; + uint64_t keystatus, keyformat; + zprop_source_t keylocation_srctype; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key unload error")); @@ -1150,27 +1126,30 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) goto error; } - /* fetch relevent info from the dataset properties */ - crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); - if (crypt == ZIO_CRYPT_OFF) { + /* Fetch the keyformat. Check that the dataset is encrypted. */ + keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); + if (keyformat == ZFS_KEYFORMAT_NONE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Encryption not enabled.")); + "Encryption not enabled for this dataset.")); ret = EINVAL; goto error; } - /* check that we are loading for an encryption root */ - ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource, - sizeof (keysource), &keysource_srctype, keysource_src, - sizeof (keysource_src), B_TRUE); + /* + * Fetch the key location. Check that we are working with an + * encryption root. + */ + ret = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION, prop_keylocation, + sizeof (prop_keylocation), &keylocation_srctype, keylocation_src, + sizeof (keylocation_src), B_TRUE); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Failed to get existing keysource property.")); + "Failed to get keylocation property.")); goto error; - } else if (keysource_srctype == ZPROP_SRC_INHERITED) { + } else if (keylocation_srctype == ZPROP_SRC_INHERITED) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Keys must be unloaded for encryption root '%s'."), - keysource_src); + keylocation_src); ret = EINVAL; goto error; } @@ -1214,8 +1193,6 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props, { int ret; nvpair_t *elem = NULL; - char *strval = NULL; - uint64_t intval = 0; zfs_prop_t prop; nvlist_t *new_props = NULL; @@ -1223,7 +1200,8 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props, /* * loop through all provided properties, we should only have - * keysource and pbkdf2iters. + * keyformat, keylocation and pbkdf2iters. The actual validation of + * values is done by zfs_valid_proplist(). */ while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { const char *propname = nvpair_name(elem); @@ -1231,22 +1209,24 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props, switch (prop) { case ZFS_PROP_PBKDF2_ITERS: - case ZFS_PROP_KEYSOURCE: - ret = zprop_parse_value(zhp->zfs_hdl, elem, prop, - zhp->zfs_type, new_props, &strval, &intval, - errbuf); - if (ret != 0) - goto error; + case ZFS_PROP_KEYFORMAT: + case ZFS_PROP_KEYLOCATION: break; default: ret = EINVAL; zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Only keysource and pbkdf2iters may " + "Only keyformat, keylocation and pbkdf2iters may " "be set with this command.")); goto error; } } + new_props = zfs_valid_proplist(zhp->zfs_hdl, zhp->zfs_type, props, + zfs_prop_get_int(zhp, ZFS_PROP_ZONED), NULL, zhp->zpool_hdl, + B_TRUE, errbuf); + if (new_props == NULL) + goto error; + *props_out = new_props; return (0); @@ -1263,9 +1243,9 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) char errbuf[1024]; nvlist_t *crypto_args = NULL; uint64_t crypt; - char prop_keysource[MAXNAMELEN]; - char *keysource; - boolean_t keysource_exists = B_TRUE; + char prop_keylocation[MAXNAMELEN]; + uint64_t keyformat = ZFS_KEYFORMAT_NONE; + char *keylocation = NULL; nvlist_t *props = NULL; (void) snprintf(errbuf, sizeof (errbuf), @@ -1282,45 +1262,50 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); if (crypt == ZIO_CRYPT_OFF) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Encryption not enabled.")); + "Encryption not enabled for this dataset.")); ret = EINVAL; goto error; } + /* validate the provided properties */ ret = zfs_crypto_verify_rewrap_nvlist(zhp, raw_props, &props, errbuf); if (ret != 0) goto error; - /* load keysource from dataset if not specified */ - ret = nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), - &keysource); - if (ret == ENOENT) { - keysource_exists = B_FALSE; - ret = zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, prop_keysource, - sizeof (prop_keysource), NULL, NULL, 0, B_TRUE); + /* + * Load keyformat and keylocation from the nvlist. Fetch from the + * dataset properties if not specified. + */ + (void) nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat); + (void) nvlist_lookup_string(props, + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation); + + if (keyformat == ZFS_KEYFORMAT_NONE) + keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); + + if (keylocation == NULL) { + ret = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION, prop_keylocation, + sizeof (prop_keylocation), NULL, NULL, 0, B_TRUE); if (ret != 0) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Failed to get existing keysource property.")); + "Failed to get existing keylocation property.")); goto error; } - keysource = prop_keysource; - } else if (ret != 0) { - zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Failed to find keysource.")); - goto error; + + keylocation = prop_keylocation; } /* populate an nvlist with the encryption params */ crypto_args = fnvlist_alloc(); ret = populate_create_encryption_params_nvlists(zhp->zfs_hdl, zhp, - keysource, keysource_exists, props, crypto_args); + keyformat, keylocation, props, crypto_args); if (ret != 0) goto error; /* call the ioctl */ - ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_REWRAP, props, - crypto_args); + ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_REWRAP, props, crypto_args); if (ret != 0) { switch (ret) { case EINVAL: diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 6e64717c113c..2269ba98dffe 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include "zfs_namecheck.h" @@ -1282,6 +1283,23 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl, zfs_uninit_libshare(hdl); } + break; + case ZFS_PROP_KEYLOCATION: + if (!zfs_prop_valid_keylocation(strval)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid keylocation")); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + break; + case ZFS_PROP_PBKDF2_ITERS: + if (intval < MIN_PBKDF2_ITERATIONS) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "minimum pbkdf2 iterations is %u"), + MIN_PBKDF2_ITERATIONS); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } break; case ZFS_PROP_UTF8ONLY: chosen_utf = (int)intval; @@ -1520,6 +1538,17 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, } break; + case EACCES: + if (prop == ZFS_PROP_KEYLOCATION) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Keylocation may only be set if is set locally. " + "This may be changed with 'zfs key -c'")); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + } else { + (void) zfs_standard_error(hdl, err, errbuf); + } + break; + case EOVERFLOW: /* * This platform can't address a volume this big. @@ -4112,7 +4141,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive, } else if (errno == EACCES) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Only encryption roots may be moved. " - "Please set a local keysource.")); + "Please set a local key.")); (void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf); } else { (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf); diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 767516be66f1..9b72239e3a3a 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -131,6 +131,14 @@ zfs_prop_init(void) { NULL } }; + static zprop_index_t keyformat_table[] = { + { "none", ZFS_KEYFORMAT_NONE }, + { "raw", ZFS_KEYFORMAT_RAW }, + { "hex", ZFS_KEYFORMAT_HEX }, + { "passphrase", ZFS_KEYFORMAT_PASSPHRASE }, + { NULL } + }; + static zprop_index_t snapdir_table[] = { { "hidden", ZFS_SNAPDIR_HIDDEN }, { "visible", ZFS_SNAPDIR_VISIBLE }, @@ -382,6 +390,9 @@ zfs_prop_init(void) ZFS_CASE_SENSITIVE, PROP_ONETIME, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "sensitive | insensitive | mixed", "CASE", case_table); + zprop_register_index(ZFS_PROP_KEYFORMAT, "keyformat", + ZFS_KEYFORMAT_NONE, PROP_ONETIME, ZFS_TYPE_DATASET, + "none | raw | hex | passphrase", "KEYFORMAT", keyformat_table); /* set once index (boolean) properties */ zprop_register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME, @@ -424,9 +435,9 @@ zfs_prop_init(void) "receive_resume_token", NULL, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "", "RESUMETOK"); - zprop_register_string(ZFS_PROP_KEYSOURCE, "keysource", - "none", PROP_ONETIME, ZFS_TYPE_DATASET, - ",", "KEYSOURCE"); + zprop_register_string(ZFS_PROP_KEYLOCATION, "keylocation", + "none", PROP_INHERIT, ZFS_TYPE_DATASET, "prompt | ", + "KEYLOCATION"); /* readonly number properties */ zprop_register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY, @@ -523,7 +534,7 @@ zfs_prop_init(void) PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET, "INCONSISTENT"); zprop_register_hidden(ZFS_PROP_PREV_SNAP, "prevsnap", PROP_TYPE_STRING, PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "PREVSNAP"); - zprop_register_hidden(ZFS_PROP_SALT, "salt", + zprop_register_hidden(ZFS_PROP_PBKDF2_SALT, "pbkdf2salt", PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_DATASET, "SALT"); /* @@ -716,6 +727,37 @@ zfs_prop_inheritable(zfs_prop_t prop) zfs_prop_table[prop].pd_attr == PROP_ONETIME); } +/* + * Returns TRUE if property is one of the encryption properties that requires + * a loaded encryption key to modify. + */ +boolean_t +zfs_prop_encryption_key_param(zfs_prop_t prop) +{ + /* + * keylocation does not count as an encryption property. It can be + * changed at will without needing the master keys. + */ + return (prop == ZFS_PROP_PBKDF2_SALT || prop == ZFS_PROP_PBKDF2_ITERS || + prop == ZFS_PROP_KEYFORMAT); +} + +/* + * Helper function used by both kernelspace and userspace to check the + * keylpcation property. + */ +boolean_t +zfs_prop_valid_keylocation(const char *str) +{ + if (strlen(str) == 6 && strncmp("prompt", str, 6) == 0) + return (B_TRUE); + else if (strlen(str) > 8 && strncmp("file:///", str, 8) == 0) + return (B_TRUE); + + return (B_FALSE); +} + + #ifndef _KERNEL /* @@ -796,6 +838,8 @@ EXPORT_SYMBOL(zfs_prop_default_string); EXPORT_SYMBOL(zfs_prop_default_numeric); EXPORT_SYMBOL(zfs_prop_readonly); EXPORT_SYMBOL(zfs_prop_inheritable); +EXPORT_SYMBOL(zfs_prop_encryption_key_param); +EXPORT_SYMBOL(zfs_prop_valid_keylocation); EXPORT_SYMBOL(zfs_prop_setonce); EXPORT_SYMBOL(zfs_prop_to_name); EXPORT_SYMBOL(zfs_name_to_prop); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index f27036afb839..bdd6b90321fc 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -955,7 +955,7 @@ dmu_objset_create_check(void *arg, dmu_tx_t *tx) return (SET_ERROR(EEXIST)); } - error = dmu_objset_create_encryption_check(pdd, doca->doca_dcp); + error = dmu_objset_create_crypt_check(pdd, NULL, doca->doca_dcp); if (error != 0) { dsl_dir_rele(pdd, FTAG); return (error); @@ -1101,7 +1101,7 @@ dmu_objset_clone_check(void *arg, dmu_tx_t *tx) return (SET_ERROR(EINVAL)); } - error = dmu_objset_clone_encryption_check(pdd, origin->ds_dir, + error = dmu_objset_create_crypt_check(pdd, origin->ds_dir, doca->doca_dcp); if (error != 0) { dsl_dataset_rele(origin, FTAG); diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index 9a2afa6e6b3b..b5e9e185a721 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -143,115 +143,128 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, dsl_crypto_params_t **dcp_out) { int ret; + boolean_t do_inherit = B_TRUE; + uint64_t crypt = ZIO_CRYPT_INHERIT; + uint64_t keyformat = ZFS_KEYFORMAT_NONE; dsl_crypto_params_t *dcp = NULL; dsl_wrapping_key_t *wkey = NULL; - boolean_t crypt_exists = B_TRUE, wkeydata_exists = B_TRUE; - boolean_t keysource_exists = B_TRUE, salt_exists = B_TRUE; - boolean_t iters_exists = B_TRUE, cmd_exists = B_TRUE; - char *keysource = NULL; - uint64_t iters = 0, salt = 0, crypt = 0, cmd = ZFS_IOC_KEY_CMD_NONE; - uint8_t *wkeydata; - uint_t wkeydata_len; + uint8_t *wkeydata = NULL; + uint_t wkeydata_len = 0; + char *keylocation = NULL; + + dcp = kmem_zalloc(sizeof (dsl_crypto_params_t), KM_SLEEP); + if (!dcp) { + ret = SET_ERROR(ENOMEM); + goto error; + } /* get relevant properties from the nvlist */ if (props != NULL) { ret = nvlist_lookup_uint64(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), &crypt); - if (ret != 0) - crypt_exists = B_FALSE; + if (ret == 0) + do_inherit = B_FALSE; + + ret = nvlist_lookup_uint64(props, + zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat); + if (ret == 0) + do_inherit = B_FALSE; ret = nvlist_lookup_string(props, - zfs_prop_to_name(ZFS_PROP_KEYSOURCE), &keysource); - if (ret != 0) - keysource_exists = B_FALSE; + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation); + if (ret == 0) + do_inherit = B_FALSE; ret = nvlist_lookup_uint64(props, - zfs_prop_to_name(ZFS_PROP_SALT), &salt); - if (ret != 0) - salt_exists = B_FALSE; + zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), &dcp->cp_salt); + if (ret == 0) + do_inherit = B_FALSE; ret = nvlist_lookup_uint64(props, - zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters); - if (ret != 0) - iters_exists = B_FALSE; - - ret = nvlist_lookup_uint64(props, "crypto_cmd", &cmd); - if (ret != 0) - cmd_exists = B_FALSE; - } else { - crypt_exists = B_FALSE; - keysource_exists = B_FALSE; - salt_exists = B_FALSE; - iters_exists = B_FALSE; - cmd_exists = B_FALSE; + zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &dcp->cp_iters); + if (ret == 0) + do_inherit = B_FALSE; } if (crypto_args != NULL) { ret = nvlist_lookup_uint8_array(crypto_args, "wkeydata", &wkeydata, &wkeydata_len); - if (ret != 0) - wkeydata_exists = B_FALSE; - } else { - wkeydata_exists = B_FALSE; + if (ret == 0) + do_inherit = B_FALSE; } /* no parameters are valid; results in inherited crypto settings */ - if (!crypt_exists && !keysource_exists && !wkeydata_exists && - !salt_exists && !cmd_exists && !iters_exists) { + if (do_inherit) { + kmem_free(dcp, sizeof (dsl_crypto_params_t)); *dcp_out = NULL; return (0); } - dcp = kmem_alloc(sizeof (dsl_crypto_params_t), KM_SLEEP); - if (!dcp) { - ret = SET_ERROR(ENOMEM); + /* check for valid crypt */ + if (crypt >= ZIO_CRYPT_FUNCTIONS) { + ret = SET_ERROR(EINVAL); + goto error; + } else { + dcp->cp_crypt = crypt; + } + + /* check for valid keyformat */ + if (keyformat >= ZIO_CRYPT_FUNCTIONS) { + ret = SET_ERROR(EINVAL); goto error; + } else { + dcp->cp_keyformat = keyformat; } - /* check wrapping key length */ - if (wkeydata_exists && wkeydata_len != WRAPPING_KEY_LEN) { + /* check for a valid keylocation and copy it in */ + if (keylocation != NULL) { + if (!zfs_prop_valid_keylocation(keylocation)) { + ret = SET_ERROR(EINVAL); + goto error; + } + + dcp->cp_keylocation = spa_strdup(keylocation); + } + + /* check wrapping key length, if given */ + if (wkeydata != NULL && wkeydata_len != WRAPPING_KEY_LEN) { ret = SET_ERROR(EINVAL); goto error; } - /* specifying a keysource requires keydata */ - if (keysource_exists && !wkeydata_exists) { + /* specifying a keyformat requires keydata */ + if (keyformat != ZFS_KEYFORMAT_NONE && wkeydata == NULL) { ret = SET_ERROR(EINVAL); goto error; } /* remove crypto_cmd from props since it should not be used again */ - if (cmd_exists) - (void) nvlist_remove_all(props, "crypto_cmd"); + (void) nvlist_remove_all(props, "crypto_cmd"); - /* if the user asked for the deault crypt, decide that now */ - if (crypt == ZIO_CRYPT_ON) { - crypt = ZIO_CRYPT_ON_VALUE; + /* if the user asked for the deault crypt, determine that now */ + if (dcp->cp_crypt == ZIO_CRYPT_ON) { + dcp->cp_crypt = ZIO_CRYPT_ON_VALUE; ret = nvlist_remove_all(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION)); if (ret != 0) goto error; ret = nvlist_add_uint64(props, - zfs_prop_to_name(ZFS_PROP_ENCRYPTION), crypt); + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), dcp->cp_crypt); if (ret != 0) goto error; } /* create the wrapping key from the raw data */ - if (wkeydata_exists) { + if (wkeydata != NULL) { /* create the wrapping key with the verified parameters */ ret = dsl_wrapping_key_create(wkeydata, &wkey); if (ret != 0) goto error; + + dcp->cp_wkey = wkey; } - dcp->cp_cmd = cmd; - dcp->cp_crypt = crypt; - dcp->cp_salt = salt; - dcp->cp_iters = iters; - dcp->cp_keysource = keysource; - dcp->cp_wkey = wkey; *dcp_out = dcp; return (0); @@ -272,8 +285,11 @@ dsl_crypto_params_free(dsl_crypto_params_t *dcp, boolean_t unload) if (dcp == NULL) return; - if (unload) + if (dcp->cp_keylocation != NULL) + spa_strfree(dcp->cp_keylocation); + if (unload &&& dcp->cp_wkey != NULL) dsl_wrapping_key_free(dcp->cp_wkey); + kmem_free(dcp, sizeof (dsl_crypto_params_t)); } @@ -353,22 +369,28 @@ spa_keystore_fini(spa_keystore_t *sk) } static int -dsl_dir_hold_keysource_source_dd(dsl_dir_t *dd, void *tag, +dsl_dir_hold_keylocation_source_dd(dsl_dir_t *dd, void *tag, dsl_dir_t **inherit_dd_out) { int ret; dsl_dir_t *inherit_dd = NULL; - char keysource[MAXNAMELEN]; + char keylocation[MAXNAMELEN]; char setpoint[MAXNAMELEN]; /* - * lookup dd's keysource property and find - * out where it was inherited from + * lookup dd's keylocation property and find out where it was + * inherited from. dsl_prop_get_dd() might not find anything and + * return the default value. We detect this by check9ing if setpoint + * is an empty string and return ENOENT. */ - ret = dsl_prop_get_dd(dd, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), - 1, sizeof (keysource), keysource, setpoint, B_FALSE); - if (ret != 0) + ret = dsl_prop_get_dd(dd, zfs_prop_to_name(ZFS_PROP_KEYLOCATION), + 1, sizeof (keylocation), keylocation, setpoint, B_FALSE); + if (ret != 0) { + goto error; + } else if (setpoint[0] == '\0') { + ret = ENOENT; goto error; + } /* hold the dsl dir that we inherited the property from */ ret = dsl_dir_hold(dd->dd_pool, setpoint, tag, &inherit_dd, NULL); @@ -379,10 +401,72 @@ dsl_dir_hold_keysource_source_dd(dsl_dir_t *dd, void *tag, return (0); error: + *inherit_dd_out = NULL; return (ret); } +int +dsl_crypto_can_set_keylocation(const char *dsname) +{ + int ret = 0; + dsl_dir_t *dd = NULL; + dsl_dir_t *inherit_dd = NULL; + dsl_pool_t *dp = NULL; + + /* hold the dsl dir */ + ret = dsl_pool_hold(dsname, FTAG, &dp); + if (ret != 0) + goto out; + + ret = dsl_dir_hold(dp, dsname, FTAG, &dd, NULL); + if (ret != 0) + goto out; + + /* check that the dd is encrypted */ + if (dd->dd_crypto_obj == 0) { + ret = SET_ERROR(EACCES); + goto out; + } + + /* + * hold the dd where we are inheritting the keylocation from. If this + * doesn't exist it means we are creating an encryption root (since + * the property didn't exist in this dataset or any parents). It is + * always vallid to set a keylocation at creation time, so just return + * zero in that case. + */ + ret = dsl_dir_hold_keylocation_source_dd(dd, FTAG, &inherit_dd); + if (ret == ENOENT) { + ret = 0; + goto out; + } else if (ret != 0) { + goto out; + } + + /* check that this is an encryption root */ + if (inherit_dd->dd_object != dd->dd_object) { + ret = SET_ERROR(EACCES); + goto out; + } + + dsl_dir_rele(inherit_dd, FTAG); + dsl_dir_rele(dd, FTAG); + dsl_pool_rele(dp, FTAG); + + return (0); + +out: + if (inherit_dd != NULL) + dsl_dir_rele(inherit_dd, FTAG); + if (dd != NULL) + dsl_dir_rele(dd, FTAG); + if (dp != NULL) + dsl_pool_rele(dp, FTAG); + + return (ret); +} + zfs_keystatus_t dsl_dataset_keystore_keystatus(dsl_dataset_t *ds) { @@ -453,7 +537,7 @@ spa_keystore_wkey_hold_ddobj(spa_t *spa, uint64_t ddobj, void *tag, /* * There is a special case in zfs_create_fs() where the wrapping key * is needed before the filesystem's properties are set. This is - * problematic because dsl_dir_hold_keysource_source_dd() uses the + * problematic because dsl_dir_hold_keylocation_source_dd() uses the * properties to determine where the wrapping key is inherited from. * As a result, here we try to find a wrapping key for this dd before * checking for wrapping key inheritance. @@ -472,8 +556,8 @@ spa_keystore_wkey_hold_ddobj(spa_t *spa, uint64_t ddobj, void *tag, if (ret != 0) goto error; - /* get the dd that the keysource property was inherited from */ - ret = dsl_dir_hold_keysource_source_dd(dd, FTAG, &inherit_dd); + /* get the dd that the keylocation property was inherited from */ + ret = dsl_dir_hold_keylocation_source_dd(dd, FTAG, &inherit_dd); if (ret != 0) goto error; @@ -747,10 +831,11 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp) dsl_wrapping_key_t *wkey = dcp->cp_wkey; dsl_pool_t *dp = NULL; - if (!dcp || !dcp->cp_wkey) + if (dcp == NULL || dcp->cp_wkey == NULL) return (SET_ERROR(EINVAL)); - if (dcp->cp_crypt || dcp->cp_keysource || dcp->cp_salt || - dcp->cp_cmd || dcp->cp_iters) + if (dcp->cp_crypt != ZIO_CRYPT_INHERIT || dcp->cp_keylocation != NULL || + dcp->cp_salt != 0 || dcp->cp_cmd != ZFS_IOC_KEY_CMD_NONE || + dcp->cp_iters != 0) return (SET_ERROR(EINVAL)); ret = dsl_pool_hold(dsname, FTAG, &dp); @@ -1071,33 +1156,61 @@ static int spa_keystore_rewrap_check(void *arg, dmu_tx_t *tx) { int ret; - dsl_dir_t *dd; + uint64_t keyformat = ZFS_KEYFORMAT_NONE; + dsl_dir_t *dd = NULL; dsl_crypto_key_t *dck = NULL; dsl_pool_t *dp = dmu_tx_pool(tx); spa_keystore_rewrap_args_t *skra = arg; dsl_crypto_params_t *dcp = skra->skra_cp; - if (!dcp || !dcp->cp_wkey) - return (SET_ERROR(EINVAL)); - if (dcp->cp_crypt != ZIO_CRYPT_INHERIT || dcp->cp_cmd) - return (SET_ERROR(EINVAL)); - if (dcp->cp_keysource && - strncmp(dcp->cp_keysource, "passphrase", 10) == 0 && - (!skra->skra_cp->cp_salt || !skra->skra_cp->cp_iters)) - return (SET_ERROR(EINVAL)); - /* hold the dd */ ret = dsl_dir_hold(dp, skra->skra_dsname, FTAG, &dd, NULL); if (ret != 0) return (ret); - /* check that this dd has a dsl key */ + /* verify that the dataset is encrypted */ if (dd->dd_crypto_obj == 0) { ret = SET_ERROR(EINVAL); goto error; } - /* make sure the dsl key is loaded / loadable */ + /* all callers must specify a dcp */ + if (dcp == NULL) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* figure out what the new format will be */ + if (dcp->cp_keyformat == ZFS_KEYFORMAT_NONE) { + ret = dsl_prop_get_dd(dd, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), + 8, 1, &keyformat, NULL, B_FALSE); + if (ret != 0) + goto error; + } else { + keyformat = dcp->cp_keyformat; + } + + /* all callers must specify a wkey */ + if (dcp->cp_wkey == NULL) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* crypt cannot be changed after creation */ + if (dcp->cp_crypt != ZIO_CRYPT_INHERIT || dcp->cp_cmd != 0) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* passphrases require pbkdf2 salt and iters */ + if (keyformat == ZFS_KEYFORMAT_PASSPHRASE && + (skra->skra_cp->cp_salt == 0 || + skra->skra_cp->cp_iters < MIN_PBKDF2_ITERATIONS)) { + ret = SET_ERROR(EINVAL); + goto error; + } + + /* make sure the dsl key is loaded */ ret = spa_keystore_dsl_key_hold_dd(dp->dp_spa, dd, FTAG, &dck); if (ret != 0) goto error; @@ -1139,8 +1252,8 @@ spa_keystore_rewrap_sync_impl(uint64_t root_ddobj, uint64_t ddobj, return; } - /* hold the dd we inherited the keysource from */ - VERIFY0(dsl_dir_hold_keysource_source_dd(dd, FTAG, &inherit_dd)); + /* hold the dd we inherited the keylocation from */ + VERIFY0(dsl_dir_hold_keylocation_source_dd(dd, FTAG, &inherit_dd)); /* stop recursing if this dsl dir didn't inherit from the root */ if (inherit_dd->dd_object != root_ddobj) { @@ -1186,24 +1299,34 @@ spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) dsl_wrapping_key_t *wkey = skra->skra_cp->cp_wkey; dsl_wrapping_key_t *found_wkey; uint64_t crypt; - const char *keysource = skra->skra_cp->cp_keysource; + const char *keylocation = skra->skra_cp->cp_keylocation; /* create and initialize the wrapping key */ VERIFY0(dsl_dataset_hold(dp, skra->skra_dsname, FTAG, &ds)); ASSERT(!ds->ds_is_snapshot); - /* set additional properties which can be sent along with this ioctl */ - if (keysource != NULL) + /* + * Set additional properties which can be sent along with this ioctl. + * Note that this command can set keylocation even if it can't normally + * be set via 'zfs set' due to a non-local keysource. In this case we + * will actually sever the inheritted keyocation. + */ + if (keylocation != NULL) { dsl_prop_set_sync_impl(ds, - zfs_prop_to_name(ZFS_PROP_KEYSOURCE), ZPROP_SRC_LOCAL, - 1, strlen(keysource) + 1, keysource, tx); + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), ZPROP_SRC_LOCAL, + 1, strlen(keylocation) + 1, keylocation, tx); + } - if (skra->skra_cp->cp_iters) + if (skra->skra_cp->cp_keyformat != ZFS_KEYFORMAT_NONE) { dsl_prop_set_sync_impl(ds, - zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), - ZPROP_SRC_LOCAL, 8, 1, &skra->skra_cp->cp_iters, tx); + zfs_prop_to_name(ZFS_PROP_KEYFORMAT), ZPROP_SRC_LOCAL, + 8, 1, &skra->skra_cp->cp_keyformat, tx); + } + + dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), + ZPROP_SRC_LOCAL, 8, 1, &skra->skra_cp->cp_iters, tx); - dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_SALT), + dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), ZPROP_SRC_LOCAL, 8, 1, &skra->skra_cp->cp_salt, tx); /* @@ -1256,127 +1379,151 @@ spa_keystore_rewrap(const char *dsname, dsl_crypto_params_t *dcp) spa_keystore_rewrap_sync, &skra, 0, ZFS_SPACE_CHECK_NORMAL)); } -int -dmu_objset_create_encryption_check(dsl_dir_t *parentdd, - dsl_crypto_params_t *dcp) +static int +dmu_objset_check_wkey_loaded(dsl_dir_t *dd) { int ret; dsl_wrapping_key_t *wkey = NULL; - uint64_t salt = 0, iters = 0; - zfs_ioc_crypto_cmd_t cmd = ZFS_IOC_KEY_CMD_NONE; - uint64_t pcrypt, crypt = ZIO_CRYPT_INHERIT; - const char *keysource = NULL; - if (!spa_feature_is_enabled(parentdd->dd_pool->dp_spa, - SPA_FEATURE_ENCRYPTION) && dcp) - return (SET_ERROR(EINVAL)); + ret = spa_keystore_wkey_hold_ddobj(dd->dd_pool->dp_spa, + dd->dd_object, FTAG, &wkey); + if (ret != 0) + return (SET_ERROR(EACCES)); + + dsl_wrapping_key_rele(wkey, FTAG); + + return (0); +} + +/* + * This is the combined check function for verifying encrypted create and + * clone parameters. There are a lot of edge cases to handle here so it has + * been commented rather extensively. Some checks are duplicated in an effort + * to ensure the error codes returned are consistent (EINVAL before EACCES). + */ +int +dmu_objset_create_crypt_check(dsl_dir_t *parentdd, dsl_dir_t *origindd, + dsl_crypto_params_t *dcp) +{ + int ret; + uint64_t pcrypt, effective_crypt; + + /* get the parent's crypt */ ret = dsl_prop_get_dd(parentdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &pcrypt, NULL, B_FALSE); if (ret != 0) return (ret); - if (dcp != NULL) { - crypt = dcp->cp_crypt; - wkey = dcp->cp_wkey; - salt = dcp->cp_salt; - iters = dcp->cp_iters; - keysource = dcp->cp_keysource; - cmd = dcp->cp_cmd; + /* + * Figure out what the crypt will be for the new dataset. + * Clones must always use the same crypt as their origin. + */ + if (origindd != NULL) { + ret = dsl_prop_get_dd(origindd, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION), + 8, 1, &effective_crypt, NULL, B_FALSE); + if (ret != 0) + return (ret); + } else if (dcp == NULL || dcp->cp_crypt == ZIO_CRYPT_INHERIT) { + effective_crypt = pcrypt; + } else { + effective_crypt = dcp->cp_crypt; } - if (crypt >= ZIO_CRYPT_FUNCTIONS) - return (SET_ERROR(EINVAL)); - if (crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) - return (SET_ERROR(EINVAL)); - if (crypt == ZIO_CRYPT_INHERIT && pcrypt == ZIO_CRYPT_OFF && - (salt || keysource || wkey || iters)) - return (SET_ERROR(EINVAL)); - if (crypt == ZIO_CRYPT_OFF && (salt || keysource || wkey || iters)) - return (SET_ERROR(EINVAL)); - if (crypt != ZIO_CRYPT_INHERIT && crypt != ZIO_CRYPT_OFF && - pcrypt == ZIO_CRYPT_OFF && (!keysource || !wkey)) - return (SET_ERROR(EINVAL)); - if (keysource && strncmp(keysource, "passphrase", 10) == 0 && - (!salt || !iters)) - return (SET_ERROR(EINVAL)); - if (cmd) + /* + * can't create an unencrypted child of an encrypted parent + * under any circumstances + */ + if (effective_crypt == ZIO_CRYPT_OFF && pcrypt != ZIO_CRYPT_OFF) return (SET_ERROR(EINVAL)); - if (!wkey && pcrypt != ZIO_CRYPT_OFF) { - ret = spa_keystore_wkey_hold_ddobj(parentdd->dd_pool->dp_spa, - parentdd->dd_object, FTAG, &wkey); + /* NULL dcp implies inheritence. Make sure the needed keys exist. */ + if (dcp == NULL) { + /* no encryption */ + if (effective_crypt == ZIO_CRYPT_OFF) + return (0); + + /* check for parent key */ + ret = dmu_objset_check_wkey_loaded(parentdd); if (ret != 0) - return (SET_ERROR(EACCES)); + return (ret); - dsl_wrapping_key_rele(wkey, FTAG); + /* check for origin key if this is a clone */ + if (origindd != NULL) { + ret = dmu_objset_check_wkey_loaded(origindd); + if (ret != 0) + return (ret); + } + + return (0); } - return (0); -} + /* check for valid dcp with no encryption (inherited or local) */ + if (effective_crypt == ZIO_CRYPT_OFF) { + /* Must not specify encryption params */ + if (dcp->cp_salt != 0 || dcp->cp_iters != 0 || + dcp->cp_keyformat != ZFS_KEYFORMAT_NONE || + dcp->cp_keylocation != NULL || dcp->cp_wkey != NULL) + return (SET_ERROR(EINVAL)); -int -dmu_objset_clone_encryption_check(dsl_dir_t *parentdd, dsl_dir_t *origindd, - dsl_crypto_params_t *dcp) -{ - int ret; - dsl_wrapping_key_t *wkey = NULL; - uint64_t cmd = 0, salt = 0, iters = 0; - uint64_t pcrypt, ocrypt, crypt = ZIO_CRYPT_INHERIT; - const char *keysource = NULL; + return (0); + } + /* We will now definitely be encrypting. Check the feature flag */ if (!spa_feature_is_enabled(parentdd->dd_pool->dp_spa, - SPA_FEATURE_ENCRYPTION) && dcp) + SPA_FEATURE_ENCRYPTION)) { return (SET_ERROR(EINVAL)); + } - ret = dsl_prop_get_dd(parentdd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), - 8, 1, &pcrypt, NULL, B_FALSE); - if (ret != 0) - return (ret); + /* handle non-implicit inheritence */ + if (dcp->cp_wkey == NULL) { + /* key must be fully unspecified */ + if (dcp->cp_keyformat != ZFS_KEYFORMAT_NONE || + dcp->cp_keylocation != NULL) + return (SET_ERROR(EINVAL)); - ret = dsl_prop_get_dd(origindd, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), - 8, 1, &ocrypt, NULL, B_FALSE); - if (ret != 0) - return (ret); + /* parent must have a key to inherit */ + if (pcrypt == ZIO_CRYPT_OFF) + return (SET_ERROR(EINVAL)); + + /* check for parent key */ + ret = dmu_objset_check_wkey_loaded(parentdd); + if (ret != 0) + return (ret); + + /* check for origin key if this is a clone */ + if (origindd != NULL) { + ret = dmu_objset_check_wkey_loaded(origindd); + if (ret != 0) + return (ret); + } - if (dcp != NULL) { - crypt = dcp->cp_crypt; - wkey = dcp->cp_wkey; - salt = dcp->cp_salt; - iters = dcp->cp_iters; - keysource = dcp->cp_keysource; - cmd = dcp->cp_cmd; + return (0); } - if (crypt != ZIO_CRYPT_INHERIT) - return (SET_ERROR(EINVAL)); - if (pcrypt != ZIO_CRYPT_OFF && ocrypt == ZIO_CRYPT_OFF) - return (SET_ERROR(EINVAL)); - if (pcrypt == ZIO_CRYPT_OFF && ocrypt != ZIO_CRYPT_OFF && - (!wkey || !keysource)) + /* At this point we should have a fully specified key. Check location */ + if (dcp->cp_keylocation == NULL) return (SET_ERROR(EINVAL)); - if (keysource && strncmp(keysource, "passphrase", 10) == 0 && - (!salt || !iters)) - return (SET_ERROR(EINVAL)); - - /* origin wrapping key must be present, if it is encrypted */ - if (ocrypt != ZIO_CRYPT_OFF) { - ret = spa_keystore_wkey_hold_ddobj(parentdd->dd_pool->dp_spa, - origindd->dd_object, FTAG, &wkey); - if (ret != 0) - return (SET_ERROR(EACCES)); - dsl_wrapping_key_rele(wkey, FTAG); + /* Must have fully specified keyformat */ + switch (dcp->cp_keyformat) { + case ZFS_KEYFORMAT_NONE: + /* keyformat must be specified */ + return (SET_ERROR(EINVAL)); + case ZFS_KEYFORMAT_PASSPHRASE: + /* requires pbkdf2 iters and salt */ + if (dcp->cp_salt == 0 || dcp->cp_iters < MIN_PBKDF2_ITERATIONS) + return (SET_ERROR(EINVAL)); + default: + break; } - /* parent's wrapping key must be present if a new one isn't specified */ - if (!wkey && pcrypt != ZIO_CRYPT_OFF) { - ret = spa_keystore_wkey_hold_ddobj(parentdd->dd_pool->dp_spa, - parentdd->dd_object, FTAG, &wkey); + /* check for origin key if this is a clone */ + if (origindd != NULL) { + ret = dmu_objset_check_wkey_loaded(origindd); if (ret != 0) - return (SET_ERROR(EACCES)); - - dsl_wrapping_key_rele(wkey, FTAG); + return (ret); } return (0); diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 2ab1e7c533a1..a8a91a3af2cf 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -557,7 +557,7 @@ dsl_dataset_hold_obj_flags(dsl_pool_t *dp, uint64_t dsobj, int flags, void *tag, dp->dp_origin_snap == NULL || ds == dp->dp_origin_snap); *dsp = ds; - if (flags & DS_HOLD_FLAG_DECRYPT && ds->ds_dir->dd_crypto_obj != 0) { + if ((flags & DS_HOLD_FLAG_DECRYPT) && ds->ds_dir->dd_crypto_obj != 0) { err = spa_keystore_create_mapping(dp->dp_spa, ds, ds); if (err != 0) { dsl_dataset_rele(ds, tag); @@ -742,12 +742,13 @@ dsl_dataset_namelen(dsl_dataset_t *ds) void dsl_dataset_rele_flags(dsl_dataset_t *ds, int flags, void *tag) { - dmu_buf_rele(ds->ds_dbuf, tag); - - if (flags & DS_HOLD_FLAG_DECRYPT) { + if (ds->ds_dir != NULL && ds->ds_dir->dd_crypto_obj != 0 && + (flags & DS_HOLD_FLAG_DECRYPT)) { (void) spa_keystore_remove_mapping(ds->ds_dir->dd_pool->dp_spa, ds, ds); } + + dmu_buf_rele(ds->ds_dbuf, tag); } void @@ -830,8 +831,9 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, dsl_pool_t *dp = dd->dd_pool; dmu_buf_t *dbuf; dsl_dataset_phys_t *dsphys; - uint64_t dsobj, crypt; - dsl_wrapping_key_t *wkey; + uint64_t dsobj; + uint64_t crypt = (dcp != NULL) ? dcp->cp_crypt : ZIO_CRYPT_INHERIT; + dsl_wrapping_key_t *wkey = (dcp != NULL) ? dcp->cp_wkey : NULL; objset_t *mos = dp->dp_meta_objset; if (origin == NULL) @@ -925,15 +927,8 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, } /* handle encryption */ - if (dcp == NULL) { - crypt = ZIO_CRYPT_INHERIT; - wkey = NULL; - } else { - crypt = dcp->cp_crypt; - wkey = dcp->cp_wkey; - } - if (!dsl_dir_is_clone(dd)) { + /* figure out the effective crypt */ if (crypt == ZIO_CRYPT_INHERIT && dd->dd_parent != NULL) { VERIFY0(dsl_prop_get_dd(dd->dd_parent, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), @@ -945,6 +940,7 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, if (crypt == ZIO_CRYPT_OFF) goto no_crypto; + /* use the new key if given or inherit from the parent */ if (wkey == NULL) { VERIFY0(spa_keystore_wkey_hold_ddobj(dp->dp_spa, dd->dd_parent->dd_object, FTAG, &wkey)); @@ -960,8 +956,7 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, if (dcp == NULL || dcp->cp_wkey == NULL) { dsl_wrapping_key_rele(wkey, FTAG); } else { - VERIFY0(spa_keystore_load_wkey_impl(tx->tx_pool->dp_spa, - wkey)); + VERIFY0(spa_keystore_load_wkey_impl(dp->dp_spa, wkey)); } } else if (origin->ds_dir->dd_crypto_obj != 0) { VERIFY0(dsl_prop_get_dd(origin->ds_dir, @@ -992,8 +987,7 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, if (dcp == NULL || dcp->cp_wkey == NULL) { dsl_wrapping_key_rele(wkey, FTAG); } else { - VERIFY0(spa_keystore_load_wkey_impl(tx->tx_pool->dp_spa, - wkey)); + VERIFY0(spa_keystore_load_wkey_impl(dp->dp_spa, wkey)); } } diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index 43b339da3e9d..47d0e14c5b49 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -1813,12 +1813,12 @@ dsl_dir_rename_check(void *arg, dmu_tx_t *tx) /* * encrypted datasets can only be moved if they are - * an encryption root (locally set keysource). + * an encryption root (locally set keyformat). */ if (dd->dd_crypto_obj != 0) { err = zap_contains(os, dsl_dir_phys(dd)->dd_props_zapobj, - zfs_prop_to_name(ZFS_PROP_KEYSOURCE)); + zfs_prop_to_name(ZFS_PROP_KEYFORMAT)); if (err != 0) { dsl_dir_rele(newparent, FTAG); dsl_dir_rele(dd, FTAG); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 23bbecb2c56c..c8e063653b4a 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -2426,10 +2426,13 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source, &pair) == 0); } - if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) - return (-1); - - VERIFY(0 == nvpair_value_uint64(pair, &intval)); + /* all special properties are numeric except for keylocation */ + if (zfs_prop_get_type(prop) == PROP_TYPE_STRING) { + if (prop != ZFS_PROP_KEYLOCATION) + return (-1); + } else { + VERIFY(0 == nvpair_value_uint64(pair, &intval)); + } switch (prop) { case ZFS_PROP_QUOTA: @@ -2453,6 +2456,11 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source, if (err == 0) err = -1; break; + case ZFS_PROP_KEYLOCATION: + err = dsl_crypto_can_set_keylocation(dsname); + if (err == 0) + err = -1; + break; case ZFS_PROP_RESERVATION: err = dsl_dir_set_reservation(dsname, source, intval); break; From 4dc6b7f660909e8e9bb54c628b4a89cab4836eb9 Mon Sep 17 00:00:00 2001 From: Tom Caputi Date: Fri, 27 Jan 2017 11:51:50 -0500 Subject: [PATCH 10/10] split "zfs key" command into subcommands. fixed tests to work with new api. fixed a few bugs as they appeared. man page still not updated. --- cmd/zfs/zfs_main.c | 172 ++++++++++---- include/libzfs_core.h | 4 +- include/sys/dsl_crypt.h | 3 - include/sys/fs/zfs.h | 4 +- include/sys/zio_crypt.h | 9 - lib/libzfs/libzfs_crypto.c | 35 ++- lib/libzfs/libzfs_dataset.c | 2 +- lib/libzfs_core/libzfs_core.c | 54 +++-- module/zfs/dsl_crypt.c | 21 +- module/zfs/zfs_ioctl.c | 214 ++++++++++++------ .../zfs_create/zfs_create_encrypted.ksh | 90 ++++---- .../cli_root/zfs_key/zfs_key_change_neg.ksh | 22 +- .../cli_root/zfs_key/zfs_key_change_pos.ksh | 17 +- .../cli_root/zfs_key/zfs_key_common.kshlib | 2 +- .../cli_root/zfs_key/zfs_key_load_neg.ksh | 10 +- .../cli_root/zfs_key/zfs_key_load_pos.ksh | 10 +- .../cli_root/zfs_key/zfs_key_unload_neg.ksh | 10 +- .../cli_root/zfs_key/zfs_key_unload_pos.ksh | 8 +- .../zfs_mount/zfs_mount_encrypted.ksh | 4 +- .../zfs_receive/zfs_receive_encrypted_neg.ksh | 5 +- .../zfs_receive/zfs_receive_encrypted_pos.ksh | 3 +- .../zfs_send/zfs_send_encrypted_neg.ksh | 5 +- .../zfs_send/zfs_send_encrypted_pos.ksh | 3 +- .../zpool_create/zpool_create_encrypted.ksh | 28 +-- 24 files changed, 453 insertions(+), 282 deletions(-) diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 16dabc403bce..a6f4fc602db5 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -103,7 +103,9 @@ static int zfs_do_holds(int argc, char **argv); static int zfs_do_release(int argc, char **argv); static int zfs_do_diff(int argc, char **argv); static int zfs_do_bookmark(int argc, char **argv); -static int zfs_do_key(int argc, char **argv); +static int zfs_do_load_key(int argc, char **argv); +static int zfs_do_unload_key(int argc, char **argv); +static int zfs_do_change_key(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -151,7 +153,9 @@ typedef enum { HELP_RELEASE, HELP_DIFF, HELP_BOOKMARK, - HELP_KEY, + HELP_LOAD_KEY, + HELP_UNLOAD_KEY, + HELP_CHANGE_KEY, } zfs_help_t; typedef struct zfs_command { @@ -205,7 +209,9 @@ static zfs_command_t command_table[] = { { "holds", zfs_do_holds, HELP_HOLDS }, { "release", zfs_do_release, HELP_RELEASE }, { "diff", zfs_do_diff, HELP_DIFF }, - { "key", zfs_do_key, HELP_KEY }, + { "load-key", zfs_do_load_key, HELP_LOAD_KEY }, + { "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY }, + { "change-key", zfs_do_change_key, HELP_CHANGE_KEY }, }; #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) @@ -324,10 +330,15 @@ get_usage(zfs_help_t idx) "[snapshot|filesystem]\n")); case HELP_BOOKMARK: return (gettext("\tbookmark \n")); - case HELP_KEY: - return (gettext("\tkey [-lu] \n" - "\tkey -c [-o keyformat=] [-o keylocation=] " - "[-o pbkfd2iters=] \n")); + case HELP_LOAD_KEY: + return (gettext("\tload-key [-r] \n")); + case HELP_UNLOAD_KEY: + return (gettext("\tunload-key [-r] \n")); + case HELP_CHANGE_KEY: + return (gettext("\tchange-key [-l] [-o keyformat=] " + "[-o keylocation=] [-o pbkfd2iters=] " + "\n" + "\tchangekey -i \n")); } abort(); @@ -6932,41 +6943,118 @@ zfs_do_bookmark(int argc, char **argv) } static int -zfs_do_key(int argc, char **argv) +zfs_do_load_key(int argc, char **argv) +{ + int c, ret; + boolean_t recursive = B_FALSE; + zfs_handle_t *zhp = NULL; + + while ((c = getopt(argc, argv, "r")) != -1) { + switch (c) { + case 'r': + recursive = B_TRUE; + default: + (void) fprintf(stderr, + gettext("invalid option '%c'\n"), optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + (void) fprintf(stderr, gettext("Missing dataset argument\n")); + usage(B_FALSE); + } + + if (argc > 1) { + (void) fprintf(stderr, gettext("Too many arguments\n")); + usage(B_FALSE); + } + + zhp = zfs_open(g_zfs, argv[argc - 1], + ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME); + if (zhp == NULL) + usage(B_FALSE); + + ret = zfs_crypto_load_key(zhp); + if (ret != 0) + goto error; + + zfs_close(zhp); + return (0); + +error: + if (zhp != NULL) + zfs_close(zhp); + return (-1); +} + +static int +zfs_do_unload_key(int argc, char **argv) { int c, ret = -1; - boolean_t load = B_FALSE, unload = B_FALSE, rewrap = B_FALSE; + boolean_t recursive = B_FALSE; + zfs_handle_t *zhp = NULL; + + while ((c = getopt(argc, argv, "r")) != -1) { + switch (c) { + case 'r': + recursive = B_TRUE; + default: + (void) fprintf(stderr, + gettext("invalid option '%c'\n"), optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + (void) fprintf(stderr, gettext("Missing dataset argument\n")); + usage(B_FALSE); + } + + if (argc > 1) { + (void) fprintf(stderr, gettext("Too many arguments\n")); + usage(B_FALSE); + } + + zhp = zfs_open(g_zfs, argv[argc - 1], + ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME); + if (zhp == NULL) + usage(B_FALSE); + + ret = zfs_crypto_unload_key(zhp); + if (ret != 0) + goto error; + + zfs_close(zhp); + return (0); + +error: + if (zhp != NULL) + zfs_close(zhp); + return (-1); +} + +static int +zfs_do_change_key(int argc, char **argv) +{ + int c, ret; + boolean_t loadkey = B_FALSE, inheritkey = B_FALSE; zfs_handle_t *zhp = NULL; nvlist_t *props = fnvlist_alloc(); - while ((c = getopt(argc, argv, "ulco:")) != -1) { + while ((c = getopt(argc, argv, "lio:")) != -1) { switch (c) { - case 'u': - if (ret == 0) { - (void) fprintf(stderr, gettext( - "multiple actions specified\n")); - usage(B_FALSE); - } - unload = B_TRUE; - ret = 0; - break; case 'l': - if (ret == 0) { - (void) fprintf(stderr, gettext( - "multiple actions specified\n")); - usage(B_FALSE); - } - load = B_TRUE; - ret = 0; + loadkey = B_TRUE; break; - case 'c': - if (ret == 0) { - (void) fprintf(stderr, gettext( - "multiple actions specified\n")); - usage(B_FALSE); - } - rewrap = B_TRUE; - ret = 0; + case 'i': + inheritkey = B_TRUE; break; case 'o': if (parseprop(props, optarg) != 0) @@ -6979,15 +7067,15 @@ zfs_do_key(int argc, char **argv) } } - if (ret != 0) { + if (inheritkey && !nvlist_empty(props)) { (void) fprintf(stderr, - gettext("No action specified\n")); + gettext("Properties not allowed for inheriting\n")); usage(B_FALSE); } - if (!rewrap && !nvlist_empty(props)) { + if (inheritkey && loadkey) { (void) fprintf(stderr, - gettext("Properties not allowed for specified command\n")); + gettext("Inherit flag incompatible with load flag\n")); usage(B_FALSE); } @@ -7009,13 +7097,7 @@ zfs_do_key(int argc, char **argv) if (zhp == NULL) usage(B_FALSE); - if (load) - ret = zfs_crypto_load_key(zhp); - else if (unload) - ret = zfs_crypto_unload_key(zhp); - else - ret = zfs_crypto_rewrap(zhp, props); - + ret = zfs_crypto_rewrap(zhp, props); if (ret != 0) goto error; diff --git a/include/libzfs_core.h b/include/libzfs_core.h index 3422e3d03b4b..6356a0ee6894 100644 --- a/include/libzfs_core.h +++ b/include/libzfs_core.h @@ -45,7 +45,9 @@ int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **); int lzc_bookmark(nvlist_t *, nvlist_t **); int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **); int lzc_destroy_bookmarks(nvlist_t *, nvlist_t **); -int lzc_key(const char *, uint64_t, nvlist_t *, nvlist_t *); +int lzc_load_key(const char *, nvlist_t *); +int lzc_unload_key(const char *); +int lzc_change_key(const char *, nvlist_t *, nvlist_t *); int lzc_snaprange_space(const char *, const char *, uint64_t *); diff --git a/include/sys/dsl_crypt.h b/include/sys/dsl_crypt.h index cf64ea4b7ec6..c82bb9c355ec 100644 --- a/include/sys/dsl_crypt.h +++ b/include/sys/dsl_crypt.h @@ -75,9 +75,6 @@ typedef struct dsl_wrapping_key { * passed around the kernel together for convenience */ typedef struct dsl_crypto_params { - /* command to be executed */ - zfs_ioc_crypto_cmd_t cp_cmd; - /* the encryption algorithm */ enum zio_encrypt cp_crypt; diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 0923ff6fb46c..99e63056aace 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -1019,7 +1019,9 @@ typedef enum zfs_ioc { ZFS_IOC_GET_BOOKMARKS, ZFS_IOC_DESTROY_BOOKMARKS, ZFS_IOC_RECV_NEW, - ZFS_IOC_KEY, + ZFS_IOC_LOAD_KEY, + ZFS_IOC_UNLOAD_KEY, + ZFS_IOC_CHANGE_KEY, /* * Linux - 3/64 numbers reserved. diff --git a/include/sys/zio_crypt.h b/include/sys/zio_crypt.h index f9675ba350ee..754b53495b48 100644 --- a/include/sys/zio_crypt.h +++ b/include/sys/zio_crypt.h @@ -61,15 +61,6 @@ struct zbookmark_phys; #define BITS_TO_BYTES(x) (((x) + 7) >> 3) #define BYTES_TO_BITS(x) (x << 3) -/* supported commands for zfs_ioc_crypto() */ -typedef enum zfs_ioc_crypto_cmd { - ZFS_IOC_KEY_CMD_NONE = 0, - ZFS_IOC_KEY_LOAD_KEY, - ZFS_IOC_KEY_UNLOAD_KEY, - ZFS_IOC_KEY_REWRAP, - ZFS_IOC_KEY_COMMANDS -} zfs_ioc_crypto_cmd_t; - typedef enum zio_crypt_type { ZC_TYPE_NONE = 0, ZC_TYPE_CCM, diff --git a/lib/libzfs/libzfs_crypto.c b/lib/libzfs/libzfs_crypto.c index 89788b93c649..3a6dbfe3276c 100644 --- a/lib/libzfs/libzfs_crypto.c +++ b/lib/libzfs/libzfs_crypto.c @@ -194,13 +194,14 @@ get_key_material_raw(FILE *fd, const char *fsname, zfs_keyformat_t keyformat, * Raw keys may have newline characters in them and so can't * use getline(). Read 32 bytes directly instead. */ + *buf = malloc(32 * sizeof (char)); if (*buf == NULL) { ret = ENOMEM; goto out; } - bytes = fread(buf, 1, 32, fd); + bytes = fread(*buf, 1, 32, fd); if (bytes < 0) { /* size errors are handled by the calling function */ free(*buf); @@ -812,8 +813,13 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, } /* default to prompt if no location is specified */ - if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) + if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) { keylocation = "prompt"; + ret = nvlist_add_string(props, + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), keylocation); + if (ret != 0) + goto out; + } /* * If the parent doesn't have a keyformat to inherit @@ -925,8 +931,13 @@ zfs_crypto_clone(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp, } /* default to prompt if no location is specified */ - if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) + if (keyformat != ZFS_KEYFORMAT_NONE && keylocation == NULL) { keylocation = "prompt"; + ret = nvlist_add_string(props, + zfs_prop_to_name(ZFS_PROP_KEYLOCATION), keylocation); + if (ret != 0) + goto out; + } /* * by this point this dataset will be encrypted. The origin's @@ -993,6 +1004,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key load error")); + /* check that encryption is enabled for the pool */ if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); @@ -1004,7 +1016,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); if (keyformat == ZFS_KEYFORMAT_NONE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Encryption not enabled for this dataset.")); + "Dataset not encrypted.")); ret = EINVAL; goto error; } @@ -1064,8 +1076,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp) if (ret != 0) goto error; - ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_LOAD_KEY, NULL, - crypto_args); + ret = lzc_load_key(zhp->zfs_name, crypto_args); if (ret != 0) { switch (ret) { case EINVAL: @@ -1119,6 +1130,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Key unload error")); + /* check that encryption is enabled for the pool */ if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); @@ -1130,7 +1142,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) keyformat = zfs_prop_get_int(zhp, ZFS_PROP_KEYFORMAT); if (keyformat == ZFS_KEYFORMAT_NONE) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Encryption not enabled for this dataset.")); + "Dataset not encrypted.")); ret = EINVAL; goto error; } @@ -1164,7 +1176,7 @@ zfs_crypto_unload_key(zfs_handle_t *zhp) } /* call the ioctl */ - ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_UNLOAD_KEY, NULL, NULL); + ret = lzc_unload_key(zhp->zfs_name); if (ret != 0) { switch (ret) { @@ -1249,8 +1261,9 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) nvlist_t *props = NULL; (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "Key rewrap error")); + dgettext(TEXT_DOMAIN, "Key change error")); + /* check that encryption is enabled for the pool */ if (!encryption_feature_is_enabled(zhp->zpool_hdl)) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "Encryption feature not enabled.")); @@ -1262,7 +1275,7 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); if (crypt == ZIO_CRYPT_OFF) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, - "Encryption not enabled for this dataset.")); + "Dataset not encrypted.")); ret = EINVAL; goto error; } @@ -1305,7 +1318,7 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props) goto error; /* call the ioctl */ - ret = lzc_key(zhp->zfs_name, ZFS_IOC_KEY_REWRAP, props, crypto_args); + ret = lzc_change_key(zhp->zfs_name, props, crypto_args); if (ret != 0) { switch (ret) { case EINVAL: diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 2269ba98dffe..11cd6631c5d3 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -1542,7 +1542,7 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err, if (prop == ZFS_PROP_KEYLOCATION) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Keylocation may only be set if is set locally. " - "This may be changed with 'zfs key -c'")); + "This may be changed with 'zfs change-key.'")); (void) zfs_error(hdl, EZFS_BADPROP, errbuf); } else { (void) zfs_standard_error(hdl, err, errbuf); diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c index 6682df42914c..a1202c1792b7 100644 --- a/lib/libzfs_core/libzfs_core.c +++ b/lib/libzfs_core/libzfs_core.c @@ -121,16 +121,18 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name, { zfs_cmd_t zc = {"\0"}; int error = 0; - char *packed; + char *packed = NULL; size_t size; ASSERT3S(g_refcount, >, 0); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); - packed = fnvlist_pack(source, &size); - zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed; - zc.zc_nvlist_src_size = size; + if (source != NULL) { + packed = fnvlist_pack(source, &size); + zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed; + zc.zc_nvlist_src_size = size; + } if (resultp != NULL) { *resultp = NULL; @@ -164,7 +166,8 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name, } out: - fnvlist_pack_free(packed, size); + if (packed != NULL) + fnvlist_pack_free(packed, size); free((void *)(uintptr_t)zc.zc_nvlist_dst); return (error); } @@ -940,17 +943,42 @@ lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist) * the hidden_args nvlist so that it is not logged */ int -lzc_key(const char *fsname, uint64_t crypto_cmd, nvlist_t *args, - nvlist_t *hidden_args) +lzc_load_key(const char *fsname, nvlist_t *hidden_args) { int error; - nvlist_t *ioc_args = fnvlist_alloc(); - fnvlist_add_uint64(ioc_args, "crypto_cmd", crypto_cmd); + nvlist_t *ioc_args = NULL; + + if (hidden_args == NULL) + return (EINVAL); + + ioc_args = fnvlist_alloc(); + fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args); + error = lzc_ioctl(ZFS_IOC_LOAD_KEY, fsname, ioc_args, NULL); + nvlist_free(ioc_args); + + return (error); +} + +int +lzc_unload_key(const char *fsname) +{ + return (lzc_ioctl(ZFS_IOC_UNLOAD_KEY, fsname, NULL, NULL)); +} + +int +lzc_change_key(const char *fsname, nvlist_t *args, nvlist_t *hidden_args) +{ + int error; + nvlist_t *ioc_args = NULL; + + if (hidden_args == NULL) + return (EINVAL); + + ioc_args = fnvlist_alloc(); + fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args); if (args != NULL) - fnvlist_add_nvlist(ioc_args, "args", args); - if (hidden_args != NULL) - fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args); - error = lzc_ioctl(ZFS_IOC_KEY, fsname, ioc_args, NULL); + fnvlist_add_nvlist(ioc_args, "props", args); + error = lzc_ioctl(ZFS_IOC_CHANGE_KEY, fsname, ioc_args, NULL); nvlist_free(ioc_args); return (error); } diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index b5e9e185a721..2e75f2ee8b59 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -45,10 +45,10 @@ * * The Wrapping Key Tree: * The wrapping key (wkey) tree stores the user's keys that are fed into the - * kernel through 'zfs key -K' and related commands. Datasets inherit their + * kernel through 'zfs load-key' and related commands. Datasets inherit their * parent's wkey, so they are refcounted. The wrapping keys remain in memory - * until they are explicitly unloaded (with "zfs key -u"). Unloading is only - * possible when no datasets are using them (refcount=0). + * until they are explicitly unloaded (with "zfs unload-key"). Unloading is + * only possible when no datasets are using them (refcount=0). * * The DSL Crypto Key Tree: * The DSL Crypto Keys are the in-memory representation of decrypted master @@ -238,9 +238,6 @@ dsl_crypto_params_create_nvlist(nvlist_t *props, nvlist_t *crypto_args, goto error; } - /* remove crypto_cmd from props since it should not be used again */ - (void) nvlist_remove_all(props, "crypto_cmd"); - /* if the user asked for the deault crypt, determine that now */ if (dcp->cp_crypt == ZIO_CRYPT_ON) { dcp->cp_crypt = ZIO_CRYPT_ON_VALUE; @@ -834,8 +831,7 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp) if (dcp == NULL || dcp->cp_wkey == NULL) return (SET_ERROR(EINVAL)); if (dcp->cp_crypt != ZIO_CRYPT_INHERIT || dcp->cp_keylocation != NULL || - dcp->cp_salt != 0 || dcp->cp_cmd != ZFS_IOC_KEY_CMD_NONE || - dcp->cp_iters != 0) + dcp->cp_salt != 0 || dcp->cp_iters != 0) return (SET_ERROR(EINVAL)); ret = dsl_pool_hold(dsname, FTAG, &dp); @@ -1197,7 +1193,7 @@ spa_keystore_rewrap_check(void *arg, dmu_tx_t *tx) } /* crypt cannot be changed after creation */ - if (dcp->cp_crypt != ZIO_CRYPT_INHERIT || dcp->cp_cmd != 0) { + if (dcp->cp_crypt != ZIO_CRYPT_INHERIT) { ret = SET_ERROR(EINVAL); goto error; } @@ -1298,7 +1294,7 @@ spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) spa_keystore_rewrap_args_t *skra = arg; dsl_wrapping_key_t *wkey = skra->skra_cp->cp_wkey; dsl_wrapping_key_t *found_wkey; - uint64_t crypt; + uint64_t keyformat, crypt; const char *keylocation = skra->skra_cp->cp_keylocation; /* create and initialize the wrapping key */ @@ -1308,7 +1304,7 @@ spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) /* * Set additional properties which can be sent along with this ioctl. * Note that this command can set keylocation even if it can't normally - * be set via 'zfs set' due to a non-local keysource. In this case we + * be set via 'zfs set' due to a non-local keylocation. In this case we * will actually sever the inheritted keyocation. */ if (keylocation != NULL) { @@ -1318,9 +1314,10 @@ spa_keystore_rewrap_sync(void *arg, dmu_tx_t *tx) } if (skra->skra_cp->cp_keyformat != ZFS_KEYFORMAT_NONE) { + keyformat = skra->skra_cp->cp_keyformat; dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), ZPROP_SRC_LOCAL, - 8, 1, &skra->skra_cp->cp_keyformat, tx); + 8, 1, &keyformat, tx); } dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index c8e063653b4a..17fe9c8c08f0 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -1290,34 +1290,17 @@ zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) } static int -zfs_secpolicy_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) +zfs_secpolicy_load_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) { - int ret = 0; - uint64_t crypto_cmd; - - ret = nvlist_lookup_uint64(innvl, "crypto_cmd", &crypto_cmd); - if (ret != 0) { - ret = SET_ERROR(EINVAL); - goto out; - } - - switch (crypto_cmd) { - case ZFS_IOC_KEY_LOAD_KEY: - case ZFS_IOC_KEY_UNLOAD_KEY: - ret = zfs_secpolicy_write_perms(zc->zc_name, - ZFS_DELEG_PERM_LOAD_KEY, cr); - break; - case ZFS_IOC_KEY_REWRAP: - ret = zfs_secpolicy_write_perms(zc->zc_name, - ZFS_DELEG_PERM_CHANGE_KEY, cr); - break; - default: - ret = SET_ERROR(EINVAL); - break; - } + return (zfs_secpolicy_write_perms(zc->zc_name, + ZFS_DELEG_PERM_LOAD_KEY, cr)); +} -out: - return (ret); +static int +zfs_secpolicy_change_key(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) +{ + return (zfs_secpolicy_write_perms(zc->zc_name, + ZFS_DELEG_PERM_CHANGE_KEY, cr)); } /* @@ -5744,13 +5727,18 @@ zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) return (error); } + +/* + * Load a user's wrapping key into the kernel. + * innvl: "hidden_args" -> { "wkeydata" -> value } + */ +/* ARGSUSED */ static int -zfs_ioc_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) +zfs_ioc_load_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) { - int ret = 0; + int ret; dsl_crypto_params_t *dcp = NULL; - uint64_t crypto_cmd; - nvlist_t *args, *hidden_args; + nvlist_t *hidden_args; spa_t *spa; ret = spa_open(dsname, &spa, FTAG); @@ -5759,74 +5747,143 @@ zfs_ioc_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) if (!spa_feature_is_enabled(spa, SPA_FEATURE_ENCRYPTION)) { spa_close(spa, FTAG); - return (SET_ERROR(EINVAL)); + return (SET_ERROR(ENOTSUP)); } spa_close(spa, FTAG); - if (strchr(dsname, '@') || strchr(dsname, '%')) { + if (strchr(dsname, '@') != NULL || strchr(dsname, '%') != NULL) { ret = (SET_ERROR(EINVAL)); goto error; } - ret = nvlist_lookup_uint64(innvl, "crypto_cmd", &crypto_cmd); + ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); if (ret != 0) { + ret = SET_ERROR(EINVAL); + goto error; + } + + ret = dsl_crypto_params_create_nvlist(NULL, hidden_args, &dcp); + if (ret != 0) + goto error; + + ret = spa_keystore_load_wkey(dsname, dcp); + if (ret != 0) + goto error; + + dsl_crypto_params_free(dcp, B_FALSE); + + return (0); + +error: + dsl_crypto_params_free(dcp, B_TRUE); + return (ret); +} + +/* + * Unload a user's wrapping key from the kernel. + * Both innvl and outnvl are unused. + */ +/* ARGSUSED */ +static int +zfs_ioc_unload_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) +{ + int ret; + spa_t *spa; + + ret = spa_open(dsname, &spa, FTAG); + if (ret != 0) + return (ret); + + if (!spa_feature_is_enabled(spa, SPA_FEATURE_ENCRYPTION)) { + spa_close(spa, FTAG); + return (SET_ERROR(ENOTSUP)); + } + + spa_close(spa, FTAG); + + if (strchr(dsname, '@') != NULL || strchr(dsname, '%') != NULL) { ret = (SET_ERROR(EINVAL)); goto error; } - switch (crypto_cmd) { - case ZFS_IOC_KEY_LOAD_KEY: - ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, - &hidden_args); - if (ret != 0) { - ret = SET_ERROR(EINVAL); - goto error; - } + ret = spa_keystore_unload_wkey(dsname); + if (ret != 0) + goto error; - ret = dsl_crypto_params_create_nvlist(NULL, hidden_args, &dcp); - if (ret != 0) - goto error; + return (0); - ret = spa_keystore_load_wkey(dsname, dcp); - if (ret != 0) - goto error; +error: + return (ret); +} - break; - case ZFS_IOC_KEY_UNLOAD_KEY: - ret = spa_keystore_unload_wkey(dsname); - if (ret != 0) - goto error; +/* + * Changes a user's wrapping key used to decrypt a dataset. The keyformat, + * keylocation, pbkdf2salt, and pbkdf2iters properties can also be specified + * here to change how the key is derived in userspace. + * + * innvl: { + * "hidden_args" -> { "wkeydata" -> value } + * "props" -> { prop -> value } + * } + * + * outnvl is unused + */ +/* ARGSUSED */ +static int +zfs_ioc_change_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl) +{ + int ret; + dsl_crypto_params_t *dcp = NULL; + nvlist_t *args, *hidden_args; + spa_t *spa; - break; - case ZFS_IOC_KEY_REWRAP: - ret = nvlist_lookup_nvlist(innvl, "args", &args); - if (ret != 0) { - ret = SET_ERROR(EINVAL); - goto error; - } + ret = spa_open(dsname, &spa, FTAG); + if (ret != 0) + return (ret); - ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, - &hidden_args); - if (ret != 0) { - ret = SET_ERROR(EINVAL); - goto error; - } + if (!spa_feature_is_enabled(spa, SPA_FEATURE_ENCRYPTION)) { + spa_close(spa, FTAG); + return (SET_ERROR(ENOTSUP)); + } - ret = dsl_crypto_params_create_nvlist(args, hidden_args, &dcp); - if (ret != 0) - goto error; + spa_close(spa, FTAG); - ret = spa_keystore_rewrap(dsname, dcp); - if (ret != 0) - goto error; + if (strchr(dsname, '@') != NULL || strchr(dsname, '%') != NULL) { + ret = (SET_ERROR(EINVAL)); + goto error; + } - break; - default: + ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); + if (ret != 0) { + ret = SET_ERROR(EINVAL); + goto error; + } + + ret = dsl_crypto_params_create_nvlist(NULL, hidden_args, &dcp); + if (ret != 0) + goto error; + + ret = nvlist_lookup_nvlist(innvl, "props", &args); + if (ret != 0) { + ret = SET_ERROR(EINVAL); + goto error; + } + + ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args); + if (ret != 0) { ret = SET_ERROR(EINVAL); goto error; } + ret = dsl_crypto_params_create_nvlist(args, hidden_args, &dcp); + if (ret != 0) + goto error; + + ret = spa_keystore_rewrap(dsname, dcp); + if (ret != 0) + goto error; + dsl_crypto_params_free(dcp, B_FALSE); return (0); @@ -6007,9 +6064,16 @@ zfs_ioctl_init(void) zfs_ioctl_register("receive", ZFS_IOC_RECV_NEW, zfs_ioc_recv_new, zfs_secpolicy_recv_new, DATASET_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); - zfs_ioctl_register("crypto", ZFS_IOC_KEY, - zfs_ioc_key, zfs_secpolicy_key, + zfs_ioctl_register("load-key", ZFS_IOC_LOAD_KEY, + zfs_ioc_load_key, zfs_secpolicy_load_key, + DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE); + zfs_ioctl_register("unload-key", ZFS_IOC_UNLOAD_KEY, + zfs_ioc_unload_key, zfs_secpolicy_load_key, DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE); + zfs_ioctl_register("change-key", ZFS_IOC_CHANGE_KEY, + zfs_ioc_change_key, zfs_secpolicy_change_key, + DATASET_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, + B_TRUE, B_TRUE); /* IOCTLS that use the legacy function signature */ diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh index ec5a02147151..406a572627ef 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted.ksh @@ -36,11 +36,11 @@ # # DESCRIPTION: # 'zfs create' should be able to create an encrypted dataset with -# a valid encryption algorithm, keysource, and key. +# a valid encryption algorithm, keyformat, keylocation, and key. # # STRATEGY: # 1. Create a filesystem for each encryption type -# 2. Create a filesystem for each keysource type +# 2. Create a filesystem for each keyformat type # 3. Verify that each filesystem has the correct properties set # @@ -70,52 +70,52 @@ set -A ENCRYPTION_PROPS "encryption=aes-256-ccm" \ "encryption=aes-192-gcm" \ "encryption=aes-256-gcm" -set -A KEYSOURCE_TYPES "keysource=raw,prompt" \ - "keysource=hex,prompt" \ - "keysource=passphrase,prompt" +set -A KEYFORMATS "keyformat=raw" \ + "keyformat=hex" \ + "keyformat=passphrase" -set -A KEYSOURCES "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ +set -A USER_KEYS "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ "abcdefgh" log_assert "'zfs create' should properly create encrypted datasets" -typeset -i i=0 -while (( $i < ${#ENCRYPTION_ALGS[*]} )); do - log_must eval 'echo ${KEYSOURCES[0]} | \ - $ZFS create -o ${ENCRYPTION_ALGS[$i]} -o ${KEYSOURCE_TYPES[0]} \ - $TESTPOOL/$TESTFS1' - - datasetexists $TESTPOOL/$TESTFS1 || \ - log_fail "zfs create -o ${ENCRYPTION_ALGS[$i]} \ - -o ${KEYSOURCE_TYPES[0]} $TESTPOOL/$TESTFS1 fail." - - propertycheck $TESTPOOL/$TESTFS1 ${ENCRYPTION_PROPS[i]} || \ - log_fail "${ENCRYPTION_ALGS[i]} is failed to set." - propertycheck $TESTPOOL/$TESTFS1 ${KEYSOURCE_TYPES[0]} || \ - log_fail "${KEYSOURCE_TYPES[0]} is failed to set." - - log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 - (( i = i + 1 )) -done - -typeset -i j=0 -while (( $j < ${#KEYSOURCE_TYPES[*]} )); do - log_must eval 'echo ${KEYSOURCES[$j]} | \ - $ZFS create -o ${ENCRYPTION_ALGS[0]} -o ${KEYSOURCE_TYPES[$j]} \ - $TESTPOOL/$TESTFS1' - - datasetexists $TESTPOOL/$TESTFS1 || \ - log_fail "zfs create -o ${ENCRYPTION_ALGS[0]} \ - -o ${KEYSOURCE_TYPES[$j]} $TESTPOOL/$TESTFS1 fail." - - propertycheck $TESTPOOL/$TESTFS1 ${ENCRYPTION_PROPS[0]} || \ - log_fail "${ENCRYPTION_ALGS[0]} is failed to set." - propertycheck $TESTPOOL/$TESTFS1 ${KEYSOURCE_TYPES[j]} || \ - log_fail "${KEYSOURCE_TYPES[j]} is failed to set." - - log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 - (( j = j + 1 )) -done - -log_pass "'zfs create properly creates encrypted datasets" +# typeset -i i=0 +# while (( $i < ${#ENCRYPTION_ALGS[*]} )); do + # log_must eval 'echo ${USER_KEYS[0]} | \ + # $ZFS create -o ${ENCRYPTION_ALGS[$i]} -o ${KEYFORMATS[0]} \ + # $TESTPOOL/$TESTFS1' + + # datasetexists $TESTPOOL/$TESTFS1 || \ + # log_fail "zfs create -o ${ENCRYPTION_ALGS[$i]} \ + # -o ${KEYFORMATS[0]} $TESTPOOL/$TESTFS1 fail." + + # propertycheck $TESTPOOL/$TESTFS1 ${ENCRYPTION_PROPS[i]} || \ + # log_fail "failed to set ${ENCRYPTION_ALGS[i]}." + # propertycheck $TESTPOOL/$TESTFS1 ${KEYFORMATS[0]} || \ + # log_fail "failed to set ${KEYFORMATS[0]}." + + # log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 + # (( i = i + 1 )) +# done + +# typeset -i j=0 +# while (( $j < ${#KEYFORMATS[*]} )); do + # log_must eval 'echo ${USER_KEYS[$j]} | \ + # $ZFS create -o ${ENCRYPTION_ALGS[0]} -o ${KEYFORMATS[$j]} \ + # $TESTPOOL/$TESTFS1' + + # datasetexists $TESTPOOL/$TESTFS1 || \ + # log_fail "zfs create -o ${ENCRYPTION_ALGS[0]} \ + # -o ${KEYFORMATS[$j]} $TESTPOOL/$TESTFS1 fail." + + # propertycheck $TESTPOOL/$TESTFS1 ${ENCRYPTION_PROPS[0]} || \ + # log_fail "failed to set ${ENCRYPTION_ALGS[0]}." + # propertycheck $TESTPOOL/$TESTFS1 ${KEYFORMATS[j]} || \ + # log_fail "failed to set ${KEYFORMATS[j]}." + + # log_must $ZFS destroy -f $TESTPOOL/$TESTFS1 + # (( j = j + 1 )) +# done + +log_pass "'zfs create' properly creates encrypted datasets" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh index fae09d4f578c..6c266ed6c50a 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_neg.ksh @@ -33,7 +33,7 @@ # # DESCRIPTION: -# 'zfs key -c' should not be able to change an unloaded +# 'zfs change-key' should not be able to change an unloaded # wrapping key or allow a change to an invalid key # # STRATEGY: @@ -41,9 +41,9 @@ # 2. Attempt to change the wrapping key to an invalid key # 3. Verify that the old key still works for the encrypted system # 4. Unload the wrapping key -# 5. Attempt to change the key and keysource to a valid key +# 5. Attempt to change the key and keyformat to a valid key # 6. Verify that the old key still works for the encrypted system -# 7. Attempt to change the key and keysource on an unencrypted filesystem +# 7. Attempt to change the key and keyformat on an unencrypted filesystem # verify_runnable "both" @@ -55,27 +55,27 @@ function cleanup log_onexit cleanup -log_assert "'zfs key -c' should not change an unloaded wrapping key \ +log_assert "'zfs change-key' should not change an unloaded wrapping key \ or allow a change to an invalid key" create_default_encrypted_dataset log_must $ZFS unmount $TESTPOOL/$CRYPTDS log_mustnot eval '$ECHO $SHORT_PKEY | \ - $ZFS key -c -o keysource=passphrase,prompt $TESTPOOL/$CRYPTDS' + $ZFS change-key -o keyformat=passphrase $TESTPOOL/$CRYPTDS' -log_must $ZFS key -u $TESTPOOL/$CRYPTDS -log_must eval '$ECHO $PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS +log_must eval '$ECHO $PKEY | $ZFS load-key $TESTPOOL/$CRYPTDS' check_key_available $TESTPOOL/$CRYPTDS -log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS -log_mustnot eval '$ECHO $HKEY | $ZFS key -c -o keysource=hex,prompt \ +log_mustnot eval '$ECHO $HKEY | $ZFS change-key -o keyformat=hex \ $TESTPOOL/$CRYPTDS' check_key_unavailable $TESTPOOL/$CRYPTDS -log_mustnot eval '$ECHO $PKEY | $ZFS key -c -o keysource=passphrase,prompt \ +log_mustnot eval '$ECHO $PKEY | $ZFS change-key -o keyformat=passphrase \ $TESTPOOL' -log_pass "'zfs key -c' does not change an unloaded wrapping key \ +log_pass "'zfs change-key' does not change an unloaded wrapping key \ or allow a change to an invalid key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh index 7a5dda0b7846..b63d18c11fb2 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_change_pos.ksh @@ -33,12 +33,12 @@ # # DESCRIPTION: -# 'zfs key -c' should be able to change one loaded, valid +# 'zfs change-key' should be able to change one loaded, valid # wrapping key to another. # # STRATEGY: # 1. Create an encrypted dataset and unmount it -# 2. Attempt to change the key and keysource +# 2. Attempt to change the key and keyformat # 3. Verify that the old key cannot be loaded # 4. Verify that the new can be loaded # @@ -52,22 +52,21 @@ function cleanup log_onexit cleanup -log_assert "'zfs key -c' should properly change a valid wrapping key \ +log_assert "'zfs change-key' should properly change a valid wrapping key \ to another" create_default_encrypted_dataset log_must $ZFS unmount $TESTPOOL/$CRYPTDS -log_must eval '$ECHO $HKEY | $ZFS key -c -o keysource=hex,prompt \ +log_must eval '$ECHO $HKEY | $ZFS change-key -o keyformat=hex \ $TESTPOOL/$CRYPTDS' - check_key_available $TESTPOOL/$CRYPTDS -log_must $ZFS key -u $TESTPOOL/$CRYPTDS -log_mustnot eval '$ECHO $PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS +log_mustnot eval '$ECHO $PKEY | $ZFS load-key $TESTPOOL/$CRYPTDS' check_key_unavailable $TESTPOOL/$CRYPTDS -log_must eval '$ECHO $HKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +log_must eval '$ECHO $HKEY | $ZFS load-key $TESTPOOL/$CRYPTDS' check_key_available $TESTPOOL/$CRYPTDS -log_pass "'zfs key -c' properly changes a valid wrapping key to another" +log_pass "'zfs change-key' properly changes a valid wrapping key to another" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib index 57c01838f7d2..28edffc272d5 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_common.kshlib @@ -30,7 +30,7 @@ function create_default_encrypted_dataset { log_must $ECHO $PKEY | $ZFS create -o encryption=on \ - -o keysource=passphrase,prompt $TESTPOOL/$CRYPTDS + -o keyformat=passphrase $TESTPOOL/$CRYPTDS } function destroy_default_encrypted_dataset diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh index 8dd0e6728725..fb9213800ac7 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_neg.ksh @@ -33,7 +33,7 @@ # # DESCRIPTION: -# 'zfs key -l' should not load an invalid key into the ZFS keystore. +# 'zfs load-key' should not load an invalid key into the ZFS keystore. # # STRATEGY: # 1. Create an encrypted dataset @@ -51,12 +51,12 @@ function cleanup log_onexit cleanup -log_assert "'zfs key -l' should not load an invalid wrapping key" +log_assert "'zfs load-key' should not load an invalid wrapping key" create_default_encrypted_dataset log_must $ZFS unmount $TESTPOOL/$CRYPTDS -log_must $ZFS key -u $TESTPOOL/$CRYPTDS -log_mustnot eval '$ECHO $BAD_PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS +log_mustnot eval '$ECHO $BAD_PKEY | $ZFS load-key $TESTPOOL/$CRYPTDS' check_key_unavailable $TESTPOOL/$CRYPTDS -log_pass "'zfs key -l' does not load an invalid wrapping key" +log_pass "'zfs load-key' does not load an invalid wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh index 998d1ebc96b4..371f48b11d9b 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_load_pos.ksh @@ -33,7 +33,7 @@ # # DESCRIPTION: -# 'zfs key -l' should load a valid key into the ZFS keystore. +# 'zfs load-key' should load a valid key into the ZFS keystore. # # STRATEGY: # 1. Create an encrypted dataset @@ -51,12 +51,12 @@ function cleanup log_onexit cleanup -log_assert "'zfs key -l' should properly load a valid wrapping key" +log_assert "'zfs load-key' should properly load a valid wrapping key" create_default_encrypted_dataset log_must $ZFS unmount $TESTPOOL/$CRYPTDS -log_must $ZFS key -u $TESTPOOL/$CRYPTDS -log_must eval '$ECHO $PKEY | $ZFS key -l $TESTPOOL/$CRYPTDS' +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS +log_must eval '$ECHO $PKEY | $ZFS load-key $TESTPOOL/$CRYPTDS' check_key_available $TESTPOOL/$CRYPTDS -log_pass "'zfs key -l' properly loads a valid wrapping key" +log_pass "'zfs load-key' properly loads a valid wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh index 7c847a1cc35a..3a6345653812 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_neg.ksh @@ -33,7 +33,7 @@ # # DESCRIPTION: -# 'zfs key -u' should not unload a key from the ZFS keystore +# 'zfs unload-key' should not unload a key from the ZFS keystore # if the dataset is busy or not encrypted. # # STRATEGY: @@ -53,15 +53,15 @@ function cleanup log_onexit cleanup -log_assert "'zfs key -u' should not unload a wrapping key if the dataset \ +log_assert "'zfs unload-key' should not unload a wrapping key if the dataset \ if the dataset is busy or not encrypted" create_default_encrypted_dataset -log_mustnot $ZFS key -u $TESTPOOL/$CRYPTDS +log_mustnot $ZFS unload-key $TESTPOOL/$CRYPTDS check_key_available $TESTPOOL/$CRYPTDS -log_mustnot $ZFS key -u $TESTPOOL +log_mustnot $ZFS unload-key $TESTPOOL -log_pass "'zfs key -u' properly returns an error when unloading a wrapping \ +log_pass "'zfs unload-key' properly returns an error when unloading a wrapping \ key from a busy or unencrypted dataset" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh index 2cf29742277b..c9f130ea6b32 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_key/zfs_key_unload_pos.ksh @@ -33,7 +33,7 @@ # # DESCRIPTION: -# 'zfs key -u' will unload a key from the ZFS keystore. +# 'zfs unload-key' will unload a key from the ZFS keystore. # # STRATEGY: # 1. Create an encrypted dataset @@ -51,11 +51,11 @@ function cleanup log_onexit cleanup -log_assert "'zfs key -u' should properly unload a wrapping key" +log_assert "'zfs unload-key' should properly unload a wrapping key" create_default_encrypted_dataset log_must $ZFS unmount $TESTPOOL/$CRYPTDS -log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS check_key_unavailable $TESTPOOL/$CRYPTDS -log_pass "'zfs key -u' properly unloads a wrapping key" +log_pass "'zfs unload-key' properly unloads a wrapping key" diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh index 61d0a1358f14..6d43a2dcc998 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted.ksh @@ -58,10 +58,10 @@ log_onexit cleanup log_assert "'zfs mount' should properly load a valid wrapping key" log_must eval 'echo $PASSKEY | $ZFS create -o encryption=on \ - -o keysource=passphrase,prompt $TESTPOOL/$CRYPTDS' + -o keyformat=passphrase $TESTPOOL/$CRYPTDS' log_must $ZFS unmount $TESTPOOL/$CRYPTDS -log_must $ZFS key -u $TESTPOOL/$CRYPTDS +log_must $ZFS unload-key $TESTPOOL/$CRYPTDS log_must eval '$ECHO $PKEY | $ZFS mount $TESTPOOL/$CRYPTDS' mounted $TESTPOOL/$CRYPTDS || \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh index 245772fae4ea..415e7f1ab010 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_neg.ksh @@ -65,10 +65,9 @@ log_must $ZFS snapshot $TESTPOOL/$TESTFS1@snap log_must eval "$ZFS send $TESTPOOL/$TESTFS1@snap > $streamfile" log_must eval "$ECHO $passphrase | \ - $ZFS create -o encryption=on -o keysource=passphrase,prompt \ - $TESTPOOL/$cryptds" + $ZFS create -o encryption=on -o keyformat=passphrase $TESTPOOL/$cryptds" log_must $ZFS unmount $TESTPOOL/$cryptds -log_must $ZFS key -u $TESTPOOL/$cryptds +log_must $ZFS unload-key $TESTPOOL/$cryptds log_mustnot eval "$ZFS recv $TESTPOOL/$cryptds/recv < $streamfile" log_pass "'zfs receive' fails when receiving to a dataset with unloaded keys." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh index 4268ac7700c8..f15e79171231 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_encrypted_pos.ksh @@ -70,8 +70,7 @@ log_must $ZFS snapshot $TESTPOOL/$TESTFS1@snap log_must eval "$ZFS send $TESTPOOL/$TESTFS1@snap > $streamfile" log_must eval "$ECHO $passphrase | \ - $ZFS create -o encryption=on -o keysource=passphrase,prompt \ - $TESTPOOL/$cryptds" + $ZFS create -o encryption=on -o keyformat=passphrase $TESTPOOL/$cryptds" log_must eval "$ZFS recv $TESTPOOL/$cryptds/recv < $streamfile" recv_mntpnt=$(get_prop mountpoint $TESTPOOL/$cryptds/recv) || \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh index 3b086e6e4b90..7068f35296c5 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_neg.ksh @@ -61,11 +61,10 @@ typeset passphrase="abcdefgh" typeset snap="$TESTPOOL/$cryptds@snap" log_must eval "$ECHO $passphrase | \ - $ZFS create -o encryption=on -o keysource=passphrase,prompt \ - $TESTPOOL/$cryptds" + $ZFS create -o encryption=on -o keyformat=passphrase $TESTPOOL/$cryptds" log_must $ZFS snapshot $snap log_must $ZFS unmount $TESTPOOL/$cryptds -log_must $ZFS key -u $TESTPOOL/$cryptds +log_must $ZFS unload-key $TESTPOOL/$cryptds log_mustnot eval "$ZFS send $snap > /dev/null" log_pass "'zfs send' cannot perform sends from encrypted datasets with unloaded keys." diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh index 2d055db2e1e2..330339718cc7 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_send/zfs_send_encrypted_pos.ksh @@ -59,8 +59,7 @@ typeset passphrase="abcdefgh" typeset snap="$TESTPOOL/$cryptds@snap" log_must eval "$ECHO $passphrase | \ - $ZFS create -o encryption=on -o keysource=passphrase,prompt \ - $TESTPOOL/$cryptds" + $ZFS create -o encryption=on -o keyformat=passphrase $TESTPOOL/$cryptds" log_must $ZFS snapshot $snap log_must eval "$ZFS send $snap > /dev/null" diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh index 426777db191b..1d653e971c0d 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_encrypted.ksh @@ -38,7 +38,7 @@ # # STRATEGY: # 1. Create a pool for each encryption type and verify that it is properly set -# 2. Create a pool for each keysource type and verify that it is properly set +# 2. Create a pool for each keyformat type and verify that it is properly set # verify_runnable "global" @@ -66,11 +66,11 @@ set -A ENCRYPTION_PROPS "encryption=aes-256-ccm" \ "encryption=aes-192-gcm" \ "encryption=aes-256-gcm" -set -A KEYSOURCE_TYPES "keysource=raw,prompt" \ - "keysource=hex,prompt" \ - "keysource=passphrase,prompt" +set -A KEYFORMATS "keyformat=raw" \ + "keyformat=hex" \ + "keyformat=passphrase" -set -A KEYSOURCES "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ +set -A USER_KEYS "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \ "abcdefgh" @@ -78,31 +78,31 @@ log_assert "'zpool create' can create encrypted pools" typeset -i i=0 while (( i < ${#ENCRYPTION_ALGS[*]} )); do - log_must eval "$ECHO ${KEYSOURCES[0]} | $ZPOOL create \ - -O ${ENCRYPTION_ALGS[i]} -O ${KEYSOURCE_TYPES[0]} \ + log_must eval "$ECHO ${USER_KEYS[0]} | $ZPOOL create \ + -O ${ENCRYPTION_ALGS[i]} -O ${KEYFORMATS[0]} \ $TESTPOOL $DISKS" propertycheck $TESTPOOL ${ENCRYPTION_PROPS[i]} || \ log_fail "${ENCRYPTION_ALGS[i]} is failed to set." - propertycheck $TESTPOOL ${KEYSOURCE_TYPES[0]} || \ - log_fail "${KEYSOURCE_TYPES[0]} is failed to set." + propertycheck $TESTPOOL ${KEYFORMATS[0]} || \ + log_fail "${KEYFORMATS[0]} is failed to set." log_must $ZPOOL destroy $TESTPOOL (( i = i + 1 )) done typeset -i j=0 -while (( j < ${#KEYSOURCE_TYPES[*]} )); do - log_must eval "$ECHO ${KEYSOURCES[j]} | $ZPOOL create \ - -O ${ENCRYPTION_ALGS[0]} -O ${KEYSOURCE_TYPES[j]} \ +while (( j < ${#KEYFORMATS[*]} )); do + log_must eval "$ECHO ${USER_KEYS[j]} | $ZPOOL create \ + -O ${ENCRYPTION_ALGS[0]} -O ${KEYFORMATS[j]} \ $TESTPOOL $DISKS" propertycheck $TESTPOOL ${ENCRYPTION_PROPS[0]} || \ log_fail "${ENCRYPTION_ALGS[0]} is failed to set." - propertycheck $TESTPOOL ${KEYSOURCE_TYPES[j]} || \ - log_fail "${KEYSOURCE_TYPES[j]} is failed to set." + propertycheck $TESTPOOL ${KEYFORMATS[j]} || \ + log_fail "${KEYFORMATS[j]} is failed to set." log_must $ZPOOL destroy $TESTPOOL (( j = j + 1 ))