mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Fix bugs and regressions in the pledge command
This change gets the pledge (formerly pledge.com) command back in tip top shape for a 3.0.1 cosmos release. It now runs on all platforms, even though it's mostly a no-op on ones that lack the kernel security stuff. The binary footprint is now smaller, since it no longer needs to link malloc. It's also now able to be built as a fat binary.
This commit is contained in:
parent
b0e3d89942
commit
7b284f6bda
18 changed files with 493 additions and 272 deletions
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/blockcancel.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
@ -37,13 +38,12 @@
|
||||||
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
||||||
* @raise EINVAL if flags are bad or first is greater than last
|
* @raise EINVAL if flags are bad or first is greater than last
|
||||||
* @raise EMFILE if a weird race condition happens on Linux
|
* @raise EMFILE if a weird race condition happens on Linux
|
||||||
* @raise ECANCELED if thread was cancelled in masked mode
|
|
||||||
* @raise EINTR possibly on OpenBSD
|
* @raise EINTR possibly on OpenBSD
|
||||||
* @raise ENOMEM on Linux maybe
|
* @raise ENOMEM on Linux maybe
|
||||||
*/
|
*/
|
||||||
int closefrom(int first) {
|
int closefrom(int first) {
|
||||||
int rc, err;
|
int rc;
|
||||||
(void)err;
|
BLOCK_CANCELATION;
|
||||||
if (first < 0) {
|
if (first < 0) {
|
||||||
// consistent with openbsd
|
// consistent with openbsd
|
||||||
// freebsd allows this but it's dangerous
|
// freebsd allows this but it's dangerous
|
||||||
|
@ -58,6 +58,7 @@ int closefrom(int first) {
|
||||||
} else {
|
} else {
|
||||||
rc = enosys();
|
rc = enosys();
|
||||||
}
|
}
|
||||||
|
ALLOW_CANCELATION;
|
||||||
STRACE("closefrom(%d) → %d% m", first, rc);
|
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ ssize_t copyfd(int in, int out, size_t n) {
|
||||||
if (dw != dr) {
|
if (dw != dr) {
|
||||||
// POSIX requires atomic IO up to PIPE_BUF
|
// POSIX requires atomic IO up to PIPE_BUF
|
||||||
// The minimum permissible PIPE_BUF is 512
|
// The minimum permissible PIPE_BUF is 512
|
||||||
abort();
|
notpossible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
|
|
|
@ -25,5 +25,6 @@
|
||||||
bool IsApeLoadable(char buf[8]) {
|
bool IsApeLoadable(char buf[8]) {
|
||||||
return READ32LE(buf) == READ32LE("\177ELF") ||
|
return READ32LE(buf) == READ32LE("\177ELF") ||
|
||||||
READ64LE(buf) == READ64LE("MZqFpD='") ||
|
READ64LE(buf) == READ64LE("MZqFpD='") ||
|
||||||
READ64LE(buf) == READ64LE("JTqFpD='");
|
READ64LE(buf) == READ64LE("jartsr='") ||
|
||||||
|
READ64LE(buf) == READ64LE("APEDBG='");
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ static int FindPromise(const char *name) {
|
||||||
/**
|
/**
|
||||||
* Parses the arguments to pledge() into a bitmask.
|
* Parses the arguments to pledge() into a bitmask.
|
||||||
*
|
*
|
||||||
|
* @param out receives the integral promises mask, which zero is defined
|
||||||
|
* as the set of all promises, and -1 is defined as the empty set of
|
||||||
|
* promises, which is equivalent to `promises` being an empty string
|
||||||
* @return 0 on success, or -1 if invalid
|
* @return 0 on success, or -1 if invalid
|
||||||
*/
|
*/
|
||||||
int ParsePromises(const char *promises, unsigned long *out,
|
int ParsePromises(const char *promises, unsigned long *out,
|
||||||
|
|
|
@ -535,7 +535,10 @@ static const struct thatispacked SyscallName {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t kPledgeDefault[] = {
|
static const uint16_t kPledgeDefault[] = {
|
||||||
__NR_linux_exit, // thread return / exit()
|
__NR_linux_exit, // thread return / exit()
|
||||||
|
#ifdef __NR_linux_arch_prctl //
|
||||||
|
__NR_linux_arch_prctl, // or else launching musl process crashes (tls)
|
||||||
|
#endif //
|
||||||
};
|
};
|
||||||
|
|
||||||
// stdio contains all the benign system calls. openbsd makes the
|
// stdio contains all the benign system calls. openbsd makes the
|
||||||
|
|
|
@ -29,6 +29,7 @@ void __stat2cosmo(struct stat *restrict st, const union metastat *ms) {
|
||||||
st->st_uid = ms->linux.st_uid;
|
st->st_uid = ms->linux.st_uid;
|
||||||
st->st_gid = ms->linux.st_gid;
|
st->st_gid = ms->linux.st_gid;
|
||||||
st->st_flags = 0;
|
st->st_flags = 0;
|
||||||
|
st->st_gen = 0;
|
||||||
st->st_rdev = ms->linux.st_rdev;
|
st->st_rdev = ms->linux.st_rdev;
|
||||||
st->st_size = ms->linux.st_size;
|
st->st_size = ms->linux.st_size;
|
||||||
st->st_blksize = ms->linux.st_blksize;
|
st->st_blksize = ms->linux.st_blksize;
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct sysinfo {
|
||||||
uint64_t bufferram; /* lingering disk pages; see fadvise */
|
uint64_t bufferram; /* lingering disk pages; see fadvise */
|
||||||
uint64_t totalswap; /* size of emergency memory */
|
uint64_t totalswap; /* size of emergency memory */
|
||||||
uint64_t freeswap; /* hopefully equal to totalswap */
|
uint64_t freeswap; /* hopefully equal to totalswap */
|
||||||
int16_t procs; /* number of processes */
|
uint16_t procs; /* number of processes */
|
||||||
int16_t __ignore1; /* padding */
|
int16_t __ignore1; /* padding */
|
||||||
int32_t __ignore2; /* padding */
|
int32_t __ignore2; /* padding */
|
||||||
uint64_t totalhigh; /* wut */
|
uint64_t totalhigh; /* wut */
|
||||||
|
|
|
@ -16,27 +16,55 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/calls/struct/sysinfo.h"
|
#include "libc/calls/struct/sysinfo.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/struct/sysinfo.internal.h"
|
||||||
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nt/accounting.h"
|
#include "libc/nt/accounting.h"
|
||||||
|
#include "libc/nt/process.h"
|
||||||
#include "libc/nt/struct/memorystatusex.h"
|
#include "libc/nt/struct/memorystatusex.h"
|
||||||
#include "libc/nt/struct/systeminfo.h"
|
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/nt/systeminfo.h"
|
|
||||||
|
static textwindows uint16_t GetProcessCount(void) {
|
||||||
|
uint16_t res;
|
||||||
|
uint32_t have, got, *pids;
|
||||||
|
uint32_t stack_memory[1000];
|
||||||
|
have = 0xFFFF * 4;
|
||||||
|
if (!_weaken(malloc) || !(pids = _weaken(malloc)(have))) {
|
||||||
|
pids = stack_memory;
|
||||||
|
have = sizeof(stack_memory);
|
||||||
|
}
|
||||||
|
if (EnumProcesses(pids, have, &got)) {
|
||||||
|
res = got / 4;
|
||||||
|
} else {
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
if (pids != stack_memory && _weaken(free)) {
|
||||||
|
_weaken(free)(pids);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
textwindows int sys_sysinfo_nt(struct sysinfo *info) {
|
textwindows int sys_sysinfo_nt(struct sysinfo *info) {
|
||||||
|
int i;
|
||||||
|
double load[3];
|
||||||
struct NtMemoryStatusEx memstat;
|
struct NtMemoryStatusEx memstat;
|
||||||
struct NtSystemInfo sysinfo;
|
BLOCK_SIGNALS;
|
||||||
GetSystemInfo(&sysinfo);
|
if (sys_getloadavg_nt(load, 3) != -1) {
|
||||||
|
for (i = 0; i < 3; ++i) {
|
||||||
|
info->loads[i] = load[i] * 65536;
|
||||||
|
}
|
||||||
|
}
|
||||||
memstat.dwLength = sizeof(struct NtMemoryStatusEx);
|
memstat.dwLength = sizeof(struct NtMemoryStatusEx);
|
||||||
if (GlobalMemoryStatusEx(&memstat)) {
|
if (GlobalMemoryStatusEx(&memstat)) {
|
||||||
info->mem_unit = 1;
|
|
||||||
info->totalram = memstat.ullTotalPhys;
|
info->totalram = memstat.ullTotalPhys;
|
||||||
info->freeram = memstat.ullAvailPhys;
|
info->freeram = memstat.ullAvailPhys;
|
||||||
info->procs = sysinfo.dwNumberOfProcessors;
|
|
||||||
info->uptime = GetTickCount64() / 1000;
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return __winerr();
|
|
||||||
}
|
}
|
||||||
|
info->uptime = GetTickCount64() / 1000;
|
||||||
|
info->procs = GetProcessCount();
|
||||||
|
info->mem_unit = 1;
|
||||||
|
ALLOW_SIGNALS;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,56 +188,6 @@ static int unveil_init(void) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Joins paths, e.g.
|
|
||||||
*
|
|
||||||
* 0 + 0 → 0
|
|
||||||
* "" + "" → ""
|
|
||||||
* "a" + 0 → "a"
|
|
||||||
* "a" + "" → "a/"
|
|
||||||
* 0 + "b" → "b"
|
|
||||||
* "" + "b" → "b"
|
|
||||||
* "." + "b" → "./b"
|
|
||||||
* "b" + "." → "b/."
|
|
||||||
* "a" + "b" → "a/b"
|
|
||||||
* "a/" + "b" → "a/b"
|
|
||||||
* "a" + "b/" → "a/b/"
|
|
||||||
* "a" + "/b" → "/b"
|
|
||||||
*
|
|
||||||
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
|
||||||
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
|
||||||
* `other` were null
|
|
||||||
*/
|
|
||||||
static char *JoinPaths(char *buf, size_t size, const char *path,
|
|
||||||
const char *other) {
|
|
||||||
size_t pathlen, otherlen;
|
|
||||||
if (!other) return (char *)path;
|
|
||||||
if (!path) return (char *)other;
|
|
||||||
pathlen = strlen(path);
|
|
||||||
if (!pathlen || *other == '/') {
|
|
||||||
return (/*unconst*/ char *)other;
|
|
||||||
}
|
|
||||||
otherlen = strlen(other);
|
|
||||||
if (path[pathlen - 1] == '/') {
|
|
||||||
if (pathlen + otherlen + 1 <= size) {
|
|
||||||
memmove(buf, path, pathlen);
|
|
||||||
memmove(buf + pathlen, other, otherlen + 1);
|
|
||||||
return buf;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (pathlen + 1 + otherlen + 1 <= size) {
|
|
||||||
memmove(buf, path, pathlen);
|
|
||||||
buf[pathlen] = '/';
|
|
||||||
memmove(buf + pathlen + 1, other, otherlen + 1);
|
|
||||||
return buf;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int sys_unveil_linux(const char *path, const char *permissions) {
|
int sys_unveil_linux(const char *path, const char *permissions) {
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||||
|
@ -302,7 +252,7 @@ int sys_unveil_linux(const char *path, const char *permissions) {
|
||||||
// next = join(dirname(next), link)
|
// next = join(dirname(next), link)
|
||||||
strcpy(b.buf2, next);
|
strcpy(b.buf2, next);
|
||||||
dir = dirname(b.buf2);
|
dir = dirname(b.buf2);
|
||||||
if ((next = JoinPaths(b.buf3, PATH_MAX, dir, b.lbuf))) {
|
if ((next = __join_paths(b.buf3, PATH_MAX, dir, b.lbuf))) {
|
||||||
// next now points to either: buf3, buf2, lbuf, rodata
|
// next now points to either: buf3, buf2, lbuf, rodata
|
||||||
strcpy(b.buf4, next);
|
strcpy(b.buf4, next);
|
||||||
next = b.buf4;
|
next = b.buf4;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#ifdef __COSMOPOLITAN__
|
||||||
|
#undef __COSMOPOLITAN__
|
||||||
|
#endif
|
||||||
|
|
||||||
#define __COSMOPOLITAN_MAJOR__ 3
|
#define __COSMOPOLITAN_MAJOR__ 3
|
||||||
#define __COSMOPOLITAN_MINOR__ 0
|
#define __COSMOPOLITAN_MINOR__ 0
|
||||||
#define __COSMOPOLITAN_PATCH__ 0
|
#define __COSMOPOLITAN_PATCH__ 0
|
||||||
|
|
|
@ -430,6 +430,9 @@ privileged void klog(const char *b, size_t n) {
|
||||||
: "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx)
|
: "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx)
|
||||||
: "0"(__NR_write), "1"(h), "2"(b), "3"(n)
|
: "0"(__NR_write), "1"(h), "2"(b), "3"(n)
|
||||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||||
|
if (rax < 0) {
|
||||||
|
__klog_handle = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
// this isn't a cancelation point because we don't acknowledge eintr
|
// this isn't a cancelation point because we don't acknowledge eintr
|
||||||
|
@ -444,6 +447,9 @@ privileged void klog(const char *b, size_t n) {
|
||||||
: "=r"(res_x0)
|
: "=r"(res_x0)
|
||||||
: "r"(r0), "r"(r1), "r"(r2), "r"(r8), "r"(r16)
|
: "r"(r0), "r"(r1), "r"(r2), "r"(r8), "r"(r16)
|
||||||
: "memory");
|
: "memory");
|
||||||
|
if (res_x0 < 0) {
|
||||||
|
__klog_handle = 0;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#error "unsupported architecture"
|
#error "unsupported architecture"
|
||||||
#endif
|
#endif
|
||||||
|
|
69
libc/str/joinpaths.c
Normal file
69
libc/str/joinpaths.c
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*-*- 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 2023 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/str/str.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins paths, e.g.
|
||||||
|
*
|
||||||
|
* 0 + 0 → 0
|
||||||
|
* "" + "" → ""
|
||||||
|
* "a" + 0 → "a"
|
||||||
|
* "a" + "" → "a/"
|
||||||
|
* 0 + "b" → "b"
|
||||||
|
* "" + "b" → "b"
|
||||||
|
* "." + "b" → "./b"
|
||||||
|
* "b" + "." → "b/."
|
||||||
|
* "a" + "b" → "a/b"
|
||||||
|
* "a/" + "b" → "a/b"
|
||||||
|
* "a" + "b/" → "a/b/"
|
||||||
|
* "a" + "/b" → "/b"
|
||||||
|
*
|
||||||
|
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
||||||
|
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
||||||
|
* `other` were null
|
||||||
|
*/
|
||||||
|
char *__join_paths(char *buf, size_t size, const char *path,
|
||||||
|
const char *other) {
|
||||||
|
size_t pathlen, otherlen;
|
||||||
|
if (!other) return (char *)path;
|
||||||
|
if (!path) return (char *)other;
|
||||||
|
pathlen = strlen(path);
|
||||||
|
if (!pathlen || *other == '/') {
|
||||||
|
return (/*unconst*/ char *)other;
|
||||||
|
}
|
||||||
|
otherlen = strlen(other);
|
||||||
|
if (path[pathlen - 1] == '/') {
|
||||||
|
if (pathlen + otherlen + 1 <= size) {
|
||||||
|
memmove(buf, path, pathlen);
|
||||||
|
memmove(buf + pathlen, other, otherlen + 1);
|
||||||
|
return buf;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pathlen + 1 + otherlen + 1 <= size) {
|
||||||
|
memmove(buf, path, pathlen);
|
||||||
|
buf[pathlen] = '/';
|
||||||
|
memmove(buf + pathlen + 1, other, otherlen + 1);
|
||||||
|
return buf;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -217,6 +217,7 @@ axdx_t tprecode8to16(char16_t *, size_t, const char *);
|
||||||
axdx_t tprecode16to8(char *, size_t, const char16_t *);
|
axdx_t tprecode16to8(char *, size_t, const char16_t *);
|
||||||
bool wcsstartswith(const wchar_t *, const wchar_t *) strlenesque;
|
bool wcsstartswith(const wchar_t *, const wchar_t *) strlenesque;
|
||||||
bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque;
|
bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque;
|
||||||
|
char *__join_paths(char *, size_t, const char *, const char *) __wur;
|
||||||
#endif /* _COSMO_SOURCE */
|
#endif /* _COSMO_SOURCE */
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -6,7 +6,7 @@ if [ $# = 0 ]; then
|
||||||
if ! [ $(id -u) = 0 ]; then
|
if ! [ $(id -u) = 0 ]; then
|
||||||
make -j16 MODE=fastbuild \
|
make -j16 MODE=fastbuild \
|
||||||
o/fastbuild/examples/ls.com \
|
o/fastbuild/examples/ls.com \
|
||||||
o/fastbuild/examples/curl.com \
|
o/fastbuild/tool/curl/curl.com \
|
||||||
o/fastbuild/examples/life.com \
|
o/fastbuild/examples/life.com \
|
||||||
o/fastbuild/examples/hello.com \
|
o/fastbuild/examples/hello.com \
|
||||||
o/fastbuild/examples/printargs.com \
|
o/fastbuild/examples/printargs.com \
|
||||||
|
@ -14,7 +14,7 @@ if [ $# = 0 ]; then
|
||||||
o/fastbuild/tool/build/pledge.com || exit
|
o/fastbuild/tool/build/pledge.com || exit
|
||||||
make -j16 MODE=$m \
|
make -j16 MODE=$m \
|
||||||
o/$m/examples/ls.com \
|
o/$m/examples/ls.com \
|
||||||
o/$m/examples/curl.com \
|
o/$m/tool/curl/curl.com \
|
||||||
o/$m/examples/life.com \
|
o/$m/examples/life.com \
|
||||||
o/$m/examples/hello.com \
|
o/$m/examples/hello.com \
|
||||||
o/$m/examples/printargs.com \
|
o/$m/examples/printargs.com \
|
||||||
|
@ -76,7 +76,7 @@ elif [ "$1" = ape_binfmt_test_suite ]; then
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
startit ape binfmt curl.com
|
startit ape binfmt curl.com
|
||||||
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
elif [ "$1" = ape_loader_test_suite ]; then
|
elif [ "$1" = ape_loader_test_suite ]; then
|
||||||
|
@ -93,7 +93,7 @@ elif [ "$1" = ape_loader_test_suite ]; then
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
startit ape loader curl.com
|
startit ape loader curl.com
|
||||||
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
ape/apeinstall.sh >/dev/null 2>&1
|
ape/apeinstall.sh >/dev/null 2>&1
|
||||||
|
@ -116,7 +116,7 @@ elif [ "$1" = ape_assimilated_test_suite ]; then
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
startit ape assimilated curl.com
|
startit ape assimilated curl.com
|
||||||
cp o/fastbuild/examples/curl.com $t/assimilated
|
cp o/fastbuild/tool/curl/curl.com $t/assimilated
|
||||||
o/fastbuild/tool/build/assimilate.com $t/assimilated/curl.com
|
o/fastbuild/tool/build/assimilate.com $t/assimilated/curl.com
|
||||||
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath 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
|
checkem
|
||||||
|
@ -133,7 +133,7 @@ elif [ "$1" = ape_native_test_suite ]; then
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
startit ape native curl.com
|
startit ape native curl.com
|
||||||
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath 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/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
elif [ "$1" = setuid_test_suite ]; then
|
elif [ "$1" = setuid_test_suite ]; then
|
||||||
|
@ -148,7 +148,7 @@ elif [ "$1" = setuid_test_suite ]; then
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
startit setuid curl.com
|
startit setuid curl.com
|
||||||
[ "$($t/pledge.com -p 'stdio rpath inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
[ "$($t/pledge.com -p 'stdio rpath inet dns' o/$m/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||||
checkem
|
checkem
|
||||||
|
|
||||||
startit setuid getuid
|
startit setuid getuid
|
||||||
|
|
|
@ -77,7 +77,7 @@ o/$(MODE)/tool/build/%.com.dbg: \
|
||||||
$(APE_NO_MODIFY_SELF)
|
$(APE_NO_MODIFY_SELF)
|
||||||
@$(APELINK)
|
@$(APELINK)
|
||||||
|
|
||||||
o/$(MODE)/tool/build/dso/sandbox.so.zip.o \
|
o/$(MODE)/tool/build/dso/sandbox-$(ARCH).so.zip.o \
|
||||||
o/$(MODE)/tool/build/false.com.zip.o \
|
o/$(MODE)/tool/build/false.com.zip.o \
|
||||||
o/$(MODE)/tool/build/echo.com.zip.o \
|
o/$(MODE)/tool/build/echo.com.zip.o \
|
||||||
o/$(MODE)/tool/build/cocmd.com.zip.o: private \
|
o/$(MODE)/tool/build/cocmd.com.zip.o: private \
|
||||||
|
@ -99,7 +99,7 @@ o/$(MODE)/tool/build/dso/sandbox.o: \
|
||||||
libc/intrin/promises.internal.h \
|
libc/intrin/promises.internal.h \
|
||||||
tool/build/build.mk
|
tool/build/build.mk
|
||||||
|
|
||||||
o/$(MODE)/tool/build/dso/sandbox.so: \
|
o/$(MODE)/tool/build/dso/sandbox-$(ARCH).so: \
|
||||||
o/$(MODE)/tool/build/dso/sandbox.o \
|
o/$(MODE)/tool/build/dso/sandbox.o \
|
||||||
o/$(MODE)/libc/calls/pledge-linux.o \
|
o/$(MODE)/libc/calls/pledge-linux.o \
|
||||||
o/$(MODE)/libc/sysv/restorert.o
|
o/$(MODE)/libc/sysv/restorert.o
|
||||||
|
@ -118,7 +118,7 @@ o/$(MODE)/tool/build/dso/sandbox.so: \
|
||||||
o/$(MODE)/tool/build/pledge.com.dbg: \
|
o/$(MODE)/tool/build/pledge.com.dbg: \
|
||||||
$(TOOL_BUILD_DEPS) \
|
$(TOOL_BUILD_DEPS) \
|
||||||
o/$(MODE)/tool/build/build.pkg \
|
o/$(MODE)/tool/build/build.pkg \
|
||||||
o/$(MODE)/tool/build/dso/sandbox.so.zip.o \
|
o/$(MODE)/tool/build/dso/sandbox-$(ARCH).so.zip.o \
|
||||||
o/$(MODE)/tool/build/pledge.o \
|
o/$(MODE)/tool/build/pledge.o \
|
||||||
$(CRT) \
|
$(CRT) \
|
||||||
$(APE_NO_MODIFY_SELF)
|
$(APE_NO_MODIFY_SELF)
|
||||||
|
|
|
@ -22,10 +22,8 @@
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
|
||||||
/*
|
// runs pledge at glibc executable load time, e.g.
|
||||||
* runs pledge at glibc executable load time, e.g.
|
// strace -vff bash -c '_PLEDGE=4194303,0 LD_PRELOAD=$HOME/sandbox.so ls'
|
||||||
* strace -vff bash -c '_PLEDGE=4194303,0 LD_PRELOAD=$HOME/sandbox.so ls'
|
|
||||||
*/
|
|
||||||
|
|
||||||
__attribute__((__constructor__)) void init(void) {
|
__attribute__((__constructor__)) void init(void) {
|
||||||
int c, i, j;
|
int c, i, j;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/pledge.h"
|
#include "libc/calls/pledge.h"
|
||||||
#include "libc/assert.h"
|
#include "ape/ape.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/landlock.h"
|
#include "libc/calls/landlock.h"
|
||||||
#include "libc/calls/pledge.internal.h"
|
#include "libc/calls/pledge.internal.h"
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
#include "libc/calls/struct/seccomp.internal.h"
|
#include "libc/calls/struct/seccomp.internal.h"
|
||||||
#include "libc/calls/struct/stat.h"
|
#include "libc/calls/struct/stat.h"
|
||||||
#include "libc/calls/struct/sysinfo.h"
|
#include "libc/calls/struct/sysinfo.h"
|
||||||
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
#include "libc/elf/struct/phdr.h"
|
#include "libc/elf/struct/phdr.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/intrin/bits.h"
|
#include "libc/intrin/bits.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
|
@ -42,13 +44,14 @@
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/math.h"
|
#include "libc/math.h"
|
||||||
#include "libc/mem/gc.internal.h"
|
#include "libc/mem/alloca.h"
|
||||||
#include "libc/mem/mem.h"
|
|
||||||
#include "libc/nexgen32e/kcpuids.h"
|
#include "libc/nexgen32e/kcpuids.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
#include "libc/sock/struct/pollfd.h"
|
#include "libc/sock/struct/pollfd.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/stdio/sysparam.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/ioprio.h"
|
#include "libc/sysv/consts/ioprio.h"
|
||||||
#include "libc/sysv/consts/map.h"
|
#include "libc/sysv/consts/map.h"
|
||||||
|
@ -75,7 +78,7 @@ __static_yoink("zipos");
|
||||||
|
|
||||||
#define USAGE \
|
#define USAGE \
|
||||||
"\
|
"\
|
||||||
usage: pledge.com [-hnN] PROG ARGS...\n\
|
usage: pledge [-hnN] PROG ARGS...\n\
|
||||||
-h show help\n\
|
-h show help\n\
|
||||||
-g GID call setgid()\n\
|
-g GID call setgid()\n\
|
||||||
-u UID call setuid()\n\
|
-u UID call setuid()\n\
|
||||||
|
@ -116,7 +119,7 @@ usage: pledge.com [-hnN] PROG ARGS...\n\
|
||||||
- vminfo: allows /proc/stat, /proc/self/maps, etc.\n\
|
- vminfo: allows /proc/stat, /proc/self/maps, etc.\n\
|
||||||
- tmppath: allows /tmp, $TMPPATH, lstat, unlink\n\
|
- tmppath: allows /tmp, $TMPPATH, lstat, unlink\n\
|
||||||
\n\
|
\n\
|
||||||
pledge.com v1.8\n\
|
Cosompolitan Pledge v1.9\n\
|
||||||
copyright 2022 justine alexandra roberts tunney\n\
|
copyright 2022 justine alexandra roberts tunney\n\
|
||||||
notice licenses are embedded in the binary\n\
|
notice licenses are embedded in the binary\n\
|
||||||
https://twitter.com/justinetunney\n\
|
https://twitter.com/justinetunney\n\
|
||||||
|
@ -130,13 +133,27 @@ the https://justine.lol/pledge/ page for online documentation.\n\
|
||||||
\n\
|
\n\
|
||||||
"
|
"
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#define ARCH_NAME "x86_64"
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
#define ARCH_NAME "aarch64"
|
||||||
|
#else
|
||||||
|
#error "unsupported architecture"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum Strategy {
|
||||||
|
kStrategyNull,
|
||||||
|
kStrategyStatic,
|
||||||
|
kStrategyDynamic,
|
||||||
|
kStrategyApe,
|
||||||
|
};
|
||||||
|
|
||||||
int g_gflag;
|
int g_gflag;
|
||||||
int g_uflag;
|
int g_uflag;
|
||||||
int g_kflag;
|
int g_kflag;
|
||||||
int g_hflag;
|
int g_hflag;
|
||||||
bool g_nice;
|
bool g_nice;
|
||||||
bool g_qflag;
|
bool g_qflag;
|
||||||
bool isdynamic;
|
|
||||||
bool g_noclose;
|
bool g_noclose;
|
||||||
long g_cpuquota;
|
long g_cpuquota;
|
||||||
long g_fszquota;
|
long g_fszquota;
|
||||||
|
@ -147,19 +164,60 @@ long g_dontdrop;
|
||||||
long g_dontunveil;
|
long g_dontunveil;
|
||||||
const char *g_test;
|
const char *g_test;
|
||||||
const char *g_chroot;
|
const char *g_chroot;
|
||||||
const char *g_promises;
|
char pledgevar[64];
|
||||||
|
char g_promises[256];
|
||||||
char dsopath[PATH_MAX];
|
char dsopath[PATH_MAX];
|
||||||
char tmppath[PATH_MAX];
|
char tmppath[PATH_MAX];
|
||||||
|
char preloadvar[PATH_MAX];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int n;
|
int n;
|
||||||
char **p;
|
char *p[10000];
|
||||||
} unveils;
|
} unveils;
|
||||||
|
|
||||||
static void GetOpts(int argc, char *argv[]) {
|
unsigned long HasPromise(unsigned long ipromises, int promise) {
|
||||||
|
return ~ipromises & (1ul << promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
long Atoi(const char *s) {
|
||||||
|
long i;
|
||||||
|
char *ep;
|
||||||
|
errno = 0;
|
||||||
|
i = strtol(s, &ep, 0);
|
||||||
|
if (*ep || errno) {
|
||||||
|
tinyprint(2, program_invocation_name, ": invalid integer: ", s, "\n", NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
long ParseSiSize(const char *s, long b) {
|
||||||
|
long i;
|
||||||
|
errno = 0;
|
||||||
|
i = sizetol(s, b);
|
||||||
|
if (errno) {
|
||||||
|
tinyprint(2, program_invocation_name, ": invalid size: ", s, "\n", NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPromise(const char *s) {
|
||||||
|
while (isspace(*s)) ++s;
|
||||||
|
if (!*s) return;
|
||||||
|
if (*g_promises) {
|
||||||
|
strlcat(g_promises, " ", sizeof(g_promises));
|
||||||
|
}
|
||||||
|
if (strlcat(g_promises, s, sizeof(g_promises)) >= sizeof(g_promises)) {
|
||||||
|
tinyprint(2, program_invocation_name, ": too many promises\n", NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetOpts(int argc, char *argv[]) {
|
||||||
int opt;
|
int opt;
|
||||||
struct sysinfo si;
|
struct sysinfo si;
|
||||||
g_promises = 0;
|
bool got_promise_flag = false;
|
||||||
g_nfdquota = 64;
|
g_nfdquota = 64;
|
||||||
g_fszquota = 256 * 1000 * 1000;
|
g_fszquota = 256 * 1000 * 1000;
|
||||||
if (!sysinfo(&si)) {
|
if (!sysinfo(&si)) {
|
||||||
|
@ -196,46 +254,36 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
g_chroot = optarg;
|
g_chroot = optarg;
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
g_gflag = atoi(optarg);
|
g_gflag = Atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
g_uflag = atoi(optarg);
|
g_uflag = Atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
g_cpuquota = atoi(optarg);
|
g_cpuquota = Atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
g_proquota = atoi(optarg);
|
g_proquota = Atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
g_nfdquota = atoi(optarg);
|
g_nfdquota = Atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
errno = 0;
|
g_fszquota = ParseSiSize(optarg, 1024);
|
||||||
g_fszquota = sizetol(optarg, 1000);
|
|
||||||
if (errno) {
|
|
||||||
kprintf("error: invalid size: -F %s\n", optarg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
errno = 0;
|
g_memquota = ParseSiSize(optarg, 1024);
|
||||||
g_memquota = sizetol(optarg, 1024);
|
|
||||||
if (errno) {
|
|
||||||
kprintf("error: invalid size: -F %s\n", optarg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
if (g_promises) {
|
AddPromise(optarg);
|
||||||
g_promises = xstrcat(g_promises, ' ', optarg);
|
got_promise_flag = true;
|
||||||
} else {
|
|
||||||
g_promises = optarg;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
unveils.p = realloc(unveils.p, ++unveils.n * sizeof(*unveils.p));
|
if (unveils.n == ARRAYLEN(unveils.p)) {
|
||||||
unveils.p[unveils.n - 1] = optarg;
|
tinyprint(2, program_invocation_name, ": too many unveils\n", NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
unveils.p[unveils.n++] = optarg;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
|
@ -246,16 +294,16 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
exit(64);
|
exit(64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!g_promises) {
|
if (!got_promise_flag) {
|
||||||
g_promises = "stdio rpath";
|
stpcpy(g_promises, "stdio rpath");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *prog;
|
const char *g_prog;
|
||||||
char pathbuf[PATH_MAX];
|
char pathbuf[PATH_MAX];
|
||||||
struct pollfd pfds[256];
|
struct pollfd pfds[256];
|
||||||
|
|
||||||
static bool SupportsLandlock(void) {
|
bool SupportsLandlock(void) {
|
||||||
int e = errno;
|
int e = errno;
|
||||||
bool r = landlock_create_ruleset(0, 0, LANDLOCK_CREATE_RULESET_VERSION) >= 0;
|
bool r = landlock_create_ruleset(0, 0, LANDLOCK_CREATE_RULESET_VERSION) >= 0;
|
||||||
errno = e;
|
errno = e;
|
||||||
|
@ -265,7 +313,7 @@ static bool SupportsLandlock(void) {
|
||||||
int GetPollMaxFds(void) {
|
int GetPollMaxFds(void) {
|
||||||
int n;
|
int n;
|
||||||
struct rlimit rl;
|
struct rlimit rl;
|
||||||
if (getrlimit(RLIMIT_NOFILE, &rl) != -1) {
|
if (!getrlimit(RLIMIT_NOFILE, &rl)) {
|
||||||
n = rl.rlim_cur;
|
n = rl.rlim_cur;
|
||||||
} else {
|
} else {
|
||||||
n = 64;
|
n = 64;
|
||||||
|
@ -274,27 +322,26 @@ int GetPollMaxFds(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizeFileDescriptors(void) {
|
void NormalizeFileDescriptors(void) {
|
||||||
int e, i, n, fd;
|
int i, n, fd;
|
||||||
|
closefrom(3); // faster and more secure if linux 5.9+ or bsd
|
||||||
n = GetPollMaxFds();
|
n = GetPollMaxFds();
|
||||||
e = errno;
|
|
||||||
closefrom(3); // more secure if linux 5.9+
|
|
||||||
errno = e;
|
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
pfds[i].fd = i;
|
pfds[i].fd = i;
|
||||||
pfds[i].events = POLLIN;
|
pfds[i].events = POLLIN;
|
||||||
}
|
}
|
||||||
if (poll(pfds, n, 0) == -1) {
|
if (poll(pfds, n, 0) == -1) {
|
||||||
kprintf("error: poll() failed: %m\n");
|
perror("poll");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
for (i = 0; i < 3; ++i) {
|
for (i = 0; i < 3; ++i) {
|
||||||
if (pfds[i].revents & POLLNVAL) {
|
if (pfds[i].revents & POLLNVAL) {
|
||||||
if ((fd = open("/dev/null", O_RDWR)) == -1) {
|
if ((fd = open("/dev/null", O_RDWR)) == -1) {
|
||||||
kprintf("error: open(\"/dev/null\") failed: %m\n");
|
perror("/dev/null");
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
if (fd != i) {
|
if (fd != i) {
|
||||||
kprintf("error: open() is broken: %d vs. %d\n", fd, i);
|
tinyprint(2, program_invocation_name, ": poll() or open() is broken\n",
|
||||||
|
NULL);
|
||||||
exit(3);
|
exit(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,7 +349,7 @@ void NormalizeFileDescriptors(void) {
|
||||||
for (i = 3; i < n; ++i) {
|
for (i = 3; i < n; ++i) {
|
||||||
if (~pfds[i].revents & POLLNVAL) {
|
if (~pfds[i].revents & POLLNVAL) {
|
||||||
if (close(pfds[i].fd) == -1) {
|
if (close(pfds[i].fd) == -1) {
|
||||||
kprintf("error: close(%d) failed: %m\n", pfds[i].fd);
|
perror("close");
|
||||||
exit(4);
|
exit(4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,8 +401,8 @@ bool PathExists(const char *path) {
|
||||||
|
|
||||||
void Unveil(const char *path, const char *perm) {
|
void Unveil(const char *path, const char *perm) {
|
||||||
if (unveil(path, perm) == -1) {
|
if (unveil(path, perm) == -1) {
|
||||||
kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm);
|
perror(path);
|
||||||
_Exit(20);
|
exit(20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,22 +415,62 @@ int UnveilIfExists(const char *path, const char *perm) {
|
||||||
} else if (errno == ENOENT) {
|
} else if (errno == ENOENT) {
|
||||||
errno = err;
|
errno = err;
|
||||||
} else {
|
} else {
|
||||||
kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm);
|
perror(path);
|
||||||
_Exit(20);
|
exit(20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Strategy GetStrategy(void) {
|
||||||
|
static enum Strategy strategy;
|
||||||
|
if (strategy == kStrategyNull) {
|
||||||
|
strategy = kStrategyStatic;
|
||||||
|
int fd;
|
||||||
|
if ((fd = open(g_prog, O_RDONLY)) != -1) {
|
||||||
|
union {
|
||||||
|
char magic[8];
|
||||||
|
Elf64_Ehdr ehdr;
|
||||||
|
} hdr = {0};
|
||||||
|
if (pread(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr)) {
|
||||||
|
if (READ64LE(hdr.magic) == READ64LE("MZqFpD='") ||
|
||||||
|
READ64LE(hdr.magic) == READ64LE("jartsr='") ||
|
||||||
|
READ64LE(hdr.magic) == READ64LE("APEDBG='")) {
|
||||||
|
strategy = kStrategyApe;
|
||||||
|
} else if ((IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) &&
|
||||||
|
IsElf64Binary(&hdr.ehdr, sizeof(hdr))) {
|
||||||
|
if (hdr.ehdr.e_type == ET_DYN) {
|
||||||
|
strategy = kStrategyDynamic;
|
||||||
|
} else {
|
||||||
|
Elf64_Phdr phdrs[16];
|
||||||
|
int count = MIN(hdr.ehdr.e_phnum, ARRAYLEN(phdrs));
|
||||||
|
int bytes = count * sizeof(Elf64_Phdr);
|
||||||
|
if (pread(fd, phdrs, bytes, hdr.ehdr.e_phoff) == bytes) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (phdrs[i].p_type == PT_INTERP ||
|
||||||
|
phdrs[i].p_type == PT_DYNAMIC) {
|
||||||
|
strategy = kStrategyDynamic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
|
||||||
void ApplyFilesystemPolicy(unsigned long ipromises) {
|
void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
if (g_dontunveil) return;
|
if (g_dontunveil) return;
|
||||||
if (!SupportsLandlock()) return;
|
if (!SupportsLandlock()) return;
|
||||||
|
|
||||||
Unveil(prog, "rx");
|
Unveil(g_prog, "rx");
|
||||||
|
|
||||||
if (isdynamic) {
|
if (GetStrategy() == kStrategyDynamic) {
|
||||||
Unveil(dsopath, "rx");
|
Unveil(dsopath, "rx");
|
||||||
UnveilIfExists("/lib", "rx");
|
UnveilIfExists("/lib", "rx");
|
||||||
UnveilIfExists("/lib64", "rx");
|
UnveilIfExists("/lib64", "rx");
|
||||||
|
@ -391,14 +478,16 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
UnveilIfExists("/usr/lib64", "rx");
|
UnveilIfExists("/usr/lib64", "rx");
|
||||||
UnveilIfExists("/usr/local/lib", "rx");
|
UnveilIfExists("/usr/local/lib", "rx");
|
||||||
UnveilIfExists("/usr/local/lib64", "rx");
|
UnveilIfExists("/usr/local/lib64", "rx");
|
||||||
UnveilIfExists("/etc/ld-musl-x86_64.path", "r");
|
UnveilIfExists("/etc/ld-musl-" ARCH_NAME ".path", "r");
|
||||||
UnveilIfExists("/etc/ld.so.conf", "r");
|
UnveilIfExists("/etc/ld.so.conf", "r");
|
||||||
UnveilIfExists("/etc/ld.so.cache", "r");
|
UnveilIfExists("/etc/ld.so.cache", "r");
|
||||||
UnveilIfExists("/etc/ld.so.conf.d", "r");
|
UnveilIfExists("/etc/ld.so.conf.d", "r");
|
||||||
UnveilIfExists("/etc/ld.so.preload", "r");
|
UnveilIfExists("/etc/ld.so.preload", "r");
|
||||||
|
// in case musl is symlinked somewhere else
|
||||||
|
UnveilIfExists("/lib/ld-musl-" ARCH_NAME ".so.1", "rx");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_STDIO)) {
|
if (HasPromise(ipromises, PROMISE_STDIO)) {
|
||||||
UnveilIfExists("/dev/fd", "r");
|
UnveilIfExists("/dev/fd", "r");
|
||||||
UnveilIfExists("/dev/log", "w");
|
UnveilIfExists("/dev/log", "w");
|
||||||
UnveilIfExists("/dev/zero", "r");
|
UnveilIfExists("/dev/zero", "r");
|
||||||
|
@ -422,15 +511,15 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
UnveilIfExists("/proc/sys/vm/overcommit_memory", "r");
|
UnveilIfExists("/proc/sys/vm/overcommit_memory", "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_INET)) {
|
if (HasPromise(ipromises, PROMISE_INET)) {
|
||||||
UnveilIfExists("/etc/ssl/certs/ca-certificates.crt", "r");
|
UnveilIfExists("/etc/ssl/certs/ca-certificates.crt", "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_RPATH)) {
|
if (HasPromise(ipromises, PROMISE_RPATH)) {
|
||||||
UnveilIfExists("/proc/filesystems", "r");
|
UnveilIfExists("/proc/filesystems", "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_DNS)) {
|
if (HasPromise(ipromises, PROMISE_DNS)) {
|
||||||
UnveilIfExists("/etc/hosts", "r");
|
UnveilIfExists("/etc/hosts", "r");
|
||||||
UnveilIfExists("/etc/hostname", "r");
|
UnveilIfExists("/etc/hostname", "r");
|
||||||
UnveilIfExists("/etc/services", "r");
|
UnveilIfExists("/etc/services", "r");
|
||||||
|
@ -438,7 +527,7 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
UnveilIfExists("/etc/resolv.conf", "r");
|
UnveilIfExists("/etc/resolv.conf", "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_TTY)) {
|
if (HasPromise(ipromises, PROMISE_TTY)) {
|
||||||
UnveilIfExists(ttyname(0), "rw");
|
UnveilIfExists(ttyname(0), "rw");
|
||||||
UnveilIfExists("/dev/tty", "rw");
|
UnveilIfExists("/dev/tty", "rw");
|
||||||
UnveilIfExists("/dev/console", "rw");
|
UnveilIfExists("/dev/console", "rw");
|
||||||
|
@ -447,18 +536,21 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
UnveilIfExists("/usr/share/terminfo", "r");
|
UnveilIfExists("/usr/share/terminfo", "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_PROT_EXEC)) {
|
if (GetStrategy() == kStrategyApe) {
|
||||||
if (UnveilIfExists("/usr/bin/ape", "rx") == -1) {
|
if (UnveilIfExists("/usr/bin/ape", "rx") == -1) {
|
||||||
|
char buf[PATH_MAX];
|
||||||
if ((p = getenv("TMPDIR"))) {
|
if ((p = getenv("TMPDIR"))) {
|
||||||
UnveilIfExists(xjoinpaths(p, ".ape"), "rx");
|
UnveilIfExists(
|
||||||
|
__join_paths(buf, sizeof(buf), p, ".ape-" APE_VERSION_STR), "rx");
|
||||||
}
|
}
|
||||||
if ((p = getenv("HOME"))) {
|
if ((p = getenv("HOME"))) {
|
||||||
UnveilIfExists(xjoinpaths(p, ".ape"), "rx");
|
UnveilIfExists(
|
||||||
|
__join_paths(buf, sizeof(buf), p, ".ape-" APE_VERSION_STR), "rx");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_VMINFO)) {
|
if (HasPromise(ipromises, PROMISE_VMINFO)) {
|
||||||
UnveilIfExists("/proc/stat", "r");
|
UnveilIfExists("/proc/stat", "r");
|
||||||
UnveilIfExists("/proc/meminfo", "r");
|
UnveilIfExists("/proc/meminfo", "r");
|
||||||
UnveilIfExists("/proc/cpuinfo", "r");
|
UnveilIfExists("/proc/cpuinfo", "r");
|
||||||
|
@ -467,7 +559,7 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
UnveilIfExists("/sys/devices/system/cpu", "r");
|
UnveilIfExists("/sys/devices/system/cpu", "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (~ipromises & (1ul << PROMISE_TMPPATH)) {
|
if (HasPromise(ipromises, PROMISE_TMPPATH)) {
|
||||||
UnveilIfExists("/tmp", "rwc");
|
UnveilIfExists("/tmp", "rwc");
|
||||||
UnveilIfExists(getenv("TMPPATH"), "rwc");
|
UnveilIfExists(getenv("TMPPATH"), "rwc");
|
||||||
}
|
}
|
||||||
|
@ -489,21 +581,22 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unveil(0, 0) == -1) {
|
if (unveil(0, 0) == -1) {
|
||||||
kprintf("error: unveil(0, 0) failed: %m\n");
|
perror("unveil");
|
||||||
_Exit(20);
|
exit(20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DropCapabilities(void) {
|
void DropCapabilities(void) {
|
||||||
int e, i;
|
int e, i;
|
||||||
|
if (!IsLinux()) return;
|
||||||
for (e = errno, i = 0;; ++i) {
|
for (e = errno, i = 0;; ++i) {
|
||||||
if (prctl(PR_CAPBSET_DROP, i) == -1) {
|
if (prctl(PR_CAPBSET_DROP, i) == -1) {
|
||||||
if (errno == EINVAL || errno == EPERM) {
|
if (errno == EINVAL || errno == EPERM) {
|
||||||
errno = e;
|
errno = e;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
kprintf("error: prctl(PR_CAPBSET_DROP, %d) failed: %m\n", i);
|
perror("prctl(PR_CAPBSET_DROP)");
|
||||||
_Exit(25);
|
exit(25);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,68 +626,20 @@ int Extract(const char *from, const char *to, int mode) {
|
||||||
return close(fdout) | close(fdin);
|
return close(fdout) | close(fdin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
int CountEnviron(char **ep) {
|
||||||
* Returns true if ELF executable uses dynamic loading magic.
|
int res = 0;
|
||||||
*/
|
while (*ep++) ++res;
|
||||||
static bool IsDynamicExecutable(const char *prog) {
|
|
||||||
bool res;
|
|
||||||
Elf64_Ehdr *e;
|
|
||||||
Elf64_Phdr *p;
|
|
||||||
struct stat st;
|
|
||||||
int i, fd, err;
|
|
||||||
fd = -1;
|
|
||||||
err = errno;
|
|
||||||
e = MAP_FAILED;
|
|
||||||
if ((fd = open(prog, O_RDONLY)) == -1) {
|
|
||||||
res = false;
|
|
||||||
goto Finish;
|
|
||||||
}
|
|
||||||
if (fstat(fd, &st) == -1 || st.st_size < 64) {
|
|
||||||
res = false;
|
|
||||||
goto Finish;
|
|
||||||
}
|
|
||||||
if ((e = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
|
||||||
res = false;
|
|
||||||
goto Finish;
|
|
||||||
}
|
|
||||||
if (READ32LE(e->e_ident) != READ32LE(ELFMAG)) {
|
|
||||||
res = false;
|
|
||||||
goto Finish;
|
|
||||||
}
|
|
||||||
if (e->e_type == ET_DYN) {
|
|
||||||
res = true;
|
|
||||||
goto Finish;
|
|
||||||
}
|
|
||||||
for (i = 0; i < e->e_phnum; ++i) {
|
|
||||||
p = GetElfProgramHeaderAddress(e, st.st_size, i);
|
|
||||||
if (p->p_type == PT_INTERP || p->p_type == PT_DYNAMIC) {
|
|
||||||
res = true;
|
|
||||||
goto Finish;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = false;
|
|
||||||
goto Finish;
|
|
||||||
Finish:
|
|
||||||
if (e != MAP_FAILED) munmap(e, st.st_size);
|
|
||||||
if (fd != -1) close(fd);
|
|
||||||
errno = err;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
const char *s;
|
const char *s;
|
||||||
bool hasfunbits;
|
bool hasfunbits;
|
||||||
char buf[PATH_MAX];
|
|
||||||
int useruid, usergid;
|
int useruid, usergid;
|
||||||
int owneruid, ownergid;
|
int owneruid, ownergid;
|
||||||
int oldfsuid, oldfsgid;
|
int oldfsuid, oldfsgid;
|
||||||
unsigned long ipromises;
|
unsigned long ipromises;
|
||||||
|
|
||||||
if (!IsLinux()) {
|
|
||||||
kprintf("error: this program is only intended for linux\n");
|
|
||||||
exit(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse flags
|
// parse flags
|
||||||
GetOpts(argc, argv);
|
GetOpts(argc, argv);
|
||||||
if (g_test) {
|
if (g_test) {
|
||||||
|
@ -612,41 +657,33 @@ int main(int argc, char *argv[]) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kprintf("error: unknown test: %s\n", g_test);
|
tinyprint(2, g_test, ": unknown test\n", NULL);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
if (optind == argc) {
|
if (optind == argc) {
|
||||||
kprintf("error: too few args\n");
|
tinyprint(2, "error: missing command\n", NULL);
|
||||||
write(2, USAGE, sizeof(USAGE) - 1);
|
write(2, USAGE, sizeof(USAGE) - 1);
|
||||||
exit(64);
|
exit(64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perform process setup
|
||||||
if (!g_noclose) {
|
if (!g_noclose) {
|
||||||
NormalizeFileDescriptors();
|
NormalizeFileDescriptors();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_nice) {
|
if (g_nice) {
|
||||||
verynice();
|
verynice();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SetCpuLimit(g_cpuquota) == -1) {
|
if (SetCpuLimit(g_cpuquota) == -1) {
|
||||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_CPU");
|
perror("setrlimit(RLIMIT_CPU)");
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SetLimit(RLIMIT_FSIZE, g_fszquota, g_fszquota * 1.5) == -1) {
|
if (SetLimit(RLIMIT_FSIZE, g_fszquota, g_fszquota * 1.5) == -1) {
|
||||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_FSIZE");
|
perror("setrlimit(RLIMIT_FSIZE)");
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SetLimit(RLIMIT_AS, g_memquota, g_memquota) == -1) {
|
if (SetLimit(RLIMIT_AS, g_memquota, g_memquota) == -1) {
|
||||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_AS");
|
perror("setrlimit(RLIMIT_AS)");
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SetLimit(RLIMIT_NPROC, g_proquota, g_proquota) == -1) {
|
if (SetLimit(RLIMIT_NPROC, g_proquota, g_proquota) == -1) {
|
||||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_NPROC");
|
perror("setrlimit(RLIMIT_NPROC)");
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// test for weird chmod bits
|
// test for weird chmod bits
|
||||||
|
@ -664,8 +701,9 @@ int main(int argc, char *argv[]) {
|
||||||
// some flags can't be allowed if binary has setuid bits
|
// some flags can't be allowed if binary has setuid bits
|
||||||
if (hasfunbits) {
|
if (hasfunbits) {
|
||||||
if (g_uflag || g_gflag) {
|
if (g_uflag || g_gflag) {
|
||||||
kprintf("error: setuid flags forbidden on setuid binaries\n");
|
tinyprint(2, program_invocation_name,
|
||||||
_Exit(6);
|
": setuid flags forbidden on setuid binaries\n", NULL);
|
||||||
|
exit(6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,8 +712,8 @@ int main(int argc, char *argv[]) {
|
||||||
oldfsuid = setfsuid(useruid);
|
oldfsuid = setfsuid(useruid);
|
||||||
oldfsgid = setfsgid(usergid);
|
oldfsgid = setfsgid(usergid);
|
||||||
if (access(g_chroot, R_OK) == -1) {
|
if (access(g_chroot, R_OK) == -1) {
|
||||||
kprintf("error: access(%#s) failed: %m\n", g_chroot);
|
perror(g_chroot);
|
||||||
_Exit(7);
|
exit(7);
|
||||||
}
|
}
|
||||||
setfsuid(oldfsuid);
|
setfsuid(oldfsuid);
|
||||||
setfsgid(oldfsgid);
|
setfsgid(oldfsgid);
|
||||||
|
@ -684,12 +722,12 @@ int main(int argc, char *argv[]) {
|
||||||
// change root fs path
|
// change root fs path
|
||||||
if (g_chroot) {
|
if (g_chroot) {
|
||||||
if (chdir(g_chroot) == -1) {
|
if (chdir(g_chroot) == -1) {
|
||||||
kprintf("error: chdir(%#s) failed: %m\n", g_chroot);
|
perror(g_chroot);
|
||||||
_Exit(8);
|
exit(8);
|
||||||
}
|
}
|
||||||
if (chroot(g_chroot) == -1) {
|
if (chroot(g_chroot) == -1) {
|
||||||
kprintf("error: chroot(%#s) failed: %m\n", g_chroot);
|
perror(g_chroot);
|
||||||
_Exit(9);
|
exit(9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,42 +736,63 @@ int main(int argc, char *argv[]) {
|
||||||
oldfsuid = setfsuid(useruid);
|
oldfsuid = setfsuid(useruid);
|
||||||
oldfsgid = setfsgid(usergid);
|
oldfsgid = setfsgid(usergid);
|
||||||
}
|
}
|
||||||
if (!(prog = commandv(argv[optind], pathbuf, sizeof(pathbuf)))) {
|
if (!(g_prog = commandv(argv[optind], pathbuf, sizeof(pathbuf)))) {
|
||||||
kprintf("error: command not found: %m\n", argv[optind]);
|
perror(argv[optind]);
|
||||||
_Exit(10);
|
exit(10);
|
||||||
}
|
}
|
||||||
if (hasfunbits) {
|
if (hasfunbits) {
|
||||||
setfsuid(oldfsuid);
|
setfsuid(oldfsuid);
|
||||||
setfsgid(oldfsgid);
|
setfsgid(oldfsgid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy environment
|
||||||
|
// to setup child environment
|
||||||
|
// and remove variables we might create
|
||||||
|
unsetenv("_PLEDGE");
|
||||||
|
unsetenv("LD_PRELOAD");
|
||||||
|
int child_environ_count = CountEnviron(environ);
|
||||||
|
int child_environ_capacity = child_environ_count + 2 + 1;
|
||||||
|
int child_environ_bytes = child_environ_capacity * sizeof(char *);
|
||||||
|
char **child_environ = alloca(child_environ_bytes);
|
||||||
|
CheckLargeStackAllocation(child_environ, child_environ_bytes);
|
||||||
|
memcpy(child_environ, environ, child_environ_count * sizeof(char *));
|
||||||
|
bzero(child_environ + child_environ_count,
|
||||||
|
(child_environ_capacity - child_environ_count) * sizeof(char *));
|
||||||
|
|
||||||
// figure out where we want the dso
|
// figure out where we want the dso
|
||||||
if (IsDynamicExecutable(prog)) {
|
if (GetStrategy() == kStrategyDynamic) {
|
||||||
isdynamic = true;
|
|
||||||
if ((s = getenv("TMPDIR")) || //
|
if ((s = getenv("TMPDIR")) || //
|
||||||
(s = getenv("HOME")) || //
|
(s = getenv("HOME")) || //
|
||||||
(s = ".")) {
|
(s = ".")) {
|
||||||
ksnprintf(dsopath, sizeof(dsopath), "%s/sandbox.so", s);
|
strlcpy(dsopath, s, sizeof(dsopath));
|
||||||
|
strlcat(dsopath, "/.pledge-sandbox.so", sizeof(dsopath));
|
||||||
if (!FileExistsAndIsNewerThan(dsopath, GetProgramExecutableName())) {
|
if (!FileExistsAndIsNewerThan(dsopath, GetProgramExecutableName())) {
|
||||||
ksnprintf(tmppath, sizeof(tmppath), "%s/sandbox.so.%d", s, getpid());
|
errno = 0;
|
||||||
if (Extract("/zip/sandbox.so", tmppath, 0755) == -1) {
|
char pidstr[21];
|
||||||
kprintf("error: extract dso failed: %m\n");
|
FormatInt64(pidstr, getpid());
|
||||||
|
strlcpy(tmppath, s, sizeof(tmppath));
|
||||||
|
strlcat(tmppath, "/.pledge-sandbox.so.", sizeof(tmppath));
|
||||||
|
strlcat(tmppath, pidstr, sizeof(tmppath));
|
||||||
|
if (Extract("/zip/sandbox-" ARCH_NAME ".so", tmppath, 0755) == -1) {
|
||||||
|
perror(tmppath);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (rename(tmppath, dsopath) == -1) {
|
if (rename(tmppath, dsopath) == -1) {
|
||||||
kprintf("error: rename dso failed: %m\n");
|
perror(dsopath);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ksnprintf(buf, sizeof(buf), "LD_PRELOAD=%s", dsopath);
|
stpcpy(preloadvar, "LD_PRELOAD=");
|
||||||
putenv(buf);
|
strlcat(preloadvar, dsopath, sizeof(preloadvar));
|
||||||
|
child_environ[child_environ_count++] = preloadvar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_dontdrop) {
|
if (g_dontdrop) {
|
||||||
if (hasfunbits) {
|
if (hasfunbits) {
|
||||||
kprintf("error: -D flag forbidden on setuid binaries\n");
|
tinyprint(2, program_invocation_name,
|
||||||
_Exit(6);
|
": -D flag forbidden on setuid binaries\n", NULL);
|
||||||
|
exit(6);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DropCapabilities();
|
DropCapabilities();
|
||||||
|
@ -743,22 +802,22 @@ int main(int argc, char *argv[]) {
|
||||||
if (usergid != ownergid) {
|
if (usergid != ownergid) {
|
||||||
// setgid binaries must use the gid of the user that ran it
|
// setgid binaries must use the gid of the user that ran it
|
||||||
if (setgid(usergid) == -1) {
|
if (setgid(usergid) == -1) {
|
||||||
kprintf("error: setgid(%d) failed: %m\n", usergid);
|
perror("setgid");
|
||||||
_Exit(11);
|
exit(11);
|
||||||
}
|
}
|
||||||
if (getgid() != usergid || getegid() != usergid) {
|
if (getgid() != usergid || getegid() != usergid) {
|
||||||
kprintf("error: setgid() broken\n");
|
tinyprint(2, "error: setgid() broken\n", NULL);
|
||||||
_Exit(12);
|
exit(12);
|
||||||
}
|
}
|
||||||
} else if (g_gflag) {
|
} else if (g_gflag) {
|
||||||
// otherwise we trust the gid flag
|
// otherwise we trust the gid flag
|
||||||
if (setgid(g_gflag) == -1) {
|
if (setgid(g_gflag) == -1) {
|
||||||
kprintf("error: setgid(%d) failed: %m\n", g_gflag);
|
perror("setgid");
|
||||||
_Exit(13);
|
exit(13);
|
||||||
}
|
}
|
||||||
if (getgid() != g_gflag || getegid() != g_gflag) {
|
if (getgid() != g_gflag || getegid() != g_gflag) {
|
||||||
kprintf("error: setgid() broken\n");
|
tinyprint(2, "error: setgid() broken\n", NULL);
|
||||||
_Exit(14);
|
exit(14);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,30 +825,34 @@ int main(int argc, char *argv[]) {
|
||||||
if (useruid != owneruid) {
|
if (useruid != owneruid) {
|
||||||
// setuid binaries must use the uid of the user that ran it
|
// setuid binaries must use the uid of the user that ran it
|
||||||
if (setuid(useruid) == -1) {
|
if (setuid(useruid) == -1) {
|
||||||
kprintf("error: setuid(%d) failed: %m\n", useruid);
|
perror("setuid");
|
||||||
_Exit(15);
|
exit(15);
|
||||||
}
|
}
|
||||||
if (getuid() != useruid || geteuid() != useruid) {
|
if (getuid() != useruid || geteuid() != useruid) {
|
||||||
kprintf("error: setuid() broken\n");
|
tinyprint(2, "error: setuid() broken\n", NULL);
|
||||||
_Exit(16);
|
exit(16);
|
||||||
}
|
}
|
||||||
} else if (g_uflag) {
|
} else if (g_uflag) {
|
||||||
// otherwise we trust the uid flag
|
// otherwise we trust the uid flag
|
||||||
if (setuid(g_uflag) == -1) {
|
if (setuid(g_uflag) == -1) {
|
||||||
kprintf("error: setuid(%d) failed: %m\n", g_uflag);
|
perror("setuid");
|
||||||
_Exit(17);
|
exit(17);
|
||||||
}
|
}
|
||||||
if (getuid() != g_uflag || geteuid() != g_uflag) {
|
if (getuid() != g_uflag || geteuid() != g_uflag) {
|
||||||
kprintf("error: setuid() broken\n");
|
tinyprint(2, "error: setuid() broken\n", NULL);
|
||||||
_Exit(18);
|
exit(18);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse requested promises
|
||||||
|
// further changes to g_promises will be *effective* transient promises
|
||||||
if (ParsePromises(g_promises, &ipromises, 0) == -1) {
|
if (ParsePromises(g_promises, &ipromises, 0) == -1) {
|
||||||
kprintf("error: bad promises list: %s\n", g_promises);
|
tinyprint(2, program_invocation_name, ": bad promises list: ", g_promises,
|
||||||
_Exit(21);
|
"\n", NULL);
|
||||||
|
exit(21);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perform unveiling
|
||||||
ApplyFilesystemPolicy(ipromises);
|
ApplyFilesystemPolicy(ipromises);
|
||||||
|
|
||||||
// pledge.com uses the return eperm instead of killing the process
|
// pledge.com uses the return eperm instead of killing the process
|
||||||
|
@ -802,38 +865,70 @@ int main(int argc, char *argv[]) {
|
||||||
__pledge_mode = PLEDGE_PENALTY_RETURN_EPERM;
|
__pledge_mode = PLEDGE_PENALTY_RETURN_EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to be able to call execv and mmap the dso
|
// weaken system call policy to allow execution
|
||||||
// it'll be pledged away once/if the dso gets loaded
|
//
|
||||||
if (!(~ipromises & (1ul << PROMISE_EXEC))) {
|
// 1. we always need to pledge("exec") in order to use execve(). this
|
||||||
g_promises = xstrcat(g_promises, ' ', "exec");
|
// is the primary disadvantage to using the `pledge` command to
|
||||||
|
// bolt security onto unsecured programs (as opposed to using
|
||||||
|
// pledge as it was intended, by having the program authors update
|
||||||
|
// their code to invoke the pledge() system call from within their
|
||||||
|
// programs at the appropriate moments)
|
||||||
|
//
|
||||||
|
// 2. we usually need to force pledge("rpath prot_exec") too; dynamic
|
||||||
|
// executables need it mmap() shared objects during initialization
|
||||||
|
// and actually portable executables need it so /usr/bin/ape can
|
||||||
|
// mmap() the ELF program headers. the only time we don't require
|
||||||
|
// `prot_exec` is when launching a native ELF PT_EXEC binaries,
|
||||||
|
// e.g. assimilated actually portable executables.
|
||||||
|
//
|
||||||
|
// 3. in some cases we can remove the `exec` and `prot_exec` promises
|
||||||
|
// later on in the loading process. on musl and glibc systems, we
|
||||||
|
// do that by injecting an LD_PRELOAD DSO which calls pledge()
|
||||||
|
// again with the requested promises.
|
||||||
|
if (!HasPromise(ipromises, PROMISE_EXEC)) {
|
||||||
|
AddPromise("exec");
|
||||||
if (!g_qflag) {
|
if (!g_qflag) {
|
||||||
// TODO(jart): Fix me.
|
// TODO(jart): Fix me.
|
||||||
// __pledge_mode |= PLEDGE_STDERR_LOGGING;
|
// __pledge_mode |= PLEDGE_STDERR_LOGGING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isdynamic) {
|
if (GetStrategy() != kStrategyStatic) {
|
||||||
g_promises = xstrcat(g_promises, ' ', "prot_exec");
|
if (!HasPromise(ipromises, PROMISE_RPATH)) {
|
||||||
|
AddPromise("rpath");
|
||||||
|
}
|
||||||
|
if (!HasPromise(ipromises, PROMISE_PROT_EXEC)) {
|
||||||
|
AddPromise("prot_exec");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pass arguments to pledge() inside the dso
|
// pass parameters to injected dso
|
||||||
if (isdynamic) {
|
if (GetStrategy() == kStrategyDynamic) {
|
||||||
ksnprintf(buf, sizeof(buf), "_PLEDGE=%ld,%ld", ~ipromises, __pledge_mode);
|
char *p = pledgevar;
|
||||||
putenv(buf);
|
p = stpcpy(p, "_PLEDGE=");
|
||||||
|
p = FormatInt64(p, ~ipromises);
|
||||||
|
p = stpcpy(p, ",");
|
||||||
|
p = FormatInt64(p, __pledge_mode);
|
||||||
|
child_environ[child_environ_count++] = pledgevar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this limit needs to come last since unveil() opens fds
|
||||||
if (SetLimit(RLIMIT_NOFILE, g_nfdquota, g_nfdquota) == -1) {
|
if (SetLimit(RLIMIT_NOFILE, g_nfdquota, g_nfdquota) == -1) {
|
||||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_NOFILE");
|
perror("setrlimit(RLIMIT_NOFILE)");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply sandbox
|
// apply sandbox
|
||||||
if (pledge(g_promises, g_promises) == -1) {
|
if (pledge(g_promises, g_promises) == -1) {
|
||||||
kprintf("error: pledge(%#s) failed: %m\n", g_promises);
|
perror("pledge");
|
||||||
_Exit(19);
|
exit(19);
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch program
|
// launch program
|
||||||
sys_execve(prog, argv + optind, environ);
|
if (!IsWindows()) {
|
||||||
kprintf("%s: execve failed: %m\n", prog);
|
sys_execve(g_prog, argv + optind, child_environ);
|
||||||
|
} else {
|
||||||
|
sys_execve_nt(g_prog, argv + optind, child_environ);
|
||||||
|
}
|
||||||
|
perror(g_prog);
|
||||||
return 127;
|
return 127;
|
||||||
}
|
}
|
||||||
|
|
61
tool/build/verynice.c
Normal file
61
tool/build/verynice.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*-*- 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 2023 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/runtime/runtime.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "third_party/getopt/getopt.internal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview `verynice foo` launches `foo` as low priority as possible
|
||||||
|
*
|
||||||
|
* This is particularly useful on Linux systems with a spinning disk
|
||||||
|
* where the classic `nice` command doesn't do much.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *prog;
|
||||||
|
|
||||||
|
static wontreturn void PrintUsage(int rc, int fd) {
|
||||||
|
tinyprint(fd, "Usage: ", prog, " COMMAND...\n", NULL);
|
||||||
|
exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
prog = argv[0];
|
||||||
|
if (!prog) prog = "verynice";
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "h")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
PrintUsage(0, 1);
|
||||||
|
default:
|
||||||
|
PrintUsage(1, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (optind == argc) {
|
||||||
|
tinyprint(2, prog, ": missing command\n", NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
verynice();
|
||||||
|
execvp(argv[optind], argv + optind);
|
||||||
|
perror(argv[optind]);
|
||||||
|
exit(127);
|
||||||
|
}
|
Loading…
Reference in a new issue