btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / usergroup.cpp
blob9be52f5a0c45b250bf9097a8ee889f6cda06c5c7
1 /*
2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <usergroup.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <sys/stat.h>
13 #include <new>
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>
24 #include <AutoDeleter.h>
27 // #pragma mark - Implementation Private
30 static bool
31 is_privileged(Team* team)
33 // currently only the root user is privileged
34 return team->effective_uid == 0;
38 static status_t
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;
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;
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;
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;
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;
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;
97 return B_OK;
101 static status_t
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;
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;
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;
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;
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;
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;
160 return B_OK;
164 ssize_t
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;
177 actualCount = 1;
180 // if groupCount 0 is supplied, we only return the number of groups
181 if (groupCount == 0)
182 return actualCount;
184 // check for sufficient space
185 if (groupCount < actualCount)
186 return B_BAD_VALUE;
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;
199 return actualCount;
203 static status_t
204 common_setgroups(int groupCount, const gid_t* groupList, bool kernel)
206 if (groupCount < 0 || groupCount > NGROUPS_MAX)
207 return B_BAD_VALUE;
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;
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 free(newGroups);
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;
235 teamLocker.Unlock();
237 malloc_referenced_release(toFree);
239 return B_OK;
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.
249 void
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;
265 status_t
266 update_set_id_user_and_group(Team* team, const char* file)
268 struct stat st;
269 status_t status = vfs_read_stat(-1, file, true, &st, false);
270 if (status != B_OK)
271 return status;
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;
285 return B_OK;
289 gid_t
290 _kern_getgid(bool effective)
292 Team* team = thread_get_current_thread()->team;
294 return effective ? team->effective_gid : team->real_gid;
298 uid_t
299 _kern_getuid(bool effective)
301 Team* team = thread_get_current_thread()->team;
303 return effective ? team->effective_uid : team->real_uid;
307 status_t
308 _kern_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
310 return common_setregid(rgid, egid, setAllIfPrivileged, true);
314 status_t
315 _kern_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
317 return common_setreuid(ruid, euid, setAllIfPrivileged, true);
321 ssize_t
322 _kern_getgroups(int groupCount, gid_t* groupList)
324 return common_getgroups(groupCount, groupList, true);
328 status_t
329 _kern_setgroups(int groupCount, const gid_t* groupList)
331 return common_setgroups(groupCount, groupList, true);
335 // #pragma mark - Syscalls
338 gid_t
339 _user_getgid(bool effective)
341 Team* team = thread_get_current_thread()->team;
343 return effective ? team->effective_gid : team->real_gid;
347 uid_t
348 _user_getuid(bool effective)
350 Team* team = thread_get_current_thread()->team;
352 return effective ? team->effective_uid : team->real_uid;
356 status_t
357 _user_setregid(gid_t rgid, gid_t egid, bool setAllIfPrivileged)
359 return common_setregid(rgid, egid, setAllIfPrivileged, false);
363 status_t
364 _user_setreuid(uid_t ruid, uid_t euid, bool setAllIfPrivileged)
366 return common_setreuid(ruid, euid, setAllIfPrivileged, false);
370 ssize_t
371 _user_getgroups(int groupCount, gid_t* groupList)
373 return common_getgroups(groupCount, groupList, false);
377 ssize_t
378 _user_setgroups(int groupCount, const gid_t* groupList)
380 // check privilege
381 Team* team = thread_get_current_thread()->team;
382 if (!is_privileged(team))
383 return EPERM;
385 return common_setgroups(groupCount, groupList, false);