On Thu, Jul 18, 2019 at 05:44:21PM -0400, Bryan Steele wrote:
> 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?

With bluhm@'s unveil accounting diff, is anyone ok wit this this
approach to fixing the issue? These are the only pcap_open_live(3)
consumers in base, but I don't feel comfortable trying to extend
the libpcap API, and pretty much everything is already poking at
libpcap internals due to bad API design for privsep.

I'd appreciate if someone could test spamlogd(8) still works.

-Bryan.

Index: pflogd.c
===================================================================
RCS file: /cvs/src/sbin/pflogd/pflogd.c,v
retrieving revision 1.59
diff -u -p -r1.59 pflogd.c
--- sbin/pflogd/pflogd.c        26 Aug 2018 18:24:46 -0000      1.59
+++ sbin/pflogd/pflogd.c        25 Jul 2019 14:00:02 -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,95 @@ 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) == -1) {
+                       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) == -1) {
+               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:
+       pcap_close(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   25 Jul 2019 14:01:25 -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 25 Jul 2019 14:01:25 -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,91 @@ 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) == -1) {
+                       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) == -1) {
+               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:
+       pcap_close(p);
+       return (NULL);
+}
+
 int
 init_pcap(void)
 {
@@ -114,7 +200,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);

Reply via email to