1*bc5a8259Sbeck /* $OpenBSD: spamlogd.c,v 1.32 2021/07/12 15:09:18 beck Exp $ */
284d82a5fSderaadt
31f68c1d4Sbeck /*
4e106bcd2Shenning * Copyright (c) 2006 Henning Brauer <henning@openbsd.org>
5e106bcd2Shenning * Copyright (c) 2006 Berk D. Demir.
64020b879Sbeck * Copyright (c) 2004-2007 Bob Beck.
7e106bcd2Shenning * Copyright (c) 2001 Theo de Raadt.
8e106bcd2Shenning * Copyright (c) 2001 Can Erkin Acar.
9e106bcd2Shenning * All rights reserved
101f68c1d4Sbeck *
111f68c1d4Sbeck * Permission to use, copy, modify, and distribute this software for any
121f68c1d4Sbeck * purpose with or without fee is hereby granted, provided that the above
131f68c1d4Sbeck * copyright notice and this permission notice appear in all copies.
141f68c1d4Sbeck *
151f68c1d4Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
161f68c1d4Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
171f68c1d4Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
181f68c1d4Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
191f68c1d4Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
201f68c1d4Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
211f68c1d4Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
221f68c1d4Sbeck */
231f68c1d4Sbeck
241f68c1d4Sbeck /* watch pf log for mail connections, update whitelist entries. */
251f68c1d4Sbeck
261f68c1d4Sbeck #include <sys/types.h>
271f68c1d4Sbeck #include <sys/socket.h>
28e106bcd2Shenning #include <sys/ioctl.h>
29b9fc9a72Sderaadt #include <sys/signal.h>
30e106bcd2Shenning
31e106bcd2Shenning #include <net/if.h>
32e106bcd2Shenning
331f68c1d4Sbeck #include <netinet/in.h>
34e106bcd2Shenning #include <netinet/ip.h>
351f68c1d4Sbeck #include <arpa/inet.h>
36e106bcd2Shenning
37e106bcd2Shenning #include <net/pfvar.h>
3868928c43Sderaadt #include <net/if_pflog.h>
39e106bcd2Shenning
401f68c1d4Sbeck #include <db.h>
411f68c1d4Sbeck #include <err.h>
42e106bcd2Shenning #include <errno.h>
431f68c1d4Sbeck #include <fcntl.h>
4498babcadSbeck #include <netdb.h>
45e106bcd2Shenning #include <pwd.h>
461f68c1d4Sbeck #include <stdio.h>
47e106bcd2Shenning #include <stdarg.h>
481f68c1d4Sbeck #include <stdlib.h>
49a574feadSbeck #include <syslog.h>
501f68c1d4Sbeck #include <string.h>
511f68c1d4Sbeck #include <unistd.h>
524053af61Sbrynet #include <pcap-int.h>
53e106bcd2Shenning #include <pcap.h>
541f68c1d4Sbeck
551f68c1d4Sbeck #include "grey.h"
5698babcadSbeck #include "sync.h"
571f68c1d4Sbeck
58e106bcd2Shenning #define MIN_PFLOG_HDRLEN 45
59e106bcd2Shenning #define PCAPSNAP 512
60e106bcd2Shenning #define PCAPTIMO 500 /* ms */
61e106bcd2Shenning #define PCAPOPTZ 1 /* optimize filter */
62e106bcd2Shenning #define PCAPFSIZ 512 /* pcap filter string size */
63e106bcd2Shenning
64e7066a17Smestre #define SPAMD_USER "_spamd"
65e7066a17Smestre
6698babcadSbeck int debug = 1;
6798babcadSbeck int greylist = 1;
6898babcadSbeck FILE *grey = NULL;
6998babcadSbeck
7098babcadSbeck u_short sync_port;
7198babcadSbeck int syncsend;
72e106bcd2Shenning u_int8_t flag_debug = 0;
73e106bcd2Shenning u_int8_t flag_inbound = 0;
74e106bcd2Shenning char *networkif = NULL;
75e106bcd2Shenning char *pflogif = "pflog0";
76e106bcd2Shenning char errbuf[PCAP_ERRBUF_SIZE];
77e106bcd2Shenning pcap_t *hpcap = NULL;
78a574feadSbeck struct syslog_data sdata = SYSLOG_DATA_INIT;
79607a4d95Sokan time_t whiteexp = WHITEEXP;
806d2bfb43Sotto extern char *__progname;
811f68c1d4Sbeck
82e106bcd2Shenning void logmsg(int , const char *, ...);
83e106bcd2Shenning void sighandler_close(int);
84e106bcd2Shenning int init_pcap(void);
85e106bcd2Shenning void logpkt_handler(u_char *, const struct pcap_pkthdr *, const u_char *);
86e106bcd2Shenning int dbupdate(char *, char *);
87e7066a17Smestre __dead void usage(void);
88e106bcd2Shenning
89e106bcd2Shenning void
logmsg(int pri,const char * msg,...)90e106bcd2Shenning logmsg(int pri, const char *msg, ...)
91e106bcd2Shenning {
92e106bcd2Shenning va_list ap;
93e106bcd2Shenning va_start(ap, msg);
94e106bcd2Shenning
95e106bcd2Shenning if (flag_debug) {
96e106bcd2Shenning vfprintf(stderr, msg, ap);
97e106bcd2Shenning fprintf(stderr, "\n");
98e106bcd2Shenning } else
99e106bcd2Shenning vsyslog_r(pri, &sdata, msg, ap);
100e106bcd2Shenning
101e106bcd2Shenning va_end(ap);
102e106bcd2Shenning }
103e106bcd2Shenning
104e106bcd2Shenning void
sighandler_close(int signal)105e106bcd2Shenning sighandler_close(int signal)
106e106bcd2Shenning {
107e106bcd2Shenning if (hpcap != NULL)
108e106bcd2Shenning pcap_breakloop(hpcap); /* sighdlr safe */
109e106bcd2Shenning }
110e106bcd2Shenning
1114053af61Sbrynet pcap_t *
pflog_read_live(const char * source,int slen,int promisc,int to_ms,char * ebuf)1124053af61Sbrynet pflog_read_live(const char *source, int slen, int promisc, int to_ms,
1134053af61Sbrynet char *ebuf)
1144053af61Sbrynet {
1154053af61Sbrynet int fd;
1164053af61Sbrynet struct bpf_version bv;
1174053af61Sbrynet struct ifreq ifr;
1184053af61Sbrynet u_int v, dlt = DLT_PFLOG;
1194053af61Sbrynet pcap_t *p;
1204053af61Sbrynet
1214053af61Sbrynet if (source == NULL || slen <= 0)
1224053af61Sbrynet return (NULL);
1234053af61Sbrynet
1244053af61Sbrynet p = pcap_create(source, ebuf);
1254053af61Sbrynet if (p == NULL)
1264053af61Sbrynet return (NULL);
1274053af61Sbrynet
1284053af61Sbrynet /* Open bpf(4) read only */
1294053af61Sbrynet if ((fd = open("/dev/bpf", O_RDONLY)) == -1)
1304053af61Sbrynet return (NULL);
1314053af61Sbrynet
1324053af61Sbrynet if (ioctl(fd, BIOCVERSION, &bv) == -1) {
1334053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
1344053af61Sbrynet pcap_strerror(errno));
1354053af61Sbrynet goto bad;
1364053af61Sbrynet }
1374053af61Sbrynet
1384053af61Sbrynet if (bv.bv_major != BPF_MAJOR_VERSION ||
1394053af61Sbrynet bv.bv_minor < BPF_MINOR_VERSION) {
1404053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE,
1414053af61Sbrynet "kernel bpf filter out of date");
1424053af61Sbrynet goto bad;
1434053af61Sbrynet }
1444053af61Sbrynet
1454053af61Sbrynet strlcpy(ifr.ifr_name, source, sizeof(ifr.ifr_name));
1464053af61Sbrynet if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
1474053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s",
1484053af61Sbrynet pcap_strerror(errno));
1494053af61Sbrynet goto bad;
1504053af61Sbrynet }
1514053af61Sbrynet
1524053af61Sbrynet if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt)) {
1534053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSDLT: %s",
1544053af61Sbrynet pcap_strerror(errno));
1554053af61Sbrynet goto bad;
1564053af61Sbrynet }
1574053af61Sbrynet
1584053af61Sbrynet p->fd = fd;
1594053af61Sbrynet p->snapshot = slen;
1604053af61Sbrynet p->linktype = dlt;
1614053af61Sbrynet
1624053af61Sbrynet /* set timeout */
1634053af61Sbrynet if (to_ms != 0) {
1644053af61Sbrynet struct timeval to;
1654053af61Sbrynet to.tv_sec = to_ms / 1000;
1664053af61Sbrynet to.tv_usec = (to_ms * 1000) % 1000000;
1674053af61Sbrynet if (ioctl(p->fd, BIOCSRTIMEOUT, &to) == -1) {
1684053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
1694053af61Sbrynet pcap_strerror(errno));
17049f581ecSbrynet goto bad;
1714053af61Sbrynet }
1724053af61Sbrynet }
1734053af61Sbrynet if (promisc)
1744053af61Sbrynet /* this is allowed to fail */
1754053af61Sbrynet ioctl(fd, BIOCPROMISC, NULL);
1764053af61Sbrynet
1774053af61Sbrynet if (ioctl(fd, BIOCGBLEN, &v) == -1) {
1784053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
1794053af61Sbrynet pcap_strerror(errno));
1804053af61Sbrynet goto bad;
1814053af61Sbrynet }
1824053af61Sbrynet p->bufsize = v;
1834053af61Sbrynet p->buffer = malloc(p->bufsize);
1844053af61Sbrynet if (p->buffer == NULL) {
1854053af61Sbrynet snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
1864053af61Sbrynet pcap_strerror(errno));
1874053af61Sbrynet goto bad;
1884053af61Sbrynet }
1894053af61Sbrynet p->activated = 1;
1904053af61Sbrynet return (p);
1914053af61Sbrynet
1924053af61Sbrynet bad:
1934053af61Sbrynet pcap_close(p);
1944053af61Sbrynet return (NULL);
1954053af61Sbrynet }
1964053af61Sbrynet
197e106bcd2Shenning int
init_pcap(void)198e106bcd2Shenning init_pcap(void)
199e106bcd2Shenning {
200e106bcd2Shenning struct bpf_program bpfp;
201e106bcd2Shenning char filter[PCAPFSIZ] = "ip and port 25 and action pass "
202e106bcd2Shenning "and tcp[13]&0x12=0x2";
203e106bcd2Shenning
2044053af61Sbrynet if ((hpcap = pflog_read_live(pflogif, PCAPSNAP, 1, PCAPTIMO,
205e106bcd2Shenning errbuf)) == NULL) {
206e106bcd2Shenning logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
207e106bcd2Shenning return (-1);
208e106bcd2Shenning }
209e106bcd2Shenning
210e106bcd2Shenning if (pcap_datalink(hpcap) != DLT_PFLOG) {
211e106bcd2Shenning logmsg(LOG_ERR, "Invalid datalink type");
212e106bcd2Shenning pcap_close(hpcap);
213e106bcd2Shenning hpcap = NULL;
214e106bcd2Shenning return (-1);
215e106bcd2Shenning }
216e106bcd2Shenning
217e106bcd2Shenning if (networkif != NULL) {
218e106bcd2Shenning strlcat(filter, " and on ", PCAPFSIZ);
219e106bcd2Shenning strlcat(filter, networkif, PCAPFSIZ);
220e106bcd2Shenning }
221e106bcd2Shenning
222e106bcd2Shenning if (pcap_compile(hpcap, &bpfp, filter, PCAPOPTZ, 0) == -1 ||
223e106bcd2Shenning pcap_setfilter(hpcap, &bpfp) == -1) {
224e106bcd2Shenning logmsg(LOG_ERR, "%s", pcap_geterr(hpcap));
225e106bcd2Shenning return (-1);
226e106bcd2Shenning }
227e106bcd2Shenning
228e106bcd2Shenning pcap_freecode(&bpfp);
229e106bcd2Shenning
230df69c215Sderaadt if (ioctl(pcap_fileno(hpcap), BIOCLOCK) == -1) {
231e106bcd2Shenning logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno));
232e106bcd2Shenning return (-1);
233e106bcd2Shenning }
234e106bcd2Shenning
235e106bcd2Shenning return (0);
236e106bcd2Shenning }
237e106bcd2Shenning
238e106bcd2Shenning void
logpkt_handler(u_char * user,const struct pcap_pkthdr * h,const u_char * sp)239e106bcd2Shenning logpkt_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
240e106bcd2Shenning {
241e106bcd2Shenning sa_family_t af;
242e106bcd2Shenning u_int8_t hdrlen;
243e106bcd2Shenning u_int32_t caplen = h->caplen;
244e106bcd2Shenning const struct ip *ip = NULL;
245e106bcd2Shenning const struct pfloghdr *hdr;
246e106bcd2Shenning char ipstraddr[40] = { '\0' };
247e106bcd2Shenning
248e106bcd2Shenning hdr = (const struct pfloghdr *)sp;
249e106bcd2Shenning if (hdr->length < MIN_PFLOG_HDRLEN) {
250e106bcd2Shenning logmsg(LOG_WARNING, "invalid pflog header length (%u/%u). "
251e106bcd2Shenning "packet dropped.", hdr->length, MIN_PFLOG_HDRLEN);
252e106bcd2Shenning return;
253e106bcd2Shenning }
254e106bcd2Shenning hdrlen = BPF_WORDALIGN(hdr->length);
255e106bcd2Shenning
256e106bcd2Shenning if (caplen < hdrlen) {
257e106bcd2Shenning logmsg(LOG_WARNING, "pflog header larger than caplen (%u/%u). "
258e106bcd2Shenning "packet dropped.", hdrlen, caplen);
259e106bcd2Shenning return;
260e106bcd2Shenning }
261e106bcd2Shenning
262e106bcd2Shenning /* We're interested in passed packets */
263e106bcd2Shenning if (hdr->action != PF_PASS)
264e106bcd2Shenning return;
265e106bcd2Shenning
266e106bcd2Shenning af = hdr->af;
267e106bcd2Shenning if (af == AF_INET) {
268e106bcd2Shenning ip = (const struct ip *)(sp + hdrlen);
269e106bcd2Shenning if (hdr->dir == PF_IN)
270e106bcd2Shenning inet_ntop(af, &ip->ip_src, ipstraddr,
271e106bcd2Shenning sizeof(ipstraddr));
272e106bcd2Shenning else if (hdr->dir == PF_OUT && !flag_inbound)
273e106bcd2Shenning inet_ntop(af, &ip->ip_dst, ipstraddr,
274e106bcd2Shenning sizeof(ipstraddr));
275e106bcd2Shenning }
276e106bcd2Shenning
277e106bcd2Shenning if (ipstraddr[0] != '\0') {
27899953b1cSbeck if (hdr->dir == PF_IN)
27999953b1cSbeck logmsg(LOG_DEBUG,"inbound %s", ipstraddr);
28099953b1cSbeck else
28199953b1cSbeck logmsg(LOG_DEBUG,"outbound %s", ipstraddr);
282e106bcd2Shenning dbupdate(PATH_SPAMD_DB, ipstraddr);
283e106bcd2Shenning }
284e106bcd2Shenning }
285e106bcd2Shenning
2861f68c1d4Sbeck int
dbupdate(char * dbname,char * ip)2871f68c1d4Sbeck dbupdate(char *dbname, char *ip)
2881f68c1d4Sbeck {
2894020b879Sbeck HASHINFO hashinfo;
290a574feadSbeck DBT dbk, dbd;
291a574feadSbeck DB *db;
2921f68c1d4Sbeck struct gdata gd;
2931f68c1d4Sbeck time_t now;
2941f68c1d4Sbeck int r;
29570333c93Sotto struct in_addr ia;
2961f68c1d4Sbeck
2971f68c1d4Sbeck now = time(NULL);
2984020b879Sbeck memset(&hashinfo, 0, sizeof(hashinfo));
2994020b879Sbeck db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
300e106bcd2Shenning if (db == NULL) {
301e106bcd2Shenning logmsg(LOG_ERR, "Can not open db %s: %s", dbname,
302e106bcd2Shenning strerror(errno));
3031f68c1d4Sbeck return (-1);
304e106bcd2Shenning }
30570333c93Sotto if (inet_pton(AF_INET, ip, &ia) != 1) {
306e106bcd2Shenning logmsg(LOG_NOTICE, "Invalid IP address %s", ip);
3071f68c1d4Sbeck goto bad;
3081f68c1d4Sbeck }
3091f68c1d4Sbeck memset(&dbk, 0, sizeof(dbk));
3101f68c1d4Sbeck dbk.size = strlen(ip);
3111f68c1d4Sbeck dbk.data = ip;
3121f68c1d4Sbeck memset(&dbd, 0, sizeof(dbd));
313e106bcd2Shenning
3141f68c1d4Sbeck /* add or update whitelist entry */
3151f68c1d4Sbeck r = db->get(db, &dbk, &dbd, 0);
3161f68c1d4Sbeck if (r == -1) {
317e106bcd2Shenning logmsg(LOG_NOTICE, "db->get failed (%m)");
3181f68c1d4Sbeck goto bad;
3191f68c1d4Sbeck }
320e106bcd2Shenning
3211f68c1d4Sbeck if (r) {
3221f68c1d4Sbeck /* new entry */
3231f68c1d4Sbeck memset(&gd, 0, sizeof(gd));
3241f68c1d4Sbeck gd.first = now;
3251f68c1d4Sbeck gd.bcount = 1;
3261f68c1d4Sbeck gd.pass = now;
327607a4d95Sokan gd.expire = now + whiteexp;
3281f68c1d4Sbeck memset(&dbk, 0, sizeof(dbk));
3291f68c1d4Sbeck dbk.size = strlen(ip);
3301f68c1d4Sbeck dbk.data = ip;
3311f68c1d4Sbeck memset(&dbd, 0, sizeof(dbd));
3321f68c1d4Sbeck dbd.size = sizeof(gd);
3331f68c1d4Sbeck dbd.data = &gd;
3341f68c1d4Sbeck r = db->put(db, &dbk, &dbd, 0);
3351f68c1d4Sbeck if (r) {
336e106bcd2Shenning logmsg(LOG_NOTICE, "db->put failed (%m)");
3371f68c1d4Sbeck goto bad;
3381f68c1d4Sbeck }
3391f68c1d4Sbeck } else {
34051210d5cSmillert /* XXX - backwards compat */
34151210d5cSmillert if (gdcopyin(&dbd, &gd) == -1) {
3421f68c1d4Sbeck /* whatever this is, it doesn't belong */
3431f68c1d4Sbeck db->del(db, &dbk, 0);
3441f68c1d4Sbeck goto bad;
3451f68c1d4Sbeck }
3461f68c1d4Sbeck gd.pcount++;
347607a4d95Sokan gd.expire = now + whiteexp;
3481f68c1d4Sbeck memset(&dbk, 0, sizeof(dbk));
3491f68c1d4Sbeck dbk.size = strlen(ip);
3501f68c1d4Sbeck dbk.data = ip;
3511f68c1d4Sbeck memset(&dbd, 0, sizeof(dbd));
3521f68c1d4Sbeck dbd.size = sizeof(gd);
3531f68c1d4Sbeck dbd.data = &gd;
3541f68c1d4Sbeck r = db->put(db, &dbk, &dbd, 0);
3551f68c1d4Sbeck if (r) {
356e106bcd2Shenning logmsg(LOG_NOTICE, "db->put failed (%m)");
3571f68c1d4Sbeck goto bad;
3581f68c1d4Sbeck }
3591f68c1d4Sbeck }
3601f68c1d4Sbeck db->close(db);
36184d82a5fSderaadt db = NULL;
3623e552309Sbeck if (syncsend)
363607a4d95Sokan sync_white(now, now + whiteexp, ip);
3641f68c1d4Sbeck return (0);
3651f68c1d4Sbeck bad:
3661f68c1d4Sbeck db->close(db);
36784d82a5fSderaadt db = NULL;
3681f68c1d4Sbeck return (-1);
3691f68c1d4Sbeck }
3701f68c1d4Sbeck
371e106bcd2Shenning void
usage(void)3721f68c1d4Sbeck usage(void)
3731f68c1d4Sbeck {
374fbc349adSderaadt fprintf(stderr,
375607a4d95Sokan "usage: %s [-DI] [-i interface] [-l pflog_interface] "
376607a4d95Sokan "[-W whiteexp] [-Y synctarget]\n",
37744857a5dShenning __progname);
37884d82a5fSderaadt exit(1);
3791f68c1d4Sbeck }
3801f68c1d4Sbeck
3811f68c1d4Sbeck int
main(int argc,char ** argv)3821f68c1d4Sbeck main(int argc, char **argv)
3831f68c1d4Sbeck {
3840ee1ef6eSokan int ch;
385e106bcd2Shenning struct passwd *pw;
386e106bcd2Shenning pcap_handler phandler = logpkt_handler;
38798babcadSbeck int syncfd = 0;
38898babcadSbeck struct servent *ent;
38998babcadSbeck char *sync_iface = NULL;
39098babcadSbeck char *sync_baddr = NULL;
391607a4d95Sokan const char *errstr;
3921f68c1d4Sbeck
393e7066a17Smestre if (geteuid())
394e7066a17Smestre errx(1, "need root privileges");
395e7066a17Smestre
39698babcadSbeck if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
39798babcadSbeck errx(1, "Can't find service \"spamd-sync\" in /etc/services");
39898babcadSbeck sync_port = ntohs(ent->s_port);
39998babcadSbeck
400607a4d95Sokan while ((ch = getopt(argc, argv, "DIi:l:W:Y:")) != -1) {
4011f68c1d4Sbeck switch (ch) {
402e106bcd2Shenning case 'D':
403e106bcd2Shenning flag_debug = 1;
404a574feadSbeck break;
405a574feadSbeck case 'I':
406e106bcd2Shenning flag_inbound = 1;
407e106bcd2Shenning break;
408e106bcd2Shenning case 'i':
409e106bcd2Shenning networkif = optarg;
4101f68c1d4Sbeck break;
41144857a5dShenning case 'l':
412e106bcd2Shenning pflogif = optarg;
41344857a5dShenning break;
414607a4d95Sokan case 'W':
415607a4d95Sokan /* limit whiteexp to 2160 hours (90 days) */
4160ee1ef6eSokan whiteexp = strtonum(optarg, 1, (24 * 90), &errstr);
417607a4d95Sokan if (errstr)
418607a4d95Sokan usage();
419607a4d95Sokan /* convert to seconds from hours */
420607a4d95Sokan whiteexp *= (60 * 60);
421607a4d95Sokan break;
42298babcadSbeck case 'Y':
42398babcadSbeck if (sync_addhost(optarg, sync_port) != 0)
42498babcadSbeck sync_iface = optarg;
42598babcadSbeck syncsend++;
42698babcadSbeck break;
4271f68c1d4Sbeck default:
4281f68c1d4Sbeck usage();
4291f68c1d4Sbeck }
4301f68c1d4Sbeck }
4311f68c1d4Sbeck
432e106bcd2Shenning signal(SIGINT , sighandler_close);
433e106bcd2Shenning signal(SIGQUIT, sighandler_close);
434e106bcd2Shenning signal(SIGTERM, sighandler_close);
435e106bcd2Shenning
436e106bcd2Shenning logmsg(LOG_DEBUG, "Listening on %s for %s %s", pflogif,
437e106bcd2Shenning (networkif == NULL) ? "all interfaces." : networkif,
438e106bcd2Shenning (flag_inbound) ? "Inbound direction only." : "");
439e106bcd2Shenning
440e106bcd2Shenning if (init_pcap() == -1)
441e106bcd2Shenning err(1, "couldn't initialize pcap");
442e106bcd2Shenning
44398babcadSbeck if (syncsend) {
44498babcadSbeck syncfd = sync_init(sync_iface, sync_baddr, sync_port);
44598babcadSbeck if (syncfd == -1)
44698babcadSbeck err(1, "sync init");
44798babcadSbeck }
44898babcadSbeck
449e106bcd2Shenning /* privdrop */
450e7066a17Smestre if ((pw = getpwnam(SPAMD_USER)) == NULL)
451e7066a17Smestre errx(1, "no such user %s", SPAMD_USER);
452e106bcd2Shenning
453e106bcd2Shenning if (setgroups(1, &pw->pw_gid) ||
454e106bcd2Shenning setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
455e106bcd2Shenning setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
456e106bcd2Shenning err(1, "failed to drop privs");
457e106bcd2Shenning
458e106bcd2Shenning if (!flag_debug) {
459e106bcd2Shenning if (daemon(0, 0) == -1)
4601f68c1d4Sbeck err(1, "daemon");
461a574feadSbeck tzset();
462a574feadSbeck openlog_r("spamlogd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);
4637c60fa66Sotto }
4641f68c1d4Sbeck
465222d93a5Smestre if (unveil(PATH_SPAMD_DB, "rw") == -1)
466*bc5a8259Sbeck err(1, "unveil %s", PATH_SPAMD_DB);
467f87e39afSbeck if (syncsend) {
468f87e39afSbeck if (pledge("stdio rpath wpath inet flock", NULL) == -1)
469f87e39afSbeck err(1, "pledge");
470f87e39afSbeck } else {
471f87e39afSbeck if (pledge("stdio rpath wpath flock", NULL) == -1)
472f87e39afSbeck err(1, "pledge");
473f87e39afSbeck }
474f87e39afSbeck
475e106bcd2Shenning pcap_loop(hpcap, -1, phandler, NULL);
476a574feadSbeck
477e106bcd2Shenning logmsg(LOG_NOTICE, "exiting");
478e106bcd2Shenning if (!flag_debug)
479e106bcd2Shenning closelog_r(&sdata);
480a574feadSbeck
4811f68c1d4Sbeck exit(0);
4821f68c1d4Sbeck }
483