root/kernel/groups.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. groups_alloc
  2. groups_free
  3. groups_to_user
  4. groups_from_user
  5. gid_cmp
  6. groups_sort
  7. groups_search
  8. set_groups
  9. set_current_groups
  10. SYSCALL_DEFINE2
  11. may_setgroups
  12. SYSCALL_DEFINE2
  13. in_group_p
  14. in_egroup_p

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

/* [<][>][^][v][top][bottom][index][help] */