Skip to content

Commit

Permalink
Implement -f option to force inherit key with zfs change-key -if
Browse files Browse the repository at this point in the history
This implements functionality already available in pyzfs
i.e. libzfs_core.lzc_change_key(b"tank/enc/data", "force_inherit")
Unlike zfs change-key -i, the key is not required to be loaded.
Work-around to #15687.

Signed-off-by: andy <[email protected]>
  • Loading branch information
digitalsignalperson committed Jan 25, 2024
1 parent a4bf6ba commit 6fd0756
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 12 deletions.
17 changes: 13 additions & 4 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"
"\t [-o keylocation=<value>] [-o pbkdf2iters=<value>]\n"
"\t <filesystem|volume>\n"
"\tchange-key -i [-l] <filesystem|volume>\n"));
"\tchange-key -i [-lf] <filesystem|volume>\n"));
case HELP_VERSION:
return (gettext("\tversion\n"));
case HELP_REDACT:
Expand Down Expand Up @@ -8373,18 +8373,21 @@ zfs_do_change_key(int argc, char **argv)
{
int c, ret;
uint64_t keystatus;
boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;
boolean_t loadkey = B_FALSE, inheritkey = B_FALSE, force = B_FALSE;
zfs_handle_t *zhp = NULL;
nvlist_t *props = fnvlist_alloc();

while ((c = getopt(argc, argv, "lio:")) != -1) {
while ((c = getopt(argc, argv, "lifo:")) != -1) {
switch (c) {
case 'l':
loadkey = B_TRUE;
break;
case 'i':
inheritkey = B_TRUE;
break;
case 'f':
force = B_TRUE;
break;
case 'o':
if (!parseprop(props, optarg)) {
nvlist_free(props);
Expand All @@ -8404,6 +8407,12 @@ zfs_do_change_key(int argc, char **argv)
usage(B_FALSE);
}

if (force && !inheritkey) {
(void) fprintf(stderr,
gettext("Force option only applies to inherit\n"));
usage(B_FALSE);
}

argc -= optind;
argv += optind;

Expand Down Expand Up @@ -8437,7 +8446,7 @@ zfs_do_change_key(int argc, char **argv)
zfs_refresh_properties(zhp);
}

ret = zfs_crypto_rewrap(zhp, props, inheritkey);
ret = zfs_crypto_rewrap(zhp, props, inheritkey, force);
if (ret != 0) {
nvlist_free(props);
zfs_close(zhp);
Expand Down
3 changes: 2 additions & 1 deletion include/libzfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,8 @@ _LIBZFS_H int zfs_crypto_clone_check(libzfs_handle_t *, zfs_handle_t *, char *,
_LIBZFS_H int zfs_crypto_attempt_load_keys(libzfs_handle_t *, const char *);
_LIBZFS_H int zfs_crypto_load_key(zfs_handle_t *, boolean_t, const char *);
_LIBZFS_H int zfs_crypto_unload_key(zfs_handle_t *);
_LIBZFS_H int zfs_crypto_rewrap(zfs_handle_t *, nvlist_t *, boolean_t);
_LIBZFS_H int zfs_crypto_rewrap(zfs_handle_t *, nvlist_t *, boolean_t,
boolean_t);

typedef struct zprop_list {
int pl_prop;
Expand Down
10 changes: 7 additions & 3 deletions lib/libzfs/libzfs_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -1583,7 +1583,8 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props,
}

int
zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey,
boolean_t forceinherit)
{
int ret;
char errbuf[ERRBUFLEN];
Expand All @@ -1600,6 +1601,9 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
char prop_keylocation[MAXNAMELEN];
char parent_name[ZFS_MAX_DATASET_NAME_LEN];

if (inheritkey && forceinherit)
cmd = DCP_CMD_FORCE_INHERIT;

(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "Key change error"));

Expand Down Expand Up @@ -1759,7 +1763,7 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)

/* check that the parent's key is loaded */
pkeystatus = zfs_prop_get_int(pzhp, ZFS_PROP_KEYSTATUS);
if (pkeystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
if (!forceinherit && pkeystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
zfs_error_aux(pzhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"Parent key must be loaded."));
ret = EACCES;
Expand All @@ -1769,7 +1773,7 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)

/* check that the key is loaded */
keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
if (!forceinherit && keystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"Key must be loaded."));
ret = EACCES;
Expand Down
10 changes: 8 additions & 2 deletions man/man8/zfs-load-key.8
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
.Nm zfs
.Cm change-key
.Fl i
.Op Fl l
.Op Fl lf
.Ar filesystem
.
.Sh DESCRIPTION
Expand Down Expand Up @@ -159,7 +159,7 @@ Unloads the keys for all encryption roots in all imported pools.
.Nm zfs
.Cm change-key
.Fl i
.Op Fl l
.Op Fl lf
.Ar filesystem
.Xc
Changes the user's key (e.g. a passphrase) used to access a dataset.
Expand Down Expand Up @@ -217,6 +217,12 @@ Indicates that zfs should make
inherit the key of its parent.
Note that this command can only be run on an encryption root
that has an encrypted parent.
.It Fl f
When used with
.Fl i
the key will be force-inherited.
This should only be used when it is known that the parent is a replica of the
original encryptionroot and has the same key.
.El
.El
.Ss Encryption
Expand Down
3 changes: 2 additions & 1 deletion tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ tags = ['functional', 'cli_root', 'zfs_bookmark']
[tests/functional/cli_root/zfs_change-key]
tests = ['zfs_change-key', 'zfs_change-key_child', 'zfs_change-key_format',
'zfs_change-key_inherit', 'zfs_change-key_load', 'zfs_change-key_location',
'zfs_change-key_pbkdf2iters', 'zfs_change-key_clones']
'zfs_change-key_pbkdf2iters', 'zfs_change-key_clones',
'zfs_change-key_inherit-force']
tags = ['functional', 'cli_root', 'zfs_change-key']

[tests/functional/cli_root/zfs_clone]
Expand Down
3 changes: 2 additions & 1 deletion tests/runfiles/sanity.run
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ tags = ['functional', 'cli_root', 'zfs_bookmark']
[tests/functional/cli_root/zfs_change-key]
tests = ['zfs_change-key', 'zfs_change-key_child', 'zfs_change-key_format',
'zfs_change-key_inherit', 'zfs_change-key_load', 'zfs_change-key_location',
'zfs_change-key_pbkdf2iters', 'zfs_change-key_clones']
'zfs_change-key_pbkdf2iters', 'zfs_change-key_clones',
'zfs_change-key_inherit-force']
tags = ['functional', 'cli_root', 'zfs_change-key']

[tests/functional/cli_root/zfs_clone]
Expand Down
1 change: 1 addition & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zfs_change-key/zfs_change-key_child.ksh \
functional/cli_root/zfs_change-key/zfs_change-key_clones.ksh \
functional/cli_root/zfs_change-key/zfs_change-key_format.ksh \
functional/cli_root/zfs_change-key/zfs_change-key_inherit-force.ksh \
functional/cli_root/zfs_change-key/zfs_change-key_inherit.ksh \
functional/cli_root/zfs_change-key/zfs_change-key.ksh \
functional/cli_root/zfs_change-key/zfs_change-key_load.ksh \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# 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.
#
# 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) 2017 Datto, Inc. All rights reserved.
#

. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zfs_load-key/zfs_load-key_common.kshlib

#
# DESCRIPTION:
# 'zfs change-key -if' should cause a dataset to inherit its parent key
# without the key being loaded
#
# STRATEGY:
# 1. Create a parent encrypted dataset
# 2. Create a child dataset
# 3. Create a copy of the parent dataset
# 4. Send a copy of the child to the copy of the parent
# 5. Attempt to force inherit the parent key without the keys being loaded
# 6. Verify the key is inherited
# 7. Load the parent key
# 8. Verify the key is available for parent and child
# 9. Attempt to mount the datasets
#

verify_runnable "both"

function cleanup
{
datasetexists $TESTPOOL/$TESTFS1 && \
destroy_dataset $TESTPOOL/$TESTFS1 -r

datasetexists $TESTPOOL/$TESTFS2 && \
destroy_dataset $TESTPOOL/$TESTFS2 -r
}

log_onexit cleanup

log_assert "'zfs change-key -if' should cause a dataset to inherit its" \
"parent key"

log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
"-o keyformat=passphrase -o keylocation=prompt $TESTPOOL/$TESTFS1"
log_must eval "echo $PASSPHRASE1 | zfs create $TESTPOOL/$TESTFS1/child"
log_must verify_encryption_root $TESTPOOL/$TESTFS1/child "$TESTPOOL/$TESTFS1"

log_must zfs snapshot -r $TESTPOOL/$TESTFS1@snap

log_must eval "zfs send -w $TESTPOOL/$TESTFS1@snap | zfs receive $TESTPOOL/$TESTFS2"
log_must verify_encryption_root $TESTPOOL/$TESTFS2 $TESTPOOL/$TESTFS2
log_must key_unavailable $TESTPOOL/$TESTFS2

log_must eval "zfs send -w $TESTPOOL/$TESTFS1/child@snap | zfs receive $TESTPOOL/$TESTFS2/child"
log_must verify_encryption_root $TESTPOOL/$TESTFS2/child $TESTPOOL/$TESTFS2/child
log_must key_unavailable $TESTPOOL/$TESTFS2/child

log_must_not zfs change-key -i $TESTPOOL/$TESTFS2/child
log_must_not zfs change-key -f $TESTPOOL/$TESTFS2/child
log_must zfs change-key -if $TESTPOOL/$TESTFS2/child
log_must verify_encryption_root $TESTPOOL/$TESTFS2/child "$TESTPOOL/$TESTFS2"

log_must key_unavailable $TESTPOOL/$TESTFS2
log_must key_unavailable $TESTPOOL/$TESTFS2/child

log_must eval "echo $PASSPHRASE | zfs load-key $TESTPOOL/$TESTFS2"

log_must key_available $TESTPOOL/$TESTFS2
log_must key_available $TESTPOOL/$TESTFS2/child

log_must zfs mount $TESTPOOL/$TESTFS2
log_must zfs mount $TESTPOOL/$TESTFS2/child

log_pass "'zfs change-key -if' causes a dataset to inherit its parent key"

0 comments on commit 6fd0756

Please sign in to comment.