brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · 0d61b3e Raw
399 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/syscalls.h>3#include <linux/slab.h>4#include <linux/fs.h>5#include <linux/file.h>6#include <linux/mount.h>7#include <linux/namei.h>8#include <linux/exportfs.h>9#include <linux/fs_struct.h>10#include <linux/fsnotify.h>11#include <linux/personality.h>12#include <linux/uaccess.h>13#include <linux/compat.h>14#include "internal.h"15#include "mount.h"16 17static long do_sys_name_to_handle(const struct path *path,18				  struct file_handle __user *ufh,19				  void __user *mnt_id, bool unique_mntid,20				  int fh_flags)21{22	long retval;23	struct file_handle f_handle;24	int handle_dwords, handle_bytes;25	struct file_handle *handle = NULL;26 27	/*28	 * We need to make sure whether the file system support decoding of29	 * the file handle if decodeable file handle was requested.30	 */31	if (!exportfs_can_encode_fh(path->dentry->d_sb->s_export_op, fh_flags))32		return -EOPNOTSUPP;33 34	if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle)))35		return -EFAULT;36 37	if (f_handle.handle_bytes > MAX_HANDLE_SZ)38		return -EINVAL;39 40	handle = kzalloc(struct_size(handle, f_handle, f_handle.handle_bytes),41			 GFP_KERNEL);42	if (!handle)43		return -ENOMEM;44 45	/* convert handle size to multiple of sizeof(u32) */46	handle_dwords = f_handle.handle_bytes >> 2;47 48	/* we ask for a non connectable maybe decodeable file handle */49	retval = exportfs_encode_fh(path->dentry,50				    (struct fid *)handle->f_handle,51				    &handle_dwords, fh_flags);52	handle->handle_type = retval;53	/* convert handle size to bytes */54	handle_bytes = handle_dwords * sizeof(u32);55	handle->handle_bytes = handle_bytes;56	if ((handle->handle_bytes > f_handle.handle_bytes) ||57	    (retval == FILEID_INVALID) || (retval < 0)) {58		/* As per old exportfs_encode_fh documentation59		 * we could return ENOSPC to indicate overflow60		 * But file system returned 255 always. So handle61		 * both the values62		 */63		if (retval == FILEID_INVALID || retval == -ENOSPC)64			retval = -EOVERFLOW;65		/*66		 * set the handle size to zero so we copy only67		 * non variable part of the file_handle68		 */69		handle_bytes = 0;70	} else71		retval = 0;72	/* copy the mount id */73	if (unique_mntid) {74		if (put_user(real_mount(path->mnt)->mnt_id_unique,75			     (u64 __user *) mnt_id))76			retval = -EFAULT;77	} else {78		if (put_user(real_mount(path->mnt)->mnt_id,79			     (int __user *) mnt_id))80			retval = -EFAULT;81	}82	/* copy the handle */83	if (retval != -EFAULT &&84		copy_to_user(ufh, handle,85			     struct_size(handle, f_handle, handle_bytes)))86		retval = -EFAULT;87	kfree(handle);88	return retval;89}90 91/**92 * sys_name_to_handle_at: convert name to handle93 * @dfd: directory relative to which name is interpreted if not absolute94 * @name: name that should be converted to handle.95 * @handle: resulting file handle96 * @mnt_id: mount id of the file system containing the file97 *          (u64 if AT_HANDLE_MNT_ID_UNIQUE, otherwise int)98 * @flag: flag value to indicate whether to follow symlink or not99 *        and whether a decodable file handle is required.100 *101 * @handle->handle_size indicate the space available to store the102 * variable part of the file handle in bytes. If there is not103 * enough space, the field is updated to return the minimum104 * value required.105 */106SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,107		struct file_handle __user *, handle, void __user *, mnt_id,108		int, flag)109{110	struct path path;111	int lookup_flags;112	int fh_flags;113	int err;114 115	if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID |116		     AT_HANDLE_MNT_ID_UNIQUE))117		return -EINVAL;118 119	lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;120	fh_flags = (flag & AT_HANDLE_FID) ? EXPORT_FH_FID : 0;121	if (flag & AT_EMPTY_PATH)122		lookup_flags |= LOOKUP_EMPTY;123	err = user_path_at(dfd, name, lookup_flags, &path);124	if (!err) {125		err = do_sys_name_to_handle(&path, handle, mnt_id,126					    flag & AT_HANDLE_MNT_ID_UNIQUE,127					    fh_flags);128		path_put(&path);129	}130	return err;131}132 133static int get_path_from_fd(int fd, struct path *root)134{135	if (fd == AT_FDCWD) {136		struct fs_struct *fs = current->fs;137		spin_lock(&fs->lock);138		*root = fs->pwd;139		path_get(root);140		spin_unlock(&fs->lock);141	} else {142		struct fd f = fdget(fd);143		if (!fd_file(f))144			return -EBADF;145		*root = fd_file(f)->f_path;146		path_get(root);147		fdput(f);148	}149 150	return 0;151}152 153enum handle_to_path_flags {154	HANDLE_CHECK_PERMS   = (1 << 0),155	HANDLE_CHECK_SUBTREE = (1 << 1),156};157 158struct handle_to_path_ctx {159	struct path root;160	enum handle_to_path_flags flags;161	unsigned int fh_flags;162};163 164static HWJS_SUSPENDS int vfs_dentry_acceptable(void *context, struct dentry *dentry)165{166	struct handle_to_path_ctx *ctx = context;167	struct user_namespace *user_ns = current_user_ns();168	struct dentry *d, *root = ctx->root.dentry;169	struct mnt_idmap *idmap = mnt_idmap(ctx->root.mnt);170	int retval = 0;171 172	if (!root)173		return 1;174 175	/* Old permission model with global CAP_DAC_READ_SEARCH. */176	if (!ctx->flags)177		return 1;178 179	/*180	 * It's racy as we're not taking rename_lock but we're able to ignore181	 * permissions and we just need an approximation whether we were able182	 * to follow a path to the file.183	 *184	 * It's also potentially expensive on some filesystems especially if185	 * there is a deep path.186	 */187	d = dget(dentry);188	while (d != root && !IS_ROOT(d)) {189		struct dentry *parent = dget_parent(d);190 191		/*192		 * We know that we have the ability to override DAC permissions193		 * as we've verified this earlier via CAP_DAC_READ_SEARCH. But194		 * we also need to make sure that there aren't any unmapped195		 * inodes in the path that would prevent us from reaching the196		 * file.197		 */198		if (!privileged_wrt_inode_uidgid(user_ns, idmap,199						 d_inode(parent))) {200			dput(d);201			dput(parent);202			return retval;203		}204 205		dput(d);206		d = parent;207	}208 209	if (!(ctx->flags & HANDLE_CHECK_SUBTREE) || d == root)210		retval = 1;211	WARN_ON_ONCE(d != root && d != root->d_sb->s_root);212	dput(d);213	return retval;214}215 216static int do_handle_to_path(struct file_handle *handle, struct path *path,217			     struct handle_to_path_ctx *ctx)218{219	int handle_dwords;220	struct vfsmount *mnt = ctx->root.mnt;221 222	/* change the handle size to multiple of sizeof(u32) */223	handle_dwords = handle->handle_bytes >> 2;224	path->dentry = exportfs_decode_fh_raw(mnt,225					  (struct fid *)handle->f_handle,226					  handle_dwords, handle->handle_type,227					  ctx->fh_flags,228					  vfs_dentry_acceptable, ctx);229	if (IS_ERR_OR_NULL(path->dentry)) {230		if (path->dentry == ERR_PTR(-ENOMEM))231			return -ENOMEM;232		return -ESTALE;233	}234	path->mnt = mntget(mnt);235	return 0;236}237 238/*239 * Allow relaxed permissions of file handles if the caller has the240 * ability to mount the filesystem or create a bind-mount of the241 * provided @mountdirfd.242 *243 * In both cases the caller may be able to get an unobstructed way to244 * the encoded file handle. If the caller is only able to create a245 * bind-mount we need to verify that there are no locked mounts on top246 * of it that could prevent us from getting to the encoded file.247 *248 * In principle, locked mounts can prevent the caller from mounting the249 * filesystem but that only applies to procfs and sysfs neither of which250 * support decoding file handles.251 */252static inline bool may_decode_fh(struct handle_to_path_ctx *ctx,253				 unsigned int o_flags)254{255	struct path *root = &ctx->root;256 257	/*258	 * Restrict to O_DIRECTORY to provide a deterministic API that avoids a259	 * confusing api in the face of disconnected non-dir dentries.260	 *261	 * There's only one dentry for each directory inode (VFS rule)...262	 */263	if (!(o_flags & O_DIRECTORY))264		return false;265 266	if (ns_capable(root->mnt->mnt_sb->s_user_ns, CAP_SYS_ADMIN))267		ctx->flags = HANDLE_CHECK_PERMS;268	else if (is_mounted(root->mnt) &&269		 ns_capable(real_mount(root->mnt)->mnt_ns->user_ns,270			    CAP_SYS_ADMIN) &&271		 !has_locked_children(real_mount(root->mnt), root->dentry))272		ctx->flags = HANDLE_CHECK_PERMS | HANDLE_CHECK_SUBTREE;273	else274		return false;275 276	/* Are we able to override DAC permissions? */277	if (!ns_capable(current_user_ns(), CAP_DAC_READ_SEARCH))278		return false;279 280	ctx->fh_flags = EXPORT_FH_DIR_ONLY;281	return true;282}283 284static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,285		   struct path *path, unsigned int o_flags)286{287	int retval = 0;288	struct file_handle f_handle;289	struct file_handle *handle = NULL;290	struct handle_to_path_ctx ctx = {};291 292	retval = get_path_from_fd(mountdirfd, &ctx.root);293	if (retval)294		goto out_err;295 296	if (!capable(CAP_DAC_READ_SEARCH) && !may_decode_fh(&ctx, o_flags)) {297		retval = -EPERM;298		goto out_path;299	}300 301	if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {302		retval = -EFAULT;303		goto out_path;304	}305	if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||306	    (f_handle.handle_bytes == 0)) {307		retval = -EINVAL;308		goto out_path;309	}310	handle = kmalloc(struct_size(handle, f_handle, f_handle.handle_bytes),311			 GFP_KERNEL);312	if (!handle) {313		retval = -ENOMEM;314		goto out_path;315	}316	/* copy the full handle */317	*handle = f_handle;318	if (copy_from_user(&handle->f_handle,319			   &ufh->f_handle,320			   f_handle.handle_bytes)) {321		retval = -EFAULT;322		goto out_handle;323	}324 325	retval = do_handle_to_path(handle, path, &ctx);326 327out_handle:328	kfree(handle);329out_path:330	path_put(&ctx.root);331out_err:332	return retval;333}334 335static HWJS_SUSPENDS long do_handle_open(int mountdirfd, struct file_handle __user *ufh,336			   int open_flag)337{338	long retval = 0;339	struct path path;340	struct file *file;341	int fd;342 343	retval = handle_to_path(mountdirfd, ufh, &path, open_flag);344	if (retval)345		return retval;346 347	fd = get_unused_fd_flags(open_flag);348	if (fd < 0) {349		path_put(&path);350		return fd;351	}352	file = file_open_root(&path, "", open_flag, 0);353	if (IS_ERR(file)) {354		put_unused_fd(fd);355		retval =  PTR_ERR(file);356	} else {357		retval = fd;358		fd_install(fd, file);359	}360	path_put(&path);361	return retval;362}363 364/**365 * sys_open_by_handle_at: Open the file handle366 * @mountdirfd: directory file descriptor367 * @handle: file handle to be opened368 * @flags: open flags.369 *370 * @mountdirfd indicate the directory file descriptor371 * of the mount point. file handle is decoded relative372 * to the vfsmount pointed by the @mountdirfd. @flags373 * value is same as the open(2) flags.374 */375SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,376		struct file_handle __user *, handle,377		int, flags)378{379	long ret;380 381	if (force_o_largefile())382		flags |= O_LARGEFILE;383 384	ret = do_handle_open(mountdirfd, handle, flags);385	return ret;386}387 388#ifdef CONFIG_COMPAT389/*390 * Exactly like fs/open.c:sys_open_by_handle_at(), except that it391 * doesn't set the O_LARGEFILE flag.392 */393COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,394			     struct file_handle __user *, handle, int, flags)395{396	return do_handle_open(mountdirfd, handle, flags);397}398#endif399