Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <Kernel/API/POSIX/sys/limits.h>
8#include <Kernel/Process.h>
9
10namespace Kernel {
11
12ErrorOr<FlatPtr> Process::sys$seteuid(UserID new_euid)
13{
14 VERIFY_NO_PROCESS_BIG_LOCK(this);
15 TRY(require_promise(Pledge::id));
16
17 if (new_euid == (uid_t)-1)
18 return EINVAL;
19
20 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
21 auto credentials = this->credentials();
22
23 if (new_euid != credentials->uid() && new_euid != credentials->suid() && !credentials->is_superuser())
24 return EPERM;
25
26 auto new_credentials = TRY(Credentials::create(
27 credentials->uid(),
28 credentials->gid(),
29 new_euid,
30 credentials->egid(),
31 credentials->suid(),
32 credentials->sgid(),
33 credentials->extra_gids(),
34 credentials->sid(),
35 credentials->pgid()));
36
37 if (credentials->euid() != new_euid)
38 protected_data.dumpable = false;
39
40 protected_data.credentials = move(new_credentials);
41 return 0;
42 });
43}
44
45ErrorOr<FlatPtr> Process::sys$setegid(GroupID new_egid)
46{
47 VERIFY_NO_PROCESS_BIG_LOCK(this);
48 TRY(require_promise(Pledge::id));
49
50 if (new_egid == (uid_t)-1)
51 return EINVAL;
52
53 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
54 auto credentials = this->credentials();
55
56 if (new_egid != credentials->gid() && new_egid != credentials->sgid() && !credentials->is_superuser())
57 return EPERM;
58
59 auto new_credentials = TRY(Credentials::create(
60 credentials->uid(),
61 credentials->gid(),
62 credentials->euid(),
63 new_egid,
64 credentials->suid(),
65 credentials->sgid(),
66 credentials->extra_gids(),
67 credentials->sid(),
68 credentials->pgid()));
69
70 if (credentials->egid() != new_egid)
71 protected_data.dumpable = false;
72
73 protected_data.credentials = move(new_credentials);
74 return 0;
75 });
76}
77
78ErrorOr<FlatPtr> Process::sys$setuid(UserID new_uid)
79{
80 VERIFY_NO_PROCESS_BIG_LOCK(this);
81 TRY(require_promise(Pledge::id));
82
83 if (new_uid == (uid_t)-1)
84 return EINVAL;
85
86 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
87 auto credentials = this->credentials();
88
89 if (new_uid != credentials->uid() && new_uid != credentials->euid() && !credentials->is_superuser())
90 return EPERM;
91
92 auto new_credentials = TRY(Credentials::create(
93 new_uid,
94 credentials->gid(),
95 new_uid,
96 credentials->egid(),
97 new_uid,
98 credentials->sgid(),
99 credentials->extra_gids(),
100 credentials->sid(),
101 credentials->pgid()));
102
103 if (credentials->euid() != new_uid)
104 protected_data.dumpable = false;
105
106 protected_data.credentials = move(new_credentials);
107 return 0;
108 });
109}
110
111ErrorOr<FlatPtr> Process::sys$setgid(GroupID new_gid)
112{
113 VERIFY_NO_PROCESS_BIG_LOCK(this);
114 TRY(require_promise(Pledge::id));
115
116 if (new_gid == (uid_t)-1)
117 return EINVAL;
118
119 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
120 auto credentials = this->credentials();
121
122 if (new_gid != credentials->gid() && new_gid != credentials->egid() && !credentials->is_superuser())
123 return EPERM;
124
125 auto new_credentials = TRY(Credentials::create(
126 credentials->uid(),
127 new_gid,
128 credentials->euid(),
129 new_gid,
130 credentials->suid(),
131 new_gid,
132 credentials->extra_gids(),
133 credentials->sid(),
134 credentials->pgid()));
135
136 if (credentials->egid() != new_gid)
137 protected_data.dumpable = false;
138
139 protected_data.credentials = move(new_credentials);
140 return 0;
141 });
142}
143
144ErrorOr<FlatPtr> Process::sys$setreuid(UserID new_ruid, UserID new_euid)
145{
146 VERIFY_NO_PROCESS_BIG_LOCK(this);
147 TRY(require_promise(Pledge::id));
148
149 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
150 auto credentials = this->credentials();
151
152 if (new_ruid == (uid_t)-1)
153 new_ruid = credentials->uid();
154 if (new_euid == (uid_t)-1)
155 new_euid = credentials->euid();
156
157 auto ok = [&credentials](UserID id) { return id == credentials->uid() || id == credentials->euid() || id == credentials->suid(); };
158 if (!ok(new_ruid) || !ok(new_euid))
159 return EPERM;
160
161 if (new_ruid < (uid_t)-1 || new_euid < (uid_t)-1)
162 return EINVAL;
163
164 auto new_credentials = TRY(Credentials::create(
165 new_ruid,
166 credentials->gid(),
167 new_euid,
168 credentials->egid(),
169 credentials->suid(),
170 credentials->sgid(),
171 credentials->extra_gids(),
172 credentials->sid(),
173 credentials->pgid()));
174
175 if (credentials->euid() != new_euid)
176 protected_data.dumpable = false;
177
178 protected_data.credentials = move(new_credentials);
179 return 0;
180 });
181}
182
183ErrorOr<FlatPtr> Process::sys$setresuid(UserID new_ruid, UserID new_euid, UserID new_suid)
184{
185 VERIFY_NO_PROCESS_BIG_LOCK(this);
186 TRY(require_promise(Pledge::id));
187
188 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
189 auto credentials = this->credentials();
190
191 if (new_ruid == (uid_t)-1)
192 new_ruid = credentials->uid();
193 if (new_euid == (uid_t)-1)
194 new_euid = credentials->euid();
195 if (new_suid == (uid_t)-1)
196 new_suid = credentials->suid();
197
198 auto ok = [&credentials](UserID id) { return id == credentials->uid() || id == credentials->euid() || id == credentials->suid(); };
199 if ((!ok(new_ruid) || !ok(new_euid) || !ok(new_suid)) && !credentials->is_superuser())
200 return EPERM;
201
202 auto new_credentials = TRY(Credentials::create(
203 new_ruid,
204 credentials->gid(),
205 new_euid,
206 credentials->egid(),
207 new_suid,
208 credentials->sgid(),
209 credentials->extra_gids(),
210 credentials->sid(),
211 credentials->pgid()));
212
213 if (credentials->euid() != new_euid)
214 protected_data.dumpable = false;
215
216 protected_data.credentials = move(new_credentials);
217 return 0;
218 });
219}
220
221ErrorOr<FlatPtr> Process::sys$setregid(GroupID new_rgid, GroupID new_egid)
222{
223 VERIFY_NO_PROCESS_BIG_LOCK(this);
224 TRY(require_promise(Pledge::id));
225
226 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
227 auto credentials = this->credentials();
228
229 if (new_rgid == (gid_t)-1)
230 new_rgid = credentials->gid();
231 if (new_egid == (gid_t)-1)
232 new_egid = credentials->egid();
233
234 auto ok = [&credentials](GroupID id) { return id == credentials->gid() || id == credentials->egid() || id == credentials->sgid(); };
235 if (!ok(new_rgid) || !ok(new_egid))
236 return EPERM;
237
238 auto new_credentials = TRY(Credentials::create(
239 credentials->uid(),
240 new_rgid,
241 credentials->euid(),
242 new_egid,
243 credentials->suid(),
244 credentials->sgid(),
245 credentials->extra_gids(),
246 credentials->sid(),
247 credentials->pgid()));
248
249 if (credentials->egid() != new_egid)
250 protected_data.dumpable = false;
251
252 protected_data.credentials = move(new_credentials);
253 return 0;
254 });
255}
256
257ErrorOr<FlatPtr> Process::sys$setresgid(GroupID new_rgid, GroupID new_egid, GroupID new_sgid)
258{
259 VERIFY_NO_PROCESS_BIG_LOCK(this);
260 TRY(require_promise(Pledge::id));
261
262 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
263 auto credentials = this->credentials();
264
265 if (new_rgid == (gid_t)-1)
266 new_rgid = credentials->gid();
267 if (new_egid == (gid_t)-1)
268 new_egid = credentials->egid();
269 if (new_sgid == (gid_t)-1)
270 new_sgid = credentials->sgid();
271
272 auto ok = [&credentials](GroupID id) { return id == credentials->gid() || id == credentials->egid() || id == credentials->sgid(); };
273 if ((!ok(new_rgid) || !ok(new_egid) || !ok(new_sgid)) && !credentials->is_superuser())
274 return EPERM;
275
276 auto new_credentials = TRY(Credentials::create(
277 credentials->uid(),
278 new_rgid,
279 credentials->euid(),
280 new_egid,
281 credentials->suid(),
282 new_sgid,
283 credentials->extra_gids(),
284 credentials->sid(),
285 credentials->pgid()));
286
287 if (credentials->egid() != new_egid)
288 protected_data.dumpable = false;
289
290 protected_data.credentials = move(new_credentials);
291 return 0;
292 });
293}
294
295ErrorOr<FlatPtr> Process::sys$setgroups(size_t count, Userspace<GroupID const*> user_gids)
296{
297 VERIFY_NO_PROCESS_BIG_LOCK(this);
298 TRY(require_promise(Pledge::id));
299
300 if (count > NGROUPS_MAX)
301 return EINVAL;
302
303 return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
304 auto credentials = this->credentials();
305
306 if (!credentials->is_superuser())
307 return EPERM;
308
309 if (!count) {
310 protected_data.credentials = TRY(Credentials::create(
311 credentials->uid(),
312 credentials->gid(),
313 credentials->euid(),
314 credentials->egid(),
315 credentials->suid(),
316 credentials->sgid(),
317 {},
318 credentials->sid(),
319 credentials->pgid()));
320 return 0;
321 }
322
323 Vector<GroupID> new_extra_gids;
324 TRY(new_extra_gids.try_resize(count));
325 TRY(copy_n_from_user(new_extra_gids.data(), user_gids, count));
326
327 HashTable<GroupID> unique_extra_gids;
328 for (auto& extra_gid : new_extra_gids) {
329 if (extra_gid != credentials->gid())
330 TRY(unique_extra_gids.try_set(extra_gid));
331 }
332
333 new_extra_gids.clear_with_capacity();
334 for (auto extra_gid : unique_extra_gids) {
335 TRY(new_extra_gids.try_append(extra_gid));
336 }
337
338 protected_data.credentials = TRY(Credentials::create(
339 credentials->uid(),
340 credentials->gid(),
341 credentials->euid(),
342 credentials->egid(),
343 credentials->suid(),
344 credentials->sgid(),
345 new_extra_gids.span(),
346 credentials->sid(),
347 credentials->pgid()));
348 return 0;
349 });
350}
351
352}