diff --git a/Makefile.am b/Makefile.am index 8c7876aa..8f297b00 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,7 @@ AM_CFLAGS=-Wall -Werror -Wextra AM_CPPFLAGS=-D_GNU_SOURCE bin_PROGRAMS=pick -pick_SOURCES=pick.c compat-reallocarray.c compat-strtonum.c compat.h +pick_SOURCES=pick.c compat-pledge.c compat-reallocarray.c compat-strtonum.c compat.h pick_CPPFLAGS=$(AM_CPPFLAGS) $(NCURSES_CFLAGS) pick_LDADD=$(NCURSES_LIBS) diff --git a/compat-pledge.c b/compat-pledge.c new file mode 100644 index 00000000..c768de59 --- /dev/null +++ b/compat-pledge.c @@ -0,0 +1,145 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +int unused; + +#ifndef HAVE_PLEDGE + +#if HAVE_SECCOMP +#include + +#include +#include +#include + +/* Most parts are taken from: */ + +#define RULE(syscall) \ + rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), 0);\ + if (rc < 0) \ + goto out + +#define RULE_IOCTL(syscall, x) \ + rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), x, \ + SCMP_A1(SCMP_CMP_EQ, syscall)); \ + if (rc < 0) \ + goto out + + +enum { + PLEDGE_STDIO = 1 << 0, /* stdio */ + PLEDGE_RPATH = 1 << 1, /* rpath */ + PLEDGE_WPATH = 1 << 2, /* wpath */ + PLEDGE_CPATH = 1 << 3, /* cpath */ + PLEDGE_TTY = 1 << 4 /* tty */ +}; + +typedef struct { + char *name; + int mask; +} promise; + +promise ptable[] = { + { "stdio", PLEDGE_STDIO }, + { "rpath", PLEDGE_RPATH }, + { "wpath", PLEDGE_WPATH }, + { "cpath", PLEDGE_CPATH }, + { "tty", PLEDGE_TTY } +}; + +int +pledge(const char *promises, const char *execpromises __attribute__((unused))) +{ + int n, token; + int flags = 0; + int rc = -1; + size_t i; + scmp_filter_ctx ctx; + + ctx = seccomp_init(SCMP_ACT_TRAP); + if (!ctx) { + errno = EACCES; + goto out; + } + + while (*promises) { + /* Skip spaces */ + while (*promises && *promises == ' ') + promises++; + + /* Look for a token */ + token = 0; + for (i = 0; i < sizeof(ptable) / sizeof(*ptable); i++) { + n = strlen(ptable[i].name); + if (!strncmp(promises, ptable[i].name, n)) { + flags |= ptable[i].mask; + promises += n; + token = 1; + break; + } + } + + /* What we saw was not any valid token */ + if (!token) { + errno = EINVAL; + goto out; + } + + /* Ensure the token is terminated by a space or end of string */ + if (*promises && *promises != ' ') { + errno = EINVAL; + goto out; + } + } + + /* + * Define rules + */ + + /* Everyone is allowed to exit */ + RULE(exit_group); + + /* Necessary for SECCOMP */ + RULE(prctl); + + if (flags & PLEDGE_STDIO) { + RULE(close); + RULE(fstat); + RULE(fstat64); + RULE(mmap); + RULE(mmap2); + RULE(munmap); + RULE(poll); + RULE(read); + RULE(rt_sigaction); + RULE(sigaction); + RULE(sigreturn); + RULE(stat64); + RULE(time); + RULE(write); + } + + if (flags & PLEDGE_TTY) { + RULE(access); /* Necessary for terminfo database */ + RULE(open); /* Necessary for fopen("/dev/tty") */ + /* Allows subset of ioctl */ + RULE_IOCTL(TCGETS, 1); + RULE_IOCTL(TCSETS, 1); + RULE_IOCTL(TIOCGWINSZ, 1); + } + + /* Build and load the filter */ + rc = seccomp_load(ctx); + if (rc < 0) + goto out; + + rc = 0; + +out: + seccomp_release(ctx); + return rc; +} +#endif /* HAVE_SECCOMP */ + +#endif /* !HAVE_PLEDGE */ diff --git a/compat.h b/compat.h index f8bbc0ce..a31576e5 100644 --- a/compat.h +++ b/compat.h @@ -19,6 +19,15 @@ #endif #endif /* __linux__ || __CYGWIN__ */ +#ifndef HAVE_PLEDGE +#ifdef HAVE_SECCOMP + +#define HAVE_PLEDGE 1 +int pledge(const char *promises, const char *); + +#endif /* HAVE_SECCOMP */ +#endif /* !HAVE_PLEDGE */ + #ifndef HAVE_REALLOCARRAY void *reallocarray(void *, size_t, size_t); diff --git a/config.h.in b/config.h.in index 66b3dbd6..e952d192 100644 --- a/config.h.in +++ b/config.h.in @@ -9,6 +9,9 @@ /* Define to 1 if you have the `reallocarray' function. */ #undef HAVE_REALLOCARRAY +/* Define if seccomp is available */ +#undef HAVE_SECCOMP + /* Define to 1 if you have the `strtonum' function. */ #undef HAVE_STRTONUM diff --git a/configure.ac b/configure.ac index 08f1ed22..c8dd904e 100644 --- a/configure.ac +++ b/configure.ac @@ -5,6 +5,8 @@ AC_CONFIG_HEADERS([config.h]) AC_PROG_CC AM_PROG_CC_C_O AC_CHECK_FUNCS([pledge reallocarray strtonum]) +AC_SEARCH_LIBS([seccomp_init], [seccomp], + [AC_DEFINE([HAVE_SECCOMP], [1], [Define if seccomp is available])]) AC_SEARCH_LIBS([setupterm], [curses], [], [ AC_SEARCH_LIBS([setupterm], [ncursesw], [AC_DEFINE([HAVE_NCURSESW_H], [1], [Define if ncursesw is available])],