From c3a7dba8533550b20d755e5f663b2997de12fca9 Mon Sep 17 00:00:00 2001 From: Gionatan Danti Date: Sun, 26 Jan 2025 21:16:33 +0100 Subject: [PATCH] require rollback permission when force receive Force receive (zfs receive -F) can rollback or destroy snapshots and file systems that do not exist on the sending side (see zfs-receive man page). This means an user having the receive permission can effectively delete data on receiving side, even if such user does not have explicit rollback or destroy permissions. This patch add the rollback permission requirement for force receive. To avoid changing current default behavior, a new zfs_recv_perm tunable is introduced. When set to 0 (default) the new permission check is disabled. When set to 1 rollback permission requirement is enabled. Fixes https://github.com/openzfs/zfs/issues/16943 Signed-off-by: Gionatan Danti --- man/man4/zfs.4 | 3 +++ man/man8/zfs-allow.8 | 2 +- module/zfs/zfs_ioctl.c | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/man/man4/zfs.4 b/man/man4/zfs.4 index dd0b3d848fe9..d84f2e9685c1 100644 --- a/man/man4/zfs.4 +++ b/man/man4/zfs.4 @@ -2188,6 +2188,9 @@ If there is an error during healing, the healing receive is not terminated instead it moves on to the next record. .El . +.It Sy zfs_recv_perm Ns = Ns Sy 0 Pq int +When not zero, force receive (zfs recv -F) requires rollback permission. +. .It Sy zfs_override_estimate_recordsize Ns = Ns Sy 0 Ns | Ns 1 Pq uint Setting this variable overrides the default logic for estimating block sizes when doing a diff --git a/man/man8/zfs-allow.8 b/man/man8/zfs-allow.8 index d26984317c2e..dcf6998e40f5 100644 --- a/man/man8/zfs-allow.8 +++ b/man/man8/zfs-allow.8 @@ -207,7 +207,7 @@ load-key subcommand Allows loading and unloading of encryption key (see \fBzfs l change-key subcommand Allows changing an encryption key via \fBzfs change-key\fR. mount subcommand Allows mounting/umounting ZFS datasets promote subcommand Must also have the \fBmount\fR and \fBpromote\fR ability in the origin file system -receive subcommand Must also have the \fBmount\fR and \fBcreate\fR ability +receive subcommand Must also have the \fBmount\fR and \fBcreate\fR ability; must also have the \fBrollback\fR ability if \fBzfs_recv_perm=1\fR and \fBzfs receive -F\fR (force receive) is used. release subcommand Allows releasing a user hold which might destroy the snapshot rename subcommand Must also have the \fBmount\fR and \fBcreate\fR ability in the new parent rollback subcommand Must also have the \fBmount\fR ability diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index b1b0ae54460b..f18e27b0bf69 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -238,6 +238,11 @@ uint64_t zfs_max_nvlist_src_size = 0; */ static uint64_t zfs_history_output_max = 1024 * 1024; +/* + * zfs_recv_perm: if true, force receive (-F) requires rollback permission + */ +static int zfs_recv_perm = 0; + uint_t zfs_allow_log_key; /* DATA_TYPE_ANY is used when zkey_type can vary. */ @@ -908,6 +913,12 @@ zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) ZFS_DELEG_PERM_MOUNT, cr)) != 0) return (error); + /* Forced receive can rollback or destroy snapshots */ + if (zfs_recv_perm && zc->zc_guid && + (error = zfs_secpolicy_write_perms(zc->zc_name, + ZFS_DELEG_PERM_ROLLBACK, cr)) != 0) + return (error); + return (zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_CREATE, cr)); } @@ -8177,3 +8188,6 @@ ZFS_MODULE_PARAM(zfs, zfs_, max_nvlist_src_size, U64, ZMOD_RW, ZFS_MODULE_PARAM(zfs, zfs_, history_output_max, U64, ZMOD_RW, "Maximum size in bytes of ZFS ioctl output that will be logged"); + +ZFS_MODULE_PARAM(zfs, zfs_, recv_perm, INT, ZMOD_RW, + "Force receive (-F) requires rollback permission");