diff --git a/fs/fhandle.c b/fs/fhandle.c index ec6feeccc276..17c066be0a5d 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -14,9 +14,40 @@ #include "internal.h" #include "mount.h" +static int fhandle_add_mac(struct file_handle *handle, size_t handle_alloc) +{ + struct files_struct *files = current->files; + struct fhandle_mac_key *key = READ_ONCE(files->key), *old; + size_t mac_size = 8; /* or whatever */ + + handle->handle_bytes += mac_size; + + if (handle->handle_type == FILEID_INVALID || + handle->handle_bytes > handle_alloc) + return FILEID_INVALID; + + if (!key) { + key = /* generate_key */; + if (!key) + return -ENOMEM; + old = cmpxchg(&files->key, NULL, key); + if (old) { + /* race */ + kfree(key); + key = old; + } + } + + /* add MAC to the end of the current handle using key */ + + handle->handle_type |= FILEID_MAC: + + return 0; +} + static long do_sys_name_to_handle(struct path *path, struct file_handle __user *ufh, - int __user *mnt_id) + int __user *mnt_id, bool mac) { long retval; struct file_handle f_handle; @@ -49,12 +80,20 @@ static long do_sys_name_to_handle(struct path *path, retval = exportfs_encode_fh(path->dentry, (struct fid *)handle->f_handle, &handle_dwords, 0); + if (retval == -ENOSPC) + retval = FILEID_INVALID; handle->handle_type = retval; /* convert handle size to bytes */ handle_bytes = handle_dwords * sizeof(u32); handle->handle_bytes = handle_bytes; + if (mac) { + retval = fhandle_add_mac(handle, f_handle.handle_bytes); + if (retval < 0) + goto out; + handle_bytes = handle->handle_bytes; + } if ((handle->handle_bytes > f_handle.handle_bytes) || - (retval == FILEID_INVALID) || (retval == -ENOSPC)) { + retval == FILEID_INVALID) /* As per old exportfs_encode_fh documentation * we could return ENOSPC to indicate overflow * But file system returned 255 always. So handle @@ -73,6 +112,7 @@ static long do_sys_name_to_handle(struct path *path, copy_to_user(ufh, handle, sizeof(struct file_handle) + handle_bytes)) retval = -EFAULT; +out: kfree(handle); return retval; } @@ -98,7 +138,7 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, int lookup_flags; int err; - if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) + if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_MAC)) != 0) return -EINVAL; lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; @@ -106,7 +146,8 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, lookup_flags |= LOOKUP_EMPTY; err = user_path_at(dfd, name, lookup_flags, &path); if (!err) { - err = do_sys_name_to_handle(&path, handle, mnt_id); + err = do_sys_name_to_handle(&path, handle, mnt_id, + flag & AT_HANDLE_MAC); path_put(&path); } return err; @@ -147,6 +188,14 @@ static int do_handle_to_path(int mountdirfd, struct file_handle *handle, retval = PTR_ERR(path->mnt); goto out_err; } + + if (handle->handle_type & FILEID_MAC) { + /* verify mac using current->files->key */ + handle->handle_bytes -= 8; + } else if(!ns_capable(path->mnt->mnt_sb->s_user_ns, CAP_DAC_READ_SEARCH)) { + return -EPERM; + } + /* change the handle size to multiple of sizeof(u32) */ handle_dwords = handle->handle_bytes >> 2; path->dentry = exportfs_decode_fh(path->mnt, @@ -176,7 +225,7 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, * directory. Ideally we would like CAP_DAC_SEARCH. * But we don't have that */ - if (!capable(CAP_DAC_READ_SEARCH)) { + if (!ns_capable(current_user_ns(), CAP_DAC_READ_SEARCH)) { retval = -EPERM; goto out_err; } diff --git a/fs/file.c b/fs/file.c index f3a4bac2cbe9..9e41b8beea52 100644 --- a/fs/file.c +++ b/fs/file.c @@ -420,6 +420,8 @@ void put_files_struct(struct files_struct *files) /* free the arrays if they are not embedded */ if (fdt != &files->fdtab) __free_fdtable(fdt); + + fhandle_key_free(files->key); kmem_cache_free(files_cachep, files); } } diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index fe848901fcc3..a6fdd9bbe98a 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -113,6 +113,9 @@ enum fid_type { * Filesystems must not use 0xff file ID. */ FILEID_INVALID = 0xff, + + /* OR-ed with the actual ID; used by the fhandle API. */ + FILEID_MAC = 0x10000000; }; struct fid { diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index d0e78174874a..1895d21435ac 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h @@ -56,6 +56,8 @@ struct files_struct { struct fdtable __rcu *fdt; struct fdtable fdtab; + + struct fhandle_mac_key *key; /* * written part on a separate cache line in SMP */