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 Weinholdstatic bool
314535495dSIngo Weinholdis_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 Weinholdstatic status_t
394eb35609SIngo Weinholdcommon_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) {
574eb35609SIngo Weinhold				team->saved_set_gid = rgid;
584eb35609SIngo Weinhold				team->real_gid = rgid;
594eb35609SIngo Weinhold				team->effective_gid = rgid;
604eb35609SIngo Weinhold				return B_OK;
614eb35609SIngo Weinhold			}
624eb35609SIngo Weinhold
634eb35609SIngo Weinhold			// not privileged -- set only the effective gid
644eb35609SIngo Weinhold			egid = rgid;
654eb35609SIngo Weinhold			rgid = team->real_gid;
664eb35609SIngo Weinhold		} else {
674eb35609SIngo Weinhold			// setregid() semantics: set the real gid, if allowed to
685b53519cSIngo Weinhold			// Note: We allow setting the real gid to the effective gid. This
695b53519cSIngo Weinhold			// is unspecified by the specs, but is common practice.
705b53519cSIngo Weinhold			if (!privileged && rgid != team->real_gid
715b53519cSIngo Weinhold				&& rgid != team->effective_gid) {
724eb35609SIngo Weinhold				return EPERM;
735b53519cSIngo Weinhold			}
745b53519cSIngo Weinhold
755b53519cSIngo Weinhold			// Note: Also common practice is to set the saved set-gid when the
765b53519cSIngo Weinhold			// real gid is set.
775b53519cSIngo Weinhold			if (rgid != team->real_gid)
785b53519cSIngo Weinhold				ssgid = rgid;
794eb35609SIngo Weinhold		}
804eb35609SIngo Weinhold	}
814eb35609SIngo Weinhold
824eb35609SIngo Weinhold	// effective gid
834eb35609SIngo Weinhold	if (egid == (gid_t)-1) {
844eb35609SIngo Weinhold		egid = team->effective_gid;
854eb35609SIngo Weinhold	} else {
864eb35609SIngo Weinhold		if (!privileged && egid != team->effective_gid
874eb35609SIngo Weinhold			&& egid != team->real_gid && egid != team->saved_set_gid) {
884eb35609SIngo Weinhold			return EPERM;
894eb35609SIngo Weinhold		}
904eb35609SIngo Weinhold	}
914eb35609SIngo Weinhold
924eb35609SIngo Weinhold	// Getting here means all checks were successful -- set the gids.
934eb35609SIngo Weinhold	team->real_gid = rgid;
944eb35609SIngo Weinhold	team->effective_gid = egid;
955b53519cSIngo Weinhold	team->saved_set_gid = ssgid;
964eb35609SIngo Weinhold
974eb35609SIngo Weinhold	return B_OK;
984eb35609SIngo Weinhold}
994eb35609SIngo Weinhold
1004eb35609SIngo Weinhold
1014eb35609SIngo Weinholdstatic status_t
1024eb35609SIngo Weinholdcommon_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel)
1034eb35609SIngo Weinhold{
1044535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
1054eb35609SIngo Weinhold
10624df6592SIngo Weinhold	TeamLocker teamLocker(team);
1074eb35609SIngo Weinhold
1084eb35609SIngo Weinhold	bool privileged = kernel || is_privileged(team);
1094eb35609SIngo Weinhold
1105b53519cSIngo Weinhold	uid_t ssuid = team->saved_set_uid;
1115b53519cSIngo Weinhold
1124eb35609SIngo Weinhold	// real uid
1134eb35609SIngo Weinhold	if (ruid == (uid_t)-1) {
1144eb35609SIngo Weinhold		ruid = team->real_uid;
1154eb35609SIngo Weinhold	} else {
1164eb35609SIngo Weinhold		if (setAllIfPrivileged) {
1174eb35609SIngo Weinhold			// setuid() semantics: If privileged set both, real, effective and
1184eb35609SIngo Weinhold			// saved set-uid, otherwise set the effective uid.
1194eb35609SIngo Weinhold			if (privileged) {
1204eb35609SIngo Weinhold				team->saved_set_uid = ruid;
1214eb35609SIngo Weinhold				team->real_uid = ruid;
1224eb35609SIngo Weinhold				team->effective_uid = ruid;
1234eb35609SIngo Weinhold				return B_OK;
1244eb35609SIngo Weinhold			}
1254eb35609SIngo Weinhold
1264eb35609SIngo Weinhold			// not privileged -- set only the effective uid
1274eb35609SIngo Weinhold			euid = ruid;
1284eb35609SIngo Weinhold			ruid = team->real_uid;
1294eb35609SIngo Weinhold		} else {
1304eb35609SIngo Weinhold			// setreuid() semantics: set the real uid, if allowed to
1314eb35609SIngo Weinhold			// Note: We allow setting the real uid to the effective uid. This
1324eb35609SIngo Weinhold			// is unspecified by the specs, but is common practice.
1334eb35609SIngo Weinhold			if (!privileged && ruid != team->real_uid
1344eb35609SIngo Weinhold				&& ruid != team->effective_uid) {
1354eb35609SIngo Weinhold				return EPERM;
1364eb35609SIngo Weinhold			}
1375b53519cSIngo Weinhold
1385b53519cSIngo Weinhold			// Note: Also common practice is to set the saved set-uid when the
1395b53519cSIngo Weinhold			// real uid is set.
1405b53519cSIngo Weinhold			if (ruid != team->real_uid)
1415b53519cSIngo Weinhold				ssuid = ruid;
1424eb35609SIngo Weinhold		}
1434eb35609SIngo Weinhold	}
1444eb35609SIngo Weinhold
1454eb35609SIngo Weinhold	// effective uid
1464eb35609SIngo Weinhold	if (euid == (uid_t)-1) {
1474eb35609SIngo Weinhold		euid = team->effective_uid;
1484eb35609SIngo Weinhold	} else {
1494eb35609SIngo Weinhold		if (!privileged && euid != team->effective_uid
1504eb35609SIngo Weinhold			&& euid != team->real_uid && euid != team->saved_set_uid) {
1514eb35609SIngo Weinhold			return EPERM;
1524eb35609SIngo Weinhold		}
1534eb35609SIngo Weinhold	}
1544eb35609SIngo Weinhold
1554eb35609SIngo Weinhold	// Getting here means all checks were successful -- set the uids.
1564eb35609SIngo Weinhold	team->real_uid = ruid;
1574eb35609SIngo Weinhold	team->effective_uid = euid;
1585b53519cSIngo Weinhold	team->saved_set_uid = ssuid;
1594eb35609SIngo Weinhold
1604eb35609SIngo Weinhold	return B_OK;
1614eb35609SIngo Weinhold}
1624eb35609SIngo Weinhold
1634eb35609SIngo Weinhold
164290946ceSIngo Weinholdssize_t
165290946ceSIngo Weinholdcommon_getgroups(int groupCount, gid_t* groupList, bool kernel)
166290946ceSIngo Weinhold{
1674535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
168290946ceSIngo Weinhold
16924df6592SIngo Weinhold	TeamLocker teamLocker(team);
170290946ceSIngo Weinhold
171290946ceSIngo Weinhold	const gid_t* groups = team->supplementary_groups;
172290946ceSIngo Weinhold	int actualCount = team->supplementary_group_count;
173290946ceSIngo Weinhold
174290946ceSIngo Weinhold	// follow the specification and return always at least one group
175290946ceSIngo Weinhold	if (actualCount == 0) {
176290946ceSIngo Weinhold		groups = &team->effective_gid;
177290946ceSIngo Weinhold		actualCount = 1;
178290946ceSIngo Weinhold	}
179290946ceSIngo Weinhold
1809fe0705bSIngo Weinhold	// if groupCount 0 is supplied, we only return the number of groups
1819fe0705bSIngo Weinhold	if (groupCount == 0)
1829fe0705bSIngo Weinhold		return actualCount;
1839fe0705bSIngo Weinhold
184290946ceSIngo Weinhold	// check for sufficient space
185290946ceSIngo Weinhold	if (groupCount < actualCount)
186290946ceSIngo Weinhold		return B_BAD_VALUE;
187290946ceSIngo Weinhold
188290946ceSIngo Weinhold	// copy
189290946ceSIngo Weinhold	if (kernel) {
190021ccbd1SPawel Dziepak		memcpy(groupList, groups, actualCount * sizeof(gid_t));
191290946ceSIngo Weinhold	} else {
192290946ceSIngo Weinhold		if (!IS_USER_ADDRESS(groupList)
193290946ceSIngo Weinhold			|| user_memcpy(groupList, groups,
194290946ceSIngo Weinhold					actualCount * sizeof(gid_t)) != B_OK) {
195290946ceSIngo Weinhold			return B_BAD_ADDRESS;
196290946ceSIngo Weinhold		}
197290946ceSIngo Weinhold	}
198290946ceSIngo Weinhold
199290946ceSIngo Weinhold	return actualCount;
200290946ceSIngo Weinhold}
201290946ceSIngo Weinhold
202290946ceSIngo Weinhold
203290946ceSIngo Weinholdstatic status_t
204290946ceSIngo Weinholdcommon_setgroups(int groupCount, const gid_t* groupList, bool kernel)
205290946ceSIngo Weinhold{
206290946ceSIngo Weinhold	if (groupCount < 0 || groupCount > NGROUPS_MAX)
207290946ceSIngo Weinhold		return B_BAD_VALUE;
208290946ceSIngo Weinhold
209290946ceSIngo Weinhold	gid_t* newGroups = NULL;
210290946ceSIngo Weinhold	if (groupCount > 0) {
211290946ceSIngo Weinhold		newGroups = (gid_t*)malloc_referenced(sizeof(gid_t) * groupCount);
212290946ceSIngo Weinhold		if (newGroups == NULL)
213290946ceSIngo Weinhold			return B_NO_MEMORY;
214290946ceSIngo Weinhold
215290946ceSIngo Weinhold		if (kernel) {
216290946ceSIngo Weinhold			memcpy(newGroups, groupList, sizeof(gid_t) * groupCount);
217290946ceSIngo Weinhold		} else {
218290946ceSIngo Weinhold			if (!IS_USER_ADDRESS(groupList)
219290946ceSIngo Weinhold				|| user_memcpy(newGroups, groupList,
220290946ceSIngo Weinhold					sizeof(gid_t) * groupCount) != B_OK) {
22158c99ce4SFredrik Holmqvist				malloc_referenced_release(newGroups);
222290946ceSIngo Weinhold				return B_BAD_ADDRESS;
223290946ceSIngo Weinhold			}
224290946ceSIngo Weinhold		}
225290946ceSIngo Weinhold	}
226290946ceSIngo Weinhold
2274535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
228290946ceSIngo Weinhold
22924df6592SIngo Weinhold	TeamLocker teamLocker(team);
23024df6592SIngo Weinhold
231290946ceSIngo Weinhold	gid_t* toFree = team->supplementary_groups;
232290946ceSIngo Weinhold	team->supplementary_groups = newGroups;
233290946ceSIngo Weinhold	team->supplementary_group_count = groupCount;
234290946ceSIngo Weinhold
23524df6592SIngo Weinhold	teamLocker.Unlock();
236290946ceSIngo Weinhold
237290946ceSIngo Weinhold	malloc_referenced_release(toFree);
238290946ceSIngo Weinhold
239290946ceSIngo Weinhold	return B_OK;
240290946ceSIngo Weinhold}
241290946ceSIngo Weinhold
242290946ceSIngo Weinhold
2434eb35609SIngo Weinhold// #pragma mark - Kernel Private
2444eb35609SIngo Weinhold
2454eb35609SIngo Weinhold
24624df6592SIngo Weinhold/*!	Copies the user and group information from \a parent to \a team.
24724df6592SIngo Weinhold	The caller must hold the lock to both \a team and \a parent.
24824df6592SIngo Weinhold*/
2494eb35609SIngo Weinholdvoid
25024df6592SIngo Weinholdinherit_parent_user_and_group(Team* team, Team* parent)
2514eb35609SIngo Weinhold{
2524eb35609SIngo Weinhold	team->saved_set_uid = parent->saved_set_uid;
2534eb35609SIngo Weinhold	team->real_uid = parent->real_uid;
2544eb35609SIngo Weinhold	team->effective_uid = parent->effective_uid;
2554eb35609SIngo Weinhold	team->saved_set_gid = parent->saved_set_gid;
2564eb35609SIngo Weinhold	team->real_gid = parent->real_gid;
2574eb35609SIngo Weinhold	team->effective_gid = parent->effective_gid;
258290946ceSIngo Weinhold
259290946ceSIngo Weinhold	malloc_referenced_acquire(parent->supplementary_groups);
260290946ceSIngo Weinhold	team->supplementary_groups = parent->supplementary_groups;
261290946ceSIngo Weinhold	team->supplementary_group_count = parent->supplementary_group_count;
2624eb35609SIngo Weinhold}
2634eb35609SIngo Weinhold
2644eb35609SIngo Weinhold
2654eb35609SIngo Weinholdstatus_t
2664535495dSIngo Weinholdupdate_set_id_user_and_group(Team* team, const char* file)
2674eb35609SIngo Weinhold{
2684eb35609SIngo Weinhold	struct stat st;
269290946ceSIngo Weinhold	status_t status = vfs_read_stat(-1, file, true, &st, false);
270290946ceSIngo Weinhold	if (status != B_OK)
271290946ceSIngo Weinhold		return status;
2724eb35609SIngo Weinhold
27324df6592SIngo Weinhold	TeamLocker teamLocker(team);
2744eb35609SIngo Weinhold
2754eb35609SIngo Weinhold	if ((st.st_mode & S_ISUID) != 0) {
2764eb35609SIngo Weinhold		team->saved_set_uid = st.st_uid;
2774eb35609SIngo Weinhold		team->effective_uid = st.st_uid;
2784eb35609SIngo Weinhold	}
2794eb35609SIngo Weinhold
2804eb35609SIngo Weinhold	if ((st.st_mode & S_ISGID) != 0) {
2814eb35609SIngo Weinhold		team->saved_set_gid = st.st_gid;
2824eb35609SIngo Weinhold		team->effective_gid = st.st_gid;
2834eb35609SIngo Weinhold	}
2844eb35609SIngo Weinhold
2854eb35609SIngo Weinhold	return B_OK;
2864eb35609SIngo Weinhold}
2874eb35609SIngo Weinhold
2884eb35609SIngo Weinhold
2894eb35609SIngo Weinholdgid_t
2904eb35609SIngo Weinhold_kern_getgid(bool effective)
2914eb35609SIngo Weinhold{
2924535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
2934eb35609SIngo Weinhold
2944eb35609SIngo Weinhold	return effective ? team->effective_gid : team->real_gid;
2954eb35609SIngo Weinhold}
2964eb35609SIngo Weinhold
2974eb35609SIngo Weinhold
2984eb35609SIngo Weinholduid_t
2994eb35609SIngo Weinhold_kern_getuid(bool effective)
3004eb35609SIngo Weinhold{
3014535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
3024eb35609SIngo Weinhold
3034eb35609SIngo Weinhold	return effective ? team->effective_uid : team->real_uid;
3044eb35609SIngo Weinhold}
3054eb35609SIngo Weinhold
3064eb35609SIngo Weinhold
307290946ceSIngo Weinholdstatus_t
308290946ceSIngo Weinhold_kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
3094eb35609SIngo Weinhold{
310290946ceSIngo Weinhold	return common_setregid(rgid, egid, setAllIfPrivileged, true);
311290946ceSIngo Weinhold}
3124eb35609SIngo Weinhold
3134eb35609SIngo Weinhold
314290946ceSIngo Weinholdstatus_t
315290946ceSIngo Weinhold_kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
316290946ceSIngo Weinhold{
317290946ceSIngo Weinhold	return common_setreuid(ruid, euid, setAllIfPrivileged, true);
3184eb35609SIngo Weinhold}
3194eb35609SIngo Weinhold
3204eb35609SIngo Weinhold
321290946ceSIngo Weinholdssize_t
322290946ceSIngo Weinhold_kern_getgroups(int groupCount, gid_t* groupList)
3234eb35609SIngo Weinhold{
324290946ceSIngo Weinhold	return common_getgroups(groupCount, groupList, true);
3254eb35609SIngo Weinhold}
3264eb35609SIngo Weinhold
3274eb35609SIngo Weinhold
3284eb35609SIngo Weinholdstatus_t
329290946ceSIngo Weinhold_kern_setgroups(int groupCount, const gid_t* groupList)
3304eb35609SIngo Weinhold{
331290946ceSIngo Weinhold	return common_setgroups(groupCount, groupList, true);
3324eb35609SIngo Weinhold}
3334eb35609SIngo Weinhold
3344eb35609SIngo Weinhold
3354eb35609SIngo Weinhold// #pragma mark - Syscalls
3364eb35609SIngo Weinhold
3374eb35609SIngo Weinhold
3384eb35609SIngo Weinholdgid_t
3394eb35609SIngo Weinhold_user_getgid(bool effective)
3404eb35609SIngo Weinhold{
3414535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
3424eb35609SIngo Weinhold
3434eb35609SIngo Weinhold	return effective ? team->effective_gid : team->real_gid;
3444eb35609SIngo Weinhold}
3454eb35609SIngo Weinhold
3464eb35609SIngo Weinhold
3474eb35609SIngo Weinholduid_t
3484eb35609SIngo Weinhold_user_getuid(bool effective)
3494eb35609SIngo Weinhold{
3504535495dSIngo Weinhold	Team* team = thread_get_current_thread()->team;
3514eb35609SIngo Weinhold
3524eb35609SIngo Weinhold	return effective ? team->effective_uid : team->real_uid;
3534eb35609SIngo Weinhold}
3544eb35609SIngo Weinhold
3554eb35609SIngo Weinhold
3564eb35609SIngo Weinholdstatus_t
3574eb35609SIngo Weinhold_user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
3584eb35609SIngo Weinhold{
3594eb35609SIngo Weinhold	return common_setregid(rgid, egid, setAllIfPrivileged, false);
3604eb35609SIngo Weinhold}
3614eb35609SIngo Weinhold
3624eb35609SIngo Weinhold
3634eb35609SIngo Weinholdstatus_t
3644eb35609SIngo Weinhold_user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
3654eb35609SIngo Weinhold{
3664eb35609SIngo Weinhold	return common_setreuid(ruid, euid, setAllIfPrivileged, false);
3674eb35609SIngo Weinhold}
368290946ceSIngo Weinhold
369290946ceSIngo Weinhold
370290946ceSIngo Weinholdssize_t
371290946ceSIngo Weinhold_user_getgroups(int groupCount, gid_t* groupList)
372290946ceSIngo Weinhold{
373290946ceSIngo Weinhold	return common_getgroups(groupCount, groupList, false);
374290946ceSIngo Weinhold}
375290946ceSIngo Weinhold
376290946ceSIngo Weinhold
377290946ceSIngo Weinholdssize_t
378290946ceSIngo Weinhold_user_setgroups(int groupCount, const gid_t* groupList)
379290946ceSIngo Weinhold{
38024df6592SIngo Weinhold	// check privilege
381d54a9e0aSPawel Dziepak	Team* team = thread_get_current_thread()->team;
382d54a9e0aSPawel Dziepak	if (!is_privileged(team))
383d54a9e0aSPawel Dziepak		return EPERM;
384290946ceSIngo Weinhold
385290946ceSIngo Weinhold	return common_setgroups(groupCount, groupList, false);
386290946ceSIngo Weinhold}
387