Back to home page

LXR

 
 

    


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