The Pedigree Project 0.1
credential-syscalls.cc
1#include "pedigree/kernel/process/Scheduler.h"
2#include "pedigree/kernel/process/TerminationDeferral.h"
3#include "pedigree/kernel/syscallError.h"
4
5#include <limits.h>
6
7#include "PosixProcess.h"
8#include "modules/system/vfs/VFS.h"
9#include "sandbox-state.h"
10#include "system-syscalls.h"
11#include "user-namespace.h"
12
13namespace {
14PosixProcess* currentProcess() {
15 Process* process = Processor::information().getCurrentThread()->getParent();
16 if (process->getType() != Process::Posix) {
17 SYSCALL_ERROR(InvalidArgument);
18 return nullptr;
19 }
20 return static_cast<PosixProcess*>(process);
21}
22int complete(PosixProcess::CredentialStatus status) {
23 switch (status) {
24 case PosixProcess::CredentialStatus::NoMemory:
25 SYSCALL_ERROR(OutOfMemory);
26 return -1;
27 case PosixProcess::CredentialStatus::Denied:
28 SYSCALL_ERROR(NotEnoughPermissions);
29 return -1;
30 case PosixProcess::CredentialStatus::Invalid:
31 SYSCALL_ERROR(InvalidArgument);
32 return -1;
33 case PosixProcess::CredentialStatus::Success:
34 Processor::information().getCurrentThread()->setErrno(0);
35 return 0;
36 }
37 SYSCALL_ERROR(InvalidArgument);
38 return -1;
39}
40int change(PosixProcess::CredentialChange type, uint32_t first, uint32_t second = UINT32_MAX,
41 uint32_t third = UINT32_MAX) {
42 PosixProcess* process = currentProcess();
43 if (!process)
44 return -1;
45 const bool group = type == PosixProcess::CredentialChange::SetGid ||
46 type == PosixProcess::CredentialChange::SetReGid ||
47 type == PosixProcess::CredentialChange::SetResGid;
48 if (!posix_global_id(group, first, first) || !posix_global_id(group, second, second) ||
49 !posix_global_id(group, third, third)) {
50 SYSCALL_ERROR(InvalidArgument);
51 return -1;
52 }
53 return complete(process->changeCredentials(*Processor::information().getCurrentThread(), type,
54 first, second, third));
55}
56} // namespace
57
58uid_t posix_getuid() {
59 Process* process = Processor::information().getCurrentThread()->getParent();
60 return posix_visible_id(false, process->getUserId());
61}
62gid_t posix_getgid() {
63 return posix_visible_id(true,
64 Processor::information().getCurrentThread()->getParent()->getGroupId());
65}
66uid_t posix_geteuid() {
67 return posix_visible_id(
68 false, Processor::information().getCurrentThread()->getParent()->getEffectiveUserId());
69}
70gid_t posix_getegid() {
71 return posix_visible_id(
72 true, Processor::information().getCurrentThread()->getParent()->getEffectiveGroupId());
73}
74int posix_setuid(uid_t id) {
75 return change(PosixProcess::CredentialChange::SetUid, id);
76}
77int posix_setgid(gid_t id) {
78 return change(PosixProcess::CredentialChange::SetGid, id);
79}
80int posix_seteuid(uid_t id) {
81 return posix_setresuid(UINT32_MAX, id, UINT32_MAX);
82}
83int posix_setegid(gid_t id) {
84 return posix_setresgid(UINT32_MAX, id, UINT32_MAX);
85}
86int posix_setreuid(uid_t real, uid_t effective) {
87 return change(PosixProcess::CredentialChange::SetReUid, real, effective);
88}
89int posix_setregid(gid_t real, gid_t effective) {
90 return change(PosixProcess::CredentialChange::SetReGid, real, effective);
91}
92int posix_setresuid(uid_t real, uid_t effective, uid_t saved) {
93 return change(PosixProcess::CredentialChange::SetResUid, real, effective, saved);
94}
95int posix_setresgid(gid_t real, gid_t effective, gid_t saved) {
96 return change(PosixProcess::CredentialChange::SetResGid, real, effective, saved);
97}
98
99long posix_setfsuid(uid_t id) {
100 PosixProcess* process = currentProcess();
101 if (!process)
102 return -1;
103 Thread* task = Processor::information().getCurrentThread();
104 uint32_t global;
105 if (!posix_global_id(false, id, global))
106 global = UINT32_MAX;
107 const uint32_t previous = process->changeFilesystemId(*task, false, global);
108 task->setErrno(0);
109 return static_cast<long>(posix_visible_id(false, previous));
110}
111long posix_setfsgid(gid_t id) {
112 PosixProcess* process = currentProcess();
113 if (!process)
114 return -1;
115 Thread* task = Processor::information().getCurrentThread();
116 uint32_t global;
117 if (!posix_global_id(true, id, global))
118 global = UINT32_MAX;
119 const uint32_t previous = process->changeFilesystemId(*task, true, global);
120 task->setErrno(0);
121 return static_cast<long>(posix_visible_id(true, previous));
122}
123
124int posix_getresuid(uid_t* real, uid_t* effective, uid_t* saved) {
125 PosixProcess* process = currentProcess();
126 if (!process)
127 return -1;
128 auto snapshot = process->snapshotCredentials();
129 snapshot.ruid = posix_visible_id(false, snapshot.ruid);
130 snapshot.euid = posix_visible_id(false, snapshot.euid);
131 snapshot.suid = posix_visible_id(false, snapshot.suid);
132 if (!PosixSubsystem::copyToUser(real, &snapshot.ruid, sizeof(uid_t)) ||
133 !PosixSubsystem::copyToUser(effective, &snapshot.euid, sizeof(uid_t)) ||
134 !PosixSubsystem::copyToUser(saved, &snapshot.suid, sizeof(uid_t))) {
135 SYSCALL_ERROR(BadAddress);
136 return -1;
137 }
138 return complete(PosixProcess::CredentialStatus::Success);
139}
140int posix_getresgid(gid_t* real, gid_t* effective, gid_t* saved) {
141 PosixProcess* process = currentProcess();
142 if (!process)
143 return -1;
144 auto snapshot = process->snapshotCredentials();
145 snapshot.rgid = posix_visible_id(true, snapshot.rgid);
146 snapshot.egid = posix_visible_id(true, snapshot.egid);
147 snapshot.sgid = posix_visible_id(true, snapshot.sgid);
148 if (!PosixSubsystem::copyToUser(real, &snapshot.rgid, sizeof(gid_t)) ||
149 !PosixSubsystem::copyToUser(effective, &snapshot.egid, sizeof(gid_t)) ||
150 !PosixSubsystem::copyToUser(saved, &snapshot.sgid, sizeof(gid_t))) {
151 SYSCALL_ERROR(BadAddress);
152 return -1;
153 }
154 return complete(PosixProcess::CredentialStatus::Success);
155}
156int posix_setgroups(size_t count, const gid_t* groups) {
157 PosixProcess* process = currentProcess();
158 if (!process)
159 return -1;
160 auto space = posix_user_namespace(*Processor::information().getCurrentThread());
161 if (!posix_capable(PosixCapabilities::Setgid) || (space && !space->groupsAllowed())) {
162 SYSCALL_ERROR(NotEnoughPermissions);
163 return -1;
164 }
165 if (count > FilesystemCredentials::MaximumGroups) {
166 SYSCALL_ERROR(InvalidArgument);
167 return -1;
168 }
169 uint32_t imported[FilesystemCredentials::MaximumGroups] = {};
170 if (count && !PosixSubsystem::copyFromUser(imported, groups, count, sizeof(gid_t))) {
171 SYSCALL_ERROR(BadAddress);
172 return -1;
173 }
174 for (size_t i = 0; i < count; ++i) {
175 if (!posix_global_id(true, imported[i], imported[i])) {
176 SYSCALL_ERROR(InvalidArgument);
177 return -1;
178 }
179 }
180 return complete(
181 process->replaceGroups(*Processor::information().getCurrentThread(), imported, count));
182}
183int posix_getgroups(size_t count, gid_t* groups) {
184 PosixProcess* process = currentProcess();
185 if (!process)
186 return -1;
187 if (count > INT_MAX) {
188 SYSCALL_ERROR(InvalidArgument);
189 return -1;
190 }
191 auto snapshot = process->snapshotCredentials();
192 for (size_t i = 0; i < snapshot.groupCount; ++i)
193 snapshot.groups[i] = posix_visible_id(true, snapshot.groups[i]);
194 if (count) {
195 if (count < snapshot.groupCount) {
196 SYSCALL_ERROR(InvalidArgument);
197 return -1;
198 }
199 if (snapshot.groupCount &&
200 !PosixSubsystem::copyToUser(groups, snapshot.groups, snapshot.groupCount, sizeof(gid_t))) {
201 SYSCALL_ERROR(BadAddress);
202 return -1;
203 }
204 }
205 Processor::information().getCurrentThread()->setErrno(0);
206 return snapshot.groupCount;
207}
208
209bool posix_exec_file_readable(File* file) {
210 Thread* task = Processor::information().getCurrentThread();
211 const int error = task->getErrno();
212 const bool readable = file && VFS::checkAccess(file, true, false, false);
213 task->setErrno(error);
214 return readable;
215}
216
217namespace {
218struct CapabilityHeader {
219 uint32_t version;
220 int32_t pid;
221};
222struct CapabilityData {
223 uint32_t effective, permitted, inheritable;
224};
225int capabilityHeader(void* user, CapabilityHeader& header) {
226 if (!PosixSubsystem::copyFromUser(&header, user, sizeof(header))) {
227 SYSCALL_ERROR(BadAddress);
228 return -1;
229 }
230 if (header.version == 0x19980330)
231 return 1;
232 if (header.version == 0x20071026 || header.version == 0x20080522)
233 return 2;
234 const uint32_t version = 0x20080522;
235 if (!PosixSubsystem::copyToUser(user, &version, sizeof(version))) {
236 SYSCALL_ERROR(BadAddress);
237 } else {
238 SYSCALL_ERROR(InvalidArgument);
239 }
240 return -1;
241}
242} // namespace
243
244int posix_capget(void* hdrp, void* datap) {
245 CapabilityHeader header = {};
246 const int words = capabilityHeader(hdrp, header);
247 if (words < 0)
248 return -1;
249 Thread& task = *Processor::information().getCurrentThread();
250 if (header.pid < 0) {
251 SYSCALL_ERROR(InvalidArgument);
252 return -1;
253 }
255 Thread* selected = &task;
256 if (header.pid && static_cast<size_t>(header.pid) !=
257 task.getUserspaceTaskId(task.getParent()->pidNamespace().get())) {
258 if (!Scheduler::instance().acquireThreadByUserspaceId(target, header.pid,
259 task.getParent()->pidNamespace().get())) {
260 SYSCALL_ERROR(NoSuchProcess);
261 return -1;
262 }
263 selected = target.get();
264 }
265 const auto credentials = posix_task_credentials(*selected);
266 CapabilityData data[2] = {};
267 for (int i = 0; i < words; ++i) {
268 data[i].effective = credentials.effective >> (32 * i);
269 data[i].permitted = credentials.permitted >> (32 * i);
270 data[i].inheritable = credentials.inheritable >> (32 * i);
271 }
272 if (datap && !PosixSubsystem::copyToUser(datap, data, words, sizeof(data[0]))) {
273 SYSCALL_ERROR(BadAddress);
274 return -1;
275 }
276 task.setErrno(0);
277 return 0;
278}
279
280int posix_capset(void* hdrp, const void* datap) {
281 TerminationDeferral lifetime;
282 CapabilityHeader header = {};
283 const int words = capabilityHeader(hdrp, header);
284 if (words < 0)
285 return -1;
286 Thread& task = *Processor::information().getCurrentThread();
287 if (header.pid &&
288 (header.pid < 0 || static_cast<size_t>(header.pid) !=
289 task.getUserspaceTaskId(task.getParent()->pidNamespace().get()))) {
290 SYSCALL_ERROR(NotEnoughPermissions);
291 return -1;
292 }
293 CapabilityData data[2] = {};
294 if (!PosixSubsystem::copyFromUser(data, datap, words, sizeof(data[0]))) {
295 SYSCALL_ERROR(BadAddress);
296 return -1;
297 }
298 auto value = posix_task_credentials(task);
299 const uint64_t effective = (uint64_t(data[1].effective) << 32) | data[0].effective;
300 const uint64_t permitted = (uint64_t(data[1].permitted) << 32) | data[0].permitted;
301 const uint64_t inheritable = (uint64_t(data[1].inheritable) << 32) | data[0].inheritable;
302 const bool setpcap = value.effective & (uint64_t(1) << PosixCapabilities::Setpcap);
303 if (((effective | permitted | inheritable) & ~PosixCapabilities::All) ||
304 (effective & ~permitted) || (permitted & ~value.permitted) ||
305 (inheritable & ~(value.inheritable | value.bounding)) ||
306 (!setpcap && (inheritable & ~(value.inheritable | value.permitted)))) {
307 SYSCALL_ERROR(NotEnoughPermissions);
308 return -1;
309 }
310 value.permitted = permitted;
311 value.effective = effective;
312 value.inheritable = inheritable;
313 value.ambient &= permitted & inheritable;
314 auto next = TaskCredentialsRef::tryAllocate(value);
315 if (!next) {
316 SYSCALL_ERROR(OutOfMemory);
317 return -1;
318 }
319 if (!posix_sandbox_set_credentials(task, next))
320 return -1;
321 task.setErrno(0);
322 return 0;
323}
Definition File.h:75
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
Process * getParent()
Definition Process.h:620
virtual int64_t getUserId() const
Definition Process.cc:2238
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
static SharedPointer< T > tryAllocate(Args...)
T * get() const
void setErrno(size_t err)
Definition Thread.h:482
size_t getErrno()
Definition Thread.h:477
Process * getParent() const
Definition Thread.h:340
size_t getUserspaceTaskId(const UserspacePidNamespace *space=nullptr) const
Definition Thread.cc:3743
static bool checkAccess(File *pFile, bool bRead, bool bWrite, bool bExecute)
Definition VFS.cc:1502