[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
greylisting and mailer pools
- To: tech_(_at_)_openbsd_(_dot_)_org
- Subject: greylisting and mailer pools
- From: Matthew Emmett <matt_(_at_)_emmett_(_dot_)_ca>
- Date: Mon, 04 Oct 2004 16:48:17 -0600
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