On Thu, Jul 25, 2019 at 10:06:52AM -0400, Bryan Steele wrote:
> 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.
>
> -Bryan.

Here's a new diff that renames the local function to avoid conflicting
with future attempts at improving the libpcap API. Requested by deraadt@

-Bryan.

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        25 Jul 2019 14:23:14 -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 *pflog_read_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 *
+pflog_read_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 = pflog_read_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);
        if (hpcap == NULL) {
                logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
                return (-1);
Index: spamlogd/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:21:57 -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/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:21:57 -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 *
+pflog_read_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 = pflog_read_live(pflogif, PCAPSNAP, 1, PCAPTIMO,
            errbuf)) == NULL) {
                logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
                return (-1);

Reply via email to