2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
20 #include <thread_types.h>
21 #include <util/AutoLock.h>
24 #include <AutoDeleter.h>
27 // #pragma mark - Implementation Private
31 is_privileged(Team
* team
)
33 // currently only the root user is privileged
34 return team
->effective_uid
== 0;
39 common_setregid(gid_t rgid
, gid_t egid
, bool setAllIfPrivileged
, bool kernel
)
41 Team
* team
= thread_get_current_thread()->team
;
43 TeamLocker
teamLocker(team
);
45 bool privileged
= kernel
|| is_privileged(team
);
47 gid_t ssgid
= team
->saved_set_gid
;
50 if (rgid
== (gid_t
)-1) {
51 rgid
= team
->real_gid
;
53 if (setAllIfPrivileged
) {
54 // setgid() semantics: If privileged set both, real, effective and
55 // saved set-gid, otherwise set the effective gid.
57 team
->saved_set_gid
= rgid
;
58 team
->real_gid
= rgid
;
59 team
->effective_gid
= rgid
;
63 // not privileged -- set only the effective gid
65 rgid
= team
->real_gid
;
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
) {
75 // Note: Also common practice is to set the saved set-gid when the
77 if (rgid
!= team
->real_gid
)
83 if (egid
== (gid_t
)-1) {
84 egid
= team
->effective_gid
;
86 if (!privileged
&& egid
!= team
->effective_gid
87 && egid
!= team
->real_gid
&& egid
!= team
->saved_set_gid
) {
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
;
102 common_setreuid(uid_t ruid
, uid_t euid
, bool setAllIfPrivileged
, bool kernel
)
104 Team
* team
= thread_get_current_thread()->team
;
106 TeamLocker
teamLocker(team
);
108 bool privileged
= kernel
|| is_privileged(team
);
110 uid_t ssuid
= team
->saved_set_uid
;
113 if (ruid
== (uid_t
)-1) {
114 ruid
= team
->real_uid
;
116 if (setAllIfPrivileged
) {
117 // setuid() semantics: If privileged set both, real, effective and
118 // saved set-uid, otherwise set the effective uid.
120 team
->saved_set_uid
= ruid
;
121 team
->real_uid
= ruid
;
122 team
->effective_uid
= ruid
;
126 // not privileged -- set only the effective uid
128 ruid
= team
->real_uid
;
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
) {
138 // Note: Also common practice is to set the saved set-uid when the
140 if (ruid
!= team
->real_uid
)
146 if (euid
== (uid_t
)-1) {
147 euid
= team
->effective_uid
;
149 if (!privileged
&& euid
!= team
->effective_uid
150 && euid
!= team
->real_uid
&& euid
!= team
->saved_set_uid
) {
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
;
165 common_getgroups(int groupCount
, gid_t
* groupList
, bool kernel
)
167 Team
* team
= thread_get_current_thread()->team
;
169 TeamLocker
teamLocker(team
);
171 const gid_t
* groups
= team
->supplementary_groups
;
172 int actualCount
= team
->supplementary_group_count
;
174 // follow the specification and return always at least one group
175 if (actualCount
== 0) {
176 groups
= &team
->effective_gid
;
180 // if groupCount 0 is supplied, we only return the number of groups
184 // check for sufficient space
185 if (groupCount
< actualCount
)
190 memcpy(groupList
, groups
, actualCount
* sizeof(gid_t
));
192 if (!IS_USER_ADDRESS(groupList
)
193 || user_memcpy(groupList
, groups
,
194 actualCount
* sizeof(gid_t
)) != B_OK
) {
195 return B_BAD_ADDRESS
;
204 common_setgroups(int groupCount
, const gid_t
* groupList
, bool kernel
)
206 if (groupCount
< 0 || groupCount
> NGROUPS_MAX
)
209 gid_t
* newGroups
= NULL
;
210 if (groupCount
> 0) {
211 newGroups
= (gid_t
*)malloc_referenced(sizeof(gid_t
) * groupCount
);
212 if (newGroups
== NULL
)
216 memcpy(newGroups
, groupList
, sizeof(gid_t
) * groupCount
);
218 if (!IS_USER_ADDRESS(groupList
)
219 || user_memcpy(newGroups
, groupList
,
220 sizeof(gid_t
) * groupCount
) != B_OK
) {
222 return B_BAD_ADDRESS
;
227 Team
* team
= thread_get_current_thread()->team
;
229 TeamLocker
teamLocker(team
);
231 gid_t
* toFree
= team
->supplementary_groups
;
232 team
->supplementary_groups
= newGroups
;
233 team
->supplementary_group_count
= groupCount
;
237 malloc_referenced_release(toFree
);
243 // #pragma mark - Kernel Private
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.
250 inherit_parent_user_and_group(Team
* team
, Team
* parent
)
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
;
259 malloc_referenced_acquire(parent
->supplementary_groups
);
260 team
->supplementary_groups
= parent
->supplementary_groups
;
261 team
->supplementary_group_count
= parent
->supplementary_group_count
;
266 update_set_id_user_and_group(Team
* team
, const char* file
)
269 status_t status
= vfs_read_stat(-1, file
, true, &st
, false);
273 TeamLocker
teamLocker(team
);
275 if ((st
.st_mode
& S_ISUID
) != 0) {
276 team
->saved_set_uid
= st
.st_uid
;
277 team
->effective_uid
= st
.st_uid
;
280 if ((st
.st_mode
& S_ISGID
) != 0) {
281 team
->saved_set_gid
= st
.st_gid
;
282 team
->effective_gid
= st
.st_gid
;
290 _kern_getgid(bool effective
)
292 Team
* team
= thread_get_current_thread()->team
;
294 return effective
? team
->effective_gid
: team
->real_gid
;
299 _kern_getuid(bool effective
)
301 Team
* team
= thread_get_current_thread()->team
;
303 return effective
? team
->effective_uid
: team
->real_uid
;
308 _kern_setregid(gid_t rgid
, gid_t egid
, bool setAllIfPrivileged
)
310 return common_setregid(rgid
, egid
, setAllIfPrivileged
, true);
315 _kern_setreuid(uid_t ruid
, uid_t euid
, bool setAllIfPrivileged
)
317 return common_setreuid(ruid
, euid
, setAllIfPrivileged
, true);
322 _kern_getgroups(int groupCount
, gid_t
* groupList
)
324 return common_getgroups(groupCount
, groupList
, true);
329 _kern_setgroups(int groupCount
, const gid_t
* groupList
)
331 return common_setgroups(groupCount
, groupList
, true);
335 // #pragma mark - Syscalls
339 _user_getgid(bool effective
)
341 Team
* team
= thread_get_current_thread()->team
;
343 return effective
? team
->effective_gid
: team
->real_gid
;
348 _user_getuid(bool effective
)
350 Team
* team
= thread_get_current_thread()->team
;
352 return effective
? team
->effective_uid
: team
->real_uid
;
357 _user_setregid(gid_t rgid
, gid_t egid
, bool setAllIfPrivileged
)
359 return common_setregid(rgid
, egid
, setAllIfPrivileged
, false);
364 _user_setreuid(uid_t ruid
, uid_t euid
, bool setAllIfPrivileged
)
366 return common_setreuid(ruid
, euid
, setAllIfPrivileged
, false);
371 _user_getgroups(int groupCount
, gid_t
* groupList
)
373 return common_getgroups(groupCount
, groupList
, false);
378 _user_setgroups(int groupCount
, const gid_t
* groupList
)
381 Team
* team
= thread_get_current_thread()->team
;
382 if (!is_privileged(team
))
385 return common_setgroups(groupCount
, groupList
, false);