*** 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();