mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 02:44:41 +03:00
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 <lundman@lundman.net> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Richard Elling <Richard.Elling@RichardElling.com> Signed-off-by: Tom Caputi <tcaputi@datto.com> Closes #7650
This commit is contained in:
committed by
Brian Behlendorf
parent
fe8a7982ca
commit
d9c460a0b6
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -662,8 +662,9 @@ recv_read(int fd, void *buf, int ilen)
|
||||
*/
|
||||
static int
|
||||
recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
const char *origin, boolean_t force, boolean_t resumable, boolean_t raw,
|
||||
int input_fd, const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
uint8_t *wkeydata, uint_t wkeylen, const char *origin, boolean_t force,
|
||||
boolean_t resumable, boolean_t raw, int input_fd,
|
||||
const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
|
||||
nvlist_t **errors)
|
||||
{
|
||||
@@ -703,7 +704,11 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
drr = *begin_record;
|
||||
}
|
||||
|
||||
if (resumable || raw) {
|
||||
/*
|
||||
* Raw receives, resumable receives, and receives that include a
|
||||
* wrapping key all use the new interface.
|
||||
*/
|
||||
if (resumable || raw || wkeydata != NULL) {
|
||||
nvlist_t *outnvl = NULL;
|
||||
nvlist_t *innvl = fnvlist_alloc();
|
||||
|
||||
@@ -715,6 +720,20 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
if (localprops != NULL)
|
||||
fnvlist_add_nvlist(innvl, "localprops", localprops);
|
||||
|
||||
if (wkeydata != NULL) {
|
||||
/*
|
||||
* wkeydata must be placed in the special
|
||||
* ZPOOL_HIDDEN_ARGS nvlist so that it
|
||||
* will not be printed to the zpool history.
|
||||
*/
|
||||
nvlist_t *hidden_args = fnvlist_alloc();
|
||||
fnvlist_add_uint8_array(hidden_args, "wkeydata",
|
||||
wkeydata, wkeylen);
|
||||
fnvlist_add_nvlist(innvl, ZPOOL_HIDDEN_ARGS,
|
||||
hidden_args);
|
||||
nvlist_free(hidden_args);
|
||||
}
|
||||
|
||||
if (origin != NULL && strlen(origin))
|
||||
fnvlist_add_string(innvl, "origin", origin);
|
||||
|
||||
@@ -846,8 +865,8 @@ int
|
||||
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, boolean_t raw, int fd)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_FALSE, raw,
|
||||
fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
B_FALSE, raw, fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -860,8 +879,8 @@ int
|
||||
lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, boolean_t raw, int fd)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_TRUE, raw,
|
||||
fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
B_TRUE, raw, fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -883,8 +902,8 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
|
||||
if (begin_record == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
return (recv_impl(snapname, props, NULL, origin, force, resumable, raw,
|
||||
fd, begin_record, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
resumable, raw, fd, begin_record, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -913,9 +932,9 @@ int lzc_receive_one(const char *snapname, nvlist_t *props,
|
||||
uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
|
||||
nvlist_t **errors)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, resumable,
|
||||
raw, input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
action_handle, errors));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
resumable, raw, input_fd, begin_record, cleanup_fd, read_bytes,
|
||||
errflags, action_handle, errors));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -927,15 +946,15 @@ int lzc_receive_one(const char *snapname, nvlist_t *props,
|
||||
* this nvlist
|
||||
*/
|
||||
int lzc_receive_with_cmdprops(const char *snapname, nvlist_t *props,
|
||||
nvlist_t *cmdprops, const char *origin, boolean_t force,
|
||||
boolean_t resumable, boolean_t raw, int input_fd,
|
||||
nvlist_t *cmdprops, uint8_t *wkeydata, uint_t wkeylen, const char *origin,
|
||||
boolean_t force, boolean_t resumable, boolean_t raw, int input_fd,
|
||||
const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
|
||||
nvlist_t **errors)
|
||||
{
|
||||
return (recv_impl(snapname, props, cmdprops, origin, force, resumable,
|
||||
raw, input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
action_handle, errors));
|
||||
return (recv_impl(snapname, props, cmdprops, wkeydata, wkeylen, origin,
|
||||
force, resumable, raw, input_fd, begin_record, cleanup_fd,
|
||||
read_bytes, errflags, action_handle, errors));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user