summaryrefslogtreecommitdiffstats
path: root/lib/libzfs
diff options
context:
space:
mode:
authorTom Caputi <[email protected]>2017-10-13 13:09:04 -0400
committerBrian Behlendorf <[email protected]>2018-08-15 09:48:49 -0700
commitd9c460a0b659c044d4397b7405712f2c9450d3c4 (patch)
tree973b92b7f835540f5ee722b56ff838828fd53116 /lib/libzfs
parentfe8a7982ca90c3c9b8a09ec33f032527d7034a7b (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.c14
-rw-r--r--lib/libzfs/libzfs_dataset.c4
-rw-r--r--lib/libzfs/libzfs_pool.c2
-rw-r--r--lib/libzfs/libzfs_sendrecv.c95
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