cifs: check all path components in resolved dfs target
authorPaulo Alcantara <pc@cjr.nz>
Wed, 24 Feb 2021 23:59:22 +0000 (20:59 -0300)
committerSteve French <stfrench@microsoft.com>
Thu, 25 Feb 2021 18:18:22 +0000 (12:18 -0600)
Handle the case where a resolved target share is like
//server/users/dir, and the user "foo" has no read permission to
access the parent folder "users" but has access to the final path
component "dir".

is_path_remote() already implements that, so call it directly.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Cc: stable@vger.kernel.org # 5.11
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/connect.c

index 820aaaa..1a6d6e1 100644 (file)
@@ -3289,73 +3289,46 @@ static void put_root_ses(struct cifs_ses *ses)
                cifs_put_smb_ses(ses);
 }
 
-/* Check if a path component is remote and then update @dfs_path accordingly */
-static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
-                            const unsigned int xid, struct TCP_Server_Info *server,
-                            struct cifs_tcon *tcon, char **dfs_path)
+/* Set up next dfs prefix path in @dfs_path */
+static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
+                           const unsigned int xid, struct TCP_Server_Info *server,
+                           struct cifs_tcon *tcon, char **dfs_path)
 {
-       char *path, *s;
-       char sep = CIFS_DIR_SEP(cifs_sb), tmp;
-       char *npath;
-       int rc = 0;
-       int added_treename = tcon->Flags & SMB_SHARE_IS_IN_DFS;
-       int skip = added_treename;
+       char *path, *npath;
+       int added_treename = is_tcon_dfs(tcon);
+       int rc;
 
        path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename);
        if (!path)
                return -ENOMEM;
 
-       /*
-        * Walk through the path components in @path and check if they're accessible. In case any of
-        * the components is -EREMOTE, then update @dfs_path with the next DFS referral request path
-        * (NOT including the remaining components).
-        */
-       s = path;
-       do {
-               /* skip separators */
-               while (*s && *s == sep)
-                       s++;
-               if (!*s)
-                       break;
-               /* next separator */
-               while (*s && *s != sep)
-                       s++;
-               /*
-                * if the treename is added, we then have to skip the first
-                * part within the separators
-                */
-               if (skip) {
-                       skip = 0;
-                       continue;
+       rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
+       if (rc == -EREMOTE) {
+               struct smb3_fs_context v = {NULL};
+               /* if @path contains a tree name, skip it in the prefix path */
+               if (added_treename) {
+                       rc = smb3_parse_devname(path, &v);
+                       if (rc)
+                               goto out;
+                       npath = build_unc_path_to_root(&v, cifs_sb, true);
+                       smb3_cleanup_fs_context_contents(&v);
+               } else {
+                       v.UNC = ctx->UNC;
+                       v.prepath = path + 1;
+                       npath = build_unc_path_to_root(&v, cifs_sb, true);
                }
-               tmp = *s;
-               *s = 0;
-               rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, path);
-               if (rc && rc == -EREMOTE) {
-                       struct smb3_fs_context v = {NULL};
-                       /* if @path contains a tree name, skip it in the prefix path */
-                       if (added_treename) {
-                               rc = smb3_parse_devname(path, &v);
-                               if (rc)
-                                       break;
-                               rc = -EREMOTE;
-                               npath = build_unc_path_to_root(&v, cifs_sb, true);
-                               smb3_cleanup_fs_context_contents(&v);
-                       } else {
-                               v.UNC = ctx->UNC;
-                               v.prepath = path + 1;
-                               npath = build_unc_path_to_root(&v, cifs_sb, true);
-                       }
-                       if (IS_ERR(npath)) {
-                               rc = PTR_ERR(npath);
-                               break;
-                       }
-                       kfree(*dfs_path);
-                       *dfs_path = npath;
+
+               if (IS_ERR(npath)) {
+                       rc = PTR_ERR(npath);
+                       goto out;
                }
-               *s = tmp;
-       } while (rc == 0);
 
+               kfree(*dfs_path);
+               *dfs_path = npath;
+               rc = -EREMOTE;
+       }
+
+out:
        kfree(path);
        return rc;
 }
@@ -3441,8 +3414,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
                        put_root_ses(root_ses);
                        set_root_ses(cifs_sb, ses, &root_ses);
                }
-               /* Check for remaining path components and then continue chasing them (-EREMOTE) */
-               rc = check_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
+               /* Get next dfs path and then continue chasing them if -EREMOTE */
+               rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
                /* Prevent recursion on broken link referrals */
                if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
                        rc = -ELOOP;