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
is_privileged(Team * team)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
common_setregid(gid_t rgid,gid_t egid,bool setAllIfPrivileged,bool kernel)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
common_setreuid(uid_t ruid,uid_t euid,bool setAllIfPrivileged,bool kernel)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
common_getgroups(int groupCount,gid_t * groupList,bool kernel)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
common_setgroups(int groupCount,const gid_t * groupList,bool kernel)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, sizeof(gid_t) * groupCount) != B_OK) {
228 free(newGroups);
229 return B_BAD_ADDRESS;
230 }
231 }
232 newGroups->count = groupCount;
233 }
234
235 Team* team = thread_get_current_thread()->team;
236 TeamLocker teamLocker(team);
237
238 BReference<BKernel::GroupsArray> previous = team->supplementary_groups;
239 // so it will not be (potentially) destroyed until after we unlock
240 team->supplementary_groups.SetTo(newGroups, true);
241
242 teamLocker.Unlock();
243
244 return B_OK;
245 }
246
247
248 // #pragma mark - Kernel Private
249
250
251 /*! Copies the user and group information from \a parent to \a team.
252 The caller must hold the lock to both \a team and \a parent.
253 */
254 void
inherit_parent_user_and_group(Team * team,Team * parent)255 inherit_parent_user_and_group(Team* team, Team* parent)
256 {
257 team->saved_set_uid = parent->saved_set_uid;
258 team->real_uid = parent->real_uid;
259 team->effective_uid = parent->effective_uid;
260 team->saved_set_gid = parent->saved_set_gid;
261 team->real_gid = parent->real_gid;
262 team->effective_gid = parent->effective_gid;
263 team->supplementary_groups = parent->supplementary_groups;
264 }
265
266
267 status_t
update_set_id_user_and_group(Team * team,const char * file)268 update_set_id_user_and_group(Team* team, const char* file)
269 {
270 struct stat st;
271 status_t status = vfs_read_stat(-1, file, true, &st, false);
272 if (status != B_OK)
273 return status;
274
275 TeamLocker teamLocker(team);
276
277 if ((st.st_mode & S_ISUID) != 0) {
278 team->saved_set_uid = st.st_uid;
279 team->effective_uid = st.st_uid;
280 }
281
282 if ((st.st_mode & S_ISGID) != 0) {
283 team->saved_set_gid = st.st_gid;
284 team->effective_gid = st.st_gid;
285 }
286
287 return B_OK;
288 }
289
290
291 gid_t
_kern_getgid(bool effective)292 _kern_getgid(bool effective)
293 {
294 Team* team = thread_get_current_thread()->team;
295
296 return effective ? team->effective_gid : team->real_gid;
297 }
298
299
300 uid_t
_kern_getuid(bool effective)301 _kern_getuid(bool effective)
302 {
303 Team* team = thread_get_current_thread()->team;
304
305 return effective ? team->effective_uid : team->real_uid;
306 }
307
308
309 status_t
_kern_setregid(gid_t rgid,gid_t egid,bool setAllIfPrivileged)310 _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
311 {
312 return common_setregid(rgid, egid, setAllIfPrivileged, true);
313 }
314
315
316 status_t
_kern_setreuid(uid_t ruid,uid_t euid,bool setAllIfPrivileged)317 _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
318 {
319 return common_setreuid(ruid, euid, setAllIfPrivileged, true);
320 }
321
322
323 ssize_t
_kern_getgroups(int groupCount,gid_t * groupList)324 _kern_getgroups(int groupCount, gid_t* groupList)
325 {
326 return common_getgroups(groupCount, groupList, true);
327 }
328
329
330 status_t
_kern_setgroups(int groupCount,const gid_t * groupList)331 _kern_setgroups(int groupCount, const gid_t* groupList)
332 {
333 return common_setgroups(groupCount, groupList, true);
334 }
335
336
337 // #pragma mark - Syscalls
338
339
340 gid_t
_user_getgid(bool effective)341 _user_getgid(bool effective)
342 {
343 Team* team = thread_get_current_thread()->team;
344
345 return effective ? team->effective_gid : team->real_gid;
346 }
347
348
349 uid_t
_user_getuid(bool effective)350 _user_getuid(bool effective)
351 {
352 Team* team = thread_get_current_thread()->team;
353
354 return effective ? team->effective_uid : team->real_uid;
355 }
356
357
358 status_t
_user_setregid(gid_t rgid,gid_t egid,bool setAllIfPrivileged)359 _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
360 {
361 return common_setregid(rgid, egid, setAllIfPrivileged, false);
362 }
363
364
365 status_t
_user_setreuid(uid_t ruid,uid_t euid,bool setAllIfPrivileged)366 _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
367 {
368 return common_setreuid(ruid, euid, setAllIfPrivileged, false);
369 }
370
371
372 ssize_t
_user_getgroups(int groupCount,gid_t * groupList)373 _user_getgroups(int groupCount, gid_t* groupList)
374 {
375 return common_getgroups(groupCount, groupList, false);
376 }
377
378
379 ssize_t
_user_setgroups(int groupCount,const gid_t * groupList)380 _user_setgroups(int groupCount, const gid_t* groupList)
381 {
382 // check privilege
383 Team* team = thread_get_current_thread()->team;
384 if (!is_privileged(team))
385 return EPERM;
386
387 return common_setgroups(groupCount, groupList, false);
388 }
389