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 31 static bool 32 is_privileged(Team* team) 33 { 34 // currently only the root user is privileged 35 return team->effective_uid == 0; 36 } 37 38 39 static status_t 40 common_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 102 static status_t 103 common_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 165 ssize_t 166 common_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 209 static status_t 210 common_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 */ 255 void 256 inherit_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 268 status_t 269 update_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 292 gid_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 301 uid_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 310 status_t 311 _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 312 { 313 return common_setregid(rgid, egid, setAllIfPrivileged, true); 314 } 315 316 317 status_t 318 _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 319 { 320 return common_setreuid(ruid, euid, setAllIfPrivileged, true); 321 } 322 323 324 ssize_t 325 _kern_getgroups(int groupCount, gid_t* groupList) 326 { 327 return common_getgroups(groupCount, groupList, true); 328 } 329 330 331 status_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 341 gid_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 350 uid_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 359 status_t 360 _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 361 { 362 return common_setregid(rgid, egid, setAllIfPrivileged, false); 363 } 364 365 366 status_t 367 _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 368 { 369 return common_setreuid(ruid, euid, setAllIfPrivileged, false); 370 } 371 372 373 ssize_t 374 _user_getgroups(int groupCount, gid_t* groupList) 375 { 376 return common_getgroups(groupCount, groupList, false); 377 } 378 379 380 ssize_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