svn commit: r253004 - head/contrib/tcpdump

Pawel Jakub Dawidek pjd at FreeBSD.org
Sun Jul 7 21:19:54 UTC 2013


Author: pjd
Date: Sun Jul  7 21:19:53 2013
New Revision: 253004
URL: http://svnweb.freebsd.org/changeset/base/253004

Log:
  Sandbox tcpdump(8) using Capsicum's capability mode and capabilities.
  For now, sandboxing is done only if -n option was specified and neither -z nor
  -V options were given. Because it is very common to run tcpdump(8) with the -n
  option for speed, I decided to commit sandboxing now. To also support
  sandboxing when -n option wasn't specified, we need Casper daemon and its
  services that are not available in FreeBSD yet.
  
  - Limit file descriptors of a file specified by -r option or files specified
    via -V option to CAP_READ only.
  
  - If neither -r nor -V options were specified, we operate on /dev/bpf.
    Limit its descriptor to CAP_READ and CAP_IOCTL plus limit allowed ioctls to
    BIOCGSTATS only.
  
  - Limit file descriptor of a file specified by -w option to CAP_SEEK and
    CAP_WRITE.
  
  - If either -C or -G options were specified, we open directory containing
    destination file and we limit directory descriptor to CAP_CREATE, CAP_FCNTL,
    CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK and CAP_WRITE. Newly opened/created
    files are limited to CAP_SEEK and CAP_WRITE only.
  
  - Enter capability mode if -n option was specified and neither -z nor -V
    options were specified.
  
  Approved by:	delphij, wxs
  Sponsored by:	The FreeBSD Foundation

Modified:
  head/contrib/tcpdump/tcpdump.c

Modified: head/contrib/tcpdump/tcpdump.c
==============================================================================
--- head/contrib/tcpdump/tcpdump.c	Sun Jul  7 20:44:04 2013	(r253003)
+++ head/contrib/tcpdump/tcpdump.c	Sun Jul  7 21:19:53 2013	(r253004)
@@ -68,6 +68,13 @@ extern int SIZE_BUF;
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
+#ifdef __FreeBSD__
+#include <sys/capability.h>
+#include <sys/ioccom.h>
+#include <net/bpf.h>
+#include <fcntl.h>
+#include <libgen.h>
+#endif	/* __FreeBSD__ */
 #ifndef WIN32
 #include <sys/wait.h>
 #include <sys/resource.h>
@@ -384,6 +391,9 @@ struct dump_info {
 	char	*CurrentFileName;
 	pcap_t	*pd;
 	pcap_dumper_t *p;
+#ifdef __FreeBSD__
+	int	dirfd;
+#endif
 };
 
 #ifdef HAVE_PCAP_SET_TSTAMP_TYPE
@@ -702,6 +712,10 @@ main(int argc, char **argv)
 #endif
 	int status;
 	FILE *VFile;
+#ifdef __FreeBSD__
+	int cansandbox;
+#endif
+
 #ifdef WIN32
 	if(wsockinit() != 0) return 1;
 #endif /* WIN32 */
@@ -1189,6 +1203,12 @@ main(int argc, char **argv)
 		pd = pcap_open_offline(RFileName, ebuf);
 		if (pd == NULL)
 			error("%s", ebuf);
+#ifdef __FreeBSD__
+		if (cap_rights_limit(fileno(pcap_file(pd)), CAP_READ) < 0 &&
+		    errno != ENOSYS) {
+			error("unable to limit pcap descriptor");
+		}
+#endif
 		dlt = pcap_datalink(pd);
 		dlt_name = pcap_datalink_val_to_name(dlt);
 		if (dlt_name == NULL) {
@@ -1437,6 +1457,20 @@ main(int argc, char **argv)
 
 	if (pcap_setfilter(pd, &fcode) < 0)
 		error("%s", pcap_geterr(pd));
+#ifdef __FreeBSD__
+	if (RFileName == NULL && VFileName == NULL) {
+		static const unsigned long cmds[] = { BIOCGSTATS };
+
+		if (cap_rights_limit(pcap_fileno(pd),
+		    CAP_IOCTL | CAP_READ) < 0 && errno != ENOSYS) {
+			error("unable to limit pcap descriptor");
+		}
+		if (cap_ioctls_limit(pcap_fileno(pd), cmds,
+		    sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) {
+			error("unable to limit ioctls on pcap descriptor");
+		}
+	}
+#endif
 	if (WFileName) {
 		pcap_dumper_t *p;
 		/* Do not exceed the default PATH_MAX for files. */
@@ -1458,9 +1492,30 @@ main(int argc, char **argv)
 #endif
 		if (p == NULL)
 			error("%s", pcap_geterr(pd));
+#ifdef __FreeBSD__
+		if (cap_rights_limit(fileno(pcap_dump_file(p)),
+		    CAP_SEEK | CAP_WRITE) < 0 && errno != ENOSYS) {
+			error("unable to limit dump descriptor");
+		}
+#endif
 		if (Cflag != 0 || Gflag != 0) {
-			callback = dump_packet_and_trunc;
+#ifdef __FreeBSD__
+			dumpinfo.WFileName = strdup(basename(WFileName));
+			dumpinfo.dirfd = open(dirname(WFileName),
+			    O_DIRECTORY | O_RDONLY);
+			if (dumpinfo.dirfd < 0) {
+				error("unable to open directory %s",
+				    dirname(WFileName));
+			}
+			if (cap_rights_limit(dumpinfo.dirfd, CAP_CREATE |
+			    CAP_FCNTL | CAP_FTRUNCATE | CAP_LOOKUP | CAP_SEEK |
+			    CAP_WRITE) < 0 && errno != ENOSYS) {
+				error("unable to limit directory rights");
+			}
+#else	/* !__FreeBSD__ */
 			dumpinfo.WFileName = WFileName;
+#endif
+			callback = dump_packet_and_trunc;
 			dumpinfo.pd = pd;
 			dumpinfo.p = p;
 			pcap_userdata = (u_char *)&dumpinfo;
@@ -1530,6 +1585,15 @@ main(int argc, char **argv)
 		(void)fflush(stderr);
 	}
 #endif /* WIN32 */
+
+#ifdef __FreeBSD__
+	cansandbox = (nflag && VFileName == NULL && zflag == NULL);
+	if (cansandbox && cap_enter() < 0 && errno != ENOSYS)
+		error("unable to enter the capability mode");
+	if (cap_sandboxed())
+		fprintf(stderr, "capability mode sandbox enabled\n");
+#endif
+
 	do {
 		status = pcap_loop(pd, cnt, callback, pcap_userdata);
 		if (WFileName == NULL) {
@@ -1569,6 +1633,12 @@ main(int argc, char **argv)
 				pd = pcap_open_offline(RFileName, ebuf);
 				if (pd == NULL)
 					error("%s", ebuf);
+#ifdef __FreeBSD__
+				if (cap_rights_limit(fileno(pcap_file(pd)),
+				    CAP_READ) < 0 && errno != ENOSYS) {
+					error("unable to limit pcap descriptor");
+				}
+#endif
 				new_dlt = pcap_datalink(pd);
 				if (WFileName && new_dlt != dlt)
 					error("%s: new dlt does not match original", RFileName);
@@ -1765,6 +1835,11 @@ dump_packet_and_trunc(u_char *user, cons
 
 		/* If the time is greater than the specified window, rotate */
 		if (t - Gflag_time >= Gflag) {
+#ifdef __FreeBSD__
+			FILE *fp;
+			int fd;
+#endif
+
 			/* Update the Gflag_time */
 			Gflag_time = t;
 			/* Update Gflag_count */
@@ -1811,13 +1886,35 @@ dump_packet_and_trunc(u_char *user, cons
 			capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
 			capng_apply(CAPNG_EFFECTIVE);
 #endif /* HAVE_CAP_NG_H */
+#ifdef __FreeBSD__
+			fd = openat(dump_info->dirfd,
+			    dump_info->CurrentFileName,
+			    O_CREAT | O_WRONLY | O_TRUNC, 0644);
+			if (fd < 0) {
+				error("unable to open file %s",
+				    dump_info->CurrentFileName);
+			}
+			fp = fdopen(fd, "w");
+			if (fp == NULL) {
+				error("unable to fdopen file %s",
+				    dump_info->CurrentFileName);
+			}
+			dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
+#else	/* !__FreeBSD__ */
 			dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+#endif
 #ifdef HAVE_CAP_NG_H
 			capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
 			capng_apply(CAPNG_EFFECTIVE);
 #endif /* HAVE_CAP_NG_H */
 			if (dump_info->p == NULL)
 				error("%s", pcap_geterr(pd));
+#ifdef __FreeBSD__
+			if (cap_rights_limit(fileno(pcap_dump_file(dump_info->p)),
+			    CAP_SEEK | CAP_WRITE) < 0 && errno != ENOSYS) {
+				error("unable to limit dump descriptor");
+			}
+#endif
 		}
 	}
 
@@ -1827,6 +1924,11 @@ dump_packet_and_trunc(u_char *user, cons
 	 * file could put it over Cflag.
 	 */
 	if (Cflag != 0 && pcap_dump_ftell(dump_info->p) > Cflag) {
+#ifdef __FreeBSD__
+		FILE *fp;
+		int fd;
+#endif
+
 		/*
 		 * Close the current file and open a new one.
 		 */
@@ -1849,9 +1951,30 @@ dump_packet_and_trunc(u_char *user, cons
 		if (dump_info->CurrentFileName == NULL)
 			error("dump_packet_and_trunc: malloc");
 		MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
+#ifdef __FreeBSD__
+		fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
+		    O_CREAT | O_WRONLY | O_TRUNC, 0644);
+		if (fd < 0) {
+			error("unable to open file %s",
+			    dump_info->CurrentFileName);
+		}
+		fp = fdopen(fd, "w");
+		if (fp == NULL) {
+			error("unable to fdopen file %s",
+			    dump_info->CurrentFileName);
+		}
+		dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
+#else	/* !__FreeBSD__ */
 		dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+#endif
 		if (dump_info->p == NULL)
 			error("%s", pcap_geterr(pd));
+#ifdef __FreeBSD__
+		if (cap_rights_limit(fileno(pcap_dump_file(dump_info->p)),
+		    CAP_SEEK | CAP_WRITE) < 0 && errno != ENOSYS) {
+			error("unable to limit dump descriptor");
+		}
+#endif
 	}
 
 	pcap_dump((u_char *)dump_info->p, h, sp);


More information about the svn-src-all mailing list