diff options
author | Tom Caputi <[email protected]> | 2017-10-13 13:09:04 -0400 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2018-08-15 09:48:49 -0700 |
commit | d9c460a0b659c044d4397b7405712f2c9450d3c4 (patch) | |
tree | 973b92b7f835540f5ee722b56ff838828fd53116 /lib/libzfs | |
parent | fe8a7982ca90c3c9b8a09ec33f032527d7034a7b (diff) |
Added encryption support for zfs recv -o / -x
One small integration that was absent from b52563 was
support for zfs recv -o / -x with regards to encryption
parameters. The main use cases of this are as follows:
* Receiving an unencrypted stream as encrypted without
needing to create a "dummy" encrypted parent so that
encryption can be inheritted.
* Allowing users to change their keylocation on receive,
so long as the receiving dataset is an encryption root.
* Allowing users to explicitly exclude or override the
encryption property from an unencrypted properties stream,
allowing it to be received as encrypted.
* Receiving a recursive heirarchy of unencrypted datasets,
encrypting the top-level one and forcing all children to
inherit the encryption.
Reviewed-by: Jorgen Lundman <[email protected]>
Reviewed by: Matthew Ahrens <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Reviewed-by: Richard Elling <[email protected]>
Signed-off-by: Tom Caputi <[email protected]>
Closes #7650
Diffstat (limited to 'lib/libzfs')
-rw-r--r-- | lib/libzfs/libzfs_crypto.c | 14 | ||||
-rw-r--r-- | lib/libzfs/libzfs_dataset.c | 4 | ||||
-rw-r--r-- | lib/libzfs/libzfs_pool.c | 2 | ||||
-rw-r--r-- | lib/libzfs/libzfs_sendrecv.c | 95 |
4 files changed, 100 insertions, 15 deletions
diff --git a/lib/libzfs/libzfs_crypto.c b/lib/libzfs/libzfs_crypto.c index 0956466e2..3318a6bd2 100644 --- a/lib/libzfs/libzfs_crypto.c +++ b/lib/libzfs/libzfs_crypto.c @@ -667,7 +667,8 @@ zfs_crypto_get_encryption_root(zfs_handle_t *zhp, boolean_t *is_encroot, int zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, - nvlist_t *pool_props, uint8_t **wkeydata_out, uint_t *wkeylen_out) + nvlist_t *pool_props, boolean_t stdin_available, uint8_t **wkeydata_out, + uint_t *wkeylen_out) { int ret; char errbuf[1024]; @@ -808,6 +809,17 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props, * encryption root. Populate the encryption params. */ if (keylocation != NULL) { + /* + * 'zfs recv -o keylocation=prompt' won't work because stdin + * is being used by the send stream, so we disallow it. + */ + if (!stdin_available && strcmp(keylocation, "prompt") == 0) { + ret = EINVAL; + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Cannot use " + "'prompt' keylocation because stdin is in use.")); + goto out; + } + ret = populate_create_encryption_params_nvlists(hdl, NULL, B_FALSE, keyformat, keylocation, props, &wkeydata, &wkeylen); diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 7f7dd1594..85b4c5531 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -3731,8 +3731,8 @@ 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, &wkeydata, - &wkeylen) != 0) { + if (zfs_crypto_create(hdl, parent, props, NULL, B_TRUE, + &wkeydata, &wkeylen) != 0) { nvlist_free(props); return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf)); } diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index d19ca7714..0785c3170 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -1224,7 +1224,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) { goto create_failed; } - if (zfs_crypto_create(hdl, NULL, zc_fsprops, props, + if (zfs_crypto_create(hdl, NULL, zc_fsprops, props, B_TRUE, &wkeydata, &wkeylen) != 0) { zfs_error(hdl, EZFS_CRYPTOFAILED, msg); goto create_failed; diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 9a4f373a1..b5c91ec20 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -3461,16 +3461,19 @@ recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap, * oxprops: valid output override (-o) and excluded (-x) properties */ static int -zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned, - boolean_t recursive, boolean_t toplevel, nvlist_t *recvprops, - nvlist_t *cmdprops, nvlist_t *origprops, nvlist_t **oxprops, - const char *errbuf) +zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, + char *fsname, boolean_t zoned, boolean_t recursive, boolean_t newfs, + boolean_t raw, boolean_t toplevel, nvlist_t *recvprops, nvlist_t *cmdprops, + nvlist_t *origprops, nvlist_t **oxprops, uint8_t **wkeydata_out, + uint_t *wkeylen_out, const char *errbuf) { nvpair_t *nvp; nvlist_t *oprops, *voprops; zfs_handle_t *zhp = NULL; zpool_handle_t *zpool_hdl = NULL; + char *cp; int ret = 0; + char namebuf[ZFS_MAX_DATASET_NAME_LEN]; if (nvlist_empty(cmdprops)) return (0); /* No properties to override or exclude */ @@ -3478,6 +3481,33 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned, *oxprops = fnvlist_alloc(); oprops = fnvlist_alloc(); + strlcpy(namebuf, fsname, ZFS_MAX_DATASET_NAME_LEN); + + /* + * Get our dataset handle. The target dataset may not exist yet. + */ + if (zfs_dataset_exists(hdl, namebuf, ZFS_TYPE_DATASET)) { + zhp = zfs_open(hdl, namebuf, ZFS_TYPE_DATASET); + if (zhp == NULL) { + ret = -1; + goto error; + } + } + + /* open the zpool handle */ + cp = strchr(namebuf, '/'); + if (cp != NULL) + *cp = '\0'; + zpool_hdl = zpool_open(hdl, namebuf); + if (zpool_hdl == NULL) { + ret = -1; + goto error; + } + + /* restore namebuf to match fsname for later use */ + if (cp != NULL) + *cp = '/'; + /* * first iteration: process excluded (-x) properties now and gather * added (-o) properties to be later processed by zfs_valid_proplist() @@ -3509,6 +3539,17 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned, goto error; } + /* raw streams can't override encryption properties */ + if ((zfs_prop_encryption_key_param(prop) || + prop == ZFS_PROP_ENCRYPTION) && (raw || !newfs)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "encryption property '%s' cannot " + "be set or excluded for raw or incremental " + "streams."), name); + ret = zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + switch (nvpair_type(nvp)) { case DATA_TYPE_BOOLEAN: /* -x property */ /* @@ -3559,6 +3600,21 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned, goto error; } + /* + * zfs_crypto_create() requires the parent name. Get it + * by truncating the fsname copy stored in namebuf. + */ + cp = strrchr(namebuf, '/'); + if (cp != NULL) + *cp = '\0'; + + if (!raw && zfs_crypto_create(hdl, namebuf, voprops, NULL, + B_FALSE, wkeydata_out, wkeylen_out) != 0) { + fnvlist_free(voprops); + ret = zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf); + goto error; + } + /* second pass: process "-o" properties */ fnvlist_merge(*oxprops, voprops); fnvlist_free(voprops); @@ -3572,6 +3628,10 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned, } error: + if (zhp != NULL) + zfs_close(zhp); + if (zpool_hdl != NULL) + zpool_close(zpool_hdl); fnvlist_free(oprops); return (ret); } @@ -3615,6 +3675,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, boolean_t toplevel = B_FALSE; boolean_t zoned = B_FALSE; boolean_t hastoken = B_FALSE; + uint8_t *wkeydata = NULL; + uint_t wkeylen = 0; begin_time = time(NULL); bzero(origin, MAXNAMELEN); @@ -4001,9 +4063,13 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, * locally set on the send side would not be received correctly. * We can infer encryption=off if the stream is not raw and * properties were included since the send side will only ever - * send the encryption property in a raw nvlist header. + * send the encryption property in a raw nvlist header. This + * check will be avoided if the user specifically overrides + * the encryption property on the command line. */ - if (!raw && rcvprops != NULL) { + if (!raw && rcvprops != NULL && + !nvlist_exists(cmdprops, + zfs_prop_to_name(ZFS_PROP_ENCRYPTION))) { uint64_t crypt; zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET); @@ -4053,13 +4119,15 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, err = zfs_error(hdl, EZFS_BADSTREAM, errbuf); goto out; } - if ((err = zfs_setup_cmdline_props(hdl, type, zoned, recursive, - toplevel, rcvprops, cmdprops, origprops, &oxprops, errbuf)) != 0) + if ((err = zfs_setup_cmdline_props(hdl, type, name, zoned, recursive, + stream_wantsnewfs, raw, toplevel, rcvprops, cmdprops, origprops, + &oxprops, &wkeydata, &wkeylen, errbuf)) != 0) goto out; - err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops, oxprops, - origin, flags->force, flags->resumable, raw, infd, drr_noswap, - cleanup_fd, &read_bytes, &errflags, action_handlep, &prop_errors); + err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops, + oxprops, wkeydata, wkeylen, origin, flags->force, flags->resumable, + raw, infd, drr_noswap, cleanup_fd, &read_bytes, &errflags, + action_handlep, &prop_errors); ioctl_errno = ioctl_err; prop_errflags = errflags; @@ -4346,6 +4414,11 @@ zfs_receive_checkprops(libzfs_handle_t *hdl, nvlist_t *props, if (prop == ZFS_PROP_ORIGIN) continue; + /* encryption params have their own verification later */ + if (prop == ZFS_PROP_ENCRYPTION || + zfs_prop_encryption_key_param(prop)) + continue; + /* * cannot override readonly, set-once and other specific * settable properties |