mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
Improve pledge() and unveil()
The pledge.com command now supports the new [WIP] unveil() support. For example, to strongly sandbox our command for listing directories. o//tool/build/assimilate.com o//examples/ls.com pledge.com -v /etc -p 'stdio rpath' o//examples/ls.com /etc This file system sandboxing is going to be perfect for us, because APE binaries are self-contained static executables that really don't use the filesystem that much. On the other hand, with non-static executables, sandboxing is going to be more difficult. For example, here's how to sandbox the `ls` command on the latest Alpine: pledge.com -v rx:/lib -v /usr/lib -v /etc -p 'stdio rpath exec' ls /etc This change fixes the `execpromises` API with pledge(). This change also adds unix.unveil() to redbean. Fixes #494
This commit is contained in:
parent
b1d9d11be1
commit
e81edf7b04
19 changed files with 535 additions and 150 deletions
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/likely.h"
|
#include "libc/bits/likely.h"
|
||||||
|
#include "libc/bits/weaken.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/strace.internal.h"
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
|
@ -24,10 +25,13 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/asan.internal.h"
|
#include "libc/intrin/asan.internal.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/intrin/promises.internal.h"
|
||||||
#include "libc/log/libfatal.internal.h"
|
#include "libc/log/libfatal.internal.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
int sys_pledge_linux(unsigned long);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces current process with program.
|
* Replaces current process with program.
|
||||||
*
|
*
|
||||||
|
@ -66,7 +70,13 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
rc = sys_execve(prog, argv, envp);
|
rc = 0;
|
||||||
|
if (IsLinux() && __execpromises && weaken(sys_pledge_linux)) {
|
||||||
|
rc = weaken(sys_pledge_linux)(__execpromises);
|
||||||
|
}
|
||||||
|
if (!rc) {
|
||||||
|
rc = sys_execve(prog, argv, envp);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = sys_execve_nt(prog, argv, envp);
|
rc = sys_execve_nt(prog, argv, envp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/asan.internal.h"
|
#include "libc/intrin/asan.internal.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/sysv/consts/at.h"
|
#include "libc/sysv/consts/at.h"
|
||||||
|
@ -36,18 +37,27 @@
|
||||||
* @param path is a filename or directory
|
* @param path is a filename or directory
|
||||||
* @param mode can be R_OK, W_OK, X_OK, F_OK
|
* @param mode can be R_OK, W_OK, X_OK, F_OK
|
||||||
* @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW
|
* @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW
|
||||||
|
* @note on Linux flags is only supported on Linux 5.8+
|
||||||
* @return 0 if ok, or -1 and sets errno
|
* @return 0 if ok, or -1 and sets errno
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
int faccessat(int dirfd, const char *path, int mode, uint32_t flags) {
|
int faccessat(int dirfd, const char *path, int mode, uint32_t flags) {
|
||||||
int rc;
|
int e, rc;
|
||||||
if (IsAsan() && !__asan_is_valid(path, 1)) {
|
if (IsAsan() && !__asan_is_valid(path, 1)) {
|
||||||
rc = efault();
|
rc = efault();
|
||||||
} else if (weaken(__zipos_notat) &&
|
} else if (weaken(__zipos_notat) &&
|
||||||
weaken(__zipos_notat)(dirfd, path) == -1) {
|
weaken(__zipos_notat)(dirfd, path) == -1) {
|
||||||
rc = -1; /* TODO(jart): implement me */
|
rc = -1; /* TODO(jart): implement me */
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
rc = sys_faccessat(dirfd, path, mode, flags);
|
e = errno;
|
||||||
|
if (!flags) goto NoFlags;
|
||||||
|
if ((rc = sys_faccessat2(dirfd, path, mode, flags)) == -1) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
errno = e;
|
||||||
|
NoFlags:
|
||||||
|
rc = sys_faccessat(dirfd, path, mode, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = sys_faccessat_nt(dirfd, path, mode, flags);
|
rc = sys_faccessat_nt(dirfd, path, mode, flags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ i32 sys_dup2(i32, i32) hidden;
|
||||||
i32 sys_dup3(i32, i32, i32) hidden;
|
i32 sys_dup3(i32, i32, i32) hidden;
|
||||||
i32 sys_execve(const char *, char *const[], char *const[]) hidden;
|
i32 sys_execve(const char *, char *const[], char *const[]) hidden;
|
||||||
i32 sys_faccessat(i32, const char *, i32, u32) hidden;
|
i32 sys_faccessat(i32, const char *, i32, u32) hidden;
|
||||||
|
i32 sys_faccessat2(i32, const char *, i32, u32) hidden;
|
||||||
i32 sys_fadvise(i32, i64, i64, i32) hidden;
|
i32 sys_fadvise(i32, i64, i64, i32) hidden;
|
||||||
i32 sys_fchdir(i32) hidden;
|
i32 sys_fchdir(i32) hidden;
|
||||||
i32 sys_fchmod(i32, u32) hidden;
|
i32 sys_fchmod(i32, u32) hidden;
|
||||||
|
@ -95,6 +96,7 @@ i32 sys_tkill(i32, i32, void *) hidden;
|
||||||
i32 sys_truncate(const char *, u64, u64) hidden;
|
i32 sys_truncate(const char *, u64, u64) hidden;
|
||||||
i32 sys_uname(char *) hidden;
|
i32 sys_uname(char *) hidden;
|
||||||
i32 sys_unlinkat(i32, const char *, i32) hidden;
|
i32 sys_unlinkat(i32, const char *, i32) hidden;
|
||||||
|
i32 sys_unveil(const char *, const char *) hidden;
|
||||||
i64 sys_copy_file_range(i32, long *, i32, long *, u64, u32) hidden;
|
i64 sys_copy_file_range(i32, long *, i32, long *, u64, u32) hidden;
|
||||||
i64 sys_getrandom(void *, u64, u32) hidden;
|
i64 sys_getrandom(void *, u64, u32) hidden;
|
||||||
i64 sys_pread(i32, void *, u64, i64, i64) hidden;
|
i64 sys_pread(i32, void *, u64, i64, i64) hidden;
|
||||||
|
@ -111,7 +113,6 @@ u32 sys_geteuid(void) hidden;
|
||||||
u32 sys_getgid(void) hidden;
|
u32 sys_getgid(void) hidden;
|
||||||
u32 sys_getuid(void) hidden;
|
u32 sys_getuid(void) hidden;
|
||||||
u32 sys_umask(u32) hidden;
|
u32 sys_umask(u32) hidden;
|
||||||
i32 sys_unveil(const char *, const char *) hidden;
|
|
||||||
void *__sys_mmap(void *, u64, u32, u32, i64, i64, i64) hidden;
|
void *__sys_mmap(void *, u64, u32, u32, i64, i64, i64) hidden;
|
||||||
void *sys_mremap(void *, u64, u64, i32, void *) hidden;
|
void *sys_mremap(void *, u64, u64, i32, void *) hidden;
|
||||||
void sys_exit(int) hidden;
|
void sys_exit(int) hidden;
|
||||||
|
|
|
@ -19,3 +19,4 @@
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
|
|
||||||
unsigned long __promises;
|
unsigned long __promises;
|
||||||
|
unsigned long __execpromises;
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_
|
||||||
#define COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_
|
#define COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_
|
||||||
|
|
||||||
#define PROMISE_DEFAULT 0
|
#define PROMISE_STDIO 0
|
||||||
#define PROMISE_STDIO 1
|
#define PROMISE_RPATH 1
|
||||||
#define PROMISE_RPATH 2
|
#define PROMISE_WPATH 2
|
||||||
#define PROMISE_WPATH 3
|
#define PROMISE_CPATH 3
|
||||||
#define PROMISE_CPATH 4
|
#define PROMISE_DPATH 4
|
||||||
#define PROMISE_DPATH 5
|
#define PROMISE_FLOCK 5
|
||||||
#define PROMISE_FLOCK 6
|
#define PROMISE_FATTR 6
|
||||||
#define PROMISE_FATTR 7
|
#define PROMISE_INET 7
|
||||||
#define PROMISE_INET 8
|
#define PROMISE_UNIX 8
|
||||||
#define PROMISE_UNIX 9
|
#define PROMISE_DNS 9
|
||||||
#define PROMISE_DNS 10
|
#define PROMISE_TTY 10
|
||||||
#define PROMISE_TTY 11
|
#define PROMISE_RECVFD 11
|
||||||
#define PROMISE_RECVFD 12
|
#define PROMISE_PROC 12
|
||||||
#define PROMISE_PROC 13
|
#define PROMISE_THREAD 13
|
||||||
#define PROMISE_THREAD 14
|
#define PROMISE_EXEC 14
|
||||||
#define PROMISE_EXEC 15
|
#define PROMISE_EXECNATIVE 15
|
||||||
#define PROMISE_EXECNATIVE 16
|
#define PROMISE_ID 16
|
||||||
#define PROMISE_ID 17
|
#define PROMISE_UNVEIL 17
|
||||||
#define PROMISE_UNVEIL 18
|
#define PROMISE_SENDFD 18
|
||||||
#define PROMISE_MAX 18
|
|
||||||
|
|
||||||
#define PLEDGED(x) (~__promises & (1L << PROMISE_##x))
|
#define PLEDGED(x) ((~__promises >> PROMISE_##x) & 1)
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
hidden extern unsigned long __promises;
|
hidden extern unsigned long __promises;
|
||||||
|
hidden extern unsigned long __execpromises;
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
#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"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
@ -157,6 +156,7 @@ static const uint16_t kPledgeLinuxRpath[] = {
|
||||||
__NR_linux_fstatat, //
|
__NR_linux_fstatat, //
|
||||||
__NR_linux_access, //
|
__NR_linux_access, //
|
||||||
__NR_linux_faccessat, //
|
__NR_linux_faccessat, //
|
||||||
|
__NR_linux_faccessat2, //
|
||||||
__NR_linux_readlink, //
|
__NR_linux_readlink, //
|
||||||
__NR_linux_readlinkat, //
|
__NR_linux_readlinkat, //
|
||||||
__NR_linux_statfs, //
|
__NR_linux_statfs, //
|
||||||
|
@ -173,6 +173,7 @@ static const uint16_t kPledgeLinuxWpath[] = {
|
||||||
__NR_linux_fstatat, //
|
__NR_linux_fstatat, //
|
||||||
__NR_linux_access, //
|
__NR_linux_access, //
|
||||||
__NR_linux_faccessat, //
|
__NR_linux_faccessat, //
|
||||||
|
__NR_linux_faccessat2, //
|
||||||
__NR_linux_readlinkat, //
|
__NR_linux_readlinkat, //
|
||||||
__NR_linux_chmod, //
|
__NR_linux_chmod, //
|
||||||
__NR_linux_fchmod, //
|
__NR_linux_fchmod, //
|
||||||
|
@ -254,6 +255,10 @@ static const uint16_t kPledgeLinuxRecvfd[] = {
|
||||||
__NR_linux_recvmsg, //
|
__NR_linux_recvmsg, //
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint16_t kPledgeLinuxSendfd[] = {
|
||||||
|
__NR_linux_sendmsg, //
|
||||||
|
};
|
||||||
|
|
||||||
static const uint16_t kPledgeLinuxProc[] = {
|
static const uint16_t kPledgeLinuxProc[] = {
|
||||||
__NR_linux_fork, //
|
__NR_linux_fork, //
|
||||||
__NR_linux_vfork, //
|
__NR_linux_vfork, //
|
||||||
|
@ -268,8 +273,10 @@ static const uint16_t kPledgeLinuxProc[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t kPledgeLinuxThread[] = {
|
static const uint16_t kPledgeLinuxThread[] = {
|
||||||
__NR_linux_clone, //
|
__NR_linux_clone, //
|
||||||
__NR_linux_futex, //
|
__NR_linux_futex, //
|
||||||
|
__NR_linux_set_robust_list, //
|
||||||
|
__NR_linux_get_robust_list, //
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t kPledgeLinuxId[] = {
|
static const uint16_t kPledgeLinuxId[] = {
|
||||||
|
@ -313,7 +320,6 @@ static const struct Pledges {
|
||||||
const uint16_t *syscalls;
|
const uint16_t *syscalls;
|
||||||
const size_t len;
|
const size_t len;
|
||||||
} kPledgeLinux[] = {
|
} kPledgeLinux[] = {
|
||||||
[PROMISE_DEFAULT] = {"default", PLEDGE(kPledgeLinuxDefault)}, //
|
|
||||||
[PROMISE_STDIO] = {"stdio", PLEDGE(kPledgeLinuxStdio)}, //
|
[PROMISE_STDIO] = {"stdio", PLEDGE(kPledgeLinuxStdio)}, //
|
||||||
[PROMISE_RPATH] = {"rpath", PLEDGE(kPledgeLinuxRpath)}, //
|
[PROMISE_RPATH] = {"rpath", PLEDGE(kPledgeLinuxRpath)}, //
|
||||||
[PROMISE_WPATH] = {"wpath", PLEDGE(kPledgeLinuxWpath)}, //
|
[PROMISE_WPATH] = {"wpath", PLEDGE(kPledgeLinuxWpath)}, //
|
||||||
|
@ -326,13 +332,13 @@ static const struct Pledges {
|
||||||
[PROMISE_DNS] = {"dns", PLEDGE(kPledgeLinuxDns)}, //
|
[PROMISE_DNS] = {"dns", PLEDGE(kPledgeLinuxDns)}, //
|
||||||
[PROMISE_TTY] = {"tty", PLEDGE(kPledgeLinuxTty)}, //
|
[PROMISE_TTY] = {"tty", PLEDGE(kPledgeLinuxTty)}, //
|
||||||
[PROMISE_RECVFD] = {"recvfd", PLEDGE(kPledgeLinuxRecvfd)}, //
|
[PROMISE_RECVFD] = {"recvfd", PLEDGE(kPledgeLinuxRecvfd)}, //
|
||||||
|
[PROMISE_SENDFD] = {"sendfd", PLEDGE(kPledgeLinuxSendfd)}, //
|
||||||
[PROMISE_PROC] = {"proc", PLEDGE(kPledgeLinuxProc)}, //
|
[PROMISE_PROC] = {"proc", PLEDGE(kPledgeLinuxProc)}, //
|
||||||
[PROMISE_THREAD] = {"thread", PLEDGE(kPledgeLinuxThread)}, //
|
[PROMISE_THREAD] = {"thread", PLEDGE(kPledgeLinuxThread)}, //
|
||||||
[PROMISE_EXEC] = {"exec", PLEDGE(kPledgeLinuxExec)}, //
|
[PROMISE_EXEC] = {"exec", PLEDGE(kPledgeLinuxExec)}, //
|
||||||
[PROMISE_EXECNATIVE] = {"execnative", PLEDGE(kPledgeLinuxExec2)}, //
|
[PROMISE_EXECNATIVE] = {"execnative", PLEDGE(kPledgeLinuxExec2)}, //
|
||||||
[PROMISE_ID] = {"id", PLEDGE(kPledgeLinuxId)}, //
|
[PROMISE_ID] = {"id", PLEDGE(kPledgeLinuxId)}, //
|
||||||
[PROMISE_UNVEIL] = {"unveil", PLEDGE(kPledgeLinuxUnveil)}, //
|
[PROMISE_UNVEIL] = {"unveil", PLEDGE(kPledgeLinuxUnveil)}, //
|
||||||
[PROMISE_MAX + 1] = {0}, //
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sock_filter kFilterStart[] = {
|
static const struct sock_filter kFilterStart[] = {
|
||||||
|
@ -342,6 +348,13 @@ static const struct sock_filter kFilterStart[] = {
|
||||||
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
|
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
|
||||||
// each filter assumes ordinal is already loaded into accumulator
|
// each filter assumes ordinal is already loaded into accumulator
|
||||||
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
|
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
|
||||||
|
// Forbid some system calls with ENOSYS (rather than EPERM)
|
||||||
|
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_openat2, 0, 1),
|
||||||
|
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (38 & SECCOMP_RET_DATA)),
|
||||||
|
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_clone3, 0, 1),
|
||||||
|
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (38 & SECCOMP_RET_DATA)),
|
||||||
|
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_statx, 0, 1),
|
||||||
|
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (38 & SECCOMP_RET_DATA)),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sock_filter kFilterEnd[] = {
|
static const struct sock_filter kFilterEnd[] = {
|
||||||
|
@ -957,8 +970,8 @@ static bool AllowFchmodat(struct Filter *f) {
|
||||||
return AppendFilter(f, PLEDGE(fragment));
|
return AppendFilter(f, PLEDGE(fragment));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len, bool needmapexec,
|
static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len,
|
||||||
bool needmorphing) {
|
bool needmapexec, bool needmorphing) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
switch (p[i]) {
|
switch (p[i]) {
|
||||||
|
@ -1045,83 +1058,100 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len, bool n
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int FindPromise(const struct Pledges *p, const char *name, size_t *len) {
|
int sys_pledge_linux(unsigned long ipromises) {
|
||||||
int i;
|
bool ok = true;
|
||||||
for (i = 0; p[i].name; ++i) {
|
int i, rc = -1;
|
||||||
if (!strcasecmp(name, p[i].name)) {
|
|
||||||
*len = p[i].len;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sys_pledge_linux(const char *promises, const char *execpromises) {
|
|
||||||
bool ok;
|
|
||||||
int rc = -1;
|
|
||||||
size_t plen;
|
|
||||||
int promise;
|
|
||||||
bool needmapexec;
|
bool needmapexec;
|
||||||
bool needexecnative;
|
|
||||||
bool needmorphing;
|
bool needmorphing;
|
||||||
|
bool needexecnative;
|
||||||
struct Filter f = {0};
|
struct Filter f = {0};
|
||||||
const uint16_t *pledge;
|
ipromises = ~ipromises;
|
||||||
unsigned long ipromises = -1;
|
needmapexec = (ipromises >> PROMISE_EXEC) & 1;
|
||||||
char *s, *tok, *state, *start;
|
needmorphing = (ipromises >> PROMISE_THREAD) & 1;
|
||||||
if (execpromises) return einval();
|
needexecnative = (ipromises >> PROMISE_EXECNATIVE) & 1;
|
||||||
needmapexec = strstr(promises, "exec");
|
if (AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) &&
|
||||||
needmorphing = strstr(promises, "thread");
|
|
||||||
needexecnative = strstr(promises, "execnative");
|
|
||||||
if ((start = s = strdup(promises)) && AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) &&
|
|
||||||
(needmapexec || needexecnative || AppendOriginVerification(&f)) &&
|
(needmapexec || needexecnative || AppendOriginVerification(&f)) &&
|
||||||
AppendPledge(&f, kPledgeLinuxDefault, ARRAYLEN(kPledgeLinuxDefault), needmapexec,
|
AppendPledge(&f, kPledgeLinuxDefault, ARRAYLEN(kPledgeLinuxDefault),
|
||||||
needmorphing)) {
|
needmapexec, needmorphing)) {
|
||||||
for (ok = true; (tok = strtok_r(start, " \t\r\n", &state)); start = 0) {
|
for (i = 0; i < ARRAYLEN(kPledgeLinux); ++i) {
|
||||||
if ((promise = FindPromise(kPledgeLinux, tok, &plen)) != -1) {
|
if ((ipromises & (1ul << i)) && kPledgeLinux[i].name) {
|
||||||
pledge = kPledgeLinux[promise].syscalls;
|
ipromises &= ~(1ul << i);
|
||||||
ipromises &= ~(1ULL << promise);
|
if (!AppendPledge(&f, kPledgeLinux[i].syscalls, kPledgeLinux[i].len,
|
||||||
} else {
|
needmapexec, needmorphing)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
rc = einval();
|
rc = einval();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!AppendPledge(&f, pledge, plen, needmapexec, needmorphing)) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ipromises) {
|
||||||
|
ok = false;
|
||||||
|
rc = einval();
|
||||||
|
}
|
||||||
if (ok && AppendFilter(&f, kFilterEnd, ARRAYLEN(kFilterEnd)) &&
|
if (ok && AppendFilter(&f, kFilterEnd, ARRAYLEN(kFilterEnd)) &&
|
||||||
(rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1) {
|
(rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1) {
|
||||||
struct sock_fprog sandbox = {.len = f.n, .filter = f.p};
|
struct sock_fprog sandbox = {.len = f.n, .filter = f.p};
|
||||||
rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox);
|
rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox);
|
||||||
}
|
}
|
||||||
if (!rc) {
|
|
||||||
__promises = ipromises;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
free(f.p);
|
free(f.p);
|
||||||
free(s);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetPromises(const char *promises) {
|
static int FindPromise(const char *name) {
|
||||||
int promise;
|
int i;
|
||||||
size_t plen;
|
for (i = 0; i < ARRAYLEN(kPledgeLinux); ++i) {
|
||||||
char *tok, *state, *start;
|
if (!strcasecmp(name, kPledgeLinux[i].name)) {
|
||||||
unsigned long ipromises = -1;
|
return i;
|
||||||
while ((tok = strtok_r(start, " \t\r\n", &state))) {
|
|
||||||
if ((promise = FindPromise(kPledgeLinux, tok, &plen)) != -1) {
|
|
||||||
ipromises &= ~(1ULL << promise);
|
|
||||||
}
|
}
|
||||||
start = 0;
|
|
||||||
}
|
}
|
||||||
__promises = ipromises;
|
STRACE("unknown promise %s", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ParsePromises(const char *promises, unsigned long *out) {
|
||||||
|
int rc = 0;
|
||||||
|
int promise;
|
||||||
|
unsigned long ipromises;
|
||||||
|
char *tok, *state, *start, *freeme;
|
||||||
|
if (promises) {
|
||||||
|
ipromises = -1;
|
||||||
|
freeme = start = strdup(promises);
|
||||||
|
while ((tok = strtok_r(start, " \t\r\n", &state))) {
|
||||||
|
if ((promise = FindPromise(tok)) != -1) {
|
||||||
|
ipromises &= ~(1ULL << promise);
|
||||||
|
} else {
|
||||||
|
rc = einval();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
free(freeme);
|
||||||
|
} else {
|
||||||
|
ipromises = 0;
|
||||||
|
}
|
||||||
|
if (!rc) {
|
||||||
|
*out = ipromises;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FixupOpenbsdPromises(char *p) {
|
||||||
|
if (!p) return;
|
||||||
|
if ((p = strstr(p, "execnative"))) {
|
||||||
|
p[4] = ' ';
|
||||||
|
p[5] = ' ';
|
||||||
|
p[6] = ' ';
|
||||||
|
p[7] = ' ';
|
||||||
|
p[8] = ' ';
|
||||||
|
p[9] = ' ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restricts system operations, e.g.
|
* Restricts system operations, e.g.
|
||||||
*
|
*
|
||||||
* pledge("stdio tty", 0);
|
* pledge("stdio rfile tty", 0);
|
||||||
*
|
*
|
||||||
* Pledging causes most system calls to become unavailable. Your system
|
* Pledging causes most system calls to become unavailable. Your system
|
||||||
* call policy is enforced by the kernel, which means it can propagate
|
* call policy is enforced by the kernel, which means it can propagate
|
||||||
|
@ -1185,12 +1215,13 @@ static void SetPromises(const char *promises) {
|
||||||
* fcntl(F_GETFL), fcntl(F_SETFL).
|
* fcntl(F_GETFL), fcntl(F_SETFL).
|
||||||
*
|
*
|
||||||
* - "rpath" (read-only path ops) allows chdir, getcwd, open(O_RDONLY),
|
* - "rpath" (read-only path ops) allows chdir, getcwd, open(O_RDONLY),
|
||||||
* openat(O_RDONLY), stat, fstat, lstat, fstatat, access, faccessat,
|
* openat(O_RDONLY), stat, fstat, lstat, fstatat, access,
|
||||||
* readlink, readlinkat, statfs, fstatfs.
|
* faccessat,faccessat2, readlink, readlinkat, statfs, fstatfs.
|
||||||
*
|
*
|
||||||
* - "wpath" (write path ops) allows getcwd, open(O_WRONLY),
|
* - "wpath" (write path ops) allows getcwd, open(O_WRONLY),
|
||||||
* openat(O_WRONLY), stat, fstat, lstat, fstatat, access, faccessat,
|
* openat(O_WRONLY), stat, fstat, lstat, fstatat, access,
|
||||||
* readlink, readlinkat, chmod, fchmod, fchmodat.
|
* faccessat,faccessat2, readlink, readlinkat, chmod, fchmod,
|
||||||
|
* fchmodat.
|
||||||
*
|
*
|
||||||
* - "cpath" (create path ops) allows open(O_CREAT), openat(O_CREAT),
|
* - "cpath" (create path ops) allows open(O_CREAT), openat(O_CREAT),
|
||||||
* rename, renameat, renameat2, link, linkat, symlink, symlinkat,
|
* rename, renameat, renameat2, link, linkat, symlink, symlinkat,
|
||||||
|
@ -1204,7 +1235,9 @@ static void SetPromises(const char *promises) {
|
||||||
* - "tty" allows ioctl(TIOCGWINSZ), ioctl(TCGETS), ioctl(TCSETS),
|
* - "tty" allows ioctl(TIOCGWINSZ), ioctl(TCGETS), ioctl(TCSETS),
|
||||||
* ioctl(TCSETSW), ioctl(TCSETSF).
|
* ioctl(TCSETSW), ioctl(TCSETSF).
|
||||||
*
|
*
|
||||||
* - "recvfd" allows recvmsg(SCM_RIGHTS).
|
* - "recvfd" allows recvmsg in general (for SCM_RIGHTS).
|
||||||
|
*
|
||||||
|
* - "recvfd" allows sendmsg in general (for SCM_RIGHTS).
|
||||||
*
|
*
|
||||||
* - "fattr" allows chmod, fchmod, fchmodat, utime, utimes, futimens,
|
* - "fattr" allows chmod, fchmod, fchmodat, utime, utimes, futimens,
|
||||||
* utimensat.
|
* utimensat.
|
||||||
|
@ -1236,24 +1269,70 @@ static void SetPromises(const char *promises) {
|
||||||
* native executables; you won't be able to run APE binaries. mmap()
|
* native executables; you won't be able to run APE binaries. mmap()
|
||||||
* and mprotect() are still prevented from creating executable memory.
|
* and mprotect() are still prevented from creating executable memory.
|
||||||
* System call origin verification can't be enabled. If you always
|
* System call origin verification can't be enabled. If you always
|
||||||
* assimilate your APE binaries, then this should be preferred.
|
* assimilate your APE binaries, then this should be preferred. On
|
||||||
|
* OpenBSD this will be rewritten to be "exec".
|
||||||
*
|
*
|
||||||
* - "unveil" allows unveil() to be called, as well as the underlying
|
* - "unveil" allows unveil() to be called, as well as the underlying
|
||||||
* landlock_create_ruleset, landlock_add_rule, landlock_restrict_self
|
* landlock_create_ruleset, landlock_add_rule, landlock_restrict_self
|
||||||
* calls on Linux.
|
* calls on Linux.
|
||||||
*
|
*
|
||||||
|
* `execpromises` only matters if "exec" or "execnative" are specified
|
||||||
|
* in `promises`. In that case, this specifies the promises that'll
|
||||||
|
* apply once execve() happens. If this is NULL then the default is
|
||||||
|
* used, which is unrestricted. OpenBSD allows child processes to escape
|
||||||
|
* the sandbox (so a pledged OpenSSH server process can do things like
|
||||||
|
* spawn a root shell). Linux however requires monotonically decreasing
|
||||||
|
* privileges. This function will will perform some validation on Linux
|
||||||
|
* to make sure that `execpromises` is a subset of `promises`. Your libc
|
||||||
|
* wrapper for execve() will then apply its SECCOMP BPF filter later.
|
||||||
|
* Since Linux has to do this before calling sys_execve(), the executed
|
||||||
|
* process will be weakened to have execute permissions too.
|
||||||
|
*
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
* @raise ENOSYS if host os isn't Linux or OpenBSD
|
* @raise ENOSYS if host os isn't Linux or OpenBSD
|
||||||
* @raise EINVAL if `execpromises` is used on Linux
|
* @raise EINVAL if `execpromises` on Linux isn't a subset of `promises`
|
||||||
*/
|
*/
|
||||||
int pledge(const char *promises, const char *execpromises) {
|
int pledge(const char *promises, const char *execpromises) {
|
||||||
int rc;
|
int rc;
|
||||||
if (IsLinux()) {
|
char *p, *q;
|
||||||
rc = sys_pledge_linux(promises, execpromises);
|
unsigned long ipromises, iexecpromises;
|
||||||
} else {
|
if (!(rc = ParsePromises(promises, &ipromises)) &&
|
||||||
rc = sys_pledge(promises, execpromises);
|
!(rc = ParsePromises(execpromises, &iexecpromises))) {
|
||||||
|
if (IsLinux()) {
|
||||||
|
// copy exec and execnative from promises to execpromises
|
||||||
|
iexecpromises =
|
||||||
|
~(~iexecpromises | (~ipromises & ((1ul << PROMISE_EXEC) | //
|
||||||
|
(1ul << PROMISE_EXECNATIVE))));
|
||||||
|
// if bits are missing in execpromises that exist in promises
|
||||||
|
// then execpromises wouldn't be a monotonic access reduction
|
||||||
|
// this check only matters when exec / execnative are allowed
|
||||||
|
if ((ipromises & ~iexecpromises) &&
|
||||||
|
(~ipromises &
|
||||||
|
((1ul << PROMISE_EXEC) | (1ul << PROMISE_EXECNATIVE)))) {
|
||||||
|
STRACE("execpromises must be a subset of promises");
|
||||||
|
rc = einval();
|
||||||
|
} else {
|
||||||
|
rc = sys_pledge_linux(ipromises);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// openbsd only supports execnative and calls it exec
|
||||||
|
if ((p = strdup(promises))) {
|
||||||
|
FixupOpenbsdPromises(p);
|
||||||
|
if ((q = execpromises ? strdup(execpromises) : 0) || !execpromises) {
|
||||||
|
FixupOpenbsdPromises(q);
|
||||||
|
rc = sys_pledge(p, q);
|
||||||
|
free(q);
|
||||||
|
} else {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
} else {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
SetPromises(promises);
|
__promises = ipromises;
|
||||||
|
__execpromises = iexecpromises;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STRACE("pledge(%#s, %#s) → %d% m", promises, execpromises, rc);
|
STRACE("pledge(%#s, %#s) → %d% m", promises, execpromises, rc);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/landlock.h"
|
#include "libc/calls/landlock.h"
|
||||||
#include "libc/calls/strace.internal.h"
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/calls/struct/stat.h"
|
#include "libc/calls/struct/stat.h"
|
||||||
|
@ -25,7 +26,10 @@
|
||||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/nexgen32e/threaded.h"
|
||||||
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/at.h"
|
||||||
#include "libc/sysv/consts/f.h"
|
#include "libc/sysv/consts/f.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/pr.h"
|
#include "libc/sysv/consts/pr.h"
|
||||||
|
@ -126,10 +130,13 @@ static int sys_unveil_linux(const char *path, const char *permissions) {
|
||||||
if ((rc = sys_open(path, O_PATH | O_CLOEXEC, 0)) == -1) return rc;
|
if ((rc = sys_open(path, O_PATH | O_CLOEXEC, 0)) == -1) return rc;
|
||||||
pb.parent_fd = rc;
|
pb.parent_fd = rc;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if ((rc = fstat(pb.parent_fd, &st)) == -1) return err_close(rc, pb.parent_fd);
|
if ((rc = sys_fstat(pb.parent_fd, &st)) == -1) {
|
||||||
if (!S_ISDIR(st.st_mode)) pb.allowed_access &= FILE_BITS;
|
|
||||||
if ((rc = landlock_add_rule(State.fd, LANDLOCK_RULE_PATH_BENEATH, &pb, 0)))
|
|
||||||
return err_close(rc, pb.parent_fd);
|
return err_close(rc, pb.parent_fd);
|
||||||
|
}
|
||||||
|
if (!S_ISDIR(st.st_mode)) pb.allowed_access &= FILE_BITS;
|
||||||
|
if ((rc = landlock_add_rule(State.fd, LANDLOCK_RULE_PATH_BENEATH, &pb, 0))) {
|
||||||
|
return err_close(rc, pb.parent_fd);
|
||||||
|
}
|
||||||
sys_close(pb.parent_fd);
|
sys_close(pb.parent_fd);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -139,6 +146,7 @@ static int sys_unveil_linux(const char *path, const char *permissions) {
|
||||||
*/
|
*/
|
||||||
int unveil(const char *path, const char *permissions) {
|
int unveil(const char *path, const char *permissions) {
|
||||||
int rc;
|
int rc;
|
||||||
|
__enable_tls();
|
||||||
if (IsLinux()) {
|
if (IsLinux()) {
|
||||||
rc = sys_unveil_linux(path, permissions);
|
rc = sys_unveil_linux(path, permissions);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall faccessat2,0xfffffffffffff1b7,globl
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall openat2,0xfffffffffffff1b5,globl
|
|
2
libc/sysv/calls/sys_faccessat2.s
Normal file
2
libc/sysv/calls/sys_faccessat2.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_faccessat2,0xfffffffffffff1b7,globl,hidden
|
2
libc/sysv/calls/sys_openat2.s
Normal file
2
libc/sysv/calls/sys_openat2.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_openat2,0xfffffffffffff1b5,globl,hidden
|
|
@ -385,9 +385,9 @@ scall fspick 0xfffffffffffff1b1 globl
|
||||||
scall pidfd_open 0xfffffffffffff1b2 globl
|
scall pidfd_open 0xfffffffffffff1b2 globl
|
||||||
scall clone3 0xfffffffffffff1b3 globl
|
scall clone3 0xfffffffffffff1b3 globl
|
||||||
scall close_range 0xfffffffffffff1b4 globl
|
scall close_range 0xfffffffffffff1b4 globl
|
||||||
scall openat2 0xfffffffffffff1b5 globl # Linux 5.6
|
scall sys_openat2 0xfffffffffffff1b5 globl hidden # Linux 5.6
|
||||||
scall pidfd_getfd 0xfffffffffffff1b6 globl
|
scall pidfd_getfd 0xfffffffffffff1b6 globl
|
||||||
scall faccessat2 0xfffffffffffff1b7 globl
|
scall sys_faccessat2 0xfffffffffffff1b7 globl hidden
|
||||||
scall process_madvise 0xfffffffffffff1b8 globl
|
scall process_madvise 0xfffffffffffff1b8 globl
|
||||||
scall epoll_pwait2 0xfffffffffffff1b9 globl
|
scall epoll_pwait2 0xfffffffffffff1b9 globl
|
||||||
scall mount_setattr 0xfffffffffffff1ba globl
|
scall mount_setattr 0xfffffffffffff1ba globl
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/mem/io.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
|
@ -48,16 +50,33 @@
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/thread/spawn.h"
|
#include "libc/thread/spawn.h"
|
||||||
|
|
||||||
|
STATIC_YOINK("zip_uri_support");
|
||||||
|
|
||||||
char testlib_enable_tmp_setup_teardown;
|
char testlib_enable_tmp_setup_teardown;
|
||||||
|
|
||||||
void OnSig(int sig) {
|
void OnSig(int sig) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetUp(void) {
|
int extract(const char *from, const char *to, int mode) {
|
||||||
if (!__is_linux_2_6_23() && !IsOpenbsd()) {
|
int fdin, fdout;
|
||||||
exit(0);
|
if ((fdin = open(from, O_RDONLY)) == -1) return -1;
|
||||||
|
if ((fdout = creat(to, mode)) == -1) {
|
||||||
|
close(fdin);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (_copyfd(fdin, fdout, -1) == -1) {
|
||||||
|
close(fdout);
|
||||||
|
close(fdin);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return close(fdout) | close(fdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pledge, default_allowsExit) {
|
TEST(pledge, default_allowsExit) {
|
||||||
|
@ -149,8 +168,8 @@ TEST(pledge, multipleCalls_canOnlyBecomeMoreRestrictive2) {
|
||||||
int ws, pid;
|
int ws, pid;
|
||||||
ASSERT_NE(-1, (pid = fork()));
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
ASSERT_SYS(0, 0, pledge("stdio", 0));
|
ASSERT_SYS(0, 0, pledge("stdio", "stdio"));
|
||||||
ASSERT_SYS(EPERM, -1, pledge("stdio unix", 0));
|
ASSERT_SYS(EPERM, -1, pledge("stdio unix", "stdio unix"));
|
||||||
_Exit(0);
|
_Exit(0);
|
||||||
}
|
}
|
||||||
EXPECT_NE(-1, wait(&ws));
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
@ -293,7 +312,7 @@ TEST(pledge, mmapExec) {
|
||||||
int ws, pid;
|
int ws, pid;
|
||||||
ASSERT_NE(-1, (pid = fork()));
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
if (!pid) {
|
if (!pid) {
|
||||||
ASSERT_SYS(0, 0, pledge("stdio exec", 0));
|
ASSERT_SYS(0, 0, pledge("stdio exec", "stdio"));
|
||||||
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
|
ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
|
||||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||||
ASSERT_SYS(0, 0, mprotect(p, FRAMESIZE, PROT_READ));
|
ASSERT_SYS(0, 0, mprotect(p, FRAMESIZE, PROT_READ));
|
||||||
|
@ -414,3 +433,98 @@ TEST(pledge, sigaction_isFineButForbidsSigsys) {
|
||||||
EXPECT_NE(-1, wait(&ws));
|
EXPECT_NE(-1, wait(&ws));
|
||||||
EXPECT_TRUE(WIFEXITED(ws) && !WEXITSTATUS(ws));
|
EXPECT_TRUE(WIFEXITED(ws) && !WEXITSTATUS(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(pledge, execpromises_ok) {
|
||||||
|
if (IsOpenbsd()) return; // b/c testing linux bpf
|
||||||
|
int ws, pid;
|
||||||
|
struct stat st;
|
||||||
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
|
if (!pid) {
|
||||||
|
ASSERT_SYS(0, 0, pledge("stdio execnative", "stdio"));
|
||||||
|
execl("life.elf", "life.elf", 0);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
|
EXPECT_EQ(42, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pledge, execpromises_notok) {
|
||||||
|
if (IsOpenbsd()) return; // b/c testing linux bpf
|
||||||
|
int ws, pid;
|
||||||
|
struct stat st;
|
||||||
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
|
if (!pid) {
|
||||||
|
ASSERT_SYS(0, 0, pledge("stdio execnative", "stdio"));
|
||||||
|
execl("sock.elf", "sock.elf", 0);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
|
EXPECT_EQ(128 + EPERM, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pledge, execpromises_reducesAtExecOnLinux) {
|
||||||
|
if (IsOpenbsd()) return; // b/c testing linux bpf
|
||||||
|
int ws, pid;
|
||||||
|
struct stat st;
|
||||||
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
|
if (!pid) {
|
||||||
|
ASSERT_SYS(0, 0, pledge("stdio inet tty execnative", "stdio tty"));
|
||||||
|
execl("sock.elf", "sock.elf", 0);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
|
EXPECT_EQ(128 + EPERM, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pledge_openbsd, execpromisesIsNull_letsItDoAnything) {
|
||||||
|
if (!IsOpenbsd()) return;
|
||||||
|
int ws, pid;
|
||||||
|
struct stat st;
|
||||||
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
|
if (!pid) {
|
||||||
|
ASSERT_SYS(0, 0, pledge("stdio execnative", 0));
|
||||||
|
execl("sock.elf", "sock.elf", 0);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
|
EXPECT_EQ(3, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pledge_openbsd, execpromisesIsSuperset_letsItDoAnything) {
|
||||||
|
if (!IsOpenbsd()) return;
|
||||||
|
int ws, pid;
|
||||||
|
struct stat st;
|
||||||
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
|
if (!pid) {
|
||||||
|
ASSERT_SYS(0, 0, pledge("stdio rpath execnative", "stdio rpath tty inet"));
|
||||||
|
execl("sock.elf", "sock.elf", 0);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
|
EXPECT_EQ(3, WEXITSTATUS(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pledge_linux, execpromisesIsSuperset_notPossible) {
|
||||||
|
if (IsOpenbsd()) return;
|
||||||
|
ASSERT_SYS(EINVAL, -1, pledge("stdio execnative", "stdio inet execnative"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(pledge_openbsd, execpromises_notok) {
|
||||||
|
if (!IsOpenbsd()) return;
|
||||||
|
int ws, pid;
|
||||||
|
struct stat st;
|
||||||
|
ASSERT_NE(-1, (pid = fork()));
|
||||||
|
if (!pid) {
|
||||||
|
ASSERT_SYS(0, 0, pledge("stdio execnative", "stdio"));
|
||||||
|
execl("sock.elf", "sock.elf", 0);
|
||||||
|
_Exit(127);
|
||||||
|
}
|
||||||
|
EXPECT_NE(-1, wait(&ws));
|
||||||
|
EXPECT_TRUE(WIFSIGNALED(ws));
|
||||||
|
EXPECT_EQ(SIGABRT, WTERMSIG(ws));
|
||||||
|
}
|
||||||
|
|
31
test/libc/mem/prog/sock.c
Normal file
31
test/libc/mem/prog/sock.c
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*-*- 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/errno.h"
|
||||||
|
#include "libc/sock/sock.h"
|
||||||
|
#include "libc/sysv/consts/af.h"
|
||||||
|
#include "libc/sysv/consts/sock.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int fd;
|
||||||
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
|
||||||
|
return fd;
|
||||||
|
} else {
|
||||||
|
return 128 + errno;
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,34 +61,64 @@ o/$(MODE)/test/libc/mem/%.com.dbg: \
|
||||||
$(TEST_LIBC_MEM_DEPS) \
|
$(TEST_LIBC_MEM_DEPS) \
|
||||||
o/$(MODE)/test/libc/mem/%.o \
|
o/$(MODE)/test/libc/mem/%.o \
|
||||||
o/$(MODE)/test/libc/mem/mem.pkg \
|
o/$(MODE)/test/libc/mem/mem.pkg \
|
||||||
o/$(MODE)/test/libc/mem/life.elf.zip.o \
|
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o \
|
||||||
$(LIBC_TESTMAIN) \
|
$(LIBC_TESTMAIN) \
|
||||||
$(CRT) \
|
$(CRT) \
|
||||||
$(APE_NO_MODIFY_SELF)
|
$(APE_NO_MODIFY_SELF)
|
||||||
@$(APELINK)
|
@$(APELINK)
|
||||||
|
|
||||||
o/$(MODE)/test/libc/mem/life.com.dbg: \
|
################################################################################
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/mem/prog/life.com.dbg: \
|
||||||
$(LIBC_RUNTIME) \
|
$(LIBC_RUNTIME) \
|
||||||
o/$(MODE)/test/libc/mem/life.o \
|
o/$(MODE)/test/libc/mem/prog/life.o \
|
||||||
$(CRT) \
|
$(CRT) \
|
||||||
$(APE)
|
$(APE)
|
||||||
@$(APELINK)
|
@$(APELINK)
|
||||||
|
|
||||||
o/$(MODE)/test/libc/mem/life.elf: \
|
o/$(MODE)/test/libc/mem/prog/life.elf: \
|
||||||
o/$(MODE)/tool/build/assimilate.com \
|
o/$(MODE)/tool/build/assimilate.com \
|
||||||
o/$(MODE)/test/libc/mem/life.com
|
o/$(MODE)/test/libc/mem/prog/life.com
|
||||||
@$(COMPILE) -ACP -T$@ \
|
@$(COMPILE) -ACP -T$@ \
|
||||||
build/bootstrap/cp.com \
|
build/bootstrap/cp.com \
|
||||||
o/$(MODE)/test/libc/mem/life.com \
|
o/$(MODE)/test/libc/mem/prog/life.com \
|
||||||
o/$(MODE)/test/libc/mem/life.elf
|
o/$(MODE)/test/libc/mem/prog/life.elf
|
||||||
@$(COMPILE) -AASSIMILATE -T$@ \
|
@$(COMPILE) -AASSIMILATE -T$@ \
|
||||||
o/$(MODE)/tool/build/assimilate.com \
|
o/$(MODE)/tool/build/assimilate.com \
|
||||||
o/$(MODE)/test/libc/mem/life.elf
|
o/$(MODE)/test/libc/mem/prog/life.elf
|
||||||
|
|
||||||
o/$(MODE)/test/libc/mem/life.elf.zip.o: \
|
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o: \
|
||||||
ZIPOBJ_FLAGS += \
|
ZIPOBJ_FLAGS += \
|
||||||
-B
|
-B
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.com.dbg: \
|
||||||
|
$(LIBC_RUNTIME) \
|
||||||
|
$(LIBC_SOCK) \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.o \
|
||||||
|
$(CRT) \
|
||||||
|
$(APE)
|
||||||
|
@$(APELINK)
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.elf: \
|
||||||
|
o/$(MODE)/tool/build/assimilate.com \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.com
|
||||||
|
@$(COMPILE) -ACP -T$@ \
|
||||||
|
build/bootstrap/cp.com \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.com \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.elf
|
||||||
|
@$(COMPILE) -AASSIMILATE -T$@ \
|
||||||
|
o/$(MODE)/tool/build/assimilate.com \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.elf
|
||||||
|
|
||||||
|
o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o: \
|
||||||
|
ZIPOBJ_FLAGS += \
|
||||||
|
-B
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
$(TEST_LIBC_MEM_OBJS): \
|
$(TEST_LIBC_MEM_OBJS): \
|
||||||
DEFAULT_CCFLAGS += \
|
DEFAULT_CCFLAGS += \
|
||||||
-fno-builtin
|
-fno-builtin
|
||||||
|
|
15
third_party/lua/lunix.c
vendored
15
third_party/lua/lunix.c
vendored
|
@ -1382,12 +1382,22 @@ static int LuaUnixSiocgifconf(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sandbox.pledge([promises:str])
|
// sandbox.pledge([promises:str[, execpromises:str]])
|
||||||
// ├─→ true
|
// ├─→ true
|
||||||
// └─→ nil, unix.Errno
|
// └─→ nil, unix.Errno
|
||||||
static int LuaUnixPledge(lua_State *L) {
|
static int LuaUnixPledge(lua_State *L) {
|
||||||
int olderr = errno;
|
int olderr = errno;
|
||||||
return SysretBool(L, "pledge", olderr, pledge(luaL_checkstring(L, 1), 0));
|
return SysretBool(L, "pledge", olderr,
|
||||||
|
pledge(luaL_checkstring(L, 1), luaL_optstring(L, 2, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// sandbox.unveil(path:str, permissions:str)
|
||||||
|
// ├─→ true
|
||||||
|
// └─→ nil, unix.Errno
|
||||||
|
static int LuaUnixUnveil(lua_State *L) {
|
||||||
|
int olderr = errno;
|
||||||
|
return SysretBool(L, "unveil", olderr,
|
||||||
|
unveil(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// unix.gethostname()
|
// unix.gethostname()
|
||||||
|
@ -2636,6 +2646,7 @@ static const luaL_Reg kLuaUnix[] = {
|
||||||
{"truncate", LuaUnixTruncate}, // shrink or extend file medium
|
{"truncate", LuaUnixTruncate}, // shrink or extend file medium
|
||||||
{"umask", LuaUnixUmask}, // set default file mask
|
{"umask", LuaUnixUmask}, // set default file mask
|
||||||
{"unlink", LuaUnixUnlink}, // remove file
|
{"unlink", LuaUnixUnlink}, // remove file
|
||||||
|
{"unveil", LuaUnixUnveil}, // filesystem sandboxing
|
||||||
{"utimensat", LuaUnixUtimensat}, // change access/modified time
|
{"utimensat", LuaUnixUtimensat}, // change access/modified time
|
||||||
{"wait", LuaUnixWait}, // wait for child to change status
|
{"wait", LuaUnixWait}, // wait for child to change status
|
||||||
{"write", LuaUnixWrite}, // write to file or socket
|
{"write", LuaUnixWrite}, // write to file or socket
|
||||||
|
|
|
@ -27,12 +27,14 @@
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/math.h"
|
#include "libc/math.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/sysconf.h"
|
#include "libc/runtime/sysconf.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/strlist.internal.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/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
@ -49,17 +51,18 @@ STATIC_YOINK("strerror_wr");
|
||||||
#define USAGE \
|
#define USAGE \
|
||||||
"\
|
"\
|
||||||
usage: pledge.com [-hnN] PROG ARGS...\n\
|
usage: pledge.com [-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\
|
||||||
-c PATH call chroot()\n\
|
-c PATH call chroot()\n\
|
||||||
-n maximum niceness\n\
|
-v [PERM:]PATH call unveil(PATH,PERM) where PERM can have rwxc\n\
|
||||||
-N don't normalize file descriptors\n\
|
-n set maximum niceness\n\
|
||||||
-C SECS set cpu limit [default: inherited]\n\
|
-N don't normalize file descriptors\n\
|
||||||
-M BYTES set virtual memory limit [default: 4gb]\n\
|
-C SECS set cpu limit [default: inherited]\n\
|
||||||
-P PROCS set process limit [default: GetCpuCount()*2]\n\
|
-M BYTES set virtual memory limit [default: 4gb]\n\
|
||||||
-F BYTES set individual file size limit [default: 4gb]\n\
|
-P PROCS set process limit [default: GetCpuCount()*2]\n\
|
||||||
-p PLEDGE may contain any of following separated by spaces\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\
|
- stdio: allow stdio and benign system calls\n\
|
||||||
- rpath: read-only path ops\n\
|
- rpath: read-only path ops\n\
|
||||||
- wpath: write path ops\n\
|
- wpath: write path ops\n\
|
||||||
|
@ -68,6 +71,7 @@ usage: pledge.com [-hnN] PROG ARGS...\n\
|
||||||
- flock: file locks\n\
|
- flock: file locks\n\
|
||||||
- tty: terminal ioctls\n\
|
- tty: terminal ioctls\n\
|
||||||
- recvfd: allow SCM_RIGHTS\n\
|
- recvfd: allow SCM_RIGHTS\n\
|
||||||
|
- sendfd: allow SCM_RIGHTS\n\
|
||||||
- fattr: allow changing some struct stat bits\n\
|
- fattr: allow changing some struct stat bits\n\
|
||||||
- inet: allow IPv4 and IPv6\n\
|
- inet: allow IPv4 and IPv6\n\
|
||||||
- unix: allow local sockets\n\
|
- unix: allow local sockets\n\
|
||||||
|
@ -75,9 +79,9 @@ usage: pledge.com [-hnN] PROG ARGS...\n\
|
||||||
- proc: allow fork, clone and friends\n\
|
- proc: allow fork, clone and friends\n\
|
||||||
- thread: allow clone\n\
|
- thread: allow clone\n\
|
||||||
- id: allow setuid and friends\n\
|
- id: allow setuid and friends\n\
|
||||||
- exec: allow executing ape binaries\n\
|
- exec: make execution more permissive\n\
|
||||||
\n\
|
\n\
|
||||||
pledge.com v1.o\n\
|
pledge.com v1.1\n\
|
||||||
copyright 2022 justine alexandra roberts tunney\n\
|
copyright 2022 justine alexandra roberts tunney\n\
|
||||||
https://twitter.com/justinetunney\n\
|
https://twitter.com/justinetunney\n\
|
||||||
https://linkedin.com/in/jtunney\n\
|
https://linkedin.com/in/jtunney\n\
|
||||||
|
@ -102,6 +106,11 @@ long g_proquota;
|
||||||
const char *g_chroot;
|
const char *g_chroot;
|
||||||
const char *g_promises;
|
const char *g_promises;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int n;
|
||||||
|
char **p;
|
||||||
|
} unveils;
|
||||||
|
|
||||||
static void GetOpts(int argc, char *argv[]) {
|
static void GetOpts(int argc, char *argv[]) {
|
||||||
int opt;
|
int opt;
|
||||||
struct sysinfo si;
|
struct sysinfo si;
|
||||||
|
@ -111,7 +120,7 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
g_fszquota = 4 * 1000 * 1000 * 1000;
|
g_fszquota = 4 * 1000 * 1000 * 1000;
|
||||||
g_memquota = 4L * 1024 * 1024 * 1024;
|
g_memquota = 4L * 1024 * 1024 * 1024;
|
||||||
if (!sysinfo(&si)) g_memquota = si.totalram;
|
if (!sysinfo(&si)) g_memquota = si.totalram;
|
||||||
while ((opt = getopt(argc, argv, "hnNp:u:g:c:C:P:M:F:")) != -1) {
|
while ((opt = getopt(argc, argv, "hnNp:u:g:c:C:P:M:F:v:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'n':
|
case 'n':
|
||||||
g_nice = true;
|
g_nice = true;
|
||||||
|
@ -147,6 +156,10 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
g_promises = optarg;
|
g_promises = optarg;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
unveils.p = realloc(unveils.p, ++unveils.n * sizeof(*unveils.p));
|
||||||
|
unveils.p[unveils.n - 1] = optarg;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
write(1, USAGE, sizeof(USAGE) - 1);
|
write(1, USAGE, sizeof(USAGE) - 1);
|
||||||
|
@ -157,9 +170,8 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!g_promises) {
|
if (!g_promises) {
|
||||||
g_promises = "stdio rpath execnative";
|
g_promises = "stdio rpath";
|
||||||
}
|
}
|
||||||
g_promises = xstrcat(g_promises, ' ', "execnative");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *prog;
|
const char *prog;
|
||||||
|
@ -290,6 +302,7 @@ void MakeProcessNice(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
int i;
|
||||||
bool hasfunbits;
|
bool hasfunbits;
|
||||||
int useruid, usergid;
|
int useruid, usergid;
|
||||||
int owneruid, ownergid;
|
int owneruid, ownergid;
|
||||||
|
@ -430,14 +443,50 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unveils.n) {
|
||||||
|
if (unveil(prog, "rx") == -1) {
|
||||||
|
kprintf("error: unveil(0, 0) failed: %m\n", prog, "rx");
|
||||||
|
_Exit(20);
|
||||||
|
}
|
||||||
|
if (strstr(g_promises, "exec") && isexecutable("/usr/bin/ape")) {
|
||||||
|
if (unveil("/usr/bin/ape", "rx") == -1) {
|
||||||
|
kprintf("error: unveil(0, 0) failed: %m\n", "/usr/bin/ape", "rx");
|
||||||
|
_Exit(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < unveils.n; ++i) {
|
||||||
|
char *s, *t;
|
||||||
|
const char *path;
|
||||||
|
const char *perm;
|
||||||
|
s = unveils.p[i];
|
||||||
|
if ((t = strchr(s, ':'))) {
|
||||||
|
*t = 0;
|
||||||
|
perm = s;
|
||||||
|
path = t + 1;
|
||||||
|
} else {
|
||||||
|
perm = "r";
|
||||||
|
path = s;
|
||||||
|
}
|
||||||
|
if (unveil(path, perm) == -1) {
|
||||||
|
kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm);
|
||||||
|
_Exit(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unveil(0, 0) == -1) {
|
||||||
|
kprintf("error: unveil(0, 0) failed: %m\n");
|
||||||
|
_Exit(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// apply sandbox
|
// apply sandbox
|
||||||
if (pledge(g_promises, 0) == -1) {
|
g_promises = xstrcat(g_promises, ' ', "execnative");
|
||||||
|
if (pledge(g_promises, g_promises) == -1) {
|
||||||
kprintf("error: pledge(%#s) failed: %m\n", g_promises);
|
kprintf("error: pledge(%#s) failed: %m\n", g_promises);
|
||||||
_Exit(19);
|
_Exit(19);
|
||||||
}
|
}
|
||||||
|
|
||||||
// launch program
|
// launch program
|
||||||
__sys_execve(prog, argv + optind, environ);
|
execve(prog, argv + optind, environ);
|
||||||
kprintf("%s: execve failed: %m\n", prog);
|
kprintf("%s: execve failed: %m\n", prog);
|
||||||
return 127;
|
return 127;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3619,7 +3619,7 @@ UNIX MODULE
|
||||||
|
|
||||||
See the unix.Rusage section below for details on returned fields.
|
See the unix.Rusage section below for details on returned fields.
|
||||||
|
|
||||||
unix.pledge([promises:str])
|
unix.pledge([promises:str[, execpromises:str]])
|
||||||
├─→ true
|
├─→ true
|
||||||
└─→ nil, unix.Errno
|
└─→ nil, unix.Errno
|
||||||
|
|
||||||
|
@ -3654,11 +3654,15 @@ UNIX MODULE
|
||||||
restrictions need to be loosened.
|
restrictions need to be loosened.
|
||||||
|
|
||||||
`promises` is a string that may include any of the following groups
|
`promises` is a string that may include any of the following groups
|
||||||
delimited by spaces.
|
delimited by spaces. This list has been curated to focus on the
|
||||||
|
system calls for which this module provides wrappers. See the
|
||||||
|
Cosmopolitan Libc pledge() documentation for a comprehensive and
|
||||||
|
authoritative list of raw system calls. Having the raw system call
|
||||||
|
list may be useful if you're executing foreign programs.
|
||||||
|
|
||||||
stdio
|
stdio
|
||||||
|
|
||||||
Allows read, write, send, recv, recvfrom, recvmsg, close,
|
Allows read, write, send, recv, recvfrom, close,
|
||||||
clock_getres, clock_gettime, dup, dup2, dup3, fchdir, fstat,
|
clock_getres, clock_gettime, dup, dup2, dup3, fchdir, fstat,
|
||||||
fsync, fdatasync, ftruncate, getdents, getegid, getrandom,
|
fsync, fdatasync, ftruncate, getdents, getegid, getrandom,
|
||||||
geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid,
|
geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid,
|
||||||
|
@ -3721,12 +3725,49 @@ UNIX MODULE
|
||||||
|
|
||||||
exec
|
exec
|
||||||
|
|
||||||
Allows execve.
|
Allows execve, access.
|
||||||
|
|
||||||
If this is used then APE binaries should be assimilated in order
|
On Linux this also weakens some security to permit running APE
|
||||||
to work on OpenBSD. On Linux, mmap() will be loosened up to allow
|
binaries. However on OpenBSD they must be assimilate beforehand.
|
||||||
creating PROT_EXEC memory (for APE loader) and system call origin
|
On Linux, mmap() will be loosened up to allow creating PROT_EXEC
|
||||||
verification won't be activated.
|
memory (for APE loader) and system call origin verification won't
|
||||||
|
be activated.
|
||||||
|
|
||||||
|
execnative
|
||||||
|
|
||||||
|
Allows execve, execveat.
|
||||||
|
|
||||||
|
Can only be used to run native executables; you won't be able to
|
||||||
|
run APE binaries. mmap() and mprotect() are still prevented from
|
||||||
|
creating executable memory. System call origin verification can't
|
||||||
|
be enabled. If you always assimilate your APE binaries, then this
|
||||||
|
should be preferred. On OpenBSD this will be rewritten to be
|
||||||
|
"exec".
|
||||||
|
|
||||||
|
`execpromises` only matters if "exec" or "execnative" are specified
|
||||||
|
in `promises`. In that case, this specifies the promises that'll
|
||||||
|
apply once execve() happens. If this is NULL then the default is
|
||||||
|
used, which is unrestricted. OpenBSD allows child processes to escape
|
||||||
|
the sandbox (so a pledged OpenSSH server process can do things like
|
||||||
|
spawn a root shell). Linux however requires monotonically decreasing
|
||||||
|
privileges. This function will will perform some validation on Linux
|
||||||
|
to make sure that `execpromises` is a subset of `promises`. Your libc
|
||||||
|
wrapper for execve() will then apply its SECCOMP BPF filter later.
|
||||||
|
Since Linux has to do this before calling sys_execve(), the executed
|
||||||
|
process will be weakened to have execute permissions too.
|
||||||
|
|
||||||
|
unix.unveil(path:str, permissions:str)
|
||||||
|
├─→ true
|
||||||
|
└─→ nil, unix.Errno
|
||||||
|
|
||||||
|
Unveil parts of a restricted filesystem view, e.g.
|
||||||
|
|
||||||
|
unix.unveil(".", "r")
|
||||||
|
unix.unveil(nil, nil)
|
||||||
|
|
||||||
|
This can be used for sandboxing file system access.
|
||||||
|
|
||||||
|
Unveil support is a work in progress.
|
||||||
|
|
||||||
unix.gmtime(unixts:int)
|
unix.gmtime(unixts:int)
|
||||||
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
|
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue