diff options
| author | manuel <manuel@mausz.at> | 2013-02-04 02:32:40 +0100 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2013-02-04 02:32:40 +0100 |
| commit | 8514473287c9594137c6fbc39f5619672ebc2430 (patch) | |
| tree | a5b965d8c7b60dee396bf8ebe25dd3eddfaa6753 | |
| parent | 35ddb916045abafaa4ae2c778b9383059fa06726 (diff) | |
| download | qmail-8514473287c9594137c6fbc39f5619672ebc2430.tar.gz qmail-8514473287c9594137c6fbc39f5619672ebc2430.tar.bz2 qmail-8514473287c9594137c6fbc39f5619672ebc2430.zip | |
[PATCH] qregex-starttls-2way-auth-20060423-mm
| -rw-r--r-- | Makefile | 67 | ||||
| -rw-r--r-- | Makefile-cert.mk | 21 | ||||
| -rw-r--r-- | README.auth | 66 | ||||
| -rw-r--r-- | README.qregex | 203 | ||||
| -rw-r--r-- | README.remote-auth | 31 | ||||
| -rw-r--r-- | README.starttls | 103 | ||||
| -rw-r--r-- | TARGETS | 6 | ||||
| -rw-r--r-- | base64.c | 122 | ||||
| -rw-r--r-- | base64.h | 7 | ||||
| -rw-r--r-- | case_startb.c | 21 | ||||
| -rw-r--r-- | conf-cc | 2 | ||||
| -rw-r--r-- | dns.c | 15 | ||||
| -rw-r--r-- | hier.c | 7 | ||||
| -rw-r--r-- | install-big.c | 4 | ||||
| -rw-r--r-- | ipalloc.h | 8 | ||||
| -rw-r--r-- | qmail-control.9 | 18 | ||||
| -rw-r--r-- | qmail-remote.8 | 43 | ||||
| -rw-r--r-- | qmail-remote.c | 444 | ||||
| -rw-r--r-- | qmail-showctl.c | 10 | ||||
| -rw-r--r-- | qmail-smtpd.8 | 133 | ||||
| -rw-r--r-- | qmail-smtpd.c | 758 | ||||
| -rw-r--r-- | qregex.c | 57 | ||||
| -rw-r--r-- | qregex.h | 5 | ||||
| -rw-r--r-- | ssl_timeoutio.c | 95 | ||||
| -rw-r--r-- | ssl_timeoutio.h | 21 | ||||
| -rw-r--r-- | tls.c | 25 | ||||
| -rw-r--r-- | tls.h | 16 | ||||
| -rw-r--r-- | update_tmprsadh.sh | 25 |
28 files changed, 2286 insertions, 47 deletions
| @@ -136,6 +136,10 @@ auto_usera.o: \ | |||
| 136 | compile auto_usera.c | 136 | compile auto_usera.c |
| 137 | ./compile auto_usera.c | 137 | ./compile auto_usera.c |
| 138 | 138 | ||
| 139 | base64.o: \ | ||
| 140 | compile base64.c base64.h stralloc.h substdio.h str.h | ||
| 141 | ./compile base64.c | ||
| 142 | |||
| 139 | binm1: \ | 143 | binm1: \ |
| 140 | binm1.sh conf-qmail | 144 | binm1.sh conf-qmail |
| 141 | cat binm1.sh \ | 145 | cat binm1.sh \ |
| @@ -808,7 +812,7 @@ dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ | |||
| 808 | forward preline condredirect bouncesaying except maildirmake \ | 812 | forward preline condredirect bouncesaying except maildirmake \ |
| 809 | maildir2mbox maildirwatch qail elq pinq idedit install-big install \ | 813 | maildir2mbox maildirwatch qail elq pinq idedit install-big install \ |
| 810 | instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ | 814 | instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ |
| 811 | binm3 binm3+df | 815 | binm3 binm3+df update_tmprsadh |
| 812 | 816 | ||
| 813 | load: \ | 817 | load: \ |
| 814 | make-load warn-auto.sh systype | 818 | make-load warn-auto.sh systype |
| @@ -1441,12 +1445,13 @@ qmail-remote: \ | |||
| 1441 | load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ | 1445 | load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ |
| 1442 | timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ | 1446 | timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ |
| 1443 | ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ | 1447 | ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ |
| 1444 | substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib | 1448 | substdio.a error.a str.a fs.a auto_qmail.o base64.o dns.lib socket.lib |
| 1445 | ./load qmail-remote control.o constmap.o timeoutread.o \ | 1449 | ./load qmail-remote control.o constmap.o timeoutread.o \ |
| 1446 | timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ | 1450 | timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ |
| 1451 | tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ | ||
| 1447 | ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ | 1452 | ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ |
| 1448 | lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ | 1453 | lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ |
| 1449 | str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` | 1454 | str.a fs.a auto_qmail.o base64.o `cat dns.lib` `cat socket.lib` |
| 1450 | 1455 | ||
| 1451 | qmail-remote.0: \ | 1456 | qmail-remote.0: \ |
| 1452 | qmail-remote.8 | 1457 | qmail-remote.8 |
| @@ -1532,16 +1537,17 @@ auto_split.h | |||
| 1532 | ./compile qmail-showctl.c | 1537 | ./compile qmail-showctl.c |
| 1533 | 1538 | ||
| 1534 | qmail-smtpd: \ | 1539 | qmail-smtpd: \ |
| 1535 | load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ | 1540 | load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \ |
| 1536 | timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ | 1541 | timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ |
| 1537 | date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ | 1542 | date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ |
| 1538 | open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ | 1543 | open.a sig.a case.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ |
| 1539 | fs.a auto_qmail.o socket.lib | 1544 | fs.a auto_qmail.o base64.o socket.lib |
| 1540 | ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ | 1545 | ./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \ |
| 1541 | timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ | 1546 | timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ |
| 1547 | tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ | ||
| 1542 | received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ | 1548 | received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ |
| 1543 | datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ | 1549 | datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ |
| 1544 | alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ | 1550 | alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o base64.o `cat \ |
| 1545 | socket.lib` | 1551 | socket.lib` |
| 1546 | 1552 | ||
| 1547 | qmail-smtpd.0: \ | 1553 | qmail-smtpd.0: \ |
| @@ -1553,7 +1559,7 @@ compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \ | |||
| 1553 | substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ | 1559 | substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ |
| 1554 | error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ | 1560 | error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ |
| 1555 | substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ | 1561 | substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ |
| 1556 | exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h | 1562 | exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h |
| 1557 | ./compile qmail-smtpd.c | 1563 | ./compile qmail-smtpd.c |
| 1558 | 1564 | ||
| 1559 | qmail-start: \ | 1565 | qmail-start: \ |
| @@ -1681,6 +1687,10 @@ compile rcpthosts.c cdb.h uint32.h byte.h open.h error.h control.h \ | |||
| 1681 | constmap.h stralloc.h gen_alloc.h rcpthosts.h | 1687 | constmap.h stralloc.h gen_alloc.h rcpthosts.h |
| 1682 | ./compile rcpthosts.c | 1688 | ./compile rcpthosts.c |
| 1683 | 1689 | ||
| 1690 | qregex.o: \ | ||
| 1691 | compile qregex.c qregex.h | ||
| 1692 | ./compile qregex.c | ||
| 1693 | |||
| 1684 | readsubdir.o: \ | 1694 | readsubdir.o: \ |
| 1685 | compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ | 1695 | compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ |
| 1686 | auto_split.h | 1696 | auto_split.h |
| @@ -1827,7 +1837,8 @@ date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \ | |||
| 1827 | ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ | 1837 | ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ |
| 1828 | ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ | 1838 | ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ |
| 1829 | prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ | 1839 | prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ |
| 1830 | maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c | 1840 | maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \ |
| 1841 | update_tmprsadh | ||
| 1831 | shar -m `cat FILES` > shar | 1842 | shar -m `cat FILES` > shar |
| 1832 | chmod 400 shar | 1843 | chmod 400 shar |
| 1833 | 1844 | ||
| @@ -2108,6 +2119,19 @@ timeoutwrite.o: \ | |||
| 2108 | compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h | 2119 | compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h |
| 2109 | ./compile timeoutwrite.c | 2120 | ./compile timeoutwrite.c |
| 2110 | 2121 | ||
| 2122 | qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a | ||
| 2123 | qmail-remote: tls.o ssl_timeoutio.o | ||
| 2124 | qmail-smtpd.o: tls.h ssl_timeoutio.h | ||
| 2125 | qmail-remote.o: tls.h ssl_timeoutio.h | ||
| 2126 | |||
| 2127 | tls.o: \ | ||
| 2128 | compile tls.c exit.h error.h | ||
| 2129 | ./compile tls.c | ||
| 2130 | |||
| 2131 | ssl_timeoutio.o: \ | ||
| 2132 | compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h | ||
| 2133 | ./compile ssl_timeoutio.c | ||
| 2134 | |||
| 2111 | token822.o: \ | 2135 | token822.o: \ |
| 2112 | compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ | 2136 | compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ |
| 2113 | gen_alloc.h gen_allocdefs.h | 2137 | gen_alloc.h gen_allocdefs.h |
| @@ -2139,3 +2163,26 @@ compile wait_nohang.c haswaitp.h | |||
| 2139 | wait_pid.o: \ | 2163 | wait_pid.o: \ |
| 2140 | compile wait_pid.c error.h haswaitp.h | 2164 | compile wait_pid.c error.h haswaitp.h |
| 2141 | ./compile wait_pid.c | 2165 | ./compile wait_pid.c |
| 2166 | |||
| 2167 | cert cert-req: \ | ||
| 2168 | Makefile-cert | ||
| 2169 | @$(MAKE) -sf $< $@ | ||
| 2170 | |||
| 2171 | Makefile-cert: \ | ||
| 2172 | conf-qmail conf-users conf-groups Makefile-cert.mk | ||
| 2173 | @cat Makefile-cert.mk \ | ||
| 2174 | | sed s}QMAIL}"`head -1 conf-qmail`"}g \ | ||
| 2175 | > $@ | ||
| 2176 | |||
| 2177 | update_tmprsadh: \ | ||
| 2178 | conf-qmail conf-users conf-groups update_tmprsadh.sh | ||
| 2179 | @cat update_tmprsadh.sh\ | ||
| 2180 | | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \ | ||
| 2181 | | sed s}QMAIL}"`head -1 conf-qmail`"}g \ | ||
| 2182 | > $@ | ||
| 2183 | chmod 755 update_tmprsadh | ||
| 2184 | |||
| 2185 | tmprsadh: \ | ||
| 2186 | update_tmprsadh | ||
| 2187 | echo "Creating new temporary RSA and DH parameters" | ||
| 2188 | ./update_tmprsadh | ||
diff --git a/Makefile-cert.mk b/Makefile-cert.mk new file mode 100644 index 0000000..d869999 --- /dev/null +++ b/Makefile-cert.mk | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | cert-req: req.pem | ||
| 2 | cert cert-req: QMAIL/control/clientcert.pem | ||
| 3 | @: | ||
| 4 | |||
| 5 | QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem | ||
| 6 | ln -s $< $@ | ||
| 7 | |||
| 8 | QMAIL/control/servercert.pem: | ||
| 9 | PATH=$$PATH:/usr/local/ssl/bin \ | ||
| 10 | openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@ | ||
| 11 | chmod 640 $@ | ||
| 12 | chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@ | ||
| 13 | |||
| 14 | req.pem: | ||
| 15 | PATH=$$PATH:/usr/local/ssl/bin openssl req \ | ||
| 16 | -new -nodes -out $@ -keyout QMAIL/control/servercert.pem | ||
| 17 | chmod 640 QMAIL/control/servercert.pem | ||
| 18 | chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem | ||
| 19 | @echo | ||
| 20 | @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" | ||
| 21 | @echo "cat signed_req.pem >> QMAIL/control/servercert.pem" | ||
diff --git a/README.auth b/README.auth new file mode 100644 index 0000000..435fe3b --- /dev/null +++ b/README.auth | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | README qmail-smtpd SMTP Authentication | ||
| 2 | ====================================== | ||
| 3 | |||
| 4 | |||
| 5 | History: | ||
| 6 | -------- | ||
| 7 | |||
| 8 | This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch | ||
| 9 | which itself uses "Mrs. Brisby's" initial code. | ||
| 10 | Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing | ||
| 11 | 'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison. | ||
| 12 | Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem | ||
| 13 | (can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons. | ||
| 14 | Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar. | ||
| 15 | Version 0.51 includes the evaluation of the 'Auth' and the 'Size' parameter in the 'Mail From:' command. | ||
| 16 | Version 0.52 uses DJB functions to copy FDs. | ||
| 17 | Version 0.56 corrects some minor mistakes displaying the 'Auth' userid. | ||
| 18 | Version 0.57 uses keyword "ESMTPA" in Received header in case of authentication to comply with RFC 3848. | ||
| 19 | |||
| 20 | |||
| 21 | Scope: | ||
| 22 | ------ | ||
| 23 | |||
| 24 | This patch supports RFC 2554 "SMTP Service Extension for Authentication" for qmail-smtpd. | ||
| 25 | Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration"). | ||
| 26 | For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html. | ||
| 27 | |||
| 28 | |||
| 29 | Installation: | ||
| 30 | ------------- | ||
| 31 | |||
| 32 | * Untar the source in the qmail-1.03 home direcotry. | ||
| 33 | * Run ./install_auth. | ||
| 34 | * Modify the compile time option "#define CRAM_MD5" to your needs. | ||
| 35 | * Re-make qmail. | ||
| 36 | |||
| 37 | |||
| 38 | Setup: | ||
| 39 | ------ | ||
| 40 | |||
| 41 | In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module' | ||
| 42 | PAM to be called by qmail-smtpd; typically | ||
| 43 | |||
| 44 | /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1 | ||
| 45 | |||
| 46 | Since qmail-smtpd does not run as root, checkpassword has to be made sticky. | ||
| 47 | There is no need to include additionally the hostname in the call. | ||
| 48 | In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information. | ||
| 49 | |||
| 50 | |||
| 51 | Changes wrt. Krysztof Dabrowski's patch: | ||
| 52 | ---------------------------------------- | ||
| 53 | |||
| 54 | * Avoid the 'hostname' in the call of the PAM. | ||
| 55 | * Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5. | ||
| 56 | * Doesn't close FD 2; thus not inhibiting logging to STDERR. | ||
| 57 | * Fixed bugs in base64.c. | ||
| 58 | * Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'. | ||
| 59 | * Evaluation of the (informational) Mail From: < > Auth=username. | ||
| 60 | * Additional support for the advertised "Size" via 'Mail From: <return-path> SIZE=123456780' (RFC 1870). | ||
| 61 | * RFC 3848 conformance for Received header in case of SMTP Auth. | ||
| 62 | |||
| 63 | |||
| 64 | Erwin Hoffmann - Cologne 2005-01-23 (www.fehcom.de) | ||
| 65 | |||
| 66 | |||
diff --git a/README.qregex b/README.qregex new file mode 100644 index 0000000..60c1aeb --- /dev/null +++ b/README.qregex | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | QREGEX (v2) 20060423 - README April 23, 2006 | ||
| 2 | A Regular Expression matching patch for qmail 1.03 and netqmail | ||
| 3 | |||
| 4 | |||
| 5 | OVERVIEW: | ||
| 6 | |||
| 7 | qregex adds the ability to match address evelopes via Regular Expressions (REs) | ||
| 8 | in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name), | ||
| 9 | `mail from` (envelope sender), and `rcpt to` (envelope recipient) commands. | ||
| 10 | It follows all the base rules that are set out with qmail (ie using control | ||
| 11 | files) so it makes for easy integretion into an existing setup (see the | ||
| 12 | install instructions for more info). The v2 is specified because qregex was | ||
| 13 | re-written to better conform to the security guarantee set forth by the author | ||
| 14 | of qmail. The original version used stdio.h and stdlib.h for reading the | ||
| 15 | control files whereas v2 now uses all stralloc functions which are much more | ||
| 16 | regulated against buffer overruns and the like. | ||
| 17 | See: http://cr.yp.to/qmail/guarantee.html | ||
| 18 | |||
| 19 | |||
| 20 | FEATURES: | ||
| 21 | |||
| 22 | Features of qregex include: | ||
| 23 | |||
| 24 | 1. Performs pattern matching on envelope senders and envelope | ||
| 25 | recipients against REs in the badmailfrom and badmailto control | ||
| 26 | files. Two additional control files, badmailfromnorelay and | ||
| 27 | badmailtonorelay, are used for pattern matching when the | ||
| 28 | RELAYCLIENT environment variable is not set. | ||
| 29 | |||
| 30 | 2. Performs pattern matching on the helo/ehlo host name. Setting the | ||
| 31 | NOBADHELO environment variable prevents the host name from being | ||
| 32 | compared to the patterns in the badhelo control file. | ||
| 33 | |||
| 34 | 3. Matches to patterns are logged. Setting the LOGREGEX environment | ||
| 35 | variable causes the matched regex pattern to be included in the log. | ||
| 36 | |||
| 37 | 4. Matching is case insensitive. | ||
| 38 | |||
| 39 | 5. qregex ignores empty envelope senders. An empty envelope sender is not | ||
| 40 | compared to the patterns in the badmailfrom and badmailfromnorelay | ||
| 41 | control files and is always accepted. | ||
| 42 | |||
| 43 | |||
| 44 | PLATFORMS: | ||
| 45 | |||
| 46 | qregex has been built and tested on the following platforms. I'm sure it won't | ||
| 47 | have any problems on any platform that qmail will run on (providing they have | ||
| 48 | a regex interface) but if you run into problems let me know. | ||
| 49 | |||
| 50 | - OpenBSD 3.x | ||
| 51 | - FreeBSD 4.x, 5.x | ||
| 52 | - Mandrake Linux 9.x | ||
| 53 | - SuSE Linux 8.x | ||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | INSTALLATION INSTRUCTIONS: | ||
| 58 | |||
| 59 | Installation is very simple, there is only one requirement. You need to use the | ||
| 60 | GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html). | ||
| 61 | (For Solaris 8 users it is installed as 'gpatch') | ||
| 62 | |||
| 63 | - If this is a new setup. | ||
| 64 | Unpack the qmail archive, cd into the qmail-1.03 directory and run | ||
| 65 | "patch < /path/to/qregex-<version>.patch". Follow the instructions as per the | ||
| 66 | included qmail INSTALL file. Once you are done come back to this file and read | ||
| 67 | the section on the control files. | ||
| 68 | |||
| 69 | If you are using netqmail, then unpack the netqmail archive. Run the collate.sh | ||
| 70 | script and cd into the resulting netqmail-<version> directory. From there, run | ||
| 71 | "patch < /path/to/qregex-<version>.patch". Complete the netqmail installation | ||
| 72 | normally. Once you are done, come back to this file and read the section on the | ||
| 73 | control files. | ||
| 74 | |||
| 75 | - If this is an existing setup. | ||
| 76 | FIRST: create your control files (see below). | ||
| 77 | cd into your existing qmail or netqmail source directory. Run | ||
| 78 | "patch < /path/to/qregex-<version>.patch" then "make qmail-smtpd". Now run | ||
| 79 | ./qmail-smtpd and test your new rules to make sure they work as expected. | ||
| 80 | |||
| 81 | Install the new binary by cd'ing to /var/qmail/bin and as root (in one command) | ||
| 82 | copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the | ||
| 83 | source directory to 'qmail-smtpd'. | ||
| 84 | (ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd) | ||
| 85 | |||
| 86 | You can also optionally just run "make setup check" as it will install the | ||
| 87 | updated documentation and man pages provided with this patch. Stopping qmail | ||
| 88 | before doing the "make setup check" is always a good idea. | ||
| 89 | |||
| 90 | |||
| 91 | LOGGING: | ||
| 92 | |||
| 93 | qregex will log matches to the patterns in the various control files. Log | ||
| 94 | messages will take these three forms depending on which control file was | ||
| 95 | matched: | ||
| 96 | |||
| 97 | badhelo | ||
| 98 | qmail-smtpd: badhelo: <host> at <remote IP> | ||
| 99 | |||
| 100 | badmailfrom and badmailfromnorelay | ||
| 101 | qmail-smtpd: badmailfrom: <sender address> at <remote IP> | ||
| 102 | |||
| 103 | badmailto and badmailtonorelay | ||
| 104 | qmail-smtpd: badmailto: <rcpt address> at <remote IP> | ||
| 105 | |||
| 106 | When the LOGREGEX environment variable is set, the matched pattern will | ||
| 107 | be included in the log. Log messages will have the regex pattern appended | ||
| 108 | to them. For example, a badhelo log message will look like this: | ||
| 109 | |||
| 110 | qmail-smtpd: badhelo: <host> at <remote IP> matches pattern: <regex> | ||
| 111 | |||
| 112 | |||
| 113 | CONTROL FILES: | ||
| 114 | |||
| 115 | qregex provides you with five control files. None of these control files | ||
| 116 | is mandatory and you can use them in any combination you choose in your setup. | ||
| 117 | |||
| 118 | The "control/badmailfrom" and "control/badmailto" files contain your REs for | ||
| 119 | matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope | ||
| 120 | recipient) smtp commands respectively. | ||
| 121 | The "control/badmailfromnorelay" and "control/badmailtonorelay" match against | ||
| 122 | the same commands but are read only when the RELAYCLIENT environment variable | ||
| 123 | is not set. | ||
| 124 | The "control/badhelo" file matches against the 'helo/ehlo' smtp command. | ||
| 125 | |||
| 126 | If you prefer you can symlink the badmailfrom and badmailto control files | ||
| 127 | (ln -s badmailfrom badmailto) and maintain fewer sets of rules. Beware | ||
| 128 | this might cause problems in certain setups. | ||
| 129 | |||
| 130 | Here's an example "badhelo" file. | ||
| 131 | ----------------------------------- | ||
| 132 | # block host strings with no dot (not a FQDN) | ||
| 133 | !\. | ||
| 134 | ----------------------------------- | ||
| 135 | |||
| 136 | An example "badmailfrom" file. | ||
| 137 | ----------------------------------- | ||
| 138 | # this will drop everything containing the string | ||
| 139 | # bad.domain.com or Bad.Domain.Com or BAD.domain.COM | ||
| 140 | bad\.domain\.com | ||
| 141 | # force users to fully qualify themselves | ||
| 142 | # (i.e. deny "user", accept "user@domain") | ||
| 143 | !@ | ||
| 144 | ----------------------------------- | ||
| 145 | |||
| 146 | And "badmailto" (a little more interesting) | ||
| 147 | ----------------------------------- | ||
| 148 | # must not contain invalid characters, brakets or multiple @'s | ||
| 149 | [!%#:*^(){}] | ||
| 150 | @.*@ | ||
| 151 | ----------------------------------- | ||
| 152 | |||
| 153 | You can use the non-RE character '!' to start an RE as a signal to qregex to | ||
| 154 | negate the action. As used above in the badmailfrom file, by negating the '@' | ||
| 155 | symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever | ||
| 156 | the address doesn't contain an @ symbol. When used inside a bracket expression, | ||
| 157 | the '!' character looses this special meaning. This is shown in the badmailto | ||
| 158 | example. | ||
| 159 | |||
| 160 | The norelay control files follow the same rules as the other control files but | ||
| 161 | are intended to address two specific scenarios. | ||
| 162 | The badmailfromnorelay file can be used to block mail trying to spoof a domain | ||
| 163 | hosted on your mail server. It prevents a mail client that is not allowed to | ||
| 164 | relay email through your server from using one of your hosted domains as its | ||
| 165 | envelope sender. | ||
| 166 | The badmailtonorelay file can be used to create email addresses that cannot | ||
| 167 | receive mail from any source not allowed to relay email through your server. | ||
| 168 | This is handy for creating email addresses for use only within your own | ||
| 169 | domain(s) that can't receive spam from the world at large. | ||
| 170 | |||
| 171 | |||
| 172 | INTERNALS: | ||
| 173 | |||
| 174 | qregex (or regexmatch as the function is called) will be called during the | ||
| 175 | `helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c". | ||
| 176 | When called, it will read the proper control file then one by one compile and | ||
| 177 | execute the regex on the string passed into qmail-smtpd. If the regex matches | ||
| 178 | it returns TRUE (1) and the qmail-smtpd process will deny the user the ability | ||
| 179 | to continue. If you change anything and think it betters this patch please | ||
| 180 | send me a new diff file so I can take a peek. | ||
| 181 | |||
| 182 | |||
| 183 | CONTACT: | ||
| 184 | qregex is maintained by: | ||
| 185 | Andrew St. Jean | ||
| 186 | andrew@arda.homeunix.net | ||
| 187 | www.arda.homeunix.net/store/qmail/ | ||
| 188 | |||
| 189 | Contributers to qregex: | ||
| 190 | Jeremy Kitchen | ||
| 191 | kitchen at scriptkitchen dot com | ||
| 192 | http://www.scriptkitchen.com/qmail | ||
| 193 | |||
| 194 | Alex Pleiner | ||
| 195 | alex@zeitform.de | ||
| 196 | zeitform Internet Dienste | ||
| 197 | http://www.zeitform.de/ | ||
| 198 | |||
| 199 | Thanos Massias | ||
| 200 | |||
| 201 | Original qregex patch written by: | ||
| 202 | Evan Borgstrom | ||
| 203 | evan at unixpimps dot org | ||
diff --git a/README.remote-auth b/README.remote-auth new file mode 100644 index 0000000..ab91f23 --- /dev/null +++ b/README.remote-auth | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | Docs for http://www.qmail.org/qmail-remote_authenticated_smtp.patch | ||
| 2 | a patch to convince qmail-remote to use Authenticated SMTP when | ||
| 3 | talking to certain hosts. | ||
| 4 | |||
| 5 | This version of the patch has been modified by Robert Sanders so that | ||
| 6 | base64 encoding of usernames and passwords is done automatically by | ||
| 7 | qmail-remote. | ||
| 8 | |||
| 9 | After applying the patch, the username password go into | ||
| 10 | /var/qmail/control/smtproutes like so: | ||
| 11 | |||
| 12 | domain:relay username password | ||
| 13 | |||
| 14 | The domain:relay part is identical to a normal smtproutes entry (see | ||
| 15 | the qmail-remote man page). For sites that require authentication, you | ||
| 16 | simply add two space separated fields to the line, where the first | ||
| 17 | field is the username, the second is the password. For example: | ||
| 18 | |||
| 19 | inside.af.mil:firewall.af.mil:26 johndoe secret | ||
| 20 | |||
| 21 | would send mail for inside.af.mil to firewall.af.mil, port 26 using | ||
| 22 | username 'johndoe' and password 'secret' | ||
| 23 | |||
| 24 | Since smtproutes will contain plain text usernames and passwords | ||
| 25 | you might want to make the file owned by qmailr and set its permissions | ||
| 26 | to 0600. Of course, the entries are going to go over the wire in the clear | ||
| 27 | anyway as part of the SMTP transaction. | ||
| 28 | |||
| 29 | Good luck. | ||
| 30 | |||
| 31 | j. | ||
diff --git a/README.starttls b/README.starttls new file mode 100644 index 0000000..0286632 --- /dev/null +++ b/README.starttls | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | Frederik Vermeulen <qmail-tls akrul inoa.net> 20060104 | ||
| 2 | http://inoa.net/qmail-tls/ | ||
| 3 | |||
| 4 | This patch implements RFC 3207 (was RFC 2487) in qmail. | ||
| 5 | This means you can get SSL or TLS encrypted and | ||
| 6 | authenticated SMTP between the MTAs and from MUA to MTA. | ||
| 7 | The code is considered experimental (but has worked for | ||
| 8 | many since its first release on 1999-03-21). | ||
| 9 | |||
| 10 | Usage: - install OpenSSL-0.9.8 http://www.openssl.org/ | ||
| 11 | (any 0.9.6 to 0.9.8 version is presumed to work) | ||
| 12 | - apply patch to netqmail-1.05 http://qmail.org/netqmail | ||
| 13 | (should work on qmail-1.03 too). The patches to | ||
| 14 | qmail-remote.c and qmail-smtpd.c can be applied separately. | ||
| 15 | - provide a server certificate in /var/qmail/control/servercert.pem. | ||
| 16 | "make cert" makes a self-signed certificate. | ||
| 17 | "make cert-req" makes a certificate request. | ||
| 18 | Note: you can add the CA certificate and intermediate | ||
| 19 | certs to the end of servercert.pem. | ||
| 20 | - replace qmail-smtpd and/or qmail-remote binary | ||
| 21 | - verify operation (header information should show | ||
| 22 | something like | ||
| 23 | "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;") | ||
| 24 | If you don't have a server to test with, you can test | ||
| 25 | by sending mail to tag-ping@tbs-internet.com, | ||
| 26 | which will bounce your mail. | ||
| 27 | |||
| 28 | Optional: - when DEBUG is defined, some extra TLS info will be logged | ||
| 29 | - qmail-remote will authenticate with the certificate in | ||
| 30 | /var/qmail/control/clientcert.pem. By preference this is | ||
| 31 | the same as servercert.pem, where nsCertType should be | ||
| 32 | == server,client or be a generic certificate (no usage specified). | ||
| 33 | - when a 512 bit RSA key is provided in /var/qmail/control/rsa512.pem, | ||
| 34 | this key will be used instead of (slow) on-the-fly generation by | ||
| 35 | qmail-smtpd. Idem for 512 and 1024 DH params in control/dh512.pem | ||
| 36 | and control/dh1024.pem. `make tmprsadh` does this. | ||
| 37 | Periodical replacement can be done by crontab: | ||
| 38 | 01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1 | ||
| 39 | - server authentication: | ||
| 40 | qmail-remote requires authentication from servers for which | ||
| 41 | /var/qmail/control/tlshosts/host.dom.ain.pem exists. | ||
| 42 | The .pem file contains the validating CA certificates | ||
| 43 | (or self-signed server certificate). CommonName has to match. | ||
| 44 | WARNING: this option may cause mail to be delayed, bounced, | ||
| 45 | doublebounced, and lost. | ||
| 46 | If /var/qmail/control/tlshosts/exhaustivelist is present, | ||
| 47 | the lists of hosts in /var/qmail/control/tlshosts is | ||
| 48 | an exhaustive list of hosts TLS is tried on. | ||
| 49 | If /var/qmail/control/notlshosts/host.dom.ain is present, | ||
| 50 | no TLS is tried on this host. | ||
| 51 | - client authentication: | ||
| 52 | when relay rules would reject an incoming mail, | ||
| 53 | qmail-smtpd can allow the mail based on a presented cert. | ||
| 54 | Certs are verified against a CA list in | ||
| 55 | /var/qmail/control/clientca.pem (eg. http://www.modssl.org/ | ||
| 56 | source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt) | ||
| 57 | and the cert email-address has to match a line in | ||
| 58 | /var/qmail/control/tlsclients. This email-address is logged | ||
| 59 | in the headers. CRLs can be provided through | ||
| 60 | /var/qmail/control/clientcrl.pem. | ||
| 61 | - cipher selection: | ||
| 62 | qmail-remote: | ||
| 63 | openssl cipher string (`man ciphers`) read from | ||
| 64 | /var/qmail/control/tlsclientciphers | ||
| 65 | qmail-smtpd: | ||
| 66 | openssl cipher string read from TLSCIPHERS environment variable | ||
| 67 | (can vary based on client IP address e.g.) | ||
| 68 | or if that is not available /var/qmail/control/tlsserverciphers | ||
| 69 | - smtps (deprecated SMTP over TLS via port 465): | ||
| 70 | qmail-remote: when connecting to port 465 | ||
| 71 | qmail-smtpd: when SMTPS environment variable is not empty | ||
| 72 | |||
| 73 | Caveats: - do a `make clean` after patching | ||
| 74 | - binaries dynamically linked with current openssl versions need | ||
| 75 | recompilation when the shared openssl libs are upgraded. | ||
| 76 | - this patch could conflict with other patches (notably those | ||
| 77 | replacing \n with \r\n, which is a bad idea on encrypted links). | ||
| 78 | - some broken servers have a problem with TLSv1 compatibility. | ||
| 79 | Uncomment the line where we set the SSL_OP_NO_TLSv1 option. | ||
| 80 | - needs working /dev/urandom (or EGD for openssl versions >0.9.7) | ||
| 81 | for seeding random number generator. | ||
| 82 | - packagers should make sure that installing without a valid | ||
| 83 | servercert is impossible | ||
| 84 | - when applied in combination with AUTH patch, AUTH patch | ||
| 85 | should be applied first and first part of this patch | ||
| 86 | will fail. This error can be ignored. Packagers should | ||
| 87 | cut the first 12 lines of this patch to make a happy | ||
| 88 | patch | ||
| 89 | - `make tmprsadh` is recommended (or should I say required), | ||
| 90 | otherwise DH generation can be unpredictably slow | ||
| 91 | - some need "-I/usr/kerberos/include" to be added in conf-cc | ||
| 92 | |||
| 93 | Copyright: GPL | ||
| 94 | Links with OpenSSL | ||
| 95 | Inspiration and code from examples in SSLeay (E. Young | ||
| 96 | <eay@cryptsoft.com> and T. Hudson <tjh@cryptsoft.com>), | ||
| 97 | stunnel (M. Trojnara <mtrojnar@ddc.daewoo.com.pl>), | ||
| 98 | Postfix/TLS (L. Jaenicke <Lutz.Jaenicke@aet.tu-cottbus.de>), | ||
| 99 | modssl (R. Engelschall <rse@engelschall.com>), | ||
| 100 | openssl examples of E. Rescorla <ekr@rtfm.com>. | ||
| 101 | |||
| 102 | Bug reports: mailto:<qmail-tls akrul inoa.net> | ||
| 103 | |||
| @@ -10,6 +10,7 @@ qmail-local.o | |||
| 10 | qmail.o | 10 | qmail.o |
| 11 | quote.o | 11 | quote.o |
| 12 | now.o | 12 | now.o |
| 13 | base64.o | ||
| 13 | gfrom.o | 14 | gfrom.o |
| 14 | myctime.o | 15 | myctime.o |
| 15 | slurpclose.o | 16 | slurpclose.o |
| @@ -168,6 +169,8 @@ control.o | |||
| 168 | constmap.o | 169 | constmap.o |
| 169 | timeoutread.o | 170 | timeoutread.o |
| 170 | timeoutwrite.o | 171 | timeoutwrite.o |
| 172 | tls.o | ||
| 173 | ssl_timeoutio.o | ||
| 171 | timeoutconn.o | 174 | timeoutconn.o |
| 172 | tcpto.o | 175 | tcpto.o |
| 173 | dns.o | 176 | dns.o |
| @@ -252,6 +255,7 @@ rcpthosts.o | |||
| 252 | qmail-qmtpd | 255 | qmail-qmtpd |
| 253 | qmail-smtpd.o | 256 | qmail-smtpd.o |
| 254 | qmail-smtpd | 257 | qmail-smtpd |
| 258 | qregex.o | ||
| 255 | sendmail.o | 259 | sendmail.o |
| 256 | sendmail | 260 | sendmail |
| 257 | tcp-env.o | 261 | tcp-env.o |
| @@ -320,6 +324,7 @@ binm2 | |||
| 320 | binm2+df | 324 | binm2+df |
| 321 | binm3 | 325 | binm3 |
| 322 | binm3+df | 326 | binm3+df |
| 327 | Makefile-cert | ||
| 323 | it | 328 | it |
| 324 | qmail-local.0 | 329 | qmail-local.0 |
| 325 | qmail-lspawn.0 | 330 | qmail-lspawn.0 |
| @@ -385,3 +390,4 @@ forgeries.0 | |||
| 385 | man | 390 | man |
| 386 | setup | 391 | setup |
| 387 | check | 392 | check |
| 393 | update_tmprsadh | ||
diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..ea8b777 --- /dev/null +++ b/base64.c | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | #include "base64.h" | ||
| 2 | #include "stralloc.h" | ||
| 3 | #include "substdio.h" | ||
| 4 | #include "str.h" | ||
| 5 | |||
| 6 | static char *b64alpha = | ||
| 7 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
| 8 | #define B64PAD '=' | ||
| 9 | |||
| 10 | /* returns 0 ok, 1 illegal, -1 problem */ | ||
| 11 | |||
| 12 | int b64decode(in,l,out) | ||
| 13 | const unsigned char *in; | ||
| 14 | int l; | ||
| 15 | stralloc *out; /* not null terminated */ | ||
| 16 | { | ||
| 17 | int p = 0; | ||
| 18 | int n; | ||
| 19 | unsigned int x; | ||
| 20 | int i, j; | ||
| 21 | char *s; | ||
| 22 | unsigned char b[3]; | ||
| 23 | |||
| 24 | if (l == 0) | ||
| 25 | { | ||
| 26 | if (!stralloc_copys(out,"")) return -1; | ||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | while(in[l-1] == B64PAD) { | ||
| 31 | p ++; | ||
| 32 | l--; | ||
| 33 | } | ||
| 34 | |||
| 35 | n = (l + p) / 4; | ||
| 36 | out->len = (n * 3) - p; | ||
| 37 | if (!stralloc_ready(out,out->len)) return -1; | ||
| 38 | s = out->s; | ||
| 39 | |||
| 40 | for(i = 0; i < n - 1 ; i++) { | ||
| 41 | x = 0; | ||
| 42 | for(j = 0; j < 4; j++) { | ||
| 43 | if(in[j] >= 'A' && in[j] <= 'Z') | ||
| 44 | x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); | ||
| 45 | else if(in[j] >= 'a' && in[j] <= 'z') | ||
| 46 | x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); | ||
| 47 | else if(in[j] >= '0' && in[j] <= '9') | ||
| 48 | x = (x << 6) + (unsigned int)(in[j] - '0' + 52); | ||
| 49 | else if(in[j] == '+') | ||
| 50 | x = (x << 6) + 62; | ||
| 51 | else if(in[j] == '/') | ||
| 52 | x = (x << 6) + 63; | ||
| 53 | else if(in[j] == '=') | ||
| 54 | x = (x << 6); | ||
| 55 | } | ||
| 56 | |||
| 57 | s[2] = (unsigned char)(x & 255); x >>= 8; | ||
| 58 | s[1] = (unsigned char)(x & 255); x >>= 8; | ||
| 59 | s[0] = (unsigned char)(x & 255); x >>= 8; | ||
| 60 | s += 3; in += 4; | ||
| 61 | } | ||
| 62 | |||
| 63 | x = 0; | ||
| 64 | for(j = 0; j < 4; j++) { | ||
| 65 | if(in[j] >= 'A' && in[j] <= 'Z') | ||
| 66 | x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); | ||
| 67 | else if(in[j] >= 'a' && in[j] <= 'z') | ||
| 68 | x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); | ||
| 69 | else if(in[j] >= '0' && in[j] <= '9') | ||
| 70 | x = (x << 6) + (unsigned int)(in[j] - '0' + 52); | ||
| 71 | else if(in[j] == '+') | ||
| 72 | x = (x << 6) + 62; | ||
| 73 | else if(in[j] == '/') | ||
| 74 | x = (x << 6) + 63; | ||
| 75 | else if(in[j] == '=') | ||
| 76 | x = (x << 6); | ||
| 77 | } | ||
| 78 | |||
| 79 | b[2] = (unsigned char)(x & 255); x >>= 8; | ||
| 80 | b[1] = (unsigned char)(x & 255); x >>= 8; | ||
| 81 | b[0] = (unsigned char)(x & 255); x >>= 8; | ||
| 82 | |||
| 83 | for(i = 0; i < 3 - p; i++) | ||
| 84 | s[i] = b[i]; | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | int b64encode(in,out) | ||
| 90 | stralloc *in; | ||
| 91 | stralloc *out; /* not null terminated */ | ||
| 92 | { | ||
| 93 | unsigned char a, b, c; | ||
| 94 | int i; | ||
| 95 | char *s; | ||
| 96 | |||
| 97 | if (in->len == 0) | ||
| 98 | { | ||
| 99 | if (!stralloc_copys(out,"")) return -1; | ||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; | ||
| 104 | s = out->s; | ||
| 105 | |||
| 106 | for (i = 0;i < in->len;i += 3) { | ||
| 107 | a = in->s[i]; | ||
| 108 | b = i + 1 < in->len ? in->s[i + 1] : 0; | ||
| 109 | c = i + 2 < in->len ? in->s[i + 2] : 0; | ||
| 110 | |||
| 111 | *s++ = b64alpha[a >> 2]; | ||
| 112 | *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; | ||
| 113 | |||
| 114 | if (i + 1 >= in->len) *s++ = B64PAD; | ||
| 115 | else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; | ||
| 116 | |||
| 117 | if (i + 2 >= in->len) *s++ = B64PAD; | ||
| 118 | else *s++ = b64alpha[c & 63]; | ||
| 119 | } | ||
| 120 | out->len = s - out->s; | ||
| 121 | return 0; | ||
| 122 | } | ||
diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..a9164c0 --- /dev/null +++ b/base64.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef BASE64_H | ||
| 2 | #define BASE64_H | ||
| 3 | |||
| 4 | extern int b64decode(); | ||
| 5 | extern int b64encode(); | ||
| 6 | |||
| 7 | #endif | ||
diff --git a/case_startb.c b/case_startb.c new file mode 100644 index 0000000..ee88efe --- /dev/null +++ b/case_startb.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #include "case.h" | ||
| 2 | |||
| 3 | int case_startb(s,len,t) | ||
| 4 | register char *s; | ||
| 5 | unsigned int len; | ||
| 6 | register char *t; | ||
| 7 | { | ||
| 8 | register unsigned char x; | ||
| 9 | register unsigned char y; | ||
| 10 | |||
| 11 | for (;;) { | ||
| 12 | y = *t++ - 'A'; | ||
| 13 | if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; | ||
| 14 | if (!y) return 1; | ||
| 15 | if (!len) return 0; | ||
| 16 | --len; | ||
| 17 | x = *s++ - 'A'; | ||
| 18 | if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; | ||
| 19 | if (x != y) return 0; | ||
| 20 | } | ||
| 21 | } | ||
| @@ -1,3 +1,3 @@ | |||
| 1 | cc -O2 | 1 | cc -O2 -DTLS=20070408 -I/usr/local/ssl/include |
| 2 | 2 | ||
| 3 | This will be used to compile .c files. | 3 | This will be used to compile .c files. |
| @@ -267,12 +267,11 @@ stralloc *sa; | |||
| 267 | int pref; | 267 | int pref; |
| 268 | { | 268 | { |
| 269 | int r; | 269 | int r; |
| 270 | struct ip_mx ix; | 270 | struct ip_mx ix = {0}; |
| 271 | 271 | ||
| 272 | if (!stralloc_copy(&glue,sa)) return DNS_MEM; | 272 | if (!stralloc_copy(&glue,sa)) return DNS_MEM; |
| 273 | if (!stralloc_0(&glue)) return DNS_MEM; | 273 | if (!stralloc_0(&glue)) return DNS_MEM; |
| 274 | if (glue.s[0]) { | 274 | if (glue.s[0]) { |
| 275 | ix.pref = 0; | ||
| 276 | if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) | 275 | if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) |
| 277 | { | 276 | { |
| 278 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; | 277 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; |
| @@ -291,9 +290,16 @@ int pref; | |||
| 291 | ix.ip = ip; | 290 | ix.ip = ip; |
| 292 | ix.pref = pref; | 291 | ix.pref = pref; |
| 293 | if (r == DNS_SOFT) return DNS_SOFT; | 292 | if (r == DNS_SOFT) return DNS_SOFT; |
| 294 | if (r == 1) | 293 | if (r == 1) { |
| 294 | #ifdef IX_FQDN | ||
| 295 | ix.fqdn = glue.s; | ||
| 296 | #endif | ||
| 295 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; | 297 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; |
| 296 | } | 298 | } |
| 299 | } | ||
| 300 | #ifdef IX_FQDN | ||
| 301 | glue.s = 0; | ||
| 302 | #endif | ||
| 297 | return 0; | 303 | return 0; |
| 298 | } | 304 | } |
| 299 | 305 | ||
| @@ -313,7 +319,7 @@ unsigned long random; | |||
| 313 | { | 319 | { |
| 314 | int r; | 320 | int r; |
| 315 | struct mx { stralloc sa; unsigned short p; } *mx; | 321 | struct mx { stralloc sa; unsigned short p; } *mx; |
| 316 | struct ip_mx ix; | 322 | struct ip_mx ix = {0}; |
| 317 | int nummx; | 323 | int nummx; |
| 318 | int i; | 324 | int i; |
| 319 | int j; | 325 | int j; |
| @@ -325,7 +331,6 @@ unsigned long random; | |||
| 325 | if (!stralloc_copy(&glue,sa)) return DNS_MEM; | 331 | if (!stralloc_copy(&glue,sa)) return DNS_MEM; |
| 326 | if (!stralloc_0(&glue)) return DNS_MEM; | 332 | if (!stralloc_0(&glue)) return DNS_MEM; |
| 327 | if (glue.s[0]) { | 333 | if (glue.s[0]) { |
| 328 | ix.pref = 0; | ||
| 329 | if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) | 334 | if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) |
| 330 | { | 335 | { |
| 331 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; | 336 | if (!ipalloc_append(ia,&ix)) return DNS_MEM; |
| @@ -76,6 +76,10 @@ void hier() | |||
| 76 | c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); | 76 | c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); |
| 77 | 77 | ||
| 78 | c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); | 78 | c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); |
| 79 | c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); | ||
| 80 | c(auto_qmail,"doc","README.auth",auto_uido,auto_gidq,0644); | ||
| 81 | c(auto_qmail,"doc","README.remote-auth",auto_uido,auto_gidq,0644); | ||
| 82 | c(auto_qmail,"doc","README.starttls",auto_uido,auto_gidq,0644); | ||
| 79 | c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); | 83 | c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); |
| 80 | c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); | 84 | c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); |
| 81 | c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); | 85 | c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); |
| @@ -143,6 +147,9 @@ void hier() | |||
| 143 | c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); | 147 | c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); |
| 144 | c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); | 148 | c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); |
| 145 | c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); | 149 | c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); |
| 150 | #ifdef TLS | ||
| 151 | c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755); | ||
| 152 | #endif | ||
| 146 | 153 | ||
| 147 | c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); | 154 | c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); |
| 148 | c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); | 155 | c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); |
diff --git a/install-big.c b/install-big.c index df813df..a058ad2 100644 --- a/install-big.c +++ b/install-big.c | |||
| @@ -76,6 +76,10 @@ void hier() | |||
| 76 | c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); | 76 | c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); |
| 77 | 77 | ||
| 78 | c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); | 78 | c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); |
| 79 | c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); | ||
| 80 | c(auto_qmail,"doc","README.auth",auto_uido,auto_gidq,0644); | ||
| 81 | c(auto_qmail,"doc","README.remote-auth",auto_uido,auto_gidq,0644); | ||
| 82 | c(auto_qmail,"doc","README.starttls",auto_uido,auto_gidq,0644); | ||
| 79 | c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); | 83 | c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); |
| 80 | c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); | 84 | c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); |
| 81 | c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); | 85 | c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); |
| @@ -3,7 +3,15 @@ | |||
| 3 | 3 | ||
| 4 | #include "ip.h" | 4 | #include "ip.h" |
| 5 | 5 | ||
| 6 | #ifdef TLS | ||
| 7 | # define IX_FQDN 1 | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #ifdef IX_FQDN | ||
| 11 | struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ; | ||
| 12 | #else | ||
| 6 | struct ip_mx { struct ip_address ip; int pref; } ; | 13 | struct ip_mx { struct ip_address ip; int pref; } ; |
| 14 | #endif | ||
| 7 | 15 | ||
| 8 | #include "gen_alloc.h" | 16 | #include "gen_alloc.h" |
| 9 | 17 | ||
diff --git a/qmail-control.9 b/qmail-control.9 index 503ce93..149c328 100644 --- a/qmail-control.9 +++ b/qmail-control.9 | |||
| @@ -20,7 +20,11 @@ other hostname-related control files. | |||
| 20 | 20 | ||
| 21 | Comments are allowed | 21 | Comments are allowed |
| 22 | in | 22 | in |
| 23 | .IR badhelo , | ||
| 23 | .IR badmailfrom , | 24 | .IR badmailfrom , |
| 25 | .IR badmailfromnorelay , | ||
| 26 | .IR badmailto , | ||
| 27 | .IR badmailtonorelay , | ||
| 24 | .IR locals , | 28 | .IR locals , |
| 25 | .IR percenthack , | 29 | .IR percenthack , |
| 26 | .IR qmqpservers , | 30 | .IR qmqpservers , |
| @@ -40,14 +44,22 @@ See the corresponding man pages for further details. | |||
| 40 | .ta 5c 10c | 44 | .ta 5c 10c |
| 41 | control default used by | 45 | control default used by |
| 42 | 46 | ||
| 47 | .I badhelo \fR(none) \fRqmail-smtpd | ||
| 43 | .I badmailfrom \fR(none) \fRqmail-smtpd | 48 | .I badmailfrom \fR(none) \fRqmail-smtpd |
| 49 | .I badmailfromnorelay \fR(none) \fRqmail-smtpd | ||
| 50 | .I badmailto \fR(none) \fRqmail-smtpd | ||
| 51 | .I badmailtonorelay \fR(none) \fRqmail-smtpd | ||
| 44 | .I bouncefrom \fRMAILER-DAEMON \fRqmail-send | 52 | .I bouncefrom \fRMAILER-DAEMON \fRqmail-send |
| 45 | .I bouncehost \fIme \fRqmail-send | 53 | .I bouncehost \fIme \fRqmail-send |
| 54 | .I clientca.pem \fR(none) \fRqmail-smtpd | ||
| 55 | .I clientcert.pem \fR(none) \fRqmail-remote | ||
| 46 | .I concurrencylocal \fR10 \fRqmail-send | 56 | .I concurrencylocal \fR10 \fRqmail-send |
| 47 | .I concurrencyremote \fR20 \fRqmail-send | 57 | .I concurrencyremote \fR20 \fRqmail-send |
| 48 | .I defaultdomain \fIme \fRqmail-inject | 58 | .I defaultdomain \fIme \fRqmail-inject |
| 49 | .I defaulthost \fIme \fRqmail-inject | 59 | .I defaulthost \fIme \fRqmail-inject |
| 50 | .I databytes \fR0 \fRqmail-smtpd | 60 | .I databytes \fR0 \fRqmail-smtpd |
| 61 | .I dh1024.pem \fR(none) \fRqmail-smtpd | ||
| 62 | .I dh512.pem \fR(none) \fRqmail-smtpd | ||
| 51 | .I doublebouncehost \fIme \fRqmail-send | 63 | .I doublebouncehost \fIme \fRqmail-send |
| 52 | .I doublebounceto \fRpostmaster \fRqmail-send | 64 | .I doublebounceto \fRpostmaster \fRqmail-send |
| 53 | .I envnoathost \fIme \fRqmail-send | 65 | .I envnoathost \fIme \fRqmail-send |
| @@ -61,11 +73,17 @@ control default used by | |||
| 61 | .I qmqpservers \fR(none) \fRqmail-qmqpc | 73 | .I qmqpservers \fR(none) \fRqmail-qmqpc |
| 62 | .I queuelifetime \fR604800 \fRqmail-send | 74 | .I queuelifetime \fR604800 \fRqmail-send |
| 63 | .I rcpthosts \fR(none) \fRqmail-smtpd | 75 | .I rcpthosts \fR(none) \fRqmail-smtpd |
| 76 | .I rsa512.pem \fR(none) \fRqmail-smtpd | ||
| 77 | .I servercert.pem \fR(none) \fRqmail-smtpd | ||
| 64 | .I smtpgreeting \fIme \fRqmail-smtpd | 78 | .I smtpgreeting \fIme \fRqmail-smtpd |
| 65 | .I smtproutes \fR(none) \fRqmail-remote | 79 | .I smtproutes \fR(none) \fRqmail-remote |
| 66 | .I timeoutconnect \fR60 \fRqmail-remote | 80 | .I timeoutconnect \fR60 \fRqmail-remote |
| 67 | .I timeoutremote \fR1200 \fRqmail-remote | 81 | .I timeoutremote \fR1200 \fRqmail-remote |
| 68 | .I timeoutsmtpd \fR1200 \fRqmail-smtpd | 82 | .I timeoutsmtpd \fR1200 \fRqmail-smtpd |
| 83 | .I tlsclients \fR(none) \fRqmail-smtpd | ||
| 84 | .I tlsclientciphers \fR(none) \fRqmail-remote | ||
| 85 | .I tlshosts/FQDN.pem \fR(none) \fRqmail-remote | ||
| 86 | .I tlsserverciphers \fR(none) \fRqmail-smtpd | ||
| 69 | .I virtualdomains \fR(none) \fRqmail-send | 87 | .I virtualdomains \fR(none) \fRqmail-send |
| 70 | .fi | 88 | .fi |
| 71 | .RE | 89 | .RE |
diff --git a/qmail-remote.8 b/qmail-remote.8 index 08bae85..995443b 100644 --- a/qmail-remote.8 +++ b/qmail-remote.8 | |||
| @@ -114,6 +114,10 @@ arguments. | |||
| 114 | always exits zero. | 114 | always exits zero. |
| 115 | .SH "CONTROL FILES" | 115 | .SH "CONTROL FILES" |
| 116 | .TP 5 | 116 | .TP 5 |
| 117 | .I clientcert.pem | ||
| 118 | SSL certificate that is used to authenticate with the remote server | ||
| 119 | during a TLS session. | ||
| 120 | .TP 5 | ||
| 117 | .I helohost | 121 | .I helohost |
| 118 | Current host name, | 122 | Current host name, |
| 119 | for use solely in saying hello to the remote SMTP server. | 123 | for use solely in saying hello to the remote SMTP server. |
| @@ -123,6 +127,16 @@ if that is supplied; | |||
| 123 | otherwise | 127 | otherwise |
| 124 | .B qmail-remote | 128 | .B qmail-remote |
| 125 | refuses to run. | 129 | refuses to run. |
| 130 | |||
| 131 | .TP 5 | ||
| 132 | .I notlshosts/<FQDN> | ||
| 133 | .B qmail-remote | ||
| 134 | will not try TLS on servers for which this file exists | ||
| 135 | .RB ( <FQDN> | ||
| 136 | is the fully-qualified domain name of the server). | ||
| 137 | .IR (tlshosts/<FQDN>.pem | ||
| 138 | takes precedence over this file however). | ||
| 139 | |||
| 126 | .TP 5 | 140 | .TP 5 |
| 127 | .I smtproutes | 141 | .I smtproutes |
| 128 | Artificial SMTP routes. | 142 | Artificial SMTP routes. |
| @@ -156,6 +170,8 @@ may be empty; | |||
| 156 | this tells | 170 | this tells |
| 157 | .B qmail-remote | 171 | .B qmail-remote |
| 158 | to look up MX records as usual. | 172 | to look up MX records as usual. |
| 173 | .I port | ||
| 174 | value of 465 (deprecated smtps port) causes TLS session to be started. | ||
| 159 | .I smtproutes | 175 | .I smtproutes |
| 160 | may include wildcards: | 176 | may include wildcards: |
| 161 | 177 | ||
| @@ -195,6 +211,33 @@ Number of seconds | |||
| 195 | .B qmail-remote | 211 | .B qmail-remote |
| 196 | will wait for each response from the remote SMTP server. | 212 | will wait for each response from the remote SMTP server. |
| 197 | Default: 1200. | 213 | Default: 1200. |
| 214 | |||
| 215 | .TP 5 | ||
| 216 | .I tlsclientciphers | ||
| 217 | A set of OpenSSL client cipher strings. Multiple ciphers | ||
| 218 | contained in a string should be separated by a colon. | ||
| 219 | |||
| 220 | .TP 5 | ||
| 221 | .I tlshosts/<FQDN>.pem | ||
| 222 | .B qmail-remote | ||
| 223 | requires TLS authentication from servers for which this file exists | ||
| 224 | .RB ( <FQDN> | ||
| 225 | is the fully-qualified domain name of the server). One of the | ||
| 226 | .I dNSName | ||
| 227 | or the | ||
| 228 | .I CommonName | ||
| 229 | attributes have to match. The file contains the trusted CA certificates. | ||
| 230 | |||
| 231 | .B WARNING: | ||
| 232 | this option may cause mail to be delayed, bounced, doublebounced, or lost. | ||
| 233 | |||
| 234 | .TP 5 | ||
| 235 | .I tlshosts/exhaustivelist | ||
| 236 | if this file exists | ||
| 237 | no TLS will be tried on hosts other than those for which a file | ||
| 238 | .B tlshosts/<FQDN>.pem | ||
| 239 | exists. | ||
| 240 | |||
| 198 | .SH "SEE ALSO" | 241 | .SH "SEE ALSO" |
| 199 | addresses(5), | 242 | addresses(5), |
| 200 | envelopes(5), | 243 | envelopes(5), |
diff --git a/qmail-remote.c b/qmail-remote.c index 7d65473..dae884d 100644 --- a/qmail-remote.c +++ b/qmail-remote.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "timeoutconn.h" | 28 | #include "timeoutconn.h" |
| 29 | #include "timeoutread.h" | 29 | #include "timeoutread.h" |
| 30 | #include "timeoutwrite.h" | 30 | #include "timeoutwrite.h" |
| 31 | #include "base64.h" | ||
| 31 | 32 | ||
| 32 | #define HUGESMTPTEXT 5000 | 33 | #define HUGESMTPTEXT 5000 |
| 33 | 34 | ||
| @@ -43,11 +44,27 @@ stralloc routes = {0}; | |||
| 43 | struct constmap maproutes; | 44 | struct constmap maproutes; |
| 44 | stralloc host = {0}; | 45 | stralloc host = {0}; |
| 45 | stralloc sender = {0}; | 46 | stralloc sender = {0}; |
| 47 | stralloc auth_smtp_user = {0}; | ||
| 48 | stralloc auth_smtp_pass = {0}; | ||
| 49 | stralloc auth_b64_user = {0}; | ||
| 50 | stralloc auth_b64_pass = {0}; | ||
| 51 | stralloc auth_status = {0}; | ||
| 46 | 52 | ||
| 47 | saa reciplist = {0}; | 53 | saa reciplist = {0}; |
| 48 | 54 | ||
| 49 | struct ip_address partner; | 55 | struct ip_address partner; |
| 50 | 56 | ||
| 57 | #ifdef TLS | ||
| 58 | # include <sys/stat.h> | ||
| 59 | # include "tls.h" | ||
| 60 | # include "ssl_timeoutio.h" | ||
| 61 | # include <openssl/x509v3.h> | ||
| 62 | # define EHLO 1 | ||
| 63 | |||
| 64 | int tls_init(); | ||
| 65 | const char *ssl_err_str = 0; | ||
| 66 | #endif | ||
| 67 | |||
| 51 | void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } | 68 | void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } |
| 52 | void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } | 69 | void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } |
| 53 | void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } | 70 | void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } |
| @@ -85,6 +102,18 @@ void perm_ambigmx() { out("D\ | |||
| 85 | Sorry. Although I'm listed as a best-preference MX or A for that host,\n\ | 102 | Sorry. Although I'm listed as a best-preference MX or A for that host,\n\ |
| 86 | it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); | 103 | it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); |
| 87 | zerodie(); } | 104 | zerodie(); } |
| 105 | void auth_user_not_set() { | ||
| 106 | if(!stralloc_copys(&auth_status, \ | ||
| 107 | "User and password not set, continuing without authentication.\n")) | ||
| 108 | temp_nomem(); | ||
| 109 | if(!stralloc_0(&auth_status)) temp_nomem(); | ||
| 110 | } | ||
| 111 | void no_supported_auth() { | ||
| 112 | if(!stralloc_copys(&auth_status, \ | ||
| 113 | "No supported AUTH method found, continuing without authentication.\n")) | ||
| 114 | temp_nomem(); | ||
| 115 | if(!stralloc_0(&auth_status)) temp_nomem(); | ||
| 116 | } | ||
| 88 | 117 | ||
| 89 | void outhost() | 118 | void outhost() |
| 90 | { | 119 | { |
| @@ -99,6 +128,9 @@ void dropped() { | |||
| 99 | outhost(); | 128 | outhost(); |
| 100 | out(" but connection died. "); | 129 | out(" but connection died. "); |
| 101 | if (flagcritical) out("Possible duplicate! "); | 130 | if (flagcritical) out("Possible duplicate! "); |
| 131 | #ifdef TLS | ||
| 132 | if (ssl_err_str) { out(ssl_err_str); out(" "); } | ||
| 133 | #endif | ||
| 102 | out("(#4.4.2)\n"); | 134 | out("(#4.4.2)\n"); |
| 103 | zerodie(); | 135 | zerodie(); |
| 104 | } | 136 | } |
| @@ -110,6 +142,12 @@ int timeout = 1200; | |||
| 110 | int saferead(fd,buf,len) int fd; char *buf; int len; | 142 | int saferead(fd,buf,len) int fd; char *buf; int len; |
| 111 | { | 143 | { |
| 112 | int r; | 144 | int r; |
| 145 | #ifdef TLS | ||
| 146 | if (ssl) { | ||
| 147 | r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len); | ||
| 148 | if (r < 0) ssl_err_str = ssl_error_str(); | ||
| 149 | } else | ||
| 150 | #endif | ||
| 113 | r = timeoutread(timeout,smtpfd,buf,len); | 151 | r = timeoutread(timeout,smtpfd,buf,len); |
| 114 | if (r <= 0) dropped(); | 152 | if (r <= 0) dropped(); |
| 115 | return r; | 153 | return r; |
| @@ -117,6 +155,12 @@ int saferead(fd,buf,len) int fd; char *buf; int len; | |||
| 117 | int safewrite(fd,buf,len) int fd; char *buf; int len; | 155 | int safewrite(fd,buf,len) int fd; char *buf; int len; |
| 118 | { | 156 | { |
| 119 | int r; | 157 | int r; |
| 158 | #ifdef TLS | ||
| 159 | if (ssl) { | ||
| 160 | r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); | ||
| 161 | if (r < 0) ssl_err_str = ssl_error_str(); | ||
| 162 | } else | ||
| 163 | #endif | ||
| 120 | r = timeoutwrite(timeout,smtpfd,buf,len); | 164 | r = timeoutwrite(timeout,smtpfd,buf,len); |
| 121 | if (r <= 0) dropped(); | 165 | if (r <= 0) dropped(); |
| 122 | return r; | 166 | return r; |
| @@ -163,6 +207,65 @@ unsigned long smtpcode() | |||
| 163 | return code; | 207 | return code; |
| 164 | } | 208 | } |
| 165 | 209 | ||
| 210 | #ifdef EHLO | ||
| 211 | saa ehlokw = {0}; /* list of EHLO keywords and parameters */ | ||
| 212 | int maxehlokwlen = 0; | ||
| 213 | |||
| 214 | unsigned long ehlo() | ||
| 215 | { | ||
| 216 | stralloc *sa; | ||
| 217 | char *s, *e, *p; | ||
| 218 | unsigned long code; | ||
| 219 | |||
| 220 | if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; | ||
| 221 | ehlokw.len = 0; | ||
| 222 | |||
| 223 | # ifdef MXPS | ||
| 224 | if (type == 's') return 0; | ||
| 225 | # endif | ||
| 226 | |||
| 227 | substdio_puts(&smtpto, "EHLO "); | ||
| 228 | substdio_put(&smtpto, helohost.s, helohost.len); | ||
| 229 | substdio_puts(&smtpto, "\r\n"); | ||
| 230 | substdio_flush(&smtpto); | ||
| 231 | |||
| 232 | code = smtpcode(); | ||
| 233 | if (code != 250) return code; | ||
| 234 | |||
| 235 | s = smtptext.s; | ||
| 236 | while (*s++ != '\n') ; /* skip the first line: contains the domain */ | ||
| 237 | |||
| 238 | e = smtptext.s + smtptext.len - 6; /* 250-?\n */ | ||
| 239 | while (s <= e) | ||
| 240 | { | ||
| 241 | int wasspace = 0; | ||
| 242 | |||
| 243 | if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); | ||
| 244 | sa = ehlokw.sa + ehlokw.len++; | ||
| 245 | if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; | ||
| 246 | |||
| 247 | /* smtptext is known to end in a '\n' */ | ||
| 248 | for (p = (s += 4); ; ++p) | ||
| 249 | if (*p == '\n' || *p == ' ' || *p == '\t') { | ||
| 250 | if (!wasspace) | ||
| 251 | if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); | ||
| 252 | if (*p == '\n') break; | ||
| 253 | wasspace = 1; | ||
| 254 | } else if (wasspace == 1) { | ||
| 255 | wasspace = 0; | ||
| 256 | s = p; | ||
| 257 | } | ||
| 258 | s = ++p; | ||
| 259 | |||
| 260 | /* keyword should consist of alpha-num and '-' | ||
| 261 | * broken AUTH might use '=' instead of space */ | ||
| 262 | for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } | ||
| 263 | } | ||
| 264 | |||
| 265 | return 250; | ||
| 266 | } | ||
| 267 | #endif | ||
| 268 | |||
| 166 | void outsmtptext() | 269 | void outsmtptext() |
| 167 | { | 270 | { |
| 168 | int i; | 271 | int i; |
| @@ -179,6 +282,11 @@ void quit(prepend,append) | |||
| 179 | char *prepend; | 282 | char *prepend; |
| 180 | char *append; | 283 | char *append; |
| 181 | { | 284 | { |
| 285 | #ifdef TLS | ||
| 286 | /* shouldn't talk to the client unless in an appropriate state */ | ||
| 287 | int state = ssl ? ssl->state : SSL_ST_BEFORE; | ||
| 288 | if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE)) | ||
| 289 | #endif | ||
| 182 | substdio_putsflush(&smtpto,"QUIT\r\n"); | 290 | substdio_putsflush(&smtpto,"QUIT\r\n"); |
| 183 | /* waiting for remote side is just too ridiculous */ | 291 | /* waiting for remote side is just too ridiculous */ |
| 184 | out(prepend); | 292 | out(prepend); |
| @@ -186,6 +294,30 @@ char *append; | |||
| 186 | out(append); | 294 | out(append); |
| 187 | out(".\n"); | 295 | out(".\n"); |
| 188 | outsmtptext(); | 296 | outsmtptext(); |
| 297 | |||
| 298 | #if defined(TLS) && defined(DEBUG) | ||
| 299 | if (ssl) { | ||
| 300 | X509 *peercert; | ||
| 301 | |||
| 302 | out("STARTTLS proto="); out(SSL_get_version(ssl)); | ||
| 303 | out("; cipher="); out(SSL_get_cipher(ssl)); | ||
| 304 | |||
| 305 | /* we want certificate details */ | ||
| 306 | if (peercert = SSL_get_peer_certificate(ssl)) { | ||
| 307 | char *str; | ||
| 308 | |||
| 309 | str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0); | ||
| 310 | out("; subject="); out(str); OPENSSL_free(str); | ||
| 311 | |||
| 312 | str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0); | ||
| 313 | out("; issuer="); out(str); OPENSSL_free(str); | ||
| 314 | |||
| 315 | X509_free(peercert); | ||
| 316 | } | ||
| 317 | out(";\n"); | ||
| 318 | } | ||
| 319 | #endif | ||
| 320 | |||
| 189 | zerodie(); | 321 | zerodie(); |
| 190 | } | 322 | } |
| 191 | 323 | ||
| @@ -214,26 +346,297 @@ void blast() | |||
| 214 | substdio_flush(&smtpto); | 346 | substdio_flush(&smtpto); |
| 215 | } | 347 | } |
| 216 | 348 | ||
| 349 | #ifdef TLS | ||
| 350 | char *partner_fqdn = 0; | ||
| 351 | |||
| 352 | # define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") | ||
| 353 | void tls_quit(const char *s1, const char *s2) | ||
| 354 | { | ||
| 355 | out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT; | ||
| 356 | } | ||
| 357 | # define tls_quit_error(s) tls_quit(s, ssl_error()) | ||
| 358 | |||
| 359 | int match_partner(const char *s, int len) | ||
| 360 | { | ||
| 361 | if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; | ||
| 362 | /* we also match if the name is *.domainname */ | ||
| 363 | if (*s == '*') { | ||
| 364 | const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); | ||
| 365 | if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; | ||
| 366 | } | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | /* don't want to fail handshake if certificate can't be verified */ | ||
| 371 | int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } | ||
| 372 | |||
| 373 | int tls_init() | ||
| 374 | { | ||
| 375 | int i; | ||
| 376 | SSL *myssl; | ||
| 377 | SSL_CTX *ctx; | ||
| 378 | stralloc saciphers = {0}; | ||
| 379 | const char *ciphers, *servercert = 0; | ||
| 380 | |||
| 381 | if (partner_fqdn) { | ||
| 382 | struct stat st; | ||
| 383 | stralloc tmp = {0}; | ||
| 384 | if (!stralloc_copys(&tmp, "control/tlshosts/") | ||
| 385 | || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) | ||
| 386 | || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); | ||
| 387 | if (stat(tmp.s, &st) == 0) | ||
| 388 | servercert = tmp.s; | ||
| 389 | else { | ||
| 390 | if (!stralloc_copys(&tmp, "control/notlshosts/") | ||
| 391 | || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) | ||
| 392 | temp_nomem(); | ||
| 393 | if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || | ||
| 394 | (stat(tmp.s, &st) == 0)) { | ||
| 395 | alloc_free(tmp.s); | ||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | alloc_free(tmp.s); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | if (!smtps) { | ||
| 403 | stralloc *sa = ehlokw.sa; | ||
| 404 | unsigned int len = ehlokw.len; | ||
| 405 | /* look for STARTTLS among EHLO keywords */ | ||
| 406 | for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; | ||
| 407 | if (!len) { | ||
| 408 | if (!servercert) return 0; | ||
| 409 | out("ZNo TLS achieved while "); out(servercert); | ||
| 410 | out(" exists"); smtptext.len = 0; TLS_QUIT; | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | SSL_library_init(); | ||
| 415 | ctx = SSL_CTX_new(SSLv23_client_method()); | ||
| 416 | if (!ctx) { | ||
| 417 | if (!smtps && !servercert) return 0; | ||
| 418 | smtptext.len = 0; | ||
| 419 | tls_quit_error("ZTLS error initializing ctx"); | ||
| 420 | } | ||
| 421 | |||
| 422 | if (servercert) { | ||
| 423 | if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { | ||
| 424 | SSL_CTX_free(ctx); | ||
| 425 | smtptext.len = 0; | ||
| 426 | out("ZTLS unable to load "); tls_quit_error(servercert); | ||
| 427 | } | ||
| 428 | /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ | ||
| 429 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); | ||
| 430 | } | ||
| 431 | |||
| 432 | /* let the other side complain if it needs a cert and we don't have one */ | ||
| 433 | # define CLIENTCERT "control/clientcert.pem" | ||
| 434 | if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) | ||
| 435 | SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); | ||
| 436 | # undef CLIENTCERT | ||
| 437 | |||
| 438 | myssl = SSL_new(ctx); | ||
| 439 | SSL_CTX_free(ctx); | ||
| 440 | if (!myssl) { | ||
| 441 | if (!smtps && !servercert) return 0; | ||
| 442 | smtptext.len = 0; | ||
| 443 | tls_quit_error("ZTLS error initializing ssl"); | ||
| 444 | } | ||
| 445 | |||
| 446 | if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); | ||
| 447 | |||
| 448 | /* while the server is preparing a responce, do something else */ | ||
| 449 | if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) | ||
| 450 | { SSL_free(myssl); temp_control(); } | ||
| 451 | if (saciphers.len) { | ||
| 452 | for (i = 0; i < saciphers.len - 1; ++i) | ||
| 453 | if (!saciphers.s[i]) saciphers.s[i] = ':'; | ||
| 454 | ciphers = saciphers.s; | ||
| 455 | } | ||
| 456 | else ciphers = "DEFAULT"; | ||
| 457 | SSL_set_cipher_list(myssl, ciphers); | ||
| 458 | alloc_free(saciphers.s); | ||
| 459 | |||
| 460 | /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */ | ||
| 461 | SSL_set_fd(myssl, smtpfd); | ||
| 462 | |||
| 463 | /* read the responce to STARTTLS */ | ||
| 464 | if (!smtps) { | ||
| 465 | if (smtpcode() != 220) { | ||
| 466 | SSL_free(myssl); | ||
| 467 | if (!servercert) return 0; | ||
| 468 | out("ZSTARTTLS rejected while "); | ||
| 469 | out(servercert); out(" exists"); TLS_QUIT; | ||
| 470 | } | ||
| 471 | smtptext.len = 0; | ||
| 472 | } | ||
| 473 | |||
| 474 | ssl = myssl; | ||
| 475 | if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) | ||
| 476 | tls_quit("ZTLS connect failed", ssl_error_str()); | ||
| 477 | |||
| 478 | if (servercert) { | ||
| 479 | X509 *peercert; | ||
| 480 | STACK_OF(GENERAL_NAME) *gens; | ||
| 481 | |||
| 482 | int r = SSL_get_verify_result(ssl); | ||
| 483 | if (r != X509_V_OK) { | ||
| 484 | out("ZTLS unable to verify server with "); | ||
| 485 | tls_quit(servercert, X509_verify_cert_error_string(r)); | ||
| 486 | } | ||
| 487 | alloc_free(servercert); | ||
| 488 | |||
| 489 | peercert = SSL_get_peer_certificate(ssl); | ||
| 490 | if (!peercert) { | ||
| 491 | out("ZTLS unable to verify server "); | ||
| 492 | tls_quit(partner_fqdn, "no certificate provided"); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* RFC 2595 section 2.4: find a matching name | ||
| 496 | * first find a match among alternative names */ | ||
| 497 | gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); | ||
| 498 | if (gens) { | ||
| 499 | for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) | ||
| 500 | { | ||
| 501 | const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); | ||
| 502 | if (gn->type == GEN_DNS) | ||
| 503 | if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break; | ||
| 504 | } | ||
| 505 | sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); | ||
| 506 | } | ||
| 507 | |||
| 508 | /* no alternative name matched, look up commonName */ | ||
| 509 | if (!gens || i >= r) { | ||
| 510 | stralloc peer = {0}; | ||
| 511 | X509_NAME *subj = X509_get_subject_name(peercert); | ||
| 512 | i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); | ||
| 513 | if (i >= 0) { | ||
| 514 | const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value; | ||
| 515 | if (s) { peer.len = s->length; peer.s = s->data; } | ||
| 516 | } | ||
| 517 | if (peer.len <= 0) { | ||
| 518 | out("ZTLS unable to verify server "); | ||
| 519 | tls_quit(partner_fqdn, "certificate contains no valid commonName"); | ||
| 520 | } | ||
| 521 | if (!match_partner(peer.s, peer.len)) { | ||
| 522 | out("ZTLS unable to verify server "); out(partner_fqdn); | ||
| 523 | out(": received certificate for "); outsafe(&peer); TLS_QUIT; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | X509_free(peercert); | ||
| 528 | } | ||
| 529 | |||
| 530 | if (smtps) if (smtpcode() != 220) | ||
| 531 | quit("ZTLS Connected to "," but greeting failed"); | ||
| 532 | |||
| 533 | return 1; | ||
| 534 | } | ||
| 535 | #endif | ||
| 536 | |||
| 217 | stralloc recip = {0}; | 537 | stralloc recip = {0}; |
| 218 | 538 | ||
| 539 | void mail_without_auth() | ||
| 540 | { | ||
| 541 | substdio_puts(&smtpto,"MAIL FROM:<"); | ||
| 542 | substdio_put(&smtpto,sender.s,sender.len); | ||
| 543 | substdio_puts(&smtpto,">\r\n"); | ||
| 544 | substdio_flush(&smtpto); | ||
| 545 | } | ||
| 546 | |||
| 219 | void smtp() | 547 | void smtp() |
| 220 | { | 548 | { |
| 221 | unsigned long code; | 549 | unsigned long code; |
| 222 | int flagbother; | 550 | int flagbother; |
| 223 | int i; | 551 | int i, j; |
| 552 | |||
| 553 | #ifndef PORT_SMTP | ||
| 554 | /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ | ||
| 555 | # define port smtp_port | ||
| 556 | #endif | ||
| 557 | |||
| 558 | #ifdef TLS | ||
| 559 | # ifdef MXPS | ||
| 560 | if (type == 'S') smtps = 1; | ||
| 561 | else if (type != 's') | ||
| 562 | # endif | ||
| 563 | if (port == 465) smtps = 1; | ||
| 564 | if (!smtps) | ||
| 565 | #endif | ||
| 224 | 566 | ||
| 225 | if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); | 567 | if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); |
| 226 | 568 | ||
| 569 | #ifdef EHLO | ||
| 570 | # ifdef TLS | ||
| 571 | if (!smtps) | ||
| 572 | # endif | ||
| 573 | code = ehlo(); | ||
| 574 | |||
| 575 | # ifdef TLS | ||
| 576 | if (tls_init()) | ||
| 577 | /* RFC2487 says we should issue EHLO (even if we might not need | ||
| 578 | * extensions); at the same time, it does not prohibit a server | ||
| 579 | * to reject the EHLO and make us fallback to HELO */ | ||
| 580 | code = ehlo(); | ||
| 581 | # endif | ||
| 582 | |||
| 583 | if (code == 250) { | ||
| 584 | /* add EHLO response checks here */ | ||
| 585 | |||
| 586 | /* and if EHLO failed, use HELO */ | ||
| 587 | } else { | ||
| 588 | #endif | ||
| 589 | |||
| 227 | substdio_puts(&smtpto,"HELO "); | 590 | substdio_puts(&smtpto,"HELO "); |
| 228 | substdio_put(&smtpto,helohost.s,helohost.len); | 591 | substdio_put(&smtpto,helohost.s,helohost.len); |
| 229 | substdio_puts(&smtpto,"\r\n"); | 592 | substdio_puts(&smtpto,"\r\n"); |
| 230 | substdio_flush(&smtpto); | 593 | substdio_flush(&smtpto); |
| 231 | if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); | 594 | if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); |
| 232 | 595 | ||
| 233 | substdio_puts(&smtpto,"MAIL FROM:<"); | 596 | #ifdef EHLO |
| 234 | substdio_put(&smtpto,sender.s,sender.len); | 597 | } |
| 235 | substdio_puts(&smtpto,">\r\n"); | 598 | #endif |
| 236 | substdio_flush(&smtpto); | 599 | i = 0; |
| 600 | if (auth_smtp_user.len && auth_smtp_pass.len) { | ||
| 601 | while((i += str_chr(smtptext.s+i,'\n') + 1) && | ||
| 602 | (i+8 < smtptext.len) && | ||
| 603 | str_diffn(smtptext.s+i+4,"AUTH",4)); | ||
| 604 | if (((i+9 < smtptext.len) && | ||
| 605 | (str_diffn(smtptext.s+i+9," ",1) || | ||
| 606 | str_diffn(smtptext.s+i+9,"=",1))) && | ||
| 607 | ( i += str_chr(smtptext.s+i,'L') + 1 ) && | ||
| 608 | str_diffn(smtptext.s+i+1,"OGIN",4)) { | ||
| 609 | |||
| 610 | if (b64encode(&auth_smtp_user,&auth_b64_user)) quit("ZConnected to "," but unable to base64encode user"); | ||
| 611 | if (b64encode(&auth_smtp_pass,&auth_b64_pass)) quit("ZConnected to "," but unable to base64encode pass"); | ||
| 612 | |||
| 613 | substdio_puts(&smtpto,"AUTH LOGIN\r\n"); | ||
| 614 | substdio_flush(&smtpto); | ||
| 615 | if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)"); | ||
| 616 | substdio_put(&smtpto,auth_b64_user.s,auth_b64_user.len); | ||
| 617 | substdio_puts(&smtpto,"\r\n"); | ||
| 618 | substdio_flush(&smtpto); | ||
| 619 | if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username)"); | ||
| 620 | substdio_put(&smtpto,auth_b64_pass.s,auth_b64_pass.len); | ||
| 621 | substdio_puts(&smtpto,"\r\n"); | ||
| 622 | substdio_flush(&smtpto); | ||
| 623 | if (smtpcode() != 235) quit("ZConnected to "," but authentication was rejected (password)"); | ||
| 624 | substdio_puts(&smtpto,"MAIL FROM:<"); | ||
| 625 | substdio_put(&smtpto,sender.s,sender.len); | ||
| 626 | substdio_puts(&smtpto,"> AUTH=<"); | ||
| 627 | substdio_put(&smtpto,sender.s,sender.len); | ||
| 628 | substdio_puts(&smtpto,">\r\n"); | ||
| 629 | substdio_flush(&smtpto); | ||
| 630 | if(!stralloc_copys(&auth_status, "Delivered with authenticated connection to \n")) temp_nomem(); | ||
| 631 | if(!stralloc_0(&auth_status)) temp_nomem(); | ||
| 632 | } else { | ||
| 633 | no_supported_auth(); | ||
| 634 | mail_without_auth(); | ||
| 635 | } | ||
| 636 | } else { | ||
| 637 | auth_user_not_set(); | ||
| 638 | mail_without_auth(); | ||
| 639 | } | ||
| 237 | code = smtpcode(); | 640 | code = smtpcode(); |
| 238 | if (code >= 500) quit("DConnected to "," but sender was rejected"); | 641 | if (code >= 500) quit("DConnected to "," but sender was rejected"); |
| 239 | if (code >= 400) quit("ZConnected to "," but sender was rejected"); | 642 | if (code >= 400) quit("ZConnected to "," but sender was rejected"); |
| @@ -246,15 +649,22 @@ void smtp() | |||
| 246 | substdio_flush(&smtpto); | 649 | substdio_flush(&smtpto); |
| 247 | code = smtpcode(); | 650 | code = smtpcode(); |
| 248 | if (code >= 500) { | 651 | if (code >= 500) { |
| 249 | out("h"); outhost(); out(" does not like recipient.\n"); | 652 | out("h"); out(auth_status.s); outhost(); |
| 653 | out(" does not like recipient.\n"); | ||
| 250 | outsmtptext(); zero(); | 654 | outsmtptext(); zero(); |
| 251 | } | 655 | } |
| 252 | else if (code >= 400) { | 656 | else if (code >= 400) { |
| 253 | out("s"); outhost(); out(" does not like recipient.\n"); | 657 | out("s"); out(auth_status.s); outhost(); |
| 658 | out(" does not like recipient.\n"); | ||
| 254 | outsmtptext(); zero(); | 659 | outsmtptext(); zero(); |
| 255 | } | 660 | } |
| 256 | else { | 661 | else { |
| 257 | out("r"); zero(); | 662 | /* |
| 663 | * James Raftery <james@now.ie> | ||
| 664 | * Log _real_ envelope recipient, post canonicalisation. | ||
| 665 | */ | ||
| 666 | out("r"); out(auth_status.s); | ||
| 667 | out("<"); outsafe(&reciplist.sa[i]); out("> "); zero(); | ||
| 258 | flagbother = 1; | 668 | flagbother = 1; |
| 259 | } | 669 | } |
| 260 | } | 670 | } |
| @@ -331,7 +741,7 @@ int argc; | |||
| 331 | char **argv; | 741 | char **argv; |
| 332 | { | 742 | { |
| 333 | static ipalloc ip = {0}; | 743 | static ipalloc ip = {0}; |
| 334 | int i; | 744 | int i,j; |
| 335 | unsigned long random; | 745 | unsigned long random; |
| 336 | char **recips; | 746 | char **recips; |
| 337 | unsigned long prefme; | 747 | unsigned long prefme; |
| @@ -347,6 +757,9 @@ char **argv; | |||
| 347 | 757 | ||
| 348 | if (!stralloc_copys(&host,argv[1])) temp_nomem(); | 758 | if (!stralloc_copys(&host,argv[1])) temp_nomem(); |
| 349 | 759 | ||
| 760 | if (!stralloc_copys(&auth_smtp_user,"")) temp_nomem(); | ||
| 761 | if (!stralloc_copys(&auth_smtp_pass,"")) temp_nomem(); | ||
| 762 | |||
| 350 | relayhost = 0; | 763 | relayhost = 0; |
| 351 | for (i = 0;i <= host.len;++i) | 764 | for (i = 0;i <= host.len;++i) |
| 352 | if ((i == 0) || (i == host.len) || (host.s[i] == '.')) | 765 | if ((i == 0) || (i == host.len) || (host.s[i] == '.')) |
| @@ -355,6 +768,16 @@ char **argv; | |||
| 355 | if (relayhost && !*relayhost) relayhost = 0; | 768 | if (relayhost && !*relayhost) relayhost = 0; |
| 356 | 769 | ||
| 357 | if (relayhost) { | 770 | if (relayhost) { |
| 771 | i = str_chr(relayhost,' '); | ||
| 772 | if (relayhost[i]) { | ||
| 773 | j = str_chr(relayhost + i + 1,' '); | ||
| 774 | if (relayhost[j]) { | ||
| 775 | relayhost[i] = 0; | ||
| 776 | relayhost[i + j + 1] = 0; | ||
| 777 | if (!stralloc_copys(&auth_smtp_user,relayhost + i + 1)) temp_nomem(); | ||
| 778 | if (!stralloc_copys(&auth_smtp_pass,relayhost + i + j + 2)) temp_nomem(); | ||
| 779 | } | ||
| 780 | } | ||
| 358 | i = str_chr(relayhost,':'); | 781 | i = str_chr(relayhost,':'); |
| 359 | if (relayhost[i]) { | 782 | if (relayhost[i]) { |
| 360 | scan_ulong(relayhost + i + 1,&port); | 783 | scan_ulong(relayhost + i + 1,&port); |
| @@ -417,6 +840,9 @@ char **argv; | |||
| 417 | if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { | 840 | if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { |
| 418 | tcpto_err(&ip.ix[i].ip,0); | 841 | tcpto_err(&ip.ix[i].ip,0); |
| 419 | partner = ip.ix[i].ip; | 842 | partner = ip.ix[i].ip; |
| 843 | #ifdef TLS | ||
| 844 | partner_fqdn = ip.ix[i].fqdn; | ||
| 845 | #endif | ||
| 420 | smtp(); /* does not return */ | 846 | smtp(); /* does not return */ |
| 421 | } | 847 | } |
| 422 | tcpto_err(&ip.ix[i].ip,errno == error_timeout); | 848 | tcpto_err(&ip.ix[i].ip,errno == error_timeout); |
diff --git a/qmail-showctl.c b/qmail-showctl.c index a24aa63..bbaa2bc 100644 --- a/qmail-showctl.c +++ b/qmail-showctl.c | |||
| @@ -214,7 +214,11 @@ void main() | |||
| 214 | _exit(111); | 214 | _exit(111); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM."); | 217 | do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern."); |
| 218 | do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern."); | ||
| 219 | do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set."); | ||
| 220 | do_lst("badmailto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern."); | ||
| 221 | do_lst("badmailtonorelay","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern and RELAYCLIENT is not set."); | ||
| 218 | do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); | 222 | do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); |
| 219 | do_str("bouncehost",1,"bouncehost","Bounce host name is "); | 223 | do_str("bouncehost",1,"bouncehost","Bounce host name is "); |
| 220 | do_int("concurrencylocal","10","Local concurrency is ",""); | 224 | do_int("concurrencylocal","10","Local concurrency is ",""); |
| @@ -267,7 +271,11 @@ void main() | |||
| 267 | if (str_equal(d->d_name,"..")) continue; | 271 | if (str_equal(d->d_name,"..")) continue; |
| 268 | if (str_equal(d->d_name,"bouncefrom")) continue; | 272 | if (str_equal(d->d_name,"bouncefrom")) continue; |
| 269 | if (str_equal(d->d_name,"bouncehost")) continue; | 273 | if (str_equal(d->d_name,"bouncehost")) continue; |
| 274 | if (str_equal(d->d_name,"badhelo")) continue; | ||
| 270 | if (str_equal(d->d_name,"badmailfrom")) continue; | 275 | if (str_equal(d->d_name,"badmailfrom")) continue; |
| 276 | if (str_equal(d->d_name,"badmailfromnorelay")) continue; | ||
| 277 | if (str_equal(d->d_name,"badmailto")) continue; | ||
| 278 | if (str_equal(d->d_name,"badmailtonorelay")) continue; | ||
| 271 | if (str_equal(d->d_name,"bouncefrom")) continue; | 279 | if (str_equal(d->d_name,"bouncefrom")) continue; |
| 272 | if (str_equal(d->d_name,"bouncehost")) continue; | 280 | if (str_equal(d->d_name,"bouncehost")) continue; |
| 273 | if (str_equal(d->d_name,"concurrencylocal")) continue; | 281 | if (str_equal(d->d_name,"concurrencylocal")) continue; |
diff --git a/qmail-smtpd.8 b/qmail-smtpd.8 index c4640b8..ce0dc02 100644 --- a/qmail-smtpd.8 +++ b/qmail-smtpd.8 | |||
| @@ -14,6 +14,15 @@ must be supplied several environment variables; | |||
| 14 | see | 14 | see |
| 15 | .BR tcp-environ(5) . | 15 | .BR tcp-environ(5) . |
| 16 | 16 | ||
| 17 | If the environment variable | ||
| 18 | .B SMTPS | ||
| 19 | is non-empty, | ||
| 20 | .B qmail-smtpd | ||
| 21 | starts a TLS session (to support the deprecated SMTPS protocol, | ||
| 22 | normally on port 465). Otherwise, | ||
| 23 | .B qmail-smtpd | ||
| 24 | offers the STARTTLS extension to ESMTP. | ||
| 25 | |||
| 17 | .B qmail-smtpd | 26 | .B qmail-smtpd |
| 18 | is responsible for counting hops. | 27 | is responsible for counting hops. |
| 19 | It rejects any message with 100 or more | 28 | It rejects any message with 100 or more |
| @@ -23,7 +32,30 @@ or | |||
| 23 | header fields. | 32 | header fields. |
| 24 | 33 | ||
| 25 | .B qmail-smtpd | 34 | .B qmail-smtpd |
| 26 | supports ESMTP, including the 8BITMIME and PIPELINING options. | 35 | supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options. |
| 36 | .B qmail-smtpd | ||
| 37 | includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements. | ||
| 38 | .B qmail-smtpd | ||
| 39 | can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes | ||
| 40 | .IR checkprogram , | ||
| 41 | which reads on file descriptor 3 the username, a 0 byte, the password | ||
| 42 | or CRAM-MD5 digest/response derived from the SMTP client, | ||
| 43 | another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type), | ||
| 44 | and a final 0 byte. | ||
| 45 | .I checkprogram | ||
| 46 | invokes | ||
| 47 | .I subprogram | ||
| 48 | upon successful authentication, which should in turn return 0 to | ||
| 49 | .BR qmail-smtpd , | ||
| 50 | effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO | ||
| 51 | (any supplied value replaced with the authenticated username). | ||
| 52 | .B qmail-smtpd | ||
| 53 | will reject the authentication attempt if it receives a nonzero return | ||
| 54 | value from | ||
| 55 | .I checkprogram | ||
| 56 | or | ||
| 57 | .IR subprogram . | ||
| 58 | |||
| 27 | .SH TRANSPARENCY | 59 | .SH TRANSPARENCY |
| 28 | .B qmail-smtpd | 60 | .B qmail-smtpd |
| 29 | converts the SMTP newline convention into the UNIX newline convention | 61 | converts the SMTP newline convention into the UNIX newline convention |
| @@ -37,11 +69,26 @@ accepts messages that contain long lines or non-ASCII characters, | |||
| 37 | even though such messages violate the SMTP protocol. | 69 | even though such messages violate the SMTP protocol. |
| 38 | .SH "CONTROL FILES" | 70 | .SH "CONTROL FILES" |
| 39 | .TP 5 | 71 | .TP 5 |
| 72 | .I badhelo | ||
| 73 | Unacceptable HELO/EHLO host names. | ||
| 74 | .B qmail-smtpd | ||
| 75 | will reject every recipient address for a message if | ||
| 76 | the host name is listed in, | ||
| 77 | or matches a POSIX regular expression pattern listed in, | ||
| 78 | .IR badhelo . | ||
| 79 | If the | ||
| 80 | .B NOBADHELO | ||
| 81 | environment variable is set, then the contents of | ||
| 82 | .IR badhelo | ||
| 83 | will be ignored. | ||
| 84 | For more information, please have a look at doc/README.qregex. | ||
| 85 | .TP 5 | ||
| 40 | .I badmailfrom | 86 | .I badmailfrom |
| 41 | Unacceptable envelope sender addresses. | 87 | Unacceptable envelope sender addresses. |
| 42 | .B qmail-smtpd | 88 | .B qmail-smtpd |
| 43 | will reject every recipient address for a message | 89 | will reject every recipient address for a message |
| 44 | if the envelope sender address is listed in | 90 | if the envelope sender address is listed in, or matches a POSIX regular expression |
| 91 | pattern listed in, | ||
| 45 | .IR badmailfrom . | 92 | .IR badmailfrom . |
| 46 | A line in | 93 | A line in |
| 47 | .I badmailfrom | 94 | .I badmailfrom |
| @@ -49,6 +96,45 @@ may be of the form | |||
| 49 | .BR @\fIhost , | 96 | .BR @\fIhost , |
| 50 | meaning every address at | 97 | meaning every address at |
| 51 | .IR host . | 98 | .IR host . |
| 99 | For more information, please have a look at doc/README.qregex. | ||
| 100 | .TP 5 | ||
| 101 | .I badmailfromnorelay | ||
| 102 | Functions the same as the | ||
| 103 | .IR badmailfrom | ||
| 104 | control file but is read only if the | ||
| 105 | .B RELAYCLIENT | ||
| 106 | environment variable is not set. | ||
| 107 | For more information, please have a look at doc/README.qregex. | ||
| 108 | .TP 5 | ||
| 109 | .I badmailto | ||
| 110 | Unacceptable envelope recipient addresses. | ||
| 111 | .B qmail-smtpd | ||
| 112 | will reject every recipient address for a message if the recipient address | ||
| 113 | is listed in, | ||
| 114 | or matches a POSIX regular expression pattern listed in, | ||
| 115 | .IR badmailto . | ||
| 116 | For more information, please have a look at doc/README.qregex. | ||
| 117 | .TP 5 | ||
| 118 | .I badmailtonorelay | ||
| 119 | Functions the same as the | ||
| 120 | .IR badmailto | ||
| 121 | control file but is read only if the | ||
| 122 | .B RELAYCLIENT | ||
| 123 | environment variable is not set. | ||
| 124 | For more information, please have a look at doc/README.qregex. | ||
| 125 | |||
| 126 | .TP 5 | ||
| 127 | .I clientca.pem | ||
| 128 | A list of Certifying Authority (CA) certificates that are used to verify | ||
| 129 | the client-presented certificates during a TLS-encrypted session. | ||
| 130 | |||
| 131 | .TP 5 | ||
| 132 | .I clientcrl.pem | ||
| 133 | A list of Certificate Revocation Lists (CRLs). If present it | ||
| 134 | should contain the CRLs of the CAs in | ||
| 135 | .I clientca.pem | ||
| 136 | and client certs will be checked for revocation. | ||
| 137 | |||
| 52 | .TP 5 | 138 | .TP 5 |
| 53 | .I databytes | 139 | .I databytes |
| 54 | Maximum number of bytes allowed in a message, | 140 | Maximum number of bytes allowed in a message, |
| @@ -76,6 +162,18 @@ If the environment variable | |||
| 76 | .B DATABYTES | 162 | .B DATABYTES |
| 77 | is set, it overrides | 163 | is set, it overrides |
| 78 | .IR databytes . | 164 | .IR databytes . |
| 165 | |||
| 166 | .TP 5 | ||
| 167 | .I dh1024.pem | ||
| 168 | If these 1024 bit DH parameters are provided, | ||
| 169 | .B qmail-smtpd | ||
| 170 | will use them for TLS sessions instead of generating one on-the-fly | ||
| 171 | (which is very timeconsuming). | ||
| 172 | .TP 5 | ||
| 173 | .I dh512.pem | ||
| 174 | 512 bit counterpart for | ||
| 175 | .B dh1024.pem. | ||
| 176 | |||
| 79 | .TP 5 | 177 | .TP 5 |
| 80 | .I localiphost | 178 | .I localiphost |
| 81 | Replacement host name for local IP addresses. | 179 | Replacement host name for local IP addresses. |
| @@ -151,6 +249,19 @@ may include wildcards: | |||
| 151 | 249 | ||
| 152 | Envelope recipient addresses without @ signs are | 250 | Envelope recipient addresses without @ signs are |
| 153 | always allowed through. | 251 | always allowed through. |
| 252 | |||
| 253 | .TP 5 | ||
| 254 | .I rsa512.pem | ||
| 255 | If this 512 bit RSA key is provided, | ||
| 256 | .B qmail-smtpd | ||
| 257 | will use it for TLS sessions instead of generating one on-the-fly. | ||
| 258 | |||
| 259 | .TP 5 | ||
| 260 | .I servercert.pem | ||
| 261 | SSL certificate to be presented to clients in TLS-encrypted sessions. | ||
| 262 | Should contain both the certificate and the private key. Certifying Authority | ||
| 263 | (CA) and intermediate certificates can be added at the end of the file. | ||
| 264 | |||
| 154 | .TP 5 | 265 | .TP 5 |
| 155 | .I smtpgreeting | 266 | .I smtpgreeting |
| 156 | SMTP greeting message. | 267 | SMTP greeting message. |
| @@ -169,6 +280,24 @@ Number of seconds | |||
| 169 | .B qmail-smtpd | 280 | .B qmail-smtpd |
| 170 | will wait for each new buffer of data from the remote SMTP client. | 281 | will wait for each new buffer of data from the remote SMTP client. |
| 171 | Default: 1200. | 282 | Default: 1200. |
| 283 | |||
| 284 | .TP 5 | ||
| 285 | .I tlsclients | ||
| 286 | A list of email addresses. When relay rules would reject an incoming message, | ||
| 287 | .B qmail-smtpd | ||
| 288 | can allow it if the client presents a certificate that can be verified against | ||
| 289 | the CA list in | ||
| 290 | .I clientca.pem | ||
| 291 | and the certificate email address is in | ||
| 292 | .IR tlsclients . | ||
| 293 | |||
| 294 | .TP 5 | ||
| 295 | .I tlsserverciphers | ||
| 296 | A set of OpenSSL cipher strings. Multiple ciphers contained in a | ||
| 297 | string should be separated by a colon. If the environment variable | ||
| 298 | .B TLSCIPHERS | ||
| 299 | is set to such a string, it takes precedence. | ||
| 300 | |||
| 172 | .SH "SEE ALSO" | 301 | .SH "SEE ALSO" |
| 173 | tcp-env(1), | 302 | tcp-env(1), |
| 174 | tcp-environ(5), | 303 | tcp-environ(5), |
diff --git a/qmail-smtpd.c b/qmail-smtpd.c index 1e28c88..c77c6cc 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c | |||
| @@ -23,14 +23,44 @@ | |||
| 23 | #include "timeoutread.h" | 23 | #include "timeoutread.h" |
| 24 | #include "timeoutwrite.h" | 24 | #include "timeoutwrite.h" |
| 25 | #include "commands.h" | 25 | #include "commands.h" |
| 26 | #include "wait.h" | ||
| 27 | #include "qregex.h" | ||
| 28 | #include "strerr.h" | ||
| 29 | |||
| 30 | #define BMCHECK_BMF 0 | ||
| 31 | #define BMCHECK_BMFNR 1 | ||
| 32 | #define BMCHECK_BMT 2 | ||
| 33 | #define BMCHECK_BMTNR 3 | ||
| 34 | #define BMCHECK_BHELO 4 | ||
| 35 | |||
| 36 | #define CRAM_MD5 | ||
| 37 | #define AUTHSLEEP 5 | ||
| 26 | 38 | ||
| 27 | #define MAXHOPS 100 | 39 | #define MAXHOPS 100 |
| 28 | unsigned int databytes = 0; | 40 | unsigned int databytes = 0; |
| 29 | int timeout = 1200; | 41 | int timeout = 1200; |
| 30 | 42 | ||
| 43 | const char *protocol = "SMTP"; | ||
| 44 | |||
| 45 | #ifdef TLS | ||
| 46 | #include <sys/stat.h> | ||
| 47 | #include "tls.h" | ||
| 48 | #include "ssl_timeoutio.h" | ||
| 49 | |||
| 50 | void tls_init(); | ||
| 51 | int tls_verify(); | ||
| 52 | void tls_nogateway(); | ||
| 53 | int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ | ||
| 54 | #endif | ||
| 55 | |||
| 31 | int safewrite(fd,buf,len) int fd; char *buf; int len; | 56 | int safewrite(fd,buf,len) int fd; char *buf; int len; |
| 32 | { | 57 | { |
| 33 | int r; | 58 | int r; |
| 59 | #ifdef TLS | ||
| 60 | if (ssl && fd == ssl_wfd) | ||
| 61 | r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); | ||
| 62 | else | ||
| 63 | #endif | ||
| 34 | r = timeoutwrite(timeout,fd,buf,len); | 64 | r = timeoutwrite(timeout,fd,buf,len); |
| 35 | if (r <= 0) _exit(1); | 65 | if (r <= 0) _exit(1); |
| 36 | return r; | 66 | return r; |
| @@ -49,8 +79,20 @@ void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _ | |||
| 49 | void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } | 79 | void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } |
| 50 | void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } | 80 | void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } |
| 51 | 81 | ||
| 52 | void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } | 82 | void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } |
| 83 | void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); } | ||
| 84 | void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } | ||
| 85 | void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); } | ||
| 86 | #ifndef TLS | ||
| 53 | void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } | 87 | void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } |
| 88 | #else | ||
| 89 | void err_nogateway() | ||
| 90 | { | ||
| 91 | out("553 sorry, that domain isn't in my list of allowed rcpthosts"); | ||
| 92 | tls_nogateway(); | ||
| 93 | out(" (#5.7.1)\r\n"); | ||
| 94 | } | ||
| 95 | #endif | ||
| 54 | void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } | 96 | void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } |
| 55 | void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } | 97 | void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } |
| 56 | void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } | 98 | void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } |
| @@ -59,6 +101,16 @@ void err_noop() { out("250 ok\r\n"); } | |||
| 59 | void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } | 101 | void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } |
| 60 | void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } | 102 | void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } |
| 61 | 103 | ||
| 104 | int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 105 | int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 106 | int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 107 | int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } | ||
| 108 | void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } | ||
| 109 | void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } | ||
| 110 | int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } | ||
| 111 | int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } | ||
| 112 | int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } | ||
| 113 | void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); } | ||
| 62 | 114 | ||
| 63 | stralloc greeting = {0}; | 115 | stralloc greeting = {0}; |
| 64 | 116 | ||
| @@ -93,9 +145,24 @@ void dohelo(arg) char *arg; { | |||
| 93 | 145 | ||
| 94 | int liphostok = 0; | 146 | int liphostok = 0; |
| 95 | stralloc liphost = {0}; | 147 | stralloc liphost = {0}; |
| 148 | |||
| 96 | int bmfok = 0; | 149 | int bmfok = 0; |
| 97 | stralloc bmf = {0}; | 150 | stralloc bmf = {0}; |
| 98 | struct constmap mapbmf; | 151 | |
| 152 | int bmfnrok = 0; | ||
| 153 | stralloc bmfnr = {0}; | ||
| 154 | |||
| 155 | int bmtok = 0; | ||
| 156 | stralloc bmt = {0}; | ||
| 157 | |||
| 158 | int bmtnrok = 0; | ||
| 159 | stralloc bmtnr = {0}; | ||
| 160 | |||
| 161 | int bhelook = 0; | ||
| 162 | stralloc bhelo = {0}; | ||
| 163 | |||
| 164 | int logregex = 0; | ||
| 165 | stralloc matchedregex = {0}; | ||
| 99 | 166 | ||
| 100 | void setup() | 167 | void setup() |
| 101 | { | 168 | { |
| @@ -109,13 +176,25 @@ void setup() | |||
| 109 | if (liphostok == -1) die_control(); | 176 | if (liphostok == -1) die_control(); |
| 110 | if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); | 177 | if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); |
| 111 | if (timeout <= 0) timeout = 1; | 178 | if (timeout <= 0) timeout = 1; |
| 112 | |||
| 113 | if (rcpthosts_init() == -1) die_control(); | 179 | if (rcpthosts_init() == -1) die_control(); |
| 114 | 180 | ||
| 115 | bmfok = control_readfile(&bmf,"control/badmailfrom",0); | 181 | bmfok = control_readfile(&bmf,"control/badmailfrom",0); |
| 116 | if (bmfok == -1) die_control(); | 182 | if (bmfok == -1) die_control(); |
| 117 | if (bmfok) | 183 | |
| 118 | if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); | 184 | bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0); |
| 185 | if (bmfnrok == -1) die_control(); | ||
| 186 | |||
| 187 | bmtok = control_readfile(&bmt,"control/badmailto",0); | ||
| 188 | if (bmtok == -1) die_control(); | ||
| 189 | |||
| 190 | bmtnrok = control_readfile(&bmtnr,"control/badmailtonorelay",0); | ||
| 191 | if (bmtnrok == -1) die_control(); | ||
| 192 | |||
| 193 | bhelook = control_readfile(&bhelo, "control/badhelo",0); | ||
| 194 | if (bhelook == -1) die_control(); | ||
| 195 | if (env_get("NOBADHELO")) bhelook = 0; | ||
| 196 | |||
| 197 | if (env_get("LOGREGEX")) logregex = 1; | ||
| 119 | 198 | ||
| 120 | if (control_readint(&databytes,"control/databytes") == -1) die_control(); | 199 | if (control_readint(&databytes,"control/databytes") == -1) die_control(); |
| 121 | x = env_get("DATABYTES"); | 200 | x = env_get("DATABYTES"); |
| @@ -131,6 +210,11 @@ void setup() | |||
| 131 | if (!remotehost) remotehost = "unknown"; | 210 | if (!remotehost) remotehost = "unknown"; |
| 132 | remoteinfo = env_get("TCPREMOTEINFO"); | 211 | remoteinfo = env_get("TCPREMOTEINFO"); |
| 133 | relayclient = env_get("RELAYCLIENT"); | 212 | relayclient = env_get("RELAYCLIENT"); |
| 213 | |||
| 214 | #ifdef TLS | ||
| 215 | if (env_get("SMTPS")) { smtps = 1; tls_init(); } | ||
| 216 | else | ||
| 217 | #endif | ||
| 134 | dohelo(remotehost); | 218 | dohelo(remotehost); |
| 135 | } | 219 | } |
| 136 | 220 | ||
| @@ -197,14 +281,56 @@ char *arg; | |||
| 197 | return 1; | 281 | return 1; |
| 198 | } | 282 | } |
| 199 | 283 | ||
| 200 | int bmfcheck() | 284 | int bmcheck(which) int which; |
| 201 | { | 285 | { |
| 202 | int j; | 286 | int i = 0; |
| 203 | if (!bmfok) return 0; | 287 | int j = 0; |
| 204 | if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; | 288 | int x = 0; |
| 205 | j = byte_rchr(addr.s,addr.len,'@'); | 289 | int negate = 0; |
| 206 | if (j < addr.len) | 290 | static stralloc bmb = {0}; |
| 207 | if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; | 291 | static stralloc curregex = {0}; |
| 292 | |||
| 293 | if (which == BMCHECK_BMF) { | ||
| 294 | if (!stralloc_copy(&bmb,&bmf)) die_nomem(); | ||
| 295 | } else if (which == BMCHECK_BMFNR) { | ||
| 296 | if (!stralloc_copy(&bmb,&bmfnr)) die_nomem(); | ||
| 297 | } else if (which == BMCHECK_BMT) { | ||
| 298 | if (!stralloc_copy(&bmb,&bmt)) die_nomem(); | ||
| 299 | } else if (which == BMCHECK_BMTNR) { | ||
| 300 | if (!stralloc_copy(&bmb,&bmtnr)) die_nomem(); | ||
| 301 | } else if (which == BMCHECK_BHELO) { | ||
| 302 | if (!stralloc_copy(&bmb,&bhelo)) die_nomem(); | ||
| 303 | } else { | ||
| 304 | die_control(); | ||
| 305 | } | ||
| 306 | |||
| 307 | while (j < bmb.len) { | ||
| 308 | i = j; | ||
| 309 | while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; | ||
| 310 | if (bmb.s[j] == '!') { | ||
| 311 | negate = 1; | ||
| 312 | j++; | ||
| 313 | } | ||
| 314 | if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem(); | ||
| 315 | if (!stralloc_0(&curregex)) die_nomem(); | ||
| 316 | if (which == BMCHECK_BHELO) { | ||
| 317 | x = matchregex(helohost.s, curregex.s); | ||
| 318 | } else { | ||
| 319 | x = matchregex(addr.s, curregex.s); | ||
| 320 | } | ||
| 321 | if ((negate) && (x == 0)) { | ||
| 322 | if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); | ||
| 323 | if (!stralloc_0(&matchedregex)) die_nomem(); | ||
| 324 | return 1; | ||
| 325 | } | ||
| 326 | if (!(negate) && (x > 0)) { | ||
| 327 | if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem(); | ||
| 328 | if (!stralloc_0(&matchedregex)) die_nomem(); | ||
| 329 | return 1; | ||
| 330 | } | ||
| 331 | j = i + 1; | ||
| 332 | negate = 0; | ||
| 333 | } | ||
| 208 | return 0; | 334 | return 0; |
| 209 | } | 335 | } |
| 210 | 336 | ||
| @@ -213,24 +339,110 @@ int addrallowed() | |||
| 213 | int r; | 339 | int r; |
| 214 | r = rcpthosts(addr.s,str_len(addr.s)); | 340 | r = rcpthosts(addr.s,str_len(addr.s)); |
| 215 | if (r == -1) die_control(); | 341 | if (r == -1) die_control(); |
| 342 | #ifdef TLS | ||
| 343 | if (r == 0) if (tls_verify()) r = -2; | ||
| 344 | #endif | ||
| 216 | return r; | 345 | return r; |
| 217 | } | 346 | } |
| 218 | 347 | ||
| 219 | 348 | ||
| 220 | int seenmail = 0; | 349 | int seenmail = 0; |
| 221 | int flagbarf; /* defined if seenmail */ | 350 | int flagbarfbmf; /* defined if seenmail */ |
| 351 | int flagbarfbmt; | ||
| 352 | int flagbarfbhelo; | ||
| 353 | int flagsize; | ||
| 222 | stralloc mailfrom = {0}; | 354 | stralloc mailfrom = {0}; |
| 223 | stralloc rcptto = {0}; | 355 | stralloc rcptto = {0}; |
| 356 | stralloc fuser = {0}; | ||
| 357 | stralloc mfparms = {0}; | ||
| 358 | |||
| 359 | int mailfrom_size(arg) char *arg; | ||
| 360 | { | ||
| 361 | long r; | ||
| 362 | unsigned long sizebytes = 0; | ||
| 363 | |||
| 364 | scan_ulong(arg,&r); | ||
| 365 | sizebytes = r; | ||
| 366 | if (databytes) if (sizebytes > databytes) return 1; | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | void mailfrom_auth(arg,len) | ||
| 371 | char *arg; | ||
| 372 | int len; | ||
| 373 | { | ||
| 374 | int j; | ||
| 375 | |||
| 376 | if (!stralloc_copys(&fuser,"")) die_nomem(); | ||
| 377 | if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } | ||
| 378 | else | ||
| 379 | while (len) { | ||
| 380 | if (*arg == '+') { | ||
| 381 | if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } | ||
| 382 | if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } | ||
| 383 | } | ||
| 384 | else | ||
| 385 | if (!stralloc_catb(&fuser,arg,1)) die_nomem(); | ||
| 386 | arg++; len--; | ||
| 387 | } | ||
| 388 | if(!stralloc_0(&fuser)) die_nomem(); | ||
| 389 | if (!remoteinfo) { | ||
| 390 | remoteinfo = fuser.s; | ||
| 391 | if (!env_unset("TCPREMOTEINFO")) die_read(); | ||
| 392 | if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | void mailfrom_parms(arg) char *arg; | ||
| 397 | { | ||
| 398 | int i; | ||
| 399 | int len; | ||
| 400 | |||
| 401 | len = str_len(arg); | ||
| 402 | if (!stralloc_copys(&mfparms,"")) die_nomem; | ||
| 403 | i = byte_chr(arg,len,'>'); | ||
| 404 | if (i > 4 && i < len) { | ||
| 405 | while (len) { | ||
| 406 | arg++; len--; | ||
| 407 | if (*arg == ' ' || *arg == '\0' ) { | ||
| 408 | if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } | ||
| 409 | if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); | ||
| 410 | if (!stralloc_copys(&mfparms,"")) die_nomem; | ||
| 411 | } | ||
| 412 | else | ||
| 413 | if (!stralloc_catb(&mfparms,arg,1)) die_nomem; | ||
| 414 | } | ||
| 415 | } | ||
| 416 | } | ||
| 224 | 417 | ||
| 225 | void smtp_helo(arg) char *arg; | 418 | void smtp_helo(arg) char *arg; |
| 226 | { | 419 | { |
| 227 | smtp_greet("250 "); out("\r\n"); | 420 | smtp_greet("250 "); out("\r\n"); |
| 228 | seenmail = 0; dohelo(arg); | 421 | seenmail = 0; dohelo(arg); |
| 422 | if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); | ||
| 229 | } | 423 | } |
| 424 | /* ESMTP extensions are published here */ | ||
| 230 | void smtp_ehlo(arg) char *arg; | 425 | void smtp_ehlo(arg) char *arg; |
| 231 | { | 426 | { |
| 232 | smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); | 427 | #ifdef TLS |
| 428 | struct stat st; | ||
| 429 | #endif | ||
| 430 | char size[FMT_ULONG]; | ||
| 431 | size[fmt_ulong(size,(unsigned int) databytes)] = 0; | ||
| 432 | smtp_greet("250-"); | ||
| 433 | #ifdef TLS | ||
| 434 | if (!ssl && (stat("control/servercert.pem",&st) == 0)) | ||
| 435 | out("\r\n250-STARTTLS"); | ||
| 436 | #endif | ||
| 437 | out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); | ||
| 438 | out("250-SIZE "); out(size); out("\r\n"); | ||
| 439 | #ifdef CRAM_MD5 | ||
| 440 | out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n"); | ||
| 441 | #else | ||
| 442 | out("250 AUTH LOGIN PLAIN\r\n"); | ||
| 443 | #endif | ||
| 233 | seenmail = 0; dohelo(arg); | 444 | seenmail = 0; dohelo(arg); |
| 445 | if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); | ||
| 234 | } | 446 | } |
| 235 | void smtp_rset() | 447 | void smtp_rset() |
| 236 | { | 448 | { |
| @@ -240,7 +452,14 @@ void smtp_rset() | |||
| 240 | void smtp_mail(arg) char *arg; | 452 | void smtp_mail(arg) char *arg; |
| 241 | { | 453 | { |
| 242 | if (!addrparse(arg)) { err_syntax(); return; } | 454 | if (!addrparse(arg)) { err_syntax(); return; } |
| 243 | flagbarf = bmfcheck(); | 455 | flagsize = 0; |
| 456 | mailfrom_parms(arg); | ||
| 457 | if (flagsize) { err_size(); return; } | ||
| 458 | flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ | ||
| 459 | if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); | ||
| 460 | if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { | ||
| 461 | flagbarfbmf = bmcheck(BMCHECK_BMFNR); | ||
| 462 | } | ||
| 244 | seenmail = 1; | 463 | seenmail = 1; |
| 245 | if (!stralloc_copys(&rcptto,"")) die_nomem(); | 464 | if (!stralloc_copys(&rcptto,"")) die_nomem(); |
| 246 | if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); | 465 | if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); |
| @@ -250,7 +469,37 @@ void smtp_mail(arg) char *arg; | |||
| 250 | void smtp_rcpt(arg) char *arg; { | 469 | void smtp_rcpt(arg) char *arg; { |
| 251 | if (!seenmail) { err_wantmail(); return; } | 470 | if (!seenmail) { err_wantmail(); return; } |
| 252 | if (!addrparse(arg)) { err_syntax(); return; } | 471 | if (!addrparse(arg)) { err_syntax(); return; } |
| 253 | if (flagbarf) { err_bmf(); return; } | 472 | if (flagbarfbhelo) { |
| 473 | if (logregex) { | ||
| 474 | strerr_warn6("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); | ||
| 475 | } else { | ||
| 476 | strerr_warn4("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip,0); | ||
| 477 | } | ||
| 478 | err_bhelo(); | ||
| 479 | return; | ||
| 480 | } | ||
| 481 | if (flagbarfbmf) { | ||
| 482 | if (logregex) { | ||
| 483 | strerr_warn6("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); | ||
| 484 | } else { | ||
| 485 | strerr_warn4("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip,0); | ||
| 486 | } | ||
| 487 | err_bmf(); | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT); | ||
| 491 | if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) { | ||
| 492 | flagbarfbmt = bmcheck(BMCHECK_BMTNR); | ||
| 493 | } | ||
| 494 | if (flagbarfbmt) { | ||
| 495 | if (logregex) { | ||
| 496 | strerr_warn6("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); | ||
| 497 | } else { | ||
| 498 | strerr_warn4("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip,0); | ||
| 499 | } | ||
| 500 | err_bmt(); | ||
| 501 | return; | ||
| 502 | } | ||
| 254 | if (relayclient) { | 503 | if (relayclient) { |
| 255 | --addr.len; | 504 | --addr.len; |
| 256 | if (!stralloc_cats(&addr,relayclient)) die_nomem(); | 505 | if (!stralloc_cats(&addr,relayclient)) die_nomem(); |
| @@ -269,6 +518,11 @@ int saferead(fd,buf,len) int fd; char *buf; int len; | |||
| 269 | { | 518 | { |
| 270 | int r; | 519 | int r; |
| 271 | flush(); | 520 | flush(); |
| 521 | #ifdef TLS | ||
| 522 | if (ssl && fd == ssl_rfd) | ||
| 523 | r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); | ||
| 524 | else | ||
| 525 | #endif | ||
| 272 | r = timeoutread(timeout,fd,buf,len); | 526 | r = timeoutread(timeout,fd,buf,len); |
| 273 | if (r == -1) if (errno == error_timeout) die_alarm(); | 527 | if (r == -1) if (errno == error_timeout) die_alarm(); |
| 274 | if (r <= 0) die_read(); | 528 | if (r <= 0) die_read(); |
| @@ -378,7 +632,7 @@ void smtp_data() { | |||
| 378 | qp = qmail_qp(&qqt); | 632 | qp = qmail_qp(&qqt); |
| 379 | out("354 go ahead\r\n"); | 633 | out("354 go ahead\r\n"); |
| 380 | 634 | ||
| 381 | received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); | 635 | received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); |
| 382 | blast(&hops); | 636 | blast(&hops); |
| 383 | hops = (hops >= MAXHOPS); | 637 | hops = (hops >= MAXHOPS); |
| 384 | if (hops) qmail_fail(&qqt); | 638 | if (hops) qmail_fail(&qqt); |
| @@ -388,28 +642,494 @@ void smtp_data() { | |||
| 388 | qqx = qmail_close(&qqt); | 642 | qqx = qmail_close(&qqt); |
| 389 | if (!*qqx) { acceptmessage(qp); return; } | 643 | if (!*qqx) { acceptmessage(qp); return; } |
| 390 | if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } | 644 | if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } |
| 391 | if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } | 645 | if (databytes) if (!bytestooverflow) { err_size(); return; } |
| 392 | if (*qqx == 'D') out("554 "); else out("451 "); | 646 | if (*qqx == 'D') out("554 "); else out("451 "); |
| 393 | out(qqx + 1); | 647 | out(qqx + 1); |
| 394 | out("\r\n"); | 648 | out("\r\n"); |
| 395 | } | 649 | } |
| 396 | 650 | ||
| 651 | /* this file is too long ----------------------------------------- SMTP AUTH */ | ||
| 652 | |||
| 653 | char unique[FMT_ULONG + FMT_ULONG + 3]; | ||
| 654 | static stralloc authin = {0}; /* input from SMTP client */ | ||
| 655 | static stralloc user = {0}; /* authorization user-id */ | ||
| 656 | static stralloc pass = {0}; /* plain passwd or digest */ | ||
| 657 | static stralloc resp = {0}; /* b64 response */ | ||
| 658 | #ifdef CRAM_MD5 | ||
| 659 | static stralloc chal = {0}; /* plain challenge */ | ||
| 660 | static stralloc slop = {0}; /* b64 challenge */ | ||
| 661 | #endif | ||
| 662 | |||
| 663 | int flagauth = 0; | ||
| 664 | char **childargs; | ||
| 665 | char ssauthbuf[512]; | ||
| 666 | substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); | ||
| 667 | |||
| 668 | int authgetl(void) { | ||
| 669 | int i; | ||
| 670 | |||
| 671 | if (!stralloc_copys(&authin,"")) die_nomem(); | ||
| 672 | for (;;) { | ||
| 673 | if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ | ||
| 674 | i = substdio_get(&ssin,authin.s + authin.len,1); | ||
| 675 | if (i != 1) die_read(); | ||
| 676 | if (authin.s[authin.len] == '\n') break; | ||
| 677 | ++authin.len; | ||
| 678 | } | ||
| 679 | |||
| 680 | if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; | ||
| 681 | authin.s[authin.len] = 0; | ||
| 682 | if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } | ||
| 683 | if (authin.len == 0) { return err_input(); } | ||
| 684 | return authin.len; | ||
| 685 | } | ||
| 686 | |||
| 687 | int authenticate(void) | ||
| 688 | { | ||
| 689 | int child; | ||
| 690 | int wstat; | ||
| 691 | int pi[2]; | ||
| 692 | |||
| 693 | if (!stralloc_0(&user)) die_nomem(); | ||
| 694 | if (!stralloc_0(&pass)) die_nomem(); | ||
| 695 | #ifdef CRAM_MD5 | ||
| 696 | if (!stralloc_0(&chal)) die_nomem(); | ||
| 697 | #endif | ||
| 698 | |||
| 699 | if (pipe(pi) == -1) return err_pipe(); | ||
| 700 | switch(child = fork()) { | ||
| 701 | case -1: | ||
| 702 | return err_fork(); | ||
| 703 | case 0: | ||
| 704 | close(pi[1]); | ||
| 705 | if(fd_copy(3,pi[0]) == -1) return err_pipe(); | ||
| 706 | sig_pipedefault(); | ||
| 707 | execvp(*childargs, childargs); | ||
| 708 | _exit(1); | ||
| 709 | } | ||
| 710 | close(pi[0]); | ||
| 711 | |||
| 712 | substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); | ||
| 713 | if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); | ||
| 714 | if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); | ||
| 715 | #ifdef CRAM_MD5 | ||
| 716 | if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); | ||
| 717 | #endif | ||
| 718 | if (substdio_flush(&ssauth) == -1) return err_write(); | ||
| 719 | |||
| 720 | close(pi[1]); | ||
| 721 | #ifdef CRAM_MD5 | ||
| 722 | if (!stralloc_copys(&chal,"")) die_nomem(); | ||
| 723 | if (!stralloc_copys(&slop,"")) die_nomem(); | ||
| 724 | #endif | ||
| 725 | byte_zero(ssauthbuf,sizeof ssauthbuf); | ||
| 726 | if (wait_pid(&wstat,child) == -1) return err_child(); | ||
| 727 | if (wait_crashed(wstat)) return err_child(); | ||
| 728 | if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ | ||
| 729 | return 0; /* yes */ | ||
| 730 | } | ||
| 731 | |||
| 732 | int auth_login(arg) char *arg; | ||
| 733 | { | ||
| 734 | int r; | ||
| 735 | |||
| 736 | if (*arg) { | ||
| 737 | if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); | ||
| 738 | } | ||
| 739 | else { | ||
| 740 | out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ | ||
| 741 | if (authgetl() < 0) return -1; | ||
| 742 | if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); | ||
| 743 | } | ||
| 744 | if (r == -1) die_nomem(); | ||
| 745 | |||
| 746 | out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ | ||
| 747 | |||
| 748 | if (authgetl() < 0) return -1; | ||
| 749 | if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); | ||
| 750 | if (r == -1) die_nomem(); | ||
| 751 | |||
| 752 | if (!user.len || !pass.len) return err_input(); | ||
| 753 | return authenticate(); | ||
| 754 | } | ||
| 755 | |||
| 756 | int auth_plain(arg) char *arg; | ||
| 757 | { | ||
| 758 | int r, id = 0; | ||
| 759 | |||
| 760 | if (*arg) { | ||
| 761 | if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); | ||
| 762 | } | ||
| 763 | else { | ||
| 764 | out("334 \r\n"); flush(); | ||
| 765 | if (authgetl() < 0) return -1; | ||
| 766 | if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); | ||
| 767 | } | ||
| 768 | if (r == -1 || !stralloc_0(&resp)) die_nomem(); | ||
| 769 | while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ | ||
| 770 | |||
| 771 | if (resp.len > id + 1) | ||
| 772 | if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); | ||
| 773 | if (resp.len > id + user.len + 2) | ||
| 774 | if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); | ||
| 775 | |||
| 776 | if (!user.len || !pass.len) return err_input(); | ||
| 777 | return authenticate(); | ||
| 778 | } | ||
| 779 | |||
| 780 | #ifdef CRAM_MD5 | ||
| 781 | int auth_cram() | ||
| 782 | { | ||
| 783 | int i, r; | ||
| 784 | char *s; | ||
| 785 | |||
| 786 | s = unique; /* generate challenge */ | ||
| 787 | s += fmt_uint(s,getpid()); | ||
| 788 | *s++ = '.'; | ||
| 789 | s += fmt_ulong(s,(unsigned long) now()); | ||
| 790 | *s++ = '@'; | ||
| 791 | *s++ = 0; | ||
| 792 | if (!stralloc_copys(&chal,"<")) die_nomem(); | ||
| 793 | if (!stralloc_cats(&chal,unique)) die_nomem(); | ||
| 794 | if (!stralloc_cats(&chal,local)) die_nomem(); | ||
| 795 | if (!stralloc_cats(&chal,">")) die_nomem(); | ||
| 796 | if (b64encode(&chal,&slop) < 0) die_nomem(); | ||
| 797 | if (!stralloc_0(&slop)) die_nomem(); | ||
| 798 | |||
| 799 | out("334 "); /* "334 base64_challenge \r\n" */ | ||
| 800 | out(slop.s); | ||
| 801 | out("\r\n"); | ||
| 802 | flush(); | ||
| 803 | |||
| 804 | if (authgetl() < 0) return -1; /* got response */ | ||
| 805 | if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); | ||
| 806 | if (r == -1 || !stralloc_0(&resp)) die_nomem(); | ||
| 807 | |||
| 808 | i = str_chr(resp.s,' '); | ||
| 809 | s = resp.s + i; | ||
| 810 | while (*s == ' ') ++s; | ||
| 811 | resp.s[i] = 0; | ||
| 812 | if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ | ||
| 813 | if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ | ||
| 814 | |||
| 815 | if (!user.len || !pass.len) return err_input(); | ||
| 816 | return authenticate(); | ||
| 817 | } | ||
| 818 | #endif | ||
| 819 | |||
| 820 | struct authcmd { | ||
| 821 | char *text; | ||
| 822 | int (*fun)(); | ||
| 823 | } authcmds[] = { | ||
| 824 | { "login",auth_login } | ||
| 825 | , { "plain",auth_plain } | ||
| 826 | #ifdef CRAM_MD5 | ||
| 827 | , { "cram-md5",auth_cram } | ||
| 828 | #endif | ||
| 829 | , { 0,err_noauth } | ||
| 830 | }; | ||
| 831 | |||
| 832 | void smtp_auth(arg) | ||
| 833 | char *arg; | ||
| 834 | { | ||
| 835 | int i; | ||
| 836 | char *cmd = arg; | ||
| 837 | |||
| 838 | if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } | ||
| 839 | if (flagauth) { err_authd(); return; } | ||
| 840 | if (seenmail) { err_authmail(); return; } | ||
| 841 | |||
| 842 | if (!stralloc_copys(&user,"")) die_nomem(); | ||
| 843 | if (!stralloc_copys(&pass,"")) die_nomem(); | ||
| 844 | if (!stralloc_copys(&resp,"")) die_nomem(); | ||
| 845 | #ifdef CRAM_MD5 | ||
| 846 | if (!stralloc_copys(&chal,"")) die_nomem(); | ||
| 847 | #endif | ||
| 848 | |||
| 849 | i = str_chr(cmd,' '); | ||
| 850 | arg = cmd + i; | ||
| 851 | while (*arg == ' ') ++arg; | ||
| 852 | cmd[i] = 0; | ||
| 853 | |||
| 854 | for (i = 0;authcmds[i].text;++i) | ||
| 855 | if (case_equals(authcmds[i].text,cmd)) break; | ||
| 856 | |||
| 857 | switch (authcmds[i].fun(arg)) { | ||
| 858 | case 0: | ||
| 859 | flagauth = 1; | ||
| 860 | protocol = "ESMTPA"; | ||
| 861 | relayclient = ""; | ||
| 862 | remoteinfo = user.s; | ||
| 863 | if (!env_unset("TCPREMOTEINFO")) die_read(); | ||
| 864 | if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); | ||
| 865 | if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); | ||
| 866 | out("235 ok, go ahead (#2.0.0)\r\n"); | ||
| 867 | break; | ||
| 868 | case 1: | ||
| 869 | err_authfail(user.s,authcmds[i].text); | ||
| 870 | } | ||
| 871 | } | ||
| 872 | |||
| 873 | |||
| 874 | /* this file is too long --------------------------------------------- GO ON */ | ||
| 875 | |||
| 876 | #ifdef TLS | ||
| 877 | stralloc proto = {0}; | ||
| 878 | int ssl_verified = 0; | ||
| 879 | const char *ssl_verify_err = 0; | ||
| 880 | |||
| 881 | void smtp_tls(char *arg) | ||
| 882 | { | ||
| 883 | if (ssl) err_unimpl(); | ||
| 884 | else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); | ||
| 885 | else tls_init(); | ||
| 886 | } | ||
| 887 | |||
| 888 | RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) | ||
| 889 | { | ||
| 890 | if (!export) keylen = 512; | ||
| 891 | if (keylen == 512) { | ||
| 892 | FILE *in = fopen("control/rsa512.pem", "r"); | ||
| 893 | if (in) { | ||
| 894 | RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); | ||
| 895 | fclose(in); | ||
| 896 | if (rsa) return rsa; | ||
| 897 | } | ||
| 898 | } | ||
| 899 | return RSA_generate_key(keylen, RSA_F4, NULL, NULL); | ||
| 900 | } | ||
| 901 | |||
| 902 | DH *tmp_dh_cb(SSL *ssl, int export, int keylen) | ||
| 903 | { | ||
| 904 | if (!export) keylen = 1024; | ||
| 905 | if (keylen == 512) { | ||
| 906 | FILE *in = fopen("control/dh512.pem", "r"); | ||
| 907 | if (in) { | ||
| 908 | DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); | ||
| 909 | fclose(in); | ||
| 910 | if (dh) return dh; | ||
| 911 | } | ||
| 912 | } | ||
| 913 | if (keylen == 1024) { | ||
| 914 | FILE *in = fopen("control/dh1024.pem", "r"); | ||
| 915 | if (in) { | ||
| 916 | DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); | ||
| 917 | fclose(in); | ||
| 918 | if (dh) return dh; | ||
| 919 | } | ||
| 920 | } | ||
| 921 | return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); | ||
| 922 | } | ||
| 923 | |||
| 924 | /* don't want to fail handshake if cert isn't verifiable */ | ||
| 925 | int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } | ||
| 926 | |||
| 927 | void tls_nogateway() | ||
| 928 | { | ||
| 929 | /* there may be cases when relayclient is set */ | ||
| 930 | if (!ssl || relayclient) return; | ||
| 931 | out("; no valid cert for gatewaying"); | ||
| 932 | if (ssl_verify_err) { out(": "); out(ssl_verify_err); } | ||
| 933 | } | ||
| 934 | void tls_out(const char *s1, const char *s2) | ||
| 935 | { | ||
| 936 | out("454 TLS "); out(s1); | ||
| 937 | if (s2) { out(": "); out(s2); } | ||
| 938 | out(" (#4.3.0)\r\n"); flush(); | ||
| 939 | } | ||
| 940 | void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } | ||
| 941 | |||
| 942 | # define CLIENTCA "control/clientca.pem" | ||
| 943 | # define CLIENTCRL "control/clientcrl.pem" | ||
| 944 | # define SERVERCERT "control/servercert.pem" | ||
| 945 | |||
| 946 | int tls_verify() | ||
| 947 | { | ||
| 948 | stralloc clients = {0}; | ||
| 949 | struct constmap mapclients; | ||
| 950 | |||
| 951 | if (!ssl || relayclient || ssl_verified) return 0; | ||
| 952 | ssl_verified = 1; /* don't do this twice */ | ||
| 953 | |||
| 954 | /* request client cert to see if it can be verified by one of our CAs | ||
| 955 | * and the associated email address matches an entry in tlsclients */ | ||
| 956 | switch (control_readfile(&clients, "control/tlsclients", 0)) | ||
| 957 | { | ||
| 958 | case 1: | ||
| 959 | if (constmap_init(&mapclients, clients.s, clients.len, 0)) { | ||
| 960 | /* if CLIENTCA contains all the standard root certificates, a | ||
| 961 | * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; | ||
| 962 | * it is probably due to 0.9.6b supporting only 8k key exchange | ||
| 963 | * data while the 0.9.6c release increases that limit to 100k */ | ||
| 964 | STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); | ||
| 965 | if (sk) { | ||
| 966 | SSL_set_client_CA_list(ssl, sk); | ||
| 967 | SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); | ||
| 968 | break; | ||
| 969 | } | ||
| 970 | constmap_free(&mapclients); | ||
| 971 | } | ||
| 972 | case 0: alloc_free(clients.s); return 0; | ||
| 973 | case -1: die_control(); | ||
| 974 | } | ||
| 975 | |||
| 976 | if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { | ||
| 977 | const char *err = ssl_error_str(); | ||
| 978 | tls_out("rehandshake failed", err); die_read(); | ||
| 979 | } | ||
| 980 | |||
| 981 | do { /* one iteration */ | ||
| 982 | X509 *peercert; | ||
| 983 | X509_NAME *subj; | ||
| 984 | stralloc email = {0}; | ||
| 985 | |||
| 986 | int n = SSL_get_verify_result(ssl); | ||
| 987 | if (n != X509_V_OK) | ||
| 988 | { ssl_verify_err = X509_verify_cert_error_string(n); break; } | ||
| 989 | peercert = SSL_get_peer_certificate(ssl); | ||
| 990 | if (!peercert) break; | ||
| 991 | |||
| 992 | subj = X509_get_subject_name(peercert); | ||
| 993 | n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); | ||
| 994 | if (n >= 0) { | ||
| 995 | const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value; | ||
| 996 | if (s) { email.len = s->length; email.s = s->data; } | ||
| 997 | } | ||
| 998 | |||
| 999 | if (email.len <= 0) | ||
| 1000 | ssl_verify_err = "contains no email address"; | ||
| 1001 | else if (!constmap(&mapclients, email.s, email.len)) | ||
| 1002 | ssl_verify_err = "email address not in my list of tlsclients"; | ||
| 1003 | else { | ||
| 1004 | /* add the cert email to the proto if it helped allow relaying */ | ||
| 1005 | --proto.len; | ||
| 1006 | if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ | ||
| 1007 | || !stralloc_catb(&proto, email.s, email.len) | ||
| 1008 | || !stralloc_cats(&proto, ")") | ||
| 1009 | || !stralloc_0(&proto)) die_nomem(); | ||
| 1010 | relayclient = ""; | ||
| 1011 | protocol = proto.s; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | X509_free(peercert); | ||
| 1015 | } while (0); | ||
| 1016 | constmap_free(&mapclients); alloc_free(clients.s); | ||
| 1017 | |||
| 1018 | /* we are not going to need this anymore: free the memory */ | ||
| 1019 | SSL_set_client_CA_list(ssl, NULL); | ||
| 1020 | SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); | ||
| 1021 | |||
| 1022 | return relayclient ? 1 : 0; | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | void tls_init() | ||
| 1026 | { | ||
| 1027 | SSL *myssl; | ||
| 1028 | SSL_CTX *ctx; | ||
| 1029 | const char *ciphers; | ||
| 1030 | stralloc saciphers = {0}; | ||
| 1031 | X509_STORE *store; | ||
| 1032 | X509_LOOKUP *lookup; | ||
| 1033 | |||
| 1034 | SSL_library_init(); | ||
| 1035 | |||
| 1036 | /* a new SSL context with the bare minimum of options */ | ||
| 1037 | ctx = SSL_CTX_new(SSLv23_server_method()); | ||
| 1038 | if (!ctx) { tls_err("unable to initialize ctx"); return; } | ||
| 1039 | |||
| 1040 | if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) | ||
| 1041 | { SSL_CTX_free(ctx); tls_err("missing certificate"); return; } | ||
| 1042 | SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); | ||
| 1043 | |||
| 1044 | #if OPENSSL_VERSION_NUMBER >= 0x00907000L | ||
| 1045 | /* crl checking */ | ||
| 1046 | store = SSL_CTX_get_cert_store(ctx); | ||
| 1047 | if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && | ||
| 1048 | (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) | ||
| 1049 | X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | | ||
| 1050 | X509_V_FLAG_CRL_CHECK_ALL); | ||
| 1051 | #endif | ||
| 1052 | |||
| 1053 | /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ | ||
| 1054 | SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); | ||
| 1055 | |||
| 1056 | /* a new SSL object, with the rest added to it directly to avoid copying */ | ||
| 1057 | myssl = SSL_new(ctx); | ||
| 1058 | SSL_CTX_free(ctx); | ||
| 1059 | if (!myssl) { tls_err("unable to initialize ssl"); return; } | ||
| 1060 | |||
| 1061 | /* this will also check whether public and private keys match */ | ||
| 1062 | if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) | ||
| 1063 | { SSL_free(myssl); tls_err("no valid RSA private key"); return; } | ||
| 1064 | |||
| 1065 | ciphers = env_get("TLSCIPHERS"); | ||
| 1066 | if (!ciphers) { | ||
| 1067 | if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1) | ||
| 1068 | { SSL_free(myssl); die_control(); } | ||
| 1069 | if (saciphers.len) { /* convert all '\0's except the last one to ':' */ | ||
| 1070 | int i; | ||
| 1071 | for (i = 0; i < saciphers.len - 1; ++i) | ||
| 1072 | if (!saciphers.s[i]) saciphers.s[i] = ':'; | ||
| 1073 | ciphers = saciphers.s; | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | if (!ciphers || !*ciphers) ciphers = "DEFAULT"; | ||
| 1077 | SSL_set_cipher_list(myssl, ciphers); | ||
| 1078 | alloc_free(saciphers.s); | ||
| 1079 | |||
| 1080 | SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); | ||
| 1081 | SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); | ||
| 1082 | SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); | ||
| 1083 | SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); | ||
| 1084 | |||
| 1085 | if (!smtps) { out("220 ready for tls\r\n"); flush(); } | ||
| 1086 | |||
| 1087 | if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { | ||
| 1088 | /* neither cleartext nor any other response here is part of a standard */ | ||
| 1089 | const char *err = ssl_error_str(); | ||
| 1090 | ssl_free(myssl); tls_out("connection failed", err); die_read(); | ||
| 1091 | } | ||
| 1092 | ssl = myssl; | ||
| 1093 | |||
| 1094 | /* populate the protocol string, used in Received */ | ||
| 1095 | if (!stralloc_copys(&proto, "ESMTPS (") | ||
| 1096 | || !stralloc_cats(&proto, SSL_get_cipher(ssl)) | ||
| 1097 | || !stralloc_cats(&proto, " encrypted)")) die_nomem(); | ||
| 1098 | if (!stralloc_0(&proto)) die_nomem(); | ||
| 1099 | protocol = proto.s; | ||
| 1100 | |||
| 1101 | /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ | ||
| 1102 | dohelo(remotehost); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | # undef SERVERCERT | ||
| 1106 | # undef CLIENTCA | ||
| 1107 | |||
| 1108 | #endif | ||
| 1109 | |||
| 397 | struct commands smtpcommands[] = { | 1110 | struct commands smtpcommands[] = { |
| 398 | { "rcpt", smtp_rcpt, 0 } | 1111 | { "rcpt", smtp_rcpt, 0 } |
| 399 | , { "mail", smtp_mail, 0 } | 1112 | , { "mail", smtp_mail, 0 } |
| 400 | , { "data", smtp_data, flush } | 1113 | , { "data", smtp_data, flush } |
| 1114 | , { "auth", smtp_auth, flush } | ||
| 401 | , { "quit", smtp_quit, flush } | 1115 | , { "quit", smtp_quit, flush } |
| 402 | , { "helo", smtp_helo, flush } | 1116 | , { "helo", smtp_helo, flush } |
| 403 | , { "ehlo", smtp_ehlo, flush } | 1117 | , { "ehlo", smtp_ehlo, flush } |
| 404 | , { "rset", smtp_rset, 0 } | 1118 | , { "rset", smtp_rset, 0 } |
| 405 | , { "help", smtp_help, flush } | 1119 | , { "help", smtp_help, flush } |
| 1120 | #ifdef TLS | ||
| 1121 | , { "starttls", smtp_tls, flush } | ||
| 1122 | #endif | ||
| 406 | , { "noop", err_noop, flush } | 1123 | , { "noop", err_noop, flush } |
| 407 | , { "vrfy", err_vrfy, flush } | 1124 | , { "vrfy", err_vrfy, flush } |
| 408 | , { 0, err_unimpl, flush } | 1125 | , { 0, err_unimpl, flush } |
| 409 | } ; | 1126 | } ; |
| 410 | 1127 | ||
| 411 | void main() | 1128 | void main(argc,argv) |
| 1129 | int argc; | ||
| 1130 | char **argv; | ||
| 412 | { | 1131 | { |
| 1132 | childargs = argv + 1; | ||
| 413 | sig_pipeignore(); | 1133 | sig_pipeignore(); |
| 414 | if (chdir(auto_qmail) == -1) die_control(); | 1134 | if (chdir(auto_qmail) == -1) die_control(); |
| 415 | setup(); | 1135 | setup(); |
diff --git a/qregex.c b/qregex.c new file mode 100644 index 0000000..59c0f76 --- /dev/null +++ b/qregex.c | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * qregex (v2) | ||
| 3 | * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $ | ||
| 4 | * | ||
| 5 | * Author : Evan Borgstrom (evan at unixpimps dot org) | ||
| 6 | * Created : 2001/12/14 23:08:16 | ||
| 7 | * Modified: $Date: 2001/12/28 07:05:21 $ | ||
| 8 | * Revision: $Revision: 2.1 $ | ||
| 9 | * | ||
| 10 | * Do POSIX regex matching on addresses for anti-relay / spam control. | ||
| 11 | * It logs to the maillog | ||
| 12 | * See the qregex-readme file included with this tarball. | ||
| 13 | * If you didn't get this file in a tarball please see the following URL: | ||
| 14 | * http://www.unixpimps.org/software/qregex | ||
| 15 | * | ||
| 16 | * qregex.c is released under a BSD style copyright. | ||
| 17 | * See http://www.unixpimps.org/software/qregex/copyright.html | ||
| 18 | * | ||
| 19 | * Note: this revision follows the coding guidelines set forth by the rest of | ||
| 20 | * the qmail code and that described at the following URL. | ||
| 21 | * http://cr.yp.to/qmail/guarantee.html | ||
| 22 | * | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <sys/types.h> | ||
| 26 | #include <regex.h> | ||
| 27 | #include "qregex.h" | ||
| 28 | |||
| 29 | #define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE) | ||
| 30 | #define REGEXEC(X,Y) regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0) | ||
| 31 | |||
| 32 | int matchregex(char *text, char *regex) { | ||
| 33 | regex_t qreg; | ||
| 34 | int retval = 0; | ||
| 35 | |||
| 36 | |||
| 37 | /* build the regex */ | ||
| 38 | if ((retval = REGCOMP(qreg, regex)) != 0) { | ||
| 39 | regfree(&qreg); | ||
| 40 | return(-retval); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* execute the regex */ | ||
| 44 | if ((retval = REGEXEC(qreg, text)) != 0) { | ||
| 45 | /* did we just not match anything? */ | ||
| 46 | if (retval == REG_NOMATCH) { | ||
| 47 | regfree(&qreg); | ||
| 48 | return(0); | ||
| 49 | } | ||
| 50 | regfree(&qreg); | ||
| 51 | return(-retval); | ||
| 52 | } | ||
| 53 | |||
| 54 | /* signal the match */ | ||
| 55 | regfree(&qreg); | ||
| 56 | return(1); | ||
| 57 | } | ||
diff --git a/qregex.h b/qregex.h new file mode 100644 index 0000000..e333a2d --- /dev/null +++ b/qregex.h | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* simple header file for the matchregex prototype */ | ||
| 2 | #ifndef _QREGEX_H_ | ||
| 3 | #define _QREGEX_H_ | ||
| 4 | int matchregex(char *text, char *regex); | ||
| 5 | #endif | ||
diff --git a/ssl_timeoutio.c b/ssl_timeoutio.c new file mode 100644 index 0000000..5b2dc9d --- /dev/null +++ b/ssl_timeoutio.c | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | #include "select.h" | ||
| 2 | #include "error.h" | ||
| 3 | #include "ndelay.h" | ||
| 4 | #include "now.h" | ||
| 5 | #include "ssl_timeoutio.h" | ||
| 6 | |||
| 7 | int ssl_timeoutio(int (*fun)(), | ||
| 8 | int t, int rfd, int wfd, SSL *ssl, char *buf, int len) | ||
| 9 | { | ||
| 10 | int n; | ||
| 11 | const datetime_sec end = (datetime_sec)t + now(); | ||
| 12 | |||
| 13 | do { | ||
| 14 | fd_set fds; | ||
| 15 | struct timeval tv; | ||
| 16 | |||
| 17 | const int r = buf ? fun(ssl, buf, len) : fun(ssl); | ||
| 18 | if (r > 0) return r; | ||
| 19 | |||
| 20 | t = end - now(); | ||
| 21 | if (t < 0) break; | ||
| 22 | tv.tv_sec = (time_t)t; tv.tv_usec = 0; | ||
| 23 | |||
| 24 | FD_ZERO(&fds); | ||
| 25 | switch (SSL_get_error(ssl, r)) | ||
| 26 | { | ||
| 27 | default: return r; /* some other error */ | ||
| 28 | case SSL_ERROR_WANT_READ: | ||
| 29 | FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv); | ||
| 30 | break; | ||
| 31 | case SSL_ERROR_WANT_WRITE: | ||
| 32 | FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv); | ||
| 33 | break; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* n is the number of descriptors that changed status */ | ||
| 37 | } while (n > 0); | ||
| 38 | |||
| 39 | if (n != -1) errno = error_timeout; | ||
| 40 | return -1; | ||
| 41 | } | ||
| 42 | |||
| 43 | int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl) | ||
| 44 | { | ||
| 45 | int r; | ||
| 46 | |||
| 47 | /* if connection is established, keep NDELAY */ | ||
| 48 | if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; | ||
| 49 | r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); | ||
| 50 | |||
| 51 | if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } | ||
| 52 | else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); | ||
| 53 | |||
| 54 | return r; | ||
| 55 | } | ||
| 56 | |||
| 57 | int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl) | ||
| 58 | { | ||
| 59 | int r; | ||
| 60 | |||
| 61 | /* if connection is established, keep NDELAY */ | ||
| 62 | if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; | ||
| 63 | r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0); | ||
| 64 | |||
| 65 | if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } | ||
| 66 | else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); | ||
| 67 | |||
| 68 | return r; | ||
| 69 | } | ||
| 70 | |||
| 71 | int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl) | ||
| 72 | { | ||
| 73 | int r; | ||
| 74 | |||
| 75 | SSL_renegotiate(ssl); | ||
| 76 | r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); | ||
| 77 | if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r; | ||
| 78 | |||
| 79 | /* this is for the server only */ | ||
| 80 | ssl->state = SSL_ST_ACCEPT; | ||
| 81 | return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); | ||
| 82 | } | ||
| 83 | |||
| 84 | int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) | ||
| 85 | { | ||
| 86 | if (!buf) return 0; | ||
| 87 | if (SSL_pending(ssl)) return SSL_read(ssl, buf, len); | ||
| 88 | return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); | ||
| 89 | } | ||
| 90 | |||
| 91 | int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) | ||
| 92 | { | ||
| 93 | if (!buf) return 0; | ||
| 94 | return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); | ||
| 95 | } | ||
diff --git a/ssl_timeoutio.h b/ssl_timeoutio.h new file mode 100644 index 0000000..073cb67 --- /dev/null +++ b/ssl_timeoutio.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #ifndef SSL_TIMEOUTIO_H | ||
| 2 | #define SSL_TIMEOUTIO_H | ||
| 3 | |||
| 4 | #include <openssl/ssl.h> | ||
| 5 | |||
| 6 | /* the version is like this: 0xMNNFFPPS: major minor fix patch status */ | ||
| 7 | #if OPENSSL_VERSION_NUMBER < 0x00906000L | ||
| 8 | # error "Need OpenSSL version at least 0.9.6" | ||
| 9 | #endif | ||
| 10 | |||
| 11 | int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl); | ||
| 12 | int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl); | ||
| 13 | int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl); | ||
| 14 | |||
| 15 | int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); | ||
| 16 | int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); | ||
| 17 | |||
| 18 | int ssl_timeoutio( | ||
| 19 | int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len); | ||
| 20 | |||
| 21 | #endif | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | #include "exit.h" | ||
| 2 | #include "error.h" | ||
| 3 | #include <openssl/ssl.h> | ||
| 4 | #include <openssl/err.h> | ||
| 5 | |||
| 6 | int smtps = 0; | ||
| 7 | SSL *ssl = NULL; | ||
| 8 | |||
| 9 | void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); } | ||
| 10 | void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); } | ||
| 11 | |||
| 12 | const char *ssl_error() | ||
| 13 | { | ||
| 14 | int r = ERR_get_error(); | ||
| 15 | if (!r) return NULL; | ||
| 16 | SSL_load_error_strings(); | ||
| 17 | return ERR_error_string(r, NULL); | ||
| 18 | } | ||
| 19 | const char *ssl_error_str() | ||
| 20 | { | ||
| 21 | const char *err = ssl_error(); | ||
| 22 | if (err) return err; | ||
| 23 | if (!errno) return 0; | ||
| 24 | return (errno == error_timeout) ? "timed out" : error_str(errno); | ||
| 25 | } | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | #ifndef TLS_H | ||
| 2 | #define TLS_H | ||
| 3 | |||
| 4 | #include <openssl/ssl.h> | ||
| 5 | |||
| 6 | extern int smtps; | ||
| 7 | extern SSL *ssl; | ||
| 8 | |||
| 9 | void ssl_free(SSL *myssl); | ||
| 10 | void ssl_exit(int status); | ||
| 11 | # define _exit ssl_exit | ||
| 12 | |||
| 13 | const char *ssl_error(); | ||
| 14 | const char *ssl_error_str(); | ||
| 15 | |||
| 16 | #endif | ||
diff --git a/update_tmprsadh.sh b/update_tmprsadh.sh new file mode 100644 index 0000000..563fcfb --- /dev/null +++ b/update_tmprsadh.sh | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | # Update temporary RSA and DH keys | ||
| 4 | # Frederik Vermeulen 2004-05-31 GPL | ||
| 5 | |||
| 6 | umask 0077 || exit 0 | ||
| 7 | |||
| 8 | export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin" | ||
| 9 | |||
| 10 | openssl genrsa -out QMAIL/control/rsa512.new 512 && | ||
| 11 | chmod 600 QMAIL/control/rsa512.new && | ||
| 12 | chown UGQMAILD QMAIL/control/rsa512.new && | ||
| 13 | mv -f QMAIL/control/rsa512.new QMAIL/control/rsa512.pem | ||
| 14 | echo | ||
| 15 | |||
| 16 | openssl dhparam -2 -out QMAIL/control/dh512.new 512 && | ||
| 17 | chmod 600 QMAIL/control/dh512.new && | ||
| 18 | chown UGQMAILD QMAIL/control/dh512.new && | ||
| 19 | mv -f QMAIL/control/dh512.new QMAIL/control/dh512.pem | ||
| 20 | echo | ||
| 21 | |||
| 22 | openssl dhparam -2 -out QMAIL/control/dh1024.new 1024 && | ||
| 23 | chmod 600 QMAIL/control/dh1024.new && | ||
| 24 | chown UGQMAILD QMAIL/control/dh1024.new && | ||
| 25 | mv -f QMAIL/control/dh1024.new QMAIL/control/dh1024.pem | ||
