xref: /haiku/src/system/kernel/usergroup.cpp (revision 9d18e521912c94c839a2c0bec57bca5c681dd030)
14eb35609SIngo Weinhold /*
24535495dSIngo Weinhold  * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
34eb35609SIngo Weinhold  * Distributed under the terms of the MIT License.
44eb35609SIngo Weinhold  */
54eb35609SIngo Weinhold 
624df6592SIngo Weinhold 
74eb35609SIngo Weinhold #include <usergroup.h>
84eb35609SIngo Weinhold 
94eb35609SIngo Weinhold #include <errno.h>
10290946ceSIngo Weinhold #include <limits.h>
114eb35609SIngo Weinhold #include <sys/stat.h>
124eb35609SIngo Weinhold 
13290946ceSIngo Weinhold #include <new>
144eb35609SIngo Weinhold 
15290946ceSIngo Weinhold #include <heap.h>
164eb35609SIngo Weinhold #include <kernel.h>
174eb35609SIngo Weinhold #include <syscalls.h>
184eb35609SIngo Weinhold #include <team.h>
194eb35609SIngo Weinhold #include <thread.h>
204eb35609SIngo Weinhold #include <thread_types.h>
214eb35609SIngo Weinhold #include <util/AutoLock.h>
22057fe191SAugustin Cavalier #include <util/ThreadAutoLock.h>
23290946ceSIngo Weinhold #include <vfs.h>
244eb35609SIngo Weinhold 
254eb35609SIngo Weinhold #include <AutoDeleter.h>
264eb35609SIngo Weinhold 
274eb35609SIngo Weinhold 
284eb35609SIngo Weinhold // #pragma mark - Implementation Private
294eb35609SIngo Weinhold 
304eb35609SIngo Weinhold 
314eb35609SIngo Weinhold static bool
is_privileged(Team * team)324535495dSIngo Weinhold is_privileged(Team* team)
334eb35609SIngo Weinhold {
344eb35609SIngo Weinhold 	// currently only the root user is privileged
354eb35609SIngo Weinhold 	return team->effective_uid == 0;
364eb35609SIngo Weinhold }
374eb35609SIngo Weinhold 
384eb35609SIngo Weinhold 
394eb35609SIngo Weinhold static status_t
common_setregid(gid_t rgid,gid_t egid,bool setAllIfPrivileged,bool kernel)404eb35609SIngo Weinhold common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel)
414eb35609SIngo Weinhold {
424535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
434eb35609SIngo Weinhold 
4424df6592SIngo Weinhold 	TeamLocker teamLocker(team);
454eb35609SIngo Weinhold 
464eb35609SIngo Weinhold 	bool privileged = kernel || is_privileged(team);
474eb35609SIngo Weinhold 
485b53519cSIngo Weinhold 	gid_t ssgid = team->saved_set_gid;
495b53519cSIngo Weinhold 
504eb35609SIngo Weinhold 	// real gid
514eb35609SIngo Weinhold 	if (rgid == (gid_t)-1) {
524eb35609SIngo Weinhold 		rgid = team->real_gid;
534eb35609SIngo Weinhold 	} else {
544eb35609SIngo Weinhold 		if (setAllIfPrivileged) {
554eb35609SIngo Weinhold 			// setgid() semantics: If privileged set both, real, effective and
564eb35609SIngo Weinhold 			// saved set-gid, otherwise set the effective gid.
574eb35609SIngo Weinhold 			if (privileged) {
584eb35609SIngo Weinhold 				team->saved_set_gid = rgid;
594eb35609SIngo Weinhold 				team->real_gid = rgid;
604eb35609SIngo Weinhold 				team->effective_gid = rgid;
614eb35609SIngo Weinhold 				return B_OK;
624eb35609SIngo Weinhold 			}
634eb35609SIngo Weinhold 
644eb35609SIngo Weinhold 			// not privileged -- set only the effective gid
654eb35609SIngo Weinhold 			egid = rgid;
664eb35609SIngo Weinhold 			rgid = team->real_gid;
674eb35609SIngo Weinhold 		} else {
684eb35609SIngo Weinhold 			// setregid() semantics: set the real gid, if allowed to
695b53519cSIngo Weinhold 			// Note: We allow setting the real gid to the effective gid. This
705b53519cSIngo Weinhold 			// is unspecified by the specs, but is common practice.
715b53519cSIngo Weinhold 			if (!privileged && rgid != team->real_gid
725b53519cSIngo Weinhold 				&& rgid != team->effective_gid) {
734eb35609SIngo Weinhold 				return EPERM;
744eb35609SIngo Weinhold 			}
755b53519cSIngo Weinhold 
765b53519cSIngo Weinhold 			// Note: Also common practice is to set the saved set-gid when the
775b53519cSIngo Weinhold 			// real gid is set.
785b53519cSIngo Weinhold 			if (rgid != team->real_gid)
795b53519cSIngo Weinhold 				ssgid = rgid;
805b53519cSIngo Weinhold 		}
814eb35609SIngo Weinhold 	}
824eb35609SIngo Weinhold 
834eb35609SIngo Weinhold 	// effective gid
844eb35609SIngo Weinhold 	if (egid == (gid_t)-1) {
854eb35609SIngo Weinhold 		egid = team->effective_gid;
864eb35609SIngo Weinhold 	} else {
874eb35609SIngo Weinhold 		if (!privileged && egid != team->effective_gid
884eb35609SIngo Weinhold 			&& egid != team->real_gid && egid != team->saved_set_gid) {
894eb35609SIngo Weinhold 			return EPERM;
904eb35609SIngo Weinhold 		}
914eb35609SIngo Weinhold 	}
924eb35609SIngo Weinhold 
934eb35609SIngo Weinhold 	// Getting here means all checks were successful -- set the gids.
944eb35609SIngo Weinhold 	team->real_gid = rgid;
954eb35609SIngo Weinhold 	team->effective_gid = egid;
965b53519cSIngo Weinhold 	team->saved_set_gid = ssgid;
974eb35609SIngo Weinhold 
984eb35609SIngo Weinhold 	return B_OK;
994eb35609SIngo Weinhold }
1004eb35609SIngo Weinhold 
1014eb35609SIngo Weinhold 
1024eb35609SIngo Weinhold static status_t
common_setreuid(uid_t ruid,uid_t euid,bool setAllIfPrivileged,bool kernel)1034eb35609SIngo Weinhold common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel)
1044eb35609SIngo Weinhold {
1054535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
1064eb35609SIngo Weinhold 
10724df6592SIngo Weinhold 	TeamLocker teamLocker(team);
1084eb35609SIngo Weinhold 
1094eb35609SIngo Weinhold 	bool privileged = kernel || is_privileged(team);
1104eb35609SIngo Weinhold 
1115b53519cSIngo Weinhold 	uid_t ssuid = team->saved_set_uid;
1125b53519cSIngo Weinhold 
1134eb35609SIngo Weinhold 	// real uid
1144eb35609SIngo Weinhold 	if (ruid == (uid_t)-1) {
1154eb35609SIngo Weinhold 		ruid = team->real_uid;
1164eb35609SIngo Weinhold 	} else {
1174eb35609SIngo Weinhold 		if (setAllIfPrivileged) {
1184eb35609SIngo Weinhold 			// setuid() semantics: If privileged set both, real, effective and
1194eb35609SIngo Weinhold 			// saved set-uid, otherwise set the effective uid.
1204eb35609SIngo Weinhold 			if (privileged) {
1214eb35609SIngo Weinhold 				team->saved_set_uid = ruid;
1224eb35609SIngo Weinhold 				team->real_uid = ruid;
1234eb35609SIngo Weinhold 				team->effective_uid = ruid;
1244eb35609SIngo Weinhold 				return B_OK;
1254eb35609SIngo Weinhold 			}
1264eb35609SIngo Weinhold 
1274eb35609SIngo Weinhold 			// not privileged -- set only the effective uid
1284eb35609SIngo Weinhold 			euid = ruid;
1294eb35609SIngo Weinhold 			ruid = team->real_uid;
1304eb35609SIngo Weinhold 		} else {
1314eb35609SIngo Weinhold 			// setreuid() semantics: set the real uid, if allowed to
1324eb35609SIngo Weinhold 			// Note: We allow setting the real uid to the effective uid. This
1334eb35609SIngo Weinhold 			// is unspecified by the specs, but is common practice.
1344eb35609SIngo Weinhold 			if (!privileged && ruid != team->real_uid
1354eb35609SIngo Weinhold 				&& ruid != team->effective_uid) {
1364eb35609SIngo Weinhold 				return EPERM;
1374eb35609SIngo Weinhold 			}
1385b53519cSIngo Weinhold 
1395b53519cSIngo Weinhold 			// Note: Also common practice is to set the saved set-uid when the
1405b53519cSIngo Weinhold 			// real uid is set.
1415b53519cSIngo Weinhold 			if (ruid != team->real_uid)
1425b53519cSIngo Weinhold 				ssuid = ruid;
1434eb35609SIngo Weinhold 		}
1444eb35609SIngo Weinhold 	}
1454eb35609SIngo Weinhold 
1464eb35609SIngo Weinhold 	// effective uid
1474eb35609SIngo Weinhold 	if (euid == (uid_t)-1) {
1484eb35609SIngo Weinhold 		euid = team->effective_uid;
1494eb35609SIngo Weinhold 	} else {
1504eb35609SIngo Weinhold 		if (!privileged && euid != team->effective_uid
1514eb35609SIngo Weinhold 			&& euid != team->real_uid && euid != team->saved_set_uid) {
1524eb35609SIngo Weinhold 			return EPERM;
1534eb35609SIngo Weinhold 		}
1544eb35609SIngo Weinhold 	}
1554eb35609SIngo Weinhold 
1564eb35609SIngo Weinhold 	// Getting here means all checks were successful -- set the uids.
1574eb35609SIngo Weinhold 	team->real_uid = ruid;
1584eb35609SIngo Weinhold 	team->effective_uid = euid;
1595b53519cSIngo Weinhold 	team->saved_set_uid = ssuid;
1604eb35609SIngo Weinhold 
1614eb35609SIngo Weinhold 	return B_OK;
1624eb35609SIngo Weinhold }
1634eb35609SIngo Weinhold 
1644eb35609SIngo Weinhold 
165290946ceSIngo Weinhold ssize_t
common_getgroups(int groupCount,gid_t * groupList,bool kernel)166290946ceSIngo Weinhold common_getgroups(int groupCount, gid_t* groupList, bool kernel)
167290946ceSIngo Weinhold {
1684535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
169290946ceSIngo Weinhold 
17024df6592SIngo Weinhold 	TeamLocker teamLocker(team);
171290946ceSIngo Weinhold 
172e372ec1eSAugustin Cavalier 	const gid_t* groups = NULL;
173e372ec1eSAugustin Cavalier 	int actualCount = 0;
174e372ec1eSAugustin Cavalier 
175e372ec1eSAugustin Cavalier 	if (team->supplementary_groups != NULL) {
176e372ec1eSAugustin Cavalier 		groups = team->supplementary_groups->groups;
177e372ec1eSAugustin Cavalier 		actualCount = team->supplementary_groups->count;
178e372ec1eSAugustin Cavalier 	}
179290946ceSIngo Weinhold 
180290946ceSIngo Weinhold 	// follow the specification and return always at least one group
181290946ceSIngo Weinhold 	if (actualCount == 0) {
182290946ceSIngo Weinhold 		groups = &team->effective_gid;
183290946ceSIngo Weinhold 		actualCount = 1;
184290946ceSIngo Weinhold 	}
185290946ceSIngo Weinhold 
1869fe0705bSIngo Weinhold 	// if groupCount 0 is supplied, we only return the number of groups
1879fe0705bSIngo Weinhold 	if (groupCount == 0)
1889fe0705bSIngo Weinhold 		return actualCount;
1899fe0705bSIngo Weinhold 
190290946ceSIngo Weinhold 	// check for sufficient space
191290946ceSIngo Weinhold 	if (groupCount < actualCount)
192290946ceSIngo Weinhold 		return B_BAD_VALUE;
193290946ceSIngo Weinhold 
194290946ceSIngo Weinhold 	// copy
195290946ceSIngo Weinhold 	if (kernel) {
196021ccbd1SPawel Dziepak 		memcpy(groupList, groups, actualCount * sizeof(gid_t));
197290946ceSIngo Weinhold 	} else {
198290946ceSIngo Weinhold 		if (!IS_USER_ADDRESS(groupList)
199290946ceSIngo Weinhold 			|| user_memcpy(groupList, groups,
200290946ceSIngo Weinhold 					actualCount * sizeof(gid_t)) != B_OK) {
201290946ceSIngo Weinhold 			return B_BAD_ADDRESS;
202290946ceSIngo Weinhold 		}
203290946ceSIngo Weinhold 	}
204290946ceSIngo Weinhold 
205290946ceSIngo Weinhold 	return actualCount;
206290946ceSIngo Weinhold }
207290946ceSIngo Weinhold 
208290946ceSIngo Weinhold 
209290946ceSIngo Weinhold static status_t
common_setgroups(int groupCount,const gid_t * groupList,bool kernel)210290946ceSIngo Weinhold common_setgroups(int groupCount, const gid_t* groupList, bool kernel)
211290946ceSIngo Weinhold {
212290946ceSIngo Weinhold 	if (groupCount < 0 || groupCount > NGROUPS_MAX)
213290946ceSIngo Weinhold 		return B_BAD_VALUE;
214290946ceSIngo Weinhold 
215e372ec1eSAugustin Cavalier 	BKernel::GroupsArray* newGroups = NULL;
216290946ceSIngo Weinhold 	if (groupCount > 0) {
217e372ec1eSAugustin Cavalier 		newGroups = (BKernel::GroupsArray*)malloc(sizeof(BKernel::GroupsArray)
218e372ec1eSAugustin Cavalier 			+ (sizeof(gid_t) * groupCount));
219290946ceSIngo Weinhold 		if (newGroups == NULL)
220290946ceSIngo Weinhold 			return B_NO_MEMORY;
221e372ec1eSAugustin Cavalier 		new(newGroups) BKernel::GroupsArray;
222290946ceSIngo Weinhold 
223290946ceSIngo Weinhold 		if (kernel) {
224e372ec1eSAugustin Cavalier 			memcpy(newGroups->groups, groupList, sizeof(gid_t) * groupCount);
225290946ceSIngo Weinhold 		} else {
226290946ceSIngo Weinhold 			if (!IS_USER_ADDRESS(groupList)
227*9d18e521SJérôme Duval 				|| user_memcpy(newGroups->groups, groupList, sizeof(gid_t) * groupCount) != B_OK) {
228*9d18e521SJérôme Duval 				free(newGroups);
229290946ceSIngo Weinhold 				return B_BAD_ADDRESS;
230290946ceSIngo Weinhold 			}
231290946ceSIngo Weinhold 		}
232e372ec1eSAugustin Cavalier 		newGroups->count = groupCount;
233290946ceSIngo Weinhold 	}
234290946ceSIngo Weinhold 
2354535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
23624df6592SIngo Weinhold 	TeamLocker teamLocker(team);
23724df6592SIngo Weinhold 
238e372ec1eSAugustin Cavalier 	BReference<BKernel::GroupsArray> previous = team->supplementary_groups;
239e372ec1eSAugustin Cavalier 		// so it will not be (potentially) destroyed until after we unlock
240e372ec1eSAugustin Cavalier 	team->supplementary_groups.SetTo(newGroups, true);
241290946ceSIngo Weinhold 
24224df6592SIngo Weinhold 	teamLocker.Unlock();
243290946ceSIngo Weinhold 
244290946ceSIngo Weinhold 	return B_OK;
245290946ceSIngo Weinhold }
246290946ceSIngo Weinhold 
247290946ceSIngo Weinhold 
2484eb35609SIngo Weinhold // #pragma mark - Kernel Private
2494eb35609SIngo Weinhold 
2504eb35609SIngo Weinhold 
25124df6592SIngo Weinhold /*!	Copies the user and group information from \a parent to \a team.
25224df6592SIngo Weinhold 	The caller must hold the lock to both \a team and \a parent.
25324df6592SIngo Weinhold */
2544eb35609SIngo Weinhold void
inherit_parent_user_and_group(Team * team,Team * parent)25524df6592SIngo Weinhold inherit_parent_user_and_group(Team* team, Team* parent)
2564eb35609SIngo Weinhold {
2574eb35609SIngo Weinhold 	team->saved_set_uid = parent->saved_set_uid;
2584eb35609SIngo Weinhold 	team->real_uid = parent->real_uid;
2594eb35609SIngo Weinhold 	team->effective_uid = parent->effective_uid;
2604eb35609SIngo Weinhold 	team->saved_set_gid = parent->saved_set_gid;
2614eb35609SIngo Weinhold 	team->real_gid = parent->real_gid;
2624eb35609SIngo Weinhold 	team->effective_gid = parent->effective_gid;
263290946ceSIngo Weinhold 	team->supplementary_groups = parent->supplementary_groups;
2644eb35609SIngo Weinhold }
2654eb35609SIngo Weinhold 
2664eb35609SIngo Weinhold 
2674eb35609SIngo Weinhold status_t
update_set_id_user_and_group(Team * team,const char * file)2684535495dSIngo Weinhold update_set_id_user_and_group(Team* team, const char* file)
2694eb35609SIngo Weinhold {
2704eb35609SIngo Weinhold 	struct stat st;
271290946ceSIngo Weinhold 	status_t status = vfs_read_stat(-1, file, true, &st, false);
272290946ceSIngo Weinhold 	if (status != B_OK)
273290946ceSIngo Weinhold 		return status;
2744eb35609SIngo Weinhold 
27524df6592SIngo Weinhold 	TeamLocker teamLocker(team);
2764eb35609SIngo Weinhold 
2774eb35609SIngo Weinhold 	if ((st.st_mode & S_ISUID) != 0) {
2784eb35609SIngo Weinhold 		team->saved_set_uid = st.st_uid;
2794eb35609SIngo Weinhold 		team->effective_uid = st.st_uid;
2804eb35609SIngo Weinhold 	}
2814eb35609SIngo Weinhold 
2824eb35609SIngo Weinhold 	if ((st.st_mode & S_ISGID) != 0) {
2834eb35609SIngo Weinhold 		team->saved_set_gid = st.st_gid;
2844eb35609SIngo Weinhold 		team->effective_gid = st.st_gid;
2854eb35609SIngo Weinhold 	}
2864eb35609SIngo Weinhold 
2874eb35609SIngo Weinhold 	return B_OK;
2884eb35609SIngo Weinhold }
2894eb35609SIngo Weinhold 
2904eb35609SIngo Weinhold 
2914eb35609SIngo Weinhold gid_t
_kern_getgid(bool effective)2924eb35609SIngo Weinhold _kern_getgid(bool effective)
2934eb35609SIngo Weinhold {
2944535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
2954eb35609SIngo Weinhold 
2964eb35609SIngo Weinhold 	return effective ? team->effective_gid : team->real_gid;
2974eb35609SIngo Weinhold }
2984eb35609SIngo Weinhold 
2994eb35609SIngo Weinhold 
3004eb35609SIngo Weinhold uid_t
_kern_getuid(bool effective)3014eb35609SIngo Weinhold _kern_getuid(bool effective)
3024eb35609SIngo Weinhold {
3034535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3044eb35609SIngo Weinhold 
3054eb35609SIngo Weinhold 	return effective ? team->effective_uid : team->real_uid;
3064eb35609SIngo Weinhold }
3074eb35609SIngo Weinhold 
3084eb35609SIngo Weinhold 
3094eb35609SIngo Weinhold status_t
_kern_setregid(gid_t rgid,gid_t egid,bool setAllIfPrivileged)3104eb35609SIngo Weinhold _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
3114eb35609SIngo Weinhold {
3124eb35609SIngo Weinhold 	return common_setregid(rgid, egid, setAllIfPrivileged, true);
3134eb35609SIngo Weinhold }
3144eb35609SIngo Weinhold 
3154eb35609SIngo Weinhold 
3164eb35609SIngo Weinhold status_t
_kern_setreuid(uid_t ruid,uid_t euid,bool setAllIfPrivileged)3174eb35609SIngo Weinhold _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
3184eb35609SIngo Weinhold {
3194eb35609SIngo Weinhold 	return common_setreuid(ruid, euid, setAllIfPrivileged, true);
3204eb35609SIngo Weinhold }
3214eb35609SIngo Weinhold 
3224eb35609SIngo Weinhold 
323290946ceSIngo Weinhold ssize_t
_kern_getgroups(int groupCount,gid_t * groupList)324290946ceSIngo Weinhold _kern_getgroups(int groupCount, gid_t* groupList)
325290946ceSIngo Weinhold {
326290946ceSIngo Weinhold 	return common_getgroups(groupCount, groupList, true);
327290946ceSIngo Weinhold }
328290946ceSIngo Weinhold 
329290946ceSIngo Weinhold 
330290946ceSIngo Weinhold status_t
_kern_setgroups(int groupCount,const gid_t * groupList)331290946ceSIngo Weinhold _kern_setgroups(int groupCount, const gid_t* groupList)
332290946ceSIngo Weinhold {
333290946ceSIngo Weinhold 	return common_setgroups(groupCount, groupList, true);
334290946ceSIngo Weinhold }
335290946ceSIngo Weinhold 
336290946ceSIngo Weinhold 
3374eb35609SIngo Weinhold // #pragma mark - Syscalls
3384eb35609SIngo Weinhold 
3394eb35609SIngo Weinhold 
3404eb35609SIngo Weinhold gid_t
_user_getgid(bool effective)3414eb35609SIngo Weinhold _user_getgid(bool effective)
3424eb35609SIngo Weinhold {
3434535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3444eb35609SIngo Weinhold 
3454eb35609SIngo Weinhold 	return effective ? team->effective_gid : team->real_gid;
3464eb35609SIngo Weinhold }
3474eb35609SIngo Weinhold 
3484eb35609SIngo Weinhold 
3494eb35609SIngo Weinhold uid_t
_user_getuid(bool effective)3504eb35609SIngo Weinhold _user_getuid(bool effective)
3514eb35609SIngo Weinhold {
3524535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3534eb35609SIngo Weinhold 
3544eb35609SIngo Weinhold 	return effective ? team->effective_uid : team->real_uid;
3554eb35609SIngo Weinhold }
3564eb35609SIngo Weinhold 
3574eb35609SIngo Weinhold 
3584eb35609SIngo Weinhold status_t
_user_setregid(gid_t rgid,gid_t egid,bool setAllIfPrivileged)3594eb35609SIngo Weinhold _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
3604eb35609SIngo Weinhold {
3614eb35609SIngo Weinhold 	return common_setregid(rgid, egid, setAllIfPrivileged, false);
3624eb35609SIngo Weinhold }
3634eb35609SIngo Weinhold 
3644eb35609SIngo Weinhold 
3654eb35609SIngo Weinhold status_t
_user_setreuid(uid_t ruid,uid_t euid,bool setAllIfPrivileged)3664eb35609SIngo Weinhold _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
3674eb35609SIngo Weinhold {
3684eb35609SIngo Weinhold 	return common_setreuid(ruid, euid, setAllIfPrivileged, false);
3694eb35609SIngo Weinhold }
370290946ceSIngo Weinhold 
371290946ceSIngo Weinhold 
372290946ceSIngo Weinhold ssize_t
_user_getgroups(int groupCount,gid_t * groupList)373290946ceSIngo Weinhold _user_getgroups(int groupCount, gid_t* groupList)
374290946ceSIngo Weinhold {
375290946ceSIngo Weinhold 	return common_getgroups(groupCount, groupList, false);
376290946ceSIngo Weinhold }
377290946ceSIngo Weinhold 
378290946ceSIngo Weinhold 
379290946ceSIngo Weinhold ssize_t
_user_setgroups(int groupCount,const gid_t * groupList)380290946ceSIngo Weinhold _user_setgroups(int groupCount, const gid_t* groupList)
381290946ceSIngo Weinhold {
38224df6592SIngo Weinhold 	// check privilege
38324df6592SIngo Weinhold 	Team* team = thread_get_current_thread()->team;
38424df6592SIngo Weinhold 	if (!is_privileged(team))
385290946ceSIngo Weinhold 		return EPERM;
386290946ceSIngo Weinhold 
387290946ceSIngo Weinhold 	return common_setgroups(groupCount, groupList, false);
388290946ceSIngo Weinhold }
389