Commit ff2c54a0 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

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: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Cc: stable@vger.kernel.org # 5.11
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 8513222b
Loading
Loading
Loading
Loading
+33 −60
Original line number Diff line number Diff line
@@ -3289,56 +3289,27 @@ 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,
/* 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;
		}
		tmp = *s;
		*s = 0;
		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, path);
		if (rc && rc == -EREMOTE) {
	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)
					break;
				rc = -EREMOTE;
				goto out;
			npath = build_unc_path_to_root(&v, cifs_sb, true);
			smb3_cleanup_fs_context_contents(&v);
		} else {
@@ -3346,16 +3317,18 @@ static int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_contex
			v.prepath = path + 1;
			npath = build_unc_path_to_root(&v, cifs_sb, true);
		}

		if (IS_ERR(npath)) {
			rc = PTR_ERR(npath);
				break;
			goto out;
		}

		kfree(*dfs_path);
		*dfs_path = npath;
		rc = -EREMOTE;
	}
		*s = tmp;
	} while (rc == 0);

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;