Prevent Make from talking to public Internet

This change introduces the nointernet() function which may be called to
prevent a process and its descendants from communicating with publicly
routable Internet addresses. GNU Make has been modified to always call
this function. In the future Landlock Make will have a way to whitelist
subnets to override this behavior, or disable it entirely. Support is
available for Linux only. Our firewall does not require root access.

Calling nointernet() will return control to the caller inside a new
process that has a SECCOMP BPF filter installed, which traps network
related system calls. Your original process then becomes a permanent
ptrace() supervisor that monitors all processes and threads descending
from the returned child. Whenever a networking system call happens the
kernel will stop the process and wakes up the monitor, which then peeks
into the child memory to read the sockaddr_in to determine if it's ok.

The downside to doing this is that there can be only one supervisor at a
time using ptrace() on a process. So this firewall won't be enabled if
you run make under strace or inside gdb. It also makes testing tricky.
This commit is contained in:
Justine Tunney 2022-08-12 05:17:06 -07:00
parent 8a0a2c0c36
commit 7cf66bc161
48 changed files with 4924 additions and 2046 deletions

View file

@ -35,10 +35,11 @@
#define WIFCONTINUED(s) ((s) == 0xffff)
#define WIFEXITED(s) (!WTERMSIG(s))
#define WIFSIGNALED(s) ((0xffff & (s)) - 1u < 0xffu)
#define WIFSTOPPED(s) ((short)(((0xffff & (s)) * 0x10001) >> 8) > 0x7f00)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WTERMSIG(s) (127 & (s))
#define W_STOPCODE(s) ((s) << 8 | 0177)
#define WIFSTOPPED(s) \
((short)(((0xffff & (unsigned)(s)) * 0x10001) >> 8) > 0x7f00)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WTERMSIG(s) (127 & (s))
#define W_STOPCODE(s) ((s) << 8 | 0177)
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -115,6 +116,7 @@ int getresuid(uint32_t *, uint32_t *, uint32_t *);
int getsid(int) nosideeffect libcesque;
int gettid(void) libcesque;
int getuid(void) libcesque;
int iopl(int);
int ioprio_get(int, int);
int ioprio_set(int, int, int);
int issetugid(void);
@ -178,6 +180,7 @@ int siginterrupt(int, int);
int symlink(const char *, const char *);
int symlinkat(const char *, int, const char *);
int sync_file_range(int, int64_t, int64_t, unsigned);
int sys_ptrace(int, ...);
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
int tgkill(int, int, int);
int tkill(int, int);
@ -194,7 +197,6 @@ int wait(int *);
int waitpid(int, int *, int);
intptr_t syscall(int, ...);
long ptrace(int, ...);
size_t GetFileSize(const char *);
ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t);
ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t);
ssize_t getfiledescriptorsize(int);

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/likely.h"
#include "libc/calls/calls.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/struct/bpf.h"
@ -25,6 +24,7 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/promises.internal.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/bsr.h"
@ -517,6 +517,7 @@ static const uint16_t kPledgeStdio[] = {
__NR_linux_migrate_pages, //
__NR_linux_sync_file_range, //
__NR_linux_set_tid_address, //
__NR_linux_membarrier, //
__NR_linux_nanosleep, //
__NR_linux_pipe, //
__NR_linux_pipe2, //

View file

@ -19,47 +19,15 @@
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/sysv/consts/ptrace.h"
#include "libc/sysv/errfuns.h"
static const char *__ptrace_describe_request(int x) {
if (x == -1) return "-1";
if (x == PTRACE_TRACEME) return "PTRACE_TRACEME";
if (x == PTRACE_PEEKDATA) return "PTRACE_PEEKDATA";
if (x == PTRACE_GETFPREGS) return "PTRACE_GETFPREGS";
if (x == PTRACE_PEEKTEXT) return "PTRACE_PEEKTEXT";
if (x == PTRACE_POKEDATA) return "PTRACE_POKEDATA";
if (x == PTRACE_PEEKUSER) return "PTRACE_PEEKUSER";
if (x == PTRACE_POKETEXT) return "PTRACE_POKETEXT";
if (x == PTRACE_POKEUSER) return "PTRACE_POKEUSER";
if (x == PTRACE_GETREGS) return "PTRACE_GETREGS";
if (x == PTRACE_GETREGSET) return "PTRACE_GETREGSET";
if (x == PTRACE_SETFPREGS) return "PTRACE_SETFPREGS";
if (x == PTRACE_SETREGS) return "PTRACE_SETREGS";
if (x == PTRACE_SETREGSET) return "PTRACE_SETREGSET";
if (x == PTRACE_GETSIGINFO) return "PTRACE_GETSIGINFO";
if (x == PTRACE_SETSIGINFO) return "PTRACE_SETSIGINFO";
if (x == PTRACE_PEEKSIGINFO) return "PTRACE_PEEKSIGINFO";
if (x == PTRACE_GETSIGMASK) return "PTRACE_GETSIGMASK";
if (x == PTRACE_SETSIGMASK) return "PTRACE_SETSIGMASK";
if (x == PTRACE_SETOPTIONS) return "PTRACE_SETOPTIONS";
if (x == PTRACE_GETEVENTMSG) return "PTRACE_GETEVENTMSG";
if (x == PTRACE_CONT) return "PTRACE_CONT";
if (x == PTRACE_SINGLESTEP) return "PTRACE_SINGLESTEP";
if (x == PTRACE_SYSCALL) return "PTRACE_SYSCALL";
if (x == PTRACE_LISTEN) return "PTRACE_LISTEN";
if (x == PTRACE_KILL) return "PTRACE_KILL";
if (x == PTRACE_INTERRUPT) return "PTRACE_INTERRUPT";
if (x == PTRACE_ATTACH) return "PTRACE_ATTACH";
if (x == PTRACE_SEIZE) return "PTRACE_SEIZE";
if (x == PTRACE_SECCOMP_GET_FILTER) return "PTRACE_SECCOMP_GET_FILTER";
if (x == PTRACE_DETACH) return "PTRACE_DETACH";
return "PTRACE_WUT";
}
/**
* Traces process.
*
* This API is terrible. Consider using sys_ptrace().
*
* @param request can be PTRACE_xxx
* @note de facto linux only atm
* @vforksafe
@ -69,22 +37,21 @@ long ptrace(int request, ...) {
int pid;
va_list va;
bool ispeek;
long rc, peek;
void *addr, *data;
long rc, peek, addr, *data;
va_start(va, request);
pid = va_arg(va, int);
addr = va_arg(va, void *);
data = va_arg(va, void *);
addr = va_arg(va, long);
data = va_arg(va, long *);
va_end(va);
if (request == -1) {
rc = einval(); /* see consts.sh */
} else {
ispeek = IsLinux() && request - 1u < 3;
if (ispeek) data = &peek;
rc = sys_ptrace(request, pid, addr, data);
rc = __sys_ptrace(request, pid, addr, data);
if (rc != -1 && ispeek) rc = peek;
}
STRACE("ptrace(%s, %d, %p, %p) → %ld% m", __ptrace_describe_request(request),
pid, addr, data);
STRACE("ptrace(%s, %d, %p, %p) → %p% m", DescribePtrace(request), pid, addr,
data, rc);
return rc;
}

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -17,20 +17,44 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/likely.h"
#define IsPeek(request) (IsLinux() && (request)-1u < 3)
/**
* Returns the byte length of file by path.
* Traces process.
*
* @return number of bytes, or -1ul w/ errno
* @see getfiledescriptorsize()
* @param op can be PTRACE_xxx
* @param pid is child process id
* @param addr points inside child address space
* @param data is address of output word when peeking
* @note de facto linux only
* @vforksafe
*/
size_t GetFileSize(const char *pathname) {
struct stat st;
if (stat(pathname, &st) == -1) return SIZE_MAX;
return st.st_size;
int sys_ptrace(int op, ...) {
va_list va;
int rc, pid;
long addr, *data;
va_start(va, op);
pid = va_arg(va, int);
addr = va_arg(va, long);
data = va_arg(va, long *);
va_end(va);
rc = __sys_ptrace(op, pid, addr, data);
#ifdef SYSDEBUG
if (UNLIKELY(__strace > 0)) {
if (rc != -1 && IsPeek(op) && data) {
STRACE("sys_ptrace(%s, %d, %p, [%p]) → %p% m", DescribePtrace(op), pid,
addr, *data, rc);
} else {
STRACE("sys_ptrace(%s, %d, %p, %p) → %p% m", DescribePtrace(op), pid,
addr, data, rc);
}
}
#endif
return rc;
}

View file

@ -104,10 +104,10 @@ i32 sys_uname(void *) hidden;
i32 sys_unlinkat(i32, const char *, i32) hidden;
i32 sys_unmount(const char *, i32) hidden;
i32 sys_unveil(const char *, const char *) hidden;
i64 __sys_ptrace(i32, i32, i64, long *) hidden;
i64 sys_copy_file_range(i32, long *, i32, long *, u64, u32) hidden;
i64 sys_getrandom(void *, u64, u32) hidden;
i64 sys_pread(i32, void *, u64, i64, i64) hidden;
i64 sys_ptrace(int, i32, void *, void *) hidden;
i64 sys_pwrite(i32, const void *, u64, i64, i64) hidden;
i64 sys_read(i32, void *, u64) hidden;
i64 sys_readlink(const char *, char *, u64) hidden;

View file

@ -13,11 +13,6 @@
#define DEV_BSIZE 512
#define NOGROUP (-1)
#undef MIN
#undef MAX
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
@ -32,9 +33,12 @@
* @error ENOENT
*/
int truncate(const char *path, uint64_t length) {
int rc;
if (!IsWindows()) {
return sys_truncate(path, length, length);
rc = sys_truncate(path, length, length);
} else {
return sys_truncate_nt(path, length);
rc = sys_truncate_nt(path, length);
}
STRACE("truncate(%#s, %'ld) → %d% m", path, length, rc);
return rc;
}