cifs: check all path components in resolved dfs target

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>
This commit is contained in:
Paulo Alcantara 2021-02-24 20:59:22 -03:00 committed by Steve French
parent 8513222b9e
commit ff2c54a040

View 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;
}
*s = tmp;
} while (rc == 0);
if (IS_ERR(npath)) {
rc = PTR_ERR(npath);
goto out;
}
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;