This patch includes netqmail-1.05, qmail-toaster-0.5 and qmail-spf-pre2. diff -ruN qmail-1.03/CHANGES netqmail-1.05-toaster-spf-saout/CHANGES --- qmail-1.03/CHANGES Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/CHANGES Fri Feb 6 13:19:33 2004 @@ -1,3 +1,24 @@ +20040121 version: netqmail 1.05 +20040121 code: qmail-smtpd is protected from exceedingly long (eg 2GB) + header lines +20040121 code: qmail_lspawn, qmail-newmrh, qmail-newu, and qmail-rspawn + are protected from misbehaving on hosts where the size of an + integer is not the same as the size of a character pointer + (eg 64 bit hosts with 32 bit ints) +20031027 version: netqmail 1.04. +20031027 doc: INSTALL points to http://lifewithqmail.org/lwq.html +20031027 doc: qmail.7 identifies installation as netqmail and points to + http://qmail.org/ +20031027 doc: qmail-queue.8 adds explanation of $QMAILQUEUE +20031027 doc: qmail-log.5 adds reference to errors from $QMAILQUEUE script +20031027 doc: FAQ also points to http://cr.yp.to/qmail/faq.html and + http://qmail.org/ +20031027 code: qmail-smtpd identifies itself as netqmail +20031027 code: if $QMAILQUEUE is set, it's invoked instead of qmail-queue +20031024 code: changed errno from int to #include. +20031024 code: fixed .qmail parsing bug. +20031024 code: recognize 0.0.0.0 as a local address. +20031024 code: sendmail's -f flag now overrides environment variables. 19980615 version: qmail 1.03. 19980614 doc: eliminated BIN.setup in favor of a web page. 19980614 code: added other auto* to qmail-showctl output. diff -ruN qmail-1.03/FAQ netqmail-1.05-toaster-spf-saout/FAQ --- qmail-1.03/FAQ Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/FAQ Fri Feb 6 13:19:33 2004 @@ -1,3 +1,6 @@ +See http://cr.yp.to/qmail/faq.html for newer FAQs not included in this +document, and http://qmail.org/ for qmail community contributions. + 1. Controlling the appearance of outgoing messages 1.1. How do I set up host masquerading? 1.2. How do I set up user masquerading? diff -ruN qmail-1.03/FILES netqmail-1.05-toaster-spf-saout/FILES --- qmail-1.03/FILES Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/FILES Fri Feb 6 19:56:19 2004 @@ -10,6 +10,7 @@ INSTALL.ids INSTALL.maildir INSTALL.mbox +INSTALL-1.03 INSTALL.vsm REMOVE.sendmail REMOVE.binmail @@ -135,6 +136,8 @@ dnsip.c dnsmxip.c dnsptr.c +dnstxt.c +spfquery.c hostname.c ipmeprint.c tcp-env.c @@ -335,13 +338,16 @@ byte.h byte_chr.c byte_copy.c +byte_cspn.c byte_cr.c byte_diff.c byte_rchr.c +byte_rcspn.c byte_zero.c str.h str_chr.c str_cpy.c +str_cpyb.c str_diff.c str_diffn.c str_len.c @@ -401,6 +407,8 @@ date822fmt.c dns.h dns.c +spf.h +spf.c trylsock.c tryrsolv.c ip.h diff -ruN qmail-1.03/INSTALL netqmail-1.05-toaster-spf-saout/INSTALL --- qmail-1.03/INSTALL Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/INSTALL Fri Feb 6 13:19:33 2004 @@ -1,84 +1 @@ -SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and -information generally), the qmail system comes with NO WARRANTY. It's -much more secure and reliable than sendmail, but that's not saying much. - - -Things you have to decide before starting: - -* The qmail home directory, normally /var/qmail. To change this -directory, edit conf-qmail now. - -* The names of the qmail users and the qmail groups. To change these -names, edit conf-users and conf-groups now. - - -To create /var/qmail and configure qmail (won't interfere with sendmail): - - 1. Create the qmail home directory: - # mkdir /var/qmail - - 2. Read INSTALL.ids. You must set up the qmail group and the qmail - users before compiling the programs. - - 3. Compile the programs and create the qmail directory tree: - # make setup check - - 4. Read INSTALL.ctl and FAQ. Minimal survival command: - # ./config - - 5. Read INSTALL.alias. Minimal survival command: - # (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root) - # chmod 644 ~alias/.qmail* - - 6. Read INSTALL.mbox and INSTALL.vsm. - - 7. Read INSTALL.maildir. - - 8. Copy /var/qmail/boot/home (or proc) to /var/qmail/rc. - - -To test qmail deliveries (won't interfere with sendmail): - - 9. Enable deliveries of messages injected into qmail: - # csh -cf '/var/qmail/rc &' - -10. Read TEST.deliver. - - -To upgrade from sendmail to qmail: - -11. Read SENDMAIL. This is what your users will want to know about the - switch from sendmail to qmail. - -12. Read REMOVE.sendmail. You must remove sendmail before installing - qmail. - -13. Read REMOVE.binmail. - -14. Add - csh -cf '/var/qmail/rc &' - to your boot scripts, so that the qmail daemons are restarted - whenever your system reboots. Make sure you include the &. - -15. Make qmail's ``sendmail'' wrapper available to MUAs: - # ln -s /var/qmail/bin/sendmail /usr/lib/sendmail - # ln -s /var/qmail/bin/sendmail /usr/sbin/sendmail - /usr/sbin might not exist on your system. - -16. Set up qmail-smtpd in /etc/inetd.conf (all on one line): - smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env - tcp-env /var/qmail/bin/qmail-smtpd - -17. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons - are running.) - -18. Read TEST.receive. - - - -That's it! To report success: - % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to -Replace First M. Last with your name. - -If you have questions about qmail, join the qmail mailing list; see -http://pobox.com/~djb/qmail.html. +See http://lifewithqmail.org/lwq.html diff -ruN qmail-1.03/Makefile netqmail-1.05-toaster-spf-saout/Makefile --- qmail-1.03/Makefile Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/Makefile Fri Feb 6 20:24:14 2004 @@ -136,6 +136,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 \ @@ -203,6 +207,10 @@ compile byte_cr.c byte.h ./compile byte_cr.c +byte_cspn.o: \ +compile byte_cspn.c byte.h + ./compile byte_cspn.c + byte_diff.o: \ compile byte_diff.c byte.h ./compile byte_diff.c @@ -211,6 +219,10 @@ compile byte_rchr.c byte.h ./compile byte_rchr.c +byte_rcspn.o: \ +compile byte_rcspn.c byte.h + ./compile byte_rcspn.c + byte_zero.o: \ compile byte_zero.c byte.h ./compile byte_zero.c @@ -263,7 +275,7 @@ cdbmake_add.o cdbmake_add.o: \ -compile cdbmake_add.c cdbmake.h uint32.h +compile cdbmake_add.c cdbmake.h alloc.h uint32.h ./compile cdbmake_add.c cdbmake_hash.o: \ @@ -393,23 +405,23 @@ rm -f trydrent.o dns.lib: \ -tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \ -alloc.a error.a fs.a str.a +tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \ +stralloc.a alloc.a error.a fs.a str.a ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \ - ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \ + ipalloc.o strsalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \ -lresolv `cat socket.lib` ) >/dev/null 2>&1 \ && echo -lresolv || exit 0 ) > dns.lib rm -f tryrsolv.o tryrsolv dns.o: \ -compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \ -stralloc.h gen_alloc.h dns.h case.h +compile dns.c ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h fmt.h alloc.h \ +str.h stralloc.h gen_alloc.h dns.h case.h ./compile dns.c dnscname: \ -load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` @@ -423,46 +435,46 @@ ./compile dnsdoe.c dnsfq: \ -load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` dnsfq.o: \ compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ -dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h ./compile dnsfq.c dnsip: \ -load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` dnsip.o: \ compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ -dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h ./compile dnsip.c dnsmxip: \ -load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \ -substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \ +load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \ +alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o \ stralloc.a alloc.a substdio.a error.a str.a fs.a `cat \ dns.lib` `cat socket.lib` dnsmxip.o: \ compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \ -gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \ +gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \ now.h datetime.h exit.h ./compile dnsmxip.c dnsptr: \ -load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \ +load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ substdio.a error.a str.a fs.a dns.lib socket.lib - ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \ + ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ socket.lib` @@ -471,6 +483,18 @@ str.h scan.h dns.h dnsdoe.h ip.h exit.h ./compile dnsptr.c +dnstxt: \ +load dnstxt.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a dns.lib socket.lib + ./load dnstxt dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat dns.lib` `cat \ + socket.lib` + +dnstxt.o: \ +compile dnstxt.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +str.h scan.h dns.h dnsdoe.h ip.h exit.h + ./compile dnstxt.c + dot-qmail.0: \ dot-qmail.5 nroff -man dot-qmail.5 > dot-qmail.0 @@ -777,24 +801,24 @@ ./compile ip.c ipalloc.o: \ -compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \ +compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h \ gen_alloc.h ./compile ipalloc.c ipme.o: \ -compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \ -stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h +compile ipme.c hassalen.h byte.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \ +stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h ./compile ipme.c ipmeprint: \ -load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \ -error.a str.a fs.a socket.lib - ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \ - substdio.a error.a str.a fs.a `cat socket.lib` +load ipmeprint.o ipme.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \ +substdio.a error.a str.a fs.a socket.lib + ./load ipmeprint ipme.o ip.o ipalloc.o strsalloc.o stralloc.a \ + alloc.a substdio.a error.a str.a fs.a `cat socket.lib` ipmeprint.o: \ compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \ -ipalloc.h ip.h gen_alloc.h exit.h +ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h ./compile ipmeprint.c it: \ @@ -804,11 +828,11 @@ qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ -dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ +dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint qreceipt qsmhook qbiff \ 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 spfquery load: \ make-load warn-auto.sh systype @@ -890,6 +914,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 +1230,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 +1328,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 @@ -1439,14 +1500,15 @@ qmail-remote: \ load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \ -timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \ +timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \ ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \ substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib ./load qmail-remote control.o constmap.o timeoutread.o \ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ - ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ + ipalloc.o strsalloc.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` + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` \ + -lssl -lcrypto qmail-remote.0: \ qmail-remote.8 @@ -1455,7 +1517,7 @@ qmail-remote.o: \ compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \ subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \ -alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \ +alloc.h quote.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h \ gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \ tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h ./compile qmail-remote.c @@ -1483,12 +1545,12 @@ trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ -auto_split.o +auto_split.o env.a ./load qmail-send qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ - substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a qmail-send.0: \ qmail-send.8 @@ -1528,21 +1590,23 @@ compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \ str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \ auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \ -auto_split.h +auto_split.h spf.h ./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 \ +fs.a auto_qmail.o base64.o socket.lib dns.o dns.lib spf.o strsalloc.o \ +str.a + ./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o `cat \ + socket.lib` -lssl -lcrypto dns.o `cat dns.lib` strsalloc.o spf.o \ + str.a qmail-smtpd.0: \ qmail-smtpd.8 @@ -1551,9 +1615,10 @@ qmail-smtpd.o: \ compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \ 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 \ +error.h ipme.h ip.h ipalloc.h gen_alloc.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 wait.h \ +fd.h base64.h spf.h strsalloc.h ./compile qmail-smtpd.c qmail-start: \ @@ -1681,6 +1746,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 @@ -1779,7 +1848,7 @@ qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \ qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \ qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \ -dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \ +dnsip.c dnsmxip.c dnsptr.c dnstxt.c hostname.c ipmeprint.c tcp-env.c \ sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \ except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \ maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \ @@ -1813,8 +1882,9 @@ trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \ sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \ trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \ -byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \ -str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \ +byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.c \ +byte_zero.c str.h spf.c spf.h spfquery.c \ +str_chr.c str_cpy.c str_cpyb.c str_diff.c str_diffn.c str_len.c str_rchr.c \ str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \ getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \ subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \ @@ -1824,7 +1894,7 @@ headerbody.h headerbody.c token822.h token822.c control.h control.c \ datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \ date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \ -ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ +ipalloc.h strsalloc.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 @@ -1892,11 +1962,28 @@ spawn.o: \ compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ -stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \ +stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \ auto_qmail.h auto_uids.h auto_spawn.h ./chkspawn ./compile spawn.c +spf.o: \ +compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \ +strsalloc.h str.h fmt.h scan.h byte.h now.h + ./compile spf.c + +spfquery: \ +load spfquery.o spf.o ip.o ipme.o ipalloc.o strsalloc.o now.o dns.o \ +datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib + ./load spfquery spf.o ip.o ipme.o ipalloc.o strsalloc.o \ + now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a \ + case.a error.a fs.a `cat dns.lib` + +spfquery.o: \ +compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h \ +spf.h exit.h + ./compile spfquery.c + splogger: \ load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib ./load splogger substdio.a error.a str.a fs.a `cat \ @@ -1912,12 +1999,12 @@ ./compile splogger.c str.a: \ -makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ -str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \ -byte_cr.o byte_zero.o - ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \ - str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \ - byte_diff.o byte_copy.o byte_cr.o byte_zero.o +makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \ +str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o byte_rcspn.o \ +byte_diff.o byte_copy.o byte_cr.o byte_zero.o + ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o \ + str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \ + byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o str_chr.o: \ compile str_chr.c str.h @@ -1927,6 +2014,10 @@ compile str_cpy.c str.h ./compile str_cpy.c +str_cpyb.o: \ +compile str_cpyb.c str.h + ./compile str_cpyb.c + str_diff.o: \ compile str_diff.c str.h ./compile str_diff.c @@ -2006,6 +2097,11 @@ compile strerr_sys.c error.h strerr.h ./compile strerr_sys.c +strsalloc.o: \ +compile strsalloc.c alloc.h gen_allocdefs.h stralloc.h strsalloc.h \ +gen_alloc.h + ./compile strsalloc.c + subfderr.o: \ compile subfderr.c readwrite.h substdio.h subfd.h substdio.h ./compile subfderr.c @@ -2066,11 +2162,11 @@ tcp-env: \ load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \ -timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \ -stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib +timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \ +getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib ./load tcp-env dns.o remoteinfo.o timeoutread.o \ - timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \ - sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ + timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \ + ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \ str.a fs.a `cat dns.lib` `cat socket.lib` tcp-env.0: \ @@ -2139,3 +2235,24 @@ wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + +cert: + openssl req -new -x509 -nodes \ + -out /var/qmail/control/servercert.pem -days 366 \ + -keyout /var/qmail/control/servercert.pem + chmod 640 /var/qmail/control/servercert.pem + cp /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem + chown vpopmail:vchkpw /var/qmail/control/servercert.pem + chown qmaild:qmail /var/qmail/control/clientcert.pem + +cert-req: + openssl req -new -nodes \ + -out req.pem \ + -keyout /var/qmail/control/servercert.pem + chmod 640 /var/qmail/control/servercert.pem + chown qmaild.qmail /var/qmail/control/servercert.pem + ln -s /var/qmail/control/servercert.pem /var/qmail/control/clientcert.pem + @echo + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" + @echo "cat signed_req.pem >> /var/qmail/control/servercert.pem" + diff -ruN qmail-1.03/README netqmail-1.05-toaster-spf-saout/README --- qmail-1.03/README Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/README Fri Feb 6 13:19:33 2004 @@ -3,6 +3,31 @@ Copyright 1998 D. J. Bernstein, qmail@pobox.com +netqmail 1.04 +20031024 +No copyright claimed for changes from qmail 1.03 to netqmail 1.04 +Russell Nelson, nelson@qmail.org +NOTE: netqmail 1.04 is a community-assembled distribution of qmail from +the official qmail-1.03.tar.gz and patches approved by the community. +D. J. Bernstein did not participate in, nor has he been asked to +approve of this distribution. + +netqmail 1.05 +20040121 +James Craig Burley claims copyright on the qmail-isoc patch. For +more details, see the COPYRIGHTS file shipped with the netqmail-1.05 +distribution. + +James' patch has been combined with the original netqmail-1.04 patch +and the result incorporated into a unified netqmail-1.05 patch. +Apart from James' copyrights, no other copyright is claimed by the +distributors of netqmail for changes from qmail 1.03 to netqmail 1.05. + +NOTE: netqmail 1.05 is a community-assembled distribution of qmail from +the official qmail-1.03.tar.gz and patches approved by the community. +D. J. Bernstein did not participate in, nor has he been asked to +approve of this distribution. + qmail is a secure, reliable, efficient, simple message transfer agent. It is meant as a replacement for the entire sendmail-binmail system on typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and diff -ruN qmail-1.03/README.auth netqmail-1.05-toaster-spf-saout/README.auth --- qmail-1.03/README.auth Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/README.auth Fri Feb 6 13:19:48 2004 @@ -0,0 +1,175 @@ +*** Warning! Cuidado! Vorsicht! *** +=================================== +*** Version 0.30 of the patch changes the arguments which must be +*** passed to qmail-smtpd. If you are upgrading from a previous +*** version of the patch, take care to ensure your invocation of +*** qmail-smtpd uses the correct arguments. Otherwise, your server +*** may run as an open relay! +=================================== +*** Warning! Cuidado! Vorsicht! *** + + +This patch adds ESMTP AUTH authentication protocol support to +qmail-1.03. It's originally based on Mrs. Brisby's smtp-auth patch +with many enhancements from Krzysztof Dabrowski . + +Beginning with version 0.30, the patch was completely rewritten to +use only djb's string functions by Eric M. Johnston . + +You can always get the newest version from: +http://members.elysium.pl/brush/qmail-smtpd-auth/ + +To use all of it's functionality you will also have to obtain and +install Krzysztof's cmd5checkpw utility available at: +http://members.elysium.pl/brush/cmd5checkpw/ + +If you need more information about SMTP-AUTH itself and the +client/server support and configuration, visit: +http://members.elysium.pl/brush/smtp-auth/ + +--- + +Detailed patch information: + +This patch adds the ESMTP AUTH option to qmail-1.03, allowing the +LOGIN, PLAIN, and CRAM-MD5 AUTH types. An appropriate checkpassword +tool is necessary to support the authentication. See +http://cr.yp.to/checkpwd.html for more information on the interface. +Note that the checkpassword tool should support all of the AUTH types +advertised by qmail-smtpd. + +As reflected in the modified qmail-smtpd(8) man page, qmail-smtpd +must be invoked with three arguments: hostname, checkprogram, and +subprogram. If these arguments are missing, qmail-smtpd will still +advertise availability of AUTH, but will fail with a permanent error +when AUTH is used. + +hostname is simply used to form the CRAM-MD5 challenge. qmail-smtpd +invokes checkprogram, feeding it the username and password, in the +case of LOGIN or PLAIN, or the username, challenge, and response, in +the case of CRAM-MD5. If the user is permitted, checkprogram invokes +subprogram, which just has to exit with a status of 0 for the user to +be authenticated. Otherwise, checkprogram exits with a non-zero +status. subprogram can usually be /usr/bin/true (or /bin/true, +depending on your flavor of OS). + +If the user is successfully authenticated, the RELAYCLIENT +environment variable is effectively set for the SMTP session, and +the TCPREMOTEINFO environment variable is set to the authenticated +username, overriding any value that tcpserver may have set. The +value of TCPREMOTEINFO is reflected in a Received header. + + +How to install it: + +Simply patch your qmail-1.03 distribution with the included patch +file and recompile & install like usual. + +The steps to do this are as follows (assuming your virgin +qmail-1.03 install is in "../qmail-1.03"): + + cp README.auth base64.c base64.h ../qmail-1.03 + patch -d ../qmail-1.03 < auth.patch + +Install qmail normally, with the exception of the new arguments +to qmail-smtpd described elsewhere in this file. + +Also obtain, unpack, compile and install the cmd5checkpw utility +(or some other checkpassword utility) and add a sample account to +/etc/poppasswd file. This file must be readable by the qmail-smtpd +user, usually qmaild. + + +How to use it: + +*** Warning: In version 0.30 the arguments have changed from +*** previous versions of qmail-smtpd-auth. Take care to make sure +*** you update your startup scripts if updating! + +If you're running qmail-smtpd from inetd, you'll want to do the +following: + +smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env \ +/var/qmail/bin/qmail-smtpd mail.acme.com /bin/cmd5checkpw /bin/true + +Replace mail.acme.com with your hostname. The second argument to +qmail-smtpd is your checkpassword utility (preferably cmd5checkpw +or some alternative that can handle CRAM-MD5). The third argument +is the executable that the checkpassword utility execs when +authentication is successful. (Note that the location of "true" +is OS dependent: you may need /usr/bin/true.) + +Invocations using tcpserver will require analagous changes. Give +your inetd a kill -HUP or restart tcpserver and away you go. + + +Caveats: + +Please note that as authentication needs vary wildly across +installations, no effort has been made to make this patch work ``out +of the box.'' You'll have to procure or develop your own +checkpassword program. Also note that CRAM-MD5 will require you to +keep plaintext passwords. You'll probably want to disable this AUTH +type if you're just using /etc/passwd (keeping in mind that PLAIN and +LOGIN aren't quite as safe over the wire) -- just undefine AUTHCRAM +in qmail-smtpd. + +Krzysztof Dabrowski's cmd5checkpw tool used as an example in this +document supports the three AUTH types included in this patch. +It's available at http://www.elysium.pl/members/brush/cmd5checkpw/. + +This patch has been generated against the stock qmail 1.03 +distribution. The results of combining this patch with others are +unknown. + + +Features: + +This patch supports the following auth methods: LOGIN, PLAIN and +CRAM-MD5. + + +Compatibility: + +The following MUA's are confirmed to work with this patch: + +Eudora 4.2.2 - CRAM-MD5 +Eudora 5.0.2 - CRAM-MD5 +The Bat 1.39 - LOGIN & CRAM-MD5 +Outlook Express 4 - LOGIN +Outlook Express 5 - LOGIN +Outlook 2000 - LOGIN +Netscape 4.x - LOGIN & PLAIN +Netscape 4.0x - LOGIN +Pegasus Mail 3.1x - CRAM-MD5 + + +Various compatibility issues: + +Testing with Pegasus Mail 3.1 revealed that it requires the new style +(RFC recommended) greeting message. Both styles are now enabled to +maintain the highest degree of compatibility with various clients. +This fix was suggested by David Harris , +the developer of Pegasus Mail. + + +Acknowledgments: + +This patch is based on work by Krzysztof Dabrowski at +http://members.elysium.pl/brush/qmail-smtpd-auth/ and ``Mrs. Brisby'' +at http://www.nimh.org/hacks/qmail-smtpd.c which has been further +developed by Eric M. Johnston . + +--- + +THIS SOFTWARE IS IN THE PUBLIC DOMAIN, IS PROVIDED BY THE AUTHOR +``AS IS,'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -ruN qmail-1.03/README.qregex netqmail-1.05-toaster-spf-saout/README.qregex --- qmail-1.03/README.qregex Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/README.qregex Fri Feb 6 13:19:48 2004 @@ -0,0 +1,110 @@ +QREGEX (v2) - README [12/28/01] +A Regular Expression matching patch for qmail 1.03 + + +OVERVIEW: + +qregex adds the ability to match address evelopes via Regular Expressions (REs) +in the qmail-smtpd process. It has the abiltiy to match both `mail from` and +`rcpt to` commands with no load at all the parent process. 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 noting is because qregex was re-written to better conform to the security +gaurantee set forth by the author. The original version used stdio.h and stdlib.h +for reading the control files where as v2 now uses all stralloc functions which +are much more regulated against buffer overruns and the likes. +See: http://cr.yp.to/qmail/guarantee.html + + + +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 provide a regex +interface) but if you run into problems let me know. + + - Solaris 2.7 (7, SunOS 5.7) + - Solaris 2.8 (8, SunOS 5.8) + - OpenBSD 2.8 + - OpenBSD 2.9 + - FreeBSD 4.3-RELEASE + - FreeBSD 5.0-CURRENT + - Linux + + + +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 like me it is installed as 'gpatch') + +- If this is a new setup. +Uncompress and untar the qmail archive, copy the 'qregex.patch' file into the new +qmail-1.03 directory and run "patch < 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 this is an existing setup. +FIRST: create your control files (see below). +Copy the 'qregex.patch' file into your existing qmail source directory. +Run "patch < 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) + + + +CONTROL FILES: + +qregex provides you with two new control files. +The first (which really isn't new) is "control/badmailfrom". This file used to be +used to statically match addresses and now will contain your REs for matching from +the 'mail from' command. +The second is "control/badmailto", it is the exact same as the first except it matches +against the 'rcpt to' command. + +If you prefer you can symlink the two files (ln -s badmailfrom badmailto) and only +need to maintain one set of rules. Beware this might cause problems in certian +setups. + + Here's an example "badmailfrom" file. + ----------------------------------- + # drop everything containing the word spam + .*spam.* + # force users to fully qualify themselves (ie deny "user", accept "user@domain") + !@ + ----------------------------------- + + And "badmailto" (a litte more interesting) + ----------------------------------- + # must not contain invalid characters, brakets or multiple @'s + [\W\D!%#:\*\^] + [\(\)] + [\{\}] + @.*@ + ----------------------------------- + +Also you can use the non-RE character '!' to start a RE to 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. + + +INTERNALS: + +qregex (or regexmatch as the function is called) will be called during both the +`rcpt to` and `mail from` handling routenes in "qmail-smtpd.c". When called it will +read the proper control file then one by one compile and execute the regex on the +envelope 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: +All comments/questions/critisim welcomed... + www : http://www.unixpimps.org/software/qregex + email: evan at unixpimps dot org diff -ruN qmail-1.03/TARGETS netqmail-1.05-toaster-spf-saout/TARGETS --- qmail-1.03/TARGETS Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/TARGETS Fri Feb 6 19:56:19 2004 @@ -15,6 +15,14 @@ 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 @@ -100,11 +108,14 @@ str_diff.o str_diffn.o str_cpy.o +str_cpyb.o str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o +byte_cspn.o +byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o @@ -171,8 +182,10 @@ timeoutconn.o tcpto.o dns.o +spf.o ip.o ipalloc.o +strsalloc.o hassalen.h ipme.o ndelay.o @@ -212,6 +225,9 @@ headerbody.o hfield.o token822.o +spf.o +spfquery.o +spfquery qmail-inject predate.o predate @@ -250,8 +266,10 @@ qmail-qmtpd.o rcpthosts.o qmail-qmtpd +base64.o qmail-smtpd.o qmail-smtpd +qregex.o sendmail.o sendmail tcp-env.o @@ -270,6 +288,8 @@ dnsip dnsmxip.o dnsmxip +dnstxt.o +dnstxt dnsfq.o dnsfq hostname.o diff -ruN qmail-1.03/UPGRADE netqmail-1.05-toaster-spf-saout/UPGRADE --- qmail-1.03/UPGRADE Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/UPGRADE Fri Feb 6 13:19:33 2004 @@ -3,7 +3,7 @@ much more secure and reliable than sendmail, but that's not saying much. -Here's how to upgrade to qmail 1.03. This procedure will overwrite the +Here's how to upgrade to netqmail 1.05. This procedure will overwrite the old qmail binaries. Furthermore, it may begin delivering messages from the queue before you have had a chance to test it. @@ -17,7 +17,7 @@ Before starting, compare conf* to your old conf*, and make any necessary -changes. You can copy conf* from 1.02. +changes. You can copy conf* from 1.02 or 1.03. How to install: diff -ruN qmail-1.03/VERSION netqmail-1.05-toaster-spf-saout/VERSION --- qmail-1.03/VERSION Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/VERSION Fri Feb 6 13:19:33 2004 @@ -1 +1 @@ -qmail 1.03 +netqmail 1.05 diff -ruN qmail-1.03/base64.c netqmail-1.05-toaster-spf-saout/base64.c --- qmail-1.03/base64.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/base64.c Fri Feb 6 13:19:48 2004 @@ -0,0 +1,90 @@ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(in,l,out) +const unsigned char *in; +int l; +stralloc *out; /* not null terminated */ +{ + int i, j; + unsigned char a[4]; + unsigned char b[3]; + char *s; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,l + 2)) return -1; /* XXX generous */ + s = out->s; + + for (i = 0;i < l;i += 4) { + for (j = 0;j < 4;j++) + if ((i + j) < l && in[i + j] != B64PAD) + { + a[j] = str_chr(b64alpha,in[i + j]); + if (a[j] > 63) return 1; + } + else a[j] = 0; + + b[0] = (a[0] << 2) | (a[1] >> 4); + b[1] = (a[1] << 4) | (a[2] >> 2); + b[2] = (a[2] << 6) | (a[3]); + + *s++ = b[0]; + + if (in[i + 1] == B64PAD) break; + *s++ = b[1]; + + if (in[i + 2] == B64PAD) break; + *s++ = b[2]; + } + out->len = s - out->s; + while (out->len && !out->s[out->len - 1]) --out->len; /* XXX avoid? */ + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; + s = out->s; + + for (i = 0;i < in->len;i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + return 0; +} diff -ruN qmail-1.03/base64.h netqmail-1.05-toaster-spf-saout/base64.h --- qmail-1.03/base64.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/base64.h Fri Feb 6 13:19:48 2004 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff -ruN qmail-1.03/byte.h netqmail-1.05-toaster-spf-saout/byte.h --- qmail-1.03/byte.h Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/byte.h Fri Feb 6 19:56:19 2004 @@ -3,6 +3,8 @@ extern unsigned int byte_chr(); extern unsigned int byte_rchr(); +extern unsigned int byte_cspn(); +extern unsigned int byte_rcspn(); extern void byte_copy(); extern void byte_copyr(); extern int byte_diff(); diff -ruN qmail-1.03/byte_cspn.c netqmail-1.05-toaster-spf-saout/byte_cspn.c --- qmail-1.03/byte_cspn.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/byte_cspn.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,11 @@ +#include "byte.h" + +unsigned int byte_cspn(s,n,c) +register char *s; +register unsigned int n; +register char *c; +{ + while(*c) + n = byte_chr(s,n,*c++); + return n; +} diff -ruN qmail-1.03/byte_rcspn.c netqmail-1.05-toaster-spf-saout/byte_rcspn.c --- qmail-1.03/byte_rcspn.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/byte_rcspn.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,17 @@ +#include "byte.h" + +unsigned int byte_rcspn(s,n,c) +register char *s; +register unsigned int n; +register char *c; +{ + unsigned int ret,pos,i; + + for(ret = n,pos = 0;*c;++c) { + i = byte_rchr(s + pos,n - pos,*c) + pos; + if (i < n) ret = pos = i; + } + + return ret; +} + diff -ruN qmail-1.03/cdb_seek.c netqmail-1.05-toaster-spf-saout/cdb_seek.c --- qmail-1.03/cdb_seek.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/cdb_seek.c Fri Feb 6 13:19:33 2004 @@ -1,6 +1,5 @@ #include #include -extern int errno; #include "cdb.h" #ifndef SEEK_SET diff -ruN qmail-1.03/cdbmake_add.c netqmail-1.05-toaster-spf-saout/cdbmake_add.c --- qmail-1.03/cdbmake_add.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/cdbmake_add.c Fri Feb 6 13:19:33 2004 @@ -1,3 +1,4 @@ +#include "alloc.h" #include "cdbmake.h" void cdbmake_init(cdbm) diff -ruN qmail-1.03/chkspawn.c netqmail-1.05-toaster-spf-saout/chkspawn.c --- qmail-1.03/chkspawn.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/chkspawn.c Fri Feb 6 13:19:48 2004 @@ -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); } diff -ruN qmail-1.03/conf-cc netqmail-1.05-toaster-spf-saout/conf-cc --- qmail-1.03/conf-cc Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/conf-cc Fri Feb 6 13:19:48 2004 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DTLS -I/usr/local/ssl/include This will be used to compile .c files. diff -ruN qmail-1.03/conf-spawn netqmail-1.05-toaster-spf-saout/conf-spawn --- qmail-1.03/conf-spawn Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/conf-spawn Fri Feb 6 13:19:48 2004 @@ -1,4 +1,4 @@ -120 +509 This is a silent concurrency limit. You can't set it above 255. On some systems you can't set it above 125. qmail will refuse to compile if the diff -ruN qmail-1.03/dns.c netqmail-1.05-toaster-spf-saout/dns.c --- qmail-1.03/dns.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/dns.c Fri Feb 6 19:56:19 2004 @@ -7,10 +7,9 @@ #include extern int res_query(); extern int res_search(); -extern int errno; -extern int h_errno; #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "fmt.h" #include "alloc.h" #include "str.h" @@ -18,17 +17,22 @@ #include "dns.h" #include "case.h" +#define MAXTXT 256 + 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]; static struct ip_address ip; +static char txt[MAXTXT]; unsigned short pref; static stralloc glue = {0}; @@ -45,18 +49,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); @@ -66,7 +85,7 @@ if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } - numanswers = ntohs(response.hdr.ancount); + numanswers = ntohs(((HEADER *)response.buf)->ancount); return 0; } @@ -179,6 +198,38 @@ return 0; } +static int findtxt(wanttype) +int wanttype; +{ + unsigned short rrtype; + unsigned short rrdlen; + int i; + + if (numanswers <= 0) return 2; + --numanswers; + if (responsepos == responseend) return DNS_SOFT; + + i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); + if (i < 0) return DNS_SOFT; + responsepos += i; + + i = responseend - responsepos; + if (i < 4 + 3 * 2) return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + if (rrtype == wanttype) { + str_copyb(txt, responsepos, rrdlen); + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + void dns_init(flagsearch) int flagsearch; { @@ -237,15 +288,18 @@ return len; } -int dns_ptr(sa,ip) -stralloc *sa; +static int dns_ptrplus(ssa,ip) +strsalloc *ssa; struct ip_address *ip; { + stralloc sa = {0}; int r; - if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM; - sa->len = iaafmt(sa->s,ip); - switch(resolve(sa,T_PTR)) + if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM; + sa.len = iaafmt(sa.s,ip); + r = resolve(&sa,T_PTR); + alloc_free(sa.s); + switch(r) { case DNS_MEM: return DNS_MEM; case DNS_SOFT: return DNS_SOFT; @@ -256,13 +310,35 @@ if (r == DNS_SOFT) return DNS_SOFT; if (r == 1) { - if (!stralloc_copys(sa,name)) return DNS_MEM; - return 0; + stralloc sa2 = {0}; + if (!stralloc_copys(&sa2,name)) return DNS_MEM; + if (!strsalloc_append(ssa,&sa2)) return DNS_MEM; } } + if (ssa->len) return 0; return DNS_HARD; } +int dns_ptr(ssa,ip) +strsalloc *ssa; +struct ip_address *ip; +{ + int r; + int j; + + if (!strsalloc_readyplus(ssa,0)) return DNS_MEM; + ssa->len = 0; + r = dns_ptrplus(ssa,ip); + if (r < 0) + { + for (j = 0;j < ssa->len;++j) + alloc_free(ssa->sa[j].s); + ssa->len = 0; + } + return r; +} + + static int dns_ipplus(ia,sa,pref) ipalloc *ia; stralloc *sa; @@ -270,6 +346,14 @@ { int r; struct ip_mx ix; +#ifdef TLS + stralloc fqdn = {0}; + + if (!stralloc_copy(&fqdn,sa)) return DNS_MEM; + if (!stralloc_0(&fqdn)) return DNS_MEM; + ix.fqdn = fqdn.s; + alloc_free(fqdn); +#endif if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; @@ -330,6 +414,9 @@ ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { +#ifdef TLS + ix.fqdn = NULL; +#endif if (!ipalloc_append(ia,&ix)) return DNS_MEM; return 0; } @@ -397,4 +484,50 @@ alloc_free(mx); return flagsoft; +} + + +static int dns_txtplus(ssa,sa) +strsalloc *ssa; +stralloc *sa; +{ + int r; + + switch(resolve(sa,T_TXT)) + { + case DNS_MEM: return DNS_MEM; + case DNS_SOFT: return DNS_SOFT; + case DNS_HARD: return DNS_HARD; + } + while ((r = findtxt(T_TXT)) != 2) + { + if (r == DNS_SOFT) return DNS_SOFT; + if (r == 1) + { + stralloc sa = {0}; + if (!stralloc_copyb(&sa,txt+1,(unsigned char)txt[0])) return DNS_MEM; + if (!strsalloc_append(ssa,&sa)) return DNS_MEM; + } + } + if (ssa->len) return 0; + return DNS_HARD; +} + +int dns_txt(ssa,sa) +strsalloc *ssa; +stralloc *sa; +{ + int r; + int j; + + if (!strsalloc_readyplus(ssa,0)) return DNS_MEM; + ssa->len = 0; + r = dns_txtplus(ssa,sa); + if (r < 0) + { + for (j = 0;j < ssa->len;++j) + alloc_free(ssa->sa[j].s); + ssa->len = 0; + } + return r; } diff -ruN qmail-1.03/dns.h netqmail-1.05-toaster-spf-saout/dns.h --- qmail-1.03/dns.h Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/dns.h Fri Feb 6 19:56:19 2004 @@ -10,5 +10,6 @@ int dns_mxip(); int dns_ip(); int dns_ptr(); +int dns_txt(); #endif diff -ruN qmail-1.03/dnsfq.c netqmail-1.05-toaster-spf-saout/dnsfq.c --- qmail-1.03/dnsfq.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/dnsfq.c Fri Feb 6 19:56:19 2004 @@ -5,15 +5,19 @@ #include "dnsdoe.h" #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "exit.h" stralloc sa = {0}; +strsalloc ssa = {0}; ipalloc ia = {0}; void main(argc,argv) int argc; char **argv; { + int j; + if (!argv[1]) _exit(100); if (!stralloc_copys(&sa,argv[1])) @@ -25,8 +29,11 @@ { substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100); } - dnsdoe(dns_ptr(&sa,&ia.ix[0].ip)); - substdio_putflush(subfdout,sa.s,sa.len); - substdio_putsflush(subfdout,"\n"); + dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip)); + for(j = 0;j < ssa.len;++j) + { + substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len); + substdio_putsflush(subfdout,"\n"); + } _exit(0); } diff -ruN qmail-1.03/dnsptr.c netqmail-1.05-toaster-spf-saout/dnsptr.c --- qmail-1.03/dnsptr.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/dnsptr.c Fri Feb 6 19:56:19 2004 @@ -6,22 +6,28 @@ #include "dns.h" #include "dnsdoe.h" #include "ip.h" +#include "strsalloc.h" #include "exit.h" -stralloc sa = {0}; +strsalloc ssa = {0}; struct ip_address ip; void main(argc,argv) int argc; char **argv; { + int j; + if (!argv[1]) _exit(100); ip_scan(argv[1],&ip); dns_init(0); - dnsdoe(dns_ptr(&sa,&ip)); - substdio_putflush(subfdout,sa.s,sa.len); - substdio_putsflush(subfdout,"\n"); + dnsdoe(dns_ptr(&ssa,&ip)); + for(j = 0;j < ssa.len;++j) + { + substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len); + substdio_putsflush(subfdout,"\n"); + } _exit(0); } diff -ruN qmail-1.03/dnstxt.c netqmail-1.05-toaster-spf-saout/dnstxt.c --- qmail-1.03/dnstxt.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/dnstxt.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,32 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "str.h" +#include "scan.h" +#include "dns.h" +#include "dnsdoe.h" +#include "strsalloc.h" +#include "exit.h" + +strsalloc ssa = {0}; +stralloc sa = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + int j; + + if (!argv[1]) _exit(100); + + if (!stralloc_copys(&sa, argv[1])) + { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); } + dns_init(0); + dnsdoe(dns_txt(&ssa,&sa)); + for (j = 0;j < ssa.len;++j) + { + substdio_put(subfdout,ssa.sa[j].s,ssa.sa[j].len); + substdio_putsflush(subfdout,"\n"); + } + _exit(0); +} diff -ruN qmail-1.03/error.3 netqmail-1.05-toaster-spf-saout/error.3 --- qmail-1.03/error.3 Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/error.3 Fri Feb 6 13:19:33 2004 @@ -3,8 +3,8 @@ error \- syscall error codes .SH SYNTAX .B #include - -extern int \fBerrno\fP; +.br +.B #include extern int \fBerror_intr\fP; .br diff -ruN qmail-1.03/error.h netqmail-1.05-toaster-spf-saout/error.h --- qmail-1.03/error.h Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/error.h Fri Feb 6 13:19:33 2004 @@ -1,7 +1,7 @@ #ifndef ERROR_H #define ERROR_H -extern int errno; +#include extern int error_intr; extern int error_nomem; diff -ruN qmail-1.03/ipalloc.h netqmail-1.05-toaster-spf-saout/ipalloc.h --- qmail-1.03/ipalloc.h Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/ipalloc.h Fri Feb 6 13:19:48 2004 @@ -3,7 +3,12 @@ #include "ip.h" +#ifdef TLS +#include "stralloc.h" +struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ; +#else struct ip_mx { struct ip_address ip; int pref; } ; +#endif #include "gen_alloc.h" diff -ruN qmail-1.03/ipme.c netqmail-1.05-toaster-spf-saout/ipme.c --- qmail-1.03/ipme.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/ipme.c Fri Feb 6 13:19:33 2004 @@ -46,6 +46,11 @@ ipme.len = 0; ix.pref = 0; + /* 0.0.0.0 is a special address which always refers to + * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a. + */ + byte_copy(&ix.ip,4,"\0\0\0\0"); + if (!ipalloc_append(&ipme,&ix)) { return 0; } if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1; len = 256; diff -ruN qmail-1.03/maildirflags.c netqmail-1.05-toaster-spf-saout/maildirflags.c --- qmail-1.03/maildirflags.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildirflags.c Fri Feb 6 13:19:48 2004 @@ -0,0 +1,23 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include +#include + +static const char rcsid[]="$Id: maildirflags.c,v 1.1 2000/10/07 01:10:19 mrsam 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); +} diff -ruN qmail-1.03/maildirgetquota.c netqmail-1.05-toaster-spf-saout/maildirgetquota.c --- qmail-1.03/maildirgetquota.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildirgetquota.c Fri Feb 6 13:19:48 2004 @@ -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: maildirgetquota.h,v 1.5 1999/12/06 13:21:05 mrsam Exp $"; + +#define QUOTABUFSIZE 256 + +int maildir_getquota(const char *, char [QUOTABUFSIZE]); + +#ifdef __cplusplus +} +#endif + +#endif diff -ruN qmail-1.03/maildirmisc.h netqmail-1.05-toaster-spf-saout/maildirmisc.h --- qmail-1.03/maildirmisc.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildirmisc.h Fri Feb 6 13:19:48 2004 @@ -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: maildirmisc.h,v 1.8 2000/12/25 17:33:06 mrsam 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 diff -ruN qmail-1.03/maildiropen.c netqmail-1.05-toaster-spf-saout/maildiropen.c --- qmail-1.03/maildiropen.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildiropen.c Fri Feb 6 13:19:48 2004 @@ -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: maildiropen.c,v 1.7 2000/12/10 04:43:44 mrsam 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); +} diff -ruN qmail-1.03/maildirparsequota.c netqmail-1.05-toaster-spf-saout/maildirparsequota.c --- qmail-1.03/maildirparsequota.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildirparsequota.c Fri Feb 6 13:19:48 2004 @@ -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: maildirparsequota.c,v 1.2 1999/12/06 13:21:05 mrsam 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); +} diff -ruN qmail-1.03/maildirquota.c netqmail-1.05-toaster-spf-saout/maildirquota.c --- qmail-1.03/maildirquota.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildirquota.c Fri Feb 6 13:19:48 2004 @@ -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: maildirquota.c,v 1.9 2002/05/01 04:05:33 mrsam Exp $"; + +/* Read the maildirsize file */ + +int maildirsize_read(const char *filename, /* The filename */ + int *fdptr, /* Keep the file descriptor open */ + off_t *sizeptr, /* Grand total of maildir size */ + unsigned *cntptr, /* Grand total of message count */ + unsigned *nlines, /* # of lines in maildirsize */ + struct stat *statptr) /* The stats on maildirsize */ +{ +char buf[5120]; +int f; +char *p; +unsigned l; +int n; +int first; + + if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0) + return (-1); + p=buf; + l=sizeof(buf); + + while (l) + { + n=read(f, p, l); + if (n < 0) + { + close(f); + return (-1); + } + if (n == 0) break; + p += n; + l -= n; + } + if (l == 0 || fstat(f, statptr)) /* maildir too big */ + { + close(f); + return (-1); + } + + *sizeptr=0; + *cntptr=0; + *nlines=0; + *p=0; + p=buf; + first=1; + while (*p) + { + long n=0; + int c=0; + char *q=p; + + while (*p) + if (*p++ == '\n') + { + p[-1]=0; + break; + } + + if (first) + { + first=0; + continue; + } + sscanf(q, "%ld %d", &n, &c); + *sizeptr += n; + *cntptr += c; + ++ *nlines; + } + *fdptr=f; + return (0); +} + +static char *makenewmaildirsizename(const char *, int *); +static int countcurnew(const char *, time_t *, off_t *, unsigned *); +static int countsubdir(const char *, const char *, + time_t *, off_t *, unsigned *); +static int statcurnew(const char *, time_t *); +static int statsubdir(const char *, const char *, time_t *); + +#define MDQUOTA_SIZE 'S' /* Total size of all messages in maildir */ +#define MDQUOTA_BLOCKS 'B' /* Total # of blocks for all messages in + maildir -- NOT IMPLEMENTED */ +#define MDQUOTA_COUNT 'C' /* Total number of messages in maildir */ + +static int qcalc(off_t s, unsigned n, const char *quota, int *percentage) +{ +off_t i; +int spercentage=0; +int npercentage=0; + + errno=ENOSPC; + while (quota && *quota) + { + int x=1; + + if (*quota < '0' || *quota > '9') + { + ++quota; + continue; + } + i=0; + while (*quota >= '0' && *quota <= '9') + i=i*10 + (*quota++ - '0'); + switch (*quota) { + default: + if (i < s) + { + *percentage=100; + return (-1); + } + + /* + ** For huge quotas, over 20mb, + ** divide numerator & denominator by 1024 to prevent + ** an overflow when multiplying by 100 + */ + + x=1; + if (i > 20000000) x=1024; + + spercentage = i ? (s/x) * 100 / (i/x):100; + break; + case 'C': + + if (i < n) + { + *percentage=100; + return (-1); + } + + /* Ditto */ + + x=1; + if (i > 20000000) x=1024; + + npercentage = i ? ((off_t)n/x) * 100 / (i/x):100; + break; + } + } + *percentage = spercentage > npercentage ? spercentage:npercentage; + return (0); +} + +static int doaddquota(const char *, int, const char *, long, int, int); + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, int *percentage); + + +int maildir_checkquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt) +{ +int dummy; + + return (docheckquota(dir, maildirsize_fdptr, quota_type, + xtra_size, xtra_cnt, &dummy)); +} + +int maildir_readquota(const char *dir, const char *quota_type) +{ +int percentage=0; +int fd=-1; + + (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage); + if (fd >= 0) + close(fd); + return (percentage); +} + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, + int *percentage) +{ +char *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); +char *newmaildirsizename; +struct stat stat_buf; +int maildirsize_fd; +off_t maildirsize_size; +unsigned maildirsize_cnt; +unsigned maildirsize_nlines; +int n; +time_t tm; +time_t maxtime; +DIR *dirp; +struct dirent *de; + + if (checkfolder == 0) return (-1); + *maildirsize_fdptr= -1; + strcat(strcpy(checkfolder, dir), "/maildirfolder"); + if (stat(checkfolder, &stat_buf) == 0) /* Go to parent */ + { + strcat(strcpy(checkfolder, dir), "/.."); + n=docheckquota(checkfolder, maildirsize_fdptr, + quota_type, xtra_size, xtra_cnt, percentage); + free(checkfolder); + return (n); + } + if (!quota_type || !*quota_type) return (0); + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + time(&tm); + if (maildirsize_read(checkfolder, &maildirsize_fd, + &maildirsize_size, &maildirsize_cnt, + &maildirsize_nlines, &stat_buf) == 0) + { + n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage); + + if (n == 0) + { + free(checkfolder); + *maildirsize_fdptr=maildirsize_fd; + return (0); + } + close(maildirsize_fd); + + if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60) + return (n); + } + + maxtime=0; + maildirsize_size=0; + maildirsize_cnt=0; + + if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt)) + { + free(checkfolder); + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size, + &maildirsize_cnt)) + { + free(checkfolder); + closedir(dirp); + return (-1); + } + } + if (dirp) + { +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + { + free(checkfolder); + return (-1); + } +#endif + } + + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(checkfolder); + return (-1); + } + + *maildirsize_fdptr=maildirsize_fd; + + if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 1)) + { + free(newmaildirsizename); + unlink(newmaildirsizename); + close(maildirsize_fd); + *maildirsize_fdptr= -1; + free(checkfolder); + return (-1); + } + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + + if (rename(newmaildirsizename, checkfolder)) + { + free(checkfolder); + unlink(newmaildirsizename); + close(maildirsize_fd); + *maildirsize_fdptr= -1; + } + free(checkfolder); + free(newmaildirsizename); + + tm=0; + + if (statcurnew(dir, &tm)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (statsubdir(dir, de->d_name, &tm)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + closedir(dirp); + return (-1); + } + } + if (dirp) + { +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + return (-1); + } +#endif + } + + if (tm != maxtime) /* Race condition, someone changed something */ + { + errno=EAGAIN; + return (-1); + } + + return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage)); +} + +int maildir_addquota(const char *dir, int maildirsize_fd, + const char *quota_type, long maildirsize_size, int maildirsize_cnt) +{ + if (!quota_type || !*quota_type) return (0); + return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 0)); +} + +static int doaddquota(const char *dir, int maildirsize_fd, + const char *quota_type, long maildirsize_size, int maildirsize_cnt, + int isnew) +{ +union { + char buf[100]; + struct stat stat_buf; + } u; /* Scrooge */ +char *newname2=0; +char *newmaildirsizename=0; +struct iovec iov[3]; +int niov; +struct iovec *p; +int n; + + niov=0; + if ( maildirsize_fd < 0) + { + newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + if (!newname2) return (-1); + strcat(strcpy(newname2, dir), "/maildirfolder"); + if (stat(newname2, &u.stat_buf) == 0) + { + strcat(strcpy(newname2, dir), "/.."); + n=doaddquota(newname2, maildirsize_fd, quota_type, + maildirsize_size, maildirsize_cnt, + isnew); + free(newname2); + return (n); + } + + strcat(strcpy(newname2, dir), "/maildirsize"); + + if ((maildirsize_fd=maildir_safeopen(newname2, + O_RDWR|O_APPEND, 0644)) < 0) + { + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(newname2); + return (-1); + } + + maildirsize_fd=maildir_safeopen(newmaildirsizename, + O_CREAT|O_RDWR|O_APPEND, 0644); + + if (maildirsize_fd < 0) + { + free(newname2); + return (-1); + } + isnew=1; + } + } + + if (isnew) + { + iov[0].iov_base=(caddr_t)quota_type; + iov[0].iov_len=strlen(quota_type); + iov[1].iov_base=(caddr_t)"\n"; + iov[1].iov_len=1; + niov=2; + } + + + sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt); + iov[niov].iov_base=(caddr_t)u.buf; + iov[niov].iov_len=strlen(u.buf); + + p=iov; + ++niov; + n=0; + while (niov) + { + if (n) + { + if (n < p->iov_len) + { + p->iov_base= + (caddr_t)((char *)p->iov_base + n); + p->iov_len -= n; + } + else + { + n -= p->iov_len; + ++p; + --niov; + continue; + } + } + + n=writev( maildirsize_fd, p, niov); + + if (n <= 0) + { + if (newname2) + { + close(maildirsize_fd); + free(newname2); + } + return (-1); + } + } + if (newname2) + { + close(maildirsize_fd); + + if (newmaildirsizename) + { + rename(newmaildirsizename, newname2); + free(newmaildirsizename); + } + free(newname2); + } + return (0); +} + +/* New maildirsize is built in the tmp subdirectory */ + +static char *makenewmaildirsizename(const char *dir, int *fd) +{ +char hostname[256]; +struct stat stat_buf; +time_t t; +char *p; + + hostname[0]=0; + hostname[sizeof(hostname)-1]=0; + gethostname(hostname, sizeof(hostname)-1); + p=(char *)malloc(strlen(dir)+strlen(hostname)+130); + if (!p) return (0); + + for (;;) + { + char tbuf[NUMBUFSIZE]; + char pbuf[NUMBUFSIZE]; + + time(&t); + strcat(strcpy(p, dir), "/tmp/"); + sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s", + str_time_t(t, tbuf), + str_pid_t(getpid(), pbuf), hostname); + + if (stat( (const char *)p, &stat_buf) < 0 && + (*fd=maildir_safeopen(p, + O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0) + break; + sleep(3); + } + return (p); +} + +static int statcurnew(const char *dir, time_t *maxtimestamp) +{ +char *p=(char *)malloc(strlen(dir)+5); +struct stat stat_buf; + + if (!p) return (-1); + strcat(strcpy(p, dir), "/cur"); + if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp) + *maxtimestamp=stat_buf.st_mtime; + strcat(strcpy(p, dir), "/new"); + if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp) + *maxtimestamp=stat_buf.st_mtime; + free(p); + return (0); +} + +static int statsubdir(const char *dir, const char *subdir, time_t *maxtime) +{ +char *p; +int n; + + if ( *subdir != '.' || strcmp(subdir, ".") == 0 || + strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0) + return (0); + + p=(char *)malloc(strlen(dir)+strlen(subdir)+2); + if (!p) return (-1); + strcat(strcat(strcpy(p, dir), "/"), subdir); + n=statcurnew(p, maxtime); + free(p); + return (n); +} + +static int docount(const char *, time_t *, off_t *, unsigned *); + +static int countcurnew(const char *dir, time_t *maxtime, + off_t *sizep, unsigned *cntp) +{ +char *p=(char *)malloc(strlen(dir)+5); +int n; + + if (!p) return (-1); + strcat(strcpy(p, dir), "/new"); + n=docount(p, maxtime, sizep, cntp); + if (n == 0) + { + strcat(strcpy(p, dir), "/cur"); + n=docount(p, maxtime, sizep, cntp); + } + free(p); + return (n); +} + +static int countsubdir(const char *dir, const char *subdir, time_t *maxtime, + off_t *sizep, unsigned *cntp) +{ +char *p; +int n; + + if ( *subdir != '.' || strcmp(subdir, ".") == 0 || + strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0) + return (0); + + p=(char *)malloc(strlen(dir)+strlen(subdir)+2); + if (!p) return (2); + strcat(strcat(strcpy(p, dir), "/"), subdir); + n=countcurnew(p, maxtime, sizep, cntp); + free(p); + return (n); +} + +static int docount(const char *dir, time_t *dirstamp, + off_t *sizep, unsigned *cntp) +{ +struct stat stat_buf; +char *p; +DIR *dirp; +struct dirent *de; +unsigned long s; + + if (stat(dir, &stat_buf)) return (0); /* Ignore */ + if (stat_buf.st_mtime > *dirstamp) *dirstamp=stat_buf.st_mtime; + if ((dirp=opendir(dir)) == 0) return (0); + while ((de=readdir(dirp)) != 0) + { + const char *n=de->d_name; + + if (*n == '.') continue; + + /* PATCH - do not count msgs marked as deleted */ + + for ( ; *n; n++) + { + if (n[0] != ':' || n[1] != '2' || + n[2] != ',') continue; + n += 3; + while (*n >= 'A' && *n <= 'Z') + { + if (*n == 'T') break; + ++n; + } + break; + } + if (*n == 'T') continue; + n=de->d_name; + + + if (maildir_parsequota(n, &s) == 0) + stat_buf.st_size=s; + else + { + p=(char *)malloc(strlen(dir)+strlen(n)+2); + if (!p) + { + closedir(dirp); + return (-1); + } + strcat(strcat(strcpy(p, dir), "/"), n); + if (stat(p, &stat_buf)) + { + free(p); + continue; + } + free(p); + } + *sizep += stat_buf.st_size; + ++*cntp; + } + +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + return (-1); +#endif + return (0); +} diff -ruN qmail-1.03/maildirquota.h netqmail-1.05-toaster-spf-saout/maildirquota.h --- qmail-1.03/maildirquota.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/maildirquota.h Fri Feb 6 13:19:48 2004 @@ -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: maildirquota.h,v 1.2 2000/09/04 17:10:28 mrsam 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 diff -ruN qmail-1.03/numlib.h netqmail-1.05-toaster-spf-saout/numlib.h --- qmail-1.03/numlib.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/numlib.h Fri Feb 6 13:19:48 2004 @@ -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: numlib.h,v 1.3 2001/08/12 15:46:40 mrsam 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 diff -ruN qmail-1.03/overmaildirquota.c netqmail-1.05-toaster-spf-saout/overmaildirquota.c --- qmail-1.03/overmaildirquota.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/overmaildirquota.c Fri Feb 6 13:19:48 2004 @@ -0,0 +1,43 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "maildirquota.h" +#include +#include +#include +#include + +static const char rcsid[]="$Id: overquota.c,v 1.0 2002/06/09 16:21:05 mr +sam Exp $"; + + +int user_over_maildirquota( const char *dir, const char *q) +{ +struct stat stat_buf; +int quotafd; +int ret_value; + + if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && + stat_buf.st_size > 0 && *q) + { + if (maildir_checkquota(dir, "afd, q, stat_buf.st_size, 1) + && errno != EAGAIN) + { + if (quotafd >= 0) close(quotafd); + ret_value = 1; + } else { + maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1); + if (quotafd >= 0) close(quotafd); + ret_value = 0; + } + } else { + ret_value = 0; + } + + return(ret_value); +} diff -ruN qmail-1.03/qmail-control.9 netqmail-1.05-toaster-spf-saout/qmail-control.9 --- qmail-1.03/qmail-control.9 Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-control.9 Fri Feb 6 19:56:19 2004 @@ -55,6 +55,7 @@ .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 @@ -63,6 +64,10 @@ .I rcpthosts \fR(none) \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd .I smtproutes \fR(none) \fRqmail-remote +.I spfbehavior \fR0 \fRqmail-smtpd +.I spfexp \fR(default) \fRqmail-smtpd +.I spfguess \fR(none) \fRqmail-smtpd +.I spfrules \fR(none) \fRqmail-smtpd .I timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd diff -ruN qmail-1.03/qmail-local.c netqmail-1.05-toaster-spf-saout/qmail-local.c --- qmail-1.03/qmail-local.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-local.c Fri Feb 6 13:19:48 2004 @@ -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)"); @@ -645,7 +656,7 @@ { cmds.s[j] = 0; k = j; - while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')) + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) cmds.s[--k] = 0; switch(cmds.s[i]) { diff -ruN qmail-1.03/qmail-log.5 netqmail-1.05-toaster-spf-saout/qmail-log.5 --- qmail-1.03/qmail-log.5 Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-log.5 Fri Feb 6 13:19:33 2004 @@ -232,6 +232,11 @@ is unable to queue a bounce message, usually because the machine is almost out of memory. It will try again later. +This can also be caused by incorrect settings of +.B $QMAILQUEUE +or errors in a program or script which +.B $QMAILQUEUE +points to. .TP .B unable to stat ... .B qmail-send diff -ruN qmail-1.03/qmail-pop3d.c netqmail-1.05-toaster-spf-saout/qmail-pop3d.c --- qmail-1.03/qmail-pop3d.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-pop3d.c Fri Feb 6 13:29:27 2004 @@ -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(); } @@ -66,14 +67,14 @@ void die_scan() { err("unable to scan $HOME/Maildir"); die(); } void err_syntax() { err("syntax error"); } -void err_unimpl() { err("unimplemented"); } +void err_unimpl(arg) char *arg; { err("unimplemented"); } void err_deleted() { err("already deleted"); } void err_nozero() { err("messages are counted from 1"); } void err_toobig() { err("not that many messages"); } void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } -void okay() { puts("+OK \r\n"); flush(); } +void okay(arg) char *arg; { substdio_puts(&ssout,"+OK \r\n"); flush(); } void printfn(fn) char *fn; { @@ -146,43 +147,66 @@ } } -void pop3_stat() +void pop3_stat(arg) char *arg; { int i; unsigned long total; 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(); } -void pop3_rset() +void pop3_rset(arg) char *arg; { int i; for (i = 0;i < numm;++i) m[i].flagdeleted = 0; last = 0; - okay(); + okay(0); } -void pop3_last() +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() +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,7 +216,23 @@ if (!stralloc_0(&line)) die_nomem(); rename(m[i].fn,line.s); /* if it fails, bummer */ } - okay(); + + 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(); } @@ -214,7 +254,7 @@ if (i == -1) return; m[i].flagdeleted = 1; if (i + 1 > last) last = i + 1; - okay(); + okay(0); } void list(i,flaguidl) @@ -222,10 +262,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,15 +274,15 @@ if (*arg) { i = msgno(arg); if (i == -1) return; - puts("+OK "); + substdio_puts(&ssout,"+OK "); list(i,flaguidl); } else { - okay(); + okay(0); for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); - puts(".\r\n"); + substdio_puts(&ssout,".\r\n"); } flush(); } @@ -267,7 +307,7 @@ fd = open_read(m[i].fn); if (fd == -1) { err_nosuch(); return; } - okay(); + okay(0); substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf)); blast(&ssmsg,limit); close(fd); @@ -299,7 +339,7 @@ getlist(); - okay(); + okay(0); commands(&ssin,pop3commands); die(); } diff -ruN qmail-1.03/qmail-popup.c netqmail-1.05-toaster-spf-saout/qmail-popup.c --- qmail-1.03/qmail-popup.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-popup.c Fri Feb 6 13:19:33 2004 @@ -64,10 +64,10 @@ void err_syntax() { err("syntax error"); } void err_wantuser() { err("USER first"); } -void err_authoriz() { err("authorization first"); } +void err_authoriz(arg) char *arg; { err("authorization first"); } -void okay() { puts("+OK \r\n"); flush(); } -void pop3_quit() { okay(); die(); } +void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +void pop3_quit(arg) char *arg; { okay(0); die(); } char unique[FMT_ULONG + FMT_ULONG + 3]; @@ -136,7 +136,7 @@ void pop3_user(arg) char *arg; { if (!*arg) { err_syntax(); return; } - okay(); + okay(0); seenuser = 1; if (!stralloc_copys(&username,arg)) die_nomem(); if (!stralloc_0(&username)) die_nomem(); diff -ruN qmail-1.03/qmail-queue.8 netqmail-1.05-toaster-spf-saout/qmail-queue.8 --- qmail-1.03/qmail-queue.8 Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-queue.8 Fri Feb 6 13:19:33 2004 @@ -40,6 +40,12 @@ However, the recipients probably expect to see a proper header, as described in .BR qmail-header(5) . + +Programs included with qmail which invoke +.B qmail-queue +will invoke the contents of +.B $QMAILQUEUE +instead, if that environment variable is set. .SH "FILESYSTEM RESTRICTIONS" .B qmail-queue imposes two constraints on the queue structure: diff -ruN qmail-1.03/qmail-remote.c netqmail-1.05-toaster-spf-saout/qmail-remote.c --- qmail-1.03/qmail-remote.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-remote.c Fri Feb 6 13:19:48 2004 @@ -26,8 +26,18 @@ #include "tcpto.h" #include "readwrite.h" #include "timeoutconn.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif + +#ifdef TLS +#include +#include +SSL *ssl = NULL; + +stralloc tlsclientciphers = {0}; +#endif #define HUGESMTPTEXT 5000 @@ -107,17 +117,94 @@ int smtpfd; int timeout = 1200; +#ifdef TLS +int flagtimedout = 0; +void sigalrm() +{ + flagtimedout = 1; +} + +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_read(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); + if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) + {char buf[1024]; + + out("ZTLS connection to "); outhost(); out(" died: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + }else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_write(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); + if (SSL_get_error(ssl, r) != SSL_ERROR_NONE) + {char buf[1024]; + + out("ZTLS connection to "); outhost(); out(" died: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + }else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + +static int client_cert_cb(SSL *s,X509 **x509, EVP_PKEY **pkey) +{ + out("ZTLS found no client cert in control/clientcert.pem\n"); + zerodie(NULL,NULL); +} + +static int verify_cb(int ok, X509_STORE_CTX * ctx) +{ + return (1); +} +#endif + int saferead(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutread(timeout,smtpfd,buf,len); +#else r = timeoutread(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,smtpfd,buf,len); +#else r = timeoutwrite(timeout,smtpfd,buf,len); +#endif if (r <= 0) dropped(); return r; } @@ -186,6 +273,34 @@ out(append); out(".\n"); outsmtptext(); + +/* TAG */ +#if defined(TLS) && defined(DEBUG) +#define ONELINE_NAME(X) X509_NAME_oneline(X,NULL,0) + + if(ssl){ + X509 *peer; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + + /* we want certificate details */ + peer=SSL_get_peer_certificate(ssl); + if (peer != NULL) { + char *str; + + str=ONELINE_NAME(X509_get_subject_name(peer)); + out("; subject="); out(str); + OPENSSL_free(str); + str=ONELINE_NAME(X509_get_issuer_name(peer)); + out("; issuer="); out(str); + OPENSSL_free(str); + X509_free(peer); + } + out(";\n"); + } +#endif + zerodie(); } @@ -216,20 +331,158 @@ stralloc recip = {0}; +#ifdef TLS +void smtp(fqdn) +char *fqdn; +#else void smtp() +#endif { unsigned long code; int flagbother; int i; - +#ifdef TLS + int needtlsauth = 0; + SSL_CTX *ctx; + int saveerrno, r; + + stralloc servercert = {0}; + struct stat st; + if(fqdn){ + if(!stralloc_copys(&servercert, "control/tlshosts/")) temp_nomem(); + if(!stralloc_catb(&servercert, fqdn, str_len(fqdn))) temp_nomem(); + if(!stralloc_catb(&servercert, ".pem", 4)) temp_nomem(); + if(!stralloc_0(&servercert)) temp_nomem(); + if (stat(servercert.s,&st) == 0) needtlsauth = 1; + } +#endif + if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); +#ifdef TLS + substdio_puts(&smtpto,"EHLO "); +#else substdio_puts(&smtpto,"HELO "); +#endif substdio_put(&smtpto,helohost.s,helohost.len); substdio_puts(&smtpto,"\r\n"); substdio_flush(&smtpto); +#ifdef TLS + if (smtpcode() != 250){ + substdio_puts(&smtpto,"HELO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); + } +#else if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - +#endif + +#ifdef TLS + i = 0; + while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+12 < smtptext.len) && + str_diffn(smtptext.s+i+4,"STARTTLS\n",9)); + if (i+12 < smtptext.len) + { + substdio_puts(&smtpto,"STARTTLS\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 220) + { + SSL_library_init(); + if(!(ctx=SSL_CTX_new(SSLv23_client_method()))) + {char buf[1024]; + + out("ZTLS not available: error initializing ctx: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + if((stat("control/clientcert.pem", &st) == 0) && + ((SSL_CTX_use_RSAPrivateKey_file(ctx, "control/clientcert.pem", SSL_FILETYPE_PEM) <= 0) || + (SSL_CTX_use_certificate_chain_file(ctx, "control/clientcert.pem") <= 0) || + (SSL_CTX_check_private_key(ctx) <= 0))) + /* if there is a cert and it is bad, I fail + if there is no cert, I leave it to the other side to complain */ + SSL_CTX_set_client_cert_cb(ctx, client_cert_cb); + + /*SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);*/ + SSL_CTX_set_cipher_list(ctx,tlsclientciphers.s); + + if (needtlsauth){ + if (!SSL_CTX_load_verify_locations(ctx, servercert.s, NULL)) + {out("ZTLS unable to load "); out(servercert.s); out("\n"); + zerodie();} + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + } + + if(!(ssl=SSL_new(ctx))) + {char buf[1024]; + + out("ZTLS not available: error initializing ssl: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + SSL_set_fd(ssl,smtpfd); + + alarm(timeout); + r = SSL_connect(ssl); saveerrno = errno; + alarm(0); + if (flagtimedout) + {out("ZTLS not available: connect timed out\n"); + zerodie();} + errno = saveerrno; + if (r<=0) + {char buf[1024]; + + out("ZTLS not available: connect failed: "); + SSL_load_error_strings(); + out(ERR_error_string(ERR_get_error(), buf)); + out("\n"); + SSL_shutdown(ssl); + zerodie(); + } + if (needtlsauth) + /* should also check alternate names */ + {char commonName[256]; + + if ((r=SSL_get_verify_result(ssl)) != X509_V_OK) + {out("ZTLS unable to verify server with "); + out(servercert.s); out(": "); + out(X509_verify_cert_error_string(r)); out("\n"); + zerodie(); + } + X509_NAME_get_text_by_NID(X509_get_subject_name( + SSL_get_peer_certificate(ssl)), + NID_commonName, commonName, 256); + if (strcasecmp(fqdn,commonName)){ + out("ZTLS connection to "); out(fqdn); + out(" wanted, certificate for "); out(commonName); + out(" received\n"); + zerodie();} + } + + substdio_puts(&smtpto,"EHLO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + + if (smtpcode() != 250) + { + quit("ZTLS connected to "," but my name was rejected"); + } + } + } + if ((!ssl) && needtlsauth) + {out("ZNo TLS achieved while "); out(servercert.s); out(" exists.\n"); + quit();} +#endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); @@ -324,6 +577,11 @@ case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } +#ifdef TLS + if (control_rldef(&tlsclientciphers,"control/tlsclientciphers",0,"DEFAULT") != 1) + temp_control(); + if(!stralloc_0(&tlsclientciphers)) temp_nomem(); +#endif } void main(argc,argv) @@ -338,7 +596,10 @@ int flagallaliases; int flagalias; char *relayhost; - + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); @@ -417,7 +678,11 @@ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; +#ifdef TLS + smtp(ip.ix[i].fqdn); /* does not return */ +#else smtp(); /* does not return */ +#endif } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); diff -ruN qmail-1.03/qmail-send.c netqmail-1.05-toaster-spf-saout/qmail-send.c --- qmail-1.03/qmail-send.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-send.c Fri Feb 6 13:19:48 2004 @@ -262,6 +262,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(); @@ -906,41 +908,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; @@ -1544,7 +1547,7 @@ numjobs = 0; for (c = 0;c < CHANNELS;++c) { - char ch; + char ch, ch1; int u; int r; do @@ -1552,7 +1555,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]; } diff -ruN qmail-1.03/qmail-showctl.c netqmail-1.05-toaster-spf-saout/qmail-showctl.c --- qmail-1.03/qmail-showctl.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-showctl.c Fri Feb 6 19:56:19 2004 @@ -15,6 +15,7 @@ #include "auto_patrn.h" #include "auto_spawn.h" #include "auto_split.h" +#include "spf.h" stralloc me = {0}; int meok; @@ -257,6 +258,10 @@ do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_int("spfbehavior","0","The SPF behavior is ",""); + do_str("spfrules",0,"","The local SPF rules are: "); + do_str("spfguess",0,"","The guess SPF rules are: "); + do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 "); do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds"); do_int("timeoutremote","1200","SMTP client data timeout is "," seconds"); do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); @@ -292,6 +297,10 @@ if (str_equal(d->d_name,"rcpthosts")) continue; if (str_equal(d->d_name,"smtpgreeting")) continue; if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"spfbehavior")) continue; + if (str_equal(d->d_name,"spfexp")) continue; + if (str_equal(d->d_name,"spfguess")) continue; + if (str_equal(d->d_name,"spfrules")) continue; if (str_equal(d->d_name,"timeoutconnect")) continue; if (str_equal(d->d_name,"timeoutremote")) continue; if (str_equal(d->d_name,"timeoutsmtpd")) continue; diff -ruN qmail-1.03/qmail-smtpd.8 netqmail-1.05-toaster-spf-saout/qmail-smtpd.8 --- qmail-1.03/qmail-smtpd.8 Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-smtpd.8 Fri Feb 6 19:56:19 2004 @@ -3,6 +3,11 @@ qmail-smtpd \- receive mail via SMTP .SH SYNOPSIS .B qmail-smtpd +[ +.I hostname +.I checkprogram +.I subprogram +] .SH DESCRIPTION .B qmail-smtpd receives mail messages via the Simple Mail Transfer Protocol (SMTP) @@ -23,7 +28,29 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options. + +.B qmail-smtpd +can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes +.IR checkprogram , +which reads on file descriptor 3 the username, a 0 byte, the password +or challenge derived from +.IR hostname , +another 0 byte, a CRAM-MD5 response (if applicable to the AUTH type), +and a final 0 byte. +.I checkprogram +invokes +.I subprogram +upon successful authentication, which should in turn return 0 to +.BR qmail-smtpd , +effectively setting the environment variables RELAYCLIENT and TCPREMOTEINFO +(any supplied value replaced with the authenticated username). +.B qmail-smtpd +will reject the authentication attempt if it receives a nonzero return +value from +.I checkprogram +or +.IR subprogram . .SH TRANSPARENCY .B qmail-smtpd converts the SMTP newline convention into the UNIX newline convention @@ -97,6 +124,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 @@ -169,6 +202,41 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. +.TP 5 +.I spfbehavior +Set to a value between 1 and 6 to enable SPF checks; 0 to disable. +1 selects 'annotate-only' mode, where +.B qmail-smtpd +will annotate incoming email with +.B Received-SPF +fields, but will not reject any messages. 2 will produce temporary +failures on DNS lookup problems so you can make sure you always have +meaningful Received-SPF headers. 3 selects 'reject' mode, +where incoming mail will be rejected if the SPF record says 'fail'. 4 +selects a more stricter rejection mode, which is like 'reject' mode, +except that incoming mail will also be rejected when the SPF record +says 'softfail'. 5 will also reject when the SPF record says 'neutral', +and 6 if no SPF records are available at all (or a syntax error was +encountered). The contents of this file are overridden by the value of +the +.B SPFBEHAVIOR +environment variable, if set. +Default: 0. +.TP 5 +.I spfexp +You can add a line with a an SPF explanation that will be shown to the +sender in case of a reject. It will override the default one. You can +use SPF macro expansion. +.TP 5 +.I spfguess +You can add a line with SPF rules that will be checked if a sender +domain doesn't have a SPF record. The local rules will also be used +in this case. +.TP 5 +.I spfrules +You can add a line with SPF rules that will be checked before other SPF +rules would fail. This can be used to always allow certain machines to +send certain mails. .SH "SEE ALSO" tcp-env(1), tcp-environ(5), @@ -177,3 +245,6 @@ qmail-newmrh(8), qmail-queue(8), qmail-remote(8) +.SH "HISTORY" +The patch enabling the ESMTP AUTH option is not part of the standard +qmail-1.03 distribution. diff -ruN qmail-1.03/qmail-smtpd.c netqmail-1.05-toaster-spf-saout/qmail-smtpd.c --- qmail-1.03/qmail-smtpd.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail-smtpd.c Fri Feb 6 20:22:32 2004 @@ -20,18 +20,83 @@ #include "now.h" #include "exit.h" #include "rcpthosts.h" +#ifndef TLS #include "timeoutread.h" #include "timeoutwrite.h" +#endif #include "commands.h" +#include "qregex.h" +#include "wait.h" +#include +#include "fd.h" +#include "dns.h" +#include "spf.h" + +#ifdef TLS +#include +SSL *ssl = NULL; + +stralloc clientcert = {0}; +stralloc tlsserverciphers = {0}; +#endif +#define AUTHCRAM #define MAXHOPS 100 +#define BMCHECK_BMF 0 +#define BMCHECK_BMT 1 unsigned int databytes = 0; +unsigned int mfchk = 0; int timeout = 1200; +unsigned int spfbehavior = 0; + +#ifdef TLS +int flagtimedout = 0; +void sigalrm() +{ + flagtimedout = 1; +} +int ssl_timeoutread(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_read(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ)); + }else r = read(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} + + +int ssl_timeoutwrite(timeout,fd,buf,n) int timeout; int fd; char *buf; int n; +{ + int r; int saveerrno; + if (flagtimedout) { errno = error_timeout; return -1; } + alarm(timeout); + if (ssl) { + while(((r = SSL_write(ssl,buf,n)) <= 0) + && (SSL_get_error(ssl, r) == SSL_ERROR_WANT_WRITE)); + }else r = write(fd,buf,n); + saveerrno = errno; + alarm(0); + if (flagtimedout) { errno = error_timeout; return -1; } + errno = saveerrno; + return r; +} +#endif int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + r = ssl_timeoutwrite(timeout,fd,buf,len); +#else r = timeoutwrite(timeout,fd,buf,len); +#endif if (r <= 0) _exit(1); return r; } @@ -49,29 +114,47 @@ 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_bmf() { out("553 sorry, your envelope sender has been denied (#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"); } +void err_bmt() { out("533 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } -void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } +#ifdef TLS +void err_nogwcert() { out("553 no valid cert for gatewaying (#5.7.1)\r\n"); } +#endif +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"); } void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } -void err_noop() { out("250 ok\r\n"); } -void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } +void err_noop(arg) char *arg; { out("250 ok\r\n"); } +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 cancelled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } stralloc greeting = {0}; +stralloc spflocal = {0}; +stralloc spfguess = {0}; +stralloc spfexp = {0}; void smtp_greet(code) char *code; { substdio_puts(&ssout,code); substdio_put(&ssout,greeting.s,greeting.len); } -void smtp_help() +void smtp_help(arg) char *arg; { - out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); + out("214 netqmail home page: http://qmail.org/netqmail\r\n"); } -void smtp_quit() +void smtp_quit(arg) char *arg; { smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } @@ -81,6 +164,9 @@ char *remoteinfo; char *local; char *relayclient; +#ifdef TLS +char *tlsciphers; +#endif stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -96,11 +182,20 @@ int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; +int tarpitcount = 0; +int tarpitdelay = 5; + +int bmtok = 0; +stralloc bmt = {0}; +struct constmap mapbmt; void setup() { char *x; unsigned long u; +#ifdef TLS + char *tlsciphers; +#endif if (control_init() == -1) die_control(); if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) @@ -110,18 +205,47 @@ if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; + if (control_readint(&tarpitcount,"control/tarpitcount") == -1) die_control(); + if (tarpitcount < 0) tarpitcount = 0; + x = env_get("TARPITCOUNT"); + if (x) { scan_ulong(x,&u); tarpitcount = u; }; + if (control_readint(&tarpitdelay,"control/tarpitdelay") == -1) die_control(); + if (tarpitdelay < 0) tarpitdelay = 0; + x = env_get("TARPITDELAY"); + if (x) { scan_ulong(x,&u); tarpitdelay = u; }; + if (rcpthosts_init() == -1) die_control(); + 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(); + if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + bmtok = control_readfile(&bmt,"control/badmailto",0); + if (bmtok == -1) die_control(); + if (!constmap_init(&mapbmt,bmt.s,bmt.len,0)) die_nomem(); if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); if (x) { scan_ulong(x,&u); databytes = u; } if (!(databytes + 1)) --databytes; + if (control_readint(&spfbehavior,"control/spfbehavior") == -1) + die_control(); + x = env_get("SPFBEHAVIOR"); + if (x) { scan_ulong(x,&u); spfbehavior = u; } + + if (control_readline(&spflocal,"control/spfrules") == -1) die_control(); + if (spflocal.len && !stralloc_0(&spflocal)) die_nomem(); + if (control_readline(&spfguess,"control/spfguess") == -1) die_control(); + if (spfguess.len && !stralloc_0(&spfguess)) die_nomem(); + if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1) + die_control(); + if (!stralloc_0(&spfexp)) die_nomem(); + remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -131,6 +255,17 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); +#ifdef TLS + if (tlsciphers = env_get("TLSCIPHERS")){ + if (!stralloc_copys(&tlsserverciphers,tlsciphers)) die_nomem(); + } + else { + if (control_rldef(&tlsserverciphers,"control/tlsserverciphers",0,"DEFAULT") != 1) + die_control(); + } + if (!stralloc_0(&tlsserverciphers)) die_nomem(); +#endif + dohelo(remotehost); } @@ -197,14 +332,57 @@ return 1; } -int bmfcheck() +int bmcheck(which) int which; +{ + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + stralloc bmb = {0}; + stralloc curregex = {0}; + + if (which == BMCHECK_BMF) { + if (!stralloc_copy(&bmb,&bmf)) die_nomem(); + } else if (which == BMCHECK_BMT) { + if (!stralloc_copy(&bmb,&bmt)) 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++; + } + stralloc_copyb(&curregex,bmb.s + j,(i - j)); + stralloc_0(&curregex); + x = matchregex(addr.s, curregex.s); + if ((negate) && (x == 0)) return 1; + if (!(negate) && (x > 0)) return 1; + j = i + 1; + negate = 0; + } + return 0; +} + +int mfcheck() { + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned int random; int j; - if (!bmfok) return 0; - if (constmap(&mapbmf,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 (!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; } @@ -218,9 +396,13 @@ int seenmail = 0; -int flagbarf; /* defined if seenmail */ +int flagbarfbmf; /* defined if seenmail */ +int flagbarfbmt; +int flagbarfspf; +stralloc spfbarfmsg = {0}; stralloc mailfrom = {0}; stralloc rcptto = {0}; +int rcptcount; void smtp_helo(arg) char *arg; { @@ -229,38 +411,167 @@ } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); +#ifdef AUTHCRAM + out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); + out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); +#else + out("\r\n250-AUTH LOGIN PLAIN"); + out("\r\n250-AUTH=LOGIN PLAIN"); +#endif +#ifdef TLS + if (!ssl) out("\r\n250-STARTTLS"); +#endif + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } -void smtp_rset() +void smtp_rset(arg) char *arg; { seenmail = 0; out("250 flushed\r\n"); } void smtp_mail(arg) char *arg; { + int r; + if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); + if (bmfok) flagbarfbmf = bmcheck(BMCHECK_BMF); + switch(mfcheck()) { + case DNS_HARD: err_hmf(); return; + case DNS_SOFT: err_smf(); return; + case DNS_MEM: die_nomem(); + } + flagbarfspf = 0; + if (spfbehavior && !relayclient) + { + switch(r = spfcheck()) { + case SPF_OK: env_put2("SPFRESULT","pass"); break; + case SPF_NONE: env_put2("SPFRESULT","none"); break; + case SPF_UNKNOWN: env_put2("SPFRESULT","unknown"); break; + case SPF_NEUTRAL: env_put2("SPFRESULT","neutral"); break; + case SPF_SOFTFAIL: env_put2("SPFRESULT","softfail"); break; + case SPF_FAIL: env_put2("SPFRESULT","fail"); break; + case SPF_ERROR: env_put2("SPFRESULT","error"); break; + } + switch (r) { + case SPF_NOMEM: + die_nomem(); + case SPF_ERROR: + if (spfbehavior < 1) break; + out("451 SPF lookup failure (#4.3.0)\r\n"); + return; + case SPF_NONE: + case SPF_UNKNOWN: + if (spfbehavior < 6) break; + case SPF_NEUTRAL: + if (spfbehavior < 5) break; + case SPF_SOFTFAIL: + if (spfbehavior < 4) break; + case SPF_FAIL: + if (spfbehavior < 3) break; + if (!spfexplanation(&spfbarfmsg)) die_nomem(); + if (!stralloc_0(&spfbarfmsg)) die_nomem(); + flagbarfspf = 1; + } + } + else + env_unset("SPFRESULT"); seenmail = 1; 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 err_spf() { + int i,j; + + for(i = 0; i < spfbarfmsg.len; i = j + 1) { + j = byte_chr(spfbarfmsg.s + i, spfbarfmsg.len - i, '\n') + i; + if (j < spfbarfmsg.len) { + out("550-"); + spfbarfmsg.s[j] = 0; + out(spfbarfmsg.s); + spfbarfmsg.s[j] = '\n'; + out("\r\n"); + } else { + out("550 "); + out(spfbarfmsg.s); + out(" (#5.7.1)\r\n"); + } + } +} +#ifdef TLS +static int verify_cb(int ok, X509_STORE_CTX * ctx) +{ + return (1); +} +#endif void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + if ((!flagbarfbmf) && (bmtok)) { flagbarfbmt = bmcheck(BMCHECK_BMT); } + if (flagbarfbmf) { err_bmf(); return; } + if (flagbarfbmt) { err_bmt(); return; } + if (flagbarfspf) { err_spf(); return; } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } else +#ifndef TLS if (!addrallowed()) { err_nogateway(); return; } +#else + if (!addrallowed()) + { + if (ssl) + { STACK_OF(X509_NAME) *sk; + X509 *peercert; + stralloc tlsclients = {0}; + struct constmap maptlsclients; + int r; + + SSL_set_verify(ssl, + SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, + verify_cb); + if ((sk = SSL_load_client_CA_file("control/clientca.pem")) == NULL) + { err_nogateway(); return; } + SSL_set_client_CA_list(ssl, sk); + if((control_readfile(&tlsclients,"control/tlsclients",0) != 1) || + !constmap_init(&maptlsclients,tlsclients.s,tlsclients.len,0)) + { err_nogateway(); return; } + + SSL_renegotiate(ssl); + SSL_do_handshake(ssl); + ssl->state = SSL_ST_ACCEPT; + SSL_do_handshake(ssl); + if ((r = SSL_get_verify_result(ssl)) != X509_V_OK) + {out("553 no valid cert for gatewaying: "); + out(X509_verify_cert_error_string(r)); + out(" (#5.7.1)\r\n"); + return; + } + + if (peercert = SSL_get_peer_certificate(ssl)) + {char emailAddress[256]; + + X509_NAME_get_text_by_NID(X509_get_subject_name( + SSL_get_peer_certificate(ssl)), + NID_pkcs9_emailAddress, emailAddress, 256); if (!stralloc_copys(&clientcert, emailAddress)) die_nomem(); + if (!constmap(&maptlsclients,clientcert.s,clientcert.len)) + { err_nogwcert(); return; } + relayclient = ""; + } + else { err_nogwcert(); return; } + } + else { err_nogateway(); return; } + } +#endif if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); + if (tarpitcount && ++rcptcount >= tarpitcount) while (sleep(tarpitdelay)); out("250 ok\r\n"); } @@ -269,7 +580,11 @@ { int r; flush(); +#ifdef TLS + r = ssl_timeoutread(timeout,fd,buf,len); +#else r = timeoutread(timeout,fd,buf,len); +#endif if (r == -1) if (errno == error_timeout) die_alarm(); if (r <= 0) die_read(); return r; @@ -316,8 +631,8 @@ if (flagmaybex) if (pos == 7) ++*hops; if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; if (flagmaybey) if (pos == 1) flaginheader = 0; + ++pos; } - ++pos; if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } } switch(state) { @@ -351,6 +666,25 @@ } } +void spfreceived() +{ + stralloc sa = {0}; + stralloc rcvd_spf = {0}; + + if (!spfbehavior || relayclient) return; + + if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem(); + if (!spfinfo(&sa)) die_nomem(); + if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem(); + if (!stralloc_append(&rcvd_spf, "\n")) die_nomem(); + if (bytestooverflow) { + bytestooverflow -= rcvd_spf.len; + if (bytestooverflow <= 0) qmail_fail(&qqt); + } + qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len); +} + + char accept_buf[FMT_ULONG]; void acceptmessage(qp) unsigned long qp; { @@ -365,10 +699,13 @@ out("\r\n"); } -void smtp_data() { +void smtp_data(arg) char *arg; { int hops; unsigned long qp; char *qqx; +#ifdef TLS + stralloc protocolinfo = {0}; +#endif if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } @@ -377,8 +714,21 @@ if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - +#ifdef TLS + if(ssl){ + if (!stralloc_copys(&protocolinfo, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)))) die_nomem(); + if (!stralloc_catb(&protocolinfo, " encrypted SMTP", 15)) die_nomem(); + if (clientcert.len){ + if (!stralloc_catb(&protocolinfo," cert ", 6)) die_nomem(); + if (!stralloc_catb(&protocolinfo,clientcert.s, clientcert.len)) die_nomem(); + } + if (!stralloc_0(&protocolinfo)) die_nomem(); + } else if (!stralloc_copyb(&protocolinfo,"SMTP",5)) die_nomem(); + received(&qqt,protocolinfo.s,local,remoteip,remotehost,remoteinfo,case_diffs(remotehost,helohost.s) ? helohost.s : 0); +#else received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); +#endif + spfreceived(); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -393,23 +743,300 @@ out(qqx + 1); out("\r\n"); } +#ifdef TLS +static RSA *tmp_rsa_cb(ssl,export,keylength) SSL *ssl; int export; int keylength; +{ + RSA* rsa; + BIO* in; + + if (!export || keylength == 512) + if (in=BIO_new(BIO_s_file_internal())) + if (BIO_read_filename(in,"control/rsa512.pem") > 0) + if (rsa=PEM_read_bio_RSAPrivateKey(in,NULL,NULL,NULL)) + return rsa; + return (RSA_generate_key(export?keylength:512,RSA_F4,NULL,NULL)); +} + +void smtp_tls(arg) char *arg; +{ + SSL_CTX *ctx; + + if (*arg) + {out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + return;} + + SSL_library_init(); + if(!(ctx=SSL_CTX_new(SSLv23_server_method()))) + {out("454 TLS not available: unable to initialize ctx (#4.3.0)\r\n"); + return;} + if(!SSL_CTX_use_RSAPrivateKey_file(ctx, "control/servercert.pem", SSL_FILETYPE_PEM)) + {out("454 TLS not available: missing RSA private key (#4.3.0)\r\n"); + return;} + if(!SSL_CTX_use_certificate_chain_file(ctx, "control/servercert.pem")) + {out("454 TLS not available: missing certificate (#4.3.0)\r\n"); + return;} + SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_cb); + SSL_CTX_set_cipher_list(ctx,tlsserverciphers.s); + SSL_CTX_load_verify_locations(ctx, "control/clientca.pem",NULL); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); + + out("220 ready for tls\r\n"); flush(); + + if(!(ssl=SSL_new(ctx))) die_read(); + SSL_set_fd(ssl,0); + if(SSL_accept(ssl)<=0) die_read(); + substdio_fdbuf(&ssout,SSL_write,ssl,ssoutbuf,sizeof(ssoutbuf)); + + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + dohelo(remotehost); +} +#endif + + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; +static stralloc user = {0}; +static stralloc pass = {0}; +static stralloc resp = {0}; +static stralloc slop = {0}; +char *hostname; +char **childargs; +substdio ssup; +char upbuf[128]; +int authd = 0; + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin, "")) die_nomem(); + + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&resp)) die_nomem(); + + if (fcntl(2,F_GETFL,0) == -1) + if (fd_copy(2,1) == -1) return err_pipe(); + close(3); + if (pipe(pi) == -1) return err_pipe(); + if (pi[0] != 3) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); + if (substdio_put(&ssup,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write(); + if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write(); + if (substdio_flush(&ssup) == -1) return err_write(); + + close(pi[1]); + byte_zero(pass.s,pass.len); + byte_zero(upbuf,sizeof upbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + while (slop.s[id]) id++; /* ignore authorize-id */ + + if (slop.len > id + 1) + if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem(); + if (slop.len > id + user.len + 2) + if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +#ifdef AUTHCRAM +int auth_cram() +{ + int i, r; + char *s; + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&pass,"<")) die_nomem(); + if (!stralloc_cats(&pass,unique)) die_nomem(); + if (!stralloc_cats(&pass,hostname)) die_nomem(); + if (!stralloc_cats(&pass,">")) die_nomem(); + if (b64encode(&pass,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + + i = str_chr(slop.s,' '); + s = slop.s + i; + while (*s == ' ') ++s; + slop.s[i] = 0; + if (!stralloc_copys(&user,slop.s)) die_nomem(); + if (!stralloc_copys(&resp,s)) die_nomem(); + + if (!user.len || !resp.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login", auth_login } +, { "plain", auth_plain } +#ifdef AUTHCRAM +, { "cram-md5", auth_cram } +#endif +, { 0, err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!hostname || !*childargs) + { + out("503 auth not available (#5.3.3)\r\n"); + return; + } + if (authd) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + authd = 1; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + out("535 authorization failed (#5.7.0)\r\n"); + } +} struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +, { "auth", smtp_auth, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } , { "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; +{ + hostname = argv[1]; + childargs = argv + 2; + +#ifdef TLS + sig_alarmcatch(sigalrm); +#endif sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); diff -ruN qmail-1.03/qmail.7 netqmail-1.05-toaster-spf-saout/qmail.7 --- qmail-1.03/qmail.7 Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail.7 Fri Feb 6 13:19:33 2004 @@ -55,12 +55,14 @@ and .BR qmail-pop3d (8). -This documentation describes version -1.03 +This documentation describes netqmail version +1.05 of .BR qmail . See .B http://pobox.com/~djb/qmail.html for other .BR qmail -related -software. +software, and +.B http://qmail.org/ +for other qmail community contributions. diff -ruN qmail-1.03/qmail.c netqmail-1.05-toaster-spf-saout/qmail.c --- qmail-1.03/qmail.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/qmail.c Fri Feb 6 13:19:33 2004 @@ -6,14 +6,25 @@ #include "fd.h" #include "qmail.h" #include "auto_qmail.h" +#include "env.h" -static char *binqqargs[2] = { "bin/qmail-queue", 0 } ; +static char *binqqargs[2] = { 0, 0 } ; + +static void setup_qqargs() +{ + if(!binqqargs[0]) + binqqargs[0] = env_get("QMAILQUEUE"); + if(!binqqargs[0]) + binqqargs[0] = "bin/qmail-queue"; +} int qmail_open(qq) struct qmail *qq; { int pim[2]; int pie[2]; + + setup_qqargs(); if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } diff -ruN qmail-1.03/qregex.c netqmail-1.05-toaster-spf-saout/qregex.c --- qmail-1.03/qregex.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/qregex.c Fri Feb 6 13:19:53 2004 @@ -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) +#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); +} diff -ruN qmail-1.03/qregex.h netqmail-1.05-toaster-spf-saout/qregex.h --- qmail-1.03/qregex.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/qregex.h Fri Feb 6 13:19:53 2004 @@ -0,0 +1,5 @@ +/* simple header file for the matchregex prototype */ +#ifndef _QREGEX_H_ +#define _QREGEX_H_ +int matchregex(char *text, char *regex); +#endif diff -ruN qmail-1.03/sendmail.c netqmail-1.05-toaster-spf-saout/sendmail.c --- qmail-1.03/sendmail.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/sendmail.c Fri Feb 6 13:19:33 2004 @@ -45,6 +45,38 @@ _exit(111); } +void do_sender(s) +const char *s; +{ + char *x; + int n; + int a; + int i; + + env_unset("QMAILNAME"); + env_unset("MAILNAME"); + env_unset("NAME"); + env_unset("QMAILHOST"); + env_unset("MAILHOST"); + + n = str_len(s); + a = str_rchr(s, '@'); + if (a == n) + { + env_put2("QMAILUSER", s); + return; + } + env_put2("QMAILHOST", s + a + 1); + + x = (char *) alloc((a + 1) * sizeof(char)); + if (!x) nomem(); + for (i = 0; i < a; i++) + x[i] = s[i]; + x[i] = 0; + env_put2("QMAILUSER", x); + alloc_free(x); +} + int flagh; char *sender; @@ -118,6 +150,7 @@ if (sender) { *arg++ = "-f"; *arg++ = sender; + do_sender(sender); } *arg++ = "--"; for (i = 0;i < argc;++i) *arg++ = argv[i]; diff -ruN qmail-1.03/spawn.c netqmail-1.05-toaster-spf-saout/spawn.c --- qmail-1.03/spawn.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/spawn.c Fri Feb 6 13:19:53 2004 @@ -5,6 +5,7 @@ #include "substdio.h" #include "byte.h" #include "str.h" +#include "alloc.h" #include "stralloc.h" #include "select.h" #include "exit.h" @@ -63,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}; @@ -73,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); } @@ -155,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(); @@ -201,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; } @@ -236,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); diff -ruN qmail-1.03/spf.c netqmail-1.05-toaster-spf-saout/spf.c --- qmail-1.03/spf.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/spf.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,866 @@ +#include "stralloc.h" +#include "strsalloc.h" +#include "alloc.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "now.h" +#include "dns.h" +#include "spf.h" + +#define SPF_EXT -1 +#define SPF_SYNTAX -2 + +#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n') +#define NXTOK(b, p, a) do { (b) = (p); \ + while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \ + while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \ + } while(0) + +/* this table and macro came from wget more or less */ +/* and was in turn stolen by me from libspf as is :) */ +const static unsigned char urlchr_table[256] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, /* NUL SOH STX ETX EOT ENQ ACK BEL */ + 1, 1, 1, 1, 1, 1, 1, 1, /* BS HT LF VT FF CR SO SI */ + 1, 1, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */ + 1, 0, 1, 1, 0, 1, 1, 0, /* SP ! " # $ % & ' */ + 0, 0, 0, 1, 0, 0, 0, 1, /* ( ) * + , - . / */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ + 0, 0, 1, 1, 1, 1, 1, 1, /* 8 9 : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ + 0, 0, 0, 1, 1, 1, 1, 0, /* X Y Z [ \ ] ^ _ */ + 1, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */ + 0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */ + 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */ + 0, 0, 0, 1, 1, 1, 1, 1, /* x y z { | } ~ DEL */ + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + + +extern stralloc addr; +extern stralloc helohost; +extern char *remoteip; +extern char *local; + +extern stralloc spflocal; +extern stralloc spfguess; +extern stralloc spfexp; + +static stralloc sender_fqdn = {0}; +static stralloc explanation = {0}; +static stralloc expdomain = {0}; +static stralloc errormsg = {0}; +static char *received; + +static int recursion; +static struct ip_address ip; + +static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; }; +static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; }; +static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; }; +static void hdr_unknown() { received = "unknown (%{xr}: domain at %{d} does not designate permitted sender hosts)"; }; +static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; }; +static void hdr_none() { received = "none (%{xr}: domain at %{d} does not designate permitted sender hosts)"; }; +static void hdr_unknown_msg(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown (%{xr}: %{xe})"; }; +static void hdr_ext(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown %{xe} (%{xr}: %{xs} uses mechanism not recognized by this client)"; }; +static void hdr_syntax() { received = "unknown (%{xr}: parse error in %{xs})"; }; +static void hdr_error(e) char *e; { stralloc_copys(&errormsg, e); received = "error (%{xr}: error in processing during lookup of %{d}: %{xe})"; }; +static void hdr_dns() { hdr_error("DNS problem"); } + + +static int matchip(struct ip_address *net, int mask, struct ip_address *ip) +{ + int j; + int bytemask; + + for (j = 0; j < 4 && mask > 0; ++j) { + if (mask > 8) bytemask = 8; else bytemask = mask; + mask -= bytemask; + + if ((net->d[j] ^ ip->d[j]) & (0x100 - (1 << (8 - bytemask)))) + return 0; + } + return 1; +} + +static int getipmask(char *mask, int ipv6) { + unsigned long r; + int pos; + + if (!mask) return 32; + + pos = scan_ulong(mask, &r); + if (!pos || (mask[pos] && !(mask[pos] == '/' && ipv6))) return -1; + if (r > 32) return -1; + + return r; +} + +int spfget(stralloc *spf, stralloc *domain) +{ + strsalloc ssa = {0}; + int j; + int begin, pos, i; + int r = SPF_NONE; + + spf->len = 0; + + switch(dns_txt(&ssa, domain)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); return SPF_ERROR; + case DNS_HARD: return SPF_NONE; + } + + for (j = 0;j < ssa.len;++j) { + pos = 0; + + NXTOK(begin, pos, &ssa.sa[j]); + if (str_len(ssa.sa[j].s + begin) < 6) continue; + if (!byte_equal(ssa.sa[j].s + begin,6,"v=spf1")) continue; + if (ssa.sa[j].s[begin + 6]) { + /* check for subversion */ + if (ssa.sa[j].s[begin + 6] != '.') continue; + for(i = begin + 7;;++i) + if (!(ssa.sa[j].s[i] >= '0' && ssa.sa[j].s[i] <= '9')) break; + if (i == (begin + 7)) continue; + if (ssa.sa[j].s[i]) continue; + } + + if (spf->len > 0) { + spf->len = 0; + hdr_unknown_msg("Multiple SPF records returned"); + r = SPF_UNKNOWN; + break; + } + if (!stralloc_0(&ssa.sa[j])) return SPF_NOMEM; + if (!stralloc_copys(spf,ssa.sa[j].s + pos)) return SPF_NOMEM; + r = SPF_OK; + } + + for (j = 0;j < ssa.len;++j) + alloc_free(ssa.sa[j].s); + alloc_free(ssa.sa); + return r; +} + +static int spf_ptr(char *spec, char *mask); + +int spfsubst(stralloc *expand, char *spec, char *domain) +{ + static char hexdigits[] = "0123456789abcdef"; + stralloc sa = {0}; + char ch; + int digits = -1; + int urlencode = 0; + int reverse = 0; + int start = expand->len; + int i, pos; + char *split = "."; + + if (!stralloc_readyplus(&sa,0)) return 0; + + if (*spec == 'x') { i = 1; ++spec; } else i = 0; + ch = *spec++; + if (!ch) { alloc_free(sa.s); return 1; } + if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; } + if (i) ch -= 32; + while(*spec >= '0' && *spec <= '9') { + if (digits < 0) digits = 0; + if (digits >= 1000000) { digits = 10000000; continue; } + digits = (digits * 10) + (*spec - '0'); + spec++; + } + + while((*spec >= 'a' && *spec <= 'z') || (*spec >= 'A' && *spec <= 'Z')) { + if (*spec == 'r') reverse = 1; + spec++; + } + + if (*spec) split = spec; + + switch(ch) { + case 'l': + pos = byte_rchr(addr.s, addr.len, '@'); + if (pos < addr.len) { + if (!stralloc_copyb(&sa, addr.s, pos)) return 0; + } else + if (!stralloc_copys(&sa, "postmaster")) return 0; + break; + case 's': + if (!stralloc_copys(&sa, addr.s)) return 0; + break; + case 'o': + pos = byte_rchr(addr.s, addr.len, '@') + 1; + if (pos > addr.len) break; + if (!stralloc_copys(&sa, addr.s + pos)) return 0; + break; + case 'd': + if (!stralloc_copys(&sa, domain)) return 0; + break; + case 'i': + if (!stralloc_ready(&sa, IPFMT)) return 0; + sa.len = ip_fmt(sa.s, &ip); + break; + case 't': + if (!stralloc_ready(&sa, FMT_ULONG)) return 0; + sa.len = fmt_ulong(sa.s, (unsigned long)now()); + break; + case 'p': + if (!sender_fqdn.len) + spf_ptr(domain, 0); + if (sender_fqdn.len) { + if (!stralloc_copy(&sa, &sender_fqdn)) return 0; + } else + if (!stralloc_copys(&sa, "unknown")) return 0; + break; + case 'v': + if (!stralloc_copys(&sa, "in-addr")) return 0; + break; + case 'h': + if (!stralloc_copys(&sa, helohost.s)) return 0; /* FIXME: FQDN? */ + break; + case 'E': + if (errormsg.len && !stralloc_copy(&sa, &errormsg)) return 0; + break; + case 'R': + if (!stralloc_copys(&sa, local)) return 0; + break; + case 'S': + if (expdomain.len > 0) { + if (!stralloc_copys(&sa, "SPF record at ")) return 0; + if (!stralloc_cats(&sa, expdomain.s)) return 0; + } else { + if (!stralloc_copys(&sa, "local policy")) return 0; + } + break; + } + + if (reverse) { + for(pos = 0; digits; ++pos) { + pos += byte_cspn(sa.s + pos, sa.len - pos, split); + if (pos >= sa.len) break; + if (!--digits) break; + } + + for(; pos > 0; pos = i - 1) { + i = byte_rcspn(sa.s, pos, split) + 1; + if (i > pos) i = 0; + if (!stralloc_catb(expand, sa.s + i, pos - i)) return 0; + if (i > 0 && !stralloc_append(expand, ".")) return 0; + } + } else { + for(pos = sa.len; digits; --pos) { + i = byte_rcspn(sa.s, pos, split) + 1; + if (i > pos) { pos = 0; break; } + pos = i; + if (!--digits) break; + } + + if (!stralloc_catb(expand, sa.s + pos, sa.len - pos)) return 0; + if (split[0] != '.' || split[1]) + for(pos = 0; pos < expand->len; pos++) { + pos += byte_cspn(expand->s + pos, expand->len - pos, split); + if (pos < expand->len) + expand->s[pos] = '.'; + } + } + + if (urlencode) { + stralloc_copyb(&sa, expand->s + start, expand->len - start); + expand->len = start; + + for(pos = 0; pos < sa.len; ++pos) { + ch = sa.s[pos]; + if (urlchr_table[(unsigned char)ch]) { + if (!stralloc_readyplus(expand, 3)) return 0; + expand->s[expand->len++] = '%'; + expand->s[expand->len++] = hexdigits[(unsigned char)ch >> 4]; + expand->s[expand->len++] = hexdigits[(unsigned char)ch & 15]; + } else + if (!stralloc_append(expand, &ch)) return 0; + } + } + + alloc_free(sa.s); + return 1; +} + +int spfexpand(stralloc *sa, char *spec, char *domain) +{ + char *p; + char append; + int pos; + + if (!stralloc_readyplus(sa, 0)) return 0; + sa->len = 0; + + for(p = spec; *p; p++) { + append = *p; + if (*p == '%') { + p++; + switch(*p) { + case '%': break; + case '_': append = ' '; break; + case '-': if (!stralloc_cats(sa, "%20")) return 0; continue; + case '{': + pos = str_chr(p, '}'); + if (p[pos] != '}') { p--; break; } + p[pos] = 0; + if (!spfsubst(sa, p + 1, domain)) return 0; + p += pos; + continue; + default: p--; + } + } + if (!stralloc_append(sa, &append)) return 0; + } + + return 1; +} + +static int spflookup(stralloc *domain); + +static int spf_include(char *spec, char *mask) +{ + stralloc sa = {0}; + int r; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + r = spflookup(&sa); + alloc_free(sa.s); + + switch(r) { + case SPF_NONE: + hdr_unknown(); + r = SPF_UNKNOWN; + break; + case SPF_SYNTAX: + r = SPF_UNKNOWN; + break; + case SPF_NEUTRAL: + case SPF_SOFTFAIL: + case SPF_FAIL: + r = SPF_NONE; + break; + } + + return r; +} + +static int spf_a(char *spec, char *mask) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + int ipmask = getipmask(mask, 1); + int r; + int j; + + if (ipmask < 0) return SPF_SYNTAX; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_ip(&ia, &sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for(j = 0; j < ia.len; ++j) + if (matchip(&ia.ix[j].ip, ipmask, &ip)) { + r = SPF_OK; + break; + } + } + + alloc_free(sa.s); + alloc_free(ia.ix); + return r; +} + +static int spf_mx(char *spec, char *mask) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + int ipmask = getipmask(mask, 1); + int random = now() + (getpid() << 16); + int r; + int j; + + if (ipmask < 0) return SPF_SYNTAX; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_mxip(&ia, &sa, random)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for(j = 0; j < ia.len; ++j) + if (matchip(&ia.ix[j].ip, ipmask, &ip)) { + r = SPF_OK; + break; + } + } + + alloc_free(sa.s); + alloc_free(ia.ix); + return r; +} + +static int spf_ptr(char *spec, char *mask) +{ + strsalloc ssa = {0}; + ipalloc ia = {0}; + int len = str_len(spec); + int r; + int j, k; + int pos; + + /* we didn't find host with the matching ip before */ + if (sender_fqdn.len == 7 && str_equal(sender_fqdn.s, "unknown")) + return SPF_NONE; + + /* the hostname found will probably be the same as before */ + while (sender_fqdn.len) { + pos = sender_fqdn.len - len; + if (pos < 0) break; + if (pos > 0 && sender_fqdn.s[pos - 1] != '.') break; + if (case_diffb(sender_fqdn.s + pos, len, spec)) break; + + return SPF_OK; + } + + /* ok, either it's the first test or it's a very weird setup */ + + if (!stralloc_readyplus(&ssa, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_ptr(&ssa, &ip)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for(j = 0; j < ssa.len; ++j) { + switch(dns_ip(&ia, &ssa.sa[j])) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: break; + default: + for(k = 0; k < ia.len; ++k) + if (matchip(&ia.ix[k].ip, 32, &ip)) { + if (!sender_fqdn.len) + if (!stralloc_copy(&sender_fqdn, &ssa.sa[j])) return SPF_NOMEM; + + pos = ssa.sa[j].len - len; + if (pos < 0) continue; + if (pos > 0 && ssa.sa[j].s[pos - 1] != '.') continue; + if (case_diffb(ssa.sa[j].s + pos, len, spec)) continue; + + stralloc_copy(&sender_fqdn, &ssa.sa[j]); + r = SPF_OK; + break; + } + } + + if (r == SPF_ERROR) break; + } + } + + for(j = 0;j < ssa.len;++j) + alloc_free(ssa.sa[j].s); + + alloc_free(ssa.sa); + alloc_free(ia.ix); + + if (!sender_fqdn.len) + if (!stralloc_copys(&sender_fqdn, "unknown")) return SPF_NOMEM; + + return r; +} + +static int spf_ip(char *spec, char *mask) +{ + struct ip_address net; + int ipmask = getipmask(mask, 0); + + if (ipmask < 0) return SPF_SYNTAX; + if (!ip_scan(spec, &net)) return SPF_SYNTAX; + + if (matchip(&net, ipmask, &ip)) return SPF_OK; + + return SPF_NONE; +} + +static int spf_exists(char *spec, char *mask) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + int r; + + if (!stralloc_copys(&sa, spec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM; + + switch(dns_ip(&ia, &sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break; + case DNS_HARD: r = SPF_NONE; break; + default: r = SPF_OK; + } + + alloc_free(sa.s); + alloc_free(ia.ix); + return r; +} + +static struct mechanisms { + char *mechanism; + int (*func)(char *spec, char *mask); + unsigned int takes_spec : 1; + unsigned int takes_mask : 1; + unsigned int expands : 1; + unsigned int filldomain : 1; + int defresult : 4; +} mechanisms[] = { + { "all", 0, 0,0,0,0,SPF_OK } +, { "include", spf_include,1,0,1,0,0 } +, { "a", spf_a, 1,1,1,1,0 } +, { "mx", spf_mx, 1,1,1,1,0 } +, { "ptr", spf_ptr, 1,0,1,1,0 } +, { "ip4", spf_ip, 1,1,0,0,0 } +, { "ip6", 0, 1,1,0,0,SPF_NONE } +, { "exists", spf_exists, 1,0,1,0,0 } +, { "extension",0, 1,1,0,0,SPF_EXT } +, { 0, 0, 1,1,0,0,SPF_EXT } +}; + +static int spfmech(char *mechanism, char *spec, char *mask, char *domain) +{ + struct mechanisms *mech; + stralloc sa = {0}; + int r; + int pos; + + for(mech = mechanisms; mech->mechanism; mech++) + if (str_equal(mech->mechanism, mechanism)) break; + + if (mech->takes_spec && !spec && mech->filldomain) spec = domain; + if (!mech->takes_spec != !spec) return SPF_SYNTAX; + if (!mech->takes_mask && mask) return SPF_SYNTAX; + if (!mech->func) return mech->defresult; + + if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM; + if (mech->expands && spec != domain) { + if (!spfexpand(&sa, spec, domain)) return SPF_NOMEM; + for (pos = 0; (sa.len - pos) > 255;) { + pos += byte_chr(sa.s + pos, sa.len - pos, '.'); + if (pos < sa.len) pos++; + } + sa.len -= pos; + if (pos > 0) byte_copy(sa.s, sa.len, sa.s + pos); + stralloc_0(&sa); + spec = sa.s; + } + + r = mech->func(spec, mask); + + alloc_free(sa.s); + return r; +} + +static struct default_aliases { + char *alias; + int defret; +} default_aliases[] = { + { "allow", SPF_OK } +, { "pass", SPF_OK } +, { "deny", SPF_FAIL } +, { "softdeny",SPF_SOFTFAIL } +, { "fail", SPF_FAIL } +, { "softfail",SPF_SOFTFAIL } +, { "unknown", SPF_NEUTRAL } +, { 0, SPF_UNKNOWN } +}; + +static int spflookup(stralloc *domain) +{ + stralloc spf = {0}; + stralloc sa = {0}; + struct default_aliases *da; + int main = !recursion; + int local_pos = -1; + int r, q; + int begin, pos; + int i; + int prefix; + int done; + char *p; + + if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM; + + /* fallthrough result */ + if (main) hdr_none(); + +redirect: + if (++recursion > 20) { + alloc_free(spf.s); + alloc_free(sa.s); + hdr_unknown_msg("Maximum nesting level exceeded, possible loop"); + return SPF_SYNTAX; + } + + if (!stralloc_0(domain)) return SPF_NOMEM; + if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM; + + r = spfget(&spf, domain); + if (r == SPF_NONE) { + if (!main) { alloc_free(spf.s); return r; } + + if (spfguess.len) { + /* try to guess */ + if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM; + if (!stralloc_append(&spf, " ")) return SPF_NOMEM; + } else + spf.len = 0; + + /* append local rulest */ + if (spflocal.len) { + local_pos = spf.len; + if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM; + } + if (!stralloc_0(&spf)) return SPF_NOMEM; + + expdomain.len = 0; + } else if (r == SPF_OK) { + if (!stralloc_0(&spf)) return SPF_NOMEM; + if (main) hdr_neutral(); + r = SPF_NEUTRAL; + + /* try to add local rules before fail all mechs */ + if (main && spflocal.len) { + pos = 0; + p = (char *) 0; + while(pos < spf.len) { + NXTOK(begin, pos, &spf); + if (!spf.s[begin]) continue; + + if (p && spf.s[begin] != *p) p = (char *) 0; + if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' || + spf.s[begin] == '?')) p = &spf.s[begin]; + + if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) { + /* ok, we can insert the local rules at p */ + local_pos = p - spf.s; + + stralloc_readyplus(&spf, spflocal.len); + p = spf.s + local_pos; + byte_copyr(p + spflocal.len, spf.len - local_pos, p); + byte_copy(p, spflocal.len, spflocal.s); + spf.len += spflocal.len; + + pos += spflocal.len; + break; + } + } + + if (pos >= spf.len) pos = spf.len - 1; + for(i = 0; i < pos; i++) + if (!spf.s[i]) spf.s[i] = ' '; + } + } else { + alloc_free(spf.s); + return r; + } + + pos = 0; + done = 0; + while(pos < spf.len) { + NXTOK(begin, pos, &spf); + if (!spf.s[begin]) continue; + + /* in local ruleset? */ + if (!done && local_pos >= 0 && begin >= local_pos) + if (begin < (local_pos + spflocal.len)) + expdomain.len = 0; + else + if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM; + + for (p = spf.s + begin;*p;++p) + if (*p == ':' || *p == '/' || *p == '=') break; + + if (*p == '=') { + *p++ = 0; + + /* modifiers are simply handled here */ + if (str_equal(spf.s + begin, "redirect")) { + if (done) continue; + + if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM; + stralloc_copy(domain, &sa); + + hdr_unknown(); + r = SPF_UNKNOWN; + + goto redirect; + } else if (str_equal(spf.s + begin, "default")) { + if (done) continue; + + for(da = default_aliases; da->alias; ++da) + if (str_equal(da->alias, p)) break; + + r = da->defret; + } else if (str_equal(spf.s + begin, "exp")) { + strsalloc ssa = {0}; + + if (!main) continue; + + if (!stralloc_copys(&sa, p)) return SPF_NOMEM; + switch(dns_txt(&ssa, &sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_SOFT: continue; /* FIXME... */ + case DNS_HARD: continue; + } + + explanation.len = 0; + for(i = 0; i < ssa.len; i++) { + if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM; + if (i < (ssa.len - 1)) + if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM; + + alloc_free(ssa.sa[i].s); + } + if (!stralloc_0(&explanation)) return SPF_NOMEM; + } /* and unknown modifiers are ignored */ + } else if (!done) { + if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM; + if (!stralloc_0(&sa)) return SPF_NOMEM; + + switch(spf.s[begin]) { + case '-': begin++; prefix = SPF_FAIL; break; + case '~': begin++; prefix = SPF_SOFTFAIL; break; + case '+': begin++; prefix = SPF_OK; break; + case '?': begin++; prefix = SPF_NEUTRAL; break; + default: prefix = SPF_OK; + } + + if (*p == '/') { + *p++ = 0; + q = spfmech(spf.s + begin, 0, p, domain->s); + } else { + if (*p) *p++ = 0; + i = str_chr(p, '/'); + if (p[i] == '/') { + p[i++] = 0; + q = spfmech(spf.s + begin, p, p + i, domain->s); + } else if (i > 0) + q = spfmech(spf.s + begin, p, 0, domain->s); + else + q = spfmech(spf.s + begin, 0, 0, domain->s); + } + + if (q == SPF_OK) q = prefix; + + switch(q) { + case SPF_OK: hdr_pass(); break; + case SPF_NEUTRAL: hdr_neutral(); break; + case SPF_SYNTAX: hdr_syntax(); break; + case SPF_SOFTFAIL: hdr_softfail(); break; + case SPF_FAIL: hdr_fail(); break; + case SPF_EXT: hdr_ext(sa.s); break; + case SPF_NONE: continue; + } + + r = q; + done = 1; /* we're done, no more mechanisms */ + } + } + + /* we fell through, no local rule applied */ + if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM; + + alloc_free(spf.s); + alloc_free(sa.s); + return r; +} + +int spfcheck() +{ + stralloc domain = {0}; + char temp[IPFMT]; + int pos; + int r; + + pos = byte_rchr(addr.s, addr.len, '@') + 1; + if (pos < addr.len) { + if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM; + } else { + pos = str_rchr(helohost.s, '@'); + if (helohost.s[pos]) { + if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM; + } else + if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM; + } + if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM; + if (!stralloc_0(&explanation)) return SPF_NOMEM; + recursion = 0; + + if (!remoteip || !ip_scan(remoteip, &ip)) { + hdr_unknown_msg("No IP address in conversation"); + return SPF_UNKNOWN; + } + + if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM; + if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM; + expdomain.len = 0; + errormsg.len = 0; + sender_fqdn.len = 0; + received = (char *) 0; + + if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip)) + { hdr_pass(); r = SPF_OK; } + else + r = spflookup(&domain); + + if (r < 0) r = SPF_UNKNOWN; + + alloc_free(domain.s); + return r; +} + +int spfexplanation(sa) +stralloc *sa; +{ + return spfexpand(sa, explanation.s, expdomain.s); +} + +int spfinfo(sa) +stralloc *sa; +{ + stralloc tmp = {0}; + if (!stralloc_copys(&tmp, received)) return 0; + if (!stralloc_0(&tmp)) return 0; + if (!spfexpand(sa, tmp.s, expdomain.s)) return 0; + alloc_free(tmp.s); + return 1; +} diff -ruN qmail-1.03/spf.h netqmail-1.05-toaster-spf-saout/spf.h --- qmail-1.03/spf.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/spf.h Fri Feb 6 19:56:19 2004 @@ -0,0 +1,20 @@ +#ifndef SPF_H +#define SPF_H + +#define SPF_OK 0 +#define SPF_NONE 1 +#define SPF_UNKNOWN 2 +#define SPF_NEUTRAL 3 +#define SPF_SOFTFAIL 4 +#define SPF_FAIL 5 +#define SPF_ERROR 6 +#define SPF_NOMEM 7 + +#define SPF_DEFEXP "See http://spf.pobox.com/" \ + "why.html?sender=%{S}&ip=%{I}&receiver=%{xR}" + +extern int spfcheck(); +extern int spfexplanation(); +extern int spfinfo(); + +#endif diff -ruN qmail-1.03/spfquery.c netqmail-1.05-toaster-spf-saout/spfquery.c --- qmail-1.03/spfquery.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/spfquery.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,84 @@ +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "alloc.h" +#include "spf.h" +#include "exit.h" + +void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); } +void die_usage() { die(100,"fatal: invalid usage\nusage: spfquery [] []\n"); } +void die_nomem() { die(111,"fatal: out of memory\n"); } + +stralloc addr = {0}; +stralloc helohost = {0}; +char *remoteip; +char *local; + +stralloc spflocal = {0}; +stralloc spfguess = {0}; +stralloc spfexp = {0}; + +void main(argc,argv) +int argc; +char **argv; +{ + stralloc sa = {0}; + int r; + + if (argc < 4) die_usage(); + + remoteip = (char *)strdup(argv[1]); + local = "localhost"; + + if (!stralloc_copys(&helohost, argv[2])) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); + + if (!stralloc_copys(&addr, argv[3])) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + + if (argc > 4) { + if (!stralloc_copys(&spflocal, argv[4])) die_nomem(); + if (spflocal.len && !stralloc_0(&spflocal)) die_nomem(); + } + + if (argc > 5) { + if (!stralloc_copys(&spfguess, argv[5])) die_nomem(); + if (spfguess.len && !stralloc_0(&spfguess)) die_nomem(); + } + + if (argc > 6) { + if (!stralloc_copys(&spfexp, argv[6])) die_nomem(); + } else + if (!stralloc_copys(&spfexp, SPF_DEFEXP)) die_nomem(); + if (spfexp.len && !stralloc_0(&spfexp)) die_nomem(); + + dns_init(0); + r = spfcheck(); + if (r == SPF_NOMEM) die_nomem(); + + substdio_puts(subfdout,"result="); + switch(r) { + case SPF_OK: substdio_puts(subfdout,"pass"); break; + case SPF_NONE: substdio_puts(subfdout,"none"); break; + case SPF_UNKNOWN: substdio_puts(subfdout,"unknown"); break; + case SPF_NEUTRAL: substdio_puts(subfdout,"neutral"); break; + case SPF_SOFTFAIL: substdio_puts(subfdout,"softfail"); break; + case SPF_FAIL: substdio_puts(subfdout,"fail"); break; + case SPF_ERROR: substdio_puts(subfdout,"error"); break; + } + + if (r == SPF_FAIL) { + substdio_puts(subfdout,": "); + if (!spfexplanation(&sa)) die_nomem(); + substdio_put(subfdout,sa.s,sa.len); + } + + substdio_putsflush(subfdout,"\n"); + + substdio_puts(subfdout,"Received-SPF: "); + if (!spfinfo(&sa)) die_nomem(); + substdio_put(subfdout,sa.s,sa.len); + substdio_putsflush(subfdout,"\n"); + + _exit(0); +} diff -ruN qmail-1.03/str.h netqmail-1.05-toaster-spf-saout/str.h --- qmail-1.03/str.h Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/str.h Fri Feb 6 19:56:19 2004 @@ -2,6 +2,7 @@ #define STR_H extern unsigned int str_copy(); +extern unsigned int str_copyb(); extern int str_diff(); extern int str_diffn(); extern unsigned int str_len(); diff -ruN qmail-1.03/str_cpyb.c netqmail-1.05-toaster-spf-saout/str_cpyb.c --- qmail-1.03/str_cpyb.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/str_cpyb.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,18 @@ +#include "str.h" + +unsigned int str_copyb(s,t,max) +register char *s; +register char *t; +unsigned int max; +{ + register int len; + + len = 0; + while (max-- > 0) { + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + if (!(*s = *t)) return len; ++s; ++t; ++len; + } + return len; +} diff -ruN qmail-1.03/strpidt.c netqmail-1.05-toaster-spf-saout/strpidt.c --- qmail-1.03/strpidt.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/strpidt.c Fri Feb 6 13:19:53 2004 @@ -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: strpidt.c,v 1.3 2000/05/27 04:59:26 mrsam 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)); +} diff -ruN qmail-1.03/strsalloc.c netqmail-1.05-toaster-spf-saout/strsalloc.c --- qmail-1.03/strsalloc.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/strsalloc.c Fri Feb 6 19:56:19 2004 @@ -0,0 +1,7 @@ +#include "alloc.h" +#include "gen_allocdefs.h" +#include "stralloc.h" +#include "strsalloc.h" + +GEN_ALLOC_readyplus(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus) +GEN_ALLOC_append(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus,strsalloc_append) diff -ruN qmail-1.03/strsalloc.h netqmail-1.05-toaster-spf-saout/strsalloc.h --- qmail-1.03/strsalloc.h Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/strsalloc.h Fri Feb 6 19:56:19 2004 @@ -0,0 +1,12 @@ +#ifndef STRSALLOC_H +#define STRSALLOC_H + +#include "stralloc.h" + +#include "gen_alloc.h" + +GEN_ALLOC_typedef(strsalloc,stralloc,sa,len,a) +extern int strsalloc_readyplus(); +extern int strsalloc_append(); + +#endif diff -ruN qmail-1.03/strtimet.c netqmail-1.05-toaster-spf-saout/strtimet.c --- qmail-1.03/strtimet.c Thu Jan 1 01:00:00 1970 +++ netqmail-1.05-toaster-spf-saout/strtimet.c Fri Feb 6 13:19:53 2004 @@ -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: strtimet.c,v 1.3 2000/05/27 04:59:26 mrsam 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)); +} diff -ruN qmail-1.03/tcp-env.c netqmail-1.05-toaster-spf-saout/tcp-env.c --- qmail-1.03/tcp-env.c Mon Jun 15 12:53:16 1998 +++ netqmail-1.05-toaster-spf-saout/tcp-env.c Fri Feb 6 19:56:19 2004 @@ -10,6 +10,7 @@ #include "scan.h" #include "subgetopt.h" #include "ip.h" +#include "strsalloc.h" #include "dns.h" #include "byte.h" #include "remoteinfo.h" @@ -34,6 +35,7 @@ int argc; char *argv[]; { + strsalloc ssa = {0}; int dummy; char *proto; int opt; @@ -74,12 +76,13 @@ temp[ip_fmt(temp,&iplocal)] = 0; if (!env_put2("TCPLOCALIP",temp)) die(); - switch(dns_ptr(&localname,&iplocal)) + switch(dns_ptr(&ssa,&iplocal)) { case DNS_MEM: die(); case DNS_SOFT: if (!stralloc_copys(&localname,"softdnserror")) die(); case 0: + if (!stralloc_copy(&localname,&ssa.sa[0])) die(); if (!stralloc_0(&localname)) die(); case_lowers(localname.s); if (!env_put2("TCPLOCALHOST",localname.s)) die(); @@ -99,12 +102,13 @@ temp[ip_fmt(temp,&ipremote)] = 0; if (!env_put2("TCPREMOTEIP",temp)) die(); - switch(dns_ptr(&remotename,&ipremote)) + switch(dns_ptr(&ssa,&ipremote)) { case DNS_MEM: die(); case DNS_SOFT: if (!stralloc_copys(&remotename,"softdnserror")) die(); case 0: + if (!stralloc_copy(&remotename,&ssa.sa[0])) die(); if (!stralloc_0(&remotename)) die(); case_lowers(remotename.s); if (!env_put2("TCPREMOTEHOST",remotename.s)) die();