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