Rewrite Linux pledge() code so it can be a payload

It's now possible to build our pledge() polyfill as a dynamic shared
object that can be injected into a glibc executable using LD_PRELOAD
This commit is contained in:
Justine Tunney 2022-08-08 11:41:08 -07:00
parent 7bd4179b9b
commit 0277d7d6e9
37 changed files with 1980 additions and 1600 deletions

View file

@ -123,6 +123,7 @@ int killpg(int, int);
int link(const char *, const char *) dontthrow;
int linkat(int, const char *, int, const char *, int);
int madvise(void *, uint64_t, int);
int memfd_create(const char *, unsigned int);
int mincore(void *, size_t, unsigned char *);
int mkdir(const char *, uint32_t);
int mkdirat(int, const char *, uint32_t);

View file

@ -188,11 +188,20 @@ o/$(MODE)/libc/calls/_timespec_frommicros.o: \
OVERRIDE_CFLAGS += \
-O2
o/$(MODE)/libc/calls/pledge.o \
o/$(MODE)/libc/calls/pledge-linux.o \
o/$(MODE)/libc/calls/unveil.o: \
OVERRIDE_CFLAGS += \
-DSTACK_FRAME_UNLIMITED
# we want -Os because:
# it makes a big difference
# we need pic because:
# so it can be an LD_PRELOAD payload
o/$(MODE)/libc/calls/pledge-linux.o: \
OVERRIDE_CFLAGS += \
-Os \
-fPIC
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS))

View file

@ -19,6 +19,8 @@
#include "libc/bits/likely.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/pledge.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -30,8 +32,6 @@
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
int sys_pledge_linux(unsigned long);
/**
* Replaces current process with program.
*
@ -72,7 +72,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
if (!IsWindows()) {
rc = 0;
if (IsLinux() && __execpromises && weaken(sys_pledge_linux)) {
rc = weaken(sys_pledge_linux)(__execpromises);
rc = weaken(sys_pledge_linux)(__execpromises, __pledge_mode, false);
}
if (!rc) {
rc = sys_execve(prog, argv, envp);

View file

@ -136,6 +136,6 @@ char *GetProgramExecutableName(void) {
return program_executable_name;
}
const void *const GetProgramExecutableNameCtor[] initarray = {
GetProgramExecutableName,
};
/* const void *const GetProgramExecutableNameCtor[] initarray = { */
/* GetProgramExecutableName, */
/* }; */

35
libc/calls/memfd_create.c Normal file
View file

@ -0,0 +1,35 @@
/*-*- 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/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
/**
* Creates anonymous file.
*
* @param name is used for the `/proc/self/fd/FD` symlink
* @param flags can have `MFD_CLOEXEC`, `MFD_ALLOW_SEALING`
* @raise ENOSYS if not RHEL8+
*/
int memfd_create(const char *name, unsigned int flags) {
int rc;
rc = sys_memfd_create(name, flags);
STRACE("memfd_create(%#s, %#x) → %d% m", name, flags, rc);
return rc;
}

View file

@ -0,0 +1,66 @@
/*-*- 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/pledge.internal.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
static int FindPromise(const char *name) {
int i;
for (i = 0; i < ARRAYLEN(kPledge); ++i) {
if (!strcasecmp(name, kPledge[i].name)) {
return i;
}
}
return -1;
}
/**
* Parses the arguments to pledge() into a bitmask.
*
* @return 0 on success, or -1 if invalid
*/
int ParsePromises(const char *promises, unsigned long *out) {
int rc = 0;
int promise;
unsigned long ipromises;
char *tok, *state, *start, buf[256];
if (promises) {
ipromises = -1;
if (memccpy(buf, promises, 0, sizeof(buf))) {
start = buf;
while ((tok = strtok_r(start, " \t\r\n", &state))) {
if ((promise = FindPromise(tok)) != -1) {
ipromises &= ~(1ULL << promise);
} else {
rc = -1;
break;
}
start = 0;
}
} else {
rc = -1;
}
} else {
ipromises = 0;
}
if (!rc) {
*out = ipromises;
}
return rc;
}

1625
libc/calls/pledge-linux.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

16
libc/calls/pledge.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_PLEDGE_H_
#define COSMOPOLITAN_LIBC_CALLS_PLEDGE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
enum PledgeMode {
kPledgeModeKillThread,
kPledgeModeKillProcess,
kPledgeModeErrno,
};
extern enum PledgeMode __pledge_mode;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_PLEDGE_H_ */

View file

@ -1,9 +1,20 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_
#include "libc/calls/pledge.h"
#include "libc/intrin/promises.internal.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int ParsePromises(const char *, unsigned long *);
struct Pledges {
const char *name;
const uint16_t *syscalls;
const size_t len;
};
hidden extern const struct Pledges kPledge[PROMISE_LEN_];
int sys_pledge_linux(unsigned long, enum PledgeMode, bool) hidden;
int ParsePromises(const char *, unsigned long *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -18,8 +18,10 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/getconsolectrlevent.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/kprintf.h"
@ -39,7 +41,7 @@ static textwindows inline bool HasWorkingConsole(void) {
}
/**
* Sends signal to this process.
* Sends signal to this thread.
*
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
* @return 0 on success or -1 w/ errno
@ -56,8 +58,7 @@ int raise(int sig) {
x = 1 / x;
rc = 0;
} else if (!IsWindows()) {
// XXX: should be tkill() or tgkill() on linux
rc = sys_kill(getpid(), sig, 1);
rc = sys_tkill(gettid(), sig, 0);
} else {
if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
// XXX: MSDN says "If this parameter is zero, the signal is

View file

@ -290,9 +290,9 @@ int sys_unveil_linux(const char *path, const char *permissions) {
* possible to use opendir() and go fishing for paths which weren't
* previously known.
*
* 5. Use ftruncate() rather than truncate(). One of the backdoors with
* Landlock is it currently can't restrict truncate() and setxattr()
* which permits certain kinds of modifications to files outside the
* 5. Use ftruncate() rather than truncate(). One issue Landlock hasn't
* addressed yet is restrictions over truncate() and setxattr() which
* could permit certain kinds of modifications to files outside the
* sandbox. When your policy is committed, we install a SECCOMP BPF
* filter to disable those calls, however similar trickery may be
* possible through other unaddressed calls like ioctl(). Using the

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/intrin/promises.internal.h"
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
@ -39,11 +40,18 @@ privileged wontreturn void _Exit(int exitcode) {
int i;
STRACE("_Exit(%d)", exitcode);
if (!IsWindows() && !IsMetal()) {
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit_group), "D"(exitcode)
: "rcx", "r11", "memory");
// this should only be possible on Linux in a pledge ultra sandbox
// On Linux _Exit1 (exit) must be called in pledge("") mode. If we
// call _Exit (exit_group) when we haven't used pledge("stdio") then
// it'll terminate the process instead. On OpenBSD we must not call
// _Exit1 (__threxit) because only _Exit (exit) is whitelisted when
// operating in pledge("") mode.
if (!(IsLinux() && !PLEDGED(STDIO))) {
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit_group), "D"(exitcode)
: "rcx", "r11", "memory");
}
// Inline _Exit1() just in case _Exit() isn't allowed by pledge()
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit), "D"(exitcode)

View file

@ -19,6 +19,7 @@
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h"
#include "libc/nt/thread.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/nr.h"
@ -35,6 +36,12 @@ privileged wontreturn void _Exit1(int rc) {
struct WinThread *wt;
STRACE("_Exit1(%d)", rc);
if (!IsWindows() && !IsMetal()) {
if (IsOpenbsd() && !PLEDGED(STDIO)) {
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit), "D"(rc)
: "rcx", "r11", "memory");
}
asm volatile("xor\t%%r10d,%%r10d\n\t"
"syscall"
: /* no outputs */

View file

@ -16,9 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/pledge.h"
#include "libc/intrin/promises.internal.h"
// XXX: should be inherited thread local
unsigned __pledge_mode;
// see also sys_pledge_linux() which is 100% pure
enum PledgeMode __pledge_mode;
unsigned long __promises;
unsigned long __execpromises;

View file

@ -22,6 +22,7 @@
#define PROMISE_PROT_EXEC 18
#define PROMISE_VMINFO 19
#define PROMISE_TMPPATH 20
#define PROMISE_LEN_ 21
#define PLEDGED(x) ((~__promises >> PROMISE_##x) & 1)

View file

@ -17,7 +17,6 @@ extern intptr_t __oldstack; /* CRT */
extern uint64_t __nosync; /* SYS */
extern _Atomic(int) __ftrace; /* SYS */
extern _Atomic(int) __strace; /* SYS */
extern uint32_t __pledge_mode; /* SYS */
extern char *program_invocation_name; /* RII */
extern char *program_invocation_short_name; /* RII */
extern uint64_t __syscount; /* RII */

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall memfd_create,0xfffffffffffff13f,globl

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_memfd_create,0xfffffffffffff13f,globl,hidden

View file

@ -417,14 +417,6 @@ syscon at AT_REMOVEDIR 0x0200 0x80 0x0800 8 0x800 0x0200 # faked
syscon at AT_EACCESS 0x0200 0x10 0x0100 1 0x100 0 # performs check using effective uid/gid; unnecessary nt
syscon at AT_EMPTY_PATH 0x1000 0 0 0 0 0 # linux 2.6.39+; see unlink, O_TMPFILE, etc.
# memfd_create() flags
#
# Unsupported flags are encoded as 0.
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon memfd MFD_CLOEXEC 1 0 0 0 0 0
syscon memfd MFD_ALLOW_SEALING 2 0 0 0 0 0
# utimensat() special values
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon termios,CANBSIZ,255,0,0,0,0,0

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,IPPORT_RESERVED,0x0400,0x0400,0x0400,0x0400,0x0400,0x0400

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon memfd,MFD_ALLOW_SEALING,2,0,0,0,0,0

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon memfd,MFD_CLOEXEC,1,0,0,0,0,0

View file

@ -1,16 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_MFD_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_MFD_H_
#include "libc/runtime/symbolic.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const unsigned int MFD_CLOEXEC;
extern const unsigned int MFD_ALLOW_SEALING;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#define MFD_CLOEXEC SYMBOLIC(MFD_CLOEXEC)
#define MFD_ALLOW_SEALING SYMBOLIC(MFD_ALLOW_SEALING)
#define MFD_CLOEXEC 1
#define MFD_ALLOW_SEALING 2
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MFD_H_ */

View file

@ -350,7 +350,7 @@ scall sched_getattr 0xfffffffffffff13b globl # ├─ karen sandler requires s
scall renameat2 0xfffffffffffff13c globl # └─ debian founder ian murdock found strangled with vacuum cord
#scall seccomp 0xfffffffffffff13d globl # wrapped manually
scall sys_getrandom 0xfff00723321f413e globl hidden # Linux 3.17+ and getentropy() on XNU/OpenBSD, coming to NetBSD in 9.2
scall memfd_create 0xfffffffffffff13f globl # wut
scall sys_memfd_create 0xfffffffffffff13f globl hidden
scall kexec_file_load 0xfffffffffffff140 globl
scall bpf 0xfffffffffffff141 globl
scall execveat 0xfffffffffffff142 globl

View file

@ -17,10 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/struct/seccomp.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
@ -58,7 +60,7 @@ void SetUp(void) {
TEST(pledge, testSoftError) {
if (IsOpenbsd()) return;
SPAWN(fork);
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
__pledge_mode = kPledgeModeErrno;
ASSERT_SYS(0, 0, pledge("stdio", 0));
ASSERT_SYS(EPERM, -1, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
_Exit(7);
@ -67,27 +69,27 @@ TEST(pledge, testSoftError) {
TEST(pledge, testKillThreadMode) {
SPAWN(fork);
__pledge_mode = SECCOMP_RET_KILL_THREAD;
__pledge_mode = kPledgeModeKillThread;
ASSERT_SYS(0, 0, pledge("stdio", 0));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
TERMS(SIGABRT);
}
TEST(pledge, testKillProcessMode) {
SPAWN(fork);
__pledge_mode = SECCOMP_RET_KILL_PROCESS;
__pledge_mode = kPledgeModeKillProcess;
ASSERT_SYS(0, 0, pledge("stdio", 0));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
TERMS(SIGABRT);
}
TEST(pledge, testLogMessage_onSoftyMode) {
TEST(pledge, testLogMessage_inSoftyMode) {
if (IsOpenbsd()) return;
int fds[2];
char msg[64] = {0};
ASSERT_SYS(0, 0, pipe(fds));
SPAWN(fork);
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
__pledge_mode = kPledgeModeErrno;
ASSERT_SYS(0, 2, dup2(fds[1], 2));
ASSERT_SYS(0, 0, pledge("stdio", 0));
ASSERT_SYS(EPERM, -1, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
@ -105,11 +107,11 @@ TEST(pledge, testLogMessage_onKillProcess) {
char msg[64] = {0};
ASSERT_SYS(0, 0, pipe(fds));
SPAWN(fork);
__pledge_mode = SECCOMP_RET_KILL;
__pledge_mode = kPledgeModeKillThread;
ASSERT_SYS(0, 2, dup2(fds[1], 2));
ASSERT_SYS(0, 0, pledge("stdio", 0));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
TERMS(SIGABRT);
close(fds[1]);
read(fds[0], msg, sizeof(msg));
close(fds[0]);
@ -118,7 +120,7 @@ TEST(pledge, testLogMessage_onKillProcess) {
}
}
TEST(pledge, testNoLogPossibleSadly_becausePledgedExec) {
TEST(pledge, testNoLogOrAbrtsignoPossibleSadly_becausePledgedExec) {
int fds[2];
char msg[64] = {0};
ASSERT_SYS(0, 0, pipe(fds));
@ -132,3 +134,11 @@ TEST(pledge, testNoLogPossibleSadly_becausePledgedExec) {
close(fds[0]);
ASSERT_STREQ("", msg);
}
TEST(pledge, testDoublePledge_isFine) {
SPAWN(fork);
__pledge_mode = kPledgeModeKillThread;
ASSERT_SYS(0, 0, pledge("stdio", 0));
ASSERT_SYS(0, 0, pledge("stdio", 0));
EXITS(0);
}

View file

@ -60,10 +60,6 @@ STATIC_YOINK("zip_uri_support");
char testlib_enable_tmp_setup_teardown;
__attribute__((__constructor__)) static void init(void) {
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
}
void OnSig(int sig) {
// do nothing
}
@ -89,6 +85,7 @@ void SetUp(void) {
if (!__is_linux_2_6_23() && !IsOpenbsd()) exit(0);
ASSERT_SYS(0, 0, extract("/zip/life.elf", "life.elf", 0755));
ASSERT_SYS(0, 0, extract("/zip/sock.elf", "sock.elf", 0755));
__pledge_mode = kPledgeModeErrno;
}
TEST(pledge, default_allowsExit) {
@ -112,11 +109,13 @@ TEST(pledge, default_allowsExit) {
EXPECT_SYS(0, 0, munmap(job, FRAMESIZE));
}
#if 0
TEST(pledge, execpromises_notok) {
if (IsOpenbsd()) return; // b/c testing linux bpf
int ws, pid;
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
__pledge_mode = kPledgeModeErrno;
ASSERT_SYS(0, 0, pledge("stdio rpath exec", "stdio"));
execl("sock.elf", "sock.elf", 0);
_Exit(127);
@ -157,8 +156,8 @@ TEST(pledge, stdio_forbidsOpeningPasswd1) {
}
TEST(pledge, stdio_forbidsOpeningPasswd2) {
if (!IsOpenbsd()) return;
int ws, pid;
__pledge_mode = kPledgeModeKillProcess;
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
ASSERT_SYS(0, 0, pledge("stdio", 0));
@ -558,7 +557,6 @@ TEST(pledge_linux, execpromisesIsSuperset_notPossible) {
}
TEST(pledge_openbsd, execpromises_notok) {
if (!IsOpenbsd()) return;
int ws, pid;
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
@ -567,8 +565,15 @@ TEST(pledge_openbsd, execpromises_notok) {
_Exit(127);
}
EXPECT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFSIGNALED(ws));
EXPECT_EQ(SIGABRT, WTERMSIG(ws));
if (IsOpenbsd()) {
EXPECT_TRUE(WIFSIGNALED(ws));
EXPECT_EQ(SIGABRT, WTERMSIG(ws));
} else {
// linux can't be consistent here since we pledged exec
// so we return EPERM instead and sock.elf passes along
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(128 + EPERM, WEXITSTATUS(ws));
}
}
TEST(pledge_openbsd, bigSyscalls) {
@ -658,3 +663,4 @@ BENCH(pledge, bench) {
}
wait(0);
}
#endif

View file

@ -373,7 +373,7 @@ TEST(unveil, usedTwice_forbidden_worksWithPledge) {
ASSERT_NE(-1, wait(&ws));
ASSERT_TRUE(*gotsome);
ASSERT_TRUE(WIFSIGNALED(ws));
ASSERT_EQ(IsOpenbsd() ? SIGABRT : SIGSYS, WTERMSIG(ws));
ASSERT_EQ(SIGABRT, WTERMSIG(ws));
EXPECT_SYS(0, 0, munmap(gotsome, FRAMESIZE));
}

View file

@ -116,7 +116,7 @@ elif [ "$1" = ape_assimilated_test_suite ]; then
startit ape assimilated curl.com
cp o//examples/curl.com $t/assimilated
o//tool/build/assimilate.com $t/assimilated/curl.com
[ "$(o/$m/tool/build/pledge.com -p 'stdio inet dns' $t/assimilated/curl.com https://justine.lol/hello.txt)" = "hello world" ]
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath inet dns' $t/assimilated/curl.com https://justine.lol/hello.txt)" = "hello world" ]
checkem
elif [ "$1" = ape_native_test_suite ]; then
@ -131,7 +131,7 @@ elif [ "$1" = ape_native_test_suite ]; then
checkem
startit ape native curl.com
[ "$(o/$m/tool/build/pledge.com -p 'stdio inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
checkem
elif [ "$1" = setuid_test_suite ]; then
@ -146,23 +146,23 @@ elif [ "$1" = setuid_test_suite ]; then
checkem
startit setuid curl.com
[ "$($t/pledge.com -p 'stdio inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
[ "$($t/pledge.com -p 'stdio rpath inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
checkem
startit setuid getuid
[ "$($t/pledge.com -pstdio o/$m/examples/printargs.com 2>&1 | grep getuid | grep -o [[:digit:]]*)" = "$(id -u)" ]
[ "$($t/pledge.com -p 'stdio rpath proc tty' o/$m/examples/printargs.com 2>&1 | grep getuid | grep -o [[:digit:]]*)" = "$(id -u)" ]
checkem
startit setuid geteuid
[ "$($t/pledge.com -pstdio o/$m/examples/printargs.com 2>&1 | grep geteuid | grep -o [[:digit:]]*)" = "$(id -u)" ]
[ "$($t/pledge.com -p 'stdio rpath proc tty' o/$m/examples/printargs.com 2>&1 | grep geteuid | grep -o [[:digit:]]*)" = "$(id -u)" ]
checkem
startit setuid no capabilities
[ "$($t/pledge.com -pstdio o/$m/examples/printargs.com 2>&1 | grep CAP_ | wc -l)" = 0 ]
[ "$($t/pledge.com -p 'stdio rpath proc tty' o/$m/examples/printargs.com 2>&1 | grep CAP_ | wc -l)" = 0 ]
checkem
startit setuid maximum nice
$t/pledge.com -np 'stdio proc' o/$m/examples/printargs.com 2>&1 | grep SCHED_IDLE >/dev/null
$t/pledge.com -np 'stdio rpath proc tty' o/$m/examples/printargs.com 2>&1 | grep SCHED_IDLE >/dev/null
checkem
startit setuid chroot

View file

@ -82,7 +82,7 @@ function UnixTest()
unix.close(reader)
pid, ws = assert(unix.wait())
assert(unix.WIFSIGNALED(ws))
assert(unix.WTERMSIG(ws) == unix.SIGSYS)
assert(unix.WTERMSIG(ws) == unix.SIGABRT)
elseif GetHostOs() == "OPENBSD" then
if assert(unix.fork()) == 0 then
assert(unix.pledge("stdio"))

View file

@ -69,10 +69,10 @@ o/$(MODE)/third_party/chibicc/test/%.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(THIRD_PARTY_CHIBICC_TEST_OBJS): CC = $(CHIBICC)
$(THIRD_PARTY_CHIBICC_TEST_OBJS): $(CHIBICC)
.PRECIOUS: $(THIRD_PARTY_CHIBICC_TEST_OBJS)
o/$(MODE)/third_party/chibicc/test/%.o: \
third_party/chibicc/test/%.c \
$(CHIBICC)
@$(COMPILE) -AOBJECTIFY.c $(CHIBICC) $(CHIBICC_FLAGS) $(OUTPUT_OPTION) -c $<
o/$(MODE)/third_party/chibicc/test/int128_test.o: QUOTA = -M1024m

View file

@ -21,6 +21,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/makedev.h"
#include "libc/calls/pledge.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/dirent.h"

View file

@ -117,6 +117,25 @@ o/$(MODE)/tool/build/printf.zip.o: o/$(MODE)/tool/build/printf
o/$(MODE)/tool/build/dd.zip.o: o/$(MODE)/tool/build/dd
@$(COMPILE) -AZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) -0 -B -Pbin $(OUTPUT_OPTION) $<
# we need pic because:
# so it can be an LD_PRELOAD payload
o/$(MODE)/tool/build/sandbox.o: \
OVERRIDE_CFLAGS += \
-fPIC
o/$(MODE)/tool/build/sandbox.so: \
o/$(MODE)/tool/build/sandbox.o \
o/$(MODE)/libc/calls/pledge-linux.o \
o/$(MODE)/libc/sysv/restorert.o
@$(COMPILE) -ALINK.so \
$(CC) \
-s \
-shared \
-nostdlib \
-Wl,--gc-sections \
$(LINKARGS) \
$(OUTPUT_OPTION)
.PHONY: o/$(MODE)/tool/build
o/$(MODE)/tool/build: \
o/$(MODE)/tool/build/emucrt \

View file

@ -20,6 +20,7 @@
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/landlock.h"
#include "libc/calls/pledge.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/seccomp.h"
@ -73,6 +74,7 @@ usage: pledge.com [-hnN] PROG ARGS...\n\
-u UID call setuid()\n\
-c PATH call chroot()\n\
-v [PERM:]PATH call unveil(PATH, PERM[rwxc])\n\
-k kill process rather than eperm'ing\n\
-n set maximum niceness\n\
-D don't drop capabilities\n\
-N don't normalize file descriptors\n\
@ -118,6 +120,7 @@ int ParsePromises(const char *, unsigned long *);
int g_gflag;
int g_uflag;
int g_kflag;
int g_hflag;
bool g_nice;
bool g_noclose;
@ -140,14 +143,16 @@ static void GetOpts(int argc, char *argv[]) {
g_promises = 0;
g_fszquota = 256 * 1000 * 1000;
g_proquota = GetCpuCount() * 100;
g_fszquota = 4 * 1000 * 1000 * 1000;
g_memquota = 4L * 1024 * 1024 * 1024;
if (!sysinfo(&si)) g_memquota = si.totalram;
while ((opt = getopt(argc, argv, "hnNp:u:g:c:C:D:P:M:F:v:")) != -1) {
while ((opt = getopt(argc, argv, "hnkNp:u:g:c:C:D:P:M:F:v:")) != -1) {
switch (opt) {
case 'n':
g_nice = true;
break;
case 'k':
g_kflag = true;
break;
case 'N':
g_noclose = true;
break;
@ -453,10 +458,12 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
if (~ipromises & (1ul << PROMISE_PROT_EXEC)) {
if (UnveilIfExists("/usr/bin/ape", "rx") == -1) {
UnveilIfExists(xjoinpaths(firstnonnull(getenv("TMPDIR"),
firstnonnull(getenv("HOME"), ".")),
".ape"),
"rx");
if ((p = getenv("TMPDIR"))) {
UnveilIfExists(xjoinpaths(p, ".ape"), "rx");
}
if ((p = getenv("HOME"))) {
UnveilIfExists(xjoinpaths(p, ".ape"), "rx");
}
}
}
@ -671,7 +678,11 @@ int main(int argc, char *argv[]) {
// model. we do this becasue it's only possible to have sigsys print
// crash messages if we're not pledging exec, which is what this tool
// always has to do currently.
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
if (g_kflag) {
__pledge_mode = kPledgeModeKillProcess;
} else {
__pledge_mode = kPledgeModeErrno;
}
// apply sandbox
if (pledge(g_promises, g_promises) == -1) {

28
tool/build/sandbox.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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/pledge.h"
#include "libc/calls/pledge.internal.h"
#include "libc/intrin/promises.internal.h"
hidden char __privileged_start;
hidden char __privileged_end;
__attribute__((__constructor__)) void InitializeSandbox(void) {
sys_pledge_linux(~(1ul << PROMISE_STDIO), kPledgeModeErrno, false);
}

View file

@ -21,6 +21,7 @@
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/pledge.h"
#include "libc/calls/struct/dirent.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/iovec.h"
@ -6587,7 +6588,7 @@ static void UnveilRedbean(void) {
}
static int EnableSandbox(void) {
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
__pledge_mode = kPledgeModeErrno;
switch (sandboxed) {
case 0:
return 0;