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 : 12 : struct group_info *groups_alloc(int gidsetsize)
16 : : {
17 : 12 : struct group_info *gi;
18 : 12 : unsigned int len;
19 : :
20 : 12 : len = sizeof(struct group_info) + sizeof(kgid_t) * gidsetsize;
21 [ - + ]: 12 : gi = kmalloc(len, GFP_KERNEL_ACCOUNT|__GFP_NOWARN|__GFP_NORETRY);
22 [ - + ]: 12 : if (!gi)
23 : 0 : gi = __vmalloc(len, GFP_KERNEL_ACCOUNT, PAGE_KERNEL);
24 [ + - ]: 12 : if (!gi)
25 : : return NULL;
26 : :
27 : 12 : atomic_set(&gi->usage, 1);
28 : 12 : gi->ngroups = gidsetsize;
29 : 12 : return gi;
30 : : }
31 : :
32 : : EXPORT_SYMBOL(groups_alloc);
33 : :
34 : 3 : void groups_free(struct group_info *group_info)
35 : : {
36 : 3 : kvfree(group_info);
37 : 0 : }
38 : :
39 : : EXPORT_SYMBOL(groups_free);
40 : :
41 : : /* export the group_info to a user-space array */
42 : 0 : static int groups_to_user(gid_t __user *grouplist,
43 : : const struct group_info *group_info)
44 : : {
45 : 0 : struct user_namespace *user_ns = current_user_ns();
46 : 0 : int i;
47 : 0 : unsigned int count = group_info->ngroups;
48 : :
49 [ # # ]: 0 : for (i = 0; i < count; i++) {
50 : 0 : gid_t gid;
51 [ # # ]: 0 : gid = from_kgid_munged(user_ns, group_info->gid[i]);
52 [ # # ]: 0 : 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 : 12 : static int groups_from_user(struct group_info *group_info,
60 : : gid_t __user *grouplist)
61 : : {
62 : 12 : struct user_namespace *user_ns = current_user_ns();
63 : 12 : int i;
64 : 12 : unsigned int count = group_info->ngroups;
65 : :
66 [ + + ]: 15 : for (i = 0; i < count; i++) {
67 : 3 : gid_t gid;
68 : 3 : kgid_t kgid;
69 [ + - ]: 3 : if (get_user(gid, grouplist+i))
70 : : return -EFAULT;
71 : :
72 [ + - ]: 3 : kgid = make_kgid(user_ns, gid);
73 [ + - ]: 3 : if (!gid_valid(kgid))
74 : : return -EINVAL;
75 : :
76 : 3 : group_info->gid[i] = kgid;
77 : : }
78 : : return 0;
79 : : }
80 : :
81 : 0 : static int gid_cmp(const void *_a, const void *_b)
82 : : {
83 : 0 : kgid_t a = *(kgid_t *)_a;
84 : 0 : kgid_t b = *(kgid_t *)_b;
85 : :
86 : 0 : return gid_gt(a, b) - gid_lt(a, b);
87 : : }
88 : :
89 : 12 : void groups_sort(struct group_info *group_info)
90 : : {
91 : 0 : 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 : 126394 : int groups_search(const struct group_info *group_info, kgid_t grp)
98 : : {
99 : 126394 : unsigned int left, right;
100 : :
101 [ # # ]: 0 : if (!group_info)
102 : : return 0;
103 : :
104 : 126394 : left = 0;
105 : 126394 : right = group_info->ngroups;
106 [ + + + + : 130288 : while (left < right) {
- - ]
107 : 3912 : unsigned int mid = (left+right)/2;
108 [ - + + + : 3912 : if (gid_gt(grp, group_info->gid[mid]))
- - ]
109 : 2460 : left = mid + 1;
110 [ + - + + : 1452 : 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 : 12 : void set_groups(struct cred *new, struct group_info *group_info)
124 : : {
125 [ - + ]: 12 : put_group_info(new->group_info);
126 : 12 : get_group_info(group_info);
127 : 12 : new->group_info = group_info;
128 : 12 : }
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 : 12 : int set_current_groups(struct group_info *group_info)
140 : : {
141 : 12 : struct cred *new;
142 : :
143 : 12 : new = prepare_creds();
144 [ + - ]: 12 : if (!new)
145 : : return -ENOMEM;
146 : :
147 : 12 : set_groups(new, group_info);
148 : 12 : return commit_creds(new);
149 : : }
150 : :
151 : : EXPORT_SYMBOL(set_current_groups);
152 : :
153 : 0 : SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
154 : : {
155 [ # # ]: 0 : const struct cred *cred = current_cred();
156 : 0 : int i;
157 : :
158 [ # # ]: 0 : if (gidsetsize < 0)
159 : : return -EINVAL;
160 : :
161 : : /* no need to grab task_lock here; it cannot change */
162 : 0 : i = cred->group_info->ngroups;
163 [ # # ]: 0 : if (gidsetsize) {
164 [ # # ]: 0 : if (i > gidsetsize) {
165 : 0 : i = -EINVAL;
166 : 0 : goto out;
167 : : }
168 [ # # ]: 0 : if (groups_to_user(grouplist, cred->group_info)) {
169 : 0 : i = -EFAULT;
170 : 0 : goto out;
171 : : }
172 : : }
173 : 0 : out:
174 : 0 : return i;
175 : : }
176 : :
177 : 12 : bool may_setgroups(void)
178 : : {
179 : 0 : struct user_namespace *user_ns = current_user_ns();
180 : :
181 [ - + - - ]: 12 : return ns_capable(user_ns, CAP_SETGID) &&
182 : : 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 : 24 : SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
191 : : {
192 : 12 : struct group_info *group_info;
193 : 12 : int retval;
194 : :
195 [ + - ]: 24 : if (!may_setgroups())
196 : : return -EPERM;
197 [ + - ]: 12 : if ((unsigned)gidsetsize > NGROUPS_MAX)
198 : : return -EINVAL;
199 : :
200 : 12 : group_info = groups_alloc(gidsetsize);
201 [ + - ]: 12 : if (!group_info)
202 : : return -ENOMEM;
203 : 12 : retval = groups_from_user(group_info, grouplist);
204 [ - + ]: 12 : if (retval) {
205 [ # # ]: 0 : put_group_info(group_info);
206 : 0 : return retval;
207 : : }
208 : :
209 : 12 : groups_sort(group_info);
210 : 12 : retval = set_current_groups(group_info);
211 [ - + ]: 12 : put_group_info(group_info);
212 : :
213 : 12 : return retval;
214 : : }
215 : :
216 : : /*
217 : : * Check whether we're fsgid/egid or in the supplemental group..
218 : : */
219 : 129448 : int in_group_p(kgid_t grp)
220 : : {
221 [ + + ]: 129448 : const struct cred *cred = current_cred();
222 : 129448 : int retval = 1;
223 : :
224 [ + + ]: 129448 : if (!gid_eq(grp, cred->fsgid))
225 [ + - ]: 126379 : retval = groups_search(cred->group_info, grp);
226 : 129448 : return retval;
227 : : }
228 : :
229 : : EXPORT_SYMBOL(in_group_p);
230 : :
231 : 15 : int in_egroup_p(kgid_t grp)
232 : : {
233 [ + - ]: 15 : const struct cred *cred = current_cred();
234 : 15 : int retval = 1;
235 : :
236 [ + - ]: 15 : if (!gid_eq(grp, cred->egid))
237 [ + - ]: 15 : retval = groups_search(cred->group_info, grp);
238 : 15 : return retval;
239 : : }
240 : :
241 : : EXPORT_SYMBOL(in_egroup_p);
|