Download | Plain Text | Line Numbers
http://code.dogmap.org./qmail/#realrcptto
Changes in version 2006.12.10:
- For QMAILRRTENYALL, use error code 554 after DATA, not 550. Thanks to
... sorry, I lost track of who found this.
- Log stat() errors for .qmail files. Thanks to Chris Bensend for suggesting
this.
Changes in version 2006.10.26:
- Logging uses substdio_puts() and substdio_flush() instead of
substdio_putsflush(). This makes log entries less likely to be
interleaved. Thanks to Matthew Dempsky for finding this.
Changes in version 2004.09.14:
- Strict syntax fix: a variable declaration was accidentally put among
statements instead of at the beginning of the block.
Changes in version 2004.08.30:
- Added QMAILRRTDENYALL.
- Cleanup: missing "break" for some switch statements (no visible behavior
changes AFAICT). Thanks to Markus Stumpf for finding these.
Changes in version 2004.02.05:
- Short-circuit speedup when "dash" is empty.
- Move duplicated code into realrcptto.c.
- Add logging of rejected addresses.
- Verified inter-patchability with netqmail-1.05 and Russell Nelson's
qmail-smtpd-viruscan patch.
diff -Naur Makefile.orig Makefile
--- Makefile.orig 1998-06-15 06:52:55.000000000 -0400
+++ Makefile 2006-12-08 03:21:25.000000000 -0500
@@ -1447,15 +1447,15 @@
./compile qmail-qmqpd.c
qmail-qmtpd: \
-load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \
+load qmail-qmtpd.o realrcptto.o rcpthosts.o control.o constmap.o received.o \
date822fmt.o now.o qmail.o cdb.a 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 dns.o ip.o ipalloc.o ipme.o byte_diff.o
- ./load qmail-qmtpd rcpthosts.o control.o constmap.o \
+str.a fs.a auto_qmail.o dns.o ip.o ipalloc.o ipme.o byte_diff.o auto_break.o auto_usera.o
+ ./load qmail-qmtpd realrcptto.o rcpthosts.o control.o constmap.o \
received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
datetime.a open.a getln.a sig.a case.a env.a stralloc.a \
- alloc.a substdio.a error.a fs.a auto_qmail.o dns.o \
+ alloc.a substdio.a error.a fs.a auto_qmail.o dns.o auto_break.o auto_usera.o \
`cat dns.lib` ip.o ipalloc.o ipme.o byte_diff.o str.a
qmail-qmtpd.0: \
qmail-qmtpd.8
@@ -1614,18 +1614,18 @@
./compile qmail-showctl.c
qmail-smtpd: \
-load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \
+load qmail-smtpd.o realrcptto.o rcpthosts.o qregex.o commands.o timeoutread.o \
timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o 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 strerr.a substdio.a error.a str.a \
-fs.a auto_qmail.o base64.o qmail-spp.o socket.lib dns.o ip.o ipalloc.o
- ./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \
+fs.a auto_qmail.o base64.o qmail-spp.o auto_break.o auto_usera.o socket.lib dns.o ip.o ipalloc.o
+ ./load qmail-smtpd realrcptto.o qregex.o rcpthosts.o commands.o timeoutread.o \
timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
datetime.a getln.a open.a sig.a case.a qmail-spp.o env.a stralloc.a \
- alloc.a strerr.a substdio.a error.a fs.a auto_qmail.o base64.o `cat \
- socket.lib` dns.o str.a `cat dns.lib`
+ alloc.a strerr.a substdio.a error.a fs.a auto_qmail.o base64.o auto_break.o \
+ auto_usera.o `cat socket.lib` dns.o str.a `cat dns.lib`
qmail-smtpd.0: \
qmail-smtpd.8
@@ -1774,6 +1774,11 @@
auto_split.h
./compile readsubdir.c
+realrcptto.o: \
+compile realrcptto.c auto_break.h auto_usera.h byte.h case.h cdb.h \
+constmap.h error.h fmt.h open.h str.h stralloc.h uint32.h
+ ./compile realrcptto.c
+
received.o: \
compile received.c fmt.h qmail.h substdio.h now.h datetime.h \
datetime.h date822fmt.h received.h
diff -Naur qmail-qmtpd.c.orig qmail-qmtpd.c
--- qmail-qmtpd.c.orig 1998-06-15 06:52:55.000000000 -0400
+++ qmail-qmtpd.c 2006-12-08 03:21:25.000000000 -0500
@@ -14,6 +14,15 @@
void badproto() { _exit(100); }
void resources() { _exit(111); }
+void die_nomem() { resources(); }
+void die_control() { resources(); }
+void die_cdb() { resources(); }
+void die_sys() { resources(); }
+
+extern void realrcptto_init();
+extern void realrcptto_start();
+extern int realrcptto();
+extern int realrcptto_deny();
int safewrite(fd,buf,len) int fd; char *buf; int len;
{
@@ -98,6 +107,8 @@
if (rcpthosts_init() == -1) resources();
relayclient = env_get("RELAYCLIENT");
relayclientlen = relayclient ? str_len(relayclient) : 0;
+
+ realrcptto_init();
if (control_readint(&databytes,"control/databytes") == -1) resources();
x = env_get("DATABYTES");
@@ -114,6 +125,7 @@
if (!local) local = "unknown";
for (;;) {
+ realrcptto_start();
if (!stralloc_copys(&failure,"")) resources();
flagsenderok = 1;
@@ -216,6 +228,10 @@
case -1: resources();
case 0: failure.s[failure.len - 1] = 'D';
}
+
+ if (!failure.s[failure.len - 1])
+ if (!realrcptto(buf))
+ failure.s[failure.len - 1] = 'D';
if (!failure.s[failure.len - 1]) {
qmail_to(&qq,buf);
@@ -231,6 +247,7 @@
result = qmail_close(&qq);
if (!flagsenderok) result = "Dunacceptable sender (#5.1.7)";
if (databytes) if (!bytestooverflow) result = "Dsorry, that message size exceeds my databytes limit (#5.3.4)";
+ if (!relayclient && realrcptto_deny()) result = "Dsorry, no mailbox here by that name. (#5.1.1)\r\n";
if (*result)
len = str_len(result);
diff -Naur qmail-smtpd.c.orig qmail-smtpd.c
--- qmail-smtpd.c.orig 1998-06-15 06:52:55.000000000 -0400
+++ qmail-smtpd.c 2006-12-09 04:02:00.000000000 -0500
@@ -111,12 +111,24 @@
void die_ipme()
{
enew(); eout("Unable to figure out my IP addresses!\n");
out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush();
eflush(); _exit(1);
}
+void die_cdb()
+{
+ enew(); eout("Unable to read cdb user database!\n");
+ out("421 unable to read cdb user database (#4.3.0)\r\n"); flush();
+ eflush(); _exit(1);
+}
+void die_sys()
+{
+ enew(); eout("Unable to read system user database!\n");
+ out("421 unable to read system user database (#4.3.0)\r\n"); flush();
+ eflush(); _exit(1);
+}
void straynewline()
{
enew(); eout("Stray newline from "); eout(remoteip); eout(".\n");
out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush();
eflush(); _exit(1);
}
@@ -161,6 +173,13 @@
int err_wantstarttls() { out("530 Must issue a STARTTLS command first (#5.7.0)\r\n"); return -1; };
void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
+extern void realrcptto_init();
+extern void realrcptto_start();
+extern int realrcptto();
+extern int realrcptto_deny();
+
+int flagauth = 0;
+
stralloc greeting = {0};
void smtp_greet(code) char *code;
@@ -259,7 +259,8 @@
fdmbrt = open_read("control/morebadrcptto.cdb");
if (fdmbrt == -1) if (errno != error_noent) die_control();
+ realrcptto_init();
if (control_readint(&databytes,"control/databytes") == -1) die_control();
x = env_get("DATABYTES");
@@ -635,7 +635,8 @@
if (!stralloc_copys(&rcptto,"")) die_nomem();
if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
if (!stralloc_0(&mailfrom)) die_nomem();
+ realrcptto_start();
recipcount = 0;
out("250 ok\r\n");
}
void smtp_rcpt(arg) char *arg; {
@@ -680,5 +701,9 @@
flagbrt = 1;
log_deny("BAD RCPT TO", mailfrom.s,addr.s);
}
+ if (!flagauth && !relayclient && !realrcptto(addr.s)) {
+ out("554 sorry, no mailbox here by that name. (#5.1.1)\r\n");
+ return;
+ }
if (!(spp_val = spp_rcpt(allowed))) return;
if (!relayclient && spp_val == 1) {
@@ -899,6 +899,7 @@
if (mailfrom.len == 1 && recipcount > 1) { err_badbounce(); return; }
if (flagbrt) { err_brt(); return; }
if (!spp_data()) return;
+ if (!relayclient && realrcptto_deny()) { out("550 sorry, no mailbox here by that name. (#5.1.1)\r\n"); return; }
seenmail = 0;
if (databytes) bytestooverflow = databytes + 1;
if (qmail_open(&qqt) == -1) { err_qqt(); return; }
@@ -961,6 +961,5 @@
static stralloc slop = {0}; /* b64 challenge */
#endif
-int flagauth = 0;
char **childargs;
char ssauthbuf[512];
diff -Naur realrcptto.c.orig realrcptto.c
--- realrcptto.c.orig 1969-12-31 19:00:00.000000000 -0500
+++ realrcptto.c 2006-12-09 18:33:04.000000000 -0500
@@ -0,0 +1,343 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#include "auto_break.h"
+#include "auto_usera.h"
+#include "byte.h"
+#include "case.h"
+#include "cdb.h"
+#include "constmap.h"
+#include "error.h"
+#include "fmt.h"
+#include "open.h"
+#include "str.h"
+#include "stralloc.h"
+#include "uint32.h"
+#include "substdio.h"
+#include "env.h"
+
+extern void die_nomem();
+extern void die_control();
+extern void die_cdb();
+extern void die_sys();
+
+static stralloc envnoathost = {0};
+static stralloc percenthack = {0};
+static stralloc locals = {0};
+static stralloc vdoms = {0};
+static struct constmap mappercenthack;
+static struct constmap maplocals;
+static struct constmap mapvdoms;
+
+static char *dash;
+static char *extension;
+static char *local;
+static struct passwd *pw;
+
+static char errbuf[128];
+static struct substdio sserr = SUBSTDIO_FDBUF(write,2,errbuf,sizeof errbuf);
+
+static char pidbuf[64];
+static char remoteipbuf[64]=" ";
+
+static int flagdenyall;
+static int flagdenyany;
+
+void realrcptto_init()
+{
+ char *x;
+
+ if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1)
+ die_control();
+
+ if (control_readfile(&locals,"control/locals",1) != 1) die_control();
+ if (!constmap_init(&maplocals,locals.s,locals.len,0)) die_nomem();
+ switch(control_readfile(&percenthack,"control/percenthack",0)) {
+ case -1: die_control();
+ case 0: if (!constmap_init(&mappercenthack,"",0,0)) die_nomem();
+ case 1:
+ if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0))
+ die_nomem();
+ }
+ switch(control_readfile(&vdoms,"control/virtualdomains",0)) {
+ case -1: die_control();
+ case 0: if (!constmap_init(&mapvdoms,"",0,1)) die_nomem();
+ case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) die_nomem();
+ }
+
+ str_copy(pidbuf + fmt_ulong(pidbuf,getpid())," ");
+ x=env_get("PROTO");
+ if (x) {
+ static char const remoteip[]="REMOTEIP";
+ unsigned int len = str_len(x);
+ if (len <= sizeof remoteipbuf - sizeof remoteip) {
+ byte_copy(remoteipbuf,len,x);
+ byte_copy(remoteipbuf + len,sizeof remoteip,remoteip);
+ x = env_get(remoteipbuf);
+ len = str_len(x);
+ if (len + 1 < sizeof remoteipbuf) {
+ byte_copy(remoteipbuf,len,x);
+ remoteipbuf[len]=' ';
+ remoteipbuf[len + 1]='\0';
+ }
+ }
+ }
+
+ x = env_get("QMAILRRTDENYALL");
+ flagdenyall = (x && x[0]=='1' && x[1]=='\0');
+}
+
+void realrcptto_start()
+{
+ flagdenyany = 0;
+}
+
+static int denyaddr(addr)
+char *addr;
+{
+ substdio_puts(&sserr,"realrcptto ");
+ substdio_puts(&sserr,pidbuf);
+ substdio_puts(&sserr,remoteipbuf);
+ substdio_puts(&sserr,addr);
+ substdio_puts(&sserr,"\n");
+ substdio_flush(&sserr);
+ flagdenyany = 1;
+ return flagdenyall;
+}
+
+static void stat_error(path,error)
+char* path;
+int error;
+{
+ substdio_puts(&sserr,"unable to stat ");
+ substdio_puts(&sserr,path);
+ substdio_puts(&sserr,": ");
+ substdio_puts(&sserr,error_str(error));
+ substdio_puts(&sserr,"\n");
+ substdio_flush(&sserr);
+}
+
+#define GETPW_USERLEN 32
+
+static int userext()
+{
+ char username[GETPW_USERLEN];
+ struct stat st;
+
+ extension = local + str_len(local);
+ for (;;) {
+ if (extension - local < sizeof(username))
+ if (!*extension || (*extension == *auto_break)) {
+ byte_copy(username,extension - local,local);
+ username[extension - local] = 0;
+ case_lowers(username);
+ errno = 0;
+ pw = getpwnam(username);
+ if (errno == error_txtbsy) die_sys();
+ if (pw)
+ if (pw->pw_uid)
+ if (stat(pw->pw_dir,&st) == 0) {
+ if (st.st_uid == pw->pw_uid) {
+ dash = "";
+ if (*extension) { ++extension; dash = "-"; }
+ return 1;
+ }
+ }
+ else
+ if (error_temp(errno)) die_sys();
+ }
+ if (extension == local) return 0;
+ --extension;
+ }
+}
+
+int realrcptto(addr)
+char *addr;
+{
+ char *homedir;
+ static stralloc localpart = {0};
+ static stralloc lower = {0};
+ static stralloc nughde = {0};
+ static stralloc wildchars = {0};
+ static stralloc safeext = {0};
+ static stralloc qme = {0};
+ unsigned int i,at;
+
+ /* Short circuit, or full logging? Short circuit. */
+ if (flagdenyall && flagdenyany) return 1;
+
+ /* qmail-send:rewrite */
+ if (!stralloc_copys(&localpart,addr)) die_nomem();
+ i = byte_rchr(localpart.s,localpart.len,'@');
+ if (i == localpart.len) {
+ if (!stralloc_cats(&localpart,"@")) die_nomem();
+ if (!stralloc_cat(&localpart,&envnoathost)) die_nomem();
+ }
+ while (constmap(&mappercenthack,localpart.s + i + 1,localpart.len - i - 1)) {
+ unsigned int j = byte_rchr(localpart.s,i,'%');
+ if (j == i) break;
+ localpart.len = i;
+ i = j;
+ localpart.s[i] = '@';
+ }
+ at = byte_rchr(localpart.s,localpart.len,'@');
+ if (constmap(&maplocals,localpart.s + at + 1,localpart.len - at - 1)) {
+ localpart.len = at;
+ localpart.s[at] = '\0';
+ } else {
+ unsigned int xlen,newlen;
+ char *x;
+ for (i = 0;;++i) {
+ if (i > localpart.len) return denyaddr(addr);
+ if (!i || (i == at + 1) || (i == localpart.len) ||
+ ((i > at) && (localpart.s[i] == '.'))) {
+ x = constmap(&mapvdoms,localpart.s + i,localpart.len - i);
+ if (x && i == at + 1) {
+ char *qmailqueue;
+ qmailqueue = env_get("QMAILQUEUE");
+ if (qmailqueue) {
+ if (!env_put("QMAILQUEUE=bin/qmail-queue")) die_nomem();
+ }
+ }
+ if (x) break;
+ }
+ }
+ if (!*x) return 1;
+ xlen = str_len(x) + 1; /* +1 for '-' */
+ newlen = xlen + at + 1; /* +1 for \0 */
+ if (xlen < 1 || newlen - 1 < xlen || newlen < 1 ||
+ !stralloc_ready(&localpart,newlen))
+ die_nomem();
+ localpart.s[newlen - 1] = '\0';
+ byte_copyr(localpart.s + xlen,at,localpart.s);
+ localpart.s[xlen - 1] = '-';
+ byte_copy(localpart.s,xlen - 1,x);
+ localpart.len = newlen;
+ }
+
+ /* qmail-lspawn:nughde_get */
+ {
+ int r,fd,flagwild;
+ if (!stralloc_copys(&lower,"!")) die_nomem();
+ if (!stralloc_cats(&lower,localpart.s)) die_nomem();
+ if (!stralloc_0(&lower)) die_nomem();
+ case_lowerb(lower.s,lower.len);
+ if (!stralloc_copys(&nughde,"")) die_nomem();
+ fd = open_read("users/cdb");
+ if (fd == -1) {
+ if (errno != error_noent) die_cdb();
+ } else {
+ uint32 dlen;
+ r = cdb_seek(fd,"",0,&dlen);
+ if (r != 1) die_cdb();
+ if (!stralloc_ready(&wildchars,(unsigned int) dlen)) die_nomem();
+ wildchars.len = dlen;
+ if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) die_cdb();
+ i = lower.len;
+ flagwild = 0;
+ do { /* i > 0 */
+ if (!flagwild || (i == 1) ||
+ (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1])
+ < wildchars.len)) {
+ r = cdb_seek(fd,lower.s,i,&dlen);
+ if (r == -1) die_cdb();
+ if (r == 1) {
+ char *x;
+ if (!stralloc_ready(&nughde,(unsigned int) dlen)) die_nomem();
+ nughde.len = dlen;
+ if (cdb_bread(fd,nughde.s,nughde.len) == -1) die_cdb();
+ if (flagwild)
+ if (!stralloc_cats(&nughde,localpart.s + i - 1)) die_nomem();
+ if (!stralloc_0(&nughde)) die_nomem();
+ close(fd);
+ x=nughde.s;
+ /* skip username */
+ x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+ if (x == nughde.s + nughde.len) return 1;
+ ++x;
+ /* skip uid */
+ x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+ if (x == nughde.s + nughde.len) return 1;
+ ++x;
+ /* skip gid */
+ x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+ if (x == nughde.s + nughde.len) return 1;
+ ++x;
+ /* skip homedir */
+ homedir=x;
+ x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+ if (x == nughde.s + nughde.len) return 1;
+ ++x;
+ /* skip dash */
+ dash=x;
+ x += byte_chr(x,nughde.s + nughde.len - x,'\0');
+ if (x == nughde.s + nughde.len) return 1;
+ ++x;
+ extension=x;
+ goto got_nughde;
+ }
+ }
+ --i;
+ flagwild = 1;
+ } while (i);
+ close(fd);
+ }
+ }
+
+ /* qmail-getpw */
+ local = localpart.s;
+ if (!userext()) {
+ extension = local;
+ dash = "-";
+ pw = getpwnam(auto_usera);
+ }
+ if (!pw) return denyaddr(addr);
+ if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem();
+ if (!stralloc_0(&nughde)) die_nomem();
+ homedir=nughde.s;
+
+ got_nughde:
+
+ /* qmail-local:qmesearch */
+ if (!*dash) return 1;
+ if (!stralloc_copys(&safeext,extension)) die_nomem();
+ case_lowerb(safeext.s,safeext.len);
+ for (i = 0;i < safeext.len;++i)
+ if (safeext.s[i] == '.')
+ safeext.s[i] = ':';
+ {
+ struct stat st;
+ int i;
+ if (!stralloc_copys(&qme,homedir)) die_nomem();
+ if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
+ if (!stralloc_cats(&qme,dash)) die_nomem();
+ if (!stralloc_cat(&qme,&safeext)) die_nomem();
+ if (!stralloc_0(&qme)) die_nomem();
+ if (stat(qme.s,&st) == 0) return 1;
+ if (errno != error_noent) {
+ stat_error(qme.s,errno);
+ return 1;
+ }
+ for (i = safeext.len;i >= 0;--i)
+ if (!i || (safeext.s[i - 1] == '-')) {
+ if (!stralloc_copys(&qme,homedir)) die_nomem();
+ if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
+ if (!stralloc_cats(&qme,dash)) die_nomem();
+ if (!stralloc_catb(&qme,safeext.s,i)) die_nomem();
+ if (!stralloc_cats(&qme,"default")) die_nomem();
+ if (!stralloc_0(&qme)) die_nomem();
+ if (stat(qme.s,&st) == 0) return 1;
+ if (errno != error_noent) {
+ stat_error(qme.s,errno);
+ return 1;
+ }
+ }
+ return denyaddr(addr);
+ }
+}
+
+int realrcptto_deny()
+{
+ return flagdenyall && flagdenyany;
+}