diff options
author | Jannis Hoffmann <jannis@fehcom.de> | 2024-07-09 11:44:11 +0200 |
---|---|---|
committer | Jannis Hoffmann <jannis@fehcom.de> | 2024-07-09 11:44:11 +0200 |
commit | f1b71c9fe7dbb4886588a036399cf5ebe16b7c47 (patch) | |
tree | e07786aa479c9fb6ee3e537078470aaab5454f80 /sqmail-4.3.07/src/qmail-smtpd.c | |
parent | a293489ee83c8b05d845a162dc2a4de026f3775d (diff) |
removed top level directory
Diffstat (limited to 'sqmail-4.3.07/src/qmail-smtpd.c')
-rwxr-xr-x | sqmail-4.3.07/src/qmail-smtpd.c | 1720 |
1 files changed, 0 insertions, 1720 deletions
diff --git a/sqmail-4.3.07/src/qmail-smtpd.c b/sqmail-4.3.07/src/qmail-smtpd.c deleted file mode 100755 index b629948..0000000 --- a/sqmail-4.3.07/src/qmail-smtpd.c +++ /dev/null @@ -1,1720 +0,0 @@ -#include <unistd.h> -#include "wildmat.h" -#include "buffer.h" -#include "stralloc.h" -#include "genalloc.h" -#include "alloc.h" -#include "qmail.h" -#include "auto_qmail.h" -#include "control.h" -#include "received.h" -#include "constmap.h" -#include "logmsg.h" -#include "ipme.h" -#include "fd.h" -#include "ip.h" -#include "qmail.h" -#include "str.h" -#include "fmt.h" -#include "scan.h" -#include "byte.h" -#include "case.h" -#include "env.h" -#include "now.h" -#include "exit.h" -#include "rcpthosts.h" -#include "recipients.h" -#include "mfrules.h" -#include "tls_start.h" -#include "smtpdlog.h" -#include "timeout.h" -#include "commands.h" -#include "cdbread.h" -#include "dns.h" -#include "wait.h" -#include "sig.h" -#include "close.h" -#include "open.h" -#include "base64.h" -#include "spf.h" - -/** @file qmail-smtpd.c -- authenticating ESMTP/ESMTPS server - @brief requires sslserver or tcpserver */ - -#define PAM111421 -#define AUTHSLEEP 5 -#define PORT_SMTPS "465" -#define SMTP_TIMEOUT 1200 - -#define MIMETYPE_LEN 9 -#define LOADER_LEN 5 -#define BASE64MESSAGE "content-transfer-encoding: base64" -#define FDIN 0 -#define FDOUT 1 -#define FDLOG 2 -#define FDAUTH 3 - -#define MAXHOPS 100 -unsigned long databytes = 0; -int timeout = SMTP_TIMEOUT; - -int modssl_info(); - -ssize_t safewrite(int fd,char *buf,int len) -{ - int r; - r = timeoutwrite(timeout,fd,buf,len); - if (r <= 0) _exit(1); - return r; -} - -ssize_t saferead(int fd,char *buf,int len) -{ - int r; - flush(); - r = timeoutread(timeout,fd,buf,len); - if (r == -1) if (errno == ETIMEDOUT) die_alarm(); - if (r <= 0) die_read(); - return r; -} - -char inbuf[BUFSIZE_LINE]; -buffer bi = BUFFER_INIT(saferead,FDIN,inbuf,sizeof(inbuf)); - -char outbuf[BUFSIZE_LINE]; -buffer bo = BUFFER_INIT(safewrite,FDOUT,outbuf,sizeof(outbuf)); - -char buflog[BUFSIZE_LOG]; -buffer bl = BUFFER_INIT(write,FDLOG,buflog,sizeof(buflog)); - -void flush() { buffer_flush(&bo); } // this triggers writing to STDIO -void out(char *s) { buffer_puts(&bo,s); } - -stralloc sa = {0}; -ipalloc ia = {0}; - -int bhelocheck(void); - -/* this file is too long -------------------------------------- DNS helper */ - -int dnsq(char *arg,char type) -{ - unsigned int random; - int at; - int r = -2; - int len; - - len = str_len(arg); - if (len < 1) return r; - - if (arg[len-1] == ' ') len--; /* trailing blank */ - if (len < 1) return r; - - at = byte_rchr(arg,len,'@'); - if (at < len) { - if (!stralloc_copyb(&sa,arg + at + 1,len - at - 1)) die_nomem(); - } else - if (!stralloc_copyb(&sa,arg,len)) die_nomem(); - - random = now() + (getpid() << 16); - - switch (type) { /* Common for IPv4 and IPv6 */ - case 'A': r = dns_ip(&ia,&sa); break; - case 'M': r = dns_mxip(&ia,&sa,random); break; - } - - switch (r) { - case DNS_ERR: out("451 DNS temporary failure (#4.3.0)\r\n"); return -1; - case DNS_MEM: die_nomem(); - default: if (ia.len) return 0; - } - - return 1; -} - -/* this file is too long -------------------------------------- Greeting */ - -static stralloc greeting = {0}; - -void smtp_greet(char *code) -{ - buffer_puts(&bo,code); - buffer_put(&bo,greeting.s,greeting.len); -} -void smtp_help() -{ - out("214 s/qmail home page: https://www.fehcom.de/sqmail.html\r\n"); -} -void smtp_quit() -{ - smtp_greet("221 "); out("\r\n"); flush(); _exit(0); -} - -char *remoteip; -char *remotehost; -char *remoteinfo; -char *local; -char *localport; -char *relayclient; -int flagutf8 = 0; - -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(char *helo) -{ - if (!stralloc_copys(&helohost,helo)) 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': flagbadhelo = bhelocheck(); - if (flagbadhelo == 0) { flagdnshelo = dnsq(helohost.s,'A'); badhelocond = "A"; } - break; - case 'M': flagbadhelo = bhelocheck(); - if (flagbadhelo == 0) { flagdnshelo = dnsq(helohost.s,'M'); badhelocond = "M"; } - break; - case '.': flagbadhelo = bhelocheck(); - if (!str_len(helo)) flagbadhelo = -2; - break; - case '!': if (!str_len(helo)) flagbadhelo = -2; - break; - } - } else { - flagbadhelo = bhelocheck(); - } - if (flagbadhelo == -3) flagbadhelo = 0; - } - if (!env_put("HELOHOST",helohost.s)) die_nomem(); -} - -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 mapbhlo; - -static struct cdb cdbm; -static struct cdb cdbl; - -static int fdbmt; -int flagmimetype = 0; /* 1: white; 2: cdb; 3: white+cdb; 4: !relay+white; 6: !relay+white+cdb; -1: found in cdb; -2: found white */ -char *badmimeinit; - -static int fdblt; -int flagloadertype = 0; /* 1: cdb; 2: !relay+cdb; -1: found in cdb */ -char *badloaderinit; - -static int fdmav; -int flagmav = 0; -int localmf = 0; /* 1: domainpart->rcpthosts; 2: ->mailformrules; 3: ->remoteinfo; 4: ->DN(Email) */ -char *localmfcheck; - -char *mfdnscheck; -char *qhpsi; -char *base64; - -int maxrcptcount = 0; -int flagerrcpts = 0; -int flagnotorious = 0; - -int tarpitcount = 0; -int tarpitdelay = 0; - -int greylist = 0; -stralloc pgbind = {0}; - -char *auth; -int smtpauth = 0; /* -1:Cert 0:none 1:login/plain 2:cram 3:login/plain/cram 11:must_login/plain 12:must_2 13:must_3 */ -int seenauth = 0; /* 1:ESMTPA 2:~CLIENTDN */ -stralloc authmethod = {0}; - -int starttls = 0; /* -1:TLS 0:none 1:STARTTLS 2:require_STARTTLS 3:relay_if_CLIENTDN 4:require_+_relay_if_CLIENTDN */ -int seentls = 0; /* 1:~STARTTLS 2:~TLS 3:~CLIENTDN */ -char *ucspitls = 0; -char *tlsversion; -char *cipher; -char *cipherperm; -char *cipherused; -char *clientdn; -char *clientcn; -char *dnemail; - -stralloc mailto = {0}; -stralloc deliverto = {0}; -char *delivermailto; -stralloc rblinfo = {0}; -char *rblsmtpd; - -int flagspf = 0; -static stralloc spfbounce = {0}; - -void setup() -{ - char *x; - unsigned long u; - int r; - flagip6 = 1; // GCC 10 implicit int declarition if global var - - if (control_init() == -1) die_control(); - if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) - die_control(); - liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0); - if (liphostok == -1) die_control(); - if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); - 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("TCP6REMOTEIP"); /* compactified IPv6 */ - if (!remoteip) remoteip = env_get("TCPREMOTEIP"); /* allow other tcpserver/sslserver */ - if (remoteip && (str_chr(remoteip,':') < str_len(remoteip))) { - if (byte_equal(remoteip,7,V4MAPPREFIX)) - { remoteip = remoteip + 7; flagip6 = 0; } - } else flagip6 = 0; - if (!remoteip) { remoteip = "unknown"; flagip6 = -1; } - local = env_get("TCP6LOCALHOST"); - if (!local) local = env_get("TCPLOCALHOST"); - if (!local) local = env_get("TCP6LOCALIP"); - if (!local) local = env_get("TCPLOCALIP"); - if (!local) local = "unknown"; - localport = env_get("TCP6LOCALPORT"); - if (!localport) localport = env_get("TCPLOCALPORT"); - if (!localport) localport = "0"; - remotehost = env_get("TCP6REMOTEHOST"); - if (!remotehost) remotehost = env_get("TCPREMOTEHOST"); - if (!remotehost) remotehost = "unknown"; - remoteinfo = env_get("TCP6REMOTEINFO"); - if (!remoteinfo) remoteinfo = env_get("TCPREMOTEINFO"); - relayclient = env_get("RELAYCLIENT"); - - if (!case_diffs(localport,PORT_SMTPS)) { - if (!modssl_info()) die_starttls(); - starttls = -1; - } - - 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(&mapbhlo,badhelo.s,badhelo.len,0)) die_nomem(); - } - - x = env_get("TARPITCOUNT"); - if (x) { scan_ulong(x,&u); tarpitcount = u; }; - x = env_get("TARPITDELAY"); - if (x) { scan_ulong(x,&u); tarpitdelay = u; }; - - x = env_get("POSTGREY"); // RFC 6647 - if (x) { - if (case_diffs(x,"-")) { - greylist = 1; - if (!stralloc_copys(&pgbind,x)) die_nomem(); - if (!stralloc_append(&pgbind,"")) die_nomem(); - } - } - - localmfcheck = env_get("LOCALMFCHECK"); - if (localmfcheck) { - localmf = 1; - if (str_len(localmfcheck) == 1 && *localmfcheck == '!') { - localmf = 2; - fdmav = open_read("control/mailfromrules.cdb"); - if (fdmav == -1 ) localmf = 1; - } - if (str_len(localmfcheck) == 1 && *localmfcheck == '=') { - localmf = 3; - } - if (str_len(localmfcheck) == 1 && *localmfcheck == '?') { - localmf = 4; - } - } - - badmimeinit = env_get("BADMIMETYPE"); - if (badmimeinit) { - fdbmt = open_read("control/badmimetypes.cdb"); - if (str_len(badmimeinit) == 1) { - if (*badmimeinit == '-') - flagmimetype = 0; - else { - if (*badmimeinit == '!') flagmimetype = 1; - if (*badmimeinit == '+') flagmimetype = 4; - } - } - if (fdbmt != -1 ) flagmimetype = flagmimetype + 2; - } - - badloaderinit = env_get("BADLOADERTYPE"); - if (badloaderinit) { - if (str_len(badloaderinit) == 1) { - if (*badloaderinit == '-') - flagloadertype = 0; - else { - flagloadertype = 1; - if (*badloaderinit == '+') flagloadertype = 2; - fdblt = open_read("control/badloadertypes.cdb"); - if (fdblt == -1 ) flagloadertype = 0; - } - } - } - - base64 = env_get("BASE64"); - qhpsi = env_get("QHPSI"); - auth = env_get("SMTPAUTH"); - if (auth) { - smtpauth = 1; - if (!case_diffs(auth,"-")) smtpauth = 0; - if (!case_diffs(auth,"!")) smtpauth = 11; - if (case_starts(auth,"cram")) smtpauth = 2; - if (case_starts(auth,"+cram")) smtpauth = 3; - if (case_starts(auth,"!cram")) smtpauth = 12; - if (case_starts(auth,"!+cram")) smtpauth = 13; - } - - if (!seentls) { - ucspitls = env_get("UCSPITLS"); - if (ucspitls) { - starttls = 1; - if (!case_diffs(ucspitls,"-")) starttls = 0; - if (!case_diffs(ucspitls,"!")) starttls = 2; - if (!case_diffs(ucspitls,"?")) starttls = 3; - if (!case_diffs(ucspitls,"@")) starttls = 4; - } - } - - delivermailto = env_get("DELIVERTO"); - if (delivermailto) { - if (!stralloc_cats(&mailto,delivermailto)) die_nomem(); - if (!stralloc_cats(&mailto," ")) die_nomem(); - } - - rblsmtpd = env_get("RBLSMTPD"); - if (rblsmtpd) { - if (!stralloc_cats(&rblinfo,rblsmtpd)) die_nomem(); - if (!stralloc_0(&rblinfo)) die_nomem(); - } - - x = env_get("SPF"); - if (x) { scan_ulong(x,&u); flagspf = u; } - if (flagspf < 0 || flagspf > 6) die_control(); - if (flagspf) { - r = control_readline(&spflocalrules,"control/spflocalrules"); - if (r == -1) die_control(); - if (!stralloc_0(&spflocalrules)) die_nomem(); - if (control_rldef(&spfexplain,"control/spfexplain",0,SPF_DEFEXP) == -1) die_control(); - if (!stralloc_0(&spfexplain)) die_nomem(); - } - - x = env_get("UTF8"); - if (x) flagutf8 = 1; - - if (!stralloc_copys(&helohost,"")) die_nomem(); // helohost is initially empty - if (!stralloc_0(&helohost)) die_nomem(); - fakehelo = 0; - -} - -void auth_info(char *method) -{ - if (!env_put("AUTHPROTOCOL",method)) die_nomem(); - if (!env_put("AUTHUSER",remoteinfo)) die_nomem(); - if (!env_unset("TCPREMOTEINFO")) die_read(); - if (!env_put("TCPREMOTEINFO",remoteinfo)) die_nomem(); - if (!env_unset("TCP6REMOTEINFO")) die_read(); - if (!env_put("TCP6REMOTEINFO",remoteinfo)) die_nomem(); - - if (!stralloc_append(&protocol,"A")) die_nomem(); -} - -int modssl_info() -{ - tlsversion = env_get("SSL_PROTOCOL"); - if (!tlsversion) return 0; - - 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 = "none"; - else { - seentls = 3; - seenauth = 2; - smtpauth = -1; - relayclient = ""; - } - - 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,"] \n")) die_nomem(); - if (!stralloc_cats(&tlsinfo," DN=")) die_nomem(); - if (!stralloc_cats(&tlsinfo,clientdn)) die_nomem(); - if (!stralloc_0(&tlsinfo)) die_nomem(); - - if (!stralloc_append(&protocol,"S")) die_nomem(); - - if (seentls == 3 && starttls == 4) { - clientcn = env_get("SSL_CLIENT_S_DN_CN"); - remoteinfo = clientcn ? clientcn : clientdn; - dnemail = env_get("SSL_CLIENT_S_DN_Email"); - if (!dnemail) dnemail = "unknown"; - if (!stralloc_cats(&authmethod,"cert")) die_nomem(); - auth_info(authmethod.s); - } - return 1; -} - -/* this file is too long --------------------------------- Address checks */ - -stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ -stralloc eddr = {0}; /* extended address; used for smart address recognition */ -stralloc rddr = {0}; /* test anti-spoofing host name */ -stralloc mailfrom = {0}; -stralloc rcptto = {0}; -stralloc user = {0}; -stralloc fuser = {0}; -stralloc mfparms = {0}; - -int seenmail = 0; -int flagaddr; /* defined if seenmail */ -int flagrcpt; -int flagdnsmf = 0; -int flagsize; -int rcptcount = 0; - -int addrparse(char *arg) -{ - int i; - char ch; - char terminator; - struct ip4_address ip4s; - struct ip6_address ip6s; - int flagesc; - int flagquoted; - - terminator = '>'; - i = str_chr(arg,'<'); - if (arg[i]) - arg += i + 1; - else - return 0; - - /* strip source route */ - if (*arg == '@') while (*arg) if (*arg++ == ':') break; - - if (!stralloc_copys(&addr,"")) die_nomem(); - flagesc = 0; - flagquoted = 0; - for (i = 0; ch = arg[i]; ++i) { /* copy arg to addr, stripping quotes */ - if (flagesc) { - if (!stralloc_append(&addr,&ch)) die_nomem(); - flagesc = 0; - } - else { - if (!flagquoted && (ch == terminator)) break; - switch (ch) { - case '\\': flagesc = 1; break; - case '"': flagquoted = !flagquoted; break; - default: if (!stralloc_append(&addr,&ch)) die_nomem(); - } - } - } - /* could check for termination failure here, but why bother? */ - if (!stralloc_append(&addr,"")) die_nomem(); - - if (liphostok) { - i = byte_rchr(addr.s,addr.len,'@'); - if (i < addr.len) /* if not, partner should go read rfc 821 */ - if (addr.s[i + 1] == '[') { - if (byte_rchr(addr.s + i + 2,addr.len - i - 2,':') < str_len(addr.s)) { /* @[IPv6::] */ - if (!addr.s[i + 1 + ip6_scanbracket(addr.s + i + 1,(char *)&ip6s.d)]) - if (ipme_is6(&ip6s)) { - addr.len = i + 1; - if (!stralloc_cat(&addr,&liphost)) die_nomem(); - } - } else { /* @[IPv4] */ - if (!addr.s[i + 1 + ip4_scanbracket(addr.s + i + 1,(char *)&ip4s.d)]) - if (ipme_is4(&ip4s)) { - addr.len = i + 1; - if (!stralloc_cat(&addr,&liphost)) die_nomem(); - } - } - } - if (!stralloc_0(&addr)) die_nomem(); - } - - if (addr.len > 900) return 0; - return 1; -} - -int bhelocheck() -{ - int i; - int j; - int k = 0; - char subvalue; - - if (badhelook && helohost.len > 1) { /* 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(&mapbhlo,eddr.s,eddr.len - 1)) return -3; - - if (constmap(&mapbhlo,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 bmfcheck() -{ - int i = 0; - int j = 0; - int k = 0; - int at = 0; - int dlen; - int rlen; - char subvalue; - - if (bmfok && mailfrom.len > 1) { - rlen = str_len(remotehost); - at = byte_rchr(mailfrom.s,mailfrom.len,'@'); - -/* '?' enhanced address to skip all other tests including MFDNSCHECK */ - - if (!stralloc_copys(&eddr,"?")) die_nomem(); - if (!stralloc_cat(&eddr,&mailfrom)) die_nomem(); - case_lowerb(eddr.s,eddr.len); - if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -110; - -/* '+' extended address for none-RELAYCLIENTS */ - - if (at && !relayclient) { - if (!stralloc_copyb(&eddr,mailfrom.s,mailfrom.len - 1)) die_nomem(); - if (!stralloc_append(&eddr,"+")) die_nomem(); - if (!stralloc_0(&eddr)) die_nomem(); - case_lowerb(eddr.s,eddr.len); - if (constmap(&mapbmf,eddr.s + at,eddr.len - at - 1)) return -5; - } - -/* '-' extended address from UNKNOWN */ - - if (at && !case_diffs(remotehost,"unknown")) { - if (!stralloc_copyb(&eddr,mailfrom.s,mailfrom.len - 1)) die_nomem(); - if (!stralloc_append(&eddr,"-")) die_nomem(); - if (!stralloc_0(&eddr)) die_nomem(); - case_lowerb(eddr.s,eddr.len); - if (constmap(&mapbmf,eddr.s + at,eddr.len - at - 1)) return -4; - } - -/* '=' extended address for WELLKNOWN senders */ - - else if (at && rlen >= mailfrom.len - at - 1) { - dlen = mailfrom.len - at - 2; - if (!stralloc_copyb(&eddr,mailfrom.s,mailfrom.len - 1)) die_nomem(); - if (!stralloc_append(&eddr,"=")) die_nomem(); - if (!stralloc_0(&eddr)) die_nomem(); - case_lowerb(eddr.s,eddr.len); - if (str_diffn(remotehost + rlen - dlen,eddr.s + at + 1,dlen)) - if (constmap(&mapbmf,eddr.s + at,eddr.len - at - 1)) return -3; - -/* '~' extended address for MISMATCHED Domains */ - - if (case_diffrs(remotehost,mailfrom.s + at + 1)) { - j = 0; - do { - if (!stralloc_copys(&eddr,"~")) die_nomem(); - if (!stralloc_cats(&eddr,remotehost + j)) die_nomem(); - if (!stralloc_0(&eddr)) die_nomem(); - if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -2; - j = byte_chr(remotehost + j,rlen - j,'.') + j + 1; - } while (j > 0 && rlen - j > 0); - } - } - -/* Standard */ - - if (constmap(&mapbmf,mailfrom.s,mailfrom.len - 1)) return -1; - if (at && at < mailfrom.len) - if (constmap(&mapbmf,mailfrom.s + at,mailfrom.len - at - 1)) return -1; - -/* Wildmating */ - - i = k = 0; - for (j = 0; j < bmf.len; ++j) { - if (!bmf.s[j]) { - subvalue = bmf.s[i] != '!'; - if (!subvalue) i++; - if ((k != subvalue) && wildmat(mailfrom.s,bmf.s + i)) k = subvalue; - i = j + 1; - } - } - return k; - } - - return 0; -} - -int brtcheck() -{ - int i; - int j = 0; - int k = 0; - char subvalue; - - if (brtok) { - if (constmap(&mapbrt,addr.s,addr.len - 1)) return -2; - - int at = byte_rchr(addr.s,addr.len,'@'); - if (at < addr.len) - if (constmap(&mapbrt,addr.s + at,addr.len - at - j)) return -1; - -/* '#' enhanced address to consider invalid rcptto addresses for none-relayclients */ - - if (!relayclient) { - if (!stralloc_copys(&eddr,"+")) die_nomem(); - if (!stralloc_cat(&eddr,&addr)) die_nomem(); - if (!stralloc_0(&eddr)) die_nomem(); - case_lowerb(eddr.s,eddr.len); - if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return 110; - } - - 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 addrallowed(char *arg) -{ - int r; - r = rcpthosts(arg,str_len(arg)); - if (r == -1) die_control(); - return r; -} - -int rcptallowed() -{ - int r; - r = recipients(addr.s,str_len(addr.s)); -#ifdef PAM111421 - if (r == 111) die_recipients(); -#endif - if (r == -3) die_recipients(); - if (r == -2) die_nomem(); - if (r == -1) die_control(); - return r; -} - -int localaddr(char *mf) -{ - int at; - int mflen; - - mflen = str_len(mf); - if (mflen < 1 ) return 0; - - if (localmf == 4) { - if (!case_diffs(dnemail,mf)) return 2; - return -4; - } - if (localmf == 3) { - if (!case_diffs(remoteinfo,mf)) return 2; - return -3; - } - else if (localmf == 2) - return mfrules(fdmav,remoteip,remotehost,remoteinfo,mf); - else { - if (str_len(localmfcheck) > 1) { - case_lowerb(localmfcheck,str_len(localmfcheck)); - at = byte_rchr(mf,mflen,'@'); - if (at < mflen) - if (!str_diffn(localmfcheck,mf + at + 1,mflen - at - 1)) return 2; - } - if (addrallowed(mf)) return 3; - return -2; - } -} - -int spf_check(int flag6) -{ - int r; - - if (mailfrom.len <= 1) { flagspf = 0; return 0; } - - DNS_INIT - r = spf_query(remoteip,helohost.s,mailfrom.s,local,flag6); - if (r == SPF_NOMEM) die_nomem(); - if (!stralloc_0(&spfinfo)) die_nomem(); - - switch (r) { - case SPF_ME: - case SPF_OK: - if (!env_put("SPFRESULT","pass")) die_nomem(); - flagspf = 10; - break; - case SPF_LOOP: - case SPF_ERROR: - case SPF_SYNTAX: - case SPF_EXHAUST: - if (!env_put("SPFRESULT","error")) die_nomem(); - if (flagspf < 2) { flagspf = 0; break; } - out("451 SPF lookup failure (#4.3.0)\r\n"); - return -1; - case SPF_NONE: - if (!env_put("SPFRESULT","none")) die_nomem(); - flagspf = 0; - break; - case SPF_UNKNOWN: - if (!env_put("SPFRESULT","unknown")) die_nomem(); - if (flagspf < 6) break; - else return 4; - case SPF_NEUTRAL: - if (!env_put("SPFRESULT","neutral")) die_nomem(); - if (flagspf < 5) break; - else return 3; - case SPF_SOFTFAIL: - if (!env_put("SPFRESULT","softfail")) die_nomem(); - if (flagspf < 4) break; - else return 2; - case SPF_FAIL: - if (!env_put("SPFRESULT","fail")) die_nomem(); - if (flagspf < 3) break; - if (!spf_parse(&spfbounce,spfexpmsg.s,expdomain.s)) die_nomem(); - return 1; - } - - return 0; -} - -/* this file is too long --------------------------------- MF parser */ - -int mailfrom_size(char *arg) -{ - unsigned long r; - unsigned long sizebytes = 0; - - scan_ulong(arg,&r); - sizebytes = r; - if (databytes) if (sizebytes > databytes) return 1; - return 0; -} - -void mailfrom_auth(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_put("TCPREMOTEINFO",remoteinfo)) die_nomem(); - if (!env_unset("TCP6REMOTEINFO")) die_read(); - if (!env_put("TCP6REMOTEINFO",remoteinfo)) die_nomem(); - } -} - -void mailfrom_parms(char *arg) -{ - int len; - - if ((len = str_len(arg))) { - if (!stralloc_copys(&mfparms,"")) die_nomem(); - while (len) { - arg++; len--; - if (*arg == ' ' || *arg == '\0' ) { - if (flagutf8) if (case_starts(mfparms.s,"SMTPUTF8")) flagutf8 = 2; - 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(); - } - } -} - -/* this file is too long --------------------------------- SMTP dialog */ - -void smtp_helo(char *arg) -{ - smtp_greet("250 "); out("\r\n"); flush(); - seenmail = 0; rcptcount = 0; seenhelo++; - dohelo(arg); -} - -void smtp_ehlo(char *arg) -{ - char size[FMT_ULONG]; - - smtp_greet("250-"); out("\r\n"); - out("250-PIPELINING\r\n250-8BITMIME\r\n"); - if (flagutf8) out("250-SMTPUTF8\r\n"); - if (starttls > 0 && !seentls) out("250-STARTTLS\r\n"); - - switch (smtpauth) { - case 1: case 11: out("250-AUTH LOGIN PLAIN\r\n"); break; - case 2: case 12: out("250-AUTH CRAM-MD5\r\n"); break; - case 3: case 13: out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n"); break; - } - - size[fmt_ulong(size,(unsigned long) databytes)] = 0; - out("250 SIZE "); out(size); out("\r\n"); - - seenhelo++; seenmail = 0; rcptcount = 0; - dohelo(arg); -} - -void smtp_rset(void) -{ - seenmail = 0; rcptcount = 0; /* RFC 5321: seenauth + seentls stay */ - - if (!stralloc_copys(&mailfrom,"")) die_nomem(); - if (!stralloc_copys(&rcptto,"")) die_nomem(); - out("250 flushed\r\n"); -} - -void smtp_starttls() -{ - if (starttls == 0) err_starttls(); - - out("220 Ready to start TLS (#5.7.0)\r\n"); - flush(); - - if (!starttls_init()) die_starttls(); - buffer_init(&bi,saferead,FDIN,inbuf,sizeof(inbuf)); - seentls = 2; - - if (!starttls_info()) die_starttls(); - if (!modssl_info()) die_starttls(); - -/* reset SMTP state */ - - seenhelo = 0; seenmail = 0; rcptcount = 0; - if (!stralloc_copys(&addr,"")) die_nomem(); - if (!stralloc_copys(&helohost,"")) die_nomem(); - if (!stralloc_copys(&mailfrom,"")) die_nomem(); - if (!stralloc_copys(&rcptto,"")) die_nomem(); - if (seenauth == 1) seenauth = 0; /* Otherwise Auth by client Cert */ -} - -void smtp_mail(char *arg) -{ - if (flagutf8) if (!stralloc_cats(&protocol,"UTF8")) die_nomem(); - if (!stralloc_0(&protocol)) die_nomem(); - - if ((starttls > 1) && !seentls) { - err_tlsreq("Reject::TLS::missing",protocol.s,remoteip,remotehost,helohost.s); - return; - } - if (smtpauth > 10 && !seenauth) { - err_authreq("Reject::AUTH::missing",protocol.s,remoteip,remotehost,helohost.s); - return; - } - if (!addrparse(arg)) { err_syntax(); return; } - - flagsize = 0; - rcptcount = 0; - mailfrom_parms(arg); - seenmail++; - if (relayclient && localmf) { - flagmav = localaddr(addr.s); - if (flagmav > 0) if (!stralloc_append(&protocol,"M")) die_nomem(); - } - if (!stralloc_copys(&rcptto,"")) die_nomem(); - if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); - if (!stralloc_0(&mailfrom)) die_nomem(); - if (!env_put("MAILFROM",mailfrom.s)) die_nomem(); - - flagaddr = bmfcheck(); - if (flagaddr != -110) - if (mfdnscheck) flagdnsmf = dnsq(mailfrom.s,'M'); - - out("250 ok\r\n"); -} - -/* this file is too long --------------------------------- Greylisting */ - -int postgrey_scanner() -{ - int child; - int wstat; - - char *postgrey_scannerarg[] = {"bin/qmail-postgrey",pgbind.s,mailfrom.s,addr.s,remoteip,remotehost,0}; - - switch (child = fork()) { - case -1: - return err_forkgl(); - case 0: - execv(*postgrey_scannerarg,postgrey_scannerarg); - _exit(1); - } - - wait_pid(&wstat,child); - if (wait_crashed(wstat)) return err_postgl(); - - switch (wait_exitcode(wstat)) { - case 10: return 1; - default: return 0; - } -} - -void smtp_rcpt(char *arg) -{ - char *rcptok = 0; - if (!seenmail) { err_wantmail(); return; } - if (!addrparse(arg)) { err_syntax(); return; } - rcptcount++; - -/* 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,"0"); - 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; - } - - if (greylist && (postgrey_scanner() == 1)) { /* Greylisting */ - postgrey("Deferred::SNDR::Grey_Listed",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - return; - } - - if (tarpitcount && flagerrcpts >= tarpitcount) { /* Tarpitting et al. */ - if (tarpitdelay == 999) flagnotorious++; - err_rcpts("Reject::RCPT::Toomany_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - return; - } - if (tarpitcount && rcptcount >= tarpitcount) - if (tarpitdelay > 0 && tarpitdelay < 999) sleep(tarpitdelay); - - 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 (flagspf) /* SPF rejects */ - if (spf_check(flagip6) > 0) { - if (!stralloc_0(&spfbounce)) die_nomem(); - err_spf("Reject::SPF::Fail",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,spfbounce.s); - return; - } - } - -/* this file is too long --------------------------------- Local checks */ - - else { - if (flagmimetype == 4 || flagmimetype == 6) flagmimetype = 0; - if (flagloadertype == 2) flagloadertype = 0; - - 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 (flagmimetype == 2 || flagmimetype == 3 || flagmimetype == 6) cdb_init(&cdbm,fdbmt); - if (flagloadertype == 1) cdb_init(&cdbl,fdblt); - - if (flagaddr && flagaddr != -110) { - switch (flagaddr) { - case -1: badmailcond = "@"; break; - case -2: badmailcond = "~"; break; - case -3: badmailcond = "="; break; - case -4: badmailcond = "-"; break; - case -5: badmailcond = "+"; break; - default: badmailcond = "*"; break; - } - err_bmf("Reject::ORIG::Bad_Mailfrom",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmailcond); - return; - } - - flagrcpt = brtcheck(); - if (flagrcpt == 110) { - err_brt("Reject::RCPT::Invalid_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - return; - } else if (flagrcpt > 0) { - err_brt("Reject::RCPT::Bad_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - return; - } - - if (flagsize) { - err_size("Reject::DATA::Invalid_Size",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; - } - -/* 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(); - - if (!stralloc_cats(&mailto,addr.s)) die_nomem(); - if (!stralloc_cats(&mailto," ")) die_nomem(); - if (!stralloc_copys(&deliverto,mailto.s)) die_nomem(); - if (!stralloc_0(&deliverto)) die_nomem(); - if (!env_put("RCPTTO",deliverto.s)) die_nomem(); - -/* this file is too long --------------------------------- Additional logging */ - - switch (flagrcpt) { - case 1: rcptok = "Recipients_Cdb"; break; - case 2: rcptok = "Recipients_Pam"; break; - case 3: rcptok = "Recipients_Users"; break; - case 4: rcptok = "Recipients_Wild"; break; - default: rcptok = "Rcpthosts_Rcptto"; break; - } - if (seenauth) - smtp_loga("Accept::AUTH::",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,remoteinfo,authmethod.s); - else if (flagmav > 0) - smtp_logg("Accept::ORIG::Local_Sender",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - else if (relayclient) - smtp_logg("Accept::SNDR::Relay_Client",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - else if (flagspf == 10) - smtp_logr("Accept::SPF::",rcptok,protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - else - smtp_logr("Accept::RCPT::",rcptok,protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - - out("250 ok\r\n"); -} - -struct qmail qqt; -unsigned long bytestooverflow = 0; - -stralloc line = {0}; -stralloc base64types = {0}; -stralloc badmimetype = {0}; -stralloc badloadertype = {0}; - -unsigned int nolines = 0; -unsigned int flagb64 = 0; /* lineno with BASE64MESSAGE */ -unsigned int flagbase = 0; /* lineno with actual base64 content */ -unsigned int flagblank = 0; - -static void queue_put(char *ch) -{ - int i; - - if (flagmimetype > 0 || flagloadertype > 0 ) { - if (line.len <= BUFSIZE_LINE) - 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; } - - if (*(line.s + 1) == 'C' || *(line.s + 1) == 'c') - if (case_startb(line.s + 1,line.len - 2,BASE64MESSAGE)) flagb64 = nolines; - if (flagb64 && nolines == flagblank + 1 && line.len > MIMETYPE_LEN + 2) flagbase = nolines; - if (*(line.s + 1) == '-') { flagb64 = 0; flagbase = 0; } - - if (flagmimetype > 0 && flagbase == nolines) { /* badmimetype */ - if (!stralloc_catb(&base64types,line.s + 1,MIMETYPE_LEN)) die_nomem(); - if (!stralloc_0(&base64types)) die_nomem(); - - if (flagmimetype == 2 || flagmimetype == 3 || flagmimetype == 6) { - if (cdb_find(&cdbm,line.s + 1,MIMETYPE_LEN)) { - cdb_free(&cdbm); close(fdbmt); - 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 (flagbase && line.len > LOADER_LEN + 2) { - if (flagloadertype >= 1 || flagmimetype >= 1) { - for (i = 0; i < line.len - LOADER_LEN; ++i) { - if (flagloadertype == 1 && *(line.s+i) == *badloaderinit) { /* badloadertype */ - if (cdb_find(&cdbl,line.s + i,LOADER_LEN)) { - cdb_free(&cdbl); close(fdbmt); - 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; - } - } - if (flagmimetype == 1 || flagmimetype == 3 || flagmimetype == 4) { - if (*(line.s + i) == ' ' || *(line.s + i) == '\t') { /* white spaces */ - if (!stralloc_copyb(&badmimetype,line.s + i - 2,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 = -2; - } - } - } - } - } - line.len = 0; - if (!stralloc_copys(&line,"L")) die_nomem(); - } - } - - if (bytestooverflow) - if (!--bytestooverflow) - qmail_fail(&qqt); - qmail_put(&qqt,ch,1); -} - -void blast(int *hops) -{ - char ch; - int state; - int flaginheader; - int pos; /* number of bytes since most recent \n, if fih */ - 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 */ -#ifdef BARELF - int seencr = 0; -#endif - - state = 1; - *hops = 0; - flaginheader = 1; - pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; - for (;;) { - buffer_get(&bi,&ch,1); -#ifdef BARELF - if (ch == '\n') { - if (seencr == 0) { buffer_seek(&bi,-1); ch = '\r'; } - } - if (ch == '\r') seencr = 1; else seencr = 0; -#endif - if (flaginheader) { - if (pos < 9) { - if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; - if (flagmaybez) if (pos == 8) ++*hops; - if (pos < 8) - if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; - if (flagmaybex) if (pos == 7) ++*hops; - if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; - if (flagmaybey) if (pos == 1) flaginheader = 0; - ++pos; - } - if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } - } - switch (state) { - case 0: - if (ch == '\n') straynewline(); - if (ch == '\r') { state = 4; continue; } - break; - case 1: /* \r\n */ - if (ch == '\n') straynewline(); - if (ch == '.') { state = 2; continue; } - if (ch == '\r') { state = 4; continue; } - state = 0; - break; - case 2: /* \r\n + . */ - if (ch == '\n') straynewline(); - if (ch == '\r') { state = 3; continue; } - state = 0; - break; - case 3: /* \r\n + .\r */ - if (ch == '\n') return; - queue_put("."); - queue_put("\r"); - if (ch == '\r') { state = 4; continue; } - state = 0; - break; - case 4: /* + \r */ - if (ch == '\n') { state = 1; break; } - if (ch != '\r') { queue_put("\r"); state = 0; } - } - queue_put(&ch); - } -} - -char accept_buf[FMT_ULONG]; - -void acceptmessage(unsigned long qp) -{ - datetime_sec when; - when = now(); - out("250 ok "); - accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; - out(accept_buf); - out(" qp "); - accept_buf[fmt_ulong(accept_buf,qp)] = 0; - out(accept_buf); - out("\r\n"); -} - -void smtp_data() -{ - int hops; - unsigned long qp; - char *qqx; - - if (!seenmail) { err_wantmail(); return; } - if (!rcptto.len) { err_wantrcpt(); return; } - if (flagnotorious) { err_notorious(); } - seenmail = 0; - if (databytes) bytestooverflow = databytes + 1; - - if (!stralloc_copys(&addr,"")) die_nomem(); - if (!stralloc_cats(&addr,rcptto.s + 1)) die_nomem(); - if (!stralloc_0(&addr)) die_nomem(); - - if (qmail_open(&qqt) == -1) { err_qqt(); return; } - qp = qmail_qp(&qqt); - - out("354 go ahead\r\n"); - - if (flagspf && !relayclient) spfheader(&qqt,spfinfo.s,local,remoteip,helohost.s,mailfrom.s); - received(&qqt,protocol.s,local,remoteip,remotehost,remoteinfo,fakehelo,tlsinfo.s,rblinfo.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) { - err_size("Reject::DATA::Invalid_Size",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); - return; - } - if (flagmimetype < 0) { - err_data("Reject::DATA::Bad_MIME",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmimetype.s); - return; - } - if (flagloadertype < 0) { - err_data("Reject::DATA::Bad_Loader",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badloadertype.s); - return; - } - if (*qqx == 'I') { - err_data("Reject::DKIM::Signature",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"fail"); - return; - } - if (*qqx == 'S') { - err_data("Reject::DATA::Spam_Message",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"spam"); - return; - } - if (*qqx == 'A') { - err_data("Reject::DATA::MIME_Attach",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"MIME"); - return; - } - if (*qqx == 'V') { - if (qhpsi) - err_data("Reject::DATA::Virus_Infected",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,qhpsi); - else - err_data("Reject::DATA::Virus_Infected",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"AV scanner"); - return; - } - if (*qqx == 'D') out("554 "); 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 authbuf[BUFSIZE_AUTH]; -buffer ba = BUFFER_INIT(safewrite,FDAUTH,authbuf,sizeof(authbuf)); - -int authgetl(void) -{ - int i; - - if (!stralloc_copys(&authin,"")) die_nomem(); - for (;;) { - if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ - i = buffer_get(&bi,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_authabort(); - if (authin.len == 0) return err_authinput(); - 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 (!env_put("AUTHUSER",user.s)) 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(FDAUTH,pi[0]) == -1) return err_pipe(); - sig_pipedefault(); - execvp(*childargs,childargs); - _exit(1); - } - close(pi[0]); - - buffer_init(&ba,write,pi[1],authbuf,sizeof(authbuf)); - if (buffer_put(&ba,user.s,user.len) == -1) return err_write(); - if (buffer_put(&ba,pass.s,pass.len) == -1) return err_write(); - if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13) - if (buffer_put(&ba,chal.s,chal.len) == -1) return err_write(); - if (buffer_flush(&ba) == -1) return err_write(); - - close(pi[1]); - if (!stralloc_copys(&chal,"")) die_nomem(); - if (!stralloc_copys(&slop,"")) die_nomem(); - byte_zero(authbuf,sizeof(authbuf)); - 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(char *arg) -{ - int r; - if (smtpauth == 2 || smtpauth == 12) return 1; /* only login/plain */ - - if (*arg) { - if ((r = b64decode((unsigned char *)arg,str_len(arg),&user)) == 1) return err_authinput(); - } - else { - out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ - if (authgetl() < 0) return -1; - if ((r = b64decode((unsigned char *)authin.s,authin.len,&user)) == 1) return err_authinput(); - } - if (r == -1) die_nomem(); - - out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ - - if (authgetl() < 0) return -1; - if ((r = b64decode((unsigned char *)authin.s,authin.len,&pass)) == 1) return err_authinput(); - if (r == -1) die_nomem(); - - if (!user.len || !pass.len) return err_authinput(); - return authenticate(); -} - -int auth_plain(char *arg) -{ - int r, id = 0; - if (smtpauth == 2 || smtpauth == 12) return 1; /* only login/plain */ - - if (*arg) { - if ((r = b64decode((unsigned char *)arg,str_len(arg),&resp)) == 1) return err_authinput(); - } - else { - out("334 \r\n"); flush(); - if (authgetl() < 0) return -1; - if ((r = b64decode((unsigned char *)authin.s,authin.len,&resp)) == 1) return err_authinput(); - } - 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_authinput(); - return authenticate(); -} - -int auth_cram() -{ - int i, r; - char *s; - if (smtpauth == 1 || smtpauth == 11) return 1; /* no challenge if login/plain */ - - 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((unsigned char *)authin.s,authin.len,&resp)) == 1) return err_authinput(); - if (r == -1 || !stralloc_0(&resp)) die_nomem(); - - i = str_rchr(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_authinput(); - 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(char *arg) -{ - int i; - char *cmd = arg; - - /* prevent users to expose userid + password over unencrypted connection */ - - if ((starttls > 1) && !seentls) { - if (!stralloc_append(&protocol,"A")) die_nomem(); - if (!stralloc_0(&protocol)) die_nomem(); - err_authsetup("Reject::TLS::required",protocol.s,remoteip,remotehost,helohost.s); - return; - } - - if ((starttls > 1) && !seenhelo) { - if (!stralloc_append(&protocol,"A")) die_nomem(); - if (!stralloc_0(&protocol)) die_nomem(); - err_tlsreq("Reject::AUTH::invalid",protocol.s,remoteip,remotehost,helohost.s); - return; - } - - if (!smtpauth) { out("503 auth not available (#5.3.3)\r\n"); flush(); _exit(0); } - if (smtpauth && !*childargs) { - err_authsetup("Reject::AUTH::setup",protocol.s,remoteip,remotehost,helohost.s); - flush(); _exit(1); - } - 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; - - if (!authcmds[i].text) { /* invalid auth cmd */ - if (!stralloc_append(&protocol,"A")) die_nomem(); - if (!stralloc_0(&protocol)) die_nomem(); - err_authinvalid("Reject::AUTH::Method",protocol.s,remoteip,remotehost,helohost.s); - return; - } - - if (!stralloc_copys(&authmethod,authcmds[i].text)) die_nomem(); - if (!stralloc_0(&authmethod)) die_nomem(); - - switch (authcmds[i].fun(arg)) { - case 0: - seenauth = 1; - relayclient = ""; - remoteinfo = user.s; - auth_info(authmethod.s); - out("235 ok, go ahead (#2.0.0)\r\n"); - break; - case 1: - if (!stralloc_append(&protocol,"A")) die_nomem(); - if (!stralloc_0(&protocol)) die_nomem(); - err_authfail("Reject::AUTH::",protocol.s,remoteip,remotehost,helohost.s,user.s,authmethod.s); - return; - } -} - -/* 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 } -, { "rset", smtp_rset, flush } -, { "help", smtp_help, flush } -, { "noop", err_noop, flush } -, { "vrfy", err_vrfy, flush } -, { "starttls", smtp_starttls, flush } -, { 0, err_unimpl, flush } -} ; - -int main(int argc, char **argv) -{ - childargs = argv + 1; - sig_pipeignore(); - if (chdir(auto_qmail) == -1) die_control(); - setup(); - smtpdlog_init(); - if (ipme_init() != 1) die_ipme(); - smtp_greet("220 "); - out(" ESMTP\r\n"); - flush(); - if (commands(&bi,&smtpcommands) == 0) die_read(); - die_nomem(); - - return 0; -} |