Improve upon the new pledge command

This commit is contained in:
Justine Tunney 2022-07-14 04:32:33 -07:00
parent b707fca77a
commit 1d744ea11b
13 changed files with 382 additions and 64 deletions

View file

@ -111,6 +111,8 @@ int getresuid(uint32_t *, uint32_t *, uint32_t *);
int getsid(int) nosideeffect libcesque;
int gettid(void) libcesque;
int getuid(void) nosideeffect libcesque;
int ioprio_get(int, int);
int ioprio_set(int, int, int);
int kill(int, int);
int killpg(int, int);
int link(const char *, const char *) dontthrow;

View file

@ -43,6 +43,11 @@ privileged wontreturn void _Exit(int exitcode) {
: /* no outputs */
: "a"(__NR_exit_group), "D"(exitcode)
: "rcx", "r11", "memory");
// this should only be possible on Linux in a pledge ultra sandbox
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit), "D"(exitcode)
: "rcx", "r11", "memory");
} else if (IsWindows()) {
ExitProcess(exitcode);
}

View file

@ -59,11 +59,11 @@ struct Filter {
};
static const uint16_t kPledgeLinuxDefault[] = {
__NR_linux_exit, //
__NR_linux_exit_group, //
__NR_linux_exit, //
};
static const uint16_t kPledgeLinuxStdio[] = {
__NR_linux_exit_group, //
__NR_linux_clock_getres, //
__NR_linux_clock_gettime, //
__NR_linux_clock_nanosleep, //
@ -1132,7 +1132,7 @@ static int sys_pledge_linux(const char *promises, const char *execpromises) {
* `promises` is a string that may include any of the following groups
* delimited by spaces.
*
* - "stdio" allows close, dup, dup2, dup3, fchdir, fstat, fsync,
* - "stdio" allows exit, close, dup, dup2, dup3, fchdir, fstat, fsync,
* fdatasync, ftruncate, getdents, getegid, getrandom, geteuid,
* getgid, getgroups, getitimer, getpgid, getpgrp, getpid, getppid,
* getresgid, getresuid, getrlimit, getsid, wait4, gettimeofday,

View file

@ -48,6 +48,23 @@
#define STATIC_STACK_ADDR(ADDR) \
STATIC_SYMBOL("ape_stack_vaddr", _STACK_STRINGIFY(ADDR))
/**
* Makes program stack executable if declared, e.g.
*
* STATIC_EXEC_STACK();
* int main() {
* char code[16] = {
* 0x55, // push %rbp
* 0xb8, 0007, 0x00, 0x00, 0x00, // mov $7,%eax
* 0x5d, // push %rbp
* 0xc3, // ret
* };
* int (*func)(void) = (void *)code;
* printf("result %d should be 7\n", func());
* }
*/
#define STATIC_EXEC_STACK() STATIC_SYMBOL("ape_stack_pf", "7")
#define _STACK_STRINGIFY(ADDR) #ADDR
#if IsAsan()

30
libc/sysv/consts/ioprio.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_IOPRIO_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_IOPRIO_H_
#define IOPRIO_WHO_PROCESS 1
#define IOPRIO_WHO_PGRP 2
#define IOPRIO_WHO_USER 3
#define IOPRIO_CLASS_SHIFT 13
#define IOPRIO_CLASS_MASK 0x07
#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)
#define IOPRIO_PRIO_CLASS(ioprio) \
(((ioprio) >> IOPRIO_CLASS_SHIFT) & IOPRIO_CLASS_MASK)
#define IOPRIO_PRIO_DATA(ioprio) ((ioprio)&IOPRIO_PRIO_MASK)
#define IOPRIO_PRIO_VALUE(class, data) \
((((class) & IOPRIO_CLASS_MASK) << IOPRIO_CLASS_SHIFT) | \
((data)&IOPRIO_PRIO_MASK))
#define IOPRIO_CLASS_NONE 0
#define IOPRIO_CLASS_RT 1
#define IOPRIO_CLASS_BE 2
#define IOPRIO_CLASS_IDLE 3
#define IOPRIO_NR_LEVELS 8
#define IOPRIO_BE_NR IOPRIO_NR_LEVELS
#define IOPRIO_NORM 4
#define IOPRIO_BE_NORM IOPRIO_NORM
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_IOPRIO_H_ */

View file

@ -46,6 +46,7 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/sock.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/spawn.h"
char testlib_enable_tmp_setup_teardown;
@ -60,15 +61,40 @@ void SetUp(void) {
}
TEST(pledge, default_allowsExit) {
int *job;
int ws, pid;
ASSERT_NE(-1, (pid = fork()));
// create small shared memory region
ASSERT_NE(-1, (job = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0)));
job[0] = 2; // create workload
job[1] = 2;
ASSERT_NE(-1, (pid = fork())); // create enclaved worker
if (!pid) {
ASSERT_SYS(0, 0, pledge("", 0));
job[0] = job[0] + job[1]; // do work
_Exit(0);
}
EXPECT_NE(-1, wait(&ws));
EXPECT_NE(-1, wait(&ws)); // wait for worker
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(0, WEXITSTATUS(ws));
EXPECT_EQ(4, job[0]); // check result
EXPECT_SYS(0, 0, munmap(job, FRAMESIZE));
}
int Enclave(void *arg, int tid) {
ASSERT_SYS(0, 0, pledge("", 0));
int *job = arg; // get job
job[0] = job[0] + job[1]; // do work
return 0; // exit
}
TEST(pledge, withThreadMemory) {
if (IsOpenbsd()) return; // openbsd doesn't allow it, wisely
struct spawn worker;
int job[2] = {2, 2}; // create workload
ASSERT_SYS(0, 0, _spawn(Enclave, job, &worker)); // create worker
ASSERT_SYS(0, 0, _join(&worker)); // wait for exit
EXPECT_EQ(4, job[0]); // check result
}
TEST(pledge, stdio_forbidsOpeningPasswd1) {

View file

@ -42,6 +42,7 @@ TEST_LIBC_MEM_DIRECTDEPS = \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_THREAD \
LIBC_TESTLIB \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_LIBCXX

View file

@ -0,0 +1,59 @@
/*-*- 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/struct/sigaction.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
/**
* @fileoverview test non-executable stack is default
*/
jmp_buf jb;
void EscapeSegfault(int sig) {
longjmp(jb, 666);
}
TEST(xstack, test) {
struct sigaction old[2];
struct sigaction sa = {
.sa_handler = EscapeSegfault,
.sa_flags = SA_NODEFER,
};
sigaction(SIGSEGV, &sa, old + 0);
sigaction(SIGBUS, &sa, old + 1);
char code[16] = {
0x55, // push %rbp
0xb8, 0007, 0x00, 0x00, 0x00, // mov $7,%eax
0x5d, // push %rbp
0xc3, // ret
};
int (*func)(void) = (void *)code;
int rc;
if (!(rc = setjmp(jb))) {
func();
abort();
}
ASSERT_EQ(666, rc);
sigaction(SIGBUS, old + 1, 0);
sigaction(SIGSEGV, old + 0, 0);
}

View file

@ -0,0 +1,41 @@
/*-*- 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/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/testlib/testlib.h"
/**
* @fileoverview tests executable stack
*/
STATIC_EXEC_STACK();
TEST(xstack, test) {
char code[16] = {
0x55, // push %rbp
0xb8, 0007, 0x00, 0x00, 0x00, // mov $7,%eax
0x5d, // push %rbp
0xc3, // ret
};
int (*func)(void) = (void *)code;
ASSERT_EQ(7, func());
}

View file

@ -37,10 +37,12 @@ TEST_LIBC_NEXGEN32E_DIRECTDEPS = \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_TESTLIB \
LIBC_UNICODE \
LIBC_X \
TOOL_VIZ_LIB
TOOL_VIZ_LIB \
THIRD_PARTY_XED
TEST_LIBC_NEXGEN32E_DEPS := \
$(call uniq,$(foreach x,$(TEST_LIBC_NEXGEN32E_DIRECTDEPS),$($(x))))
@ -58,6 +60,13 @@ o/$(MODE)/test/libc/nexgen32e/%.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
# we can't run this test on openbsd because rwx memory isn't allowed
o/$(MODE)/test/libc/nexgen32e/stackrwx_test.com.ok: \
o/$(MODE)/tool/build/runit.com \
o/$(MODE)/tool/build/runitd.com \
o/$(MODE)/test/libc/nexgen32e/stackrwx_test.com
@$(COMPILE) -ATEST -tT$@ $^ $(filter-out openbsd,$(HOSTS))
$(TEST_LIBC_NEXGEN32E_OBJS): \
DEFAULT_CCFLAGS += \
-fno-builtin

View file

@ -126,21 +126,21 @@ void GetElfHeader(char ehdr[hasatleast 64], const char *image) {
ehdr[i++] = c;
} else {
kprintf("%s: ape printf elf header too long\n", prog);
exit(__COUNTER__);
exit(1);
}
}
if (i != 64) {
kprintf("%s: ape printf elf header too short\n", prog);
exit(__COUNTER__);
exit(2);
}
if (READ32LE(ehdr) != READ32LE("\177ELF")) {
kprintf("%s: ape printf elf header didn't have elf magic\n", prog);
exit(__COUNTER__);
exit(3);
}
return;
}
kprintf("%s: printf statement not found in first 4096 bytes\n", prog);
exit(__COUNTER__);
exit(4);
}
void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
@ -151,7 +151,7 @@ void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
int rc, skip, count, bs, offset, size;
if (!(script = memmem(image, imagesize, "'\n#'\"\n", 6))) {
kprintf("%s: ape shell script not found\n", prog);
exit(__COUNTER__);
exit(5);
}
script += 6;
DCHECK_EQ(REG_OK, regcomp(&rx,
@ -163,11 +163,11 @@ void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
if (rc != REG_OK) {
if (rc == REG_NOMATCH) {
kprintf("%s: ape macho dd command not found\n", prog);
exit(__COUNTER__);
exit(6);
}
regerror(rc, &rx, errstr, sizeof(errstr));
kprintf("%s: ape macho dd regex failed: %s\n", prog, errstr);
exit(__COUNTER__);
exit(7);
}
bs = atoi(script + rm[1].rm_so);
skip = atoi(script + rm[2].rm_so);
@ -175,23 +175,23 @@ void GetMachoPayload(const char *image, size_t imagesize, int *out_offset,
if (__builtin_mul_overflow(skip, bs, &offset) ||
__builtin_mul_overflow(count, bs, &size)) {
kprintf("%s: integer overflow parsing macho\n");
exit(__COUNTER__);
exit(8);
}
if (offset < 64) {
kprintf("%s: ape macho dd offset should be ≥64: %d\n", prog, offset);
exit(__COUNTER__);
exit(9);
}
if (offset >= imagesize) {
kprintf("%s: ape macho dd offset is outside file: %d\n", prog, offset);
exit(__COUNTER__);
exit(10);
}
if (size < 32) {
kprintf("%s: ape macho dd size should be ≥32: %d\n", prog, size);
exit(__COUNTER__);
exit(11);
}
if (size > imagesize - offset) {
kprintf("%s: ape macho dd size is outside file: %d\n", prog, size);
exit(__COUNTER__);
exit(12);
}
*out_offset = offset;
*out_size = size;
@ -218,20 +218,20 @@ void Assimilate(void) {
struct stat st;
if ((fd = open(prog, O_RDWR)) == -1) {
kprintf("%s: open(O_RDWR) failed: %m\n", prog);
exit(__COUNTER__);
exit(13);
}
if (fstat(fd, &st) == -1) {
kprintf("%s: fstat() failed: %m\n", prog);
exit(__COUNTER__);
exit(14);
}
if (st.st_size < 8192) {
kprintf("%s: ape binaries must be at least 4096 bytes\n", prog);
exit(__COUNTER__);
exit(15);
}
if ((p = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
MAP_FAILED) {
kprintf("%s: mmap failed: %m\n", prog);
exit(__COUNTER__);
exit(16);
}
if (READ32LE(p) == READ32LE("\177ELF")) {
if (!g_force) {
@ -253,7 +253,7 @@ void Assimilate(void) {
}
if (READ64LE(p) != READ64LE("MZqFpD='")) {
kprintf("%s: this file is not an actually portable executable\n", prog);
exit(__COUNTER__);
exit(17);
}
if (g_mode == MODE_ELF) {
AssimilateElf(p, st.st_size);
@ -263,7 +263,7 @@ void Assimilate(void) {
Finish:
if (munmap(p, st.st_size) == -1) {
kprintf("%s: munmap() failed: %m\n", prog);
exit(__COUNTER__);
exit(18);
}
}

View file

@ -777,7 +777,7 @@ int main(int argc, char *argv[]) {
ccversion = atoi(optarg);
break;
case 'P':
proquota = sizetol(optarg, 1024);
proquota = atoi(optarg);
break;
case 'F':
fszquota = sizetol(optarg, 1000);

View file

@ -18,35 +18,47 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/math.h"
#include "libc/nexgen32e/kcpuids.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ioprio.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/poll.h"
#include "libc/sysv/consts/prio.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/sched.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
STATIC_YOINK("strerror_wr");
#define GETOPTS "hFp:u:g:c:"
#define USAGE \
"\
usage: pledge.com [-h] PROG ARGS...\n\
-h show help\n\
-g GID call setgid()\n\
-u UID call setuid()\n\
-c PATH call chroot()\n\
-F don't normalize file descriptors\n\
-p PLEDGE may contain any of following separated by spaces\n\
usage: pledge.com [-hnN] PROG ARGS...\n\
-h show help\n\
-g GID call setgid()\n\
-u UID call setuid()\n\
-c PATH call chroot()\n\
-n maximum niceness\n\
-N don't normalize file descriptors\n\
-C SECS set cpu limit [default: inherited]\n\
-M BYTES set virtual memory limit [default: 4gb]\n\
-P PROCS set process limit [default: GetCpuCount()*2]\n\
-F BYTES set individual file size limit [default: 4gb]\n\
-p PLEDGE may contain any of following separated by spaces\n\
- stdio: allow stdio and benign system calls\n\
- rpath: read-only path ops\n\
- wpath: write path ops\n\
@ -80,17 +92,29 @@ the https://justine.lol/pledge/ page for online documentation.\n\
int g_gflag;
int g_uflag;
int g_hflag;
bool g_nice;
bool g_noclose;
const char *g_pflag;
long g_cpuquota;
long g_fszquota;
long g_memquota;
long g_proquota;
const char *g_chroot;
const char *g_promises;
static void GetOpts(int argc, char *argv[]) {
int opt;
g_pflag = "";
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
g_promises = 0;
g_proquota = GetCpuCount() * 2;
g_fszquota = 256 * 1000 * 1000;
g_fszquota = 4 * 1000 * 1000 * 1000;
g_memquota = 4L * 1024 * 1024 * 1024;
while ((opt = getopt(argc, argv, "hnNp:u:g:c:C:P:M:F:")) != -1) {
switch (opt) {
case 'p':
g_pflag = optarg;
case 'n':
g_nice = true;
break;
case 'N':
g_noclose = true;
break;
case 'c':
g_chroot = optarg;
@ -101,8 +125,24 @@ static void GetOpts(int argc, char *argv[]) {
case 'u':
g_uflag = atoi(optarg);
break;
case 'C':
g_cpuquota = atoi(optarg);
break;
case 'P':
g_proquota = atoi(optarg);
break;
case 'M':
g_memquota = sizetol(optarg, 1024);
break;
case 'F':
g_noclose = true;
g_fszquota = sizetol(optarg, 1000);
break;
case 'p':
if (g_promises) {
g_promises = xstrcat(g_promises, ' ', optarg);
} else {
g_promises = optarg;
}
break;
case 'h':
case '?':
@ -113,13 +153,20 @@ static void GetOpts(int argc, char *argv[]) {
exit(64);
}
}
if (!g_promises) {
g_promises = "stdio rpath execnative";
}
g_promises = xstrcat(g_promises, ' ', "execnative");
}
const char *prog;
char pledges[1024];
char pathbuf[PATH_MAX];
struct pollfd pfds[256];
int GetBaseCpuFreqMhz(void) {
return KCPUIDS(16H, EAX) & 0x7fff;
}
int GetPollMaxFds(void) {
int n;
struct rlimit rl;
@ -140,17 +187,17 @@ void NormalizeFileDescriptors(void) {
}
if (poll(pfds, n, 0) == -1) {
kprintf("error: poll() failed: %m\n");
exit(__COUNTER__);
exit(1);
}
for (i = 0; i < 3; ++i) {
if (pfds[i].revents & POLLNVAL) {
if ((fd = open("/dev/null", O_RDWR)) == -1) {
kprintf("error: open(\"/dev/null\") failed: %m\n");
exit(__COUNTER__);
exit(2);
}
if (fd != i) {
kprintf("error: open() is broken: %d vs. %d\n", fd, i);
exit(__COUNTER__);
exit(3);
}
}
}
@ -158,12 +205,87 @@ void NormalizeFileDescriptors(void) {
if (~pfds[i].revents & POLLNVAL) {
if (close(pfds[i].fd) == -1) {
kprintf("error: close(%d) failed: %m\n", pfds[i].fd);
exit(__COUNTER__);
exit(4);
}
}
}
}
void SetCpuLimit(int secs) {
int mhz, lim;
struct rlimit rlim;
if (secs <= 0) return;
if (!(mhz = GetBaseCpuFreqMhz())) return;
lim = ceil(3100. / mhz * secs);
rlim.rlim_cur = lim;
rlim.rlim_max = lim + 1;
if (setrlimit(RLIMIT_CPU, &rlim) != -1) {
return;
} else if (getrlimit(RLIMIT_CPU, &rlim) != -1) {
if (lim < rlim.rlim_cur) {
rlim.rlim_cur = lim;
if (setrlimit(RLIMIT_CPU, &rlim) != -1) {
return;
}
}
}
kprintf("error: setrlimit(RLIMIT_CPU) failed: %m\n");
exit(20);
}
void SetFszLimit(long n) {
struct rlimit rlim;
if (n <= 0) return;
rlim.rlim_cur = n;
rlim.rlim_max = n << 1;
if (setrlimit(RLIMIT_FSIZE, &rlim) != -1) {
return;
} else if (getrlimit(RLIMIT_FSIZE, &rlim) != -1) {
rlim.rlim_cur = n;
if (setrlimit(RLIMIT_FSIZE, &rlim) != -1) {
return;
}
}
kprintf("error: setrlimit(RLIMIT_FSIZE) failed: %m\n");
exit(21);
}
void SetMemLimit(long n) {
struct rlimit rlim = {n, n};
if (n <= 0) return;
if (setrlimit(RLIMIT_AS, &rlim) == -1) {
kprintf("error: setrlimit(RLIMIT_AS) failed: %m\n");
exit(22);
}
}
void SetProLimit(long n) {
struct rlimit rlim = {n, n};
if (n <= 0) return;
if (setrlimit(RLIMIT_NPROC, &rlim) == -1) {
kprintf("error: setrlimit(RLIMIT_NPROC) failed: %m\n");
exit(22);
}
}
void MakeProcessNice(void) {
if (!g_nice) return;
if (setpriority(PRIO_PROCESS, 0, 19) == -1) {
kprintf("error: setpriority(PRIO_PROCESS, 0, 19) failed: %m\n");
exit(23);
}
if (ioprio_set(IOPRIO_WHO_PROCESS, 0,
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) == -1) {
kprintf("error: ioprio_set() failed: %m\n");
exit(23);
}
struct sched_param p = {sched_get_priority_min(SCHED_IDLE)};
if (sched_setscheduler(0, SCHED_IDLE, &p) == -1) {
kprintf("error: sched_setscheduler(SCHED_IDLE) failed: %m\n");
exit(23);
}
}
int main(int argc, char *argv[]) {
bool hasfunbits;
int useruid, usergid;
@ -172,13 +294,13 @@ int main(int argc, char *argv[]) {
if (!IsLinux()) {
kprintf("error: this program is only intended for linux\n");
exit(__COUNTER__);
exit(5);
}
// parse flags
GetOpts(argc, argv);
if (optind == argc) {
kprintf("error: too few args\n", g_pflag);
kprintf("error: too few args\n");
write(2, USAGE, sizeof(USAGE) - 1);
exit(64);
}
@ -187,6 +309,13 @@ int main(int argc, char *argv[]) {
NormalizeFileDescriptors();
}
// set resource limits
MakeProcessNice();
SetCpuLimit(g_cpuquota);
SetFszLimit(g_fszquota);
SetMemLimit(g_memquota);
SetProLimit(g_proquota);
// test for weird chmod bits
usergid = getgid();
ownergid = getegid();
@ -203,7 +332,7 @@ int main(int argc, char *argv[]) {
if (hasfunbits) {
if (g_uflag || g_gflag) {
kprintf("error: setuid flags forbidden on setuid binaries\n");
_Exit(__COUNTER__);
_Exit(6);
}
}
@ -213,7 +342,7 @@ int main(int argc, char *argv[]) {
oldfsgid = setfsgid(usergid);
if (access(g_chroot, R_OK) == -1) {
kprintf("error: access(%#s) failed: %m\n", g_chroot);
_Exit(__COUNTER__);
_Exit(7);
}
setfsuid(oldfsuid);
setfsgid(oldfsgid);
@ -230,11 +359,11 @@ int main(int argc, char *argv[]) {
if (g_chroot) {
if (chdir(g_chroot) == -1) {
kprintf("error: chdir(%#s) failed: %m\n", g_chroot);
_Exit(__COUNTER__);
_Exit(8);
}
if (chroot(g_chroot) == -1) {
kprintf("error: chroot(%#s) failed: %m\n", g_chroot);
_Exit(__COUNTER__);
_Exit(9);
}
}
@ -245,7 +374,7 @@ int main(int argc, char *argv[]) {
}
if (!(prog = commandv(argv[optind], pathbuf, sizeof(pathbuf)))) {
kprintf("error: command not found: %m\n", argv[optind]);
_Exit(__COUNTER__);
_Exit(10);
}
if (hasfunbits) {
setfsuid(oldfsuid);
@ -257,21 +386,21 @@ int main(int argc, char *argv[]) {
// setgid binaries must use the gid of the user that ran it
if (setgid(usergid) == -1) {
kprintf("error: setgid(%d) failed: %m\n", usergid);
_Exit(__COUNTER__);
_Exit(11);
}
if (getgid() != usergid || getegid() != usergid) {
kprintf("error: setgid() broken\n");
_Exit(__COUNTER__);
_Exit(12);
}
} else if (g_gflag) {
// otherwise we trust the gid flag
if (setgid(g_gflag) == -1) {
kprintf("error: setgid(%d) failed: %m\n", g_gflag);
_Exit(__COUNTER__);
_Exit(13);
}
if (getgid() != g_gflag || getegid() != g_gflag) {
kprintf("error: setgid() broken\n");
_Exit(__COUNTER__);
_Exit(14);
}
}
@ -280,29 +409,28 @@ int main(int argc, char *argv[]) {
// setuid binaries must use the uid of the user that ran it
if (setuid(useruid) == -1) {
kprintf("error: setuid(%d) failed: %m\n", useruid);
_Exit(__COUNTER__);
_Exit(15);
}
if (getuid() != useruid || geteuid() != useruid) {
kprintf("error: setuid() broken\n");
_Exit(__COUNTER__);
_Exit(16);
}
} else if (g_uflag) {
// otherwise we trust the uid flag
if (setuid(g_uflag) == -1) {
kprintf("error: setuid(%d) failed: %m\n", g_uflag);
_Exit(__COUNTER__);
_Exit(17);
}
if (getuid() != g_uflag || geteuid() != g_uflag) {
kprintf("error: setuid() broken\n");
_Exit(__COUNTER__);
_Exit(18);
}
}
// apply sandbox
ksnprintf(pledges, sizeof(pledges), "%s execnative", g_pflag);
if (pledge(pledges, 0) == -1) {
kprintf("error: pledge(%#s) failed: %m\n", pledges);
_Exit(__COUNTER__);
if (pledge(g_promises, 0) == -1) {
kprintf("error: pledge(%#s) failed: %m\n", g_promises);
_Exit(19);
}
// launch program