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(struct team* team, struct team* parent) 247 { 248 InterruptsSpinLocker _(gTeamSpinlock); 249 250 team->saved_set_uid = parent->saved_set_uid; 251 team->real_uid = parent->real_uid; 252 team->effective_uid = parent->effective_uid; 253 team->saved_set_gid = parent->saved_set_gid; 254 team->real_gid = parent->real_gid; 255 team->effective_gid = parent->effective_gid; 256 257 malloc_referenced_acquire(parent->supplementary_groups); 258 team->supplementary_groups = parent->supplementary_groups; 259 team->supplementary_group_count = parent->supplementary_group_count; 260 } 261 262 263 status_t 264 update_set_id_user_and_group(struct team* team, const char* file) 265 { 266 struct stat st; 267 status_t status = vfs_read_stat(-1, file, true, &st, false); 268 if (status != B_OK) 269 return status; 270 271 InterruptsSpinLocker _(gTeamSpinlock); 272 273 if ((st.st_mode & S_ISUID) != 0) { 274 team->saved_set_uid = st.st_uid; 275 team->effective_uid = st.st_uid; 276 } 277 278 if ((st.st_mode & S_ISGID) != 0) { 279 team->saved_set_gid = st.st_gid; 280 team->effective_gid = st.st_gid; 281 } 282 283 return B_OK; 284 } 285 286 287 gid_t 288 _kern_getgid(bool effective) 289 { 290 struct team* team = thread_get_current_thread()->team; 291 292 return effective ? team->effective_gid : team->real_gid; 293 } 294 295 296 uid_t 297 _kern_getuid(bool effective) 298 { 299 struct team* team = thread_get_current_thread()->team; 300 301 return effective ? team->effective_uid : team->real_uid; 302 } 303 304 305 status_t 306 _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 307 { 308 return common_setregid(rgid, egid, setAllIfPrivileged, true); 309 } 310 311 312 status_t 313 _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 314 { 315 return common_setreuid(ruid, euid, setAllIfPrivileged, true); 316 } 317 318 319 ssize_t 320 _kern_getgroups(int groupCount, gid_t* groupList) 321 { 322 return common_getgroups(groupCount, groupList, true); 323 } 324 325 326 status_t 327 _kern_setgroups(int groupCount, const gid_t* groupList) 328 { 329 return common_setgroups(groupCount, groupList, true); 330 } 331 332 333 // #pragma mark - Syscalls 334 335 336 gid_t 337 _user_getgid(bool effective) 338 { 339 struct team* team = thread_get_current_thread()->team; 340 341 return effective ? team->effective_gid : team->real_gid; 342 } 343 344 345 uid_t 346 _user_getuid(bool effective) 347 { 348 struct team* team = thread_get_current_thread()->team; 349 350 return effective ? team->effective_uid : team->real_uid; 351 } 352 353 354 status_t 355 _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged) 356 { 357 return common_setregid(rgid, egid, setAllIfPrivileged, false); 358 } 359 360 361 status_t 362 _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged) 363 { 364 return common_setreuid(ruid, euid, setAllIfPrivileged, false); 365 } 366 367 368 ssize_t 369 _user_getgroups(int groupCount, gid_t* groupList) 370 { 371 return common_getgroups(groupCount, groupList, false); 372 } 373 374 375 ssize_t 376 _user_setgroups(int groupCount, const gid_t* groupList) 377 { 378 if (!is_privileged(thread_get_current_thread()->team)) 379 return EPERM; 380 381 return common_setgroups(groupCount, groupList, false); 382 } 383