diff -Naur qmail-1.03/Makefile qmail-1.03.patched/Makefile --- qmail-1.03/Makefile 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03.patched/Makefile 2002-12-12 13:44:07.168951000 -0500 @@ -1542,7 +1542,7 @@ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.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` + socket.lib` dns.o `cat dns.lib` qmail-smtpd.0: \ qmail-smtpd.8 diff -Naur qmail-1.03/qmail-smtpd.c qmail-1.03.patched/qmail-smtpd.c --- qmail-1.03/qmail-smtpd.c 1998-06-15 06:53:16.000000000 -0400 +++ qmail-1.03.patched/qmail-smtpd.c 2002-12-12 14:43:40.794130000 -0500 @@ -23,6 +23,7 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "dns.h" #define MAXHOPS 100 unsigned int databytes = 0; @@ -36,11 +37,16 @@ return r; } -char ssoutbuf[512]; +char ssoutbuf[512], sserrbuf[512]; substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); +substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf, sizeof sserrbuf); void flush() { substdio_flush(&ssout); } +void flusherr() { substdio_flush(&sserr); } void out(s) char *s; { substdio_puts(&ssout,s); } +void err(s) char *s; { substdio_puts(&sserr,s); } + +stralloc mailfrom = {0}; void die_read() { _exit(1); } void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } @@ -49,8 +55,6 @@ void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } -void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\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_syntax() { out("555 syntax error (#5.5.4)\r\n"); } void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } @@ -58,7 +62,79 @@ void err_noop() { out("250 ok\r\n"); } void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +void err_smf() { out("451 DNS temporary failure (#4.3.0)\r\n"); } + +char *remoteip; +char *remotehost; +char *remoteinfo; +char *local; +char *relayclient; +char *nodnscheck; + +void err_prefix(stralloc *from) { + err("qmail-smtp: "); + err(remoteip); + err("="); + err(remotehost); + if (from->len) { + err("; "); + err(from->s); + } + err(", "); +} + +void err_suffix() { + err("\n"); + flusherr(); +} + +void err_bmf(stralloc *from) { + err_prefix(from); + err("On badmailfrom"); + err_suffix(); + out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); +} + +void err_nogateway(stralloc *from, stralloc *to) { + err_prefix(from); + err("Not in rcpthost: "); + err(to->s); + err_suffix(); + out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); +} + +void err_relay(stralloc *from, stralloc *to) { + err_prefix(from); + err("Tried to Relay: "); + err(to->s); + err_suffix(); + out("553 we don't relay (#5.7.1)\r\n"); +} + +stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ +void err_dnsbl(char *dnsbl, stralloc *dnsblrc) { + stralloc msg = {0}; + stralloc_copys(&msg, "553 Blacklisted at "); + stralloc_cats(&msg, dnsbl); + stralloc_catb(&msg, "\r\n", 3); + out(msg.s); + msg.len -= 3; + stralloc_catb(&msg, "=", 1); + stralloc_cats(&msg, dnsblrc->s); + stralloc_0(&msg); + err_prefix(&addr); + err(msg.s + 4); + err_suffix(); +} + +void err_nomx() +{ + err_prefix(&addr); + err("No MX"); + err_suffix(); + out("553 No DNS MX record found\r\n"); +} stralloc greeting = {0}; @@ -71,22 +147,17 @@ { out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); } + void smtp_quit() { smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } -char *remoteip; -char *remotehost; -char *remoteinfo; -char *local; -char *relayclient; - stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ void dohelo(arg) char *arg; { - if (!stralloc_copys(&helohost,arg)) die_nomem(); + if (!stralloc_copys(&helohost,arg)) die_nomem(); if (!stralloc_0(&helohost)) die_nomem(); fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } @@ -96,6 +167,12 @@ int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; +int rdblok = 0; +struct constmap maprdbl; +stralloc rdbl = {0}; +int dwlok = 0; +struct constmap mapdwl; +stralloc dwl = {0}; void setup() { @@ -131,12 +208,21 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + nodnscheck = env_get("NODNSCHECK"); + if (!nodnscheck) { + rdblok = control_readfile(&rdbl, "control/remotedomainblacklist", 0); + if (rdblok == -1) die_control(); + if (rdblok) + if (!constmap_init(&maprdbl, rdbl.s, rdbl.len, 0)) die_nomem(); + dwlok = control_readfile(&dwl, "control/nodnscheck", 0); + if (dwlok == -1) die_control(); + if (dwlok) + if (!constmap_init(&mapdwl, dwl.s, dwl.len, 0)) die_nomem(); + } dohelo(remotehost); } -stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ - int addrparse(arg) char *arg; { @@ -216,10 +302,20 @@ return r; } +int addrrelay() +{ + char *s; + + for (s = addr.s + addr.len; s-- > addr.s;) + if (*s == '@') break; + while(s-- > addr.s) + if (*s == '@' || *s == '%' || *s == '!') return 1; + return 0; +} + int seenmail = 0; int flagbarf; /* defined if seenmail */ -stralloc mailfrom = {0}; stralloc rcptto = {0}; void smtp_helo(arg) char *arg; @@ -237,9 +333,63 @@ seenmail = 0; out("250 flushed\r\n"); } + +int dnscheck(stralloc *to) +{ + int i, j; + unsigned int random; + stralloc sa = {0}; + ipalloc ia = {0}; + + dns_init(0); + + if ((i = byte_rchr(to->s, to->len, '@')) < to->len) { + char *le, *ele; + i++; + /* First make sure it's not on the domain exclusion list */ + if (dwlok) { + stralloc_copys(&sa, to->s + i); + stralloc_0(&sa); + for (ele = (le = dwl.s) + dwl.len; le < ele; le += str_len(le) + 1) + if (!case_diffs(sa.s, le)) return 1; + } + /* Look it up on any remote blacklist */ + if (rdblok) { + for (ele = (le = rdbl.s) + rdbl.len; le < ele;) { + /* First check to see if domain is on blacklist */ + stralloc_copys(&sa, to->s + i); + stralloc_cats(&sa, ".", 1); + stralloc_cats(&sa, le); + stralloc_0(&sa); + j = dns_ip(&ia, &sa); + if (j == DNS_MEM) die_nomem(); + if (j == DNS_SOFT) { err_smf(); return 0; } + if (j != DNS_HARD) { + /* Found it in the blacklist */ + char tmp[IPFMT]; + stralloc_copyb(&sa, tmp, ip_fmt(tmp, &ia.ix[0].ip)); + stralloc_0(&sa); + err_dnsbl(le, &sa); + return 0; + } + le += str_len(le) + 1; + } + } + /* Now check for MX record */ + random = now() + (getpid() << 16); + stralloc_copys(&sa, addr.s + i); + j = dns_mxip(&ia, &sa, random); + if (j == DNS_MEM) die_nomem(); + if (j == DNS_SOFT) { err_smf(); return 0; } + if (j == DNS_HARD) { err_nomx(); return 0; } + } + return 1; +} + void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } + if (!nodnscheck && !dnscheck(&addr)) return; flagbarf = bmfcheck(); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); @@ -247,17 +397,19 @@ if (!stralloc_0(&mailfrom)) die_nomem(); out("250 ok\r\n"); } + void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + if (addrrelay()) { err_relay(&mailfrom, &addr); return; } + if (flagbarf) { err_bmf(&mailfrom); return; } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } else - if (!addrallowed()) { err_nogateway(); return; } + if (!addrallowed()) { err_nogateway(&mailfrom, &addr); return; } if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); diff -Naur qmail-1.03/qmailDomainChecks.txt qmail-1.03.patched/qmailDomainChecks.txt --- qmail-1.03/qmailDomainChecks.txt 1969-12-31 19:00:00.000000000 -0500 +++ qmail-1.03.patched/qmailDomainChecks.txt 2002-12-12 15:30:45.314624000 -0500 @@ -0,0 +1,50 @@ +README for qmail patches created by Gary Gendel + +I started this project because I wanted to utilize the domain-based +blacklists from rfc-ignorant.org. This required me to make patches to +qmail-smtpd.c. The code changes is pretty minor, but some of the code had +to be shuffled to allow me to access some global variables that were declared +later than I needed them. I've released this code into the public domain, no +restrictions. + +The patches add the following checks: +1) Disallows '@', '%' and '!' in sender's name (Prevents relaying). If any + found it is logged (stderr) and rejected. + +2) If NODNSCHECK environment variable is set then it skips the remainder + of the checks. Otherwise: + +3) It looks in controls/nodnscheck. If the mail from domain is listed in the + file then it skips the remainer of the checks. For example, aol.com shows + up in postmaster.rfc-ignorant.com and I actually get legitimate email + from there. + +4) It looks in controls/domainblacklist. It does a domain blacklist lookup + on the mail from domain for each blacklist server in the file. + For example I list: + + whois.rfc-ignorant.com + postmaster.rfc-ignorant.com + + If it is found on the list, it is logged (stderr) and rejected. + +5) The mail from domain is queried for a DNS MX or A record. If none exists + then it is logged (stderr) and rejected. + +Installation: + +Download the patch from http://www.genashor.com/qmail/ the patch will be named +qmailDomainChecks_xx.patch, where xx should be replaced by the +appropriate version number. + +Go to the qmail-1.03 source directory and type: + + patch -p1 < [path-to-patch]/qmailDomainChecks_xx.patch + +Compile and install as usual. If it helps cut down your spam, I'd love to +hear from you. + +----------------------------------------------------------------------------- +Any questions, comments email me: gary at genashor.com + +Gary Gendel