Hello folks,
Here's a suggested improvement to spamlogd(8) which keeps greytrap entries
tarpitted while they keep trying.
To this end I modified spamlogd.c so that a known greytrapped host is
updated as a greytrap entry in /var/db/spamd on every incoming connection
to
port 8025. This requires a pf(4) rule that logs incoming connections to
this
port.
In spamd(8)'s default mode, also greylisted hosts connect to this port, so
we
have to look in the database and not interfere with the greylisting
process.
In spamd(8)'s blacklist-only mode, this idea could be used to add/update
greytrap entries for all blacklisted hosts (so also those from
spamd.conf(5)).
However, these blacklists often contain false positives and legitimate
hosts
that are blacklisted for a short period, so this is probably not a good
idea.
Thoughts?
$ diff -u spamlogd.c{.54,}
--- spamlogd.c.54 Wed Aug 21 18:13:30 2013
+++ spamlogd.c Thu Aug 29 13:30:58 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,27 +255,29 @@
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 {
- /* XXX - backwards compat */
- if (gdcopyin(&dbd, &gd) == -1) {
+ if (dbd.size != sizeof(gd)) {
/* whatever this is, it doesn't belong */
db->del(db, &dbk, 0);
goto bad;
}
- gd.pcount++;
- gd.expire = now + whiteexp;
+ memcpy(&gd, dbd.data, sizeof(gd));
+ 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 +292,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 +310,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 +327,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 +347,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) */
$ diff -u spamlogd.8{.head,}
--- spamlogd.8.head Fri Mar 4 21:01:49 2011
+++ spamlogd.8 Thu Aug 29 12:20:51 2013
@@ -25,6 +25,7 @@
.Op Fl DI
.Op Fl i Ar interface
.Op Fl l Ar pflog_interface
+.Op Fl T Ar trapexp
.Op Fl W Ar whiteexp
.Op Fl Y Ar synctarget
.Sh DESCRIPTION
@@ -40,13 +41,14 @@
whitelist entries whenever a connection
to port 25 is logged to the
.Xr pflog 4
-interface.
-The source addresses of inbound connections are whitelisted
+interface, and existing trapped entries whenever a connection
+to port 8025 is logged.
+The source addresses of inbound connections are added or updated
when seen by
.Nm
to ensure that their entries in
.Pa /var/db/spamd
-do not expire if the connecting host continues to send legitimate mail.
+do not expire if the connecting host continues to send mail.
The destination addresses of outbound connections are whitelisted
when seen by
.Nm
@@ -78,6 +80,12 @@
interface to listen for connection notifications.
The default is to watch for connections logged on
.Dq pflog0 .
+.It Fl T Ar trapexp
+Adjust the time for
+.Ar trapexp
+in hours.
+The default is 24 hours (one day); maximum is 2160 hours
+(approximately 90 days).
.It Fl W Ar whiteexp
Adjust the time for
.Ar whiteexp
--
Gemaakt met Opera's revolutionaire e-mailprogramma:
http://www.opera.com/mail/
(Remove the obvious prefix to reply privately.)