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