Line data Source code
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 <asm/uaccess.h>
11 :
12 : /* init to 2 - one for init_task, one to ensure it is never freed */
13 : struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
14 :
15 297 : struct group_info *groups_alloc(int gidsetsize)
16 : {
17 : struct group_info *group_info;
18 : int nblocks;
19 : int i;
20 :
21 297 : nblocks = (gidsetsize + NGROUPS_PER_BLOCK - 1) / NGROUPS_PER_BLOCK;
22 : /* Make sure we always allocate at least one indirect block pointer */
23 297 : nblocks = nblocks ? : 1;
24 297 : group_info = kmalloc(sizeof(*group_info) + nblocks*sizeof(gid_t *), GFP_USER);
25 297 : if (!group_info)
26 : return NULL;
27 297 : group_info->ngroups = gidsetsize;
28 297 : group_info->nblocks = nblocks;
29 297 : atomic_set(&group_info->usage, 1);
30 :
31 297 : if (gidsetsize <= NGROUPS_SMALL)
32 297 : group_info->blocks[0] = group_info->small_block;
33 : else {
34 0 : for (i = 0; i < nblocks; i++) {
35 : kgid_t *b;
36 0 : b = (void *)__get_free_page(GFP_USER);
37 0 : if (!b)
38 : goto out_undo_partial_alloc;
39 0 : group_info->blocks[i] = b;
40 : }
41 : }
42 297 : return group_info;
43 :
44 : out_undo_partial_alloc:
45 0 : while (--i >= 0) {
46 0 : free_page((unsigned long)group_info->blocks[i]);
47 : }
48 0 : kfree(group_info);
49 0 : return NULL;
50 : }
51 :
52 : EXPORT_SYMBOL(groups_alloc);
53 :
54 277 : void groups_free(struct group_info *group_info)
55 : {
56 277 : if (group_info->blocks[0] != group_info->small_block) {
57 : int i;
58 0 : for (i = 0; i < group_info->nblocks; i++)
59 0 : free_page((unsigned long)group_info->blocks[i]);
60 : }
61 277 : kfree(group_info);
62 277 : }
63 :
64 : EXPORT_SYMBOL(groups_free);
65 :
66 : /* export the group_info to a user-space array */
67 49 : static int groups_to_user(gid_t __user *grouplist,
68 : const struct group_info *group_info)
69 : {
70 : struct user_namespace *user_ns = current_user_ns();
71 : int i;
72 49 : unsigned int count = group_info->ngroups;
73 :
74 597 : for (i = 0; i < count; i++) {
75 : gid_t gid;
76 548 : gid = from_kgid_munged(user_ns, GROUP_AT(group_info, i));
77 548 : if (put_user(gid, grouplist+i))
78 : return -EFAULT;
79 : }
80 : return 0;
81 : }
82 :
83 : /* fill a group_info from a user-space array - it must be allocated already */
84 297 : static int groups_from_user(struct group_info *group_info,
85 : gid_t __user *grouplist)
86 : {
87 : struct user_namespace *user_ns = current_user_ns();
88 : int i;
89 297 : unsigned int count = group_info->ngroups;
90 :
91 1535 : for (i = 0; i < count; i++) {
92 : gid_t gid;
93 : kgid_t kgid;
94 1238 : if (get_user(gid, grouplist+i))
95 : return -EFAULT;
96 :
97 : kgid = make_kgid(user_ns, gid);
98 1238 : if (!gid_valid(kgid))
99 : return -EINVAL;
100 :
101 1238 : GROUP_AT(group_info, i) = kgid;
102 : }
103 : return 0;
104 : }
105 :
106 : /* a simple Shell sort */
107 297 : static void groups_sort(struct group_info *group_info)
108 : {
109 : int base, max, stride;
110 297 : int gidsetsize = group_info->ngroups;
111 :
112 297 : for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1)
113 : ; /* nothing */
114 297 : stride /= 3;
115 :
116 915 : while (stride) {
117 321 : max = gidsetsize - stride;
118 2175 : for (base = 0; base < max; base++) {
119 : int left = base;
120 1854 : int right = left + stride;
121 1854 : kgid_t tmp = GROUP_AT(group_info, right);
122 :
123 4511 : while (left >= 0 && gid_gt(GROUP_AT(group_info, left), tmp)) {
124 803 : GROUP_AT(group_info, right) =
125 : GROUP_AT(group_info, left);
126 : right = left;
127 803 : left -= stride;
128 : }
129 1854 : GROUP_AT(group_info, right) = tmp;
130 : }
131 321 : stride /= 3;
132 : }
133 297 : }
134 :
135 : /* a simple bsearch */
136 94445 : int groups_search(const struct group_info *group_info, kgid_t grp)
137 : {
138 : unsigned int left, right;
139 :
140 94445 : if (!group_info)
141 : return 0;
142 :
143 : left = 0;
144 94445 : right = group_info->ngroups;
145 500017 : while (left < right) {
146 311138 : unsigned int mid = (left+right)/2;
147 311138 : if (gid_gt(grp, GROUP_AT(group_info, mid)))
148 24089 : left = mid + 1;
149 287049 : else if (gid_lt(grp, GROUP_AT(group_info, mid)))
150 : right = mid;
151 : else
152 : return 1;
153 : }
154 : return 0;
155 : }
156 :
157 : /**
158 : * set_groups - Change a group subscription in a set of credentials
159 : * @new: The newly prepared set of credentials to alter
160 : * @group_info: The group list to install
161 : */
162 297 : void set_groups(struct cred *new, struct group_info *group_info)
163 : {
164 594 : put_group_info(new->group_info);
165 297 : groups_sort(group_info);
166 : get_group_info(group_info);
167 297 : new->group_info = group_info;
168 297 : }
169 :
170 : EXPORT_SYMBOL(set_groups);
171 :
172 : /**
173 : * set_current_groups - Change current's group subscription
174 : * @group_info: The group list to impose
175 : *
176 : * Validate a group subscription and, if valid, impose it upon current's task
177 : * security record.
178 : */
179 297 : int set_current_groups(struct group_info *group_info)
180 : {
181 : struct cred *new;
182 :
183 297 : new = prepare_creds();
184 297 : if (!new)
185 : return -ENOMEM;
186 :
187 297 : set_groups(new, group_info);
188 297 : return commit_creds(new);
189 : }
190 :
191 : EXPORT_SYMBOL(set_current_groups);
192 :
193 266 : SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
194 : {
195 133 : const struct cred *cred = current_cred();
196 : int i;
197 :
198 133 : if (gidsetsize < 0)
199 : return -EINVAL;
200 :
201 : /* no need to grab task_lock here; it cannot change */
202 133 : i = cred->group_info->ngroups;
203 133 : if (gidsetsize) {
204 49 : if (i > gidsetsize) {
205 : i = -EINVAL;
206 : goto out;
207 : }
208 49 : if (groups_to_user(grouplist, cred->group_info)) {
209 : i = -EFAULT;
210 : goto out;
211 : }
212 : }
213 : out:
214 : return i;
215 : }
216 :
217 297 : bool may_setgroups(void)
218 : {
219 : struct user_namespace *user_ns = current_user_ns();
220 :
221 297 : return ns_capable(user_ns, CAP_SETGID) &&
222 : userns_may_setgroups(user_ns);
223 : }
224 :
225 : /*
226 : * SMP: Our groups are copy-on-write. We can set them safely
227 : * without another task interfering.
228 : */
229 :
230 594 : SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
231 : {
232 : struct group_info *group_info;
233 : int retval;
234 :
235 297 : if (!may_setgroups())
236 : return -EPERM;
237 297 : if ((unsigned)gidsetsize > NGROUPS_MAX)
238 : return -EINVAL;
239 :
240 297 : group_info = groups_alloc(gidsetsize);
241 297 : if (!group_info)
242 : return -ENOMEM;
243 297 : retval = groups_from_user(group_info, grouplist);
244 297 : if (retval) {
245 0 : put_group_info(group_info);
246 : return retval;
247 : }
248 :
249 297 : retval = set_current_groups(group_info);
250 594 : put_group_info(group_info);
251 :
252 : return retval;
253 : }
254 :
255 : /*
256 : * Check whether we're fsgid/egid or in the supplemental group..
257 : */
258 98200 : int in_group_p(kgid_t grp)
259 : {
260 98200 : const struct cred *cred = current_cred();
261 : int retval = 1;
262 :
263 98200 : if (!gid_eq(grp, cred->fsgid))
264 94439 : retval = groups_search(cred->group_info, grp);
265 98200 : return retval;
266 : }
267 :
268 : EXPORT_SYMBOL(in_group_p);
269 :
270 6 : int in_egroup_p(kgid_t grp)
271 : {
272 6 : const struct cred *cred = current_cred();
273 : int retval = 1;
274 :
275 6 : if (!gid_eq(grp, cred->egid))
276 6 : retval = groups_search(cred->group_info, grp);
277 6 : return retval;
278 : }
279 :
280 : EXPORT_SYMBOL(in_egroup_p);
|