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