*** ../qmail-1.03/Makefile Mon Jun 15 06:53:16 1998 --- Makefile Tue Dec 4 12:32:44 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 \ *************** *** 1446,1452 **** 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` qmail-remote.0: \ qmail-remote.8 --- 1450,1457 ---- 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` \ ! -L/usr/lib -lssl -lcrypto qmail-remote.0: \ qmail-remote.8 *************** *** 1536,1548 **** 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: \ qmail-smtpd.8 --- 1541,1553 ---- 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` -L/usr/lib -lssl -lcrypto qmail-smtpd.0: \ qmail-smtpd.8 *************** *** 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: \ --- 1558,1565 ---- 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: \ *************** *** 2139,2141 **** --- 2145,2166 ---- wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + + cert: + /usr/bin/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: + /usr/bin/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" *** README.auth.orig Mon Dec 3 22:43:19 2001 --- README.auth Mon Dec 3 22:46:26 2001 *************** *** 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 . + + Beginning with version 0.30, the patch was completely rewritten to + use only djb's string functions by Eric M. Johnston . + + 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 effectively 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 , + 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 . + + --- + + 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.big-concurrency.orig Mon Dec 3 22:43:27 2001 --- README.big-concurrency Mon Dec 3 22:43:27 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) *** README.tarpit.orig Mon Dec 3 22:43:33 2001 --- README.tarpit Mon Dec 3 22:43:33 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. *** README.tls.orig Tue Dec 4 15:31:48 2001 --- README.tls Tue Dec 4 15:31:35 2001 *************** *** 0 **** --- 1,89 ---- + Frederik Vermeulen 20010627 + http://www.esat.kuleuven.ac.be/~vermeule/qmail/tls.patch + + This patch implements RFC2487 in qmail. This means you can + get SSL or TLS encrypted and authenticated SMTP between + the MTAs and between MTA and an MUA like Netscape. + The code is considered experimental (but has worked for + many since its first release on 1999-03-21). + + Usage: - install OpenSSL-0.9.6a http://www.openssl.org/ + - apply patch to qmail-1.03 http://www.qmail.org/ + Makefile and conf-cc were patched for appropriate + linking. Apart from that, the patches to qmail-remote.c + and qmail-smtpd.c can be applied separately. + - provide a server certificate in /var/qmail/control/servercert.pem. + "make cert" makes a self-signed certificate. + "make cert-req" makes a certificate request. + Note: you can add the CA certificate and intermediate + certs to the end of servercert.pem. + - replace qmail-smtpd and/or qmail-remote binary + - verify operation (header information should show + somothing like + "Received [..] with DES-CBC3-SHA encrypted SMTP;") + If you don't have a server to test with, you can test + by sending mail to ping@mail.linux.student.kuleuven.ac.be, + which will bounce your mail. + + Optional: - when DEBUG is defined, some extra TLS info will be logged + - qmail-remote will authenticate with the certificate in + /var/qmail/control/clientcert.pem. By preference this is + the same as servercert.pem, where nsCertType should be + == server,client or be a generic certificate (no usage specified). + - when a 512 RSA key is provided in /var/qmail/control/rsa512.pem, + this key will be used instead of on-the-fly generation by + qmail-smtpd. Periodical replacement can be done by crontab: + 01 01 * * * umask 0077; /usr/local/ssl/bin/openssl genrsa \ + -out /var/qmail/control/rsa512.new 512 > /dev/null 2>&1;\ + chmod 600 /var/qmail/control/rsa512.new; chown qmaild.qmail \ + /var/qmail/control/rsa512.new; /bin/mv -f \ + /var/qmail/control/rsa512.new /var/qmail/control/rsa512.pem + - server authentication: + qmail-remote requires authentication from servers for which + /var/qmail/control/tlshosts/host.dom.ain.pem exists. + The .pem file contains the validating CA certificates + (or self-signed server certificate with openssl-0.9.5). + CommonName has to match. + WARNING: this option may cause mail to be delayed, bounced, + doublebounced, and lost. + - client authentication: + when relay rules would reject an incoming mail, + qmail-smtpd can allow the mail based on a presented cert. + Certs are verified against a CA list in + /var/qmail/control/clientca.pem (eg. http://www.modssl.org/ + source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt) + and the cert email-address has to match a line in + /var/qmail/control/tlsclients. This email-address is logged + in the headers. + - cipher selection: + qmail-remote: + openssl cipher string read from + /var/qmail/control/tlsclientciphers + qmail-smtpd: + openssl cipher string read from TLSCIPHERS environment variable + (can vary based on client IP address e.g.) + or if that is not available /var/qmail/control/tlsserverciphers + + Caveats: - from this version on the server cert is in servercert.pem. + - binaries dynamically linked with current openssl versions need + recompilation when the shared openssl libs are upgraded. + - this patch could conflict with other patches (notably those + replacing \n with \r\n, which is a bad idea on encrypted links). + - some broken servers have a problem with TLSv1 compatibility. + Uncomment the line where we set the SSL_OP_NO_TLSv1 option. + - needs working /dev/urandom for seeding random number generator. + + Copyright: GPL + Links with OpenSSL + Inspiration and code from examples in SSLeay (E. Young + and T. Hudson ), + stunnel (M. Trojnara ), + Postfix/TLS (L. Jaenicke ), + and modssl (R. Engelschall ). + Debug code, tlscipher selection, many feature suggestions, + French docs https://www.TBS-internet.com/ssl/qmail-tls.html + from Jean-Philippe Donnio + Openssl usage consulting from Bodo M"oller + Bug report from Andy Dustman + + Bug reports: mailto: *** ../qmail-1.03/TARGETS Mon Jun 15 06:53:16 1998 --- TARGETS Mon Dec 3 21:47:09 2001 *************** *** 250,255 **** --- 250,256 ---- qmail-qmtpd.o rcpthosts.o qmail-qmtpd + base64.o qmail-smtpd.o qmail-smtpd sendmail.o *** base64.c.orig Mon Dec 3 21:47:10 2001 --- base64.c Mon Dec 3 21:47:10 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 Dec 3 21:47:10 2001 --- base64.h Mon Dec 3 21:47:10 2001 *************** *** 0 **** --- 1,7 ---- + #ifndef BASE64_H + #define BASE64_H + + extern int b64decode(); + extern int b64encode(); + + #endif *** ../qmail-1.03/chkspawn.c Mon Jun 15 06:53:16 1998 --- chkspawn.c Mon Dec 3 21:47:10 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); } *** ../qmail-1.03/conf-cc Mon Jun 15 06:53:16 1998 --- conf-cc Tue Dec 4 12:41:30 2001 *************** *** 1,3 **** ! cc -O2 This will be used to compile .c files. --- 1,3 ---- ! cc -O2 -DTLS -I/usr/include/openssl This will be used to compile .c files. *** ../qmail-1.03/conf-groups Mon Jun 15 06:53:16 1998 --- conf-groups Mon Dec 3 21:47:10 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 *** ../qmail-1.03/conf-spawn Mon Jun 15 06:53:16 1998 --- conf-spawn Mon Dec 3 22:09:39 2001 *************** *** 1,5 **** ! 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 ! limit is too high. --- 1 ---- ! 400 *** ../qmail-1.03/dns.c Mon Jun 15 06:53:16 1998 --- dns.c Mon Dec 3 21:48:34 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; } *************** *** 270,275 **** --- 287,300 ---- { 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,335 **** --- 355,363 ---- 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; } *** ../qmail-1.03/ipalloc.h Mon Jun 15 06:53:16 1998 --- ipalloc.h Mon Dec 3 21:48:34 2001 *************** *** 3,9 **** --- 3,14 ---- #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" *** ../qmail-1.03/qmail-remote.c Mon Jun 15 06:53:16 1998 --- qmail-remote.c Mon Dec 3 21:48:34 2001 *************** *** 26,33 **** --- 26,43 ---- #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" + #ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" + #endif + + #ifdef TLS + #include + #include + SSL *ssl = NULL; + + stralloc tlsclientciphers = {0}; + #endif #define HUGESMTPTEXT 5000 *************** *** 107,123 **** --- 117,210 ---- 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,191 **** --- 273,306 ---- 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); + Free(str); + str=ONELINE_NAME(X509_get_issuer_name(peer)); + out("; issuer="); out(str); + Free(str); + X509_free(peer); + } + out(";\n"); + } + #endif + zerodie(); } *************** *** 216,235 **** stralloc recip = {0}; void smtp() { unsigned long code; int flagbother; int i; ! if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); 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"); ! substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); --- 331,488 ---- 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,329 **** --- 577,587 ---- 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,344 **** int flagallaliases; int flagalias; char *relayhost; ! sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); --- 596,605 ---- 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,423 **** --- 678,688 ---- 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); *** ../qmail-1.03/qmail-send.c Mon Jun 15 06:53:16 1998 --- qmail-send.c Mon Dec 3 21:47:10 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,912 **** 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) --- 908,914 ---- 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]; if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) *************** *** 914,946 **** 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; --- 916,948 ---- 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 --- 1546,1552 ---- numjobs = 0; for (c = 0;c < CHANNELS;++c) { ! char ch, ch1; int u; int r; do *************** *** 1552,1558 **** --- 1554,1566 ---- 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]; } *** ../qmail-1.03/qmail-smtpd.8 Mon Jun 15 06:53:16 1998 --- qmail-smtpd.8 Mon Dec 3 21:47:10 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. *** ../qmail-1.03/qmail-smtpd.c Mon Jun 15 06:53:16 1998 --- qmail-smtpd.c Tue Dec 4 14:24:59 2001 *************** *** 16,37 **** --- 16,91 ---- #include "scan.h" #include "byte.h" #include "case.h" + #include "wait.h" #include "env.h" #include "now.h" #include "exit.h" #include "rcpthosts.h" + #include "fd.h" + #ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" + #endif #include "commands.h" + #ifdef TLS + #include + SSL *ssl = NULL; + + stralloc clientcert = {0}; + stralloc tlsserverciphers = {0}; + #endif + #undef 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; } *************** *** 45,56 **** --- 99,115 ---- void die_read() { _exit(1); } void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } + void die_crash() { out("421 child crashed (#4.3.0)\r\n"); flush(); _exit(1); } + void die_fork() { out("421 unable to start checkpassword.\r\n"); flush(); _exit(1); } void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } 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,64 **** --- 118,132 ---- 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,86 **** --- 149,157 ---- char *remoteinfo; char *local; char *relayclient; + #ifdef TLS + char *tlsciphers; + #endif stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ *************** *** 96,107 **** int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; void setup() { char *x; unsigned long u; ! if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) die_control(); --- 167,183 ---- int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; + int tarpitcount = 0; + int tarpitdelay = 5; void setup() { 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) die_control(); *************** *** 110,115 **** --- 186,200 ---- 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); *************** *** 131,136 **** --- 216,232 ---- 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); } *************** *** 221,226 **** --- 317,323 ---- 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() --- 326,367 ---- } void smtp_ehlo(arg) char *arg; { ! smtp_greet("250-"); ! #ifdef TLS ! #ifdef AUTHCRAM ! if (ssl) { ! 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"); ! } ! else { ! out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); ! out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); ! out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250 8BITMIME\r\n"); ! } ! #else ! if (ssl) { ! out("\r\n250-AUTH LOGIN PLAIN"); ! out("\r\n250-AUTH=LOGIN PLAIN"); ! out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ! } ! else { ! out("\r\n250-AUTH LOGIN PLAIN"); ! out("\r\n250-AUTH=LOGIN PLAIN"); ! out("\r\n250-PIPELINING\r\n250-STARTTLS\r\n250 8BITMIME\r\n"); ! } ! #endif ! #else ! #ifdef AUTHCRAM ! 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"); ! #else ! out("\r\n250-AUTH LOGIN PLAIN"); ! out("\r\n250-AUTH=LOGIN PLAIN"); ! out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ! #endif ! #endif seenmail = 0; dohelo(arg); } void smtp_rset() *************** *** 245,252 **** --- 377,391 ---- 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"); } + #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,266 **** --- 396,454 ---- 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(); + if (tarpitcount && ++rcptcount >= tarpitcount) while (sleep(tarpitdelay)); out("250 ok\r\n"); } *************** *** 269,275 **** --- 457,467 ---- { 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,374 **** --- 561,569 ---- 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,384 **** if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); ! received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); --- 572,591 ---- 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); *************** *** 394,415 **** out("\r\n"); } struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } , { "rset", smtp_rset, 0 } , { "help", smtp_help, flush } , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } , { 0, err_unimpl, flush } } ; ! void main() ! { sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); --- 601,897 ---- 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(); + } + + #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; + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + out("535 authorization failed (#5.7.0)\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 + 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(argc,argv) ! int argc; ! char **argv; ! { ! #ifdef TLS ! sig_alarmcatch(sigalrm); ! #endif ! hostname = argv[1]; ! childargs = argv + 2; ! sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); *** ../qmail-1.03/spawn.c Mon Jun 15 06:53:16 1998 --- spawn.c Mon Dec 3 21:47:10 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);