[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

greylisting and mailer pools



Hi,

>From Evan Harris' greylisting whitepaper [1]:

  Issues Affecting The Proposed Implementation
  [...]
  Another issue occurs when a large organization uses a pool of
  outbound mail servers for sending email to a system using
  Greylisting. If the pool is configured so that the same mailserver
  (with the same IP) will always retry deliveries for a particular
  mail, there is no issue.

  But if that pool of mail servers happens to be configured in such a
  way that subsequent delivery attempts for a particular mail may be
  made from any one of several sending MTAs, then we have a
  possibility where legitimate mail deliveries may take significantly
  longer than expected. The possible maximum delay is dependant on the
  number of MTAs in the sending pool, and if the distribution of the
  retry attempts is random or deterministic. In a worst-case scenario,
  it is even possible that mail may be delayed long enough to cause it
  to bounce.

  Other than adding a manual entry for networks of this type, one
  proposed method of dealing with this issue is to perform the IP
  address checks of the sending relay based on the subnet they are at
  rather than the specific IP. Since most of the sites that do this
  have most or all of their email servers on the same /24 subnet, this
  method works well in avoiding this issue without requiring manual
  intervention, at the expense of making it a little easier for
  spammers to circumvent the system.

Below is a patch to spamd and spamlogd that adds support for a subnet
mask option.  The greylisting tuple is then based on the masked IP, as
suggested above.

I sent a similar patch to tech@ some time ago, but the tree was
frozen.  Here is improved (hopefully!) patch.

Thanks,
Matt

1. http://projects.puremagic.com/greylisting/whitepaper.html


Index: libexec/spamd/grey.c
===================================================================
RCS file: /cvs/src/libexec/spamd/grey.c,v
retrieving revision 1.17
diff -u -p -r1.17 grey.c
--- libexec/spamd/grey.c	2004/08/15 21:49:45	1.17
+++ libexec/spamd/grey.c	2004/09/01 01:02:38
@@ -42,6 +42,7 @@
 #include "grey.h"
 
 extern time_t passtime, greyexp, whiteexp;
+extern int gmask;
 extern struct syslog_data sdata;
 extern struct passwd *pw;
 extern pid_t jail_pid;
@@ -126,7 +127,7 @@ configure_pf(char **addrs, int count)
 	}
 	for (i = 0; i < count; i++)
 		if (addrs[i] != NULL)
-			fprintf(pf, "%s/32\n", addrs[i]);
+			fprintf(pf, "%s/%d\n", addrs[i], gmask);
 	fclose(pf);
 	waitpid(pid, NULL, 0);
 	signal(SIGCHLD, sig_term_chld);
@@ -296,15 +297,34 @@ greyupdate(char *dbname, char *ip, char 
 	struct gdata	gd;
 	time_t		now;
 	int		r;
+        char            a[128];
+        in_addr_t       addr, mask;
+        u_char          *p;
 
 	now = time(NULL);
 
+        /* apply gmask to ip */
+        if (gmask < 32) {
+                mask = 0xffffffff << (32 - gmask);
+                addr = ntohl(inet_addr(ip)) & mask;
+                addr = htonl(addr);
+                p = (u_char *) &addr;
+                sprintf(a, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
+                
+                if (debug)
+                        fprintf(stderr,
+                                "Grey IP %s masked to %s\n",
+                                ip, a);
+        } else {
+                strlcpy(a, ip, sizeof(a));
+        }
+
 	/* open with lock, find record, update, close, unlock */
 	memset(&btreeinfo, 0, sizeof(btreeinfo));
 	db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo);
 	if (db == NULL)
 		return(-1);
-	if (asprintf(&key, "%s\n%s\n%s", ip, from, to) == -1)
+	if (asprintf(&key, "%s\n%s\n%s", a, from, to) == -1)
 		goto bad;
 	memset(&dbk, 0, sizeof(dbk));
 	dbk.size = strlen(key);
Index: libexec/spamd/spamd.8
===================================================================
RCS file: /cvs/src/libexec/spamd/spamd.8,v
retrieving revision 1.49
diff -u -p -r1.49 spamd.8
--- libexec/spamd/spamd.8	2004/03/16 09:19:25	1.49
+++ libexec/spamd/spamd.8	2004/09/01 01:02:38
@@ -36,6 +36,7 @@
 .Op Fl b Ar address
 .Op Fl c Ar maxcon
 .Op Fl G Ar passtime:greyexp:whiteexp
+.Op Fl m Ar netmask
 .Op Fl n Ar name
 .Op Fl p Ar port
 .Op Fl r Ar reply
@@ -84,6 +85,10 @@ into the background.
 Adjust the three time parameters for greylisting; see
 .Sx GREYLISTING
 below.
+.It Fl m Ar subnet mask
+Adjust the subnet mask for greylisting; see
+.Sx GREYLISTING
+below.
 .It Fl g
 Greylisting mode; see
 .Sx GREYLISTING
@@ -244,7 +249,9 @@ will use the db file in
 .Pa /var/db/spamd
 to track these non-blacklisted connections to
 .Nm
-by connecting IP address, envelope-from, and envelope-to, or "tuple" for
+by connecting IP address masked by
+.Ar netmask
+(by default 32), envelope-from, and envelope-to, or "tuple" for
 short.
 .Pp
 A previously unseen tuple is added to the
Index: libexec/spamd/spamd.c
===================================================================
RCS file: /cvs/src/libexec/spamd/spamd.c,v
retrieving revision 1.71
diff -u -p -r1.71 spamd.c
--- libexec/spamd/spamd.c	2004/08/17 09:38:07	1.71
+++ libexec/spamd/spamd.c	2004/09/01 01:02:39
@@ -109,6 +109,7 @@ FILE *grey;
 time_t passtime = PASSTIME;
 time_t greyexp = GREYEXP;
 time_t whiteexp = WHITEEXP;
+int gmask = 32;
 struct passwd *pw;
 pid_t jail_pid = -1;
 
@@ -138,7 +139,7 @@ usage(void)
 	fprintf(stderr,
 	    "usage: spamd [-45dgv] [-B maxblack] [-b address] [-c maxcon]\n");
 	fprintf(stderr,
-	    "             [-G mins:hours:hours] [-n name] [-p port]\n");
+	    "             [-G mins:hours:hours] [-m netmask] [-n name] [-p port]\n");
 	fprintf(stderr,
 	    "             [-r reply] [-s secs] [-w window]\n");
 	exit(1);
@@ -915,7 +916,7 @@ main(int argc, char *argv[])
 	if (gethostname(hostname, sizeof hostname) == -1)
 		err(1, "gethostname");
 
-	while ((ch = getopt(argc, argv, "45b:c:B:p:dgG:r:s:n:vw:")) != -1) {
+	while ((ch = getopt(argc, argv, "45b:c:B:p:dgG:m:r:s:n:vw:")) != -1) {
 		switch (ch) {
 		case '4':
 			nreply = "450";
@@ -957,6 +958,10 @@ main(int argc, char *argv[])
 			/* convert to seconds from hours */
 			greyexp *= (60 * 60);
 			break;
+                case 'm':
+                        if (sscanf(optarg, "%d", &gmask) != 1)
+                                usage();
+                        break;
 		case 'r':
 			reply = optarg;
 			break;
Index: libexec/spamlogd/spamlogd.c
===================================================================
RCS file: /cvs/src/libexec/spamlogd/spamlogd.c,v
retrieving revision 1.9
diff -u -p -r1.9 spamlogd.c
--- libexec/spamlogd/spamlogd.c	2004/08/10 16:06:01	1.9
+++ libexec/spamlogd/spamlogd.c	2004/09/01 01:02:39
@@ -36,6 +36,7 @@
 
 struct syslog_data sdata = SYSLOG_DATA_INIT;
 int inbound; /* do we only whitelist inbound smtp? */
+int gmask = 32;
 
 extern char *__progname;
 
@@ -49,19 +50,32 @@ dbupdate(char *dbname, char *ip)
 	time_t		now;
 	int		r;
 	struct in_addr	ia;
+        char            a[128];
+        in_addr_t       addr, mask;
+        u_char          *p;
+
+        if (gmask < 32) {
+                mask = 0xffffffff << (32 - gmask);
+                addr = ntohl(inet_addr(ip)) & mask;
+                addr = htonl(addr);
+                p = (u_char *) &addr;
+                sprintf(a, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
+        } else {
+                strlcpy(a, ip, sizeof(a));
+        }
 
 	now = time(NULL);
 	memset(&btreeinfo, 0, sizeof(btreeinfo));
 	db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_BTREE, &btreeinfo);
 	if (db == NULL)
 		return(-1);
-	if (inet_pton(AF_INET, ip, &ia) != 1) {
-		syslog_r(LOG_NOTICE, &sdata, "invalid ip address %s", ip);
+	if (inet_pton(AF_INET, a, &ia) != 1) {
+		syslog_r(LOG_NOTICE, &sdata, "invalid ip address %s", a);
 		goto bad;
 	}
 	memset(&dbk, 0, sizeof(dbk));
-	dbk.size = strlen(ip);
-	dbk.data = ip;
+	dbk.size = strlen(a);
+	dbk.data = a;
 	memset(&dbd, 0, sizeof(dbd));
 	/* add or update whitelist entry */
 	r = db->get(db, &dbk, &dbd, 0);
@@ -77,8 +91,8 @@ dbupdate(char *dbname, char *ip)
 		gd.pass = now;
 		gd.expire = now + WHITEEXP;
 		memset(&dbk, 0, sizeof(dbk));
-		dbk.size = strlen(ip);
-		dbk.data = ip;
+		dbk.size = strlen(a);
+		dbk.data = a;
 		memset(&dbd, 0, sizeof(dbd));
 		dbd.size = sizeof(gd);
 		dbd.data = &gd;
@@ -97,8 +111,8 @@ dbupdate(char *dbname, char *ip)
 		gd.pcount++;
 		gd.expire = now + WHITEEXP;
 		memset(&dbk, 0, sizeof(dbk));
-		dbk.size = strlen(ip);
-		dbk.data = ip;
+		dbk.size = strlen(a);
+		dbk.data = a;
 		memset(&dbd, 0, sizeof(dbd));
 		dbd.size = sizeof(gd);
 		dbd.data = &gd;
@@ -120,7 +134,7 @@ dbupdate(char *dbname, char *ip)
 static int
 usage(void)
 {
-	fprintf(stderr, "usage: %s [-I] [-i interface]\n", __progname);
+	fprintf(stderr, "usage: %s [-I] [-i interface] [-m netmask]\n", __progname);
 	exit(1);
 }
 
@@ -140,7 +154,7 @@ main(int argc, char **argv)
 	FILE *f;
 
 
-	while ((ch = getopt(argc, argv, "i:I")) != -1) {
+	while ((ch = getopt(argc, argv, "i:Im:")) != -1) {
 		switch (ch) {
 		case 'i':
 			if (targv[15])	/* may only set once */
@@ -152,6 +166,10 @@ main(int argc, char **argv)
 		case 'I':
 			inbound = 1;
 			break;
+                case 'm':
+                        if (sscanf(optarg, "%d", &gmask) != 1)
+                                usage();
+                        break;
 		default:
 			usage();
 			break;



Visit your host, monkey.org