From 219a6f3c65800b71d02941302e5b31861ef2739a Mon Sep 17 00:00:00 2001 From: Laurent Ghigonis Date: Sun, 2 Dec 2012 22:52:32 +0100 Subject: work in progress on gg_sniff --- gg_sniff/Makefile | 21 ++++++++ gg_sniff/README.txt | 37 +++++++++++++ gg_sniff/dns.c | 3 ++ gg_sniff/gg_sniff.c | 112 ++++++++++++++++++++++++++++++++++++++++ gg_sniff/gg_sniff.h | 12 +++++ gg_sniff/pcap.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 331 insertions(+) create mode 100644 gg_sniff/Makefile create mode 100644 gg_sniff/README.txt create mode 100644 gg_sniff/dns.c create mode 100644 gg_sniff/gg_sniff.c create mode 100644 gg_sniff/gg_sniff.h create mode 100644 gg_sniff/pcap.c diff --git a/gg_sniff/Makefile b/gg_sniff/Makefile new file mode 100644 index 0000000..a883274 --- /dev/null +++ b/gg_sniff/Makefile @@ -0,0 +1,21 @@ +CFLAGS += $(shell pkg-config --cflags elementary evas ecore) +LIBS += $(shell pkg-config --libs elementary evas ecore) +CFLAGS += -Wall -O2 + +BINARY=gg_sniff + +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin + +$(BINARY): $(BINARY).o + $(CC) -o $@ $< $(LIBS) + +install: $(BINARY) + @echo "installation of $(BINARY)" + mkdir -p $(BINDIR) + useradd -d /var/empty/ -s /sbin/nologin _gg_sniff + install -m 0755 $(BINARY) $(BINDIR) + +clean: + rm -f $(BINARY) $(BINARY).o + diff --git a/gg_sniff/README.txt b/gg_sniff/README.txt new file mode 100644 index 0000000..dcf42e1 --- /dev/null +++ b/gg_sniff/README.txt @@ -0,0 +1,37 @@ +gg_sniff - glouglou probe client for network activity + +WARNING: Work in progress, don't expect this to work ! + +Requirements +============ + +* libglouglou +* libpcap +* libevent2 + +Installation +============ + +git clone git@meg:glouglou +cd gg_sniff +make && sudo make install + +Usage +===== + +gg_sniff -i eth0 + +Notes on architecture and security +================================== + +gg_sniff must be run as root. It drops priviledges to user _gg_sniff and chroots +into _gg_sniff user home (/var/empty). +gg_sniff does: +* configuration, glouglou server reporting, droppriv and chroot (gg_sniff.c) +* read pcapfd to capture network traffic (pcap.c) +* async DNS resolving using evdns (dns.c) + +Note that gg_sniff activates extra protections on libpcap file descriptor, by +setting it to readonly, for now on OpenBSD only. +It does so by reimplementing some of libpcap functions, see +pcap.c my_pcap_open_live() diff --git a/gg_sniff/dns.c b/gg_sniff/dns.c new file mode 100644 index 0000000..0b7d1de --- /dev/null +++ b/gg_sniff/dns.c @@ -0,0 +1,3 @@ +#include +#include +#include diff --git a/gg_sniff/gg_sniff.c b/gg_sniff/gg_sniff.c new file mode 100644 index 0000000..3b23d34 --- /dev/null +++ b/gg_sniff/gg_sniff.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if defined(__OpenBSD__) +#include "pcap-int.h" +#endif + +#define GG_SNIFF_USER "_gg_sniff" + +int net_socket; + +#if defined(__OPENBSD__) +void __dead +#else +void +#endif +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-hv]", + __progname); + exit(1); +} + +static void +sig_handler(int sig, short why, void *data) +{ + log_info("got signal %d", sig); + if (sig == SIGINT || sig == SIGTERM) + event_loopexit(NULL); +} + +int +main(int argc, char **argv) +{ + struct event_base *ev_base; + struct sockaddr_in sock_addr; + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + char errbuf[PCAP_ERRBUF_SIZE]; + struct bpf_program bprog; + pcap_t *pcap; + int loglevel = 0; + int op; + int sock_on = 1; + + if (geteuid() != 0) + errx(1, "must be root"); + + while ((op = getopt(argc, argv, "hv")) != -1) { + switch (op) { + case 'h': + usage(); + /* NOTREACHED */ + case 'v': + loglevel++; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + gg_log_init(GG_SNIFF_LOGFILE, loglevel); + + ev_base = event_base_new(); + + ggcli = gg_client_connect(); + + ggsniff_pcap_init(); + + ggsniff_dns_init(); + + signal_set(&ev_sigint, SIGINT, sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL); + signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL); + signal_set(&ev_sighup, SIGHUP, sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sigchld, NULL); + signal_add(&ev_sighup, NULL); + signal(SIGPIPE, SIG_IGN); + + droppriv(GG_SNIFF_USER, 1, NULL); + + log_info("entering event loop"); + event_base_dispatch(ev_base); + + ggsniff_dns_shutdown(); + ggsniff_pcap_shutdown(); + gg_client_disconnect(ggcli); + + log_info("exiting"); + exit(0); +} diff --git a/gg_sniff/gg_sniff.h b/gg_sniff/gg_sniff.h new file mode 100644 index 0000000..bbb9f10 --- /dev/null +++ b/gg_sniff/gg_sniff.h @@ -0,0 +1,12 @@ +/* gg_sniff.c */ + +/* pcap.c */ + +void ggsniff_pcap_init(); +void ggsniff_pcap_shutdown(); + +/* dns.c */ + +void ggsniff_dns_init(); +void ggsniff_dns_shutdown(); + diff --git a/gg_sniff/pcap.c b/gg_sniff/pcap.c new file mode 100644 index 0000000..b13a486 --- /dev/null +++ b/gg_sniff/pcap.c @@ -0,0 +1,146 @@ +#define PCAP_INTERFACE "lo" +#define PCAP_SNAPLEN 100 +#define PCAP_FILTER "not port 4430 and not port 53" +#define PCAP_COUNT 20 +#define PCAP_TO 300 + +static pcap_t * +ggsniff_pcap_init(void) +{ + pcap = my_pcap_open_live(PCAP_INTERFACE, PCAP_SNAPLEN, 1, PCAP_TO, errbuf, -1, 0); + if (pcap == NULL) + fatal("capture: pcap_open_live failed on interface %s\n" + "with snaplen %d : %s", + PCAP_INTERFACE, PCAP_SNAPLEN, errbuf); + if (pcap_compile(pcap, &bprog, PCAP_FILTER, 0, 0) < 0) + fatal("capture: pcap_compile failed with filter %s : %s", + PCAP_FILTER, pcap_geterr(pcap)); + if (pcap_setfilter(pcap, &bprog) < 0) + fatal("capture: pcap_setfilter failed : %s", + pcap_geterr(pcap)); +} + +/* + * reimplement pcap_open_live with more restrictions on the bpf fd : + * - open device read only + * - lock the fd + * based on OpenBSD tcpdump, privsep_pcap.c v1.16 + */ + +static pcap_t * +my_pcap_open_live(const char *dev, int slen, int promisc, int to_ms, + char *ebuf, u_int dlt, u_int dirfilt) +{ +#if defined(__OpenBSD__) + struct bpf_version bv; + u_int v; + pcap_t *p; + char bpf[sizeof "/dev/bpf0000000000"]; + int fd, n = 0; + struct ifreq ifr; + + p = xmalloc(sizeof(*p)); + bzero(p, sizeof(*p)); + + /* priv part */ + + do { + snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++); + fd = open(bpf, O_RDONLY); + } while (fd < 0 && errno == EBUSY); + if (fd < 0) + return NULL; + + v = 32768; /* XXX this should be a user-accessible hook */ + ioctl(fd, BIOCSBLEN, &v); + + strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) < 0) + return NULL; + + if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt)) + return NULL; + + if (promisc) + /* this is allowed to fail */ + ioctl(fd, BIOCPROMISC, NULL); + if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0) + return NULL; + + /* lock the descriptor */ + if (ioctl(fd, BIOCLOCK, NULL) < 0) + return NULL; + + /* end of priv part */ + + /* fd is locked, can only use 'safe' ioctls */ + if (ioctl(fd, BIOCVERSION, &bv) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", + pcap_strerror(errno)); + return NULL; + } + + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "kernel bpf filter out of date"); + return NULL; + } + + p->fd = fd; + p->snapshot = slen; + + /* Get the data link layer type. */ + if (ioctl(fd, BIOCGDLT, &v) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", + pcap_strerror(errno)); + return NULL; + } +#if _BSDI_VERSION - 0 >= 199510 + /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */ + switch (v) { + + case DLT_SLIP: + v = DLT_SLIP_BSDOS; + break; + + case DLT_PPP: + v = DLT_PPP_BSDOS; + break; + } +#endif + p->linktype = v; + + /* XXX hack from tcpdump */ + if (p->linktype == DLT_PFLOG && p->snapshot < 160) + p->snapshot = 160; + + /* set timeout */ + if (to_ms != 0) { + struct timeval to; + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms * 1000) % 1000000; + if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", + pcap_strerror(errno)); + return NULL; + } + } + + if (ioctl(fd, BIOCGBLEN, &v) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", + pcap_strerror(errno)); + return NULL; + } + p->bufsize = v; + p->buffer = (u_char *)malloc(p->bufsize); + if (p->buffer == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + return NULL; + } + return p; +#else /* defined(__OpenBSD__) */ + return pcap_open_live(dev, slen, promisc, to_ms, ebuf); +#endif +} -- cgit v1.2.3-59-g8ed1b