smbd: Simplify openat_pathref_fsp_case_insensitive()
authorVolker Lendecke <vl@samba.org>
Wed, 18 Oct 2023 09:50:20 +0000 (11:50 +0200)
committerVolker Lendecke <vl@samba.org>
Tue, 7 Nov 2023 12:46:37 +0000 (12:46 +0000)
This is more lines of code, but it's still a simplification. With this
patch we don't call the full openat_pathref_fsp() anymore when looking
up the last component in filename_convert_dirfsp(), instead we do the
direct SMB_VFS_OPENAT(). We don't need the whole complexity of
non_widelink_open() for this case, we do know that we have a real
non-cwd dirfsp.

The other big change that is not obvious just from looking at the
patch: This removes the special case for looking up posix
symlinks. Before this patch, filename_convert_dirfsp() returned a
proper smb_filename but without an attached fsp when a smb1 posix
client hits a symlink. This caused all sorts of special case code
everywhere. For example smbd_do_qfilepathinfo() needs to cover both
cases just for the smb1 posix symlink case. This special-case handling
can go now. We can do the path lookup in the smb1-only qpathinfo code
and call into the common code with a proper fsp.

When hitting a symlink and with O_PATH available, we'll get the
symlink opened with an O_PATH fd. Without O_PATH we obviously can't do
that, there we get fd=-1 and an indication that we don't have the
procfd fallback around.

Why all this?

I want to present FIFOs (and eventually symlinks) as reparse points as
the very next step. Without this patch, there is no real unified way
to get the file attributes from disk. Now we can use the proper logic
of fdos_mode() everywhere and not rely on special cases for fsp==NULL.

This patch also changes some error codes for smb1 posix extensions. I
chose to just change the test instead of going after each and every
change. As long as we do get an error, I'm willing to accept that we
slightly change error path behaviour for this deprecated code.

And, I tried to split this up into smaller patches but I failed.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/modules/vfs_widelinks.c
source3/script/tests/test_symlink_traversal_smb1_posix.sh
source3/smbd/filename.c
source3/smbd/files.c
source3/smbd/open.c
source3/smbd/proto.h
source3/smbd/smb2_create.c

index 29f2d4834f65cbc488cd99281a14c5daa4f25a4b..c5b5084e108eb38b3b7c5cf15a0e58f623dfa536 100644 (file)
@@ -389,7 +389,7 @@ static int widelinks_openat(vfs_handle_struct *handle,
                        fsp->fsp_name->st = full_fname->st;
                }
                TALLOC_FREE(full_fname);
-               errno = ENOENT;
+               errno = ELOOP;
        }
        return ret;
 }
index 1ad1d56f12a5000ef730934fbd66047a3b22431c..52d6cfb9e3d08bb1a804d9d250f91c03ef137a86 100755 (executable)
@@ -194,13 +194,13 @@ test_symlink_traversal_SMB1_posix_onename()
        # ls commands.
        #
        smbclient_expect_error "ls" "$name" "" "NT_STATUS_OK" || return 1
-       smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
-       smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+       smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_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_PATH_NOT_FOUND" || return 1
        # Now in subdirectory emptydir
        smbclient_expect_error "ls" "emptydir/$name" "" "NT_STATUS_OK" || return 1
-       smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
-       smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+       smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_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_PATH_NOT_FOUND" || return 1
        #
        # SMB1+POSIX stat commands. All symlinks can be stat'ed.
@@ -211,9 +211,9 @@ test_symlink_traversal_SMB1_posix_onename()
        # del commands. Under SMB1+POSIX we can legitimately delete symlinks, so don't
        # try and delete symlink targets, we need them for the later tests.
        #
-       smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+       smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
        # Now in subdirectory emptydir
-       smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_NOT_A_DIRECTORY" || return 1
+       smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
 
        if [ "$do_rename" = "do rename" ]; then
                #
index 552940f60d70355ce65d96f650a0d61045c82f81..91d16e074953ecbc7eadeca996d9de5fba84f645 100644 (file)
@@ -29,7 +29,6 @@
 #include "fake_file.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
-#include "lib/util/memcache.h"
 #include "libcli/smb/reparse.h"
 
 uint32_t ucf_flags_from_smb_request(struct smb_request *req)
@@ -640,155 +639,6 @@ static NTSTATUS filename_convert_normalize_new(
        return NT_STATUS_OK;
 }
 
-/*
- * Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
- * fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
- * the first attempt based on the filename sent by the client gives
- * ENOENT.
- */
-static NTSTATUS openat_pathref_fsp_case_insensitive(
-       struct files_struct *dirfsp,
-       struct smb_filename *smb_fname_rel,
-       uint32_t ucf_flags)
-{
-       const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
-       DATA_BLOB cache_key = { .data = NULL, };
-       char *found_name = NULL;
-       NTSTATUS status;
-       bool ok;
-
-       SET_STAT_INVALID(smb_fname_rel->st);
-
-       /* Check veto files - only looks at last component. */
-       if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
-               DBG_DEBUG("veto files rejecting last component %s\n",
-                         smb_fname_str_dbg(smb_fname_rel));
-               return NT_STATUS_NETWORK_OPEN_RESTRICTION;
-       }
-
-       status = openat_pathref_fsp(dirfsp, smb_fname_rel);
-
-       if (NT_STATUS_IS_OK(status)) {
-               return NT_STATUS_OK;
-       }
-
-       if (VALID_STAT(smb_fname_rel->st)) {
-               /*
-                * We got an error although the object existed. Might
-                * be a symlink we don't want.
-                */
-               return status;
-       }
-
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
-               /*
-                * Only retry on ENOENT
-                */
-               return status;
-       }
-
-       if (posix || dirfsp->conn->case_sensitive) {
-               /*
-                * Only return case insensitive if required
-                */
-               return status;
-       }
-
-       if (lp_stat_cache()) {
-               char *base_name = smb_fname_rel->base_name;
-               char *original_relname = NULL;
-               DATA_BLOB value = { .data = NULL };
-
-               ok = get_real_filename_cache_key(
-                       talloc_tos(), dirfsp, base_name, &cache_key);
-               if (!ok) {
-                       /*
-                        * probably ENOMEM, just bail
-                        */
-                       return status;
-               }
-
-               DO_PROFILE_INC(statcache_lookups);
-
-               ok = memcache_lookup(
-                       NULL, GETREALFILENAME_CACHE, cache_key, &value);
-               if (!ok) {
-                       DO_PROFILE_INC(statcache_misses);
-                       goto lookup;
-               }
-               DO_PROFILE_INC(statcache_hits);
-
-               /*
-                * For the "new filename" case we need to preserve the
-                * capitalization the client sent us, see
-                * https://bugzilla.samba.org/show_bug.cgi?id=15481
-                */
-               original_relname = smb_fname_rel->base_name;
-
-               smb_fname_rel->base_name = talloc_memdup(
-                       smb_fname_rel, value.data, value.length);
-               if (smb_fname_rel->base_name == NULL) {
-                       TALLOC_FREE(cache_key.data);
-                       return NT_STATUS_NO_MEMORY;
-               }
-
-               if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
-                       DBG_DEBUG("veto files rejecting last component %s\n",
-                                 smb_fname_str_dbg(smb_fname_rel));
-                       TALLOC_FREE(cache_key.data);
-                       return NT_STATUS_NETWORK_OPEN_RESTRICTION;
-               }
-
-               status = openat_pathref_fsp(dirfsp, smb_fname_rel);
-               if (NT_STATUS_IS_OK(status)) {
-                       TALLOC_FREE(cache_key.data);
-                       TALLOC_FREE(original_relname);
-                       return NT_STATUS_OK;
-               }
-
-               memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
-               TALLOC_FREE(smb_fname_rel->base_name);
-               smb_fname_rel->base_name = original_relname;
-       }
-
-lookup:
-       status = get_real_filename_at(
-               dirfsp, smb_fname_rel->base_name, smb_fname_rel, &found_name);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
-           (ucf_flags & UCF_PREP_CREATEFILE)) {
-               /*
-                * dropbox
-                */
-               status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
-       }
-
-       if (NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(smb_fname_rel->base_name);
-               smb_fname_rel->base_name = found_name;
-
-               if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
-                       DBG_DEBUG("veto files rejecting last component %s\n",
-                               smb_fname_str_dbg(smb_fname_rel));
-                       return NT_STATUS_NETWORK_OPEN_RESTRICTION;
-               }
-
-               status = openat_pathref_fsp(dirfsp, smb_fname_rel);
-       }
-
-       if (NT_STATUS_IS_OK(status) && (cache_key.data != NULL)) {
-               DATA_BLOB value = {
-                       .data = (uint8_t *)smb_fname_rel->base_name,
-                       .length = strlen(smb_fname_rel->base_name) + 1,
-               };
-
-               memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
-       }
-
-       TALLOC_FREE(cache_key.data);
-
-       return status;
-}
-
 static const char *previous_slash(const char *name_in, const char *slash)
 {
        const char *prev = NULL;
@@ -936,6 +786,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
        struct smb_filename *smb_dirname = NULL;
        struct smb_filename *smb_fname_rel = NULL;
        struct smb_filename *smb_fname = NULL;
+       struct open_symlink_err *symlink_err = NULL;
        const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
        char *dirname = NULL;
        const char *fname_rel = NULL;
@@ -1021,8 +872,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
                        posix ? SMB_FILENAME_POSIX_PATH : 0,
                        &smb_dirname);
        } else {
-               struct open_symlink_err *symlink_err = NULL;
-
                status = normalize_filename_case(conn, dirname, ucf_flags);
                if (!NT_STATUS_IS_OK(status)) {
                        DBG_ERR("normalize_filename_case %s failed: %s\n",
@@ -1043,14 +892,6 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
                if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
                        size_t name_in_len, dirname_len;
 
-                       if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
-                           strnequal(symlink_err->reparse->substitute_name,
-                                     "msdfs:",
-                                     6)) {
-                               status = NT_STATUS_PATH_NOT_COVERED;
-                               goto fail;
-                       }
-
                        name_in_len = strlen(name_in);
                        dirname_len = strlen(dirname);
 
@@ -1143,45 +984,27 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
                goto fail;
        }
 
-       status = openat_pathref_fsp_case_insensitive(
-               smb_dirname->fsp, smb_fname_rel, ucf_flags);
+       status = openat_pathref_fsp_lcomp(smb_dirname->fsp,
+                                         smb_fname_rel,
+                                         ucf_flags);
 
-       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
-           VALID_STAT(smb_fname_rel->st) &&
-           S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
+       if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
 
                /*
-                * If we're on an MSDFS share, see if this is
-                * an MSDFS link.
+                * Upper layers might need the link target. Here we
+                * still have the relname around, get the symlink err.
                 */
-               if (lp_host_msdfs() &&
-                   lp_msdfs_root(SNUM(conn)) &&
-                   is_msdfs_link(smb_dirname->fsp, smb_fname_rel))
-               {
-                       status = NT_STATUS_PATH_NOT_COVERED;
+               status = create_open_symlink_err(mem_ctx,
+                                                smb_dirname->fsp,
+                                                smb_fname_rel,
+                                                &symlink_err);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_DEBUG("Could not read symlink for %s: %s\n",
+                                 smb_fname_str_dbg(
+                                         smb_fname_rel->fsp->fsp_name),
+                                 nt_errstr(status));
                        goto fail;
                }
-
-#if defined(WITH_SMB1SERVER)
-               /*
-                * In SMB1 posix mode, if this is a symlink,
-                * allow access to the name with a NULL smb_fname->fsp.
-                */
-               if (ucf_flags & UCF_LCOMP_LNK_OK) {
-                       SMB_ASSERT(smb_fname_rel->fsp == NULL);
-                       SMB_ASSERT(streamname == NULL);
-
-                       smb_fname = full_path_from_dirfsp_atname(
-                               mem_ctx,
-                               smb_dirname->fsp,
-                               smb_fname_rel);
-                       if (smb_fname == NULL) {
-                               status = NT_STATUS_NO_MEMORY;
-                               goto fail;
-                       }
-                       goto done;
-               }
-#endif
        }
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
@@ -1314,6 +1137,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
 done:
        *_dirfsp = smb_dirname->fsp;
        *_smb_fname = smb_fname;
+       *_symlink_err = symlink_err;
 
        smb_fname_fsp_unlink(smb_fname_rel);
        TALLOC_FREE(smb_fname_rel);
@@ -1366,10 +1190,34 @@ next:
                                                   _smb_fname,
                                                   &symlink_err);
 
+       if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) {
+               /*
+                * lcomp is a symlink
+                */
+               if (ucf_flags & UCF_LCOMP_LNK_OK) {
+                       TALLOC_FREE(symlink_err);
+                       return NT_STATUS_OK;
+               }
+               close_file_free(NULL, _dirfsp, ERROR_CLOSE);
+               status = NT_STATUS_STOPPED_ON_SYMLINK;
+       }
+
        if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
                return status;
        }
 
+       /*
+        * If we're on an MSDFS share, see if this is
+        * an MSDFS link.
+        */
+       if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) &&
+           strnequal(symlink_err->reparse->substitute_name, "msdfs:", 6))
+       {
+               TALLOC_FREE(*_smb_fname);
+               TALLOC_FREE(symlink_err);
+               return NT_STATUS_PATH_NOT_COVERED;
+       }
+
        if (!lp_follow_symlinks(SNUM(conn))) {
                status = (symlink_err->unparsed == 0)
                                 ? NT_STATUS_OBJECT_NAME_NOT_FOUND
index 4f977f7235bc0d23b85c952624060e15f0f4be23..b94468ad6afd7da1700115256cb771c2dedbe8ea 100644 (file)
@@ -25,6 +25,7 @@
 #include "util_tdb.h"
 #include "lib/util/bitmap.h"
 #include "lib/util/strv.h"
+#include "lib/util/memcache.h"
 #include "libcli/smb/reparse.h"
 
 #define FILE_HANDLE_OFFSET 0x1000
@@ -831,6 +832,135 @@ NTSTATUS create_open_symlink_err(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
+static int smb_vfs_openat_ci(TALLOC_CTX *mem_ctx,
+                            bool case_sensitive,
+                            struct connection_struct *conn,
+                            struct files_struct *dirfsp,
+                            struct smb_filename *smb_fname_rel,
+                            files_struct *fsp,
+                            const struct vfs_open_how *how)
+{
+       char *orig_base_name = smb_fname_rel->base_name;
+       DATA_BLOB cache_key = {
+               .data = NULL,
+       };
+       DATA_BLOB cache_value = {
+               .data = NULL,
+       };
+       NTSTATUS status;
+       int fd;
+       bool ok;
+
+       fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
+       if ((fd >= 0) || case_sensitive) {
+               return fd;
+       }
+       if (errno != ENOENT) {
+               return -1;
+       }
+
+       if (!lp_stat_cache()) {
+               goto lookup;
+       }
+
+       ok = get_real_filename_cache_key(mem_ctx,
+                                        dirfsp,
+                                        orig_base_name,
+                                        &cache_key);
+       if (!ok) {
+               /*
+                * probably ENOMEM, just bail
+                */
+               errno = ENOMEM;
+               return -1;
+       }
+
+       DO_PROFILE_INC(statcache_lookups);
+
+       ok = memcache_lookup(NULL,
+                            GETREALFILENAME_CACHE,
+                            cache_key,
+                            &cache_value);
+       if (!ok) {
+               DO_PROFILE_INC(statcache_misses);
+               goto lookup;
+       }
+       DO_PROFILE_INC(statcache_hits);
+
+       smb_fname_rel->base_name = talloc_strndup(mem_ctx,
+                                                 (char *)cache_value.data,
+                                                 cache_value.length);
+       if (smb_fname_rel->base_name == NULL) {
+               TALLOC_FREE(cache_key.data);
+               smb_fname_rel->base_name = orig_base_name;
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
+               DBG_DEBUG("veto files rejecting last component %s\n",
+                         smb_fname_str_dbg(smb_fname_rel));
+               TALLOC_FREE(cache_key.data);
+               smb_fname_rel->base_name = orig_base_name;
+               errno = EPERM;
+               return -1;
+       }
+
+       fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
+       if (fd >= 0) {
+               TALLOC_FREE(cache_key.data);
+               return fd;
+       }
+
+       memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
+
+       /*
+        * For the "new filename" case we need to preserve the
+        * capitalization the client sent us, see
+        * https://bugzilla.samba.org/show_bug.cgi?id=15481
+        */
+       TALLOC_FREE(smb_fname_rel->base_name);
+       smb_fname_rel->base_name = orig_base_name;
+
+lookup:
+
+       status = get_real_filename_at(dirfsp,
+                                     orig_base_name,
+                                     mem_ctx,
+                                     &smb_fname_rel->base_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("get_real_filename_at() failed: %s\n",
+                         nt_errstr(status));
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (IS_VETO_PATH(conn, smb_fname_rel->base_name)) {
+               DBG_DEBUG("found veto files path component "
+                         "%s => %s\n",
+                         orig_base_name,
+                         smb_fname_rel->base_name);
+               TALLOC_FREE(smb_fname_rel->base_name);
+               smb_fname_rel->base_name = orig_base_name;
+               errno = ENOENT;
+               return -1;
+       }
+
+       fd = SMB_VFS_OPENAT(conn, dirfsp, smb_fname_rel, fsp, how);
+
+       if ((fd >= 0) && (cache_key.data != NULL)) {
+               DATA_BLOB value = {
+                       .data = (uint8_t *)smb_fname_rel->base_name,
+                       .length = strlen(smb_fname_rel->base_name) + 1,
+               };
+
+               memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
+               TALLOC_FREE(cache_key.data);
+       }
+
+       return fd;
+}
+
 NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
                                      struct connection_struct *conn,
                                      struct files_struct *in_dirfsp,
@@ -855,7 +985,7 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
        struct open_symlink_err *symlink_err = NULL;
        struct files_struct *fsp = NULL;
        char *path = NULL, *next = NULL;
-       bool case_sensitive, ok, is_toplevel;
+       bool ok, is_toplevel;
        int fd;
        NTSTATUS status;
        struct vfs_open_how how = {
@@ -1030,48 +1160,13 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
 next:
        next = strv_next(path, rel_fname.base_name);
 
-       fd = SMB_VFS_OPENAT(
-               conn,
-               dirfsp,
-               &rel_fname,
-               fsp,
-               &how);
-
-       case_sensitive = (posix || conn->case_sensitive);
-
-       if ((fd == -1) && (errno == ENOENT) && !case_sensitive) {
-               const char *orig_base_name = rel_fname.base_name;
-
-               status = get_real_filename_at(
-                       dirfsp,
-                       rel_fname.base_name,
-                       talloc_tos(),
-                       &rel_fname.base_name);
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       DBG_DEBUG("get_real_filename_at failed: %s\n",
-                                 nt_errstr(status));
-                       goto fail;
-               }
-
-               /* Name might have been demangled - check veto files. */
-               if (IS_VETO_PATH(conn, rel_fname.base_name)) {
-                       DBG_DEBUG("%s contains veto files path component "
-                                 "%s => %s\n",
-                                 path_in,
-                                 orig_base_name,
-                                 rel_fname.base_name);
-                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-                       goto fail;
-               }
-
-               fd = SMB_VFS_OPENAT(
-                       conn,
-                       dirfsp,
-                       &rel_fname,
-                       fsp,
-                       &how);
-       }
+       fd = smb_vfs_openat_ci(talloc_tos(),
+                              posix || conn->case_sensitive,
+                              conn,
+                              dirfsp,
+                              &rel_fname,
+                              fsp,
+                              &how);
 
 #ifndef O_PATH
        if ((fd == -1) && (errno == ELOOP)) {
@@ -1304,6 +1399,162 @@ fail:
        return status;
 }
 
+/*
+ * Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
+ * fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
+ * the first attempt based on the filename sent by the client gives
+ * ENOENT.
+ */
+NTSTATUS openat_pathref_fsp_lcomp(struct files_struct *dirfsp,
+                                 struct smb_filename *smb_fname_rel,
+                                 uint32_t ucf_flags)
+{
+       struct connection_struct *conn = dirfsp->conn;
+       const char *orig_rel_base_name = smb_fname_rel->base_name;
+       struct files_struct *fsp = NULL;
+       struct smb_filename *full_fname = NULL;
+       struct vfs_open_how how = {
+               .flags = O_RDONLY | O_NONBLOCK | O_NOFOLLOW,
+       };
+       NTSTATUS status;
+       int ret, fd;
+
+       /*
+        * Make sure we don't need of the all the magic in
+        * openat_pathref_fsp() with regards non_widelink_open etc.
+        */
+
+       SMB_ASSERT((smb_fname_rel->fsp == NULL) &&
+                  (dirfsp != dirfsp->conn->cwd_fsp) &&
+                  (strchr_m(smb_fname_rel->base_name, '/') == NULL) &&
+                  !is_named_stream(smb_fname_rel));
+
+       SET_STAT_INVALID(smb_fname_rel->st);
+
+       /* Check veto files - only looks at last component. */
+       if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
+               DBG_DEBUG("veto files rejecting last component %s\n",
+                         smb_fname_str_dbg(smb_fname_rel));
+               return NT_STATUS_NETWORK_OPEN_RESTRICTION;
+       }
+
+       status = fsp_new(conn, conn, &fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("fsp_new() failed: %s\n", nt_errstr(status));
+               return status;
+       }
+
+       GetTimeOfDay(&fsp->open_time);
+       fsp_set_gen_id(fsp);
+       ZERO_STRUCT(conn->sconn->fsp_fi_cache);
+
+       fsp->fsp_flags.is_pathref = true;
+
+       full_fname = full_path_from_dirfsp_atname(conn, dirfsp, smb_fname_rel);
+       if (full_fname == NULL) {
+               DBG_DEBUG("full_path_from_dirfsp_atname(%s/%s) failed\n",
+                         dirfsp->fsp_name->base_name,
+                         smb_fname_rel->base_name);
+               file_free(NULL, fsp);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = fsp_attach_smb_fname(fsp, &full_fname);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("fsp_attach_smb_fname(fsp, %s) failed: %s\n",
+                         smb_fname_str_dbg(full_fname),
+                         nt_errstr(status));
+               file_free(NULL, fsp);
+               return status;
+       }
+
+       fd = smb_vfs_openat_ci(smb_fname_rel,
+                              (ucf_flags & UCF_POSIX_PATHNAMES) ||
+                                      conn->case_sensitive,
+                              conn,
+                              dirfsp,
+                              smb_fname_rel,
+                              fsp,
+                              &how);
+
+       if ((fd == -1) && (errno == ENOENT)) {
+               status = map_nt_error_from_unix(errno);
+               DBG_DEBUG("smb_vfs_openat(%s/%s) failed: %s\n",
+                         dirfsp->fsp_name->base_name,
+                         smb_fname_rel->base_name,
+                         strerror(errno));
+               file_free(NULL, fsp);
+               return status;
+       }
+
+       if (smb_fname_rel->base_name != orig_rel_base_name) {
+               struct smb_filename new_fullname = *smb_fname_rel;
+
+               DBG_DEBUG("rel->base_name changed from %s to %s\n",
+                         orig_rel_base_name,
+                         smb_fname_rel->base_name);
+
+               new_fullname.base_name = full_path_from_dirfsp_at_basename(
+                       talloc_tos(), dirfsp, new_fullname.base_name);
+               if (new_fullname.base_name == NULL) {
+                       fd_close(fsp);
+                       file_free(NULL, fsp);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               status = fsp_set_smb_fname(fsp, &new_fullname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       fd_close(fsp);
+                       file_free(NULL, fsp);
+                       return status;
+               }
+       }
+
+       fsp_set_fd(fsp, fd);
+
+       if (fd >= 0) {
+               ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
+       } else {
+               ret = SMB_VFS_FSTATAT(fsp->conn,
+                                     dirfsp,
+                                     smb_fname_rel,
+                                     &fsp->fsp_name->st,
+                                     AT_SYMLINK_NOFOLLOW);
+       }
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               DBG_DEBUG("SMB_VFS_%sSTAT(%s/%s) failed: %s\n",
+                         (fd >= 0) ? "F" : "",
+                         dirfsp->fsp_name->base_name,
+                         smb_fname_rel->base_name,
+                         nt_errstr(status));
+               fd_close(fsp);
+               file_free(NULL, fsp);
+               return status;
+       }
+
+       fsp->fsp_flags.is_directory = S_ISDIR(fsp->fsp_name->st.st_ex_mode);
+       fsp->file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
+
+       smb_fname_rel->st = fsp->fsp_name->st;
+
+       status = fsp_smb_fname_link(fsp,
+                                   &smb_fname_rel->fsp_link,
+                                   &smb_fname_rel->fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("fsp_smb_fname_link() failed: %s\n",
+                         nt_errstr(status));
+               fd_close(fsp);
+               file_free(NULL, fsp);
+               return status;
+       }
+
+       DBG_DEBUG("fsp [%s]: OK, fd=%d\n", fsp_str_dbg(fsp), fd);
+
+       talloc_set_destructor(smb_fname_rel, smb_fname_fsp_destructor);
+       return NT_STATUS_OK;
+}
+
 void smb_fname_fsp_unlink(struct smb_filename *smb_fname)
 {
        talloc_set_destructor(smb_fname, NULL);
index 8c34269c86cd25cee0b04cdbaa274737e0291b10..87a252a3d1bbc38ab1c4c4b677cddef583ab2ca2 100644 (file)
@@ -5961,6 +5961,31 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                goto fail;
        }
 
+       if (!(create_options & FILE_OPEN_REPARSE_POINT) &&
+           (smb_fname->fsp != NULL) && /* new files don't have an fsp */
+           VALID_STAT(smb_fname->fsp->fsp_name->st))
+       {
+               mode_t type = (smb_fname->fsp->fsp_name->st.st_ex_mode &
+                              S_IFMT);
+
+               switch (type) {
+               case S_IFREG:
+                       FALL_THROUGH;
+               case S_IFDIR:
+                       break;
+               case S_IFLNK:
+                       /*
+                        * We should never get this far with a symlink
+                        * "as such". Report as not existing.
+                        */
+                       status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                       goto fail;
+               default:
+                       status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+                       goto fail;
+               }
+       }
+
        if (req == NULL) {
                oplock_request |= INTERNAL_OPEN_ONLY;
        }
index 902caf27890ad0070346e1fdf54fcf9fbfdb32a6..e2902783efb15671e4bc97cad69c4212eb2309da 100644 (file)
@@ -448,6 +448,9 @@ NTSTATUS openat_pathref_fsp_nosymlink(TALLOC_CTX *mem_ctx,
                                      bool posix,
                                      struct smb_filename **_smb_fname,
                                      struct open_symlink_err **_symlink_err);
+NTSTATUS openat_pathref_fsp_lcomp(struct files_struct *dirfsp,
+                                 struct smb_filename *smb_fname_rel,
+                                 uint32_t ucf_flags);
 NTSTATUS readlink_talloc(
        TALLOC_CTX *mem_ctx,
        struct files_struct *dirfsp,
index eaefd052a9b51ebd277bd455da12999c3c2d12f8..abcce5afb4fd388b57ff65e4e4fcceb351e03dd7 100644 (file)
@@ -1136,6 +1136,21 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                     &state->info,
                                     &in_context_blobs,
                                     state->out_context_blobs);
+       if (NT_STATUS_IS_OK(status) &&
+           !(state->in_create_options & FILE_OPEN_REPARSE_POINT))
+       {
+
+               mode_t mode = state->result->fsp_name->st.st_ex_mode;
+
+               if (!(S_ISREG(mode) || S_ISDIR(mode))) {
+                       /*
+                        * Only open files and dirs without
+                        * FILE_OPEN_REPARSE_POINT
+                        */
+                       close_file_free(smb1req, &state->result, ERROR_CLOSE);
+                       status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+               }
+       }
        if (!NT_STATUS_IS_OK(status)) {
                if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
                        SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);