From 923c7538236564c46ee80c253a416705321f13e3 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 27 Dec 2012 11:39:12 +0800 Subject: [PATCH 01/93] userns: Allow unprivileged reboot In a container with its own pid namespace and user namespace, rebooting the system won't reboot the host, but terminate all the processes in it and thus have the container shutdown, so it's safe. Signed-off-by: Li Zefan Signed-off-by: Eric W. Biederman --- kernel/sys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/sys.c b/kernel/sys.c index 265b37690421..24d1ef56cd95 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -433,11 +433,12 @@ static DEFINE_MUTEX(reboot_mutex); SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { + struct pid_namespace *pid_ns = task_active_pid_ns(current); char buffer[256]; int ret = 0; /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT)) + if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) return -EPERM; /* For safety, we require "magic" arguments. */ @@ -453,7 +454,7 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, * pid_namespace, the command is handled by reboot_pid_ns() which will * call do_exit(). */ - ret = reboot_pid_ns(task_active_pid_ns(current), cmd); + ret = reboot_pid_ns(pid_ns, cmd); if (ret) return ret; From c61a2810a2161986353705b44d9503e6bb079f4f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 28 Dec 2012 18:58:39 -0800 Subject: [PATCH 02/93] userns: Avoid recursion in put_user_ns When freeing a deeply nested user namespace free_user_ns calls put_user_ns on it's parent which may in turn call free_user_ns again. When -fno-optimize-sibling-calls is passed to gcc one stack frame per user namespace is left on the stack, potentially overflowing the kernel stack. CONFIG_FRAME_POINTER forces -fno-optimize-sibling-calls so we can't count on gcc to optimize this code. Remove struct kref and use a plain atomic_t. Making the code more flexible and easier to comprehend. Make the loop in free_user_ns explict to guarantee that the stack does not overflow with CONFIG_FRAME_POINTER enabled. I have tested this fix with a simple program that uses unshare to create a deeply nested user namespace structure and then calls exit. With 1000 nesteuser namespaces before this change running my test program causes the kernel to die a horrible death. With 10,000,000 nested user namespaces after this change my test program runs to completion and causes no harm. Acked-by: Serge Hallyn Pointed-out-by: Vasily Kulikov Signed-off-by: "Eric W. Biederman" --- include/linux/user_namespace.h | 10 +++++----- kernel/user.c | 4 +--- kernel/user_namespace.c | 17 +++++++++-------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index b9bd2e6c73cc..4ce009324933 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -21,7 +21,7 @@ struct user_namespace { struct uid_gid_map uid_map; struct uid_gid_map gid_map; struct uid_gid_map projid_map; - struct kref kref; + atomic_t count; struct user_namespace *parent; kuid_t owner; kgid_t group; @@ -35,18 +35,18 @@ extern struct user_namespace init_user_ns; static inline struct user_namespace *get_user_ns(struct user_namespace *ns) { if (ns) - kref_get(&ns->kref); + atomic_inc(&ns->count); return ns; } extern int create_user_ns(struct cred *new); extern int unshare_userns(unsigned long unshare_flags, struct cred **new_cred); -extern void free_user_ns(struct kref *kref); +extern void free_user_ns(struct user_namespace *ns); static inline void put_user_ns(struct user_namespace *ns) { - if (ns) - kref_put(&ns->kref, free_user_ns); + if (ns && atomic_dec_and_test(&ns->count)) + free_user_ns(ns); } struct seq_operations; diff --git a/kernel/user.c b/kernel/user.c index 33acb5e53a5f..57ebfd42023c 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -47,9 +47,7 @@ struct user_namespace init_user_ns = { .count = 4294967295U, }, }, - .kref = { - .refcount = ATOMIC_INIT(3), - }, + .count = ATOMIC_INIT(3), .owner = GLOBAL_ROOT_UID, .group = GLOBAL_ROOT_GID, .proc_inum = PROC_USER_INIT_INO, diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 2b042c42fbc4..24f8ec3b64d8 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -78,7 +78,7 @@ int create_user_ns(struct cred *new) return ret; } - kref_init(&ns->kref); + atomic_set(&ns->count, 1); /* Leave the new->user_ns reference with the new user namespace. */ ns->parent = parent_ns; ns->owner = owner; @@ -104,15 +104,16 @@ int unshare_userns(unsigned long unshare_flags, struct cred **new_cred) return create_user_ns(cred); } -void free_user_ns(struct kref *kref) +void free_user_ns(struct user_namespace *ns) { - struct user_namespace *parent, *ns = - container_of(kref, struct user_namespace, kref); + struct user_namespace *parent; - parent = ns->parent; - proc_free_inum(ns->proc_inum); - kmem_cache_free(user_ns_cachep, ns); - put_user_ns(parent); + do { + parent = ns->parent; + proc_free_inum(ns->proc_inum); + kmem_cache_free(user_ns_cachep, ns); + ns = parent; + } while (atomic_dec_and_test(&parent->count)); } EXPORT_SYMBOL(free_user_ns); From 0bd14b4fd72afd5df41e9fd59f356740f22fceba Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 27 Dec 2012 22:27:29 -0800 Subject: [PATCH 03/93] userns: Allow any uid or gid mappings that don't overlap. When I initially wrote the code for /proc//uid_map. I was lazy and avoided duplicate mappings by the simple expedient of ensuring the first number in a new extent was greater than any number in the previous extent. Unfortunately that precludes a number of valid mappings, and someone noticed and complained. So use a simple check to ensure that ranges in the mapping extents don't overlap. Acked-by: Serge Hallyn Signed-off-by: "Eric W. Biederman" --- kernel/user_namespace.c | 45 +++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 24f8ec3b64d8..8b650837083e 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -520,6 +520,42 @@ struct seq_operations proc_projid_seq_operations = { .show = projid_m_show, }; +static bool mappings_overlap(struct uid_gid_map *new_map, struct uid_gid_extent *extent) +{ + u32 upper_first, lower_first, upper_last, lower_last; + unsigned idx; + + upper_first = extent->first; + lower_first = extent->lower_first; + upper_last = upper_first + extent->count - 1; + lower_last = lower_first + extent->count - 1; + + for (idx = 0; idx < new_map->nr_extents; idx++) { + u32 prev_upper_first, prev_lower_first; + u32 prev_upper_last, prev_lower_last; + struct uid_gid_extent *prev; + + prev = &new_map->extent[idx]; + + prev_upper_first = prev->first; + prev_lower_first = prev->lower_first; + prev_upper_last = prev_upper_first + prev->count - 1; + prev_lower_last = prev_lower_first + prev->count - 1; + + /* Does the upper range intersect a previous extent? */ + if ((prev_upper_first <= upper_last) && + (prev_upper_last >= upper_first)) + return true; + + /* Does the lower range intersect a previous extent? */ + if ((prev_lower_first <= lower_last) && + (prev_lower_last >= lower_first)) + return true; + } + return false; +} + + static DEFINE_MUTEX(id_map_mutex); static ssize_t map_write(struct file *file, const char __user *buf, @@ -532,7 +568,7 @@ static ssize_t map_write(struct file *file, const char __user *buf, struct user_namespace *ns = seq->private; struct uid_gid_map new_map; unsigned idx; - struct uid_gid_extent *extent, *last = NULL; + struct uid_gid_extent *extent = NULL; unsigned long page = 0; char *kbuf, *pos, *next_line; ssize_t ret = -EINVAL; @@ -635,14 +671,11 @@ static ssize_t map_write(struct file *file, const char __user *buf, if ((extent->lower_first + extent->count) <= extent->lower_first) goto out; - /* For now only accept extents that are strictly in order */ - if (last && - (((last->first + last->count) > extent->first) || - ((last->lower_first + last->count) > extent->lower_first))) + /* Do the ranges in extent overlap any previous extents? */ + if (mappings_overlap(&new_map, extent)) goto out; new_map.nr_extents++; - last = extent; /* Fail if the file contains too many extents */ if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) && From e11f0ae388f227d7ad03953e19034dec55286650 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 25 Jan 2013 16:48:31 -0800 Subject: [PATCH 04/93] userns: Recommend use of memory control groups. In the help text describing user namespaces recommend use of memory control groups. In many cases memory control groups are the only mechanism there is to limit how much memory a user who can create user namespaces can use. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- Documentation/namespaces/resource-control.txt | 14 ++++++++++++++ init/Kconfig | 7 +++++++ 2 files changed, 21 insertions(+) create mode 100644 Documentation/namespaces/resource-control.txt diff --git a/Documentation/namespaces/resource-control.txt b/Documentation/namespaces/resource-control.txt new file mode 100644 index 000000000000..abc13c394738 --- /dev/null +++ b/Documentation/namespaces/resource-control.txt @@ -0,0 +1,14 @@ +There are a lot of kinds of objects in the kernel that don't have +individual limits or that have limits that are ineffective when a set +of processes is allowed to switch user ids. With user namespaces +enabled in a kernel for people who don't trust their users or their +users programs to play nice this problems becomes more acute. + +Therefore it is recommended that memory control groups be enabled in +kernels that enable user namespaces, and it is further recommended +that userspace configure memory control groups to limit how much +memory user's they don't trust to play nice can use. + +Memory control groups can be configured by installing the libcgroup +package present on most distros editing /etc/cgrules.conf, +/etc/cgconfig.conf and setting up libpam-cgroup. diff --git a/init/Kconfig b/init/Kconfig index 7d30240e5bfe..c8c58bddfed3 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1035,6 +1035,13 @@ config USER_NS help This allows containers, i.e. vservers, to use user namespaces to provide different user info for different servers. + + When user namespaces are enabled in the kernel it is + recommended that the MEMCG and MEMCG_KMEM options also be + enabled and that user-space use the memory control groups to + limit the amount of memory a memory unprivileged users can + use. + If unsure, say N. config PID_NS From ec2aa8e8dd7d35524a68c89b9e7ceb07fb002561 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 20 Aug 2012 17:28:58 -0700 Subject: [PATCH 05/93] userns: Allow the userns root to mount of devpts - The context in which devpts is mounted has no effect on the creation of ptys as the /dev/ptmx interface has been used by unprivileged users for many years. - Only support unprivileged mounts in combination with the newinstance option to ensure that mounting of /dev/pts in a user namespace will not allow the options of an existing mount of devpts to be modified. - Create /dev/pts/ptmx as the root user in the user namespace that mounts devpts so that it's permissions to be changed. Acked-by: Serge Hallyn Signed-off-by: "Eric W. Biederman" --- fs/devpts/inode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 472e6befc54d..073d30b9d1ac 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -243,6 +243,13 @@ static int mknod_ptmx(struct super_block *sb) struct dentry *root = sb->s_root; struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; + kuid_t root_uid; + kgid_t root_gid; + + root_uid = make_kuid(current_user_ns(), 0); + root_gid = make_kgid(current_user_ns(), 0); + if (!uid_valid(root_uid) || !gid_valid(root_gid)) + return -EINVAL; mutex_lock(&root->d_inode->i_mutex); @@ -273,6 +280,8 @@ static int mknod_ptmx(struct super_block *sb) mode = S_IFCHR|opts->ptmxmode; init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2)); + inode->i_uid = root_uid; + inode->i_gid = root_gid; d_add(dentry, inode); @@ -438,6 +447,12 @@ static struct dentry *devpts_mount(struct file_system_type *fs_type, if (error) return ERR_PTR(error); + /* Require newinstance for all user namespace mounts to ensure + * the mount options are not changed. + */ + if ((current_user_ns() != &init_user_ns) && !opts.newinstance) + return ERR_PTR(-EINVAL); + if (opts.newinstance) s = sget(fs_type, NULL, set_anon_super, flags, NULL); else @@ -491,6 +506,9 @@ static struct file_system_type devpts_fs_type = { .name = "devpts", .mount = devpts_mount, .kill_sb = devpts_kill_sb, +#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES + .fs_flags = FS_USERNS_MOUNT | FS_USERNS_DEV_MOUNT, +#endif }; /* From b3c6761d9b5cc82deea7416d11c2118f6380e99c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 25 Jan 2013 16:37:44 -0800 Subject: [PATCH 06/93] userns: Allow the userns root to mount ramfs. There is no backing store to ramfs and file creation rules are the same as for any other filesystem so it is semantically safe to allow unprivileged users to mount it. The memory control group successfully limits how much memory ramfs can consume on any system that cares about a user namespace root using ramfs to exhaust memory the memory control group can be deployed. Signed-off-by: "Eric W. Biederman" --- fs/ramfs/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index eab8c09d3801..c24f1e10b946 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -260,6 +260,7 @@ static struct file_system_type ramfs_fs_type = { .name = "ramfs", .mount = ramfs_mount, .kill_sb = ramfs_kill_sb, + .fs_flags = FS_USERNS_MOUNT, }; static struct file_system_type rootfs_fs_type = { .name = "rootfs", From 2b8576cb09a7b1b581c253554bf9e05d3adce7d6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 25 Jan 2013 16:32:10 -0800 Subject: [PATCH 07/93] userns: Allow the userns root to mount tmpfs. There is no backing store to tmpfs and file creation rules are the same as for any other filesystem so it is semantically safe to allow unprivileged users to mount it. ramfs is safe for the same reasons so allow either flavor of tmpfs to be mounted by a user namespace root user. The memory control group successfully limits how much memory tmpfs can consume on any system that cares about a user namespace root using tmpfs to exhaust memory the memory control group can be deployed. Signed-off-by: "Eric W. Biederman" --- mm/shmem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/shmem.c b/mm/shmem.c index 5c90d84c2b02..197ca5eccbae 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2766,6 +2766,7 @@ static struct file_system_type shmem_fs_type = { .name = "tmpfs", .mount = shmem_mount, .kill_sb = kill_litter_super, + .fs_flags = FS_USERNS_MOUNT, }; int __init shmem_init(void) @@ -2823,6 +2824,7 @@ static struct file_system_type shmem_fs_type = { .name = "tmpfs", .mount = ramfs_mount, .kill_sb = kill_litter_super, + .fs_flags = FS_USERNS_MOUNT, }; int __init shmem_init(void) From bc1b69ed22a704fb1cc83d75b2eb46508a06c820 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Mon, 28 Jan 2013 11:09:01 +0800 Subject: [PATCH 08/93] userns: Allow the unprivileged users to mount mqueue fs This patch allow the unprivileged user to mount mqueuefs in user ns. If two userns share the same ipcns,the files in mqueue fs should be seen in both these two userns. If the userns has its own ipcns,it has its own mqueue fs too. ipcns has already done this job well. Signed-off-by: Gao feng Signed-off-by: Eric W. Biederman --- ipc/mqueue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 71a3ca18c873..023c9867ff43 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -1383,6 +1383,7 @@ static struct file_system_type mqueue_fs_type = { .name = "mqueue", .mount = mqueue_mount, .kill_sb = kill_litter_super, + .fs_flags = FS_USERNS_MOUNT, }; int mq_init_ns(struct ipc_namespace *ns) From eea553c21fbfa486978c82525ee8256239d4f921 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 02:09:50 -0800 Subject: [PATCH 09/93] ceph: Only allow mounts in the initial network namespace Today ceph opens tcp sockets from a delayed work callback. Delayed work happens from kernel threads which are always in the initial network namespace. Therefore fail early if someone attempts to mount a ceph filesystem from something other than the initial network namespace. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- net/ceph/ceph_common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index ee71ea26777a..1deb29af82fd 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include @@ -292,6 +294,9 @@ ceph_parse_options(char *options, const char *dev_name, int err = -ENOMEM; substring_t argstr[MAX_OPT_ARGS]; + if (current->nsproxy->net_ns != &init_net) + return ERR_PTR(-EINVAL); + opt = kzalloc(sizeof(*opt), GFP_KERNEL); if (!opt) return ERR_PTR(-ENOMEM); From 05cb11c17e892f0e131b6c2ba25d63221aafbd11 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 02:56:19 -0800 Subject: [PATCH 10/93] ceph: Translate between uid and gids in cap messages and kuids and kgids - Make the uid and gid arguments of send_cap_msg() used to compose ceph_mds_caps messages of type kuid_t and kgid_t. - Pass inode->i_uid and inode->i_gid in __send_cap to send_cap_msg() through variables of type kuid_t and kgid_t. - Modify struct ceph_cap_snap to store uids and gids in types kuid_t and kgid_t. This allows capturing inode->i_uid and inode->i_gid in ceph_queue_cap_snap() without loss and pssing them to __ceph_flush_snaps() where they are removed from struct ceph_cap_snap and passed to send_cap_msg(). - In handle_cap_grant translate uid and gids in the initial user namespace stored in struct ceph_mds_cap into kuids and kgids before setting inode->i_uid and inode->i_gid. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/caps.c | 14 +++++++------- fs/ceph/super.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index a1d9bb30c1bf..39eb4665b122 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -930,7 +930,7 @@ static int send_cap_msg(struct ceph_mds_session *session, u64 size, u64 max_size, struct timespec *mtime, struct timespec *atime, u64 time_warp_seq, - uid_t uid, gid_t gid, umode_t mode, + kuid_t uid, kgid_t gid, umode_t mode, u64 xattr_version, struct ceph_buffer *xattrs_buf, u64 follows) @@ -974,8 +974,8 @@ static int send_cap_msg(struct ceph_mds_session *session, ceph_encode_timespec(&fc->atime, atime); fc->time_warp_seq = cpu_to_le32(time_warp_seq); - fc->uid = cpu_to_le32(uid); - fc->gid = cpu_to_le32(gid); + fc->uid = cpu_to_le32(from_kuid(&init_user_ns, uid)); + fc->gid = cpu_to_le32(from_kgid(&init_user_ns, gid)); fc->mode = cpu_to_le32(mode); fc->xattr_version = cpu_to_le64(xattr_version); @@ -1081,8 +1081,8 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, struct timespec mtime, atime; int wake = 0; umode_t mode; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct ceph_mds_session *session; u64 xattr_version = 0; struct ceph_buffer *xattr_blob = NULL; @@ -2359,8 +2359,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { inode->i_mode = le32_to_cpu(grant->mode); - inode->i_uid = le32_to_cpu(grant->uid); - inode->i_gid = le32_to_cpu(grant->gid); + inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid)); + inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, inode->i_uid, inode->i_gid); } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 66ebe720e40d..f053bbd1886f 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -138,8 +138,8 @@ struct ceph_cap_snap { struct ceph_snap_context *context; umode_t mode; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct ceph_buffer *xattr_blob; u64 xattr_version; From ab871b903e9095772c219b512d9eae96c4663a5d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 03:40:12 -0800 Subject: [PATCH 11/93] ceph: Translate inode uid and gid attributes to/from kuids and kgids. - In fill_inode() transate uids and gids in the initial user namespace into kuids and kgids stored in inode->i_uid and inode->i_gid. - In ceph_setattr() if they have changed convert inode->i_uid and inode->i_gid into initial user namespace uids and gids for transmission. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/inode.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 2971eaa65cdc..40e0787022e9 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -612,8 +612,8 @@ static int fill_inode(struct inode *inode, if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { inode->i_mode = le32_to_cpu(info->mode); - inode->i_uid = le32_to_cpu(info->uid); - inode->i_gid = le32_to_cpu(info->gid); + inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid)); + inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, inode->i_uid, inode->i_gid); } @@ -1570,8 +1570,9 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) inode->i_uid = attr->ia_uid; dirtied |= CEPH_CAP_AUTH_EXCL; } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || - attr->ia_uid != inode->i_uid) { - req->r_args.setattr.uid = cpu_to_le32(attr->ia_uid); + !uid_eq(attr->ia_uid, inode->i_uid)) { + req->r_args.setattr.uid = cpu_to_le32( + from_kuid(&init_user_ns, attr->ia_uid)); mask |= CEPH_SETATTR_UID; release |= CEPH_CAP_AUTH_SHARED; } @@ -1583,8 +1584,9 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) inode->i_gid = attr->ia_gid; dirtied |= CEPH_CAP_AUTH_EXCL; } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || - attr->ia_gid != inode->i_gid) { - req->r_args.setattr.gid = cpu_to_le32(attr->ia_gid); + !gid_eq(attr->ia_gid, inode->i_gid)) { + req->r_args.setattr.gid = cpu_to_le32( + from_kgid(&init_user_ns, attr->ia_gid)); mask |= CEPH_SETATTR_GID; release |= CEPH_CAP_AUTH_SHARED; } From ff3d0046625c1b37df37beb8477135d44dae2823 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:01:53 -0800 Subject: [PATCH 12/93] ceph: Convert struct ceph_mds_request to use kuid_t and kgid_t Hold the uid and gid for a pending ceph mds request using the types kuid_t and kgid_t. When a request message is finally created convert the kuid_t and kgid_t values into uids and gids in the initial user namespace. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/mds_client.c | 4 ++-- fs/ceph/mds_client.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9165eb8309eb..7a3dfe0a9a80 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1658,8 +1658,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, head->mdsmap_epoch = cpu_to_le32(mdsc->mdsmap->m_epoch); head->op = cpu_to_le32(req->r_op); - head->caller_uid = cpu_to_le32(req->r_uid); - head->caller_gid = cpu_to_le32(req->r_gid); + head->caller_uid = cpu_to_le32(from_kuid(&init_user_ns, req->r_uid)); + head->caller_gid = cpu_to_le32(from_kgid(&init_user_ns, req->r_gid)); head->args = req->r_args; ceph_encode_filepath(&p, end, ino1, path1); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index dd26846dd71d..ff4188bf6199 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -184,8 +184,8 @@ struct ceph_mds_request { union ceph_mds_request_args r_args; int r_fmode; /* file mode, if expecting cap */ - uid_t r_uid; - gid_t r_gid; + kuid_t r_uid; + kgid_t r_gid; /* for choosing which mds to send this request to */ int r_direct_mode; From bd2bae6a66df9261a39e47291b0a6b00cd0831e0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:05:39 -0800 Subject: [PATCH 13/93] ceph: Convert kuids and kgids before printing them. Before printing kuid and kgids values convert them into the initial user namespace. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/caps.c | 3 ++- fs/ceph/inode.c | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 39eb4665b122..ae2be696eb5b 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2362,7 +2362,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid)); inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, - inode->i_uid, inode->i_gid); + from_kuid(&init_user_ns, inode->i_uid), + from_kgid(&init_user_ns, inode->i_gid)); } if ((issued & CEPH_CAP_LINK_EXCL) == 0) diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 40e0787022e9..d45895f4a04d 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -615,7 +615,8 @@ static int fill_inode(struct inode *inode, inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid)); inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, - inode->i_uid, inode->i_gid); + from_kuid(&init_user_ns, inode->i_uid), + from_kgid(&init_user_ns, inode->i_gid)); } if ((issued & CEPH_CAP_LINK_EXCL) == 0) @@ -1565,7 +1566,8 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) if (ia_valid & ATTR_UID) { dout("setattr %p uid %d -> %d\n", inode, - inode->i_uid, attr->ia_uid); + from_kuid(&init_user_ns, inode->i_uid), + from_kuid(&init_user_ns, attr->ia_uid)); if (issued & CEPH_CAP_AUTH_EXCL) { inode->i_uid = attr->ia_uid; dirtied |= CEPH_CAP_AUTH_EXCL; @@ -1579,7 +1581,8 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) } if (ia_valid & ATTR_GID) { dout("setattr %p gid %d -> %d\n", inode, - inode->i_gid, attr->ia_gid); + from_kgid(&init_user_ns, inode->i_gid), + from_kgid(&init_user_ns, attr->ia_gid)); if (issued & CEPH_CAP_AUTH_EXCL) { inode->i_gid = attr->ia_gid; dirtied |= CEPH_CAP_AUTH_EXCL; From d5ea055f1cc0ff6d4170c7f60f3cb5eb09d927bc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:07:20 -0800 Subject: [PATCH 14/93] ceph: Enable building when user namespaces are enabled. Now that conversions happen from kuids and kgids when generating ceph messages and conversion happen to kuids and kgids after receiving celph messages, and all intermediate data structures store uids and gids as type kuid_t and kgid_t it is safe to enable ceph with user namespace support enabled. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index c8c58bddfed3..7170d549159a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1076,7 +1076,6 @@ config UIDGID_CONVERTED # Filesystems depends on 9P_FS = n depends on AFS_FS = n - depends on CEPH_FS = n depends on CIFS = n depends on CODA_FS = n depends on GFS2_FS = n From 97fc8b1ebf6a0fe4bb9c71a8e91a822c22c09bc5 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 17:07:42 -0800 Subject: [PATCH 15/93] 9p: Add 'u' and 'g' format specifies for kuids and kgids This allows concentrating all of the conversion to and from kuids and kgids into the format needed by the 9p protocol into one location. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- net/9p/protocol.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 3d33ecf13327..c289c6cee98e 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -85,6 +85,8 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) d - int32_t q - int64_t s - string + u - numeric uid + g - numeric gid S - stat Q - qid D - data blob (int32_t size followed by void *, results are not freed) @@ -163,6 +165,26 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, (*sptr)[len] = 0; } break; + case 'u': { + kuid_t *uid = va_arg(ap, kuid_t *); + __le32 le_val; + if (pdu_read(pdu, &le_val, sizeof(le_val))) { + errcode = -EFAULT; + break; + } + *uid = make_kuid(&init_user_ns, + le32_to_cpu(le_val)); + } break; + case 'g': { + kgid_t *gid = va_arg(ap, kgid_t *); + __le32 le_val; + if (pdu_read(pdu, &le_val, sizeof(le_val))) { + errcode = -EFAULT; + break; + } + *gid = make_kgid(&init_user_ns, + le32_to_cpu(le_val)); + } break; case 'Q':{ struct p9_qid *qid = va_arg(ap, struct p9_qid *); @@ -377,6 +399,20 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, errcode = -EFAULT; } break; + case 'u': { + kuid_t uid = va_arg(ap, kuid_t); + __le32 val = cpu_to_le32( + from_kuid(&init_user_ns, uid)); + if (pdu_write(pdu, &val, sizeof(val))) + errcode = -EFAULT; + } break; + case 'g': { + kgid_t gid = va_arg(ap, kgid_t); + __le32 val = cpu_to_le32( + from_kgid(&init_user_ns, gid)); + if (pdu_write(pdu, &val, sizeof(val))) + errcode = -EFAULT; + } break; case 'Q':{ const struct p9_qid *qid = va_arg(ap, const struct p9_qid *); From f791f7c5e354870eaa5e31c4038c6723683283f1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 16:09:41 -0800 Subject: [PATCH 16/93] 9p: Transmit kuid and kgid values Modify the p9_client_rpc format specifiers of every function that directly transmits a uid or a gid from 'd' to 'u' or 'g' as appropriate. Modify those same functions to take kuid_t and kgid_t parameters instead of uid_t and gid_t parameters. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: Eric W. Biederman --- fs/9p/v9fs.c | 2 +- include/net/9p/client.h | 10 +++++----- net/9p/client.c | 25 +++++++++++++------------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index d934f04e7736..4e0bd9d62ed6 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -375,7 +375,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->flags &= ~V9FS_ACL_MASK; } - fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0, + fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID, v9ses->aname); if (IS_ERR(fid)) { retval = PTR_ERR(fid); diff --git a/include/net/9p/client.h b/include/net/9p/client.h index fc9b90b0c052..6b031ab6e4db 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -220,17 +220,17 @@ void p9_client_destroy(struct p9_client *clnt); void p9_client_disconnect(struct p9_client *clnt); void p9_client_begin_disconnect(struct p9_client *clnt); struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, - char *uname, u32 n_uname, char *aname); + char *uname, kuid_t n_uname, char *aname); struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname, char **wnames, int clone); int p9_client_open(struct p9_fid *fid, int mode); int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, char *extension); int p9_client_link(struct p9_fid *fid, struct p9_fid *oldfid, char *newname); -int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, gid_t gid, +int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, kgid_t gid, struct p9_qid *qid); int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, - gid_t gid, struct p9_qid *qid); + kgid_t gid, struct p9_qid *qid); int p9_client_clunk(struct p9_fid *fid); int p9_client_fsync(struct p9_fid *fid, int datasync); int p9_client_remove(struct p9_fid *fid); @@ -250,9 +250,9 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, u64 request_mask); int p9_client_mknod_dotl(struct p9_fid *oldfid, char *name, int mode, - dev_t rdev, gid_t gid, struct p9_qid *); + dev_t rdev, kgid_t gid, struct p9_qid *); int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, - gid_t gid, struct p9_qid *); + kgid_t gid, struct p9_qid *); int p9_client_lock_dotl(struct p9_fid *fid, struct p9_flock *flock, u8 *status); int p9_client_getlock_dotl(struct p9_fid *fid, struct p9_getlock *fl); struct p9_req_t *p9_tag_lookup(struct p9_client *, u16); diff --git a/net/9p/client.c b/net/9p/client.c index 34d417670935..17855f080acd 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1100,7 +1100,7 @@ void p9_client_begin_disconnect(struct p9_client *clnt) EXPORT_SYMBOL(p9_client_begin_disconnect); struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, - char *uname, u32 n_uname, char *aname) + char *uname, kuid_t n_uname, char *aname) { int err = 0; struct p9_req_t *req; @@ -1117,7 +1117,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, goto error; } - req = p9_client_rpc(clnt, P9_TATTACH, "ddss?d", fid->fid, + req = p9_client_rpc(clnt, P9_TATTACH, "ddss?u", fid->fid, afid ? afid->fid : P9_NOFID, uname, aname, n_uname); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1270,7 +1270,7 @@ int p9_client_open(struct p9_fid *fid, int mode) EXPORT_SYMBOL(p9_client_open); int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, - gid_t gid, struct p9_qid *qid) + kgid_t gid, struct p9_qid *qid) { int err = 0; struct p9_client *clnt; @@ -1279,13 +1279,14 @@ int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, p9_debug(P9_DEBUG_9P, ">>> TLCREATE fid %d name %s flags %d mode %d gid %d\n", - ofid->fid, name, flags, mode, gid); + ofid->fid, name, flags, mode, + from_kgid(&init_user_ns, gid)); clnt = ofid->clnt; if (ofid->mode != -1) return -EINVAL; - req = p9_client_rpc(clnt, P9_TLCREATE, "dsddd", ofid->fid, name, flags, + req = p9_client_rpc(clnt, P9_TLCREATE, "dsddg", ofid->fid, name, flags, mode, gid); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1358,7 +1359,7 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, } EXPORT_SYMBOL(p9_client_fcreate); -int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid, +int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, kgid_t gid, struct p9_qid *qid) { int err = 0; @@ -1369,7 +1370,7 @@ int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid, dfid->fid, name, symtgt); clnt = dfid->clnt; - req = p9_client_rpc(clnt, P9_TSYMLINK, "dssd", dfid->fid, name, symtgt, + req = p9_client_rpc(clnt, P9_TSYMLINK, "dssg", dfid->fid, name, symtgt, gid); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -2106,7 +2107,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) EXPORT_SYMBOL(p9_client_readdir); int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, - dev_t rdev, gid_t gid, struct p9_qid *qid) + dev_t rdev, kgid_t gid, struct p9_qid *qid) { int err; struct p9_client *clnt; @@ -2116,7 +2117,7 @@ int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, clnt = fid->clnt; p9_debug(P9_DEBUG_9P, ">>> TMKNOD fid %d name %s mode %d major %d " "minor %d\n", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev)); - req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddd", fid->fid, name, mode, + req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddg", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev), gid); if (IS_ERR(req)) return PTR_ERR(req); @@ -2137,7 +2138,7 @@ int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, EXPORT_SYMBOL(p9_client_mknod_dotl); int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, - gid_t gid, struct p9_qid *qid) + kgid_t gid, struct p9_qid *qid) { int err; struct p9_client *clnt; @@ -2146,8 +2147,8 @@ int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, err = 0; clnt = fid->clnt; p9_debug(P9_DEBUG_9P, ">>> TMKDIR fid %d name %s mode %d gid %d\n", - fid->fid, name, mode, gid); - req = p9_client_rpc(clnt, P9_TMKDIR, "dsdd", fid->fid, name, mode, + fid->fid, name, mode, from_kgid(&init_user_ns, gid)); + req = p9_client_rpc(clnt, P9_TMKDIR, "dsdg", fid->fid, name, mode, gid); if (IS_ERR(req)) return PTR_ERR(req); From 447c50943fd008755122c7a62bac068e73c1cf2c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 16:18:50 -0800 Subject: [PATCH 17/93] 9p: Modify the stat structures to use kuid_t and kgid_t 9p has thre strucrtures that can encode inode stat information. Modify all of those structures to contain kuid_t and kgid_t values. Modify he wire encoders and decoders of those structures to use 'u' and 'g' instead of 'd' in the format string where uids and gids are present. This results in all kuid and kgid conversion to and from on the wire values being performed by the same code in protocol.c where the client is known at the time of the conversion. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: Eric W. Biederman --- fs/9p/vfs_inode.c | 6 +++--- include/net/9p/9p.h | 14 +++++++------- net/9p/client.c | 18 +++++++++++++----- net/9p/protocol.c | 13 +++++++------ 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 890bed538f9b..1581fe218934 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -228,9 +228,9 @@ v9fs_blank_wstat(struct p9_wstat *wstat) wstat->uid = NULL; wstat->gid = NULL; wstat->muid = NULL; - wstat->n_uid = ~0; - wstat->n_gid = ~0; - wstat->n_muid = ~0; + wstat->n_uid = INVALID_UID; + wstat->n_gid = INVALID_GID; + wstat->n_muid = INVALID_UID; wstat->extension = NULL; } diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 7184853ca360..27dfe85772b1 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -407,17 +407,17 @@ struct p9_wstat { char *gid; char *muid; char *extension; /* 9p2000.u extensions */ - u32 n_uid; /* 9p2000.u extensions */ - u32 n_gid; /* 9p2000.u extensions */ - u32 n_muid; /* 9p2000.u extensions */ + kuid_t n_uid; /* 9p2000.u extensions */ + kgid_t n_gid; /* 9p2000.u extensions */ + kuid_t n_muid; /* 9p2000.u extensions */ }; struct p9_stat_dotl { u64 st_result_mask; struct p9_qid qid; u32 st_mode; - u32 st_uid; - u32 st_gid; + kuid_t st_uid; + kgid_t st_gid; u64 st_nlink; u64 st_rdev; u64 st_size; @@ -471,8 +471,8 @@ struct p9_stat_dotl { struct p9_iattr_dotl { u32 valid; u32 mode; - u32 uid; - u32 gid; + kuid_t uid; + kgid_t gid; u64 size; u64 atime_sec; u64 atime_nsec; diff --git a/net/9p/client.c b/net/9p/client.c index 17855f080acd..8eb75425e6e6 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1711,7 +1711,9 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid) (unsigned long long)ret->qid.path, ret->qid.version, ret->mode, ret->atime, ret->mtime, (unsigned long long)ret->length, ret->name, ret->uid, ret->gid, ret->muid, ret->extension, - ret->n_uid, ret->n_gid, ret->n_muid); + from_kuid(&init_user_ns, ret->n_uid), + from_kgid(&init_user_ns, ret->n_gid), + from_kuid(&init_user_ns, ret->n_muid)); p9_free_req(clnt, req); return ret; @@ -1765,8 +1767,10 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, "<<< st_btime_sec=%lld st_btime_nsec=%lld\n" "<<< st_gen=%lld st_data_version=%lld", ret->st_result_mask, ret->qid.type, ret->qid.path, - ret->qid.version, ret->st_mode, ret->st_nlink, ret->st_uid, - ret->st_gid, ret->st_rdev, ret->st_size, ret->st_blksize, + ret->qid.version, ret->st_mode, ret->st_nlink, + from_kuid(&init_user_ns, ret->st_uid), + from_kgid(&init_user_ns, ret->st_gid), + ret->st_rdev, ret->st_size, ret->st_blksize, ret->st_blocks, ret->st_atime_sec, ret->st_atime_nsec, ret->st_mtime_sec, ret->st_mtime_nsec, ret->st_ctime_sec, ret->st_ctime_nsec, ret->st_btime_sec, ret->st_btime_nsec, @@ -1829,7 +1833,9 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst) (unsigned long long)wst->qid.path, wst->qid.version, wst->mode, wst->atime, wst->mtime, (unsigned long long)wst->length, wst->name, wst->uid, wst->gid, wst->muid, wst->extension, - wst->n_uid, wst->n_gid, wst->n_muid); + from_kuid(&init_user_ns, wst->n_uid), + from_kgid(&init_user_ns, wst->n_gid), + from_kuid(&init_user_ns, wst->n_muid)); req = p9_client_rpc(clnt, P9_TWSTAT, "dwS", fid->fid, wst->size+2, wst); if (IS_ERR(req)) { @@ -1858,7 +1864,9 @@ int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr) " valid=%x mode=%x uid=%d gid=%d size=%lld\n" " atime_sec=%lld atime_nsec=%lld\n" " mtime_sec=%lld mtime_nsec=%lld\n", - p9attr->valid, p9attr->mode, p9attr->uid, p9attr->gid, + p9attr->valid, p9attr->mode, + from_kuid(&init_user_ns, p9attr->uid), + from_kgid(&init_user_ns, p9attr->gid), p9attr->size, p9attr->atime_sec, p9attr->atime_nsec, p9attr->mtime_sec, p9attr->mtime_nsec); diff --git a/net/9p/protocol.c b/net/9p/protocol.c index c289c6cee98e..ab9127ec5b7a 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -199,11 +199,12 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, va_arg(ap, struct p9_wstat *); memset(stbuf, 0, sizeof(struct p9_wstat)); - stbuf->n_uid = stbuf->n_gid = stbuf->n_muid = - -1; + stbuf->n_uid = stbuf->n_muid = INVALID_UID; + stbuf->n_gid = INVALID_GID; + errcode = p9pdu_readf(pdu, proto_version, - "wwdQdddqssss?sddd", + "wwdQdddqssss?sugu", &stbuf->size, &stbuf->type, &stbuf->dev, &stbuf->qid, &stbuf->mode, &stbuf->atime, @@ -316,7 +317,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, memset(stbuf, 0, sizeof(struct p9_stat_dotl)); errcode = p9pdu_readf(pdu, proto_version, - "qQdddqqqqqqqqqqqqqqq", + "qQdugqqqqqqqqqqqqqqq", &stbuf->st_result_mask, &stbuf->qid, &stbuf->st_mode, @@ -426,7 +427,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, va_arg(ap, const struct p9_wstat *); errcode = p9pdu_writef(pdu, proto_version, - "wwdQdddqssss?sddd", + "wwdQdddqssss?sugu", stbuf->size, stbuf->type, stbuf->dev, &stbuf->qid, stbuf->mode, stbuf->atime, @@ -504,7 +505,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, struct p9_iattr_dotl *); errcode = p9pdu_writef(pdu, proto_version, - "ddddqqqqq", + "ddugqqqqq", p9attr->valid, p9attr->mode, p9attr->uid, From b464255699077c6b33ea58ee01db80f5729511ad Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 11:48:53 -0800 Subject: [PATCH 18/93] 9p: Modify struct 9p_fid to use a kuid_t not a uid_t Change struct 9p_fid and it's associated functions to use kuid_t's instead of uid_t. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- fs/9p/fid.c | 17 +++++++++-------- fs/9p/v9fs.c | 2 +- include/net/9p/client.h | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index da8eefbe830d..afd4724b2d92 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -74,19 +74,20 @@ int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) * */ -static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any) +static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any) { struct v9fs_dentry *dent; struct p9_fid *fid, *ret; p9_debug(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n", - dentry->d_name.name, dentry, uid, any); + dentry->d_name.name, dentry, from_kuid(&init_user_ns, uid), + any); dent = (struct v9fs_dentry *) dentry->d_fsdata; ret = NULL; if (dent) { spin_lock(&dent->lock); list_for_each_entry(fid, &dent->fidlist, dlist) { - if (any || fid->uid == uid) { + if (any || uid_eq(fid->uid, uid)) { ret = fid; break; } @@ -126,7 +127,7 @@ static int build_path_from_dentry(struct v9fs_session_info *v9ses, } static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, - uid_t uid, int any) + kuid_t uid, int any) { struct dentry *ds; char **wnames, *uname; @@ -233,7 +234,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) { - uid_t uid; + kuid_t uid; int any, access; struct v9fs_session_info *v9ses; @@ -253,7 +254,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) break; default: - uid = ~0; + uid = INVALID_UID; any = 0; break; } @@ -272,7 +273,7 @@ struct p9_fid *v9fs_fid_clone(struct dentry *dentry) return ret; } -static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, uid_t uid) +static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid) { struct p9_fid *fid, *ret; @@ -289,7 +290,7 @@ struct p9_fid *v9fs_writeback_fid(struct dentry *dentry) int err; struct p9_fid *fid; - fid = v9fs_fid_clone_with_uid(dentry, 0); + fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID); if (IS_ERR(fid)) goto error_out; /* diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 4e0bd9d62ed6..d64967cbd73e 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -387,7 +387,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE) fid->uid = v9ses->uid; else - fid->uid = ~0; + fid->uid = INVALID_UID; #ifdef CONFIG_9P_FSCACHE /* register the session for caching */ diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 6b031ab6e4db..5ff70f433e87 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -187,7 +187,7 @@ struct p9_fid { int mode; struct p9_qid qid; u32 iounit; - uid_t uid; + kuid_t uid; void *rdir; From 76ed23a5d703d94ede1ef6c12c14a75add691202 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 11:57:40 -0800 Subject: [PATCH 19/93] 9p: Modify struct v9fs_session_info to use a kuids and kgids Change struct v9fs_session_info and the code that popluates it to use kuids and kgids. When parsing the 9p mount options convert the dfltuid, dflutgid, and the session uid from the current user namespace into kuids and kgids. Modify V9FS_DEFUID and V9FS_DEFGUID to be kuid and kgid values. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- fs/9p/v9fs.c | 30 +++++++++++++++++++++++++----- fs/9p/v9fs.h | 10 +++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index d64967cbd73e..58e6cbce4156 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -161,7 +161,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ret = r; continue; } - v9ses->dfltuid = option; + v9ses->dfltuid = make_kuid(current_user_ns(), option); + if (!uid_valid(v9ses->dfltuid)) { + p9_debug(P9_DEBUG_ERROR, + "uid field, but not a uid?\n"); + ret = -EINVAL; + continue; + } break; case Opt_dfltgid: r = match_int(&args[0], &option); @@ -171,7 +177,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ret = r; continue; } - v9ses->dfltgid = option; + v9ses->dfltgid = make_kgid(current_user_ns(), option); + if (!gid_valid(v9ses->dfltgid)) { + p9_debug(P9_DEBUG_ERROR, + "gid field, but not a gid?\n"); + ret = -EINVAL; + continue; + } break; case Opt_afid: r = match_int(&args[0], &option); @@ -248,8 +260,9 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) else if (strcmp(s, "client") == 0) { v9ses->flags |= V9FS_ACCESS_CLIENT; } else { + uid_t uid; v9ses->flags |= V9FS_ACCESS_SINGLE; - v9ses->uid = simple_strtoul(s, &e, 10); + uid = simple_strtoul(s, &e, 10); if (*e != '\0') { ret = -EINVAL; pr_info("Unknown access argument %s\n", @@ -257,6 +270,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) kfree(s); goto free_and_return; } + v9ses->uid = make_kuid(current_user_ns(), uid); + if (!uid_valid(v9ses->uid)) { + ret = -EINVAL; + pr_info("Uknown uid %s\n", s); + kfree(s); + goto free_and_return; + } } kfree(s); @@ -319,7 +339,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, list_add(&v9ses->slist, &v9fs_sessionlist); spin_unlock(&v9fs_sessionlist_lock); - v9ses->uid = ~0; + v9ses->uid = INVALID_UID; v9ses->dfltuid = V9FS_DEFUID; v9ses->dfltgid = V9FS_DEFGID; @@ -364,7 +384,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->flags &= ~V9FS_ACCESS_MASK; v9ses->flags |= V9FS_ACCESS_ANY; - v9ses->uid = ~0; + v9ses->uid = INVALID_UID; } if (!v9fs_proto_dotl(v9ses) || !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 34c59f14a1c9..a8e127c89627 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -109,9 +109,9 @@ struct v9fs_session_info { char *uname; /* user name to mount as */ char *aname; /* name of remote hierarchy being mounted */ unsigned int maxdata; /* max data for client interface */ - unsigned int dfltuid; /* default uid/muid for legacy support */ - unsigned int dfltgid; /* default gid for legacy support */ - u32 uid; /* if ACCESS_SINGLE, the uid that has access */ + kuid_t dfltuid; /* default uid/muid for legacy support */ + kgid_t dfltgid; /* default gid for legacy support */ + kuid_t uid; /* if ACCESS_SINGLE, the uid that has access */ struct p9_client *clnt; /* 9p client */ struct list_head slist; /* list of sessions registered with v9fs */ struct backing_dev_info bdi; @@ -165,8 +165,8 @@ extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, #define V9FS_PORT 564 #define V9FS_DEFUSER "nobody" #define V9FS_DEFANAME "" -#define V9FS_DEFUID (-2) -#define V9FS_DEFGID (-2) +#define V9FS_DEFUID KUIDT_INIT(-2) +#define V9FS_DEFGID KGIDT_INIT(-2) static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode) { From d4ef4e3581044890e4a04607a4e8f42d3ebcf704 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 12:08:21 -0800 Subject: [PATCH 20/93] 9p: Modify v9fs_get_fsgid_for_create to return a kgid Modify v9fs_get_fsgid_for_create to return a kgid and modify all of the variables that hold the result of v9fs_get_fsgid_for_create to be of type kgid_t. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- fs/9p/vfs_inode_dotl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 40895546e103..e27886573e7d 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -57,7 +57,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode, * group of the new file system object. */ -static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) +static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) { BUG_ON(dir_inode == NULL); @@ -246,7 +246,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, int *opened) { int err = 0; - gid_t gid; + kgid_t gid; umode_t mode; char *name = NULL; struct p9_qid qid; @@ -391,7 +391,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir, int err; struct v9fs_session_info *v9ses; struct p9_fid *fid = NULL, *dfid = NULL; - gid_t gid; + kgid_t gid; char *name; umode_t mode; struct inode *inode; @@ -692,7 +692,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry, const char *symname) { int err; - gid_t gid; + kgid_t gid; char *name; struct p9_qid qid; struct inode *inode; @@ -832,7 +832,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode, dev_t rdev) { int err; - gid_t gid; + kgid_t gid; char *name; umode_t mode; struct v9fs_session_info *v9ses; From 4fa814be258169caef51e320b8b06cb3b139d4a0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 12:11:45 -0800 Subject: [PATCH 21/93] 9p: Allow building 9p with user namespaces enabled. Now that the uid_t -> kuid_t, gid_t -> kgid_t conversion has been completed in 9p allow 9p to be built when user namespaces are enabled. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index 7170d549159a..394d24f99efe 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1070,11 +1070,7 @@ config UIDGID_CONVERTED bool default y - # Networking - depends on NET_9P = n - # Filesystems - depends on 9P_FS = n depends on AFS_FS = n depends on CIFS = n depends on CODA_FS = n From 66fdb93f882d21612a5287cd1303c9b1391ebf5d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 13:04:05 -0800 Subject: [PATCH 22/93] afs: Remove unused structure afs_store_status While looking for kuid_t and kgid_t conversions I found this structure that has never been used since it was added to the kernel in 2007. The obvious for this structure to be used is in xdr_encode_AFS_StoreStatus and that function uses a small handful of local variables instead. So remove the unnecessary structure to prevent confusion. Cc: David Howells Signed-off-by: "Eric W. Biederman" --- fs/afs/afs.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/afs/afs.h b/fs/afs/afs.h index c548aa346f0d..3d8fd35c0dd0 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -133,13 +133,6 @@ struct afs_file_status { /* * AFS file status change request */ -struct afs_store_status { - u32 mask; /* which bits of the struct are set */ - u32 mtime_client; /* last time client changed data */ - u32 owner; /* owner ID */ - u32 group; /* group ID */ - umode_t mode; /* UNIX mode */ -}; #define AFS_SET_MTIME 0x01 /* set the mtime */ #define AFS_SET_OWNER 0x02 /* set the owner ID */ From f74f70f8b10b435f5f20247e70d1d86b53a59685 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:23:54 -0800 Subject: [PATCH 23/93] afs: Only allow mounting afs in the intial network namespace rxrpc sockets only work in the initial network namespace so it isn't possible to support afs in any other network namespace. Cc: David Howells Signed-off-by: "Eric W. Biederman" --- fs/afs/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/afs/super.c b/fs/afs/super.c index 43165009428d..7c31ec399575 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "internal.h" #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ @@ -363,6 +365,10 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, memset(¶ms, 0, sizeof(params)); + ret = -EINVAL; + if (current->nsproxy->net_ns != &init_net) + goto error; + /* parse the options and device name */ if (options) { ret = afs_parse_options(¶ms, options, &dev_name); From a0a5386ac6400493cc2eb8b58583e56af0708730 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:20:48 -0800 Subject: [PATCH 24/93] afs: Support interacting with multiple user namespaces Modify struct afs_file_status to store owner as a kuid_t and group as a kgid_t. In xdr_decode_AFSFetchStatus as owner is now a kuid_t and group is now a kgid_t don't use the EXTRACT macro. Instead perform the work of the extract macro explicitly. Read the value with ntohl and convert it to the appropriate type with make_kuid or make_kgid. Test if the value is different from what is stored in status and update changed. Update the value in status. In xdr_encode_AFS_StoreStatus call from_kuid or from_kgid as we are computing the on the wire encoding. Initialize uids with GLOBAL_ROOT_UID instead of 0. Initialize gids with GLOBAL_ROOT_GID instead of 0. Cc: David Howells Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/afs/afs.h | 4 ++-- fs/afs/fsclient.c | 14 ++++++++++---- fs/afs/inode.c | 6 +++--- init/Kconfig | 1 - 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/fs/afs/afs.h b/fs/afs/afs.h index 3d8fd35c0dd0..3c462ff6db63 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -119,8 +119,8 @@ struct afs_file_status { u64 size; /* file size */ afs_dataversion_t data_version; /* current data version */ u32 author; /* author ID */ - u32 owner; /* owner ID */ - u32 group; /* group ID */ + kuid_t owner; /* owner ID */ + kgid_t group; /* group ID */ afs_access_t caller_access; /* access rights for authenticated caller */ afs_access_t anon_access; /* access rights for unauthenticated caller */ umode_t mode; /* UNIX mode */ diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index b960ff05ea0b..c2e930ec2888 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -42,6 +42,8 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, umode_t mode; u64 data_version, size; u32 changed = 0; /* becomes non-zero if ctime-type changes seen */ + kuid_t owner; + kgid_t group; #define EXTRACT(DST) \ do { \ @@ -56,7 +58,9 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, size = ntohl(*bp++); data_version = ntohl(*bp++); EXTRACT(status->author); - EXTRACT(status->owner); + owner = make_kuid(&init_user_ns, ntohl(*bp++)); + changed |= !uid_eq(owner, status->owner); + status->owner = owner; EXTRACT(status->caller_access); /* call ticket dependent */ EXTRACT(status->anon_access); EXTRACT(status->mode); @@ -65,7 +69,9 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, bp++; /* seg size */ status->mtime_client = ntohl(*bp++); status->mtime_server = ntohl(*bp++); - EXTRACT(status->group); + group = make_kgid(&init_user_ns, ntohl(*bp++)); + changed |= !gid_eq(group, status->group); + status->group = group; bp++; /* sync counter */ data_version |= (u64) ntohl(*bp++) << 32; EXTRACT(status->lock_count); @@ -181,12 +187,12 @@ static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr) if (attr->ia_valid & ATTR_UID) { mask |= AFS_SET_OWNER; - owner = attr->ia_uid; + owner = from_kuid(&init_user_ns, attr->ia_uid); } if (attr->ia_valid & ATTR_GID) { mask |= AFS_SET_GROUP; - group = attr->ia_gid; + group = from_kgid(&init_user_ns, attr->ia_gid); } if (attr->ia_valid & ATTR_MODE) { diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 95cffd38239f..789bc253b5f6 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -69,7 +69,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) set_nlink(inode, vnode->status.nlink); inode->i_uid = vnode->status.owner; - inode->i_gid = 0; + inode->i_gid = GLOBAL_ROOT_GID; inode->i_size = vnode->status.size; inode->i_ctime.tv_sec = vnode->status.mtime_server; inode->i_ctime.tv_nsec = 0; @@ -175,8 +175,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_op = &afs_autocell_inode_operations; set_nlink(inode, 2); - inode->i_uid = 0; - inode->i_gid = 0; + inode->i_uid = GLOBAL_ROOT_UID; + inode->i_gid = GLOBAL_ROOT_GID; inode->i_ctime.tv_sec = get_seconds(); inode->i_ctime.tv_nsec = 0; inode->i_atime = inode->i_mtime = inode->i_ctime; diff --git a/init/Kconfig b/init/Kconfig index 394d24f99efe..4570b02abcc5 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1071,7 +1071,6 @@ config UIDGID_CONVERTED default y # Filesystems - depends on AFS_FS = n depends on CIFS = n depends on CODA_FS = n depends on GFS2_FS = n From 9fd973e085f7759f710603422b2e11ad5f2e000d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 18:50:54 -0800 Subject: [PATCH 25/93] coda: Restrict coda messages to the initial pid namespace Remove the slight chance that pids in coda messages will be interpreted in the wrong pid namespace. - Explicitly send all pids in coda messages in the initial pid namespace. - Only allow mounts from processes in the initial pid namespace. - Only allow processes in the initial pid namespace to open the coda character device to communicate with coda. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- fs/coda/inode.c | 4 ++++ fs/coda/psdev.c | 4 ++++ fs/coda/upcall.c | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/coda/inode.c b/fs/coda/inode.c index be2aa4909487..77bbaf4666a7 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -157,6 +158,9 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) int error; int idx; + if (task_active_pid_ns(current) != &init_pid_ns) + return -EINVAL; + idx = get_device_index((struct coda_mount_data *) data); /* Ignore errors in data, for backward compatibility */ diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 761d5b31b18d..dd60f905d4fe 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -266,6 +267,9 @@ static int coda_psdev_open(struct inode * inode, struct file * file) struct venus_comm *vcp; int idx, err; + if (task_active_pid_ns(current) != &init_pid_ns) + return -EINVAL; + idx = iminor(inode); if (idx < 0 || idx >= MAX_CODADEVS) return -ENODEV; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 0c68fd31fbf2..5c6d2cd6ee86 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -50,8 +50,8 @@ static void *alloc_upcall(int opcode, int size) return ERR_PTR(-ENOMEM); inp->ih.opcode = opcode; - inp->ih.pid = current->pid; - inp->ih.pgid = task_pgrp_nr(current); + inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns); + inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns); inp->ih.uid = current_fsuid(); return (void*)inp; From d83f5901bc0cd7131a3b8534169ee889efc4c257 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 19:21:14 -0800 Subject: [PATCH 26/93] coda: Restrict coda messages to the initial user namespace Remove the slight chance that uids and gids in coda messages will be interpreted in the wrong user namespace. - Only allow processes in the initial user namespace to open the coda character device to communicate with coda filesystems. - Explicitly convert the uids in the coda header into the initial user namespace. - In coda_vattr_to_attr make kuids and kgids from the initial user namespace uids and gids in struct coda_vattr that just came from userspace. - In coda_iattr_to_vattr convert kuids and kgids into uids and gids in the intial user namespace and store them in struct coda_vattr for sending to coda userspace programs. Nothing needs to be changed with mounts as coda does not support being mounted in anything other than the initial user namespace. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- fs/coda/coda_linux.c | 8 ++++---- fs/coda/psdev.c | 3 +++ fs/coda/upcall.c | 6 +++--- include/linux/coda_psdev.h | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 854ace712685..2849f41e72a2 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -100,9 +100,9 @@ void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) if (attr->va_mode != (u_short) -1) inode->i_mode = attr->va_mode | inode_type; if (attr->va_uid != -1) - inode->i_uid = (uid_t) attr->va_uid; + inode->i_uid = make_kuid(&init_user_ns, (uid_t) attr->va_uid); if (attr->va_gid != -1) - inode->i_gid = (gid_t) attr->va_gid; + inode->i_gid = make_kgid(&init_user_ns, (gid_t) attr->va_gid); if (attr->va_nlink != -1) set_nlink(inode, attr->va_nlink); if (attr->va_size != -1) @@ -171,10 +171,10 @@ void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) vattr->va_mode = iattr->ia_mode; } if ( valid & ATTR_UID ) { - vattr->va_uid = (vuid_t) iattr->ia_uid; + vattr->va_uid = (vuid_t) from_kuid(&init_user_ns, iattr->ia_uid); } if ( valid & ATTR_GID ) { - vattr->va_gid = (vgid_t) iattr->ia_gid; + vattr->va_gid = (vgid_t) from_kgid(&init_user_ns, iattr->ia_gid); } if ( valid & ATTR_SIZE ) { vattr->va_size = iattr->ia_size; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index dd60f905d4fe..ebc2bae6c289 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -270,6 +270,9 @@ static int coda_psdev_open(struct inode * inode, struct file * file) if (task_active_pid_ns(current) != &init_pid_ns) return -EINVAL; + if (current_user_ns() != &init_user_ns) + return -EINVAL; + idx = iminor(inode); if (idx < 0 || idx >= MAX_CODADEVS) return -ENODEV; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 5c6d2cd6ee86..3a731976dc5e 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -52,7 +52,7 @@ static void *alloc_upcall(int opcode, int size) inp->ih.opcode = opcode; inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns); inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns); - inp->ih.uid = current_fsuid(); + inp->ih.uid = from_kuid(&init_user_ns, current_fsuid()); return (void*)inp; } @@ -157,7 +157,7 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, } int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) + kuid_t uid) { union inputArgs *inp; union outputArgs *outp; @@ -166,7 +166,7 @@ int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, insize = SIZE(release); UPARG(CODA_CLOSE); - inp->ih.uid = uid; + inp->ih.uid = from_kuid(&init_user_ns, uid); inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index 8031d6eef102..5b8721efa948 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -34,7 +34,7 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, const char *name, int length, int *type, struct CodaFid *resfid); int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid); + kuid_t uid); int venus_open(struct super_block *sb, struct CodaFid *fid, int flags, struct file **f); int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, From 17499e332962e495b3ee2e5507e09b853ed6f607 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 19:36:06 -0800 Subject: [PATCH 27/93] coda: Cache permisions in struct coda_inode_info in a kuid_t. - Change c_uid in struct coda_indoe_info from a vuid_t to a kuid_t. - Initialize c_uid to GLOBAL_ROOT_UID instead of 0. - Use uid_eq to compare cached kuids. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- fs/coda/cache.c | 4 ++-- fs/coda/coda_fs_i.h | 2 +- fs/coda/inode.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 958ae0e0ff8c..1da168c61d35 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -33,7 +33,7 @@ void coda_cache_enter(struct inode *inode, int mask) spin_lock(&cii->c_lock); cii->c_cached_epoch = atomic_read(&permission_epoch); - if (cii->c_uid != current_fsuid()) { + if (!uid_eq(cii->c_uid, current_fsuid())) { cii->c_uid = current_fsuid(); cii->c_cached_perm = mask; } else @@ -65,7 +65,7 @@ int coda_cache_check(struct inode *inode, int mask) spin_lock(&cii->c_lock); hit = (mask & cii->c_cached_perm) == mask && - cii->c_uid == current_fsuid() && + uid_eq(cii->c_uid, current_fsuid()) && cii->c_cached_epoch == atomic_read(&permission_epoch); spin_unlock(&cii->c_lock); diff --git a/fs/coda/coda_fs_i.h b/fs/coda/coda_fs_i.h index b24fdfd8a3f0..c64075213218 100644 --- a/fs/coda/coda_fs_i.h +++ b/fs/coda/coda_fs_i.h @@ -25,7 +25,7 @@ struct coda_inode_info { u_short c_flags; /* flags (see below) */ unsigned int c_mapcount; /* nr of times this inode is mapped */ unsigned int c_cached_epoch; /* epoch for cached permissions */ - vuid_t c_uid; /* fsuid for cached permissions */ + kuid_t c_uid; /* fsuid for cached permissions */ unsigned int c_cached_perm; /* cached access permissions */ spinlock_t c_lock; struct inode vfs_inode; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 77bbaf4666a7..cf674e9179a3 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -49,7 +49,7 @@ static struct inode *coda_alloc_inode(struct super_block *sb) return NULL; memset(&ei->c_fid, 0, sizeof(struct CodaFid)); ei->c_flags = 0; - ei->c_uid = 0; + ei->c_uid = GLOBAL_ROOT_UID; ei->c_cached_perm = 0; spin_lock_init(&ei->c_lock); return &ei->vfs_inode; From 515ee7bd9758208dea081dbc933400d6be81028a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 19:43:05 -0800 Subject: [PATCH 28/93] coda: Allow coda to be built when user namespace support is enabled Now that the coda kernel to userspace has been modified to convert between kuids and kgids and uids and gids, and all internal coda structures have be modified to store uids and gids as kuids and kgids it is safe to allow code to be built with user namespace support enabled. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 4570b02abcc5..f516d52f93c8 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on CODA_FS = n depends on GFS2_FS = n depends on NCP_FS = n depends on NFSD = n From 9522751cded17c231acd1cf92bc21b3da1b07f38 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:49:40 -0800 Subject: [PATCH 29/93] ocfs2: Handle kuids and kgids in acl/xattr conversions. Explicitly deal with the different kinds of acls because they need different conversions. Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/acl.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 260b16281fc3..8a404576fb26 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -65,7 +65,20 @@ static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size) acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); - acl->a_entries[n].e_id = le32_to_cpu(entry->e_id); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + acl->a_entries[n].e_uid = + make_kuid(&init_user_ns, + le32_to_cpu(entry->e_id)); + break; + case ACL_GROUP: + acl->a_entries[n].e_gid = + make_kgid(&init_user_ns, + le32_to_cpu(entry->e_id)); + break; + default: + break; + } value += sizeof(struct posix_acl_entry); } @@ -91,7 +104,21 @@ static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size) for (n = 0; n < acl->a_count; n++, entry++) { entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); - entry->e_id = cpu_to_le32(acl->a_entries[n].e_id); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + entry->e_id = cpu_to_le32( + from_kuid(&init_user_ns, + acl->a_entries[n].e_uid)); + break; + case ACL_GROUP: + entry->e_id = cpu_to_le32( + from_kgid(&init_user_ns, + acl->a_entries[n].e_gid)); + break; + default: + entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); + break; + } } return ocfs2_acl; } From 03ab30f73dbf2f4f719d2c0c2acef81bf0445eb7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 16:59:23 -0800 Subject: [PATCH 30/93] ocfs2: convert between kuids and kgids and DLM locks Convert between uid and gids stored in the on the wire format of dlm locks aka struct ocfs2_meta_lvb and kuids and kgids stored in inode->i_uid and inode->i_gid. Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/dlmglue.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 4f7795fb5fc0..f99af1cb849c 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -2045,8 +2045,8 @@ static void __ocfs2_stuff_meta_lvb(struct inode *inode) lvb->lvb_version = OCFS2_LVB_VERSION; lvb->lvb_isize = cpu_to_be64(i_size_read(inode)); lvb->lvb_iclusters = cpu_to_be32(oi->ip_clusters); - lvb->lvb_iuid = cpu_to_be32(inode->i_uid); - lvb->lvb_igid = cpu_to_be32(inode->i_gid); + lvb->lvb_iuid = cpu_to_be32(i_uid_read(inode)); + lvb->lvb_igid = cpu_to_be32(i_gid_read(inode)); lvb->lvb_imode = cpu_to_be16(inode->i_mode); lvb->lvb_inlink = cpu_to_be16(inode->i_nlink); lvb->lvb_iatime_packed = @@ -2095,8 +2095,8 @@ static void ocfs2_refresh_inode_from_lvb(struct inode *inode) else inode->i_blocks = ocfs2_inode_sector_count(inode); - inode->i_uid = be32_to_cpu(lvb->lvb_iuid); - inode->i_gid = be32_to_cpu(lvb->lvb_igid); + i_uid_write(inode, be32_to_cpu(lvb->lvb_iuid)); + i_gid_write(inode, be32_to_cpu(lvb->lvb_igid)); inode->i_mode = be16_to_cpu(lvb->lvb_imode); set_nlink(inode, be16_to_cpu(lvb->lvb_inlink)); ocfs2_unpack_timespec(&inode->i_atime, From 2c03417627c5edaeeae1e8c5da6e8fd6f8c720d3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:27:11 -0800 Subject: [PATCH 31/93] ocfs2: Convert uid and gids between in core and on disk inodes Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/inode.c | 12 ++++++------ fs/ocfs2/namei.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index d89e08a81eda..f87f9bd1edff 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -269,8 +269,8 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, inode->i_generation = le32_to_cpu(fe->i_generation); inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); inode->i_mode = le16_to_cpu(fe->i_mode); - inode->i_uid = le32_to_cpu(fe->i_uid); - inode->i_gid = le32_to_cpu(fe->i_gid); + i_uid_write(inode, le32_to_cpu(fe->i_uid)); + i_gid_write(inode, le32_to_cpu(fe->i_gid)); /* Fast symlinks will have i_size but no allocated clusters. */ if (S_ISLNK(inode->i_mode) && !fe->i_clusters) { @@ -1259,8 +1259,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle, fe->i_size = cpu_to_le64(i_size_read(inode)); ocfs2_set_links_count(fe, inode->i_nlink); - fe->i_uid = cpu_to_le32(inode->i_uid); - fe->i_gid = cpu_to_le32(inode->i_gid); + fe->i_uid = cpu_to_le32(i_uid_read(inode)); + fe->i_gid = cpu_to_le32(i_gid_read(inode)); fe->i_mode = cpu_to_le16(inode->i_mode); fe->i_atime = cpu_to_le64(inode->i_atime.tv_sec); fe->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); @@ -1290,8 +1290,8 @@ void ocfs2_refresh_inode(struct inode *inode, ocfs2_set_inode_flags(inode); i_size_write(inode, le64_to_cpu(fe->i_size)); set_nlink(inode, ocfs2_read_links_count(fe)); - inode->i_uid = le32_to_cpu(fe->i_uid); - inode->i_gid = le32_to_cpu(fe->i_gid); + i_uid_write(inode, le32_to_cpu(fe->i_uid)); + i_gid_write(inode, le32_to_cpu(fe->i_gid)); inode->i_mode = le16_to_cpu(fe->i_mode); if (S_ISLNK(inode->i_mode) && le32_to_cpu(fe->i_clusters) == 0) inode->i_blocks = 0; diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index f1fd0741162b..04ee1b57c243 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -512,8 +512,8 @@ static int __ocfs2_mknod_locked(struct inode *dir, fe->i_suballoc_loc = cpu_to_le64(suballoc_loc); fe->i_suballoc_bit = cpu_to_le16(suballoc_bit); fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot); - fe->i_uid = cpu_to_le32(inode->i_uid); - fe->i_gid = cpu_to_le32(inode->i_gid); + fe->i_uid = cpu_to_le32(i_uid_read(inode)); + fe->i_gid = cpu_to_le32(i_gid_read(inode)); fe->i_mode = cpu_to_le16(inode->i_mode); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev)); From ba6135609c2b56851e37e1d89ddbdbae4e774a71 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:33:53 -0800 Subject: [PATCH 32/93] ocfs2: For tracing report the uid and gid values in the initial user namespace Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 37d313ede159..8ee93322db2b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1116,7 +1116,8 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) (unsigned long long)OCFS2_I(inode)->ip_blkno, dentry->d_name.len, dentry->d_name.name, attr->ia_valid, attr->ia_mode, - attr->ia_uid, attr->ia_gid); + from_kuid(&init_user_ns, attr->ia_uid), + from_kgid(&init_user_ns, attr->ia_gid)); /* ensuring we don't even attempt to truncate a symlink */ if (S_ISLNK(inode->i_mode)) From 488c8ef033c6409cd8d23bcd04eed2f56301836d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:35:29 -0800 Subject: [PATCH 33/93] ocfs2: Compare kuids and kgids using uid_eq and gid_eq Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/file.c | 8 ++++---- fs/ocfs2/refcounttree.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 8ee93322db2b..0a2924a2d9e6 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1175,14 +1175,14 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) } } - if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || - (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + if ((attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || + (attr->ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) { /* * Gather pointers to quota structures so that allocation / * freeing of quota structures happens here and not inside * dquot_transfer() where we have problems with lock ordering */ - if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid + if (attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid) && OCFS2_HAS_RO_COMPAT_FEATURE(sb, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { transfer_to[USRQUOTA] = dqget(sb, make_kqid_uid(attr->ia_uid)); @@ -1191,7 +1191,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) goto bail_unlock; } } - if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid + if (attr->ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid) && OCFS2_HAS_RO_COMPAT_FEATURE(sb, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(attr->ia_gid)); diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 30a055049e16..934a4ac3e7fc 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -4407,7 +4407,7 @@ static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir, * rights to do so. */ if (preserve) { - if ((current_fsuid() != inode->i_uid) && !capable(CAP_CHOWN)) + if (!uid_eq(current_fsuid(), inode->i_uid) && !capable(CAP_CHOWN)) return -EPERM; if (!in_group_p(inode->i_gid) && !capable(CAP_CHOWN)) return -EPERM; From ecb528e3ea208750693731538411f86f78a4d965 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:36:56 -0800 Subject: [PATCH 34/93] ocfs2: Enable building with user namespaces enabled Now that ocfs2 has been converted to store uids and gids in kuid_t and kgid_t and all of the conversions have been added to the appropriate places it is safe to allow building and using ocfs2 with user namespace support enabled. Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index f516d52f93c8..9cb63c94cab8 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1076,7 +1076,6 @@ config UIDGID_CONVERTED depends on NCP_FS = n depends on NFSD = n depends on NFS_FS = n - depends on OCFS2_FS = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From 393551e9898136513007b1e88a25bd4dcdb0d759 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 02:09:03 -0800 Subject: [PATCH 35/93] gfs2: Remove improper checks in gfs2_set_dqblk. In set_dqblk it is an error to look at fdq->d_id or fdq->d_flags. Userspace quota applications do not set these fields when calling quotactl(Q_XSETQLIM,...), and the kernel does not set those fields when quota_setquota calls set_dqblk. gfs2 never looks at fdq->d_id or fdq->d_flags after checking to see if they match the id and type supplied to set_dqblk. No other linux filesystem in set_dqblk looks at either fdq->d_id or fdq->d_flags. Therefore remove these bogus checks from gfs2 and allow normal quota setting applications to work. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index ae55e248c3b7..e4f6ccf3da64 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1543,13 +1543,9 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, switch(qid.type) { case USRQUOTA: type = QUOTA_USER; - if (fdq->d_flags != FS_USER_QUOTA) - return -EINVAL; break; case GRPQUOTA: type = QUOTA_GROUP; - if (fdq->d_flags != FS_GROUP_QUOTA) - return -EINVAL; break; default: return -EINVAL; @@ -1557,8 +1553,6 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, if (fdq->d_fieldmask & ~GFS2_FIELDMASK) return -EINVAL; - if (fdq->d_id != from_kqid(&init_user_ns, qid)) - return -EINVAL; error = qd_get(sdp, type, from_kqid(&init_user_ns, qid), &qd); if (error) From f4108a607f75b073423eed229ee4f95e5fc10631 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:49:26 -0800 Subject: [PATCH 36/93] gfs2: Split NO_QUOTA_CHANGE inot NO_UID_QUTOA_CHANGE and NO_GID_QUTOA_CHANGE Split NO_QUOTA_CHANGE into NO_UID_QUTOA_CHANGE and NO_GID_QUTOA_CHANGE so the constants may be well typed. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/bmap.c | 2 +- fs/gfs2/dir.c | 2 +- fs/gfs2/inode.c | 10 +++++----- fs/gfs2/quota.c | 4 ++-- fs/gfs2/quota.h | 5 +++-- fs/gfs2/super.c | 2 +- fs/gfs2/xattr.c | 4 ++-- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index a68e91bcef3d..10c54e3c2e72 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1098,7 +1098,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 size) if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) return error; diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 9a35670fdc38..6d3e3e2cabf8 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1849,7 +1849,7 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, if (!ht) return -ENOMEM; - error = gfs2_quota_hold(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(dip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) goto out; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 2b6f5698ef18..aa1050287f3c 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -548,7 +548,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, if (error) return error; - error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_lock(dip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) goto fail; @@ -1589,15 +1589,15 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) ngid = attr->ia_gid; if (!(attr->ia_valid & ATTR_UID) || ouid == nuid) - ouid = nuid = NO_QUOTA_CHANGE; + ouid = nuid = NO_UID_QUOTA_CHANGE; if (!(attr->ia_valid & ATTR_GID) || ogid == ngid) - ogid = ngid = NO_QUOTA_CHANGE; + ogid = ngid = NO_GID_QUOTA_CHANGE; error = gfs2_quota_lock(ip, nuid, ngid); if (error) return error; - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { + if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { error = gfs2_quota_check(ip, nuid, ngid); if (error) goto out_gunlock_q; @@ -1611,7 +1611,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (error) goto out_end_trans; - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { + if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); gfs2_quota_change(ip, -blocks, ouid, ogid); gfs2_quota_change(ip, blocks, nuid, ngid); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index e4f6ccf3da64..dbfacaa8b6fb 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -524,7 +524,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) ip->i_res->rs_qa_qd_num++; qd++; - if (uid != NO_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { + if (uid != NO_UID_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { error = qdsb_get(sdp, QUOTA_USER, uid, qd); if (error) goto out; @@ -532,7 +532,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) qd++; } - if (gid != NO_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { + if (gid != NO_GID_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { error = qdsb_get(sdp, QUOTA_GROUP, gid, qd); if (error) goto out; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index f25d98b87904..7f67323a6a72 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -14,7 +14,8 @@ struct gfs2_inode; struct gfs2_sbd; struct shrink_control; -#define NO_QUOTA_CHANGE ((u32)-1) +#define NO_UID_QUOTA_CHANGE INVALID_UID +#define NO_GID_QUOTA_CHANGE INVALID_GID extern int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid); extern void gfs2_quota_unhold(struct gfs2_inode *ip); @@ -41,7 +42,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) int ret; if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return 0; - ret = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + ret = gfs2_quota_lock(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (ret) return ret; if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index d6488674d916..7cc5a6054547 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1429,7 +1429,7 @@ static int gfs2_dinode_dealloc(struct gfs2_inode *ip) if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) return error; diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 76c144b3c9bb..80c25ae79048 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c @@ -331,7 +331,7 @@ static int ea_remove_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) goto out_alloc; @@ -1461,7 +1461,7 @@ int gfs2_ea_dealloc(struct gfs2_inode *ip) if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) return error; From 558e85289fca3d3397882442d1a695936c4f2662 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 18:15:33 -0800 Subject: [PATCH 37/93] gfs2: Report quotas in the caller's user namespace. When a quota is queried return the uid or the gid in the mapped into the caller's user namespace. In addition perform the munged version of the mapping so that instead of -1 a value that does not map is reported as the overflowuid or the overflowgid. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index dbfacaa8b6fb..f8279eed56e1 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1509,7 +1509,7 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr; fdq->d_version = FS_DQUOT_VERSION; fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA; - fdq->d_id = from_kqid(&init_user_ns, qid); + fdq->d_id = from_kqid_munged(current_user_ns(), qid); fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift; fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift; fdq->d_bcount = be64_to_cpu(qlvb->qb_value) << sdp->sd_fsb2bb_shift; From 2f6c9896f71e6b6c1c565ea76dd9f5e89579c120 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 18:33:38 -0800 Subject: [PATCH 38/93] gfs2: Introduce qd2index Both qd_alloc and qd2offset perform the exact same computation to get an index from a gfs2_quota_data. Make life a little simpler and factor out this index computation. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index f8279eed56e1..0e7c982377a1 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -120,11 +120,17 @@ int gfs2_shrink_qd_memory(struct shrinker *shrink, struct shrink_control *sc) return (atomic_read(&qd_lru_count) * sysctl_vfs_cache_pressure) / 100; } +static u64 qd2index(struct gfs2_quota_data *qd) +{ + return (2 * (u64)qd->qd_id) + + test_bit(QDF_USER, &qd->qd_flags) ? 0 : 1; +} + static u64 qd2offset(struct gfs2_quota_data *qd) { u64 offset; - offset = 2 * (u64)qd->qd_id + !test_bit(QDF_USER, &qd->qd_flags); + offset = qd2index(qd); offset *= sizeof(struct gfs2_quota); return offset; @@ -147,7 +153,7 @@ static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, qd->qd_slot = -1; INIT_LIST_HEAD(&qd->qd_reclaim); - error = gfs2_glock_get(sdp, 2 * (u64)id + !user, + error = gfs2_glock_get(sdp, qd2index(qd), &gfs2_quota_glops, CREATE, &qd->qd_gl); if (error) goto fail; From e08d8d7f201dc1e64f5d9d5aa2cd4f37aecaaab4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:25:50 -0800 Subject: [PATCH 39/93] gfs2: Modify struct gfs2_quota_change_host to use struct kqid Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 0e7c982377a1..02913e95491c 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -71,7 +71,7 @@ struct gfs2_quota_change_host { u64 qc_change; u32 qc_flags; /* GFS2_QCF_... */ - u32 qc_id; + struct kqid qc_id; }; static LIST_HEAD(qd_lru_list); @@ -1200,7 +1200,9 @@ static void gfs2_quota_change_in(struct gfs2_quota_change_host *qc, const void * qc->qc_change = be64_to_cpu(str->qc_change); qc->qc_flags = be32_to_cpu(str->qc_flags); - qc->qc_id = be32_to_cpu(str->qc_id); + qc->qc_id = make_kqid(&init_user_ns, + (qc->qc_flags & GFS2_QCF_USER)?USRQUOTA:GRPQUOTA, + be32_to_cpu(str->qc_id)); } int gfs2_quota_init(struct gfs2_sbd *sdp) @@ -1264,7 +1266,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) continue; error = qd_alloc(sdp, (qc.qc_flags & GFS2_QCF_USER), - qc.qc_id, &qd); + from_kqid(&init_user_ns, qc.qc_id), &qd); if (error) { brelse(bh); goto fail; From b59c8b6f9d1b1220e5ed72152f42a658bf739d90 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:35:56 -0800 Subject: [PATCH 40/93] gfs2: Modify qdsb_get to take a struct kqid Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 02913e95491c..20762ae2a9c4 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -464,12 +464,13 @@ static void qd_unlock(struct gfs2_quota_data *qd) qd_put(qd); } -static int qdsb_get(struct gfs2_sbd *sdp, int user, u32 id, +static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { int error; - error = qd_get(sdp, user, id, qdp); + error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, + from_kqid(&init_user_ns, qid), qdp); if (error) return error; @@ -518,20 +519,20 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return 0; - error = qdsb_get(sdp, QUOTA_USER, ip->i_inode.i_uid, qd); + error = qdsb_get(sdp, make_kqid_uid(ip->i_inode.i_uid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; qd++; - error = qdsb_get(sdp, QUOTA_GROUP, ip->i_inode.i_gid, qd); + error = qdsb_get(sdp, make_kqid_gid(ip->i_inode.i_gid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; qd++; if (uid != NO_UID_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { - error = qdsb_get(sdp, QUOTA_USER, uid, qd); + error = qdsb_get(sdp, make_kqid_uid(uid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; @@ -539,7 +540,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) } if (gid != NO_GID_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { - error = qdsb_get(sdp, QUOTA_GROUP, gid, qd); + error = qdsb_get(sdp, make_kqid_gid(gid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; From ed87dabcc3fc0a5040f95dd3f7206cffebca5c79 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:42:40 -0800 Subject: [PATCH 41/93] gfs2: Convert gfs2_quota_refresh to take a kqid - In quota_refresh_user_store convert the user supplied uid into a kqid and pass it to gfs2_quota_refresh. - In quota_refresh_group_store convert the user supplied gid into a kqid and pass it to gfs2_quota_refresh. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 5 +++-- fs/gfs2/quota.h | 2 +- fs/gfs2/sys.c | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 20762ae2a9c4..47315c091a09 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1177,13 +1177,14 @@ static int gfs2_quota_sync_timeo(struct super_block *sb, int type) return gfs2_quota_sync(sb, type); } -int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id) +int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid) { struct gfs2_quota_data *qd; struct gfs2_holder q_gh; int error; - error = qd_get(sdp, user, id, &qd); + error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, + from_kqid(&init_user_ns, qid), &qd); if (error) return error; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 7f67323a6a72..bef805de8491 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -28,7 +28,7 @@ extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, u32 uid, u32 gid); extern int gfs2_quota_sync(struct super_block *sb, int type); -extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id); +extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid); extern int gfs2_quota_init(struct gfs2_sbd *sdp); extern void gfs2_quota_cleanup(struct gfs2_sbd *sdp); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 8056b7b7238e..e6d8d482422f 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -175,6 +175,7 @@ static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf, static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + struct kqid qid; int error; u32 id; @@ -183,13 +184,18 @@ static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, id = simple_strtoul(buf, NULL, 0); - error = gfs2_quota_refresh(sdp, 1, id); + qid = make_kqid(current_user_ns(), USRQUOTA, id); + if (!qid_valid(qid)) + return -EINVAL; + + error = gfs2_quota_refresh(sdp, qid); return error ? error : len; } static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + struct kqid qid; int error; u32 id; @@ -198,7 +204,11 @@ static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, id = simple_strtoul(buf, NULL, 0); - error = gfs2_quota_refresh(sdp, 0, id); + qid = make_kqid(current_user_ns(), GRPQUOTA, id); + if (!qid_valid(qid)) + return -EINVAL; + + error = gfs2_quota_refresh(sdp, qid); return error ? error : len; } From 05e0a60d8025e280e56b3fa36ea8facc7c1c65c2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:52:08 -0800 Subject: [PATCH 42/93] gfs2: Store qd_id in struct gfs2_quota_data as a struct kqid - Change qd_id in struct gfs2_qutoa_data to struct kqid. - Remove the now unnecessary QDF_USER bit field in qd_flags. - Propopoage this change through the code generally making things simpler along the way. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/incore.h | 3 +-- fs/gfs2/quota.c | 69 ++++++++++++++++++------------------------------ 2 files changed, 26 insertions(+), 46 deletions(-) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index c373a24fedd9..5b298bdab90c 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -391,7 +391,6 @@ struct gfs2_revoke_replay { }; enum { - QDF_USER = 0, QDF_CHANGE = 1, QDF_LOCKED = 2, QDF_REFRESH = 3, @@ -403,7 +402,7 @@ struct gfs2_quota_data { atomic_t qd_count; - u32 qd_id; + struct kqid qd_id; unsigned long qd_flags; /* QDF_... */ s64 qd_change; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 47315c091a09..8cb4d10cb16a 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -122,8 +122,9 @@ int gfs2_shrink_qd_memory(struct shrinker *shrink, struct shrink_control *sc) static u64 qd2index(struct gfs2_quota_data *qd) { - return (2 * (u64)qd->qd_id) + - test_bit(QDF_USER, &qd->qd_flags) ? 0 : 1; + struct kqid qid = qd->qd_id; + return (2 * (u64)from_kqid(&init_user_ns, qid)) + + (qid.type == USRQUOTA) ? 0 : 1; } static u64 qd2offset(struct gfs2_quota_data *qd) @@ -136,7 +137,7 @@ static u64 qd2offset(struct gfs2_quota_data *qd) return offset; } -static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, +static int qd_alloc(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { struct gfs2_quota_data *qd; @@ -147,9 +148,7 @@ static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, return -ENOMEM; atomic_set(&qd->qd_count, 1); - qd->qd_id = id; - if (user) - set_bit(QDF_USER, &qd->qd_flags); + qd->qd_id = qid; qd->qd_slot = -1; INIT_LIST_HEAD(&qd->qd_reclaim); @@ -167,7 +166,7 @@ static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, return error; } -static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, +static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { struct gfs2_quota_data *qd = NULL, *new_qd = NULL; @@ -179,8 +178,7 @@ static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, found = 0; spin_lock(&qd_lru_lock); list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { - if (qd->qd_id == id && - !test_bit(QDF_USER, &qd->qd_flags) == !user) { + if (qid_eq(qd->qd_id, qid)) { if (!atomic_read(&qd->qd_count) && !list_empty(&qd->qd_reclaim)) { /* Remove it from reclaim list */ @@ -214,7 +212,7 @@ static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, return 0; } - error = qd_alloc(sdp, user, id, &new_qd); + error = qd_alloc(sdp, qid, &new_qd); if (error) return error; } @@ -469,8 +467,7 @@ static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, { int error; - error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, - from_kqid(&init_user_ns, qid), qdp); + error = qd_get(sdp, qid, qdp); if (error) return error; @@ -574,18 +571,10 @@ static int sort_qd(const void *a, const void *b) const struct gfs2_quota_data *qd_a = *(const struct gfs2_quota_data **)a; const struct gfs2_quota_data *qd_b = *(const struct gfs2_quota_data **)b; - if (!test_bit(QDF_USER, &qd_a->qd_flags) != - !test_bit(QDF_USER, &qd_b->qd_flags)) { - if (test_bit(QDF_USER, &qd_a->qd_flags)) - return -1; - else - return 1; - } - if (qd_a->qd_id < qd_b->qd_id) + if (qid_lt(qd_a->qd_id, qd_b->qd_id)) return -1; - if (qd_a->qd_id > qd_b->qd_id) + if (qid_lt(qd_b->qd_id, qd_a->qd_id)) return 1; - return 0; } @@ -602,9 +591,9 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) if (!test_bit(QDF_CHANGE, &qd->qd_flags)) { qc->qc_change = 0; qc->qc_flags = 0; - if (test_bit(QDF_USER, &qd->qd_flags)) + if (qd->qd_id.type == USRQUOTA) qc->qc_flags = cpu_to_be32(GFS2_QCF_USER); - qc->qc_id = cpu_to_be32(qd->qd_id); + qc->qc_id = cpu_to_be32(from_kqid(&init_user_ns, qd->qd_id)); } x = be64_to_cpu(qc->qc_change) + change; @@ -1047,8 +1036,8 @@ static int print_message(struct gfs2_quota_data *qd, char *type) printk(KERN_INFO "GFS2: fsid=%s: quota %s for %s %u\n", sdp->sd_fsname, type, - (test_bit(QDF_USER, &qd->qd_flags)) ? "user" : "group", - qd->qd_id); + (qd->qd_id.type == USRQUOTA) ? "user" : "group", + from_kqid(&init_user_ns, qd->qd_id)); return 0; } @@ -1070,8 +1059,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) { qd = ip->i_res->rs_qa_qd[x]; - if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) || - (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags)))) + if (!(qid_eq(qd->qd_id, make_kqid_uid(uid)) || + qid_eq(qd->qd_id, make_kqid_gid(gid)))) continue; value = (s64)be64_to_cpu(qd->qd_qb.qb_value); @@ -1081,10 +1070,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) { print_message(qd, "exceeded"); - quota_send_warning(make_kqid(&init_user_ns, - test_bit(QDF_USER, &qd->qd_flags) ? - USRQUOTA : GRPQUOTA, - qd->qd_id), + quota_send_warning(qd->qd_id, sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN); error = -EDQUOT; @@ -1094,10 +1080,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) time_after_eq(jiffies, qd->qd_last_warn + gfs2_tune_get(sdp, gt_quota_warn_period) * HZ)) { - quota_send_warning(make_kqid(&init_user_ns, - test_bit(QDF_USER, &qd->qd_flags) ? - USRQUOTA : GRPQUOTA, - qd->qd_id), + quota_send_warning(qd->qd_id, sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN); error = print_message(qd, "warning"); qd->qd_last_warn = jiffies; @@ -1121,8 +1104,8 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change, for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) { qd = ip->i_res->rs_qa_qd[x]; - if ((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) || - (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags))) { + if (qid_eq(qd->qd_id, make_kqid_uid(uid)) || + qid_eq(qd->qd_id, make_kqid_gid(gid))) { do_qc(qd, change); } } @@ -1183,8 +1166,7 @@ int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid) struct gfs2_holder q_gh; int error; - error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, - from_kqid(&init_user_ns, qid), &qd); + error = qd_get(sdp, qid, &qd); if (error) return error; @@ -1267,8 +1249,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) if (!qc.qc_change) continue; - error = qd_alloc(sdp, (qc.qc_flags & GFS2_QCF_USER), - from_kqid(&init_user_ns, qc.qc_id), &qd); + error = qd_alloc(sdp, qc.qc_id, &qd); if (error) { brelse(bh); goto fail; @@ -1509,7 +1490,7 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, else return -EINVAL; - error = qd_get(sdp, type, from_kqid(&init_user_ns, qid), &qd); + error = qd_get(sdp, qid, &qd); if (error) return error; error = do_glock(qd, FORCE, &q_gh); @@ -1564,7 +1545,7 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, if (fdq->d_fieldmask & ~GFS2_FIELDMASK) return -EINVAL; - error = qd_get(sdp, type, from_kqid(&init_user_ns, qid), &qd); + error = qd_get(sdp, qid, &qd); if (error) return error; From 236c64e4b79b78059ec3e17362d8f02f6dc06f26 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 20:09:30 -0800 Subject: [PATCH 43/93] gfs2: Remove the QUOTA_USER and QUOTA_GROUP defines Remove the QUOTA_USER and QUOTA_GRUP defines. Remove the last vestigal users of QUOTA_USER and QUOTA_GROUP. Now that struct kqid is used throughout the gfs2 quota code the need there is to use QUOTA_USER and QUOTA_GROUP and the defines are just extraneous and confusing. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 8cb4d10cb16a..0bbb0407fd96 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -65,9 +65,6 @@ #include "inode.h" #include "util.h" -#define QUOTA_USER 1 -#define QUOTA_GROUP 0 - struct gfs2_quota_change_host { u64 qc_change; u32 qc_flags; /* GFS2_QCF_... */ @@ -1476,18 +1473,14 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, struct gfs2_quota_data *qd; struct gfs2_holder q_gh; int error; - int type; memset(fdq, 0, sizeof(struct fs_disk_quota)); if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return -ESRCH; /* Crazy XFS error code */ - if (qid.type == USRQUOTA) - type = QUOTA_USER; - else if (qid.type == GRPQUOTA) - type = QUOTA_GROUP; - else + if ((qid.type != USRQUOTA) && + (qid.type != GRPQUOTA)) return -EINVAL; error = qd_get(sdp, qid, &qd); @@ -1499,7 +1492,7 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr; fdq->d_version = FS_DQUOT_VERSION; - fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA; + fdq->d_flags = (qid.type == USRQUOTA) ? FS_USER_QUOTA : FS_GROUP_QUOTA; fdq->d_id = from_kqid_munged(current_user_ns(), qid); fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift; fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift; @@ -1526,21 +1519,13 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, int alloc_required; loff_t offset; int error; - int type; if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return -ESRCH; /* Crazy XFS error code */ - switch(qid.type) { - case USRQUOTA: - type = QUOTA_USER; - break; - case GRPQUOTA: - type = QUOTA_GROUP; - break; - default: + if ((qid.type != USRQUOTA) && + (qid.type != GRPQUOTA)) return -EINVAL; - } if (fdq->d_fieldmask & ~GFS2_FIELDMASK) return -EINVAL; From 7c06b5d67225dc99ca81a33db3e055e08da857c3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 20:27:54 -0800 Subject: [PATCH 44/93] gfs2: Use kuid_t and kgid_t types where appropriate. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/inode.c | 3 ++- fs/gfs2/quota.c | 8 ++++---- fs/gfs2/quota.h | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index aa1050287f3c..ce07ce4734f4 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1580,7 +1580,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); - u32 ouid, ogid, nuid, ngid; + kuid_t ouid, nuid; + kgid_t ogid, ngid; int error; ouid = inode->i_uid; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 0bbb0407fd96..87f274039c4b 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -492,7 +492,7 @@ static void qdsb_put(struct gfs2_quota_data *qd) qd_put(qd); } -int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) +int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data **qd; @@ -918,7 +918,7 @@ static int do_glock(struct gfs2_quota_data *qd, int force_refresh, return error; } -int gfs2_quota_lock(struct gfs2_inode *ip, u32 uid, u32 gid) +int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; @@ -1039,7 +1039,7 @@ static int print_message(struct gfs2_quota_data *qd, char *type) return 0; } -int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) +int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; @@ -1088,7 +1088,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) } void gfs2_quota_change(struct gfs2_inode *ip, s64 change, - u32 uid, u32 gid) + kuid_t uid, kgid_t gid) { struct gfs2_quota_data *qd; unsigned int x; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index bef805de8491..4f5e6e44ed83 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -17,15 +17,15 @@ struct shrink_control; #define NO_UID_QUOTA_CHANGE INVALID_UID #define NO_GID_QUOTA_CHANGE INVALID_GID -extern int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid); +extern int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_unhold(struct gfs2_inode *ip); -extern int gfs2_quota_lock(struct gfs2_inode *ip, u32 uid, u32 gid); +extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_unlock(struct gfs2_inode *ip); -extern int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid); +extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, - u32 uid, u32 gid); + kuid_t uid, kgid_t gid); extern int gfs2_quota_sync(struct super_block *sb, int type); extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid); From 6b24c0d279eacfb499854d09ea7f2b69d1721a29 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 21:56:13 -0800 Subject: [PATCH 45/93] gfs2: Use uid_eq and gid_eq where appropriate Where kuid_t values are compared use uid_eq and where kgid_t values are compared use gid_eq. This is unfortunately necessary because of the type safety that keeps someone from accidentally mixing kuids and kgids with other types. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/acl.c | 2 +- fs/gfs2/inode.c | 19 +++++++++++-------- fs/gfs2/quota.c | 6 ++++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index f850020ad906..f69ac0af5496 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -237,7 +237,7 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name, return -EINVAL; if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) return value ? -EACCES : 0; - if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER)) + if (!uid_eq(current_fsuid(), inode->i_uid) && !capable(CAP_FOWNER)) return -EPERM; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index ce07ce4734f4..bb7c754f46a2 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -368,10 +368,11 @@ static void munge_mode_uid_gid(const struct gfs2_inode *dip, struct inode *inode) { if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && - (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) { + (dip->i_inode.i_mode & S_ISUID) && + !uid_eq(dip->i_inode.i_uid, GLOBAL_ROOT_UID)) { if (S_ISDIR(inode->i_mode)) inode->i_mode |= S_ISUID; - else if (dip->i_inode.i_uid != current_fsuid()) + else if (!uid_eq(dip->i_inode.i_uid, current_fsuid())) inode->i_mode &= ~07111; inode->i_uid = dip->i_inode.i_uid; } else @@ -978,8 +979,8 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, return -EPERM; if ((dip->i_inode.i_mode & S_ISVTX) && - dip->i_inode.i_uid != current_fsuid() && - ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER)) + !uid_eq(dip->i_inode.i_uid, current_fsuid()) && + !uid_eq(ip->i_inode.i_uid, current_fsuid()) && !capable(CAP_FOWNER)) return -EPERM; if (IS_APPEND(&dip->i_inode)) @@ -1589,16 +1590,17 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) nuid = attr->ia_uid; ngid = attr->ia_gid; - if (!(attr->ia_valid & ATTR_UID) || ouid == nuid) + if (!(attr->ia_valid & ATTR_UID) || uid_eq(ouid, nuid)) ouid = nuid = NO_UID_QUOTA_CHANGE; - if (!(attr->ia_valid & ATTR_GID) || ogid == ngid) + if (!(attr->ia_valid & ATTR_GID) || gid_eq(ogid, ngid)) ogid = ngid = NO_GID_QUOTA_CHANGE; error = gfs2_quota_lock(ip, nuid, ngid); if (error) return error; - if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { + if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || + !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { error = gfs2_quota_check(ip, nuid, ngid); if (error) goto out_gunlock_q; @@ -1612,7 +1614,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (error) goto out_end_trans; - if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { + if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || + !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); gfs2_quota_change(ip, -blocks, ouid, ogid); gfs2_quota_change(ip, blocks, nuid, ngid); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 87f274039c4b..afd2e5d38e5a 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -525,7 +525,8 @@ int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) ip->i_res->rs_qa_qd_num++; qd++; - if (uid != NO_UID_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { + if (!uid_eq(uid, NO_UID_QUOTA_CHANGE) && + !uid_eq(uid, ip->i_inode.i_uid)) { error = qdsb_get(sdp, make_kqid_uid(uid), qd); if (error) goto out; @@ -533,7 +534,8 @@ int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) qd++; } - if (gid != NO_GID_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { + if (!gid_eq(gid, NO_GID_QUOTA_CHANGE) && + !gid_eq(gid, ip->i_inode.i_gid)) { error = qdsb_get(sdp, make_kqid_gid(gid), qd); if (error) goto out; From d0546426426ca96f0b815581c0a9ed0db28319b6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 22:08:10 -0800 Subject: [PATCH 46/93] gfs2: Convert uids and gids between dinodes and vfs inodes. When reading dinodes from the disk convert uids and gids into kuids and kgids to store in vfs data structures. When writing to dinodes to the disk convert kuids and kgids in the in memory structures into plain uids and gids. For now all on disk data structures are assumed to be stored in the initial user namespace. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/glops.c | 4 ++-- fs/gfs2/inode.c | 4 ++-- fs/gfs2/super.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 78d4184ffc7d..444b6503ebc4 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -322,8 +322,8 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) break; }; - ip->i_inode.i_uid = be32_to_cpu(str->di_uid); - ip->i_inode.i_gid = be32_to_cpu(str->di_gid); + i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid)); + i_gid_write(&ip->i_inode, be32_to_cpu(str->di_gid)); gfs2_set_nlink(&ip->i_inode, be32_to_cpu(str->di_nlink)); i_size_write(&ip->i_inode, be64_to_cpu(str->di_size)); gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks)); diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index bb7c754f46a2..b862d114e155 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -456,8 +456,8 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip, di->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino); di->di_num.no_addr = cpu_to_be64(ip->i_no_addr); di->di_mode = cpu_to_be32(ip->i_inode.i_mode); - di->di_uid = cpu_to_be32(ip->i_inode.i_uid); - di->di_gid = cpu_to_be32(ip->i_inode.i_gid); + di->di_uid = cpu_to_be32(i_uid_read(&ip->i_inode)); + di->di_gid = cpu_to_be32(i_gid_read(&ip->i_inode)); di->di_nlink = 0; di->di_size = cpu_to_be64(ip->i_inode.i_size); di->di_blocks = cpu_to_be64(1); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 7cc5a6054547..a03425376500 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -721,8 +721,8 @@ void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) str->di_num.no_addr = cpu_to_be64(ip->i_no_addr); str->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino); str->di_mode = cpu_to_be32(ip->i_inode.i_mode); - str->di_uid = cpu_to_be32(ip->i_inode.i_uid); - str->di_gid = cpu_to_be32(ip->i_inode.i_gid); + str->di_uid = cpu_to_be32(i_uid_read(&ip->i_inode)); + str->di_gid = cpu_to_be32(i_gid_read(&ip->i_inode)); str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink); str->di_size = cpu_to_be64(i_size_read(&ip->i_inode)); str->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode)); From 0f07bd3753e25c80fe24428273c791f350b3a1eb Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 22:17:00 -0800 Subject: [PATCH 47/93] gfs2: Enable building with user namespaces enabled Now that all of the necessary work has been done to push kuids and kgids throughout gfs2 and to convert between kuids and kgids when reading and writing the on disk structures it is safe to enable gfs2 when multiple user namespaces are enabled. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 9cb63c94cab8..591fc75710b3 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on GFS2_FS = n depends on NCP_FS = n depends on NFSD = n depends on NFS_FS = n From 1ac7fd8190b79c822631ed537186fb8b2d9e9b74 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:28:28 -0800 Subject: [PATCH 48/93] ncpfs: Support interacting with multiple user namespaces ncpfs does not natively support uids and gids so this conversion was simply a matter of updating the the type of the mounteduid, the uid and the gid on the superblock. Fixing the ioctls that read them, updating the mount option parser and the mount option printer. Cc: Petr Vandrovec Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/ncpfs/inode.c | 55 ++++++++++++++++++++++++++------------------ fs/ncpfs/ioctl.c | 25 +++++++++++--------- fs/ncpfs/ncp_fs_sb.h | 6 ++--- init/Kconfig | 1 - 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 1acdad7fcec7..e2be336d1c22 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -331,12 +331,15 @@ static int ncp_show_options(struct seq_file *seq, struct dentry *root) struct ncp_server *server = NCP_SBP(root->d_sb); unsigned int tmp; - if (server->m.uid != 0) - seq_printf(seq, ",uid=%u", server->m.uid); - if (server->m.gid != 0) - seq_printf(seq, ",gid=%u", server->m.gid); - if (server->m.mounted_uid != 0) - seq_printf(seq, ",owner=%u", server->m.mounted_uid); + if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID)) + seq_printf(seq, ",uid=%u", + from_kuid_munged(&init_user_ns, server->m.uid)); + if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID)) + seq_printf(seq, ",gid=%u", + from_kgid_munged(&init_user_ns, server->m.gid)); + if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID)) + seq_printf(seq, ",owner=%u", + from_kuid_munged(&init_user_ns, server->m.mounted_uid)); tmp = server->m.file_mode & S_IALLUGO; if (tmp != NCP_DEFAULT_FILE_MODE) seq_printf(seq, ",mode=0%o", tmp); @@ -381,13 +384,13 @@ static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) data->flags = 0; data->int_flags = 0; - data->mounted_uid = 0; + data->mounted_uid = GLOBAL_ROOT_UID; data->wdog_pid = NULL; data->ncp_fd = ~0; data->time_out = NCP_DEFAULT_TIME_OUT; data->retry_count = NCP_DEFAULT_RETRY_COUNT; - data->uid = 0; - data->gid = 0; + data->uid = GLOBAL_ROOT_UID; + data->gid = GLOBAL_ROOT_GID; data->file_mode = NCP_DEFAULT_FILE_MODE; data->dir_mode = NCP_DEFAULT_DIR_MODE; data->info_fd = -1; @@ -399,13 +402,19 @@ static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) goto err; switch (optval) { case 'u': - data->uid = optint; + data->uid = make_kuid(current_user_ns(), optint); + if (!uid_valid(data->uid)) + goto err; break; case 'g': - data->gid = optint; + data->gid = make_kgid(current_user_ns(), optint); + if (!gid_valid(data->gid)) + goto err; break; case 'o': - data->mounted_uid = optint; + data->mounted_uid = make_kuid(current_user_ns(), optint); + if (!uid_valid(data->mounted_uid)) + goto err; break; case 'm': data->file_mode = optint; @@ -480,13 +489,13 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) data.flags = md->flags; data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE; - data.mounted_uid = md->mounted_uid; + data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid); data.wdog_pid = find_get_pid(md->wdog_pid); data.ncp_fd = md->ncp_fd; data.time_out = md->time_out; data.retry_count = md->retry_count; - data.uid = md->uid; - data.gid = md->gid; + data.uid = make_kuid(current_user_ns(), md->uid); + data.gid = make_kgid(current_user_ns(), md->gid); data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; data.info_fd = -1; @@ -499,13 +508,13 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data; data.flags = md->flags; - data.mounted_uid = md->mounted_uid; + data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid); data.wdog_pid = find_get_pid(md->wdog_pid); data.ncp_fd = md->ncp_fd; data.time_out = md->time_out; data.retry_count = md->retry_count; - data.uid = md->uid; - data.gid = md->gid; + data.uid = make_kuid(current_user_ns(), md->uid); + data.gid = make_kgid(current_user_ns(), md->gid); data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; data.info_fd = -1; @@ -520,6 +529,10 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) goto out; break; } + error = -EINVAL; + if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) || + !gid_valid(data.gid)) + goto out; error = -EBADF; ncp_filp = fget(data.ncp_fd); if (!ncp_filp) @@ -886,12 +899,10 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) goto out; result = -EPERM; - if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != server->m.uid))) + if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid)) goto out; - if (((attr->ia_valid & ATTR_GID) && - (attr->ia_gid != server->m.gid))) + if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid)) goto out; if (((attr->ia_valid & ATTR_MODE) && diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 6958adfaff08..d44318d27504 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -45,7 +45,7 @@ ncp_get_fs_info(struct ncp_server * server, struct inode *inode, return -EINVAL; } /* TODO: info.addr = server->m.serv_addr; */ - SET_UID(info.mounted_uid, server->m.mounted_uid); + SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid)); info.connection = server->connection; info.buffer_size = server->buffer_size; info.volume_number = NCP_FINFO(inode)->volNumber; @@ -69,7 +69,7 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode, DPRINTK("info.version invalid: %d\n", info2.version); return -EINVAL; } - info2.mounted_uid = server->m.mounted_uid; + info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); info2.connection = server->connection; info2.buffer_size = server->buffer_size; info2.volume_number = NCP_FINFO(inode)->volNumber; @@ -135,7 +135,7 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode, DPRINTK("info.version invalid: %d\n", info2.version); return -EINVAL; } - info2.mounted_uid = server->m.mounted_uid; + info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); info2.connection = server->connection; info2.buffer_size = server->buffer_size; info2.volume_number = NCP_FINFO(inode)->volNumber; @@ -348,22 +348,25 @@ static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg { u16 uid; - SET_UID(uid, server->m.mounted_uid); + SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid)); if (put_user(uid, (u16 __user *)argp)) return -EFAULT; return 0; } case NCP_IOC_GETMOUNTUID32: - if (put_user(server->m.mounted_uid, - (u32 __user *)argp)) + { + uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); + if (put_user(uid, (u32 __user *)argp)) return -EFAULT; return 0; + } case NCP_IOC_GETMOUNTUID64: - if (put_user(server->m.mounted_uid, - (u64 __user *)argp)) + { + uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); + if (put_user(uid, (u64 __user *)argp)) return -EFAULT; return 0; - + } case NCP_IOC_GETROOT: { struct ncp_setroot_ioctl sr; @@ -810,7 +813,7 @@ long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct ncp_server *server = NCP_SERVER(inode); - uid_t uid = current_uid(); + kuid_t uid = current_uid(); int need_drop_write = 0; long ret; @@ -824,7 +827,7 @@ long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } - if (server->m.mounted_uid != uid) { + if (!uid_eq(server->m.mounted_uid, uid)) { switch (cmd) { /* * Only mount owner can issue these ioctls. Information diff --git a/fs/ncpfs/ncp_fs_sb.h b/fs/ncpfs/ncp_fs_sb.h index 54cc0cdb3dcb..c51b2c543539 100644 --- a/fs/ncpfs/ncp_fs_sb.h +++ b/fs/ncpfs/ncp_fs_sb.h @@ -23,15 +23,15 @@ struct ncp_mount_data_kernel { unsigned long flags; /* NCP_MOUNT_* flags */ unsigned int int_flags; /* internal flags */ #define NCP_IMOUNT_LOGGEDIN_POSSIBLE 0x0001 - uid_t mounted_uid; /* Who may umount() this filesystem? */ + kuid_t mounted_uid; /* Who may umount() this filesystem? */ struct pid *wdog_pid; /* Who cares for our watchdog packets? */ unsigned int ncp_fd; /* The socket to the ncp port */ unsigned int time_out; /* How long should I wait after sending a NCP request? */ unsigned int retry_count; /* And how often should I retry? */ unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; umode_t file_mode; umode_t dir_mode; int info_fd; diff --git a/init/Kconfig b/init/Kconfig index 591fc75710b3..b526f4c35b95 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on NCP_FS = n depends on NFSD = n depends on NFS_FS = n depends on XFS_FS = n From ddca4e1730cf1b72044ae76ddf17b29d790b4dbc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:50:52 -0800 Subject: [PATCH 49/93] nfs_common: Update the translation between nfsv3 acls linux posix acls - Use kuid_t and kgit in struct nfsacl_encode_desc. - Convert from kuids and kgids when generating on the wire values. - Convert on the wire values to kuids and kgids when read. - Modify cmp_acl_entry to be type safe comparison on posix acls. Only acls with type ACL_USER and ACL_GROUP can appear more than once and as such need to compare more than their tag. - The e_id field is being removed from posix acls so don't initialize it. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs_common/nfsacl.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c index 6940439bd609..ed628f71274c 100644 --- a/fs/nfs_common/nfsacl.c +++ b/fs/nfs_common/nfsacl.c @@ -38,8 +38,8 @@ struct nfsacl_encode_desc { unsigned int count; struct posix_acl *acl; int typeflag; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; }; struct nfsacl_simple_acl { @@ -60,14 +60,16 @@ xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem) *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag); switch(entry->e_tag) { case ACL_USER_OBJ: - *p++ = htonl(nfsacl_desc->uid); + *p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid)); break; case ACL_GROUP_OBJ: - *p++ = htonl(nfsacl_desc->gid); + *p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid)); break; case ACL_USER: + *p++ = htonl(from_kuid(&init_user_ns, entry->e_uid)); + break; case ACL_GROUP: - *p++ = htonl(entry->e_id); + *p++ = htonl(from_kgid(&init_user_ns, entry->e_gid)); break; default: /* Solaris depends on that! */ *p++ = 0; @@ -148,6 +150,7 @@ xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem) (struct nfsacl_decode_desc *) desc; __be32 *p = elem; struct posix_acl_entry *entry; + unsigned int id; if (!nfsacl_desc->acl) { if (desc->array_len > NFS_ACL_MAX_ENTRIES) @@ -160,14 +163,22 @@ xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem) entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++]; entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT; - entry->e_id = ntohl(*p++); + id = ntohl(*p++); entry->e_perm = ntohl(*p++); switch(entry->e_tag) { - case ACL_USER_OBJ: case ACL_USER: - case ACL_GROUP_OBJ: + entry->e_uid = make_kuid(&init_user_ns, id); + if (!uid_valid(entry->e_uid)) + return -EINVAL; + break; case ACL_GROUP: + entry->e_gid = make_kgid(&init_user_ns, id); + if (!gid_valid(entry->e_gid)) + return -EINVAL; + break; + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: case ACL_OTHER: if (entry->e_perm & ~S_IRWXO) return -EINVAL; @@ -190,9 +201,13 @@ cmp_acl_entry(const void *x, const void *y) if (a->e_tag != b->e_tag) return a->e_tag - b->e_tag; - else if (a->e_id > b->e_id) + else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid)) return 1; - else if (a->e_id < b->e_id) + else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid)) + return -1; + else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid)) + return 1; + else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid)) return -1; else return 0; @@ -213,22 +228,18 @@ posix_acl_from_nfsacl(struct posix_acl *acl) sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry), cmp_acl_entry, NULL); - /* Clear undefined identifier fields and find the ACL_GROUP_OBJ - and ACL_MASK entries. */ + /* Find the ACL_GROUP_OBJ and ACL_MASK entries. */ FOREACH_ACL_ENTRY(pa, acl, pe) { switch(pa->e_tag) { case ACL_USER_OBJ: - pa->e_id = ACL_UNDEFINED_ID; break; case ACL_GROUP_OBJ: - pa->e_id = ACL_UNDEFINED_ID; group_obj = pa; break; case ACL_MASK: mask = pa; /* fall through */ case ACL_OTHER: - pa->e_id = ACL_UNDEFINED_ID; break; } } From bf37f794372d5b8fda66702e1f3e70d4f07b6533 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 15:55:38 -0800 Subject: [PATCH 50/93] sunrpc: Use userns friendly constants. Instead of (uid_t)0 use GLOBAL_ROOT_UID. Instead of (gid_t)0 use GLOBAL_ROOT_GID. Instead of (uid_t)-1 use INVALID_UID Instead of (gid_t)-1 use INVALID_GID. Instead of NOGROUP use INVALID_GID. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth.c | 4 ++-- net/sunrpc/auth_generic.c | 4 ++-- net/sunrpc/auth_unix.c | 6 +++--- net/sunrpc/svcauth_unix.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index b5c067bccc45..4cd0ecfe9837 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -519,8 +519,8 @@ rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) { struct rpc_auth *auth = task->tk_client->cl_auth; struct auth_cred acred = { - .uid = 0, - .gid = 0, + .uid = GLOBAL_ROOT_UID, + .gid = GLOBAL_ROOT_GID, }; dprintk("RPC: %5u looking up %s cred\n", diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 6ed6f201b022..9d2e0b045596 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -18,8 +18,8 @@ # define RPCDBG_FACILITY RPCDBG_AUTH #endif -#define RPC_MACHINE_CRED_USERID ((uid_t)0) -#define RPC_MACHINE_CRED_GROUPID ((gid_t)0) +#define RPC_MACHINE_CRED_USERID GLOBAL_ROOT_UID +#define RPC_MACHINE_CRED_GROUPID GLOBAL_ROOT_GID struct generic_cred { struct rpc_cred gc_base; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 52c5abdee211..2f8627082fa7 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -85,7 +85,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) cred->uc_gids[i] = gid; } if (i < NFS_NGROUPS) - cred->uc_gids[i] = NOGROUP; + cred->uc_gids[i] = INVALID_GID; return &cred->uc_base; } @@ -137,7 +137,7 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) return 0; } if (groups < NFS_NGROUPS && - cred->uc_gids[groups] != NOGROUP) + cred->uc_gids[groups] != INVALID_GID) return 0; return 1; } @@ -166,7 +166,7 @@ unx_marshal(struct rpc_task *task, __be32 *p) *p++ = htonl((u32) cred->uc_uid); *p++ = htonl((u32) cred->uc_gid); hold = p++; - for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++) + for (i = 0; i < 16 && cred->uc_gids[i] != INVALID_GID; i++) *p++ = htonl((u32) cred->uc_gids[i]); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 4d0129203733..c8cb5dc8d871 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -750,8 +750,8 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) } /* Signal that mapping to nobody uid/gid is required */ - cred->cr_uid = (uid_t) -1; - cred->cr_gid = (gid_t) -1; + cred->cr_uid = INVALID_UID; + cred->cr_gid = INVALID_GID; cred->cr_group_info = groups_alloc(0); if (cred->cr_group_info == NULL) return SVC_CLOSE; /* kmalloc failure - client must retry */ From 7eaf040b720bc8c0ce5cd49151ca194ca2d56842 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 16:31:17 -0800 Subject: [PATCH 51/93] sunrpc: Use kuid_t and kgid_t where appropriate Convert variables that store uids and gids to be of type kuid_t and kgid_t instead of type uid_t and gid_t. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- include/linux/sunrpc/auth.h | 7 ++++--- include/linux/sunrpc/svcauth.h | 4 ++-- net/sunrpc/auth_gss/auth_gss.c | 8 ++++---- net/sunrpc/auth_unix.c | 4 ++-- net/sunrpc/svcauth_unix.c | 8 ++++---- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index f25ba922baaf..58fda1c3c783 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -17,14 +17,15 @@ #include #include +#include /* size of the nodename buffer */ #define UNX_MAXNODENAME 32 /* Work around the lack of a VFS credential */ struct auth_cred { - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct group_info *group_info; const char *principal; unsigned char machine_cred : 1; @@ -48,7 +49,7 @@ struct rpc_cred { unsigned long cr_flags; /* various flags */ atomic_t cr_count; /* ref count */ - uid_t cr_uid; + kuid_t cr_uid; /* per-flavor data */ }; diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index dd74084a9799..ff374ab30839 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -18,8 +18,8 @@ #include struct svc_cred { - uid_t cr_uid; - gid_t cr_gid; + kuid_t cr_uid; + kgid_t cr_gid; struct group_info *cr_group_info; u32 cr_flavor; /* pseudoflavor */ char *cr_principal; /* for gss */ diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 6e5c824b040b..4daab81ca337 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -256,7 +256,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct struct gss_upcall_msg { atomic_t count; - uid_t uid; + kuid_t uid; struct rpc_pipe_msg msg; struct list_head list; struct gss_auth *auth; @@ -303,7 +303,7 @@ gss_release_msg(struct gss_upcall_msg *gss_msg) } static struct gss_upcall_msg * -__gss_find_upcall(struct rpc_pipe *pipe, uid_t uid) +__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid) { struct gss_upcall_msg *pos; list_for_each_entry(pos, &pipe->in_downcall, list) { @@ -445,7 +445,7 @@ static void gss_encode_msg(struct gss_upcall_msg *gss_msg, static struct gss_upcall_msg * gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt, - uid_t uid, const char *service_name) + kuid_t uid, const char *service_name) { struct gss_upcall_msg *gss_msg; int vers; @@ -475,7 +475,7 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_new, *gss_msg; - uid_t uid = cred->cr_uid; + kuid_t uid = cred->cr_uid; gss_new = gss_alloc_msg(gss_auth, clnt, uid, gss_cred->gc_principal); if (IS_ERR(gss_new)) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 2f8627082fa7..372d9156f6e3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -18,8 +18,8 @@ struct unx_cred { struct rpc_cred uc_base; - gid_t uc_gid; - gid_t uc_gids[NFS_NGROUPS]; + kgid_t uc_gid; + kgid_t uc_gids[NFS_NGROUPS]; }; #define uc_uid uc_base.cr_uid diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index c8cb5dc8d871..caae662f9fa3 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -415,7 +415,7 @@ svcauth_unix_info_release(struct svc_xprt *xpt) struct unix_gid { struct cache_head h; - uid_t uid; + kuid_t uid; struct group_info *gi; }; @@ -475,7 +475,7 @@ static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h) return sunrpc_cache_pipe_upcall(cd, h, unix_gid_request); } -static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid); +static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid); static int unix_gid_parse(struct cache_detail *cd, char *mesg, int mlen) @@ -615,7 +615,7 @@ void unix_gid_cache_destroy(struct net *net) cache_destroy_net(cd, net); } -static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid) +static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid) { struct unix_gid ug; struct cache_head *ch; @@ -628,7 +628,7 @@ static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid) return NULL; } -static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp) +static struct group_info *unix_gid_find(kuid_t uid, struct svc_rqst *rqstp) { struct unix_gid *ug; struct group_info *gi; From 0b4d51b02a2e941beec6f02a6c7a32c5a28c5b43 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 16:39:32 -0800 Subject: [PATCH 52/93] sunrpc: Use uid_eq and gid_eq where appropriate When comparing uids use uid_eq instead of ==. When comparing gids use gid_eq instead of ==. And unfortunate cost of type safety. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_generic.c | 8 ++++---- net/sunrpc/auth_gss/auth_gss.c | 4 ++-- net/sunrpc/auth_unix.c | 2 +- net/sunrpc/svcauth_unix.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 9d2e0b045596..bff3e4730f6b 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -129,8 +129,8 @@ machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flag { if (!gcred->acred.machine_cred || gcred->acred.principal != acred->principal || - gcred->acred.uid != acred->uid || - gcred->acred.gid != acred->gid) + !uid_eq(gcred->acred.uid, acred->uid) || + !gid_eq(gcred->acred.gid, acred->gid)) return 0; return 1; } @@ -147,8 +147,8 @@ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) if (acred->machine_cred) return machine_cred_match(acred, gcred, flags); - if (gcred->acred.uid != acred->uid || - gcred->acred.gid != acred->gid || + if (!uid_eq(gcred->acred.uid, acred->uid) || + !gid_eq(gcred->acred.gid, acred->gid) || gcred->acred.machine_cred != 0) goto out_nomatch; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 4daab81ca337..1b8b3e4fad46 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -307,7 +307,7 @@ __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid) { struct gss_upcall_msg *pos; list_for_each_entry(pos, &pipe->in_downcall, list) { - if (pos->uid != uid) + if (!uid_eq(pos->uid, uid)) continue; atomic_inc(&pos->count); dprintk("RPC: %s found msg %p\n", __func__, pos); @@ -1115,7 +1115,7 @@ gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) } if (gss_cred->gc_principal != NULL) return 0; - return rc->cr_uid == acred->uid; + return uid_eq(rc->cr_uid, acred->uid); } /* diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 372d9156f6e3..8365a9cade98 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -123,7 +123,7 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) unsigned int i; - if (cred->uc_uid != acred->uid || cred->uc_gid != acred->gid) + if (!uid_eq(cred->uc_uid, acred->uid) || !gid_eq(cred->uc_gid, acred->gid)) return 0; if (acred->group_info != NULL) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index caae662f9fa3..92166b57ec7a 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -433,7 +433,7 @@ static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew) { struct unix_gid *orig = container_of(corig, struct unix_gid, h); struct unix_gid *new = container_of(cnew, struct unix_gid, h); - return orig->uid == new->uid; + return uid_eq(orig->uid, new->uid); } static void unix_gid_init(struct cache_head *cnew, struct cache_head *citem) { From 9132adb0212c7ddf37d1c2a26c12f8fe7706827d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 16:46:42 -0800 Subject: [PATCH 53/93] sunrpc: Simplify auth_unix now that everything is a kgid_t In unx_create_cred directly assign gids from acred->group_info to cred->uc_gids. In unx_match directly compare uc_gids with group_info. Now that both group_info and unx_cred gids are stored as kgids this is valid and the extra layer of translation can be removed. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_unix.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 8365a9cade98..9f3885745fb3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -79,11 +79,8 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) groups = NFS_NGROUPS; cred->uc_gid = acred->gid; - for (i = 0; i < groups; i++) { - gid_t gid; - gid = from_kgid(&init_user_ns, GROUP_AT(acred->group_info, i)); - cred->uc_gids[i] = gid; - } + for (i = 0; i < groups; i++) + cred->uc_gids[i] = GROUP_AT(acred->group_info, i); if (i < NFS_NGROUPS) cred->uc_gids[i] = INVALID_GID; @@ -130,12 +127,9 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) groups = acred->group_info->ngroups; if (groups > NFS_NGROUPS) groups = NFS_NGROUPS; - for (i = 0; i < groups ; i++) { - gid_t gid; - gid = from_kgid(&init_user_ns, GROUP_AT(acred->group_info, i)); - if (cred->uc_gids[i] != gid) + for (i = 0; i < groups ; i++) + if (!gid_eq(cred->uc_gids[i], GROUP_AT(acred->group_info, i))) return 0; - } if (groups < NFS_NGROUPS && cred->uc_gids[groups] != INVALID_GID) return 0; From cdba321e291f0fbf5abda4d88340292b858e3d4d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 17:10:52 -0800 Subject: [PATCH 54/93] sunrpc: Convert kuids and kgids to uids and gids for printing When printing kuids and kgids for debugging purpropses convert them to ordinary integers so their values can be fed to the oridnary print functions. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_generic.c | 4 +++- net/sunrpc/auth_gss/auth_gss.c | 13 ++++++++----- net/sunrpc/auth_unix.c | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index bff3e4730f6b..b6badafc6494 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -96,7 +96,9 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) dprintk("RPC: allocated %s cred %p for uid %d gid %d\n", gcred->acred.machine_cred ? "machine" : "generic", - gcred, acred->uid, acred->gid); + gcred, + from_kuid(&init_user_ns, acred->uid), + from_kgid(&init_user_ns, acred->gid)); return &gcred->gc_base; } diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 1b8b3e4fad46..afbbcfb1078b 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -517,7 +517,7 @@ gss_refresh_upcall(struct rpc_task *task) int err = 0; dprintk("RPC: %5u %s for uid %u\n", - task->tk_pid, __func__, cred->cr_uid); + task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid)); gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); if (PTR_ERR(gss_msg) == -EAGAIN) { /* XXX: warning on the first, under the assumption we @@ -549,7 +549,8 @@ gss_refresh_upcall(struct rpc_task *task) gss_release_msg(gss_msg); out: dprintk("RPC: %5u %s for uid %u result %d\n", - task->tk_pid, __func__, cred->cr_uid, err); + task->tk_pid, __func__, + from_kuid(&init_user_ns, cred->cr_uid), err); return err; } @@ -562,7 +563,8 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) DEFINE_WAIT(wait); int err = 0; - dprintk("RPC: %s for uid %u\n", __func__, cred->cr_uid); + dprintk("RPC: %s for uid %u\n", + __func__, from_kuid(&init_user_ns, cred->cr_uid)); retry: gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); if (PTR_ERR(gss_msg) == -EAGAIN) { @@ -604,7 +606,7 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) gss_release_msg(gss_msg); out: dprintk("RPC: %s for uid %u result %d\n", - __func__, cred->cr_uid, err); + __func__, from_kuid(&init_user_ns, cred->cr_uid), err); return err; } @@ -1059,7 +1061,8 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) int err = -ENOMEM; dprintk("RPC: %s for uid %d, flavor %d\n", - __func__, acred->uid, auth->au_flavor); + __func__, from_kuid(&init_user_ns, acred->uid), + auth->au_flavor); if (!(cred = kzalloc(sizeof(*cred), GFP_NOFS))) goto out_err; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 9f3885745fb3..55b6ca6fbbd3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -65,7 +65,8 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) unsigned int i; dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", - acred->uid, acred->gid); + from_kuid(&init_user_ns, acred->uid), + from_kgid(&init_user_ns, acred->gid)); if (!(cred = kmalloc(sizeof(*cred), GFP_NOFS))) return ERR_PTR(-ENOMEM); From e572fc739822ad779493b8a72bd27f2101fc3373 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 17:18:04 -0800 Subject: [PATCH 55/93] sunrpc: Use gid_valid to test for gid != INVALID_GID In auth unix there are a couple of places INVALID_GID is used a sentinel to mark the end of uc_gids array. Use gid_valid as a type safe way to verify we have not hit the end of valid data in the array. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_unix.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 55b6ca6fbbd3..c434fde2079b 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -131,8 +131,7 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) for (i = 0; i < groups ; i++) if (!gid_eq(cred->uc_gids[i], GROUP_AT(acred->group_info, i))) return 0; - if (groups < NFS_NGROUPS && - cred->uc_gids[groups] != INVALID_GID) + if (groups < NFS_NGROUPS && gid_valid(cred->uc_gids[groups])) return 0; return 1; } @@ -161,7 +160,7 @@ unx_marshal(struct rpc_task *task, __be32 *p) *p++ = htonl((u32) cred->uc_uid); *p++ = htonl((u32) cred->uc_gid); hold = p++; - for (i = 0; i < 16 && cred->uc_gids[i] != INVALID_GID; i++) + for (i = 0; i < 16 && gid_valid(cred->uc_gids[i]); i++) *p++ = htonl((u32) cred->uc_gids[i]); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ From 90602c7b192fdd3e6b7c7623479f4bc86ed7ee34 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 00:25:43 -0800 Subject: [PATCH 56/93] sunrpc: Update gss uid to security context mapping. - Use from_kuid when generating the on the wire uid values. - Use make_kuid when reading on the wire values. In gss_encode_v0_msg, since the uid in gss_upcall_msg is now a kuid_t generate the necessary uid_t value on the stack copy it into gss_msg->databuf where it can safely live until the message is no longer needed. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_gss/auth_gss.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index afbbcfb1078b..a3600671989a 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -395,8 +395,11 @@ gss_upcall_callback(struct rpc_task *task) static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg) { - gss_msg->msg.data = &gss_msg->uid; - gss_msg->msg.len = sizeof(gss_msg->uid); + uid_t uid = from_kuid(&init_user_ns, gss_msg->uid); + memcpy(gss_msg->databuf, &uid, sizeof(uid)); + gss_msg->msg.data = gss_msg->databuf; + gss_msg->msg.len = sizeof(uid); + BUG_ON(sizeof(uid) > UPCALL_BUF_LEN); } static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, @@ -409,7 +412,7 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ", mech->gm_name, - gss_msg->uid); + from_kuid(&init_user_ns, gss_msg->uid)); p += gss_msg->msg.len; if (clnt->cl_principal) { len = sprintf(p, "target=%s ", clnt->cl_principal); @@ -620,7 +623,8 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) struct gss_upcall_msg *gss_msg; struct rpc_pipe *pipe = RPC_I(filp->f_dentry->d_inode)->pipe; struct gss_cl_ctx *ctx; - uid_t uid; + uid_t id; + kuid_t uid; ssize_t err = -EFBIG; if (mlen > MSG_BUF_MAXSIZE) @@ -635,12 +639,18 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) goto err; end = (const void *)((char *)buf + mlen); - p = simple_get_bytes(buf, end, &uid, sizeof(uid)); + p = simple_get_bytes(buf, end, &id, sizeof(id)); if (IS_ERR(p)) { err = PTR_ERR(p); goto err; } + uid = make_kuid(&init_user_ns, id); + if (!uid_valid(uid)) { + err = -EINVAL; + goto err; + } + err = -ENOMEM; ctx = gss_alloc_context(); if (ctx == NULL) From 683428fae8c73d7d7da0fa2e0b6beb4d8df4e808 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 01:40:53 -0800 Subject: [PATCH 57/93] sunrpc: Update svcgss xdr handle to rpsec_contect cache For each received uid call make_kuid and validate the result. For each received gid call make_kgid and validate the result. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_gss/svcauth_gss.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 73e957386600..ecd1d58bf611 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -418,6 +418,7 @@ static int rsc_parse(struct cache_detail *cd, { /* contexthandle expiry [ uid gid N mechname ...mechdata... ] */ char *buf = mesg; + int id; int len, rv; struct rsc rsci, *rscp = NULL; time_t expiry; @@ -444,7 +445,7 @@ static int rsc_parse(struct cache_detail *cd, goto out; /* uid, or NEGATIVE */ - rv = get_int(&mesg, &rsci.cred.cr_uid); + rv = get_int(&mesg, &id); if (rv == -EINVAL) goto out; if (rv == -ENOENT) @@ -452,8 +453,16 @@ static int rsc_parse(struct cache_detail *cd, else { int N, i; + /* uid */ + rsci.cred.cr_uid = make_kuid(&init_user_ns, id); + if (!uid_valid(rsci.cred.cr_uid)) + goto out; + /* gid */ - if (get_int(&mesg, &rsci.cred.cr_gid)) + if (get_int(&mesg, &id)) + goto out; + rsci.cred.cr_gid = make_kgid(&init_user_ns, id); + if (!gid_valid(rsci.cred.cr_gid)) goto out; /* number of additional gid's */ @@ -467,11 +476,10 @@ static int rsc_parse(struct cache_detail *cd, /* gid's */ status = -EINVAL; for (i=0; i Date: Sat, 2 Feb 2013 01:57:30 -0800 Subject: [PATCH 58/93] sunrpc: Hash uids by first computing their value in the initial userns In svcauth_unix introduce a helper unix_gid_hash as otherwise the expresion to generate the hash value is just too long. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth.c | 2 +- net/sunrpc/svcauth_unix.c | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 4cd0ecfe9837..392adc41e2e5 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -412,7 +412,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, *entry, *new; unsigned int nr; - nr = hash_long(acred->uid, cache->hashbits); + nr = hash_long(from_kuid(&init_user_ns, acred->uid), cache->hashbits); rcu_read_lock(); hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 92166b57ec7a..faf17195a9fd 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -419,6 +419,11 @@ struct unix_gid { struct group_info *gi; }; +static int unix_gid_hash(kuid_t uid) +{ + return hash_long(from_kuid(&init_user_ns, uid), GID_HASHBITS); +} + static void unix_gid_put(struct kref *kref) { struct cache_head *item = container_of(kref, struct cache_head, ref); @@ -530,7 +535,7 @@ static int unix_gid_parse(struct cache_detail *cd, ug.h.expiry_time = expiry; ch = sunrpc_cache_update(cd, &ug.h, &ugp->h, - hash_long(uid, GID_HASHBITS)); + unix_gid_hash(uid)); if (!ch) err = -ENOMEM; else { @@ -621,7 +626,7 @@ static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid) struct cache_head *ch; ug.uid = uid; - ch = sunrpc_cache_lookup(cd, &ug.h, hash_long(uid, GID_HASHBITS)); + ch = sunrpc_cache_lookup(cd, &ug.h, unix_gid_hash(uid)); if (ch) return container_of(ch, struct unix_gid, h); else From a570abbb966ee7de6c4357a58be11a558fa7099b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 02:45:08 -0800 Subject: [PATCH 59/93] sunrpc: Properly encode kuids and kgids in RPC_AUTH_UNIX credentials When writing kuids onto the wire first map them into the initial user namespace. When writing kgids onto the wire first map them into the initial user namespace. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_unix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index c434fde2079b..dc37021fc3e5 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -157,11 +157,11 @@ unx_marshal(struct rpc_task *task, __be32 *p) */ p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); - *p++ = htonl((u32) cred->uc_uid); - *p++ = htonl((u32) cred->uc_gid); + *p++ = htonl((u32) from_kuid(&init_user_ns, cred->uc_uid)); + *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gid)); hold = p++; for (i = 0; i < 16 && gid_valid(cred->uc_gids[i]); i++) - *p++ = htonl((u32) cred->uc_gids[i]); + *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gids[i])); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ From 25da9263710ec94c964259c79fa9a3a635cd3a50 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 02:49:44 -0800 Subject: [PATCH 60/93] sunrpc: Properly encode kuids and kgids in auth.unix.gid rpc pipe upcalls. When a new rpc connection is established with an in-kernel server, the traffic passes through svc_process_common, and svc_set_client and down into svcauth_unix_set_client if it is of type RPC_AUTH_NULL or RPC_AUTH_UNIX. svcauth_unix_set_client then looks at the uid of the credential we have assigned to the incomming client and if we don't have the groups already cached makes an upcall to get a list of groups that the client can use. The upcall encodes send a rpc message to user space encoding the uid of the user whose groups we want to know. Encode the kuid of the user in the initial user namespace as nfs mounts can only happen today in the initial user namespace. When a reply to an upcall comes in convert interpret the uid and gid values from the rpc pipe as uids and gids in the initial user namespace and convert them into kuids and kgids before processing them further. When reading proc files listing the uid to gid list cache convert the kuids and kgids from into uids and gids the initial user namespace. As we are displaying server internal details it makes sense to display these values from the servers perspective. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/svcauth_unix.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index faf17195a9fd..bdea0a1b6d1d 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -470,7 +470,7 @@ static void unix_gid_request(struct cache_detail *cd, char tuid[20]; struct unix_gid *ug = container_of(h, struct unix_gid, h); - snprintf(tuid, 20, "%u", ug->uid); + snprintf(tuid, 20, "%u", from_kuid(&init_user_ns, ug->uid)); qword_add(bpp, blen, tuid); (*bpp)[-1] = '\n'; } @@ -486,7 +486,8 @@ static int unix_gid_parse(struct cache_detail *cd, char *mesg, int mlen) { /* uid expiry Ngid gid0 gid1 ... gidN-1 */ - int uid; + int id; + kuid_t uid; int gids; int rv; int i; @@ -498,9 +499,12 @@ static int unix_gid_parse(struct cache_detail *cd, return -EINVAL; mesg[mlen-1] = 0; - rv = get_int(&mesg, &uid); + rv = get_int(&mesg, &id); if (rv) return -EINVAL; + uid = make_kuid(&init_user_ns, id); + if (!uid_valid(uid)) + return -EINVAL; ug.uid = uid; expiry = get_expiry(&mesg); @@ -554,7 +558,7 @@ static int unix_gid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) { - struct user_namespace *user_ns = current_user_ns(); + struct user_namespace *user_ns = &init_user_ns; struct unix_gid *ug; int i; int glen; @@ -570,7 +574,7 @@ static int unix_gid_show(struct seq_file *m, else glen = 0; - seq_printf(m, "%u %d:", ug->uid, glen); + seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen); for (i = 0; i < glen; i++) seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i))); seq_printf(m, "\n"); From f025adf191924e3a75ce80e130afcd2485b53bb8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 03:03:04 -0800 Subject: [PATCH 61/93] sunrpc: Properly decode kuids and kgids in RPC_AUTH_UNIX credentials When reading kuids from the wire map them into the initial user namespace, and validate the mapping succeded. When reading kgids from the wire map them into the initial user namespace, and validate the mapping succeded. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/svcauth_unix.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index bdea0a1b6d1d..a1852e19ed0c 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -821,8 +821,10 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) argv->iov_base = (void*)((__be32*)argv->iov_base + slen); /* skip machname */ argv->iov_len -= slen*4; - cred->cr_uid = svc_getnl(argv); /* uid */ - cred->cr_gid = svc_getnl(argv); /* gid */ + cred->cr_uid = make_kuid(&init_user_ns, svc_getnl(argv)); /* uid */ + cred->cr_gid = make_kgid(&init_user_ns, svc_getnl(argv)); /* gid */ + if (!uid_valid(cred->cr_uid) || !gid_valid(cred->cr_gid)) + goto badcred; slen = svc_getnl(argv); /* gids length */ if (slen > 16 || (len -= (slen + 2)*4) < 0) goto badcred; From 4e963d4f3ed1b756ff20f84960fcb4db42509146 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:03:16 -0800 Subject: [PATCH 62/93] nfs: Pass GLOBAL_ROOT_UID and GLOBAL_ROOT_GID to keyring alloc Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/idmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index bc3968fa81e5..f77017c4e28b 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -193,7 +193,8 @@ static int nfs_idmap_init_keyring(void) if (!cred) return -ENOMEM; - keyring = keyring_alloc(".id_resolver", 0, 0, cred, + keyring = keyring_alloc(".id_resolver", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, KEY_ALLOC_NOT_IN_QUOTA, NULL); From 54f834cd5501fb5fc801e4719a3ad0c894a3af2c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:16:29 -0800 Subject: [PATCH 63/93] nfs: Convert struct nfs_fattr to Use kuid_t and kgid_t Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- include/linux/nfs_xdr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 29adb12c7ecf..13441ddac33d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -48,8 +48,8 @@ struct nfs_fattr { unsigned int valid; /* which fields are valid */ umode_t mode; __u32 nlink; - __u32 uid; - __u32 gid; + kuid_t uid; + kgid_t gid; dev_t rdev; __u64 size; union { From 9f309c86cf343c59c79d25d9bde5d4a895d2e81f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:21:47 -0800 Subject: [PATCH 64/93] nfs: Convert idmap to use kuids and kgids Convert nfs_map_name_to_uid to return a kuid_t value. Convert nfs_map_name_to_gid to return a kgid_t value. Convert nfs_map_uid_to_name to take a kuid_t paramater. Convert nfs_map_gid_to_name to take a kgid_t paramater. Tweak nfs_fattr_map_owner_to_name to use a kuid_t intermediate value. Tweak nfs_fattr_map_group_to_name to use a kgid_t intermediate value. Which makes these functions properly handle kuids and kgids, including erroring of the generated kuid or kgid is invalid. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/idmap.c | 50 ++++++++++++++++++++++++++------------- include/linux/nfs_idmap.h | 9 +++---- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index f77017c4e28b..b9623d19d599 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -97,7 +97,7 @@ static void nfs_fattr_free_group_name(struct nfs_fattr *fattr) static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr *fattr) { struct nfs4_string *owner = fattr->owner_name; - __u32 uid; + kuid_t uid; if (!(fattr->valid & NFS_ATTR_FATTR_OWNER_NAME)) return false; @@ -111,7 +111,7 @@ static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr static bool nfs_fattr_map_group_name(struct nfs_server *server, struct nfs_fattr *fattr) { struct nfs4_string *group = fattr->group_name; - __u32 gid; + kgid_t gid; if (!(fattr->valid & NFS_ATTR_FATTR_GROUP_NAME)) return false; @@ -837,43 +837,61 @@ idmap_release_pipe(struct inode *inode) nfs_idmap_abort_pipe_upcall(idmap, -EPIPE); } -int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid) { struct idmap *idmap = server->nfs_client->cl_idmap; + __u32 id = -1; + int ret = 0; - if (nfs_map_string_to_numeric(name, namelen, uid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "uid", uid, idmap); + if (!nfs_map_string_to_numeric(name, namelen, &id)) + ret = nfs_idmap_lookup_id(name, namelen, "uid", &id, idmap); + if (ret == 0) { + *uid = make_kuid(&init_user_ns, id); + if (!uid_valid(*uid)) + ret = -ERANGE; + } + return ret; } -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) +int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, kgid_t *gid) { struct idmap *idmap = server->nfs_client->cl_idmap; + __u32 id = -1; + int ret = 0; - if (nfs_map_string_to_numeric(name, namelen, gid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "gid", gid, idmap); + if (!nfs_map_string_to_numeric(name, namelen, &id)) + ret = nfs_idmap_lookup_id(name, namelen, "gid", &id, idmap); + if (ret == 0) { + *gid = make_kgid(&init_user_ns, id); + if (!gid_valid(*gid)) + ret = -ERANGE; + } + return ret; } -int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) +int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf, size_t buflen) { struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; + __u32 id; + id = from_kuid(&init_user_ns, uid); if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(uid, "user", buf, buflen, idmap); + ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap); if (ret < 0) - ret = nfs_map_numeric_to_string(uid, buf, buflen); + ret = nfs_map_numeric_to_string(id, buf, buflen); return ret; } -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen) +int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen) { struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; + __u32 id; + id = from_kgid(&init_user_ns, gid); if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(gid, "group", buf, buflen, idmap); + ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap); if (ret < 0) - ret = nfs_map_numeric_to_string(gid, buf, buflen); + ret = nfs_map_numeric_to_string(id, buf, buflen); return ret; } diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 2dcef3ab58b6..0f4b79da6584 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -36,6 +36,7 @@ #ifndef NFS_IDMAP_H #define NFS_IDMAP_H +#include #include @@ -67,10 +68,10 @@ void nfs_fattr_init_names(struct nfs_fattr *fattr, void nfs_fattr_free_names(struct nfs_fattr *); void nfs_fattr_map_and_free_names(struct nfs_server *, struct nfs_fattr *); -int nfs_map_name_to_uid(const struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_group_to_gid(const struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_uid_to_name(const struct nfs_server *, __u32, char *, size_t); -int nfs_map_gid_to_group(const struct nfs_server *, __u32, char *, size_t); +int nfs_map_name_to_uid(const struct nfs_server *, const char *, size_t, kuid_t *); +int nfs_map_group_to_gid(const struct nfs_server *, const char *, size_t, kgid_t *); +int nfs_map_uid_to_name(const struct nfs_server *, kuid_t, char *, size_t); +int nfs_map_gid_to_group(const struct nfs_server *, kgid_t, char *, size_t); extern unsigned int nfs_idmap_cache_timeout; #endif /* NFS_IDMAP_H */ From cfa0898d4ff1874b86d21768adb49d2c033fa90b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:45:54 -0800 Subject: [PATCH 65/93] nfs: Convert nfs2xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Add an additional failure mode for incoming uid or gids that are invalid. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/nfs2xdr.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 06b9df49f7f7..62db136339ea 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -290,8 +290,13 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->mode = be32_to_cpup(p++); fattr->nlink = be32_to_cpup(p++); - fattr->uid = be32_to_cpup(p++); - fattr->gid = be32_to_cpup(p++); + fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++)); + if (!uid_valid(fattr->uid)) + goto out_uid; + fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++)); + if (!gid_valid(fattr->gid)) + goto out_gid; + fattr->size = be32_to_cpup(p++); fattr->du.nfs2.blocksize = be32_to_cpup(p++); @@ -313,6 +318,12 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime); return 0; +out_uid: + dprintk("NFS: returned invalid uid\n"); + return -EINVAL; +out_gid: + dprintk("NFS: returned invalid gid\n"); + return -EINVAL; out_overflow: print_overflow_msg(__func__, xdr); return -EIO; @@ -351,11 +362,11 @@ static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) else *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); if (attr->ia_valid & ATTR_UID) - *p++ = cpu_to_be32(attr->ia_uid); + *p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid)); else *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); if (attr->ia_valid & ATTR_GID) - *p++ = cpu_to_be32(attr->ia_gid); + *p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid)); else *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); if (attr->ia_valid & ATTR_SIZE) From 57a38dae2ac3f3d618bf197314e2805840220d0f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:50:00 -0800 Subject: [PATCH 66/93] nfs: Convert nfs3xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Add an additional failure mode incoming for uids or gids that are invalid. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/nfs3xdr.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index bffc32406fbf..fa6d72131c19 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -592,13 +592,13 @@ static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr) if (attr->ia_valid & ATTR_UID) { *p++ = xdr_one; - *p++ = cpu_to_be32(attr->ia_uid); + *p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid)); } else *p++ = xdr_zero; if (attr->ia_valid & ATTR_GID) { *p++ = xdr_one; - *p++ = cpu_to_be32(attr->ia_gid); + *p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid)); } else *p++ = xdr_zero; @@ -657,8 +657,12 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode; fattr->nlink = be32_to_cpup(p++); - fattr->uid = be32_to_cpup(p++); - fattr->gid = be32_to_cpup(p++); + fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++)); + if (!uid_valid(fattr->uid)) + goto out_uid; + fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++)); + if (!gid_valid(fattr->gid)) + goto out_gid; p = xdr_decode_size3(p, &fattr->size); p = xdr_decode_size3(p, &fattr->du.nfs3.used); @@ -675,6 +679,12 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->valid |= NFS_ATTR_FATTR_V3; return 0; +out_uid: + dprintk("NFS: returned invalid uid\n"); + return -EINVAL; +out_gid: + dprintk("NFS: returned invalid gid\n"); + return -EINVAL; out_overflow: print_overflow_msg(__func__, xdr); return -EIO; From e5782076e72be2a39bf261f7db03e1769ec05198 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:22:02 -0800 Subject: [PATCH 67/93] nfs: Convert nfs4xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. When printing kuids and kgids convert them to values in the initial user namespace then use normal printf formats. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/nfs4xdr.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 26b143920433..e3edda554ac7 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1002,7 +1002,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ); if (owner_namelen < 0) { dprintk("nfs: couldn't resolve uid %d to string\n", - iap->ia_uid); + from_kuid(&init_user_ns, iap->ia_uid)); /* XXX */ strcpy(owner_name, "nobody"); owner_namelen = sizeof("nobody") - 1; @@ -1014,7 +1014,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group, IDMAP_NAMESZ); if (owner_grouplen < 0) { dprintk("nfs: couldn't resolve gid %d to string\n", - iap->ia_gid); + from_kgid(&init_user_ns, iap->ia_gid)); strcpy(owner_group, "nobody"); owner_grouplen = sizeof("nobody") - 1; /* goto out; */ @@ -3778,14 +3778,14 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t } static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, - const struct nfs_server *server, uint32_t *uid, + const struct nfs_server *server, kuid_t *uid, struct nfs4_string *owner_name) { uint32_t len; __be32 *p; int ret = 0; - *uid = -2; + *uid = make_kuid(&init_user_ns, -2); if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U))) return -EIO; if (likely(bitmap[1] & FATTR4_WORD1_OWNER)) { @@ -3813,7 +3813,7 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, __func__, len); bitmap[1] &= ~FATTR4_WORD1_OWNER; } - dprintk("%s: uid=%d\n", __func__, (int)*uid); + dprintk("%s: uid=%d\n", __func__, (int)from_kuid(&init_user_ns, *uid)); return ret; out_overflow: print_overflow_msg(__func__, xdr); @@ -3821,14 +3821,14 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, } static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, - const struct nfs_server *server, uint32_t *gid, + const struct nfs_server *server, kgid_t *gid, struct nfs4_string *group_name) { uint32_t len; __be32 *p; int ret = 0; - *gid = -2; + *gid = make_kgid(&init_user_ns, -2); if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U))) return -EIO; if (likely(bitmap[1] & FATTR4_WORD1_OWNER_GROUP)) { @@ -3856,7 +3856,7 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, __func__, len); bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP; } - dprintk("%s: gid=%d\n", __func__, (int)*gid); + dprintk("%s: gid=%d\n", __func__, (int)from_kgid(&init_user_ns, *gid)); return ret; out_overflow: print_overflow_msg(__func__, xdr); From 9ff593c4739c76c31ffdf7f2a1d67489ece728e6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:26:23 -0800 Subject: [PATCH 68/93] nfs: kuid and kgid conversions for nfs/inode.c - Use uid_eq and gid_eq when comparing kuids and kgids. - Use make_kuid(&init_user_ns, -2) and make_kgid(&init_user_ns, -2) as the initial uid and gid on nfs inodes, instead of using the typeunsafe value of -2. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/inode.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ebeb94ce1b0b..ce2b0394c4d6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -332,8 +332,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) inode->i_version = 0; inode->i_size = 0; clear_nlink(inode); - inode->i_uid = -2; - inode->i_gid = -2; + inode->i_uid = make_kuid(&init_user_ns, -2); + inode->i_gid = make_kgid(&init_user_ns, -2); inode->i_blocks = 0; memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); nfsi->write_io = 0; @@ -1009,9 +1009,9 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat /* Have any file permissions changed? */ if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && inode->i_uid != fattr->uid) + if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && !uid_eq(inode->i_uid, fattr->uid)) invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && inode->i_gid != fattr->gid) + if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && !gid_eq(inode->i_gid, fattr->gid)) invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; /* Has the link count changed? */ @@ -1440,7 +1440,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | NFS_INO_REVAL_FORCED); if (fattr->valid & NFS_ATTR_FATTR_OWNER) { - if (inode->i_uid != fattr->uid) { + if (!uid_eq(inode->i_uid, fattr->uid)) { invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; inode->i_uid = fattr->uid; } @@ -1451,7 +1451,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | NFS_INO_REVAL_FORCED); if (fattr->valid & NFS_ATTR_FATTR_GROUP) { - if (inode->i_gid != fattr->gid) { + if (!gid_eq(inode->i_gid, fattr->gid)) { invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; inode->i_gid = fattr->gid; } From 4277bbf750d95a4c86925fa8f8956568b4912d43 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:59:11 -0800 Subject: [PATCH 69/93] nfs: Enable building with user namespaces enabled. Now that the kuids and kgids conversion have propogated through net/sunrpc/ and the fs/nfs/ it is safe to enable building nfs when user namespaces are enabled. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index b526f4c35b95..d7b926abe91f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1073,7 +1073,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n depends on NFSD = n - depends on NFS_FS = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From 6c1810e040d87fcd8fc95aedfd2ef6979d71e517 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:38:35 -0800 Subject: [PATCH 70/93] nfsd: Remove declaration of nonexistent nfs4_acl_permisison Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/acl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index 34e5c40af5ef..8b186a4955cc 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -44,8 +44,6 @@ struct nfs4_acl *nfs4_acl_new(int); int nfs4_acl_get_whotype(char *, u32); int nfs4_acl_write_who(int who, char *p); -int nfs4_acl_permission(struct nfs4_acl *acl, uid_t owner, gid_t group, - uid_t who, u32 mask); #define NFS4_ACL_TYPE_DEFAULT 0x01 #define NFS4_ACL_DIR 0x02 From b5663898ec3fa7f1a58a9def9592be345bb173c2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 03:54:35 -0800 Subject: [PATCH 71/93] nfsd: idmap use u32 not uid_t as the intermediate type u32 and uid_t have the same size and semantics so this change should have no operational effect. This just removes the WTF factor when looking at variables that hold both uids and gids whos type is uid_t. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs4idmap.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index a1f10c0a6255..7e84dfa23d83 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -65,7 +65,7 @@ MODULE_PARM_DESC(nfs4_disable_idmapping, struct ent { struct cache_head h; int type; /* User / Group */ - uid_t id; + u32 id; char name[IDMAP_NAMESZ]; char authname[IDMAP_NAMESZ]; }; @@ -540,7 +540,7 @@ rqst_authname(struct svc_rqst *rqstp) static __be32 idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, - uid_t *id) + u32 *id) { struct ent *item, key = { .type = type, @@ -564,7 +564,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen } static int -idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) +idmap_id_to_name(struct svc_rqst *rqstp, int type, u32 id, char *name) { struct ent *item, key = { .id = id, @@ -587,7 +587,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) } static bool -numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) +numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) { int ret; char buf[11]; @@ -603,7 +603,7 @@ numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namel } static __be32 -do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) +do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) { if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) if (numeric_name_to_id(rqstp, type, name, namelen, id)) @@ -616,7 +616,7 @@ do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u } static int -do_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) +do_id_to_name(struct svc_rqst *rqstp, int type, u32 id, char *name) { if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) return sprintf(name, "%u", id); From 65e10f6d0ab09ba95c2eb07cac43208692cf670e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 03:24:21 -0800 Subject: [PATCH 72/93] nfsd: Convert idmap to use kuids and kgids Convert nfsd_map_name_to_uid to return a kuid_t value. Convert nfsd_map_name_to_gid to return a kgid_t value. Convert nfsd_map_uid_to_name to take a kuid_t parameter. Convert nfsd_map_gid_to_name to take a kgid_t paramater. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/idmap.h | 8 ++++---- fs/nfsd/nfs4idmap.c | 26 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/idmap.h b/fs/nfsd/idmap.h index 9d513efc01ba..bf95f6b817a4 100644 --- a/fs/nfsd/idmap.h +++ b/fs/nfsd/idmap.h @@ -54,9 +54,9 @@ static inline void nfsd_idmap_shutdown(struct net *net) } #endif -__be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, __u32 *); -__be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, __u32 *); -int nfsd_map_uid_to_name(struct svc_rqst *, __u32, char *); -int nfsd_map_gid_to_name(struct svc_rqst *, __u32, char *); +__be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, kuid_t *); +__be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, kgid_t *); +int nfsd_map_uid_to_name(struct svc_rqst *, kuid_t, char *); +int nfsd_map_gid_to_name(struct svc_rqst *, kgid_t, char *); #endif /* LINUX_NFSD_IDMAP_H */ diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 7e84dfa23d83..0ce12346df9c 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -625,26 +625,40 @@ do_id_to_name(struct svc_rqst *rqstp, int type, u32 id, char *name) __be32 nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, - __u32 *id) + kuid_t *uid) { - return do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); + __be32 status; + u32 id = -1; + status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); + *uid = make_kuid(&init_user_ns, id); + if (!uid_valid(*uid)) + status = nfserr_badowner; + return status; } __be32 nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, - __u32 *id) + kgid_t *gid) { - return do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id); + __be32 status; + u32 id = -1; + status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); + *gid = make_kgid(&init_user_ns, id); + if (!gid_valid(*gid)) + status = nfserr_badowner; + return status; } int -nfsd_map_uid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) +nfsd_map_uid_to_name(struct svc_rqst *rqstp, kuid_t uid, char *name) { + u32 id = from_kuid(&init_user_ns, uid); return do_id_to_name(rqstp, IDMAP_TYPE_USER, id, name); } int -nfsd_map_gid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) +nfsd_map_gid_to_name(struct svc_rqst *rqstp, kgid_t gid, char *name) { + u32 id = from_kgid(&init_user_ns, gid); return do_id_to_name(rqstp, IDMAP_TYPE_GROUP, id, name); } From e097258f2eb4a91e7389ae69a3c87df111637a3f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 04:04:22 -0800 Subject: [PATCH 73/93] nfsd: Remove nfsd_luid, nfsd_lgid, nfsd_ruid and nfsd_rgid These trivial macros that don't currently do anything are the last vestiages of an old attempt at uid mapping that was removed from the kernel in September of 2002. Remove them to make it clear what the code is currently doing. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/auth.h | 6 ------ fs/nfsd/nfs3xdr.c | 4 ++-- fs/nfsd/nfsxdr.c | 4 ++-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/auth.h b/fs/nfsd/auth.h index 78b3c0e93822..53325a12ba62 100644 --- a/fs/nfsd/auth.h +++ b/fs/nfsd/auth.h @@ -1,6 +1,5 @@ /* * nfsd-specific authentication stuff. - * uid/gid mapping not yet implemented. * * Copyright (C) 1995, 1996 Olaf Kirch */ @@ -8,11 +7,6 @@ #ifndef LINUX_NFSD_AUTH_H #define LINUX_NFSD_AUTH_H -#define nfsd_luid(rq, uid) ((u32)(uid)) -#define nfsd_lgid(rq, gid) ((u32)(gid)) -#define nfsd_ruid(rq, uid) ((u32)(uid)) -#define nfsd_rgid(rq, gid) ((u32)(gid)) - /* * Set the current process's fsuid/fsgid etc to those of the NFS * client user diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 324c0baf7cda..1884a3fbb584 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -167,8 +167,8 @@ encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); - *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); + *p++ = htonl((u32) stat->uid); + *p++ = htonl((u32) stat->gid); if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 979b42106979..1e51e7034a89 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -151,8 +151,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs_ftypes[type >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); - *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); + *p++ = htonl((u32) stat->uid); + *p++ = htonl((u32) stat->gid); if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { *p++ = htonl(NFS_MAXPATHLEN); From 458878a705c822a6be267977e435b16576bde59b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 04:16:08 -0800 Subject: [PATCH 74/93] nfsd: Convert nfs3xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs3xdr.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 1884a3fbb584..925c944bc0bc 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -105,12 +105,14 @@ decode_sattr3(__be32 *p, struct iattr *iap) iap->ia_mode = ntohl(*p++); } if (*p++) { - iap->ia_valid |= ATTR_UID; - iap->ia_uid = ntohl(*p++); + iap->ia_uid = make_kuid(&init_user_ns, ntohl(*p++)); + if (uid_valid(iap->ia_uid)) + iap->ia_valid |= ATTR_UID; } if (*p++) { - iap->ia_valid |= ATTR_GID; - iap->ia_gid = ntohl(*p++); + iap->ia_gid = make_kgid(&init_user_ns, ntohl(*p++)); + if (gid_valid(iap->ia_gid)) + iap->ia_valid |= ATTR_GID; } if (*p++) { u64 newsize; @@ -167,8 +169,8 @@ encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) stat->uid); - *p++ = htonl((u32) stat->gid); + *p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid)); + *p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid)); if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { From 7c19723e997a3990951c0db0500009fb90c0c5b9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 04:32:35 -0800 Subject: [PATCH 75/93] nfsd: Convert nfsxdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. If the conversion results in an invalid result don't set the ATTR_UID or ATTR_GID. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfsxdr.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 1e51e7034a89..4201ede0ec91 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -100,12 +100,14 @@ decode_sattr(__be32 *p, struct iattr *iap) iap->ia_mode = tmp; } if ((tmp = ntohl(*p++)) != (u32)-1) { - iap->ia_valid |= ATTR_UID; - iap->ia_uid = tmp; + iap->ia_uid = make_kuid(&init_user_ns, tmp); + if (uid_valid(iap->ia_uid)) + iap->ia_valid |= ATTR_UID; } if ((tmp = ntohl(*p++)) != (u32)-1) { - iap->ia_valid |= ATTR_GID; - iap->ia_gid = tmp; + iap->ia_gid = make_kgid(&init_user_ns, tmp); + if (gid_valid(iap->ia_gid)) + iap->ia_valid |= ATTR_GID; } if ((tmp = ntohl(*p++)) != (u32)-1) { iap->ia_valid |= ATTR_SIZE; @@ -151,8 +153,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs_ftypes[type >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) stat->uid); - *p++ = htonl((u32) stat->gid); + *p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid)); + *p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid)); if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { *p++ = htonl(NFS_MAXPATHLEN); From ab8e4aee0a3f73d1b12e6d63b42075f0586ad4fd Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 05:18:08 -0800 Subject: [PATCH 76/93] nfsd: Handle kuids and kgids in the nfs4acl to posix_acl conversion In struct nfs4_ace remove the member who and replace it with an anonymous union holding who_uid and who_gid. Allowing typesafe storage uids and gids. Add a helper pace_gt for sorting posix_acl_entries. In struct posix_user_ace_state to replace uid with a union of kuid_t uid and kgid_t gid. Remove all initializations of the deprecated posic_acl_entry e_id field. Which is not present when user namespaces are enabled. Split find_uid into two functions find_uid and find_gid that work in a typesafe manner. In nfs4xdr update nfsd4_encode_fattr to deal with the changes in struct nfs4_ace. Rewrite nfsd4_encode_name to take a kuid_t and a kgid_t instead of a generic id and flag if it is a group or a uid. Replace the group flag with a test for a valid gid. Modify nfsd4_encode_user to take a kuid_t and call the modifed nfsd4_encode_name. Modify nfsd4_encode_group to take a kgid_t and call the modified nfsd4_encode_name. Modify nfsd4_encode_aclname to take an ace instead of taking the fields of an ace broken out. This allows it to detect if the ace is for a user or a group and to pass the appropriate value while still being typesafe. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs4acl.c | 63 ++++++++++++++++++++++++++++++++------------ fs/nfsd/nfs4xdr.c | 41 +++++++++++++++++----------- include/linux/nfs4.h | 6 ++++- 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 9c51aff02ae2..8a50b3c18093 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -264,7 +264,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->flag = eflag; ace->access_mask = deny_mask_from_posix(deny, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_uid = pa->e_uid; ace++; acl->naces++; } @@ -273,7 +273,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->access_mask = mask_from_posix(pa->e_perm & pas.mask, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_uid = pa->e_uid; ace++; acl->naces++; pa++; @@ -300,7 +300,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->access_mask = mask_from_posix(pa->e_perm & pas.mask, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_gid = pa->e_gid; ace++; acl->naces++; pa++; @@ -329,7 +329,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP; ace->access_mask = deny_mask_from_posix(deny, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_gid = pa->e_gid; ace++; acl->naces++; } @@ -345,6 +345,18 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, acl->naces++; } +static bool +pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2) +{ + if (pace1->e_tag != pace2->e_tag) + return pace1->e_tag > pace2->e_tag; + if (pace1->e_tag == ACL_USER) + return uid_gt(pace1->e_uid, pace2->e_uid); + if (pace1->e_tag == ACL_GROUP) + return gid_gt(pace1->e_gid, pace2->e_gid); + return false; +} + static void sort_pacl_range(struct posix_acl *pacl, int start, int end) { int sorted = 0, i; @@ -355,8 +367,8 @@ sort_pacl_range(struct posix_acl *pacl, int start, int end) { while (!sorted) { sorted = 1; for (i = start; i < end; i++) { - if (pacl->a_entries[i].e_id - > pacl->a_entries[i+1].e_id) { + if (pace_gt(&pacl->a_entries[i], + &pacl->a_entries[i+1])) { sorted = 0; tmp = pacl->a_entries[i]; pacl->a_entries[i] = pacl->a_entries[i+1]; @@ -398,7 +410,10 @@ struct posix_ace_state { }; struct posix_user_ace_state { - uid_t uid; + union { + kuid_t uid; + kgid_t gid; + }; struct posix_ace_state perms; }; @@ -521,7 +536,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) if (error) goto out_err; low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; for (i=0; i < state->users->n; i++) { pace++; @@ -531,7 +545,7 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) goto out_err; low_mode_from_nfs4(state->users->aces[i].perms.allow, &pace->e_perm, flags); - pace->e_id = state->users->aces[i].uid; + pace->e_uid = state->users->aces[i].uid; add_to_mask(state, &state->users->aces[i].perms); } @@ -541,7 +555,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) if (error) goto out_err; low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; add_to_mask(state, &state->group); for (i=0; i < state->groups->n; i++) { @@ -552,14 +565,13 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) goto out_err; low_mode_from_nfs4(state->groups->aces[i].perms.allow, &pace->e_perm, flags); - pace->e_id = state->groups->aces[i].uid; + pace->e_gid = state->groups->aces[i].gid; add_to_mask(state, &state->groups->aces[i].perms); } pace++; pace->e_tag = ACL_MASK; low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; pace++; pace->e_tag = ACL_OTHER; @@ -567,7 +579,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) if (error) goto out_err; low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; return pacl; out_err: @@ -587,12 +598,13 @@ static inline void deny_bits(struct posix_ace_state *astate, u32 mask) astate->deny |= mask & ~astate->allow; } -static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array *a, uid_t uid) +static int find_uid(struct posix_acl_state *state, kuid_t uid) { + struct posix_ace_state_array *a = state->users; int i; for (i = 0; i < a->n; i++) - if (a->aces[i].uid == uid) + if (uid_eq(a->aces[i].uid, uid)) return i; /* Not found: */ a->n++; @@ -603,6 +615,23 @@ static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array return i; } +static int find_gid(struct posix_acl_state *state, kgid_t gid) +{ + struct posix_ace_state_array *a = state->groups; + int i; + + for (i = 0; i < a->n; i++) + if (gid_eq(a->aces[i].gid, gid)) + return i; + /* Not found: */ + a->n++; + a->aces[i].gid = gid; + a->aces[i].perms.allow = state->everyone.allow; + a->aces[i].perms.deny = state->everyone.deny; + + return i; +} + static void deny_bits_array(struct posix_ace_state_array *a, u32 mask) { int i; @@ -636,7 +665,7 @@ static void process_one_v4_ace(struct posix_acl_state *state, } break; case ACL_USER: - i = find_uid(state, state->users, ace->who); + i = find_uid(state, ace->who_uid); if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { allow_bits(&state->users->aces[i].perms, mask); } else { @@ -658,7 +687,7 @@ static void process_one_v4_ace(struct posix_acl_state *state, } break; case ACL_GROUP: - i = find_uid(state, state->groups, ace->who); + i = find_gid(state, ace->who_gid); if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { allow_bits(&state->groups->aces[i].perms, mask); } else { diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 0dc11586682f..3812b06d24b1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -293,13 +293,13 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ace->whotype = nfs4_acl_get_whotype(buf, dummy32); status = nfs_ok; if (ace->whotype != NFS4_ACL_WHO_NAMED) - ace->who = 0; + ; else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) status = nfsd_map_name_to_gid(argp->rqstp, - buf, dummy32, &ace->who); + buf, dummy32, &ace->who_gid); else status = nfsd_map_name_to_uid(argp->rqstp, - buf, dummy32, &ace->who); + buf, dummy32, &ace->who_uid); if (status) return status; } @@ -1926,7 +1926,7 @@ static u32 nfs4_file_type(umode_t mode) } static __be32 -nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, +nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, kuid_t uid, kgid_t gid, __be32 **p, int *buflen) { int status; @@ -1935,10 +1935,10 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, return nfserr_resource; if (whotype != NFS4_ACL_WHO_NAMED) status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1)); - else if (group) - status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1)); + else if (gid_valid(gid)) + status = nfsd_map_gid_to_name(rqstp, gid, (u8 *)(*p + 1)); else - status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1)); + status = nfsd_map_uid_to_name(rqstp, uid, (u8 *)(*p + 1)); if (status < 0) return nfserrno(status); *p = xdr_encode_opaque(*p, NULL, status); @@ -1948,22 +1948,33 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, } static inline __be32 -nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen) +nfsd4_encode_user(struct svc_rqst *rqstp, kuid_t user, __be32 **p, int *buflen) { - return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen); + return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, user, INVALID_GID, + p, buflen); } static inline __be32 -nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen) +nfsd4_encode_group(struct svc_rqst *rqstp, kgid_t group, __be32 **p, int *buflen) { - return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen); + return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, INVALID_UID, group, + p, buflen); } static inline __be32 -nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, +nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace, __be32 **p, int *buflen) { - return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); + kuid_t uid = INVALID_UID; + kgid_t gid = INVALID_GID; + + if (ace->whotype == NFS4_ACL_WHO_NAMED) { + if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) + gid = ace->who_gid; + else + uid = ace->who_uid; + } + return nfsd4_encode_name(rqstp, ace->whotype, uid, gid, p, buflen); } #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ @@ -2224,9 +2235,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, WRITE32(ace->type); WRITE32(ace->flag); WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL); - status = nfsd4_encode_aclname(rqstp, ace->whotype, - ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP, - &p, &buflen); + status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen); if (status == nfserr_resource) goto out_resource; if (status) diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index e111fa419a4e..7b8fc73810ad 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -13,6 +13,7 @@ #define _LINUX_NFS4_H #include +#include #include struct nfs4_ace { @@ -20,7 +21,10 @@ struct nfs4_ace { uint32_t flag; uint32_t access_mask; int whotype; - uid_t who; + union { + kuid_t who_uid; + kgid_t who_gid; + }; }; struct nfs4_acl { From 03bc6d1cc1759e6b5959cacc02a19ef36e95e741 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:24:49 -0800 Subject: [PATCH 77/93] nfsd: Modify nfsd4_cb_sec to use kuids and kgids Change uid and gid in struct nfsd4_cb_sec to be of type kuid_t and kgid_t. In nfsd4_decode_cb_sec when reading uids and gids off the wire convert them to kuids and kgids, and if they don't convert to valid kuids or valid kuids ignore RPC_AUTH_UNIX and don't fill in any of the fields. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs4xdr.c | 13 ++++++++++--- fs/nfsd/state.h | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 3812b06d24b1..2d1d06bae3a7 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -464,9 +464,16 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ READ32(dummy); READ_BUF(dummy * 4); if (cbs->flavor == (u32)(-1)) { - cbs->uid = uid; - cbs->gid = gid; - cbs->flavor = RPC_AUTH_UNIX; + kuid_t kuid = make_kuid(&init_user_ns, uid); + kgid_t kgid = make_kgid(&init_user_ns, gid); + if (uid_valid(kuid) && gid_valid(kgid)) { + cbs->uid = kuid; + cbs->gid = kgid; + cbs->flavor = RPC_AUTH_UNIX; + } else { + dprintk("RPC_AUTH_UNIX with invalid" + "uid or gid ignoring!\n"); + } } break; case RPC_AUTH_GSS: diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index d1c229feed52..1a8c7391f7ae 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -152,8 +152,8 @@ struct nfsd4_channel_attrs { struct nfsd4_cb_sec { u32 flavor; /* (u32)(-1) used to mean "no valid flavor" */ - u32 uid; - u32 gid; + kuid_t uid; + kgid_t gid; }; struct nfsd4_create_session { From 4c1e1b34d5c800ad3ac9a7e2805b0bea70ad2278 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:42:53 -0800 Subject: [PATCH 78/93] nfsd: Store ex_anon_uid and ex_anon_gid as kuids and kgids Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/auth.c | 2 +- fs/nfsd/export.c | 22 ++++++++++++++-------- include/linux/nfsd/export.h | 4 ++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 34a10d78b839..4d6642b38eae 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -58,7 +58,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) for (i = 0; i < rqgi->ngroups; i++) { if (gid_eq(GLOBAL_ROOT_GID, GROUP_AT(rqgi, i))) - GROUP_AT(gi, i) = make_kgid(&init_user_ns, exp->ex_anon_gid); + GROUP_AT(gi, i) = exp->ex_anon_gid; else GROUP_AT(gi, i) = GROUP_AT(rqgi, i); } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index a3946cf13fc8..5681c5906f08 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -544,13 +544,17 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) err = get_int(&mesg, &an_int); if (err) goto out3; - exp.ex_anon_uid= an_int; + exp.ex_anon_uid= make_kuid(&init_user_ns, an_int); + if (!uid_valid(exp.ex_anon_uid)) + goto out3; /* anon gid */ err = get_int(&mesg, &an_int); if (err) goto out3; - exp.ex_anon_gid= an_int; + exp.ex_anon_gid= make_kgid(&init_user_ns, an_int); + if (!gid_valid(exp.ex_anon_gid)) + goto out3; /* fsid */ err = get_int(&mesg, &an_int); @@ -613,7 +617,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) } static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); + kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fslocs); static void show_secinfo(struct seq_file *m, struct svc_export *exp); static int svc_export_show(struct seq_file *m, @@ -1179,15 +1183,17 @@ static void show_secinfo(struct seq_file *m, struct svc_export *exp) } static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) + kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fsloc) { show_expflags(m, flag, NFSEXP_ALLFLAGS); if (flag & NFSEXP_FSID) seq_printf(m, ",fsid=%d", fsid); - if (anonu != (uid_t)-2 && anonu != (0x10000-2)) - seq_printf(m, ",anonuid=%u", anonu); - if (anong != (gid_t)-2 && anong != (0x10000-2)) - seq_printf(m, ",anongid=%u", anong); + if (!uid_eq(anonu, make_kuid(&init_user_ns, (uid_t)-2)) && + !uid_eq(anonu, make_kuid(&init_user_ns, 0x10000-2))) + seq_printf(m, ",anonuid=%u", from_kuid(&init_user_ns, anonu)); + if (!gid_eq(anong, make_kgid(&init_user_ns, (gid_t)-2)) && + !gid_eq(anong, make_kgid(&init_user_ns, 0x10000-2))) + seq_printf(m, ",anongid=%u", from_kgid(&init_user_ns, anong)); if (fsloc && fsloc->locations_count > 0) { char *loctype = (fsloc->migrated) ? "refer" : "replicas"; int i; diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 24c139288db4..7898c997dfea 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -49,8 +49,8 @@ struct svc_export { struct auth_domain * ex_client; int ex_flags; struct path ex_path; - uid_t ex_anon_uid; - gid_t ex_anon_gid; + kuid_t ex_anon_uid; + kgid_t ex_anon_gid; int ex_fsid; unsigned char * ex_uuid; /* 16 byte fsid */ struct nfsd4_fs_locations ex_fslocs; From 6fab877900030ba3ae11928efb6087589f1e514c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:53:11 -0800 Subject: [PATCH 79/93] nfsd: Properly compare and initialize kuids and kgids Use uid_eq(uid, GLOBAL_ROOT_UID) instead of !uid. Use gid_eq(gid, GLOBAL_ROOT_GID) instead of !gid. Use uid_eq(uid, INVALID_UID) instead of uid == -1 Use gid_eq(uid, INVALID_GID) instead of gid == -1 Use uid = GLOBAL_ROOT_UID instead of uid = 0; Use gid = GLOBAL_ROOT_GID instead of gid = 0; Use !uid_eq(uid1, uid2) instead of uid1 != uid2. Use !gid_eq(gid1, gid2) instead of gid1 != gid2. Use uid_eq(uid1, uid2) instead of uid1 == uid2. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/auth.c | 10 +++++----- fs/nfsd/nfs4recover.c | 4 ++-- fs/nfsd/nfs4state.c | 6 +++--- fs/nfsd/vfs.c | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 4d6642b38eae..06cddd572264 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -47,9 +47,9 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) if (!gi) goto oom; } else if (flags & NFSEXP_ROOTSQUASH) { - if (!new->fsuid) + if (uid_eq(new->fsuid, GLOBAL_ROOT_UID)) new->fsuid = exp->ex_anon_uid; - if (!new->fsgid) + if (gid_eq(new->fsgid, GLOBAL_ROOT_GID)) new->fsgid = exp->ex_anon_gid; gi = groups_alloc(rqgi->ngroups); @@ -66,9 +66,9 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) gi = get_group_info(rqgi); } - if (new->fsuid == (uid_t) -1) + if (uid_eq(new->fsuid, INVALID_UID)) new->fsuid = exp->ex_anon_uid; - if (new->fsgid == (gid_t) -1) + if (gid_eq(new->fsgid, INVALID_GID)) new->fsgid = exp->ex_anon_gid; ret = set_groups(new, gi); @@ -76,7 +76,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) if (ret < 0) goto error; - if (new->fsuid) + if (!uid_eq(new->fsuid, GLOBAL_ROOT_UID)) new->cap_effective = cap_drop_nfsd_set(new->cap_effective); else new->cap_effective = cap_raise_nfsd_set(new->cap_effective, diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index ba6fdd4a0455..4914af4a817e 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -73,8 +73,8 @@ nfs4_save_creds(const struct cred **original_creds) if (!new) return -ENOMEM; - new->fsuid = 0; - new->fsgid = 0; + new->fsuid = GLOBAL_ROOT_UID; + new->fsgid = GLOBAL_ROOT_GID; *original_creds = override_creds(new); put_cred(new); return 0; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ac8ed96c4199..0af6d3c114ed 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1202,7 +1202,7 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2) if (g1->ngroups != g2->ngroups) return false; for (i=0; ingroups; i++) - if (GROUP_AT(g1, i) != GROUP_AT(g2, i)) + if (!gid_eq(GROUP_AT(g1, i), GROUP_AT(g2, i))) return false; return true; } @@ -1227,8 +1227,8 @@ static bool same_creds(struct svc_cred *cr1, struct svc_cred *cr2) { if ((is_gss_cred(cr1) != is_gss_cred(cr2)) - || (cr1->cr_uid != cr2->cr_uid) - || (cr1->cr_gid != cr2->cr_gid) + || (!uid_eq(cr1->cr_uid, cr2->cr_uid)) + || (!gid_eq(cr1->cr_gid, cr2->cr_gid)) || !groups_equal(cr1->cr_group_info, cr2->cr_group_info)) return false; if (cr1->cr_principal == cr2->cr_principal) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index d586117fa94a..31ff1d642e31 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -401,8 +401,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, /* Revoke setuid/setgid on chown */ if (!S_ISDIR(inode->i_mode) && - (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) || - ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid))) { + (((iap->ia_valid & ATTR_UID) && !uid_eq(iap->ia_uid, inode->i_uid)) || + ((iap->ia_valid & ATTR_GID) && !gid_eq(iap->ia_gid, inode->i_gid)))) { iap->ia_valid |= ATTR_KILL_PRIV; if (iap->ia_valid & ATTR_MODE) { /* we're setting mode too, just clear the s*id bits */ @@ -1205,7 +1205,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp, * send along the gid on create when it tries to implement * setgid directories via NFS: */ - if (current_fsuid() != 0) + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) iap->ia_valid &= ~(ATTR_UID|ATTR_GID); if (iap->ia_valid) return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); @@ -2150,7 +2150,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, * with NFSv3. */ if ((acc & NFSD_MAY_OWNER_OVERRIDE) && - inode->i_uid == current_fsuid()) + uid_eq(inode->i_uid, current_fsuid())) return 0; /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */ From c9617a44b39d75b2098283d56355d02d4d5bee8b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 07:00:40 -0800 Subject: [PATCH 80/93] nfsd: Enable building with user namespaces enabled. Now that the kuids and kgids conversion have propogated through net/sunrpc/ and the fs/nfsd/ it is safe to enable building nfsd when user namespaces are enabled. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index d7b926abe91f..69c5ccce55e7 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on NFSD = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From 46bbc25f9ff1242cd1c042580aec11ddb5e41369 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 5 Feb 2013 23:55:44 -0800 Subject: [PATCH 81/93] cifs: Override unmappable incoming uids and gids The cifs protocol has a 64bit space for uids and gids, while linux only supports a 32bit space today. Instead of silently truncating 64bit cifs ids, replace cifs ids that do not fit in the 32bit linux id space with the default uid and gids for the cifs mount. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/inode.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ed6208ff85a7..d7ea2a6eaead 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -244,15 +244,19 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, break; } - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) - fattr->cf_uid = cifs_sb->mnt_uid; - else - fattr->cf_uid = le64_to_cpu(info->Uid); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) - fattr->cf_gid = cifs_sb->mnt_gid; - else - fattr->cf_gid = le64_to_cpu(info->Gid); + fattr->cf_uid = cifs_sb->mnt_uid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { + u64 id = le64_to_cpu(info->Uid); + if (id < ((uid_t)-1)) + fattr->cf_uid = id; + } + + fattr->cf_gid = cifs_sb->mnt_gid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { + u64 id = le64_to_cpu(info->Gid); + if (id < ((gid_t)-1)) + fattr->cf_gid = id; + } fattr->cf_nlink = le64_to_cpu(info->Nlinks); } From 355958f2893d97375da17696d2566dd4ab7b54a8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:10:23 -0800 Subject: [PATCH 82/93] cifs: Use BUILD_BUG_ON to validate uids and gids are the same size The assumption that sizeof(uid_t) is the same as sizeof(gid_t) is completely reasonable but since we can verify the condition at compile time. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsacl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 5cbd00e74067..2e811f45b413 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -297,6 +297,7 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, * probably a safe assumption but might be better to check based on * sidtype. */ + BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); if (sidkey->datalen != sizeof(uid_t)) { rc = -EIO; cFYI(1, "%s: Downcall contained malformed key " From 8e3028b908a048ac0b3102504b7daad7b5544846 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:21:22 -0800 Subject: [PATCH 83/93] cifs: Pass GLOBAL_ROOT_UID and GLOBAL_ROOT_GID to keyring_alloc keyring_alloc has been updated to take a kuid_t and kgid_t so pass GLOBAL_ROOT_UID instead of 0 for the uid and GLOBAL_ROOT_GID instead of 0 for the gid. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsacl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 2e811f45b413..aaaf5ce142b8 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -347,7 +347,8 @@ init_cifs_idmap(void) if (!cred) return -ENOMEM; - keyring = keyring_alloc(".cifs_idmap", 0, 0, cred, + keyring = keyring_alloc(".cifs_idmap", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, KEY_ALLOC_NOT_IN_QUOTA, NULL); From 8abf2775dd425ec3c767ea7c5a51b45fc8be76c2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:33:17 -0800 Subject: [PATCH 84/93] cifs: Use kuids and kgids SID to uid/gid mapping Update id_mode_to_cifs_acl to take a kuid_t and a kgid_t. Replace NO_CHANGE_32 with INVALID_UID and INVALID_GID, and tests for NO_CHANGE_32 with uid_valid and gid_valid. Carefully unpack the value returned from request_key. memcpy the value into the expected type. The convert the uid/gid into a kuid/kgid. And then only if the result is a valid kuid or kgid update fuid/fgid. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsacl.c | 43 +++++++++++++++++++++++++++++-------------- fs/cifs/cifspdu.h | 1 - fs/cifs/cifsproto.h | 2 +- fs/cifs/inode.c | 8 ++++---- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index aaaf5ce142b8..f1e3f25fe004 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -266,8 +266,8 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, struct key *sidkey; char *sidstr; const struct cred *saved_cred; - uid_t fuid = cifs_sb->mnt_uid; - gid_t fgid = cifs_sb->mnt_gid; + kuid_t fuid = cifs_sb->mnt_uid; + kgid_t fgid = cifs_sb->mnt_gid; /* * If we have too many subauthorities, then something is really wrong. @@ -306,10 +306,21 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, goto out_key_put; } - if (sidtype == SIDOWNER) - memcpy(&fuid, &sidkey->payload.value, sizeof(uid_t)); - else - memcpy(&fgid, &sidkey->payload.value, sizeof(gid_t)); + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + memcpy(&id, &sidkey->payload.value, sizeof(uid_t)); + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fuid = uid; + } else { + kgid_t gid; + gid_t id; + memcpy(&id, &sidkey->payload.value, sizeof(gid_t)); + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fgid = gid; + } out_key_put: key_put(sidkey); @@ -776,7 +787,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, /* Convert permission bits from mode to equivalent CIFS ACL */ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - __u32 secdesclen, __u64 nmode, uid_t uid, gid_t gid, int *aclflag) + __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag) { int rc = 0; __u32 dacloffset; @@ -808,17 +819,19 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, *aclflag = CIFS_ACL_DACL; } else { memcpy(pnntsd, pntsd, secdesclen); - if (uid != NO_CHANGE_32) { /* chown */ + if (uid_valid(uid)) { /* chown */ + uid_t id; owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + le32_to_cpu(pnntsd->osidoffset)); nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid), GFP_KERNEL); if (!nowner_sid_ptr) return -ENOMEM; - rc = id_to_sid(uid, SIDOWNER, nowner_sid_ptr); + id = from_kuid(&init_user_ns, uid); + rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); if (rc) { cFYI(1, "%s: Mapping error %d for owner id %d", - __func__, rc, uid); + __func__, rc, id); kfree(nowner_sid_ptr); return rc; } @@ -826,17 +839,19 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, kfree(nowner_sid_ptr); *aclflag = CIFS_ACL_OWNER; } - if (gid != NO_CHANGE_32) { /* chgrp */ + if (gid_valid(gid)) { /* chgrp */ + gid_t id; group_sid_ptr = (struct cifs_sid *)((char *)pnntsd + le32_to_cpu(pnntsd->gsidoffset)); ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid), GFP_KERNEL); if (!ngroup_sid_ptr) return -ENOMEM; - rc = id_to_sid(gid, SIDGROUP, ngroup_sid_ptr); + id = from_kgid(&init_user_ns, gid); + rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); if (rc) { cFYI(1, "%s: Mapping error %d for group id %d", - __func__, rc, gid); + __func__, rc, id); kfree(ngroup_sid_ptr); return rc; } @@ -1004,7 +1019,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, /* Convert mode bits to an ACL so we can update the ACL on the server */ int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, - uid_t uid, gid_t gid) + kuid_t uid, kgid_t gid) { int rc = 0; int aclflag = CIFS_ACL_DACL; /* default flag to set */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index b9d59a948a2c..e996ff6b26d1 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -277,7 +277,6 @@ #define CIFS_NO_HANDLE 0xFFFF #define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL -#define NO_CHANGE_32 0xFFFFFFFFUL /* IPC$ in ASCII */ #define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1988c1baa224..0060ec2124bf 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -161,7 +161,7 @@ extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct inode *inode, const char *path, const __u16 *pfid); extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64, - uid_t, gid_t); + kuid_t, kgid_t); extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, const char *, u32 *); extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index d7ea2a6eaead..d4cf7509c106 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2090,8 +2090,8 @@ static int cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) { unsigned int xid; - uid_t uid = NO_CHANGE_32; - gid_t gid = NO_CHANGE_32; + kuid_t uid = INVALID_UID; + kgid_t gid = INVALID_GID; struct inode *inode = direntry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsInodeInfo *cifsInode = CIFS_I(inode); @@ -2150,7 +2150,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) #ifdef CONFIG_CIFS_ACL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { - if (uid != NO_CHANGE_32 || gid != NO_CHANGE_32) { + if (uid_valid(uid) || gid_valid(gid)) { rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64, uid, gid); if (rc) { @@ -2174,7 +2174,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) #ifdef CONFIG_CIFS_ACL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { rc = id_mode_to_cifs_acl(inode, full_path, mode, - NO_CHANGE_32, NO_CHANGE_32); + INVALID_UID, INVALID_GID); if (rc) { cFYI(1, "%s: Setting ACL failed with error: %d", __func__, rc); From dbfb98af18194cff87d4c1dea8d43faf14eae2e7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:44:13 -0800 Subject: [PATCH 85/93] cifs: Convert from a kuid before printing current_fsuid Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsproto.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0060ec2124bf..feb9b6e2ab5a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -46,7 +46,8 @@ extern void _free_xid(unsigned int); ({ \ unsigned int __xid = _get_xid(); \ cFYI(1, "CIFS VFS: in %s as Xid: %u with uid: %d", \ - __func__, __xid, current_fsuid()); \ + __func__, __xid, \ + from_kuid(&init_user_ns, current_fsuid())); \ __xid; \ }) From 49418b2c28c901294f8b36ff14c766c9458c3623 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:57:56 -0800 Subject: [PATCH 86/93] cifs: Modify struct cifs_unix_set_info_args to hold a kuid_t and a kgid_t Use INVALID_UID and INVALID_GID instead of NO_CHANGE_64 to indicate the value should not be changed. In cifs_fill_unix_set_info convert from kuids and kgids into uids and gids that will fit in FILE_UNIX_BASIC_INFO. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/cifssmb.c | 10 ++++++++-- fs/cifs/dir.c | 18 +++++++++--------- fs/cifs/file.c | 4 ++-- fs/cifs/inode.c | 14 +++++++------- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index feb9b6e2ab5a..f450f0683ddd 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -305,8 +305,8 @@ struct cifs_unix_set_info_args { __u64 atime; __u64 mtime; __u64 mode; - __u64 uid; - __u64 gid; + kuid_t uid; + kgid_t gid; dev_t device; }; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 76d0d2998850..00e12f2d626b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -5819,8 +5819,14 @@ static void cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, const struct cifs_unix_set_info_args *args) { + u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64; u64 mode = args->mode; + if (uid_valid(args->uid)) + uid = from_kuid(&init_user_ns, args->uid); + if (gid_valid(args->gid)) + gid = from_kgid(&init_user_ns, args->gid); + /* * Samba server ignores set of file size to zero due to bugs in some * older clients, but we should be precise - we use SetFileSize to @@ -5833,8 +5839,8 @@ cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, data_offset->LastStatusChange = cpu_to_le64(args->ctime); data_offset->LastAccessTime = cpu_to_le64(args->atime); data_offset->LastModificationTime = cpu_to_le64(args->mtime); - data_offset->Uid = cpu_to_le64(args->uid); - data_offset->Gid = cpu_to_le64(args->gid); + data_offset->Uid = cpu_to_le64(uid); + data_offset->Gid = cpu_to_le64(gid); /* better to leave device as zero when it is */ data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 8719bbe0dcc3..1cd016217448 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -342,14 +342,14 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, *created |= FILE_CREATED; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64) current_fsuid(); + args.uid = current_fsuid(); if (inode->i_mode & S_ISGID) - args.gid = (__u64) inode->i_gid; + args.gid = inode->i_gid; else - args.gid = (__u64) current_fsgid(); + args.gid = current_fsgid(); } else { - args.uid = NO_CHANGE_64; - args.gid = NO_CHANGE_64; + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ } CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, current->tgid); @@ -588,11 +588,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode, .device = device_number, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64) current_fsuid(); - args.gid = (__u64) current_fsgid(); + args.uid = current_fsuid(); + args.gid = current_fsgid(); } else { - args.uid = NO_CHANGE_64; - args.gid = NO_CHANGE_64; + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ } rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0a6677ba212b..b9baf5f66349 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -487,8 +487,8 @@ int cifs_open(struct inode *inode, struct file *file) */ struct cifs_unix_set_info_args args = { .mode = inode->i_mode, - .uid = NO_CHANGE_64, - .gid = NO_CHANGE_64, + .uid = INVALID_UID, /* no change */ + .gid = INVALID_GID, /* no change */ .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index d4cf7509c106..737643940540 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1249,14 +1249,14 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, .device = 0, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64)current_fsuid(); + args.uid = current_fsuid(); if (parent->i_mode & S_ISGID) - args.gid = (__u64)parent->i_gid; + args.gid = parent->i_gid; else - args.gid = (__u64)current_fsgid(); + args.gid = current_fsgid(); } else { - args.uid = NO_CHANGE_64; - args.gid = NO_CHANGE_64; + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ } CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, cifs_sb->local_nls, @@ -2017,12 +2017,12 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_UID) args->uid = attrs->ia_uid; else - args->uid = NO_CHANGE_64; + args->uid = INVALID_UID; /* no change */ if (attrs->ia_valid & ATTR_GID) args->gid = attrs->ia_gid; else - args->gid = NO_CHANGE_64; + args->gid = INVALID_GID; /* no change */ if (attrs->ia_valid & ATTR_ATIME) args->atime = cifs_UnixTimeToNT(attrs->ia_atime); From 6d4a083205c27bc2e2a1c03dff4acf2fe457d1c4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:48:56 -0800 Subject: [PATCH 87/93] cifs: Convert struct tcon_link to use a kuid. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 2 +- fs/cifs/connect.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index aea1eec64911..7993a7a07ae2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -837,7 +837,7 @@ struct cifs_tcon { */ struct tcon_link { struct rb_node tl_rbnode; - uid_t tl_uid; + kuid_t tl_uid; unsigned long tl_flags; #define TCON_LINK_MASTER 0 #define TCON_LINK_PENDING 1 diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 17c3643e5950..541169459223 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3919,7 +3919,7 @@ cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) } static struct cifs_tcon * -cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) { int rc; struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); @@ -3989,7 +3989,7 @@ cifs_sb_tcon_pending_wait(void *unused) /* find and return a tlink with given uid */ static struct tcon_link * -tlink_rb_search(struct rb_root *root, uid_t uid) +tlink_rb_search(struct rb_root *root, kuid_t uid) { struct rb_node *node = root->rb_node; struct tcon_link *tlink; @@ -3997,9 +3997,9 @@ tlink_rb_search(struct rb_root *root, uid_t uid) while (node) { tlink = rb_entry(node, struct tcon_link, tl_rbnode); - if (tlink->tl_uid > uid) + if (uid_gt(tlink->tl_uid, uid)) node = node->rb_left; - else if (tlink->tl_uid < uid) + else if (uid_lt(tlink->tl_uid, uid)) node = node->rb_right; else return tlink; @@ -4018,7 +4018,7 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) tlink = rb_entry(*new, struct tcon_link, tl_rbnode); parent = *new; - if (tlink->tl_uid > new_tlink->tl_uid) + if (uid_gt(tlink->tl_uid, new_tlink->tl_uid)) new = &((*new)->rb_left); else new = &((*new)->rb_right); @@ -4048,7 +4048,7 @@ struct tcon_link * cifs_sb_tlink(struct cifs_sb_info *cifs_sb) { int ret; - uid_t fsuid = current_fsuid(); + kuid_t fsuid = current_fsuid(); struct tcon_link *tlink, *newtlink; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) From 4a2c8cf56953a6ebe3c8671433607b7f96f200d5 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:53:25 -0800 Subject: [PATCH 88/93] cifs: Convert struct cifs_fattr to use kuid and kgids In cifs_unix_to_basic_fattr only update the cifs_fattr with an id if it is valid after conversion. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/inode.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7993a7a07ae2..98312fda0d35 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1245,8 +1245,8 @@ struct cifs_fattr { u64 cf_eof; u64 cf_bytes; u64 cf_createtime; - uid_t cf_uid; - gid_t cf_gid; + kuid_t cf_uid; + kgid_t cf_gid; umode_t cf_mode; dev_t cf_rdev; unsigned int cf_nlink; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 737643940540..9638233964fc 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -247,15 +247,21 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, fattr->cf_uid = cifs_sb->mnt_uid; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { u64 id = le64_to_cpu(info->Uid); - if (id < ((uid_t)-1)) - fattr->cf_uid = id; + if (id < ((uid_t)-1)) { + kuid_t uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fattr->cf_uid = uid; + } } fattr->cf_gid = cifs_sb->mnt_gid; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { u64 id = le64_to_cpu(info->Gid); - if (id < ((gid_t)-1)) - fattr->cf_gid = id; + if (id < ((gid_t)-1)) { + kgid_t gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fattr->cf_gid = gid; + } } fattr->cf_nlink = le64_to_cpu(info->Nlinks); From fef59fd728366aa9bf125b8859aff84fa0bd1a36 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 02:23:02 -0800 Subject: [PATCH 89/93] cifs: Convert struct cifsFileInfo to use a kuid Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 2 +- fs/cifs/file.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 98312fda0d35..04aa74e5b348 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -930,7 +930,7 @@ struct cifsFileInfo { struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head flist; /* next fid (file instance) for this inode */ struct cifs_fid_locks *llist; /* brlocks held by this fid */ - unsigned int uid; /* allows finding which FileInfo structure */ + kuid_t uid; /* allows finding which FileInfo structure */ __u32 pid; /* process id who opened file */ struct cifs_fid fid; /* file id from remote */ /* BB add lock scope info here if needed */ ; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b9baf5f66349..c23fbd81fe1a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1649,7 +1649,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, are always at the end of the list but since the first entry might have a close pending, we go through the whole list */ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (fsuid_only && open_file->uid != current_fsuid()) + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { if (!open_file->invalidHandle) { @@ -1702,7 +1702,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (!any_available && open_file->pid != current->tgid) continue; - if (fsuid_only && open_file->uid != current_fsuid()) + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { if (!open_file->invalidHandle) { From 3da46565043a3d7b9fd5c924e7de2d3e65e9d2a9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:37:39 -0800 Subject: [PATCH 90/93] cifs: Modify struct smb_vol to use kuids and kgids Add two helper functions get_option_uid and get_option_gid to handle the work of parsing uid and gids paramaters from the command line and making kuids and kgids out of them. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 10 +++++----- fs/cifs/connect.c | 50 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 04aa74e5b348..e308e8bd0772 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -399,11 +399,11 @@ struct smb_vol { char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ - uid_t cred_uid; - uid_t linux_uid; - gid_t linux_gid; - uid_t backupuid; - gid_t backupgid; + kuid_t cred_uid; + kuid_t linux_uid; + kgid_t linux_gid; + kuid_t backupuid; + kgid_t backupgid; umode_t file_mode; umode_t dir_mode; unsigned secFlg; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 541169459223..9bd13a75602d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -987,6 +987,41 @@ static int get_option_ul(substring_t args[], unsigned long *option) return rc; } +static int get_option_uid(substring_t args[], kuid_t *result) +{ + unsigned long value; + kuid_t uid; + int rc; + + rc = get_option_ul(args, &value); + if (rc) + return rc; + + uid = make_kuid(current_user_ns(), value); + if (!uid_valid(uid)) + return -EINVAL; + + *result = uid; + return 0; +} + +static int get_option_gid(substring_t args[], kgid_t *result) +{ + unsigned long value; + kgid_t gid; + int rc; + + rc = get_option_ul(args, &value); + if (rc) + return rc; + + gid = make_kgid(current_user_ns(), value); + if (!gid_valid(gid)) + return -EINVAL; + + *result = gid; + return 0; +} static int cifs_parse_security_flavors(char *value, struct smb_vol *vol) @@ -1424,47 +1459,42 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, /* Numeric Values */ case Opt_backupuid: - if (get_option_ul(args, &option)) { + if (get_option_uid(args, &vol->backupuid)) { cERROR(1, "%s: Invalid backupuid value", __func__); goto cifs_parse_mount_err; } - vol->backupuid = option; vol->backupuid_specified = true; break; case Opt_backupgid: - if (get_option_ul(args, &option)) { + if (get_option_gid(args, &vol->backupgid)) { cERROR(1, "%s: Invalid backupgid value", __func__); goto cifs_parse_mount_err; } - vol->backupgid = option; vol->backupgid_specified = true; break; case Opt_uid: - if (get_option_ul(args, &option)) { + if (get_option_uid(args, &vol->linux_uid)) { cERROR(1, "%s: Invalid uid value", __func__); goto cifs_parse_mount_err; } - vol->linux_uid = option; uid_specified = true; break; case Opt_cruid: - if (get_option_ul(args, &option)) { + if (get_option_uid(args, &vol->cred_uid)) { cERROR(1, "%s: Invalid cruid value", __func__); goto cifs_parse_mount_err; } - vol->cred_uid = option; break; case Opt_gid: - if (get_option_ul(args, &option)) { + if (get_option_gid(args, &vol->linux_gid)) { cERROR(1, "%s: Invalid gid value", __func__); goto cifs_parse_mount_err; } - vol->linux_gid = option; gid_specified = true; break; case Opt_file_mode: From 1f68233c52e9f2bb53130a0063bc1e6864f6d204 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:20:20 -0800 Subject: [PATCH 91/93] cifs: Convert struct cifs_sb_info to use kuids and kgids Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifs_fs_sb.h | 8 ++++---- fs/cifs/cifsfs.c | 14 ++++++++++---- fs/cifs/connect.c | 2 +- fs/cifs/misc.c | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index c865bfdfe819..37e4a72a7d1c 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -55,10 +55,10 @@ struct cifs_sb_info { unsigned int wsize; unsigned long actimeo; /* attribute cache timeout (jiffies) */ atomic_t active; - uid_t mnt_uid; - gid_t mnt_gid; - uid_t mnt_backupuid; - gid_t mnt_backupgid; + kuid_t mnt_uid; + kgid_t mnt_gid; + kuid_t mnt_backupuid; + kgid_t mnt_backupgid; umode_t mnt_file_mode; umode_t mnt_dir_mode; unsigned int mnt_cifs_flags; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f653835d067b..17590731786d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -376,13 +376,15 @@ cifs_show_options(struct seq_file *s, struct dentry *root) (int)(srcaddr->sa_family)); } - seq_printf(s, ",uid=%u", cifs_sb->mnt_uid); + seq_printf(s, ",uid=%u", + from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid)); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) seq_printf(s, ",forceuid"); else seq_printf(s, ",noforceuid"); - seq_printf(s, ",gid=%u", cifs_sb->mnt_gid); + seq_printf(s, ",gid=%u", + from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid)); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) seq_printf(s, ",forcegid"); else @@ -437,9 +439,13 @@ cifs_show_options(struct seq_file *s, struct dentry *root) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) seq_printf(s, ",noperm"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) - seq_printf(s, ",backupuid=%u", cifs_sb->mnt_backupuid); + seq_printf(s, ",backupuid=%u", + from_kuid_munged(&init_user_ns, + cifs_sb->mnt_backupuid)); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) - seq_printf(s, ",backupgid=%u", cifs_sb->mnt_backupgid); + seq_printf(s, ",backupgid=%u", + from_kgid_munged(&init_user_ns, + cifs_sb->mnt_backupgid)); seq_printf(s, ",rsize=%u", cifs_sb->rsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9bd13a75602d..01279b8f5d1f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2743,7 +2743,7 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) if (new->rsize && new->rsize < old->rsize) return 0; - if (old->mnt_uid != new->mnt_uid || old->mnt_gid != new->mnt_gid) + if (!uid_eq(old->mnt_uid, new->mnt_uid) || !gid_eq(old->mnt_gid, new->mnt_gid)) return 0; if (old->mnt_file_mode != new->mnt_file_mode || diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 3a00c0d0cead..1b15bf839f37 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -569,7 +569,7 @@ bool backup_cred(struct cifs_sb_info *cifs_sb) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { - if (cifs_sb->mnt_backupuid == current_fsuid()) + if (uid_eq(cifs_sb->mnt_backupuid, current_fsuid())) return true; } if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { From 64ed39dd1ef284c0338799a6167b77a6d6e01982 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 02:30:39 -0800 Subject: [PATCH 92/93] cifs: Convert struct cifs_ses to use a kuid_t and a kgid_t Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifs_spnego.c | 6 ++++-- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/connect.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 086f381d6489..10e774761299 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -149,10 +149,12 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) goto out; dp = description + strlen(description); - sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); + sprintf(dp, ";uid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->linux_uid)); dp = description + strlen(description); - sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); + sprintf(dp, ";creduid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); if (sesInfo->user_name) { dp = description + strlen(description); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e308e8bd0772..aaef57beba0e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -702,8 +702,8 @@ struct cifs_ses { char *serverNOS; /* name of network operating system of server */ char *serverDomain; /* security realm of server */ __u64 Suid; /* remote smb uid */ - uid_t linux_uid; /* overriding owner of files on the mount */ - uid_t cred_uid; /* owner of credentials */ + kuid_t linux_uid; /* overriding owner of files on the mount */ + kuid_t cred_uid; /* owner of credentials */ unsigned int capabilities; char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for TCP names - will ipv6 and sctp addresses fit? */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 01279b8f5d1f..067b0e169fe4 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2271,7 +2271,7 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) { switch (ses->server->secType) { case Kerberos: - if (vol->cred_uid != ses->cred_uid) + if (!uid_eq(vol->cred_uid, ses->cred_uid)) return 0; break; default: From 139321c65c0584cd65c4c87a5eb3fdb4fdbd0e19 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 02:31:39 -0800 Subject: [PATCH 93/93] cifs: Enable building with user namespaces enabled. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 69c5ccce55e7..b2de5edee0ce 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1071,7 +1071,6 @@ config UIDGID_CONVERTED default y # Filesystems - depends on CIFS = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS