xref: /haiku/src/system/kernel/usergroup.cpp (revision 2222d0559df303a9846a2fad53741f8b20b14d7c)
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