[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: