2 * Supplementary group IDs
4 #include <linux/cred.h>
5 #include <linux/export.h>
6 #include <linux/slab.h>
7 #include <linux/security.h>
8 #include <linux/sort.h>
9 #include <linux/syscalls.h>
10 #include <linux/user_namespace.h>
11 #include <linux/vmalloc.h>
12 #include <linux/uaccess.h>
14 struct group_info
*groups_alloc(int gidsetsize
)
16 struct group_info
*gi
;
19 len
= sizeof(struct group_info
) + sizeof(kgid_t
) * gidsetsize
;
20 gi
= kmalloc(len
, GFP_KERNEL_ACCOUNT
|__GFP_NOWARN
|__GFP_NORETRY
);
22 gi
= __vmalloc(len
, GFP_KERNEL_ACCOUNT
, PAGE_KERNEL
);
26 atomic_set(&gi
->usage
, 1);
27 gi
->ngroups
= gidsetsize
;
31 EXPORT_SYMBOL(groups_alloc
);
33 void groups_free(struct group_info
*group_info
)
38 EXPORT_SYMBOL(groups_free
);
40 /* export the group_info to a user-space array */
41 static int groups_to_user(gid_t __user
*grouplist
,
42 const struct group_info
*group_info
)
44 struct user_namespace
*user_ns
= current_user_ns();
46 unsigned int count
= group_info
->ngroups
;
48 for (i
= 0; i
< count
; i
++) {
50 gid
= from_kgid_munged(user_ns
, group_info
->gid
[i
]);
51 if (put_user(gid
, grouplist
+i
))
57 /* fill a group_info from a user-space array - it must be allocated already */
58 static int groups_from_user(struct group_info
*group_info
,
59 gid_t __user
*grouplist
)
61 struct user_namespace
*user_ns
= current_user_ns();
63 unsigned int count
= group_info
->ngroups
;
65 for (i
= 0; i
< count
; i
++) {
68 if (get_user(gid
, grouplist
+i
))
71 kgid
= make_kgid(user_ns
, gid
);
75 group_info
->gid
[i
] = kgid
;
80 static int gid_cmp(const void *_a
, const void *_b
)
82 kgid_t a
= *(kgid_t
*)_a
;
83 kgid_t b
= *(kgid_t
*)_b
;
85 return gid_gt(a
, b
) - gid_lt(a
, b
);
88 static void groups_sort(struct group_info
*group_info
)
90 sort(group_info
->gid
, group_info
->ngroups
, sizeof(*group_info
->gid
),
94 /* a simple bsearch */
95 int groups_search(const struct group_info
*group_info
, kgid_t grp
)
97 unsigned int left
, right
;
103 right
= group_info
->ngroups
;
104 while (left
< right
) {
105 unsigned int mid
= (left
+right
)/2;
106 if (gid_gt(grp
, group_info
->gid
[mid
]))
108 else if (gid_lt(grp
, group_info
->gid
[mid
]))
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
121 void set_groups(struct cred
*new, struct group_info
*group_info
)
123 put_group_info(new->group_info
);
124 groups_sort(group_info
);
125 get_group_info(group_info
);
126 new->group_info
= group_info
;
129 EXPORT_SYMBOL(set_groups
);
132 * set_current_groups - Change current's group subscription
133 * @group_info: The group list to impose
135 * Validate a group subscription and, if valid, impose it upon current's task
138 int set_current_groups(struct group_info
*group_info
)
142 new = prepare_creds();
146 set_groups(new, group_info
);
147 return commit_creds(new);
150 EXPORT_SYMBOL(set_current_groups
);
152 SYSCALL_DEFINE2(getgroups
, int, gidsetsize
, gid_t __user
*, grouplist
)
154 const struct cred
*cred
= current_cred();
160 /* no need to grab task_lock here; it cannot change */
161 i
= cred
->group_info
->ngroups
;
163 if (i
> gidsetsize
) {
167 if (groups_to_user(grouplist
, cred
->group_info
)) {
176 bool may_setgroups(void)
178 struct user_namespace
*user_ns
= current_user_ns();
180 return ns_capable(user_ns
, CAP_SETGID
) &&
181 userns_may_setgroups(user_ns
);
185 * SMP: Our groups are copy-on-write. We can set them safely
186 * without another task interfering.
189 SYSCALL_DEFINE2(setgroups
, int, gidsetsize
, gid_t __user
*, grouplist
)
191 struct group_info
*group_info
;
194 if (!may_setgroups())
196 if ((unsigned)gidsetsize
> NGROUPS_MAX
)
199 group_info
= groups_alloc(gidsetsize
);
202 retval
= groups_from_user(group_info
, grouplist
);
204 put_group_info(group_info
);
208 retval
= set_current_groups(group_info
);
209 put_group_info(group_info
);
215 * Check whether we're fsgid/egid or in the supplemental group..
217 int in_group_p(kgid_t grp
)
219 const struct cred
*cred
= current_cred();
222 if (!gid_eq(grp
, cred
->fsgid
))
223 retval
= groups_search(cred
->group_info
, grp
);
227 EXPORT_SYMBOL(in_group_p
);
229 int in_egroup_p(kgid_t grp
)
231 const struct cred
*cred
= current_cred();
234 if (!gid_eq(grp
, cred
->egid
))
235 retval
= groups_search(cred
->group_info
, grp
);
239 EXPORT_SYMBOL(in_egroup_p
);