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/syscalls.h>
9 #include <linux/user_namespace.h>
10 #include <linux/vmalloc.h>
11 #include <linux/uaccess.h>
13 struct group_info
*groups_alloc(int gidsetsize
)
15 struct group_info
*gi
;
18 len
= sizeof(struct group_info
) + sizeof(kgid_t
) * gidsetsize
;
19 gi
= kmalloc(len
, GFP_KERNEL_ACCOUNT
|__GFP_NOWARN
|__GFP_NORETRY
);
21 gi
= __vmalloc(len
, GFP_KERNEL_ACCOUNT
, PAGE_KERNEL
);
25 atomic_set(&gi
->usage
, 1);
26 gi
->ngroups
= gidsetsize
;
30 EXPORT_SYMBOL(groups_alloc
);
32 void groups_free(struct group_info
*group_info
)
37 EXPORT_SYMBOL(groups_free
);
39 /* export the group_info to a user-space array */
40 static int groups_to_user(gid_t __user
*grouplist
,
41 const struct group_info
*group_info
)
43 struct user_namespace
*user_ns
= current_user_ns();
45 unsigned int count
= group_info
->ngroups
;
47 for (i
= 0; i
< count
; i
++) {
49 gid
= from_kgid_munged(user_ns
, group_info
->gid
[i
]);
50 if (put_user(gid
, grouplist
+i
))
56 /* fill a group_info from a user-space array - it must be allocated already */
57 static int groups_from_user(struct group_info
*group_info
,
58 gid_t __user
*grouplist
)
60 struct user_namespace
*user_ns
= current_user_ns();
62 unsigned int count
= group_info
->ngroups
;
64 for (i
= 0; i
< count
; i
++) {
67 if (get_user(gid
, grouplist
+i
))
70 kgid
= make_kgid(user_ns
, gid
);
74 group_info
->gid
[i
] = kgid
;
79 /* a simple Shell sort */
80 static void groups_sort(struct group_info
*group_info
)
82 int base
, max
, stride
;
83 int gidsetsize
= group_info
->ngroups
;
85 for (stride
= 1; stride
< gidsetsize
; stride
= 3 * stride
+ 1)
90 max
= gidsetsize
- stride
;
91 for (base
= 0; base
< max
; base
++) {
93 int right
= left
+ stride
;
94 kgid_t tmp
= group_info
->gid
[right
];
96 while (left
>= 0 && gid_gt(group_info
->gid
[left
], tmp
)) {
97 group_info
->gid
[right
] = group_info
->gid
[left
];
101 group_info
->gid
[right
] = tmp
;
107 /* a simple bsearch */
108 int groups_search(const struct group_info
*group_info
, kgid_t grp
)
110 unsigned int left
, right
;
116 right
= group_info
->ngroups
;
117 while (left
< right
) {
118 unsigned int mid
= (left
+right
)/2;
119 if (gid_gt(grp
, group_info
->gid
[mid
]))
121 else if (gid_lt(grp
, group_info
->gid
[mid
]))
130 * set_groups - Change a group subscription in a set of credentials
131 * @new: The newly prepared set of credentials to alter
132 * @group_info: The group list to install
134 void set_groups(struct cred
*new, struct group_info
*group_info
)
136 put_group_info(new->group_info
);
137 groups_sort(group_info
);
138 get_group_info(group_info
);
139 new->group_info
= group_info
;
142 EXPORT_SYMBOL(set_groups
);
145 * set_current_groups - Change current's group subscription
146 * @group_info: The group list to impose
148 * Validate a group subscription and, if valid, impose it upon current's task
151 int set_current_groups(struct group_info
*group_info
)
155 new = prepare_creds();
159 set_groups(new, group_info
);
160 return commit_creds(new);
163 EXPORT_SYMBOL(set_current_groups
);
165 SYSCALL_DEFINE2(getgroups
, int, gidsetsize
, gid_t __user
*, grouplist
)
167 const struct cred
*cred
= current_cred();
173 /* no need to grab task_lock here; it cannot change */
174 i
= cred
->group_info
->ngroups
;
176 if (i
> gidsetsize
) {
180 if (groups_to_user(grouplist
, cred
->group_info
)) {
189 bool may_setgroups(void)
191 struct user_namespace
*user_ns
= current_user_ns();
193 return ns_capable(user_ns
, CAP_SETGID
) &&
194 userns_may_setgroups(user_ns
);
198 * SMP: Our groups are copy-on-write. We can set them safely
199 * without another task interfering.
202 SYSCALL_DEFINE2(setgroups
, int, gidsetsize
, gid_t __user
*, grouplist
)
204 struct group_info
*group_info
;
207 if (!may_setgroups())
209 if ((unsigned)gidsetsize
> NGROUPS_MAX
)
212 group_info
= groups_alloc(gidsetsize
);
215 retval
= groups_from_user(group_info
, grouplist
);
217 put_group_info(group_info
);
221 retval
= set_current_groups(group_info
);
222 put_group_info(group_info
);
228 * Check whether we're fsgid/egid or in the supplemental group..
230 int in_group_p(kgid_t grp
)
232 const struct cred
*cred
= current_cred();
235 if (!gid_eq(grp
, cred
->fsgid
))
236 retval
= groups_search(cred
->group_info
, grp
);
240 EXPORT_SYMBOL(in_group_p
);
242 int in_egroup_p(kgid_t grp
)
244 const struct cred
*cred
= current_cred();
247 if (!gid_eq(grp
, cred
->egid
))
248 retval
= groups_search(cred
->group_info
, grp
);
252 EXPORT_SYMBOL(in_egroup_p
);