mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 15:38:22 +00:00
Use LD_PRELOAD to inject pledge() in glibc progs
We're now able to drop both `exec` and `prot_exec` privileges automatically when launching glibc dynamic executables. We also have really outstanding standard error logging now, that explains which promises are needed, even in cases where `exec` is used.
This commit is contained in:
parent
0277d7d6e9
commit
6b3d257588
5 changed files with 286 additions and 178 deletions
|
@ -68,7 +68,6 @@
|
||||||
#define TTY 0x8000
|
#define TTY 0x8000
|
||||||
#define UNIX 0x4000
|
#define UNIX 0x4000
|
||||||
#define NOBITS 0x8000
|
#define NOBITS 0x8000
|
||||||
#define NOSIGSYS 0x8000
|
|
||||||
#define RESTRICT 0x1000
|
#define RESTRICT 0x1000
|
||||||
|
|
||||||
#define PLEDGE(pledge) pledge, ARRAYLEN(pledge)
|
#define PLEDGE(pledge) pledge, ARRAYLEN(pledge)
|
||||||
|
@ -190,7 +189,7 @@ static const uint16_t kPledgeStdio[] = {
|
||||||
__NR_linux_eventfd2, //
|
__NR_linux_eventfd2, //
|
||||||
__NR_linux_signalfd, //
|
__NR_linux_signalfd, //
|
||||||
__NR_linux_signalfd4, //
|
__NR_linux_signalfd4, //
|
||||||
__NR_linux_sigaction | NOSIGSYS, //
|
__NR_linux_sigaction, //
|
||||||
__NR_linux_sigaltstack, //
|
__NR_linux_sigaltstack, //
|
||||||
__NR_linux_sigprocmask, //
|
__NR_linux_sigprocmask, //
|
||||||
__NR_linux_sigsuspend, //
|
__NR_linux_sigsuspend, //
|
||||||
|
@ -585,18 +584,21 @@ static privileged void KillThisThread(void) {
|
||||||
: "rcx", "r11", "memory");
|
: "rcx", "r11", "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged bool HasSyscall(struct Pledges *p, uint16_t n) {
|
static privileged int HasSyscall(struct Pledges *p, uint16_t n) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < p->len; ++i) {
|
for (i = 0; i < p->len; ++i) {
|
||||||
if ((p->syscalls[i] & 0x0fff) == n) {
|
if (p->syscalls[i] == n) {
|
||||||
return true;
|
return 1;
|
||||||
|
}
|
||||||
|
if ((p->syscalls[i] & 0xfff) == n) {
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static privileged void OnSigSys(int sig, siginfo_t *si, ucontext_t *ctx) {
|
static privileged void OnSigSys(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||||
int i;
|
int i, ok;
|
||||||
bool found;
|
bool found;
|
||||||
char ord[17], rip[17];
|
char ord[17], rip[17];
|
||||||
enum PledgeMode mode = si->si_errno;
|
enum PledgeMode mode = si->si_errno;
|
||||||
|
@ -604,15 +606,23 @@ static privileged void OnSigSys(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||||
FixCpy(ord, si->si_syscall, 12);
|
FixCpy(ord, si->si_syscall, 12);
|
||||||
HexCpy(rip, ctx->uc_mcontext.rip);
|
HexCpy(rip, ctx->uc_mcontext.rip);
|
||||||
for (found = i = 0; i < ARRAYLEN(kPledge); ++i) {
|
for (found = i = 0; i < ARRAYLEN(kPledge); ++i) {
|
||||||
if (HasSyscall(kPledge + i, si->si_syscall)) {
|
switch (HasSyscall(kPledge + i, si->si_syscall)) {
|
||||||
Log("error: has not pledged ", kPledge[i].name, //
|
case 1:
|
||||||
|
Log("error: should pledge ", kPledge[i].name, //
|
||||||
" (ord=", ord, " rip=", rip, ")\n", 0);
|
" (ord=", ord, " rip=", rip, ")\n", 0);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
case 2:
|
||||||
|
Log("error: maybe pledge ", kPledge[i].name, //
|
||||||
|
" (ord=", ord, " rip=", rip, ")\n", 0);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
Log("error: unsupported syscall (ord=", ord, " rip=", rip, ")\n", 0);
|
Log("error: bad syscall (ord=", ord, " rip=", rip, ")\n", 0);
|
||||||
}
|
}
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case kPledgeModeKillProcess:
|
case kPledgeModeKillProcess:
|
||||||
|
@ -876,6 +886,7 @@ static privileged void AllowSetsockoptRestrict(struct Filter *f) {
|
||||||
// The optname argument of getsockopt() must be one of:
|
// The optname argument of getsockopt() must be one of:
|
||||||
//
|
//
|
||||||
// - SO_TYPE (0x03)
|
// - SO_TYPE (0x03)
|
||||||
|
// - SO_ERROR (0x04)
|
||||||
// - SO_REUSEPORT (0x0f)
|
// - SO_REUSEPORT (0x0f)
|
||||||
// - SO_REUSEADDR (0x02)
|
// - SO_REUSEADDR (0x02)
|
||||||
// - SO_KEEPALIVE (0x09)
|
// - SO_KEEPALIVE (0x09)
|
||||||
|
@ -885,20 +896,21 @@ static privileged void AllowSetsockoptRestrict(struct Filter *f) {
|
||||||
static privileged void AllowGetsockoptRestrict(struct Filter *f) {
|
static privileged void AllowGetsockoptRestrict(struct Filter *f) {
|
||||||
static const int nr = __NR_linux_getsockopt;
|
static const int nr = __NR_linux_getsockopt;
|
||||||
static const struct sock_filter fragment[] = {
|
static const struct sock_filter fragment[] = {
|
||||||
/* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, nr, 0, 13 - 1),
|
/* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, nr, 0, 14 - 1),
|
||||||
/* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])),
|
/* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])),
|
||||||
/* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 1, 0),
|
/* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 1, 0),
|
||||||
/* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6, 0, 12 - 4),
|
/* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6, 0, 13 - 4),
|
||||||
/* L4*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])),
|
/* L4*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])),
|
||||||
/* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x03, 5, 0),
|
/* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x03, 6, 0),
|
||||||
/* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0f, 4, 0),
|
/* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x04, 5, 0),
|
||||||
/* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 3, 0),
|
/* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0f, 4, 0),
|
||||||
/* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x09, 2, 0),
|
/* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 3, 0),
|
||||||
/* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x14, 1, 0),
|
/* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x09, 2, 0),
|
||||||
/*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x15, 0, 1),
|
/*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x14, 1, 0),
|
||||||
/*L11*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
|
/*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x15, 0, 1),
|
||||||
/*L12*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
|
/*L12*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
|
||||||
/*L13*/ /* next filter */
|
/*L13*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
|
||||||
|
/*L14*/ /* next filter */
|
||||||
};
|
};
|
||||||
AppendFilter(f, PLEDGE(fragment));
|
AppendFilter(f, PLEDGE(fragment));
|
||||||
}
|
}
|
||||||
|
@ -1215,24 +1227,6 @@ static privileged void AllowSendtoAddrless(struct Filter *f) {
|
||||||
AppendFilter(f, PLEDGE(fragment));
|
AppendFilter(f, PLEDGE(fragment));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The sig parameter of sigaction() must NOT be
|
|
||||||
//
|
|
||||||
// - SIGSYS (31) [always eperm]
|
|
||||||
//
|
|
||||||
static privileged void AllowSigactionNosigsys(struct Filter *f) {
|
|
||||||
static const int nr = __NR_linux_sigaction;
|
|
||||||
static const struct sock_filter fragment[] = {
|
|
||||||
/*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, nr, 0, 6 - 1),
|
|
||||||
/*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])),
|
|
||||||
/*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 31, 0, 1),
|
|
||||||
/*L3*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | Eperm),
|
|
||||||
/*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
|
|
||||||
/*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
|
|
||||||
/*L6*/ /* next filter */
|
|
||||||
};
|
|
||||||
AppendFilter(f, PLEDGE(fragment));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The family parameter of socket() must be one of:
|
// The family parameter of socket() must be one of:
|
||||||
//
|
//
|
||||||
// - AF_INET (0x02)
|
// - AF_INET (0x02)
|
||||||
|
@ -1479,9 +1473,6 @@ static privileged void AppendPledge(struct Filter *f, //
|
||||||
case __NR_linux_fchmodat | NOBITS:
|
case __NR_linux_fchmodat | NOBITS:
|
||||||
AllowFchmodatNobits(f);
|
AllowFchmodatNobits(f);
|
||||||
break;
|
break;
|
||||||
case __NR_linux_sigaction | NOSIGSYS:
|
|
||||||
AllowSigactionNosigsys(f);
|
|
||||||
break;
|
|
||||||
case __NR_linux_prctl | STDIO:
|
case __NR_linux_prctl | STDIO:
|
||||||
AllowPrctlStdio(f);
|
AllowPrctlStdio(f);
|
||||||
break;
|
break;
|
||||||
|
@ -1582,7 +1573,11 @@ privileged int sys_pledge_linux(unsigned long ipromises, //
|
||||||
AppendPledge(&f, PLEDGE(kPledgeDefault));
|
AppendPledge(&f, PLEDGE(kPledgeDefault));
|
||||||
for (i = 0; i < ARRAYLEN(kPledge); ++i) {
|
for (i = 0; i < ARRAYLEN(kPledge); ++i) {
|
||||||
if (~ipromises & (1ul << i)) {
|
if (~ipromises & (1ul << i)) {
|
||||||
|
if (kPledge[i].len) {
|
||||||
AppendPledge(&f, kPledge[i].syscalls, kPledge[i].len);
|
AppendPledge(&f, kPledge[i].syscalls, kPledge[i].len);
|
||||||
|
} else {
|
||||||
|
AbortPledge("bad ipromises");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ TEST(pledge, testKillProcessMode) {
|
||||||
TEST(pledge, testLogMessage_inSoftyMode) {
|
TEST(pledge, testLogMessage_inSoftyMode) {
|
||||||
if (IsOpenbsd()) return;
|
if (IsOpenbsd()) return;
|
||||||
int fds[2];
|
int fds[2];
|
||||||
char msg[64] = {0};
|
char msg[256] = {0};
|
||||||
ASSERT_SYS(0, 0, pipe(fds));
|
ASSERT_SYS(0, 0, pipe(fds));
|
||||||
SPAWN(fork);
|
SPAWN(fork);
|
||||||
__pledge_mode = kPledgeModeErrno;
|
__pledge_mode = kPledgeModeErrno;
|
||||||
|
@ -98,13 +98,13 @@ TEST(pledge, testLogMessage_inSoftyMode) {
|
||||||
read(fds[0], msg, sizeof(msg));
|
read(fds[0], msg, sizeof(msg));
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
if (IsLinux()) {
|
if (IsLinux()) {
|
||||||
ASSERT_STARTSWITH("error: has not pledged inet", msg);
|
ASSERT_STARTSWITH("error: maybe pledge inet", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pledge, testLogMessage_onKillProcess) {
|
TEST(pledge, testLogMessage_onKillProcess) {
|
||||||
int fds[2];
|
int fds[2];
|
||||||
char msg[64] = {0};
|
char msg[256] = {0};
|
||||||
ASSERT_SYS(0, 0, pipe(fds));
|
ASSERT_SYS(0, 0, pipe(fds));
|
||||||
SPAWN(fork);
|
SPAWN(fork);
|
||||||
__pledge_mode = kPledgeModeKillThread;
|
__pledge_mode = kPledgeModeKillThread;
|
||||||
|
@ -116,13 +116,13 @@ TEST(pledge, testLogMessage_onKillProcess) {
|
||||||
read(fds[0], msg, sizeof(msg));
|
read(fds[0], msg, sizeof(msg));
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
if (IsLinux()) {
|
if (IsLinux()) {
|
||||||
ASSERT_STARTSWITH("error: has not pledged inet", msg);
|
ASSERT_STARTSWITH("error: maybe pledge inet", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(pledge, testNoLogOrAbrtsignoPossibleSadly_becausePledgedExec) {
|
TEST(pledge, testNoLogOrAbrtsignoPossibleSadly_becausePledgedExec) {
|
||||||
int fds[2];
|
int fds[2];
|
||||||
char msg[64] = {0};
|
char msg[256] = {0};
|
||||||
ASSERT_SYS(0, 0, pipe(fds));
|
ASSERT_SYS(0, 0, pipe(fds));
|
||||||
SPAWN(fork);
|
SPAWN(fork);
|
||||||
ASSERT_SYS(0, 2, dup2(fds[1], 2));
|
ASSERT_SYS(0, 2, dup2(fds[1], 2));
|
||||||
|
|
|
@ -119,23 +119,45 @@ o/$(MODE)/tool/build/dd.zip.o: o/$(MODE)/tool/build/dd
|
||||||
|
|
||||||
# we need pic because:
|
# we need pic because:
|
||||||
# so it can be an LD_PRELOAD payload
|
# so it can be an LD_PRELOAD payload
|
||||||
o/$(MODE)/tool/build/sandbox.o: \
|
o/$(MODE)/tool/build/dso/sandbox.o: \
|
||||||
OVERRIDE_CFLAGS += \
|
OVERRIDE_CFLAGS += \
|
||||||
-fPIC
|
-fPIC
|
||||||
|
|
||||||
o/$(MODE)/tool/build/sandbox.so: \
|
o/$(MODE)/tool/build/dso/sandbox.o: \
|
||||||
o/$(MODE)/tool/build/sandbox.o \
|
libc/calls/calls.h \
|
||||||
|
tool/build/dso/sandbox.c \
|
||||||
|
libc/calls/pledge.h \
|
||||||
|
libc/runtime/runtime.h \
|
||||||
|
libc/calls/pledge.internal.h \
|
||||||
|
libc/intrin/promises.internal.h \
|
||||||
|
tool/build/build.mk
|
||||||
|
|
||||||
|
o/$(MODE)/tool/build/dso/sandbox.so: \
|
||||||
|
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
|
||||||
@$(COMPILE) -ALINK.so \
|
@$(CC) -s \
|
||||||
$(CC) \
|
|
||||||
-s \
|
|
||||||
-shared \
|
-shared \
|
||||||
-nostdlib \
|
-nostdlib \
|
||||||
-Wl,--gc-sections \
|
-Wl,--gc-sections \
|
||||||
$(LINKARGS) \
|
o/$(MODE)/tool/build/dso/sandbox.o \
|
||||||
|
o/$(MODE)/libc/calls/pledge-linux.o \
|
||||||
|
o/$(MODE)/libc/sysv/restorert.o \
|
||||||
$(OUTPUT_OPTION)
|
$(OUTPUT_OPTION)
|
||||||
|
|
||||||
|
o/$(MODE)/tool/build/dso/sandbox.so.zip.o: \
|
||||||
|
ZIPOBJ_FLAGS += \
|
||||||
|
-B
|
||||||
|
|
||||||
|
o/$(MODE)/tool/build/pledge.com.dbg: \
|
||||||
|
$(TOOL_BUILD_DEPS) \
|
||||||
|
o/$(MODE)/tool/build/build.pkg \
|
||||||
|
o/$(MODE)/tool/build/dso/sandbox.so.zip.o \
|
||||||
|
o/$(MODE)/tool/build/pledge.o \
|
||||||
|
$(CRT) \
|
||||||
|
$(APE_NO_MODIFY_SELF)
|
||||||
|
@$(APELINK)
|
||||||
|
|
||||||
.PHONY: o/$(MODE)/tool/build
|
.PHONY: o/$(MODE)/tool/build
|
||||||
o/$(MODE)/tool/build: \
|
o/$(MODE)/tool/build: \
|
||||||
o/$(MODE)/tool/build/emucrt \
|
o/$(MODE)/tool/build/emucrt \
|
||||||
|
|
|
@ -16,13 +16,35 @@
|
||||||
│ 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/calls.h"
|
||||||
#include "libc/calls/pledge.h"
|
#include "libc/calls/pledge.h"
|
||||||
#include "libc/calls/pledge.internal.h"
|
#include "libc/calls/pledge.internal.h"
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
|
||||||
hidden char __privileged_start;
|
/*
|
||||||
hidden char __privileged_end;
|
* runs pledge at glibc executable load time, e.g.
|
||||||
|
* strace -vff bash -c '_PLEDGE=4194303,0,1 LD_PRELOAD=$HOME/sandbox.so ls'
|
||||||
|
*/
|
||||||
|
|
||||||
__attribute__((__constructor__)) void InitializeSandbox(void) {
|
hidden uint8_t __privileged_start[1];
|
||||||
sys_pledge_linux(~(1ul << PROMISE_STDIO), kPledgeModeErrno, false);
|
hidden uint8_t __privileged_end[1];
|
||||||
|
|
||||||
|
__attribute__((__constructor__)) void init(void) {
|
||||||
|
int c, i, j;
|
||||||
|
const char *s;
|
||||||
|
uint64_t arg[3] = {0};
|
||||||
|
s = getenv("_PLEDGE");
|
||||||
|
for (i = j = 0; i < 3; ++i) {
|
||||||
|
while ((c = s[j] & 255)) {
|
||||||
|
++j;
|
||||||
|
if ('0' <= c & c <= '9') {
|
||||||
|
arg[i] *= 10;
|
||||||
|
arg[i] += c - '0';
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_pledge_linux(~arg[0], arg[1], arg[2]);
|
||||||
}
|
}
|
|
@ -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/assert.h"
|
||||||
#include "libc/bits/bits.h"
|
#include "libc/bits/bits.h"
|
||||||
#include "libc/bits/safemacros.internal.h"
|
#include "libc/bits/safemacros.internal.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include "libc/intrin/promises.internal.h"
|
#include "libc/intrin/promises.internal.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/math.h"
|
#include "libc/math.h"
|
||||||
|
#include "libc/mem/io.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nexgen32e/kcpuids.h"
|
#include "libc/nexgen32e/kcpuids.h"
|
||||||
#include "libc/runtime/gc.internal.h"
|
#include "libc/runtime/gc.internal.h"
|
||||||
|
@ -65,6 +67,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
STATIC_YOINK("strerror_wr");
|
STATIC_YOINK("strerror_wr");
|
||||||
|
STATIC_YOINK("zip_uri_support");
|
||||||
|
|
||||||
#define USAGE \
|
#define USAGE \
|
||||||
"\
|
"\
|
||||||
|
@ -123,6 +126,7 @@ int g_uflag;
|
||||||
int g_kflag;
|
int g_kflag;
|
||||||
int g_hflag;
|
int g_hflag;
|
||||||
bool g_nice;
|
bool g_nice;
|
||||||
|
bool isdynamic;
|
||||||
bool g_noclose;
|
bool g_noclose;
|
||||||
long g_cpuquota;
|
long g_cpuquota;
|
||||||
long g_fszquota;
|
long g_fszquota;
|
||||||
|
@ -131,6 +135,8 @@ long g_proquota;
|
||||||
long g_dontdrop;
|
long g_dontdrop;
|
||||||
const char *g_chroot;
|
const char *g_chroot;
|
||||||
const char *g_promises;
|
const char *g_promises;
|
||||||
|
char dsopath[PATH_MAX];
|
||||||
|
char tmppath[PATH_MAX];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int n;
|
int n;
|
||||||
|
@ -393,7 +399,8 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||||
|
|
||||||
Unveil(prog, "rx");
|
Unveil(prog, "rx");
|
||||||
|
|
||||||
if (IsDynamicExecutable(prog)) {
|
if (isdynamic) {
|
||||||
|
Unveil(dsopath, "rx");
|
||||||
UnveilIfExists("/lib", "rx");
|
UnveilIfExists("/lib", "rx");
|
||||||
UnveilIfExists("/lib64", "rx");
|
UnveilIfExists("/lib64", "rx");
|
||||||
UnveilIfExists("/usr/lib", "rx");
|
UnveilIfExists("/usr/lib", "rx");
|
||||||
|
@ -518,8 +525,36 @@ void DropCapabilities(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileExistsAndIsNewerThan(const char *filepath, const char *thanpath) {
|
||||||
|
struct stat st1, st2;
|
||||||
|
if (stat(filepath, &st1) == -1) return false;
|
||||||
|
if (stat(thanpath, &st2) == -1) return false;
|
||||||
|
if (st1.st_mtim.tv_sec < st2.st_mtim.tv_sec) return false;
|
||||||
|
if (st1.st_mtim.tv_sec > st2.st_mtim.tv_sec) return true;
|
||||||
|
return st1.st_mtim.tv_nsec >= st2.st_mtim.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Extract(const char *from, const char *to, int mode) {
|
||||||
|
int fdin, fdout;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
const char *s;
|
||||||
bool hasfunbits;
|
bool hasfunbits;
|
||||||
|
int fdin, fdout;
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
int e, zipfd, memfd;
|
||||||
int useruid, usergid;
|
int useruid, usergid;
|
||||||
int owneruid, ownergid;
|
int owneruid, ownergid;
|
||||||
int oldfsuid, oldfsgid;
|
int oldfsuid, oldfsgid;
|
||||||
|
@ -607,6 +642,29 @@ int main(int argc, char *argv[]) {
|
||||||
setfsgid(oldfsgid);
|
setfsgid(oldfsgid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// figure out where we want the dso
|
||||||
|
if (IsDynamicExecutable(prog)) {
|
||||||
|
isdynamic = true;
|
||||||
|
if ((s = getenv("TMPDIR")) || //
|
||||||
|
(s = getenv("HOME")) || //
|
||||||
|
(s = ".")) {
|
||||||
|
ksnprintf(dsopath, sizeof(dsopath), "%s/sandbox.so", s);
|
||||||
|
if (!FileExistsAndIsNewerThan(dsopath, GetProgramExecutableName())) {
|
||||||
|
ksnprintf(tmppath, sizeof(tmppath), "%s/sandbox.so.%d", s, getpid());
|
||||||
|
if (Extract("/zip/sandbox.so", tmppath, 0755) == -1) {
|
||||||
|
kprintf("error: extract dso failed: %m\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (rename(tmppath, dsopath) == -1) {
|
||||||
|
kprintf("error: rename dso failed: %m\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksnprintf(buf, sizeof(buf), "LD_PRELOAD=%s", dsopath);
|
||||||
|
putenv(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (g_dontdrop) {
|
if (g_dontdrop) {
|
||||||
if (hasfunbits) {
|
if (hasfunbits) {
|
||||||
kprintf("error: -D flag forbidden on setuid binaries\n");
|
kprintf("error: -D flag forbidden on setuid binaries\n");
|
||||||
|
@ -669,11 +727,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
ApplyFilesystemPolicy(ipromises);
|
ApplyFilesystemPolicy(ipromises);
|
||||||
|
|
||||||
// we always need exec which is a weakness of this model
|
|
||||||
if (!(~ipromises & (1ul << PROMISE_EXEC))) {
|
|
||||||
g_promises = xstrcat(g_promises, ' ', "exec");
|
|
||||||
}
|
|
||||||
|
|
||||||
// pledge.com uses the return eperm instead of killing the process
|
// pledge.com uses the return eperm instead of killing the process
|
||||||
// model. we do this becasue it's only possible to have sigsys print
|
// model. we do this becasue it's only possible to have sigsys print
|
||||||
// crash messages if we're not pledging exec, which is what this tool
|
// crash messages if we're not pledging exec, which is what this tool
|
||||||
|
@ -684,6 +737,22 @@ int main(int argc, char *argv[]) {
|
||||||
__pledge_mode = kPledgeModeErrno;
|
__pledge_mode = kPledgeModeErrno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need to be able to call execv and mmap the dso
|
||||||
|
// it'll be pledged away once/if the dso gets loaded
|
||||||
|
if (!(~ipromises & (1ul << PROMISE_EXEC))) {
|
||||||
|
g_promises = xstrcat(g_promises, ' ', "exec");
|
||||||
|
}
|
||||||
|
if (isdynamic) {
|
||||||
|
g_promises = xstrcat(g_promises, ' ', "prot_exec");
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass arguments to pledge() inside the dso
|
||||||
|
if (isdynamic) {
|
||||||
|
ksnprintf(buf, sizeof(buf), "_PLEDGE=%ld,%ld,%ld", ~ipromises,
|
||||||
|
__pledge_mode, false);
|
||||||
|
putenv(buf);
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
kprintf("error: pledge(%#s) failed: %m\n", g_promises);
|
||||||
|
|
Loading…
Add table
Reference in a new issue