xref: /haiku/src/system/kernel/usergroup.cpp (revision 021ccbd18f13a354359087e5b797cac792471679)
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>
22290946ceSIngo Weinhold #include <vfs.h>
234eb35609SIngo Weinhold 
244eb35609SIngo Weinhold #include <AutoDeleter.h>
254eb35609SIngo Weinhold 
264eb35609SIngo Weinhold 
274eb35609SIngo Weinhold // #pragma mark - Implementation Private
284eb35609SIngo Weinhold 
294eb35609SIngo Weinhold 
304eb35609SIngo Weinhold static bool
314535495dSIngo Weinhold is_privileged(Team* team)
324eb35609SIngo Weinhold {
334eb35609SIngo Weinhold 	// currently only the root user is privileged
344eb35609SIngo Weinhold 	return team->effective_uid == 0;
354eb35609SIngo Weinhold }
364eb35609SIngo Weinhold 
374eb35609SIngo Weinhold 
384eb35609SIngo Weinhold static status_t
394eb35609SIngo Weinhold common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel)
404eb35609SIngo Weinhold {
414535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
424eb35609SIngo Weinhold 
4324df6592SIngo Weinhold 	TeamLocker teamLocker(team);
444eb35609SIngo Weinhold 
454eb35609SIngo Weinhold 	bool privileged = kernel || is_privileged(team);
464eb35609SIngo Weinhold 
475b53519cSIngo Weinhold 	gid_t ssgid = team->saved_set_gid;
485b53519cSIngo Weinhold 
494eb35609SIngo Weinhold 	// real gid
504eb35609SIngo Weinhold 	if (rgid == (gid_t)-1) {
514eb35609SIngo Weinhold 		rgid = team->real_gid;
524eb35609SIngo Weinhold 	} else {
534eb35609SIngo Weinhold 		if (setAllIfPrivileged) {
544eb35609SIngo Weinhold 			// setgid() semantics: If privileged set both, real, effective and
554eb35609SIngo Weinhold 			// saved set-gid, otherwise set the effective gid.
564eb35609SIngo Weinhold 			if (privileged) {
5724df6592SIngo Weinhold 				InterruptsSpinLocker schedulerLocker(gSchedulerLock);
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.
9424df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
954eb35609SIngo Weinhold 	team->real_gid = rgid;
964eb35609SIngo Weinhold 	team->effective_gid = egid;
975b53519cSIngo Weinhold 	team->saved_set_gid = ssgid;
984eb35609SIngo Weinhold 
994eb35609SIngo Weinhold 	return B_OK;
1004eb35609SIngo Weinhold }
1014eb35609SIngo Weinhold 
1024eb35609SIngo Weinhold 
1034eb35609SIngo Weinhold static status_t
1044eb35609SIngo Weinhold common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel)
1054eb35609SIngo Weinhold {
1064535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
1074eb35609SIngo Weinhold 
10824df6592SIngo Weinhold 	TeamLocker teamLocker(team);
1094eb35609SIngo Weinhold 
1104eb35609SIngo Weinhold 	bool privileged = kernel || is_privileged(team);
1114eb35609SIngo Weinhold 
1125b53519cSIngo Weinhold 	uid_t ssuid = team->saved_set_uid;
1135b53519cSIngo Weinhold 
1144eb35609SIngo Weinhold 	// real uid
1154eb35609SIngo Weinhold 	if (ruid == (uid_t)-1) {
1164eb35609SIngo Weinhold 		ruid = team->real_uid;
1174eb35609SIngo Weinhold 	} else {
1184eb35609SIngo Weinhold 		if (setAllIfPrivileged) {
1194eb35609SIngo Weinhold 			// setuid() semantics: If privileged set both, real, effective and
1204eb35609SIngo Weinhold 			// saved set-uid, otherwise set the effective uid.
1214eb35609SIngo Weinhold 			if (privileged) {
12224df6592SIngo Weinhold 				InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1234eb35609SIngo Weinhold 				team->saved_set_uid = ruid;
1244eb35609SIngo Weinhold 				team->real_uid = ruid;
1254eb35609SIngo Weinhold 				team->effective_uid = ruid;
1264eb35609SIngo Weinhold 				return B_OK;
1274eb35609SIngo Weinhold 			}
1284eb35609SIngo Weinhold 
1294eb35609SIngo Weinhold 			// not privileged -- set only the effective uid
1304eb35609SIngo Weinhold 			euid = ruid;
1314eb35609SIngo Weinhold 			ruid = team->real_uid;
1324eb35609SIngo Weinhold 		} else {
1334eb35609SIngo Weinhold 			// setreuid() semantics: set the real uid, if allowed to
1344eb35609SIngo Weinhold 			// Note: We allow setting the real uid to the effective uid. This
1354eb35609SIngo Weinhold 			// is unspecified by the specs, but is common practice.
1364eb35609SIngo Weinhold 			if (!privileged && ruid != team->real_uid
1374eb35609SIngo Weinhold 				&& ruid != team->effective_uid) {
1384eb35609SIngo Weinhold 				return EPERM;
1394eb35609SIngo Weinhold 			}
1405b53519cSIngo Weinhold 
1415b53519cSIngo Weinhold 			// Note: Also common practice is to set the saved set-uid when the
1425b53519cSIngo Weinhold 			// real uid is set.
1435b53519cSIngo Weinhold 			if (ruid != team->real_uid)
1445b53519cSIngo Weinhold 				ssuid = ruid;
1454eb35609SIngo Weinhold 		}
1464eb35609SIngo Weinhold 	}
1474eb35609SIngo Weinhold 
1484eb35609SIngo Weinhold 	// effective uid
1494eb35609SIngo Weinhold 	if (euid == (uid_t)-1) {
1504eb35609SIngo Weinhold 		euid = team->effective_uid;
1514eb35609SIngo Weinhold 	} else {
1524eb35609SIngo Weinhold 		if (!privileged && euid != team->effective_uid
1534eb35609SIngo Weinhold 			&& euid != team->real_uid && euid != team->saved_set_uid) {
1544eb35609SIngo Weinhold 			return EPERM;
1554eb35609SIngo Weinhold 		}
1564eb35609SIngo Weinhold 	}
1574eb35609SIngo Weinhold 
1584eb35609SIngo Weinhold 	// Getting here means all checks were successful -- set the uids.
15924df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1604eb35609SIngo Weinhold 	team->real_uid = ruid;
1614eb35609SIngo Weinhold 	team->effective_uid = euid;
1625b53519cSIngo Weinhold 	team->saved_set_uid = ssuid;
1634eb35609SIngo Weinhold 
1644eb35609SIngo Weinhold 	return B_OK;
1654eb35609SIngo Weinhold }
1664eb35609SIngo Weinhold 
1674eb35609SIngo Weinhold 
168290946ceSIngo Weinhold ssize_t
169290946ceSIngo Weinhold common_getgroups(int groupCount, gid_t* groupList, bool kernel)
170290946ceSIngo Weinhold {
1714535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
172290946ceSIngo Weinhold 
17324df6592SIngo Weinhold 	TeamLocker teamLocker(team);
174290946ceSIngo Weinhold 
175290946ceSIngo Weinhold 	const gid_t* groups = team->supplementary_groups;
176290946ceSIngo Weinhold 	int actualCount = team->supplementary_group_count;
177290946ceSIngo Weinhold 
178290946ceSIngo Weinhold 	// follow the specification and return always at least one group
179290946ceSIngo Weinhold 	if (actualCount == 0) {
180290946ceSIngo Weinhold 		groups = &team->effective_gid;
181290946ceSIngo Weinhold 		actualCount = 1;
182290946ceSIngo Weinhold 	}
183290946ceSIngo Weinhold 
1849fe0705bSIngo Weinhold 	// if groupCount 0 is supplied, we only return the number of groups
1859fe0705bSIngo Weinhold 	if (groupCount == 0)
1869fe0705bSIngo Weinhold 		return actualCount;
1879fe0705bSIngo Weinhold 
188290946ceSIngo Weinhold 	// check for sufficient space
189290946ceSIngo Weinhold 	if (groupCount < actualCount)
190290946ceSIngo Weinhold 		return B_BAD_VALUE;
191290946ceSIngo Weinhold 
192290946ceSIngo Weinhold 	// copy
193290946ceSIngo Weinhold 	if (kernel) {
194*021ccbd1SPawel Dziepak 		memcpy(groupList, groups, actualCount * sizeof(gid_t));
195290946ceSIngo Weinhold 	} else {
196290946ceSIngo Weinhold 		if (!IS_USER_ADDRESS(groupList)
197290946ceSIngo Weinhold 			|| user_memcpy(groupList, groups,
198290946ceSIngo Weinhold 					actualCount * sizeof(gid_t)) != B_OK) {
199290946ceSIngo Weinhold 			return B_BAD_ADDRESS;
200290946ceSIngo Weinhold 		}
201290946ceSIngo Weinhold 	}
202290946ceSIngo Weinhold 
203290946ceSIngo Weinhold 	return actualCount;
204290946ceSIngo Weinhold }
205290946ceSIngo Weinhold 
206290946ceSIngo Weinhold 
207290946ceSIngo Weinhold static status_t
208290946ceSIngo Weinhold common_setgroups(int groupCount, const gid_t* groupList, bool kernel)
209290946ceSIngo Weinhold {
210290946ceSIngo Weinhold 	if (groupCount < 0 || groupCount > NGROUPS_MAX)
211290946ceSIngo Weinhold 		return B_BAD_VALUE;
212290946ceSIngo Weinhold 
213290946ceSIngo Weinhold 	gid_t* newGroups = NULL;
214290946ceSIngo Weinhold 	if (groupCount > 0) {
215290946ceSIngo Weinhold 		newGroups = (gid_t*)malloc_referenced(sizeof(gid_t) * groupCount);
216290946ceSIngo Weinhold 		if (newGroups == NULL)
217290946ceSIngo Weinhold 			return B_NO_MEMORY;
218290946ceSIngo Weinhold 
219290946ceSIngo Weinhold 		if (kernel) {
220290946ceSIngo Weinhold 			memcpy(newGroups, groupList, sizeof(gid_t) * groupCount);
221290946ceSIngo Weinhold 		} else {
222290946ceSIngo Weinhold 			if (!IS_USER_ADDRESS(groupList)
223290946ceSIngo Weinhold 				|| user_memcpy(newGroups, groupList,
224290946ceSIngo Weinhold 					sizeof(gid_t) * groupCount) != B_OK) {
225290946ceSIngo Weinhold 				free(newGroups);
226290946ceSIngo Weinhold 				return B_BAD_ADDRESS;
227290946ceSIngo Weinhold 			}
228290946ceSIngo Weinhold 		}
229290946ceSIngo Weinhold 	}
230290946ceSIngo Weinhold 
2314535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
232290946ceSIngo Weinhold 
23324df6592SIngo Weinhold 	TeamLocker teamLocker(team);
23424df6592SIngo Weinhold 
235290946ceSIngo Weinhold 	gid_t* toFree = team->supplementary_groups;
236290946ceSIngo Weinhold 	team->supplementary_groups = newGroups;
237290946ceSIngo Weinhold 	team->supplementary_group_count = groupCount;
238290946ceSIngo Weinhold 
23924df6592SIngo Weinhold 	teamLocker.Unlock();
240290946ceSIngo Weinhold 
241290946ceSIngo Weinhold 	malloc_referenced_release(toFree);
242290946ceSIngo Weinhold 
243290946ceSIngo Weinhold 	return B_OK;
244290946ceSIngo Weinhold }
245290946ceSIngo Weinhold 
246290946ceSIngo Weinhold 
2474eb35609SIngo Weinhold // #pragma mark - Kernel Private
2484eb35609SIngo Weinhold 
2494eb35609SIngo Weinhold 
25024df6592SIngo Weinhold /*!	Copies the user and group information from \a parent to \a team.
25124df6592SIngo Weinhold 	The caller must hold the lock to both \a team and \a parent.
25224df6592SIngo Weinhold */
2534eb35609SIngo Weinhold void
25424df6592SIngo Weinhold inherit_parent_user_and_group(Team* team, Team* parent)
2554eb35609SIngo Weinhold {
25624df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
25724df6592SIngo Weinhold 
2584eb35609SIngo Weinhold 	team->saved_set_uid = parent->saved_set_uid;
2594eb35609SIngo Weinhold 	team->real_uid = parent->real_uid;
2604eb35609SIngo Weinhold 	team->effective_uid = parent->effective_uid;
2614eb35609SIngo Weinhold 	team->saved_set_gid = parent->saved_set_gid;
2624eb35609SIngo Weinhold 	team->real_gid = parent->real_gid;
2634eb35609SIngo Weinhold 	team->effective_gid = parent->effective_gid;
264290946ceSIngo Weinhold 
26524df6592SIngo Weinhold 	schedulerLocker.Unlock();
26624df6592SIngo Weinhold 
267290946ceSIngo Weinhold 	malloc_referenced_acquire(parent->supplementary_groups);
268290946ceSIngo Weinhold 	team->supplementary_groups = parent->supplementary_groups;
269290946ceSIngo Weinhold 	team->supplementary_group_count = parent->supplementary_group_count;
2704eb35609SIngo Weinhold }
2714eb35609SIngo Weinhold 
2724eb35609SIngo Weinhold 
2734eb35609SIngo Weinhold status_t
2744535495dSIngo Weinhold update_set_id_user_and_group(Team* team, const char* file)
2754eb35609SIngo Weinhold {
2764eb35609SIngo Weinhold 	struct stat st;
277290946ceSIngo Weinhold 	status_t status = vfs_read_stat(-1, file, true, &st, false);
278290946ceSIngo Weinhold 	if (status != B_OK)
279290946ceSIngo Weinhold 		return status;
2804eb35609SIngo Weinhold 
28124df6592SIngo Weinhold 	TeamLocker teamLocker(team);
28224df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
2834eb35609SIngo Weinhold 
2844eb35609SIngo Weinhold 	if ((st.st_mode & S_ISUID) != 0) {
2854eb35609SIngo Weinhold 		team->saved_set_uid = st.st_uid;
2864eb35609SIngo Weinhold 		team->effective_uid = st.st_uid;
2874eb35609SIngo Weinhold 	}
2884eb35609SIngo Weinhold 
2894eb35609SIngo Weinhold 	if ((st.st_mode & S_ISGID) != 0) {
2904eb35609SIngo Weinhold 		team->saved_set_gid = st.st_gid;
2914eb35609SIngo Weinhold 		team->effective_gid = st.st_gid;
2924eb35609SIngo Weinhold 	}
2934eb35609SIngo Weinhold 
2944eb35609SIngo Weinhold 	return B_OK;
2954eb35609SIngo Weinhold }
2964eb35609SIngo Weinhold 
2974eb35609SIngo Weinhold 
2984eb35609SIngo Weinhold gid_t
2994eb35609SIngo Weinhold _kern_getgid(bool effective)
3004eb35609SIngo Weinhold {
3014535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3024eb35609SIngo Weinhold 
30324df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
30424df6592SIngo Weinhold 
3054eb35609SIngo Weinhold 	return effective ? team->effective_gid : team->real_gid;
3064eb35609SIngo Weinhold }
3074eb35609SIngo Weinhold 
3084eb35609SIngo Weinhold 
3094eb35609SIngo Weinhold uid_t
3104eb35609SIngo Weinhold _kern_getuid(bool effective)
3114eb35609SIngo Weinhold {
3124535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3134eb35609SIngo Weinhold 
31424df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
31524df6592SIngo Weinhold 
3164eb35609SIngo Weinhold 	return effective ? team->effective_uid : team->real_uid;
3174eb35609SIngo Weinhold }
3184eb35609SIngo Weinhold 
3194eb35609SIngo Weinhold 
3204eb35609SIngo Weinhold status_t
3214eb35609SIngo Weinhold _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
3224eb35609SIngo Weinhold {
3234eb35609SIngo Weinhold 	return common_setregid(rgid, egid, setAllIfPrivileged, true);
3244eb35609SIngo Weinhold }
3254eb35609SIngo Weinhold 
3264eb35609SIngo Weinhold 
3274eb35609SIngo Weinhold status_t
3284eb35609SIngo Weinhold _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
3294eb35609SIngo Weinhold {
3304eb35609SIngo Weinhold 	return common_setreuid(ruid, euid, setAllIfPrivileged, true);
3314eb35609SIngo Weinhold }
3324eb35609SIngo Weinhold 
3334eb35609SIngo Weinhold 
334290946ceSIngo Weinhold ssize_t
335290946ceSIngo Weinhold _kern_getgroups(int groupCount, gid_t* groupList)
336290946ceSIngo Weinhold {
337290946ceSIngo Weinhold 	return common_getgroups(groupCount, groupList, true);
338290946ceSIngo Weinhold }
339290946ceSIngo Weinhold 
340290946ceSIngo Weinhold 
341290946ceSIngo Weinhold status_t
342290946ceSIngo Weinhold _kern_setgroups(int groupCount, const gid_t* groupList)
343290946ceSIngo Weinhold {
344290946ceSIngo Weinhold 	return common_setgroups(groupCount, groupList, true);
345290946ceSIngo Weinhold }
346290946ceSIngo Weinhold 
347290946ceSIngo Weinhold 
3484eb35609SIngo Weinhold // #pragma mark - Syscalls
3494eb35609SIngo Weinhold 
3504eb35609SIngo Weinhold 
3514eb35609SIngo Weinhold gid_t
3524eb35609SIngo Weinhold _user_getgid(bool effective)
3534eb35609SIngo Weinhold {
3544535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3554eb35609SIngo Weinhold 
35624df6592SIngo Weinhold 	TeamLocker teamLocker(team);
35724df6592SIngo Weinhold 
3584eb35609SIngo Weinhold 	return effective ? team->effective_gid : team->real_gid;
3594eb35609SIngo Weinhold }
3604eb35609SIngo Weinhold 
3614eb35609SIngo Weinhold 
3624eb35609SIngo Weinhold uid_t
3634eb35609SIngo Weinhold _user_getuid(bool effective)
3644eb35609SIngo Weinhold {
3654535495dSIngo Weinhold 	Team* team = thread_get_current_thread()->team;
3664eb35609SIngo Weinhold 
36724df6592SIngo Weinhold 	TeamLocker teamLocker(team);
36824df6592SIngo Weinhold 
3694eb35609SIngo Weinhold 	return effective ? team->effective_uid : team->real_uid;
3704eb35609SIngo Weinhold }
3714eb35609SIngo Weinhold 
3724eb35609SIngo Weinhold 
3734eb35609SIngo Weinhold status_t
3744eb35609SIngo Weinhold _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
3754eb35609SIngo Weinhold {
3764eb35609SIngo Weinhold 	return common_setregid(rgid, egid, setAllIfPrivileged, false);
3774eb35609SIngo Weinhold }
3784eb35609SIngo Weinhold 
3794eb35609SIngo Weinhold 
3804eb35609SIngo Weinhold status_t
3814eb35609SIngo Weinhold _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
3824eb35609SIngo Weinhold {
3834eb35609SIngo Weinhold 	return common_setreuid(ruid, euid, setAllIfPrivileged, false);
3844eb35609SIngo Weinhold }
385290946ceSIngo Weinhold 
386290946ceSIngo Weinhold 
387290946ceSIngo Weinhold ssize_t
388290946ceSIngo Weinhold _user_getgroups(int groupCount, gid_t* groupList)
389290946ceSIngo Weinhold {
390290946ceSIngo Weinhold 	return common_getgroups(groupCount, groupList, false);
391290946ceSIngo Weinhold }
392290946ceSIngo Weinhold 
393290946ceSIngo Weinhold 
394290946ceSIngo Weinhold ssize_t
395290946ceSIngo Weinhold _user_setgroups(int groupCount, const gid_t* groupList)
396290946ceSIngo Weinhold {
39724df6592SIngo Weinhold 	// check privilege
39824df6592SIngo Weinhold 	{
39924df6592SIngo Weinhold 		Team* team = thread_get_current_thread()->team;
40024df6592SIngo Weinhold 		TeamLocker teamLocker(team);
40124df6592SIngo Weinhold 		if (!is_privileged(team))
402290946ceSIngo Weinhold 			return EPERM;
40324df6592SIngo Weinhold 	}
404290946ceSIngo Weinhold 
405290946ceSIngo Weinhold 	return common_setgroups(groupCount, groupList, false);
406290946ceSIngo Weinhold }
407