From b0c5a1bf27516611d34b6b7d56a0376e3c64171b Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 5 Feb 2013 23:48:16 +0100 Subject: [PATCH] realrcptto --- Makefile | 13 +- qmail-qmtpd.c | 17 +++ qmail-smtpd.c | 27 +++- realrcptto.c | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 475 insertions(+), 3 deletions(-) create mode 100644 realrcptto.c diff --git a/Makefile b/Makefile index 72ec51e..aef981f 100644 --- a/Makefile +++ b/Makefile @@ -1434,11 +1434,13 @@ sig.h substdio.h readwrite.h exit.h now.h datetime.h fmt.h env.h qmail-qmtpd: \ load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \ -date822fmt.o now.o qmail.o auto_qmail.o \ +date822fmt.o now.o qmail.o auto_qmail.o realrcptto.o slurpclose.o \ +auto_break.o auto_usera.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 ./load qmail-qmtpd rcpthosts.o control.o constmap.o received.o \ - date822fmt.o now.o qmail.o auto_qmail.o \ + date822fmt.o now.o qmail.o auto_qmail.o realrcptto.o slurpclose.o \ + auto_break.o auto_usera.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 @@ -1607,12 +1609,14 @@ qmail-smtpd: \ load qmail-smtpd.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 auto_qmail.o base64.o qmail-spp.o dns.o \ +realrcptto.o slurpclose.o auto_break.o auto_usera.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 \ socket.lib dns.lib ./load qmail-smtpd qregex.o rcpthosts.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 auto_qmail.o base64.o qmail-spp.o dns.o \ + realrcptto.o slurpclose.o auto_break.o auto_usera.o \ tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ 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 \ @@ -1764,6 +1768,11 @@ compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ 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 --git a/qmail-qmtpd.c b/qmail-qmtpd.c index df911a6..d772206 100644 --- a/qmail-qmtpd.c +++ b/qmail-qmtpd.c @@ -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 @@ main() 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 @@ main() if (!local) local = "unknown"; for (;;) { + realrcptto_start(); if (!stralloc_copys(&failure,"")) resources(); flagsenderok = 1; @@ -216,6 +228,10 @@ main() case -1: resources(); case 0: failure.s[failure.len - 1] = 'D'; } + + if (!failure.s[failure.len - 1]) + if (!realrcptto(buf,1)) + failure.s[failure.len - 1] = 'D'; if (!failure.s[failure.len - 1]) { qmail_to(&qq,buf); @@ -231,6 +247,7 @@ main() 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 --git a/qmail-smtpd.c b/qmail-smtpd.c index 33ca503..9fb495b 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c @@ -116,6 +116,18 @@ void die_ipme() 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"); @@ -162,6 +174,13 @@ int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } 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; @@ -260,6 +279,7 @@ void setup() if (fdmbrt == -1) if (errno != error_noent) die_control(); + realrcptto_init(); if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); @@ -604,6 +624,7 @@ void smtp_mail(arg) char *arg; 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"); } @@ -653,6 +674,10 @@ void smtp_rcpt(arg) char *arg; { flagbrt = 1; log_deny("BAD RCPT TO", mailfrom.s,addr.s); } + if (!flagauth && !relayclient && !realrcptto(addr.s,1)) { + 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) { if (!allowed) { @@ -785,6 +810,7 @@ void smtp_data() { 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; } @@ -821,7 +847,6 @@ static stralloc chal = {0}; /* plain challenge */ static stralloc slop = {0}; /* b64 challenge */ #endif -int flagauth = 0; char **childargs; char ssauthbuf[512]; substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); diff --git a/realrcptto.c b/realrcptto.c new file mode 100644 index 0000000..7ec331a --- /dev/null +++ b/realrcptto.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#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" +#include "slurpclose.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, depth) +char *addr; +int depth; +{ + if (depth == 1) + { + 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; + } +} + +// max lookups +#define MAXRECURSION 5 + +static int handleqme(qme, depth) +stralloc *qme; +int depth; +{ + stralloc cmds = {0}; + char *username = NULL; + int fd, i, j, k, count; + + if (depth >= MAXRECURSION) return 0; + if (!stralloc_ready(&cmds,0)) die_nomem(); + cmds.len = 0; + + fd = open_read(qme->s); + if (fd == -1) return 0; + if (slurpclose(fd,&cmds,256) == -1) die_nomem(); + if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) + if (!stralloc_cats(&cmds,"\n")) die_nomem(); + + i = count = 0; + for (j = 0;j < cmds.len;++j) + { + if (cmds.s[j] == '\n') + { + cmds.s[j] = 0; + k = j; + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) + cmds.s[--k] = 0; + switch(cmds.s[i]) + { + case 0: + case '#': + case '.': + case '/': + case '|': + case '+': + break; + case '&': + ++i; + default: + count++; + username = cmds.s + i; + break; + } + i = j + 1; + } + } + + if (count == 1 && username) + realrcptto(username, ++depth); + + return 1; +} + +int realrcptto(addr, depth) +char *addr; +int depth; +{ + env_unset("DTUSER"); + return realrcptto_ex(addr, depth); +} + +int realrcptto_ex(addr, depth) +char *addr; +int depth; +{ + char *homedir, *username; + 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, depth); + 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) { + // set QMAILQUEUE if catch-all + 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 */ + username=x; + 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, depth); + if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem(); + if (!stralloc_0(&nughde)) die_nomem(); + homedir=nughde.s; + username=pw->pw_name; + + got_nughde: + + /* qmail-local:qmesearch */ + //if (!*dash) return 1; + if (!*dash) { env_put2("DTUSER", username); 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 (stat(qme.s,&st) == 0) { handleqme(&qme, depth); 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 (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; } + if (errno != error_noent) { + stat_error(qme.s,errno); + return 1; + } + } + return denyaddr(addr, depth); + } +} + +int realrcptto_deny() +{ + return flagdenyall && flagdenyany; +} -- cgit v1.2.3