aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Ghigonis <laurent@p1sec.com>2012-12-02 22:52:32 +0100
committerLaurent Ghigonis <laurent@p1sec.com>2012-12-02 22:52:32 +0100
commit219a6f3c65800b71d02941302e5b31861ef2739a (patch)
treeb1b8fe4bc0e3060501f85188f35396feda624568
parentoops, fix _gg_trackproc user (diff)
downloadglouglou-219a6f3c65800b71d02941302e5b31861ef2739a.tar.xz
glouglou-219a6f3c65800b71d02941302e5b31861ef2739a.zip
work in progress on gg_sniff
-rw-r--r--gg_sniff/Makefile21
-rw-r--r--gg_sniff/README.txt37
-rw-r--r--gg_sniff/dns.c3
-rw-r--r--gg_sniff/gg_sniff.c112
-rw-r--r--gg_sniff/gg_sniff.h12
-rw-r--r--gg_sniff/pcap.c146
6 files changed, 331 insertions, 0 deletions
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 <event2/dns.h>
+#include <event2/util.h>
+#include <event2/event.h>
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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <pcap.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <libglouglou.h>
+#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
+}