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