Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Supplementary group IDs
0004  */
0005 #include <linux/cred.h>
0006 #include <linux/export.h>
0007 #include <linux/slab.h>
0008 #include <linux/security.h>
0009 #include <linux/sort.h>
0010 #include <linux/syscalls.h>
0011 #include <linux/user_namespace.h>
0012 #include <linux/vmalloc.h>
0013 #include <linux/uaccess.h>
0014 
0015 struct group_info *groups_alloc(int gidsetsize)
0016 {
0017     struct group_info *gi;
0018     gi = kvmalloc(struct_size(gi, gid, gidsetsize), GFP_KERNEL_ACCOUNT);
0019     if (!gi)
0020         return NULL;
0021 
0022     atomic_set(&gi->usage, 1);
0023     gi->ngroups = gidsetsize;
0024     return gi;
0025 }
0026 
0027 EXPORT_SYMBOL(groups_alloc);
0028 
0029 void groups_free(struct group_info *group_info)
0030 {
0031     kvfree(group_info);
0032 }
0033 
0034 EXPORT_SYMBOL(groups_free);
0035 
0036 /* export the group_info to a user-space array */
0037 static int groups_to_user(gid_t __user *grouplist,
0038               const struct group_info *group_info)
0039 {
0040     struct user_namespace *user_ns = current_user_ns();
0041     int i;
0042     unsigned int count = group_info->ngroups;
0043 
0044     for (i = 0; i < count; i++) {
0045         gid_t gid;
0046         gid = from_kgid_munged(user_ns, group_info->gid[i]);
0047         if (put_user(gid, grouplist+i))
0048             return -EFAULT;
0049     }
0050     return 0;
0051 }
0052 
0053 /* fill a group_info from a user-space array - it must be allocated already */
0054 static int groups_from_user(struct group_info *group_info,
0055     gid_t __user *grouplist)
0056 {
0057     struct user_namespace *user_ns = current_user_ns();
0058     int i;
0059     unsigned int count = group_info->ngroups;
0060 
0061     for (i = 0; i < count; i++) {
0062         gid_t gid;
0063         kgid_t kgid;
0064         if (get_user(gid, grouplist+i))
0065             return -EFAULT;
0066 
0067         kgid = make_kgid(user_ns, gid);
0068         if (!gid_valid(kgid))
0069             return -EINVAL;
0070 
0071         group_info->gid[i] = kgid;
0072     }
0073     return 0;
0074 }
0075 
0076 static int gid_cmp(const void *_a, const void *_b)
0077 {
0078     kgid_t a = *(kgid_t *)_a;
0079     kgid_t b = *(kgid_t *)_b;
0080 
0081     return gid_gt(a, b) - gid_lt(a, b);
0082 }
0083 
0084 void groups_sort(struct group_info *group_info)
0085 {
0086     sort(group_info->gid, group_info->ngroups, sizeof(*group_info->gid),
0087          gid_cmp, NULL);
0088 }
0089 EXPORT_SYMBOL(groups_sort);
0090 
0091 /* a simple bsearch */
0092 int groups_search(const struct group_info *group_info, kgid_t grp)
0093 {
0094     unsigned int left, right;
0095 
0096     if (!group_info)
0097         return 0;
0098 
0099     left = 0;
0100     right = group_info->ngroups;
0101     while (left < right) {
0102         unsigned int mid = (left+right)/2;
0103         if (gid_gt(grp, group_info->gid[mid]))
0104             left = mid + 1;
0105         else if (gid_lt(grp, group_info->gid[mid]))
0106             right = mid;
0107         else
0108             return 1;
0109     }
0110     return 0;
0111 }
0112 
0113 /**
0114  * set_groups - Change a group subscription in a set of credentials
0115  * @new: The newly prepared set of credentials to alter
0116  * @group_info: The group list to install
0117  */
0118 void set_groups(struct cred *new, struct group_info *group_info)
0119 {
0120     put_group_info(new->group_info);
0121     get_group_info(group_info);
0122     new->group_info = group_info;
0123 }
0124 
0125 EXPORT_SYMBOL(set_groups);
0126 
0127 /**
0128  * set_current_groups - Change current's group subscription
0129  * @group_info: The group list to impose
0130  *
0131  * Validate a group subscription and, if valid, impose it upon current's task
0132  * security record.
0133  */
0134 int set_current_groups(struct group_info *group_info)
0135 {
0136     struct cred *new;
0137     const struct cred *old;
0138     int retval;
0139 
0140     new = prepare_creds();
0141     if (!new)
0142         return -ENOMEM;
0143 
0144     old = current_cred();
0145 
0146     set_groups(new, group_info);
0147 
0148     retval = security_task_fix_setgroups(new, old);
0149     if (retval < 0)
0150         goto error;
0151 
0152     return commit_creds(new);
0153 
0154 error:
0155     abort_creds(new);
0156     return retval;
0157 }
0158 
0159 EXPORT_SYMBOL(set_current_groups);
0160 
0161 SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
0162 {
0163     const struct cred *cred = current_cred();
0164     int i;
0165 
0166     if (gidsetsize < 0)
0167         return -EINVAL;
0168 
0169     /* no need to grab task_lock here; it cannot change */
0170     i = cred->group_info->ngroups;
0171     if (gidsetsize) {
0172         if (i > gidsetsize) {
0173             i = -EINVAL;
0174             goto out;
0175         }
0176         if (groups_to_user(grouplist, cred->group_info)) {
0177             i = -EFAULT;
0178             goto out;
0179         }
0180     }
0181 out:
0182     return i;
0183 }
0184 
0185 bool may_setgroups(void)
0186 {
0187     struct user_namespace *user_ns = current_user_ns();
0188 
0189     return ns_capable_setid(user_ns, CAP_SETGID) &&
0190         userns_may_setgroups(user_ns);
0191 }
0192 
0193 /*
0194  *  SMP: Our groups are copy-on-write. We can set them safely
0195  *  without another task interfering.
0196  */
0197 
0198 SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
0199 {
0200     struct group_info *group_info;
0201     int retval;
0202 
0203     if (!may_setgroups())
0204         return -EPERM;
0205     if ((unsigned)gidsetsize > NGROUPS_MAX)
0206         return -EINVAL;
0207 
0208     group_info = groups_alloc(gidsetsize);
0209     if (!group_info)
0210         return -ENOMEM;
0211     retval = groups_from_user(group_info, grouplist);
0212     if (retval) {
0213         put_group_info(group_info);
0214         return retval;
0215     }
0216 
0217     groups_sort(group_info);
0218     retval = set_current_groups(group_info);
0219     put_group_info(group_info);
0220 
0221     return retval;
0222 }
0223 
0224 /*
0225  * Check whether we're fsgid/egid or in the supplemental group..
0226  */
0227 int in_group_p(kgid_t grp)
0228 {
0229     const struct cred *cred = current_cred();
0230     int retval = 1;
0231 
0232     if (!gid_eq(grp, cred->fsgid))
0233         retval = groups_search(cred->group_info, grp);
0234     return retval;
0235 }
0236 
0237 EXPORT_SYMBOL(in_group_p);
0238 
0239 int in_egroup_p(kgid_t grp)
0240 {
0241     const struct cred *cred = current_cred();
0242     int retval = 1;
0243 
0244     if (!gid_eq(grp, cred->egid))
0245         retval = groups_search(cred->group_info, grp);
0246     return retval;
0247 }
0248 
0249 EXPORT_SYMBOL(in_egroup_p);