diff options
Diffstat (limited to 'sqmail-4.3.07/src/qmail-smtpam.c')
-rwxr-xr-x | sqmail-4.3.07/src/qmail-smtpam.c | 633 |
1 files changed, 0 insertions, 633 deletions
diff --git a/sqmail-4.3.07/src/qmail-smtpam.c b/sqmail-4.3.07/src/qmail-smtpam.c deleted file mode 100755 index e9566e0..0000000 --- a/sqmail-4.3.07/src/qmail-smtpam.c +++ /dev/null @@ -1,633 +0,0 @@ -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <unistd.h> -#include "sig.h" -#include "genalloc.h" -#include "stralloc.h" -#include "buffer.h" -#include "scan.h" -#include "case.h" -#include "byte.h" -#include "error.h" -#include "auto_qmail.h" -#include "control.h" -#include "dns.h" -#include "alloc.h" -#include "quote.h" -#include "ip.h" -#include "ipalloc.h" -#include "ipme.h" -#include "str.h" -#include "now.h" -#include "exit.h" -#include "constmap.h" -#include "tcpto.h" -#include "socket_if.h" -#include "ucspissl.h" -#include "timeout.h" -#include "timeoutconn.h" -#include "tls_remote.h" -#include "tls_errors.h" -#include "tls_timeoutio.h" -#include "uint_t.h" - -#define MAX_SIZE 200000000 -#define HUGESMTPTEXT 5000 -#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ -#define PORT_SMTPS 465 -#define VERIFYDEPTH 1 -#define FDPAM 3 -#define TCP_TIMEOUT 60 -#define SMTP_TIMEOUT 1200 - -#define WHO "qmail-smtpam" - -/** @file qmail-smtpam.c -- TLS enabled SMTP PAM to check mailbox at remote MX - */ - -int flagauth = 0; /* 1 = login; 2 = plain; 3 =crammd5 */ -int flagsmtps = 0; /* RFC 8314 - 'implicit TLS' */ -int flagtls = 0; /* -2 = rejected; -1 = not; 0 = no, default; - > 0 see tls_remote.c - +10 = SMTPS; +20 = QMTPS; 100 = active TLS connection */ -int flagverify = 0; /* 1 = verify Cert against CA ; -1 = Cert pinning */ -int flagutf8mail = 0; - -unsigned long port = PORT_SMTP; - -GEN_ALLOC_typedef(saa,stralloc,sa,len,a) -GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) - -stralloc helohost = {0}; -stralloc host = {0}; -stralloc ports = {0}; -stralloc remotehost = {0}; -stralloc sender = {0}; -stralloc canonhost = {0}; -stralloc canonbox = {0}; -stralloc sendip = {0}; -stralloc recipient = {0}; - -stralloc domainips = {0}; -struct constmap mapdomainips; -char ip4[4]; -char ip6[16]; -uint32 ifidx = 0; - -stralloc routes = {0}; -struct constmap maproutes; - -struct ip_mx partner; - -SSL *ssl; -SSL_CTX *ctx; - -void out(char *s) { if (buffer_puts(buffer_1small,s) == -1) _exit(111); } -void zero() { if (buffer_put(buffer_1small,"\0",1) == -1) _exit(111); } -void zerodie() { zero(); buffer_flush(buffer_1small); _exit(111); } -void outsafe(stralloc *sa) -{ - int i; - char ch; - for (i = 0; i < sa->len; ++i) { - ch = sa->s[i]; - if (ch < 33) ch = '?'; - if (ch > 126) ch = '?'; - if (buffer_put(buffer_1small,&ch,1) == -1) _exit(111); - } -} - -void temp_noip() -{ - out("Zinvalid ipaddr in control/domainips (#4.3.0)\n"); - zerodie(); -} -void temp_nomem() -{ - out("ZOut of memory. (#4.3.0)\n"); - zerodie(); -} -void temp_oserr() -{ - out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); - zerodie(); -} -void temp_osip() -{ - out("ZCan't bind to local ip address: "); - outsafe(&sendip); - out(". (#4.3.0)\n"); - zerodie(); -} -void temp_noconn() -{ - out("ZSorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); - zerodie(); -} -void temp_dnscanon() -{ - out("ZCNAME lookup failed temporarily for: "); - outsafe(&canonhost); - out(". (#4.4.3)\n"); - zerodie(); -} -void temp_dns() -{ - out("ZSorry, I couldn't find any host named: "); - outsafe(&host); - out(". (#4.1.2)\n"); - zerodie(); -} -void temp_chdir() -{ - out("ZUnable to switch to home directory. (#4.3.0)\n"); - zerodie(); -} -void temp_control() -{ - out("ZUnable to read control files. (#4.3.0)\n"); - zerodie(); -} -void perm_usage() -{ - out("Dqmail-smtpam was invoked improperly. (#5.3.5)\n"); - zerodie(); -} -void perm_dns() -{ - out("DSorry, I couldn't find any host named: "); - outsafe(&host); - out(". (#5.1.2)\n"); - zerodie(); -} -void outhost() -{ - char ipaddr[IPFMT]; - int len; - - switch (partner.af) { - case AF_INET: - len = ip4_fmt(ipaddr,(char *)&partner.addr.ip4.d); break; - case AF_INET6: - len = ip6_fmt(ipaddr,(char *)&partner.addr.ip6.d); break; - } - if (buffer_put(buffer_1small,ipaddr,len) == -1) _exit(0); -} - -int flagcritical = 0; - -void dropped() -{ - out("ZConnected to "); - outhost(); - out(" but connection died. "); - if (flagcritical) out("Possible duplicate! "); - out("(#4.4.2)\n"); - zerodie(); -} - -int timeoutconnect = TCP_TIMEOUT; -int smtpfd; -int timeout = SMTP_TIMEOUT; - -ssize_t saferead(int fd,char *buf,int len) -{ - int r; - r = timeoutread(timeout,smtpfd,buf,len); - if (r <= 0) dropped(); - return r; -} - -ssize_t safewrite(int fd,char *buf,int len) -{ - int r; - r = timeoutwrite(timeout,smtpfd,buf,len); - if (r <= 0) dropped(); - return r; -} - -char outbuf[1450]; -buffer bo = BUFFER_INIT(safewrite,-1,outbuf,sizeof(outbuf)); -char frombuf[128]; -buffer bi = BUFFER_INIT(saferead,-1,frombuf,sizeof(frombuf)); - -stralloc smtptext = {0}; - -void get(char *ch) -{ - buffer_get(&bi,ch,1); - if (*ch != '\r') - if (smtptext.len < HUGESMTPTEXT) - if (!stralloc_append(&smtptext,ch)) temp_nomem(); -} - -unsigned long smtpcode() -{ - unsigned char ch; - unsigned long code; - - if (!stralloc_copys(&smtptext,"")) temp_nomem(); - - get(&ch); code = ch - '0'; - get(&ch); code = code * 10 + (ch - '0'); - get(&ch); code = code * 10 + (ch - '0'); - for (;;) { - get(&ch); - if (ch != '-') break; - while (ch != '\n') get(&ch); - get(&ch); - get(&ch); - get(&ch); - } - while (ch != '\n') get(&ch); - - return code; -} - -void outsmtptext() -{ - int i; - if (smtptext.s) if (smtptext.len) { - out("Remote host said: "); - for (i = 0; i < smtptext.len; ++i) - if (!smtptext.s[i]) smtptext.s[i] = '?'; - if (buffer_put(buffer_1small,smtptext.s,smtptext.len) == -1) _exit(111); - smtptext.len = 0; - } -} - -void quit(char *prepend,char *append) -{ - buffer_putsflush(&bo,"QUIT\r\n"); - /* waiting for remote side is just too ridiculous */ - out(prepend); - outhost(); - out(append); - out(".\n"); - outsmtptext(); - zerodie(); -} - -stralloc recip = {0}; - -/* this file is too long -------------------------------------- client TLS */ - -stralloc cafile = {0}; -stralloc cadir = {0}; -stralloc certfile = {0}; -stralloc keyfile = {0}; -stralloc keypwd = {0}; -stralloc ciphers = {0}; -stralloc tlsdest = {0}; - -char *tlsdestinfo = 0; -char *tlsdomaininfo = 0; - -stralloc domaincerts = {0}; -struct constmap mapdomaincerts; -stralloc tlsdestinations = {0}; -struct constmap maptlsdestinations; -unsigned long verifydepth = VERIFYDEPTH; - -void tls_init() -{ -/* Client CTX */ - - ctx = ssl_client(); - ssl_errstr(); - if (!ctx) temp_tlsctx(); - -/* Fetch CA infos for dest */ - - if (flagverify > 0) - if (cafile.len || cadir.len) - if (!ssl_ca(ctx,cafile.s,cadir.s,(int) verifydepth)) temp_tlsca(); - - if (ciphers.len) - if (!ssl_ciphers(ctx,ciphers.s)) temp_tlscipher(); - -/* Set SSL Context */ - - ssl = ssl_new(ctx,smtpfd); - if (!ssl) temp_tlsctx(); - -/* Setup SSL FDs */ - - if (!tls_conn(ssl,smtpfd)) temp_tlscon(); - -/* Go on in none-blocking mode */ - - if (tls_timeoutconn(timeout,smtpfd,smtpfd,ssl) <= 0) - temp_tlserr(); -} - -int starttls_peer() -{ - int i = 0; - - while ( (i += str_chr(smtptext.s + i,'\n') + 1) && - (i < smtptext.len - 8) ) { - if (!str_diffn(smtptext.s + i + 4,"STARTTLS",8)) return 1; } - - return 0; -} - -void tls_peercheck() -{ - X509 *cert; - - cert = SSL_get_peer_certificate(ssl); - if (!cert) { flagtls = 100; return; } - - if (flagverify < 0) { - if (cafile.len) case_lowerb(cafile.s,cafile.len); - switch (tls_fingerprint(cert,cafile.s + 1,cafile.len - 1)) { - case -1: temp_tlspeercert(); - case -2: temp_tlsdigest(); - case -3: temp_invaliddigest(); - case 1: temp_tlscertfp(); - } - } else { - switch (tls_checkpeer(ssl,cert,remotehost,flagtls,flagverify)) { - case -1: temp_tlspeercert(); - case -2: temp_tlspeerverify(); - case -3: temp_tlspeervalid(); - case 1: flagtls = 101; break; - case 2: flagtls = 102; break; - case 3: flagtls = 103; break; - } - } - - if (flagtls < 100) flagtls = 100; - - X509_free(cert); - - return; -} - -int utf8flag(unsigned char *ch,int len) -{ - int i = 0; - while (i < len) - if (ch[i++] > 127) return 1; - return 0; -} - -/* this file is too long -------------------------------------- SMTP connection */ - -unsigned long code; - -void smtp_greeting() -{ - buffer_puts(&bo,"EHLO "); - buffer_put(&bo,helohost.s,helohost.len); - buffer_puts(&bo,"\r\n"); - buffer_flush(&bo); - - if (smtpcode() != 250) { - buffer_puts(&bo,"HELO "); - buffer_put(&bo,helohost.s,helohost.len); - buffer_puts(&bo,"\r\n"); - buffer_flush(&bo); - code = smtpcode(); - if (code >= 500) quit("DConnected to"," but my name was rejected"); - if (code != 250) quit("ZConnected to"," but my name was rejected"); - } -} - -void smtp_starttls() -{ - buffer_puts(&bo,"STARTTLS\r\n"); - buffer_flush(&bo); - if (smtpcode() == 220) { - tls_init(); - tls_peercheck(); - smtp_greeting(); - } else { - flagtls = -2; - quit("ZConnected to"," but STARTTLS was rejected"); - } -} - -void smtp() -{ - - if (flagtls > 10 && flagtls < 20) { /* SMTPS */ - tls_init(); - tls_peercheck(); - } - - code = smtpcode(); - if (code >= 500) quit("DConnected to "," but sender was rejected"); - if (code >= 400) quit("ZConnected to "," but sender was probably greylisted"); - - smtp_greeting(); - - if (flagutf8mail) buffer_puts(&bo," SMTPUTF8"); - - if (flagtls > 0 && flagtls < 10) /* STARTTLS */ - if (starttls_peer()) { - smtp_starttls(); - } else if (flagtls > 2) { - temp_tlshost(); - } - - buffer_puts(&bo,"MAIL FROM:<>"); - if (flagutf8mail) - buffer_puts(&bo," SMTPUTF8"); - buffer_puts(&bo,"\r\n"); - buffer_flush(&bo); - code = smtpcode(); - if (code >= 500) quit("DConnected to "," but sender was rejected"); - if (code >= 400) quit("ZConnected to "," but sender was rejected"); - - buffer_puts(&bo,"RCPT TO:<"); - buffer_put(&bo,recipient.s,recipient.len); - buffer_puts(&bo,">\r\n"); - buffer_flush(&bo); - code = smtpcode(); - close(smtpfd); - if (code == 250) _exit(0); - _exit(1); -} - -void getcontrols() -{ - if (control_init() == -1) temp_control(); - if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); - if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) - temp_control(); - if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1) - temp_control(); - switch (control_readfile(&domainips,"control/domainips",0)) { - case -1: temp_control(); - case 0: if (!constmap_init(&mapdomainips,"",0,1)) temp_nomem(); break; - case 1: if (!constmap_init(&mapdomainips,domainips.s,domainips.len,1)) temp_nomem(); break; - } - switch (control_readfile(&tlsdestinations,"control/tlsdestinations",0)) { - case -1: temp_control(); - case 0: if (!constmap_init(&maptlsdestinations,"",0,1)) temp_nomem(); break; - case 1: if (!constmap_init(&maptlsdestinations,tlsdestinations.s,tlsdestinations.len,1)) temp_nomem(); break; - } - -} - -char up[513]; -int uplen; - -int main(int argc,char **argv) -{ - static ipalloc ip = {0}; - stralloc netif = {0}; - int i, j, k; - int r; /* reserved for return code */ - int p; /* reserved for port */ - char *localip = 0; - char *tlsdestinfo = 0; - - sig_pipeignore(); - if (argc < 2) perm_usage(); - if (chdir(auto_qmail) == -1) temp_chdir(); - getcontrols(); - - if (!stralloc_copys(&host,argv[1])) temp_nomem(); - - if (argv[2]) { - if (!stralloc_copys(&ports,argv[2])) temp_nomem(); - if (*ports.s == 's') { ports.s++; flagsmtps = 1; } - scan_ulong(ports.s,&port); - } - - if (ipme_init() != 1) temp_oserr(); - -/* this file is too long -------------------------------------- set domain ip + helohost */ - - if (!localip) - localip = constmap(&mapdomainips,"*",1); /* one for all */ - - if (localip) { - j = str_chr(localip,'%'); - if (localip[j] != '%') j = 0; - k = str_chr(localip,'|'); - if (localip[k] != '|') k = 0; - if (k) { /* helohost */ - if (!stralloc_copys(&helohost,localip + k + 1)) temp_nomem(); - localip[k] = 0; - } - if (j) { /* if index */ - localip[j] = 0; - if (!stralloc_copys(&netif,localip + j + 1)) temp_nomem(); - if (!stralloc_0(&netif)) temp_nomem(); - } - } - - -/* this file is too long -------------------------------------- TLS destinations */ - - flagtls = tls_destination((const stralloc) host); // un-terminated - - if (flagtls > 0) { - if (tlsdestinfo) { - i = str_chr(tlsdestinfo,'|'); /* ca file or cert fingerprint */ - if (tlsdestinfo[i] == '|') { - tlsdestinfo[i] = 0; - j = str_chr(tlsdestinfo+i+1,'|'); /* cipher */ - if (tlsdestinfo[i + j + 1] == '|') { - tlsdestinfo[i + j + 1] = 0; - k = str_chr(tlsdestinfo + i + j + 2,'|'); /* cone domain */ - if (tlsdestinfo[i + j + k + 2] == '|') { - tlsdestinfo[i + j + k + 2] = 0; - if (str_diffn(tlsdestinfo + j + k + 3,canonhost.s,canonhost.len)) flagtls = 0; - } - p = str_chr(tlsdestinfo + i + j + 2,';'); /* verifydepth;port */ - if (tlsdestinfo[i + j + p + 2] == ';') { - if (tlsdestinfo[i + j + p + 3] == 's') { flagsmtps = 1; p++; } - tlsdestinfo[i + j + p + 2] = 0; - if (p > 0) scan_ulong(tlsdestinfo+i+j + 2,&verifydepth); - scan_ulong(tlsdestinfo+i+j + p + 3,&port); - } - } - if (!stralloc_copys(&ciphers,tlsdestinfo + i + 1)) temp_nomem(); - } - if (!stralloc_copys(&cafile,tlsdestinfo)) temp_nomem(); - } - -/* cafile starts with '=' => it is a fingerprint - cafile ends with '/' => consider it as cadir */ - - if (cafile.len) { - flagverify = 1; - if (cafile.s[cafile.len] == '/') { - cafile.len = 0; - flagverify = 2; - if (!stralloc_copys(&cadir,tlsdestinfo)) temp_nomem(); - if (!stralloc_0(&cadir)) temp_nomem(); - } else { - if (cafile.s[0] == '%') flagverify = -1; - if (!stralloc_0(&cafile)) temp_nomem(); - } - } else { - cafile.len = cadir.len = ciphers.len = p = 0; - } - - if (port == PORT_SMTPS || flagsmtps) flagtls = flagtls + 10; - } - -/* this file is too long -------------------------------------- Setup connection */ - - uplen = 0; - for (;;) { - do - r = read(FDPAM,up + uplen,sizeof(up) - uplen); - while ((r == -1) && (errno == EINTR)); - if (r == -1) _exit(111); - if (r == 0) break; - uplen += r; - if (uplen >= sizeof(up)) _exit(111); - } - close(FDPAM); - - if (!stralloc_copyb(&recipient,up,uplen)) temp_nomem(); - if (!stralloc_0(&recipient)) temp_nomem(); - if (!stralloc_0(&host)) temp_nomem(); - if (!stralloc_copys(&remotehost,host.s)) temp_nomem(); - - flagutf8mail = utf8flag(recipient.s,recipient.len); - - switch (dns_ip(&ip,&remotehost)) { - case DNS_MEM: temp_nomem(); - case DNS_ERR: temp_dns(); - case DNS_COM: temp_dnscanon(); - default: if (ip.len <= 0) perm_dns(); - } - - smtpfd = socket(ip.ix[i].af,SOCK_STREAM,0); - if (smtpfd == -1) temp_oserr(); - - if (localip) { /* set domain ip */ - if (!stralloc_copyb(&sendip,localip,str_len(localip))) temp_nomem(); - j = str_chr(localip,':'); - if (j && localip[j] == ':') { /* IPv6 */ - if (!ip6_scan(localip,ip6)) temp_noip(); - ifidx = socket_getifidx(netif.s); - if (socket_bind6(smtpfd,ip6,0,ifidx) < 0) temp_osip(); - } else { /* IPv4 */ - if (!ip4_scan(localip,ip4)) temp_noip(); - if (socket_bind4(smtpfd,ip4,0) < 0) temp_osip(); - } - } - - r = timeoutconn(smtpfd,&ip.ix[i].addr,(unsigned int) port,timeoutconnect,ifidx); - if (r == 0) { - tcpto_err(&ip.ix[i],0); - partner = ip.ix[i]; - smtp(); /* does not return */ - } - tcpto_err(&ip.ix[i],errno == ETIMEDOUT); - close(smtpfd); - - temp_noconn(); -} |