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 = team->supplementary_groups; 173 int actualCount = team->supplementary_group_count; 174 175 // follow the specification and return always at least one group 176 if (actualCount == 0) { 177 groups = &team->effective_gid; 178 actualCount = 1; 179 } 180 181 // if groupCount 0 is supplied, we only return the number of groups 182 if (groupCount == 0) 183 return actualCount; 184 185 // check for sufficient space 186 if (groupCount < actualCount) 187 return B_BAD_VALUE; 188 189 // copy 190 if (kernel) { 191 memcpy(groupList, groups, actualCount * sizeof(gid_t)); 192 } else { 193 if (!IS_USER_ADDRESS(groupList) 194 || user_memcpy(groupList, groups, 195 actualCount * sizeof(gid_t)) != B_OK) { 196 return B_BAD_ADDRESS; 197 } 198 } 199 200 return actualCount; 201 } 202 203 204 static status_t 205 common_setgroups(int groupCount, const gid_t* groupList, bool kernel) 206 { 207 if (groupCount < 0 || groupCount > NGROUPS_MAX) 208 return B_BAD_VALUE; 209 210 gid_t* newGroups = NULL; 211 if (groupCount > 0) { 212 newGroups = (gid_t*)malloc_referenced(sizeof(gid_t) * groupCount); 213 if (newGroups == NULL) 214 return B_NO_MEMORY; 215 216 if (kernel) { 217 memcpy(newGroups, groupList, sizeof(gid_t) * groupCount); 218 } else { 219 if (!IS_USER_ADDRESS(groupList) 220 || user_memcpy(newGroups, groupList, 221 sizeof(gid_t) * groupCount) != B_OK) { 222 malloc_referenced_release(newGroups); 223 return B_BAD_ADDRESS; 224 } 225 } 226 } 227 228 Team* team = thread_get_current_thread()->team; 229 230 TeamLocker teamLocker(team); 231 232 gid_t* toFree = team->supplementary_groups; 233 team->supplementary_groups = newGroups; 234 team->supplementary_group_count = groupCount; 235 236 teamLocker.Unlock(); 237 238 malloc_referenced_release(toFree); 239 240 return B_OK; 241 } 242 243 244 // #pragma mark - Kernel Private 245 246 247 /*! Copies the user and group information from \a parent to \a team. 248 The caller must hold the lock to both \a team and \a parent. 249 */ 250 void 251 inherit_parent_user_and_group(Team* team, Team* parent) 252 { 253 team->saved_set_uid = parent->saved_set_uid; 254 team->real_uid = parent->real_uid; 255 team->effective_uid = parent->effective_uid; 256 team->saved_set_gid = parent->saved_set_gid; 257 team->real_gid = parent->real_gid; 258 team->effective_gid = parent->effective_gid; 259 260 malloc_referenced_acquire(parent->supplementary_groups); 261 team->supplementary_groups = parent->supplementary_groups; 262 team->supplementary_group_count = parent->supplementary_group_count; 263 } 264 265 266 status_t 267 update_set_id_user_and_group(Team* team, const char* file) 268 { 269 struct stat st; 270 status_t status = vfs_read_stat(-1, file, true, &st, false); 271 if (status != B_OK) 272 return status; 273 274 TeamLocker teamLocker(team); 275 276 if ((st.st_mode & S_ISUID) != 0) { 277 team->saved_set_uid = st.st_uid; 278 team->effective_uid = st.st_uid; 279 } 280 281 if ((st.st_mode & S_ISGID) != 0) { 282 team->saved_set_gid = st.st_gid; 283 team->effective_gid = st.st_gid; 284 } 285 286 return B_OK; 287 } 288 289 290 gid_t 291 _kern_getgid(bool effective) 292 { 293 Team* team = thread_get_current_thread()->team; 294 295 return effective ? team->effective_gid : team->real_gid; 296 } 297 298 299 uid_t 300 _kern_getuid(bool effective) 301 { 302 Team* team = thread_get_current_thread()->team; 303 304 return effective ? team->effective_uid : team->real_uid; 305 } 306 307 308 status_t 309 _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 310 { 311 return common_setregid(rgid, egid, setAllIfPrivileged, true); 312 } 313 314 315 status_t 316 _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 317 { 318 return common_setreuid(ruid, euid, setAllIfPrivileged, true); 319 } 320 321 322 ssize_t 323 _kern_getgroups(int groupCount, gid_t* groupList) 324 { 325 return common_getgroups(groupCount, groupList, true); 326 } 327 328 329 status_t 330 _kern_setgroups(int groupCount, const gid_t* groupList) 331 { 332 return common_setgroups(groupCount, groupList, true); 333 } 334 335 336 // #pragma mark - Syscalls 337 338 339 gid_t 340 _user_getgid(bool effective) 341 { 342 Team* team = thread_get_current_thread()->team; 343 344 return effective ? team->effective_gid : team->real_gid; 345 } 346 347 348 uid_t 349 _user_getuid(bool effective) 350 { 351 Team* team = thread_get_current_thread()->team; 352 353 return effective ? team->effective_uid : team->real_uid; 354 } 355 356 357 status_t 358 _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 359 { 360 return common_setregid(rgid, egid, setAllIfPrivileged, false); 361 } 362 363 364 status_t 365 _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 366 { 367 return common_setreuid(ruid, euid, setAllIfPrivileged, false); 368 } 369 370 371 ssize_t 372 _user_getgroups(int groupCount, gid_t* groupList) 373 { 374 return common_getgroups(groupCount, groupList, false); 375 } 376 377 378 ssize_t 379 _user_setgroups(int groupCount, const gid_t* groupList) 380 { 381 // check privilege 382 Team* team = thread_get_current_thread()->team; 383 if (!is_privileged(team)) 384 return EPERM; 385 386 return common_setgroups(groupCount, groupList, false); 387 } 388