cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

groups.c (4897B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Supplementary group IDs
      4 */
      5#include <linux/cred.h>
      6#include <linux/export.h>
      7#include <linux/slab.h>
      8#include <linux/security.h>
      9#include <linux/sort.h>
     10#include <linux/syscalls.h>
     11#include <linux/user_namespace.h>
     12#include <linux/vmalloc.h>
     13#include <linux/uaccess.h>
     14
     15struct group_info *groups_alloc(int gidsetsize)
     16{
     17	struct group_info *gi;
     18	gi = kvmalloc(struct_size(gi, gid, gidsetsize), GFP_KERNEL_ACCOUNT);
     19	if (!gi)
     20		return NULL;
     21
     22	atomic_set(&gi->usage, 1);
     23	gi->ngroups = gidsetsize;
     24	return gi;
     25}
     26
     27EXPORT_SYMBOL(groups_alloc);
     28
     29void groups_free(struct group_info *group_info)
     30{
     31	kvfree(group_info);
     32}
     33
     34EXPORT_SYMBOL(groups_free);
     35
     36/* export the group_info to a user-space array */
     37static int groups_to_user(gid_t __user *grouplist,
     38			  const struct group_info *group_info)
     39{
     40	struct user_namespace *user_ns = current_user_ns();
     41	int i;
     42	unsigned int count = group_info->ngroups;
     43
     44	for (i = 0; i < count; i++) {
     45		gid_t gid;
     46		gid = from_kgid_munged(user_ns, group_info->gid[i]);
     47		if (put_user(gid, grouplist+i))
     48			return -EFAULT;
     49	}
     50	return 0;
     51}
     52
     53/* fill a group_info from a user-space array - it must be allocated already */
     54static int groups_from_user(struct group_info *group_info,
     55    gid_t __user *grouplist)
     56{
     57	struct user_namespace *user_ns = current_user_ns();
     58	int i;
     59	unsigned int count = group_info->ngroups;
     60
     61	for (i = 0; i < count; i++) {
     62		gid_t gid;
     63		kgid_t kgid;
     64		if (get_user(gid, grouplist+i))
     65			return -EFAULT;
     66
     67		kgid = make_kgid(user_ns, gid);
     68		if (!gid_valid(kgid))
     69			return -EINVAL;
     70
     71		group_info->gid[i] = kgid;
     72	}
     73	return 0;
     74}
     75
     76static int gid_cmp(const void *_a, const void *_b)
     77{
     78	kgid_t a = *(kgid_t *)_a;
     79	kgid_t b = *(kgid_t *)_b;
     80
     81	return gid_gt(a, b) - gid_lt(a, b);
     82}
     83
     84void groups_sort(struct group_info *group_info)
     85{
     86	sort(group_info->gid, group_info->ngroups, sizeof(*group_info->gid),
     87	     gid_cmp, NULL);
     88}
     89EXPORT_SYMBOL(groups_sort);
     90
     91/* a simple bsearch */
     92int groups_search(const struct group_info *group_info, kgid_t grp)
     93{
     94	unsigned int left, right;
     95
     96	if (!group_info)
     97		return 0;
     98
     99	left = 0;
    100	right = group_info->ngroups;
    101	while (left < right) {
    102		unsigned int mid = (left+right)/2;
    103		if (gid_gt(grp, group_info->gid[mid]))
    104			left = mid + 1;
    105		else if (gid_lt(grp, group_info->gid[mid]))
    106			right = mid;
    107		else
    108			return 1;
    109	}
    110	return 0;
    111}
    112
    113/**
    114 * set_groups - Change a group subscription in a set of credentials
    115 * @new: The newly prepared set of credentials to alter
    116 * @group_info: The group list to install
    117 */
    118void set_groups(struct cred *new, struct group_info *group_info)
    119{
    120	put_group_info(new->group_info);
    121	get_group_info(group_info);
    122	new->group_info = group_info;
    123}
    124
    125EXPORT_SYMBOL(set_groups);
    126
    127/**
    128 * set_current_groups - Change current's group subscription
    129 * @group_info: The group list to impose
    130 *
    131 * Validate a group subscription and, if valid, impose it upon current's task
    132 * security record.
    133 */
    134int set_current_groups(struct group_info *group_info)
    135{
    136	struct cred *new;
    137
    138	new = prepare_creds();
    139	if (!new)
    140		return -ENOMEM;
    141
    142	set_groups(new, group_info);
    143	return commit_creds(new);
    144}
    145
    146EXPORT_SYMBOL(set_current_groups);
    147
    148SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
    149{
    150	const struct cred *cred = current_cred();
    151	int i;
    152
    153	if (gidsetsize < 0)
    154		return -EINVAL;
    155
    156	/* no need to grab task_lock here; it cannot change */
    157	i = cred->group_info->ngroups;
    158	if (gidsetsize) {
    159		if (i > gidsetsize) {
    160			i = -EINVAL;
    161			goto out;
    162		}
    163		if (groups_to_user(grouplist, cred->group_info)) {
    164			i = -EFAULT;
    165			goto out;
    166		}
    167	}
    168out:
    169	return i;
    170}
    171
    172bool may_setgroups(void)
    173{
    174	struct user_namespace *user_ns = current_user_ns();
    175
    176	return ns_capable_setid(user_ns, CAP_SETGID) &&
    177		userns_may_setgroups(user_ns);
    178}
    179
    180/*
    181 *	SMP: Our groups are copy-on-write. We can set them safely
    182 *	without another task interfering.
    183 */
    184
    185SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
    186{
    187	struct group_info *group_info;
    188	int retval;
    189
    190	if (!may_setgroups())
    191		return -EPERM;
    192	if ((unsigned)gidsetsize > NGROUPS_MAX)
    193		return -EINVAL;
    194
    195	group_info = groups_alloc(gidsetsize);
    196	if (!group_info)
    197		return -ENOMEM;
    198	retval = groups_from_user(group_info, grouplist);
    199	if (retval) {
    200		put_group_info(group_info);
    201		return retval;
    202	}
    203
    204	groups_sort(group_info);
    205	retval = set_current_groups(group_info);
    206	put_group_info(group_info);
    207
    208	return retval;
    209}
    210
    211/*
    212 * Check whether we're fsgid/egid or in the supplemental group..
    213 */
    214int in_group_p(kgid_t grp)
    215{
    216	const struct cred *cred = current_cred();
    217	int retval = 1;
    218
    219	if (!gid_eq(grp, cred->fsgid))
    220		retval = groups_search(cred->group_info, grp);
    221	return retval;
    222}
    223
    224EXPORT_SYMBOL(in_group_p);
    225
    226int in_egroup_p(kgid_t grp)
    227{
    228	const struct cred *cred = current_cred();
    229	int retval = 1;
    230
    231	if (!gid_eq(grp, cred->egid))
    232		retval = groups_search(cred->group_info, grp);
    233	return retval;
    234}
    235
    236EXPORT_SYMBOL(in_egroup_p);