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