On Thu, Jul 18, 2019 at 11:46:46AM -0400, Bryan Steele wrote: > On Thu, Jul 18, 2019 at 04:13:10PM +0200, Alexander Bluhm wrote: > > Hi, > > > > Can we track unveil(2) violators in process accounting lastcomm(1)? > > This makes it easier to find them. > > > > $ lastcomm | grep -e '-[A-Z]U' > > pflogd -FU root __ 0.00 secs Thu Jul 18 14:19 > > (2:33:22.00) > > > > Seems that pflogd(8) has to be investigated. > > Interesting. > > This appears to be a false positive, libpcap will first attempt to open > /dev/bpf as O_RDWR and then falls back to O_RDONLY on EACCES. I'm fairly > confident that pflogd(8) does not need write access to bpf. If anything, > unveil(2) appears to have found an old bug here, as before pflogd was > always opening the device with both read/write permissions. > > tcpdump avoids this by internalizing more parts of libpcap, and also > opening /dev/bpf O_RDONLY itself. > > spamlogd appears to be the only other user of pcap_open_live() in base, > unfortunately it calls unveil after, so /dev/bpf is opened O_RDWR. I > don't use spamlogd, though.
Here's a potential diff for both pflogd and spamlogd, I've tested it works with pflogd. I only compile tested spamlogd. But it should avoid the unveil accounting errors. This duplicates a lot of code into both, but I don't feel like trying to extent the libpcap API for this. comments? ok? Index: pflogd.c =================================================================== RCS file: /cvs/src/sbin/pflogd/pflogd.c,v retrieving revision 1.59 diff -u -p -u -r1.59 pflogd.c --- sbin/pflogd/pflogd.c 26 Aug 2018 18:24:46 -0000 1.59 +++ sbin/pflogd/pflogd.c 18 Jul 2019 21:30:39 -0000 @@ -73,6 +73,7 @@ void dump_packet(u_char *, const struct void dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *); int flush_buffer(FILE *); int if_exists(char *); +pcap_t *pcap_live(const char *, int, int, int, char *); void logmsg(int, const char *, ...); void purge_buffer(void); int reset_dump(void); @@ -194,10 +195,97 @@ if_exists(char *ifname) return (if_nametoindex(ifname) != 0); } +pcap_t * +pcap_live(const char *source, int slen, int promisc, int to_ms, + char *ebuf) +{ + int fd; + struct bpf_version bv; + struct ifreq ifr; + u_int v, dlt = DLT_PFLOG; + pcap_t *p; + + if (source == NULL || slen <= 0) + return (NULL); + + p = pcap_create(source, ebuf); + if (p == NULL) + return (NULL); + + /* Open bpf(4) read only */ + if ((fd = open("/dev/bpf", O_RDONLY)) == -1) + return (NULL); + + if (ioctl(fd, BIOCVERSION, &bv) == -1) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", + pcap_strerror(errno)); + goto bad; + } + + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "kernel bpf filter out of date"); + goto bad; + } + + strlcpy(ifr.ifr_name, source, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) == -1) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s", + pcap_strerror(errno)); + goto bad; + } + + if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt)) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSDLT: %s", + pcap_strerror(errno)); + goto bad; + } + + p->fd = fd; + p->snapshot = slen; + p->linktype = dlt; + + /* 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)); + } + } + if (promisc) + /* this is allowed to fail */ + ioctl(fd, BIOCPROMISC, NULL); + + if (ioctl(fd, BIOCGBLEN, &v) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", + pcap_strerror(errno)); + goto bad; + } + p->bufsize = v; + p->buffer = malloc(p->bufsize); + if (p->buffer == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + goto bad; + } + p->activated = 1; + return (p); + +bad: + if (fd >= 0) + close(fd); + free(p); + return (NULL); +} + int init_pcap(void) { - hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); + hpcap = pcap_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); if (hpcap == NULL) { logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); return (-1); Index: Makefile =================================================================== RCS file: /cvs/src/libexec/spamlogd/Makefile,v retrieving revision 1.8 diff -u -p -u -r1.8 Makefile --- libexec/spamlogd/Makefile 28 Jun 2018 02:23:27 -0000 1.8 +++ libexec/spamlogd/Makefile 18 Jul 2019 21:37:48 -0000 @@ -5,6 +5,8 @@ SRCS= spamlogd.c sync.c gdcopy.c MAN= spamlogd.8 CFLAGS+= -Wall -Wstrict-prototypes -I${.CURDIR}/../spamd +# for pcap-int.h +CFLAGS+=-I${.CURDIR}/../../lib/libpcap LDADD+= -lpcap -lcrypto DPADD+= ${LIBPCAP} ${LIBCRYPTO} .PATH: ${.CURDIR}/../spamd Index: spamlogd.c =================================================================== RCS file: /cvs/src/libexec/spamlogd/spamlogd.c,v retrieving revision 1.28 diff -u -p -u -r1.28 spamlogd.c --- libexec/spamlogd/spamlogd.c 25 Oct 2018 06:41:50 -0000 1.28 +++ libexec/spamlogd/spamlogd.c 18 Jul 2019 21:37:48 -0000 @@ -49,6 +49,7 @@ #include <syslog.h> #include <string.h> #include <unistd.h> +#include <pcap-int.h> #include <pcap.h> #include "grey.h" @@ -107,6 +108,93 @@ sighandler_close(int signal) pcap_breakloop(hpcap); /* sighdlr safe */ } +pcap_t * +pcap_live(const char *source, int slen, int promisc, int to_ms, + char *ebuf) +{ + int fd; + struct bpf_version bv; + struct ifreq ifr; + u_int v, dlt = DLT_PFLOG; + pcap_t *p; + + if (source == NULL || slen <= 0) + return (NULL); + + p = pcap_create(source, ebuf); + if (p == NULL) + return (NULL); + + /* Open bpf(4) read only */ + if ((fd = open("/dev/bpf", O_RDONLY)) == -1) + return (NULL); + + if (ioctl(fd, BIOCVERSION, &bv) == -1) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", + pcap_strerror(errno)); + goto bad; + } + + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "kernel bpf filter out of date"); + goto bad; + } + + strlcpy(ifr.ifr_name, source, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) == -1) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s", + pcap_strerror(errno)); + goto bad; + } + + if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt)) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSDLT: %s", + pcap_strerror(errno)); + goto bad; + } + + p->fd = fd; + p->snapshot = slen; + p->linktype = dlt; + + /* 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)); + } + } + if (promisc) + /* this is allowed to fail */ + ioctl(fd, BIOCPROMISC, NULL); + + if (ioctl(fd, BIOCGBLEN, &v) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", + pcap_strerror(errno)); + goto bad; + } + p->bufsize = v; + p->buffer = malloc(p->bufsize); + if (p->buffer == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + goto bad; + } + p->activated = 1; + return (p); + +bad: + if (fd >= 0) + close(fd); + free(p); + return (NULL); +} + int init_pcap(void) { @@ -114,7 +202,7 @@ init_pcap(void) char filter[PCAPFSIZ] = "ip and port 25 and action pass " "and tcp[13]&0x12=0x2"; - if ((hpcap = pcap_open_live(pflogif, PCAPSNAP, 1, PCAPTIMO, + if ((hpcap = pcap_live(pflogif, PCAPSNAP, 1, PCAPTIMO, errbuf)) == NULL) { logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); return (-1);