3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/process/Thread.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/syscallError.h"
12#include "FileDescriptor.h"
13#include "PosixSubsystem.h"
14#include "memfd-file.h"
16#include "modules/system/vfs/MountView.h"
17#include "namespace-file.h"
18#include "sandbox-state.h"
21constexpr size_t MaximumRules = 4096;
22constexpr size_t MaximumLayers = 16;
23constexpr int BadDescriptorState = 77;
24constexpr uint64_t FileRights = LandlockAccess::Execute | LandlockAccess::ReadFile |
25 LandlockAccess::WriteFile | LandlockAccess::Truncate;
32 int finish(
int value) {
44 return process->getType() == Process::Posix
49class RulesetFilesystem final :
public RamFs {
52 setProcessOwnership(
false);
55RulesetFilesystem rulesetFilesystem;
57class RulesetFile final :
public File {
59 explicit RulesetFile(uint64_t requested)
60 :
File(
String(
"landlock-ruleset"), 0, 0, 0, 0, &rulesetFilesystem, 0, nullptr),
64 const uint64_t handled;
70 SYSCALL_ERROR(NoSuchDevice);
75 bool allowResize(
size_t,
size_t)
override {
76 SYSCALL_ERROR(InvalidArgument);
82 uint64_t
readBytewise(uint64_t, uint64_t, uintptr_t,
bool)
override {
83 SYSCALL_ERROR(InvalidArgument);
86 uint64_t
writeBytewise(uint64_t, uint64_t, uintptr_t,
bool)
override {
87 SYSCALL_ERROR(InvalidArgument);
93 auto* current = subsystem();
94 if (!current || fd < 0 || !current->acquireFileDescriptor(fd, descriptor)) {
95 SYSCALL_ERROR(BadFileDescriptor);
98 auto* file = descriptor->getFile();
99 if (!file || file->getFilesystem() != &rulesetFilesystem) {
100 syscallError(BadDescriptorState);
103 return static_cast<RulesetFile*
>(file);
106uint64_t inodeIdentity(
File* file) {
107 const auto attributes = file->getAttributes();
108 return attributes.inode ? attributes.inode : file->getInode();
112 if (!rule.anchor || !path) {
115 File* candidate = path->node();
116 File* anchor = rule.anchor->node();
117 return candidate == anchor || (candidate->getFilesystem() == anchor->getFilesystem() &&
118 rule.inode && inodeIdentity(candidate) == rule.inode);
122uint64_t LandlockDomain::layerAccess(
const FilesystemPathRef* ancestry,
size_t count)
const {
123 uint64_t allowed = ~handled;
124 for (
size_t i = 0; i < count; ++i) {
125 for (
const auto& rule : rules) {
126 if (sameObject(rule, ancestry[i])) {
127 allowed |= rule.access;
134bool PosixSubsystem::filesystemConstrained()
const {
135 return static_cast<bool>(posix_sandbox_domain());
138uint64_t PosixSubsystem::filesystemAccess(
const FilesystemPathRef* ancestry,
size_t count)
const {
139 auto* view = count ? VfsMountView::fromPath(ancestry[0]) : nullptr;
140 if (count == 1 && ancestry[0] && view && !view->attachmentId(ancestry[0])) {
141 File* file = ancestry[0]->node();
144 file->getFilesystem() == &rulesetFilesystem || posix_uts_file_namespace(file, space)) {
149 uint64_t allowed = ~uint64_t(0);
150 for (
auto domain = posix_sandbox_domain(); domain; domain = domain->previous) {
151 allowed &= domain->layerAccess(ancestry, count);
156bool PosixSubsystem::filesystemReparent(
const FilesystemPathRef* source,
size_t sourceCount,
158 size_t destinationCount)
const {
159 for (
auto domain = posix_sandbox_domain(); domain; domain = domain->previous) {
160 const uint64_t before = domain->layerAccess(source, sourceCount);
161 const uint64_t after = domain->layerAccess(destination, destinationCount);
162 if (after & ~before & LandlockAccess::All) {
170 if (!posix_sandbox_domain()) {
173 auto* view = VfsMountView::fromPath(path);
174 if (!view || !path) {
175 SYSCALL_ERROR(PermissionDenied);
178 return view->checkFilesystemAccess(path, requested);
181bool posix_landlock_open(
const FilesystemPathRef& path,
int flags,
bool& allowTruncate) {
182 allowTruncate =
true;
183 if ((flags & O_PATH) || !posix_sandbox_domain()) {
186 auto* view = VfsMountView::fromPath(path);
187 if (!path || !view) {
188 SYSCALL_ERROR(PermissionDenied);
191 const uint64_t allowed = view->filesystemAccess(path);
192 allowTruncate = allowed & LandlockAccess::Truncate;
193 File* file = path->node();
194 uint64_t required = 0;
196 required = LandlockAccess::ReadDir;
198 if ((flags & O_ACCMODE) != O_WRONLY) {
199 required |= LandlockAccess::ReadFile;
201 if ((flags & O_ACCMODE) != O_RDONLY) {
202 required |= LandlockAccess::WriteFile;
205 required |= LandlockAccess::Truncate;
208 if ((allowed & required) != required) {
209 SYSCALL_ERROR(PermissionDenied);
215int posix_landlock_create_ruleset(
const void* attributes,
size_t size,
unsigned flags) {
216 LandlockResult result;
218 if (flags != 1 || attributes || size) {
219 SYSCALL_ERROR(InvalidArgument);
220 return result.finish(-1);
222 return result.finish(3);
225 SYSCALL_ERROR(BadAddress);
226 return result.finish(-1);
228 if (size <
sizeof(uint64_t) || size > 4096) {
229 syscallError(size > 4096 ? Error::TooBig : Error::InvalidArgument);
230 return result.finish(-1);
234 SYSCALL_ERROR(BadAddress);
235 return result.finish(-1);
238 for (
size_t offset =
sizeof(handled); offset < size;) {
239 const size_t amount = size - offset <
sizeof(bytes) ? size - offset : sizeof(bytes);
240 const uintptr_t base =
reinterpret_cast<uintptr_t
>(attributes);
241 if (base > ~uintptr_t(0) - offset ||
244 SYSCALL_ERROR(BadAddress);
245 return result.finish(-1);
247 for (
size_t i = 0; i < amount; ++i) {
249 SYSCALL_ERROR(TooBig);
250 return result.finish(-1);
255 if (!handled || (handled & ~LandlockAccess::All)) {
256 syscallError(handled ? Error::InvalidArgument : Error::NoMessage);
257 return result.finish(-1);
259 auto* current = subsystem();
260 auto* file = current ?
new RulesetFile(handled) : nullptr;
263 SYSCALL_ERROR(OutOfMemory);
264 return result.finish(-1);
267 retained.adopt(file);
268 auto* descriptor =
new FileDescriptor(file, 0, 0xffffffff, FD_CLOEXEC, O_RDWR);
271 SYSCALL_ERROR(OutOfMemory);
272 return result.finish(-1);
275 return result.finish(
static_cast<int>(current->installFileDescriptor(descriptor, published)));
278int posix_landlock_add_rule(
int ruleset,
int type,
const void* attributes,
unsigned flags) {
279 LandlockResult result;
280 if (flags || type != 1) {
281 SYSCALL_ERROR(InvalidArgument);
282 return result.finish(-1);
285 auto* file = acquireRuleset(ruleset, rulesetDescriptor);
287 return result.finish(-1);
293 static_assert(
sizeof(input) == 12,
"Linux Landlock path rule ABI");
295 SYSCALL_ERROR(BadAddress);
296 return result.finish(-1);
298 if (!input.allowed || (input.allowed & ~file->handled)) {
299 syscallError(input.allowed ? Error::InvalidArgument : Error::NoMessage);
300 return result.finish(-1);
303 if (input.parentFd < 0 || !subsystem()->acquireFileDescriptor(input.parentFd, parent)) {
304 SYSCALL_ERROR(BadFileDescriptor);
305 return result.finish(-1);
307 auto anchor = parent->openingPath();
308 auto* view = VfsMountView::fromPath(anchor);
309 if (!anchor || !view || !view->attachmentId(anchor) || anchor->node()->
isSymlink()) {
310 syscallError(BadDescriptorState);
311 return result.finish(-1);
313 if (!anchor->node()->
isDirectory() && (input.allowed & ~FileRights)) {
314 SYSCALL_ERROR(InvalidArgument);
315 return result.finish(-1);
317 LandlockRule rule{anchor, inodeIdentity(anchor->node()), input.allowed};
319 for (
auto& existing : file->rules) {
320 if (sameObject(existing, anchor)) {
321 existing.access |= input.allowed;
322 return result.finish(0);
325 if (file->rules.count() >= MaximumRules || !file->rules.tryReserve(file->rules.count() + 1)) {
326 SYSCALL_ERROR(OutOfMemory);
327 return result.finish(-1);
329 file->rules.pushBack(pedigree_std::move(rule));
330 return result.finish(0);
333int posix_landlock_restrict_self(
int ruleset,
unsigned flags) {
334 LandlockResult result;
336 SYSCALL_ERROR(InvalidArgument);
337 return result.finish(-1);
339 if (!posix_no_new_privs()) {
340 SYSCALL_ERROR(NotEnoughPermissions);
341 return result.finish(-1);
344 auto* file = acquireRuleset(ruleset, descriptor);
346 return result.finish(-1);
348 auto previous = posix_sandbox_domain();
349 if (previous && previous->depth >= MaximumLayers) {
350 SYSCALL_ERROR(TooBig);
351 return result.finish(-1);
355 SYSCALL_ERROR(OutOfMemory);
356 return result.finish(-1);
358 domain->previous = previous;
359 domain->depth = previous ? previous->depth + 1 : 1;
361 domain->handled = file->handled | LandlockAccess::Refer;
364 if (!domain->rules.tryReserve(file->rules.count())) {
365 SYSCALL_ERROR(OutOfMemory);
366 return result.finish(-1);
368 for (
const auto& rule : file->rules) {
369 domain->rules.pushBack(rule);
372 return result.finish(posix_sandbox_restrict(domain) ? 0 : -1);
OpenFileDescriptionLease acquireOpenFileDescription() const
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool isSeekable() const
bool supportsRegularFileOperations()
virtual bool isSocket() const
virtual bool isBytewise() const
virtual bool isDirectory()
virtual bool isFifo() const
virtual bool isPipe() const
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()
static SharedPointer< T > tryAdopt(T *ptr)
A vector / dynamic array.