diff -r 2ce477411646 -r 0a8afafb5d3e FILES.auth
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FILES.auth	Wed Oct 29 17:27:18 2014 +0900
@@ -0,0 +1,17 @@
+The qmail-smtpd Auth patch modifies the following QMAIL 1.03 files:
+
+= TARGETS
+= Makefile
+= qmail-smtpd.c
+= qmail-smtpd.8
+
+Added files:
+
+& base64.c
+& base64.h
+
+Informational files:
+
+% install_auth.sh  (Installation shell script)
+% README.auth
+% README.auth.old (old description of SMTP Auth)
diff -r 2ce477411646 -r 0a8afafb5d3e Makefile
--- a/Makefile	Wed Oct 29 11:34:37 2014 +0900
+++ b/Makefile	Wed Oct 29 17:27:18 2014 +0900
@@ -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 \
@@ -1537,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 \
 	tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
 	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 \
+	alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o  `cat \
 	socket.lib`
 
 qmail-smtpd.0: \
@@ -1555,7 +1559,7 @@
 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 base64.h
 	./compile qmail-smtpd.c
 
 qmail-start: \
@@ -2158,7 +2162,7 @@
 
 cert cert-req: \
 Makefile-cert
-	@$(MAKE) -sf $< $@
+	@$(MAKE) -sf Makefile-cert $@
 
 Makefile-cert: \
 conf-qmail conf-users conf-groups Makefile-cert.mk
diff -r 2ce477411646 -r 0a8afafb5d3e Makefile-cert.mk
--- a/Makefile-cert.mk	Wed Oct 29 11:34:37 2014 +0900
+++ b/Makefile-cert.mk	Wed Oct 29 17:27:18 2014 +0900
@@ -3,7 +3,7 @@
 	@:
 
 QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem
-	ln -s $< $@
+	ln -s QMAIL/control/servercert.pem $@
 
 QMAIL/control/servercert.pem:
 	PATH=$$PATH:/usr/local/ssl/bin \
diff -r 2ce477411646 -r 0a8afafb5d3e README.auth
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.auth	Wed Oct 29 17:27:18 2014 +0900
@@ -0,0 +1,58 @@
+README qmail-smtpd SMTP Authentication
+======================================
+
+
+History:
+--------
+
+This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch 
+which itself uses "Mrs. Brisby's" initial code. 
+Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing
+'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison.
+Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem
+(can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons.
+Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar.
+
+
+Scope:
+------
+
+This patch supports partly RFC 2554 "SMTP Service Extension for Authentication".
+For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html.
+
+
+Installation:
+-------------
+
+* Untar the source in the qmail-1.03 home direcotry.
+* Run ./install_auth.
+* Modify the compile time option "#define CRAM_MD5" to your needs.
+* Re-make qmail.
+
+
+Setup:
+------
+
+In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module'
+PAM to be called by qmail-smtpd; typically
+
+	/var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1
+
+Since qmail-smtpd does not run as root, checkpassword has to be made sticky.
+There is no need to include additionally the hostname in the call.
+In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information.
+
+
+Changes wrt. Krysztof Dabrowski's patch:
+----------------------------------------
+
+* Avoid the 'hostname' in the call of the PAM.
+* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5.
+* Doesn't close FD 2; thus not inhibiting logging to STDERR.
+* Fixed bugs in base64.c.
+* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'.
+
+
+Erwin Hoffmann - Cologne 2004-04-03 (www.fehcom.de)
+
+
diff -r 2ce477411646 -r 0a8afafb5d3e README.auth-ext
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.auth-ext	Wed Oct 29 17:27:18 2014 +0900
@@ -0,0 +1,88 @@
+=========================================================================
+	     README netqmail-1.06-smtpd SMTP-AUTH Extension
+	http://www.gentei.org/~yuuji/software/qmail-smtpd-auth-ext/
+=========================================================================
+
+
+Description:
+------------
+
+This patch is modified version of patches of Erwin Hoffmann - Hoehn's 
+qmail-smtpd SMTP Authentication 0.51(2010-02-08).
+This patch is modifed to be applied to the source of
+netqmail-1.06 + netqmail-1.06-tls-20110119.patch(*).
+
+(*) http://inoa.net/qmail-tls/ - Qmail-TLS patch
+
+
+Installation:
+-------------
+
+# tar zxpf netqmail-1.06.tar.gz
+# cd netqmail-1.06
+# cat <SomeWhere>/netqmail-1.06-tls-20110119.patch | patch -p2
+# cat <SomeWhere>/qmail-smtpd-auth-ext-5.diff | patch -p1
+
+And then, follow the instructions in the file netqmail-1.06-tls-20110119.patch
+and README.auth.
+
+Setup:
+------
+You should read document of TLS patch and README.auth.
+Here is a digest.
+
+* CERTS
+# cd /var/qmail/control
+# openssl req -new -x509 -nodes -out servercert.pem \
+	-keyout servercert.pem -days 3650
+# openssl ciphers tlsclientchiphers
+# openssl ciphers tlsserverchiphers
+
+* Invocation with daemontools
+Here is a typical `run' script.
+-----------------------------------------------
+#!/bin/sh
+exec env - \
+PATH=/var/qmail/bin:$PATH \
+AUTHTLS=1 \
+envuidgid qmaild softlimit -d3000000 \
+tcpserver -vR -p -c40 -U 0 submission qmail-smtpd cmd5apoppw /usr/bin/true 2>&1
+-----------------------------------------------
+
+The cmd5apoppw command is described in the section "cmd5*" below.
+AUTHTLS=1 inhibit plain text transport of password, that is
+client should activate TLS for auth-login.
+If the administrator doesn't care about plain text communication,
+omit AUTHTLS=1 line.  Note that if the system uses cmd5apoppw,
+password drain doesn't have DIRECT impact on system security
+because mail password is independent from system password.  Of course,
+it brings privacy breakage and has indirect impact on system security,
+in such case as that an user always exchange administrative information
+in plain-text email.  That is a matter of literacy but of internet 
+protocol.
+
+
+cmd5*:
+------
+You might want to use smtp-auth with qmail-smtpd.  You also need to install
+cmd5checkpw or cmd5apoppw(*2).
+
+(*2) http://www.gentei.org/~yuuji/software/qmapop-smtp-auth/
+
+We recommend to use cmd5apoppw program, by which users can manipulate
+mail password by themselves and can have separate password for each
+extensional mail address.
+
+* User mail password manipulation in cmd5apoppw schema
+All users' mail password should be stored into their own ~/.apop file.
+The ~/.apop password file can be created by `apoppasswd' command.
+  % apoppasswd
+    (New password twice)
+If an user want to use mail extension `user-ext', its password can
+be set by as follows:
+  % apoppasswd -e ext
+Password file for `user-ext' is ~/.apop-ext.
+
+
+-- 
+HIROSE, Yuuji yuuji_at_yatex.org 2014-10-29
diff -r 2ce477411646 -r 0a8afafb5d3e TARGETS
--- a/TARGETS	Wed Oct 29 11:34:37 2014 +0900
+++ b/TARGETS	Wed Oct 29 17:27:18 2014 +0900
@@ -10,6 +10,7 @@
 qmail.o
 quote.o
 now.o
+base64.o
 gfrom.o
 myctime.o
 slurpclose.o
diff -r 2ce477411646 -r 0a8afafb5d3e base64.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base64.c	Wed Oct 29 17:27:18 2014 +0900
@@ -0,0 +1,122 @@
+#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 p = 0;
+  int n;
+  unsigned int x;
+  int i, j;
+  char *s;
+  unsigned char b[3];
+
+  if (l == 0)
+  {
+    if (!stralloc_copys(out,"")) return -1;
+    return 0;
+  }
+
+  while(in[l-1] == B64PAD) {
+    p ++;
+    l--;
+  }
+
+  n = (l + p) / 4;
+  out->len = (n * 3) - p;
+  if (!stralloc_ready(out,out->len)) return -1;
+  s = out->s;
+
+  for(i = 0; i < n - 1 ; i++) {
+    x = 0;
+    for(j = 0; j < 4; j++) {
+      if(in[j] >= 'A' && in[j] <= 'Z')
+        x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+      else if(in[j] >= 'a' && in[j] <= 'z')
+        x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+      else if(in[j] >= '0' && in[j] <= '9')
+        x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+      else if(in[j] == '+')
+        x = (x << 6) + 62;
+      else if(in[j] == '/')
+        x = (x << 6) + 63;
+      else if(in[j] == '=')
+        x = (x << 6);
+    }
+
+    s[2] = (unsigned char)(x & 255); x >>= 8;
+    s[1] = (unsigned char)(x & 255); x >>= 8;
+    s[0] = (unsigned char)(x & 255); x >>= 8;
+    s += 3; in += 4;
+  }
+
+  x = 0;
+  for(j = 0; j < 4; j++) {
+    if(in[j] >= 'A' && in[j] <= 'Z')
+      x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+    else if(in[j] >= 'a' && in[j] <= 'z')
+      x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+    else if(in[j] >= '0' && in[j] <= '9')
+      x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+    else if(in[j] == '+')
+      x = (x << 6) + 62;
+    else if(in[j] == '/')
+      x = (x << 6) + 63;
+    else if(in[j] == '=')
+      x = (x << 6);
+  }
+
+  b[2] = (unsigned char)(x & 255); x >>= 8;
+  b[1] = (unsigned char)(x & 255); x >>= 8;
+  b[0] = (unsigned char)(x & 255); x >>= 8;
+
+  for(i = 0; i < 3 - p; i++)
+    s[i] = b[i];
+
+  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 -r 2ce477411646 -r 0a8afafb5d3e base64.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base64.h	Wed Oct 29 17:27:18 2014 +0900
@@ -0,0 +1,7 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+extern int b64decode();
+extern int b64encode();
+
+#endif
diff -r 2ce477411646 -r 0a8afafb5d3e install_auth.sh
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/install_auth.sh	Wed Oct 29 17:27:18 2014 +0900
@@ -0,0 +1,97 @@
+#!/bin/sh
+#
+# qmail-smtpd AUTH (UN)INSTALL Script (install_auth.sh)
+# -----------------------------------------------------
+#
+# Purpose:      To install and uninstall the qmail-smtpd Authentication Patch
+#
+# Parameters:   -u (uninstall)
+#	        VRF (Version to be uninstalled)
+#
+# Usage:        ./install_auth.sh [-u] [Version]
+#
+#		Installation: 	./install_auth.sh
+# 		Uninstallation: ./install_auth.sh -u 105
+#
+# Return Codes: 0 - Patches applied successfully
+#		1 - Original QMAIL files not found (Patch not extracted in QMAIL source directory)
+#		2 - Patch files not found 
+#
+# Output:	install_auth.log
+#
+# History:      1.0.0 - Erwin Hoffmann - Initial release
+#
+#---------------------------------------------------------------------------------------
+#
+DATE=$(date)
+LOCDIR=${PWD}
+QMAILHOME=$(head -1 conf-qmail)
+SOLARIS=$(sh ./find-systype.sh | grep -ciq "SunOS")
+LOGFILE=auth.log
+TARGETS=FILES.auth
+IFSKEEP=${IFS}
+REL=043 # Should be identical to qmail-smtpd AUTH level
+BUILD=2004094213621
+
+
+if [ $# -eq 0 ] ; then
+
+	echo "Installing qmail-smtpd AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1 
+
+	for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
+		echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
+		if [ -s ${FILE} ] ; then
+			cp ${FILE} ${FILE}.$REL | tee -a $LOGFILE 2>&1
+			echo "--> ${FILE} copied to ${FILE}.$REL" | tee -a $LOGFILE 2>&1
+		else
+			echo "${FILE} not found !"
+			exit 1
+		fi
+		if [ -s ${FILE}.patch ] ; then
+			if [ ${SOLARIS} -gt 0 ]; then
+				echo "--> Patching qmail source file ${FILE} for Solaris ...." | tee -a $LOGFILE 2>&1
+				patch -i ${FILE}.patch ${FILE} 2>&1 | tee -a $LOGFILE
+			else
+				echo "--> Patching qmail source file ${FILE}  ...." | tee -a $LOGFILE 2>&1
+				patch ${FILE} ${FILE}.patch 2>&1 | tee -a $LOGFILE
+			fi
+		else
+			echo "!! ${FILE}.patch not found !"
+			exit 2
+		fi
+	done 
+
+
+	echo "Copying documentation and samples to ${QMAILHOME}/doc/ ..." | tee -a $LOGFILE 2>&1 
+
+	cp -v README.auth* ${QMAILHOME}/doc/ | tee -a $LOGFILE 2>&1
+	echo ""
+	echo "If you dont wont CRAM-MD5 suport disable '#define CRAM_MD5' in qmail-smtpd !"
+	echo "Installation of qmail-smtpd AUTH $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1 
+
+# Now go for the uninstallation....
+
+elif [ "$1" = "-u" ] ; then
+
+# Get the Version Number from INPUT 
+
+	if [ $# -eq 2 ] ; then
+		REL=$2
+	fi
+
+	echo "De-installing qmail-smtpd AUTH $REL (Build $BUILD) at $DATE <<<" | tee -a $LOGFILE 2>&1 
+
+	for FILE in $(grep "^= " ${TARGETS} | awk '{print $2}'); do
+		echo "Targeting file $FILE ..." | tee -a $LOGFILE 2>&1
+		if [ -s ${FILE}.$REL ] ; then
+			mv ${FILE}.$REL ${FILE} | tee -a $LOGFILE 2>&1
+			touch ${FILE}
+			echo "--> ${FILE}.$REL moved to ${FILE}" | tee -a $LOGFILE 2>&1
+		else
+			echo "!! ${FILE}.$REL not found !"
+		fi
+	done
+	echo "De-installation of qmail-smtpd AUTH $REL (Build $BUILD) finished at $DATE <<<" | tee -a $LOGFILE 2>&1 
+fi
+
+exit 0
diff -r 2ce477411646 -r 0a8afafb5d3e qmail-smtpd.8
--- a/qmail-smtpd.8	Wed Oct 29 11:34:37 2014 +0900
+++ b/qmail-smtpd.8	Wed Oct 29 17:27:18 2014 +0900
@@ -32,7 +32,29 @@
 header fields.
 
 .B qmail-smtpd
-supports ESMTP, including the 8BITMIME and PIPELINING options.
+supports ESMTP, including the 8BITMIME, DATA, 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 CRAM-MD5 digest/response derived from the SMTP client,
+another 0 byte, a CRAM-MD5 challenge (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
diff -r 2ce477411646 -r 0a8afafb5d3e qmail-smtpd.c
--- a/qmail-smtpd.c	Wed Oct 29 11:34:37 2014 +0900
+++ b/qmail-smtpd.c	Wed Oct 29 17:27:18 2014 +0900
@@ -23,6 +23,10 @@
 #include "timeoutread.h"
 #include "timeoutwrite.h"
 #include "commands.h"
+#include "wait.h"
+
+#define CRAM_MD5
+#define AUTHSLEEP 5
 
 #define MAXHOPS 100
 unsigned int databytes = 0;
@@ -86,6 +90,16 @@
 void err_vrfy(arg) char *arg; { 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 canceled (#5.0.0)\r\n"); return -1; }
+int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
+void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); }
 
 stralloc greeting = {0};
 
@@ -273,7 +287,12 @@
   if (!ssl && (stat("control/servercert.pem",&st) == 0))
     out("\r\n250-STARTTLS");
 #endif
-  out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
+  out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
+#ifdef CRAM_MD5
+  out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n");
+#else
+  out("250 AUTH LOGIN PLAIN\r\n");
+#endif
   seenmail = 0; dohelo(arg);
 }
 void smtp_rset(arg) char *arg;
@@ -680,10 +699,240 @@
 
 #endif
 
+/* this file is too long ----------------------------------------- SMTP AUTH */
+
+char unique[FMT_ULONG + FMT_ULONG + 3];
+static stralloc authin = {0};   /* input from SMTP client */
+static stralloc user = {0};     /* plain userid */
+static stralloc pass = {0};     /* plain passwd or digest */
+static stralloc resp = {0};     /* b64 response */
+#ifdef CRAM_MD5
+static stralloc chal = {0};     /* plain challenge */
+static stralloc slop = {0};     /* b64 challenge */
+#endif
+
+int flagauth;
+
+char **childargs;
+char ssauthbuf[512];
+substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf));
+
+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();
+#ifdef CRAM_MD5
+  if (!stralloc_0(&chal)) die_nomem();
+#endif
+
+  if (pipe(pi) == -1) return err_pipe();
+  switch(child = fork()) {
+    case -1:
+      return err_fork();
+    case 0:
+      close(pi[1]);
+      dup2(pi[0],3);
+      sig_pipedefault();
+        execvp(*childargs, childargs);
+      _exit(1);
+  }
+  close(pi[0]);
+
+  substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
+  if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
+  if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
+#ifdef CRAM_MD5
+  if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
+#endif
+  if (substdio_flush(&ssauth) == -1) return err_write();
+
+  close(pi[1]);
+#ifdef CRAM_MD5
+  byte_zero(chal.s,chal.len);
+  byte_zero(slop.s,slop.len);
+#endif
+  byte_zero(ssauthbuf,sizeof ssauthbuf);
+  if (wait_pid(&wstat,child) == -1) return err_child();
+  if (wait_crashed(wstat)) return err_child();
+  if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */
+  return 0; /* yes */
+}
+
+int auth_login(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),&resp) == 1) return err_input();
+  }
+  else {
+    out("334 \r\n"); flush();
+    if (authgetl() < 0) return -1;
+    if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
+  }
+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
+  while (resp.s[id]) id++;                       /* "authorize-id\0userid\0passwd\0" */
+
+  if (resp.len > id + 1)
+    if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem();
+  if (resp.len > id + user.len + 2)
+    if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem();
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+
+#ifdef CRAM_MD5
+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(&chal,"<")) die_nomem();          /* generate challenge */
+  if (!stralloc_cats(&chal,unique)) die_nomem();
+  if (!stralloc_cats(&chal,local)) die_nomem();
+  if (!stralloc_cats(&chal,">")) die_nomem();
+  if (b64encode(&chal,&slop) < 0) die_nomem();
+  if (!stralloc_0(&slop)) die_nomem();
+
+  out("334 ");                                          /* "334 mychallenge \r\n" */
+  out(slop.s);
+  out("\r\n");
+  flush();
+
+  if (authgetl() < 0) return -1;                        /* got response */
+  if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
+
+  i = str_chr(resp.s,' ');
+  s = resp.s + i;
+  while (*s == ' ') ++s;
+  resp.s[i] = 0;
+  if (!stralloc_copys(&user,resp.s)) die_nomem();       /* userid */
+  if (!stralloc_copys(&pass,s)) die_nomem();            /* digest */
+
+  if (!user.len || !pass.len) return err_input();
+  return authenticate();
+}
+#endif
+
+struct authcmd {
+  char *text;
+  int (*fun)();
+} authcmds[] = {
+  { "login",auth_login }
+,  { "plain",auth_plain }
+#ifdef CRAM_MD5
+, { "cram-md5",auth_cram }
+#endif
+, { 0,err_noauth }
+};
+
+void smtp_auth(arg)
+char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; }
+  if (env_get("AUTHTLS") && !str_diff(protocol, "SMTP")) {
+    out("503 need TLS for AUTH (#5.5.1)\r\n");
+    return;
+  }
+  if (flagauth) { 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();
+#ifdef CRAM_MD5
+  if (!stralloc_copys(&chal,"")) die_nomem();
+#endif
+
+  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:
+      flagauth = 1;
+      relayclient = "";
+      remoteinfo = user.s;
+      if (!env_unset("TCPREMOTEINFO")) die_read();
+      if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
+      if (!env_put2("RELAYCLIENT",relayclient)) die_nomem();
+      out("235 ok, go ahead (#2.0.0)\r\n");
+      break;
+    case 1:
+      err_authfail(user.s,authcmds[i].text);
+  }
+}
+
+/* this file is too long --------------------------------------------- GO ON */
+
 struct commands smtpcommands[] = {
   { "rcpt", smtp_rcpt, 0 }
 , { "mail", smtp_mail, 0 }
 , { "data", smtp_data, flush }
+, { "auth", smtp_auth, flush }
 , { "quit", smtp_quit, flush }
 , { "helo", smtp_helo, flush }
 , { "ehlo", smtp_ehlo, flush }
@@ -697,8 +946,11 @@
 , { 0, err_unimpl, flush }
 } ;
 
-void main()
+void main(argc,argv)
+int argc;
+char **argv;
 {
+  childargs = argv + 1;
   sig_pipeignore();
   if (chdir(auto_qmail) == -1) die_control();
   setup();