summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Benjamin Barenblat <bbaren@google.com>2019-12-03 09:17:35 -0500
committerGravatar Benjamin Barenblat <bbaren@google.com>2019-12-03 09:51:06 -0500
commit22a59d2d8b48cb2762362bd71e24293a53bca09a (patch)
tree2658f134b28d211346bce6576f087aacd184acf2
parent44041648e79f1a3d0f46903ad84ebcd5261d0c98 (diff)
Add a basic seccomp profile to zhm
Prevent zhm from doing some evil things while it runs (e.g., ptracing) by implementing a basic seccomp-bpf filter. The filter still allows a lot of potentially dangerous operations (e.g., unlink(2)), but this is a good start. The filter is based partly on a close reading of the zhm and libhesiod source code and partly on empirical evidence from running zhm under strace. I’ve run zhm with this filter for several days without incident, but some edge cases (e.g., server failover) are still untested. configure decides whether or not to enable seccomp by looking for libseccomp. By default, it treats seccomp as an enhancement and enables it opportunistically. Builders can force seccomp to be enabled or disabled by passing --with-seccomp or --without-seccomp, respectively, to configure.
-rw-r--r--configure.ac22
-rw-r--r--zhm/Makefile.in3
-rw-r--r--zhm/zhm.c113
3 files changed, 136 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index dd0dc5a..cb0d568 100644
--- a/configure.ac
+++ b/configure.ac
@@ -264,7 +264,27 @@ if test "x$with_ares" != "xno"; then
AC_MSG_ERROR(libcares not found)))
fi
AC_SUBST(ARES_LIBS)
-
+
+AC_ARG_WITH(seccomp,
+ [AS_HELP_STRING([--without-seccomp], [Disable seccomp])
+AS_HELP_STRING([--with-seccomp=PREFIX], [Specify location of libseccomp])],
+ [seccomp="$withval"], [seccomp=maybe])
+AS_IF([test "x$seccomp" != "xno"], [
+ AS_IF([test "x$seccomp" != "xyes" && test "x$seccomp" != "xmaybe"], [
+ CPPFLAGS="$CPPFLAGS -I$seccomp/include"
+ LDFLAGS="$LDFLAGS -I$seccomp/lib"
+ ])
+ AC_CHECK_LIB(seccomp, seccomp_init, [
+ SECCOMP_LIBS="-lseccomp"
+ AC_DEFINE(HAVE_SECCOMP, 1,
+ [Define to compile with libseccomp support.])
+ ], [
+ AS_IF([test "x$seccomp" != "xmaybe"],
+ AC_MSG_ERROR([libseccomp not found]))
+ ])
+])
+AC_SUBST(SECCOMP_LIBS)
+
AC_PROG_GCC_TRADITIONAL
AC_FUNC_VPRINTF
AC_FUNC_GETPGRP
diff --git a/zhm/Makefile.in b/zhm/Makefile.in
index 77bf2c0..3284bd3 100644
--- a/zhm/Makefile.in
+++ b/zhm/Makefile.in
@@ -33,13 +33,14 @@ CFLAGS=@CFLAGS@
ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
LDFLAGS=@LDFLAGS@
HESIOD_LIBS=@HESIOD_LIBS@
+SECCOMP_LIBS=@SECCOMP_LIBS@
OBJS= timer.o queue.o zhm.o zhm_client.o zhm_server.o
all: zhm zhm.8
zhm: ${OBJS} ${LIBZEPHYR}
- ${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBZEPHYR} ${HESIOD_LIBS} -lcom_err
+ ${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBZEPHYR} ${HESIOD_LIBS} -lcom_err ${SECCOMP_LIBS}
zhm.8: ${srcdir}/zhm.8.in Makefile
${editman} ${srcdir}/$@.in > $@.tmp
diff --git a/zhm/zhm.c b/zhm/zhm.c
index ec4696b..8386cec 100644
--- a/zhm/zhm.c
+++ b/zhm/zhm.c
@@ -6,6 +6,7 @@
* $Id$
*
* Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
+ * Copyright 2019 Google LLC.
* For copying and distribution information, see the file
* "mit-copyright.h".
*/
@@ -13,6 +14,10 @@
#include "zhm.h"
#include <zephyr_version.h>
+#ifdef HAVE_SECCOMP
+#include <seccomp.h>
+#endif
+
static const char rcsid_hm_c[] = "$Id$";
#ifdef HAVE_HESIOD
@@ -49,6 +54,9 @@ static void init_hm(void);
#ifndef DEBUG
static void detach(void);
#endif
+#ifdef HAVE_SECCOMP
+static int set_seccomp_enforcing(void);
+#endif
static void send_stats(ZNotice_t *, struct sockaddr_in *);
static char *strsave(const char *);
@@ -164,6 +172,14 @@ main(int argc,
DPR2("zephyr server port: %u\n", ntohs(serv_sin.sin_port));
DPR2("zephyr client port: %u\n", ntohs(cli_port));
+#ifdef HAVE_SECCOMP
+ if (set_seccomp_enforcing()) {
+ printf("Unable to enable seccomp; exiting.\n");
+ exit(ZERR_INTERNAL);
+ }
+ DPR("Seccomp enabled.\n");
+#endif
+
/* Main loop */
for (;;) {
/* Wait for incoming packets or queue timeouts. */
@@ -519,6 +535,103 @@ stats_malloc(size_t size)
return p;
}
+#ifdef HAVE_SECCOMP
+static int
+set_seccomp_enforcing(void)
+{
+ scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
+ if (ctx == NULL) {
+ DPR("seccomp_init failed.");
+ return 1;
+ }
+ int r = 0;
+
+#define ALLOW_SYSCALL(syscall) \
+ do { \
+ if ((r = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), \
+ 0)) < 0) { \
+ Zperr(-r); \
+ goto out; \
+ } \
+ } while (0)
+
+ /* The main ZHM loop fundamentally consists of a select(2), a ZReceivePacket
+ * (a recvfrom(2)), and a ZSendPacket (a sendto(2)). */
+ ALLOW_SYSCALL(select);
+ ALLOW_SYSCALL(recvfrom);
+ ALLOW_SYSCALL(sendto);
+
+ /* If stuff breaks, we need to log with syslog(3). */
+ ALLOW_SYSCALL(openat);
+ ALLOW_SYSCALL(fstat);
+ ALLOW_SYSCALL(read);
+ ALLOW_SYSCALL(lseek);
+ ALLOW_SYSCALL(close);
+ ALLOW_SYSCALL(getpid);
+ ALLOW_SYSCALL(socket);
+ ALLOW_SYSCALL(connect);
+ ALLOW_SYSCALL(sendto);
+
+ /* We might also use com_err, which manipulates the terminal during its
+ * writes. */
+ ALLOW_SYSCALL(write);
+ ALLOW_SYSCALL(ioctl);
+
+ /* Exiting the process is okay. */
+ ALLOW_SYSCALL(exit_group);
+ ALLOW_SYSCALL(exit);
+
+ /* We might exit in response to a signal. */
+ ALLOW_SYSCALL(rt_sigreturn);
+#ifdef __NR_sigreturn
+ ALLOW_SYSCALL(sigreturn);
+#endif
+
+ /* When it's time to exit, we need to remove the PID file. */
+ ALLOW_SYSCALL(unlink);
+
+#ifdef DEBUG
+ /* Logging stuff to stderr (with DPR and DPR2) is okay. write(2) has already
+ * been allowed above, so don't add it again. */
+ /* ALLOW_SYSCALL(write); */
+#endif
+
+#ifdef HAVE_HESIOD
+ /* If a Zephyr server goes offline; we need to query Hesiod for a new
+ * server. Some of these syscalls have already been allowed above, so don't
+ * add them again. */
+ ALLOW_SYSCALL(brk);
+ ALLOW_SYSCALL(getuid);
+ ALLOW_SYSCALL(geteuid);
+ ALLOW_SYSCALL(getgid);
+ ALLOW_SYSCALL(getegid);
+ /* ALLOW_SYSCALL(openat); */
+ /* ALLOW_SYSCALL(fstat); */
+ ALLOW_SYSCALL(mmap);
+ /* ALLOW_SYSCALL(close); */
+ /* ALLOW_SYSCALL(getpid); */
+ ALLOW_SYSCALL(stat);
+ /* ALLOW_SYSCALL(read); */
+ /* ALLOW_SYSCALL(socket); */
+ /* ALLOW_SYSCALL(connect); */
+ ALLOW_SYSCALL(poll);
+ /* ALLOW_SYSCALL(sendto); */
+ /* ALLOW_SYSCALL(recvfrom); */
+#endif
+
+#undef ALLOW_SYSCALL
+
+ if ((r = seccomp_load(ctx)) < 0) {
+ Zperr(-r);
+ goto out;
+ }
+
+out:
+ seccomp_release(ctx);
+ return r;
+}
+#endif
+
static void
send_stats(ZNotice_t *notice,
struct sockaddr_in *sin)