s3: smbd: Add new version of dfs_path_lookup() that uses filename_convert_dirfsp().
authorJeremy Allison <jra@samba.org>
Tue, 9 Aug 2022 19:07:30 +0000 (12:07 -0700)
committerStefan Metzmacher <metze@samba.org>
Tue, 16 Aug 2022 18:27:13 +0000 (18:27 +0000)
Commented out as not yet used but it's easier to see the
new logic this way.

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

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15146
(cherry picked from commit 22d4f62537199d9454be312a546e251f04022497)

source3/smbd/msdfs.c

index d48c5d6c906afd91920a4ce21b77d9126004b0f5..ab7f9f0ad78876eaa2107bf262908d8fe74187f9 100644 (file)
@@ -713,6 +713,267 @@ bool is_msdfs_link(struct files_struct *dirfsp,
        return (NT_STATUS_IS_OK(status));
 }
 
+#if 0
+/*****************************************************************
+ Used by other functions to decide if a dfs path is remote,
+ and to get the list of referred locations for that remote path.
+
+ consumedcntp: how much of the dfs path is being redirected. the client
+ should try the remaining path on the redirected server.
+*****************************************************************/
+
+static NTSTATUS dfs_path_lookup(TALLOC_CTX *ctx,
+               connection_struct *conn,
+               const char *dfspath, /* Incoming complete dfs path */
+               const char *reqpath, /* Parsed out remaining path. */
+               uint32_t ucf_flags,
+               size_t *consumedcntp,
+               struct referral **ppreflist,
+               size_t *preferral_count)
+{
+       NTSTATUS status;
+       struct smb_filename *parent_smb_fname = NULL;
+       struct smb_filename *smb_fname_rel = NULL;
+       NTTIME twrp = 0;
+       char *local_pathname = NULL;
+       char *last_component = NULL;
+       char *atname = NULL;
+       size_t removed_components = 0;
+       bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
+       char *p = NULL;
+       char *canon_dfspath = NULL;
+
+       DBG_DEBUG("Conn path = %s reqpath = %s\n", conn->connectpath, reqpath);
+
+       local_pathname = talloc_strdup(ctx, reqpath);
+       if (local_pathname == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       /* We know reqpath isn't a DFS path. */
+       ucf_flags &= ~UCF_DFS_PATHNAME;
+
+       if (ucf_flags & UCF_GMT_PATHNAME) {
+               extract_snapshot_token(local_pathname, &twrp);
+               ucf_flags &= ~UCF_GMT_PATHNAME;
+       }
+
+       /*
+        * We should have been given a DFS path to resolve.
+        * This should return NT_STATUS_PATH_NOT_COVERED.
+        *
+        * Do a pathname walk, stripping off components
+        * until we get NT_STATUS_OK instead of
+        * NT_STATUS_PATH_NOT_COVERED.
+        *
+        * Fail on any other error.
+        */
+
+       for (;;) {
+               TALLOC_CTX *frame = NULL;
+               struct files_struct *dirfsp = NULL;
+               struct smb_filename *smb_fname_walk = NULL;
+
+               TALLOC_FREE(parent_smb_fname);
+
+               /*
+                * Use a local stackframe as filename_convert_dirfsp()
+                * opens handles on the last two components in the path.
+                * Allow these to be freed as we step back through
+                * the local_pathname.
+                */
+               frame = talloc_stackframe();
+               status = filename_convert_dirfsp(frame,
+                                                conn,
+                                                local_pathname,
+                                                ucf_flags,
+                                                twrp,
+                                                &dirfsp,
+                                                &smb_fname_walk);
+               /* If we got a name, save it. */
+               if (smb_fname_walk != NULL) {
+                       parent_smb_fname = talloc_move(ctx, &smb_fname_walk);
+               }
+               TALLOC_FREE(frame);
+
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
+                       /*
+                        * For any other status than NT_STATUS_PATH_NOT_COVERED
+                        * (including NT_STATUS_OK) we exit the walk.
+                        * If it's an error we catch it outside the loop.
+                        */
+                       break;
+               }
+
+               /* Step back one component and save it off as last_component. */
+               TALLOC_FREE(last_component);
+               p = strrchr(local_pathname, '/');
+               if (p == NULL) {
+                       /*
+                        * We removed all components.
+                        * Go around once more to make
+                        * sure we can open the root '\0'.
+                        */
+                       last_component = talloc_strdup(ctx, local_pathname);
+                       *local_pathname = '\0';
+               } else {
+                       last_component = talloc_strdup(ctx, p+1);
+                       *p = '\0';
+               }
+               if (last_component == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+               /* Integer wrap check. */
+               if (removed_components + 1 < removed_components) {
+                       status = NT_STATUS_INVALID_PARAMETER;
+                       goto out;
+               }
+               removed_components++;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("dfspath = %s. reqpath = %s. Error %s.\n",
+                       dfspath,
+                       reqpath,
+                       nt_errstr(status));
+               goto out;
+       }
+
+       if (parent_smb_fname->fsp == NULL) {
+               /* Unable to open parent. */
+               DBG_DEBUG("dfspath = %s. reqpath = %s. "
+                         "Unable to open parent directory (%s).\n",
+                       dfspath,
+                       reqpath,
+                       smb_fname_str_dbg(parent_smb_fname));
+               status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+               goto out;
+       }
+
+       if (removed_components == 0) {
+               /*
+                * We never got NT_STATUS_PATH_NOT_COVERED.
+                * There was no DFS redirect.
+                */
+               DBG_DEBUG("dfspath = %s. reqpath = %s. "
+                       "No removed components.\n",
+                       dfspath,
+                       reqpath);
+               status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+               goto out;
+       }
+
+       /*
+        * One of the removed_components was the MSDFS link
+        * at the end. We need to count this in the resolved
+        * path below, so remove one from removed_components.
+        */
+       removed_components--;
+
+       /*
+        * Now parent_smb_fname->fsp is the parent directory dirfsp,
+        * last_component is the untranslated MS-DFS link name.
+        * Search for it in the parent directory to get the real
+        * filename on disk.
+        */
+       status = get_real_filename_at(parent_smb_fname->fsp,
+                                     last_component,
+                                     ctx,
+                                     &atname);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("dfspath = %s. reqpath = %s "
+                       "get_real_filename_at(%s, %s) error (%s)\n",
+                       dfspath,
+                       reqpath,
+                       smb_fname_str_dbg(parent_smb_fname),
+                       last_component,
+                       nt_errstr(status));
+               goto out;
+       }
+
+        smb_fname_rel = synthetic_smb_fname(ctx,
+                               atname,
+                               NULL,
+                               NULL,
+                               twrp,
+                               posix ? SMB_FILENAME_POSIX_PATH : 0);
+       if (smb_fname_rel == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       /* Get the referral to return. */
+       status = SMB_VFS_READ_DFS_PATHAT(conn,
+                                        ctx,
+                                        parent_smb_fname->fsp,
+                                        smb_fname_rel,
+                                        ppreflist,
+                                        preferral_count);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("dfspath = %s. reqpath = %s. "
+                       "SMB_VFS_READ_DFS_PATHAT(%s, %s) error (%s)\n",
+                       dfspath,
+                       reqpath,
+                       smb_fname_str_dbg(parent_smb_fname),
+                       smb_fname_str_dbg(smb_fname_rel),
+                       nt_errstr(status));
+               goto out;
+       }
+
+       /*
+        * Now we must work out how much of the
+        * given pathname we consumed.
+        */
+       canon_dfspath = talloc_strdup(ctx, dfspath);
+       if (!canon_dfspath) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+       /* Canonicalize the raw dfspath. */
+       string_replace(canon_dfspath, '\\', '/');
+
+       /*
+        * reqpath comes out of parse_dfs_path(), so it has
+        * no trailing backslash. Make sure that canon_dfspath hasn't either.
+        */
+       trim_char(canon_dfspath, 0, '/');
+
+       DBG_DEBUG("Unconsumed path: %s\n", canon_dfspath);
+
+       while (removed_components > 0) {
+               p = strrchr(canon_dfspath, '/');
+               if (p != NULL) {
+                       *p = '\0';
+               }
+               removed_components--;
+               if (p == NULL && removed_components != 0) {
+                       DBG_ERR("Component missmatch. path = %s, "
+                               "%zu components left\n",
+                               canon_dfspath,
+                               removed_components);
+                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                       goto out;
+               }
+       }
+       *consumedcntp = strlen(canon_dfspath);
+       DBG_DEBUG("Path consumed: %s (%zu)\n", canon_dfspath, *consumedcntp);
+       status = NT_STATUS_OK;
+
+  out:
+
+       TALLOC_FREE(parent_smb_fname);
+       TALLOC_FREE(local_pathname);
+       TALLOC_FREE(last_component);
+       TALLOC_FREE(atname);
+       TALLOC_FREE(smb_fname_rel);
+       TALLOC_FREE(canon_dfspath);
+       return status;
+}
+#endif
+
 /*****************************************************************
  Used by other functions to decide if a dfs path is remote,
  and to get the list of referred locations for that remote path.