]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
seccomp: add support for seccomp through libseccomp
authorVincent Bernat <bernat@luffy.cx>
Sat, 12 Oct 2013 14:00:40 +0000 (16:00 +0200)
committerVincent Bernat <bernat@luffy.cx>
Sat, 12 Oct 2013 14:53:23 +0000 (16:53 +0200)
The support is only for the monitor process (running as root). It is
enabled when the monitor has been initiliazed, before the event loop.

The monitor has to open a lot of files and read them (files in /proc,
/sys). Moreover, for some files, it has to write to them (for example,
stdout, /dev/log and for writing interface aliases). Therefore, there
are many registered syscalls. It should be possible to filter more but
this would require some efforts.

Becuase it is difficult to reliably report errors to the user and we
may have to execute arbitrary code because we need to resolve
hostnames (and therefore, connect to nscd, LDAP or anything else
handled by NSS), this does not seem to be reliable enough, yet.

Moreover, displaying failed syscall is a bit hackhish.

Maybe we could enable it by default if we change the default behaviour
from killing the offending process to just return a failed errno. Or
just logging the problem.

NEWS
configure.ac
m4/seccomp.m4 [new file with mode: 0644]
src/daemon/Makefile.am
src/daemon/lldpd.h
src/daemon/priv-seccomp.c [new file with mode: 0644]
src/daemon/priv.c

diff --git a/NEWS b/NEWS
index 7e599264c09069600d86cc4f131c1ffbca4755d7..92e214b80775ce29ed2af98f6d7e25ca1fd52271 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,7 @@ lldpd (0.7.7)
       configurable through `lldpcli`.
     + Add support for "team" driver (alternative to bond devices).
     + Preliminary support for DTrace/systemtap.
+    + Preliminary support for seccomp (for monitor process).
   * Fixes:
     + Various bugs related to fixed point number handling (for
       coordinates in LLDP-MED)
index 1982ed68590589a85e408f7541429e29c2adc7c2..1e409e144008d087d1912db3ec3a6e63b6f68d26 100644 (file)
@@ -161,7 +161,7 @@ fi
 
 # XML
 AC_ARG_WITH([xml],
-  AC_HELP_STRING(
+  AS_HELP_STRING(
     [--with-xml],
     [Enable XML output via libxml2 @<:@default=no@:>@]
   ))
@@ -171,7 +171,7 @@ fi
 
 # JSON
 AC_ARG_WITH([json],
-  AC_HELP_STRING(
+  AS_HELP_STRING(
     [--with-json],
     [Enable JSON output via Jansson @<:@default=no@:>@]
   ))
@@ -179,6 +179,15 @@ if test x"$with_json" = x"yes"; then
    lldp_CHECK_JANSSON
 fi
 
+# Seccomp
+AC_ARG_WITH([seccomp],
+  AS_HELP_STRING(
+    [--with-seccomp],
+    [Enable seccomp support (with libseccomp) @<:@default=no@:>@]),
+  [],
+  [with_seccomp=no])
+lldp_CHECK_SECCOMP
+
 # OS X launchd support
 lldp_ARG_WITH([launchddaemonsdir], [Directory for launchd configuration file (OSX)],
                                    [/Library/LaunchDaemons])
@@ -221,6 +230,7 @@ AM_CONDITIONAL([HAVE_CHECK], [test x"$have_check" = x"yes"])
 AM_CONDITIONAL([USE_SNMP], [test x"$with_snmp" = x"yes"])
 AM_CONDITIONAL([USE_XML], [test x"$with_xml" = x"yes"])
 AM_CONDITIONAL([USE_JSON], [test x"$with_json" = x"yes"])
+AM_CONDITIONAL([USE_SECCOMP], [test x"$with_seccomp" = x"yes"])
 AC_OUTPUT
 
 if test x"$LIBEVENT_EMBEDDED" = x; then
@@ -251,6 +261,7 @@ cat <<EOF
   XML output.....: ${with_xml-no}
   JSON output....: ${with_json-no}
   Oldies support.: $enable_oldies
+  seccomp........: ${with_seccomp-no}
 ---------------------------------------------
 
 Check the above options and compile with:
diff --git a/m4/seccomp.m4 b/m4/seccomp.m4
new file mode 100644 (file)
index 0000000..b53249d
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# lldp_CHECK_SECCOMP
+#
+
+AC_DEFUN([lldp_CHECK_SECCOMP], [
+   if test x"$with_seccomp" != x"no"; then
+      PKG_CHECK_MODULES([SECCOMP], [libseccomp >= 1], [
+         AC_SUBST([SECCOMP_LIBS])
+         AC_SUBST([SECCOMP_CFLAGS])
+         AC_DEFINE_UNQUOTED([USE_SECCOMP], 1, [Define to indicate to enable seccomp support])
+         with_seccomp=yes
+      ], [
+         if test x"$with_seccomp" = x"yes"; then
+            AC_MSG_FAILURE([*** no seccomp support found])
+         fi
+         with_seccomp=no
+      ])
+   fi
+])
index a0c5bc85f3107ee93b1e7fd753cbc12047bf9634..7e40524565b714052dcac015b8678488f804f52a 100644 (file)
@@ -91,6 +91,19 @@ liblldpd_la_CFLAGS  += @NETSNMP_CFLAGS@
 liblldpd_la_LIBADD  += @NETSNMP_LIBS@
 endif
 
+# seccomp support
+if USE_SECCOMP
+BUILT_SOURCES += syscall-names.h
+syscall-names.h:
+       echo "static const char *syscall_names[] = {" > $@ ;\
+       echo "#include <sys/syscall.h>" | cpp -dM | grep '^#define __NR_' | \
+               LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([0-9]+)(.*)/ [\2] = "\1",/p' >> $@ ;\
+       echo "};" >> $@
+liblldpd_la_SOURCES += priv-seccomp.c syscall-names.h
+liblldpd_la_CFLAGS  += @SECCOMP_CFLAGS@
+liblldpd_la_LIBADD  += @SECCOMP_LIBS@
+endif
+
 ## lldpd
 lldpd_SOURCES = main.c
 lldpd_LDADD   = liblldpd.la @LIBEVENT_LDFLAGS@
index a568b38019602577f57ae6985c2a98d8fb9ca08e..e37528b4e0fa1b4d6ca874a70fcb4de748de68d7 100644 (file)
@@ -239,6 +239,10 @@ enum priv_cmd {
        PRIV_SNMP_SOCKET,
 };
 
+/* priv-seccomp.c */
+#ifdef USE_SECCOMP
+int priv_seccomp_init(int, int);
+#endif
 
 /* privsep_io.c */
 int     may_read(void *, size_t);
diff --git a/src/daemon/priv-seccomp.c b/src/daemon/priv-seccomp.c
new file mode 100644 (file)
index 0000000..7962752
--- /dev/null
@@ -0,0 +1,183 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * 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 "lldpd.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "syscall-names.h"
+#include <seccomp.h>
+
+#ifndef SYS_SECCOMP
+# define SYS_SECCOMP 1
+#endif
+
+#if defined(__i386__)
+# define REG_SYSCALL   REG_EAX
+# define ARCH_NR       AUDIT_ARCH_I386
+#elif defined(__x86_64__)
+# define REG_SYSCALL   REG_RAX
+# define ARCH_NR       AUDIT_ARCH_X86_64
+#else
+# error "Platform does not support seccomp filter yet"
+# define REG_SYSCALL   0
+# define ARCH_NR       0
+#endif
+
+static int monitored = -1;
+static int trapped = 0;
+/**
+ * SIGSYS signal handler
+ * @param nr the signal number
+ * @param info siginfo_t pointer
+ * @param void_context handler context
+ *
+ * Simple signal handler for SIGSYS displaying the error, killing the child and
+ * exiting.
+ *
+ */
+static void
+priv_seccomp_trap_handler(int signal, siginfo_t *info, void *vctx)
+{
+       ucontext_t *ctx = (ucontext_t *)(vctx);
+       unsigned int syscall;
+
+       if (trapped)
+               _exit(161);     /* Avoid loops */
+
+       /* Get details */
+       if (info->si_code != SYS_SECCOMP)
+               return;
+       if (!ctx)
+               _exit(161);
+       syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
+       trapped = 1;
+
+       /* Log them. Technically, `log_warnx()` is not signal safe, but we are
+        * unlikely to reenter here. */
+       log_warnx("seccomp", "invalid syscall attempted: %s(%d)",
+           (syscall < sizeof(syscall_names))?syscall_names[syscall]:"unknown",
+           syscall);
+
+       /* Kill children and exit */
+       kill(monitored, SIGTERM);
+       fatalx("invalid syscall not allowed: stop here");
+       _exit(161);
+}
+
+/**
+ * Install a TRAP action signal handler
+ *
+ * This function installs the TRAP action signal handler and is based on
+ * examples from Will Drewry and Kees Cook.  Returns zero on success, negative
+ * values on failure.
+ *
+ */
+static int
+priv_seccomp_trap_install()
+{
+       struct sigaction signal_handler = {};
+       sigset_t signal_mask;
+
+       sigemptyset(&signal_mask);
+       sigaddset(&signal_mask, SIGSYS);
+
+       signal_handler.sa_sigaction = &priv_seccomp_trap_handler;
+       signal_handler.sa_flags = SA_SIGINFO;
+       if (sigaction(SIGSYS, &signal_handler, NULL) < 0)
+               return -errno;
+       if (sigprocmask(SIG_UNBLOCK, &signal_mask, NULL))
+               return -errno;
+
+       return 0;
+}
+
+/**
+ * Initialize seccomp.
+ *
+ * @param remote file descriptor to talk with the unprivileged process
+ * @param monitored monitored child
+ * @return negative on failures or 0 if everything was setup
+ */
+int
+priv_seccomp_init(int remote, int child)
+{
+       int rc = -1;
+       scmp_filter_ctx ctx;
+
+       log_debug("seccomp", "initialize libseccomp filter");
+       monitored = child;
+       if (priv_seccomp_trap_install() < 0) {
+               log_warn("seccomp", "unable to install SIGSYS handler");
+               goto failure_scmp;
+       }
+
+       if ((ctx = seccomp_init(SCMP_ACT_TRAP)) == NULL) {
+               log_warnx("seccomp", "unable to initialize libseccomp subsystem");
+               goto failure_scmp;
+       }
+
+       if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW,
+                   SCMP_SYS(read), 1, SCMP_CMP(0, SCMP_CMP_EQ, remote))) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW,
+               SCMP_SYS(write), 1, SCMP_CMP(0, SCMP_CMP_EQ, remote))) < 0) {
+               errno = -rc;
+               log_warn("seccomp", "unable to allow read/write on remote socket");
+               goto failure_scmp;
+       }
+
+       /* We are far more generic from here. */
+       if ((rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0)) < 0 || /* write needed for */
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(bind), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(uname), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(unlink), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(wait4), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0)) < 0 ||
+           /* The following are for resolving addresses */
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(connect), 0)) < 0 ||
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0)) < 0 ||
+
+           (rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0)) < 0) {
+               errno = -rc;
+               log_warn("seccomp", "unable to build seccomp rules");
+               goto failure_scmp;
+       }
+
+       if ((rc = seccomp_load(ctx)) < 0) {
+               errno = -rc;
+               log_warn("seccomp", "unable to load libseccomp filter");
+               goto failure_scmp;
+       }
+
+failure_scmp:
+       seccomp_release(ctx);
+       return rc;
+}
index c8e262d9b15236d8b0f99d246319689c2ef72294..a4ff6cd80a5ad143a4dda4e98b82a6cfc48f3bf9 100644 (file)
@@ -342,12 +342,16 @@ static struct dispatch_actions actions[] = {
 
 /* Main loop, run as root */
 static void
-priv_loop()
+priv_loop(int remote)
 {
        enum priv_cmd cmd;
        struct dispatch_actions *a;
 
        setproctitle("monitor");
+#ifdef USE_SECCOMP
+       if (priv_seccomp_init(remote, monitored) != 0)
+          fatal("privsep", "cannot continue without seccomp setup");
+#endif
        while (!may_read(&cmd, sizeof(enum priv_cmd))) {
                for (a = actions; a->function != NULL; a++) {
                        if (cmd == a->msg) {
@@ -514,7 +518,7 @@ priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid)
                 if (waitpid(monitored, &status, WNOHANG) != 0)
                         /* Child is already dead */
                         _exit(1);
-               priv_loop();
+               priv_loop(pair[1]);
                exit(0);
        }
 }