blob: 434f6665f187d817cdfc00b928cfd84d33eae258 [file] [log] [blame]
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -07001/*
2 * Supplementary group IDs
3 */
4#include <linux/cred.h>
Paul Gortmaker9984de12011-05-23 14:51:41 -04005#include <linux/export.h>
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -07006#include <linux/slab.h>
7#include <linux/security.h>
Rasmus Villemoesb7b25622017-07-10 15:51:17 -07008#include <linux/sort.h>
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -07009#include <linux/syscalls.h>
Eric W. Biederman273d2c62014-12-05 18:01:11 -060010#include <linux/user_namespace.h>
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070011#include <linux/vmalloc.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080012#include <linux/uaccess.h>
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070013
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070014struct group_info *groups_alloc(int gidsetsize)
15{
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070016 struct group_info *gi;
17 unsigned int len;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070018
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070019 len = sizeof(struct group_info) + sizeof(kgid_t) * gidsetsize;
20 gi = kmalloc(len, GFP_KERNEL_ACCOUNT|__GFP_NOWARN|__GFP_NORETRY);
21 if (!gi)
Michal Hocko19809c22017-05-08 15:57:44 -070022 gi = __vmalloc(len, GFP_KERNEL_ACCOUNT, PAGE_KERNEL);
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070023 if (!gi)
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070024 return NULL;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070025
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070026 atomic_set(&gi->usage, 1);
27 gi->ngroups = gidsetsize;
28 return gi;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070029}
30
31EXPORT_SYMBOL(groups_alloc);
32
33void groups_free(struct group_info *group_info)
34{
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070035 kvfree(group_info);
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070036}
37
38EXPORT_SYMBOL(groups_free);
39
40/* export the group_info to a user-space array */
41static int groups_to_user(gid_t __user *grouplist,
42 const struct group_info *group_info)
43{
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080044 struct user_namespace *user_ns = current_user_ns();
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070045 int i;
46 unsigned int count = group_info->ngroups;
47
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080048 for (i = 0; i < count; i++) {
49 gid_t gid;
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070050 gid = from_kgid_munged(user_ns, group_info->gid[i]);
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080051 if (put_user(gid, grouplist+i))
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070052 return -EFAULT;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070053 }
54 return 0;
55}
56
57/* fill a group_info from a user-space array - it must be allocated already */
58static int groups_from_user(struct group_info *group_info,
59 gid_t __user *grouplist)
60{
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080061 struct user_namespace *user_ns = current_user_ns();
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070062 int i;
63 unsigned int count = group_info->ngroups;
64
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080065 for (i = 0; i < count; i++) {
66 gid_t gid;
67 kgid_t kgid;
68 if (get_user(gid, grouplist+i))
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070069 return -EFAULT;
70
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080071 kgid = make_kgid(user_ns, gid);
72 if (!gid_valid(kgid))
73 return -EINVAL;
74
Alexey Dobriyan81243ea2016-10-07 17:03:12 -070075 group_info->gid[i] = kgid;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070076 }
77 return 0;
78}
79
Rasmus Villemoesb7b25622017-07-10 15:51:17 -070080static int gid_cmp(const void *_a, const void *_b)
81{
82 kgid_t a = *(kgid_t *)_a;
83 kgid_t b = *(kgid_t *)_b;
84
85 return gid_gt(a, b) - gid_lt(a, b);
86}
87
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070088static void groups_sort(struct group_info *group_info)
89{
Rasmus Villemoesb7b25622017-07-10 15:51:17 -070090 sort(group_info->gid, group_info->ngroups, sizeof(*group_info->gid),
91 gid_cmp, NULL);
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070092}
93
94/* a simple bsearch */
Eric W. Biedermanae2975b2011-11-14 15:56:38 -080095int groups_search(const struct group_info *group_info, kgid_t grp)
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -070096{
97 unsigned int left, right;
98
99 if (!group_info)
100 return 0;
101
102 left = 0;
103 right = group_info->ngroups;
104 while (left < right) {
105 unsigned int mid = (left+right)/2;
Alexey Dobriyan81243ea2016-10-07 17:03:12 -0700106 if (gid_gt(grp, group_info->gid[mid]))
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700107 left = mid + 1;
Alexey Dobriyan81243ea2016-10-07 17:03:12 -0700108 else if (gid_lt(grp, group_info->gid[mid]))
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700109 right = mid;
110 else
111 return 1;
112 }
113 return 0;
114}
115
116/**
117 * set_groups - Change a group subscription in a set of credentials
118 * @new: The newly prepared set of credentials to alter
119 * @group_info: The group list to install
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700120 */
Wang YanQing8f6c5ff2014-04-03 14:48:26 -0700121void set_groups(struct cred *new, struct group_info *group_info)
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700122{
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700123 put_group_info(new->group_info);
124 groups_sort(group_info);
125 get_group_info(group_info);
126 new->group_info = group_info;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700127}
128
129EXPORT_SYMBOL(set_groups);
130
131/**
132 * set_current_groups - Change current's group subscription
133 * @group_info: The group list to impose
134 *
135 * Validate a group subscription and, if valid, impose it upon current's task
136 * security record.
137 */
138int set_current_groups(struct group_info *group_info)
139{
140 struct cred *new;
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700141
142 new = prepare_creds();
143 if (!new)
144 return -ENOMEM;
145
Wang YanQing8f6c5ff2014-04-03 14:48:26 -0700146 set_groups(new, group_info);
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700147 return commit_creds(new);
148}
149
150EXPORT_SYMBOL(set_current_groups);
151
152SYSCALL_DEFINE2(getgroups, int, gidsetsize, gid_t __user *, grouplist)
153{
154 const struct cred *cred = current_cred();
155 int i;
156
157 if (gidsetsize < 0)
158 return -EINVAL;
159
160 /* no need to grab task_lock here; it cannot change */
161 i = cred->group_info->ngroups;
162 if (gidsetsize) {
163 if (i > gidsetsize) {
164 i = -EINVAL;
165 goto out;
166 }
167 if (groups_to_user(grouplist, cred->group_info)) {
168 i = -EFAULT;
169 goto out;
170 }
171 }
172out:
173 return i;
174}
175
Eric W. Biederman7ff4d902014-12-05 17:19:27 -0600176bool may_setgroups(void)
177{
178 struct user_namespace *user_ns = current_user_ns();
179
Eric W. Biederman273d2c62014-12-05 18:01:11 -0600180 return ns_capable(user_ns, CAP_SETGID) &&
181 userns_may_setgroups(user_ns);
Eric W. Biederman7ff4d902014-12-05 17:19:27 -0600182}
183
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700184/*
185 * SMP: Our groups are copy-on-write. We can set them safely
186 * without another task interfering.
187 */
188
189SYSCALL_DEFINE2(setgroups, int, gidsetsize, gid_t __user *, grouplist)
190{
191 struct group_info *group_info;
192 int retval;
193
Eric W. Biederman7ff4d902014-12-05 17:19:27 -0600194 if (!may_setgroups())
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700195 return -EPERM;
196 if ((unsigned)gidsetsize > NGROUPS_MAX)
197 return -EINVAL;
198
199 group_info = groups_alloc(gidsetsize);
200 if (!group_info)
201 return -ENOMEM;
202 retval = groups_from_user(group_info, grouplist);
203 if (retval) {
204 put_group_info(group_info);
205 return retval;
206 }
207
208 retval = set_current_groups(group_info);
209 put_group_info(group_info);
210
211 return retval;
212}
213
214/*
215 * Check whether we're fsgid/egid or in the supplemental group..
216 */
Eric W. Biederman72cda3d2012-02-09 09:09:39 -0800217int in_group_p(kgid_t grp)
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700218{
219 const struct cred *cred = current_cred();
220 int retval = 1;
221
Eric W. Biederman72cda3d2012-02-09 09:09:39 -0800222 if (!gid_eq(grp, cred->fsgid))
223 retval = groups_search(cred->group_info, grp);
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700224 return retval;
225}
226
227EXPORT_SYMBOL(in_group_p);
228
Eric W. Biederman72cda3d2012-02-09 09:09:39 -0800229int in_egroup_p(kgid_t grp)
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700230{
231 const struct cred *cred = current_cred();
232 int retval = 1;
233
Eric W. Biederman72cda3d2012-02-09 09:09:39 -0800234 if (!gid_eq(grp, cred->egid))
235 retval = groups_search(cred->group_info, grp);
Alexey Dobriyan30639b6a2009-06-16 15:33:40 -0700236 return retval;
237}
238
239EXPORT_SYMBOL(in_egroup_p);