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 <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> Autobuild-User(master): Volker Lendecke <v...@samba.org> Autobuild-Date(master): Mon Aug 15 16:00:26 UTC 2022 on sn-devel-184 commit 4708ba2f013c5f5ea5aa5dcf4873c2b4a86fb8ff Author: Volker Lendecke <v...@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 <me...@samba.org> Signed-off-by: Volker Lendecke <v...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 8544f4490a0b5e54b807daedddb96778744b62ee Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit d6653067b20e61af1f05423764c8486a1a5445c8 Author: Volker Lendecke <v...@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 <me...@samba.org> Signed-off-by: Volker Lendecke <v...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 35b99c87ef92df006f8b0a41bbea051f0faeadb9 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 17484d069b92d08b0228fb509ea42ab4c3f496a8 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit f7dc27558329eea7d2c4d75ee101c7f9d3a7afe3 Author: Volker Lendecke <v...@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 <me...@samba.org> Signed-off-by: Volker Lendecke <v...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit ae1a84f7313bdf4702492451714eacc78ee7745f Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit f7618dd31a9f8f6c0dbfdedd1a664eed25e2e449 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit b89001e9226ecb0f4e5c906f7195f0e53cd7d608 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 37ba6df174d73b82e951de401cba7f839ad61ab5 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit ce804b78164a3166a16ca3071028536761fd18d7 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 2369d0833361faf4a125431e735fce7efb6024d6 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit cea9451f780d13e528f1722a67eccbbc78b2daf9 Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 2b51bad747551605ba3b70ac3b692107a0cd7aad Author: Stefan Metzmacher <me...@samba.org> Date: Thu Aug 11 00:41:28 2022 +0200 wafsamba: allow cflags for CHECK_TYPE[_IN]() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Volker Lendecke <v...@samba.org> commit 085f14857531dab179af66a69962486c7dd2592c Author: Stefan Metzmacher <me...@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 <me...@samba.org> Reviewed-by: Volker Lendecke <v...@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