--- qmail-smtpd-orig.c Mon Jun 15 12:53:16 1998 +++ /usr/local/src/qmail-1.03/qmail-smtpd.c Fri Jan 10 22:07:02 2003 @@ -23,11 +23,24 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "dns.h" +#include "wait.h" +#include "fd.h" +#define AUTHCRAM #define MAXHOPS 100 unsigned int databytes = 0; +unsigned int nullsendermultrcpts = 0; int timeout = 1200; +/* SpaceNet - maex */ +char strnum[FMT_ULONG]; +char sserrbuf[512]; +substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof(sserrbuf)); +void logerr(s) char *s; { if(substdio_puts(&sserr,s) == -1) _exit(1); } +void logerrf(s) char *s; { if(substdio_puts(&sserr,s) == -1) _exit(1); if(substdio_flush(&sserr) == -1) _exit(1); } +void logerrpid() { strnum[fmt_ulong(strnum,getpid())] = 0; logerr("pid "); logerr(strnum); logerr(" from "); } + int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; @@ -48,9 +61,40 @@ void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } 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_smf() { out("451 DNS temporary failure (#4.3.0)\r\n"); } +void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } +void err_hmf(arg1,arg2) char *arg1,*arg2; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Non-existing DNS_MX: "); + logerr("MAIL "); logerr(arg2); logerrf("\n"); + out("552 sorry, your envelope sender domain must exist (#5.7.1)\r\n"); } +void err_nogateway(arg1,arg2,arg3) char *arg1,*arg2,*arg3; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Invalid RELAY client: "); + logerr("MAIL from: <"); logerr(arg2); logerr (">, RCPT "); logerr(arg3); logerrf("\n"); + out("553 sorry, that domain isn't allowed to be relayed thru this MTA (#5.7.1)\r\n"); } +void err_bmf(arg1,arg2,arg3) char *arg1,*arg2,*arg3; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Invalid SENDER address: "); + logerr("MAIL from: <"); logerr(arg2); logerr (">, RCPT "); logerr(arg3); logerrf("\n"); + out("554 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); + flush(); _exit(1); } +void err_rcp(arg1,arg2,arg3) char *arg1,*arg2,*arg3; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Invalid RECIPIENT address: "); + logerr("MAIL from: <"); logerr(arg2); logerr (">, RCPT "); logerr(arg3); logerrf("\n"); + out("555 sorry, your envelope recipient is in my badrcptto list (#5.7.1)\r\n"); + flush(); _exit(1); } +void err_nullsender(arg1,arg2) char *arg1,*arg2; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Invalid NULLSENDER Mail: "); + logerr ("Last RCPT "); logerr(arg2); logerrf("\n"); + out("556 sorry, bounce messages should have a single envelope recipient (#5.7.1)\r\n"); + flush(); _exit(1); } +void err_mrc(arg1,arg2,arg3) char *arg1,*arg2,*arg3; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Too many RECIPIENTS: "); + logerr("MAIL from: <"); logerr(arg2); logerr (">, Last RCPT "); logerr(arg3); logerrf("\n"); + out("557 sorry, too many recipients (#5.7.1)\r\n"); + flush(); _exit(1); } +void err_bhs(arg1,arg2) char *arg1,*arg2; { + logerr("qmail-smtpd: "); logerrpid(); logerr(arg1); logerr(" Blackholed SENDER address: "); + logerr("MAIL "); logerr(arg2); logerrf("\n"); + flush(); _exit(1); } 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,6 +102,17 @@ 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"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } +int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } + + stralloc greeting = {0}; @@ -69,7 +124,10 @@ } void smtp_help() { - out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); + out("214 Available commands:\r\n"); + out("214 RCPT MAIL DATA AUTH\r\n"); + out("214 QUIT HELO EHLO RSET\r\n"); + out("214 HELP NOOP VRFY\r\n"); } void smtp_quit() { @@ -81,6 +139,8 @@ char *remoteinfo; char *local; char *relayclient; +char *nodnscheck; +char *msgsize; stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -97,10 +157,59 @@ stralloc bmf = {0}; struct constmap mapbmf; +/* Wildmat Variables */ +int bmpok = 0; +stralloc bmp = {0}; +struct constmap mapbmp; +/* Wildmat Patch END */ + +/* RECIPIENT Check Variables */ +int rcpok = 0; +stralloc rcp = {0}; +struct constmap maprcp; +int brpok = 0; +stralloc brp = {0}; +struct constmap mapbrp; + +/* RELAYCLIENT Check Variables */ +int relayclientsok = 0; +stralloc relayclients = {0}; +struct constmap maprelayclients; +int relaydomainsok = 0; +stralloc relaydomains = {0}; +struct constmap maprelaydomains; + +/* RELAYMAILFROM Check Variables */ +int rmfok = 0; +stralloc rmf = {0}; +struct constmap maprmf; + +/* TARPIT Check Variables */ +int tarpitcount = 0; +int tarpitdelay = 5; + +/* NODNSCHECK Check Variables */ +int nodnschecksok = 0; +stralloc nodnschecks = {0}; +struct constmap mapnodnschecks; + +/* BLACKHOLE Sender Check Variables */ +int bhsok = 0; +stralloc bhs = {0}; +struct constmap mapbhs; + +/* NULLSENDERMULTRCPTS Check Variable */ +int nullsendermaxrcpts = 1; /* Default value: only one RCPT for Null-Sender Mail */ + +/* MAXRECIPIENTS Check Variable */ +int maxrcptcount = 0; /* Default value: only one RCPT for Null-Sender Mail */ + void setup() { char *x; unsigned long u; + unsigned int i; + unsigned len; if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) @@ -110,18 +219,80 @@ if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; +/* TARPIT Patch - include Control Files */ + + if (control_readint(&tarpitcount,"control/tarpitcount") == -1) die_control(); + if (tarpitcount < 0) tarpitcount = 0; + x = env_get("TARPITCOUNT"); + if (x) { scan_ulong(x,&u); tarpitcount = u; }; + if (control_readint(&tarpitdelay,"control/tarpitdelay") == -1) die_control(); + if (tarpitdelay < 0) tarpitdelay = 0; + x = env_get("TARPITDELAY"); + if (x) { scan_ulong(x,&u); tarpitdelay = u; }; + +/* MAXRECPIENTS - include Control Files */ + + if (control_readint(&maxrcptcount,"control/maxrecipients") == -1) die_control(); + if (maxrcptcount < 0) maxrcptcount = 0; + x = env_get("MAXRECIPIENTS"); + if (x) { scan_ulong(x,&u); maxrcptcount = u; }; + if (rcpthosts_init() == -1) die_control(); bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); if (bmfok) if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + +/* RELAYMAILFROM Patch - include Control File */ + + rmfok = control_readfile(&rmf,"control/relaymailfrom",0); + if (rmfok == -1) die_control(); + if (rmfok) + if (!constmap_init(&maprmf,rmf.s,rmf.len,0)) die_nomem(); + +/* Wildmat Patch - include Control file */ + + bmpok = control_readfile(&bmp,"control/badmailpatterns",0); + if (bmpok == -1) die_control(); + if (bmpok) + if (!constmap_init(&mapbmp,bmp.s,bmp.len,0)) die_nomem(); +/* RECIPIENT Patch - include Control file */ + + rcpok = control_readfile(&rcp,"control/badrcptto",0); + if (rcpok == -1) die_control(); + if (rcpok) + if (!constmap_init(&maprcp,rcp.s,rcp.len,0)) die_nomem(); + + brpok = control_readfile(&brp,"control/badrcptpatterns",0); + if (brpok == -1) die_control(); + if (brpok) + if (!constmap_init(&mapbrp,brp.s,brp.len,0)) die_nomem(); + +/* BLACKHOLE Sender Patch - include Control file */ + + bhsok = control_readfile(&bhs,"control/blackholedsender",0); + if (bhsok == -1) die_control(); + if (bhsok) + if (!constmap_init(&mapbhs,bhs.s,bhs.len,0)) die_nomem(); + if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); if (x) { scan_ulong(x,&u); databytes = u; } if (!(databytes + 1)) --databytes; - + +/* NULLSENDERMULTRCPTS from environment */ + + x = env_get("NULLSENDERMULTRCPTS"); + if (x) { + scan_ulong(x,&u); nullsendermaxrcpts = u; + nullsendermultrcpts = nullsendermaxrcpts; } /* Customized # RCPTS for Null-Sender Mail */ + else { + nullsendermultrcpts = 0; /* Any number of RCTPS allowed */ + } + if (!(nullsendermaxrcpts + 1)) --nullsendermaxrcpts; + remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -131,6 +302,51 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + +/* DNSCHECK Patch - include Control file */ + + nodnscheck = env_get("NODNSCHECK"); + + if (! nodnscheck) + { + /* Look up "MAIL from:" addresses to skip for DNS check in control/nodnscheck. */ + nodnschecksok = control_readfile(&nodnschecks,"control/nodnscheck",0); + if (nodnschecksok == -1) die_control(); + if (nodnschecksok) + if (!constmap_init (&mapnodnschecks, nodnschecks.s, nodnschecks.len, 0)) die_nomem(); + } + + if (! relayclient) + { + /* Attempt to look up the IP number in control/relayclients. */ + relayclientsok = control_readfile(&relayclients,"control/relayclients",0); + if (relayclientsok == -1) die_control(); + if (relayclientsok) { + if (!constmap_init (&maprelayclients, relayclients.s, relayclients.len, 1)) die_nomem(); + + for (i = len = str_len (remoteip); i > 0; i --) + if ((i == len) || (remoteip[i - 1] == '.')) { + if ((relayclient = constmap (&maprelayclients, remoteip, i))) + break; + } + } + } + + if (! relayclient) + { + /* Attempt to look up the host name in control/relaydomains. */ + relaydomainsok = control_readfile(&relaydomains,"control/relaydomains",0); + if (relaydomainsok == -1) die_control(); + if (relaydomainsok) { + if (!constmap_init (&maprelaydomains, relaydomains.s, relaydomains.len, 1)) die_nomem(); + + for (i = 0, len = str_len (remotehost); i <= len; i ++) + if ((i == 0) || (i == len) || (remotehost[i] == '.')) + if ((relayclient = constmap (&maprelaydomains, remotehost + i, len - i))) + break; + } + } + dohelo(remotehost); } @@ -197,14 +413,155 @@ return 1; } +/* SIZELIMIT Function by Chris Harris */ + +int sizelimit(arg) + char *arg; + { + int i; + long r; + unsigned long sizebytes = 0; + + i = str_chr(arg,'<'); + if (arg[i]) + arg += i + 1; + else { + arg += str_chr(arg,':'); + if (*arg == ':') ++arg; + while (*arg == ' ') ++arg; + } + + arg += str_chr(arg,' '); + if (*arg == ' ') while (*arg == ' ') ++arg; + else return 1; + + i = str_chr(arg,'='); + arg[i] = 0; + if (case_equals(arg,"SIZE")) { + arg += i; + while (*++arg && *arg > 47 && *arg < 58) { + sizebytes *= 10; + sizebytes += *arg - 48; + } + r = databytes - sizebytes; + if (r < 0) return 0; + } + return 1; + } + int bmfcheck() { + int i; int j; - if (!bmfok) return 0; + int k = 0; + char subvalue; + + if (bmfok) { if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; j = byte_rchr(addr.s,addr.len,'@'); if (j < addr.len) if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + } + +/* Include control file control/badmailpatterns and evaluate with Wildmat check */ + + if (bmpok) { + i = 0; + for (j = 0;j < bmp.len;++j) + if (!bmp.s[j]) { + subvalue = bmp.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(addr.s, bmp.s + i)) k = subvalue; + i = j + 1; + } + return k; + } + return 0; +} + +/* Check on "black holed" senders */ + +int bhscheck() +{ + int j; + + if (bhsok) { + if (constmap(&mapbhs,addr.s,addr.len - 1)) return 1; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapbhs,addr.s + j,addr.len - j - 1)) return 1; + } + return 0; +} + + +int rmfcheck() +{ + int j; + if (!rmfok) return 0; + if (constmap(&maprmf,addr.s,addr.len - 1)) return 1; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&maprmf,addr.s + j,addr.len - j - 1)) return 1; + return 0; +} + +int rcpcheck() +{ + int i; + int j; + int k = 0; + char subvalue; + +/* Include control file control/badrcptto */ + + if (rcpok) { + if (constmap(&maprcp,addr.s,addr.len - 1)) return 1; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&maprcp,addr.s + j,addr.len - j - 1)) { + return 1; + } + } + +/* Include control file control/badrcptpatterns and evaluate with Wildmat check */ + + if (brpok) { + i = 0; + for (j = 0;j < brp.len;++j) + if (!brp.s[j]) { + subvalue = brp.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(addr.s, brp.s + i)) k = subvalue; + i = j + 1; + } + return k; + } + return 0; +} + +int dnscheck() +{ + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned int random; + int j; + + if (nodnschecksok) { + if (constmap(&mapnodnschecks,addr.s,addr.len - 1)) return 0; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapnodnschecks,addr.s + j,addr.len - j - 1)) return 0; + } + + random = now() + (getpid() << 16); + j = byte_rchr(addr.s,addr.len,'@') + 1; + if (j < addr.len) { + stralloc_copys(&sa, addr.s + j); + dns_init(0); + j = dns_mxip(&ia,&sa,random); + if (j < 0) return j; + } return 0; } @@ -218,18 +575,35 @@ int seenmail = 0; +int rcptcount; /* defined if seenmail */ int flagbarf; /* defined if seenmail */ +int flagrcpt; /* recipient filter */ +int flagbhs; /* blackholed sender */ stralloc mailfrom = {0}; stralloc rcptto = {0}; +char size_buf[FMT_ULONG]; /* needed for SIZE CMD */ void smtp_helo(arg) char *arg; { - smtp_greet("250 "); out("\r\n"); - seenmail = 0; dohelo(arg); + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_size() +{ + size_buf[fmt_ulong(size_buf,(unsigned long) databytes)] = 0; + out("250 SIZE "); out(size_buf); out("\r\n"); } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); +#ifdef AUTHCRAM + out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); + out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); +#else + out("\r\n250-AUTH LOGIN PLAIN"); + out("\r\n250-AUTH=LOGIN PLAIN"); +#endif + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() @@ -240,27 +614,49 @@ void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); +/* Return error if incoming SMTP msg exceeds DATABYTES */ + if (databytes && !sizelimit(arg)) { err_size(); return; } +/* Terminate SMTP Session immediatly if BLACKHOLED Sender is seen */ + if (bhscheck()) { err_bhs(remoteip,arg); return; } + if (!relayclient) { if (rmfcheck()) relayclient = ""; else relayclient = 0; } /* We redefine relayclient */ + if (!relayclient) { flagbarf = bmfcheck(); } /* We check bad SENDERS only in non-RELAYCLIENT case */ + if (!nodnscheck) { + switch(dnscheck()) { + case DNS_HARD: err_hmf(remoteip,arg); return; + case DNS_SOFT: err_smf(); return; + case DNS_MEM: die_nomem(); } + } seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); + rcptcount = 0; out("250 ok\r\n"); } -void smtp_rcpt(arg) char *arg; { +void smtp_rcpt(arg) char *arg; +{ if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + flagrcpt = rcpcheck(); + if (flagbarf) { err_bmf(remoteip,mailfrom.s,arg); 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(remoteip,mailfrom.s,arg); return; } if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); + if (flagrcpt) { err_rcp(remoteip,mailfrom.s,arg); return; } /* RECIPIENT BAD* check */ + rcptcount++; + + if ( nullsendermultrcpts != 0 ) { /* Null-Sender Check on # RCPTS */ + if ( mailfrom.len == 1 && rcptcount > nullsendermaxrcpts ) { err_nullsender(remoteip,arg); return; } } + if ( maxrcptcount != 0 ) { /* Check on max. number of RCPTS */ + if (rcptcount > maxrcptcount) { err_mrc(remoteip,mailfrom.s,arg); return; } } + if (tarpitcount && rcptcount >= tarpitcount) while (sleep(tarpitdelay)); /* TARPIT Delay */ out("250 ok\r\n"); } @@ -300,13 +696,23 @@ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ int flagmaybey; /* 1 if this line might match \r\n, if fih */ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ + int seencr; state = 1; *hops = 0; flaginheader = 1; - pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; - for (;;) { + pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; seencr = 0; /* qmail-smtpd-newline patch */ + for (;;) { substdio_get(&ssin,&ch,1); + if (ch == '\n') + { + if (seencr == 0) + { + substdio_seek(ssin,-1); + ch = '\r'; + } + } + if (ch == '\r') seencr = 1; else seencr = 0; if (flaginheader) { if (pos < 9) { if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; @@ -388,16 +794,231 @@ qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } + if (databytes) if (!bytestooverflow) { err_size(); return; } if (*qqx == 'D') out("554 "); else out("451 "); out(qqx + 1); out("\r\n"); } +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; +static stralloc user = {0}; +static stralloc pass = {0}; +static stralloc resp = {0}; +static stralloc slop = {0}; +char *hostname; +char **childargs; +substdio ssup; +char upbuf[128]; +int authd = 0; + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin, "")) die_nomem(); + + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&resp)) die_nomem(); + + if (fd_copy(2,1) == -1) return err_pipe(); + close(3); + if (pipe(pi) == -1) return err_pipe(); + if (pi[0] != 3) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); + if (substdio_put(&ssup,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write(); + if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write(); + if (substdio_flush(&ssup) == -1) return err_write(); + + close(pi[1]); + byte_zero(pass.s,pass.len); + byte_zero(upbuf,sizeof upbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + while (slop.s[id]) id++; /* ignore authorize-id */ + + if (slop.len > id + 1) + if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem(); + if (slop.len > id + user.len + 2) + if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +#ifdef AUTHCRAM +int auth_cram() +{ + int i, r; + char *s; + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&pass,"<")) die_nomem(); + if (!stralloc_cats(&pass,unique)) die_nomem(); + if (!stralloc_cats(&pass,hostname)) die_nomem(); + if (!stralloc_cats(&pass,">")) die_nomem(); + if (b64encode(&pass,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + + i = str_chr(slop.s,' '); + s = slop.s + i; + while (*s == ' ') ++s; + slop.s[i] = 0; + if (!stralloc_copys(&user,slop.s)) die_nomem(); + if (!stralloc_copys(&resp,s)) die_nomem(); + + if (!user.len || !resp.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login", auth_login } +, { "plain", auth_plain } +#ifdef AUTHCRAM +, { "cram-md5", auth_cram } +#endif +, { 0, err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!hostname || !*childargs) + { + out("503 auth not available (#5.3.3)\r\n"); + return; + } + if (authd) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + authd = 1; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + out("535 authorization failed (#5.7.0)\r\n"); + } +} + struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +, { "auth", smtp_auth, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } @@ -408,8 +1029,13 @@ , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) +int argc; +char **argv; { + hostname = argv[1]; + childargs = argv + 2; + sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup();