*** Makefile.orig	Mon Jun 15 05:53:16 1998
--- Makefile	Fri May 10 00:31:38 2002
***************
*** 136,141 ****
--- 136,145 ----
  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 \
***************
*** 1536,1547 ****
  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
  	./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`
  
  qmail-smtpd.0: \
--- 1540,1551 ----
  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 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 base64.o  `cat \
  	socket.lib`
  
  qmail-smtpd.0: \
***************
*** 1553,1559 ****
  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
  	./compile qmail-smtpd.c
  
  qmail-start: \
--- 1557,1564 ----
  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 wait.h \
! fd.h base64.h
  	./compile qmail-smtpd.c
  
  qmail-start: \


*** TARGETS.orig	Mon Jun 15 05:53:16 1998
--- TARGETS	Fri May 10 00:31:38 2002
***************
*** 250,255 ****
--- 250,256 ----
  qmail-qmtpd.o
  rcpthosts.o
  qmail-qmtpd
+ base64.o
  qmail-smtpd.o
  qmail-smtpd
  sendmail.o


*** qmail-smtpd.8.orig	Mon Jun 15 05:53:16 1998
--- qmail-smtpd.8	Fri May 10 00:31:38 2002
***************
*** 3,8 ****
--- 3,13 ----
  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,29 ****
  header fields.
  
  .B qmail-smtpd
! supports ESMTP, including the 8BITMIME and PIPELINING options.
  .SH TRANSPARENCY
  .B qmail-smtpd
  converts the SMTP newline convention into the UNIX newline convention
--- 28,56 ----
  header fields.
  
  .B qmail-smtpd
! 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,179 ****
--- 204,209 ----
  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.


*** qmail-smtpd.c.orig	Mon Jun 15 05:53:16 1998
--- qmail-smtpd.c	Fri May 10 00:33:35 2002
***************
*** 23,29 ****
--- 23,32 ----
  #include "timeoutread.h"
  #include "timeoutwrite.h"
  #include "commands.h"
+ #include "wait.h"
+ #include "fd.h"
  
+ #define AUTHCRAM
  #define MAXHOPS 100
  unsigned int databytes = 0;
  int timeout = 1200;
***************
*** 59,64 ****
--- 62,76 ----
  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};
  
***************
*** 229,235 ****
  }
  void smtp_ehlo(arg) char *arg;
  {
!   smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
    seenmail = 0; dohelo(arg);
  }
  void smtp_rset()
--- 241,255 ----
  }
  void smtp_ehlo(arg) char *arg;
  {
!   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
!   out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
    seenmail = 0; dohelo(arg);
  }
  void smtp_rset()
***************
*** 394,403 ****
--- 414,639 ----
    out("\r\n");
  }
  
+ 
+ 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 }
***************
*** 408,415 ****
  , { 0, err_unimpl, flush }
  } ;
  
! void main()
  {
    sig_pipeignore();
    if (chdir(auto_qmail) == -1) die_control();
    setup();
--- 644,656 ----
  , { 0, err_unimpl, flush }
  } ;
  
! void main(argc,argv)
! int argc;
! char **argv;
  {
+   hostname = argv[1];
+   childargs = argv + 2;
+ 
    sig_pipeignore();
    if (chdir(auto_qmail) == -1) die_control();
    setup();
