/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ │ above copyright notice and this permission notice appear in all copies. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/filter.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/str/str.h" #include "libc/sysv/consts/nrlinux.h" #include "libc/sysv/consts/pr.h" #include "libc/sysv/errfuns.h" #include "tool/net/sandbox.h" static const uint16_t kPledgeLinuxDefault[] = { __NR_linux_exit, // __NR_linux_exit_group, // 0, // }; static const uint16_t kPledgeLinuxStdio[] = { __NR_linux_clock_getres, // __NR_linux_clock_gettime, // __NR_linux_close, // __NR_linux_dup, // __NR_linux_dup2, // __NR_linux_dup3, // __NR_linux_fchdir, // /* __NR_linux_fcntl, // */ __NR_linux_fstat, // __NR_linux_fsync, // __NR_linux_ftruncate, // __NR_linux_getdents, // __NR_linux_getegid, // __NR_linux_getrandom, // __NR_linux_geteuid, // __NR_linux_getgid, // __NR_linux_getgroups, // __NR_linux_getitimer, // __NR_linux_getpgid, // __NR_linux_getpgrp, // __NR_linux_getpid, // __NR_linux_getppid, // __NR_linux_getresgid, // __NR_linux_getresuid, // __NR_linux_getrlimit, // __NR_linux_getsid, // __NR_linux_gettimeofday, // __NR_linux_getuid, // __NR_linux_lseek, // __NR_linux_madvise, // __NR_linux_brk, // __NR_linux_mmap, // __NR_linux_mprotect, // __NR_linux_munmap, // __NR_linux_nanosleep, // __NR_linux_pipe, // __NR_linux_pipe2, // __NR_linux_poll, // __NR_linux_pread, // __NR_linux_preadv, // __NR_linux_pwrite, // __NR_linux_pwritev, // __NR_linux_read, // __NR_linux_readv, // __NR_linux_recvfrom, // __NR_linux_recvmsg, // __NR_linux_select, // __NR_linux_sendmsg, // __NR_linux_sendto, // __NR_linux_setitimer, // __NR_linux_shutdown, // __NR_linux_sigaction, // __NR_linux_sigprocmask, // __NR_linux_sigreturn, // __NR_linux_socketpair, // __NR_linux_umask, // __NR_linux_wait4, // __NR_linux_write, // __NR_linux_writev, // 0, // }; static const uint16_t kPledgeLinuxRpath[] = { __NR_linux_chdir, // __NR_linux_getcwd, // __NR_linux_openat, // __NR_linux_fstatat, // __NR_linux_faccessat, // __NR_linux_readlinkat, // __NR_linux_lstat, // __NR_linux_chmod, // __NR_linux_fchmod, // __NR_linux_fchmodat, // __NR_linux_chown, // __NR_linux_fchown, // __NR_linux_fchownat, // __NR_linux_fstat, // 0, // }; static const uint16_t kPledgeLinuxWpath[] = { __NR_linux_getcwd, // __NR_linux_openat, // __NR_linux_fstatat, // __NR_linux_faccessat, // __NR_linux_readlinkat, // __NR_linux_lstat, // __NR_linux_chmod, // __NR_linux_fchmod, // __NR_linux_fchmodat, // __NR_linux_chown, // __NR_linux_fchown, // __NR_linux_fchownat, // __NR_linux_fstat, // 0, // }; static const uint16_t kPledgeLinuxCpath[] = { __NR_linux_rename, // __NR_linux_renameat, // __NR_linux_link, // __NR_linux_linkat, // __NR_linux_symlink, // __NR_linux_symlinkat, // __NR_linux_unlink, // __NR_linux_unlinkat, // __NR_linux_mkdir, // __NR_linux_mkdirat, // __NR_linux_rmdir, // 0, // }; static const uint16_t kPledgeLinuxDpath[] = { __NR_linux_mknod, // 0, // }; static const uint16_t kPledgeLinuxTmppath[] = { __NR_linux_lstat, // __NR_linux_chmod, // __NR_linux_chown, // __NR_linux_unlink, // __NR_linux_fstat, // 0, // }; static const uint16_t kPledgeLinuxInet[] = { __NR_linux_socket, // __NR_linux_listen, // __NR_linux_bind, // __NR_linux_connect, // __NR_linux_accept4, // __NR_linux_accept, // __NR_linux_getpeername, // __NR_linux_getsockname, // __NR_linux_setsockopt, // __NR_linux_getsockopt, // 0, // }; static const uint16_t kPledgeLinuxFattr[] = { __NR_linux_utimes, // __NR_linux_utimensat, // __NR_linux_chmod, // __NR_linux_fchmod, // __NR_linux_fchmodat, // __NR_linux_chown, // __NR_linux_fchownat, // __NR_linux_lchown, // __NR_linux_fchown, // __NR_linux_utimes, // 0, // }; static const uint16_t kPledgeLinuxUnix[] = { __NR_linux_socket, // __NR_linux_listen, // __NR_linux_bind, // __NR_linux_connect, // __NR_linux_accept4, // __NR_linux_accept, // __NR_linux_getpeername, // __NR_linux_getsockname, // __NR_linux_setsockopt, // __NR_linux_getsockopt, // 0, // }; static const uint16_t kPledgeLinuxDns[] = { __NR_linux_sendto, // __NR_linux_recvfrom, // __NR_linux_socket, // __NR_linux_connect, // 0, // }; static const uint16_t kPledgeLinuxProc[] = { __NR_linux_fork, // __NR_linux_vfork, // __NR_linux_kill, // __NR_linux_getpriority, // __NR_linux_setpriority, // __NR_linux_setrlimit, // __NR_linux_setpgid, // __NR_linux_setsid, // 0, // }; static const uint16_t kPledgeLinuxExec[] = { __NR_linux_execve, // 0, // }; static const uint16_t kPledgeLinuxId[] = { __NR_linux_setuid, // __NR_linux_setreuid, // __NR_linux_setresuid, // __NR_linux_setgid, // __NR_linux_setregid, // __NR_linux_setresgid, // __NR_linux_setgroups, // __NR_linux_setrlimit, // __NR_linux_getpriority, // __NR_linux_setpriority, // 0, // }; static const struct Pledges { const char *name; const uint16_t *syscalls; } kPledgeLinux[] = { {"default", kPledgeLinuxDefault}, // {"stdio", kPledgeLinuxStdio}, // {"rpath", kPledgeLinuxRpath}, // {"wpath", kPledgeLinuxWpath}, // {"cpath", kPledgeLinuxCpath}, // {"dpath", kPledgeLinuxDpath}, // {"tmppath", kPledgeLinuxTmppath}, // {"inet", kPledgeLinuxInet}, // {"fattr", kPledgeLinuxFattr}, // {"unix", kPledgeLinuxUnix}, // {"dns", kPledgeLinuxDns}, // {"proc", kPledgeLinuxProc}, // {"exec", kPledgeLinuxExec}, // {"id", kPledgeLinuxId}, // {0}, // }; static const struct sock_filter kFilterStart[] = { _SECCOMP_MACHINE(AUDIT_ARCH_X86_64), // _SECCOMP_LOAD_SYSCALL_NR(), // }; static const struct sock_filter kFilterEnd[] = { _SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM }; struct Filter { size_t n; struct sock_filter *p; }; static bool AppendFilter(struct Filter *f, struct sock_filter *p, size_t n) { size_t m; struct sock_filter *q; m = f->n + n; if (!(q = realloc(f->p, m * sizeof(*f->p)))) return false; memcpy(q + f->n, p, n * sizeof(*q)); f->p = q; f->n = m; return true; } static bool AppendPledge(struct Filter *f, const uint16_t *p) { int i; for (i = 0; p[i]; ++i) { struct sock_filter fragment[] = {_SECCOMP_ALLOW_SYSCALL(p[i])}; if (!AppendFilter(f, fragment, ARRAYLEN(fragment))) { return false; } } return true; } static const uint16_t *FindPledge(const struct Pledges *p, const char *name) { int i; for (i = 0; p[i].name; ++i) { if (!strcasecmp(name, p[i].name)) { return p[i].syscalls; } } return 0; } static int sys_pledge_linux(const char *promises, const char *execpromises) { bool ok; int rc = -1; struct Filter f = {0}; const uint16_t *pledge; char *s, *tok, *state, *start; if (execpromises) return einval(); if ((start = s = strdup(promises)) && AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && AppendPledge(&f, kPledgeLinuxDefault)) { for (ok = true; (tok = strtok_r(start, " \t\r\n", &state)); start = 0) { if (!(pledge = FindPledge(kPledgeLinux, tok))) { ok = false; rc = einval(); break; } if (!AppendPledge(&f, pledge)) { ok = false; break; } } if (ok && AppendFilter(&f, kFilterEnd, ARRAYLEN(kFilterEnd)) && (rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1) { struct sock_fprog sandbox = {.len = f.n, .filter = f.p}; rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox); } } free(f.p); free(s); return rc; } /** * Restricts system operations. * * Only available on OpenBSD and Linux. * * By default exit and exit_group are always allowed. This is useful * for processes that perform pure computation and interface with the * parent via shared memory. * * `promises` is a string that may include any of the following groups * delimited by spaces. * * - "stdio" allows clock_getres, clock_gettime, close, dup, dup2, dup3, * fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, * geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, * getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, * getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, * pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, * recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, * sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, write, * writev. * * - "rpath" allows chdir, getcwd, openat, fstatat, faccessat, * readlinkat, lstat, chmod, fchmod, fchmodat, chown, fchown, * fchownat, fstat. * * - "wpath" allows getcwd, openat, fstatat, faccessat, readlinkat, * lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. * * - "cpath" allows rename, renameat, link, linkat, symlink, symlinkat, * unlink, unlinkat, mkdir, mkdirat, rmdir. * * - "dpath" allows mknod * * - "tmppath" allows lstat, chmod, chown, unlink, fstat. * * - "inet" allows socket, listen, bind, connect, accept4, accept, * getpeername, getsockname, setsockopt, getsockopt. * * - "fattr" allows utimes, utimensat, chmod, fchmod, fchmodat, chown, * fchownat, lchown, fchown, utimes. * * - "unix" allows socket, listen, bind, connect, accept4, accept, * getpeername, getsockname, setsockopt, getsockopt. * * - "dns" allows sendto, recvfrom, socket, connect. * * - "proc" allows fork, vfork, kill, getpriority, setpriority, * setrlimit, setpgid, setsid. * * - "exec" allows execve. * * - "id" allows setuid, setreuid, setresuid, setgid, setregid, * setresgid, setgroups, setrlimit, getpriority, setpriority. * */ int pledge(const char *promises, const char *execpromises) { int rc; if (IsLinux()) { rc = sys_pledge_linux(promises, execpromises); } else { rc = sys_pledge(promises, execpromises); } STRACE("pledge(%#s, %#s) → %d% m", promises, execpromises, rc); return rc; }