1/* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include <usergroup.h> 8 9#include <errno.h> 10#include <limits.h> 11#include <sys/stat.h> 12 13#include <new> 14 15#include <heap.h> 16#include <kernel.h> 17#include <syscalls.h> 18#include <team.h> 19#include <thread.h> 20#include <thread_types.h> 21#include <util/AutoLock.h> 22#include <util/ThreadAutoLock.h> 23#include <vfs.h> 24 25#include <AutoDeleter.h> 26 27 28// #pragma mark - Implementation Private 29 30 31static bool 32is_privileged(Team* team) 33{ 34 // currently only the root user is privileged 35 return team->effective_uid == 0; 36} 37 38 39static status_t 40common_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged, bool kernel) 41{ 42 Team* team = thread_get_current_thread()->team; 43 44 TeamLocker teamLocker(team); 45 46 bool privileged = kernel || is_privileged(team); 47 48 gid_t ssgid = team->saved_set_gid; 49 50 // real gid 51 if (rgid == (gid_t)-1) { 52 rgid = team->real_gid; 53 } else { 54 if (setAllIfPrivileged) { 55 // setgid() semantics: If privileged set both, real, effective and 56 // saved set-gid, otherwise set the effective gid. 57 if (privileged) { 58 team->saved_set_gid = rgid; 59 team->real_gid = rgid; 60 team->effective_gid = rgid; 61 return B_OK; 62 } 63 64 // not privileged -- set only the effective gid 65 egid = rgid; 66 rgid = team->real_gid; 67 } else { 68 // setregid() semantics: set the real gid, if allowed to 69 // Note: We allow setting the real gid to the effective gid. This 70 // is unspecified by the specs, but is common practice. 71 if (!privileged && rgid != team->real_gid 72 && rgid != team->effective_gid) { 73 return EPERM; 74 } 75 76 // Note: Also common practice is to set the saved set-gid when the 77 // real gid is set. 78 if (rgid != team->real_gid) 79 ssgid = rgid; 80 } 81 } 82 83 // effective gid 84 if (egid == (gid_t)-1) { 85 egid = team->effective_gid; 86 } else { 87 if (!privileged && egid != team->effective_gid 88 && egid != team->real_gid && egid != team->saved_set_gid) { 89 return EPERM; 90 } 91 } 92 93 // Getting here means all checks were successful -- set the gids. 94 team->real_gid = rgid; 95 team->effective_gid = egid; 96 team->saved_set_gid = ssgid; 97 98 return B_OK; 99} 100 101 102static status_t 103common_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged, bool kernel) 104{ 105 Team* team = thread_get_current_thread()->team; 106 107 TeamLocker teamLocker(team); 108 109 bool privileged = kernel || is_privileged(team); 110 111 uid_t ssuid = team->saved_set_uid; 112 113 // real uid 114 if (ruid == (uid_t)-1) { 115 ruid = team->real_uid; 116 } else { 117 if (setAllIfPrivileged) { 118 // setuid() semantics: If privileged set both, real, effective and 119 // saved set-uid, otherwise set the effective uid. 120 if (privileged) { 121 team->saved_set_uid = ruid; 122 team->real_uid = ruid; 123 team->effective_uid = ruid; 124 return B_OK; 125 } 126 127 // not privileged -- set only the effective uid 128 euid = ruid; 129 ruid = team->real_uid; 130 } else { 131 // setreuid() semantics: set the real uid, if allowed to 132 // Note: We allow setting the real uid to the effective uid. This 133 // is unspecified by the specs, but is common practice. 134 if (!privileged && ruid != team->real_uid 135 && ruid != team->effective_uid) { 136 return EPERM; 137 } 138 139 // Note: Also common practice is to set the saved set-uid when the 140 // real uid is set. 141 if (ruid != team->real_uid) 142 ssuid = ruid; 143 } 144 } 145 146 // effective uid 147 if (euid == (uid_t)-1) { 148 euid = team->effective_uid; 149 } else { 150 if (!privileged && euid != team->effective_uid 151 && euid != team->real_uid && euid != team->saved_set_uid) { 152 return EPERM; 153 } 154 } 155 156 // Getting here means all checks were successful -- set the uids. 157 team->real_uid = ruid; 158 team->effective_uid = euid; 159 team->saved_set_uid = ssuid; 160 161 return B_OK; 162} 163 164 165ssize_t 166common_getgroups(int groupCount, gid_t* groupList, bool kernel) 167{ 168 Team* team = thread_get_current_thread()->team; 169 170 TeamLocker teamLocker(team); 171 172 const gid_t* groups = NULL; 173 int actualCount = 0; 174 175 if (team->supplementary_groups != NULL) { 176 groups = team->supplementary_groups->groups; 177 actualCount = team->supplementary_groups->count; 178 } 179 180 // follow the specification and return always at least one group 181 if (actualCount == 0) { 182 groups = &team->effective_gid; 183 actualCount = 1; 184 } 185 186 // if groupCount 0 is supplied, we only return the number of groups 187 if (groupCount == 0) 188 return actualCount; 189 190 // check for sufficient space 191 if (groupCount < actualCount) 192 return B_BAD_VALUE; 193 194 // copy 195 if (kernel) { 196 memcpy(groupList, groups, actualCount * sizeof(gid_t)); 197 } else { 198 if (!IS_USER_ADDRESS(groupList) 199 || user_memcpy(groupList, groups, 200 actualCount * sizeof(gid_t)) != B_OK) { 201 return B_BAD_ADDRESS; 202 } 203 } 204 205 return actualCount; 206} 207 208 209static status_t 210common_setgroups(int groupCount, const gid_t* groupList, bool kernel) 211{ 212 if (groupCount < 0 || groupCount > NGROUPS_MAX) 213 return B_BAD_VALUE; 214 215 BKernel::GroupsArray* newGroups = NULL; 216 if (groupCount > 0) { 217 newGroups = (BKernel::GroupsArray*)malloc(sizeof(BKernel::GroupsArray) 218 + (sizeof(gid_t) * groupCount)); 219 if (newGroups == NULL) 220 return B_NO_MEMORY; 221 new(newGroups) BKernel::GroupsArray; 222 223 if (kernel) { 224 memcpy(newGroups->groups, groupList, sizeof(gid_t) * groupCount); 225 } else { 226 if (!IS_USER_ADDRESS(groupList) 227 || user_memcpy(newGroups->groups, groupList, 228 sizeof(gid_t) * groupCount) != B_OK) { 229 delete newGroups; 230 return B_BAD_ADDRESS; 231 } 232 } 233 newGroups->count = groupCount; 234 } 235 236 Team* team = thread_get_current_thread()->team; 237 TeamLocker teamLocker(team); 238 239 BReference<BKernel::GroupsArray> previous = team->supplementary_groups; 240 // so it will not be (potentially) destroyed until after we unlock 241 team->supplementary_groups.SetTo(newGroups, true); 242 243 teamLocker.Unlock(); 244 245 return B_OK; 246} 247 248 249// #pragma mark - Kernel Private 250 251 252/*! Copies the user and group information from \a parent to \a team. 253 The caller must hold the lock to both \a team and \a parent. 254*/ 255void 256inherit_parent_user_and_group(Team* team, Team* parent) 257{ 258 team->saved_set_uid = parent->saved_set_uid; 259 team->real_uid = parent->real_uid; 260 team->effective_uid = parent->effective_uid; 261 team->saved_set_gid = parent->saved_set_gid; 262 team->real_gid = parent->real_gid; 263 team->effective_gid = parent->effective_gid; 264 team->supplementary_groups = parent->supplementary_groups; 265} 266 267 268status_t 269update_set_id_user_and_group(Team* team, const char* file) 270{ 271 struct stat st; 272 status_t status = vfs_read_stat(-1, file, true, &st, false); 273 if (status != B_OK) 274 return status; 275 276 TeamLocker teamLocker(team); 277 278 if ((st.st_mode & S_ISUID) != 0) { 279 team->saved_set_uid = st.st_uid; 280 team->effective_uid = st.st_uid; 281 } 282 283 if ((st.st_mode & S_ISGID) != 0) { 284 team->saved_set_gid = st.st_gid; 285 team->effective_gid = st.st_gid; 286 } 287 288 return B_OK; 289} 290 291 292gid_t 293_kern_getgid(bool effective) 294{ 295 Team* team = thread_get_current_thread()->team; 296 297 return effective ? team->effective_gid : team->real_gid; 298} 299 300 301uid_t 302_kern_getuid(bool effective) 303{ 304 Team* team = thread_get_current_thread()->team; 305 306 return effective ? team->effective_uid : team->real_uid; 307} 308 309 310status_t 311_kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 312{ 313 return common_setregid(rgid, egid, setAllIfPrivileged, true); 314} 315 316 317status_t 318_kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 319{ 320 return common_setreuid(ruid, euid, setAllIfPrivileged, true); 321} 322 323 324ssize_t 325_kern_getgroups(int groupCount, gid_t* groupList) 326{ 327 return common_getgroups(groupCount, groupList, true); 328} 329 330 331status_t 332_kern_setgroups(int groupCount, const gid_t* groupList) 333{ 334 return common_setgroups(groupCount, groupList, true); 335} 336 337 338// #pragma mark - Syscalls 339 340 341gid_t 342_user_getgid(bool effective) 343{ 344 Team* team = thread_get_current_thread()->team; 345 346 return effective ? team->effective_gid : team->real_gid; 347} 348 349 350uid_t 351_user_getuid(bool effective) 352{ 353 Team* team = thread_get_current_thread()->team; 354 355 return effective ? team->effective_uid : team->real_uid; 356} 357 358 359status_t 360_user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 361{ 362 return common_setregid(rgid, egid, setAllIfPrivileged, false); 363} 364 365 366status_t 367_user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 368{ 369 return common_setreuid(ruid, euid, setAllIfPrivileged, false); 370} 371 372 373ssize_t 374_user_getgroups(int groupCount, gid_t* groupList) 375{ 376 return common_getgroups(groupCount, groupList, false); 377} 378 379 380ssize_t 381_user_setgroups(int groupCount, const gid_t* groupList) 382{ 383 // check privilege 384 Team* team = thread_get_current_thread()->team; 385 if (!is_privileged(team)) 386 return EPERM; 387 388 return common_setgroups(groupCount, groupList, false); 389} 390