<<
path:
root/public/0x2lib.git/html/0x2_Landlock.jai
blob: 3c7cfb4218cdfbb16900cee3c93fc9c16cf178fe
[raw]
[clear marker]
3/** CAVE: Rulesets support only till ABI v7 !
6 /usr/include/linux/landlock.h
9 https://docs.kernel.org/userspace-api/landlock.html
12 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/samples/landlock/sandboxer.c
17 rules := landlock_all_rules();
18 ok, ll_fd := landlock_create_ruleset(*rules);
19 assert(ok, "Create Ruleset");
21 ok = landlock_lock_privileges();
22 assert(ok, "Lock Privileges");
24 ok = landlock_restrict_self(ll_fd);
25 assert(ok, "Restrict Self");
30LL_Ruleset_Create :: enum_flags u64 {
34 // CAVE: https://docs.kernel.org/userspace-api/landlock.html#landlock-errata
38LL_Restrict_Self :: enum_flags u64 {
39 LOG_SAME_EXEC_OFF :: 1;
44landlock_rule_type :: enum #specified {
49/** Supports till ABI v7 */
50LL_Filesystem :: enum_flags u64 {
65 TRUNCATE; /** ABI v3+ */
66 IOCTL_DEV; /** ABI v5+ */
69LL_Network :: enum_flags u64 {
74LL_Scoped :: enum_flags u64 {
75 ABSTRACT_UNIX_SOCKET :: 1;
80landlock_ruleset_attr :: struct {
81 handled_access_fs: LL_Filesystem;
82 handled_access_net: LL_Network;
86landlock_path_beneath_attr :: struct {
87 allowed_access: LL_Filesystem;
91landlock_net_port_attr :: struct {
92 allowed_access: LL_Network;
97/** https://man7.org/linux/man-pages/man2/landlock_create_ruleset.2.html
99 CAVE: Flags must be 0 if attr is used.
100 Otherwise flags can be set to: .VERSION
102 Quote from above man page:
104 If attr is NULL and size is 0, then the returned value is
105 the highest supported Landlock ABI version (starting at 1).
106 This version can be used for a best-effort security
107 approach, which is encouraged when user space is not pinned
108 to a specific kernel version. [...]
111landlock_create_ruleset :: (
112 ruleset: *landlock_ruleset_attr,
113 flags: LL_Ruleset_Create = .NULL,
114 loc := #caller_location
116 -> (ok: bool, fd: int)
118 assert(CPU == .X64 && OS == .LINUX, "Sorry, only support for x64 Linux!");
120 size := ifx ruleset == null then 0 else size_of(landlock_ruleset_attr);
122 SYS_LANDLOCK_CREATE_RULESET,
129 print_last_error(ERR_LL_CREATE_RULESET, loc);
137/** https://man7.org/linux/man-pages/man2/landlock_restrict_self.2.html */
138landlock_restrict_self :: (ruleset_fd: int) -> ok: bool {
139 /** According to above man page, flags MUST be ZERO */
140 ok := syscall(SYS_LANDLOCK_RESTRICT_SELF, ruleset_fd, 0);
142 print_last_error(ERR_LL_RESTRICT);
148landlock_add_rule :: (
150 rule_attr: *landlock_net_port_attr
154 return add_rule(ruleset_fd, .NET_PORT, rule_attr);
157landlock_add_rule :: (
159 rule_attr: *landlock_path_beneath_attr
163 return add_rule(ruleset_fd, .PATH_BENEATH, rule_attr);
168 * The following procedures are non standard and added by me
172/** According to the man pages you must lock privileges
173 before applying any landlock rules.
175 https://man7.org/linux/man-pages/man7/landlock.7.html
178 'man prctl' on the return value:
180 On success, a nonnegative value is returned.
181 On error, -1 is returned, and errno is set to indicate the error.
184landlock_lock_privileges :: () -> ok: bool {
185 ok := prctl(PR_SET_NO_NEW_PRIVS, 1);
187 print_last_error(ERR_PRCTL);
193landlock_is_version_equal_or_higher :: (pinned_version: int, $print_version := false) -> bool {
194 ok, system_version := landlock_create_ruleset(null, .VERSION);
196 log_error("Could not determine version");
200 #if print_version then log("Landlock System Version: %", system_version);
201 return system_version >= pinned_version;
204/** Returns _all_ available rules, essentially locking everything.
206 This might be useful if you want more a whitelist, rather than
207 a blacklist. Remove flags like this:
209 my_ruleset.handled_access_fs &= ~TRUNCATE;
211 Or if you want to remove items based on the supported ABI version.
213landlock_all_rules :: () -> landlock_ruleset_attr {
215 ll_fs := type_info(LL_Filesystem);
216 ll_net := type_info(LL_Network);
217 ll_scoped := type_info(LL_Scoped);
222 append(*sb, "return .{ handled_access_fs = ");
224 for ll_fs.names array_add(*tmp, tprint(".%", it));
225 fs_values := join(..tmp, " | ");
227 append(*sb, fs_values);
228 append(*sb, ", handled_access_net = ");
232 for ll_net.names array_add(*tmp, tprint(".%", it));
233 net_values := join(..tmp, " | ");
235 append(*sb, net_values);
236 append(*sb, ", scoped = ");
240 for ll_scoped.names array_add(*tmp, tprint(".%", it));
241 scoped_values := join(..tmp, " | ");
243 append(*sb, scoped_values);
246 code := builder_to_string(*sb);
258 String_Builder, builder_to_string, append,
259 array_reset, array_add,
260 tprint, log, log_error,
262) Basic :: #import "Basic";
264using,only(join) String :: #import "String";
266libc :: #library,system "libc";
269/** This assertion is important, since syscall numbers might shift when using
272#assert(CPU == .X64 && OS == .LINUX) "Only x64 Linux support!";
277 Glibc didn't implented landlock functions yet, so we have to wrap
278 the syscalls ourselves.
280 Jai's Linux-faced stdlib does not have the syscall numbers for
281 the landlock functions, so we stole them from the Android lib.
282 Which are identical with x64 Linux anyway.
286SYS_LANDLOCK_CREATE_RULESET :: 444;
287SYS_LANDLOCK_ADD_RULE :: 445;
288SYS_LANDLOCK_RESTRICT_SELF :: 446;
290/** /usr/include/linux/prctl.h */
291PR_SET_NO_NEW_PRIVS :: 38;
300ERR_LL_CREATE_RULESET :: #code {
303 log_error("Landlock is supported by the kernel but disabled at boot time.");
305 log_error("Unknown flags, or unknown access, or too small size.");
307 log_error("size is too big.");
309 log_error("attr was not a valid address.");
311 log_error("Empty accesses (i.e., attr did not specify any access rights to restrict).");
312 case; log_error("Uncovered error: %", err);
316ERR_LL_ADD_RULE :: #code {
319 log_error("rule_type is set correctly, but TCP is not supported by the kernel.");
321 log_error("Landlock is supported, but this feature is disabled at boot time by the kernel.");
323 log_error("Flags is not 0 OR inalivid port number supplied.");
325 log_error("Empty accesses (i.e., rule_attr.allowed_access is 0).");
327 log_error("No valid file descriptor supplied in `ruleset_fd` or `rule_attr`.");
329 log_error("No valid file descriptor in `ruleset_fd`.");
331 log_error("`ruleset_fd` has no write access to the underlying ruleset.");
333 log_error("`rule_attr` was not a valid address.");
337ERR_LL_RESTRICT :: #code {
340 log_error("Landlock is supported by the kernel but disabled at boot time.");
342 log_error("flags is not 0.");
344 log_error("ruleset_fd is not a file descriptor for the current thread.");
346 log_error("ruleset_fd is not a ruleset file descriptor.");
348 log_error("ruleset_fd has no read access to the underlying ruleset.\nOr the calling thread is not running with no_new_privs,\nor it doesn't have the CAP_SYS_ADMIN in its user namespace.");
350 log_error("The maximum number of composed rulesets is reached for the calling thread. This limit is currently 64.");
357 log_error("OP value is not recognized/supported");
362prctl :: (op: int, a2 := 0, a3 := 0, a4 := 0, a5 := 0) -> int #foreign libc;
364/** https://man7.org/linux/man-pages/man2/landlock_add_rule.2.html */
367 rule_type: landlock_rule_type,
372 /** According to above man page, flags MUST be ZERO */
373 ok := syscall(SYS_LANDLOCK_ADD_RULE, ruleset_fd, rule_type, rule_attr, 0);
375 print_last_error(ERR_LL_ADD_RULE);
381print_last_error :: ($code: Code, loc := #caller_location) {
385 log_error("-- Error --------------------");
388 #insert,scope() code;
390 log_error("------------------------------");
396------------------------------------------------------------------------------
397This software is available under 2 licenses -- choose whichever you prefer.
398------------------------------------------------------------------------------
399ALTERNATIVE A - MIT License
400Copyright (c) 2026 Adam Blazeowsky
401Permission is hereby granted, free of charge, to any person obtaining a copy of
402this software and associated documentation files (the "Software"), to deal in
403the Software without restriction, including without limitation the rights to
404use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
405of the Software, and to permit persons to whom the Software is furnished to do
406so, subject to the following conditions:
407The above copyright notice and this permission notice shall be included in all
408copies or substantial portions of the Software.
409THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
410IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
411FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
412AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
413LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
414OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
416------------------------------------------------------------------------------
417ALTERNATIVE B - Public Domain (www.unlicense.org)
418This is free and unencumbered software released into the public domain.
419Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
420software, either in source code form or as a compiled binary, for any purpose,
421commercial or non-commercial, and by any means.
422In jurisdictions that recognize copyright laws, the author or authors of this
423software dedicate any and all copyright interest in the software to the public
424domain. We make this dedication for the benefit of the public at large and to
425the detriment of our heirs and successors. We intend this dedication to be an
426overt act of relinquishment in perpetuity of all present and future rights to
427this software under copyright law.
428THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
429IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
430FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
431AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
432ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
433WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
434------------------------------------------------------------------------------