summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Hadaway <raker@gentoo.org>2002-09-11 21:22:25 +0000
committerNick Hadaway <raker@gentoo.org>2002-09-11 21:22:25 +0000
commitbc411a17e3ac765250f3a2cd365645f4ba35bae9 (patch)
tree4237b8da2e61ffd1ba44cc75fb3c1def01cc5f50 /net-mail/qmail
parentadded more USE flag dont's (diff)
downloadgentoo-2-bc411a17e3ac765250f3a2cd365645f4ba35bae9.tar.gz
gentoo-2-bc411a17e3ac765250f3a2cd365645f4ba35bae9.tar.bz2
gentoo-2-bc411a17e3ac765250f3a2cd365645f4ba35bae9.zip
new qmail ebuild! :)
Diffstat (limited to 'net-mail/qmail')
-rw-r--r--net-mail/qmail/files/1.03-r8/qmail-1.03-starttls-smtp-auth.patch1408
1 files changed, 1408 insertions, 0 deletions
diff --git a/net-mail/qmail/files/1.03-r8/qmail-1.03-starttls-smtp-auth.patch b/net-mail/qmail/files/1.03-r8/qmail-1.03-starttls-smtp-auth.patch
new file mode 100644
index 000000000000..683aac888516
--- /dev/null
+++ b/net-mail/qmail/files/1.03-r8/qmail-1.03-starttls-smtp-auth.patch
@@ -0,0 +1,1408 @@
+
+A word of warning: the TLS part of this patch is not type-safe at
+at least one point (hey, I didn't write it.) I don't think this
+causes problems on i386 architectures, but it made qmail-smtpd
+crash frequently on an Alpha. Commenting out the substdio_fdbuf(...);
+call in qmail-smtpd appears to fix the issue.
+
+diff -urP qmail-1.03-vanilla/Makefile qmail-1.03-tls-auth/Makefile
+--- qmail-1.03-vanilla/Makefile Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/Makefile Wed Jun 19 16:09:58 2002
+@@ -136,6 +136,10 @@
+ compile auto_usera.c
+ ./compile auto_usera.c
+
++base64.o: \
++compile base64.c base64.h stralloc.h substdio.h str.h
++ ./compile base64.c
++
+ binm1: \
+ binm1.sh conf-qmail
+ cat binm1.sh \
+@@ -1446,7 +1450,8 @@
+ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
+ ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
+ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
+- str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
++ str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` \
++ -lssl -lcrypto
+
+ qmail-remote.0: \
+ qmail-remote.8
+@@ -1536,13 +1541,13 @@
+ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
+ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
+ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
+-fs.a auto_qmail.o socket.lib
++fs.a auto_qmail.o base64.o socket.lib
+ ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
+ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
+ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
+ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
+- alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
+- socket.lib`
++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o `cat \
++ socket.lib` -lssl -lcrypto
+
+ qmail-smtpd.0: \
+ qmail-smtpd.8
+@@ -1553,7 +1558,8 @@
+ substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
+ error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
+ substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
+-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
++exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h wait.h \
++fd.h base64.h
+ ./compile qmail-smtpd.c
+
+ qmail-start: \
+@@ -2139,3 +2145,23 @@
+ wait_pid.o: \
+ compile wait_pid.c error.h haswaitp.h
+ ./compile wait_pid.c
++
++cert:
++ openssl req -new -x509 -nodes \
++ -out /var/qmail/control/servercert.pem -days 366 \
++ -keyout /var/qmail/control/servercert.pem
++ chmod 640 /var/qmail/control/servercert.pem
++ chown qmaild.qmail /var/qmail/control/servercert.pem
++ ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
++
++cert-req:
++ openssl req -new -nodes \
++ -out req.pem \
++ -keyout /var/qmail/control/servercert.pem
++ chmod 640 /var/qmail/control/servercert.pem
++ chown qmaild.qmail /var/qmail/control/servercert.pem
++ ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem
++ @echo
++ @echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
++ @echo "cat signed_req.pem >> /var/qmail/control/servercert.pem"
++
+diff -urP qmail-1.03-vanilla/README.auth qmail-1.03-tls-auth/README.auth
+--- qmail-1.03-vanilla/README.auth Wed Dec 31 18:00:00 1969
++++ qmail-1.03-tls-auth/README.auth Wed Jun 19 15:31:37 2002
+@@ -0,0 +1,175 @@
++*** Warning! Cuidado! Vorsicht! ***
++===================================
++*** Version 0.30 of the patch changes the arguments which must be
++*** passed to qmail-smtpd. If you are upgrading from a previous
++*** version of the patch, take care to ensure your invocation of
++*** qmail-smtpd uses the correct arguments. Otherwise, your server
++*** may run as an open relay!
++===================================
++*** Warning! Cuidado! Vorsicht! ***
++
++
++This patch adds ESMTP AUTH authentication protocol support to
++qmail-1.03. It's originally based on Mrs. Brisby's smtp-auth patch
++with many enhancements from Krzysztof Dabrowski <brush@elysium.pl>.
++
++Beginning with version 0.30, the patch was completely rewritten to
++use only djb's string functions by Eric M. Johnston <emj@postal.net>.
++
++You can always get the newest version from:
++http://members.elysium.pl/brush/qmail-smtpd-auth/
++
++To use all of it's functionality you will also have to obtain and
++install Krzysztof's cmd5checkpw utility available at:
++http://members.elysium.pl/brush/cmd5checkpw/
++
++If you need more information about SMTP-AUTH itself and the
++client/server support and configuration, visit:
++http://members.elysium.pl/brush/smtp-auth/
++
++---
++
++Detailed patch information:
++
++This patch adds the ESMTP AUTH option to qmail-1.03, allowing the
++LOGIN, PLAIN, and CRAM-MD5 AUTH types. An appropriate checkpassword
++tool is necessary to support the authentication. See
++http://cr.yp.to/checkpwd.html for more information on the interface.
++Note that the checkpassword tool should support all of the AUTH types
++advertised by qmail-smtpd.
++
++As reflected in the modified qmail-smtpd(8) man page, qmail-smtpd
++must be invoked with three arguments: hostname, checkprogram, and
++subprogram. If these arguments are missing, qmail-smtpd will still
++advertise availability of AUTH, but will fail with a permanent error
++when AUTH is used.
++
++hostname is simply used to form the CRAM-MD5 challenge. qmail-smtpd
++invokes checkprogram, feeding it the username and password, in the
++case of LOGIN or PLAIN, or the username, challenge, and response, in
++the case of CRAM-MD5. If the user is permitted, checkprogram invokes
++subprogram, which just has to exit with a status of 0 for the user to
++be authenticated. Otherwise, checkprogram exits with a non-zero
++status. subprogram can usually be /usr/bin/true (or /bin/true,
++depending on your flavor of OS).
++
++If the user is successfully authenticated, the RELAYCLIENT
++environment variable is effectively set for the SMTP session, and
++the TCPREMOTEINFO environment variable is set to the authenticated
++username, overriding any value that tcpserver may have set. The
++value of TCPREMOTEINFO is reflected in a Received header.
++
++
++How to install it:
++
++Simply patch your qmail-1.03 distribution with the included patch
++file and recompile & install like usual.
++
++The steps to do this are as follows (assuming your virgin
++qmail-1.03 install is in "../qmail-1.03"):
++
++ cp README.auth base64.c base64.h ../qmail-1.03
++ patch -d ../qmail-1.03 < auth.patch
++
++Install qmail normally, with the exception of the new arguments
++to qmail-smtpd described elsewhere in this file.
++
++Also obtain, unpack, compile and install the cmd5checkpw utility
++(or some other checkpassword utility) and add a sample account to
++/etc/poppasswd file. This file must be readable by the qmail-smtpd
++user, usually qmaild.
++
++
++How to use it:
++
++*** Warning: In version 0.30 the arguments have changed from
++*** previous versions of qmail-smtpd-auth. Take care to make sure
++*** you update your startup scripts if updating!
++
++If you're running qmail-smtpd from inetd, you'll want to do the
++following:
++
++smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env \
++/var/qmail/bin/qmail-smtpd mail.acme.com /bin/cmd5checkpw /bin/true
++
++Replace mail.acme.com with your hostname. The second argument to
++qmail-smtpd is your checkpassword utility (preferably cmd5checkpw
++or some alternative that can handle CRAM-MD5). The third argument
++is the executable that the checkpassword utility execs when
++authentication is successful. (Note that the location of "true"
++is OS dependent: you may need /usr/bin/true.)
++
++Invocations using tcpserver will require analagous changes. Give
++your inetd a kill -HUP or restart tcpserver and away you go.
++
++
++Caveats:
++
++Please note that as authentication needs vary wildly across
++installations, no effort has been made to make this patch work ``out
++of the box.'' You'll have to procure or develop your own
++checkpassword program. Also note that CRAM-MD5 will require you to
++keep plaintext passwords. You'll probably want to disable this AUTH
++type if you're just using /etc/passwd (keeping in mind that PLAIN and
++LOGIN aren't quite as safe over the wire) -- just undefine AUTHCRAM
++in qmail-smtpd.
++
++Krzysztof Dabrowski's cmd5checkpw tool used as an example in this
++document supports the three AUTH types included in this patch.
++It's available at http://www.elysium.pl/members/brush/cmd5checkpw/.
++
++This patch has been generated against the stock qmail 1.03
++distribution. The results of combining this patch with others are
++unknown.
++
++
++Features:
++
++This patch supports the following auth methods: LOGIN, PLAIN and
++CRAM-MD5.
++
++
++Compatibility:
++
++The following MUA's are confirmed to work with this patch:
++
++Eudora 4.2.2 - CRAM-MD5
++Eudora 5.0.2 - CRAM-MD5
++The Bat 1.39 - LOGIN & CRAM-MD5
++Outlook Express 4 - LOGIN
++Outlook Express 5 - LOGIN
++Outlook 2000 - LOGIN
++Netscape 4.x - LOGIN & PLAIN
++Netscape 4.0x - LOGIN
++Pegasus Mail 3.1x - CRAM-MD5
++
++
++Various compatibility issues:
++
++Testing with Pegasus Mail 3.1 revealed that it requires the new style
++(RFC recommended) greeting message. Both styles are now enabled to
++maintain the highest degree of compatibility with various clients.
++This fix was suggested by David Harris <David.Harris@pmail.gen.nz>,
++the developer of Pegasus Mail.
++
++
++Acknowledgments:
++
++This patch is based on work by Krzysztof Dabrowski at
++http://members.elysium.pl/brush/qmail-smtpd-auth/ and ``Mrs. Brisby''
++at http://www.nimh.org/hacks/qmail-smtpd.c which has been further
++developed by Eric M. Johnston <emj@postal.net>.
++
++---
++
++THIS SOFTWARE IS IN THE PUBLIC DOMAIN, IS PROVIDED BY THE AUTHOR
++``AS IS,'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
++OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
++EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+diff -urP qmail-1.03-vanilla/TARGETS qmail-1.03-tls-auth/TARGETS
+--- qmail-1.03-vanilla/TARGETS Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/TARGETS Wed Jun 19 15:30:20 2002
+@@ -250,6 +250,7 @@
+ qmail-qmtpd.o
+ rcpthosts.o
+ qmail-qmtpd
++base64.o
+ qmail-smtpd.o
+ qmail-smtpd
+ sendmail.o
+diff -urP qmail-1.03-vanilla/base64.c qmail-1.03-tls-auth/base64.c
+--- qmail-1.03-vanilla/base64.c Wed Dec 31 18:00:00 1969
++++ qmail-1.03-tls-auth/base64.c Wed Jun 19 15:29:53 2002
+@@ -0,0 +1,90 @@
++#include "base64.h"
++#include "stralloc.h"
++#include "substdio.h"
++#include "str.h"
++
++static char *b64alpha =
++ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
++#define B64PAD '='
++
++/* returns 0 ok, 1 illegal, -1 problem */
++
++int b64decode(in,l,out)
++const unsigned char *in;
++int l;
++stralloc *out; /* not null terminated */
++{
++ int i, j;
++ unsigned char a[4];
++ unsigned char b[3];
++ char *s;
++
++ if (l == 0)
++ {
++ if (!stralloc_copys(out,"")) return -1;
++ return 0;
++ }
++
++ if (!stralloc_ready(out,l + 2)) return -1; /* XXX generous */
++ s = out->s;
++
++ for (i = 0;i < l;i += 4) {
++ for (j = 0;j < 4;j++)
++ if ((i + j) < l && in[i + j] != B64PAD)
++ {
++ a[j] = str_chr(b64alpha,in[i + j]);
++ if (a[j] > 63) return 1;
++ }
++ else a[j] = 0;
++
++ b[0] = (a[0] << 2) | (a[1] >> 4);
++ b[1] = (a[1] << 4) | (a[2] >> 2);
++ b[2] = (a[2] << 6) | (a[3]);
++
++ *s++ = b[0];
++
++ if (in[i + 1] == B64PAD) break;
++ *s++ = b[1];
++
++ if (in[i + 2] == B64PAD) break;
++ *s++ = b[2];
++ }
++ out->len = s - out->s;
++ while (out->len && !out->s[out->len - 1]) --out->len; /* XXX avoid? */
++ return 0;
++}
++
++int b64encode(in,out)
++stralloc *in;
++stralloc *out; /* not null terminated */
++{
++ unsigned char a, b, c;
++ int i;
++ char *s;
++
++ if (in->len == 0)
++ {
++ if (!stralloc_copys(out,"")) return -1;
++ return 0;
++ }
++
++ if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1;
++ s = out->s;
++
++ for (i = 0;i < in->len;i += 3) {
++ a = in->s[i];
++ b = i + 1 < in->len ? in->s[i + 1] : 0;
++ c = i + 2 < in->len ? in->s[i + 2] : 0;
++
++ *s++ = b64alpha[a >> 2];
++ *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
++
++ if (i + 1 >= in->len) *s++ = B64PAD;
++ else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
++
++ if (i + 2 >= in->len) *s++ = B64PAD;
++ else *s++ = b64alpha[c & 63];
++ }
++ out->len = s - out->s;
++ return 0;
++}
+diff -urP qmail-1.03-vanilla/base64.h qmail-1.03-tls-auth/base64.h
+--- qmail-1.03-vanilla/base64.h Wed Dec 31 18:00:00 1969
++++ qmail-1.03-tls-auth/base64.h Wed Jun 19 15:29:53 2002
+@@ -0,0 +1,7 @@
++#ifndef BASE64_H
++#define BASE64_H
++
++extern int b64decode();
++extern int b64encode();
++
++#endif
+diff -urP qmail-1.03-vanilla/conf-cc qmail-1.03-tls-auth/conf-cc
+--- qmail-1.03-vanilla/conf-cc Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/conf-cc Wed Jun 19 15:35:59 2002
+@@ -1,3 +1,3 @@
+-cc -O2
++cc -O2 -DTLS -I/usr/local/ssl/include
+
+ This will be used to compile .c files.
+diff -urP qmail-1.03-vanilla/dns.c qmail-1.03-tls-auth/dns.c
+--- qmail-1.03-vanilla/dns.c Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/dns.c Wed Jun 19 15:36:06 2002
+@@ -270,6 +270,14 @@
+ {
+ int r;
+ struct ip_mx ix;
++#ifdef TLS
++ stralloc fqdn = {0};
++
++ if (!stralloc_copy(&fqdn,sa)) return DNS_MEM;
++ if (!stralloc_0(&fqdn)) return DNS_MEM;
++ ix.fqdn = fqdn.s;
++ alloc_free(fqdn);
++#endif
+
+ if (!stralloc_copy(&glue,sa)) return DNS_MEM;
+ if (!stralloc_0(&glue)) return DNS_MEM;
+@@ -330,6 +338,9 @@
+ ix.pref = 0;
+ if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
+ {
++#ifdef TLS
++ ix.fqdn = NULL;
++#endif
+ if (!ipalloc_append(ia,&ix)) return DNS_MEM;
+ return 0;
+ }
+diff -urP qmail-1.03-vanilla/ipalloc.h qmail-1.03-tls-auth/ipalloc.h
+--- qmail-1.03-vanilla/ipalloc.h Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/ipalloc.h Wed Jun 19 15:36:15 2002
+@@ -3,7 +3,12 @@
+
+ #include "ip.h"
+
++#ifdef TLS
++#include "stralloc.h"
++struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
++#else
+ struct ip_mx { struct ip_address ip; int pref; } ;
++#endif
+
+ #include "gen_alloc.h"
+
+diff -urP qmail-1.03-vanilla/qmail-remote.c qmail-1.03-tls-auth/qmail-remote.c
+--- qmail-1.03-vanilla/qmail-remote.c Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/qmail-remote.c Wed Jun 19 15:36:38 2002
+@@ -26,8 +26,18 @@
+ #include "tcpto.h"
+ #include "readwrite.h"
+ #include "timeoutconn.h"
++#ifndef TLS
+ #include "timeoutread.h"
+ #include "timeoutwrite.h"
++#endif
++
++#ifdef TLS
++#include <sys/stat.h>
++#include <openssl/ssl.h>
++SSL *ssl = NULL;
++
++stralloc tlsclientciphers = {0};
++#endif
+
+ #define HUGESMTPTEXT 5000
+
+@@ -107,17 +117,94 @@
+ int smtpfd;
+ int timeout = 1200;
+
++#ifdef TLS
++int flagtimedout = 0;
++void sigalrm()
++{
++ flagtimedout = 1;
++}
++
++int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
++{
++ int r; int saveerrno;
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ alarm(timeout);
++ if (ssl) {
++ while(((r = SSL_read(ssl,buf,n)) <= 0)
++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ));
++ if (SSL_get_error(ssl, r) != SSL_ERROR_NONE)
++ {char buf[1024];
++
++ out("ZTLS connection to "); outhost(); out(" died: ");
++ SSL_load_error_strings();
++ out(ERR_error_string(ERR_get_error(), buf)); out("\n");
++ SSL_shutdown(ssl);
++ zerodie();
++ }
++ }else r = read(fd,buf,n);
++ saveerrno = errno;
++ alarm(0);
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ errno = saveerrno;
++ return r;
++}
++
++int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
++{
++ int r; int saveerrno;
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ alarm(timeout);
++ if (ssl) {
++ while(((r = SSL_write(ssl,buf,n)) <= 0)
++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE));
++ if (SSL_get_error(ssl, r) != SSL_ERROR_NONE)
++ {char buf[1024];
++
++ out("ZTLS connection to "); outhost(); out(" died: ");
++ SSL_load_error_strings();
++ out(ERR_error_string(ERR_get_error(), buf)); out("\n");
++ SSL_shutdown(ssl);
++ zerodie();
++ }
++ }else r = write(fd,buf,n);
++ saveerrno = errno;
++ alarm(0);
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ errno = saveerrno;
++ return r;
++}
++
++static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey)
++{
++ out("ZTLS found no client cert in control/clientcert.pem\n");
++ zerodie(NULL,NULL);
++}
++
++static int verify_cb(int ok, X509_STORE_CTX * ctx)
++{
++ return (1);
++}
++#endif
++
+ int saferead(fd,buf,len) int fd; char *buf; int len;
+ {
+ int r;
++#ifdef TLS
++ r = ssl_timeoutread(timeout,smtpfd,buf,len);
++#else
+ r = timeoutread(timeout,smtpfd,buf,len);
++#endif
+ if (r <= 0) dropped();
+ return r;
+ }
+ int safewrite(fd,buf,len) int fd; char *buf; int len;
+ {
+ int r;
++#ifdef TLS
++ r = ssl_timeoutwrite(timeout,smtpfd,buf,len);
++#else
+ r = timeoutwrite(timeout,smtpfd,buf,len);
++#endif
+ if (r <= 0) dropped();
+ return r;
+ }
+@@ -186,6 +273,34 @@
+ out(append);
+ out(".\n");
+ outsmtptext();
++
++/* TAG */
++#if defined(TLS) && defined(DEBUG)
++#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0)
++
++ if(ssl){
++ X509 *peer;
++
++ out("STARTTLS proto="); out(SSL_get_version(ssl));
++ out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
++
++ /* we want certificate details */
++ peer=SSL_get_peer_certificate(ssl);
++ if (peer != NULL) {
++ char *str;
++
++ str=ONELINE_NAME(X509_get_subject_name(peer));
++ out("; subject="); out(str);
++ OPENSSL_free(str);
++ str=ONELINE_NAME(X509_get_issuer_name(peer));
++ out("; issuer="); out(str);
++ OPENSSL_free(str);
++ X509_free(peer);
++ }
++ out(";\n");
++ }
++#endif
++
+ zerodie();
+ }
+
+@@ -216,20 +331,158 @@
+
+ stralloc recip = {0};
+
++#ifdef TLS
++void smtp(fqdn)
++char *fqdn;
++#else
+ void smtp()
++#endif
+ {
+ unsigned long code;
+ int flagbother;
+ int i;
+-
++#ifdef TLS
++ int needtlsauth = 0;
++ SSL_CTX *ctx;
++ int saveerrno, r;
++
++ stralloc servercert = {0};
++ struct stat st;
++ if(fqdn){
++ if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem();
++ if(!stralloc_catb(&servercert, fqdn, str_len(fqdn))) temp_nomem();
++ if(!stralloc_catb(&servercert, ".pem", 4)) temp_nomem();
++ if(!stralloc_0(&servercert)) temp_nomem();
++ if (stat(servercert.s,&st) == 0) needtlsauth = 1;
++ }
++#endif
++
+ if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
+
++#ifdef TLS
++ substdio_puts(&smtpto,"EHLO ");
++#else
+ substdio_puts(&smtpto,"HELO ");
++#endif
+ substdio_put(&smtpto,helohost.s,helohost.len);
+ substdio_puts(&smtpto,"\r\n");
+ substdio_flush(&smtpto);
++#ifdef TLS
++ if (smtpcode() != 250){
++ substdio_puts(&smtpto,"HELO ");
++ substdio_put(&smtpto,helohost.s,helohost.len);
++ substdio_puts(&smtpto,"\r\n");
++ substdio_flush(&smtpto);
++ if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
++ }
++#else
+ if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
+-
++#endif
++
++#ifdef TLS
++ i = 0;
++ while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) &&
++ str_diffn(smtptext.s+i+4,"STARTTLS\n",9));
++ if (i+12 < smtptext.len)
++ {
++ substdio_puts(&smtpto,"STARTTLS\r\n");
++ substdio_flush(&smtpto);
++ if (smtpcode() == 220)
++ {
++ SSL_library_init();
++ if(!(ctx=SSL_CTX_new(SSLv23_client_method())))
++ {char buf[1024];
++
++ out("ZTLS not available: error initializing ctx: ");
++ SSL_load_error_strings();
++ out(ERR_error_string(ERR_get_error(), buf));
++ out("\n");
++ SSL_shutdown(ssl);
++ zerodie();
++ }
++ if((stat("control/clientcert.pem", &st) == 0) &&
++ ((SSL_CTX_use_RSAPrivateKey_file(ctx, "control/clientcert.pem", SSL_FILETYPE_PEM) <= 0) ||
++ (SSL_CTX_use_certificate_chain_file(ctx, "control/clientcert.pem") <= 0) ||
++ (SSL_CTX_check_private_key(ctx) <= 0)))
++ /* if there is a cert and it is bad, I fail
++ if there is no cert, I leave it to the other side to complain */
++ SSL_CTX_set_client_cert_cb(ctx, client_cert_cb);
++
++ /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/
++ SSL_CTX_set_cipher_list(ctx,tlsclientciphers.s);
++
++ if (needtlsauth){
++ if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL))
++ {out("ZTLS unable to load "); out(servercert.s); out("\n");
++ zerodie();}
++ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
++ }
++
++ if(!(ssl=SSL_new(ctx)))
++ {char buf[1024];
++
++ out("ZTLS not available: error initializing ssl: ");
++ SSL_load_error_strings();
++ out(ERR_error_string(ERR_get_error(), buf));
++ out("\n");
++ SSL_shutdown(ssl);
++ zerodie();
++ }
++ SSL_set_fd(ssl,smtpfd);
++
++ alarm(timeout);
++ r = SSL_connect(ssl); saveerrno = errno;
++ alarm(0);
++ if (flagtimedout)
++ {out("ZTLS not available: connect timed out\n");
++ zerodie();}
++ errno = saveerrno;
++ if (r<=0)
++ {char buf[1024];
++
++ out("ZTLS not available: connect failed: ");
++ SSL_load_error_strings();
++ out(ERR_error_string(ERR_get_error(), buf));
++ out("\n");
++ SSL_shutdown(ssl);
++ zerodie();
++ }
++ if (needtlsauth)
++ /* should also check alternate names */
++ {char commonName[256];
++
++ if ((r=SSL_get_verify_result(ssl)) != X509_V_OK)
++ {out("ZTLS unable to verify server with ");
++ out(servercert.s); out(": ");
++ out(X509_verify_cert_error_string(r)); out("\n");
++ zerodie();
++ }
++ X509_NAME_get_text_by_NID(X509_get_subject_name(
++ SSL_get_peer_certificate(ssl)),
++ NID_commonName, commonName, 256);
++ if (strcasecmp(fqdn,commonName)){
++ out("ZTLS connection to "); out(fqdn);
++ out(" wanted, certificate for "); out(commonName);
++ out(" received\n");
++ zerodie();}
++ }
++
++ substdio_puts(&smtpto,"EHLO ");
++ substdio_put(&smtpto,helohost.s,helohost.len);
++ substdio_puts(&smtpto,"\r\n");
++ substdio_flush(&smtpto);
++
++ if (smtpcode() != 250)
++ {
++ quit("ZTLS connected to "," but my name was rejected");
++ }
++ }
++ }
++ if ((!ssl) && needtlsauth)
++ {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n");
++ quit();}
++#endif
++
+ substdio_puts(&smtpto,"MAIL FROM:<");
+ substdio_put(&smtpto,sender.s,sender.len);
+ substdio_puts(&smtpto,">\r\n");
+@@ -324,6 +577,11 @@
+ case 1:
+ if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
+ }
++#ifdef TLS
++ if (control_rldef(&tlsclientciphers,"control/tlsclientciphers",0,"DEFAULT") != 1)
++ temp_control();
++ if(!stralloc_0(&tlsclientciphers)) temp_nomem();
++#endif
+ }
+
+ void main(argc,argv)
+@@ -338,7 +596,10 @@
+ int flagallaliases;
+ int flagalias;
+ char *relayhost;
+-
++
++#ifdef TLS
++ sig_alarmcatch(sigalrm);
++#endif
+ sig_pipeignore();
+ if (argc < 4) perm_usage();
+ if (chdir(auto_qmail) == -1) temp_chdir();
+@@ -417,7 +678,11 @@
+ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
+ tcpto_err(&ip.ix[i].ip,0);
+ partner = ip.ix[i].ip;
++#ifdef TLS
++ smtp(ip.ix[i].fqdn); /* does not return */
++#else
+ smtp(); /* does not return */
++#endif
+ }
+ tcpto_err(&ip.ix[i].ip,errno == error_timeout);
+ close(smtpfd);
+diff -urP qmail-1.03-vanilla/qmail-smtpd.8 qmail-1.03-tls-auth/qmail-smtpd.8
+--- qmail-1.03-vanilla/qmail-smtpd.8 Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/qmail-smtpd.8 Wed Jun 19 15:30:20 2002
+@@ -3,6 +3,11 @@
+ qmail-smtpd \- receive mail via SMTP
+ .SH SYNOPSIS
+ .B qmail-smtpd
++[
++.I hostname
++.I checkprogram
++.I subprogram
++]
+ .SH DESCRIPTION
+ .B qmail-smtpd
+ receives mail messages via the Simple Mail Transfer Protocol (SMTP)
+@@ -23,7 +28,29 @@
+ header fields.
+
+ .B qmail-smtpd
+-supports ESMTP, including the 8BITMIME and PIPELINING options.
++supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options.
++
++.B qmail-smtpd
++can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes
++.IR checkprogram ,
++which reads on file descriptor 3 the username, a 0 byte, the password
++or challenge derived from
++.IR hostname ,
++another 0 byte, a CRAM-MD5 response (if applicable to the AUTH type),
++and a final 0 byte.
++.I checkprogram
++invokes
++.I subprogram
++upon successful authentication, which should in turn return 0 to
++.BR qmail-smtpd ,
++effectively setting the environment variables RELAYCLIENT and TCPREMOTEINFO
++(any supplied value replaced with the authenticated username).
++.B qmail-smtpd
++will reject the authentication attempt if it receives a nonzero return
++value from
++.I checkprogram
++or
++.IR subprogram .
+ .SH TRANSPARENCY
+ .B qmail-smtpd
+ converts the SMTP newline convention into the UNIX newline convention
+@@ -177,3 +204,6 @@
+ qmail-newmrh(8),
+ qmail-queue(8),
+ qmail-remote(8)
++.SH "HISTORY"
++The patch enabling the ESMTP AUTH option is not part of the standard
++qmail-1.03 distribution.
+diff -urP qmail-1.03-vanilla/qmail-smtpd.c qmail-1.03-tls-auth/qmail-smtpd.c
+--- qmail-1.03-vanilla/qmail-smtpd.c Mon Jun 15 05:53:16 1998
++++ qmail-1.03-tls-auth/qmail-smtpd.c Wed Jun 19 16:05:56 2002
+@@ -20,18 +20,75 @@
+ #include "now.h"
+ #include "exit.h"
+ #include "rcpthosts.h"
++#ifndef TLS
+ #include "timeoutread.h"
+ #include "timeoutwrite.h"
++#endif
+ #include "commands.h"
++#include "wait.h"
++#include "fd.h"
+
++#ifdef TLS
++#include <openssl/ssl.h>
++SSL *ssl = NULL;
++
++stralloc clientcert = {0};
++stralloc tlsserverciphers = {0};
++#endif
++
++#define AUTHCRAM
+ #define MAXHOPS 100
+ unsigned int databytes = 0;
+ int timeout = 1200;
+
++#ifdef TLS
++int flagtimedout = 0;
++void sigalrm()
++{
++ flagtimedout = 1;
++}
++int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
++{
++ int r; int saveerrno;
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ alarm(timeout);
++ if (ssl) {
++ while(((r = SSL_read(ssl,buf,n)) <= 0)
++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ));
++ }else r = read(fd,buf,n);
++ saveerrno = errno;
++ alarm(0);
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ errno = saveerrno;
++ return r;
++}
++
++
++int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n;
++{
++ int r; int saveerrno;
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ alarm(timeout);
++ if (ssl) {
++ while(((r = SSL_write(ssl,buf,n)) <= 0)
++ && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE));
++ }else r = write(fd,buf,n);
++ saveerrno = errno;
++ alarm(0);
++ if (flagtimedout) { errno = error_timeout; return -1; }
++ errno = saveerrno;
++ return r;
++}
++#endif
++
+ int safewrite(fd,buf,len) int fd; char *buf; int len;
+ {
+ int r;
++#ifdef TLS
++ r = ssl_timeoutwrite(timeout,fd,buf,len);
++#else
+ r = timeoutwrite(timeout,fd,buf,len);
++#endif
+ if (r <= 0) _exit(1);
+ return r;
+ }
+@@ -51,6 +108,9 @@
+
+ void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
+ void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
++#ifdef TLS
++void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); }
++#endif
+ void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }
+ void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
+ void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
+@@ -59,6 +119,15 @@
+ void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
+ void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
+
++int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
++int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
++int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
++int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
++void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
++void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
++int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
++int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; }
++int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
+
+ stralloc greeting = {0};
+
+@@ -81,6 +150,9 @@
+ char *remoteinfo;
+ char *local;
+ char *relayclient;
++#ifdef TLS
++char *tlsciphers;
++#endif
+
+ stralloc helohost = {0};
+ char *fakehelo; /* pointer into helohost, or 0 */
+@@ -101,6 +173,9 @@
+ {
+ char *x;
+ unsigned long u;
++#ifdef TLS
++ char *tlsciphers;
++#endif
+
+ if (control_init() == -1) die_control();
+ if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)
+@@ -131,6 +206,17 @@
+ if (!remotehost) remotehost = "unknown";
+ remoteinfo = env_get("TCPREMOTEINFO");
+ relayclient = env_get("RELAYCLIENT");
++#ifdef TLS
++ if (tlsciphers = env_get("TLSCIPHERS")){
++ if (!stralloc_copys(&tlsserverciphers,tlsciphers)) die_nomem();
++ }
++ else {
++ if (control_rldef(&tlsserverciphers,"control/tlsserverciphers",0,"DEFAULT") != 1)
++ die_control();
++ }
++ if (!stralloc_0(&tlsserverciphers)) die_nomem();
++#endif
++
+ dohelo(remotehost);
+ }
+
+@@ -229,7 +315,18 @@
+ }
+ void smtp_ehlo(arg) char *arg;
+ {
+- smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
++ smtp_greet("250-");
++#ifdef AUTHCRAM
++ out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN");
++ out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN");
++#else
++ out("\r\n250-AUTH LOGIN PLAIN");
++ out("\r\n250-AUTH=LOGIN PLAIN");
++#endif
++#ifdef TLS
++ if (!ssl) out("\r\n250-STARTTLS");
++#endif
++ out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+ seenmail = 0; dohelo(arg);
+ }
+ void smtp_rset()
+@@ -247,6 +344,12 @@
+ if (!stralloc_0(&mailfrom)) die_nomem();
+ out("250 ok\r\n");
+ }
++#ifdef TLS
++static int verify_cb(int ok, X509_STORE_CTX * ctx)
++{
++ return (1);
++}
++#endif
+ void smtp_rcpt(arg) char *arg; {
+ if (!seenmail) { err_wantmail(); return; }
+ if (!addrparse(arg)) { err_syntax(); return; }
+@@ -257,7 +360,54 @@
+ if (!stralloc_0(&addr)) die_nomem();
+ }
+ else
++#ifndef TLS
+ if (!addrallowed()) { err_nogateway(); return; }
++#else
++ if (!addrallowed())
++ {
++ if (ssl)
++ { STACK_OF(X509_NAME) *sk;
++ X509 *peercert;
++ stralloc tlsclients = {0};
++ struct constmap maptlsclients;
++ int r;
++
++ SSL_set_verify(ssl,
++ SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
++ verify_cb);
++ if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL)
++ { err_nogateway(); return; }
++ SSL_set_client_CA_list(ssl, sk);
++ if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) ||
++ !constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0))
++ { err_nogateway(); return; }
++
++ SSL_renegotiate(ssl);
++ SSL_do_handshake(ssl);
++ ssl->state = SSL_ST_ACCEPT;
++ SSL_do_handshake(ssl);
++ if ((r = SSL_get_verify_result(ssl)) != X509_V_OK)
++ {out("553 no valid cert for gatewaying: ");
++ out(X509_verify_cert_error_string(r));
++ out(" (#5.7.1)\r\n");
++ return;
++ }
++
++ if (peercert = SSL_get_peer_certificate(ssl))
++ {char emailAddress[256];
++
++ X509_NAME_get_text_by_NID(X509_get_subject_name(
++ SSL_get_peer_certificate(ssl)),
++ NID_pkcs9_emailAddress, emailAddress, 256); if (!stralloc_copys(&clientcert, emailAddress)) die_nomem();
++ if (!constmap(&maptlsclients,clientcert.s,clientcert.len))
++ { err_nogwcert(); return; }
++ relayclient = "";
++ }
++ else { err_nogwcert(); return; }
++ }
++ else { err_nogateway(); return; }
++ }
++#endif
+ if (!stralloc_cats(&rcptto,"T")) die_nomem();
+ if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
+ if (!stralloc_0(&rcptto)) die_nomem();
+@@ -269,7 +419,11 @@
+ {
+ int r;
+ flush();
++#ifdef TLS
++ r = ssl_timeoutread(timeout,fd,buf,len);
++#else
+ r = timeoutread(timeout,fd,buf,len);
++#endif
+ if (r == -1) if (errno == error_timeout) die_alarm();
+ if (r <= 0) die_read();
+ return r;
+@@ -369,6 +523,9 @@
+ int hops;
+ unsigned long qp;
+ char *qqx;
++#ifdef TLS
++ stralloc protocolinfo = {0};
++#endif
+
+ if (!seenmail) { err_wantmail(); return; }
+ if (!rcptto.len) { err_wantrcpt(); return; }
+@@ -377,8 +534,20 @@
+ if (qmail_open(&qqt) == -1) { err_qqt(); return; }
+ qp = qmail_qp(&qqt);
+ out("354 go ahead\r\n");
+-
++#ifdef TLS
++ if(ssl){
++ if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem();
++ if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem();
++ if (clientcert.len){
++ if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem();
++ if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem();
++ }
++ if (!stralloc_0(&protocolinfo)) die_nomem();
++ } else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem();
++ received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0);
++#else
+ received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
++#endif
+ blast(&hops);
+ hops = (hops >= MAXHOPS);
+ if (hops) qmail_fail(&qqt);
+@@ -393,23 +562,299 @@
+ out(qqx + 1);
+ out("\r\n");
+ }
++#ifdef TLS
++static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength;
++{
++ RSA* rsa;
++ BIO* in;
++
++ if (!export || keylength == 512)
++ if (in=BIO_new(BIO_s_file_internal()))
++ if (BIO_read_filename(in,"control/rsa512.pem") > 0)
++ if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL))
++ return rsa;
++ return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL));
++}
++
++void smtp_tls(arg) char *arg;
++{
++ SSL_CTX *ctx;
++
++ if (*arg)
++ {out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
++ return;}
++
++ SSL_library_init();
++ if(!(ctx=SSL_CTX_new(SSLv23_server_method())))
++ {out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n");
++ return;}
++ if(!SSL_CTX_use_RSAPrivateKey_file(ctx, "control/servercert.pem", SSL_FILETYPE_PEM))
++ {out("454 TLS not available: missing RSA private key (#4.3.0)\r\n");
++ return;}
++ if(!SSL_CTX_use_certificate_chain_file(ctx, "control/servercert.pem"))
++ {out("454 TLS not available: missing certificate (#4.3.0)\r\n");
++ return;}
++ SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb);
++ SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s);
++ SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL);
++ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
++
++ out("220 ready for tls\r\n"); flush();
++
++ if(!(ssl=SSL_new(ctx))) die_read();
++ SSL_set_fd(ssl,0);
++ if(SSL_accept(ssl)<=0) die_read();
++ substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf));
++
++ remotehost = env_get("TCPREMOTEHOST");
++ if (!remotehost) remotehost = "unknown";
++ dohelo(remotehost);
++}
++#endif
++
++
++char unique[FMT_ULONG + FMT_ULONG + 3];
++static stralloc authin = {0};
++static stralloc user = {0};
++static stralloc pass = {0};
++static stralloc resp = {0};
++static stralloc slop = {0};
++char *hostname;
++char **childargs;
++substdio ssup;
++char upbuf[128];
++int authd = 0;
++
++int authgetl(void) {
++ int i;
++
++ if (!stralloc_copys(&authin, "")) die_nomem();
++
++ for (;;) {
++ if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
++ i = substdio_get(&ssin,authin.s + authin.len,1);
++ if (i != 1) die_read();
++ if (authin.s[authin.len] == '\n') break;
++ ++authin.len;
++ }
++
++ if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
++ authin.s[authin.len] = 0;
++
++ if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
++ if (authin.len == 0) { return err_input(); }
++ return authin.len;
++}
++
++int authenticate(void)
++{
++ int child;
++ int wstat;
++ int pi[2];
++
++ if (!stralloc_0(&user)) die_nomem();
++ if (!stralloc_0(&pass)) die_nomem();
++ if (!stralloc_0(&resp)) die_nomem();
++
++ if (fd_copy(2,1) == -1) return err_pipe();
++ close(3);
++ if (pipe(pi) == -1) return err_pipe();
++ if (pi[0] != 3) return err_pipe();
++ switch(child = fork()) {
++ case -1:
++ return err_fork();
++ case 0:
++ close(pi[1]);
++ sig_pipedefault();
++ execvp(*childargs, childargs);
++ _exit(1);
++ }
++ close(pi[0]);
++
++ substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
++ if (substdio_put(&ssup,user.s,user.len) == -1) return err_write();
++ if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write();
++ if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write();
++ if (substdio_flush(&ssup) == -1) return err_write();
++
++ close(pi[1]);
++ byte_zero(pass.s,pass.len);
++ byte_zero(upbuf,sizeof upbuf);
++ if (wait_pid(&wstat,child) == -1) return err_child();
++ if (wait_crashed(wstat)) return err_child();
++ if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */
++ return 0; /* yes */
++}
++
++int auth_login(arg) char *arg;
++{
++ int r;
++
++ if (*arg) {
++ if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
++ }
++ else {
++ out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */
++ if (authgetl() < 0) return -1;
++ if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
++ }
++ if (r == -1) die_nomem();
++
++ out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */
++
++ if (authgetl() < 0) return -1;
++ if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
++ if (r == -1) die_nomem();
++
++ if (!user.len || !pass.len) return err_input();
++ return authenticate();
++}
++
++int auth_plain(arg) char *arg;
++{
++ int r, id = 0;
++
++ if (*arg) {
++ if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input();
++ }
++ else {
++ out("334 \r\n"); flush();
++ if (authgetl() < 0) return -1;
++ if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
++ }
++ if (r == -1 || !stralloc_0(&slop)) die_nomem();
++ while (slop.s[id]) id++; /* ignore authorize-id */
++
++ if (slop.len > id + 1)
++ if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem();
++ if (slop.len > id + user.len + 2)
++ if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem();
++
++ if (!user.len || !pass.len) return err_input();
++ return authenticate();
++}
++
++#ifdef AUTHCRAM
++int auth_cram()
++{
++ int i, r;
++ char *s;
++
++ s = unique;
++ s += fmt_uint(s,getpid());
++ *s++ = '.';
++ s += fmt_ulong(s,(unsigned long) now());
++ *s++ = '@';
++ *s++ = 0;
++
++ if (!stralloc_copys(&pass,"<")) die_nomem();
++ if (!stralloc_cats(&pass,unique)) die_nomem();
++ if (!stralloc_cats(&pass,hostname)) die_nomem();
++ if (!stralloc_cats(&pass,">")) die_nomem();
++ if (b64encode(&pass,&slop) < 0) die_nomem();
++ if (!stralloc_0(&slop)) die_nomem();
++
++ out("334 ");
++ out(slop.s);
++ out("\r\n");
++ flush();
++
++ if (authgetl() < 0) return -1;
++ if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
++ if (r == -1 || !stralloc_0(&slop)) die_nomem();
++
++ i = str_chr(slop.s,' ');
++ s = slop.s + i;
++ while (*s == ' ') ++s;
++ slop.s[i] = 0;
++ if (!stralloc_copys(&user,slop.s)) die_nomem();
++ if (!stralloc_copys(&resp,s)) die_nomem();
++
++ if (!user.len || !resp.len) return err_input();
++ return authenticate();
++}
++#endif
++
++struct authcmd {
++ char *text;
++ int (*fun)();
++} authcmds[] = {
++ { "login", auth_login }
++, { "plain", auth_plain }
++#ifdef AUTHCRAM
++, { "cram-md5", auth_cram }
++#endif
++, { 0, err_noauth }
++};
++
++void smtp_auth(arg)
++char *arg;
++{
++ int i;
++ char *cmd = arg;
++
++ if (!hostname || !*childargs)
++ {
++ out("503 auth not available (#5.3.3)\r\n");
++ return;
++ }
++ if (authd) { err_authd(); return; }
++ if (seenmail) { err_authmail(); return; }
++
++ if (!stralloc_copys(&user,"")) die_nomem();
++ if (!stralloc_copys(&pass,"")) die_nomem();
++ if (!stralloc_copys(&resp,"")) die_nomem();
++
++ i = str_chr(cmd,' ');
++ arg = cmd + i;
++ while (*arg == ' ') ++arg;
++ cmd[i] = 0;
++
++ for (i = 0;authcmds[i].text;++i)
++ if (case_equals(authcmds[i].text,cmd)) break;
++
++ switch (authcmds[i].fun(arg)) {
++ case 0:
++ authd = 1;
++ relayclient = "";
++ remoteinfo = user.s;
++ if (!env_unset("TCPREMOTEINFO")) die_read();
++ if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
++ out("235 ok, go ahead (#2.0.0)\r\n");
++ break;
++ case 1:
++ out("535 authorization failed (#5.7.0)\r\n");
++ }
++}
+
+ struct commands smtpcommands[] = {
+ { "rcpt", smtp_rcpt, 0 }
+ , { "mail", smtp_mail, 0 }
+ , { "data", smtp_data, flush }
++, { "auth", smtp_auth, flush }
+ , { "quit", smtp_quit, flush }
+ , { "helo", smtp_helo, flush }
+ , { "ehlo", smtp_ehlo, flush }
+ , { "rset", smtp_rset, 0 }
+ , { "help", smtp_help, flush }
++#ifdef TLS
++, { "starttls", smtp_tls, flush }
++#endif
+ , { "noop", err_noop, flush }
+ , { "vrfy", err_vrfy, flush }
+ , { 0, err_unimpl, flush }
+ } ;
+
+-void main()
+-{
++void main(argc,argv)
++int argc;
++char **argv;
++{
++ hostname = argv[1];
++ childargs = argv + 2;
++
++#ifdef TLS
++ sig_alarmcatch(sigalrm);
++#endif
+ sig_pipeignore();
+ if (chdir(auto_qmail) == -1) die_control();
+ setup();