Add receive:append permission for limited 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 adds the receive:append permission, which only permits
limited, non-forced receive. Behavior for users with full receive
permission is not changed in any way.

Fixes #16943
Reviewed-by: Ameer Hamza <ahamza@ixsystems.com>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Gionatan Danti <g.danti@assyoma.it>
Closes #17015
This commit is contained in:
shodanshok 2025-03-13 18:54:14 +01:00 committed by GitHub
parent 1b495eeab3
commit 201d262949
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 62 additions and 4 deletions

View File

@ -5292,6 +5292,7 @@ zfs_do_receive(int argc, char **argv)
#define ZFS_DELEG_PERM_SHARE "share" #define ZFS_DELEG_PERM_SHARE "share"
#define ZFS_DELEG_PERM_SEND "send" #define ZFS_DELEG_PERM_SEND "send"
#define ZFS_DELEG_PERM_RECEIVE "receive" #define ZFS_DELEG_PERM_RECEIVE "receive"
#define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append"
#define ZFS_DELEG_PERM_ALLOW "allow" #define ZFS_DELEG_PERM_ALLOW "allow"
#define ZFS_DELEG_PERM_USERPROP "userprop" #define ZFS_DELEG_PERM_USERPROP "userprop"
#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */ #define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */

View File

@ -46,6 +46,7 @@ extern "C" {
#define ZFS_DELEG_PERM_SHARE "share" #define ZFS_DELEG_PERM_SHARE "share"
#define ZFS_DELEG_PERM_SEND "send" #define ZFS_DELEG_PERM_SEND "send"
#define ZFS_DELEG_PERM_RECEIVE "receive" #define ZFS_DELEG_PERM_RECEIVE "receive"
#define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append"
#define ZFS_DELEG_PERM_ALLOW "allow" #define ZFS_DELEG_PERM_ALLOW "allow"
#define ZFS_DELEG_PERM_USERPROP "userprop" #define ZFS_DELEG_PERM_USERPROP "userprop"
#define ZFS_DELEG_PERM_VSCAN "vscan" #define ZFS_DELEG_PERM_VSCAN "vscan"

View File

@ -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. change-key subcommand Allows changing an encryption key via \fBzfs change-key\fR.
mount subcommand Allows mounting/umounting ZFS datasets mount subcommand Allows mounting/umounting ZFS datasets
promote subcommand Must also have the \fBmount\fR and \fBpromote\fR ability in the origin file system 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, required for \fBzfs receive -F\fR (see also \fBreceive:append\fR for limited, non forced receive)
release subcommand Allows releasing a user hold which might destroy the snapshot 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 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 rollback subcommand Must also have the \fBmount\fR ability
@ -215,6 +215,7 @@ send subcommand
share subcommand Allows sharing file systems over NFS or SMB protocols share subcommand Allows sharing file systems over NFS or SMB protocols
snapshot subcommand Must also have the \fBmount\fR ability snapshot subcommand Must also have the \fBmount\fR ability
receive:append other Must also have the \fBmount\fR and \fBcreate\fR ability, limited receive ability (can not do receive -F)
groupquota other Allows accessing any \fBgroupquota@\fI\fR property groupquota other Allows accessing any \fBgroupquota@\fI\fR property
groupobjquota other Allows accessing any \fBgroupobjquota@\fI\fR property groupobjquota other Allows accessing any \fBgroupobjquota@\fI\fR property
groupused other Allows reading any \fBgroupused@\fI\fR property groupused other Allows reading any \fBgroupused@\fI\fR property

View File

@ -52,6 +52,7 @@ const zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
{ZFS_DELEG_PERM_MOUNT}, {ZFS_DELEG_PERM_MOUNT},
{ZFS_DELEG_PERM_PROMOTE}, {ZFS_DELEG_PERM_PROMOTE},
{ZFS_DELEG_PERM_RECEIVE}, {ZFS_DELEG_PERM_RECEIVE},
{ZFS_DELEG_PERM_RECEIVE_APPEND},
{ZFS_DELEG_PERM_RENAME}, {ZFS_DELEG_PERM_RENAME},
{ZFS_DELEG_PERM_ROLLBACK}, {ZFS_DELEG_PERM_ROLLBACK},
{ZFS_DELEG_PERM_SNAPSHOT}, {ZFS_DELEG_PERM_SNAPSHOT},

View File

@ -900,9 +900,18 @@ zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
(void) innvl; (void) innvl;
int error; int error;
/*
* zfs receive -F requires full receive permission,
* otherwise receive:append permission is enough
*/
if ((error = zfs_secpolicy_write_perms(zc->zc_name, if ((error = zfs_secpolicy_write_perms(zc->zc_name,
ZFS_DELEG_PERM_RECEIVE, cr)) != 0) ZFS_DELEG_PERM_RECEIVE, cr)) != 0) {
return (error); if (zc->zc_guid || nvlist_exists(innvl, "force"))
return (error);
if ((error = zfs_secpolicy_write_perms(zc->zc_name,
ZFS_DELEG_PERM_RECEIVE_APPEND, cr)) != 0)
return (error);
}
if ((error = zfs_secpolicy_write_perms(zc->zc_name, if ((error = zfs_secpolicy_write_perms(zc->zc_name,
ZFS_DELEG_PERM_MOUNT, cr)) != 0) ZFS_DELEG_PERM_MOUNT, cr)) != 0)

View File

@ -256,6 +256,9 @@ function check_fs_perm
receive) receive)
verify_fs_receive $user $perm $fs verify_fs_receive $user $perm $fs
;; ;;
receive:append)
verify_fs_receive_append $user $perm $fs
;;
*) *)
common_perm $user $perm $fs common_perm $user $perm $fs
;; ;;
@ -425,6 +428,45 @@ function verify_fs_receive
return 0 return 0
} }
function verify_fs_receive_append
{
typeset user=$1
typeset perm=$2
typeset fs=$3
typeset dtst
typeset stamp=${perm}.${user}.$RANDOM
typeset newfs=$fs/newfs.$stamp
typeset bak_user=$TEST_BASE_DIR/bak.$user.$stamp
log_must zfs create $newfs
typeset dtst="$newfs"
typeset dtstsnap=$dtst@snap.$stamp
log_must zfs snapshot $dtstsnap
log_must eval "zfs send $dtstsnap > $bak_user"
log_must_busy zfs destroy -rf $dtst
log_must zfs allow $user create,mount,canmount $fs
user_run $user eval "zfs receive -o canmount=off -F $dtst < $bak_user"
log_must zfs unallow $user create,mount,canmount $fs
if datasetexists $dtstsnap ; then
return 1
fi
log_must zfs allow $user create,mount,canmount $fs
user_run $user eval "zfs receive -o canmount=off $dtst < $bak_user"
log_must zfs unallow $user create,mount,canmount $fs
if ! datasetexists $dtstsnap ; then
return 1
fi
rm -rf $bak_user
return 0
}
function verify_userprop function verify_userprop
{ {
typeset user=$1 typeset user=$1

View File

@ -86,7 +86,8 @@ set -A perms create true false \
clone true true \ clone true true \
promote true true \ promote true true \
xattr true false \ xattr true false \
receive true false receive true false \
receive:append true false
elif is_freebsd; then elif is_freebsd; then
# Results in Results in # Results in Results in
@ -126,6 +127,7 @@ set -A perms create true false \
rename true true \ rename true true \
promote true true \ promote true true \
receive true false \ receive true false \
receive:append true false \
destroy true true destroy true true
else else
@ -160,6 +162,7 @@ set -A perms create true false \
zoned true false \ zoned true false \
xattr true false \ xattr true false \
receive true false \ receive true false \
receive:append true false \
destroy true true destroy true true
if is_global_zone; then if is_global_zone; then