smbd: move processing of root_dir_fid outside of SMB_VFS_CREATE_FILE()
authorRalph Boehme <slow@samba.org>
Thu, 14 May 2020 11:42:05 +0000 (13:42 +0200)
committerJeremy Allison <jra@samba.org>
Thu, 14 May 2020 18:07:39 +0000 (18:07 +0000)
The only two callers that pass a root_dir_fid != 0 are reply_ntcreate_and_X()
and call_nt_transact_create(). By moving root_dir_fid processing outside of
SMB_VFS_CREATE_FILE() we can avoid rewriting get_relative_fid_filename() to work
with *AT semantics and dirfsps.

Bonus points: the previous code in both functions passed fname to
filename_convert() which can't really have worked as it is relative to
root_dir_fid so filename_convert() will not be able to stat() the relative path
and it's components.

The only test I can find that uses a root dir_fid is raw.samba3rootdirfid and
that uses a handle on the share root which will work.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14380

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
selftest/knownfail.d/samba3.raw.samba3rootdirfid2 [deleted file]
source3/smbd/nttrans.c

diff --git a/selftest/knownfail.d/samba3.raw.samba3rootdirfid2 b/selftest/knownfail.d/samba3.raw.samba3rootdirfid2
deleted file mode 100644 (file)
index 745c841..0000000
+++ /dev/null
@@ -1 +0,0 @@
-^samba3.raw.samba3rootdirfid2
index 5f82c30f0dfcb37ff0f6c6ec82106b89e8d3ded2..de218103f6bab6ad5f2114a5edd6b90091a36ac0 100644 (file)
@@ -431,6 +431,78 @@ static struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx
        return result;
 }
 
+/*
+ * Calculate the full path name given a relative fid.
+ */
+static NTSTATUS get_relative_fid_filename(connection_struct *conn,
+                                         struct smb_request *req,
+                                         uint16_t root_dir_fid,
+                                         char *path,
+                                         char **path_out)
+{
+       struct files_struct *dir_fsp = NULL;
+       char *new_path = NULL;
+
+       if (root_dir_fid == 0 || path == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       dir_fsp = file_fsp(req, root_dir_fid);
+       if (dir_fsp == NULL) {
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       if (is_ntfs_stream_smb_fname(dir_fsp->fsp_name)) {
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       if (!dir_fsp->fsp_flags.is_directory) {
+               /*
+                * Check to see if this is a mac fork of some kind.
+                */
+               if (conn->fs_capabilities & FILE_NAMED_STREAMS) {
+                       char *stream = NULL;
+
+                       stream = strchr_m(path, ':');
+                       if (stream != NULL) {
+                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                       }
+               }
+
+               /*
+                * We need to handle the case when we get a relative open
+                * relative to a file and the pathname is blank - this is a
+                * reopen! (hint from demyn plantenberg)
+                */
+               return NT_STATUS_INVALID_HANDLE;
+       }
+
+       if (ISDOT(dir_fsp->fsp_name->base_name)) {
+               /*
+                * We're at the toplevel dir, the final file name
+                * must not contain ./, as this is filtered out
+                * normally by srvstr_get_path and unix_convert
+                * explicitly rejects paths containing ./.
+                */
+               new_path = talloc_strdup(talloc_tos(), path);
+       } else {
+               /*
+                * Copy in the base directory name.
+                */
+
+               new_path = talloc_asprintf(talloc_tos(),
+                                          "%s/%s",
+                                          dir_fsp->fsp_name->base_name,
+                                          path);
+       }
+       if (new_path == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       *path_out = new_path;
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Reply to an NT create and X call.
 ****************************************************************************/
@@ -537,6 +609,21 @@ void reply_ntcreate_and_X(struct smb_request *req)
                }
        }
 
+       if (root_dir_fid != 0) {
+               char *new_fname = NULL;
+
+               status = get_relative_fid_filename(conn,
+                                                  req,
+                                                  root_dir_fid,
+                                                  fname,
+                                                  &new_fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       reply_nterror(req, status);
+                       goto out;
+               }
+               fname = new_fname;
+       }
+
        ucf_flags = filename_create_ucf_flags(req, create_disposition);
        status = filename_convert(ctx,
                                conn,
@@ -569,7 +656,7 @@ void reply_ntcreate_and_X(struct smb_request *req)
        status = SMB_VFS_CREATE_FILE(
                conn,                                   /* conn */
                req,                                    /* req */
-               root_dir_fid,                           /* root_dir_fid */
+               0,                                      /* root_dir_fid */
                smb_fname,                              /* fname */
                access_mask,                            /* access_mask */
                share_access,                           /* share_access */
@@ -1126,6 +1213,21 @@ static void call_nt_transact_create(connection_struct *conn,
                }
        }
 
+       if (root_dir_fid != 0) {
+               char *new_fname = NULL;
+
+               status = get_relative_fid_filename(conn,
+                                                  req,
+                                                  root_dir_fid,
+                                                  fname,
+                                                  &new_fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       reply_nterror(req, status);
+                       goto out;
+               }
+               fname = new_fname;
+       }
+
        ucf_flags = filename_create_ucf_flags(req, create_disposition);
        status = filename_convert(ctx,
                                conn,
@@ -1237,7 +1339,7 @@ static void call_nt_transact_create(connection_struct *conn,
        status = SMB_VFS_CREATE_FILE(
                conn,                                   /* conn */
                req,                                    /* req */
-               root_dir_fid,                           /* root_dir_fid */
+               0,                                      /* root_dir_fid */
                smb_fname,                              /* fname */
                access_mask,                            /* access_mask */
                share_access,                           /* share_access */