Matt Simerson 9/26/2002 msimerson@interland.com Mail Toaster qmail patch 2.3 This patch is the careful combination of other patches. big-concurrency.patch (see README.big-concurrency) raises the number of simultaneous outgoing connections we can make. smtp tarpitting patch (see README.tarpit) Eric Johnson's SMTP-AUTH patch (see the README.auth or 'man qmail-smtpd' after installing). TLS patch - (see README.tls) qmail-queue - (see README.qmailqueue) - Use for filtering qmail-pop3d maildir++ quota support - Bill Shupp's patch ChangeLog: v2.3 SMTP-auth patch updated 0.31 dns patch removed (using djbdns negates it's necessity and I've now updated all my ancient mail toasters to use djbdns.) qmail-badrcptto patch removed (use qmail-queue instead) evelope sender patch removed (use qmail-queue instead) v2.2 *** ../qmail-1.03.orig/Makefile Mon Jun 15 03:53:16 1998 --- Makefile Thu Sep 26 12:05:41 2002 *************** *** 136,141 **** --- 136,145 ---- compile auto_usera.c ./compile auto_usera.c + base64.o: \ + compile base64.c base64.h stralloc.h substdio.h str.h + ./compile base64.c + binm1: \ binm1.sh conf-qmail cat binm1.sh \ *************** *** 890,895 **** --- 894,931 ---- readwrite.h open.h headerbody.h maildir.h strerr.h ./compile maildirwatch.c + maildirgetquota.o: \ + compile maildirgetquota.c maildirgetquota.h maildirmisc.h + ./compile maildirgetquota.c + + maildirflags.o: \ + compile maildirflags.c + ./compile maildirflags.c + + maildiropen.o: \ + compile maildiropen.c maildirmisc.h + ./compile maildiropen.c + + maildirparsequota.o: \ + compile maildirparsequota.c + ./compile maildirparsequota.c + + maildirquota.o: \ + compile maildirquota.c maildirquota.h maildirmisc.h numlib.h + ./compile maildirquota.c + + overmaildirquota.o: \ + compile overmaildirquota.c + ./compile overmaildirquota.c + + strtimet.o: \ + compile strtimet.c + ./compile strtimet.c + + strpidt.o: \ + compile strpidt.c + ./compile strpidt.c + mailsubj: \ warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split cat warn-auto.sh mailsubj.sh \ *************** *** 1174,1185 **** load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ ! fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ ! auto_patrn.o `cat socket.lib` qmail-local.0: \ qmail-local.8 --- 1210,1224 ---- load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ ! fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \ ! maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \ ! strtimet.o strpidt.o ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ ! auto_patrn.o `cat socket.lib` maildirquota.o maildirgetquota.o \ ! maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o qmail-local.0: \ qmail-local.8 *************** *** 1269,1279 **** qmail-pop3d: \ load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \ maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \ ! stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib ./load qmail-pop3d commands.o case.a timeoutread.o \ timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \ open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \ ! fs.a `cat socket.lib` qmail-pop3d.0: \ qmail-pop3d.8 --- 1308,1320 ---- qmail-pop3d: \ load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \ maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \ ! stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \ ! maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o ./load qmail-pop3d commands.o case.a timeoutread.o \ timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \ open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \ ! fs.a `cat socket.lib` maildirquota.o maildirgetquota.o \ ! maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o qmail-pop3d.0: \ qmail-pop3d.8 *************** *** 1439,1449 **** --- 1480,1492 ---- qmail-remote: \ load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ + ssl_timeoutio.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 dns.lib socket.lib ./load qmail-remote control.o constmap.o timeoutread.o \ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ + ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ 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` *************** *** 1483,1494 **** trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ ! auto_split.o ./load qmail-send qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ ! substdio.a error.a str.a fs.a auto_qmail.o auto_split.o qmail-send.0: \ qmail-send.8 --- 1526,1537 ---- trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ ! auto_split.o env.a ./load qmail-send qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ ! substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a qmail-send.0: \ qmail-send.8 *************** *** 1534,1547 **** qmail-smtpd: \ load qmail-smtpd.o 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 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: \ --- 1577,1592 ---- qmail-smtpd: \ load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + ssl_timeoutio.o ndelay.a \ 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 \ + ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ ! alloc.a substdio.a error.a str.a fs.a auto_qmail.o 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: \ --- 1598,1605 ---- 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: \ *************** *** 2108,2113 **** --- 2154,2163 ---- compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h ./compile timeoutwrite.c + ssl_timeoutio.o: \ + compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h + ./compile ssl_timeoutio.c + token822.o: \ compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ gen_alloc.h gen_allocdefs.h *************** *** 2139,2141 **** --- 2189,2211 ---- 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 999 \ + -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" + *** ../qmail-1.03.orig/README.auth Thu Sep 26 13:16:59 2002 --- README.auth Thu Sep 26 12:05:41 2002 *************** *** 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 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. *** ../qmail-1.03.orig/README.big-concurrency Thu Sep 26 13:16:59 2002 --- README.big-concurrency Thu Sep 26 12:33:12 2002 *************** *** 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) *** ../qmail-1.03.orig/README.maildirquota Thu Sep 26 13:16:59 2002 --- README.maildirquota Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,28 ---- + + This patch adds maildirquota (Maildir++) support to qmail-pop3d and + qmail-local. It was created because when vpopmail switched to maildirquotas, + a user's quota usage was not decreased after deleting mail via qmail-pop3d. + Also, because .qmail files would allow qmail-local to write directly to a + Maildir whithout piping through vdelivermail first, quotas were not effective + for aliases. Actually, this was the case with vpopmail's old quota system as + well. + + This patch is not specific to vpopmail. If you use qmail with other agents that + support Maildir++, this should work for you. + + The functions used in this patch are taken from maildrop 1.3.9 and courier's + pop daemon, by Sam Varshavchik (www.courier-mta.org). The Maildir++ + specification, also by Sam, can be viewed here: + + http://inter7.com/courierimap/README.maildirquota.html + + However, Sam had NOTHING to do with this patch, so please don't bug him about + it. Either bug me directly, or the vpopmail list (vchkpw@inter7.com), who + actually requested it. + + Cheers, + + Bill Shupp + hostmaster@shupp.org + www.shupp.org + *** ../qmail-1.03.orig/README.qmailqueue Thu Sep 26 13:16:59 2002 --- README.qmailqueue Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,22 ---- + From: Bruce Guenter + To: qmail@list.cr.yp.to + Subject: QMAILQUEUE patch for qmail-1.03 + Date: Mon, 25 Jan 1999 15:37:21 -0600 + + Greetings. + + Appended is a patch to qmail-1.03 that causes any program that would run + qmail-queue to look for an environment variable QMAILQUEUE. If it is + present, it is used in place of the string "bin/qmail-queue" when + running qmail-queue. This could be used, for example, to add a program + into the qmail-smtpd->qmail-queue pipeline that could do filtering, + rewrite broken headers, etc. (this is my planned usage for it). + + This has undergone virtually no testing, but it looks so simple that it + almost has to be correct. No warranties, etc. Note that the chdir to + /var/qmail is always done before exec'ing the program. + + Does this look like a reasonable thing to do? + -- + Bruce Guenter, QCC Communications Corp. EMail: bruce.guenter@qcc.sk.ca + Phone: (306)249-0220 WWW: http://www.qcc.sk.ca/~bguenter/ *** ../qmail-1.03.orig/README.tarpit Thu Sep 26 13:16:59 2002 --- README.tarpit Thu Sep 26 12:33:32 2002 *************** *** 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. *** ../qmail-1.03.orig/README.tls Thu Sep 26 13:16:59 2002 --- README.tls Thu Sep 26 12:33:37 2002 *************** *** 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.orig/TARGETS Mon Jun 15 03:53:16 1998 --- TARGETS Thu Sep 26 12:05:41 2002 *************** *** 15,20 **** --- 15,28 ---- slurpclose.o make-makelib makelib + maildirflags.o + maildirparsequota.o + maildiropen.o + maildirgetquota.o + maildirquota.o + overmaildirquota.o + strtimet.o + strpidt.o case_diffb.o case_diffs.o case_lowerb.o *************** *** 168,173 **** --- 176,182 ---- constmap.o timeoutread.o timeoutwrite.o + ssl_timeoutio.o timeoutconn.o tcpto.o dns.o *************** *** 250,255 **** --- 259,265 ---- qmail-qmtpd.o rcpthosts.o qmail-qmtpd + base64.o qmail-smtpd.o qmail-smtpd sendmail.o *************** *** 320,325 **** --- 330,336 ---- binm2+df binm3 binm3+df + Makefile-cert it qmail-local.0 qmail-lspawn.0 *** ../qmail-1.03.orig/base64.c Thu Sep 26 13:22:04 2002 --- base64.c Thu Sep 26 12:05:41 2002 *************** *** 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; + } *** ../qmail-1.03.orig/base64.h Thu Sep 26 13:22:04 2002 --- base64.h Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,7 ---- + #ifndef BASE64_H + #define BASE64_H + + extern int b64decode(); + extern int b64encode(); + + #endif *** ../qmail-1.03.orig/chkspawn.c Mon Jun 15 03:53:16 1998 --- chkspawn.c Thu Sep 26 12:05:41 2002 *************** *** 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.orig/conf-cc Mon Jun 15 03:53:16 1998 --- conf-cc Thu Sep 26 12:05:41 2002 *************** *** 1,3 **** ! cc -O2 This will be used to compile .c files. --- 1,3 ---- ! cc -O2 -DTLS -I/usr/local/ssl/include This will be used to compile .c files. *** ../qmail-1.03.orig/conf-groups Mon Jun 15 03:53:16 1998 --- conf-groups Thu Sep 26 13:15:39 2002 *************** *** 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.orig/conf-spawn Mon Jun 15 03:53:16 1998 --- conf-spawn Thu Sep 26 13:15:34 2002 *************** *** 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 ---- ! 509 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 *** ../qmail-1.03.orig/dns.c Mon Jun 15 03:53:16 1998 --- dns.c Thu Sep 26 12:05:41 2002 *************** *** 269,280 **** int pref; { int r; ! struct ip_mx ix; if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { - ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; --- 269,279 ---- int pref; { int r; ! struct ip_mx ix = {0}; if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; *************** *** 293,301 **** ix.ip = ip; ix.pref = pref; if (r == DNS_SOFT) return DNS_SOFT; ! if (r == 1) if (!ipalloc_append(ia,&ix)) return DNS_MEM; } return 0; } --- 292,307 ---- ix.ip = ip; ix.pref = pref; if (r == DNS_SOFT) return DNS_SOFT; ! if (r == 1) { ! #ifdef IX_FQDN ! ix.fqdn = glue.s; ! #endif if (!ipalloc_append(ia,&ix)) return DNS_MEM; } + } + #ifdef IX_FQDN + glue.s = 0; + #endif return 0; } *************** *** 315,321 **** { int r; struct mx { stralloc sa; unsigned short p; } *mx; ! struct ip_mx ix; int nummx; int i; int j; --- 321,327 ---- { int r; struct mx { stralloc sa; unsigned short p; } *mx; ! struct ip_mx ix = {0}; int nummx; int i; int j; *************** *** 327,333 **** if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { - ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; --- 333,338 ---- *** ../qmail-1.03.orig/ipalloc.h Mon Jun 15 03:53:16 1998 --- ipalloc.h Thu Sep 26 12:05:41 2002 *************** *** 3,9 **** --- 3,17 ---- #include "ip.h" + #ifdef TLS + # define IX_FQDN 1 + #endif + + #ifdef IX_FQDN + 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.orig/maildirflags.c Thu Sep 26 13:16:59 2002 --- maildirflags.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,23 ---- + /* + ** Copyright 2000 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #include + #include + + static const char rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + int maildir_hasflag(const char *filename, char flag) + { + const char *p=strrchr(filename, '/'); + + if (p) + filename=p+1; + + p=strrchr(p, ':'); + if (p && strncmp(p, ":2,", 3) == 0 && + strchr(p+3, flag)) + return (1); + return (0); + } *** ../qmail-1.03.orig/maildirgetquota.c Thu Sep 26 13:16:59 2002 --- maildirgetquota.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,50 ---- + /* + ** Copyright 1998 - 2000 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #include "maildirgetquota.h" + #include "maildirmisc.h" + #if HAVE_UNISTD_H + #include + #endif + #include + #include + #include + #include + #include + + int maildir_getquota(const char *dir, char buf[QUOTABUFSIZE]) + { + char *p; + struct stat stat_buf; + int n; + int l; + + p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + if (!p) return (-1); + + strcat(strcpy(p, dir), "/maildirfolder"); + if (stat(p, &stat_buf) == 0) + { + strcat(strcpy(p, dir), "/.."); + n=maildir_getquota(p, buf); + free(p); + return (n); + } + + strcat(strcpy(p, dir), "/maildirsize"); + n=maildir_safeopen(p, O_RDONLY, 0); + free(p); + if (n < 0) return (n); + if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0) + { + close(n); + return (-1); + } + close(n); + for (n=0; n + #include + + #ifdef __cplusplus + extern "C" { + #endif + + static const char maildirgetquota_h_rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + #define QUOTABUFSIZE 256 + + int maildir_getquota(const char *, char [QUOTABUFSIZE]); + + #ifdef __cplusplus + } + #endif + + #endif *** ../qmail-1.03.orig/maildirmisc.h Thu Sep 26 13:16:59 2002 --- maildirmisc.h Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,145 ---- + #ifndef maildirmisc_h + #define maildirmisc_h + + /* + ** Copyright 2000 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + + #ifdef __cplusplus + extern "C" { + #endif + + static const char maildirmisc_h_rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + /* + ** + ** Miscellaneous maildir-related code + ** + */ + + /* Some special folders */ + + #define INBOX "INBOX" + #define DRAFTS "Drafts" + #define SENT "Sent" + #define TRASH "Trash" + + #define SHAREDSUBDIR "shared-folders" + + char *maildir_folderdir(const char *, /* maildir */ + const char *); /* folder name */ + /* Returns the directory corresponding to foldername (foldername is + ** checked to make sure that it's a valid name, else we set errno + ** to EINVAL, and return (0). + */ + + char *maildir_filename(const char *, /* maildir */ + const char *, /* folder */ + const char *); /* filename */ + /* + ** Builds the filename to this message, suitable for opening. + ** If the file doesn't appear to be there, search the maildir to + ** see if someone changed the flags, and return the current filename. + */ + + int maildir_safeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + + /* + ** Same arguments as open(). When we're accessing a shared maildir, + ** prevent someone from playing cute and dumping a bunch of symlinks + ** in there. This function will open the indicate file only if the + ** last component is not a symlink. + ** This is implemented by opening the file with O_NONBLOCK (to prevent + ** a DOS attack of someone pointing the symlink to a pipe, causing + ** the open to hang), clearing O_NONBLOCK, then stat-int the file + ** descriptor, lstating the filename, and making sure that dev/ino + ** match. + */ + + int maildir_semisafeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + + /* + ** Same thing, except that we allow ONE level of soft link indirection, + ** because we're reading from our own maildir, which points to the + ** message in the sharable maildir. + */ + + int maildir_mkdir(const char *); /* directory */ + /* + ** Create maildir including all subdirectories in the path (like mkdir -p) + */ + + void maildir_purgetmp(const char *); /* maildir */ + /* purges old stuff out of tmp */ + + void maildir_purge(const char *, /* directory */ + unsigned); /* time_t to purge */ + + void maildir_getnew(const char *, /* maildir */ + const char *); /* folder */ + /* move messages from new to cur */ + + int maildir_deletefolder(const char *, /* maildir */ + const char *); /* folder */ + /* deletes a folder */ + + int maildir_mddelete(const char *); /* delete a maildir folder by path */ + + void maildir_list_sharable(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to callback func */ + /* list sharable folders */ + + int maildir_shared_subscribe(const char *, /* maildir */ + const char *); /* folder */ + /* subscribe to a shared folder */ + + void maildir_list_shared(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to the callback func */ + /* list subscribed folders */ + + int maildir_shared_unsubscribe(const char *, /* maildir */ + const char *); /* folder */ + /* unsubscribe from a shared folder */ + + char *maildir_shareddir(const char *, /* maildir */ + const char *); /* folder */ + /* + ** Validate and return a path to a shared folder. folderdir must be + ** a name of a valid shared folder. + */ + + void maildir_shared_sync(const char *); /* maildir */ + /* "sync" the shared folder */ + + int maildir_sharedisro(const char *); /* maildir */ + /* maildir is a shared read-only folder */ + + int maildir_unlinksharedmsg(const char *); /* filename */ + /* Remove a message from a shared folder */ + + /* Internal function that reads a symlink */ + + char *maildir_getlink(const char *); + + /* Determine whether the maildir filename has a certain flag */ + + int maildir_hasflag(const char *filename, char); + + #define MAILDIR_DELETED(f) maildir_hasflag((f), 'T') + + #ifdef __cplusplus + } + #endif + + #endif *** ../qmail-1.03.orig/maildiropen.c Thu Sep 26 13:16:59 2002 --- maildiropen.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,133 ---- + /* + ** Copyright 2000 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + + #include + #include + #include + #include + #include + #if HAVE_UNISTD_H + #include + #endif + #include + #include + #include + #include + + #include "maildirmisc.h" + + static const char rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + char *maildir_getlink(const char *filename) + { + #if HAVE_READLINK + size_t bufsiz; + char *buf; + + bufsiz=0; + buf=0; + + for (;;) + { + int n; + + if (buf) free(buf); + bufsiz += 256; + if ((buf=malloc(bufsiz)) == 0) + { + perror("malloc"); + return (0); + } + if ((n=readlink(filename, buf, bufsiz)) < 0) + { + free(buf); + return (0); + } + if (n < bufsiz) + { + buf[n]=0; + break; + } + } + return (buf); + #else + return (0); + #endif + } + + int maildir_semisafeopen(const char *path, int mode, int perm) + { + + #if HAVE_READLINK + + char *l=maildir_getlink(path); + + if (l) + { + int f; + + if (*l != '/') + { + char *q=malloc(strlen(path)+strlen(l)+2); + char *s; + + if (!q) + { + free(l); + return (-1); + } + + strcpy(q, path); + if ((s=strchr(q, '/')) != 0) + s[1]=0; + else *q=0; + strcat(q, l); + free(l); + l=q; + } + + f=maildir_safeopen(l, mode, perm); + + free(l); + return (f); + } + #endif + + return (maildir_safeopen(path, mode, perm)); + } + + int maildir_safeopen(const char *path, int mode, int perm) + { + struct stat stat1, stat2; + + int fd=open(path, mode + #ifdef O_NONBLOCK + | O_NONBLOCK + #else + | O_NDELAY + #endif + , perm); + + if (fd < 0) return (fd); + if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1) + || lstat(path, &stat2)) + { + close(fd); + return (-1); + } + + if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino) + { + close(fd); + errno=ENOENT; + return (-1); + } + + return (fd); + } *** ../qmail-1.03.orig/maildirparsequota.c Thu Sep 26 13:16:59 2002 --- maildirparsequota.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,44 ---- + /* + ** Copyright 1998 - 1999 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + #include "maildirquota.h" + #include + #include + + static const char rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + int maildir_parsequota(const char *n, unsigned long *s) + { + const char *o; + int yes; + + if ((o=strrchr(n, '/')) == 0) o=n; + + for (; *o; o++) + if (*o == ':') break; + yes=0; + for ( ; o >= n; --o) + { + if (*o == '/') break; + + if (*o == ',' && o[1] == 'S' && o[2] == '=') + { + yes=1; + o += 3; + break; + } + } + if (yes) + { + *s=0; + while (*o >= '0' && *o <= '9') + *s= *s*10 + (*o++ - '0'); + return (0); + } + return (-1); + } *** ../qmail-1.03.orig/maildirquota.c Thu Sep 26 13:16:59 2002 --- maildirquota.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,685 ---- + /* + ** Copyright 1998 - 2002 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + + #include + /* #if HAVE_DIRENT_H */ + #include + #define NAMLEN(dirent) strlen((dirent)->d_name) + /* #else + #define dirent direct + #define NAMLEN(dirent) (dirent)->d_namlen + #if HAVE_SYS_NDIR_H + #include + #endif + #if HAVE_SYS_DIR_H + #include + #endif + #if HAVE_NDIR_H + #include + #endif + #endif */ + #include + /* #if HAVE_SYS_STAT_H */ + #include + /* #endif */ + #include + + #include "maildirquota.h" + #include "maildirmisc.h" + #include + #include + #include + #include + /* #if HAVE_FCNTL_H */ + #include + /* #endif */ + #if HAVE_UNISTD_H + #include + #endif + #include + #include "numlib.h" + + static const char rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + /* Read the maildirsize file */ + + int maildirsize_read(const char *filename, /* The filename */ + int *fdptr, /* Keep the file descriptor open */ + off_t *sizeptr, /* Grand total of maildir size */ + unsigned *cntptr, /* Grand total of message count */ + unsigned *nlines, /* # of lines in maildirsize */ + struct stat *statptr) /* The stats on maildirsize */ + { + char buf[5120]; + int f; + char *p; + unsigned l; + int n; + int first; + + if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0) + return (-1); + p=buf; + l=sizeof(buf); + + while (l) + { + n=read(f, p, l); + if (n < 0) + { + close(f); + return (-1); + } + if (n == 0) break; + p += n; + l -= n; + } + if (l == 0 || fstat(f, statptr)) /* maildir too big */ + { + close(f); + return (-1); + } + + *sizeptr=0; + *cntptr=0; + *nlines=0; + *p=0; + p=buf; + first=1; + while (*p) + { + long n=0; + int c=0; + char *q=p; + + while (*p) + if (*p++ == '\n') + { + p[-1]=0; + break; + } + + if (first) + { + first=0; + continue; + } + sscanf(q, "%ld %d", &n, &c); + *sizeptr += n; + *cntptr += c; + ++ *nlines; + } + *fdptr=f; + return (0); + } + + static char *makenewmaildirsizename(const char *, int *); + static int countcurnew(const char *, time_t *, off_t *, unsigned *); + static int countsubdir(const char *, const char *, + time_t *, off_t *, unsigned *); + static int statcurnew(const char *, time_t *); + static int statsubdir(const char *, const char *, time_t *); + + #define MDQUOTA_SIZE 'S' /* Total size of all messages in maildir */ + #define MDQUOTA_BLOCKS 'B' /* Total # of blocks for all messages in + maildir -- NOT IMPLEMENTED */ + #define MDQUOTA_COUNT 'C' /* Total number of messages in maildir */ + + static int qcalc(off_t s, unsigned n, const char *quota, int *percentage) + { + off_t i; + int spercentage=0; + int npercentage=0; + + errno=ENOSPC; + while (quota && *quota) + { + int x=1; + + if (*quota < '0' || *quota > '9') + { + ++quota; + continue; + } + i=0; + while (*quota >= '0' && *quota <= '9') + i=i*10 + (*quota++ - '0'); + switch (*quota) { + default: + if (i < s) + { + *percentage=100; + return (-1); + } + + /* + ** For huge quotas, over 20mb, + ** divide numerator & denominator by 1024 to prevent + ** an overflow when multiplying by 100 + */ + + x=1; + if (i > 20000000) x=1024; + + spercentage = i ? (s/x) * 100 / (i/x):100; + break; + case 'C': + + if (i < n) + { + *percentage=100; + return (-1); + } + + /* Ditto */ + + x=1; + if (i > 20000000) x=1024; + + npercentage = i ? ((off_t)n/x) * 100 / (i/x):100; + break; + } + } + *percentage = spercentage > npercentage ? spercentage:npercentage; + return (0); + } + + static int doaddquota(const char *, int, const char *, long, int, int); + + static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, int *percentage); + + + int maildir_checkquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt) + { + int dummy; + + return (docheckquota(dir, maildirsize_fdptr, quota_type, + xtra_size, xtra_cnt, &dummy)); + } + + int maildir_readquota(const char *dir, const char *quota_type) + { + int percentage=0; + int fd=-1; + + (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage); + if (fd >= 0) + close(fd); + return (percentage); + } + + static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, + int *percentage) + { + char *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + char *newmaildirsizename; + struct stat stat_buf; + int maildirsize_fd; + off_t maildirsize_size; + unsigned maildirsize_cnt; + unsigned maildirsize_nlines; + int n; + time_t tm; + time_t maxtime; + DIR *dirp; + struct dirent *de; + + if (checkfolder == 0) return (-1); + *maildirsize_fdptr= -1; + strcat(strcpy(checkfolder, dir), "/maildirfolder"); + if (stat(checkfolder, &stat_buf) == 0) /* Go to parent */ + { + strcat(strcpy(checkfolder, dir), "/.."); + n=docheckquota(checkfolder, maildirsize_fdptr, + quota_type, xtra_size, xtra_cnt, percentage); + free(checkfolder); + return (n); + } + if (!quota_type || !*quota_type) return (0); + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + time(&tm); + if (maildirsize_read(checkfolder, &maildirsize_fd, + &maildirsize_size, &maildirsize_cnt, + &maildirsize_nlines, &stat_buf) == 0) + { + n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage); + + if (n == 0) + { + free(checkfolder); + *maildirsize_fdptr=maildirsize_fd; + return (0); + } + close(maildirsize_fd); + + if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60) + return (n); + } + + maxtime=0; + maildirsize_size=0; + maildirsize_cnt=0; + + if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt)) + { + free(checkfolder); + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size, + &maildirsize_cnt)) + { + free(checkfolder); + closedir(dirp); + return (-1); + } + } + if (dirp) + { + #if CLOSEDIR_VOID + closedir(dirp); + #else + if (closedir(dirp)) + { + free(checkfolder); + return (-1); + } + #endif + } + + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(checkfolder); + return (-1); + } + + *maildirsize_fdptr=maildirsize_fd; + + if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 1)) + { + free(newmaildirsizename); + unlink(newmaildirsizename); + close(maildirsize_fd); + *maildirsize_fdptr= -1; + free(checkfolder); + return (-1); + } + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + + if (rename(newmaildirsizename, checkfolder)) + { + free(checkfolder); + unlink(newmaildirsizename); + close(maildirsize_fd); + *maildirsize_fdptr= -1; + } + free(checkfolder); + free(newmaildirsizename); + + tm=0; + + if (statcurnew(dir, &tm)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (statsubdir(dir, de->d_name, &tm)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + closedir(dirp); + return (-1); + } + } + if (dirp) + { + #if CLOSEDIR_VOID + closedir(dirp); + #else + if (closedir(dirp)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + return (-1); + } + #endif + } + + if (tm != maxtime) /* Race condition, someone changed something */ + { + errno=EAGAIN; + return (-1); + } + + return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage)); + } + + int maildir_addquota(const char *dir, int maildirsize_fd, + const char *quota_type, long maildirsize_size, int maildirsize_cnt) + { + if (!quota_type || !*quota_type) return (0); + return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 0)); + } + + static int doaddquota(const char *dir, int maildirsize_fd, + const char *quota_type, long maildirsize_size, int maildirsize_cnt, + int isnew) + { + union { + char buf[100]; + struct stat stat_buf; + } u; /* Scrooge */ + char *newname2=0; + char *newmaildirsizename=0; + struct iovec iov[3]; + int niov; + struct iovec *p; + int n; + + niov=0; + if ( maildirsize_fd < 0) + { + newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + if (!newname2) return (-1); + strcat(strcpy(newname2, dir), "/maildirfolder"); + if (stat(newname2, &u.stat_buf) == 0) + { + strcat(strcpy(newname2, dir), "/.."); + n=doaddquota(newname2, maildirsize_fd, quota_type, + maildirsize_size, maildirsize_cnt, + isnew); + free(newname2); + return (n); + } + + strcat(strcpy(newname2, dir), "/maildirsize"); + + if ((maildirsize_fd=maildir_safeopen(newname2, + O_RDWR|O_APPEND, 0644)) < 0) + { + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(newname2); + return (-1); + } + + maildirsize_fd=maildir_safeopen(newmaildirsizename, + O_CREAT|O_RDWR|O_APPEND, 0644); + + if (maildirsize_fd < 0) + { + free(newname2); + return (-1); + } + isnew=1; + } + } + + if (isnew) + { + iov[0].iov_base=(caddr_t)quota_type; + iov[0].iov_len=strlen(quota_type); + iov[1].iov_base=(caddr_t)"\n"; + iov[1].iov_len=1; + niov=2; + } + + + sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt); + iov[niov].iov_base=(caddr_t)u.buf; + iov[niov].iov_len=strlen(u.buf); + + p=iov; + ++niov; + n=0; + while (niov) + { + if (n) + { + if (n < p->iov_len) + { + p->iov_base= + (caddr_t)((char *)p->iov_base + n); + p->iov_len -= n; + } + else + { + n -= p->iov_len; + ++p; + --niov; + continue; + } + } + + n=writev( maildirsize_fd, p, niov); + + if (n <= 0) + { + if (newname2) + { + close(maildirsize_fd); + free(newname2); + } + return (-1); + } + } + if (newname2) + { + close(maildirsize_fd); + + if (newmaildirsizename) + { + rename(newmaildirsizename, newname2); + free(newmaildirsizename); + } + free(newname2); + } + return (0); + } + + /* New maildirsize is built in the tmp subdirectory */ + + static char *makenewmaildirsizename(const char *dir, int *fd) + { + char hostname[256]; + struct stat stat_buf; + time_t t; + char *p; + + hostname[0]=0; + hostname[sizeof(hostname)-1]=0; + gethostname(hostname, sizeof(hostname)-1); + p=(char *)malloc(strlen(dir)+strlen(hostname)+130); + if (!p) return (0); + + for (;;) + { + char tbuf[NUMBUFSIZE]; + char pbuf[NUMBUFSIZE]; + + time(&t); + strcat(strcpy(p, dir), "/tmp/"); + sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s", + str_time_t(t, tbuf), + str_pid_t(getpid(), pbuf), hostname); + + if (stat( (const char *)p, &stat_buf) < 0 && + (*fd=maildir_safeopen(p, + O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0) + break; + sleep(3); + } + return (p); + } + + static int statcurnew(const char *dir, time_t *maxtimestamp) + { + char *p=(char *)malloc(strlen(dir)+5); + struct stat stat_buf; + + if (!p) return (-1); + strcat(strcpy(p, dir), "/cur"); + if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp) + *maxtimestamp=stat_buf.st_mtime; + strcat(strcpy(p, dir), "/new"); + if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp) + *maxtimestamp=stat_buf.st_mtime; + free(p); + return (0); + } + + static int statsubdir(const char *dir, const char *subdir, time_t *maxtime) + { + char *p; + int n; + + if ( *subdir != '.' || strcmp(subdir, ".") == 0 || + strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0) + return (0); + + p=(char *)malloc(strlen(dir)+strlen(subdir)+2); + if (!p) return (-1); + strcat(strcat(strcpy(p, dir), "/"), subdir); + n=statcurnew(p, maxtime); + free(p); + return (n); + } + + static int docount(const char *, time_t *, off_t *, unsigned *); + + static int countcurnew(const char *dir, time_t *maxtime, + off_t *sizep, unsigned *cntp) + { + char *p=(char *)malloc(strlen(dir)+5); + int n; + + if (!p) return (-1); + strcat(strcpy(p, dir), "/new"); + n=docount(p, maxtime, sizep, cntp); + if (n == 0) + { + strcat(strcpy(p, dir), "/cur"); + n=docount(p, maxtime, sizep, cntp); + } + free(p); + return (n); + } + + static int countsubdir(const char *dir, const char *subdir, time_t *maxtime, + off_t *sizep, unsigned *cntp) + { + char *p; + int n; + + if ( *subdir != '.' || strcmp(subdir, ".") == 0 || + strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0) + return (0); + + p=(char *)malloc(strlen(dir)+strlen(subdir)+2); + if (!p) return (2); + strcat(strcat(strcpy(p, dir), "/"), subdir); + n=countcurnew(p, maxtime, sizep, cntp); + free(p); + return (n); + } + + static int docount(const char *dir, time_t *dirstamp, + off_t *sizep, unsigned *cntp) + { + struct stat stat_buf; + char *p; + DIR *dirp; + struct dirent *de; + unsigned long s; + + if (stat(dir, &stat_buf)) return (0); /* Ignore */ + if (stat_buf.st_mtime > *dirstamp) *dirstamp=stat_buf.st_mtime; + if ((dirp=opendir(dir)) == 0) return (0); + while ((de=readdir(dirp)) != 0) + { + const char *n=de->d_name; + + if (*n == '.') continue; + + /* PATCH - do not count msgs marked as deleted */ + + for ( ; *n; n++) + { + if (n[0] != ':' || n[1] != '2' || + n[2] != ',') continue; + n += 3; + while (*n >= 'A' && *n <= 'Z') + { + if (*n == 'T') break; + ++n; + } + break; + } + if (*n == 'T') continue; + n=de->d_name; + + + if (maildir_parsequota(n, &s) == 0) + stat_buf.st_size=s; + else + { + p=(char *)malloc(strlen(dir)+strlen(n)+2); + if (!p) + { + closedir(dirp); + return (-1); + } + strcat(strcat(strcpy(p, dir), "/"), n); + if (stat(p, &stat_buf)) + { + free(p); + continue; + } + free(p); + } + *sizep += stat_buf.st_size; + ++*cntp; + } + + #if CLOSEDIR_VOID + closedir(dirp); + #else + if (closedir(dirp)) + return (-1); + #endif + return (0); + } *** ../qmail-1.03.orig/maildirquota.h Thu Sep 26 13:16:59 2002 --- maildirquota.h Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,45 ---- + #ifndef maildirquota_h + #define maildirquota_h + + /* + ** Copyright 1998 - 1999 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + + #include + #include + + #ifdef __cplusplus + extern "C" { + #endif + + static const char maildirquota_h_rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + int maildir_checkquota(const char *, /* Pointer to directory */ + int *, /* Initialized to -1, or opened descriptor for maildirsize */ + const char *, /* The quota */ + long, /* Extra bytes planning to add/remove from maildir */ + int); /* Extra messages planning to add/remove from maildir */ + + int maildir_addquota(const char *, /* Pointer to the maildir */ + int, /* Must be the int pointed to by 2nd arg to checkquota */ + const char *, /* The quota */ + long, /* +/- bytes */ + int); /* +/- files */ + + int maildir_readquota(const char *, /* Directory */ + const char *); /* Quota, from getquota */ + + int maildir_parsequota(const char *, unsigned long *); + /* Attempt to parse file size encoded in filename. Returns 0 if + ** parsed, non-zero if we didn't parse. */ + + #ifdef __cplusplus + } + #endif + + #endif *** ../qmail-1.03.orig/numlib.h Thu Sep 26 13:16:59 2002 --- numlib.h Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,45 ---- + #ifndef numlib_h + #define numlib_h + + /* + ** Copyright 1998 - 1999 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #ifdef __cplusplus + extern "C" { + #endif + + static const char numlib_h_rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + #if HAVE_CONFIG_H + #include "config.h" + #endif + + #include + #include + + #define NUMBUFSIZE 60 + + /* Convert various system types to decimal */ + + char *str_time_t(time_t, char *); + char *str_off_t(off_t, char *); + char *str_pid_t(pid_t, char *); + char *str_ino_t(ino_t, char *); + char *str_uid_t(uid_t, char *); + char *str_gid_t(gid_t, char *); + char *str_size_t(size_t, char *); + + char *str_sizekb(unsigned long, char *); /* X Kb or X Mb */ + + /* Convert selected system types to hex */ + + char *strh_time_t(time_t, char *); + char *strh_pid_t(pid_t, char *); + char *strh_ino_t(ino_t, char *); + + #ifdef __cplusplus + } + #endif + #endif *** ../qmail-1.03.orig/overmaildirquota.c Thu Sep 26 13:16:59 2002 --- overmaildirquota.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,43 ---- + /* + ** Copyright 1998 - 1999 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + #include "maildirquota.h" + #include + #include + #include + #include + + static const char rcsid[]="$Id: overquota.c,v 1.0 2002/06/09 16:21:05 mr + sam Exp $"; + + + int user_over_maildirquota( const char *dir, const char *q) + { + struct stat stat_buf; + int quotafd; + int ret_value; + + if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && + stat_buf.st_size > 0 && *q) + { + if (maildir_checkquota(dir, "afd, q, stat_buf.st_size, 1) + && errno != EAGAIN) + { + if (quotafd >= 0) close(quotafd); + ret_value = 1; + } else { + maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1); + if (quotafd >= 0) close(quotafd); + ret_value = 0; + } + } else { + ret_value = 0; + } + + return(ret_value); + } *** ../qmail-1.03.orig/qmail-control.9 Mon Jun 15 03:53:16 1998 --- qmail-control.9 Thu Sep 26 12:05:41 2002 *************** *** 43,48 **** --- 43,50 ---- .I badmailfrom \fR(none) \fRqmail-smtpd .I bouncefrom \fRMAILER-DAEMON \fRqmail-send .I bouncehost \fIme \fRqmail-send + .I clientca.pem \fR(none) \fRqmail-smtpd + .I clientcert.pem \fR(none) \fRqmail-remote .I concurrencylocal \fR10 \fRqmail-send .I concurrencyremote \fR20 \fRqmail-send .I defaultdomain \fIme \fRqmail-inject *************** *** 61,71 **** --- 63,79 ---- .I qmqpservers \fR(none) \fRqmail-qmqpc .I queuelifetime \fR604800 \fRqmail-send .I rcpthosts \fR(none) \fRqmail-smtpd + .I rsa512.pem \fR(none) \fRqmail-smtpd + .I servercert.pem \fR(none) \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd .I smtproutes \fR(none) \fRqmail-remote .I timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd + .I tlsclients \fR(none) \fRqmail-smtpd + .I tlsclientciphers \fR(none) \fRqmail-remote + .I tlshosts/FQDN.pem \fR(none) \fRqmail-remote + .I tlsserverciphers \fR(none) \fRqmail-smtpd .I virtualdomains \fR(none) \fRqmail-send .fi .RE *** ../qmail-1.03.orig/qmail-local.c Mon Jun 15 03:53:16 1998 --- qmail-local.c Thu Sep 26 12:05:41 2002 *************** *** 66,71 **** --- 66,72 ---- char buf[1024]; char outbuf[1024]; + #define QUOTABUFSIZE 256 /* child process */ *************** *** 86,94 **** --- 87,101 ---- int fd; substdio ss; substdio ssout; + char quotabuf[QUOTABUFSIZE]; sig_alarmcatch(sigalrm); if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); } + if (maildir_getquota(dir, quotabuf) == 0) { + if (user_over_maildirquota(dir,quotabuf)==1) { + _exit(1); + } + } pid = getpid(); host[0] = 0; gethostname(host,sizeof(host)); *************** *** 99,105 **** s += fmt_str(s,"tmp/"); s += fmt_ulong(s,time); *s++ = '.'; s += fmt_ulong(s,pid); *s++ = '.'; ! s += fmt_strn(s,host,sizeof(host)); *s++ = 0; if (stat(fntmptph,&st) == -1) if (errno == error_noent) break; /* really should never get to this point */ if (loop == 2) _exit(1); --- 106,115 ---- s += fmt_str(s,"tmp/"); s += fmt_ulong(s,time); *s++ = '.'; s += fmt_ulong(s,pid); *s++ = '.'; ! s += fmt_strn(s,host,sizeof(host)); ! s += fmt_strn(s,",S=",sizeof(",S=")); ! if (fstat(0,&st) == -1) if (errno == error_noent) break; ! s += fmt_ulong(s,st.st_size); *s++ = 0; if (stat(fntmptph,&st) == -1) if (errno == error_noent) break; /* really should never get to this point */ if (loop == 2) _exit(1); *************** *** 159,164 **** --- 169,175 ---- switch(wait_exitcode(wstat)) { case 0: break; + case 1: strerr_die1x(1, "User over quota. (#5.1.1)"); case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)"); case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)"); case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)"); *** ../qmail-1.03.orig/qmail-pop3d.c Mon Jun 15 03:53:16 1998 --- qmail-pop3d.c Thu Sep 26 12:05:41 2002 *************** *** 16,21 **** --- 16,26 ---- #include "readwrite.h" #include "timeoutread.h" #include "timeoutwrite.h" + #include + #include "maildirquota.h" + #include "maildirmisc.h" + + #define QUOTABUFSIZE 256 void die() { _exit(0); } *************** *** 45,63 **** { substdio_put(&ssout,buf,len); } - void puts(s) char *s; - { - substdio_puts(&ssout,s); - } void flush() { substdio_flush(&ssout); } void err(s) char *s; { ! puts("-ERR "); ! puts(s); ! puts("\r\n"); flush(); } --- 50,64 ---- { substdio_put(&ssout,buf,len); } void flush() { substdio_flush(&ssout); } void err(s) char *s; { ! substdio_puts(&ssout,"-ERR "); ! substdio_puts(&ssout,s); ! substdio_puts(&ssout,"\r\n"); flush(); } *************** *** 73,79 **** void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } ! void okay() { puts("+OK \r\n"); flush(); } void printfn(fn) char *fn; { --- 74,80 ---- void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } ! void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); } void printfn(fn) char *fn; { *************** *** 153,163 **** total = 0; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; ! puts("+OK "); put(strnum,fmt_uint(strnum,numm)); ! puts(" "); put(strnum,fmt_ulong(strnum,total)); ! puts("\r\n"); flush(); } --- 154,164 ---- total = 0; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; ! substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,numm)); ! substdio_puts(&ssout," "); put(strnum,fmt_ulong(strnum,total)); ! substdio_puts(&ssout,"\r\n"); flush(); } *************** *** 171,188 **** void pop3_last() { ! puts("+OK "); put(strnum,fmt_uint(strnum,last)); ! puts("\r\n"); flush(); } void pop3_quit() { int i; for (i = 0;i < numm;++i) if (m[i].flagdeleted) { ! if (unlink(m[i].fn) == -1) err_nounlink(); } else if (str_start(m[i].fn,"new/")) { --- 172,212 ---- void pop3_last() { ! substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,last)); ! substdio_puts(&ssout,"\r\n"); flush(); } void pop3_quit() { int i; + char quotabuf[QUOTABUFSIZE]; + int has_quota=maildir_getquota(".", quotabuf); + + long deleted_bytes=0; + long deleted_messages=0; + for (i = 0;i < numm;++i) if (m[i].flagdeleted) { ! unsigned long un=0; ! const char *filename=m[i].fn; ! if (has_quota == 0 && !MAILDIR_DELETED(filename)) { ! if (maildir_parsequota(filename, &un)) { ! struct stat stat_buf; ! ! if (stat(filename, &stat_buf) == 0) ! un=stat_buf.st_size; ! } ! } ! if (unlink(m[i].fn) == -1) { ! err_nounlink(); ! un=0; ! } ! if (un) { ! deleted_bytes -= un; ! deleted_messages -= 1; ! } } else if (str_start(m[i].fn,"new/")) { *************** *** 192,197 **** --- 216,236 ---- if (!stralloc_0(&line)) die_nomem(); rename(m[i].fn,line.s); /* if it fails, bummer */ } + + if (deleted_messages < 0) { + int quotafd; + + if (maildir_checkquota(".", "afd, quotabuf, deleted_bytes, + deleted_messages) && errno != EAGAIN && + deleted_bytes >= 0) + { + if (quotafd >= 0) close (quotafd); + } else { + maildir_addquota(".", quotafd, quotabuf, + deleted_bytes, deleted_messages); + if (quotafd >= 0) close(quotafd); + } + } okay(); die(); } *************** *** 222,231 **** int flaguidl; { put(strnum,fmt_uint(strnum,i + 1)); ! puts(" "); if (flaguidl) printfn(m[i].fn); else put(strnum,fmt_ulong(strnum,m[i].size)); ! puts("\r\n"); } void dolisting(arg,flaguidl) char *arg; int flaguidl; --- 261,270 ---- int flaguidl; { put(strnum,fmt_uint(strnum,i + 1)); ! substdio_puts(&ssout," "); if (flaguidl) printfn(m[i].fn); else put(strnum,fmt_ulong(strnum,m[i].size)); ! substdio_puts(&ssout,"\r\n"); } void dolisting(arg,flaguidl) char *arg; int flaguidl; *************** *** 234,240 **** if (*arg) { i = msgno(arg); if (i == -1) return; ! puts("+OK "); list(i,flaguidl); } else { --- 273,279 ---- if (*arg) { i = msgno(arg); if (i == -1) return; ! substdio_puts(&ssout,"+OK "); list(i,flaguidl); } else { *************** *** 242,248 **** for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); ! puts(".\r\n"); } flush(); } --- 281,287 ---- for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); ! substdio_puts(&ssout,".\r\n"); } flush(); } *** ../qmail-1.03.orig/qmail-remote.8 Mon Jun 15 03:53:16 1998 --- qmail-remote.8 Thu Sep 26 12:05:41 2002 *************** *** 114,119 **** --- 114,123 ---- always exits zero. .SH "CONTROL FILES" .TP 5 + .I clientcert.pem + SSL certificate that is used to authenticate with the remote server + during a TLS session. + .TP 5 .I helohost Current host name, for use solely in saying hello to the remote SMTP server. *************** *** 156,161 **** --- 160,167 ---- this tells .B qmail-remote to look up MX records as usual. + .I port + value of 465 (deprecated smtps port) causes TLS session to be started. .I smtproutes may include wildcards: *************** *** 195,200 **** --- 201,223 ---- .B qmail-remote will wait for each response from the remote SMTP server. Default: 1200. + + .TP 5 + .I tlsclientciphers + An OpenSSL client cipher string. + + .TP 5 + .I tlshosts/.pem + .B qmail-remote + requires authentication from servers for which this certificate exists + .RB ( + is the fully-qualified domain name of the server). The + .I CommonName + attributes have to match. + + .B WARNING: + this option may cause mail to be delayed, bounced, doublebounced, or lost. + .SH "SEE ALSO" addresses(5), envelopes(5), *** ../qmail-1.03.orig/qmail-remote.c Mon Jun 15 03:53:16 1998 --- qmail-remote.c Thu Sep 26 12:05:41 2002 *************** *** 47,52 **** --- 47,56 ---- saa reciplist = {0}; struct ip_address partner; + #ifdef TLS + char *partner_fqdn = 0; + #define PORT_SMTPS 465 + #endif void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } *************** *** 107,115 **** --- 111,168 ---- int smtpfd; int timeout = 1200; + #ifdef TLS + + #include + #include + #include + #include "ssl_timeoutio.h" + + SSL *ssl = NULL; + + stralloc tlsclientciphers = {0}; + + void ssl_shutdie() + { + SSL_shutdown(ssl); + SSL_free(ssl); + zerodie(); + } + void ssl_zerodie() + { + char buf[1024]; + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + ssl_shutdie(); + } + + static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey) + { + out("ZTLS found no client cert in control/clientcert.pem\n"); + ssl_shutdie(); + return 0; /* isn't reached but... */ + } + + 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 + if (ssl) { + r = ssl_timeoutread(timeout,smtpfd,smtpfd,ssl,buf,len); + if (r == -2) { + out("ZTLS connection to "); outhost(); out(" died: "); + ssl_zerodie(); + } + } else + #endif r = timeoutread(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; *************** *** 117,122 **** --- 170,184 ---- int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; + #ifdef TLS + if (ssl) { + r = ssl_timeoutwrite(timeout,smtpfd,smtpfd,ssl,buf,len); + if (r == -2) { + out("ZTLS connection to "); outhost(); out(" died: "); + ssl_zerodie(); + } + } else + #endif r = timeoutwrite(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; *************** *** 186,191 **** --- 248,283 ---- out(append); out(".\n"); outsmtptext(); + + /* TAG */ + #ifdef TLS + if (ssl) { + # ifdef DEBUG + X509 *peercert; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + + /* we want certificate details */ + if (peercert = SSL_get_peer_certificate(ssl)) { + char *str; + + str = X509_NAME_oneline(X509_get_subject_name(peercert),NULL,0); + out("; subject="); out(str); + OPENSSL_free(str); + + str = X509_NAME_oneline(X509_get_issuer_name(peercert),NULL,0); + out("; issuer="); out(str); + OPENSSL_free(str); + + X509_free(peercert); + } + out(";\n"); + # endif + ssl_shutdie(); + } else + #endif + zerodie(); } *************** *** 221,235 **** --- 313,479 ---- unsigned long code; int flagbother; int i; + #ifdef TLS + char *starttls; + char *servercert = 0; + struct stat st; + /* ifdef PORT_SMTP to go along with qmtp patch */ + #ifdef PORT_SMTP + int smtps = (PORT_SMTPS == port); + #else + int smtps = (PORT_SMTPS == smtp_port); + #endif + + if (smtps) + starttls = ""; + else { + #endif if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + #ifdef TLS + substdio_puts(&smtpto,"EHLO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 250) { + #endif + 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"); + #ifdef TLS + } else { + starttls = smtptext.s + 4; /* skipping "250 " */ + do { + starttls += str_chr(starttls, '\n') + 5; + if (starttls + 9 > smtptext.s + smtptext.len) { starttls = 0; break; } + } while (str_diffn(starttls,"STARTTLS\n",9)); + + if (starttls) { /* found STARTTLS */ + substdio_puts(&smtpto,"STARTTLS\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 220) starttls = 0; + } + } + } + + if (partner_fqdn) { + stralloc tmp = {0}; + if (!stralloc_copys(&tmp, "control/tlshosts/")) temp_nomem(); + if (!stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn))) temp_nomem(); + if (!stralloc_catb(&tmp, ".pem", 4)) temp_nomem(); + if (!stralloc_0(&tmp)) temp_nomem(); + if (stat(tmp.s,&st)) /* no such file */ + alloc_free(tmp.s); + else if (starttls) + servercert = tmp.s; + else { + out("ZNo TLS achieved while "); out(tmp.s); out(" exists.\n"); + quit(); + } + } + + if (starttls) { + SSL_CTX *ctx; + + SSL_library_init(); + if (!(ctx = SSL_CTX_new(SSLv23_client_method()))) { + out("ZTLS not available: error initializing ctx: "); + ssl_zerodie(); + } + /* 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 */ + if (stat("control/clientcert.pem",&st) == 0) + if ( + 1 != SSL_CTX_use_RSAPrivateKey_file(ctx, + "control/clientcert.pem",SSL_FILETYPE_PEM) || + 1 != SSL_CTX_use_certificate_chain_file(ctx,"control/clientcert.pem") || + 1 != SSL_CTX_check_private_key(ctx) + ) + SSL_CTX_set_client_cert_cb(ctx, client_cert_cb); + + if (servercert && !SSL_CTX_load_verify_locations(ctx,servercert,NULL)) { + out("ZTLS unable to load "); out(servercert); out("\n"); + zerodie(); + } + + if (!(ssl = SSL_new(ctx))) { + out("ZTLS not available: error initializing ssl: "); + ssl_zerodie(); + } + SSL_CTX_free(ctx); + if (servercert) SSL_set_verify(ssl,SSL_VERIFY_PEER,verify_cb); + /*SSL_set_options(ssl, SSL_OP_NO_TLSv1);*/ + SSL_set_cipher_list(ssl,tlsclientciphers.s); + SSL_set_fd(ssl,smtpfd); + + if (ssl_timeoutconn(timeout,smtpfd,smtpfd,ssl) <= 0) { + out("ZTLS not available: connect "); + if (errno == error_timeout) + { out("timed out\n"); ssl_shutdie(); } + else + { out("failed: "); ssl_zerodie(); } + } + if (servercert) { + /* should also check alternate names */ + char commonName[256]; + X509 *peercert; + int r; + + if ((r = SSL_get_verify_result(ssl)) != X509_V_OK) { + out("ZTLS unable to verify server with "); + out(servercert); out(": "); + out(X509_verify_cert_error_string(r)); out("\n"); + ssl_shutdie(); + } + alloc_free(servercert); + + peercert = SSL_get_peer_certificate(ssl); + X509_NAME_get_text_by_NID( + X509_get_subject_name(peercert), NID_commonName, commonName, 256); + X509_free(peercert); + + r = case_diffs(partner_fqdn,commonName); + /* we also match if the cert has commonName *.domainname */ + /* instead, i would like an implementation of RFC2595, part2.4 */ + if (r && commonName[0] == '*' && commonName[1] == '.') { + char *partner_domain = partner_fqdn + str_chr(partner_fqdn,'.'); + if (*partner_domain) r = case_diffs(partner_domain+1,commonName+2); + } + if (r) { + out("ZTLS connection to "); out(partner_fqdn); + out(" wanted, certificate for "); out(commonName); + out(" received\n"); + ssl_shutdie(); + } + } + + if (smtps) if (smtpcode() != 220) + quit("ZTLS Connected to "," but greeting failed"); + + /* RFC2487 says we should issue EHLO (even if we do not need extensions) */ + /* at the same time, it does not prohibit a server to reject the EHLO */ + /* and make us fallback to HELO */ + substdio_puts(&smtpto,"EHLO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + + 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("ZTLS connected to "," but my name was rejected"); + } + } + #endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); *************** *** 324,329 **** --- 568,579 ---- case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } + #ifdef TLS + if (1 != + control_rldef(&tlsclientciphers, "control/tlsclientciphers",0,"DEFAULT") + ) temp_control(); + if (!stralloc_0(&tlsclientciphers)) temp_nomem(); + #endif } void main(argc,argv) *************** *** 417,422 **** --- 667,675 ---- 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 + partner_fqdn = ip.ix[i].fqdn; + #endif smtp(); /* does not return */ } tcpto_err(&ip.ix[i].ip,errno == error_timeout); *** ../qmail-1.03.orig/qmail-send.c Mon Jun 15 03:53:16 1998 --- qmail-send.c Thu Sep 26 12:05:41 2002 *************** *** 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]; } *** ../qmail-1.03.orig/qmail-smtpd.8 Mon Jun 15 03:53:16 1998 --- qmail-smtpd.8 Thu Sep 26 12:05:41 2002 *************** *** 3,8 **** --- 3,13 ---- qmail-smtpd \- receive mail via SMTP .SH SYNOPSIS .B qmail-smtpd + [ + .I hostname + .I checkprogram + .I subprogram + ] .SH DESCRIPTION .B qmail-smtpd receives mail messages via the Simple Mail Transfer Protocol (SMTP) *************** *** 14,19 **** --- 19,33 ---- see .BR tcp-environ(5) . + If the environment variable + .B SMTPS + is non-empty, + .B qmail-smtpd + starts a TLS session (to support the deprecated SMTPS protocol, + normally on port 465). Otherwise, + .B qmail-smtpd + offers the STARTTLS extension to ESMTP. + .B qmail-smtpd is responsible for counting hops. It rejects any message with 100 or more *************** *** 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 --- 37,65 ---- 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 *************** *** 49,54 **** --- 85,96 ---- .BR @\fIhost , meaning every address at .IR host . + + .TP 5 + .I clientca.pem + A list of Certifying Authority (CA) certificates that are used to verify + the client-presented certificates during a TLS-encrypted session. + .TP 5 .I databytes Maximum number of bytes allowed in a message, *************** *** 151,156 **** --- 193,211 ---- Envelope recipient addresses without @ signs are always allowed through. + + .TP 5 + .I rsa512.pem + If this 512 RSA key is provided, + .B qmail-smtpd + will use it for TLS sessions instead of generaring one on-the-fly. + + .TP 5 + .I servercert.pem + SSL certificate to be presented to clients in + TLS-encrypted sessions. Certifying Authority + (CA) and intermediate certificates can be added at the end of the file. + .TP 5 .I smtpgreeting SMTP greeting message. *************** *** 169,174 **** --- 224,246 ---- .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. + + .TP 5 + .I tlsclients + A list of email addresses. When relay rules would reject an incoming message, + .B qmail-smtpd + can allow it if the client presents a certificate that can be verified against + the CA list in + .I clientca.pem + and the certificate email address is in + .IR tlsclients . + + .TP 5 + .I tlsserverciphers + An OpenSSL cipher string. If the environment variable + .B TLSCIPHERS + is set, it takes precedence. + .SH "SEE ALSO" tcp-env(1), tcp-environ(5), *************** *** 177,179 **** --- 249,254 ---- 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.orig/qmail-smtpd.c Mon Jun 15 03:53:16 1998 --- qmail-smtpd.c Thu Sep 26 12:05:41 2002 *************** *** 23,36 **** --- 23,66 ---- #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" + #include "wait.h" + #include "fd.h" + #define AUTHCRAM #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; + #ifdef TLS + + #include "ssl_timeoutio.h" + #include + + SSL *ssl = NULL; + /* SSL_get_Xfd() are broken */ + int ssl_rfd = -1, ssl_wfd = -1; + + int smtps = 0; + void init_tls(); + + stralloc clientcert = {0}; + stralloc tlsserverciphers = {0}; + + static int verify_cb(int ok, X509_STORE_CTX * ctx) + { + return 1; + } + + #endif + int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; + #ifdef TLS + if (ssl && fd == ssl_wfd) + r = ssl_timeoutwrite(timeout,ssl_rfd,ssl_wfd,ssl,buf,len); + else + #endif r = timeoutwrite(timeout,fd,buf,len); if (r <= 0) _exit(1); return r; *************** *** 51,56 **** --- 81,94 ---- 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(const char *s) + { + out("553 no valid cert for gatewaying"); + if (s) { out(": "); out(s); } + out(" (#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 **** --- 97,111 ---- 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 **** --- 143,150 ---- int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; + int tarpitcount = 0; + int tarpitdelay = 5; void setup() { *************** *** 110,115 **** --- 159,173 ---- 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 **** --- 189,211 ---- if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + + #ifdef TLS + x = env_get("TLSCIPHERS"); + if (x) { if (*x) if (!stralloc_copys(&tlsserverciphers,x)) die_nomem(); } + else if (control_readline(&tlsserverciphers,"control/tlsserverciphers") == -1) + die_control(); + if (!tlsserverciphers.len) + if (!stralloc_copys(&tlsserverciphers,"DEFAULT")) die_nomem(); + if (!stralloc_0(&tlsserverciphers)) die_nomem(); + + x = env_get("SMTPS"); + if (x && *x) { + smtps = 1; + init_tls(); + } + else + #endif dohelo(remotehost); } *************** *** 221,235 **** int flagbarf; /* defined if seenmail */ stralloc mailfrom = {0}; stralloc rcptto = {0}; void smtp_helo(arg) char *arg; { smtp_greet("250 "); out("\r\n"); seenmail = 0; dohelo(arg); } 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() --- 296,323 ---- int flagbarf; /* defined if seenmail */ stralloc mailfrom = {0}; stralloc rcptto = {0}; + int rcptcount; void smtp_helo(arg) char *arg; { smtp_greet("250 "); out("\r\n"); seenmail = 0; dohelo(arg); } + /* ESMTP extensions are published here */ void smtp_ehlo(arg) char *arg; { ! smtp_greet("250-"); ! #ifdef TLS ! if (!ssl) out("\r\n250-STARTTLS"); ! #endif ! #ifdef AUTHCRAM ! out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); ! out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); ! #else ! out("\r\n250-AUTH LOGIN PLAIN"); ! out("\r\n250-AUTH=LOGIN PLAIN"); ! #endif ! out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } void smtp_rset() *************** *** 245,250 **** --- 333,339 ---- 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; { *************** *** 257,266 **** if (!stralloc_0(&addr)) die_nomem(); } else ! if (!addrallowed()) { err_nogateway(); return; } if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); out("250 ok\r\n"); } --- 346,416 ---- if (!stralloc_0(&addr)) die_nomem(); } else ! if (!addrallowed()) { ! #ifdef TLS ! static int checkcert = 1; ! stralloc tlsclients; ! struct constmap maptlsclients; ! ! if (ssl && checkcert) { ! STACK_OF(X509_NAME) *sk; ! checkcert = 0; ! if (control_readfile(&tlsclients,"control/tlsclients",0) == 1) ! switch (constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0)) ! { ! default: ! if (sk = SSL_load_client_CA_file("control/clientca.pem")) { ! checkcert = 1; ! SSL_set_client_CA_list(ssl, sk); ! break; ! } ! constmap_free(&maptlsclients); ! case 0: ! alloc_free(tlsclients.s); ! } ! } ! if (ssl && checkcert) { ! const char *errstr = NULL; ! checkcert = 0; ! do { /* renegotiate with the client */ ! int r; ! X509 *peercert; ! char emailAddress[256]; ! ! SSL_set_verify(ssl,SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,verify_cb); ! if (SSL_renegotiate(ssl) <= 0) break; ! if (SSL_do_handshake(ssl) <= 0) break; ! /* SSL_set_accept_state() clears crypto state so don't use */ ! ssl->state = SSL_ST_ACCEPT; ! if (SSL_do_handshake(ssl) <= 0) break; ! ! if ((r = SSL_get_verify_result(ssl)) != X509_V_OK) { ! errstr = X509_verify_cert_error_string(r); ! break; ! } ! if (!(peercert = SSL_get_peer_certificate(ssl))) break; ! ! X509_NAME_get_text_by_NID(X509_get_subject_name(peercert), ! NID_pkcs9_emailAddress, emailAddress, 256); ! if (!stralloc_copys(&clientcert, emailAddress)) die_nomem(); ! X509_free(peercert); ! ! /* checked out; set relayclient so no need to check next "RCPT TO" */ ! if (constmap(&maptlsclients,clientcert.s,clientcert.len)) ! relayclient = ""; ! } while (0); ! constmap_free(&maptlsclients); ! alloc_free(tlsclients.s); ! if (!relayclient) { err_nogwcert(errstr); return; } ! } ! else ! #endif ! { err_nogateway(); return; } ! } 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,274 **** --- 419,429 ---- { int r; flush(); + #ifdef TLS + if (ssl && fd == ssl_rfd) + r = ssl_timeoutread(timeout,ssl_rfd,ssl_wfd,ssl,buf,len); + else + #endif r = timeoutread(timeout,fd,buf,len); if (r == -1) if (errno == error_timeout) die_alarm(); if (r <= 0) die_read(); *************** *** 378,383 **** --- 533,554 ---- qp = qmail_qp(&qqt); out("354 go ahead\r\n"); + #ifdef TLS + if (ssl) { + static stralloc protocol = {0}; + if (!protocol.len) { + if (!stralloc_copys(&protocol,SSL_get_cipher(ssl))) die_nomem(); + if (!stralloc_catb(&protocol," encrypted SMTP",15)) die_nomem(); + if (smtps) if (!stralloc_append(&protocol,"S")) die_nomem(); + if (clientcert.len) { + if (!stralloc_catb(&protocol," cert ",6)) die_nomem(); + if (!stralloc_catb(&protocol,clientcert.s,clientcert.len)) die_nomem(); + } + if (!stralloc_0(&protocol)) die_nomem(); + } + received(&qqt,protocol.s,local,remoteip,remotehost,remoteinfo,fakehelo); + } else + #endif received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); *************** *** 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(); --- 565,898 ---- out("\r\n"); } + + char unique[FMT_ULONG + FMT_ULONG + 3]; + static stralloc authin = {0}; + static stralloc user = {0}; + static stralloc pass = {0}; + static stralloc resp = {0}; + static stralloc slop = {0}; + char *hostname; + char **childargs; + substdio ssup; + char upbuf[128]; + int authd = 0; + + int authgetl(void) { + int i; + + if (!stralloc_copys(&authin, "")) die_nomem(); + + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; + } + + int authenticate(void) + { + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&resp)) die_nomem(); + + if (fd_copy(2,1) == -1) return err_pipe(); + close(3); + if (pipe(pi) == -1) return err_pipe(); + if (pi[0] != 3) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); + if (substdio_put(&ssup,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write(); + if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write(); + if (substdio_flush(&ssup) == -1) return err_write(); + + close(pi[1]); + byte_zero(pass.s,pass.len); + byte_zero(upbuf,sizeof upbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */ + return 0; /* yes */ + } + + int auth_login(arg) char *arg; + { + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); + } + + int auth_plain(arg) char *arg; + { + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + while (slop.s[id]) id++; /* ignore authorize-id */ + + if (slop.len > id + 1) + if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem(); + if (slop.len > id + user.len + 2) + if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); + } + + #ifdef AUTHCRAM + int auth_cram() + { + int i, r; + char *s; + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&pass,"<")) die_nomem(); + if (!stralloc_cats(&pass,unique)) die_nomem(); + if (!stralloc_cats(&pass,hostname)) die_nomem(); + if (!stralloc_cats(&pass,">")) die_nomem(); + if (b64encode(&pass,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + + i = str_chr(slop.s,' '); + s = slop.s + i; + while (*s == ' ') ++s; + slop.s[i] = 0; + if (!stralloc_copys(&user,slop.s)) die_nomem(); + if (!stralloc_copys(&resp,s)) die_nomem(); + + if (!user.len || !resp.len) return err_input(); + return authenticate(); + } + #endif + + struct authcmd { + char *text; + int (*fun)(); + } authcmds[] = { + { "login", auth_login } + , { "plain", auth_plain } + #ifdef AUTHCRAM + , { "cram-md5", auth_cram } + #endif + , { 0, err_noauth } + }; + + void smtp_auth(arg) + char *arg; + { + int i; + char *cmd = arg; + + if (!hostname || !*childargs) + { + out("503 auth not available (#5.3.3)\r\n"); + return; + } + if (authd) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + authd = 1; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + out("535 authorization failed (#5.7.0)\r\n"); + } + } + + #ifdef TLS + void smtp_tls(arg) char *arg; + { + if (ssl) { + err_unimpl(); + return; + } + if (*arg) { + out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + return; + } + init_tls(); + } + + static RSA *tmp_rsa_cb(ssl,export,keylen) SSL *ssl; int export; int keylen; + { + if (!export) keylen = 512; + if (keylen == 512) { + BIO *in = BIO_new_file("control/rsa512.pem", "r"); + if (in) { + RSA *rsa = PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL); + BIO_free(in); + if (rsa) return rsa; + } + } + return RSA_generate_key(keylen,RSA_F4,NULL,NULL); + } + + void init_tls() + { + SSL_CTX *ctx; + + SSL_library_init(); + + /* a new SSL context with the bare minimum of options */ + if (!(ctx = SSL_CTX_new(SSLv23_server_method()))) { + out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n"); + flush(); + if (smtps) die_read(); + return; + } + if (!SSL_CTX_use_certificate_chain_file(ctx,"control/servercert.pem")) { + out("454 TLS not available: missing certificate (#4.3.0)\r\n"); + flush(); + if (smtps) die_read(); + SSL_CTX_free(ctx); + return; + } + SSL_CTX_load_verify_locations(ctx,"control/clientca.pem",NULL); + + /* a new SSL object, with the rest added to it directly to avoid copying */ + if (!(ssl = SSL_new(ctx))) die_read(); + SSL_CTX_free(ctx); + if (!SSL_use_RSAPrivateKey_file(ssl,"control/servercert.pem",SSL_FILETYPE_PEM)) { + out("454 TLS not available: missing RSA private key (#4.3.0)\r\n"); + flush(); + if (smtps) die_read(); + return; + } + SSL_set_tmp_rsa_callback(ssl,tmp_rsa_cb); + SSL_set_cipher_list(ssl,tlsserverciphers.s); + SSL_set_verify(ssl,SSL_VERIFY_NONE,verify_cb); + + if (!smtps) { out("220 ready for tls\r\n"); flush(); } + + ssl_rfd = substdio_fileno(&ssin); + SSL_set_rfd(ssl,ssl_rfd); + ssl_wfd = substdio_fileno(&ssout); + SSL_set_wfd(ssl,ssl_wfd); + + if (ssl_timeoutaccept(timeout,ssl_rfd,ssl_wfd,ssl) <= 0) { + SSL_free(ssl); ssl = 0; /* so that out() doesn't go via SSL_write() */ + /* cleartext response is not part of any standard, nor is any other response here */ + out("454 TLS not available: connection "); + if (errno == error_timeout) + out("timed out"); + else + out("failed"); + out(" (#4.3.0)\r\n"); + flush(); + die_read(); + } + + /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ + 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; { + hostname = argv[1]; + childargs = argv + 2; + sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); *** ../qmail-1.03.orig/qmail.c Mon Jun 15 03:53:16 1998 --- qmail.c Thu Sep 26 12:05:41 2002 *************** *** 6,19 **** #include "fd.h" #include "qmail.h" #include "auto_qmail.h" ! static char *binqqargs[2] = { "bin/qmail-queue", 0 } ; int qmail_open(qq) struct qmail *qq; { int pim[2]; int pie[2]; if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } --- 6,30 ---- #include "fd.h" #include "qmail.h" #include "auto_qmail.h" + #include "env.h" ! static char *binqqargs[2] = { 0, 0 } ; ! ! static void setup_qqargs() ! { ! if(!binqqargs[0]) ! binqqargs[0] = env_get("QMAILQUEUE"); ! if(!binqqargs[0]) ! binqqargs[0] = "bin/qmail-queue"; ! } int qmail_open(qq) struct qmail *qq; { int pim[2]; int pie[2]; + + setup_qqargs(); if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } *** ../qmail-1.03.orig/spawn.c Mon Jun 15 03:53:16 1998 --- spawn.c Thu Sep 26 12:05:41 2002 *************** *** 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); *** ../qmail-1.03.orig/ssl_timeoutio.c Thu Sep 26 13:16:59 2002 --- ssl_timeoutio.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,244 ---- + #include "select.h" + #include "error.h" + #include "ndelay.h" + #include + + int ssl_timeoutaccept(t,rfd,wfd,ssl) long t; int rfd; int wfd; SSL *ssl; + { + int r; + int n = rfd + 1; + int maxfd = (rfd > wfd ? rfd : wfd) + 1; + + fd_set rfds, wfds; + fd_set *pwfds = (fd_set *) 0; + struct timeval tv; + long end = t + time((long *) 0); + + /* if connection is established, keep it that way */ + if (ndelay_on(rfd) == -1) return -1; + if (ndelay_on(wfd) == -1) return -1; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(rfd,&rfds); + + /* number of descriptors that changes status */ + while (0 < (n = select(n,&rfds,pwfds,(fd_set *) 0,&tv))) + { + r = SSL_accept(ssl); + if (r > 0) { + SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + return r; + } + + switch (SSL_get_error(ssl, r)) + { + case SSL_ERROR_WANT_READ: + pwfds = (fd_set *) 0; + n = rfd + 1; + break; + case SSL_ERROR_WANT_WRITE: + pwfds = &wfds; + FD_ZERO(&wfds); + FD_SET(wfd,&wfds); + n = maxfd; + break; + default: + /* some other error */ + ndelay_off(rfd); + ndelay_off(wfd); + return -2; + } + + if ((t = end - time((long *)0)) < 0) break; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(rfd,&rfds); + } + + ndelay_off(rfd); + ndelay_off(wfd); + if (n != -1) errno = error_timeout; + return -1; + } + + int ssl_timeoutconn(t,rfd,wfd,ssl) long t; int rfd; int wfd; SSL *ssl; + { + int r; + int n = wfd + 1; + int maxfd = (rfd > wfd ? rfd : wfd) + 1; + + fd_set rfds, wfds; + fd_set *prfds = (fd_set *) 0; + struct timeval tv; + long end = t + time((long *) 0); + + /* if connection is established, keep it that way */ + if (ndelay_on(rfd) == -1) return -1; + if (ndelay_on(wfd) == -1) return -1; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(wfd,&wfds); + + /* number of descriptors that changes status */ + while (0 < (n = select(n,prfds,&wfds,(fd_set *) 0,&tv))) + { + r = SSL_connect(ssl); + if (r > 0) { + SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + return r; + } + + switch (SSL_get_error(ssl, r)) + { + case SSL_ERROR_WANT_READ: + /* try again as SSL_write() might be re-negotiating */ + prfds = &rfds; + FD_ZERO(&rfds); + FD_SET(rfd,&rfds); + n = maxfd; + break; + case SSL_ERROR_WANT_WRITE: + /* try again as network write operation would block */ + prfds = (fd_set *) 0; + n = wfd + 1; + break; + default: + /* some other error */ + ndelay_off(rfd); + ndelay_off(wfd); + return -2; + } + + if ((t = end - time((long *)0)) < 0) break; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(wfd,&wfds); + } + + ndelay_off(rfd); + ndelay_off(wfd); + if (n != -1) errno = error_timeout; + return -1; + } + + int ssl_timeoutread(t,rfd,wfd,ssl,buf,len) + long t; int rfd; int wfd; SSL *ssl; char *buf; int len; + { + int r, n, maxfd; + fd_set rfds, wfds; + fd_set *pwfds = (fd_set *) 0; + struct timeval tv; + long end; + + if (SSL_pending(ssl)) + return SSL_read(ssl,buf,len); + + n = rfd + 1; + maxfd = (rfd > wfd ? rfd : wfd) + 1; + end = t + time((long *)0); + + do { + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(rfd,&rfds); + + n = select(n,&rfds,pwfds,(fd_set *) 0,&tv); + if (n == -1) return -1; + if (n == 0) break; /* timed out */ + + r = SSL_read(ssl,buf,len); + if (r > 0) return r; + + switch (SSL_get_error(ssl, r)) + { + case SSL_ERROR_WANT_READ: + /* try again as an incomplete record has been read */ + pwfds = (fd_set *) 0; + n = rfd + 1; + break; + case SSL_ERROR_WANT_WRITE: + /* try again as SSL_read() might be re-negotiating */ + pwfds = &wfds; + FD_ZERO(&wfds); + FD_SET(wfd,&wfds); + n = maxfd; + break; + default: + /* some other error */ + return -2; + } + } while (0 < (t = end - time((long *)0))); + + errno = error_timeout; + return -1; + } + + int ssl_timeoutwrite(t,rfd,wfd,ssl,buf,len) + long t; int rfd; int wfd; SSL* ssl; char *buf; int len; + { + int r; + int n = wfd + 1; + int maxfd = (rfd > wfd ? rfd : wfd) + 1; + + fd_set rfds, wfds; + fd_set *prfds = (fd_set *) 0; + struct timeval tv; + long end = t + time((long *) 0); + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(wfd,&wfds); + + /* number of descriptors that changes status */ + while (0 < (n = select(n,prfds,&wfds,(fd_set *) 0,&tv))) + { + r = SSL_write(ssl,buf,len); + if (r > 0) return r; + + switch (SSL_get_error(ssl, r)) + { + case SSL_ERROR_WANT_READ: + /* try again as SSL_write() might be re-negotiating */ + prfds = &rfds; + FD_ZERO(&rfds); + FD_SET(rfd,&rfds); + n = maxfd; + break; + case SSL_ERROR_WANT_WRITE: + /* try again as network write operation would block */ + prfds = (fd_set *) 0; + n = wfd + 1; + break; + default: + /* some other error */ + return -2; + } + + if ((t = end - time((long *)0)) < 0) break; + + tv.tv_sec = t; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(wfd,&wfds); + } + + if (n != -1) errno = error_timeout; + return -1; + } *** ../qmail-1.03.orig/ssl_timeoutio.h Thu Sep 26 13:16:59 2002 --- ssl_timeoutio.h Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,9 ---- + #ifndef SSL_TIMEOUTIO_H + #define SSL_TIMEOUTIO_H + + extern int ssl_timeoutaccept(); + extern int ssl_timeoutconn(); + extern int ssl_timeoutread(); + extern int ssl_timeoutwrite(); + + #endif *** ../qmail-1.03.orig/strpidt.c Thu Sep 26 13:16:59 2002 --- strpidt.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,26 ---- + /* + ** Copyright 1998 - 2000 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + #include "numlib.h" + #include + + static const char rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + char *str_pid_t(pid_t t, char *arg) + { + char buf[NUMBUFSIZE]; + char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); + } *** ../qmail-1.03.orig/strtimet.c Thu Sep 26 13:16:59 2002 --- strtimet.c Thu Sep 26 12:05:41 2002 *************** *** 0 **** --- 1,26 ---- + /* + ** Copyright 1998 - 2000 Double Precision, Inc. + ** See COPYING for distribution information. + */ + + #if HAVE_CONFIG_H + #include "config.h" + #endif + #include "numlib.h" + #include + + static const char rcsid[]="$Id: qmail-1.03-toaster-2.3.patch,v 1.2 2003/10/25 05:06:24 matt Exp $"; + + char *str_time_t(time_t t, char *arg) + { + char buf[NUMBUFSIZE]; + char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); + }