From cd3c5880b13c9c25ad480ecafadbfe6e86a7c5c7 Mon Sep 17 00:00:00 2001 From: Gleb Smirnoff Date: Wed, 1 Feb 2017 16:13:05 -0800 Subject: [PATCH] Add support for libcasper library available on FreeBSD 11.0 and newer. The patch allows tcpdump to run sandboxed and still do name resolution. The code is obtained from FreeBSD tree, where it was developed by Pawel Jakub Dawidek Mariusz Zaborski --- addrtoname.c | 25 ++++++++++++-- config.h.in | 4 +-- configure | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 9 +++++ tcpdump.c | 51 ++++++++++++++++++++++++++++- 5 files changed, 176 insertions(+), 5 deletions(-) diff --git a/addrtoname.c b/addrtoname.c index 5831b346..c679c83f 100644 --- a/addrtoname.c +++ b/addrtoname.c @@ -26,6 +26,11 @@ #include "config.h" #endif +#ifdef HAVE_CASPER +#include +#include +#endif /* HAVE_CASPER */ + #include #ifdef USE_ETHER_NTOHOST @@ -197,6 +202,9 @@ intoa(uint32_t addr) static uint32_t f_netmask; static uint32_t f_localnet; +#ifdef HAVE_CASPER +extern cap_channel_t *capdns; +#endif /* * Return a name for the IP address pointed to by ap. This address @@ -242,7 +250,13 @@ getname(netdissect_options *ndo, const u_char *ap) */ if (!ndo->ndo_nflag && (addr & f_netmask) == f_localnet) { - hp = gethostbyaddr((char *)&addr, 4, AF_INET); +#ifdef HAVE_CASPER + if (capdns != NULL) { + hp = cap_gethostbyaddr(capdns, (char *)&addr, 4, + AF_INET); + } else +#endif + hp = gethostbyaddr((char *)&addr, 4, AF_INET); if (hp) { char *dotp; @@ -297,7 +311,14 @@ getname6(netdissect_options *ndo, const u_char *ap) * Do not print names if -n was given. */ if (!ndo->ndo_nflag) { - hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET6); +#ifdef HAVE_CASPER + if (capdns != NULL) { + hp = cap_gethostbyaddr(capdns, (char *)&addr, + sizeof(addr), AF_INET6); + } else +#endif + hp = gethostbyaddr((char *)&addr, sizeof(addr), + AF_INET6); if (hp) { char *dotp; diff --git a/config.h.in b/config.h.in index 27ba4b10..bf5f120d 100644 --- a/config.h.in +++ b/config.h.in @@ -12,8 +12,8 @@ /* capsicum support available */ #undef HAVE_CAPSICUM -/* Define to 1 if you have the `cap_enter' function. */ -#undef HAVE_CAP_ENTER +/* Casper library available */ +#undef HAVE_CASPER /* Define to 1 if you have the `cap_ioctls_limit' function. */ #undef HAVE_CAP_IOCTLS_LIMIT diff --git a/configure b/configure index 0a0dfdc1..79226387 100755 --- a/configure +++ b/configure @@ -4588,6 +4588,86 @@ else fi done + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_init in -lcasper" >&5 +$as_echo_n "checking for cap_init in -lcasper... " >&6; } +if ${ac_cv_lib_casper_cap_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcasper $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_init (); +int +main () +{ +return cap_init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_casper_cap_init=yes +else + ac_cv_lib_casper_cap_init=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_casper_cap_init" >&5 +$as_echo "$ac_cv_lib_casper_cap_init" >&6; } +if test "x$ac_cv_lib_casper_cap_init" = xyes; then : + LIBS="$LIBS -lcasper" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_gethostbyaddr in -lcap_dns" >&5 +$as_echo_n "checking for cap_gethostbyaddr in -lcap_dns... " >&6; } +if ${ac_cv_lib_cap_dns_cap_gethostbyaddr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcap_dns $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_gethostbyaddr (); +int +main () +{ +return cap_gethostbyaddr (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cap_dns_cap_gethostbyaddr=yes +else + ac_cv_lib_cap_dns_cap_gethostbyaddr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_dns_cap_gethostbyaddr" >&5 +$as_echo "$ac_cv_lib_cap_dns_cap_gethostbyaddr" >&6; } +if test "x$ac_cv_lib_cap_dns_cap_gethostbyaddr" = xyes; then : + LIBS="$LIBS -lcap_dns" +fi + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to sandbox using capsicum" >&5 $as_echo_n "checking whether to sandbox using capsicum... " >&6; } @@ -4601,6 +4681,18 @@ else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to sandbox using Casper library" >&5 +$as_echo_n "checking whether to sandbox using Casper library... " >&6; } +if test "x$ac_cv_lib_casper_cap_init" = "xyes" -a "x$ac_cv_lib_cap_dns_cap_gethostbyaddr" = "xyes"; then + +$as_echo "#define HAVE_CASPER 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi # # We must check this before checking whether to check the OS's IPv6, diff --git a/configure.in b/configure.in index d7484ede..d8cc3882 100644 --- a/configure.in +++ b/configure.in @@ -225,6 +225,8 @@ if test ! -z "$with_sandbox-capsicum" && test "$with_sandbox-capsicum" != "no" ; AC_CHECK_FUNCS(cap_enter cap_rights_limit cap_ioctls_limit openat, ac_lbl_capsicum_function_seen=yes, ac_lbl_capsicum_function_not_seen=yes) + AC_CHECK_LIB(casper, cap_init, LIBS="$LIBS -lcasper") + AC_CHECK_LIB(cap_dns, cap_gethostbyaddr, LIBS="$LIBS -lcap_dns") fi AC_MSG_CHECKING([whether to sandbox using capsicum]) if test "x$ac_lbl_capsicum_function_seen" = "xyes" -a "x$ac_lbl_capsicum_function_not_seen" != "xyes"; then @@ -233,6 +235,13 @@ if test "x$ac_lbl_capsicum_function_seen" = "xyes" -a "x$ac_lbl_capsicum_functio else AC_MSG_RESULT(no) fi +AC_MSG_CHECKING([whether to sandbox using Casper library]) +if test "x$ac_cv_lib_casper_cap_init" = "xyes" -a "x$ac_cv_lib_cap_dns_cap_gethostbyaddr" = "xyes"; then + AC_DEFINE(HAVE_CASPER, 1, [Casper support available]) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi # # We must check this before checking whether to check the OS's IPv6, diff --git a/tcpdump.c b/tcpdump.c index 73bf1387..a0c273af 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -80,6 +80,11 @@ The Regents of the University of California. All rights reserved.\n"; #include #include #include +#ifdef HAVE_CASPER +#include +#include +#include +#endif /* HAVE_CASPER */ #endif /* HAVE_CAPSICUM */ #include #include @@ -170,6 +175,10 @@ static int infoprint; char *program_name; +#ifdef HAVE_CASPER +cap_channel_t *capdns; +#endif + /* Forwards */ static void error(const char *, ...) __attribute__((noreturn)) @@ -720,6 +729,35 @@ get_next_file(FILE *VFile, char *ptr) return ret; } +#ifdef HAVE_CASPER +static cap_channel_t * +capdns_setup(void) +{ + cap_channel_t *capcas, *capdnsloc; + const char *types[1]; + int families[2]; + + capcas = cap_init(); + if (capcas == NULL) + error("unable to create casper process"); + capdnsloc = cap_service_open(capcas, "system.dns"); + /* Casper capability no longer needed. */ + cap_close(capcas); + if (capdnsloc == NULL) + error("unable to open system.dns service"); + /* Limit system.dns to reverse DNS lookups. */ + types[0] = "ADDR"; + if (cap_dns_type_limit(capdnsloc, types, 1) < 0) + error("unable to limit access to system.dns service"); + families[0] = AF_INET; + families[1] = AF_INET6; + if (cap_dns_family_limit(capdnsloc, families, 2) < 0) + error("unable to limit access to system.dns service"); + + return (capdnsloc); +} +#endif /* HAVE_CASPER */ + #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION static int tstamp_precision_from_string(const char *precision) @@ -1779,6 +1817,12 @@ main(int argc, char **argv) pcap_freecode(&fcode); exit_tcpdump(0); } + +#ifdef HAVE_CASPER + if (!ndo->ndo_nflag) + capdns = capdns_setup(); +#endif /* HAVE_CASPER */ + init_print(ndo, localnet, netmask, timezone_offset); #ifndef _WIN32 @@ -1995,7 +2039,12 @@ main(int argc, char **argv) } #ifdef HAVE_CAPSICUM - cansandbox = (ndo->ndo_nflag && VFileName == NULL && zflag == NULL); + cansandbox = (VFileName == NULL && zflag == NULL); +#ifdef HAVE_CASPER + cansandbox = (cansandbox && (ndo->ndo_nflag || capdns != NULL)); +#else + cansandbox = (cansandbox && ndo->ndo_nflag); +#endif /* HAVE_CASPER */ if (cansandbox && cap_enter() < 0 && errno != ENOSYS) error("unable to enter the capability mode"); #endif /* HAVE_CAPSICUM */ -- 2.39.5