Skip to content

Commit 22a59d2

Browse files
committed
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.
1 parent 4404164 commit 22a59d2

File tree

3 files changed

+136
-2
lines changed

3 files changed

+136
-2
lines changed

configure.ac

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,27 @@ if test "x$with_ares" != "xno"; then
264264
AC_MSG_ERROR(libcares not found)))
265265
fi
266266
AC_SUBST(ARES_LIBS)
267-
267+
268+
AC_ARG_WITH(seccomp,
269+
[AS_HELP_STRING([--without-seccomp], [Disable seccomp])
270+
AS_HELP_STRING([--with-seccomp=PREFIX], [Specify location of libseccomp])],
271+
[seccomp="$withval"], [seccomp=maybe])
272+
AS_IF([test "x$seccomp" != "xno"], [
273+
AS_IF([test "x$seccomp" != "xyes" && test "x$seccomp" != "xmaybe"], [
274+
CPPFLAGS="$CPPFLAGS -I$seccomp/include"
275+
LDFLAGS="$LDFLAGS -I$seccomp/lib"
276+
])
277+
AC_CHECK_LIB(seccomp, seccomp_init, [
278+
SECCOMP_LIBS="-lseccomp"
279+
AC_DEFINE(HAVE_SECCOMP, 1,
280+
[Define to compile with libseccomp support.])
281+
], [
282+
AS_IF([test "x$seccomp" != "xmaybe"],
283+
AC_MSG_ERROR([libseccomp not found]))
284+
])
285+
])
286+
AC_SUBST(SECCOMP_LIBS)
287+
268288
AC_PROG_GCC_TRADITIONAL
269289
AC_FUNC_VPRINTF
270290
AC_FUNC_GETPGRP

zhm/Makefile.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ CFLAGS=@CFLAGS@
3333
ALL_CFLAGS=${CFLAGS} -I${top_srcdir}/h -I${BUILDTOP}/h ${CPPFLAGS}
3434
LDFLAGS=@LDFLAGS@
3535
HESIOD_LIBS=@HESIOD_LIBS@
36+
SECCOMP_LIBS=@SECCOMP_LIBS@
3637

3738
OBJS= timer.o queue.o zhm.o zhm_client.o zhm_server.o
3839

3940
all: zhm zhm.8
4041

4142
zhm: ${OBJS} ${LIBZEPHYR}
42-
${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBZEPHYR} ${HESIOD_LIBS} -lcom_err
43+
${LIBTOOL} --mode=link ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LIBZEPHYR} ${HESIOD_LIBS} -lcom_err ${SECCOMP_LIBS}
4344

4445
zhm.8: ${srcdir}/zhm.8.in Makefile
4546
${editman} ${srcdir}/$@.in > $@.tmp

zhm/zhm.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
* $Id$
77
*
88
* Copyright (c) 1987,1991 by the Massachusetts Institute of Technology.
9+
* Copyright 2019 Google LLC.
910
* For copying and distribution information, see the file
1011
* "mit-copyright.h".
1112
*/
1213

1314
#include "zhm.h"
1415
#include <zephyr_version.h>
1516

17+
#ifdef HAVE_SECCOMP
18+
#include <seccomp.h>
19+
#endif
20+
1621
static const char rcsid_hm_c[] = "$Id$";
1722

1823
#ifdef HAVE_HESIOD
@@ -49,6 +54,9 @@ static void init_hm(void);
4954
#ifndef DEBUG
5055
static void detach(void);
5156
#endif
57+
#ifdef HAVE_SECCOMP
58+
static int set_seccomp_enforcing(void);
59+
#endif
5260
static void send_stats(ZNotice_t *, struct sockaddr_in *);
5361
static char *strsave(const char *);
5462

@@ -164,6 +172,14 @@ main(int argc,
164172
DPR2("zephyr server port: %u\n", ntohs(serv_sin.sin_port));
165173
DPR2("zephyr client port: %u\n", ntohs(cli_port));
166174

175+
#ifdef HAVE_SECCOMP
176+
if (set_seccomp_enforcing()) {
177+
printf("Unable to enable seccomp; exiting.\n");
178+
exit(ZERR_INTERNAL);
179+
}
180+
DPR("Seccomp enabled.\n");
181+
#endif
182+
167183
/* Main loop */
168184
for (;;) {
169185
/* Wait for incoming packets or queue timeouts. */
@@ -519,6 +535,103 @@ stats_malloc(size_t size)
519535
return p;
520536
}
521537

538+
#ifdef HAVE_SECCOMP
539+
static int
540+
set_seccomp_enforcing(void)
541+
{
542+
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
543+
if (ctx == NULL) {
544+
DPR("seccomp_init failed.");
545+
return 1;
546+
}
547+
int r = 0;
548+
549+
#define ALLOW_SYSCALL(syscall) \
550+
do { \
551+
if ((r = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), \
552+
0)) < 0) { \
553+
Zperr(-r); \
554+
goto out; \
555+
} \
556+
} while (0)
557+
558+
/* The main ZHM loop fundamentally consists of a select(2), a ZReceivePacket
559+
* (a recvfrom(2)), and a ZSendPacket (a sendto(2)). */
560+
ALLOW_SYSCALL(select);
561+
ALLOW_SYSCALL(recvfrom);
562+
ALLOW_SYSCALL(sendto);
563+
564+
/* If stuff breaks, we need to log with syslog(3). */
565+
ALLOW_SYSCALL(openat);
566+
ALLOW_SYSCALL(fstat);
567+
ALLOW_SYSCALL(read);
568+
ALLOW_SYSCALL(lseek);
569+
ALLOW_SYSCALL(close);
570+
ALLOW_SYSCALL(getpid);
571+
ALLOW_SYSCALL(socket);
572+
ALLOW_SYSCALL(connect);
573+
ALLOW_SYSCALL(sendto);
574+
575+
/* We might also use com_err, which manipulates the terminal during its
576+
* writes. */
577+
ALLOW_SYSCALL(write);
578+
ALLOW_SYSCALL(ioctl);
579+
580+
/* Exiting the process is okay. */
581+
ALLOW_SYSCALL(exit_group);
582+
ALLOW_SYSCALL(exit);
583+
584+
/* We might exit in response to a signal. */
585+
ALLOW_SYSCALL(rt_sigreturn);
586+
#ifdef __NR_sigreturn
587+
ALLOW_SYSCALL(sigreturn);
588+
#endif
589+
590+
/* When it's time to exit, we need to remove the PID file. */
591+
ALLOW_SYSCALL(unlink);
592+
593+
#ifdef DEBUG
594+
/* Logging stuff to stderr (with DPR and DPR2) is okay. write(2) has already
595+
* been allowed above, so don't add it again. */
596+
/* ALLOW_SYSCALL(write); */
597+
#endif
598+
599+
#ifdef HAVE_HESIOD
600+
/* If a Zephyr server goes offline; we need to query Hesiod for a new
601+
* server. Some of these syscalls have already been allowed above, so don't
602+
* add them again. */
603+
ALLOW_SYSCALL(brk);
604+
ALLOW_SYSCALL(getuid);
605+
ALLOW_SYSCALL(geteuid);
606+
ALLOW_SYSCALL(getgid);
607+
ALLOW_SYSCALL(getegid);
608+
/* ALLOW_SYSCALL(openat); */
609+
/* ALLOW_SYSCALL(fstat); */
610+
ALLOW_SYSCALL(mmap);
611+
/* ALLOW_SYSCALL(close); */
612+
/* ALLOW_SYSCALL(getpid); */
613+
ALLOW_SYSCALL(stat);
614+
/* ALLOW_SYSCALL(read); */
615+
/* ALLOW_SYSCALL(socket); */
616+
/* ALLOW_SYSCALL(connect); */
617+
ALLOW_SYSCALL(poll);
618+
/* ALLOW_SYSCALL(sendto); */
619+
/* ALLOW_SYSCALL(recvfrom); */
620+
#endif
621+
622+
#undef ALLOW_SYSCALL
623+
624+
if ((r = seccomp_load(ctx)) < 0) {
625+
Zperr(-r);
626+
goto out;
627+
}
628+
629+
out:
630+
seccomp_release(ctx);
631+
return r;
632+
}
633+
#endif
634+
522635
static void
523636
send_stats(ZNotice_t *notice,
524637
struct sockaddr_in *sin)

0 commit comments

Comments
 (0)