Matt Simerson 3/26/2001 mpsimerson@hostpro.com This patch is merely the combination of other patches. First, to closely resemble the FreeBSD qmail port, we include the dns (AOL) patch to accept >512 dns entries. For the same reason, we also change qmails nofiles group to qnofiles. On top of that we install the big-concurrency.patch (see README.big-concurrency) which raises the number of simultaneous outgoing connections we can make. We also add Chris Johnson's smtp tarpitting patch (see README.tarpit) and finally Eric Johnson's SMTP-AUTH patch (see the README.auth or 'man qmail-smtpd' after installing). *** Makefile.orig Mon Jun 15 03:53:16 1998 --- Makefile Mon Mar 26 16:51:47 2001 *************** *** 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 03:53:16 1998 --- TARGETS Mon Mar 26 16:51:47 2001 *************** *** 250,255 **** --- 250,256 ---- qmail-qmtpd.o rcpthosts.o qmail-qmtpd + base64.o qmail-smtpd.o qmail-smtpd sendmail.o *** dns.c.orig Mon Jun 15 03:53:16 1998 --- dns.c Mon Mar 26 16:51:40 2001 *************** *** 21,30 **** static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } ! static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response; static int responselen; static unsigned char *responseend; static unsigned char *responsepos; static int numanswers; static char name[MAXDNAME]; --- 21,32 ---- static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } ! static struct { unsigned char *buf; } response; ! static int responsebuflen = 0; static int responselen; static unsigned char *responseend; static unsigned char *responsepos; + static u_long saveresoptions; static int numanswers; static char name[MAXDNAME]; *************** *** 45,62 **** errno = 0; if (!stralloc_copy(&glue,domain)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; ! responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response)); if (responselen <= 0) { if (errno == ECONNREFUSED) return DNS_SOFT; if (h_errno == TRY_AGAIN) return DNS_SOFT; return DNS_HARD; } - if (responselen >= sizeof(response)) - responselen = sizeof(response); responseend = response.buf + responselen; responsepos = response.buf + sizeof(HEADER); ! n = ntohs(response.hdr.qdcount); while (n-- > 0) { i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); --- 47,79 ---- errno = 0; if (!stralloc_copy(&glue,domain)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; ! if (!responsebuflen) ! if (response.buf = (unsigned char *)alloc(PACKETSZ+1)) ! responsebuflen = PACKETSZ+1; ! else return DNS_MEM; ! ! responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); ! if ((responselen >= responsebuflen) || ! (responselen > 0 && (((HEADER *)response.buf)->tc))) ! { ! if (responsebuflen < 65536) ! if (alloc_re(&response.buf, responsebuflen, 65536)) ! responsebuflen = 65536; ! else return DNS_MEM; ! saveresoptions = _res.options; ! _res.options |= RES_USEVC; ! responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); ! _res.options = saveresoptions; ! } if (responselen <= 0) { if (errno == ECONNREFUSED) return DNS_SOFT; if (h_errno == TRY_AGAIN) return DNS_SOFT; return DNS_HARD; } responseend = response.buf + responselen; responsepos = response.buf + sizeof(HEADER); ! n = ntohs(((HEADER *)response.buf)->qdcount); while (n-- > 0) { i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); *************** *** 66,72 **** if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } ! numanswers = ntohs(response.hdr.ancount); return 0; } --- 83,89 ---- if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } ! numanswers = ntohs(((HEADER *)response.buf)->ancount); return 0; } *** qmail-smtpd.c.orig Mon Jun 15 03:53:16 1998 --- qmail-smtpd.c Mon Mar 26 17:14:29 2001 *************** *** 23,28 **** --- 23,30 ---- #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" + #include "wait.h" + #include "fd.h" #define MAXHOPS 100 unsigned int databytes = 0; *************** *** 59,64 **** --- 61,75 ---- 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}; *************** *** 96,101 **** --- 107,114 ---- int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; + int tarpitcount = 0; + int tarpitdelay = 5; void setup() { *************** *** 110,115 **** --- 123,136 ---- if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; + if (control_readint(&tarpitcount,"control/tarpitcount") == -1) die_control(); + if (tarpitcount < 0) tarpitcount = 0; + x = env_get("TARPITCOUNT"); + if (x) { scan_ulong(x,&u); tarpitcount = u; }; + if (control_readint(&tarpitdelay,"control/tarpitdelay") == -1) die_control(); + if (tarpitdelay < 0) tarpitdelay = 0; + x = env_get("TARPITDELAY"); + if (x) { scan_ulong(x,&u); tarpitdelay = u; }; if (rcpthosts_init() == -1) die_control(); bmfok = control_readfile(&bmf,"control/badmailfrom",0); *************** *** 221,226 **** --- 242,248 ---- int flagbarf; /* defined if seenmail */ stralloc mailfrom = {0}; stralloc rcptto = {0}; + int rcptcount; void smtp_helo(arg) char *arg; { *************** *** 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() --- 251,260 ---- } void smtp_ehlo(arg) char *arg; { ! smtp_greet("250-"); ! out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); ! out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); ! out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() *************** *** 245,250 **** --- 270,276 ---- if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); if (!stralloc_0(&mailfrom)) die_nomem(); + rcptcount = 0; out("250 ok\r\n"); } void smtp_rcpt(arg) char *arg; { *************** *** 261,266 **** --- 287,293 ---- if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); + if (tarpitcount && ++rcptcount >= tarpitcount) while (sleep(tarpitdelay)); out("250 ok\r\n"); } *************** *** 394,403 **** --- 421,640 ---- 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 ok, go on\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(); + } + + 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(); + } + + struct authcmd { + char *text; + int (*fun)(); + } authcmds[] = { + { "login", auth_login } + , { "plain", auth_plain } + , { "cram-md5", auth_cram } + , { 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; + 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(); --- 645,657 ---- , { 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(); *** qmail-smtpd.8.orig Mon Jun 15 03:53:16 1998 --- qmail-smtpd.8 Mon Mar 26 17:20:20 2001 *************** *** 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. *** base64.c.orig Mon Mar 26 17:31:42 2001 --- base64.c Mon Mar 26 17:30:34 2001 *************** *** 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; + } *** base64.h.orig Mon Mar 26 17:31:45 2001 --- base64.h Mon Mar 26 17:30:37 2001 *************** *** 0 **** --- 1,7 ---- + #ifndef BASE64_H + #define BASE64_H + + extern int b64decode(); + extern int b64encode(); + + #endif *** conf-groups.orig Mon Mar 26 17:36:43 2001 --- conf-groups Mon Mar 26 17:36:52 2001 *************** *** 1,5 **** qmail ! nofiles These are the qmail groups. The second group should not have access to any files, but it must be usable for processes; this requirement --- 1,5 ---- qmail ! qnofiles These are the qmail groups. The second group should not have access to any files, but it must be usable for processes; this requirement *** README.auth.orig Mon Mar 26 17:43:28 2001 --- README.auth Mon Mar 26 17:43:14 2001 *************** *** 0 **** --- 1,90 ---- + qmail-smtpd AUTH patch + 20010105 + Eric M. Johnston, emj@postal.net + + 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 checks out, 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 effectively set to the + authenticated username, overriding any value that tcpserver may have + set. The value of TCPREMOTEINFO is reflected in a Received header. + + + 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). + + You may be interested in Krzysztof Dabrowski's cmd5checkpw tool at + http://www.elysium.pl/members/brush/cmd5checkpw/ which supports the + three AUTH types included in this patch. + + This patch has been generated against the stock qmail 1.03 + distribution. The results of combining this patch with others are + unknown. + + + Compatibility + + MUAs and AUTH types tested with this software are: + + Netscape Communicator 4.76 - LOGIN & PLAIN + Microsoft Outlook 2000 - LOGIN + Microsoft Outlook Express 5 - LOGIN + Eudora 5.0.2 - CRAM-MD5 + + Please report bugs, incompatibilities, successes, etc. to + emj@postal.net. The latest version of this software will be posted + (Webmaster willing) at http://www.qmail.org/. + + + 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. + + + Changes + + 20010105: initial release + + --- + + 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. *** README.tarpit.orig Mon Mar 26 18:16:18 2001 --- README.tarpit Mon Mar 26 18:16:10 2001 *************** *** 0 **** --- 1,25 ---- + Chris Johnson + cjohnson-qmail@palomine.net + + What's tarpitting? It's the practice of inserting a small sleep in an SMTP + session for each RCPT TO after some set number of RCPT TOs. The idea is to + thwart spammers who would hand your SMTP server a single message with a long + list of RCPT TOs. If a spammer were to attempt to use your server to relay a + message with, say, 10,000 recipients, and you inserted a five-second delay for + each recipient after the fiftieth, the spammer would be "tarpitted" and would + likely assume that his connection had stalled and give up. + + The subject originally came up in a discussion on the qmail mailing list of + ways to run an open relay safely (I didn't suggest it, and I don't do that kind + of thing), but it could also be useful in keeping your own dial-up customers + from using you as a spam relay. + + This patch will allow qmail-smtpd to do tarpitting. There are two control files + involved: control/tarpitcount and control/tarpitdelay. tarpitcount is the + number of RCPT TOs you accept before you start tarpitting, and tarpitdelay is + the number of seconds of delay to introduce after each subsequent RCPT TO. + tarpitcount defaults to 0 (which means no tarpitting), and tarpitdelay defaults + to 5. You can override both tarpitcount and tarpitdelay by setting TARPITCOUNT + and TARPITDELAY in qmail-smtpd's environment (with tcpserver). If you used the + earlier version of this patch, note that this version no longer uses the + NOTARPIT environment variable; set TARPITCOUNT to 0 to achieve the same effect. *** chkspawn.c.orig Mon Jun 15 03:53:16 1998 --- chkspawn.c Mon Mar 26 18:45:04 2001 *************** *** 22,29 **** _exit(1); } ! if (auto_spawn > 255) { ! substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n"); substdio_flush(subfderr); _exit(1); } --- 22,29 ---- _exit(1); } ! if (auto_spawn > 65000) { ! substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n"); substdio_flush(subfderr); _exit(1); } *** conf-spawn.orig Mon Jun 15 03:53:16 1998 --- conf-spawn Mon Mar 26 18:45:04 2001 *************** *** 1,4 **** ! 120 This is a silent concurrency limit. You can't set it above 255. On some systems you can't set it above 125. qmail will refuse to compile if the --- 1,4 ---- ! 400 This is a silent concurrency limit. You can't set it above 255. On some systems you can't set it above 125. qmail will refuse to compile if the *** conf-split.orig Mon Mar 26 19:03:31 2001 --- conf-split Mon Mar 26 19:03:48 2001 *************** *** 1,3 **** ! 23 This is the queue subdirectory split. --- 1,3 ---- ! 239 This is the queue subdirectory split. *** qmail-send.c.orig Mon Jun 15 03:53:16 1998 --- qmail-send.c Mon Mar 26 18:59:31 2001 *************** *** 262,267 **** --- 262,269 ---- while (!stralloc_copys(&comm_buf[c],"")) nomem(); ch = delnum; while (!stralloc_append(&comm_buf[c],&ch)) nomem(); + ch = delnum >> 8; + while (!stralloc_append(&comm_buf[c],&ch)) nomem(); fnmake_split(id); while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); while (!stralloc_0(&comm_buf[c])) nomem(); *************** *** 906,946 **** dline[c].len = REPORTMAX; /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ /* but from a security point of view, we don't trust rspawn */ ! if (!ch && (dline[c].len > 1)) { delnum = (unsigned int) (unsigned char) dline[c].s[0]; if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) log1("warning: internal error: delivery report out of range\n"); else { strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; ! if (dline[c].s[1] == 'Z') if (jo[d[c][delnum].j].flagdying) { ! dline[c].s[1] = 'D'; --dline[c].len; while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); while (!stralloc_0(&dline[c])) nomem(); } ! switch(dline[c].s[1]) { case 'K': log3("delivery ",strnum3,": success: "); ! logsafe(dline[c].s + 2); log1("\n"); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; case 'Z': log3("delivery ",strnum3,": deferral: "); ! logsafe(dline[c].s + 2); log1("\n"); break; case 'D': log3("delivery ",strnum3,": failure: "); ! logsafe(dline[c].s + 2); log1("\n"); ! addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; --- 908,949 ---- dline[c].len = REPORTMAX; /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ /* but from a security point of view, we don't trust rspawn */ ! if (!ch && (dline[c].len > 2)) { delnum = (unsigned int) (unsigned char) dline[c].s[0]; + delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8; if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) log1("warning: internal error: delivery report out of range\n"); else { strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; ! if (dline[c].s[2] == 'Z') if (jo[d[c][delnum].j].flagdying) { ! dline[c].s[2] = 'D'; --dline[c].len; while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); while (!stralloc_0(&dline[c])) nomem(); } ! switch(dline[c].s[2]) { case 'K': log3("delivery ",strnum3,": success: "); ! logsafe(dline[c].s + 3); log1("\n"); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; case 'Z': log3("delivery ",strnum3,": deferral: "); ! logsafe(dline[c].s + 3); log1("\n"); break; case 'D': log3("delivery ",strnum3,": failure: "); ! logsafe(dline[c].s + 3); log1("\n"); ! addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; *************** *** 1544,1550 **** numjobs = 0; for (c = 0;c < CHANNELS;++c) { ! char ch; int u; int r; do --- 1547,1553 ---- numjobs = 0; for (c = 0;c < CHANNELS;++c) { ! char ch, ch1; int u; int r; do *************** *** 1552,1558 **** --- 1555,1567 ---- while ((r == -1) && (errno == error_intr)); if (r < 1) { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } + do + r = read(chanfdin[c],&ch1,1); + while ((r == -1) && (errno == error_intr)); + if (r < 1) + { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } u = (unsigned int) (unsigned char) ch; + u += (unsigned int) ((unsigned char) ch1) << 8; if (concurrency[c] > u) concurrency[c] = u; numjobs += concurrency[c]; } *** spawn.c.orig Mon Jun 15 03:53:16 1998 --- spawn.c Mon Mar 26 18:52:33 2001 *************** *** 63,69 **** int flagreading = 1; char outbuf[1024]; substdio ssout; ! int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ int flagabort = 0; /* if 1, everything except delnum is garbage */ int delnum; stralloc messid = {0}; --- 63,69 ---- int flagreading = 1; char outbuf[1024]; substdio ssout; ! int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */ int flagabort = 0; /* if 1, everything except delnum is garbage */ int delnum; stralloc messid = {0}; *************** *** 73,78 **** --- 73,79 ---- void err(s) char *s; { char ch; ch = delnum; substdio_put(&ssout,&ch,1); + ch = delnum >> 8; substdio_put(&ssout,&ch,1); substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1); } *************** *** 155,170 **** { case 0: delnum = (unsigned int) (unsigned char) ch; ! messid.len = 0; stage = 1; break; case 1: if (!stralloc_append(&messid,&ch)) flagabort = 1; if (ch) break; ! sender.len = 0; stage = 2; break; ! case 2: if (!stralloc_append(&sender,&ch)) flagabort = 1; if (ch) break; ! recip.len = 0; stage = 3; break; ! case 3: if (!stralloc_append(&recip,&ch)) flagabort = 1; if (ch) break; docmd(); --- 156,174 ---- { case 0: delnum = (unsigned int) (unsigned char) ch; ! stage = 1; break; case 1: + delnum += (unsigned int) ((unsigned int) ch) << 8; + messid.len = 0; stage = 2; break; + case 2: if (!stralloc_append(&messid,&ch)) flagabort = 1; if (ch) break; ! sender.len = 0; stage = 3; break; ! case 3: if (!stralloc_append(&sender,&ch)) flagabort = 1; if (ch) break; ! recip.len = 0; stage = 4; break; ! case 4: if (!stralloc_append(&recip,&ch)) flagabort = 1; if (ch) break; docmd(); *************** *** 201,207 **** initialize(argc,argv); ! ch = auto_spawn; substdio_putflush(&ssout,&ch,1); for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } --- 205,212 ---- initialize(argc,argv); ! ch = auto_spawn; substdio_put(&ssout,&ch,1); ! ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1); for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } *************** *** 236,242 **** continue; /* read error on a readable pipe? be serious */ if (r == 0) { ! ch = i; substdio_put(&ssout,&ch,1); report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len); substdio_put(&ssout,"",1); substdio_flush(&ssout); --- 241,248 ---- continue; /* read error on a readable pipe? be serious */ if (r == 0) { ! char ch; ch = i; substdio_put(&ssout,&ch,1); ! ch = i >> 8; substdio_put(&ssout,&ch,1); report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len); substdio_put(&ssout,"",1); substdio_flush(&ssout); *** README.big-concurrency.orig Mon Mar 26 19:21:26 2001 --- README.big-concurrency Mon Mar 26 19:21:15 2001 *************** *** 0 **** --- 1,73 ---- + From: "Johannes Erdfelt" + To: qmail@list.cr.yp.to + Subject: Re: mail volume + Date: 4 Aug 1999 20:41:00 -0700 + + On Thu, Aug 05, 1999, richard@illuin.demon.co.uk wrote: + > On Wed, 4 Aug 1999, Daemeon Reiydelle wrote: + > + > > (2.6 or later). There may be limitations within e.g. qmail-[lr]spawn + > > about how many children it can manage. I am not working with that code + > > right know so I don't know. Anyone? + > + > This is what people have been trying to say -- the protocol between + > qmail-Xspawn and qmail-send only passes a single byte for the delivery + > attempt back in the status messages. if you want to increase the maximum + > number above 256 one has to modify qmail-send and the common code in + > qmail-Xspawn. making it a short should allow up to 2**16 concurrency + > remotes. + > + > **CAUTION** if you do this one should realise that qmail-send might try to + > open 64K connections to the /same/ host because it doesn't maintain a + > per-domain concurrency. this is distinctly Unfriendly. I produced some + > code for qmail to do this, but when I asked my ISP if i could open >>1024 + > connections to one of their mail relays for testing they were less than + > enthusiastic... (the code is on my desktop system somewhere between here + > and Austin where I'm moving to next week, so I can't email it, and without + > testing it I won't email it. the changes to up the concurrency are fairly + > straightforward, the once for a per-domain concurrency are non-trivial) + + This is the patch that I use at suse.com. We do almost 1 million + messages a day with this patch and concurrencyremote set to 400. + + This patch comes with the standard disclaimer. No warranty, it may not + work, etc. But it works for me :) + + It's also not pretty. It's against qmail-1.03+verh-0.02 (the ezmlm patch + l and h patch). So the offsets may be off a little bit. + + JE + + + From: "Fred Lindberg" + To: "nelson@crynwr.com" + Cc: "qmail@list.cr.yp.to" + Subject: Solved: qmail-bigrem limits linux + Date: Thu, 12 Aug 1999 13:41:18 -0500 + + Dear Russell, + + Thanks for your input. The limiting factor for my redhat linux 2.2.5 + installation was a per user process limit of 256. The reason I + sometimes got 256 or 257 concurrency is that reporting is not exactly + synchronous with forking. + + Thus, in addition to increasing the number of file handles, you need to + rebuild the kernel after editing: + + /usr/src/linux/include/linux/tasks.h NR_TASKS from 512 to e.g. 2048. + Per-user limit defined to half this on the line below. + + There also appears to be a bug that limits the number of per process + file handles to 1024. There are "unofficial" patches for this, but + AFAIK, not integrated into the official kernel. + + If anyone has more insight into this, I'd love to hear it. With the + patch, our P100/64MB does very well at a concurrencyremote of 400. + Since performance was limited by concurrency (many slow/dead clients) + we got considerably better throughput. + + + -Sincerely, Fred + + (Frederik Lindberg, Infectious Diseases, WashU, St. Louis, MO, USA)