--- EXTTODO-INFO.orig 2006-07-31 09:58:12.000000000 -0300 +++ EXTTODO-INFO 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,11 @@ +Files modified: +Makefile +EXTTODO +FILES +TARGETS +qmail-send.c +qmail-todo.c +qmail-start.c +hier.c +install-big.c + --- EXTTODO.orig 2006-07-31 09:58:12.000000000 -0300 +++ EXTTODO 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,114 @@ +EXTTODO by Claudio Jeker and +Andre Oppermann +(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd. + +The EXTTODO patch is a part of the qmail-ldap patch. +This patches for qmail come with NO WARRANTY. + +These patches are under the BSD license. + +RELEASE: 5. Jan. 2003 + +EXTTODO: +====================== + +TOC: + WHAT DOES IT DO + INSTALL + CONFIG FILES + SETUP + BIG PICTURE + +NEWS: + + This is the first release of the EXTTODO patch. + +================================================================================ + +WHAT DOES IT DO + + The exttodo patch addresses a problem known as the silly qmail (queue) + problem. This problem is found only on system with high injection rates. + + qmail with a big local and remote concurrency could deliver a tremendous + amount of messages but normally this can not be achieved because qmail-send + becomes a bottleneck on those high volumes servers. + qmail-send preprocesses all new messages before distributing them for local + or remote delivering. In one run qmail-send does one todo run but has the + ability to close multiple jobs. Because of this layout qmail-send can not + feed all the new available (local/remote) delivery slots and therefor it is + not possible to achieve the maximum throughput. + This would be a minor problem if one qmail-send run could be done in extreme + short time but because of many file system calls (fsync and (un)link) a todo + run is expensive and throttles the throughput. + + The exttodo patch tries to solve the problem by moving the todo routine into + an external program. This reduces the run time in qmail-send. + + exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares + incoming messages for local and remote delivering (by creating info/ + local/ and remote/ and removing todo/). See also + INTERNALS. As next qmail-todo transmits the to qmail-send which will + add this message into the priority queue which schedules the message for + delivery. + +INSTALL + + To enable the exttodo patch you need to define EXTERNAL_TODO while compiling + qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO). + + NOTE: the exttodo patch can also be used on qmail systems without the + qmail-ldap patch. + +================================================================================ + +CONFIG FILES + + No additional control files are used or needed. + +================================================================================ + +SETUP + + qmail-todo will be started by qmail-start and therefor no additional setup + is needed. + + To verify that exttodo is running just check if qmail-todo is running. + +================================================================================ + +BIG PICTURE + + +-------+ +-------+ + | clean | | clean | + +--0-1--+ +--0-1--+ +-----------+ + trigger ^ | ^ | +->0,1 lspawn | + | | v | v / +-----------+ + +-------+ v +--2-3--+ +--5-6--+ / + | | | | 0<--7 1,2<-+ + | queue |--+--| todo | | send | + | | | | 1-->8 3,4<-+ + +-------+ +-------+ +---0---+ \ + | \ +-----------+ + v +->0,1 rspwan | + +---0---+ +-----------+ + | logger| + +-------+ + +Communication between qmail-send and qmail-todo + +todo -> send: + D[LRB]\0 + Start delivery for new message with id . + the character L, R or B defines the type + of delivery, local, remote or both respectively. + L\0 + Dump string to the logger without adding additional \n or similar. +send -> todo: + H Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains + X Quit ASAP. + +qmail-todo sends "\0" terminated messages whereas qmail-send just send one +character to qmail-todo. + + --- FILES.orig 2006-07-31 09:58:11.000000000 -0300 +++ FILES 2006-07-31 09:58:12.000000000 -0300 @@ -432,3 +432,4 @@ tcp-environ.5 constmap.h constmap.c +qmail-todo.c --- Makefile-cert.mk.orig 2006-07-31 09:58:12.000000000 -0300 +++ Makefile-cert.mk 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,21 @@ +cert-req: req.pem +cert cert-req: QMAIL/control/clientcert.pem + @: + +QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem + ln -s $< $@ + +QMAIL/control/servercert.pem: + PATH=$$PATH:/usr/local/ssl/bin \ + openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@ + chmod 644 $@ + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@ + +req.pem: + PATH=$$PATH:/usr/local/ssl/bin openssl req \ + -new -nodes -out $@ -keyout QMAIL/control/servercert.pem + chmod 644 QMAIL/control/servercert.pem + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem + @echo + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" + @echo "cat signed_req.pem >> QMAIL/control/servercert.pem" --- Makefile.orig 2006-07-31 09:58:11.000000000 -0300 +++ Makefile 2006-07-31 09:58:12.000000000 -0300 @@ -1,5 +1,9 @@ # Don't edit Makefile! Use conf-* for configuration. +DEFINES=-DEXTERNAL_TODO # use to enable external todo + +SSL_LIBS=/usr/lib + SHELL=/bin/sh default: it @@ -136,6 +140,10 @@ 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 \ @@ -703,7 +711,7 @@ hier.o: \ compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h - ./compile hier.c + ./compile $(DEFINES) hier.c home: \ home.sh conf-qmail @@ -755,7 +763,7 @@ install-big.o: \ compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \ fifo.h - ./compile install-big.c + ./compile $(DEFINES) install-big.c install.o: \ compile install.c substdio.h strerr.h error.h open.h readwrite.h \ @@ -808,7 +816,7 @@ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ -binm3 binm3+df +binm3 binm3+df qmail-todo update_tmprsadh load: \ make-load warn-auto.sh systype @@ -890,6 +898,38 @@ 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,12 +1214,15 @@ 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 +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` + 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,11 +1312,13 @@ 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 +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` + 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 @@ -1419,13 +1464,14 @@ nroff -man qmail-qstat.8 > qmail-qstat.0 qmail-queue: \ -load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \ -datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \ -str.a fs.a auto_qmail.o auto_split.o auto_uids.o +load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o qregex.o \ +datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a \ +substdio.a error.a control.o constmap.o str.a fs.a auto_qmail.o \ +auto_split.o auto_uids.o ./load qmail-queue triggerpull.o fmtqfn.o now.o \ - date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ - auto_split.o auto_uids.o + date822fmt.o qregex.o control.o constmap.o datetime.a case.a seek.a \ + ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \ + str.a fs.a auto_qmail.o auto_split.o auto_uids.o qmail-queue.0: \ qmail-queue.8 @@ -1444,6 +1490,7 @@ 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 \ + tls.o ssl_timeoutio.o -L$(SSL_LIBS) -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` @@ -1509,7 +1556,7 @@ scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \ qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \ fmtqfn.h readsubdir.h direntry.h - ./compile qmail-send.c + ./compile $(DEFINES) qmail-send.c qmail-showctl: \ load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \ @@ -1532,17 +1579,18 @@ ./compile qmail-showctl.c qmail-smtpd: \ -load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ +load qmail-smtpd.o rcpthosts.o qregex.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 \ +open.a sig.a case.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ +fs.a auto_qmail.o socket.lib base64.o dns.o dns.lib + ./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + tls.o ssl_timeoutio.o ndelay.a -L$(SSL_LIBS) -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 `cat \ - socket.lib` + alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o \ + base64.o `cat socket.lib` dns.o `cat dns.lib` qmail-smtpd.0: \ qmail-smtpd.8 @@ -1553,7 +1601,7 @@ 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 +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h ./compile qmail-smtpd.c qmail-start: \ @@ -1574,7 +1622,7 @@ qmail-start.o: \ compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h - ./compile qmail-start.c + ./compile $(DEFINES) qmail-start.c qmail-tcpok: \ load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \ @@ -1606,6 +1654,20 @@ fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h ./compile qmail-tcpto.c +qmail-todo: \ +load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \ +readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \ +substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \ + readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + +qmail-todo.o: \ +compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \ +exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \ +scan.h select.h str.h stralloc.h substdio.h trigger.h + ./compile $(DEFINES) qmail-todo.c + qmail-upq: \ warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split cat warn-auto.sh qmail-upq.sh \ @@ -1681,6 +1743,10 @@ constmap.h stralloc.h gen_alloc.h rcpthosts.h ./compile rcpthosts.c +qregex.o: \ +compile qregex.c qregex.h + ./compile qregex.c + readsubdir.o: \ compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ auto_split.h @@ -1827,7 +1893,8 @@ ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ -maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c +maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \ +update_tmprsadh shar -m `cat FILES` > shar chmod 400 shar @@ -2108,6 +2175,19 @@ compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h ./compile timeoutwrite.c +qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a +qmail-remote: tls.o ssl_timeoutio.o +qmail-smtpd.o: tls.h ssl_timeoutio.h +qmail-remote.o: tls.h ssl_timeoutio.h + +tls.o: \ +compile tls.c exit.h error.h + ./compile tls.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,3 +2219,26 @@ wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + +cert cert-req: \ +Makefile-cert + @$(MAKE) -sf $< $@ + +Makefile-cert: \ +conf-qmail conf-users conf-groups Makefile-cert.mk + @cat Makefile-cert.mk \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > $@ + +update_tmprsadh: \ +conf-qmail conf-users conf-groups update_tmprsadh.sh + @cat update_tmprsadh.sh\ + | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > $@ + chmod 755 update_tmprsadh + +tmprsadh: \ +update_tmprsadh + echo "Creating new temporary RSA and DH parameters" + ./update_tmprsadh --- README.auth.orig 2006-07-31 09:58:12.000000000 -0300 +++ README.auth 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,66 @@ +README qmail-smtpd SMTP Authentication +====================================== + + +History: +-------- + +This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch +which itself uses "Mrs. Brisby's" initial code. +Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing +'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison. +Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem +(can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons. +Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar. +Version 0.51 includes the evaluation of the 'Auth' and the 'Size' parameter in the 'Mail From:' command. +Version 0.52 uses DJB functions to copy FDs. +Version 0.56 corrects some minor mistakes displaying the 'Auth' userid. +Version 0.57 uses keyword "ESMTPA" in Received header in case of authentication to comply with RFC 3848. + + +Scope: +------ + +This patch supports RFC 2554 "SMTP Service Extension for Authentication" for qmail-smtpd. +Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration"). +For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html. + + +Installation: +------------- + +* Untar the source in the qmail-1.03 home direcotry. +* Run ./install_auth. +* Modify the compile time option "#define CRAM_MD5" to your needs. +* Re-make qmail. + + +Setup: +------ + +In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module' +PAM to be called by qmail-smtpd; typically + + /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1 + +Since qmail-smtpd does not run as root, checkpassword has to be made sticky. +There is no need to include additionally the hostname in the call. +In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information. + + +Changes wrt. Krysztof Dabrowski's patch: +---------------------------------------- + +* Avoid the 'hostname' in the call of the PAM. +* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5. +* Doesn't close FD 2; thus not inhibiting logging to STDERR. +* Fixed bugs in base64.c. +* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'. +* Evaluation of the (informational) Mail From: < > Auth=username. +* Additional support for the advertised "Size" via 'Mail From: SIZE=123456780' (RFC 1870). +* RFC 3848 conformance for Received header in case of SMTP Auth. + + +Erwin Hoffmann - Cologne 2005-01-23 (www.fehcom.de) + + --- README.qregex.orig 2006-07-31 09:58:12.000000000 -0300 +++ README.qregex 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,203 @@ +QREGEX (v2) 20060423 - README April 23, 2006 +A Regular Expression matching patch for qmail 1.03 and netqmail + + +OVERVIEW: + +qregex adds the ability to match address evelopes via Regular Expressions (REs) +in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name), +`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands. +It follows all the base rules that are set out with qmail (ie using control +files) so it makes for easy integretion into an existing setup (see the +install instructions for more info). The v2 is specified because qregex was +re-written to better conform to the security guarantee set forth by the author +of qmail. The original version used stdio.h and stdlib.h for reading the +control files whereas v2 now uses all stralloc functions which are much more +regulated against buffer overruns and the like. +See: http://cr.yp.to/qmail/guarantee.html + + +FEATURES: + +Features of qregex include: + +1. Performs pattern matching on envelope senders and envelope + recipients against REs in the badmailfrom and badmailto control + files. Two additional control files, badmailfromnorelay and + badmailtonorelay, are used for pattern matching when the + RELAYCLIENT environment variable is not set. + +2. Performs pattern matching on the helo/ehlo host name. Setting the + NOBADHELO environment variable prevents the host name from being + compared to the patterns in the badhelo control file. + +3. Matches to patterns are logged. Setting the LOGREGEX environment + variable causes the matched regex pattern to be included in the log. + +4. Matching is case insensitive. + +5. qregex ignores empty envelope senders. An empty envelope sender is not + compared to the patterns in the badmailfrom and badmailfromnorelay + control files and is always accepted. + + +PLATFORMS: + +qregex has been built and tested on the following platforms. I'm sure it won't +have any problems on any platform that qmail will run on (providing they have +a regex interface) but if you run into problems let me know. + + - OpenBSD 3.x + - FreeBSD 4.x, 5.x + - Mandrake Linux 9.x + - SuSE Linux 8.x + + + +INSTALLATION INSTRUCTIONS: + +Installation is very simple, there is only one requirement. You need to use the +GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html). +(For Solaris 8 users it is installed as 'gpatch') + +- If this is a new setup. +Unpack the qmail archive, cd into the qmail-1.03 directory and run +"patch < /path/to/qregex-.patch". Follow the instructions as per the +included qmail INSTALL file. Once you are done come back to this file and read +the section on the control files. + +If you are using netqmail, then unpack the netqmail archive. Run the collate.sh +script and cd into the resulting netqmail- directory. From there, run +"patch < /path/to/qregex-.patch". Complete the netqmail installation +normally. Once you are done, come back to this file and read the section on the +control files. + +- If this is an existing setup. +FIRST: create your control files (see below). +cd into your existing qmail or netqmail source directory. Run +"patch < /path/to/qregex-.patch" then "make qmail-smtpd". Now run +./qmail-smtpd and test your new rules to make sure they work as expected. + +Install the new binary by cd'ing to /var/qmail/bin and as root (in one command) +copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the +source directory to 'qmail-smtpd'. +(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd) + +You can also optionally just run "make setup check" as it will install the +updated documentation and man pages provided with this patch. Stopping qmail +before doing the "make setup check" is always a good idea. + + +LOGGING: + +qregex will log matches to the patterns in the various control files. Log +messages will take these three forms depending on which control file was +matched: + +badhelo +qmail-smtpd: badhelo: at + +badmailfrom and badmailfromnorelay +qmail-smtpd: badmailfrom: at + +badmailto and badmailtonorelay +qmail-smtpd: badmailto: at + +When the LOGREGEX environment variable is set, the matched pattern will +be included in the log. Log messages will have the regex pattern appended +to them. For example, a badhelo log message will look like this: + +qmail-smtpd: badhelo: at matches pattern: + + +CONTROL FILES: + +qregex provides you with five control files. None of these control files +is mandatory and you can use them in any combination you choose in your setup. + +The "control/badmailfrom" and "control/badmailto" files contain your REs for +matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope +recipient) smtp commands respectively. +The "control/badmailfromnorelay" and "control/badmailtonorelay" match against +the same commands but are read only when the RELAYCLIENT environment variable +is not set. +The "control/badhelo" file matches against the 'helo/ehlo' smtp command. + +If you prefer you can symlink the badmailfrom and badmailto control files +(ln -s badmailfrom badmailto) and maintain fewer sets of rules. Beware +this might cause problems in certain setups. + + Here's an example "badhelo" file. + ----------------------------------- + # block host strings with no dot (not a FQDN) + !\. + ----------------------------------- + + An example "badmailfrom" file. + ----------------------------------- + # this will drop everything containing the string + # bad.domain.com or Bad.Domain.Com or BAD.domain.COM + bad\.domain\.com + # force users to fully qualify themselves + # (i.e. deny "user", accept "user@domain") + !@ + ----------------------------------- + + And "badmailto" (a little more interesting) + ----------------------------------- + # must not contain invalid characters, brakets or multiple @'s + [!%#:*^(){}] + @.*@ + ----------------------------------- + +You can use the non-RE character '!' to start an RE as a signal to qregex to +negate the action. As used above in the badmailfrom file, by negating the '@' +symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever +the address doesn't contain an @ symbol. When used inside a bracket expression, +the '!' character looses this special meaning. This is shown in the badmailto +example. + +The norelay control files follow the same rules as the other control files but +are intended to address two specific scenarios. +The badmailfromnorelay file can be used to block mail trying to spoof a domain +hosted on your mail server. It prevents a mail client that is not allowed to +relay email through your server from using one of your hosted domains as its +envelope sender. +The badmailtonorelay file can be used to create email addresses that cannot +receive mail from any source not allowed to relay email through your server. +This is handy for creating email addresses for use only within your own +domain(s) that can't receive spam from the world at large. + + +INTERNALS: + +qregex (or regexmatch as the function is called) will be called during the +`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c". +When called, it will read the proper control file then one by one compile and +execute the regex on the string passed into qmail-smtpd. If the regex matches +it returns TRUE (1) and the qmail-smtpd process will deny the user the ability +to continue. If you change anything and think it betters this patch please +send me a new diff file so I can take a peek. + + +CONTACT: +qregex is maintained by: + Andrew St. Jean + andrew@arda.homeunix.net + www.arda.homeunix.net/store/qmail/ + +Contributers to qregex: + Jeremy Kitchen + kitchen at scriptkitchen dot com + http://www.scriptkitchen.com/qmail + + Alex Pleiner + alex@zeitform.de + zeitform Internet Dienste + http://www.zeitform.de/ + + Thanos Massias + +Original qregex patch written by: + Evan Borgstrom + evan at unixpimps dot org --- README.tap.orig 2006-07-31 09:58:12.000000000 -0300 +++ README.tap 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,28 @@ +qmail provides the ability to make a copy of each email that flows through the system. +This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2 + +The qmail tap patch adds additional functionality: +1) Specify which email addresses to tap using a regex style control file. With the + regex function, you can specify full domains or individual email addresses. + +2) Specify which email address to send the emails to. + +3) Qmail does not need to be restated to when the taps control file is changed. + +The regex match is applied to both the to and from email addresses. So email +sent to or from the addresses will be copied. Matching is case insensitive. +If there are multiple matches, the first match is used. + +The queue tap patch adds a new control file: + +/var/qmail/control/taps +Contains a regex style list of addresses to tap and the email +address of where you want the copy sent to. + +Examples: +a) To tap a whole domain add a line like: +.*@domain.com:joe@example.com + + +b) To tap an individual email address add a line like: +user@domain.com:other@example.com --- TARGETS.orig 1998-06-15 07:53:16.000000000 -0300 +++ TARGETS 2006-07-31 09:58:12.000000000 -0300 @@ -10,11 +10,20 @@ qmail.o quote.o now.o +base64.o gfrom.o myctime.o 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,6 +177,8 @@ constmap.o timeoutread.o timeoutwrite.o +tls.o +ssl_timeoutio.o timeoutconn.o tcpto.o dns.o @@ -320,6 +331,7 @@ binm2+df binm3 binm3+df +Makefile-cert it qmail-local.0 qmail-lspawn.0 @@ -385,3 +397,7 @@ man setup check +qmail-todo.o +qmail-todo +qregex.o +update_tmprsadh --- base64.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ base64.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,122 @@ +#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 p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while(in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + out->len = (n * 3) - p; + if (!stralloc_ready(out,out->len)) return -1; + s = out->s; + + for(i = 0; i < n - 1 ; i++) { + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for(i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; + s = out->s; + + for (i = 0;i < in->len;i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + return 0; +} --- base64.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ base64.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif --- case_startb.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ case_startb.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,21 @@ +#include "case.h" + +int case_startb(s,len,t) +register char *s; +unsigned int len; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (!len) return 0; + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + if (x != y) return 0; + } +} --- chkspawn.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ chkspawn.c 2006-07-31 09:58:12.000000000 -0300 @@ -22,8 +22,8 @@ _exit(1); } - if (auto_spawn > 255) { - substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n"); + if (auto_spawn > 65000) { + substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n"); substdio_flush(subfderr); _exit(1); } --- conf-cc.orig 1998-06-15 07:53:16.000000000 -0300 +++ conf-cc 2006-07-31 09:58:12.000000000 -0300 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DTLS=20060104 -I/usr/include/openssl This will be used to compile .c files. --- conf-spawn.orig 1998-06-15 07:53:16.000000000 -0300 +++ conf-spawn 2006-07-31 09:58:12.000000000 -0300 @@ -1,4 +1,4 @@ -120 +500 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 --- date822fmt.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ date822fmt.c 2006-07-31 09:58:12.000000000 -0300 @@ -1,3 +1,4 @@ +#include #include "datetime.h" #include "fmt.h" #include "date822fmt.h" @@ -12,18 +13,51 @@ { unsigned int i; unsigned int len; + time_t now; + datetime_sec utc; + datetime_sec local; + struct tm *tm; + struct datetime new_dt; + int minutes; + + utc = datetime_untai(dt); + now = (time_t)utc; + tm = localtime(&now); + new_dt.year = tm->tm_year; + new_dt.mon = tm->tm_mon; + new_dt.mday = tm->tm_mday; + new_dt.hour = tm->tm_hour; + new_dt.min = tm->tm_min; + new_dt.sec = tm->tm_sec; + local = datetime_untai(&new_dt); + len = 0; - i = fmt_uint(s,dt->mday); len += i; if (s) s += i; + i = fmt_uint(s,new_dt.mday); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i; + i = fmt_str(s,montab[new_dt.mon]); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i; + i = fmt_uint(s,new_dt.year + 1900); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.hour,2); len += i; if (s) s += i; i = fmt_str(s,":"); len += i; if (s) s += i; - i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.min,2); len += i; if (s) s += i; i = fmt_str(s,":"); len += i; if (s) s += i; - i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i; - i = fmt_str(s," -0000\n"); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.sec,2); len += i; if (s) s += i; + + if (local < utc) { + minutes = (utc - local + 30) / 60; + i = fmt_str(s," -"); len += i; if (s) s += i; + i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i; + i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i; + } + else { + minutes = (local - utc + 30) / 60; + i = fmt_str(s," +"); len += i; if (s) s += i; + i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i; + i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i; + } + + i = fmt_str(s,"\n"); len += i; if (s) s += i; + return len; } --- dns.c.orig 2006-07-31 09:58:11.000000000 -0300 +++ dns.c 2006-07-31 09:58:12.000000000 -0300 @@ -19,10 +19,12 @@ static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } -static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response; +static struct { unsigned char *buf; } response; +static int responsebuflen = 0; static int responselen; static unsigned char *responseend; static unsigned char *responsepos; +static u_long saveresoptions; static int numanswers; static char name[MAXDNAME]; @@ -43,18 +45,33 @@ errno = 0; if (!stralloc_copy(&glue,domain)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; - responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response)); + if (!responsebuflen) + if (response.buf = (unsigned char *)alloc(PACKETSZ+1)) + responsebuflen = PACKETSZ+1; + else return DNS_MEM; + + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + if ((responselen >= responsebuflen) || + (responselen > 0 && (((HEADER *)response.buf)->tc))) + { + if (responsebuflen < 65536) + if (alloc_re(&response.buf, responsebuflen, 65536)) + responsebuflen = 65536; + else return DNS_MEM; + saveresoptions = _res.options; + _res.options |= RES_USEVC; + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + _res.options = saveresoptions; + } if (responselen <= 0) { if (errno == ECONNREFUSED) return DNS_SOFT; if (h_errno == TRY_AGAIN) return DNS_SOFT; return DNS_HARD; } - if (responselen >= sizeof(response)) - responselen = sizeof(response); responseend = response.buf + responselen; responsepos = response.buf + sizeof(HEADER); - n = ntohs(response.hdr.qdcount); + n = ntohs(((HEADER *)response.buf)->qdcount); while (n-- > 0) { i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); @@ -64,7 +81,7 @@ if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } - numanswers = ntohs(response.hdr.ancount); + numanswers = ntohs(((HEADER *)response.buf)->ancount); return 0; } @@ -267,12 +284,11 @@ int pref; { int r; - struct ip_mx ix; + struct ip_mx ix = {0}; 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; @@ -291,9 +307,16 @@ ix.ip = ip; ix.pref = pref; if (r == DNS_SOFT) return DNS_SOFT; - if (r == 1) + 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; } @@ -313,7 +336,7 @@ { int r; struct mx { stralloc sa; unsigned short p; } *mx; - struct ip_mx ix; + struct ip_mx ix = {0}; int nummx; int i; int j; @@ -325,7 +348,6 @@ 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; --- hier.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ hier.c 2006-07-31 09:58:12.000000000 -0300 @@ -55,6 +55,8 @@ d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700); dsplit("queue/mess",auto_uidq,0750); + dsplit("queue/todo",auto_uidq,0750); + dsplit("queue/intd",auto_uidq,0700); dsplit("queue/info",auto_uids,0700); dsplit("queue/local",auto_uids,0700); dsplit("queue/remote",auto_uids,0700); @@ -76,6 +78,7 @@ c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); @@ -108,6 +111,9 @@ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); +#ifdef EXTERNAL_TODO + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); +#endif c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); @@ -143,6 +149,9 @@ c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); +#ifdef TLS + c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755); +#endif c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); --- install-big.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ install-big.c 2006-07-31 09:58:12.000000000 -0300 @@ -76,6 +76,7 @@ c(auto_qmail,"boot","binm3+df",auto_uido,auto_gidq,0755); c(auto_qmail,"doc","FAQ",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","UPGRADE",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","SENDMAIL",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","INSTALL",auto_uido,auto_gidq,0644); @@ -108,6 +109,9 @@ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); +#ifdef EXTERNAL_TODO + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); +#endif c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); --- ipalloc.h.orig 1998-06-15 07:53:16.000000000 -0300 +++ ipalloc.h 2006-07-31 09:58:12.000000000 -0300 @@ -3,7 +3,15 @@ #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" --- maildirflags.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildirflags.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,23 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include +#include + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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); +} --- maildirgetquota.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildirgetquota.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +#define QUOTABUFSIZE 256 + +int maildir_getquota(const char *, char [QUOTABUFSIZE]); + +#ifdef __cplusplus +} +#endif + +#endif --- maildirmisc.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildirmisc.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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 --- maildiropen.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildiropen.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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); +} --- maildirparsequota.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildirparsequota.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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); +} --- maildirquota.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildirquota.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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)) + { + unlink(newmaildirsizename); + free(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); +} --- maildirquota.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ maildirquota.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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 --- numlib.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ numlib.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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 --- overmaildirquota.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ overmaildirquota.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,41 @@ +/* +** 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: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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-clean.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-clean.c 2006-07-31 09:58:12.000000000 -0300 @@ -73,22 +73,26 @@ if (line.len < 7) { respond("x"); continue; } if (line.len > 100) { respond("x"); continue; } if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */ - for (i = 5;i < line.len - 1;++i) + for (i = line.len - 2;i > 4;--i) + { + if (line.s[i] == '/') break; if ((unsigned char) (line.s[i] - '0') > 9) { respond("x"); continue; } - if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; } + } + if (line.s[i] == '/') + if (!scan_ulong(line.s + i + 1,&id)) { respond("x"); continue; } if (byte_equal(line.s,5,"foop/")) { #define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \ if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; } - U("intd/",0) + U("intd/",1) U("mess/",1) respond("+"); } else if (byte_equal(line.s,4,"todo/")) { - U("intd/",0) - U("todo/",0) + U("intd/",1) + U("todo/",1) respond("+"); } else --- qmail-control.9.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-control.9 2006-07-31 09:58:12.000000000 -0300 @@ -20,7 +20,11 @@ Comments are allowed in +.IR badhelo , .IR badmailfrom , +.IR badmailfromnorelay , +.IR badmailto , +.IR badmailtonorelay , .IR locals , .IR percenthack , .IR qmqpservers , @@ -40,14 +44,22 @@ .ta 5c 10c control default used by +.I badhelo \fR(none) \fRqmail-smtpd .I badmailfrom \fR(none) \fRqmail-smtpd +.I badmailfromnorelay \fR(none) \fRqmail-smtpd +.I badmailto \fR(none) \fRqmail-smtpd +.I badmailtonorelay \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 .I defaulthost \fIme \fRqmail-inject .I databytes \fR0 \fRqmail-smtpd +.I dh1024.pem \fR(none) \fRqmail-smtpd +.I dh512.pem \fR(none) \fRqmail-smtpd .I doublebouncehost \fIme \fRqmail-send .I doublebounceto \fRpostmaster \fRqmail-send .I envnoathost \fIme \fRqmail-send @@ -55,23 +67,32 @@ .I idhost \fIme \fRqmail-inject .I localiphost \fIme \fRqmail-smtpd .I locals \fIme \fRqmail-send +.I mfcheck \fR0 \fRqmail-smtpd .I morercpthosts \fR(none) \fRqmail-smtpd .I percenthack \fR(none) \fRqmail-send .I plusdomain \fIme \fRqmail-inject .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 taps \fR(none) \fRqmail-queue .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 .SH "SEE ALSO" qmail-inject(8), qmail-qmqpc(8), +qmail-queue(8), qmail-remote(8), qmail-send(8), qmail-showctl(8), --- qmail-local.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ qmail-local.c 2006-07-31 09:58:12.000000000 -0300 @@ -66,6 +66,7 @@ char buf[1024]; char outbuf[1024]; +#define QUOTABUFSIZE 256 /* child process */ @@ -86,9 +87,15 @@ 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,7 +106,10 @@ 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; + 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,6 +169,7 @@ 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-pop3d.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ qmail-pop3d.c 2006-07-31 09:58:12.000000000 -0300 @@ -16,6 +16,11 @@ #include "readwrite.h" #include "timeoutread.h" #include "timeoutwrite.h" +#include +#include "maildirquota.h" +#include "maildirmisc.h" + +#define QUOTABUFSIZE 256 void die() { _exit(0); } @@ -45,19 +50,15 @@ { 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"); + substdio_puts(&ssout,"-ERR "); + substdio_puts(&ssout,s); + substdio_puts(&ssout,"\r\n"); flush(); } @@ -73,7 +74,7 @@ void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } -void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); } void printfn(fn) char *fn; { @@ -153,11 +154,11 @@ total = 0; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,numm)); - puts(" "); + substdio_puts(&ssout," "); put(strnum,fmt_ulong(strnum,total)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } @@ -171,18 +172,41 @@ void pop3_last(arg) char *arg; { - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,last)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } void pop3_quit(arg) char *arg; { 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) { - if (unlink(m[i].fn) == -1) err_nounlink(); + 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,6 +216,21 @@ 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(0); die(); } @@ -222,10 +261,10 @@ int flaguidl; { put(strnum,fmt_uint(strnum,i + 1)); - puts(" "); + substdio_puts(&ssout," "); if (flaguidl) printfn(m[i].fn); else put(strnum,fmt_ulong(strnum,m[i].size)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); } void dolisting(arg,flaguidl) char *arg; int flaguidl; @@ -234,7 +273,7 @@ if (*arg) { i = msgno(arg); if (i == -1) return; - puts("+OK "); + substdio_puts(&ssout,"+OK "); list(i,flaguidl); } else { @@ -242,7 +281,7 @@ for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); - puts(".\r\n"); + substdio_puts(&ssout,".\r\n"); } flush(); } --- qmail-qstat.sh.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-qstat.sh 2006-07-31 09:58:12.000000000 -0300 @@ -1,7 +1,7 @@ cd QMAIL messdirs=`echo queue/mess/* | wc -w` messfiles=`find queue/mess/* -print | wc -w` -tododirs=`echo queue/todo | wc -w` -todofiles=`find queue/todo -print | wc -w` +tododirs=`echo queue/todo/* | wc -w` +todofiles=`find queue/todo/* -print | wc -w` echo messages in queue: `expr $messfiles - $messdirs` echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs` --- qmail-queue.8.orig 2006-07-31 09:58:12.000000000 -0300 +++ qmail-queue.8 2006-07-31 09:58:12.000000000 -0300 @@ -40,6 +40,12 @@ However, the recipients probably expect to see a proper header, as described in .BR qmail-header(5) . +.SH "CONTROL FILES" +.TP 5 +.I taps +Should contain regex syntax of email addresses to tap and +the associated email address to send the copy to. The two +fields should be separated by a colon. Programs included with qmail which invoke .B qmail-queue --- qmail-queue.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-queue.c 2006-07-31 09:58:12.000000000 -0300 @@ -16,6 +16,8 @@ #include "auto_uids.h" #include "date822fmt.h" #include "fmtqfn.h" +#include "stralloc.h" +#include "constmap.h" #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ #define ADDR 1003 @@ -25,6 +27,14 @@ char outbuf[256]; struct substdio ssout; +int tapok = 0; +stralloc tap = {0}; +struct constmap maptap; +stralloc chkaddr = {0}; +int tapped; +stralloc tapaddr = {0}; +stralloc controlfile = {0}; + datetime_sec starttime; struct datetime dt; unsigned long mypid; @@ -175,13 +185,20 @@ alarm(DEATH); + stralloc_copys( &controlfile, auto_qmail); + stralloc_cats( &controlfile, "/control/taps"); + stralloc_0( &controlfile); + tapok = control_readfile(&tap,controlfile.s,0); + if (tapok == -1) die(65); + if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65); + pidopen(); if (fstat(messfd,&pidst) == -1) die(63); messnum = pidst.st_ino; messfn = fnnum("mess/",1); - todofn = fnnum("todo/",0); - intdfn = fnnum("intd/",0); + todofn = fnnum("todo/",1); + intdfn = fnnum("intd/",1); if (link(pidfn,messfn) == -1) die(64); if (unlink(pidfn) == -1) die(63); @@ -219,14 +236,28 @@ if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (ch != 'F') die(91); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); + stralloc_0(&chkaddr); for (len = 0;len < ADDR;++len) { + if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1); + else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_put(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } if (len >= ADDR) die(11); + /* check the from address */ + stralloc_0(&chkaddr); + if (tapped == 0 && tapcheck()==1 ) { + tapped = 1; + if ( tapaddr.len > 0 ) { + if (substdio_bput(&ssout,"T",1) == -1) die_write(); + if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + } + } + if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write(); for (;;) @@ -237,10 +268,24 @@ if (substdio_bput(&ssout,&ch,1) == -1) die_write(); for (len = 0;len < ADDR;++len) { + if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1); + else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } + + /* check the to address */ + stralloc_0(&chkaddr); + if (tapped == 0 && tapcheck()==1 ) { + tapped = 1; + if ( tapaddr.len > 0 ) { + if (substdio_bput(&ssout,"T",1) == -1) die_write(); + if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + } + } + if (len >= ADDR) die(11); } @@ -252,3 +297,42 @@ triggerpull(); die(0); } + +int tapcheck() +{ + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + stralloc curregex = {0}; + char tmpbuf[200]; + + while (j < tap.len) { + i = j; + while ((tap.s[i] != ':') && (i < tap.len)) i++; + if (tap.s[j] == '!') { + negate = 1; + j++; + } + stralloc_copys(&tapaddr, &tap.s[i+1]); + + stralloc_copyb(&curregex,tap.s + j,(i - j)); + stralloc_0(&curregex); + x = matchregex(chkaddr.s, curregex.s, tmpbuf); + + while ((tap.s[i] != '\0') && (i < tap.len)) i++; + + if ((negate) && (x == 0)) { + return 1; + } + if (!(negate) && (x > 0)) { + return 1; + } + j = i + 1; + negate = 0; + + + } + return 0; +} + --- qmail-remote.8.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-remote.8 2006-07-31 09:58:12.000000000 -0300 @@ -114,6 +114,10 @@ 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. @@ -123,6 +127,16 @@ otherwise .B qmail-remote refuses to run. + +.TP 5 +.I notlshosts/ +.B qmail-remote +will not try TLS on servers for which this file exists +.RB ( +is the fully-qualified domain name of the server). +.IR (tlshosts/.pem +takes precedence over this file however). + .TP 5 .I smtproutes Artificial SMTP routes. @@ -156,6 +170,8 @@ 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,6 +211,33 @@ .B qmail-remote will wait for each response from the remote SMTP server. Default: 1200. + +.TP 5 +.I tlsclientciphers +A set of OpenSSL client cipher strings. Multiple ciphers +contained in a string should be separated by a colon. + +.TP 5 +.I tlshosts/.pem +.B qmail-remote +requires TLS authentication from servers for which this certificate exists +.RB ( +is the fully-qualified domain name of the server). One of the +.I dNSName +or the +.I CommonName +attributes have to match. + +.B WARNING: +this option may cause mail to be delayed, bounced, doublebounced, or lost. + +.TP 5 +.I tlshosts/exhaustivelist +if this file exists +no TLS will be tried on hosts other than those for which a file +.B tlshosts/.pem +exists. + .SH "SEE ALSO" addresses(5), envelopes(5), --- qmail-remote.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-remote.c 2006-07-31 09:58:12.000000000 -0300 @@ -48,6 +48,17 @@ struct ip_address partner; +#ifdef TLS +# include +# include "tls.h" +# include "ssl_timeoutio.h" +# include +# define EHLO 1 + +int tls_init(); +const char *ssl_err_str = 0; +#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); } void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } @@ -99,6 +110,9 @@ outhost(); out(" but connection died. "); if (flagcritical) out("Possible duplicate! "); +#ifdef TLS + if (ssl_err_str) { out(ssl_err_str); out(" "); } +#endif out("(#4.4.2)\n"); zerodie(); } @@ -110,6 +124,12 @@ 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 < 0) ssl_err_str = ssl_error_str(); + } else +#endif r = timeoutread(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -117,6 +137,12 @@ 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 < 0) ssl_err_str = ssl_error_str(); + } else +#endif r = timeoutwrite(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -163,6 +189,64 @@ return code; } +#ifdef EHLO +saa ehlokw = {0}; /* list of EHLO keywords and parameters */ +int maxehlokwlen = 0; + +unsigned long ehlo() +{ + stralloc *sa; + char *s, *e, *p; + unsigned long code; + + if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; + ehlokw.len = 0; + +# ifdef MXPS + if (type == 's') return 0; +# endif + + substdio_puts(&smtpto, "EHLO "); + substdio_put(&smtpto, helohost.s, helohost.len); + substdio_puts(&smtpto, "\r\n"); + substdio_flush(&smtpto); + + code = smtpcode(); + if (code != 250) return code; + + s = smtptext.s; + while (*s++ != '\n') ; /* skip the first line: contains the domain */ + + e = smtptext.s + smtptext.len - 6; /* 250-?\n */ + while (s <= e) + { + int wasspace = 0; + + if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); + sa = ehlokw.sa + ehlokw.len++; + if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; + + /* smtptext is known to end in a '\n' */ + for (p = (s += 4); ; ++p) + if (*p == '\n' || *p == ' ' || *p == '\t') { + if (!wasspace) + if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); + if (*p == '\n') break; + wasspace = 1; + } else if (wasspace == 1) { + wasspace = 0; + s = p; + } + s = ++p; + /* keyword should consist of alpha-num and '-' + * broken AUTH might use '=' instead of space */ + for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } + } + + return 250; +} +#endif + void outsmtptext() { int i; @@ -179,6 +263,11 @@ char *prepend; char *append; { +#ifdef TLS + /* shouldn't talk to the client unless in an appropriate state */ + int state = ssl ? ssl->state : SSL_ST_BEFORE; + if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE)) +#endif substdio_putsflush(&smtpto,"QUIT\r\n"); /* waiting for remote side is just too ridiculous */ out(prepend); @@ -186,6 +275,30 @@ out(append); out(".\n"); outsmtptext(); + +#if defined(TLS) && defined(DEBUG) + if (ssl) { + X509 *peercert; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_get_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 + zerodie(); } @@ -214,6 +327,194 @@ substdio_flush(&smtpto); } +#ifdef TLS +char *partner_fqdn = 0; + +# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") +void tls_quit(const char *s1, const char *s2) +{ + out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT; +} +# define tls_quit_error(s) tls_quit(s, ssl_error()) + +int match_partner(const char *s, int len) +{ + if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; + /* we also match if the name is *.domainname */ + if (*s == '*') { + const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); + if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; + } + return 0; +} + +/* don't want to fail handshake if certificate can't be verified */ +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } + +int tls_init() +{ + int i; + SSL *myssl; + SSL_CTX *ctx; + stralloc saciphers = {0}; + const char *ciphers, *servercert = 0; + + if (partner_fqdn) { + struct stat st; + stralloc tmp = {0}; + if (!stralloc_copys(&tmp, "control/tlshosts/") + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) + || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); + if (stat(tmp.s, &st) == 0) + servercert = tmp.s; + else { + if (!stralloc_copys(&tmp, "control/notlshosts/") + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) + temp_nomem(); + if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || + (stat(tmp.s, &st) == 0)) { + alloc_free(tmp.s); + return 0; + } + alloc_free(tmp.s); + } + } + + if (!smtps) { + stralloc *sa = ehlokw.sa; + unsigned int len = ehlokw.len; + /* look for STARTTLS among EHLO keywords */ + for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; + if (!len) { + if (!servercert) return 0; + out("ZNo TLS achieved while "); out(servercert); + out(" exists"); smtptext.len = 0; TLS_QUIT; + } + } + + SSL_library_init(); + ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ctx) { + if (!smtps && !servercert) return 0; + smtptext.len = 0; + tls_quit_error("ZTLS error initializing ctx"); + } + + if (servercert) { + if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { + SSL_CTX_free(ctx); + smtptext.len = 0; + out("ZTLS unable to load "); tls_quit_error(servercert); + } + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + } + + /* let the other side complain if it needs a cert and we don't have one */ +# define CLIENTCERT "control/clientcert.pem" + if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) + SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); +# undef CLIENTCERT + + myssl = SSL_new(ctx); + SSL_CTX_free(ctx); + if (!myssl) { + if (!smtps && !servercert) return 0; + smtptext.len = 0; + tls_quit_error("ZTLS error initializing ssl"); + } + + if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); + + /* while the server is preparing a responce, do something else */ + if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) + { SSL_free(myssl); temp_control(); } + if (saciphers.len) { + for (i = 0; i < saciphers.len - 1; ++i) + if (!saciphers.s[i]) saciphers.s[i] = ':'; + ciphers = saciphers.s; + } + else ciphers = "DEFAULT"; + SSL_set_cipher_list(myssl, ciphers); + alloc_free(saciphers.s); + + /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */ + SSL_set_fd(myssl, smtpfd); + + /* read the responce to STARTTLS */ + if (!smtps) { + if (smtpcode() != 220) { + SSL_free(myssl); + if (!servercert) return 0; + out("ZSTARTTLS rejected while "); + out(servercert); out(" exists"); TLS_QUIT; + } + smtptext.len = 0; + } + + ssl = myssl; + if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) + tls_quit("ZTLS connect failed", ssl_error_str()); + + if (servercert) { + X509 *peercert; + STACK_OF(GENERAL_NAME) *gens; + + int r = SSL_get_verify_result(ssl); + if (r != X509_V_OK) { + out("ZTLS unable to verify server with "); + tls_quit(servercert, X509_verify_cert_error_string(r)); + } + alloc_free(servercert); + + peercert = SSL_get_peer_certificate(ssl); + if (!peercert) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "no certificate provided"); + } + + /* RFC 2595 section 2.4: find a matching name + * first find a match among alternative names */ + gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); + if (gens) { + for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) + { + const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); + if (gn->type == GEN_DNS) + if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break; + } + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + } + + /* no alternative name matched, look up commonName */ + if (!gens || i >= r) { + stralloc peer = {0}; + X509_NAME *subj = X509_get_subject_name(peercert); + i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); + if (i >= 0) { + const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value; + if (s) { peer.len = s->length; peer.s = s->data; } + } + if (peer.len <= 0) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "certificate contains no valid commonName"); + } + if (!match_partner(peer.s, peer.len)) { + out("ZTLS unable to verify server "); out(partner_fqdn); + out(": received certificate for "); outsafe(&peer); TLS_QUIT; + } + } + + X509_free(peercert); + } + + if (smtps) if (smtpcode() != 220) + quit("ZTLS Connected to "," but greeting failed"); + + return 1; +} +#endif + stralloc recip = {0}; void smtp() @@ -221,8 +522,46 @@ unsigned long code; int flagbother; int i; + +#ifndef PORT_SMTP + /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ +# define port smtp_port +#endif + +#ifdef TLS +# ifdef MXPS + if (type == 'S') smtps = 1; + else if (type != 's') +# endif + if (port == 465) smtps = 1; + if (!smtps) +#endif - if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + code = smtpcode(); + if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed"); + if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */ + if (code != 220) quit("ZConnected to "," but greeting failed"); + +#ifdef EHLO +# ifdef TLS + if (!smtps) +# endif + code = ehlo(); + +# ifdef TLS + if (tls_init()) + /* RFC2487 says we should issue EHLO (even if we might not need + * extensions); at the same time, it does not prohibit a server + * to reject the EHLO and make us fallback to HELO */ + code = ehlo(); +# endif + + if (code == 250) { + /* add EHLO response checks here */ + + /* and if EHLO failed, use HELO */ + } else { +#endif substdio_puts(&smtpto,"HELO "); substdio_put(&smtpto,helohost.s,helohost.len); @@ -230,6 +569,10 @@ substdio_flush(&smtpto); if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); +#ifdef EHLO + } +#endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); @@ -254,7 +597,11 @@ outsmtptext(); zero(); } else { - out("r"); zero(); + /* + * James Raftery + * Log _real_ envelope recipient, post canonicalisation. + */ + out("r<"); outsafe(&reciplist.sa[i]); out("> "); zero(); flagbother = 1; } } @@ -417,7 +764,10 @@ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; - smtp(); /* does not return */ +#ifdef TLS + partner_fqdn = ip.ix[i].fqdn; +#endif + smtp(); /* only returns when the next MX is to be tried */ } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); --- qmail-send.9.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-send.9 2006-07-31 09:58:12.000000000 -0300 @@ -115,6 +115,10 @@ (If that bounces, .B qmail-send gives up.) +As a special case, if the first line of +.IR doublebounceto +is blank (contains a single linefeed), qmail-send will not queue +the double-bounce at all. .TP 5 .I envnoathost Presumed domain name for addresses without @ signs. --- qmail-send.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-send.c 2006-07-31 09:58:12.000000000 -0300 @@ -44,6 +44,8 @@ int lifetime = 604800; +int bouncemaxbytes = 50000; + stralloc percenthack = {0}; struct constmap mappercenthack; stralloc locals = {0}; @@ -96,7 +98,7 @@ } void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); } -void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); } +void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,1); } void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); } void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); } void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); } @@ -262,6 +264,8 @@ 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(); @@ -683,6 +687,8 @@ } if (str_equal(sender.s,"#@[]")) log3("triple bounce: discarding ",fn2.s,"\n"); + else if (!*sender.s && *doublebounceto.s == '@') + log3("double bounce: discarding ",fn2.s,"\n"); else { if (qmail_open(&qqt) == -1) @@ -740,9 +746,17 @@ qmail_fail(&qqt); else { + int bytestogo = bouncemaxbytes; + int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); - while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) + while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) { qmail_put(&qqt,buf,r); + bytestogo -= bytestoget; + bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; + } + if (r > 0) { + qmail_puts(&qqt,"\n\n--- End of message stripped.\n"); + } close(fd); if (r == -1) qmail_fail(&qqt); @@ -906,41 +920,42 @@ 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)) + 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[1] == 'Z') + if (dline[c].s[2] == 'Z') if (jo[d[c][delnum].j].flagdying) { - dline[c].s[1] = 'D'; + 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[1]) + switch(dline[c].s[2]) { case 'K': log3("delivery ",strnum3,": success: "); - logsafe(dline[c].s + 2); + 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 + 2); + logsafe(dline[c].s + 3); log1("\n"); break; case 'D': log3("delivery ",strnum3,": failure: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); - addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); + 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; @@ -1215,8 +1230,10 @@ /* this file is too long ---------------------------------------------- TODO */ +#ifndef EXTERNAL_TODO datetime_sec nexttodorun; -DIR *tododir; /* if 0, have to opendir again */ +int flagtododir = 0; /* if 0, have to readsubdir_init again */ +readsubdir todosubdir; stralloc todoline = {0}; char todobuf[SUBSTDIO_INSIZE]; char todobufinfo[512]; @@ -1224,7 +1241,7 @@ void todo_init() { - tododir = 0; + flagtododir = 0; nexttodorun = now(); trigger_set(); } @@ -1236,7 +1253,7 @@ { if (flagexitasap) return; trigger_selprep(nfds,rfds); - if (tododir) *wakeup = 0; + if (flagtododir) *wakeup = 0; if (*wakeup > nexttodorun) *wakeup = nexttodorun; } @@ -1253,8 +1270,7 @@ char ch; int match; unsigned long id; - unsigned int len; - direntry *d; + int z; int c; unsigned long uid; unsigned long pid; @@ -1265,32 +1281,26 @@ if (flagexitasap) return; - if (!tododir) + if (!flagtododir) { if (!trigger_pulled(rfds)) if (recent < nexttodorun) return; trigger_set(); - tododir = opendir("todo"); - if (!tododir) - { - pausedir("todo"); - return; - } + readsubdir_init(&todosubdir, "todo", pausedir); + flagtododir = 1; nexttodorun = recent + SLEEP_TODO; } - d = readdir(tododir); - if (!d) + switch(readsubdir_next(&todosubdir, &id)) { - closedir(tododir); - tododir = 0; - return; + case 1: + break; + case 0: + flagtododir = 0; + default: + return; } - if (str_equal(d->d_name,".")) return; - if (str_equal(d->d_name,"..")) return; - len = scan_ulong(d->d_name,&id); - if (!len || d->d_name[len]) return; fnmake_todo(id); @@ -1438,10 +1448,148 @@ if (fdchan[c] != -1) close(fdchan[c]); } +#endif + +/* this file is too long ------------------------------------- EXTERNAL TODO */ + +#ifdef EXTERNAL_TODO +stralloc todoline = {0}; +char todobuf[2048]; +int todofdin; +int todofdout; +int flagtodoalive; + +void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n"); + flagexitasap = 1; flagtodoalive = 0; } + +void todo_init() +{ + todofdout = 7; + todofdin = 8; + flagtodoalive = 1; + /* sync with external todo */ + if (write(todofdout, "S", 1) != 1) tododied(); + + return; +} + +void todo_selprep(nfds,rfds,wakeup) +int *nfds; +fd_set *rfds; +datetime_sec *wakeup; +{ + if (flagexitasap) { + if (flagtodoalive) { + write(todofdout, "X", 1); + } + } + if (flagtodoalive) { + FD_SET(todofdin,rfds); + if (*nfds <= todofdin) + *nfds = todofdin + 1; + } +} + +void todo_del(char* s) +{ + int flagchan[CHANNELS]; + struct prioq_elt pe; + unsigned long id; + unsigned int len; + int c; + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + switch(*s++) { + case 'L': + flagchan[0] = 1; + break; + case 'R': + flagchan[1] = 1; + break; + case 'B': + flagchan[0] = 1; + flagchan[1] = 1; + break; + case 'X': + break; + default: + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + len = scan_ulong(s,&id); + if (!len || s[len]) { + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + pe.id = id; pe.dt = now(); + for (c = 0;c < CHANNELS;++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pe)) nomem(); + + for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; + if (c == CHANNELS) + while (!prioq_insert(&pqdone,&pe)) nomem(); + + return; +} + +void todo_do(rfds) +fd_set *rfds; +{ + int r; + char ch; + int i; + + if (!flagtodoalive) return; + if (!FD_ISSET(todofdin,rfds)) return; + + r = read(todofdin,todobuf,sizeof(todobuf)); + if (r == -1) return; + if (r == 0) { + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + return; + } + for (i = 0;i < r;++i) { + ch = todobuf[i]; + while (!stralloc_append(&todoline,&ch)) nomem(); + if (todoline.len > REPORTMAX) + todoline.len = REPORTMAX; + /* qmail-todo is responsible for keeping it short */ + if (!ch && (todoline.len > 1)) { + switch (todoline.s[0]) { + case 'D': + if (flagexitasap) break; + todo_del(todoline.s + 1); + break; + case 'L': + log1(todoline.s + 1); + break; + case 'X': + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + break; + default: + log1("warning: qmail-send unable to understand qmail-todo: report mangled\n"); + break; + } + todoline.len = 0; + } + } +} + +#endif /* this file is too long ---------------------------------------------- MAIN */ int getcontrols() { if (control_init() == -1) return 0; + if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0; if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; @@ -1504,6 +1652,9 @@ log1("alert: unable to reread controls: unable to switch to home directory\n"); return; } +#ifdef EXTERNAL_TODO + write(todofdout, "H", 1); +#endif regetcontrols(); while (chdir("queue") == -1) { @@ -1544,7 +1695,7 @@ numjobs = 0; for (c = 0;c < CHANNELS;++c) { - char ch; + char ch, ch1; int u; int r; do @@ -1552,7 +1703,13 @@ 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]; } @@ -1568,8 +1725,12 @@ todo_init(); cleanup_init(); +#ifdef EXTERNAL_TODO + while (!flagexitasap || !del_canexit() || flagtodoalive) +#else while (!flagexitasap || !del_canexit()) - { +#endif + { recent = now(); if (flagrunasap) { flagrunasap = 0; pqrun(); } --- qmail-showctl.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-showctl.c 2006-07-31 09:58:12.000000000 -0300 @@ -214,7 +214,11 @@ _exit(111); } - do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM."); + do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern."); + do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern."); + do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set."); + do_lst("badmailto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern."); + do_lst("badmailtonorelay","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern and RELAYCLIENT is not set."); do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); do_str("bouncehost",1,"bouncehost","Bounce host name is "); do_int("concurrencylocal","10","Local concurrency is ",""); @@ -267,7 +271,11 @@ if (str_equal(d->d_name,"..")) continue; if (str_equal(d->d_name,"bouncefrom")) continue; if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"badhelo")) continue; if (str_equal(d->d_name,"badmailfrom")) continue; + if (str_equal(d->d_name,"badmailfromnorelay")) continue; + if (str_equal(d->d_name,"badmailto")) continue; + if (str_equal(d->d_name,"badmailtonorelay")) continue; if (str_equal(d->d_name,"bouncefrom")) continue; if (str_equal(d->d_name,"bouncehost")) continue; if (str_equal(d->d_name,"concurrencylocal")) continue; --- qmail-smtpd.8.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-smtpd.8 2006-07-31 09:58:12.000000000 -0300 @@ -14,6 +14,15 @@ 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,7 +32,30 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options. +.B qmail-smtpd +includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements. +.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 CRAM-MD5 digest/response derived from the SMTP client, +another 0 byte, a CRAM-MD5 challenge (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 @@ -37,11 +69,26 @@ even though such messages violate the SMTP protocol. .SH "CONTROL FILES" .TP 5 +.I badhelo +Unacceptable HELO/EHLO host names. +.B qmail-smtpd +will reject every recipient address for a message if +the host name is listed in, +or matches a POSIX regular expression pattern listed in, +.IR badhelo . +If the +.B NOBADHELO +environment variable is set, then the contents of +.IR badhelo +will be ignored. +For more information, please have a look at doc/README.qregex. +.TP 5 .I badmailfrom Unacceptable envelope sender addresses. .B qmail-smtpd will reject every recipient address for a message -if the envelope sender address is listed in +if the envelope sender address is listed in or matches a POSIX regular expression +pattern listed in, .IR badmailfrom . A line in .I badmailfrom @@ -49,6 +96,45 @@ .BR @\fIhost , meaning every address at .IR host . +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badmailfromnorelay +Functions the same as the +.IR badmailfrom +control file but is read only if the +.B RELAYCLIENT +environment variable is not set. +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badmailto +Unacceptable envelope recipient addresses. +.B qmail-smtpd +will reject every recipient address for a message if the recipient address +is listed in, +or matches a POSIX regular expression pattern listed in, +.IR badmailto . +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badmailtonorelay +Functions the same as the +.IR badmailto +control file but is read only if the +.B RELAYCLIENT +environment variable is not set. +For more information, please have a look at doc/README.qregex. + +.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 clientcrl.pem +A list of Certificate Revocation Lists (CRLs). If present it +should contain the CRLs of the CAs in +.I clientca.pem +and client certs will be checked for revocation. + .TP 5 .I databytes Maximum number of bytes allowed in a message, @@ -76,6 +162,18 @@ .B DATABYTES is set, it overrides .IR databytes . + +.TP 5 +.I dh1024.pem +If these 1024 bit DH parameters are provided, +.B qmail-smtpd +will use them for TLS sessions instead of generating one on-the-fly +(which is very timeconsuming). +.TP 5 +.I dh512.pem +512 bit counterpart for +.B dh1024.pem. + .TP 5 .I localiphost Replacement host name for local IP addresses. @@ -97,6 +195,12 @@ This is done before .IR rcpthosts . .TP 5 +.I mfcheck +If set, +.B qmail-smtpd +tries to resolve the domain of the envelope from address. It can be +handy when you want to filter out spamhosts. +.TP 5 .I morercpthosts Extra allowed RCPT domains. If @@ -151,6 +255,19 @@ Envelope recipient addresses without @ signs are always allowed through. + +.TP 5 +.I rsa512.pem +If this 512 bit RSA key is provided, +.B qmail-smtpd +will use it for TLS sessions instead of generating one on-the-fly. + +.TP 5 +.I servercert.pem +SSL certificate to be presented to clients in TLS-encrypted sessions. +Should contain both the certificate and the private key. Certifying Authority +(CA) and intermediate certificates can be added at the end of the file. + .TP 5 .I smtpgreeting SMTP greeting message. @@ -169,6 +286,24 @@ .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 +A set of OpenSSL cipher strings. Multiple ciphers contained in a +string should be separated by a colon. If the environment variable +.B TLSCIPHERS +is set to such a string, it takes precedence. + .SH "SEE ALSO" tcp-env(1), tcp-environ(5), --- qmail-smtpd.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ qmail-smtpd.c 2006-07-31 09:58:12.000000000 -0300 @@ -23,14 +23,49 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "dns.h" +#include "qregex.h" +#include "strerr.h" +#include "wait.h" + +#define CRAM_MD5 +#define AUTHSLEEP 5 + +#define BMCHECK_BMF 0 +#define BMCHECK_BMFNR 1 +#define BMCHECK_BMT 2 +#define BMCHECK_BMTNR 3 +#define BMCHECK_BHELO 4 #define MAXHOPS 100 unsigned int databytes = 0; +unsigned int mfchk = 0; int timeout = 1200; +int rcptcounter = 0; +int maxrcpt = -1; +int heloreq = 0; + +const char *protocol = "SMTP"; + +#ifdef TLS +#include +#include "tls.h" +#include "ssl_timeoutio.h" + +void tls_init(); +int tls_verify(); +void tls_nogateway(); +int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ +#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; @@ -49,8 +84,24 @@ void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } -void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } +void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); } +void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } +void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); } +void err_heloreq() { out("553 sorry, you must provide your HELO host (#5.7.1)\r\n"); } +void err_hmf() { out("553 sorry, your envelope sender domain must exist (#5.7.1)\r\n"); } +void err_smf() { out("451 DNS temporary failure (#4.3.0)\r\n"); } +#ifndef TLS void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +#else +void err_nogateway() +{ + out("553 sorry, that domain isn't in my list of allowed rcpthosts"); + tls_nogateway(); + out(" (#5.7.1)\r\n"); +} +#endif +void err_maxrcpt() { out("553 sorry, that message exceed max addresses limit (#5.7.1)\r\n"); } void err_unimpl(arg) char *arg; { 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,6 +110,16 @@ void err_vrfy(arg) char *arg; { 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 canceled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } +void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); } stralloc greeting = {0}; @@ -93,9 +154,31 @@ int liphostok = 0; stralloc liphost = {0}; + int bmfok = 0; stralloc bmf = {0}; -struct constmap mapbmf; + +int bmfnrok = 0; +stralloc bmfnr = {0}; + +int bmtok = 0; +stralloc bmt = {0}; + +int bmtnrok = 0; +stralloc bmtnr = {0}; + +int bhelook = 0; +stralloc bhelo = {0}; + +int logregex = 0; +stralloc matchedregex = {0}; + +int mruok = 0; +stralloc mru = {0}; +struct constmap mapmru; + +int tarpitcount = 0; +int tarpitdelay = 5; void setup() { @@ -110,12 +193,48 @@ if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; + if (control_readint(&maxrcpt,"control/maxrcpt") == -1) die_control(); + mruok = control_readfile(&mru,"control/maxrcptusers",0); + if (mruok == -1) die_control(); + if (mruok) + if (!constmap_init(&mapmru,mru.s,mru.len,0)) die_nomem(); + + if (control_readint(&heloreq,"control/heloreq") == -1) die_control(); + x = env_get("HELOREQ"); + if (x) { scan_ulong(x,&u); heloreq = u; } + + 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(); + if (control_readint(&mfchk,"control/mfcheck") == -1) die_control(); + x = env_get("MFCHECK"); + if (x) { scan_ulong(x,&u); mfchk = u; } + bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); - if (bmfok) - if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0); + if (bmfnrok == -1) die_control(); + + bmtok = control_readfile(&bmt,"control/badmailto",0); + if (bmtok == -1) die_control(); + + bmtnrok = control_readfile(&bmtnr,"control/badmailtonorelay",0); + if (bmtnrok == -1) die_control(); + + bhelook = control_readfile(&bhelo, "control/badhelo",0); + if (bhelook == -1) die_control(); + if (env_get("NOBADHELO")) bhelook = 0; + + if (env_get("LOGREGEX")) logregex = 1; if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); @@ -131,6 +250,11 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + +#ifdef TLS + if (env_get("SMTPS")) { smtps = 1; tls_init(); } + else +#endif dohelo(remotehost); } @@ -197,14 +321,87 @@ return 1; } -int bmfcheck() +int bmcheck(which) int which; +{ + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + static stralloc bmb = {0}; + static stralloc curregex = {0}; + + if (which == BMCHECK_BMF) { + if (!stralloc_copy(&bmb,&bmf)) die_nomem(); + } else if (which == BMCHECK_BMFNR) { + if (!stralloc_copy(&bmb,&bmfnr)) die_nomem(); + } else if (which == BMCHECK_BMT) { + if (!stralloc_copy(&bmb,&bmt)) die_nomem(); + } else if (which == BMCHECK_BMTNR) { + if (!stralloc_copy(&bmb,&bmtnr)) die_nomem(); + } else if (which == BMCHECK_BHELO) { + if (!stralloc_copy(&bmb,&bhelo)) die_nomem(); + } else { + die_control(); + } + + while (j < bmb.len) { + i = j; + while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; + if (bmb.s[j] == '!') { + negate = 1; + j++; + } + if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&curregex)) die_nomem(); + if (which == BMCHECK_BHELO) { + x = matchregex(helohost.s, curregex.s); + } else { + x = matchregex(addr.s, curregex.s); + } + if ((negate) && (x == 0)) { + if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); + return 1; + } + if (!(negate) && (x > 0)) { + if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); + return 1; + } + j = i + 1; + negate = 0; + } + return 0; +} + +int mfcheck() +{ + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned int random; + int j; + + if (str_equal(addr.s,"#@[]")) return 0; + if (!mfchk) return 0; + random = now() + (getpid() << 16); + j = byte_rchr(addr.s,addr.len,'@') + 1; + if (j < addr.len) { + stralloc_copys(&sa, addr.s + j); + dns_init(0); + j = dns_mxip(&ia,&sa,random); + if (j < 0) return j; + } + return 0; +} + +int mrucheck() { int j; - if (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; + if (!mruok) return 0; + if (constmap(&mapmru,addr.s,addr.len - 1)) return 1; j = byte_rchr(addr.s,addr.len,'@'); if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + if (constmap(&mapmru,addr.s + j,addr.len - j - 1)) return 1; return 0; } @@ -213,24 +410,140 @@ int r; r = rcpthosts(addr.s,str_len(addr.s)); if (r == -1) die_control(); +#ifdef TLS + if (r == 0) if (tls_verify()) r = -2; +#endif return r; } int seenmail = 0; -int flagbarf; /* defined if seenmail */ +int flagbarfbmf; /* defined if seenmail */ +int flagbarfbmt; +int flagbarfbhelo; +int flagmru; +int flagsize; stralloc mailfrom = {0}; stralloc rcptto = {0}; +int rcptcount; +stralloc fuser = {0}; +stralloc mfparms = {0}; + +int mailfrom_size(arg) char *arg; +{ + long r; + unsigned long sizebytes = 0; + + scan_ulong(arg,&r); + sizebytes = r; + if (databytes) if (sizebytes > databytes) return 1; + return 0; +} + +void mailfrom_auth(arg,len) +char *arg; +int len; +{ + int j; + + if (!stralloc_copys(&fuser,"")) die_nomem(); + if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } + else + while (len) { + if (*arg == '+') { + if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } + if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } + } + else + if (!stralloc_catb(&fuser,arg,1)) die_nomem(); + arg++; len--; + } + if(!stralloc_0(&fuser)) die_nomem(); + if (!remoteinfo) { + remoteinfo = fuser.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + } +} + +void mailfrom_parms(arg) char *arg; +{ + int i; + int len; + + len = str_len(arg); + if (!stralloc_copys(&mfparms,"")) die_nomem; + i = byte_chr(arg,len,'>'); + if (i > 4 && i < len) { + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); + if (!stralloc_copys(&mfparms,"")) die_nomem; + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem; + } + } +} void smtp_helo(arg) char *arg; { - smtp_greet("250 "); out("\r\n"); + if (heloreq && !*arg) { + strerr_warn4("qmail-smtpd: heloreq: <",helohost.s,"> at ",remoteip,0); + err_heloreq(); + return; + } seenmail = 0; dohelo(arg); + if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); + if (flagbarfbhelo) { + if (logregex) { + strerr_warn6("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn4("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip,0); + } + err_bhelo(); + return; + } + smtp_greet("250 "); out("\r\n"); } +/* ESMTP extensions are published here */ void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + if (heloreq && !*arg) { + strerr_warn4("qmail-smtpd: heloreq: <",helohost.s,"> at ",remoteip,0); + err_heloreq(); + return; + } seenmail = 0; dohelo(arg); + if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); + if (flagbarfbhelo) { + if (logregex) { + strerr_warn6("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn4("qmail-smtpd: badhelo: <",helohost.s,"> at ",remoteip,0); + } + err_bhelo(); + return; + } +#ifdef TLS + struct stat st; +#endif + char size[FMT_ULONG]; + size[fmt_ulong(size,(unsigned int) databytes)] = 0; + smtp_greet("250-"); +#ifdef TLS + if (!ssl && (stat("control/servercert.pem",&st) == 0)) + out("\r\n250-STARTTLS"); +#endif + out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); + out("250-SIZE "); out(size); out("\r\n"); +#ifdef CRAM_MD5 + out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n"); +#else + out("250 AUTH LOGIN PLAIN\r\n"); +#endif } void smtp_rset(arg) char *arg; { @@ -239,18 +552,60 @@ } void smtp_mail(arg) char *arg; { + rcptcounter = 0; if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); + flagsize = 0; + mailfrom_parms(arg); + if (flagsize) { err_size(); return; } + flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ + flagmru = mrucheck(); + if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); + if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { + flagbarfbmf = bmcheck(BMCHECK_BMFNR); + } seenmail = 1; + switch(mfcheck()) { + case DNS_HARD: err_hmf(); return; + case DNS_SOFT: err_smf(); return; + case DNS_MEM: die_nomem(); + } 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; { + rcptcounter++; if (!seenmail) { err_wantmail(); return; } + if (checkrcptcount() == 1 && !flagmru) { + strerr_warn4("qmail-smtpd: maxrcptusers: <",mailfrom.s,"> at ",remoteip,0); + err_maxrcpt(); + return; + } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + if (flagbarfbmf) { + if (logregex) { + strerr_warn6("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn4("qmail-smtpd: badmailfrom: <",mailfrom.s,"> at ",remoteip,0); + } + err_bmf(); + return; + } + if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT); + if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) { + flagbarfbmt = bmcheck(BMCHECK_BMTNR); + } + if (flagbarfbmt) { + if (logregex) { + strerr_warn6("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip," matches pattern: ",matchedregex.s,0); + } else { + strerr_warn4("qmail-smtpd: badmailto: <",addr.s,"> at ",remoteip,0); + } + err_bmt(); + return; + } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); @@ -261,6 +616,7 @@ 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,6 +625,11 @@ { 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,7 +739,7 @@ qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -388,28 +749,493 @@ qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } + if (databytes) if (!bytestooverflow) { err_size(); return; } if (*qqx == 'D') out("554 "); else out("451 "); out(qqx + 1); out("\r\n"); } +#ifdef TLS +stralloc proto = {0}; +int ssl_verified = 0; +const char *ssl_verify_err = 0; + +void smtp_tls(char *arg) +{ + if (ssl) err_unimpl(); + else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + else tls_init(); +} + +RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) +{ + if (!export) keylen = 512; + if (keylen == 512) { + FILE *in = fopen("control/rsa512.pem", "r"); + if (in) { + RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); + fclose(in); + if (rsa) return rsa; + } + } + return RSA_generate_key(keylen, RSA_F4, NULL, NULL); +} + +DH *tmp_dh_cb(SSL *ssl, int export, int keylen) +{ + if (!export) keylen = 1024; + if (keylen == 512) { + FILE *in = fopen("control/dh512.pem", "r"); + if (in) { + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); + fclose(in); + if (dh) return dh; + } + } + if (keylen == 1024) { + FILE *in = fopen("control/dh1024.pem", "r"); + if (in) { + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); + fclose(in); + if (dh) return dh; + } + } + return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); +} + +/* don't want to fail handshake if cert isn't verifiable */ +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } + +void tls_nogateway() +{ + /* there may be cases when relayclient is set */ + if (!ssl || relayclient) return; + out("; no valid cert for gatewaying"); + if (ssl_verify_err) { out(": "); out(ssl_verify_err); } +} +void tls_out(const char *s1, const char *s2) +{ + out("454 TLS "); out(s1); + if (s2) { out(": "); out(s2); } + out(" (#4.3.0)\r\n"); flush(); +} +void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } + +# define CLIENTCA "control/clientca.pem" +# define CLIENTCRL "control/clientcrl.pem" +# define SERVERCERT "control/servercert.pem" + +int tls_verify() +{ + stralloc clients = {0}; + struct constmap mapclients; + + if (!ssl || relayclient || ssl_verified) return 0; + ssl_verified = 1; /* don't do this twice */ + + /* request client cert to see if it can be verified by one of our CAs + * and the associated email address matches an entry in tlsclients */ + switch (control_readfile(&clients, "control/tlsclients", 0)) + { + case 1: + if (constmap_init(&mapclients, clients.s, clients.len, 0)) { + /* if CLIENTCA contains all the standard root certificates, a + * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; + * it is probably due to 0.9.6b supporting only 8k key exchange + * data while the 0.9.6c release increases that limit to 100k */ + STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); + if (sk) { + SSL_set_client_CA_list(ssl, sk); + SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); + break; + } + constmap_free(&mapclients); + } + case 0: alloc_free(clients.s); return 0; + case -1: die_control(); + } + + if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { + const char *err = ssl_error_str(); + tls_out("rehandshake failed", err); die_read(); + } + + do { /* one iteration */ + X509 *peercert; + X509_NAME *subj; + stralloc email = {0}; + + int n = SSL_get_verify_result(ssl); + if (n != X509_V_OK) + { ssl_verify_err = X509_verify_cert_error_string(n); break; } + peercert = SSL_get_peer_certificate(ssl); + if (!peercert) break; + + subj = X509_get_subject_name(peercert); + n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); + if (n >= 0) { + const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value; + if (s) { email.len = s->length; email.s = s->data; } + } + + if (email.len <= 0) + ssl_verify_err = "contains no email address"; + else if (!constmap(&mapclients, email.s, email.len)) + ssl_verify_err = "email address not in my list of tlsclients"; + else { + /* add the cert email to the proto if it helped allow relaying */ + --proto.len; + if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ + || !stralloc_catb(&proto, email.s, email.len) + || !stralloc_cats(&proto, ")") + || !stralloc_0(&proto)) die_nomem(); + relayclient = ""; + protocol = proto.s; + } + + X509_free(peercert); + } while (0); + constmap_free(&mapclients); alloc_free(clients.s); + + /* we are not going to need this anymore: free the memory */ + SSL_set_client_CA_list(ssl, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + + return relayclient ? 1 : 0; +} + +void tls_init() +{ + SSL *myssl; + SSL_CTX *ctx; + const char *ciphers; + stralloc saciphers = {0}; + X509_STORE *store; + X509_LOOKUP *lookup; + + SSL_library_init(); + + /* a new SSL context with the bare minimum of options */ + ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ctx) { tls_err("unable to initialize ctx"); return; } + + if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) + { SSL_CTX_free(ctx); tls_err("missing certificate"); return; } + SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L + /* crl checking */ + store = SSL_CTX_get_cert_store(ctx); + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && + (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); +#endif + + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); + + /* a new SSL object, with the rest added to it directly to avoid copying */ + myssl = SSL_new(ctx); + SSL_CTX_free(ctx); + if (!myssl) { tls_err("unable to initialize ssl"); return; } + + /* this will also check whether public and private keys match */ + if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) + { SSL_free(myssl); tls_err("no valid RSA private key"); return; } + + ciphers = env_get("TLSCIPHERS"); + if (!ciphers) { + if (control_readfile(&saciphers, "control/tlsserverciphers") == -1) + { SSL_free(myssl); die_control(); } + if (saciphers.len) { /* convert all '\0's except the last one to ':' */ + int i; + for (i = 0; i < saciphers.len - 1; ++i) + if (!saciphers.s[i]) saciphers.s[i] = ':'; + ciphers = saciphers.s; + } + } + if (!ciphers || !*ciphers) ciphers = "DEFAULT"; + SSL_set_cipher_list(myssl, ciphers); + alloc_free(saciphers.s); + + SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); + SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); + SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); + SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); + + if (!smtps) { out("220 ready for tls\r\n"); flush(); } + + if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { + /* neither cleartext nor any other response here is part of a standard */ + const char *err = ssl_error_str(); + ssl_free(myssl); tls_out("connection failed", err); die_read(); + } + ssl = myssl; + + /* populate the protocol string, used in Received */ + if (!stralloc_copys(&proto, "(") + || !stralloc_cats(&proto, SSL_get_cipher(ssl)) + || !stralloc_cats(&proto, " encrypted) SMTP")) die_nomem(); + if (!stralloc_0(&proto)) die_nomem(); + protocol = proto.s; + + /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ + dohelo(remotehost); +} + +# undef SERVERCERT +# undef CLIENTCA + +#endif + +/* this file is too long ----------------------------------------- SMTP AUTH */ + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; /* input from SMTP client */ +static stralloc user = {0}; /* authorization user-id */ +static stralloc pass = {0}; /* plain passwd or digest */ +static stralloc resp = {0}; /* b64 response */ +#ifdef CRAM_MD5 +static stralloc chal = {0}; /* plain challenge */ +static stralloc slop = {0}; /* b64 challenge */ +#endif + +int flagauth = 0; +char **childargs; +char ssauthbuf[512]; +substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); + +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(); +#ifdef CRAM_MD5 + if (!stralloc_0(&chal)) die_nomem(); +#endif + + if (pipe(pi) == -1) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if(fd_copy(3,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); + if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); +#ifdef CRAM_MD5 + if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); +#endif + if (substdio_flush(&ssauth) == -1) return err_write(); + + close(pi[1]); +#ifdef CRAM_MD5 + if (!stralloc_copys(&chal,"")) die_nomem(); + if (!stralloc_copys(&slop,"")) die_nomem(); +#endif + byte_zero(ssauthbuf,sizeof ssauthbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); 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),&resp) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ + + if (resp.len > id + 1) + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); + if (resp.len > id + user.len + 2) + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +#ifdef CRAM_MD5 +int auth_cram() +{ + int i, r; + char *s; + + s = unique; /* generate challenge */ + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (!stralloc_copys(&chal,"<")) die_nomem(); + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,local)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); /* "334 base64_challenge \r\n" */ + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; /* got response */ + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + + i = str_chr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login",auth_login } +, { "plain",auth_plain } +#ifdef CRAM_MD5 +, { "cram-md5",auth_cram } +#endif +, { 0,err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } + if (flagauth) { 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(); +#ifdef CRAM_MD5 + if (!stralloc_copys(&chal,"")) die_nomem(); +#endif + + 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: + flagauth = 1; + protocol = "ESMTPA"; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + err_authfail(user.s,authcmds[i].text); + } +} + +/* this file is too long --------------------------------------------- GO ON */ + 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() +void main(argc,argv) +int argc; +char **argv; { + childargs = argv + 1; sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); @@ -419,3 +1245,9 @@ if (commands(&ssin,&smtpcommands) == 0) die_read(); die_nomem(); } + +int checkrcptcount() { + if (maxrcpt == -1) { return 0; } + else if (rcptcounter > maxrcpt) { return 1; } + return 0; +} --- qmail-start.c.orig 1998-06-15 07:53:16.000000000 -0300 +++ qmail-start.c 2006-07-31 09:58:12.000000000 -0300 @@ -8,6 +8,9 @@ char *(qcargs[]) = { "qmail-clean", 0 }; char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; char *(qrargs[]) = { "qmail-rspawn", 0 }; +#ifdef EXTERNAL_TODO +char *(qtargs[]) = { "qmail-todo", 0}; +#endif void die() { _exit(111); } @@ -18,13 +21,28 @@ int pi4[2]; int pi5[2]; int pi6[2]; - -void close23456() { close(2); close(3); close(4); close(5); close(6); } +#ifdef EXTERNAL_TODO +int pi7[2]; +int pi8[2]; +int pi9[2]; +int pi10[2]; +#endif + +void close23456() { + close(2); close(3); close(4); close(5); close(6); +#ifdef EXTERNAL_TODO + close(7); close(8); +#endif +} void closepipes() { close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]); close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]); close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]); +#ifdef EXTERNAL_TODO + close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]); + close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]); +#endif } void main(argc,argv) @@ -40,6 +58,10 @@ if (fd_copy(4,0) == -1) die(); if (fd_copy(5,0) == -1) die(); if (fd_copy(6,0) == -1) die(); +#ifdef EXTERNAL_TODO + if (fd_copy(7,0) == -1) die(); + if (fd_copy(8,0) == -1) die(); +#endif if (argv[1]) { qlargs[1] = argv[1]; @@ -70,6 +92,12 @@ if (pipe(pi4) == -1) die(); if (pipe(pi5) == -1) die(); if (pipe(pi6) == -1) die(); +#ifdef EXTERNAL_TODO + if (pipe(pi7) == -1) die(); + if (pipe(pi8) == -1) die(); + if (pipe(pi9) == -1) die(); + if (pipe(pi10) == -1) die(); +#endif switch(fork()) { case -1: die(); @@ -105,6 +133,34 @@ execvp(*qcargs,qcargs); die(); } + +#ifdef EXTERNAL_TODO + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,pi7[0]) == -1) die(); + if (fd_copy(1,pi8[1]) == -1) die(); + close23456(); + if (fd_copy(2,pi9[1]) == -1) die(); + if (fd_copy(3,pi10[0]) == -1) die(); + closepipes(); + execvp(*qtargs,qtargs); + die(); + } + + switch(fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidq) == -1) die(); + if (fd_copy(0,pi9[0]) == -1) die(); + if (fd_copy(1,pi10[1]) == -1) die(); + close23456(); + closepipes(); + execvp(*qcargs,qcargs); + die(); + } +#endif if (prot_uid(auto_uids) == -1) die(); if (fd_copy(0,1) == -1) die(); @@ -114,6 +170,10 @@ if (fd_copy(4,pi4[0]) == -1) die(); if (fd_copy(5,pi5[1]) == -1) die(); if (fd_copy(6,pi6[0]) == -1) die(); +#ifdef EXTERNAL_TODO + if (fd_copy(7,pi7[1]) == -1) die(); + if (fd_copy(8,pi8[0]) == -1) die(); +#endif closepipes(); execvp(*qsargs,qsargs); die(); --- qmail-todo.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ qmail-todo.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,703 @@ +#include +#include +#include "alloc.h" +#include "auto_qmail.h" +#include "byte.h" +#include "constmap.h" +#include "control.h" +#include "direntry.h" +#include "error.h" +#include "exit.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "getln.h" +#include "open.h" +#include "ndelay.h" +#include "now.h" +#include "readsubdir.h" +#include "readwrite.h" +#include "scan.h" +#include "select.h" +#include "str.h" +#include "stralloc.h" +#include "substdio.h" +#include "trigger.h" + +/* critical timing feature #1: if not triggered, do not busy-loop */ +/* critical timing feature #2: if triggered, respond within fixed time */ +/* important timing feature: when triggered, respond instantly */ +#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ +#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ +#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */ +#define SLEEP_SYSFAIL 123 + +stralloc percenthack = {0}; +struct constmap mappercenthack; +stralloc locals = {0}; +struct constmap maplocals; +stralloc vdoms = {0}; +struct constmap mapvdoms; +stralloc envnoathost = {0}; + +char strnum[FMT_ULONG]; + +/* XXX not good, if qmail-send.c changes this has to be updated */ +#define CHANNELS 2 +char *chanaddr[CHANNELS] = { "local/", "remote/" }; + +datetime_sec recent; + +void log1(char *x); +void log3(char* x, char* y, char* z); + +int flagstopasap = 0; +void sigterm(void) +{ + if (flagstopasap == 0) + log1("status: qmail-todo stop processing asap\n"); + flagstopasap = 1; +} + +int flagreadasap = 0; void sighup(void) { flagreadasap = 1; } +int flagsendalive = 1; void senddied(void) { flagsendalive = 0; } + +void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); } +void pausedir(dir) char *dir; +{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); } + +void cleandied() +{ + log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n"); + flagstopasap = 1; +} + + +/* this file is not so long ------------------------------------- FILENAMES */ + +stralloc fn = {0}; + +void fnmake_init(void) +{ + while (!stralloc_ready(&fn,FMTQFN)) nomem(); +} + +void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); } +void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_chanaddr(unsigned long id, int c) +{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } + + +/* this file is not so long ------------------------------------- REWRITING */ + +stralloc rwline = {0}; + +/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ +/* may trash recip. must set up rwline, between a T and a \0. */ +int rewrite(char *recip) +{ + int i; + int j; + char *x; + static stralloc addr = {0}; + int at; + + if (!stralloc_copys(&rwline,"T")) return 0; + if (!stralloc_copys(&addr,recip)) return 0; + + i = byte_rchr(addr.s,addr.len,'@'); + if (i == addr.len) { + if (!stralloc_cats(&addr,"@")) return 0; + if (!stralloc_cat(&addr,&envnoathost)) return 0; + } + + while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { + j = byte_rchr(addr.s,i,'%'); + if (j == i) break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s,addr.len,'@'); + + if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + for (i = 0;i <= addr.len;++i) + if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) + if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) { + if (!*x) break; + if (!stralloc_cats(&rwline,x)) return 0; + if (!stralloc_cats(&rwline,"-")) return 0; + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 2; +} + +/* this file is not so long --------------------------------- COMMUNICATION */ + +substdio sstoqc; char sstoqcbuf[1024]; +substdio ssfromqc; char ssfromqcbuf[1024]; +stralloc comm_buf = {0}; +int comm_pos; +int fdout = -1; +int fdin = -1; + +void comm_init(void) +{ + substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf)); + substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf)); + + fdout = 1; /* stdout */ + fdin = 0; /* stdin */ + if (ndelay_on(fdout) == -1) + /* this is so stupid: NDELAY semantics should be default on write */ + senddied(); /* drastic, but better than risking deadlock */ + + while (!stralloc_ready(&comm_buf,1024)) nomem(); +} + +int comm_canwrite(void) +{ + /* XXX: could allow a bigger buffer; say 10 recipients */ + /* XXX: returns true if there is something in the buffer */ + if (!flagsendalive) return 0; + if (comm_buf.s && comm_buf.len) return 1; + return 0; +} + +void log1(char* x) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto fail; + if (!stralloc_cats(&comm_buf,x)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void log3(char* x, char *y, char *z) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto fail; + if (!stralloc_cats(&comm_buf,x)) goto fail; + if (!stralloc_cats(&comm_buf,y)) goto fail; + if (!stralloc_cats(&comm_buf,z)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_write(unsigned long id, int local, int remote) +{ + int pos; + char *s; + + if(local && remote) s="B"; + else if(local) s="L"; + else if(remote) s="R"; + else s="X"; + + pos = comm_buf.len; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,"D")) goto fail; + if (!stralloc_cats(&comm_buf,s)) goto fail; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +static int issafe(char ch) +{ + if (ch == '%') return 0; /* general principle: allman's code is crap */ + if (ch < 33) return 0; + if (ch > 126) return 0; + return 1; +} + +void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid) +{ + int pos; + int i; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf,": bytes ")) goto fail; + strnum[fmt_ulong(strnum,size)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf," from <")) goto fail; + i = comm_buf.len; + if (!stralloc_cats(&comm_buf,from)) goto fail; + for (;i < comm_buf.len;++i) + if (comm_buf.s[i] == '\n') + comm_buf.s[i] = '/'; + else + if (!issafe(comm_buf.s[i])) + comm_buf.s[i] = '_'; + if (!stralloc_cats(&comm_buf,"> qp ")) goto fail; + strnum[fmt_ulong(strnum,pid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf," uid ")) goto fail; + strnum[fmt_ulong(strnum,uid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf,"\n")) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_exit(void) +{ + int w; + + /* if it fails exit, we have already stoped */ + if (!stralloc_cats(&comm_buf,"X")) _exit(1); + if (!stralloc_0(&comm_buf)) _exit(1); +} + +void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds) +{ + if (flagsendalive) { + if (flagstopasap && comm_canwrite() == 0) + comm_exit(); + if (comm_canwrite()) { + FD_SET(fdout,wfds); + if (*nfds <= fdout) + *nfds = fdout + 1; + } + FD_SET(fdin,rfds); + if (*nfds <= fdin) + *nfds = fdin + 1; + } +} + +void comm_do(fd_set *wfds, fd_set *rfds) +{ + /* first write then read */ + if (flagsendalive) + if (comm_canwrite()) + if (FD_ISSET(fdout,wfds)) { + int w; + int len; + len = comm_buf.len; + w = write(fdout,comm_buf.s + comm_pos,len - comm_pos); + if (w <= 0) { + if ((w == -1) && (errno == error_pipe)) + senddied(); + } else { + comm_pos += w; + if (comm_pos == len) { + comm_buf.len = 0; + comm_pos = 0; + } + } + } + if (flagsendalive) + if (FD_ISSET(fdin,rfds)) { + /* there are only two messages 'H' and 'X' */ + char c; + int r; + r = read(fdin, &c, 1); + if (r <= 0) { + if ((r == -1) && (errno != error_intr)) + senddied(); + } else { + switch(c) { + case 'H': + sighup(); + break; + case 'X': + sigterm(); + break; + default: + log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n"); + break; + } + } + } +} + +/* this file is not so long ------------------------------------------ TODO */ + +datetime_sec nexttodorun; +/* DIR *tododir; if 0, have to opendir again */ +int flagtododir = 0; /* if 0, have to readsubdir_init again */ +readsubdir todosubdir; +stralloc todoline = {0}; +char todobuf[SUBSTDIO_INSIZE]; +char todobufinfo[512]; +char todobufchan[CHANNELS][1024]; + +void todo_init(void) +{ +/* tododir = 0; */ + flagtododir = 0; + nexttodorun = now(); + trigger_set(); +} + +void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup) +{ + if (flagstopasap) return; + trigger_selprep(nfds,rfds); +/* if (tododir) *wakeup = 0; */ + if (flagtododir) *wakeup = 0; + if (*wakeup > nexttodorun) *wakeup = nexttodorun; +} + +void todo_do(fd_set *rfds) +{ + struct stat st; + substdio ss; int fd; + substdio ssinfo; int fdinfo; + substdio sschan[CHANNELS]; + int fdchan[CHANNELS]; + int flagchan[CHANNELS]; + char ch; + int match; + unsigned long id; +/* unsigned int len; + direntry *d; */ + int z; + int c; + unsigned long uid; + unsigned long pid; + + fd = -1; + fdinfo = -1; + for (c = 0;c < CHANNELS;++c) fdchan[c] = -1; + + if (flagstopasap) return; + +/* if (!tododir) */ + if (!flagtododir) + { + if (!trigger_pulled(rfds)) + if (recent < nexttodorun) + return; + trigger_set(); +/* tododir = opendir("todo"); + if (!tododir) + { + pausedir("todo"); + return; + } */ + readsubdir_init(&todosubdir, "todo", pausedir); + flagtododir = 1; + nexttodorun = recent + SLEEP_TODO; + } + +/* d = readdir(tododir); + if (!d) */ + switch(readsubdir_next(&todosubdir, &id)) + { +/* closedir(tododir); + tododir = 0; + return; */ + case 1: + break; + case 0: + flagtododir = 0; + default: + return; + } +/* if (str_equal(d->d_name,".")) return; + if (str_equal(d->d_name,"..")) return; + len = scan_ulong(d->d_name,&id); + if (!len || d->d_name[len]) return; +*/ + fnmake_todo(id); + + fd = open_read(fn.s); + if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; } + + fnmake_mess(id); + /* just for the statistics */ + if (stat(fn.s,&st) == -1) + { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; } + + for (c = 0;c < CHANNELS;++c) + { + fnmake_chanaddr(id,c); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } + } + + fnmake_info(id); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } + + fdinfo = open_excl(fn.s); + if (fdinfo == -1) + { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } + + strnum[fmt_ulong(strnum,id)] = 0; + log3("new msg ",strnum,"\n"); + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + + substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf)); + substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo)); + + uid = 0; + pid = 0; + + for (;;) + { + if (getln(&ss,&todoline,&match,'\0') == -1) + { + /* perhaps we're out of memory, perhaps an I/O error */ + fnmake_todo(id); + log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail; + } + if (!match) break; + + switch(todoline.s[0]) + { + case 'u': + scan_ulong(todoline.s + 1,&uid); + break; + case 'p': + scan_ulong(todoline.s + 1,&pid); + break; + case 'F': + if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1) + { + fnmake_info(id); + log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; + } + comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid); + break; + case 'T': + switch(rewrite(todoline.s + 1)) + { + case 0: nomem(); goto fail; + case 2: c = 1; break; + default: c = 0; break; + } + if (fdchan[c] == -1) + { + fnmake_chanaddr(id,c); + fdchan[c] = open_excl(fn.s); + if (fdchan[c] == -1) + { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } + substdio_fdbuf(&sschan[c] + ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); + flagchan[c] = 1; + } + if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1) + { + fnmake_chanaddr(id,c); + log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; + } + break; + default: + fnmake_todo(id); + log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail; + } + } + + close(fd); fd = -1; + + fnmake_info(id); + if (substdio_flush(&ssinfo) == -1) + { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdinfo) == -1) + { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdinfo); fdinfo = -1; + + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) + { + fnmake_chanaddr(id,c); + if (substdio_flush(&sschan[c]) == -1) + { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdchan[c]) == -1) + { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdchan[c]); fdchan[c] = -1; + } + + fnmake_todo(id); + if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + { + log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + return; + } + + comm_write(id, flagchan[0], flagchan[1]); + + return; + + fail: + if (fd != -1) close(fd); + if (fdinfo != -1) close(fdinfo); + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) close(fdchan[c]); +} + +/* this file is too long ---------------------------------------------- MAIN */ + +int getcontrols(void) +{ + if (control_init() == -1) return 0; + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; + if (control_readfile(&locals,"control/locals",1) != 1) return 0; + if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; + switch(control_readfile(&percenthack,"control/percenthack",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + switch(control_readfile(&vdoms,"control/virtualdomains",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; + } + return 1; +} + +stralloc newlocals = {0}; +stralloc newvdoms = {0}; + +void regetcontrols(void) +{ + int r; + + if (control_readfile(&newlocals,"control/locals",1) != 1) + { log1("alert: qmail-todo: unable to reread control/locals\n"); return; } + r = control_readfile(&newvdoms,"control/virtualdomains",0); + if (r == -1) + { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals,&newlocals)) nomem(); + while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); + + if (r) + { + while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); + while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); + } + else + while (!constmap_init(&mapvdoms,"",0,1)) nomem(); +} + +void reread(void) +{ + if (chdir(auto_qmail) == -1) + { + log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n"); + return; + } + regetcontrols(); + while (chdir("queue") == -1) + { + log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } +} + +void main() +{ + datetime_sec wakeup; + fd_set rfds; + fd_set wfds; + int nfds; + struct timeval tv; + int r; + char c; + + if (chdir(auto_qmail) == -1) + { log1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(111); } + if (!getcontrols()) + { log1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(111); } + if (chdir("queue") == -1) + { log1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(111); } + sig_pipeignore(); + umask(077); + + fnmake_init(); + + todo_init(); + comm_init(); + + do { + r = read(fdin, &c, 1); + if ((r == -1) && (errno != error_intr)) + _exit(100); /* read failed probably qmail-send died */ + } while (r =! 1); /* we assume it is a 'S' */ + + for (;;) + { + recent = now(); + + if (flagreadasap) { flagreadasap = 0; reread(); } + if (!flagsendalive) { + /* qmail-send finaly exited, so do the same. */ + if (flagstopasap) _exit(0); + /* qmail-send died. We can not log and we can not work therefor _exit(1). */ + _exit(1); + } + + wakeup = recent + SLEEP_FOREVER; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + nfds = 1; + + todo_selprep(&nfds,&rfds,&wakeup); + comm_selprep(&nfds,&wfds,&rfds); + + if (wakeup <= recent) tv.tv_sec = 0; + else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; + tv.tv_usec = 0; + + if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1) + if (errno == error_intr) + ; + else + log1("warning: qmail-todo: trouble in select\n"); + else + { + recent = now(); + + todo_do(&rfds); + comm_do(&wfds, &rfds); + } + } + /* NOTREACHED */ +} + --- qregex.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ qregex.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,57 @@ +/* + * qregex (v2) + * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $ + * + * Author : Evan Borgstrom (evan at unixpimps dot org) + * Created : 2001/12/14 23:08:16 + * Modified: $Date: 2001/12/28 07:05:21 $ + * Revision: $Revision: 2.1 $ + * + * Do POSIX regex matching on addresses for anti-relay / spam control. + * It logs to the maillog + * See the qregex-readme file included with this tarball. + * If you didn't get this file in a tarball please see the following URL: + * http://www.unixpimps.org/software/qregex + * + * qregex.c is released under a BSD style copyright. + * See http://www.unixpimps.org/software/qregex/copyright.html + * + * Note: this revision follows the coding guidelines set forth by the rest of + * the qmail code and that described at the following URL. + * http://cr.yp.to/qmail/guarantee.html + * + */ + +#include +#include +#include "qregex.h" + +#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE) +#define REGEXEC(X,Y) regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0) + +int matchregex(char *text, char *regex) { + regex_t qreg; + int retval = 0; + + + /* build the regex */ + if ((retval = REGCOMP(qreg, regex)) != 0) { + regfree(&qreg); + return(-retval); + } + + /* execute the regex */ + if ((retval = REGEXEC(qreg, text)) != 0) { + /* did we just not match anything? */ + if (retval == REG_NOMATCH) { + regfree(&qreg); + return(0); + } + regfree(&qreg); + return(-retval); + } + + /* signal the match */ + regfree(&qreg); + return(1); +} --- qregex.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ qregex.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,5 @@ +/* simple header file for the matchregex prototype */ +#ifndef _QREGEX_H_ +#define _QREGEX_H_ +int matchregex(char *text, char *regex); +#endif --- spawn.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ spawn.c 2006-07-31 09:58:12.000000000 -0300 @@ -64,7 +64,7 @@ int flagreading = 1; char outbuf[1024]; substdio ssout; -int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ +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}; @@ -74,6 +74,7 @@ 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); } @@ -156,16 +157,19 @@ { case 0: delnum = (unsigned int) (unsigned char) ch; - messid.len = 0; stage = 1; break; + 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 = 2; break; - case 2: + sender.len = 0; stage = 3; break; + case 3: if (!stralloc_append(&sender,&ch)) flagabort = 1; if (ch) break; - recip.len = 0; stage = 3; break; - case 3: + recip.len = 0; stage = 4; break; + case 4: if (!stralloc_append(&recip,&ch)) flagabort = 1; if (ch) break; docmd(); @@ -202,7 +206,8 @@ initialize(argc,argv); - ch = auto_spawn; substdio_putflush(&ssout,&ch,1); + 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; } @@ -237,7 +242,8 @@ continue; /* read error on a readable pipe? be serious */ if (r == 0) { - ch = i; substdio_put(&ssout,&ch,1); + 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); --- ssl_timeoutio.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ ssl_timeoutio.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,94 @@ +#include "select.h" +#include "error.h" +#include "ndelay.h" +#include "ssl_timeoutio.h" + +int ssl_timeoutio(int (*fun)(), + long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + int n; + const long end = t + time(NULL); + + do { + fd_set fds; + struct timeval tv; + + const int r = buf ? fun(ssl, buf, len) : fun(ssl); + if (r > 0) return r; + + t = end - time(NULL); + if (t < 0) break; + tv.tv_sec = t; tv.tv_usec = 0; + + FD_ZERO(&fds); + switch (SSL_get_error(ssl, r)) + { + default: return r; /* some other error */ + case SSL_ERROR_WANT_READ: + FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv); + break; + case SSL_ERROR_WANT_WRITE: + FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv); + break; + } + + /* n is the number of descriptors that changed status */ + } while (n > 0); + + if (n != -1) errno = error_timeout; + return -1; +} + +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + SSL_renegotiate(ssl); + r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); + if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r; + + /* this is for the server only */ + ssl->state = SSL_ST_ACCEPT; + return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); +} + +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) return 0; + if (SSL_pending(ssl)) return SSL_read(ssl, buf, len); + return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); +} + +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) return 0; + return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); +} --- ssl_timeoutio.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ ssl_timeoutio.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,21 @@ +#ifndef SSL_TIMEOUTIO_H +#define SSL_TIMEOUTIO_H + +#include + +/* the version is like this: 0xMNNFFPPS: major minor fix patch status */ +#if OPENSSL_VERSION_NUMBER < 0x00906000L +# error "Need OpenSSL version at least 0.9.6" +#endif + +int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl); +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl); +int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl); + +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len); +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len); + +int ssl_timeoutio( + int (*fun)(), long t, int rfd, int wfd, SSL *ssl, char *buf, int len); + +#endif --- strpidt.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ strpidt.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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)); +} --- strtimet.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ strtimet.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,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-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins 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)); +} --- tls.c.orig 2006-07-31 09:58:12.000000000 -0300 +++ tls.c 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,25 @@ +#include "exit.h" +#include "error.h" +#include +#include + +int smtps = 0; +SSL *ssl = NULL; + +void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); } +void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); } + +const char *ssl_error() +{ + int r = ERR_get_error(); + if (!r) return NULL; + SSL_load_error_strings(); + return ERR_error_string(r, NULL); +} +const char *ssl_error_str() +{ + const char *err = ssl_error(); + if (err) return err; + if (!errno) return 0; + return (errno == error_timeout) ? "timed out" : error_str(errno); +} --- tls.h.orig 2006-07-31 09:58:12.000000000 -0300 +++ tls.h 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,16 @@ +#ifndef TLS_H +#define TLS_H + +#include + +extern int smtps; +extern SSL *ssl; + +void ssl_free(SSL *myssl); +void ssl_exit(int status); +# define _exit ssl_exit + +const char *ssl_error(); +const char *ssl_error_str(); + +#endif --- update_tmprsadh.sh.orig 2006-07-31 09:58:12.000000000 -0300 +++ update_tmprsadh.sh 2006-07-31 09:58:12.000000000 -0300 @@ -0,0 +1,25 @@ +#!/bin/sh + +# Update temporary RSA and DH keys +# Frederik Vermeulen 2004-05-31 GPL + +umask 0077 || exit 0 + +export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin" + +openssl genrsa -out QMAIL/control/rsa512.new 512 && +chmod 600 QMAIL/control/rsa512.new && +chown UGQMAILD QMAIL/control/rsa512.new && +mv -f QMAIL/control/rsa512.new QMAIL/control/rsa512.pem +echo + +openssl dhparam -2 -out QMAIL/control/dh512.new 512 && +chmod 600 QMAIL/control/dh512.new && +chown UGQMAILD QMAIL/control/dh512.new && +mv -f QMAIL/control/dh512.new QMAIL/control/dh512.pem +echo + +openssl dhparam -2 -out QMAIL/control/dh1024.new 1024 && +chmod 600 QMAIL/control/dh1024.new && +chown UGQMAILD QMAIL/control/dh1024.new && +mv -f QMAIL/control/dh1024.new QMAIL/control/dh1024.pem