LCOV - code coverage report
Current view: top level - kernel - groups.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 91 103 88.3 %
Date: 2021-04-22 12:43:58 Functions: 15 18 83.3 %

          Line data    Source code
       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          49 : struct group_info *groups_alloc(int gidsetsize)
      16             : {
      17          49 :         struct group_info *gi;
      18          98 :         gi = kvmalloc(struct_size(gi, gid, gidsetsize), GFP_KERNEL_ACCOUNT);
      19          49 :         if (!gi)
      20             :                 return NULL;
      21             : 
      22          49 :         atomic_set(&gi->usage, 1);
      23          49 :         gi->ngroups = gidsetsize;
      24          49 :         return gi;
      25             : }
      26             : 
      27             : EXPORT_SYMBOL(groups_alloc);
      28             : 
      29          38 : void groups_free(struct group_info *group_info)
      30             : {
      31          38 :         kvfree(group_info);
      32           0 : }
      33             : 
      34             : EXPORT_SYMBOL(groups_free);
      35             : 
      36             : /* export the group_info to a user-space array */
      37           6 : static int groups_to_user(gid_t __user *grouplist,
      38             :                           const struct group_info *group_info)
      39             : {
      40           6 :         struct user_namespace *user_ns = current_user_ns();
      41           6 :         int i;
      42           6 :         unsigned int count = group_info->ngroups;
      43             : 
      44          10 :         for (i = 0; i < count; i++) {
      45           4 :                 gid_t gid;
      46           4 :                 gid = from_kgid_munged(user_ns, group_info->gid[i]);
      47           4 :                 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 */
      54          49 : static int groups_from_user(struct group_info *group_info,
      55             :     gid_t __user *grouplist)
      56             : {
      57          49 :         struct user_namespace *user_ns = current_user_ns();
      58          49 :         int i;
      59          49 :         unsigned int count = group_info->ngroups;
      60             : 
      61          74 :         for (i = 0; i < count; i++) {
      62          25 :                 gid_t gid;
      63          25 :                 kgid_t kgid;
      64          25 :                 if (get_user(gid, grouplist+i))
      65          49 :                         return -EFAULT;
      66             : 
      67          25 :                 kgid = make_kgid(user_ns, gid);
      68          25 :                 if (!gid_valid(kgid))
      69             :                         return -EINVAL;
      70             : 
      71          25 :                 group_info->gid[i] = kgid;
      72             :         }
      73             :         return 0;
      74             : }
      75             : 
      76           0 : static int gid_cmp(const void *_a, const void *_b)
      77             : {
      78           0 :         kgid_t a = *(kgid_t *)_a;
      79           0 :         kgid_t b = *(kgid_t *)_b;
      80             : 
      81           0 :         return gid_gt(a, b) - gid_lt(a, b);
      82             : }
      83             : 
      84          49 : void groups_sort(struct group_info *group_info)
      85             : {
      86          49 :         sort(group_info->gid, group_info->ngroups, sizeof(*group_info->gid),
      87             :              gid_cmp, NULL);
      88          49 : }
      89             : EXPORT_SYMBOL(groups_sort);
      90             : 
      91             : /* a simple bsearch */
      92         270 : int groups_search(const struct group_info *group_info, kgid_t grp)
      93             : {
      94         270 :         unsigned int left, right;
      95             : 
      96         270 :         if (!group_info)
      97             :                 return 0;
      98             : 
      99         270 :         left = 0;
     100         270 :         right = group_info->ngroups;
     101         275 :         while (left < right) {
     102           8 :                 unsigned int mid = (left+right)/2;
     103           8 :                 if (gid_gt(grp, group_info->gid[mid]))
     104           0 :                         left = mid + 1;
     105           8 :                 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             :  */
     118          49 : void set_groups(struct cred *new, struct group_info *group_info)
     119             : {
     120          98 :         put_group_info(new->group_info);
     121          49 :         get_group_info(group_info);
     122          49 :         new->group_info = group_info;
     123          49 : }
     124             : 
     125             : EXPORT_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             :  */
     134          49 : int set_current_groups(struct group_info *group_info)
     135             : {
     136          49 :         struct cred *new;
     137             : 
     138          49 :         new = prepare_creds();
     139          49 :         if (!new)
     140             :                 return -ENOMEM;
     141             : 
     142          49 :         set_groups(new, group_info);
     143          49 :         return commit_creds(new);
     144             : }
     145             : 
     146             : EXPORT_SYMBOL(set_current_groups);
     147             : 
     148          36 : SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
     149             : {
     150          18 :         const struct cred *cred = current_cred();
     151          18 :         int i;
     152             : 
     153          18 :         if (gidsetsize < 0)
     154             :                 return -EINVAL;
     155             : 
     156             :         /* no need to grab task_lock here; it cannot change */
     157          18 :         i = cred->group_info->ngroups;
     158          18 :         if (gidsetsize) {
     159           6 :                 if (i > gidsetsize) {
     160           0 :                         i = -EINVAL;
     161           0 :                         goto out;
     162             :                 }
     163           6 :                 if (groups_to_user(grouplist, cred->group_info)) {
     164           0 :                         i = -EFAULT;
     165           0 :                         goto out;
     166             :                 }
     167             :         }
     168          18 : out:
     169          18 :         return i;
     170             : }
     171             : 
     172          49 : bool may_setgroups(void)
     173             : {
     174          49 :         struct user_namespace *user_ns = current_user_ns();
     175             : 
     176          49 :         return ns_capable_setid(user_ns, CAP_SETGID) &&
     177          49 :                 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             : 
     185          98 : SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
     186             : {
     187          49 :         struct group_info *group_info;
     188          49 :         int retval;
     189             : 
     190          49 :         if (!may_setgroups())
     191             :                 return -EPERM;
     192          49 :         if ((unsigned)gidsetsize > NGROUPS_MAX)
     193             :                 return -EINVAL;
     194             : 
     195          49 :         group_info = groups_alloc(gidsetsize);
     196          49 :         if (!group_info)
     197             :                 return -ENOMEM;
     198          49 :         retval = groups_from_user(group_info, grouplist);
     199          49 :         if (retval) {
     200           0 :                 put_group_info(group_info);
     201           0 :                 return retval;
     202             :         }
     203             : 
     204          49 :         groups_sort(group_info);
     205          49 :         retval = set_current_groups(group_info);
     206          98 :         put_group_info(group_info);
     207             : 
     208          49 :         return retval;
     209             : }
     210             : 
     211             : /*
     212             :  * Check whether we're fsgid/egid or in the supplemental group..
     213             :  */
     214        1505 : int in_group_p(kgid_t grp)
     215             : {
     216        1505 :         const struct cred *cred = current_cred();
     217        1505 :         int retval = 1;
     218             : 
     219        1505 :         if (!gid_eq(grp, cred->fsgid))
     220         265 :                 retval = groups_search(cred->group_info, grp);
     221        1505 :         return retval;
     222             : }
     223             : 
     224             : EXPORT_SYMBOL(in_group_p);
     225             : 
     226           5 : int in_egroup_p(kgid_t grp)
     227             : {
     228           5 :         const struct cred *cred = current_cred();
     229           5 :         int retval = 1;
     230             : 
     231           5 :         if (!gid_eq(grp, cred->egid))
     232           5 :                 retval = groups_search(cred->group_info, grp);
     233           5 :         return retval;
     234             : }
     235             : 
     236             : EXPORT_SYMBOL(in_egroup_p);

Generated by: LCOV version 1.14