From ea448c0411074c4e6cc425f635e7fa4ae4970b0f Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 14 Feb 2025 17:07:14 +0100 Subject: [PATCH 1/5] vfs: Add VFS_OPEN_HOW_RESOLVE_NO_XDEV flag It disallows traversal of mount points during path resolution, including bind mounts. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805 Signed-off-by: Samuel Cabrero --- source3/include/vfs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 581148fa053..97999d0d539 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -905,6 +905,7 @@ struct vfs_aio_state { #define VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS 1 #define VFS_OPEN_HOW_WITH_BACKUP_INTENT 2 +#define VFS_OPEN_HOW_RESOLVE_NO_XDEV 4 struct vfs_open_how { int flags; -- 2.48.1 From 28a1aa9d2eabccb75394e04466c3aeed3b0dd52a Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 14 Feb 2025 17:08:12 +0100 Subject: [PATCH 2/5] vfs: Allow RESOLVE_NO_XDEV in vfs openat functions Allow this flag in the resolve field of the open_how structure. Signed-off-by: Samuel Cabrero --- source3/modules/vfs_aio_pthread.c | 3 ++- source3/modules/vfs_ceph.c | 3 ++- source3/modules/vfs_ceph_new.c | 3 ++- source3/modules/vfs_default.c | 3 ++- source3/modules/vfs_fruit.c | 3 ++- source3/modules/vfs_glusterfs.c | 3 ++- source3/modules/vfs_shadow_copy2.c | 3 ++- source3/modules/vfs_streams_depot.c | 3 ++- source3/modules/vfs_streams_xattr.c | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/source3/modules/vfs_aio_pthread.c b/source3/modules/vfs_aio_pthread.c index bd0c94b8cce..b10db8655a3 100644 --- a/source3/modules/vfs_aio_pthread.c +++ b/source3/modules/vfs_aio_pthread.c @@ -457,7 +457,8 @@ static int aio_pthread_openat_fn(vfs_handle_struct *handle, bool aio_allow_open = lp_parm_bool( SNUM(handle->conn), "aio_pthread", "aio open", false); - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; return -1; } diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c index 3913cb01b2c..943805a6022 100644 --- a/source3/modules/vfs_ceph.c +++ b/source3/modules/vfs_ceph.c @@ -472,7 +472,8 @@ static int cephwrap_openat(struct vfs_handle_struct *handle, int result = -ENOENT; int dirfd = -1; - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; return -1; } diff --git a/source3/modules/vfs_ceph_new.c b/source3/modules/vfs_ceph_new.c index 8d1f6f7fbec..58907925886 100644 --- a/source3/modules/vfs_ceph_new.c +++ b/source3/modules/vfs_ceph_new.c @@ -2144,7 +2144,8 @@ static int vfs_ceph_openat(struct vfs_handle_struct *handle, int result = -ENOENT; START_PROFILE(syscall_openat); - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { result = -ENOSYS; goto err_out; } diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 5d16cbb5bf3..92f36a7681b 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -619,7 +619,8 @@ static int vfswrap_openat(vfs_handle_struct *handle, SMB_ASSERT((dirfd != -1) || (smb_fname->base_name[0] == '/')); if (how->resolve & ~(VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS | - VFS_OPEN_HOW_WITH_BACKUP_INTENT)) { + VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; result = -1; goto out; diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 70829def8d6..83e24ef6e27 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -1742,7 +1742,8 @@ static int fruit_openat(vfs_handle_struct *handle, how); } - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; return -1; } diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c index 63dc7a30b04..16f7ccbf58a 100644 --- a/source3/modules/vfs_glusterfs.c +++ b/source3/modules/vfs_glusterfs.c @@ -731,7 +731,8 @@ static int vfs_gluster_openat(struct vfs_handle_struct *handle, START_PROFILE(syscall_openat); - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { END_PROFILE(syscall_openat); errno = ENOSYS; return -1; diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c index 42626653e0f..458cce4c6b8 100644 --- a/source3/modules/vfs_shadow_copy2.c +++ b/source3/modules/vfs_shadow_copy2.c @@ -1573,7 +1573,8 @@ static int shadow_copy2_openat(vfs_handle_struct *handle, int ret; bool ok; - if ((how.resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how.resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; return -1; } diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c index 322e04268b8..ae5df7ec39a 100644 --- a/source3/modules/vfs_streams_depot.c +++ b/source3/modules/vfs_streams_depot.c @@ -753,7 +753,8 @@ static int streams_depot_openat(struct vfs_handle_struct *handle, how); } - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; return -1; } diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index ac01cc46043..d79906ee50b 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -416,7 +416,8 @@ static int streams_xattr_openat(struct vfs_handle_struct *handle, how); } - if ((how->resolve & ~VFS_OPEN_HOW_WITH_BACKUP_INTENT) != 0) { + if (how->resolve & ~(VFS_OPEN_HOW_WITH_BACKUP_INTENT | + VFS_OPEN_HOW_RESOLVE_NO_XDEV)) { errno = ENOSYS; return -1; } -- 2.48.1 From 73df8c9b8daa771635440d057de217e4749c8bf5 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 14 Feb 2025 17:13:39 +0100 Subject: [PATCH 3/5] vfs: Use RESOLVE_NO_XDEV by default on all shares Enable the flag by default on all shares, it will be automatically disabled if the system does not support openat2(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805 Signed-off-by: Samuel Cabrero --- script/autobuild.py | 2 +- selftest/target/Samba3.pm | 2 ++ source3/modules/vfs_default.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/script/autobuild.py b/script/autobuild.py index 137a6eb0999..5a7dad4b8fa 100755 --- a/script/autobuild.py +++ b/script/autobuild.py @@ -322,7 +322,7 @@ tasks = { "samba-no-opath-build": { "git-clone-required": True, "sequence": [ - ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=1 -DDISABLE_PROC_FDS=1' ./configure.developer --without-ad-dc " + samba_configure_params), + ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_XDEV=1 -DDISABLE_PROC_FDS=1' ./configure.developer --without-ad-dc " + samba_configure_params), ("make", "make -j"), ("check-clean-tree", CLEAN_SOURCE_TREE_CMD), ("chmod-R-a-w", "chmod -R a-w ."), diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 8906608bc1f..c34619ac279 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -304,6 +304,7 @@ sub setup_nt4_dc server schannel require seal:torturetest\$ = no vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS = no + vfs_default:VFS_OPEN_HOW_RESOLVE_NO_XDEV = no fss: sequence timeout = 1 check parent directory delete on close = yes @@ -2113,6 +2114,7 @@ sub setup_fileserver virusfilter:infected file action = rename virusfilter:scan on close = yes vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS = no + vfs_default:VFS_OPEN_HOW_RESOLVE_NO_XDEV = no [volumeserialnumber] path = $volume_serial_number_sharedir diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 92f36a7681b..28597ad9c16 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -76,6 +76,17 @@ static int vfswrap_connect(vfs_handle_struct *handle, const char *service, const #ifdef DISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS handle->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS; #endif + bval = lp_parm_bool(SNUM(handle->conn), + "vfs_default", + "VFS_OPEN_HOW_RESOLVE_NO_XDEV", + true); + if (bval) { + handle->conn->open_how_resolve |= + VFS_OPEN_HOW_RESOLVE_NO_XDEV; + } +#ifdef DISABLE_VFS_OPEN_HOW_RESOLVE_NO_XDEV + handle->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV; +#endif return 0; /* Return >= 0 for success */ } -- 2.48.1 From de1f1b3735f579c597dd9656620f9a67a5ff10a4 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 14 Feb 2025 17:14:59 +0100 Subject: [PATCH 4/5] vfs: Pass the RESOLVE_NO_XDEV from upper layers to openat2() syscall BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805 Signed-off-by: Samuel Cabrero --- source3/modules/vfs_default.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 28597ad9c16..1df814a7ab7 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -664,12 +664,19 @@ static int vfswrap_openat(vfs_handle_struct *handle, } #endif - if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS) { + if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS || + how->resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) { struct open_how linux_how = { .flags = flags, .mode = mode, - .resolve = RESOLVE_NO_SYMLINKS, + .resolve = 0, }; + if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS) { + linux_how.resolve |= RESOLVE_NO_SYMLINKS; + } + if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) { + linux_how.resolve |= RESOLVE_NO_XDEV; + } result = openat2(dirfd, smb_fname->base_name, @@ -682,10 +689,13 @@ static int vfswrap_openat(vfs_handle_struct *handle, * openat2(), so indicate to * the callers that * VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS + * or VFS_OPEN_HOW_RESOLVE_NO_XDEV * would just be a waste of time. */ fsp->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS; + fsp->conn->open_how_resolve &= + ~VFS_OPEN_HOW_RESOLVE_NO_XDEV; } goto out; } -- 2.48.1 From 96781482160852e9ac002b59a9e8dbbe3a207dbf Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 14 Feb 2025 17:16:33 +0100 Subject: [PATCH 5/5] smbd: Fix crossing direct automounter mount points The workaround implemented in commit ac7a16f9cc4bd97ef546d1b7b02605991000d0f9 to trigger automounts does not work for direct automounts (either with systemd-automount or autofs daemon). In direct automounts the mount point is a real directory instead of a "ghost" directory so when turning the O_PATH handle into a real one through /proc/self/fd/ openat() does not return ENOENT, it returs a fd referring to the mount point without triggering the mount. To trigger the mount first we have to know when we are crossing mount points by using the RESOLVE_NO_XDEV flag in open_how.resolve, then we can check with fstatfs() the .f_type and fallback to a path-based open for automounts or retry without RESOLVE_NO_XDEV otherwise. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15805 Signed-off-by: Samuel Cabrero --- source3/smbd/open.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 3982c39ed47..02ec16a1125 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -874,6 +874,7 @@ NTSTATUS reopen_from_fsp(struct files_struct *dirfsp, }; mode_t mode = fsp->fsp_name->st.st_ex_mode; int new_fd; + struct vfs_open_how pathref_how = *how; if (S_ISLNK(mode)) { return NT_STATUS_STOPPED_ON_SYMLINK; @@ -884,15 +885,38 @@ NTSTATUS reopen_from_fsp(struct files_struct *dirfsp, fsp->fsp_flags.is_pathref = false; +#if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H) + /* There is no point in setting RESOLVE_NO_XDEV if we can't + * check with fstatfs later + */ + if (S_ISDIR(fsp->fsp_name->st.st_ex_mode) && + fsp->conn->open_how_resolve & VFS_OPEN_HOW_RESOLVE_NO_XDEV) { + /* + * If the *at cwd_fsp is a pathref (opened with O_PATH) + * and old_fd refers to an automounter mount point not + * yet mounted, we will get a fd referring to the + * mount point without actually triggering the mount + * (man 2 openat). To detect this situation set the + * RESOLVE_NO_XDEV flag so openat2 will return an + * error when crossing mount points. Then check + * with fstatfs if it is an autofs mount point or not, + * falling back to name-based openat or retry without + * RESOLVE_NO_XDEV otherwise (could be a bind mount, + * other type of mount of an automounter mount point + * already mounted). + */ + pathref_how.resolve |= VFS_OPEN_HOW_RESOLVE_NO_XDEV; + } +again: +#endif new_fd = SMB_VFS_OPENAT(fsp->conn, fsp->conn->cwd_fsp, &proc_fname, fsp, - how); + &pathref_how); if (new_fd == -1) { #if defined(HAVE_FSTATFS) && defined(HAVE_LINUX_MAGIC_H) - if (S_ISDIR(fsp->fsp_name->st.st_ex_mode) && - (errno == ENOENT)) { + if (errno == EXDEV) { struct statfs sbuf = {}; int ret = fstatfs(old_fd, &sbuf); if (ret == -1) { @@ -901,14 +925,16 @@ NTSTATUS reopen_from_fsp(struct files_struct *dirfsp, } else if (sbuf.f_type == AUTOFS_SUPER_MAGIC) { /* * When reopening an as-yet - * unmounted autofs mount - * point we get ENOENT. We + * unmounted autofs mount we * have to retry pathbased. */ goto namebased_open; + } else { + pathref_how.resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_XDEV; + goto again; } - /* restore ENOENT if changed in the meantime */ - errno = ENOENT; + /* restore EXDEV if changed in the meantime */ + errno = EXDEV; } #endif status = map_nt_error_from_unix(errno); -- 2.48.1