--- qmail-smtpd.c.orig 1998-06-15 +++ qmail-smtpd.c 2007-03-21 --- .././qmail-1.03/qmail-smtpd.c Mon Jun 15 12:53:16 1998 +++ ../qmail-1.03.2418/qmail-smtpd.c Sat Jan 6 21:33:00 2007 @@ -1,4 +1,3 @@ -#include "sig.h" #include "readwrite.h" #include "stralloc.h" #include "substdio.h" @@ -20,14 +19,52 @@ #include "now.h" #include "exit.h" #include "rcpthosts.h" +#include "recipients.h" +#include "mfrules.h" +#include "ucspitls.h" #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "cdb.h" +#include "dns.h" +#include "wait.h" + +#define RELAYMAILFROM +#define REQBRACKETS +#define AUTHSLEEP 5 + +#define MIMETYPE_LEN 9 +#define LOADER_LEN 5 +#define FDLOG 2 #define MAXHOPS 100 -unsigned int databytes = 0; +unsigned long databytes = 0; int timeout = 1200; +char *reply554; +char *replymav; + +/* this file is too long -------------------------------------- logging */ + +char sslogbuf[512]; +substdio sslog = SUBSTDIO_FDBUF(write,FDLOG,sslogbuf,sizeof(sslogbuf)); +void logc(s) char *s; { if(substdio_puts(&sslog,s) == -1) _exit(1); } /* single string */ +void logp(s1) char *s1; { logc(" P:"); logc(s1); } /* protocol */ +void logh(s1,s2,s3) char *s1, *s2, *s3; { logc(" S:"); logc(s1); logc(":"); logc(s2); logc(" H:"); logc(s3); } /* host */ +void logf(s) char *s; { logc(" F:"); logc(s); } /* mailfrom */ +void logt(s) char *s; { logc(" T:"); logc(s); } /* rcptto */ +void logi(s) char *s; { logc(" '"); logc(s); logc("'"); } /* information */ +void logn(s) char *s; { if(substdio_puts(&sslog,s) == -1) _exit(1); if(substdio_flush(&sslog) == -1) _exit(1); } /* end */ + +void loga(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; + { logc(s1); logc(s2), logh(s3,s4,s5); logi(s7); logc(" ?="); logi(s6); logn("\n"); } +void logb(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; + { logc(s1); logc(s2), logh(s3,s4,s5); logi(s7); logc(" !="); logi(s6); logn("\n"); } +void logs(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; + { logc(s1); logp(s2); logh(s3,s4,s5); logf(s6); logt(s7); logn("\n"); } +void logd(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; + { logc(s1); logp(s2); logh(s3,s4,s5); logf(s6); logt(s7); logi(s8); logn("\n"); } + int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; @@ -42,15 +79,15 @@ void flush() { substdio_flush(&ssout); } void out(s) char *s; { substdio_puts(&ssout,s); } +/* this file is too long -------------------------------------- Exit codes */ + void die_read() { _exit(1); } void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } 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 die_starttls() { out("454 TLS not available due to temporary reason (#5.7.3)\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 +95,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_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } +void err_helo(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; { + out("550 sorry, invalid HELO/EHLO greeting (#5.7.1)\r\n"); + logd(s1,s2,s3,s4,s5,s6,s7,s8); + return; + } +void err_bmf(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; { + out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); + logd(s1,s2,s3,s4,s5,s6,s7,s8); + return; + } +void err_mav(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("553 sorry, invalid sender address specified "); + if (replymav) out(replymav); + out(" (#5.7.1)\r\n"); + logs(s1,s2,s3,s4,s5,s6,s7); + return; + } +void err_nogateway(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); + logs(s1,s2,s3,s4,s5,s6,s7); + return; + } +void err_mfdns(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("553 sorry, your envelope sender must exist (#5.7.1)\r\n"); + logs(s1,s2,s3,s4,s5,s6,s7); + return; + } +void err_rcpts(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("550 sorry, too many recipients (#5.7.1)\r\n"); + logs(s1,s2,s3,s4,s5,s6,s7); + return; + } +void err_brcptto(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("550 sorry, your envelope recipient is in my badrcptto list (#5.7.1)\r\n"); + logs(s1,s2,s3,s4,s5,s6,s7); + return; + } +void err_recipient(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + if (env_get("RECIPIENTS450")) + out("450 sorry, mailbox currently unavailable (#4.2.1)\r\n"); + else + out("550 sorry, no mailbox by that name (#5.7.1)\r\n"); + logs(s1,s2,s3,s4,s5,s6,s7); + return; + } +void err_data(s1,s2,s3,s4,s5,s6,s7,s8) char *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; { + out("554 sorry, invalid message content "); + if (reply554) out(reply554); + out(" (#5.3.2)\r\n"); + logd(s1,s2,s3,s4,s5,s6,s7,s8); + return; + } + +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 canceled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } +void err_authfail(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("535 authentication failed (#5.7.1)\r\n"); loga(s1,s2,s3,s4,s5,s6,s7); } +void err_authreq(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("535 authentication required (#5.7.1)\r\n"); logs(s1,s2,s3,s4,s5,s6,s7); } +int err_starttls() { out("454 TLS not available due to temporary reason (#5.7.3)\r\n"); return -1; } +void err_tlsreq(s1,s2,s3,s4,s5,s6,s7) char *s1, *s2, *s3, *s4, *s5, *s6, *s7; { + out("535 STARTTLS required (#5.7.1)\r\n"); logs(s1,s2,s3,s4,s5,s6,s7); } + +/* this file is too long -------------------------------------- Greeting */ stralloc greeting = {0}; @@ -82,21 +191,99 @@ char *local; char *relayclient; +stralloc protocol = {0}; stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ +stralloc tlsinfo = {0}; + +char *helocheck; +int flagbadhelo; +int flagdnshelo; +int seenhelo = 0; + +char *badmailcond; +char *badhelocond; -void dohelo(arg) char *arg; { +void dohelo(arg) char *arg; +{ if (!stralloc_copys(&helohost,arg)) die_nomem(); if (!stralloc_0(&helohost)) die_nomem(); fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; + if (helocheck) { + if (str_len(helocheck) == 1) { + switch (*helocheck) { + case '=': flagbadhelo = bhelocheck(); if (fakehelo) { flagdnshelo = 1; badhelocond = "="; } break; + case 'A': flagdnshelo = dnsq(helohost.s,"A"); badhelocond = "A"; flagbadhelo = bhelocheck(); break; + case 'M': flagdnshelo = dnsq(helohost.s,"M"); badhelocond = "M"; flagbadhelo = bhelocheck(); break; + case '.': flagbadhelo = bhelocheck(); if (!str_len(arg)) flagbadhelo = -2; break; + case '!': if (!str_len(arg)) flagbadhelo = -2; break; + } + } + else + flagbadhelo = bhelocheck(); + } } int liphostok = 0; stralloc liphost = {0}; + int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; +int brtok= 0; +stralloc brt = {0}; +struct constmap mapbrt; + +int badhelook = 0; +stralloc badhelo = {0}; +struct constmap mapbadhelo; + +#ifdef RELAYMAILFROM +int relaymailfromok = 0; +stralloc relaymailfrom = {0}; +struct constmap maprmf; +#endif + +static int fdbmt; +int flagmimetype = 0; + +static int fdblt; +int flagloadertype = 0; +char *badloaderinit; + +static int fdmav; +int flagmav = 0; +int localmf = 0; +int flaglocal = -1; +char *localmfcheck; + +char *mfdnscheck; +char *qhpsi; +char *base64; + +int maxrcptcount = 0; +int flagerrcpts = 0; + +int tarpitcount = 0; +int tarpitdelay = 5; + +unsigned int greetdelay = 0; + +char *auth; +char *reqauth; +int smtpauth = 0; +int seenauth = 0; + +int starttls = 0; +int seenttls = 0; +char *reqttls; +char *tlsversion; +char *cipher; +char *cipherperm; +char *cipherused; +char *clientdn; + void setup() { char *x; @@ -111,17 +298,24 @@ if (timeout <= 0) timeout = 1; if (rcpthosts_init() == -1) die_control(); + if (recipients_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(); + brtok = control_readfile(&brt,"control/badrcptto",0); + if (brtok == -1) die_control(); + if (brtok) + if (!constmap_init(&mapbrt,brt.s,brt.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; - + + if (!stralloc_copys(&protocol,"ESMTP")) die_nomem(); /* RFC 3848 */ remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -131,11 +325,93 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); - dohelo(remotehost); + + mfdnscheck = env_get("MFDNSCHECK"); + + x = env_get("MAXRECIPIENTS"); + if (x) { scan_ulong(x,&u); maxrcptcount = u; }; + if (!(maxrcptcount + 1)) --maxrcptcount; + + helocheck = env_get("HELOCHECK"); + if (helocheck) { + badhelook = control_readfile(&badhelo,"control/badhelo",0); + if (badhelook == -1) die_control(); + if (badhelook) + if (!constmap_init(&mapbadhelo,badhelo.s,badhelo.len,0)) die_nomem(); + } + + 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; }; + x = env_get("GREETDELAY"); + if (x) { scan_ulong(x,&u); greetdelay = u; }; + + localmfcheck = env_get("LOCALMFCHECK"); + if (localmfcheck) { + localmf = 1; + replymav = env_get("REPLYMAV"); + if (str_len(localmfcheck) == 1 && *localmfcheck == '!') { + localmf = 2; + fdmav = open_read("control/mailfromrules.cdb"); + if (fdmav == -1 ) localmf = 1; + } + } + + if (env_get("BADMIMETYPE")) { + flagmimetype = 1; + fdbmt = open_read("control/badmimetypes.cdb"); + if (fdbmt == -1 ) flagmimetype = 0; + } + + badloaderinit = env_get("BADLOADERTYPE"); + if (badloaderinit) { + if (str_len(badloaderinit) == 1) { + flagloadertype = 1; + fdblt = open_read("control/badloadertypes.cdb"); + if (fdblt == -1 ) flagloadertype = 0; + } + } + + base64 = env_get("BASE64"); + reply554 = env_get("REPLY554"); + qhpsi = env_get("QHPSI"); + if (!qhpsi) qhpsi = "unknown"; + + auth = env_get("SMTPAUTH"); + if (auth) { + smtpauth = 2; + if (case_starts(auth,"cram") || case_starts(auth,"CRAM")) smtpauth = 3; + reqauth = env_get("REQUIREAUTH"); + } + + if(env_get("UCSPITLS")) starttls = 1; + if(starttls) reqttls = env_get("REQUIRETLS"); + +#ifdef RELAYMAILFROM + if (!relayclient) { + relaymailfromok = control_readfile(&relaymailfrom,"control/relaymailfrom",0); + if (relaymailfromok == -1) die_control(); + if (relaymailfromok) + if (!constmap_init(&maprmf,relaymailfrom.s,relaymailfrom.len,0)) die_nomem(); + } +#endif + + if (!stralloc_copys(&helohost,"")) die_nomem(); /* helohost is empty */ + if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = 0; } +/* this file is too long -------------------------------------- SMTP ADDRESSES */ stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ +stralloc eddr = {0}; /* extended address; used for smart address recognition */ +stralloc sa = {0}; +ipalloc ia = {0}; int addrparse(arg) char *arg; @@ -151,12 +427,17 @@ i = str_chr(arg,'<'); if (arg[i]) arg += i + 1; +#ifdef REQBRACKETS + else + return 0; +#else else { /* partner should go read rfc 821 */ terminator = ' '; arg += str_chr(arg,':'); if (*arg == ':') ++arg; while (*arg == ' ') ++arg; } +#endif /* strip source route */ if (*arg == '@') while (*arg) if (*arg++ == ':') break; @@ -199,71 +480,489 @@ int bmfcheck() { + int i; int j; - if (!bmfok) return 0; - 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; + int k = 0; + char subvalue; + + if (bmfok) { + + if (!relayclient) { /* '+' address for none-RELAYCLIENTS */ + eddr.len = 0; + if (!stralloc_copyb(&eddr,addr.s,addr.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"+")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + + if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -6; + j = byte_rchr(eddr.s,eddr.len,'@'); + if (j < eddr.len) + if (constmap(&mapbmf,eddr.s + j,eddr.len - j - 1)) return -5; + } + + if (!case_diffs(remotehost,"unknown")) { /* '-' address from UNKNOWN */ + eddr.len = 0; + if (!stralloc_copyb(&eddr,addr.s,addr.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"-")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + + if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -4; + j = byte_rchr(eddr.s,eddr.len,'@'); + if (j < eddr.len) + if (constmap(&mapbmf,eddr.s + j,eddr.len - j - 1)) return -3; + } + + if (constmap(&mapbmf,addr.s,addr.len - 1)) return -2; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return -1; + + i = 0; + for (j = 0;j < bmf.len;++j) + if (!bmf.s[j]) { + subvalue = bmf.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(addr.s,bmf.s + i)) k = subvalue; + i = j + 1; + } + return k; + } return 0; } +int brtcheck() +{ + int i; + int j; + int k = 0; + char subvalue; -int addrallowed() + if (brtok) { + if (constmap(&mapbrt,addr.s,addr.len - 1)) return -2; + j = byte_rchr(addr.s,addr.len,'@'); + if (j < addr.len) + if (constmap(&mapbrt,addr.s + j,addr.len - j - 1)) return -1; + + i = 0; + for (j = 0;j < brt.len;++j) + if (!brt.s[j]) { + subvalue = brt.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(addr.s,brt.s + i)) k = subvalue; + i = j + 1; + } + return k; + } + return 0; +} + +int bhelocheck() +{ + int i; + int j; + int k = 0; + char subvalue; + + if (badhelook) { + + eddr.len = 0; /* helohost! */ + if (!stralloc_copyb(&eddr,helohost.s,helohost.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"!")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + if (constmap(&mapbadhelo,eddr.s,eddr.len - 1)) return -3; + + if (constmap(&mapbadhelo,helohost.s,helohost.len - 1)) return -1; + + i = 0; + for (j = 0;j < badhelo.len;++j) + if (!badhelo.s[j]) { + subvalue = badhelo.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(helohost.s,badhelo.s + i)) k = subvalue; + i = j + 1; + } + return k; + } + return 0; +} +int dnsq(arg,type) char *arg, *type; +{ + unsigned int random; + int i; + int len; + + len = str_len(arg); + if (len < 1) return -2; + + sa.len = 0; + if (arg[len-1] == ' ') len--; /* trailing blank */ + i = byte_rchr(arg,len,'@'); + if (i < len) { + if (!stralloc_copyb(&sa,arg+i+1,len-i-1)) die_nomem(); + } else + if (!stralloc_copyb(&sa,arg,len)) die_nomem(); + + dns_init(0); + random = now() + (getpid() << 16); + switch(*type) { + case 'A': i = dns_ip(&ia,&sa); break; + case 'M': i = dns_mxip(&ia,&sa,random); break; + } + switch(i) { + case DNS_HARD: return 1; + case DNS_SOFT: out("451 DNS temporary failure (#4.3.0)\r\n"); return -1; + case DNS_MEM: die_nomem(); + } + + return 0; +} + +int addrallowed(char *add) { int r; - r = rcpthosts(addr.s,str_len(addr.s)); + r = rcpthosts(add,str_len(add)); if (r == -1) die_control(); return r; } +int rcptallowed() +{ + int r; + r = recipients(addr.s,str_len(addr.s)); + if (r == -2) die_nomem(); + if (r == -1) die_control(); + return r; +} + +int localaddr(char *mf) +{ + int j; + int mflen; + + mflen = str_len(mf); + if (localmf == 2) return mfrules(fdmav,remoteip,remotehost,remoteinfo,mf); + else { + if (str_len(localmfcheck) > 1) { + case_lowerb(localmfcheck,str_len(localmfcheck)); + j = byte_rchr(mf,mflen,'@'); + if (j < mflen) + if (!str_diffn(localmfcheck,mf+j+1,mflen-j-1)) return 2; + } + if(addrallowed(mf)) return 3; + return -2; + } +} int seenmail = 0; int flagbarf; /* defined if seenmail */ +int flagrcpt; +int flagdnsmf; +int flagsize; +int rcptcount = 0; + stralloc mailfrom = {0}; stralloc rcptto = {0}; +stralloc user = {0}; +stralloc fuser = {0}; +stralloc mfparms = {0}; + +int mailfrom_size(arg) char *arg; +{ + long r; + unsigned long sizebytes = 0; + + scan_ulong(arg,&r); + sizebytes = r; + if (databytes) if (sizebytes > databytes) return 1; + return 0; +} + +void mailfrom_auth(arg,len) +char *arg; +int len; +{ + if (!stralloc_copys(&fuser,"")) die_nomem(); + if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } + else + while (len) { + if (*arg == '+') { + if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } + if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } + } + else + if (!stralloc_catb(&fuser,arg,1)) die_nomem(); + arg++; len--; + } + if (!stralloc_0(&fuser)) die_nomem(); + if (!remoteinfo) { + remoteinfo = fuser.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + } +} + +void mailfrom_parms(arg) char *arg; +{ + int i; + int len; + + len = str_len(arg); + if (!stralloc_copys(&mfparms,"")) die_nomem; + i = byte_chr(arg,len,'>'); + if (i > 4 && i < len) { + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); + if (!stralloc_copys(&mfparms,"")) die_nomem; + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem; + } + } +} + +#ifdef RELAYMAILFROM +int rmfcheck() +{ + if (!relaymailfromok) return 0; + if (constmap(&maprmf,addr.s,addr.len - 1)) return 1; + return 0; +} +#endif + +/* this file is too long ----------------------------------------- SMTP DIALOG */ void smtp_helo(arg) char *arg; { smtp_greet("250 "); out("\r\n"); - seenmail = 0; dohelo(arg); + seenmail = 0; rcptcount = 0; seenhelo++; dohelo(arg); } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); - seenmail = 0; dohelo(arg); + char size[FMT_ULONG]; + size[fmt_ulong(size,(unsigned long) databytes)] = 0; + smtp_greet("250-"); out("\r\n"); + out("250-PIPELINING\r\n250-8BITMIME\r\n"); + out("250"); + if (smtpauth || (starttls && !seenttls)) out("-"); + else out(" "); + out("SIZE "); out(size); out("\r\n"); + if (smtpauth) { + if(starttls && !seenttls) out("250-"); + else out("250 "); + if (smtpauth <= 2) out("AUTH LOGIN PLAIN\r\n"); + if (smtpauth == 3) out("AUTH LOGIN PLAIN CRAM-MD5\r\n"); + } + if (starttls && !seenttls) out("250 STARTTLS\r\n"); + seenhelo++; seenmail = 0; rcptcount = 0; dohelo(arg); } void smtp_rset() { - seenmail = 0; + seenmail = 0; rcptcount = 0; seenauth = 0; seenttls = 0; + mailfrom.len = 0; rcptto.len = 0; tlsinfo.len = 0; out("250 flushed\r\n"); } +void smtp_starttls() +{ + if (starttls != 1) err_unimpl; + out("220 Ready to start TLS (#5.7.0)\r\n"); + flush(); + + if(!ucspitls()) die_starttls(); + seenttls = 1; + + if(!ucspitlsinfo()) die_starttls(); + + tlsversion = env_get("SSL_PROTOCOL"); + if (!tlsversion) tlsversion = "unknown"; + cipher = env_get("SSL_CIPHER"); + if (!cipher) cipher = "unknown"; + cipherperm = env_get("SSL_CIPHER_ALGKEYSIZE"); + if (!cipherperm) cipherperm = "unknown"; + cipherused = env_get("SSL_CIPHER_USEKEYSIZE"); + if (!cipherused) cipherused = "unknown"; + clientdn = env_get("SSL_CLIENT_S_DN"); + if (!clientdn) clientdn = "unknown"; + + if (!stralloc_copys(&tlsinfo,tlsversion)) die_nomem(); + if (!stralloc_cats(&tlsinfo,": ")) die_nomem(); + if (!stralloc_cats(&tlsinfo,cipher)) die_nomem(); + if (!stralloc_cats(&tlsinfo," [")) die_nomem(); + if (!stralloc_cats(&tlsinfo,cipherused)) die_nomem(); + if (!stralloc_cats(&tlsinfo,"/")) die_nomem(); + if (!stralloc_cats(&tlsinfo,cipherperm)) die_nomem(); + if (!stralloc_cats(&tlsinfo,"] ")) die_nomem(); + if (!stralloc_cats(&tlsinfo,"DN=")) die_nomem(); + if (!stralloc_cats(&tlsinfo,clientdn)) die_nomem(); + if (!stralloc_0(&tlsinfo)) die_nomem(); + + if (!stralloc_cats(&protocol,"S")) die_nomem(); + logb("Accept::SNDR::Start_TLS:"," P:ESMTPS",remoteip,remotehost,helohost.s,clientdn,cipher); + + /* reset SMTP state */ + seenhelo = 0; seenmail = 0; rcptcount = 0; seenauth = 0; + helohost.len = 0; mailfrom.len = 0; rcptto.len = 0; +} void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); - seenmail = 1; + flagsize = 0; + rcptcount = 0; + mailfrom_parms(arg); + seenmail++; + if (relayclient) { + if(localmf) flagmav = localaddr(addr.s); + switch(flagmav) { + case -9: die_nomem(); break; + case 2: if (!stralloc_cats(&protocol,"M")) die_nomem(); break; + default: break; + } + } + else { +#ifdef RELAYMAILFROM + if (rmfcheck()) { relayclient = ""; flaglocal = -2; } else relayclient = 0; +#endif + } if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); + flagbarf = bmfcheck(); + if (mfdnscheck) flagdnsmf = dnsq(mailfrom.s,"M"); + if (!stralloc_0(&protocol)) 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 (relayclient) { + rcptcount++; + +/* this file is too long --------------------------------- Sesssion checks */ + + if (reqttls) /* STTARTTLS rejects */ + if (!seenttls) { + err_tlsreq("Reject::SNDR::Missing_TLS:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (reqauth) /* Auth rejects */ + if (!seenauth) { + err_authreq("Reject::ORIG::Missing_Auth:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + +/* this file is too long --------------------------------- Split Horizon envelope checks */ + + if (!relayclient) { + if (!seenhelo && helocheck) /* Helo rejects */ + if (str_len(helocheck) == 1) { + err_helo("Reject::SNDR::Bad_Helo:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (flagbadhelo) { + switch(flagbadhelo) { + case -2: badhelocond = "!"; break; + case -1: badhelocond = "."; break; + default: badhelocond = "*"; break; + } + err_helo("Reject::SNDR::Bad_Helo:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badhelocond); + return; + } + if (flagdnshelo > 0) { + err_helo("Reject::SNDR::DNS_Helo:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badhelocond); + return; + } + if (flagdnsmf > 0) { /* Mail from rejects */ + err_mfdns("Reject::ORIG::DNS_MF:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (!addrallowed(addr.s)) { /* Relaying rejects */ + err_nogateway("Reject::SNDR::Invalid_Relay:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + flagrcpt = rcptallowed(); /* Rcpt to rejects */ + if (!flagrcpt) { + err_recipient("Reject::RCPT::Failed_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + flagerrcpts++; + return; + } + if (tarpitcount && flagerrcpts >= tarpitcount) { /* Tarpitting et al. */ + err_rcpts("Reject::RCPT::Toomany_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (maxrcptcount && rcptcount > maxrcptcount) { + err_rcpts("Reject::RCPT::Toomany_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (tarpitcount && rcptcount >= tarpitcount) { + unsigned int rest = tarpitdelay; + while (rest = sleep(rest)); + } + } + +/* this file is too long --------------------------------- Local checks */ + + else { + if (flagmav < 0) { + err_mav("Reject::ORIG::Invalid_Mailfrom:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); + } + +/* this file is too long --------------------------------- Common checks */ + + if (flagbarf) { + switch(flagbarf) { + case -1: badmailcond = "@"; break; + case -2: badmailcond = "@"; break; + case -3: badmailcond = "-"; break; + case -4: badmailcond = "-"; break; + case -5: badmailcond = "+"; break; + case -6: badmailcond = "+"; break; + default: badmailcond = "*"; break; + } + err_bmf("Reject::ORIG::Bad_Mailfrom:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmailcond); + return; } - else - if (!addrallowed()) { err_nogateway(); return; } + + flagrcpt = brtcheck(); + if (flagrcpt) { + err_brcptto("Reject::RCPT::Bad_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (flagsize) { + err_size(); + logs("Reject::DATA::Invalid_Size:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + +/* this file is too long --------------------------------- Checks done; mailfrom/recipient accepted */ + if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); out("250 ok\r\n"); -} +/* this file is too long --------------------------------- Additional logging */ + + if (flaglocal > 0) + logs("Accept::ORIG::Local_Sender:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + else if (relayclient) + if (flaglocal == -2) + logs("Accept::ORIG::Relay_Mailfrom:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + else + logs("Accept::SNDR::Relay_Client:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + else + switch(flagrcpt) { + case 1: logs("Accept::RCPT::Recipients_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break; + case 2: logs("Accept::RCPT::Recipients_Verp:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break; + case 3: logs("Accept::RCPT::Recipients_Domain:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break; + default: logs("Accept::RCPT::Rcpthosts_Rcptto:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); break; + } +} int saferead(fd,buf,len) int fd; char *buf; int len; { @@ -279,11 +978,69 @@ substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); struct qmail qqt; -unsigned int bytestooverflow = 0; +unsigned long bytestooverflow = 0; + +stralloc line = {0}; +stralloc base64types = {0}; +stralloc badmimetype = {0}; +stralloc badloadertype = {0}; + +unsigned int nolines = 0; +unsigned int flag64 = 0; +unsigned int flagbase = 0; +unsigned int flagblank = 0; void put(ch) char *ch; { + uint32 dlen; + int i; + + if (line.len < 1025) + if (!stralloc_catb(&line,ch,1)) die_nomem(); /* Reassamble chars to line; prepend with 'L' */ + + if (*ch == '\n') { + nolines++; + + if (line.len == 2) { flagblank = nolines; flagbase = 0; } /* trigger */ + if (*(line.s+1) == 'C' || *(line.s+1) == 'c') + if (case_startb(line.s+1,line.len-2,"content-transfer-encoding: base64")) flag64 = nolines; /* base64 attachments */ + if (flag64 && nolines == flagblank+1 && line.len > MIMETYPE_LEN+2) { + flagbase = nolines; + if (!stralloc_catb(&base64types,line.s+1,MIMETYPE_LEN)) die_nomem(); + if (!stralloc_0(&base64types)) die_nomem(); + if (flagmimetype == 1) { + if (cdb_seek(fdbmt,line.s+1,MIMETYPE_LEN,&dlen)) { + if (!stralloc_copyb(&badmimetype,line.s+1,MIMETYPE_LEN)) die_nomem(); + if (!stralloc_0(&badmimetype)) die_nomem(); + if (!stralloc_cats(&rcptto,"M")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + qmail_fail(&qqt); + flagmimetype = -1; + } + } + } + + if (flagloadertype == 1 && flagmimetype != -1) { + if (line.len > MIMETYPE_LEN + 2 && flagbase > flagblank) { + for ( i = 0; i < line.len - LOADER_LEN; ++i ) { + if (*(line.s+i) == *badloaderinit) { + if (cdb_seek(fdblt,line.s+i,LOADER_LEN,&dlen)) { + if (!stralloc_copyb(&badloadertype,line.s+i,LOADER_LEN)) die_nomem(); + if (!stralloc_0(&badloadertype)) die_nomem(); + if (!stralloc_cats(&rcptto,"L")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + qmail_fail(&qqt); + flagloadertype = -1; + } + } + } + } + } + line.len = 0; + if(!stralloc_copys(&line,"L")) die_nomem(); + } + if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qqt); @@ -316,8 +1073,8 @@ if (flagmaybex) if (pos == 7) ++*hops; if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; if (flagmaybey) if (pos == 1) flaginheader = 0; + ++pos; } - ++pos; if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } } switch(state) { @@ -378,26 +1135,251 @@ qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + received(&qqt,protocol.s,local,remoteip,remotehost,remoteinfo,fakehelo,tlsinfo.s); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); + if (base64 && base64types.len == 0) { + if (!stralloc_cats(&rcptto,"Q")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + } qmail_from(&qqt,mailfrom.s); qmail_put(&qqt,rcptto.s,rcptto.len); 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 (*qqx == 'D') out("554 "); else out("451 "); - out(qqx + 1); + if (databytes) + if (!bytestooverflow) { + err_size(); + logs("Reject::DATA::Invalid_Size:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (flagmimetype == -1) { + err_data("Reject::DATA::Bad_MIME:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmimetype.s); + return; + } + if (flagloadertype == -1) { + err_data("Reject::DATA::Bad_Loader:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badloadertype.s); + return; + } + if (*qqx == 'D') { + err_data("Reject::DATA::Virus_Infected:",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,qhpsi); + return; + } + else { out("451 "); out(qqx + 1); out("\r\n"); } +} + + +/* this file is too long ----------------------------------------- SMTP AUTH */ + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; /* input from SMTP client */ +static stralloc pass = {0}; /* plain passwd or digest */ +static stralloc resp = {0}; /* b64 response */ +static stralloc chal = {0}; /* CRAM-MD5 plain challenge */ +static stralloc slop = {0}; /* CRAM-MD5 b64 challenge */ + +char **childargs; +char ssauthbuf[512]; +substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); + +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(&chal)) die_nomem(); + + if (pipe(pi) == -1) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if (fd_copy(3,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); + if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); + if (smtpauth == 3) if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); + if (substdio_flush(&ssauth) == -1) return err_write(); + + close(pi[1]); + if (!stralloc_copys(&chal,"")) die_nomem(); + if (!stralloc_copys(&slop,"")) die_nomem(); + byte_zero(ssauthbuf,sizeof ssauthbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); 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),&resp) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ + + if (resp.len > id + 1) + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); + if (resp.len > id + user.len + 2) + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_cram() +{ + int i, r; + char *s; + + s = unique; /* generate challenge */ + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (!stralloc_copys(&chal,"<")) die_nomem(); + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,local)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); /* "334 base64_challenge \r\n" */ + out(slop.s); out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; /* got response */ + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + + i = str_chr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { { "login",auth_login }, { "plain",auth_plain }, { "cram-md5",auth_cram }, { 0,err_noauth } }; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } + if (seenauth) { 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(); + if (!stralloc_copys(&chal,"")) die_nomem(); /* only needed for CRAM-MD5 */ + + i = str_chr(cmd,' '); /* get AUTH type */ + 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: + seenauth = 1; + if (!stralloc_cats(&protocol,"A")) die_nomem(); + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + loga("Accept::ORIG::Valid_Auth:"," P:EMSTPA",remoteip,remotehost,helohost.s,user.s,authcmds[i].text); + break; + case 1: + err_authfail("Reject::ORIG::Failed_Auth:"," P:ESMTPA",remoteip,remotehost,helohost.s,user.s,authcmds[i].text); + } } +/* this file is too long --------------------------------------------- GO ON */ + 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 } @@ -405,15 +1387,20 @@ , { "help", smtp_help, flush } , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } +, { "starttls", smtp_starttls, flush } , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) +int argc; +char **argv; { + childargs = argv + 1; sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); if (ipme_init() != 1) die_ipme(); + if (greetdelay) sleep(greetdelay); smtp_greet("220 "); out(" ESMTP\r\n"); if (commands(&ssin,&smtpcommands) == 0) die_read();