mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
parent
1c6b5c0acd
commit
38c3fa63fe
14 changed files with 646 additions and 352 deletions
|
@ -16,6 +16,14 @@
|
|||
#define LANDLOCK_ACCESS_FS_MAKE_FIFO 0x0400ul
|
||||
#define LANDLOCK_ACCESS_FS_MAKE_BLOCK 0x0800ul
|
||||
#define LANDLOCK_ACCESS_FS_MAKE_SYM 0x1000ul
|
||||
|
||||
/**
|
||||
* Allow renaming or linking file to a different directory.
|
||||
*
|
||||
* @see https://lore.kernel.org/r/20220329125117.1393824-8-mic@digikod.net
|
||||
* @see https://docs.kernel.org/userspace-api/landlock.html
|
||||
* @note ABI 2+
|
||||
*/
|
||||
#define LANDLOCK_ACCESS_FS_REFER 0x2000ul
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchgp.h"
|
||||
|
@ -40,7 +41,7 @@ relegated void __assert_fail(const char *expr, const char *file, int line) {
|
|||
owner = 0;
|
||||
me = sys_gettid();
|
||||
kprintf("%s:%d: assert(%s) failed (tid %d)\n", file, line, expr, me);
|
||||
if (_lockcmpxchgp(&sync, &owner, me)) {
|
||||
if (__vforked || _lockcmpxchgp(&sync, &owner, me)) {
|
||||
__restore_tty();
|
||||
if (weaken(ShowBacktrace)) {
|
||||
weaken(ShowBacktrace)(2, __builtin_frame_address(0));
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
#define PROMISE_EXEC 15
|
||||
#define PROMISE_EXECNATIVE 16
|
||||
#define PROMISE_ID 17
|
||||
#define PROMISE_MAX 17
|
||||
#define PROMISE_UNVEIL 18
|
||||
#define PROMISE_MAX 18
|
||||
|
||||
#define PLEDGED(x) (~__promises & (1L << PROMISE_##x))
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
|
|||
--__strace;
|
||||
owner = 0;
|
||||
me = sys_gettid();
|
||||
if (_lockcmpxchgp(&sync, &owner, me)) {
|
||||
if (__vforked || _lockcmpxchgp(&sync, &owner, me)) {
|
||||
if (!__vforked) {
|
||||
rip = ctx ? ctx->uc_mcontext.rip : 0;
|
||||
err = errno;
|
||||
|
|
|
@ -302,6 +302,12 @@ static const uint16_t kPledgeLinuxExec2[] = {
|
|||
__NR_linux_execveat, //
|
||||
};
|
||||
|
||||
static const uint16_t kPledgeLinuxUnveil[] = {
|
||||
__NR_linux_landlock_create_ruleset, //
|
||||
__NR_linux_landlock_add_rule, //
|
||||
__NR_linux_landlock_restrict_self, //
|
||||
};
|
||||
|
||||
static const struct Pledges {
|
||||
const char *name;
|
||||
const uint16_t *syscalls;
|
||||
|
@ -325,6 +331,7 @@ static const struct Pledges {
|
|||
[PROMISE_EXEC] = {"exec", PLEDGE(kPledgeLinuxExec)}, //
|
||||
[PROMISE_EXECNATIVE] = {"execnative", PLEDGE(kPledgeLinuxExec2)}, //
|
||||
[PROMISE_ID] = {"id", PLEDGE(kPledgeLinuxId)}, //
|
||||
[PROMISE_UNVEIL] = {"unveil", PLEDGE(kPledgeLinuxUnveil)}, //
|
||||
[PROMISE_MAX + 1] = {0}, //
|
||||
};
|
||||
|
||||
|
@ -1231,6 +1238,10 @@ static void SetPromises(const char *promises) {
|
|||
* System call origin verification can't be enabled. If you always
|
||||
* assimilate your APE binaries, then this should be preferred.
|
||||
*
|
||||
* - "unveil" allows unveil() to be called, as well as the underlying
|
||||
* landlock_create_ruleset, landlock_add_rule, landlock_restrict_self
|
||||
* calls on Linux.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise ENOSYS if host os isn't Linux or OpenBSD
|
||||
* @raise EINVAL if `execpromises` is used on Linux
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/pr.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
|
@ -45,7 +46,7 @@
|
|||
* - Integrate with pledge and remove the file access?
|
||||
* - Stuff state into the .protected section?
|
||||
*/
|
||||
static struct {
|
||||
_Thread_local static struct {
|
||||
int abi;
|
||||
int fd;
|
||||
uint64_t read;
|
||||
|
@ -54,7 +55,6 @@ static struct {
|
|||
uint64_t create;
|
||||
} State = {
|
||||
.abi = 2,
|
||||
.fd = 0,
|
||||
.read = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR |
|
||||
LANDLOCK_ACCESS_FS_REFER,
|
||||
.write = LANDLOCK_ACCESS_FS_WRITE_FILE,
|
||||
|
@ -65,21 +65,27 @@ static struct {
|
|||
LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM,
|
||||
};
|
||||
|
||||
forceinline int unveil_final(void) {
|
||||
static int unveil_final(void) {
|
||||
int rc;
|
||||
if (State.fd == -1) return 0;
|
||||
assert(State.fd > 0);
|
||||
if ((rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1 &&
|
||||
(rc = landlock_restrict_self(State.fd, 0)) != -1 &&
|
||||
(rc = close(State.fd)) != -1)
|
||||
(rc = sys_close(State.fd)) != -1)
|
||||
State.fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
forceinline int unveil_init(void) {
|
||||
int rc;
|
||||
if ((rc = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)) <
|
||||
0)
|
||||
static int err_close(int rc, int fd) {
|
||||
int serrno = errno;
|
||||
sys_close(fd);
|
||||
errno = serrno;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int unveil_init(void) {
|
||||
int rc, fd;
|
||||
if ((rc = landlock_create_ruleset(0, 0, LANDLOCK_CREATE_RULESET_VERSION)) < 0)
|
||||
return -1;
|
||||
State.abi = rc;
|
||||
if (State.abi < 2) State.read &= ~LANDLOCK_ACCESS_FS_REFER;
|
||||
|
@ -87,21 +93,17 @@ forceinline int unveil_init(void) {
|
|||
.handled_access_fs = State.read | State.write | State.exec | State.create,
|
||||
};
|
||||
if ((rc = landlock_create_ruleset(&attr, sizeof(attr), 0)) < 0) return -1;
|
||||
State.fd = rc;
|
||||
// grant file descriptor a higher number that's less likely to interfere
|
||||
if ((fd = __sys_fcntl(rc, F_DUPFD, 100)) == -1) return err_close(-1, rc);
|
||||
if (sys_close(rc) == -1) return err_close(-1, fd);
|
||||
State.fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
forceinline int err_close(int rc, int fd) {
|
||||
int serrno = errno;
|
||||
close(fd);
|
||||
errno = serrno;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sys_unveil_linux(const char *path, const char *permissions) {
|
||||
int rc;
|
||||
if (State.fd == 0 && (rc = unveil_init()) == -1) return rc;
|
||||
if (path == NULL && permissions == NULL) return unveil_final();
|
||||
if (!State.fd && (rc = unveil_init()) == -1) return rc;
|
||||
if (!path && !permissions) return unveil_final();
|
||||
struct landlock_path_beneath_attr pb = {0};
|
||||
for (const char *c = permissions; *c != '\0'; c++) {
|
||||
switch (*c) {
|
||||
|
@ -121,17 +123,20 @@ static int sys_unveil_linux(const char *path, const char *permissions) {
|
|||
return einval();
|
||||
}
|
||||
}
|
||||
if ((rc = open(path, O_RDONLY | O_PATH | O_CLOEXEC)) == -1) return rc;
|
||||
if ((rc = sys_open(path, O_PATH | O_CLOEXEC, 0)) == -1) return rc;
|
||||
pb.parent_fd = rc;
|
||||
struct stat st;
|
||||
if ((rc = fstat(pb.parent_fd, &st)) == -1) return err_close(rc, pb.parent_fd);
|
||||
if (!S_ISDIR(st.st_mode)) pb.allowed_access &= FILE_BITS;
|
||||
if ((rc = landlock_add_rule(State.fd, LANDLOCK_RULE_PATH_BENEATH, &pb, 0)))
|
||||
return err_close(rc, pb.parent_fd);
|
||||
close(pb.parent_fd);
|
||||
sys_close(pb.parent_fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unveil parts of a restricted filesystem view.
|
||||
*/
|
||||
int unveil(const char *path, const char *permissions) {
|
||||
int rc;
|
||||
if (IsLinux()) {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_unveil,0xfff072ffffffffff,globl
|
||||
.scall sys_unveil,0xfff072ffffffffff,globl,hidden
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall unveil,0xfff072ffffffffff,globl
|
|
@ -322,5 +322,29 @@
|
|||
#define __NR_linux_io_uring_setup 0x01a9
|
||||
#define __NR_linux_io_uring_enter 0x01aa
|
||||
#define __NR_linux_io_uring_register 0x01ab
|
||||
#define __NR_linux_open_tree 0x01ac
|
||||
#define __NR_linux_move_mount 0x01ad
|
||||
#define __NR_linux_fsopen 0x01ae
|
||||
#define __NR_linux_fsconfig 0x01af
|
||||
#define __NR_linux_fsmount 0x01b0
|
||||
#define __NR_linux_fspick 0x01b1
|
||||
#define __NR_linux_pidfd_open 0x01b2
|
||||
#define __NR_linux_clone3 0x01b3
|
||||
#define __NR_linux_close_range 0x01b4
|
||||
#define __NR_linux_openat2 0x01b5
|
||||
#define __NR_linux_pidfd_getfd 0x01b6
|
||||
#define __NR_linux_faccessat2 0x01b7
|
||||
#define __NR_linux_process_madvise 0x01b8
|
||||
#define __NR_linux_epoll_pwait2 0x01b9
|
||||
#define __NR_linux_mount_setattr 0x01ba
|
||||
#define __NR_linux_quotactl_fd 0x01bb
|
||||
#define __NR_linux_landlock_create_ruleset 0x01bc
|
||||
#define __NR_linux_landlock_add_rule 0x01bd
|
||||
#define __NR_linux_landlock_restrict_self 0x01be
|
||||
#define __NR_linux_memfd_secret 0x01bf
|
||||
#define __NR_linux_process_mrelease 0x01c0
|
||||
#define __NR_linux_futex_waitv 0x01c1
|
||||
#define __NR_linux_set_mempolicy_home_node 0x01c2
|
||||
#define __NR_linux_sys_unveil 0x0fff
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ */
|
||||
|
|
|
@ -399,7 +399,6 @@ scall memfd_secret 0xfffffffffffff1bf globl
|
|||
scall process_mrelease 0xfffffffffffff1c0 globl
|
||||
scall futex_waitv 0xfffffffffffff1c1 globl
|
||||
scall set_mempolicy_home_node 0xfffffffffffff1c2 globl
|
||||
scall sys_unveil 0xfff072ffffffffff globl
|
||||
|
||||
# The Fifth Bell System Interface, Community Edition
|
||||
# » besiyata dishmaya
|
||||
|
@ -799,7 +798,7 @@ scall sendsyslog 0xfff070ffffffffff globl
|
|||
scall setrtable 0xfff136ffffffffff globl
|
||||
scall swapctl 0x10f0c1ffffffffff globl
|
||||
scall thrkill 0xfff077ffffffffff globl
|
||||
scall unveil 0xfff072ffffffffff globl
|
||||
scall sys_unveil 0xfff072ffffffffff globl hidden
|
||||
|
||||
# The Fifth Bell System Interface, Community Edition
|
||||
# » beyond the pale
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/bits/atomic.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
@ -63,7 +64,7 @@ void testlib_finish(void) {
|
|||
void testlib_error_enter(const char *file, const char *func) {
|
||||
atomic_fetch_sub_explicit(&__ftrace, 1, memory_order_relaxed);
|
||||
atomic_fetch_sub_explicit(&__strace, 1, memory_order_relaxed);
|
||||
_spinlock(&testlib_error_lock);
|
||||
if (!__vforked) _spinlock(&testlib_error_lock);
|
||||
if (!IsWindows()) sys_getpid(); /* make strace easier to read */
|
||||
if (!IsWindows()) sys_getpid();
|
||||
if (g_testlib_shoulddebugbreak) {
|
||||
|
|
22
test/libc/mem/life.c
Normal file
22
test/libc/mem/life.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return 42;
|
||||
}
|
|
@ -42,8 +42,11 @@ TEST_LIBC_MEM_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_THREAD \
|
||||
LIBC_X \
|
||||
LIBC_ZIPOS \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_LIBCXX
|
||||
|
||||
|
@ -58,11 +61,34 @@ o/$(MODE)/test/libc/mem/%.com.dbg: \
|
|||
$(TEST_LIBC_MEM_DEPS) \
|
||||
o/$(MODE)/test/libc/mem/%.o \
|
||||
o/$(MODE)/test/libc/mem/mem.pkg \
|
||||
o/$(MODE)/test/libc/mem/life.elf.zip.o \
|
||||
$(LIBC_TESTMAIN) \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/mem/life.com.dbg: \
|
||||
$(LIBC_RUNTIME) \
|
||||
o/$(MODE)/test/libc/mem/life.o \
|
||||
$(CRT) \
|
||||
$(APE)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/test/libc/mem/life.elf: \
|
||||
o/$(MODE)/tool/build/assimilate.com \
|
||||
o/$(MODE)/test/libc/mem/life.com
|
||||
@$(COMPILE) -ACP -T$@ \
|
||||
build/bootstrap/cp.com \
|
||||
o/$(MODE)/test/libc/mem/life.com \
|
||||
o/$(MODE)/test/libc/mem/life.elf
|
||||
@$(COMPILE) -AASSIMILATE -T$@ \
|
||||
o/$(MODE)/tool/build/assimilate.com \
|
||||
o/$(MODE)/test/libc/mem/life.elf
|
||||
|
||||
o/$(MODE)/test/libc/mem/life.elf.zip.o: \
|
||||
ZIPOBJ_FLAGS += \
|
||||
-B
|
||||
|
||||
$(TEST_LIBC_MEM_OBJS): \
|
||||
DEFAULT_CCFLAGS += \
|
||||
-fno-builtin
|
||||
|
|
198
test/libc/mem/unveil_test.c
Normal file
198
test/libc/mem/unveil_test.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/landlock.h"
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/io.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
|
||||
#define EACCES_OR_ENOENT (IsOpenbsd() ? ENOENT : EACCES)
|
||||
|
||||
#define SPAWN() \
|
||||
{ \
|
||||
int ws, pid; \
|
||||
ASSERT_NE(-1, (pid = fork())); \
|
||||
if (!pid) {
|
||||
|
||||
#define EXITS(rc) \
|
||||
_Exit(0); \
|
||||
} \
|
||||
ASSERT_NE(-1, wait(&ws)); \
|
||||
ASSERT_TRUE(WIFEXITED(ws)); \
|
||||
ASSERT_EQ(rc, WEXITSTATUS(ws)); \
|
||||
}
|
||||
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
struct stat st;
|
||||
|
||||
static bool SupportsLandlock(void) {
|
||||
int e = errno;
|
||||
bool r = landlock_create_ruleset(0, 0, LANDLOCK_CREATE_RULESET_VERSION) >= 0;
|
||||
errno = e;
|
||||
return r;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void init(void) {
|
||||
if (!(IsLinux() && SupportsLandlock()) && !IsOpenbsd()) exit(0);
|
||||
}
|
||||
|
||||
void SetUp(void) {
|
||||
// make sure zipos maps executable into memory early
|
||||
ASSERT_SYS(0, 0, stat("/zip/life.elf", &st));
|
||||
}
|
||||
|
||||
int extract(const char *from, const char *to, int mode) {
|
||||
int fdin, fdout;
|
||||
if ((fdin = open(from, O_RDONLY)) == -1) return -1;
|
||||
if ((fdout = creat(to, mode)) == -1) {
|
||||
close(fdin);
|
||||
return -1;
|
||||
}
|
||||
if (_copyfd(fdin, fdout, -1) == -1) {
|
||||
close(fdout);
|
||||
close(fdin);
|
||||
return -1;
|
||||
}
|
||||
return close(fdout) | close(fdin);
|
||||
}
|
||||
|
||||
TEST(unveil, api_differences) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, stat("/", &st));
|
||||
ASSERT_SYS(0, 0, unveil(".", "rw"));
|
||||
if (IsOpenbsd()) {
|
||||
// openbsd imposes restrictions immediately
|
||||
ASSERT_SYS(ENOENT, -1, open("/", O_RDONLY | O_DIRECTORY));
|
||||
} else {
|
||||
// restrictions on linux don't go into effect until unveil(0,0)
|
||||
ASSERT_SYS(0, 3, open("/", O_RDONLY | O_DIRECTORY));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
// error numbers are inconsistent
|
||||
ASSERT_SYS(EACCES_OR_ENOENT, -1, open("/", O_RDONLY | O_DIRECTORY));
|
||||
// wut
|
||||
if (IsLinux()) {
|
||||
ASSERT_SYS(0, 3, open("/", O_PATH)); // wut
|
||||
ASSERT_SYS(0, 0, stat("/", &st)); // wut
|
||||
}
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(unveil, rx_readOnlyPreexistingExecutable_worksFine) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, mkdir("folder", 0755));
|
||||
ASSERT_SYS(0, 0, extract("/zip/life.elf", "folder/life.elf", 0755));
|
||||
ASSERT_SYS(0, 0, unveil("folder", "rx"));
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
SPAWN();
|
||||
execv("folder/life.elf", (char *[]){"folder/life.elf", 0});
|
||||
EXITS(42);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(unveil, r_noExecutePreexistingExecutable_raisesEacces) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, mkdir("folder", 0755));
|
||||
ASSERT_SYS(0, 0, extract("/zip/life.elf", "folder/life.elf", 0755));
|
||||
ASSERT_SYS(0, 0, unveil("folder", "r"));
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
SPAWN();
|
||||
ASSERT_SYS(EACCES, -1,
|
||||
execv("folder/life.elf", (char *[]){"folder/life.elf", 0}));
|
||||
EXITS(0);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(unveil, rwc_createExecutableFile_isAllowedButCantBeRun) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, mkdir("folder", 0755));
|
||||
ASSERT_SYS(0, 0, unveil("folder", "rwc"));
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
ASSERT_SYS(0, 0, extract("/zip/life.elf", "folder/life.elf", 0755));
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, stat("folder/life.elf", &st));
|
||||
ASSERT_SYS(EACCES, -1,
|
||||
execv("folder/life.elf", (char *[]){"folder/life.elf", 0}));
|
||||
EXITS(0);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(unveil, rwcx_createExecutableFile_canAlsoBeRun) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, mkdir("folder", 0755));
|
||||
ASSERT_SYS(0, 0, unveil("folder", "rwcx"));
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
ASSERT_SYS(0, 0, extract("/zip/life.elf", "folder/life.elf", 0755));
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, stat("folder/life.elf", &st));
|
||||
execv("folder/life.elf", (char *[]){"folder/life.elf", 0});
|
||||
EXITS(42);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(unveil, dirfdHacking_doesntWork) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, mkdir("jail", 0755));
|
||||
ASSERT_SYS(0, 0, mkdir("garden", 0755));
|
||||
ASSERT_SYS(0, 0, touch("garden/secret.txt", 0644));
|
||||
ASSERT_SYS(0, 3, open("garden", O_RDONLY | O_DIRECTORY));
|
||||
ASSERT_SYS(0, 0, unveil("jail", "rw"));
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
ASSERT_SYS(EACCES_OR_ENOENT, -1, openat(3, "secret.txt", O_RDONLY));
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(unveil, overlappingDirectories_inconsistentBehavior) {
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, makedirs("f1/f2", 0755));
|
||||
ASSERT_SYS(0, 0, extract("/zip/life.elf", "f1/f2/life.elf", 0755));
|
||||
ASSERT_SYS(0, 0, unveil("f1", "x"));
|
||||
ASSERT_SYS(0, 0, unveil("f1/f2", "r"));
|
||||
ASSERT_SYS(0, 0, unveil(0, 0));
|
||||
if (IsOpenbsd()) {
|
||||
// OpenBSD favors the most restrictive policy
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, stat("f1/f2/life.elf", &st));
|
||||
ASSERT_SYS(EACCES, -1,
|
||||
execv("f1/f2/life.elf", (char *[]){"f1/f2/life.elf", 0}));
|
||||
EXITS(0);
|
||||
} else {
|
||||
// Landlock (Linux) uses the union of policies
|
||||
SPAWN();
|
||||
ASSERT_SYS(0, 0, stat("f1/f2/life.elf", &st));
|
||||
execv("f1/f2/life.elf", (char *[]){"f1/f2/life.elf", 0});
|
||||
EXITS(42);
|
||||
}
|
||||
EXITS(0);
|
||||
}
|
Loading…
Add table
Reference in a new issue