Op Thu, 29 Aug 2013 14:04:59 +0200 schreef Boudewijn Dijkstra
<[email protected]>:
Here's a suggested improvement to spamlogd(8) which keeps greytrap
entries tarpitted while they keep trying. [...]
Because at least one person expressed an interest in my modification, find
below an updated patch that fixes a subtle bug. The previous version could
accidentally trap hosts that were just whitelisted but not yet added in
the pf table <spamd-white>. The version below leaves these entries alone.
--- spamlogd.c.54 Fri Mar 18 23:37:06 2011
+++ spamlogd.c Mon Sep 9 10:52:51 2013
@@ -21,7 +21,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* watch pf log for mail connections, update whitelist entries. */
+/* watch pf log for mail connections, update spamdb entries. */
#include <sys/types.h>
#include <sys/socket.h>
@@ -33,6 +33,7 @@
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <net/pfvar.h>
@@ -64,6 +65,7 @@
int greylist = 1;
FILE *grey = NULL;
+u_short spamd_port;
u_short sync_port;
int syncsend;
u_int8_t flag_debug = 0;
@@ -74,13 +76,14 @@
pcap_t *hpcap = NULL;
struct syslog_data sdata = SYSLOG_DATA_INIT;
time_t whiteexp = WHITEEXP;
+time_t trapexp = TRAPEXP;
extern char *__progname;
void logmsg(int , const char *, ...);
void sighandler_close(int);
int init_pcap(void);
void logpkt_handler(u_char *, const struct pcap_pkthdr *, const u_char *);
-int dbupdate(char *, char *);
+int dbupdate(char *, char *, int);
void usage(void);
void
@@ -110,9 +113,11 @@
init_pcap(void)
{
struct bpf_program bpfp;
- char filter[PCAPFSIZ] = "ip and port 25 and action pass "
- "and tcp[13]&0x12=0x2";
+ char filter[PCAPFSIZ];
+ snprintf(filter, PCAPFSIZ, "ip and (port 25 or %d) and action pass "
+ "and tcp[13]&0x12=0x2", spamd_port);
+
if ((hpcap = pcap_open_live(pflogif, PCAPSNAP, 1, PCAPTIMO,
errbuf)) == NULL) {
logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
@@ -157,6 +162,11 @@
const struct ip *ip = NULL;
const struct pfloghdr *hdr;
char ipstraddr[40] = { '\0' };
+ int white = 1;
+ unsigned int off;
+ const struct tcphdr *tcp;
+ unsigned int iplen;
+ unsigned int port;
hdr = (const struct pfloghdr *)sp;
if (hdr->length < MIN_PFLOG_HDRLEN) {
@@ -185,26 +195,34 @@
else if (hdr->dir == PF_OUT && !flag_inbound)
inet_ntop(af, &ip->ip_dst, ipstraddr,
sizeof(ipstraddr));
+ off = ntohs(ip->ip_off);
+ if ((off & 0x1fff) == 0) {
+ iplen = ip->ip_hl * 4;
+ tcp = (const struct tcphdr *)(sp + hdrlen + iplen);
+ port = ntohs(tcp->th_dport);
+ if (port == spamd_port)
+ white = 0;
+ }
}
if (ipstraddr[0] != '\0') {
- if (hdr->dir == PF_IN)
- logmsg(LOG_DEBUG,"inbound %s", ipstraddr);
- else
- logmsg(LOG_DEBUG,"outbound %s", ipstraddr);
- dbupdate(PATH_SPAMD_DB, ipstraddr);
+ logmsg(LOG_DEBUG, "%s %s %s",
+ hdr->dir == PF_IN ? "inbound" : "outbound",
+ white ? "white" : "spamd",
+ ipstraddr);
+ dbupdate(PATH_SPAMD_DB, ipstraddr, white);
}
}
int
-dbupdate(char *dbname, char *ip)
+dbupdate(char *dbname, char *ip, int white)
{
HASHINFO hashinfo;
DBT dbk, dbd;
DB *db;
struct gdata gd;
time_t now;
- int r;
+ int r, mod;
struct in_addr ia;
now = time(NULL);
@@ -224,7 +242,7 @@
dbk.data = ip;
memset(&dbd, 0, sizeof(dbd));
- /* add or update whitelist entry */
+ /* add or update entry */
r = db->get(db, &dbk, &dbd, 0);
if (r == -1) {
logmsg(LOG_NOTICE, "db->get failed (%m)");
@@ -237,18 +255,11 @@
gd.first = now;
gd.bcount = 1;
gd.pass = now;
- gd.expire = now + whiteexp;
- memset(&dbk, 0, sizeof(dbk));
- dbk.size = strlen(ip);
- dbk.data = ip;
- memset(&dbd, 0, sizeof(dbd));
- dbd.size = sizeof(gd);
- dbd.data = &gd;
- r = db->put(db, &dbk, &dbd, 0);
- if (r) {
- logmsg(LOG_NOTICE, "db->put failed (%m)");
- goto bad;
- }
+ if (white) {
+ gd.expire = now + whiteexp;
+ mod = 1;
+ } else
+ mod = 0;
} else {
if (dbd.size != sizeof(gd)) {
/* whatever this is, it doesn't belong */
@@ -256,8 +267,22 @@
goto bad;
}
memcpy(&gd, dbd.data, sizeof(gd));
- gd.pcount++;
- gd.expire = now + whiteexp;
+ /* leave existing WHITE entries alone */
+ if (gd.pcount >= 0)
+ mod = 0;
+ else {
+ if (white) {
+ gd.expire = now + whiteexp;
+ gd.pcount++;
+ } else {
+ gd.expire = now + trapexp;
+ gd.bcount++;
+ gd.pcount = -1;
+ }
+ mod = 1;
+ }
+ }
+ if (mod) {
memset(&dbk, 0, sizeof(dbk));
dbk.size = strlen(ip);
dbk.data = ip;
@@ -272,8 +297,12 @@
}
db->close(db);
db = NULL;
- if (syncsend)
- sync_white(now, now + whiteexp, ip);
+ if (mod && syncsend) {
+ if (white)
+ sync_white(now, now + trapexp, ip);
+ else
+ sync_trapped(now, now + trapexp, ip);
+ }
return (0);
bad:
db->close(db);
@@ -286,7 +315,7 @@
{
fprintf(stderr,
"usage: %s [-DI] [-i interface] [-l pflog_interface] "
- "[-W whiteexp] [-Y synctarget]\n",
+ "[-W whiteexp] [-T trapexp] [-Y synctarget]\n",
__progname);
exit(1);
}
@@ -303,11 +332,14 @@
char *sync_baddr = NULL;
const char *errstr;
+ if ((ent = getservbyname("spamd", "tcp")) == NULL)
+ errx(1, "Can't find service \"spamd\" in /etc/services");
+ spamd_port = ntohs(ent->s_port);
if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
errx(1, "Can't find service \"spamd-sync\" in /etc/services");
sync_port = ntohs(ent->s_port);
- while ((ch = getopt(argc, argv, "DIi:l:W:Y:")) != -1) {
+ while ((ch = getopt(argc, argv, "DIi:l:T:W:Y:")) != -1) {
switch (ch) {
case 'D':
flag_debug = 1;
@@ -320,6 +352,14 @@
break;
case 'l':
pflogif = optarg;
+ break;
+ case 'T':
+ /* limit trapexp to 2160 hours (90 days) */
+ trapexp = strtonum(optarg, 1, (24 * 90), &errstr);
+ if (errstr)
+ usage();
+ /* convert to seconds from hours */
+ trapexp *= (60 * 60);
break;
case 'W':
/* limit whiteexp to 2160 hours (90 days) */
P.S. the sig in my previous message didn't instruct people to remove the
obvious prefix to reply privately. My apologies.
--
(Remove the obvious prefix to reply privately.)
Gemaakt met Opera's e-mailprogramma: http://www.opera.com/mail/