Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a6b5058f authored by Aurelien Aptel's avatar Aurelien Aptel Committed by Steve French
Browse files

fs/cifs: make share unaccessible at root level mountable



if, when mounting //HOST/share/sub/dir/foo we can query /sub/dir/foo but
not any of the path components above:

- store the /sub/dir/foo prefix in the cifs super_block info
- in the superblock, set root dentry to the subpath dentry (instead of
  the share root)
- set a flag in the superblock to remember it
- use prefixpath when building path from a dentry

fixes bso#8950

Signed-off-by: default avatarAurelien Aptel <aaptel@suse.com>
CC: Stable <stable@vger.kernel.org>
Reviewed-by: default avatarPavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent bd975d1e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -46,6 +46,9 @@
#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
#define CIFS_MOUNT_MAP_SFM_CHR	0x800000 /* SFM/MAC mapping for illegal chars */
#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
					      * root mountable
					      */

struct cifs_sb_info {
	struct rb_root tlink_tree;
@@ -67,5 +70,6 @@ struct cifs_sb_info {
	struct backing_dev_info bdi;
	struct delayed_work prune_tlinks;
	struct rcu_head rcu;
	char *prepath;
};
#endif				/* _CIFS_FS_SB_H */
+13 −1
Original line number Diff line number Diff line
@@ -689,6 +689,14 @@ cifs_do_mount(struct file_system_type *fs_type,
		goto out_cifs_sb;
	}

	if (volume_info->prepath) {
		cifs_sb->prepath = kstrdup(volume_info->prepath, GFP_KERNEL);
		if (cifs_sb->prepath == NULL) {
			root = ERR_PTR(-ENOMEM);
			goto out_cifs_sb;
		}
	}

	cifs_setup_cifs_sb(volume_info, cifs_sb);

	rc = cifs_mount(cifs_sb, volume_info);
@@ -727,7 +735,11 @@ cifs_do_mount(struct file_system_type *fs_type,
		sb->s_flags |= MS_ACTIVE;
	}

	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
		root = dget(sb->s_root);
	else
		root = cifs_get_root(volume_info, sb);

	if (IS_ERR(root))
		goto out_super;

+49 −0
Original line number Diff line number Diff line
@@ -3485,6 +3485,44 @@ cifs_get_volume_info(char *mount_data, const char *devname)
	return volume_info;
}

static int
cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
					unsigned int xid,
					struct cifs_tcon *tcon,
					struct cifs_sb_info *cifs_sb,
					char *full_path)
{
	int rc;
	char *s;
	char sep, tmp;

	sep = CIFS_DIR_SEP(cifs_sb);
	s = full_path;

	rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
	while (rc == 0) {
		/* skip separators */
		while (*s == sep)
			s++;
		if (!*s)
			break;
		/* next separator */
		while (*s && *s != sep)
			s++;

		/*
		 * temporarily null-terminate the path at the end of
		 * the current component
		 */
		tmp = *s;
		*s = 0;
		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
						     full_path);
		*s = tmp;
	}
	return rc;
}

int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{
@@ -3622,6 +3660,16 @@ cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
			kfree(full_path);
			goto mount_fail_check;
		}

		rc = cifs_are_all_path_components_accessible(server,
							     xid, tcon, cifs_sb,
							     full_path);
		if (rc != 0) {
			cifs_dbg(VFS, "cannot query dirs between root and final path, "
				 "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
			rc = 0;
		}
		kfree(full_path);
	}

@@ -3891,6 +3939,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)

	bdi_destroy(&cifs_sb->bdi);
	kfree(cifs_sb->mountdata);
	kfree(cifs_sb->prepath);
	call_rcu(&cifs_sb->rcu, delayed_free);
}

+18 −2
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ build_path_from_dentry(struct dentry *direntry)
	struct dentry *temp;
	int namelen;
	int dfsplen;
	int pplen = 0;
	char *full_path;
	char dirsep;
	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
@@ -95,8 +96,12 @@ build_path_from_dentry(struct dentry *direntry)
		dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
	else
		dfsplen = 0;

	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
		pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;

cifs_bp_rename_retry:
	namelen = dfsplen;
	namelen = dfsplen + pplen;
	seq = read_seqbegin(&rename_lock);
	rcu_read_lock();
	for (temp = direntry; !IS_ROOT(temp);) {
@@ -137,7 +142,7 @@ build_path_from_dentry(struct dentry *direntry)
		}
	}
	rcu_read_unlock();
	if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
	if (namelen != dfsplen + pplen || read_seqretry(&rename_lock, seq)) {
		cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
			 namelen, dfsplen);
		/* presumably this is only possible if racing with a rename
@@ -153,6 +158,17 @@ build_path_from_dentry(struct dentry *direntry)
	   those safely to '/' if any are found in the middle of the prepath */
	/* BB test paths to Windows with '/' in the midst of prepath */

	if (pplen) {
		int i;

		cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
		memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
		full_path[dfsplen] = '\\';
		for (i = 0; i < pplen-1; i++)
			if (full_path[dfsplen+1+i] == '/')
				full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
	}

	if (dfsplen) {
		strncpy(full_path, tcon->treeName, dfsplen);
		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
+20 −2
Original line number Diff line number Diff line
@@ -1002,10 +1002,26 @@ struct inode *cifs_root_iget(struct super_block *sb)
	struct inode *inode = NULL;
	long rc;
	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
	char *path = NULL;
	int len;

	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
	    && cifs_sb->prepath) {
		len = strlen(cifs_sb->prepath);
		path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
		if (path == NULL)
			return ERR_PTR(-ENOMEM);
		path[0] = '/';
		memcpy(path+1, cifs_sb->prepath, len);
	} else {
		path = kstrdup("", GFP_KERNEL);
		if (path == NULL)
			return ERR_PTR(-ENOMEM);
	}

	xid = get_xid();
	if (tcon->unix_ext) {
		rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
		rc = cifs_get_inode_info_unix(&inode, path, sb, xid);
		/* some servers mistakenly claim POSIX support */
		if (rc != -EOPNOTSUPP)
			goto iget_no_retry;
@@ -1013,7 +1029,8 @@ struct inode *cifs_root_iget(struct super_block *sb)
		tcon->unix_ext = false;
	}

	rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
	convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
	rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL);

iget_no_retry:
	if (!inode) {
@@ -1042,6 +1059,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
	}

out:
	kfree(path);
	/* can not call macro free_xid here since in a void func
	 * TODO: This is no longer true
	 */