diff --git a/configure.ac b/configure.ac index dd0dc5aa..cb0d568e 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/debian/control b/debian/control index bf03797a..d1df79d7 100644 --- a/debian/control +++ b/debian/control @@ -6,7 +6,8 @@ Build-Depends: debhelper (>= 5), libc-ares-dev, libkrb5-dev (>= 1.2.2-4), comerr-dev, ss-dev, libreadline-dev | libreadline5-dev, libx11-dev, libxt-dev, x11proto-core-dev, libncurses5-dev, bison, libhesiod-dev, autotools-dev, python (>= 2.5), python-central, - autoconf, libtool, automake, git-core | git, devscripts + autoconf, libtool, automake, git-core | git, devscripts, + libseccomp-dev Build-Conflicts: autoconf2.13 Standards-Version: 3.9.2.0 Homepage: http://zephyr.1ts.org/ diff --git a/debian/rules b/debian/rules index 8f575bbb..daed056a 100755 --- a/debian/rules +++ b/debian/rules @@ -17,7 +17,8 @@ PACKAGES:=-plibzephyr4 -pzephyr-clients -pzephyr-server -plibzephyr-dev -plibzep export DH_OPTIONS CONFIGURE_ROOT=--prefix=/usr --mandir=\$${prefix}/share/man \ --infodir=\$${prefix}/share/info --sysconfdir=/etc --datadir=/etc \ - --with-cares=/usr --with-hesiod=/usr --enable-cmu-zwgcplus + --with-cares=/usr --with-hesiod=/usr --with-seccomp=/usr \ + --enable-cmu-zwgcplus CONFIGURE_krb5=--with-krb5=/usr CONFIGURE_krb45=--with-krb4=/usr --with-krb5=/usr CONFIGURE_krb=--with-krb4=/usr diff --git a/zhm/Makefile.in b/zhm/Makefile.in index 77bf2c0a..3284bd34 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 ec4696b7..8386cec5 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 +#ifdef HAVE_SECCOMP +#include +#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)