474 lines · 13.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/syscalls.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/mount.h>#include <linux/namei.h>#include <linux/exportfs.h>#include <linux/fs_struct.h>#include <linux/fsnotify.h>#include <linux/personality.h>#include <linux/uaccess.h>#include <linux/compat.h>#include <linux/nsfs.h>#include "internal.h"#include "mount.h" static long do_sys_name_to_handle(const struct path *path,				  struct file_handle __user *ufh,				  void __user *mnt_id, bool unique_mntid,				  int fh_flags){	long retval;	struct file_handle f_handle;	int handle_dwords, handle_bytes;	struct file_handle *handle = NULL; 	/*	 * We need to make sure whether the file system support decoding of	 * the file handle if decodeable file handle was requested.	 */	if (!exportfs_can_encode_fh(path->dentry->d_sb->s_export_op, fh_flags))		return -EOPNOTSUPP; 	/*	 * A request to encode a connectable handle for a disconnected dentry	 * is unexpected since AT_EMPTY_PATH is not allowed.	 */	if (fh_flags & EXPORT_FH_CONNECTABLE &&	    WARN_ON(path->dentry->d_flags & DCACHE_DISCONNECTED))		return -EINVAL; 	if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))		return -EFAULT; 	if (f_handle.handle_bytes > MAX_HANDLE_SZ)		return -EINVAL; 	handle = kzalloc_flex(*handle, f_handle, f_handle.handle_bytes);	if (!handle)		return -ENOMEM; 	/* convert handle size to multiple of sizeof(u32) */	handle_dwords = f_handle.handle_bytes >> 2; 	/* Encode a possibly decodeable/connectable file handle */	retval = exportfs_encode_fh(path->dentry,				    (struct fid *)handle->f_handle,				    &handle_dwords, fh_flags);	handle->handle_type = retval;	/* convert handle size to bytes */	handle_bytes = handle_dwords * sizeof(u32);	handle->handle_bytes = handle_bytes;	if ((handle->handle_bytes > f_handle.handle_bytes) ||	    (retval == FILEID_INVALID) || (retval < 0)) {		/* As per old exportfs_encode_fh documentation		 * we could return ENOSPC to indicate overflow		 * But file system returned 255 always. So handle		 * both the values		 */		if (retval == FILEID_INVALID || retval == -ENOSPC)			retval = -EOVERFLOW;		/*		 * set the handle size to zero so we copy only		 * non variable part of the file_handle		 */		handle_bytes = 0;	} else {		/*		 * When asked to encode a connectable file handle, encode this		 * property in the file handle itself, so that we later know		 * how to decode it.		 * For sanity, also encode in the file handle if the encoded		 * object is a directory and verify this during decode, because		 * decoding directory file handles is quite different than		 * decoding connectable non-directory file handles.		 */		if (fh_flags & EXPORT_FH_CONNECTABLE) {			handle->handle_type |= FILEID_IS_CONNECTABLE;			if (d_is_dir(path->dentry))				handle->handle_type |= FILEID_IS_DIR;		}		retval = 0;	}	/* copy the mount id */	if (unique_mntid) {		if (put_user(real_mount(path->mnt)->mnt_id_unique,			     (u64 __user *) mnt_id))			retval = -EFAULT;	} else {		if (put_user(real_mount(path->mnt)->mnt_id,			     (int __user *) mnt_id))			retval = -EFAULT;	}	/* copy the handle */	if (retval != -EFAULT &&		copy_to_user(ufh, handle,			     struct_size(handle, f_handle, handle_bytes)))		retval = -EFAULT;	kfree(handle);	return retval;} /** * sys_name_to_handle_at: convert name to handle * @dfd: directory relative to which name is interpreted if not absolute * @name: name that should be converted to handle. * @handle: resulting file handle * @mnt_id: mount id of the file system containing the file *          (u64 if AT_HANDLE_MNT_ID_UNIQUE, otherwise int) * @flag: flag value to indicate whether to follow symlink or not *        and whether a decodable file handle is required. * * @handle->handle_size indicate the space available to store the * variable part of the file handle in bytes. If there is not * enough space, the field is updated to return the minimum * value required. */SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,		struct file_handle __user *, handle, void __user *, mnt_id,		int, flag){	struct path path;	int lookup_flags;	int fh_flags = 0;	int err; 	if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID |		     AT_HANDLE_MNT_ID_UNIQUE | AT_HANDLE_CONNECTABLE))		return -EINVAL; 	/*	 * AT_HANDLE_FID means there is no intention to decode file handle	 * AT_HANDLE_CONNECTABLE means there is an intention to decode a	 * connected fd (with known path), so these flags are conflicting.	 * AT_EMPTY_PATH could be used along with a dfd that refers to a	 * disconnected non-directory, which cannot be used to encode a	 * connectable file handle, because its parent is unknown.	 */	if (flag & AT_HANDLE_CONNECTABLE &&	    flag & (AT_HANDLE_FID | AT_EMPTY_PATH))		return -EINVAL;	else if (flag & AT_HANDLE_FID)		fh_flags |= EXPORT_FH_FID;	else if (flag & AT_HANDLE_CONNECTABLE)		fh_flags |= EXPORT_FH_CONNECTABLE; 	lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;	CLASS(filename_uflags, filename)(name, flag);	err = filename_lookup(dfd, filename, lookup_flags, &path, NULL);	if (!err) {		err = do_sys_name_to_handle(&path, handle, mnt_id,					    flag & AT_HANDLE_MNT_ID_UNIQUE,					    fh_flags);		path_put(&path);	}	return err;} static int get_path_anchor(int fd, struct path *root){	if (fd >= 0) {		CLASS(fd, f)(fd);		if (fd_empty(f))			return -EBADF;		*root = fd_file(f)->f_path;		path_get(root);		return 0;	} 	if (fd == AT_FDCWD) {		get_fs_pwd(current->fs, root);		return 0;	} 	if (fd == FD_PIDFS_ROOT) {		pidfs_get_root(root);		return 0;	} 	if (fd == FD_NSFS_ROOT) {		nsfs_get_root(root);		return 0;	} 	return -EBADF;} static int vfs_dentry_acceptable(void *context, struct dentry *dentry){	struct handle_to_path_ctx *ctx = context;	struct user_namespace *user_ns = current_user_ns();	struct dentry *d, *root = ctx->root.dentry;	struct mnt_idmap *idmap = mnt_idmap(ctx->root.mnt);	int retval = 0; 	if (!root)		return 1; 	/* Old permission model with global CAP_DAC_READ_SEARCH. */	if (!ctx->flags)		return 1; 	/*	 * Verify that the decoded dentry itself has a valid id mapping.	 * In case the decoded dentry is the mountfd root itself, this	 * verifies that the mountfd inode itself has a valid id mapping.	 */	if (!privileged_wrt_inode_uidgid(user_ns, idmap, d_inode(dentry)))		return 0; 	/*	 * It's racy as we're not taking rename_lock but we're able to ignore	 * permissions and we just need an approximation whether we were able	 * to follow a path to the file.	 *	 * It's also potentially expensive on some filesystems especially if	 * there is a deep path.	 */	d = dget(dentry);	while (d != root && !IS_ROOT(d)) {		struct dentry *parent = dget_parent(d); 		/*		 * We know that we have the ability to override DAC permissions		 * as we've verified this earlier via CAP_DAC_READ_SEARCH. But		 * we also need to make sure that there aren't any unmapped		 * inodes in the path that would prevent us from reaching the		 * file.		 */		if (!privileged_wrt_inode_uidgid(user_ns, idmap,						 d_inode(parent))) {			dput(d);			dput(parent);			return retval;		} 		dput(d);		d = parent;	} 	if (!(ctx->flags & HANDLE_CHECK_SUBTREE) || d == root)		retval = 1;	/*	 * exportfs_decode_fh_raw() does not call acceptable() callback with	 * a disconnected directory dentry, so we should have reached either	 * mount fd directory or sb root.	 */	if (ctx->fh_flags & EXPORT_FH_DIR_ONLY)		WARN_ON_ONCE(d != root && d != root->d_sb->s_root);	dput(d);	return retval;} static int do_handle_to_path(struct file_handle *handle, struct path *path,			     struct handle_to_path_ctx *ctx){	int handle_dwords;	struct vfsmount *mnt = ctx->root.mnt;	struct dentry *dentry; 	/* change the handle size to multiple of sizeof(u32) */	handle_dwords = handle->handle_bytes >> 2;	dentry = exportfs_decode_fh_raw(mnt, (struct fid *)handle->f_handle,					handle_dwords, handle->handle_type,					ctx->fh_flags, vfs_dentry_acceptable,					ctx);	if (IS_ERR_OR_NULL(dentry)) {		if (dentry == ERR_PTR(-ENOMEM))			return -ENOMEM;		return -ESTALE;	}	path->dentry = dentry;	path->mnt = mntget(mnt);	return 0;} static bool capable_wrt_mount(struct mount *mount){	struct mnt_namespace *mnt_ns; 	/*	 * For ->mnt_ns access.	 * The following READ_ONCE() is semantically rcu_dereference().	 */	guard(rcu)();	mnt_ns = READ_ONCE(mount->mnt_ns);	return mnt_ns && ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN);} static inline int may_decode_fh(struct handle_to_path_ctx *ctx,				unsigned int o_flags){	struct path *root = &ctx->root; 	if (capable(CAP_DAC_READ_SEARCH))		return 0; 	/*	 * Allow relaxed permissions of file handles if the caller has	 * the ability to mount the filesystem or create a bind-mount of	 * the provided @mountdirfd.	 *	 * In both cases the caller may be able to get an unobstructed	 * way to the encoded file handle. If the caller is only able to	 * create a bind-mount we need to verify that there are no	 * locked mounts on top of it that could prevent us from getting	 * to the encoded file.	 *	 * In principle, locked mounts can prevent the caller from	 * mounting the filesystem but that only applies to procfs and	 * sysfs neither of which support decoding file handles.	 *	 * Restrict to O_DIRECTORY to provide a deterministic API that	 * avoids a confusing api in the face of disconnected non-dir	 * dentries.	 *	 * There's only one dentry for each directory inode (VFS rule)...	 */	if (!(o_flags & O_DIRECTORY))		return -EPERM; 	if (ns_capable(root->mnt->mnt_sb->s_user_ns, CAP_SYS_ADMIN))		ctx->flags = HANDLE_CHECK_PERMS;	else if (is_mounted(root->mnt) &&		 capable_wrt_mount(real_mount(root->mnt)) &&		 !has_locked_children(real_mount(root->mnt), root->dentry))		ctx->flags = HANDLE_CHECK_PERMS | HANDLE_CHECK_SUBTREE;	else		return -EPERM; 	/* Are we able to override DAC permissions? */	if (!ns_capable(current_user_ns(), CAP_DAC_READ_SEARCH))		return -EPERM; 	ctx->fh_flags = EXPORT_FH_DIR_ONLY;	return 0;} static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,		   struct path *path, unsigned int o_flags){	int retval = 0;	struct file_handle f_handle;	struct file_handle *handle __free(kfree) = NULL;	struct handle_to_path_ctx ctx = {};	const struct export_operations *eops; 	if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))		return -EFAULT; 	if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||	    (f_handle.handle_bytes == 0))		return -EINVAL; 	if (f_handle.handle_type < 0 ||	    FILEID_USER_FLAGS(f_handle.handle_type) & ~FILEID_VALID_USER_FLAGS)		return -EINVAL; 	retval = get_path_anchor(mountdirfd, &ctx.root);	if (retval)		return retval; 	eops = ctx.root.mnt->mnt_sb->s_export_op;	if (eops && eops->permission)		retval = eops->permission(&ctx, o_flags);	else		retval = may_decode_fh(&ctx, o_flags);	if (retval)		goto out_path; 	handle = kmalloc_flex(*handle, f_handle, f_handle.handle_bytes);	if (!handle) {		retval = -ENOMEM;		goto out_path;	}	/* copy the full handle */	*handle = f_handle;	if (copy_from_user(&handle->f_handle,			   &ufh->f_handle,			   f_handle.handle_bytes)) {		retval = -EFAULT;		goto out_path;	} 	/*	 * If handle was encoded with AT_HANDLE_CONNECTABLE, verify that we	 * are decoding an fd with connected path, which is accessible from	 * the mount fd path.	 */	if (f_handle.handle_type & FILEID_IS_CONNECTABLE) {		ctx.fh_flags |= EXPORT_FH_CONNECTABLE;		ctx.flags |= HANDLE_CHECK_SUBTREE;	}	if (f_handle.handle_type & FILEID_IS_DIR)		ctx.fh_flags |= EXPORT_FH_DIR_ONLY;	/* Filesystem code should not be exposed to user flags */	handle->handle_type &= ~FILEID_USER_FLAGS_MASK;	retval = do_handle_to_path(handle, path, &ctx); out_path:	path_put(&ctx.root);	return retval;} static struct file *file_open_handle(struct path *path, int open_flag){	const struct export_operations *eops; 	eops = path->mnt->mnt_sb->s_export_op;	if (eops->open)		return eops->open(path, open_flag); 	return file_open_root(path, "", open_flag, 0);} static long do_handle_open(int mountdirfd, struct file_handle __user *ufh,			   int open_flag){	long retval;	struct path path __free(path_put) = {}; 	retval = handle_to_path(mountdirfd, ufh, &path, open_flag);	if (retval)		return retval; 	return FD_ADD(open_flag, file_open_handle(&path, open_flag));} /** * sys_open_by_handle_at: Open the file handle * @mountdirfd: directory file descriptor * @handle: file handle to be opened * @flags: open flags. * * @mountdirfd indicate the directory file descriptor * of the mount point. file handle is decoded relative * to the vfsmount pointed by the @mountdirfd. @flags * value is same as the open(2) flags. */SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,		struct file_handle __user *, handle,		int, flags){	long ret; 	if (force_o_largefile())		flags |= O_LARGEFILE; 	ret = do_handle_open(mountdirfd, handle, flags);	return ret;} #ifdef CONFIG_COMPAT/* * Exactly like fs/open.c:sys_open_by_handle_at(), except that it * doesn't set the O_LARGEFILE flag. */COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,			     struct file_handle __user *, handle, int, flags){	return do_handle_open(mountdirfd, handle, flags);}#endif