summaryrefslogtreecommitdiff
path: root/sqmail-4.3.07/src/qmail-smtpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sqmail-4.3.07/src/qmail-smtpd.c')
-rwxr-xr-xsqmail-4.3.07/src/qmail-smtpd.c1720
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;
-}