[Prev][Next][Index][Thread]
Yet another anti-spam patch...
Hello everyone!
On behalf on my manager, I spent a decent part of today poking around the
internals of qmail-smtpd.c.
So, you're asking yourself, what's new about this patch?
Not a whole lot, but a few details:
* Defaults DENYMAIL to "DNSCHECK" for those of us running
tcpd/twist.
* Turns off the super-silly millisecond?-based date/time stamp
within splogger(syslog replicates this much more understandably).
Of course if you're running a large site, you may want to
know what happened down to the millisecond...
* Slightly modified/prettier DENYMAIL logging.
Many thanks to the originators of DENYMAIL, hopefully it'll be helping
onShore soon, full-time.
Just FYI, here's what's in my configuration files:
inetd.conf:
smtp stream tcp nowait qmaild /usr/sbin/tcpd
/var/qmail/bin/qmail-smtpd
(Since the 'twist' feature of tcpd ditches all of our env. variables
anyway, why not get rid of the overhead of tcp-env?)
hosts.allow:
qmail-smtpd: ALL: twist { { /var/qmail/bin/qmail-smtpd 1>&3; } 2>&1 |
/var/qmail/bin/splogger qmail; } 3>&1
Have fun, and feel free to let me know if anyone comes up with a better
way to do this, short of revamping all of qmail to use syslog()
directly...
...Adam W. Dace <adamd@xxxxxxxxxxx>...onShore Inc...http://www.onShore.com/...
P.S. This patch is for qmail-1.01.
diff -ur qmail-1.01-original/Makefile qmail-1.01+antispam/Makefile
--- qmail-1.01-original/Makefile Tue Apr 15 00:05:23 1997
+++ qmail-1.01+antispam/Makefile Tue Jun 3 11:05:24 1997
@@ -1522,12 +1522,12 @@
load qmail-smtpd.o ip.o ipme.o ipalloc.o control.o constmap.o \
received.o date822fmt.o now.o qmail.o fd.a wait.a datetime.a open.a \
getln.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a \
-str.a fs.a auto_qmail.o socket.lib
+str.a fs.a auto_qmail.o dns.o dns.lib socket.lib
./load qmail-smtpd ip.o ipme.o ipalloc.o control.o \
constmap.o received.o date822fmt.o now.o qmail.o fd.a \
wait.a datetime.a open.a getln.a sig.a case.a env.a \
stralloc.a alloc.a substdio.a error.a str.a fs.a \
- auto_qmail.o `cat socket.lib`
+ auto_qmail.o dns.o `cat dns.lib` `cat socket.lib`
qmail-smtpd.0: \
qmail-smtpd.8
diff -ur qmail-1.01-original/qmail-smtpd.c qmail-1.01+antispam/qmail-smtpd.c
--- qmail-1.01-original/qmail-smtpd.c Tue Apr 15 00:05:23 1997
+++ qmail-1.01+antispam/qmail-smtpd.c Tue Jun 3 14:44:47 1997
@@ -1,8 +1,18 @@
+/* Begin anti-spam #defines */
+#define BADRCPT 0
+#define XLOGGING 1
+#define DENYSPAM 1
+#define MAILFROMCHECK 1
+#define MAILFROMDNS 1
+/* End anti-spam #defines */
#include "sig.h"
#include "readwrite.h"
#include "getln.h"
#include "stralloc.h"
#include "substdio.h"
+#ifdef XLOGGING
+#include "subfd.h"
+#endif
#include "alloc.h"
#include "auto_qmail.h"
#include "control.h"
@@ -19,6 +29,9 @@
#include "env.h"
#include "now.h"
#include "exit.h"
+#ifdef MAILFROMDNS
+#include "dns.h"
+#endif
#define MAXHOPS 100
int timeout = 1200;
@@ -58,6 +71,11 @@
stralloc bmf = {0};
struct constmap mapbmf;
int flagbarf; /* defined if seenmail */
+#ifdef BADRCPT
+int brtok = 0;
+stralloc brt = {0};
+struct constmap mapbadrcptto;
+#endif
stralloc helohost = {0};
stralloc mailfrom = {0};
@@ -71,6 +89,9 @@
char *remoteinfo;
char *local;
char *relayclient;
+#ifdef DENYSPAM
+char *denymail;
+#endif
void dohelo(arg) char *arg;
{
@@ -90,6 +111,12 @@
remoteinfo = env_get("TCPREMOTEINFO");
relayclient = env_get("RELAYCLIENT");
dohelo(remotehost);
+#ifdef DENYSPAM
+ denymail = env_get("DENYMAIL");
+ /* If for some reason the environment variable isn't set,
+ * default to DNS checking. */
+ if (!denymail) denymail = "DNSCHECK";
+#endif
}
void straynewline()
@@ -230,9 +257,121 @@
return 1;
}
+#ifdef XLOGGING
+static int issafe(ch) char ch;
+{
+ if (ch == ' ') return 1;
+ if (ch == '.') return 1;
+ if (ch == '@') return 1;
+ if (ch == '%') return 1;
+ if (ch == '+') return 1;
+ if (ch == '/') return 1;
+ if (ch == '=') return 1;
+ if (ch == ':') return 1;
+ if (ch == '-') return 1;
+ if ((ch >= 'a') && (ch <= 'z')) return 1;
+ if ((ch >= 'A') && (ch <= 'Z')) return 1;
+ if ((ch >= '0') && (ch <= '9')) return 1;
+ return 0;
+
+ if (ch < 33) return 0;
+ if (ch > 126) return 0;
+ return 1;
+}
+
+static void safeErr(s) char *s;
+{
+ char ch;
+ while (ch = *s++) {
+ if (!issafe(ch)) ch = '?';
+ substdio_bput(subfderr, &ch, 1);
+ }
+}
+static void log_deny(m,f,t) char *m,*f,*t;
+{
+ substdio_bputs(subfderr, "smtpd: "); safeErr(m);
+ substdio_bputs(subfderr, " check failed\nsmtpd: FROM: <"); safeErr(f);
+ substdio_bputs(subfderr, "> TO: <"); safeErr(t);
+ substdio_bputs(subfderr, "> HELO Hostname: ["); safeErr(helohost.s);
+ substdio_bputs(subfderr, "]\n");
+ substdio_flush(subfderr);
+}
+#ifdef BADRCPT
+static void log_brt(s,r) char *s,*r;
+{
+/*
+ substdio_bputs(subfderr, "BRT: check failed (");
+ safeErr(s);
+ substdio_bputs(subfderr, ")\n");
+ substdio_flush(subfderr);
+*/
+ log_deny("BRT:", s, r);
+}
+#endif
+static void log_bmf(s,r) char *s,*r;
+{
+/*
+ substdio_bputs(subfderr, "BMF: check failed (");
+ safeErr(mailfrom.s);
+ substdio_bputs(subfderr, ")\n");
+ substdio_flush(subfderr);
+*/
+ log_deny("BMF:", s, r);
+}
+static void log_helo()
+{
+ substdio_bputs(subfderr, "Received: from ");
+ safeErr(remotehost);
+ substdio_bputs(subfderr, " (HELO ");
+ safeErr(helohost.s);
+ substdio_bputs(subfderr, ")\n");
+ substdio_flush(subfderr);
+}
+
+#else /* not XLOGGING */
+#define log_brt(s,r)
+#define log_bmf(s,r)
+#define log_deny(m,f,t)
+#define log_helo()
+#endif /* not XLOGGING */
+
+#ifdef MAILFROMDNS
+int badmxcheck(dom) char *dom;
+{
+ ipalloc checkip = {0};
+ int ret=0;
+ stralloc checkhost = {0};
+
+ if (!*dom) return (DNS_HARD);
+ if (!stralloc_copys(&checkhost,dom)) return (DNS_SOFT);
+
+ switch (dns_mxip(&checkip,&checkhost,1))
+ {
+ case DNS_MEM:
+ case DNS_SOFT:
+ ret=DNS_SOFT;
+ break;
+
+ case DNS_HARD:
+ ret=DNS_HARD;
+ break;
+ case 1:
+ if (checkip.len <= 0) ret=DNS_HARD;
+ break;
+ }
+
+ return (ret);
+}
+#endif
+
int addrallowed()
{
int j;
+#ifdef BADRCPT
+ if (brtok)
+ if (constmap(&mapbadrcptto, addr.s, addr.len - 1))
+ {log_brt(mailfrom.s,addr.s); return 0;}
+#endif
if (!rhok) return 1;
j = byte_rchr(addr.s,addr.len,'@');
if (j >= addr.len) return 1; /* can be taken care of by envnoathost */
@@ -251,7 +390,15 @@
if (constmap(&mapbmf,addr.s,addr.len - 1)) { flagbarf = 1; return; }
j = byte_rchr(addr.s,addr.len,'@');
if (j < addr.len)
- if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) flagbarf = 1;
+#ifdef DENYSPAM
+ {
+#endif
+ if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) { flagbarf = 1; return; }
+#ifdef DENYSPAM
+ /* allow for bulkemailer@ checking */
+ if (constmap(&mapbmf,addr.s, j + 1)) { flagbarf = 1; return; }
+ }
+#endif
}
void smtp_greet(code) char *code; {
@@ -260,7 +407,11 @@
void smtp_quit() { smtp_greet("221 "); out("\r\n"); die(); }
void smtp_help() { out("214-qmail home page: http://pobox.com/~djb/qmail.html\r\n214 send comments to qmail@xxxxxxxxx\r\n"); }
void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+/* zzz
+ void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+*/
+void err_bmf() { out("553 syntax error, please forward to your postmaster (#5.7.1)\r\n"); }
+void err_dns() { out("451 DNS temporary failure (#4.3.0)\r\n"); }
void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
void err_seenmail() { out("503 one MAIL per message (#5.5.1)\r\n"); }
@@ -286,10 +437,11 @@
if (!stralloc_copys(&mailfrom,addr.s)) outofmem();
if (!stralloc_0(&mailfrom)) outofmem(); }
void smtp_rcpt(arg) char *arg; {
+int i,j; char *why;
if (!seenmail) { err_wantmail(); return; }
if (!arg) { err_syntax(); return; }
if (!addrparse(arg)) { err_syntax(); return; }
- if (flagbarf) { err_bmf(); return; }
+ if (flagbarf) { err_bmf(); log_bmf(mailfrom.s,addr.s); return; }
if (relayclient)
{
--addr.len;
@@ -298,10 +450,86 @@
}
else
if (!addrallowed()) { err_nogateway(); return; }
+#ifdef DENYSPAM
+/************
+ DENYMAIL is set for this session from this client,
+ so heavy checking of mailfrom
+ SPAM -> refuse all mail
+ NOBOUNCE -> refuse null mailfrom
+ DNSCHECK -> validate Mailfrom domain
+************/
+
+ if (denymail)
+ {
+ why = denymail;
+
+ if (!str_diff("SPAM", denymail))
+ flagbarf=1;
+ else
+ if (!mailfrom.s[0] || !str_diff("#@[]", mailfrom.s)) /*mjr*/
+ /* if (!mailfrom.s[0]) */
+ {
+ if (!str_diff("NOBOUNCE", denymail))
+ flagbarf=1;
+ }
+#ifdef MAILFROMCHECK
+ else
+ {
+ why = "MAIL FROM: Syntax";
+ if ((i=byte_chr(mailfrom.s,mailfrom.len,'@')) >= mailfrom.len)
+ flagbarf=1; /* no '@' in from */
+ else
+ {
+ /* money!@xxxxxxxxxx */
+ if (mailfrom.s[i-1] == '!')
+ flagbarf=1;
+
+ /* check syntax, visual */
+ if ((j = byte_rchr(mailfrom.s+i, mailfrom.len-i, '.')) >= mailfrom.len-i)
+ flagbarf=1; /* curious no '.' in domain.TLD */
+
+ j = mailfrom.len-(i+1+j+1);
+ if (j < 2 || j > 3)
+ flagbarf=1; /* root domain, not a country (2), nor TLD (3)*/
+
+#ifdef MAILFROMDNS
+ if (!flagbarf)
+ if (!str_diff("DNSCHECK", denymail))
+ {
+ /* check syntax, via DNS */
+ why = "MAIL FROM: DNS";
+ switch (badmxcheck(&mailfrom.s[i+1]))
+ {
+ case 0: break; /*valid*/
+ case DNS_SOFT: flagbarf=2; /*fail tmp*/
+ why = "MAIL FROM: DNS";
+ break;
+ case DNS_HARD: flagbarf=1;
+ break;
+ }
+ }
+#endif
+ }
+ }
+#endif
+
+logit:
+ if (flagbarf)
+ {
+ log_deny(why, mailfrom.s, addr.s);
+ if (2==flagbarf)
+ err_dns();
+ else
+ err_bmf();
+ return;
+ }
+ }/* denymail */
+#endif
out("250 ok\r\n");
if (!stralloc_cats(&rcptto,"T")) outofmem();
if (!stralloc_cats(&rcptto,addr.s)) outofmem();
- if (!stralloc_0(&rcptto)) outofmem(); }
+ if (!stralloc_0(&rcptto)) outofmem();
+}
char accept_buf[FMT_ULONG];
void acceptmessage(qp) unsigned long qp;
@@ -326,7 +554,8 @@
qp = qmail_qp(&qqt);
out("354 go ahead\r\n");
- received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0);
+ received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,(r=case_diffs(remotehost,helohost.s)) ? helohost.s : 0);
+ if (r) log_helo();
blast(&ssin,&hops);
hops = (hops >= MAXHOPS);
if (hops) qmail_fail(&qqt);
@@ -416,6 +645,15 @@
bmfok = 1;
if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die();
}
+#ifdef BADRCPT
+ switch(control_readfile(&brt,"control/badrcptto",0))
+ {
+ case -1: die();
+ case 1:
+ brtok = 1;
+ if (!constmap_init(&mapbadrcptto,brt.s,brt.len,0)) die();
+ }
+#endif
}
void main()
diff -ur qmail-1.01-original/splogger.c qmail-1.01+antispam/splogger.c
--- qmail-1.01-original/splogger.c Tue Apr 15 00:05:23 1997
+++ qmail-1.01+antispam/splogger.c Tue Jun 3 12:33:39 1997
@@ -32,13 +32,13 @@
if (bufpos) {
buf[bufpos] = 0;
if (flagcont)
- syslog(priority,"%s+%s",stamp,buf); /* logger folds invisibly; GPACIC */
+ syslog(priority,"%s",buf); /* logger folds invisibly; GPACIC */
else {
stamp_make();
priority = LOG_INFO;
if (str_start(buf,"warning:")) priority = LOG_WARNING;
if (str_start(buf,"alert:")) priority = LOG_ALERT;
- syslog(priority,"%s %s",stamp,buf);
+ syslog(priority,"%s",buf);
flagcont = 1;
}
}
Follow-Ups: