LCOV - code coverage report
Current view: top level - kernel - groups.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 89 0.0 %
Date: 2023-08-24 13:40:31 Functions: 0 16 0.0 %

          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           0 : struct group_info *groups_alloc(int gidsetsize)
      16             : {
      17             :         struct group_info *gi;
      18           0 :         gi = kvmalloc(struct_size(gi, gid, gidsetsize), GFP_KERNEL_ACCOUNT);
      19           0 :         if (!gi)
      20             :                 return NULL;
      21             : 
      22           0 :         atomic_set(&gi->usage, 1);
      23           0 :         gi->ngroups = gidsetsize;
      24           0 :         return gi;
      25             : }
      26             : 
      27             : EXPORT_SYMBOL(groups_alloc);
      28             : 
      29           0 : void groups_free(struct group_info *group_info)
      30             : {
      31           0 :         kvfree(group_info);
      32           0 : }
      33             : 
      34             : EXPORT_SYMBOL(groups_free);
      35             : 
      36             : /* export the group_info to a user-space array */
      37           0 : static int groups_to_user(gid_t __user *grouplist,
      38             :                           const struct group_info *group_info)
      39             : {
      40           0 :         struct user_namespace *user_ns = current_user_ns();
      41             :         int i;
      42           0 :         unsigned int count = group_info->ngroups;
      43             : 
      44           0 :         for (i = 0; i < count; i++) {
      45             :                 gid_t gid;
      46           0 :                 gid = from_kgid_munged(user_ns, group_info->gid[i]);
      47           0 :                 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           0 : static int groups_from_user(struct group_info *group_info,
      55             :     gid_t __user *grouplist)
      56             : {
      57           0 :         struct user_namespace *user_ns = current_user_ns();
      58             :         int i;
      59           0 :         unsigned int count = group_info->ngroups;
      60             : 
      61           0 :         for (i = 0; i < count; i++) {
      62             :                 gid_t gid;
      63             :                 kgid_t kgid;
      64           0 :                 if (get_user(gid, grouplist+i))
      65             :                         return -EFAULT;
      66             : 
      67           0 :                 kgid = make_kgid(user_ns, gid);
      68           0 :                 if (!gid_valid(kgid))
      69             :                         return -EINVAL;
      70             : 
      71           0 :                 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           0 : void groups_sort(struct group_info *group_info)
      85             : {
      86           0 :         sort(group_info->gid, group_info->ngroups, sizeof(*group_info->gid),
      87             :              gid_cmp, NULL);
      88           0 : }
      89             : EXPORT_SYMBOL(groups_sort);
      90             : 
      91             : /* a simple bsearch */
      92           0 : int groups_search(const struct group_info *group_info, kgid_t grp)
      93             : {
      94             :         unsigned int left, right;
      95             : 
      96           0 :         if (!group_info)
      97             :                 return 0;
      98             : 
      99           0 :         left = 0;
     100           0 :         right = group_info->ngroups;
     101           0 :         while (left < right) {
     102           0 :                 unsigned int mid = (left+right)/2;
     103           0 :                 if (gid_gt(grp, group_info->gid[mid]))
     104           0 :                         left = mid + 1;
     105           0 :                 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           0 : void set_groups(struct cred *new, struct group_info *group_info)
     119             : {
     120           0 :         put_group_info(new->group_info);
     121           0 :         get_group_info(group_info);
     122           0 :         new->group_info = group_info;
     123           0 : }
     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           0 : int set_current_groups(struct group_info *group_info)
     135             : {
     136             :         struct cred *new;
     137             :         const struct cred *old;
     138             :         int retval;
     139             : 
     140           0 :         new = prepare_creds();
     141           0 :         if (!new)
     142             :                 return -ENOMEM;
     143             : 
     144           0 :         old = current_cred();
     145             : 
     146           0 :         set_groups(new, group_info);
     147             : 
     148           0 :         retval = security_task_fix_setgroups(new, old);
     149             :         if (retval < 0)
     150             :                 goto error;
     151             : 
     152           0 :         return commit_creds(new);
     153             : 
     154             : error:
     155             :         abort_creds(new);
     156             :         return retval;
     157             : }
     158             : 
     159             : EXPORT_SYMBOL(set_current_groups);
     160             : 
     161           0 : SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
     162             : {
     163           0 :         const struct cred *cred = current_cred();
     164             :         int i;
     165             : 
     166           0 :         if (gidsetsize < 0)
     167             :                 return -EINVAL;
     168             : 
     169             :         /* no need to grab task_lock here; it cannot change */
     170           0 :         i = cred->group_info->ngroups;
     171           0 :         if (gidsetsize) {
     172           0 :                 if (i > gidsetsize) {
     173             :                         i = -EINVAL;
     174             :                         goto out;
     175             :                 }
     176           0 :                 if (groups_to_user(grouplist, cred->group_info)) {
     177           0 :                         i = -EFAULT;
     178           0 :                         goto out;
     179             :                 }
     180             :         }
     181             : out:
     182           0 :         return i;
     183             : }
     184             : 
     185           0 : bool may_setgroups(void)
     186             : {
     187           0 :         struct user_namespace *user_ns = current_user_ns();
     188             : 
     189           0 :         return ns_capable_setid(user_ns, CAP_SETGID) &&
     190             :                 userns_may_setgroups(user_ns);
     191             : }
     192             : 
     193             : /*
     194             :  *      SMP: Our groups are copy-on-write. We can set them safely
     195             :  *      without another task interfering.
     196             :  */
     197             : 
     198           0 : SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
     199             : {
     200             :         struct group_info *group_info;
     201             :         int retval;
     202             : 
     203           0 :         if (!may_setgroups())
     204             :                 return -EPERM;
     205           0 :         if ((unsigned)gidsetsize > NGROUPS_MAX)
     206             :                 return -EINVAL;
     207             : 
     208           0 :         group_info = groups_alloc(gidsetsize);
     209           0 :         if (!group_info)
     210             :                 return -ENOMEM;
     211           0 :         retval = groups_from_user(group_info, grouplist);
     212           0 :         if (retval) {
     213           0 :                 put_group_info(group_info);
     214           0 :                 return retval;
     215             :         }
     216             : 
     217           0 :         groups_sort(group_info);
     218           0 :         retval = set_current_groups(group_info);
     219           0 :         put_group_info(group_info);
     220             : 
     221           0 :         return retval;
     222             : }
     223             : 
     224             : /*
     225             :  * Check whether we're fsgid/egid or in the supplemental group..
     226             :  */
     227           0 : int in_group_p(kgid_t grp)
     228             : {
     229           0 :         const struct cred *cred = current_cred();
     230           0 :         int retval = 1;
     231             : 
     232           0 :         if (!gid_eq(grp, cred->fsgid))
     233           0 :                 retval = groups_search(cred->group_info, grp);
     234           0 :         return retval;
     235             : }
     236             : 
     237             : EXPORT_SYMBOL(in_group_p);
     238             : 
     239           0 : int in_egroup_p(kgid_t grp)
     240             : {
     241           0 :         const struct cred *cred = current_cred();
     242           0 :         int retval = 1;
     243             : 
     244           0 :         if (!gid_eq(grp, cred->egid))
     245           0 :                 retval = groups_search(cred->group_info, grp);
     246           0 :         return retval;
     247             : }
     248             : 
     249             : EXPORT_SYMBOL(in_egroup_p);

Generated by: LCOV version 1.14