diff options
author | Benjamin Barenblat <bbaren@google.com> | 2019-12-03 09:17:35 -0500 |
---|---|---|
committer | Benjamin Barenblat <bbaren@google.com> | 2019-12-03 09:51:06 -0500 |
commit | 22a59d2d8b48cb2762362bd71e24293a53bca09a (patch) | |
tree | 2658f134b28d211346bce6576f087aacd184acf2 /zhm | |
parent | 44041648e79f1a3d0f46903ad84ebcd5261d0c98 (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.
Diffstat (limited to 'zhm')
-rw-r--r-- | zhm/Makefile.in | 3 | ||||
-rw-r--r-- | zhm/zhm.c | 113 |
2 files changed, 115 insertions, 1 deletions
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 @@ -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) |