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