[SCM] Samba Shared Repository - branch master updated

Volker Lendecke vlendec at samba.org
Mon Aug 15 16:01:01 UTC 2022


The branch, master has been updated
       via  076c22fbd7e selftest/Samba3: let nt4_dc* use vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=no
       via  4708ba2f013 vfs_default: Use openat2(RESOLVE_NO_SYMLINKS) if available
       via  8544f4490a0 vfs_default: prepare O_PATH usage with openat2()
       via  d6653067b20 s3:smbd: let openat_pathref_dirfsp_nosymlink() try VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS first
       via  35b99c87ef9 s3:smbd: let openat_pathref_dirfsp_nosymlink() handle ELOOP similar to ENOTDIR
       via  17484d069b9 s3:smbd: let openat_pathref_dirfsp_nosymlink() do a verification loop against . and .. first
       via  f7dc2755832 vfs: define VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS
       via  ae1a84f7313 lib/replace: let DISABLE_OPATH also undef __NR_openat2
       via  f7618dd31a9 lib/replace: add fallback defines for __NR_openat2
       via  b89001e9226 lib/replace: use syscall(__NR_openat2) if available
       via  37ba6df174d lib/replace: always include <sys/syscall.h> in replace.c if available
       via  ce804b78164 lib/replace: add a replacement for openat2() that returns ENOSYS
       via  2369d083336 vfs_btrfs: fix include order, includes.h or replace.h should be first
       via  cea9451f780 vfs_io_uring: hide a possible definition of struct open_how in liburing/compat.h
       via  2b51bad7475 wafsamba: allow cflags for CHECK_TYPE[_IN]()
       via  085f1485753 s3:tests: add a lot more tests to test_symlink_traversal_smb2.sh
      from  a38fad29803 s3:utils: Fix NULL check

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 076c22fbd7ecbf22dbfeb1711609f07fd42f88b0
Author: Stefan Metzmacher <metze at samba.org>
Date:   Fri Aug 12 10:55:42 2022 +0200

    selftest/Samba3: let nt4_dc* use vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=no
    
    We should always test the code path without openat2 being available,
    even if the kernel supports it.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>
    
    Autobuild-User(master): Volker Lendecke <vl at samba.org>
    Autobuild-Date(master): Mon Aug 15 16:00:26 UTC 2022 on sn-devel-184

commit 4708ba2f013c5f5ea5aa5dcf4873c2b4a86fb8ff
Author: Volker Lendecke <vl at samba.org>
Date:   Fri Jun 17 17:41:52 2022 +0200

    vfs_default: Use openat2(RESOLVE_NO_SYMLINKS) if available
    
    This improves the following test:
    
     time smbtorture //127.0.0.1/m -Uroot%test \
            smb2.create.bench-path-contention-shared \
            --option='torture:bench_path=Apps\1\2\3\4\5\6\7\8\9\10' \
            --option="torture:timelimit=600" \
            --option="torture:nprocs=1"
    
    From:
    
       open[num/s=14186,avslat=0.000044,minlat=0.000042,maxlat=0.000079]
       close[num/s=14185,avslat=0.000027,minlat=0.000025,maxlat=0.000057]
    
    to:
    
       open[num/s=16917,avslat=0.000038,minlat=0.000035,maxlat=0.000340]
       close[num/s=16916,avslat=0.000020,minlat=0.000019,maxlat=0.000104]
    
    Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
    
    Signed-off-by: Volker Lendecke <vl at samba.org>
    Signed-off-by: Stefan Metzmacher <metze at samba.org>

commit 8544f4490a0b5e54b807daedddb96778744b62ee
Author: Stefan Metzmacher <metze at samba.org>
Date:   Wed Jul 27 18:43:14 2022 +0000

    vfs_default: prepare O_PATH usage with openat2()
    
    When O_PATH is specified in flags, flag bits other than O_CLOEXEC,
    O_DIRECTORY, and O_NOFOLLOW are ignored.
    
    In preparation to use openat2(), which gives an error instead of
    ignoring flags, we better remove unexpected flags, callers typically
    pass O_RDONLY and O_NONBLOCK.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit d6653067b20e61af1f05423764c8486a1a5445c8
Author: Volker Lendecke <vl at samba.org>
Date:   Thu Jul 14 19:44:04 2022 +0200

    s3:smbd: let openat_pathref_dirfsp_nosymlink() try VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS first
    
    This will reduce the amount of syscalls and the related cost drastically
    for long path names.
    
    Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
    
    Signed-off-by: Volker Lendecke <vl at samba.org>
    Signed-off-by: Stefan Metzmacher <metze at samba.org>

commit 35b99c87ef92df006f8b0a41bbea051f0faeadb9
Author: Stefan Metzmacher <metze at samba.org>
Date:   Fri Aug 12 19:12:44 2022 +0200

    s3:smbd: let openat_pathref_dirfsp_nosymlink() handle ELOOP similar to ENOTDIR
    
    This is no likely to happen as we use O_NOFOLLOW with O_DIRECTORY,
    but it's better to be prepared...
    
    This will be more important in the upcoming openat2(RESOLVE_NO_SYMLINK)
    case, but we should be consitent...
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit 17484d069b92d08b0228fb509ea42ab4c3f496a8
Author: Stefan Metzmacher <metze at samba.org>
Date:   Wed Aug 10 22:01:10 2022 +0200

    s3:smbd: let openat_pathref_dirfsp_nosymlink() do a verification loop against . and .. first
    
    I guess we should catch NT_STATUS_OBJECT_NAME_INVALID first,
    currently the check is already done in check_path_syntax*,
    but we may remove it in future.
    
    But the most important reason for this is the
    openat2(RESOLVE_NO_SYMLINK) optimization, which will
    be introduced in the following commits.
    
    Review with: git show -w
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit f7dc27558329eea7d2c4d75ee101c7f9d3a7afe3
Author: Volker Lendecke <vl at samba.org>
Date:   Fri Jun 3 16:45:41 2022 +0200

    vfs: define VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS
    
    This will allow us to make use of openat2(RESOLVE_NO_SYMLINKS) soon.
    
    The caller should check if connection_struct.open_how_resolve contains
    VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS before using it, this avoids waisting
    cpu time. But even then the caller must be prepared to handle -1/ENOSYS.
    
    Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
    
    Signed-off-by: Volker Lendecke <vl at samba.org>
    Signed-off-by: Stefan Metzmacher <metze at samba.org>

commit ae1a84f7313bdf4702492451714eacc78ee7745f
Author: Stefan Metzmacher <metze at samba.org>
Date:   Fri Aug 12 10:53:06 2022 +0200

    lib/replace: let DISABLE_OPATH also undef __NR_openat2
    
    The reason for DISABLE_OPATH is to simulate a non-linux
    system, so we should not use openat2() either.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit f7618dd31a9f8f6c0dbfdedd1a664eed25e2e449
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Aug 8 15:33:24 2022 +0200

    lib/replace: add fallback defines for __NR_openat2
    
    sys/syscall.h might be older than the runtime kernel.
    
    If the kernel has support for openat2() we should
    try to use if anyway.
    
    The callers have to deal with ENOSYS anyway,
    so there's no difference if we get that from syscall(__NR_openat2)
    or directly from rep_openat2().
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit b89001e9226ecb0f4e5c906f7195f0e53cd7d608
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Aug 8 15:25:39 2022 +0200

    lib/replace: use syscall(__NR_openat2) if available
    
    There's no glibc wrapper for openat2() yet, so we need
    to use syscall(__NR_openat2) ourself.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit 37ba6df174d73b82e951de401cba7f839ad61ab5
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Aug 8 15:24:28 2022 +0200

    lib/replace: always include <sys/syscall.h> in replace.c if available
    
    It will be used for openat2() soon.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit ce804b78164a3166a16ca3071028536761fd18d7
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Aug 8 15:23:29 2022 +0200

    lib/replace: add a replacement for openat2() that returns ENOSYS
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit 2369d0833361faf4a125431e735fce7efb6024d6
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Aug 8 15:29:28 2022 +0200

    vfs_btrfs: fix include order, includes.h or replace.h should be first
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit cea9451f780d13e528f1722a67eccbbc78b2daf9
Author: Stefan Metzmacher <metze at samba.org>
Date:   Tue Aug 9 10:29:24 2022 +0000

    vfs_io_uring: hide a possible definition of struct open_how in liburing/compat.h
    
    liburing.h will include liburing/compat.h, which either includes
    linux/openat2.h or defines struct open_how itself.
    
    This will help with the following changes, which will provide
    openat2() via libreplace's system/filesys.h, either including
    linux/openat2.h or defining open_how ourself.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit 2b51bad747551605ba3b70ac3b692107a0cd7aad
Author: Stefan Metzmacher <metze at samba.org>
Date:   Thu Aug 11 00:41:28 2022 +0200

    wafsamba: allow cflags for CHECK_TYPE[_IN]()
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

commit 085f14857531dab179af66a69962486c7dd2592c
Author: Stefan Metzmacher <metze at samba.org>
Date:   Fri Aug 12 19:07:39 2022 +0200

    s3:tests: add a lot more tests to test_symlink_traversal_smb2.sh
    
    We now also test more path components checking the difference between
    OBJECT_NAME_NOT_FOUND and OBJECT_PATH_NOT_FOUND.
    
    We also test with symlinks within the path instead of only checking
    symlinks as final path components (at least for the dirfsp part).
    
    This ensures the following commits won't introduce regressions
    when adding the openat2(RESOLVE_NO_SYMLINK) optimization.
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Volker Lendecke <vl at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 buildtools/wafsamba/samba_autoconf.py              |   7 +-
 lib/replace/replace.c                              |  52 ++++++++-
 lib/replace/system/filesys.h                       |  35 +++++++
 lib/replace/wscript                                |   1 +
 selftest/target/Samba3.pm                          |   2 +
 source3/include/vfs.h                              |   3 +
 source3/modules/vfs_btrfs.c                        |   4 +-
 source3/modules/vfs_default.c                      |  71 ++++++++++++-
 source3/modules/vfs_io_uring.c                     |  18 ++++
 .../script/tests/test_symlink_traversal_smb2.sh    | 116 +++++++++++++++++++++
 source3/smbd/files.c                               | 111 ++++++++++++++++++--
 source3/wscript                                    |  13 +++
 12 files changed, 416 insertions(+), 17 deletions(-)


Changeset truncated at 500 lines:

diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py
index 9db53e40724..3ca2f334190 100644
--- a/buildtools/wafsamba/samba_autoconf.py
+++ b/buildtools/wafsamba/samba_autoconf.py
@@ -146,7 +146,7 @@ def header_list(conf, headers=None, lib=None):
 
 
 @conf
-def CHECK_TYPE(conf, t, alternate=None, headers=None, define=None, lib=None, msg=None):
+def CHECK_TYPE(conf, t, alternate=None, headers=None, define=None, lib=None, msg=None, cflags=''):
     '''check for a single type'''
     if define is None:
         define = 'HAVE_' + t.upper().replace(' ', '_')
@@ -158,6 +158,7 @@ def CHECK_TYPE(conf, t, alternate=None, headers=None, define=None, lib=None, msg
                      headers=headers,
                      local_include=False,
                      msg=msg,
+                     cflags=cflags,
                      lib=lib,
                      link=False)
     if not ret and alternate:
@@ -177,9 +178,9 @@ def CHECK_TYPES(conf, list, headers=None, define=None, alternate=None, lib=None)
 
 
 @conf
-def CHECK_TYPE_IN(conf, t, headers=None, alternate=None, define=None):
+def CHECK_TYPE_IN(conf, t, headers=None, alternate=None, define=None, cflags=''):
     '''check for a single type with a header'''
-    return CHECK_TYPE(conf, t, headers=headers, alternate=alternate, define=define)
+    return CHECK_TYPE(conf, t, headers=headers, alternate=alternate, define=define, cflags=cflags)
 
 
 @conf
diff --git a/lib/replace/replace.c b/lib/replace/replace.c
index 0652cb4e6d6..cbf372e494f 100644
--- a/lib/replace/replace.c
+++ b/lib/replace/replace.c
@@ -33,6 +33,10 @@
 #include "system/locale.h"
 #include "system/wait.h"
 
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
 #ifdef _WIN32
 #define mkdir(d,m) _mkdir(d)
 #endif
@@ -1058,9 +1062,6 @@ const char *rep_getprogname(void)
 #endif /* HAVE_GETPROGNAME */
 
 #ifndef HAVE_COPY_FILE_RANGE
-# ifdef HAVE_SYSCALL_COPY_FILE_RANGE
-# include <sys/syscall.h>
-# endif
 ssize_t rep_copy_file_range(int fd_in,
 			    loff_t *off_in,
 			    int fd_out,
@@ -1081,3 +1082,48 @@ ssize_t rep_copy_file_range(int fd_in,
 	return -1;
 }
 #endif /* HAVE_COPY_FILE_RANGE */
+
+#ifndef HAVE_OPENAT2
+
+/* fallback known wellknown __NR_openat2 values */
+#ifndef __NR_openat2
+# if defined(LINUX) && defined(HAVE_SYS_SYSCALL_H)
+#  if defined(__i386__)
+#   define __NR_openat2 437
+#  elif defined(__x86_64__) && defined(__LP64__)
+#   define __NR_openat2 437 /* 437 0x1B5 */
+#  elif defined(__x86_64__) && defined(__ILP32__)
+#   define __NR_openat2 1073742261 /* 1073742261 0x400001B5 */
+#  elif defined(__aarch64__)
+#   define __NR_openat2 437
+#  elif defined(__arm__)
+#   define __NR_openat2 437
+#  elif defined(__sparc__)
+#   define __NR_openat2 437
+#  endif
+# endif /* defined(LINUX) && defined(HAVE_SYS_SYSCALL_H) */
+#endif /* !__NR_openat2 */
+
+#ifdef DISABLE_OPATH
+/*
+ * systems without O_PATH also don't have openat2,
+ * so make sure we at a realistic combination.
+ */
+#undef __NR_openat2
+#endif /* DISABLE_OPATH */
+
+long rep_openat2(int dirfd, const char *pathname,
+		 struct open_how *how, size_t size)
+{
+#ifdef __NR_openat2
+	return syscall(__NR_openat2,
+		       dirfd,
+		       pathname,
+		       how,
+		       size);
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+#endif /* !HAVE_OPENAT2 */
diff --git a/lib/replace/system/filesys.h b/lib/replace/system/filesys.h
index bb9482c69af..8005b18780f 100644
--- a/lib/replace/system/filesys.h
+++ b/lib/replace/system/filesys.h
@@ -243,4 +243,39 @@ int rep_fsetxattr (int filedes, const char *name, const void *value, size_t size
 
 #endif /* !defined(HAVE_XATTR_XATTR) || defined(XATTR_ADDITIONAL_OPTIONS) */
 
+#ifdef HAVE_LINUX_OPENAT2_H
+#include <linux/openat2.h>
+#else /* ! HAVE_LINUX_OPENAT2_H */
+/* how->resolve flags for openat2(2). */
+#define RESOLVE_NO_XDEV		0x01 /* Block mount-point crossings
+					(includes bind-mounts). */
+#define RESOLVE_NO_MAGICLINKS	0x02 /* Block traversal through procfs-style
+					"magic-links". */
+#define RESOLVE_NO_SYMLINKS	0x04 /* Block traversal through all symlinks
+					(implies OEXT_NO_MAGICLINKS) */
+#define RESOLVE_BENEATH		0x08 /* Block "lexical" trickery like
+					"..", symlinks, and absolute
+					paths which escape the dirfd. */
+#define RESOLVE_IN_ROOT		0x10 /* Make all jumps to "/" and ".."
+					be scoped inside the dirfd
+					(similar to chroot(2)). */
+#define RESOLVE_CACHED		0x20 /* Only complete if resolution can be
+					completed through cached lookup. May
+					return -EAGAIN if that's not
+					possible. */
+struct __rep_open_how {
+	uint64_t flags;
+	uint64_t mode;
+	uint64_t resolve;
+};
+#define open_how __rep_open_how
+#endif /* ! HAVE_LINUX_OPENAT2_H */
+
+#ifndef HAVE_OPENAT2
+long rep_openat2(int dirfd, const char *pathname,
+		 struct open_how *how, size_t size);
+#define openat2(dirfd, pathname, how, size) \
+	rep_openat2(dirfd, pathname, how, size)
+#endif /* !HAVE_OPENAT2 */
+
 #endif
diff --git a/lib/replace/wscript b/lib/replace/wscript
index dd9b19219a1..2f179992c82 100644
--- a/lib/replace/wscript
+++ b/lib/replace/wscript
@@ -66,6 +66,7 @@ def configure(conf):
     conf.CHECK_HEADERS('errno.h')
     conf.CHECK_HEADERS('getopt.h iconv.h')
     conf.CHECK_HEADERS('memory.h nss.h sasl/sasl.h')
+    conf.CHECK_HEADERS('linux/openat2.h')
 
     conf.CHECK_FUNCS_IN('inotify_init', 'inotify', checklibc=True,
                         headers='sys/inotify.h')
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 2313f6fce36..387856e07a0 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -275,6 +275,8 @@ sub setup_nt4_dc
 	server schannel = auto
 	rpc start on demand helpers = false
 
+	vfs_default:VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS = no
+
 	fss: sequence timeout = 1
 	check parent directory delete on close = yes
 ";
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 866d2a5f4a8..2fd8d1cdd06 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -716,6 +716,7 @@ typedef struct connection_struct {
 	bool ipc;
 	bool read_only; /* Attributes for the current user of the share. */
 	bool have_proc_fds;
+	uint64_t open_how_resolve; /* supported vfs_open_how.resolve features */
 	uint32_t share_access;
 	/* Does this filesystem honor
 	   sub second timestamps on files
@@ -905,6 +906,8 @@ struct vfs_aio_state {
 	uint64_t duration;
 };
 
+#define VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS 1
+
 struct vfs_open_how {
 	int flags;
 	mode_t mode;
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index 1ee3f1831c6..a7ba0ece206 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -17,6 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "includes.h"
+#include "system/filesys.h"
 #include <linux/ioctl.h>
 #include <linux/fs.h>
 #include <sys/ioctl.h>
@@ -24,8 +26,6 @@
 #include <fcntl.h>
 #include <dirent.h>
 #include <libgen.h>
-#include "system/filesys.h"
-#include "includes.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "librpc/gen_ndr/smbXsrv.h"
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index dee8ff50df4..48ff174ebbe 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -49,7 +49,28 @@
 
 static int vfswrap_connect(vfs_handle_struct *handle, const char *service, const char *user)
 {
+	bool bval;
+
 	handle->conn->have_proc_fds = sys_have_proc_fds();
+
+	/*
+	 * assume the kernel will support openat2(),
+	 * it will be reset on the first ENOSYS.
+	 *
+	 * Note that libreplace will always provide openat2(),
+	 * but return -1/errno = ENOSYS...
+	 *
+	 * The option is only there to test the fallback code.
+	 */
+	bval = lp_parm_bool(SNUM(handle->conn),
+			    "vfs_default",
+			    "VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS",
+			    true);
+	if (bval) {
+		handle->conn->open_how_resolve |=
+			VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS;
+	}
+
 	return 0;    /* Return >= 0 for success */
 }
 
@@ -701,7 +722,7 @@ static int vfswrap_openat(vfs_handle_struct *handle,
 
 	START_PROFILE(syscall_openat);
 
-	if (how->resolve != 0) {
+	if (how->resolve & ~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS) {
 		errno = ENOSYS;
 		result = -1;
 		goto out;
@@ -714,8 +735,55 @@ static int vfswrap_openat(vfs_handle_struct *handle,
 	if (fsp->fsp_flags.is_pathref) {
 		flags |= O_PATH;
 	}
+	if (flags & O_PATH) {
+		/*
+		 * From "man 2 openat":
+		 *
+		 *   When O_PATH is specified in flags, flag bits other than
+		 *   O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored.
+		 *
+		 * From "man 2 openat2":
+		 *
+		 *   Whereas  openat(2)  ignores  unknown  bits  in  its  flags
+		 *   argument, openat2() returns an error if unknown or
+		 *   conflicting flags are specified in how.flags.
+		 *
+		 * So we better clear ignored/invalid flags
+		 * and only keep the exptected once.
+		 */
+		flags &= (O_PATH|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+	}
 #endif
 
+	if (how->resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS) {
+		struct open_how linux_how = {
+			.flags = flags,
+			.mode = mode,
+			.resolve = RESOLVE_NO_SYMLINKS,
+		};
+
+		result = openat2(fsp_get_pathref_fd(dirfsp),
+				 smb_fname->base_name,
+				 &linux_how,
+				 sizeof(linux_how));
+		if (result == -1) {
+			if (errno == ENOSYS) {
+				/*
+				 * The kernel doesn't support
+				 * openat2(), so indicate to
+				 * the callers that
+				 * VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS
+				 * would just be a waste of time.
+				 */
+				fsp->conn->open_how_resolve &=
+					~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS;
+			}
+			goto out;
+		}
+
+		goto done;
+	}
+
 	if (fsp->fsp_flags.is_pathref && !have_opath) {
 		become_root();
 		became_root = true;
@@ -730,6 +798,7 @@ static int vfswrap_openat(vfs_handle_struct *handle,
 		unbecome_root();
 	}
 
+done:
 	fsp->fsp_flags.have_proc_fds = fsp->conn->have_proc_fds;
 
 out:
diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c
index 5168df7a97b..65dd151bb02 100644
--- a/source3/modules/vfs_io_uring.c
+++ b/source3/modules/vfs_io_uring.c
@@ -20,6 +20,24 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include "replace.h"
+
+/*
+ * liburing.h only needs a forward declaration
+ * of struct open_how.
+ *
+ * If struct open_how is defined in liburing/compat.h
+ * itself, hide it away in order to avoid conflicts
+ * with including linux/openat2.h or defining 'struct open_how'
+ * in libreplace.
+ */
+struct open_how;
+#ifdef HAVE_STRUCT_OPEN_HOW_LIBURING_COMPAT_H
+#define open_how __ignore_liburing_compat_h_open_how
+#include <liburing/compat.h>
+#undef open_how
+#endif /* HAVE_STRUCT_OPEN_HOW_LIBURING_COMPAT_H */
+
 #include "includes.h"
 #include "system/filesys.h"
 #include "smbd/smbd.h"
diff --git a/source3/script/tests/test_symlink_traversal_smb2.sh b/source3/script/tests/test_symlink_traversal_smb2.sh
index eadd7592de5..971d5344216 100755
--- a/source3/script/tests/test_symlink_traversal_smb2.sh
+++ b/source3/script/tests/test_symlink_traversal_smb2.sh
@@ -50,7 +50,11 @@ do_cleanup()
 	(
 		#subshell.
 		cd "$share_test_dir" || return
+		rm -f "symlink_to_dot"
 		rm -f "file_exists"
+		rm -f "symlink_to_file_exists"
+		rm -rf "dir_exists"
+		rm -f "symlink_to_dir_exists"
 		rm -f "symlink_noexist"
 		rm -f "symlink_file_outside_share"
 		rm -f "symlink_file_outside_share_noexist"
@@ -93,7 +97,13 @@ chmod 0 "$dir_outside_share_noperms"
 (
 	#subshell.
 	cd "$share_test_dir" || return
+	ln -s "." "symlink_to_dot"
 	touch "file_exists"
+	ln -s "file_exists" "symlink_to_file_exists"
+	mkdir "dir_exists"
+	ln -s "dir_exists" "symlink_to_dir_exists"
+	touch "dir_exists/subfile_exists"
+	mkdir "dir_exists/subdir_exists"
 	ln -s "noexist" "symlink_noexist"
 	ln -s "$file_outside_share" "symlink_file_outside_share"
 	ln -s "$file_outside_share_noexist" "symlink_file_outside_share_noexist"
@@ -107,7 +117,13 @@ chmod 0 "$dir_outside_share_noperms"
 	(
 		#subshell
 		cd "emptydir" || return
+		ln -s "." "symlink_to_dot"
 		touch "file_exists"
+		ln -s "file_exists" "symlink_to_file_exists"
+		mkdir "dir_exists"
+		ln -s "dir_exists" "symlink_to_dir_exists"
+		touch "dir_exists/subfile_exists"
+		mkdir "dir_exists/subdir_exists"
 		ln -s "noexist" "symlink_noexist"
 		ln -s "$file_outside_share" "symlink_file_outside_share"
 		ln -s "$file_outside_share_noexist" "symlink_file_outside_share_noexist"
@@ -126,6 +142,8 @@ chmod 0 "$dir_outside_share_noperms"
 	touch "dir_inside_share_noperms/noperm_file_exists"
 	chmod 0 "dir_inside_share_noperms"
 	ln -s "dir_inside_share_noperms" "symlink_dir_inside_share_noperms"
+	mkdir "dir_inside_share_noperms/noperm_subdir_exists"
+	touch "dir_inside_share_noperms/noperm_subdir_exists/noperm_subdir_file_exists"
 )
 
 #
@@ -179,25 +197,33 @@ test_symlink_traversal_SMB2_onename()
 	#
 	smbclient_expect_error "get" "$name" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 	smbclient_expect_error "get" "$name/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "get" "$name/noexistsdir/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
 	smbclient_expect_error "get" "$name/*" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
 	smbclient_expect_error "get" "$name/*/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
+	smbclient_expect_error "get" "$name/*/noexistsdir/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
 	# Now in subdirectory emptydir
 	smbclient_expect_error "get" "emptydir/$name" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 	smbclient_expect_error "get" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "get" "emptydir/$name/noexistsdir/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
 	smbclient_expect_error "get" "emptydir/$name/*" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
 	smbclient_expect_error "get" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
+	smbclient_expect_error "get" "emptydir/$name/*/noexistsdir/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
 	#
 	# ls commands.
 	#
 	smbclient_expect_error "ls" "$name" "" "NT_STATUS_NO_SUCH_FILE" || return 1
 	smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "$name/noexistsdir/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
 	smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 	smbclient_expect_error "ls" "$name/*/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
+	smbclient_expect_error "ls" "$name/*/noexistsdir/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
 	# Now in subdirectory emptydir
 	smbclient_expect_error "ls" "emptydir/$name" "" "NT_STATUS_NO_SUCH_FILE" || return 1
 	smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "emptydir/$name/noexistsdir/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
 	smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 	smbclient_expect_error "ls" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
+	smbclient_expect_error "ls" "emptydir/$name/*/noexistsdir/noexist" "" "NT_STATUS_OBJECT_NAME_INVALID" || return 1
 
 	#
 	# del commands.
@@ -215,9 +241,21 @@ test_symlink_traversal_SMB2_onename()
 		#
 		smbclient_expect_error "rename" "file_exists" "$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 		smbclient_expect_error "rename" "file_exists" "$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_file_exists" "$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_file_exists" "$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "dir_exists" "$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "dir_exists" "$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_dir_exists" "$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_dir_exists" "$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
 		# Now in subdirectory emptydir
 		smbclient_expect_error "rename" "file_exists" "emptydir/$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 		smbclient_expect_error "rename" "file_exists" "emptydir/$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_file_exists" "emptydir/$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_file_exists" "emptydir/$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "dir_exists" "emptydir/$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "dir_exists" "emptydir/$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_dir_exists" "emptydir/$name" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+		smbclient_expect_error "rename" "symlink_to_dir_exists" "emptydir/$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
 	fi
 	return 0
 }
@@ -234,6 +272,80 @@ test_symlink_traversal_SMB2()
 	test_symlink_traversal_SMB2_onename "symlink_dir_outside_share_noexist" "no rename" || return 1
 	test_symlink_traversal_SMB2_onename "symlink_file_outside_share_noperms" "do rename" || return 1
 	test_symlink_traversal_SMB2_onename "symlink_dir_outside_share_noperms" "do rename" || return 1
+
+	# Note the share has 'follow symlinks = yes'
+	smbclient_expect_error "ls" "." "" "NT_STATUS_NO_SUCH_FILE" || return 1
+	smbclient_expect_error "ls" "noexist1" "" "NT_STATUS_NO_SUCH_FILE" || return 1
+	smbclient_expect_error "ls" "noexist1/noexist2" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "noexist1/noexist2/noexist3" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "symlink_to_dot" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "symlink_to_dot/noexist1" "" "NT_STATUS_NO_SUCH_FILE" || return 1
+	smbclient_expect_error "ls" "symlink_to_dot/noexist1/noexist2" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "symlink_to_dot/noexist1/noexist2/noexist3" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "file_exists" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "file_exists/noexist1" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+	smbclient_expect_error "ls" "file_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "file_exists/noexist1/noexist2/noexist3" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "symlink_to_file_exists" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "symlink_to_file_exists/noexist1" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+	smbclient_expect_error "ls" "symlink_to_file_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "symlink_to_file_exists/noexist1/noexist2/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "dir_exists" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "dir_exists/noexist1" "" "NT_STATUS_NO_SUCH_FILE" || return 1
+	smbclient_expect_error "ls" "dir_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "dir_exists/noexist1/noexist2/noexist3" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "dir_exists/subfile_exists" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "dir_exists/subfile_exists/noexist1" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+	smbclient_expect_error "ls" "dir_exists/subfile_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "dir_exists/subfile_exists/noexist1/noexist2/noexist3" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "dir_exists/subdir_exists" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "dir_exists/subdir_exists/noexist1" "" "NT_STATUS_NO_SUCH_FILE" || return 1
+	smbclient_expect_error "ls" "dir_exists/subdir_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "dir_exists/subdir_exists/noexist1/noexist2/noexist3" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
+	smbclient_expect_error "ls" "symlink_to_dir_exists" "" "NT_STATUS_OK" || return 1
+	smbclient_expect_error "ls" "symlink_to_dir_exists/noexist1" "" "NT_STATUS_NO_SUCH_FILE" || return 1
+	smbclient_expect_error "ls" "symlink_to_dir_exists/noexist1/noexist2" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1


-- 
Samba Shared Repository



More information about the samba-cvs mailing list