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