commit 8bbdaedf947a1ae18d1a3ce29ddace6cf20f17eb
parent bcf03557040282bfcfac1de37b6398944ba9d4c9
Author: Joris Vink <joris@coders.se>
Date: Fri, 4 Oct 2019 10:59:48 +0200
Allow configuring seccomp on Linux via the python api.
A new hook in the koreapp class is called right before seccomp
is enabled. This hook receives a Kore seccomp object which has
the following methods:
seccomp.allow("syscall")
seccomp.allow_arg("syscall", arg, value)
seccomp.allow_flag("syscall", arg, flag)
seccomp.allow_mask("syscall", arg, mask)
seccomp.deny("syscall")
seccomp.deny_arg("syscall", arg, value, errno=EACCES)
seccomp.deny_flag("syscall", arg, flag, errno=EACCES)
seccomp.deny_mask("syscall", arg, mask, errno=EACCES)
This allows you to finetune the seccomp filters for your application
from inside your koreapp.
Diffstat:
6 files changed, 405 insertions(+), 11 deletions(-)
diff --git a/include/kore/python_api.h b/include/kore/python_api.h
@@ -35,6 +35,11 @@ void kore_python_log_error(const char *);
PyObject *kore_python_callable(PyObject *, const char *);
+#if defined(__linux__)
+void kore_python_seccomp_hook(void);
+void kore_python_seccomp_cleanup(void);
+#endif
+
#if !defined(KORE_SINGLE_BINARY)
extern const char *kore_pymodule;
#endif
diff --git a/include/kore/python_methods.h b/include/kore/python_methods.h
@@ -132,6 +132,50 @@ static PyTypeObject pyconfig_type = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
};
+#if defined(__linux__)
+struct pyseccomp {
+ PyObject_HEAD
+ size_t elm;
+ u_int8_t *filters;
+};
+
+static PyObject *pyseccomp_allow(struct pyseccomp *, PyObject *);
+static PyObject *pyseccomp_allow_arg(struct pyseccomp *, PyObject *);
+static PyObject *pyseccomp_allow_flag(struct pyseccomp *, PyObject *);
+static PyObject *pyseccomp_allow_mask(struct pyseccomp *, PyObject *);
+
+static PyObject *pyseccomp_deny(struct pyseccomp *, PyObject *, PyObject *);
+static PyObject *pyseccomp_deny_arg(struct pyseccomp *, PyObject *, PyObject *);
+static PyObject *pyseccomp_deny_flag(struct pyseccomp *,
+ PyObject *, PyObject *);
+static PyObject *pyseccomp_deny_mask(struct pyseccomp *,
+ PyObject *, PyObject *);
+
+static PyMethodDef pyseccomp_methods[] = {
+ METHOD("allow", pyseccomp_allow, METH_VARARGS),
+ METHOD("allow_arg", pyseccomp_allow_arg, METH_VARARGS),
+ METHOD("allow_flag", pyseccomp_allow_flag, METH_VARARGS),
+ METHOD("allow_mask", pyseccomp_allow_mask, METH_VARARGS),
+ METHOD("deny", pyseccomp_deny, METH_VARARGS | METH_KEYWORDS),
+ METHOD("deny_arg", pyseccomp_deny_arg, METH_VARARGS | METH_KEYWORDS),
+ METHOD("deny_flag", pyseccomp_deny_flag, METH_VARARGS | METH_KEYWORDS),
+ METHOD("deny_mask", pyseccomp_deny_mask, METH_VARARGS | METH_KEYWORDS),
+ METHOD(NULL, NULL, -1)
+};
+
+static void pyseccomp_dealloc(struct pyseccomp *);
+
+static PyTypeObject pyseccomp_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "kore.seccomp",
+ .tp_doc = "kore seccomp configuration",
+ .tp_methods = pyseccomp_methods,
+ .tp_basicsize = sizeof(struct pyseccomp),
+ .tp_dealloc = (destructor)pyseccomp_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+#endif
+
struct pydomain {
PyObject_HEAD
struct kore_domain *config;
diff --git a/include/kore/seccomp.h b/include/kore/seccomp.h
@@ -134,6 +134,9 @@
/* The length of a filter. */
#define KORE_FILTER_LEN(x) (sizeof(x) / sizeof(x[0]))
+/* Used to mark the end of a BPF program. */
+#define KORE_BPF_GUARD { USHRT_MAX, UCHAR_MAX, UCHAR_MAX, UINT_MAX }
+
/*
* Macro for applications to make easily define custom filter.
*
@@ -158,6 +161,12 @@
void kore_seccomp_init(void);
void kore_seccomp_drop(void);
void kore_seccomp_enable(void);
+int kore_seccomp_syscall_resolve(const char *);
int kore_seccomp_filter(const char *, void *, size_t);
+struct sock_filter *kore_seccomp_syscall_filter(const char *, int);
+struct sock_filter *kore_seccomp_syscall_arg(const char *, int, int, int);
+struct sock_filter *kore_seccomp_syscall_flag(const char *, int, int, int);
+struct sock_filter *kore_seccomp_syscall_mask(const char *, int, int, int);
+
#endif
diff --git a/misc/linux-platform.sh b/misc/linux-platform.sh
@@ -19,5 +19,20 @@ esac
cat << __EOF
/* Auto generated by linux-platform.sh - DO NOT EDIT */
+
+#include <sys/syscall.h>
+
#define SECCOMP_AUDIT_ARCH $seccomp_audit_arch
+
+struct {
+ const char *name;
+ int nr;
+} kore_syscall_map [] = {
+__EOF
+
+awk 'BEGIN { print "#include <sys/syscall.h>" } /p_syscall_meta/ { syscall = substr($NF, 19); printf "#if defined(SYS_%s)\n { \"%s\", SYS_%s },\n#endif\n", syscall, syscall, syscall }' /proc/kallsyms | gcc -E -P -
+
+cat << __EOF
+ { NULL, 0 }
+};
__EOF
diff --git a/src/python.c b/src/python.c
@@ -214,14 +214,27 @@ static struct sock_filter filter_python[] = {
KORE_SYSCALL_ALLOW(listen),
KORE_SYSCALL_ALLOW(sendto),
KORE_SYSCALL_ALLOW(recvfrom),
- KORE_SYSCALL_ALLOW(getsockopt),
- KORE_SYSCALL_ALLOW(setsockopt),
KORE_SYSCALL_ALLOW(getsockname),
-
+ KORE_SYSCALL_ALLOW(getpeername),
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET),
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_INET6),
KORE_SYSCALL_ALLOW_ARG(socket, 0, AF_UNIX),
};
+
+#define PYSECCOMP_ACTION_ALLOW 1
+#define PYSECCOMP_ACTION_DENY 2
+
+#define PYSECCOMP_SYSCALL_FILTER 1
+#define PYSECCOMP_SYSCALL_ARG 2
+#define PYSECCOMP_SYSCALL_MASK 3
+#define PYSECCOMP_SYSCALL_FLAG 4
+
+static int pyseccomp_filter_install(struct pyseccomp *,
+ const char *, int, int, int, int);
+static PyObject *pyseccomp_common_action(struct pyseccomp *, PyObject *,
+ PyObject *, int, int);
+
+static struct pyseccomp *py_seccomp = NULL;
#endif
static TAILQ_HEAD(, pyproc) procs;
@@ -471,6 +484,219 @@ kore_python_proc_reap(void)
}
}
+#if defined(__linux__)
+void
+kore_python_seccomp_hook(void)
+{
+ struct kore_runtime *rt;
+ PyObject *func, *result;
+
+ if ((func = kore_module_getsym("koreapp.seccomp", &rt)) == NULL)
+ return;
+
+ if (rt->type != KORE_RUNTIME_PYTHON)
+ return;
+
+ py_seccomp = PyObject_New(struct pyseccomp, &pyseccomp_type);
+ if (py_seccomp == NULL)
+ fatal("failed to create seccomp object");
+
+ py_seccomp->elm = 0;
+ py_seccomp->filters = NULL;
+
+ result = PyObject_CallFunctionObjArgs(func,
+ (PyObject *)py_seccomp, NULL);
+ kore_python_log_error("koreapp.seccomp");
+
+ kore_seccomp_filter("koreapp", py_seccomp->filters, py_seccomp->elm);
+
+ Py_XDECREF(result);
+}
+
+void
+kore_python_seccomp_cleanup(void)
+{
+ Py_XDECREF(py_seccomp);
+ py_seccomp = NULL;
+}
+
+static void
+pyseccomp_dealloc(struct pyseccomp *seccomp)
+{
+ kore_free(seccomp->filters);
+
+ seccomp->elm = 0;
+ seccomp->filters = NULL;
+}
+
+static PyObject *
+pyseccomp_allow(struct pyseccomp *seccomp, PyObject *args)
+{
+ const char *syscall;
+
+ if (!PyArg_ParseTuple(args, "s", &syscall))
+ return (NULL);
+
+ if (!pyseccomp_filter_install(seccomp, syscall,
+ PYSECCOMP_SYSCALL_FILTER, 0, 0, SECCOMP_RET_ALLOW))
+ return (NULL);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+pyseccomp_allow_arg(struct pyseccomp *seccomp, PyObject *args)
+{
+ return (pyseccomp_common_action(seccomp, args, NULL,
+ PYSECCOMP_SYSCALL_ARG, PYSECCOMP_ACTION_ALLOW));
+}
+
+static PyObject *
+pyseccomp_allow_flag(struct pyseccomp *seccomp, PyObject *args)
+{
+ return (pyseccomp_common_action(seccomp, args, NULL,
+ PYSECCOMP_SYSCALL_FLAG, PYSECCOMP_ACTION_ALLOW));
+}
+
+static PyObject *
+pyseccomp_allow_mask(struct pyseccomp *seccomp, PyObject *args)
+{
+ return (pyseccomp_common_action(seccomp, args, NULL,
+ PYSECCOMP_SYSCALL_MASK, PYSECCOMP_ACTION_ALLOW));
+}
+
+static PyObject *
+pyseccomp_deny(struct pyseccomp *seccomp, PyObject *args, PyObject *kwargs)
+{
+ long err;
+ const char *syscall;
+
+ if (!PyArg_ParseTuple(args, "s", &syscall))
+ return (NULL);
+
+ err = EACCES;
+
+ if (kwargs != NULL)
+ python_long_from_dict(kwargs, "errno", &err);
+
+ if (!pyseccomp_filter_install(seccomp, syscall,
+ PYSECCOMP_SYSCALL_FILTER, 0, 0, SECCOMP_RET_ERRNO | (int)err))
+ return (NULL);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+pyseccomp_deny_arg(struct pyseccomp *seccomp, PyObject *args, PyObject *kwargs)
+{
+ return (pyseccomp_common_action(seccomp, args, kwargs,
+ PYSECCOMP_SYSCALL_ARG, PYSECCOMP_ACTION_DENY));
+}
+
+static PyObject *
+pyseccomp_deny_flag(struct pyseccomp *seccomp, PyObject *args, PyObject *kwargs)
+{
+ return (pyseccomp_common_action(seccomp, args, kwargs,
+ PYSECCOMP_SYSCALL_FLAG, PYSECCOMP_ACTION_DENY));
+}
+
+static PyObject *
+pyseccomp_deny_mask(struct pyseccomp *seccomp, PyObject *args, PyObject *kwargs)
+{
+ return (pyseccomp_common_action(seccomp, args, kwargs,
+ PYSECCOMP_SYSCALL_MASK, PYSECCOMP_ACTION_DENY));
+}
+
+static PyObject *
+pyseccomp_common_action(struct pyseccomp *sc, PyObject *args,
+ PyObject *kwargs, int which, int action)
+{
+ long err;
+ const char *syscall;
+ int arg, val;
+
+ if (!PyArg_ParseTuple(args, "sii", &syscall, &arg, &val))
+ return (NULL);
+
+ switch (action) {
+ case PYSECCOMP_ACTION_ALLOW:
+ action = SECCOMP_RET_ALLOW;
+ break;
+ case PYSECCOMP_ACTION_DENY:
+ err = EACCES;
+ if (kwargs != NULL)
+ python_long_from_dict(kwargs, "errno", &err);
+ action = SECCOMP_RET_ERRNO | (int)err;
+ break;
+ default:
+ fatal("%s: bad action %d", __func__, action);
+ }
+
+ if (!pyseccomp_filter_install(sc, syscall, which, arg, val, action))
+ return (NULL);
+
+ Py_RETURN_NONE;
+}
+
+static int
+pyseccomp_filter_install(struct pyseccomp *seccomp, const char *syscall,
+ int which, int arg, int val, int action)
+{
+ struct sock_filter *filter;
+ size_t elm, len, off;
+
+ switch (which) {
+ case PYSECCOMP_SYSCALL_FILTER:
+ filter = kore_seccomp_syscall_filter(syscall, action);
+ break;
+ case PYSECCOMP_SYSCALL_ARG:
+ filter = kore_seccomp_syscall_arg(syscall, action, arg, val);
+ break;
+ case PYSECCOMP_SYSCALL_MASK:
+ filter = kore_seccomp_syscall_mask(syscall, action, arg, val);
+ break;
+ case PYSECCOMP_SYSCALL_FLAG:
+ filter = kore_seccomp_syscall_flag(syscall, action, arg, val);
+ break;
+ default:
+ fatal("%s: invalid syscall instruction %d", __func__, which);
+ }
+
+ if (filter == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "system call '%s' does not exist", syscall);
+ return (KORE_RESULT_ERROR);
+ }
+
+ elm = 0;
+
+ /*
+ * Find the number of elements in the BPF program, by looking for
+ * the KORE_BPF_GUARD element.
+ */
+ for (;;) {
+ if (filter[elm].code == USHRT_MAX &&
+ filter[elm].jt == UCHAR_MAX &&
+ filter[elm].jf == UCHAR_MAX &&
+ filter[elm].k == UINT_MAX)
+ break;
+
+ elm++;
+ }
+
+ len = elm * sizeof(struct sock_filter);
+ off = seccomp->elm * sizeof(struct sock_filter);
+ seccomp->filters = kore_realloc(seccomp->filters, off + len);
+
+ memcpy(seccomp->filters + off, filter, len);
+ seccomp->elm += elm;
+
+ kore_free(filter);
+
+ return (KORE_RESULT_OK);
+}
+#endif
+
static int
python_long_from_dict(PyObject *dict, const char *key, long *result)
{
@@ -1205,6 +1431,10 @@ python_module_init(void)
python_push_type("pydomain", pykore, &pydomain_type);
python_push_type("pyconnection", pykore, &pyconnection_type);
+#if defined(__linux__)
+ python_push_type("pyseccomp", pykore, &pyseccomp_type);
+#endif
+
#if defined(KORE_USE_CURL)
python_push_type("pyhttpclient", pykore, &pyhttp_client_type);
#endif
diff --git a/src/seccomp.c b/src/seccomp.c
@@ -31,6 +31,10 @@
#include "seccomp.h"
#include "platform.h"
+#if defined(KORE_USE_PYTHON)
+#include "python_api.h"
+#endif
+
#if defined(KORE_DEBUG)
#define SECCOMP_KILL_POLICY SECCOMP_RET_TRAP
#else
@@ -128,6 +132,9 @@ static struct sock_filter filter_epilogue[] = {
BPF_STMT(BPF_RET+BPF_K, SECCOMP_KILL_POLICY)
};
+static struct sock_filter *seccomp_filter_update(struct sock_filter *,
+ const char *, size_t);
+
#define filter_prologue_len KORE_FILTER_LEN(filter_prologue)
#define filter_epilogue_len KORE_FILTER_LEN(filter_epilogue)
@@ -188,9 +195,15 @@ kore_seccomp_enable(void)
sa.sa_sigaction = seccomp_trap;
if (sigfillset(&sa.sa_mask) == -1)
- fatal("sigfillset: %s", errno_s);
+ fatalx("sigfillset: %s", errno_s);
if (sigaction(SIGSYS, &sa, NULL) == -1)
- fatal("sigaction: %s", errno_s);
+ fatalx("sigaction: %s", errno_s);
+#endif
+
+#if defined(KORE_USE_PYTHON)
+ ufilter = TAILQ_FIRST(&filters);
+ kore_python_seccomp_hook();
+ ufilter = NULL;
#endif
/* Allow application to add its own filters. */
@@ -219,7 +232,7 @@ kore_seccomp_enable(void)
/* Build the entire bpf program now. */
if ((sf = calloc(prog_len, sizeof(*sf))) == NULL)
- fatal("calloc");
+ fatalx("calloc");
off = 0;
for (i = 0; i < filter_prologue_len; i++)
@@ -228,11 +241,10 @@ kore_seccomp_enable(void)
TAILQ_FOREACH(filter, &filters, list) {
for (i = 0; i < filter->instructions; i++)
sf[off++] = filter->prog[i];
-
- if (!kore_quiet) {
+#if defined(KORE_DEBUG)
kore_log(LOG_INFO,
"seccomp filter '%s' added", filter->name);
- }
+#endif
}
for (i = 0; i < filter_epilogue_len; i++)
@@ -240,16 +252,20 @@ kore_seccomp_enable(void)
/* Lock and load it. */
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
- fatal("prctl: %s", errno_s);
+ fatalx("prctl: %s", errno_s);
prog.filter = sf;
prog.len = prog_len;
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1)
- fatal("prctl: %s", errno_s);
+ fatalx("prctl: %s", errno_s);
if (!kore_quiet)
kore_log(LOG_INFO, "seccomp sandbox activated");
+
+#if defined(KORE_USE_PYTHON)
+ kore_python_seccomp_cleanup();
+#endif
}
int
@@ -277,6 +293,63 @@ kore_seccomp_filter(const char *name, void *prog, size_t len)
return (KORE_RESULT_OK);
}
+int
+kore_seccomp_syscall_resolve(const char *name)
+{
+ int i;
+
+ for (i = 0; kore_syscall_map[i].name != NULL; i++) {
+ if (!strcmp(name, kore_syscall_map[i].name))
+ return (kore_syscall_map[i].nr);
+ }
+
+ return (-1);
+}
+
+struct sock_filter *
+kore_seccomp_syscall_filter(const char *name, int action)
+{
+ struct sock_filter filter[] = {
+ KORE_SYSCALL_FILTER(exit, action),
+ KORE_BPF_GUARD
+ };
+
+ return (seccomp_filter_update(filter, name, KORE_FILTER_LEN(filter)));
+}
+
+struct sock_filter *
+kore_seccomp_syscall_arg(const char *name, int action, int arg, int value)
+{
+ struct sock_filter filter[] = {
+ KORE_SYSCALL_ARG(exit, arg, value, action),
+ KORE_BPF_GUARD
+ };
+
+ return (seccomp_filter_update(filter, name, KORE_FILTER_LEN(filter)));
+}
+
+struct sock_filter *
+kore_seccomp_syscall_mask(const char *name, int action, int arg, int value)
+{
+ struct sock_filter filter[] = {
+ KORE_SYSCALL_MASK(exit, arg, value, action),
+ KORE_BPF_GUARD
+ };
+
+ return (seccomp_filter_update(filter, name, KORE_FILTER_LEN(filter)));
+}
+
+struct sock_filter *
+kore_seccomp_syscall_flag(const char *name, int action, int arg, int value)
+{
+ struct sock_filter filter[] = {
+ KORE_SYSCALL_WITH_FLAG(exit, arg, value, action),
+ KORE_BPF_GUARD
+ };
+
+ return (seccomp_filter_update(filter, name, KORE_FILTER_LEN(filter)));
+}
+
#if defined(KORE_DEBUG)
static void
seccomp_trap(int sig, siginfo_t *info, void *ucontext)
@@ -284,3 +357,21 @@ seccomp_trap(int sig, siginfo_t *info, void *ucontext)
kore_log(LOG_INFO, "sandbox violation - syscall=%d", info->si_syscall);
}
#endif
+
+static struct sock_filter *
+seccomp_filter_update(struct sock_filter *filter, const char *name, size_t elm)
+{
+ int nr;
+ struct sock_filter *result;
+
+ if ((nr = kore_seccomp_syscall_resolve(name)) == -1)
+ return (NULL);
+
+ result = kore_calloc(elm, sizeof(struct sock_filter));
+ memcpy(result, filter, elm * sizeof(struct sock_filter));
+
+ /* Update the syscall number to the one specified. */
+ result[0].k = nr;
+
+ return (result);
+}