diff -ruN ../netqmail-1.06-original/CHANNELS netqmail-1.06/CHANNELS --- ../netqmail-1.06-original/CHANNELS 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHANNELS 2019-06-26 16:39:31.572826981 +0200 @@ -0,0 +1,100 @@ +CHANNELS by Reed Sandberg +Copyright (c) 2007-2008 The SMB Exchange, INC + +This patch is free software; you can redistribute it and/or modify +it under the Artistic License. + +This patch for (net)qmail comes with NO WARRANTY. + +RELEASE: November 15, 2008 + + +qmail manages two different queues +with different configurable concurrency settings (rates) based on a set +of domains - those delivered locally (control files: locals, +virtualdomains, concurrencylocal) and those delivered remotely (domains +not listed in above control files and concurrencyremote). Luckily, +qmail's author (DJB) spent some time abstracting the implementation of +these channels and this patch advances the abstraction to add an +arbitrary number of channels - each with a distinct set of domains and +throttling capabilities. + +BIG PICTURE +With ext_todo patch. Adapted from: +EXTTODO by Claudio Jeker and +Andre Oppermann +(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd. + + + +-------+ +-------+ +-------+ + | clean | | clean | | logger| + +--0-1--+ +--0-1--+ +---0---+ +-----------+ + trigger ^ | ^ | | +->0,1 lspawn | + | | v | v v / +-----------+ + +-------+ v +--2-3--+ +--5-6--------------0-------+ / + | | | | 0<--7 1,2<-+ + | queue |--+--| todo | | send | + | | | | 1-->8 3,4<-+ + +-------+ +-------+ +--11,12---...-------X,Y----+ \ + | | \ +-----------+ + v v +->0,1 rspwan | + +--0,1-+ +--0,1-+ +-----------+ + |rspawn| ... |rspawn| + +------+ +------+ + +Communication between qmail-send and qmail-todo + +todo -> send: + D[01]{n}\0 + Start delivery for a new message with id . + the character '0' or '1' indicates whether this message + will go through the corresponding channel (false/true) + by position where n is the number of channels. E.g. D1011\0: + means there are four channels, the first 2 are always + the local and default remote channels, and the rest are + an optional number of supplemental channels (defined + at compile-time by conf-channels). So this message + has a local recipient, and a recipient on the first and + second supplemental channels. + L\0 + Dump string to the logger without adding additional \n or similar. +send -> todo: + H Got a SIGHUP, reread ~/control/locals, ~/control/virtualdomains, + ~/control/concurrencyremote, ~/control/concurrencylocal, + ~/control/concurrencysupplX, ~/control/supplsX + X Quit ASAP. + +qmail-todo sends "\0" terminated messages whereas qmail-send just send one +character to qmail-todo. + + +CAVEATS +qmail-qread ignores all supplemental channels - contributions are welcome! + +Supplemental channels use qmail-rspawn for remote recipients only. + +Dynamic throttling and resource limits +File descriptor limits are imposed on a per-process basis (FD_SET), on a +per-account basis (ulimit -n, /etc/security/limits.conf on Linux, pam limits, etc.) +and then on a system-wide basis by the OS (/proc/sys/fs/file-max on Linux, etc). +concurrencyremote, concurrencysupplX, etc are each subject to the hard limit in +conf-spawn, which in turn is bounded by per-process limits. Note that this limit +applies separately to each queue, not to all queues in total. The sum of all +concurrency limits for each queue in total is bounded on a per-account basis +(ulimit -n). These limits can easily be approached if you are running many +supplemental channels. + +qmail double checks the concurrency limits on startup for each channel (using FD_SET) +and silently curbs them if needed because bad things happen if this limit is breached. +If you're sending qmail-send a HUP signal after editing concurrency limits (dynamic +throttling) be aware that qmail's builtin checks can be circumvented, here's what +qmail's author has to say on the subject (from chkspawn.c): +This means that the qmail daemons could crash if you set the run-time concurrency higher +than [the per-process limit]. + +Even if the per-process limits are in check, per-account and system-wide file descriptor +limits may still cause bad things to happen if you're not careful (you've been warned!). + +Enjoy! +Reed Sandberg + diff -ruN ../netqmail-1.06-original/CHKUSER.automatic_patching netqmail-1.06/CHKUSER.automatic_patching --- ../netqmail-1.06-original/CHKUSER.automatic_patching 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.automatic_patching 2019-02-27 20:57:13.376025224 +0100 @@ -0,0 +1,94 @@ +Chkuser 2.0.9 automatic patching + +When to use automatic patching +============================== + +The release.tar package contains some .patch files, ready for installation, +trying to semplify the most frequent situations. + +You may use one of these patches if you have these sources: + + - a clean qmail 1.03 or netqmail 1.05 + +You may also consider using one of these patches if you have additional compatible +patches installed. This means that these additional patches should not have changed +the same sources and lines which are going to be used by chkuser. + +If you have any doubt, backup your sources and try the automatic installation, +otherwise execute the manual installation (that's very easy). + +Backup +====== + +Save you qmail working sources before making any change. + +Basic installation +================== + +Download the newest release.tar package and untar it. It will create a directory +containing all release chkuser files and patches. + +Chose the most appropriate .patch file to be applied, according to your qmail +installation: .patch files names are self-describing. + +Position in the qmail/netqmail source directory: + + $ cd /usr/.../netqmail-1.05 + +Apply selected patch: + + $ patch < /path_to_chkuser_release_dir/netqmail-1.05_chkuser-2.x.x.patch + +No errors should be displayed. If you see any error, better you restore your +sources and go to manual editing. + +editing vpopmail home path + + If your production home path for vpopmail (or whatever you call him) user + is NOT /home/vpopmail, you must perform the following additional actions. + + Edit Makefile, changing the line referring to vpopmail's home path and + putting the right home path: + + VPOPMAIL_HOME=/home/vpopmail + + Edit conf-cc, changing the string referring to vpopmail's home path and + putting the right home path: + + cc -O2 -I/home/vpopmail/include + +chkuser settings +================ + +Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the +ones you don't want. Default settings should cover the most of situations. + +See the related settings pages for more informations. + +Make +==== +Now, make (or gmake on *BSD) as your usual. No errors (just warnings) should +come out. If you see any error, better you restore your sources +and go to manual editing. + +Checking +======== +Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run: + + $ ./qmail-smtpd + mail from + mail from + rcpt to: + rcpt to: + +You should see error and ok messages, depending on the addresses you typed. + +Install +======= +Copy the new executable in the /var/qmail/bin directory (or make install). + +Running +======= +This patched qmail-smtpd must be executed in a different way than the normal one. +See the running pages for detailed instructions. + diff -ruN ../netqmail-1.06-original/CHKUSER.changelog netqmail-1.06/CHKUSER.changelog --- ../netqmail-1.06-original/CHKUSER.changelog 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.changelog 2019-02-27 20:57:13.376025224 +0100 @@ -0,0 +1,183 @@ + +CHKUSER 2.0 change log + +2.0.9 - 21 march 2007 + New features + - New variable for accepting only authorized senders: + #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH" + if the variable exists, then messages are accepted only if RELAYCLIENT is set + If defined, it works always despite CHKUSER being ON/OFF + This feature can be used for a "submission port" (see RFC 2476) + - Improved checking of MySQL server availability (if MySQL is used as vpopmail user's DB) + - Introduction of a new variable for disabling chkuser on the fly: CHKUSER_DISABLE_VARIABLE + (can be used for single IP or defined as "RELAYCLIENT" for all authorized e-mail clients) + - Improved starting variables checking sequence + CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together + and in such a case a fatal error is displayed; (in previous versions + CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition). + CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or + CHKUSER_STARTING_VARIABLE is evaluated, so CHKUSER_ALWAYS_ON or + CHKUSER_STARTING_VARIABLE can set the general behaviour, while + CHKUSER_DISABLE_VARIABLE should be invoked to handle exceptions. + - New variable for accepting qmail doublebounces: CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE + Sender "#@[]" will be accepted. It is used by qmail for doublebounces, and should be enabled for + selected IP senders. + - define CHKUSER_ENABLE_VAUTH_OPEN has been substituted by CHKUSER_VAUTH_OPEN_CALL: this new + define must contain the name of the call used to open the auth DB + + Updated features + - checking for ezmlm mailing list is now done looking for file "editor" + within mailing-list directory + - defines for allowed character within sender and rcpt addresses increased + up to CHKUSER_ALLOW_SENDER_CHAR_10 and CHKUSER_ALLOW_RCPT_CHAR_10 + - updated SMTP error strings; more exact and detailed error codes + (thanks to Olivier Dony and Dane Thorsen) + - logging of valid rcpt. If CHKUSER_LOG_VALID_RCPT is defined then all valid + recipients are logged, even if domain does not want bouncing or chkuser + is disabled + + Bugs corrected + - negative checking of backend DB connection did not report + DB unavailability in some situations + (thanks to Matt Brookings of Inter7) + - in check_rcpt_address_format format checking was done using defines + reserved for senders + +V 2.0.8 - 7 december 2004 + Features + Freeze of new features of 2.0.7, except null senders behaviour. + CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST is no more available + CHKUSER_ENABLE_NULL_SENDER is no more available + NULL SENDERS are now always accepted. No option is available to disable + this behaviour. Previous chkuser versions broke RFC compatibility on + null senders, and complicated real life e-mailing. + Logging of null senders <> is now available. + + Bugs corrected + Sender controls were not executed if CHKUSER_STARTING_VARIABLE was defined + (thanks to Charles Sprickman) + Domains not in control/virtualdomains are now explicitely excluded from + following cascade checks; in previous versions following cascade + checks were done using fake domains paths. + vget_assign is now handled correctly (a domain in rcpthosts but not + in virtualdomains could have an incorrect path in previous versions + (this bug is also in all chkusr versions) + + Defaults changed + CHKUSER_RCPT_FORMAT is now undefined as default + CHKUSER_RCPT_MX is now undefined as default. + CHKUSER_SENDER_FORMAT is now undefined as default + CHKUSER_SENDER_MX is now undefined as default. + CHKUSER_ERROR_DELAY_INCREASE new default is 300 milliseconds + +V 2.0.7 - 25 october 2004 + Features + added vclose() of DB auth connection, overriding + qmail-smtpd _exit call + improved MX checking; now SOFT failure is handled as + temporary error. + added #define CHKUSER_RCPTMX_TMP_STRING + added #define CHKUSER_SENDERMX_TMP_STRING + added handling of mailman mailing lists + (and related #define CHKUSER_ENABLE_MAILMAN_LISTS) + changed order of checking for recipients: + 1 - valias + 2 - alias + 3 - alias extensions + 4 - users + 5 - users extensions + 6 - lists + added #define CHKUSER_ACCEPT_NULL_SENDER (default defined) + added #define CHKUSER_ENABLE_ALIAS_DEFAULT (default not defined) + enables checking of .qmail-alias-default + added #define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" + in order to allow a easy identification of remote IP + (substitutes RELAYCLIENT in chkuser logging) + added #define CHKUSER_ALLOW_RCPT_SRS + enable usage of "#" and "+" characters within rcpt address + added #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' + added #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' + added #define CHKUSER_ALLOW_RCPT_CHAR_3 '£' + added #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' + added #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' + #define CHKUSER_ENABLE_USERS_EXTENSIONS + substitutes #define CHKUSER_ENABLE_EXTENSIONS + #define CHKUSER_ENABLE_EZMLM_LISTS + substitutes #define CHKUSER_ENABLE_LISTS + #define CHKUSER_USERS_DASH + substitutes #define CHKUSER_EXTENSION_DASH + + Bugs corrected + sender address "name@" could cause a crash. Corrected + (Thanks to Dmitry Petukhov) + Corrected Makefile: now qmail-smtpd.c recompiles if chkuser.h + changes + Corrected a bug in #endif sequence related to + #define CHKUSER_RCPT_FORMAT (thanks to Alex Plainer) + Corrected a bug in chkuser_sender; now is not executed when + chkuser is disabled + Corrected check of format for domains: + "xn--" admitted as leading string + Deleted correction over usage of RELAYCLIENT variable + Previous correction could affect a special + feature of RELAYCLIENT (thanks to Alex Pleiner) + + Defaults changed + #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST (default undefined) + + +V 2.0.6 - 25 september 2004 + No bugs, just doc updates and an empty patch file corrected + + #define CHKUSER_ENABLE_VGET_REAL_DOMAIN was existing and working in code, + but not reported both in docs and inside chkuser_settings.h + (default is commented, but this #define is important) + patch for toaster-0.6-1 was empty. Now the correct one is provided + +V 2.0.5 - 23 september 2004 + This is the first public release. + + added #define CHKUSER_ALLOW_SENDER_CHAR_1 (default not defined) + added #define CHKUSER_ALLOW_SENDER_CHAR_2 (default not defined) + added #define CHKUSER_ALLOW_SENDER_CHAR_3 (default not defined) + added #define CHKUSER_ALLOW_SENDER_CHAR_4 (default not defined) + added #define CHKUSER_ALLOW_SENDER_CHAR_5 (default not defined) + added #define CHKUSER_MIN_DOMAIN_LEN (default defined 4) - + Previously it was hard coded as 5. Juergen Kendzorra + showed me some existing names long only 4 chars. + added #define CHKUSER_LOG_VALID_SENDER (default defined) + +V 2.0.4 - 15 september 2004 + + added #define CHKUSER_SENDER_NOCHECK_VARIABLE (default not defined) + added #define CHKUSER_DEBUG_STDERR (default not defined) + added #define CHKUSER_ALLOW_SENDER_SRS (default not defined) + cleaned some typos in code and documentation (thanks to Juergen + Kendzorra - http://www.kendzorra.de) + + +V 2.0.3 - 8 september 2004 + This is the first version released outside, for wider testing. + + Tested Makefile for netqmail 1.05 + Added Makefiles for applying over other patches + +V 2.0.0 - july 2004 + chkuser 2.0.0 starts here, and is a private internal release. + Version 2.0 is much more modular than previous one (named chkusr), + and has been designed with the goal of enabling more features and + semplifying installations and upgrades of the patch himself. + + chkusr changes his name, to reflect a deep change of the patch. + + Chkusr 1.0 received a lot of feedbacks and suggestions. + The most of these suggestions are now inside version 2.0. + + - Marcelo Coelho (marcelo at tpn.com.br), segnaled me some + unseen minor bugs of chkusr 1.0 (minor but very annoying to + my pride) and suggested some very interesting features + (some of them are now in chkuser 2.0). + - Iulian Margarintescu (http:://www.erata.net) suggested a + workable way of introducing quota check on recipients + (now in chkuser 2.0). + diff -ruN ../netqmail-1.06-original/CHKUSER.copyright netqmail-1.06/CHKUSER.copyright --- ../netqmail-1.06-original/CHKUSER.copyright 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.copyright 2019-02-27 20:57:13.376025224 +0100 @@ -0,0 +1,15 @@ + +chkuser for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + +Author: Antonio Nati tonix@interazioni.it + +All rights on this software and +the identifying words chkusr and chkuser kept by the author + +This software may be freely used, modified and distributed, +but this lines must be kept in every original or derived version. + +Original author "Antonio Nati" and the web URL +"http://www.interazioni.it/opensource" +must be indicated in every related work or web page + diff -ruN ../netqmail-1.06-original/CHKUSER.log_format netqmail-1.06/CHKUSER.log_format --- ../netqmail-1.06-original/CHKUSER.log_format 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.log_format 2019-02-27 20:57:13.376025224 +0100 @@ -0,0 +1,69 @@ + +chkuser 2.0.9 logging format + +When #defines for logging are enabled, chkuser patch emits log informations +on the same qmail-smtpd log destination + +This is the log format: + + CHKUSER "brief message": \ + from \ + remote \ + rcpt : "extended message" + +where + brief message + * accepted rcpt + * relaying rcpt + * rejected relaying + * rejected rcpt + * no auth resource + * mbx overquota + * rejected intrusion + * intrusion threshold + * accepted sender + * rejected sender + * must auth + + sender sender declared within "mail from" + + remoteinfo the value of "TCPREMOTEINFO" or the autenticated user + + relayclient the value of CHKUSER_IDENTIFY env variable (this name + is defined by #define CHKUSER_IDENTIFY_REMOTE_VARIABLE) + + helo helo declared from remote system + + hostname the value of "TCPREMOTEHOST" + + remotehostip the value of "TCPREMOTEIP" + + recipient recipient address + + extended message this field has more wide description for + some generic "brief message": + accepted rcpt found existing recipient + accepted rcpt accepted any recipient for any rcpt doman (from 2.0.9) + accepted rcpt accepted any recipient for this domain (from 2.0.9) + relaying rcpt client allowed to relay + rejected relaying client not allowed to relay + rejected rcpt not existing recipient + rejected rcpt max number of recipients + rejected rcpt max number of invalid recipients + rejected rcpt invalid rcpt address format + rejected rcpt invalid rcpt MX domain + rejected rcpt temporary DNS problem (from 2.0.9) + intrusion threshold max number of allowed rcpt + intrusion threshold max number of allowed invalid rcpt + rejected intrusion rcpt ignored, session over intrusion threshold + no auth resource no auth resource available + must auth sender not authenticated/authorized (from 2.0.9) + mbx overquota rcpt mailbox is overquota + accepted sender sender accepted (from 2.0.9) + accepted sender accepted any sender always (from 2.0.9) + accepted sender accepted null sender always (from 2.0.9) + accepted doublebounce accepted qmail doublebounce #@[] (from 2.0.9) + rejected sender invalid sender address format + rejected sender invalid sender MX domain + rejected sender temporary DNS problem (from 2.0.9) + diff -ruN ../netqmail-1.06-original/CHKUSER.manual_patching netqmail-1.06/CHKUSER.manual_patching --- ../netqmail-1.06-original/CHKUSER.manual_patching 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.manual_patching 2019-02-27 20:57:13.377025213 +0100 @@ -0,0 +1,182 @@ +Chkuser 2.0 manual editing + +Manual editing is a very simple operation. + +Watching the patch design, shown in the patch design page, you may see that +only some simple changes must be done to qmail-smtpd.c and Makefile. + +Backup +====== + +Save you qmail working sources before making any change. + +Basic installation +================== + +Download the newest release.tar package and untar it. It will create a directory +containing all chkuser files and patches. + +Position in the qmail/netqmail source directory: + + $ cd /usr/.../netqmail-1.05 + +Copy all the chkuser sources: + + $ cp /path_to_release_tar/chkuser* . + +edit qmail-smtpd.c + within qmail-smtpd.c, change the following lines: + + At the end of initial #include declarations, add the following (+) lines: + + #include "timeoutwrite.h" + #include "commands.h" + ++ /* start chkuser code */ ++ #include "chkuser.h" ++ /* end chkuser code */ + + #define MAXHOPS 100 + +Within smtp_mail routine, add the following (+) lines + + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ /* start chkuser code */ ++ if (chkuser_sender (&addr) != CHKUSER_OK) { return; } ++ /* end chkuser code */ + flagbarf = bmfcheck(); + + Within smtp_rcpt routine, delete the following (-) lines and substitute + them with the (+) ones: + +- if (relayclient) { +- --addr.len; +- if (!stralloc_cats(&addr,relayclient)) die_nomem(); +- } +- else +- if (!addrallowed()) { err_nogateway(); return; } + ++ /* start chkuser code */ ++ switch (chkuser_realrcpt (&mailfrom, &addr)) { ++ case CHKUSER_KO: ++ return; ++ break; ++ case CHKUSER_RELAYING: ++ --addr.len; ++ if (!stralloc_cats(&addr,relayclient)) die_nomem(); ++ if (!stralloc_0(&addr)) die_nomem(); ++ break; ++ } ++ /* end chkuser code */ + + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + +edit Makefile + Within Makefile, change or add the following lines. + + At the begininng of the file: + + # Don't edit Makefile! Use conf-* for configuration. + ++ VPOPMAIL_HOME=/home/vpopmail ++ SMTPD_CHKUSER_OBJ=chkuser.o dns.o ++ VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib` + + SHELL=/bin/sh + + Be carefule to use the right path, if your vpopmail production home + path is NOT "/home/vpopmail". + + dns.lib is added to qmail-smtpd building instructions, so, if you + have previously patched qmail-smtpd in order to include dns.lib, take + care to delete the duplication from the previous lines. + + Before "clean:" insert the chkuser.o definition: + + exit.h auto_spawn.h + ./compile chkspawn.c ++ chkuser.o: \ ++ compile chkuser.c chkuser.h chkuser_settings.h ++ ./compile chkuser.c + + clean: \ + + Beware: the "./compile chkuser.c" line has an heading TAB. + + Change the qmail-smtpd compiling and linking instructions, + deleting the (-) lines and adding the (+) ones. + + + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ + open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ +- fs.a auto_qmail.o socket.lib ++ fs.a auto_qmail.o socket.lib $(SMTPD_CHKUSER_OBJ) +- ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ ++ ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) 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 \ ++ $(VPOPMAIL_LIBS) \ ++ `cat socket.lib` + + Beware: all the lines starting from and following "./load" have an heading TAB. + +edit TARGETS + Append the following blue line at the end of TARGETS file: + + man + setup + check ++ chkuser.o + +edit conf-cc + Edit conf-cc, adding the include path of production vpopmail: + + cc -O2 -I/home/vpopmail/include + + Be carefule to use the right path, if your vpopmail production home path + is NOT "/home/vpopmail". + +chkuser settings +================ +Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the +ones you don't want. Default settings should cover the most of situations. + +See the related settings pages for more informations. + +Make +==== +Now, make (or gmake on *BSD) as your usual. No errors (just warnings) +should come out. If you see any error, check carefully edited lines. + +Checking +======== +Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run: + + $ ./qmail-smtpd + mail from + mail from + rcpt to: + rcpt to: + +You should see error and ok messages, depending on the addresses you typed. + +Install +======= +Copy the new executable in the /var/qmail/bin directory (or make install). + +Running +======= +This patched qmail-smtpd must be executed in a different way than the normal one. +See the running pages for detailed instructions. + diff -ruN ../netqmail-1.06-original/CHKUSER.readme netqmail-1.06/CHKUSER.readme --- ../netqmail-1.06-original/CHKUSER.readme 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.readme 2019-02-27 20:57:13.377025213 +0100 @@ -0,0 +1,54 @@ +chkuser 2.0 - README + +Description +=========== +The original qmail-smtpd accepts by default all messages, checking later for +the existence of the recipient. So, if the message is delivered to not existing +recipients a lot of additional system work and network traffic are generated, +with multiple expensive bouncing if the sender is a fake one. + +chkuser has been developed with the goal to improve the acceptance SMTP phase +of qmail-smtpd. qmail-smtpd patched with chkuser may check the existance of +e-mail recipients immediately in the SMTP acceptance phase of a message and +rejects istantly all messages not directed to existing users, avoiding +additional traffic, work and messages bounced more times. + +These goals are achieved enquirying the existing vpopmail archives (each +format is supported: cdb, MySQL, LDAP, etc.) by using standard vpopmail calls, +or using customized chkuser routines. + +Version 2.0 - From chkusr to chkuser +==================================== +Version 2.0 is a lot different from previous versions, so it deserves a more +evident change in the name. + +Version 2.0 has been designed with the goal to be modular, and to make more easy +both adding new features to chkuser code and semplifing code update. + +Patching over original qmail files is done over a few points, while the most of +chkuser code remains ouside, in dedicated chkuser's files. + +Same for settings, that are inside a dedicated chkuser_settings.h file. + +The intention is to semplify upgrading: for future chkuser releases, upgrading +will require only to update chkuser specific files, leaving all the rest +untouched, and changing chkuser_settings.h only if new features must be enabled. + +Logging and SPAM +================ +chkuser 2.0 has detailed logging of accepted and refused recipients and senders, +allowing a deep analysis of "who's sending to who". This can lead to more +sophisticated future enhancements of anti-SPAM features. + +Intrusion rejection +=================== +chkuser 2.0 can be tuned to reject sessions exceeding some recipients limits +(limits can be set for max recipients and max not existing recipients). + +URL Location +============ +For any new release, support, FAQ, mailing lists, or other information, see: + + http://www.interazioni.it/opensource + + diff -ruN ../netqmail-1.06-original/CHKUSER.running netqmail-1.06/CHKUSER.running --- ../netqmail-1.06-original/CHKUSER.running 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/CHKUSER.running 2019-02-27 20:57:13.377025213 +0100 @@ -0,0 +1,103 @@ + +CHKUSER 2.0.9 - Running instructions + +Chkuser may run using the most of security, following very strictly the sacurity +model used By Dan Berstein. To achieve this goal, chkuser may switch between +differents UID/GID, for differente purposes. + +However this is incompatible with TLS patches (like toaster-0.6-1), as these patches +want to run under a unique UID/GID. Luckily, qmail is enought robust to let us +run this way. + +To achieve both these goals, chkuser uses a #define (CHKUSER_ENABLE_UIDGID) +that indicates if UID/GID switching is wanted, and running instructions must +adapt to this way. + +Instead, when this define is not used, another way of running must be used. +(Just for precision, even if the CHKUSER_ENABLE_UIDGID define is used, chkuser +may be run without switching UID/GID). + +Running with UID/GID switch +=========================== + +If you want the most security when using chkuser, and you have enabled +CHKUSER_ENABLE_UIDGID within chkuser_settings.h (it's enabled by default), use +these instructions. + +Description. + qmail-smtpd-chkusr must be installed (by default in /var/qmail/bin) with + setuid (user qmaild) and setgid (group qnofiles), and executed by tcpserver + with -u vpopmail-user and -g vchkpw-group parameters. + + qmail-smtpd-chkusr starts running with the original qmail-smtpd uid and gid, + switching to needed uid and gid only for vpopmail checks on user existance, + turning back to the starting uid and gid. + +Instructions. + You have to set SUID (set-user-ID-on-execution) and SGID + (set-group-ID-on-execution) bits on qmail-smtpd-chkusr: + chown qmaild qmail-smtpd + chgrp nofiles qmail-smtpd + chmod 6555 qmail-smtpd + + and the result you see should be like (different size and date, of course): + -r-sr-sr-x 1 qmaild nofiles 57056 Feb 14 18:18 qmail-smtpd-chkusr + + Integrate qmail-smtpd in your start files: + + As example, a real start command for qmail-smtpd-chkusr may be + + #!/bin/sh -e + # + # Using splogger to send the log through syslog. + + exec env - PATH="/var/qmail/bin:/usr/local/bin" \ + tcpserver -t 5 -v -p -x \ + -u -g -l 0 smtp \ + qmail-smtpd-chkusr splogger smtpd & + + where + = vpopmail uid + = vchkpw gid + = your host.domain (!) + = your tcp.permission.to.relay cdb + + NOTE: if you are using more system users for your domains, the execution + uid (which I indicated as vpopmail) should be set to root. + + +Running with fixed UID/GID +========================== +You may use these instructions if you've not defined CHKUSER_ENABLE_UIDGID, or if +you want to run qmail-smtpd as unique user, despite of CHKUSER_ENABLE_UIDGID define. +qmail-smtpd is well safe and robust, and there is no risk running it directly as +vpopmail user, unless you use untrusted software layered down. + +Description. + qmail-smtpd must be installed normally (-r-xr-xr-x) and executed by tcpserver + with -u vpopmail-user and -g vchkpw-group parameters. + +Instructions. + Integrate qmail-smtpd-chkusr in your start files: + + As example, a real start command for qmail-smtpd-chkusr may be + + #!/bin/sh -e + # + # Using splogger to send the log through syslog. + + exec env - PATH="/var/qmail/bin:/usr/local/bin" \ + tcpserver -t 5 -v -p -x \ + -u -g -l 0 smtp \ + qmail-smtpd-chkusr splogger smtpd & + + where + = vpopmail uid + = vchkpw gid + = your host.domain (!) + = your tcp.permission.to.relay cdb + + NOTE: if you are using more system users for your domains, the execution user + (which I indicated as vpopmail) should be set to root. + + diff -ruN ../netqmail-1.06-original/FILES netqmail-1.06/FILES --- ../netqmail-1.06-original/FILES 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/FILES 2019-06-26 16:39:31.573826970 +0200 @@ -136,6 +136,8 @@ dnsip.c dnsmxip.c dnsptr.c +dnstxt.c +spfquery.c hostname.c ipmeprint.c tcp-env.c @@ -336,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 @@ -402,6 +407,8 @@ date822fmt.c dns.h dns.c +spf.h +spf.c trylsock.c tryrsolv.c ip.h @@ -432,3 +439,7 @@ tcp-environ.5 constmap.h constmap.c +qmail-todo.c +channels.g +conf-channels +CHANNELS diff -ruN ../netqmail-1.06-original/LICENSE.authentication netqmail-1.06/LICENSE.authentication --- ../netqmail-1.06-original/LICENSE.authentication 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/LICENSE.authentication 2019-02-27 20:57:13.377025213 +0100 @@ -0,0 +1,43 @@ +AUTHOR +====== + +Author: + Dr. Erwin Hoffmann - FEHCom Germany +Web-Site: + http://www.fehcom.de/qmail.html +E-Mail: + feh@fehcom.de + + +LICENSE +======= + +qmail AUTHENTICATION is free software. +This includes: + You can download and use qmail AUTHENTICATION (and parts of it) as you like. + You can modify the source code without notification to or permission by the author. +Please check: + http://www.cr.yp.to/softwarelaw.html + + +DEPENDENCIES +============ + +qmail AUTHENTICATION patches (modifies) parts of the qmail-1.03 source files. +It should only be applied against the source as supplied by D.J. Bernstein. + + +FITNESS +======= + +The Author does not guarantee a specific fitness of qmail AUTHENTICATION. +If you use qmail AUTHENTICATION, it's on your own risk. + + +DISTRIBUTION +============ + +qmail AUTHENTICATION may be included in ports and packages under the following conditions: + The port/package has to show the current version number of qmail AUTHENTICATION. + All files (namely this) have to be included. + diff -ruN ../netqmail-1.06-original/MakeArgs.c netqmail-1.06/MakeArgs.c --- ../netqmail-1.06-original/MakeArgs.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/MakeArgs.c 2019-02-27 20:57:13.377025213 +0100 @@ -0,0 +1,144 @@ +/* + * $Log: MakeArgs.c,v $ + * Revision 2.8 2007-12-21 14:35:42+05:30 Cprogrammer + * included env.h to prevent compiler warning + * + * Revision 2.7 2007-12-20 12:45:28+05:30 Cprogrammer + * expand environment variables with '$' sign + * + * Revision 2.6 2005-08-23 17:31:28+05:30 Cprogrammer + * removed sccsid variable + * + * Revision 2.5 2005-04-02 19:06:02+05:30 Cprogrammer + * djb version + * + * Revision 2.4 2005-03-30 22:52:47+05:30 Cprogrammer + * BUG - Incorrect free + * + * Revision 2.3 2004-07-12 22:47:58+05:30 Cprogrammer + * bug fix. Free all allocated members + * + * Revision 2.2 2002-12-21 18:21:09+05:30 Cprogrammer + * added functionality of escaping text via quotes + * + * Revision 2.1 2002-08-13 20:35:44+05:30 Cprogrammer + * addition spaces were not getting skipped + * + * Revision 1.2 2002-03-03 17:23:05+05:30 Cprogrammer + * replaced strcpy with scopy + * + * Revision 1.1 2001-12-13 01:46:09+05:30 Cprogrammer + * Initial revision + * + */ +#include "alloc.h" +#include "str.h" +#include "stralloc.h" +#include "env.h" +#include + +#define isEscape(ch) ((ch) == '"' || (ch) == '\'') + +/* + * function to expand a string into command line + * arguments. To free memory allocated by this + * function the following should be done + * + * free(argv); + * + */ +char ** +MakeArgs(char *cmmd) +{ + char *ptr, *marker; + char **argv; + int argc, idx; + static stralloc sptr = { 0 }; + + for (ptr = cmmd;*ptr && isspace((int) *ptr);ptr++); + idx = str_len(ptr); + if (!stralloc_copys(&sptr, ptr)) + return((char **) 0); + if (!stralloc_0(&sptr)) + return((char **) 0); + /*- + * Get the number of arguments by counting + * white spaces. Allow escape via the double + * quotes character at the first word + */ + for (argc = 0, ptr = sptr.s;*ptr;) + { + for (;*ptr && isspace((int) *ptr);ptr++); + if (!*ptr) + break; + argc++; + marker = ptr; + /*- Move till you hit the next white space */ + for (;*ptr && !isspace((int) *ptr);ptr++) + { + /*- + * 1. If escape char is encounted skip till you + * hit the terminating escape char + * 2. If terminating escape char is missing, come + * back to the start escape char + */ + if (ptr == marker && isEscape(*ptr)) + { + for (ptr++;*ptr && !isEscape(*ptr);ptr++); + if (!*ptr) + ptr = marker; + } + } /*- for(;*ptr && !isspace((int) *ptr);ptr++) */ + } /*- for (argc = 0, ptr = sptr.s;*ptr;) */ + /* + * Allocate memory to store the arguments + * Do not bother extra bytes occupied by + * white space characters. + */ + if (!(argv = (char **) alloc((argc + 1) * sizeof(char *)))) + return ((char **) 0); + for (idx = 0, ptr = sptr.s;*ptr;) + { + for (;*ptr && isspace((int) *ptr);ptr++) + *ptr = 0; + if (!*ptr) + break; + if (*ptr == '$') + argv[idx++] = env_get(ptr + 1); + else + argv[idx++] = ptr; + marker = ptr; + for (;*ptr && !isspace((int) *ptr);ptr++) + { + if (ptr == marker && isEscape(*ptr)) + { + for (ptr++;*ptr && !isEscape(*ptr);ptr++); + if (!*ptr) + ptr = marker; + else /*- Remove the quotes */ + { + argv[idx - 1] += 1; + *ptr = 0; + } + } + } + } /*- for (idx = 0, ptr = sptr.s;*ptr;) */ + argv[idx++] = (char *) 0; + return (argv); +} + +void +FreeMakeArgs(char **argv) +{ + alloc_free(argv); + return; +} + +void +getversion_MakeArgs__c() +{ + static char *x = "$Id: MakeArgs.c,v 2.8 2007-12-21 14:35:42+05:30 Cprogrammer Stab mbhangui $"; + x++; + x--; + return; +} diff -ruN ../netqmail-1.06-original/Makefile netqmail-1.06/Makefile --- ../netqmail-1.06-original/Makefile 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/Makefile 2019-06-26 16:48:00.745225709 +0200 @@ -1,5 +1,14 @@ # Don't edit Makefile! Use conf-* for configuration. +VPOPMAIL_HOME=/home/vpopmail +VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib` + +SMTPD_CHKUSER_OBJ=chkuser.o dns.o + +LIBDOMAINKEYS=/usr/lib + +DEFINES=-DEXTERNAL_TODO # use to enable external todo + SHELL=/bin/sh default: it @@ -136,6 +145,18 @@ compile auto_usera.c ./compile auto_usera.c +base64.o: \ +compile base64.c base64.h stralloc.h substdio.h str.h + ./compile base64.c + +md5c.o : \ +compile md5c.c md5.h + ./compile md5c.c + +hmac_md5.o : \ +compile hmac_md5.c hmac_md5.h global.h + ./compile hmac_md5.c + binm1: \ binm1.sh conf-qmail cat binm1.sh \ @@ -203,6 +224,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 +236,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 @@ -300,6 +329,10 @@ exit.h auto_spawn.h ./compile chkspawn.c +chkuser.o: \ +compile chkuser.c chkuser.h chkuser_settings.h + ./compile chkuser.c + clean: \ TARGETS rm -f `cat TARGETS` @@ -320,10 +353,13 @@ chmod 755 compile condredirect: \ -load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \ -substdio.a error.a str.a fs.a auto_qmail.o - ./load condredirect qmail.o strerr.a fd.a sig.a wait.a \ - seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o +load condredirect.o srs.o rcpthosts.o cdb.a control.o constmap.o case.a \ + getln.a stralloc.a alloc.a open.a qmail.o strerr.a fd.a sig.a \ + wait.a seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o + ./load condredirect srs.o rcpthosts.o cdb.a control.o constmap.o \ + case.a getln.a stralloc.a alloc.a open.a qmail.o strerr.a fd.a sig.a \ + wait.a seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 condredirect.0: \ condredirect.1 @@ -331,7 +367,7 @@ condredirect.o: \ compile condredirect.c sig.h readwrite.h exit.h env.h error.h fork.h \ -wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h +wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h stralloc.h srs.h ./compile condredirect.c config: \ @@ -393,84 +429,96 @@ 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 gen_alloc.h fmt.h alloc.h \ +str.h stralloc.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` dnscname.o: \ -compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \ +compile dnscname.c substdio.h subfd.h stralloc.h \ gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h ./compile dnscname.c dnsdoe.o: \ -compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h +compile dnsdoe.c substdio.h subfd.h exit.h dns.h dnsdoe.h ./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 +compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \ +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.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 +compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \ +dns.h dnsdoe.h ip.h ipalloc.h strsalloc.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 \ +compile dnsmxip.c substdio.h subfd.h stralloc.h \ +gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.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` dnsptr.o: \ -compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \ +compile dnsptr.c substdio.h subfd.h stralloc.h gen_alloc.h \ 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 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 @@ -593,10 +641,13 @@ rm -f tryvfork.o tryvfork forward: \ -load forward.o qmail.o strerr.a alloc.a fd.a wait.a sig.a env.a \ -substdio.a error.a str.a fs.a auto_qmail.o - ./load forward qmail.o strerr.a alloc.a fd.a wait.a sig.a \ - env.a substdio.a error.a str.a fs.a auto_qmail.o +load forward.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \ + cdb.a case.a open.a stralloc.a alloc.a getln.a \ + fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o + ./load forward srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \ + cdb.a case.a open.a stralloc.a alloc.a getln.a \ + fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 forward.0: \ forward.1 @@ -604,7 +655,7 @@ forward.o: \ compile forward.c sig.h readwrite.h exit.h env.h qmail.h substdio.h \ -strerr.h substdio.h fmt.h +strerr.h substdio.h fmt.h stralloc.h srs.h ./compile forward.c fs.a: \ @@ -702,8 +753,15 @@ ./compile hfield.c hier.o: \ -compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h - ./compile hier.c +compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h channels.h + ./compile $(DEFINES) hier.c + +channels.h: \ +conf-channels channels.g + cat channels.g \ + | sed s}NUMCHANNELS}"`head -1 conf-channels`"}g \ + > channels.h + chmod 644 channels.h home: \ home.sh conf-qmail @@ -754,8 +812,8 @@ install-big.o: \ compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \ -fifo.h - ./compile install-big.c +fifo.h channels.h + ./compile $(DEFINES) install-big.c install.o: \ compile install.c substdio.h strerr.h error.h open.h readwrite.h \ @@ -777,38 +835,52 @@ ./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 readwrite.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 auto_qmail.o open.a getln.a \ +strsalloc.o stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib + ./load ipmeprint ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a \ + 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 auto_qmail.h ./compile ipmeprint.c +ipmetest: \ +load ipmetest.o ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a stralloc.a alloc.a substdio.a \ +error.a str.a fs.a env.a socket.lib + ./load ipmetest ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a stralloc.a alloc.a \ + substdio.a error.a env.a str.a fs.a `cat socket.lib` + +ipmetest.o: \ +compile ipmetest.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \ +ipalloc.h ip.h gen_alloc.h exit.h auto_qmail.h + ./compile ipmetest.c + it: \ qmail-local qmail-lspawn qmail-getpw qmail-remote qmail-rspawn \ +qmail-newmvrt \ qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \ predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \ 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 ipmetest qreceipt qreceipt qsmhook qbiff \ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ +dktest qmail-dk qmail-dkim dkim spawn-filter dk-filter surblfilter \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ -binm3 binm3+df +binm3 binm3+df srsfilter surblqueue dknewkey qmail-todo spfquery update_tmprsadh load: \ make-load warn-auto.sh systype @@ -890,6 +962,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 \ @@ -934,8 +1038,9 @@ preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \ maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \ qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \ -qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \ -envelopes.0 forgeries.0 +qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 dkim.8 dktest.8 \ +envelopes.0 forgeries.0 qmail-dk.0 qmail-dkim.0 dk-filter.0 spawn-filter.0 \ +surblfilter.0 mbox.0: \ mbox.5 @@ -1107,11 +1212,80 @@ | sed s}SPAWN}"`head -1 conf-spawn`"}g \ > qmail-control.5 +qmail-dk: \ +load qmail-dk.o triggerpull.o fmtqfn.o now.o date822fmt.o mess822_ok.o \ +subgetopt.o MakeArgs.o datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \ +str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \ +$(LIBDOMAINKEYS)/libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib + ./load qmail-dk triggerpull.o fmtqfn.o now.o mess822_ok.o \ + date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \ + subgetopt.o MakeArgs.o substdio.a error.a fs.a auto_qmail.o \ + auto_split.o auto_uids.o \ + fd.a wait.a $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto env.a control.o open.a getln.a \ + stralloc.a alloc.a substdio.a str.a case.a `cat dns.lib` + +qmail-dk.0: \ +qmail-dk.8 + nroff -man qmail-dk.8 > qmail-dk.0 +qmail-dk.8: qmail-dk.9 + cat qmail-dk.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > qmail-dk.8 + +qmail-dk.o: \ +compile qmail-dk.c readwrite.h sig.h exit.h open.h seek.h fmt.h \ +qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \ +env.h wait.h fd.h fork.h str.h uint64.h \ +auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h + ./compile qmail-dk.c + +dktest: load dktest.o scan_ulong.o dktrace.o \ +dns.o strsalloc.o ip.o error.o ipalloc.o fmt_ulong.o \ +scan_xlong.o socket_v4mappedprefix.o socket_v6any.o \ +case_diffs.o case_diffb.o fmt_str.o stralloc.a alloc.a str.a \ +$(LIBDOMAINKEYS)/libdomainkeys.a dns.lib + ./load dktest scan_ulong.o dktrace.o \ + dns.o strsalloc.o ip.o error.o ipalloc.o fmt_ulong.o \ + scan_xlong.o socket_v4mappedprefix.o socket_v6any.o \ + case_diffs.o case_diffb.o fmt_str.o stralloc.a alloc.a str.a \ + $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto `cat dns.lib` + +dktest.o: compile dktest.c domainkeys.h conf-domainkeys + ./compile `grep -h -v "^#" conf-domainkeys` dktest.c + +dktest.8: dktest.9 + cat dktest.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > dktest.8 + +dktrace.o: compile dktrace.c dktrace.h str.h case.h conf-domainkeys + ./compile `grep -h -v "^#" conf-domainkeys` dktrace.c + +qmail-dkim: \ +load qmail-dkim.o triggerpull.o fmtqfn.o now.o date822fmt.o \ +subgetopt.o MakeArgs.o dkimdns.o datetime.a seek.a ndelay.a \ +open.a sig.a alloc.a substdio.a error.a \ +str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \ +$(LIBDOMAINKEYS)/libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib libdkim.a + g++ -o qmail-dkim qmail-dkim.o triggerpull.o dkimdns.o fmtqfn.o now.o \ + subgetopt.o MakeArgs.o date822fmt.o datetime.a seek.a ndelay.a \ + open.a sig.a substdio.a error.a fs.a auto_qmail.o \ + auto_split.o auto_uids.o fd.a wait.a \ + $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto env.a control.o open.a getln.a \ + stralloc.a alloc.a substdio.a str.a case.a libdkim.a `cat dns.lib` + +qmail-dkim.o: \ +compile qmail-dkim.c readwrite.h sig.h exit.h open.h seek.h fmt.h \ +qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \ +sgetopt.h env.h wait.h fd.h fork.h str.h dkim.h \ +auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h + ./compile -DHAVE_CONFIG_H qmail-dkim.c + qmail-getpw: \ load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \ auto_usera.o ./load qmail-getpw case.a substdio.a error.a str.a fs.a \ - auto_break.o auto_usera.o + auto_break.o auto_usera.o qmail-getpw.0: \ qmail-getpw.8 @@ -1125,6 +1299,28 @@ | sed s}SPAWN}"`head -1 conf-spawn`"}g \ > qmail-getpw.8 +qmail-dkim.0: qmail-dkim.8 + nroff -man qmail-dkim.8 > qmail-dkim.0 +qmail-dkim.8: qmail-dkim.9 + cat qmail-dkim.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > qmail-dkim.8 + +dkim.8: dkim.9 + cat dkim.9 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > dkim.8 + +dk-filter.0: dk-filter.8 + nroff -man dk-filter.8 > dk-filter.0 +dk-filter.8: dk-filter.9 + cat dk-filter.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > dk-filter.8 + +dknewkey: dknewkey.sh warn-auto.sh + rm -f dknewkey + cat warn-auto.sh dknewkey.sh > dknewkey + qmail-getpw.o: \ compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \ error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \ @@ -1136,15 +1332,16 @@ nroff -man qmail-header.5 > qmail-header.0 qmail-inject: \ -load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \ +load qmail-inject.o rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o quote.o now.o \ control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \ getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \ substdio.a error.a str.a fs.a auto_qmail.o - ./load qmail-inject headerbody.o hfield.o newfield.o \ + ./load qmail-inject rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o \ quote.o now.o control.o date822fmt.o constmap.o qmail.o \ case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \ token822.o env.a stralloc.a alloc.a substdio.a error.a \ - str.a fs.a auto_qmail.o + str.a fs.a auto_qmail.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 qmail-inject.0: \ qmail-inject.8 @@ -1171,15 +1368,20 @@ > qmail-limits.7 qmail-local: \ -load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ +load qmail-local.o constmap.o control.o rcpthosts.o srs.o cdb.a qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ -fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib - ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ +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 constmap.o control.o rcpthosts.o srs.o cdb.a \ + 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 \ + -I/usr/local/include -L/usr/local/lib -lsrs2 qmail-local.0: \ qmail-local.8 @@ -1200,11 +1402,11 @@ qmail-lspawn: \ load qmail-lspawn.o spawn.o prot.o slurpclose.o coe.o sig.a wait.a \ case.a cdb.a fd.a open.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o auto_uids.o auto_spawn.o +fs.a auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o ./load qmail-lspawn spawn.o prot.o slurpclose.o coe.o \ sig.a wait.a case.a cdb.a fd.a open.a stralloc.a alloc.a \ substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \ - auto_spawn.o + auto_spawn.o envread.o str_diffn.o qmail-lspawn.0: \ qmail-lspawn.8 @@ -1213,9 +1415,22 @@ qmail-lspawn.o: \ compile qmail-lspawn.c fd.h wait.h prot.h substdio.h stralloc.h \ gen_alloc.h scan.h exit.h fork.h error.h cdb.h uint32.h case.h \ -slurpclose.h auto_qmail.h auto_uids.h qlx.h +slurpclose.h auto_qmail.h auto_uids.h qlx.h env.h ./compile qmail-lspawn.c +qmail-newmvrt: \ +load qmail-newmvrt.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \ +stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o + ./load qmail-newmvrt cdbmss.o getln.a open.a cdbmake.a \ + seek.a case.a stralloc.a alloc.a strerr.a substdio.a \ + error.a str.a auto_qmail.o + +qmail-newmvrt.o: \ +compile qmail-newmvrt.c strerr.h stralloc.h gen_alloc.h substdio.h \ +getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \ +uint32.h substdio.h + ./compile qmail-newmvrt.c + qmail-newmrh: \ load qmail-newmrh.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \ stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o @@ -1269,11 +1484,13 @@ qmail-pop3d: \ load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \ maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \ -stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib +stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \ +maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o ./load qmail-pop3d commands.o case.a timeoutread.o \ timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \ open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \ - fs.a `cat socket.lib` + fs.a `cat socket.lib` maildirquota.o maildirgetquota.o \ + maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o qmail-pop3d.0: \ qmail-pop3d.8 @@ -1419,13 +1636,13 @@ nroff -man qmail-qstat.8 > qmail-qstat.0 qmail-queue: \ -load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \ -datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \ -str.a fs.a auto_qmail.o auto_split.o auto_uids.o +load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o wildmat.o qregex.o \ +datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \ +env.a control.o constmap.o str.a fs.a auto_qmail.o auto_split.o auto_uids.o ./load qmail-queue triggerpull.o fmtqfn.o now.o \ - date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ - auto_split.o auto_uids.o + date822fmt.o wildmat.o qregex.o env.a control.o constmap.o datetime.a case.a seek.a ndelay.a open.a sig.a \ + getln.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o auto_uids.o + qmail-queue.0: \ qmail-queue.8 @@ -1439,14 +1656,18 @@ 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 +substdio.a error.a str.a fs.a auto_qmail.o \ +base64.o md5c.o hmac_md5.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 \ + tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ + 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` + base64.o md5c.o hmac_md5.o \ + str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` qmail-remote.0: \ qmail-remote.8 @@ -1455,7 +1676,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 @@ -1463,11 +1684,11 @@ qmail-rspawn: \ load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \ seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \ -auto_qmail.o auto_uids.o auto_spawn.o +auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o ./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \ sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \ substdio.a error.a str.a auto_qmail.o auto_uids.o \ - auto_spawn.o + auto_spawn.o envread.o str_diffn.o qmail-rspawn.0: \ qmail-rspawn.8 @@ -1475,31 +1696,33 @@ qmail-rspawn.o: \ compile qmail-rspawn.c fd.h wait.h substdio.h exit.h fork.h error.h \ -tcpto.h +tcpto.h env.h ./compile qmail-rspawn.c qmail-send: \ -load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \ +load qmail-send.o rcpthosts.o cdb.a srs.o qsutil.o control.o constmap.o newfield.o prioq.o \ trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ -auto_split.o env.a - ./load qmail-send qsutil.o control.o constmap.o newfield.o \ +auto_split.o env.a auto_spawn.o + ./load qmail-send rcpthosts.o cdb.a srs.o qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ - substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a + substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a auto_spawn.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 qmail-send.0: \ qmail-send.8 nroff -man qmail-send.8 > qmail-send.0 qmail-send.8: \ -qmail-send.9 conf-break conf-spawn +qmail-send.9 conf-break conf-spawn conf-channels cat qmail-send.9 \ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ | sed s}BREAK}"`head -1 conf-break`"}g \ | sed s}SPAWN}"`head -1 conf-spawn`"}g \ + | sed s}CHANNELS}"`head -1 conf-channels`"}g \ > qmail-send.8 qmail-send.o: \ @@ -1508,8 +1731,8 @@ substdio.h alloc.h error.h stralloc.h gen_alloc.h str.h byte.h fmt.h \ scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \ qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \ -fmtqfn.h readsubdir.h direntry.h - ./compile qmail-send.c +fmtqfn.h readsubdir.h direntry.h channels.h auto_qmail.h + ./compile $(DEFINES) qmail-send.c qmail-showctl: \ load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \ @@ -1528,21 +1751,26 @@ 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 \ -timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ -date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ -open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o socket.lib - ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ - timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ - received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ - datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` +strerr.a wildmat.o qregex.o \ +timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \ +received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \ +datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a substdio.a \ +error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib lock.a policy.o \ +$(SMTPD_CHKUSER_OBJ) + ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \ + strerr.a wildmat.o qregex.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \ + tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ + constmap.o received.o date822fmt.o now.o qmail.o spf.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 strerr.a str.a fs.a auto_qmail.o base64.o policy.o \ + $(VPOPMAIL_LIBS) \ + `cat socket.lib` qmail-smtpd.0: \ qmail-smtpd.8 @@ -1551,9 +1779,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 \ -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 +error.h ipme.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ip.h qmail.h \ +substdio.h strerr.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 spf.h dns.h base64.h \ +cdb.h chkuser.h ./compile qmail-smtpd.c qmail-start: \ @@ -1573,8 +1802,8 @@ > qmail-start.8 qmail-start.o: \ -compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h - ./compile qmail-start.c +compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h channels.h + ./compile $(DEFINES) qmail-start.c qmail-tcpok: \ load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \ @@ -1606,6 +1835,20 @@ fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h ./compile qmail-tcpto.c +qmail-todo: \ +load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \ +readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \ +substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \ + readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \ + alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + +qmail-todo.o: \ +compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \ +exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \ +scan.h select.h str.h stralloc.h substdio.h trigger.h channels.h + ./compile $(DEFINES) qmail-todo.c + qmail-upq: \ warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split cat warn-auto.sh qmail-upq.sh \ @@ -1639,10 +1882,10 @@ qreceipt: \ load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \ getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \ -str.a auto_qmail.o +str.a auto_qmail.o scan_ulong.o ./load qreceipt headerbody.o hfield.o quote.o token822.o \ qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \ - substdio.a error.a str.a auto_qmail.o + substdio.a error.a str.a auto_qmail.o scan_ulong.o qreceipt.0: \ qreceipt.1 @@ -1779,7 +2022,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 ipmetest.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 +2056,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,10 +2068,11 @@ 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 +maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \ +update_tmprsadh shar -m `cat FILES` > shar chmod 400 shar @@ -1897,6 +2142,23 @@ ./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 case.h + ./compile spf.c + +spfquery: \ +load spfquery.o spf.o ip.o getln.o getln2.o open_read.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 getln.o getln2.o open_read.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` `cat socket.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 \ @@ -1911,13 +2173,33 @@ scan.h fmt.h ./compile splogger.c +srs.o: \ +compile srs.c srs.h auto_qmail.h stralloc.h + ./compile srs.c + + +srsfilter: \ +load srsfilter.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \ + cdb.a case.a open.a stralloc.a alloc.a getln.a fd.a wait.a sig.a \ + env.a substdio.a error.a str.a fs.a auto_qmail.o + ./load srsfilter srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \ + cdb.a case.a open.a stralloc.a alloc.a getln.a fd.a wait.a sig.a \ + env.a substdio.a error.a str.a fs.a auto_qmail.o \ + -I/usr/local/include -L/usr/local/lib -lsrs2 + +srsfilter.o: \ +compile srsfilter.c sig.h readwrite.h exit.h env.h qmail.h substdio.h strerr.h \ +substdio.h fmt.h stralloc.h srs.h + ./compile srsfilter.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 +2209,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 +2292,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 +2357,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: \ @@ -2108,6 +2399,19 @@ compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h ./compile timeoutwrite.c +qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a +qmail-remote: tls.o ssl_timeoutio.o +qmail-smtpd.o: tls.h ssl_timeoutio.h +qmail-remote.o: tls.h ssl_timeoutio.h + +tls.o: \ +compile tls.c exit.h error.h + ./compile tls.c + +ssl_timeoutio.o: \ +compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h + ./compile ssl_timeoutio.c + token822.o: \ compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \ gen_alloc.h gen_allocdefs.h @@ -2139,3 +2443,173 @@ wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c +MakeArgs.o: compile MakeArgs.c alloc.h str.h alloc.h stralloc.h + ./compile MakeArgs.c + +spawn-filter: \ +load spawn-filter.o auto_qmail.o \ +fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \ +case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o \ +error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a + ./load spawn-filter \ + fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \ + case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o auto_qmail.o \ + error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a + +spawn-filter.o: \ +compile spawn-filter.c fmt.h str.h strerr.h env.h substdio.h stralloc.h error.h \ +wait.h qregex.h + ./compile spawn-filter.c + +qregex.o: \ +compile qregex.c qregex.h case.h stralloc.h constmap.h substdio.h byte.h env.h + ./compile qregex.c + +wildmat.o: \ +compile wildmat.c + ./compile wildmat.c + +spawn-filter.0: \ +spawn-filter.8 + nroff -man spawn-filter.8 > spawn-filter.0 + +spawn-filter.8: \ +spawn-filter.9 + cat spawn-filter.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > spawn-filter.8 + +str_cspn.o: \ +compile str.h str_cspn.c + ./compile str_cspn.c + +mess822_ok.o: \ +compile mess822_ok.c uint64.h + +surblfilter.0: surblfilter.8 + nroff -man surblfilter.8 > surblfilter.0 + +surblfilter.8: surblfilter.9 + cat surblfilter.9 \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > surblfilter.8 + +surblfilter: \ +load surblfilter.o envread.o strerr_die.o strerr_sys.o \ +control.o alloc.o alloc_re.o error.o \ +error_str.o auto_qmail.o \ +case_startb.o byte_diff.o str_cspn.o \ +byte_copy.o byte_chr.o byte_rchr.o byte_cr.o \ +getln.o getln2.o open_read.o str_len.o str_diffn.o \ +str_cpy.o str_chr.o scan_xlong.o \ +now.o scan_ulong.o mess822_ok.o constmap.o \ +ip.o strsalloc.o dns.o ipalloc.o fmt_str.o fmt_ulong.o \ +socket_v6any.o socket_v4mappedprefix.o \ +sgetopt.o subgetopt.o base64sub.o \ +case_diffb.o stralloc.a substdio.a + ./load surblfilter envread.o strerr_die.o strerr_sys.o \ + control.o alloc.o alloc_re.o error.o \ + error_str.o auto_qmail.o \ + case_startb.o byte_diff.o str_cspn.o \ + byte_copy.o byte_chr.o byte_rchr.o byte_cr.o \ + getln.o getln2.o open_read.o str_len.o str_diffn.o \ + str_cpy.o str_chr.o scan_xlong.o \ + now.o scan_ulong.o mess822_ok.o constmap.o \ + ip.o strsalloc.o dns.o ipalloc.o fmt_str.o fmt_ulong.o \ + socket_v6any.o socket_v4mappedprefix.o \ + sgetopt.o subgetopt.o base64sub.o \ + case_diffb.o stralloc.a substdio.a -lresolv + +surblfilter.o: \ +compile surblfilter.c alloc.h error.h str.h case.h \ +constmap.h auto_qmail.h stralloc.h env.h control.h \ +strerr.h substdio.h getln.h byte.h dns.h ip.h \ +ipalloc.h mess822.h scan.h subgetopt.h uint64.h \ +base64.h + ./compile surblfilter.c + +surblqueue: \ +surblqueue.sh conf-qmail + cat surblqueue.sh \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > surblqueue + chmod 755 surblqueue + +hastai.h: \ +trytai.c compile load + (( ./compile trytai.c && ./load trytai -ltai) >/dev/null \ + 2>&1 \ + && echo \#define HASTAI 1 || exit 0 ) > hastai.h + rm -f trytai.o trytai + +uint64.h: \ +tryulong64.c compile load uint64.h1 uint64.h2 + ( ( ./compile tryulong64.c && ./load tryulong64 && \ + ./tryulong64 ) >/dev/null 2>&1 \ + && cat uint64.h1 || cat uint64.h2 ) > uint64.h + rm -f tryulong64.o tryulong64 + +base64sub.o: \ +compile base64sub.c base64.h stralloc.h substdio.h str.h + ./compile base64sub.c + +dk-filter: \ +warn-auto.sh dk-filter.sh conf-qmail + cat warn-auto.sh dk-filter.sh \ + | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \ + > dk-filter + +DKIMHDRS = dkim.h dkimdns.h dkimbase.h dkimsign.h dkimverify.h time_t_size.h +DKIMSRCS = dkimfuncs.cpp dkimbase.cpp +DKIMOBJS = $(DKIMSRCS:.cpp=.o) +dkim: libdkim.a dkim.o dkimdns.o + g++ -o dkim $(LFLAGS) -L. dkim.o dkimdns.o libdkim.a `cat dns.lib` -lcrypto + +time_t_size.h: time_t_size.c compile load + (./compile time_t_size.c && ./load time_t_size && \ + ./time_t_size) > time_t_size.h + rm -f time_t_size.o time_t_size + +dkimfuncs.o: dkimfuncs.cpp time_t_size.h + g++ -DHAVE_EVP_SHA256 -c dkimfuncs.cpp + +dkimverify.o: dkim.h dkimdns.h dkimverify.h dkimverify.cpp time_t_size.h + g++ -DHAVE_EVP_SHA256 -c dkimverify.cpp + +dkimsign.o: dkim.h dkimsign.h dkimsign.cpp + g++ -DHAVE_EVP_SHA256 -c dkimsign.cpp + +dkim.o: dkim.c $(DKIMHDRS) + g++ -DHAVE_EVP_SHA256 -I. -DHAVE_EVP_SHA256 -c dkim.c + +libdkim.a: $(DKIMOBJS) dkimverify.o dkimsign.o makelib time_t_size.h + rm -f libdkim.a + ./makelib libdkim.a $(DKIMOBJS) dkimsign.o dkimverify.o +.cpp.o: + g++ -I. -DHAVE_EVP_SHA256 $(CFLAGS) $(INCL) -c $< + +cert cert-req: \ +Makefile-cert + @$(MAKE) -sf $< $@ + +Makefile-cert: \ +conf-qmail conf-users conf-groups Makefile-cert.mk + @cat Makefile-cert.mk \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > $@ + +update_tmprsadh: \ +conf-qmail conf-users conf-groups update_tmprsadh.sh + @cat update_tmprsadh.sh\ + | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > $@ + chmod 755 update_tmprsadh + +tmprsadh: \ +update_tmprsadh + echo "Creating new temporary RSA and DH parameters" + ./update_tmprsadh + +policy.o: policy.c policy.h conf-policy + ./compile policy.c `head -1 conf-policy` diff -ruN ../netqmail-1.06-original/Makefile-cert.mk netqmail-1.06/Makefile-cert.mk --- ../netqmail-1.06-original/Makefile-cert.mk 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/Makefile-cert.mk 2019-02-27 20:57:13.379025191 +0100 @@ -0,0 +1,21 @@ +cert-req: req.pem +cert cert-req: QMAIL/control/clientcert.pem + @: + +QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem + ln -s $< $@ + +QMAIL/control/servercert.pem: + PATH=$$PATH:/usr/local/ssl/bin \ + openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@ + chmod 640 $@ + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@ + +req.pem: + PATH=$$PATH:/usr/local/ssl/bin openssl req \ + -new -nodes -out $@ -keyout QMAIL/control/servercert.pem + chmod 640 QMAIL/control/servercert.pem + chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem + @echo + @echo "Send req.pem to your CA to obtain signed_req.pem, and do:" + @echo "cat signed_req.pem >> QMAIL/control/servercert.pem" diff -ruN ../netqmail-1.06-original/README.PATCH netqmail-1.06/README.PATCH --- ../netqmail-1.06-original/README.PATCH 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.PATCH 2020-07-29 21:22:44.084295087 +0200 @@ -0,0 +1,547 @@ +v. 2020.07.29 +Combined patch for netqmail-1.06 by Roberto Puzzanghera [roberto dot puzzanghera at sagredo dot eu] +More info at https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html +========================================================================================================== + += This patch puts together +* Erwin Hoffmann's qmail-authentication patch v. 0.8.3, which updates the patches provided by + Krysztof Dabrowski and Bjoern Kalkbrenner. + It provides cram-md5, login, plain authentication support for qmail-smtpd and qmail-remote. + http://www.fehcom.de/qmail/smtpauth.html##PATCHES +* Frederik Vermeulen's qmail-tls patch v. 20200107 + implements SSL or TLS encrypted and authenticated SMTP. + http://inoa.net/qmail-tls/ + The file update_tmprsadh was modified to chown all .pem files to vpopmail. +* Marcel Telka's force-tls patch v. 2016.05.15 + optionally gets qmail to require TLS before authentication to improve security. + You have to declare FORCETLS=0 if you want to allow the auth without TLS + https://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06_force-tls.patch-2012.10.28 +* Antonio Nati's chkuser patch v. 2.0.9 + performs, among the other things, a check for the existence of recipients during the SMTP conversation, + bouncing emails of fake senders. + http://www.interazioni.it/opensource/chkuser/ +* Flavio Curti's qmail-queue-custom-error patch + enables simscan and qmail-dkim to return the appropriate message for each e-mail it refuses to deliver. + https://no-way.org/uploads/qmail-error/ +* Christophe Saout's qmail-SPF rc5 patch + Modified by Manvendra Bhangui to make it IPv4-mapped IPv6 addresses compliant. + checks incoming mails inside the SMTP daemon, add Received-SPF lines and optionally block undesired transfers. + http://www.saout.de/misc/spf/ +* Marcelo Coelho's qmail-SRS patch + implements Sender Rewriting Scheme fixing SPF break upon email forwarding. + http://www.mco2.com.br/opensource/qmail/srs/ +* Christopher K. Davis' oversize dns patch + enables qmail to handle large DNS packets. + http://www.ckdhr.com/ckd/qmail-103.patch +* Jul's reread-concurrency v.2 patch + rereads control/concurrencylocal and control/concurrencyremote files when qmail-send receives a HUP signal. + http://js.hu/package/qmail/index.html +* Johannes Erdfelt's Big Concurrency patch + sets the spawn limit above 255 + http://qmail.org/big-concurrency.patch +* Mihai Secasiu's Big Concurrency fix v.1.0 patch + fixes a compiler error if you set concurrency higher than 509 in conf-spawn. + http://patchlog.com/linux/qmail-big-concurrency/ +* Bill Shupp's netqmail-maildir++.patch + adds maildirquota support to qmail-pop3d and qmail-local. + Fixed a bug where the filesize part of the S= component of the Maildir++ compatible filename + is wrong (tx MG). More info here: + https://notes.sagredo.eu/en/qmail-notes-185/installing-dovecot-and-sieve-on-a-vpopmail-qmail-server-28.html#comment995 + https://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/netqmail-maildir.patch +* Kyle B. Wheeler's "Better qmail-smtpd Logging" v.5 patch + facilitates diagnostics of qmail-smtpd logging its actions and decisions (search for a line with qmail-smtp:) + http://www.memoryhole.net/qmail/#logging +* John Simpson's (?) Greeting delay patch + adds a user-definable delay after SMTP clients have initiated SMTP sessions, prior to qmail-smtpd responding + with "220 ESMTP". It can reject connections from clients which tried to send commands before greeting. + https://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-greetdelay.patch +* Manvendra Bhangui's DKIM and SURBL filter v.1.28 patch + adds DKIM signing & verification and SURBL filtering support to qmail. + qmail-dk is based on Russ Nelson's patch: http//:www.qmail.org/qmail-1.03-dk-0.54.patch + qmail-dkim uses hacked libdkim libraries from libdkim project at http://libdkim.sourceforge.net/ + surbfilter is built on djb functions and some functions have been ruthlessly borrowed from qmail surbl + interface by Pieter Droogendijk and the surblhost program at http://surblhost.sourceforge.net/ + (file hier.c modified to chown /var/qmail/control/cache and subdirs to vpopmail) + http://sourceforge.net/projects/indimail/files/netqmail-addons/qmail-dkim-1.0/ + http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/ANNOUNCE.surblfilter +* Claudio Jeker and Andre Oppermann's EXTTODO patch (release 5. Jan. 2003) + addresses a problem known as the silly qmail (queue) problem + http://www.nrg4u.com/qmail/ext_todo-20030105.patch +* Russell Nelson's big-todo patch + makes qmail use a hashing mechanism in the todo folder similar to that used in the rest of the queue + http://www.qmail.org/big-todo.103.patch +* Stephane Cottin's qmail-inject-null-sender patch (let's call it in this way) + prevents qmail-inject from rewriting the null sender, fixing an issue with sieve vacation/reject messages. + More info here: http://www.dovecot.org/list/dovecot/2009-June/040811.html + https://notes.sagredo.eu/files/qmail/patches/qmail-inject-null-sender.patch +* Russell Nelson's (modified by Charles Cazabon) doublebounce-trim patch, which updates the original + version by Russel Nelson + prevents double bounces from hitting your queue a second time provided that you delete the first line + from /var/qmail/control/doublebounceto + http://qmail.org/doublebounce-trim.patch +* Will Harris' esmtp-size patch + enables qmail-smtpd to reject messages if they're larger than the maximum number of bytes allowed + according to the /var/qmail/control/databytes control file. + http://will.harris.ch/qmail-smtpd.c.diff +* Inter7's qmail-taps-extended patch + http://notes.sagredo.eu/files/qmail/patches/qmail-tap.diff + Extended by Michai Secasiu (http://patchlog.com/patches/qmail-taps-extended/) + Provides the ability to archive each email that flows through the system. + Archiving only messages from or to certain email addresses is possible as well. +* Rolf Eike Beer's qmail-remote CRLF patch + enables qmail-remote to handle CR properly, always sending the line breaks as CRLF and avoiding to + double the CR (like qmail-remote normally does) + http://opensource.sf-tec.de/qmail/ +* Andy Repton's outgoingip patch (adjusted by Sergio Gelato) + by default all outgoing emails are sent through the first IP address on the interface. In case of a multiple + IP server this patch makes qmail send outgoing emails with the IP eventually stored in control/outgoingip. + The ehlo domain is NOT modified by this patch. + http://www.qmail.org/outgoingip.patch + Robbie Walker provided a patch to correct qmail-qmqpc.c's call to timeoutconn(), because the function + signature was modified by the original outgoingip patch + https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment373 +* Iain Patterson's qmail-smtpd pid, qp log patch + makes qmail-smtpd log a line similar to the following: + @4000000039b89c95026a89b4 mail recv: pid 8155 from qp 8157 + The pid allows you to match the message up with a given tcpserver process and the qp lets you find a + particular delivery. + http://iain.cx/qmail/patches.html#smtpd_pidqp +* Jonathan de Boyne Pollard's any-to-cname patch + avoids qmail getting large amounts of DNS data we have no interest in and that may overflow our response + buffer. + http://www.memoryhole.net/qmail/#any-to-cname +* Matthias Andree's qmail-rfc2821 patch + makes qmail rfc2821 compliant + http://www-dt.e-technik.uni-dortmund.de/~ma/qmail/patch-qmail-1.03-rfc2821.diff +* Jonathan de Boyne Pollard's smtpd-502-to-500 patch + makes qmail rfc2821 compliant + https://notes.sagredo.eu/files/qmail/patches/smtpd-502-to-500.patch +* Fabio Busatto's qmail-dnsbl patch + allows you to reject spam and virus looking at the sender's ip address. + Modified by Luca Franceschini to add support for whitelists, TXT and A queries, configurable return codes + 451 or 553 with custom messages + http://qmail-dnsbl.sourceforge.net/ +* Scott Gifford's qmail-moreipme patch v. 0.6 + prevents a problem caused by an MX or other mail routing directive instructing qmail to connect to + itself without realizing it's connecting to itself, saving CPU time. + http://www.suspectclass.com/sgifford/qmail/qmail-1.03-moreipme.README + http://www.suspectclass.com/sgifford/qmail/qmail-1.03-moreipme-0.6.patch +* Alex Nee's qmail-hide-ip-headers patch + It will hide your Private or Public IP in the email Headers when you are sending Mail as a Relay Client. + https://notes.sagredo.eu/files/qmail/patches/qmail-hide-ip-headers.patch +* John Saunders' qmail-date-localtime patch + causes the various qmail programs to generate date stamps in the local timezone. + https://notes.sagredo.eu/files/qmail/patches/qmail-date-localtime.patch +* Dean Gaudet's qmail-liberal-lf patch v. 0.95 + allow qmail-smtpd to accept messages that are terminated with a single \n instead of the required \r\n + sequence. + http://www.arctic.org/~dean/patches/qmail-0.95-liberal-lf.patch +* Michael Samuel's maxrcpt patch + allows you to set a limit on how many recipients are specified for any one email message by setting + control/maxrcpt. RFC 2821 section 4.5.3.1 says that an MTA MUST allow at least 100 recipients for each + message, since this is one of the favourite tricks of the spammer. + http://copilotco.com/mail-archives/qmail.1997/msg03066.html +* Inter7's qmail-eMPF patch + More info: http://www.qmailwiki.org/EMPF + eMPF follows a set of administrator-defined rules describing who can message whom. With this, + companies can segregate various parts of their organizations email activities, as well as provide a + variety of security-enhancing services. +* qregex (by Andrew St. Jean http://www.arda.homeunix.net/downloads-qmail/, contributors: Jeremy Kitchen, + Alex Pleiner, + Thanos Massias. Original patch by Evan Borgstrom) + adds the ability to match address evelopes via Regular Expressions (REs) in the qmail-smtpd process. + Added new control file 'badhelonorelay', control/badmailto renamed control/badrcptto (Tx Luca Franceschini). +* brtlimit + Luca Franceschini derived this patch from http://netdevice.com/qmail/patch/goodrcptto-12.patch + added control/brtlimit and BRTLIMIT variable to limit max invalid recipient errors before closing + the connection (man qmail-control) +* validrcptto + https://notes.sagredo.eu/files/qmail/patches/validrcptto.README + Luca Franceschini grabbed the code from several patches with additional features: + http://qmail.jms1.net/patches/validrcptto cdb.shtml, + http://netdevice.com/qmail/patch/goodrcptto-ms-12.patch, http://patch.be/qmail/badrcptto.html + It works in conjunction with chkuser with both cdb and mysql accounts. +* reject-relay-test by Russell Nelson + http://qmail.org/qmail-smtpd-relay-reject + It gets qmail to reject relay probes generated by so-called anti-spammers. These relay probes have + '!', '%' and '@' in the local (username) part of the address. +* Luca Franceschini + added DISABLETLS environment variable, useful if you want to disable TLS on a desired port + added FORCEAUTHMAILFROM environment variable to REQUIRE that authenticated user and 'mail from' are identical + added SMTPAUTHMETHOD, SMTPAUTHUSER and SMTP_AUTH_USER env variables for external plugins (see + http://qmail-spp.sourceforge.net/doc/) +* fixed little bug in 'mail from' address handling + patch by Andre Opperman at http://qmail.cr.yp.narkive.com/kBry6GJl/bug-in-qmail-smtpd-c-addrparse-function +* Luca Franceschini's qlog patch + smtpd logging with fixed format. An entry 'qlogenvelope' is generated after accepting or rejecting + every recipients in the envelope phase. +* Luca Franceschini's reject null senders patch + useful in special cases if you temporarily need to reject the null sender (although breaks RFC compatibility). + You just need to put 1 (actually any number different from 0) in your control/rejectnullsenders to reject + the null sender with 421 error message. +* dnscname patch + Removes CNAME check in order to avoid getting large amounts of data of no interest in and that may + overflow the response buffer. + https://lists.gt.net/qmail/users/138190 +* Luca Franceschini's rcptcheck patch + (based on original patch from Jay Soffian (http://www.soffian.org/downloads/qmail/qmail-smtpd-doc.html) + Originally designed for the purpose of receipt validation, it can also be used to limit the numbr of + email a given IP and/or auth-user and/or domain can send in a given time interval. It has to be used + in conjuction with the rcptcheck-overlimit.sh LF's script + https://notes.sagredo.eu/files/qmail/rcptcheck-overlimit.sh + https://notes.sagredo.eu/files/qmail/patches/rcptcheck.patch +* Reed Sandberg's qmail-channels patch + Allows you to add an arbitrary number of supplemental remote queues, each distinguished by a list of + recipient domains and separate throttling (concurrency) capabilities. This patch also allows dynamic + throttling of the concurrency control files so you can just send qmail-send a HUP signal instead of + restarting the service every time. + This patch is useful when some email providers complain of too many emails receveid at the same time + (in case of news letters for instance). Look here for more info + https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1328 + Edit conf-channels before compiling: Total number of channels (queues) available for delivery. Must be at + least 2, and anything above 2 are considered supplemental channels. + http://www.thesmbexchange.com/eng/qmail-channels_patch.html +* Endersys R&D team's qmail-remote-logging patch + gets qmail-remote to log sender, recipient and IP adddress all together in the "Delivery success/failure" line + https://web.archive.org/web/20120530051612/http://blog.endersys.com/2009/12/qmail-canonicalised-recipient-logging-and-more-patch/ +* notqmail.org's cve-2005-1513 patch + addresses a vulnerability issue spotted by Georgi Guninski in 2005 + https://www.qualys.com/2020/05/19/cve-2005-1513/remote-code-execution-qmail.txt + += Disclaimer +This patch comes with the usual warranty: it works for me, it may not work for you, +use at your own risk etc. etc. :) +Comments, suggestions, criticisms are always welcome! + += Usage + +* Install libdomainkeys +wget https://notes.sagredo.eu/files/qmail/tar/libdomainkeys-0.69.tar.gz +tar xzf libdomainkeys-0.69.tar.gz +wget https://notes.sagredo.eu/files/qmail/patches/libdomainkeys/libdomainkeys-openssl-1.1.patch +wget https://notes.sagredo.eu/files/qmail/patches/libdomainkeys-0.69.diff +cd libdomainkeys-0.69 +chown -R root.root . +patch -p1 < ../libdomainkeys-openssl-1.1.patch +patch < ../libdomainkeys-0.69.diff +make +cp libdomainkeys.a /usr/lib + +* Install libsrs2 +wget https://notes.sagredo.eu/files/qmail/tar/libsrs2-1.0.18.tar.gz +tar xzf libsrs2-1.0.18.tar.gz +cd libsrs2-1.0.18 +./configure +make +make install +ldconfig +cd ../ + +* Apply the patch and compile +wget https://notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06.patch-latest.gz +wget http://qmail.org/netqmail-1.06.tar.gz +tar xzf netqmail-1.06.tar.gz +cd netqmail-1.06 +chown -R root.root . +gunzip -c ../roberto-netqmail-1.06.patch-latest.gz | patch +make +make setup check + +* You have to export SMTPAUTH in your run file if you want to do the auth + +* You have to export SURBL=1 in your run file if you want to enable SURBL + +* /var/qmail/control/cache must be owned by the user who runs qmail-smtpd, vpopmail.vchkpwd in my case. + Change the permissions according to your qmail configuration. + +================================================================================================================= + += Changelog + +2020.07.29 +-dk-filter: corrected a bug where dk-filter was using DKIMDOMAIN unconditionally. Now it uses DKIMDOMAIN + only if _SENDER is null (tx Manvendra Bhangui). + +2020.07.27 +-added cve-2005-1513 patch + +2020.04.25 +-qmail-smtpd.c: added rcptcount = 0; in smtp_rset function to prevent the maxrcpto error if control/maxrcpt limit + has been exceeded in multiple messages sent sequentially rather than in a single mail (tx Alexandre Fonceca). + More info here: https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1594 + +2020.04.16 +-qmail-remote-logging patch added + +2020.04.10 +-DKIM patch updated to v. 1.28 + * outgoing messages from null sender ("<>") will be signed as well with the domain in env variable DKIMDOMAIN + * declaring NODK env variable disables old domainkeys signature, while defining NODKIM disables DKIM. + +2020.01.11 +-qmail-tls patch updated to v. 20200107 + * working client cert authentication with TLSv1.3 (Rolf Eike Beer) + +2019.12.08 +-BUG qmail-smtpd.c: now TLS is defined before chkuser.h call, to avoid errors on closing the db connection + (tx ChangHo.Na https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1469) + +2019.07.12 +-qmail-channels patch added + more info here http://www.thesmbexchange.com/eng/qmail-channels_patch.html +-improved verbosity of die_read function in qmail-smtpd.c (qmail-smtpd: read failure) + more info here https://notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06/die_read.patch + +2019.06.19 +-DKIM patch updated to v. 1.26 + * BUG - honor body length tag in verification + +2019.05.24 +-qmail-tls updated to v. 20190517 + * bug: qmail-smtpd ssl_free before tls_out error string (K. Wheeler) + +2019.05.23 +-DKIM patch updated to v. 1.25 + * SIGSEGV - when the txt data for domainkeys is very large exposed a bug in the way realloc() was used incorrectly. + * On 32 bit systems, variable defined as time_t overflows. Now qmail-dkim will skip expiry check in such conditions. + +2019.04.25 +-bug fixed on qmail-smtpd.c: it was selecting the wrong openssl version on line 2331 (tx ChangHo.Na) + +2019.04.09 +-qmail-tls updated to v. 20190408 + * make compatible with openssl 1.1.0 (Rolf Eike Beer, Dirk Engling, Alexander Hof) + * compiler warnings on char * casts (Kai Peter) + +2019.03.22 +-fixed a bug causing crashes with qmail-remote when using openssl-1.1 (tx Luca Franceschini) +(https://notes.sagredo.eu/files/qmail//patches//roberto-netqmail-1.06/2019.03.22-fix.patch) + +2019.02.13 +-Port to openssl-1.1 +-DKIM patch updated to v. 1.24 + * bug fix: restored signaturedomains/nosignaturedomains functionalities. + +2018.08.25 +-DKIM patch updated to v. 1.23 + * fixed a bug where including round brackets in the From: field ouside the double quotes, i.e. + From: "Name Surname (My Company)" , results in a DKIMContext structure invalid + error (tx Mirko Buffoni). + * qmail-dkim and dkim were issuing a failure for emails which had multiple signature with at least one good + signature. Now qmail-dkim and dkim will issue a success if at least one good signature is found. + +2018.08.23 +-logging patch + * fixed a bug in logit and logit2 functions where after a RSET command and a subsequent brutal quit + of the smtp conversation '^]' by the client cause a segfault (tx Mirko Buffoni, more info here + https://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html#comment1132) +-patch info moved to 'README.PATCH' file + +2018.04.03 +-DKIM patch updated to v. 1.22 + * openssl 1.1.0 port + * various improvements, bug fixes + +2018.01.10 +-maildir++ + * fixed a bug where the filesize part of the S= component of the Maildir++ compatible filename + is wrong (tx MG). More info here: http://notes.sagredo.eu/en/qmail-notes-185/installing-dovecot-and-sieve-on-a- +vpopmail-qmail-server-28.html#comment995 +-qmail-queue-extra + * removed, because it was causing more problems than advantages, as the domain of the log@yourdomain.tld + had to match the system domain inside control/me and shouldn't be a virtual domain as well. + +2017.10.11 (tx Luca Franceschini) +-qlogfix + * log strings should terminate with \n to avoid trailing ^M using splogger + * bug reporting custom errors from qmail-queue in qlog +-added dnscname patch +-added rcptcheck patch + +2017.08.18 +-qmail-smtpd now retains authentication upon rset + (tx to Andreas http://notes.sagredo.eu/qmail-notes-185/smtp-auth-qmail-tls-forcetls-patch-for-qmail-84.html#comme +nt750) + +2017-05-14 +-DKIM patch updated to v. 1.20 + It now manages long TXT records, avoiding the rejection of some hotmail.com messages. + +2016-12-19 +-Several new patches and improvements added (thanks to Luca Franceschini) +More info here http://notes.sagredo.eu/node/178 + -qregex patch + -brtlimit patch + -validrcptto patch + -rbl patch (updates qmail-dnsbl patch) + -reject-relay-test patch + -added DISABLETLS environment variable, useful if you want to disable TLS on a desired port + -added FORCEAUTHMAILFROM environment variable to REQUIRE that authenticated user and 'mail from' are identical + -fixed little bug in 'mail from' address handling (patch by Andre Opperman at http://qmail.cr.yp.narkive.com/kBry + 6GJl/bug-in-qmail-smtpd-c-addrparse-function) + -added SMTPAUTHMETHOD, SMTPAUTHUSER and SMTP_AUTH_USER env variables for external plugins + -qlog patch + -reject null senders patch + -bouncecontrolmime patch + -qmail-taps-extended (updates qmail-tap) + +2016-12-02 +-fixed BUG in qmail-remote.c: in case of remote server who doesn't allow EHLO the response for an alternative + HELO was checked twice, making the connection to die. (Thanks to Luca Franceschini) + Patch applied: http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/fix_sagredo_remotehelo.patch + +2016-09-19 +-qmail-tls patch updated to v. 20160918 + * bug: qmail-remote accepting any dNSName, without checking that is matches (E. Surovegin) + * bug: documentation regarding RSA and DH keys (K. Peter, G. A. Bofill) + +2016-05-15 +-force-tls patch improved (a big thanks to Marcel Telka). Now qmail-smtpd avoids to write the auth verb if the + the STARTTLS command was not sent by the client + +2016-03-09 +-DKIM patch upgraded to v. 1.19 + * verification will not fail when a dkim signature does not include the subject provided that the + UNSIGNED_SUBJECT environment variable is declared. + +2015-12-26 +-qmail-tls patch updated to v. 20151215 + * typo in #if OPENSSL_VERSION_NUMBER for 2015-12-08 patch release (V. Smith) + * add ECDH to qmail-smtpd + * increase size of RSA and DH pregenerated keys to 2048 bits + * qmail-smtpd sets RELAYCLIENT if relaying allowed by cert + more info at http://inoa.net/qmail-tls/ + +2015-12-15 +-DKIM patch by Manvendra Bhangui updated to v. 1.18 + +2015-10-03 +-qmail-authentication: updated to v. 0.8.3 + +2015-08-08 +-fixed a bug on qmail-remote.c that was causing the sending of an additionale ehlo greeting (thanks to Cristoph Gr +over) + +2015-04-11 +-qmail-authentication: updated to v. 0.8.2 +-qmail-tls: upgraded to v. 20141216 (POODLE vulnerability fixed) + +2015-03-28 +-added qmail-eMPF patch + +2014-11-19 +-security fix: the SSLv3 connection is now switched off + +2014-11-15 +-modified the QUEUE_EXTRA variable in extra.h to improve the qmail-send's log + +2014-04-14 +-added maxrcpt patch + +2014-03-10 +-added qmail-0.95-liberal-lf patch + +2013-12-30 +-added qmail-srs +-the character "=" is now considered valid in the sender address by chkuser in order to accept SRS + +2013-12-18 +-added qmail-date-localtime patch + +2013-12-14 +-added qmail-hide-ip patch + +2013-12-10 +-the original greetdelay by e.h. has been replaced with the improved patch by John Simpson. Now + communications trying to send commands before the greeting will be closed. Premature disconnections will be + logged as well. +-CHKUSER_SENDER_FORMAT enabled to reject fake senders without any domain declared (like ) +-chkuser logging: I slightly modified the log line adding the variables' name just to facilitate its interpretation +-added qmail-moreipme patch + +2013-12-07 +-added qmail-dnsbl patch + +2013-12-05 +-added two patches to make qmail rfc2821 compliant + +2013-11-23 +-added any-to-cname patch + +2013-09-27 +-DKIM patch upgraded to v. 1.17. Defined -DHAVE_SHA_256 while compiling dkimverify.cpp in the Makefile. + This solved an issue while verifying signatures using sha256. + +2013-09-16 +-Minor fixes to the DKIM patch. + +2013-09-13 +-DKIM patch upgraded to v. 1.16. The signing at qmail-remote level has been revised by its author. + +2013-08-25 +-qmail-qmqpc.c call to timeoutconn() needed a correction because the function signature was modified by the + outgoingip patch. Thanks to Robbie Walker (diff here http://notes.sagredo.eu/node/82#comment-373) + +2013-08-21 +-fixed a bug in hier.c which caused the installation not to build properly the queue/todo dir structure (thanks to + Scott Ramshaw) + +2013-08-18 +-DKIM-SURBL patch by Manvendra Bhangui updated to v. 1.14 + +2013-08-12 +-DKIM patch upgraded to v. 1.12. The new patch adds surblfilter functionality. +-added qmail-smtpd pid, qp log patch + +2013-08-08 +-qmail-SPF modified by Manvendra Bhangui to make it IPv6-mapped IPv4 addresses compliant. In order to have it + working with such addresses you have to patch tcpserver.c accordingly. You can use a patch fot ucspi-tcp6-0.98 + by Manvendra Bhangui at http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/tcpserver-ipv6mapped_ip + v4.patch or wait for v. 0.99 relase of ucspi-tcp6 +-added outgoingip patch +-added qmail-bounce patch + +2013-03-31 +qmail-auth updated to latest v. 0.8.1 Added authentication by recipient domain for qmail-remote. +Look at README.auth for further details + +2013-02-11 +some code adjustments in qmail-smtpd.c smtpd_ehlo() to restore total compatibility with esmtp-size patch + +2013-02-08 +qmail-auth updated to latest v. 0.7.6. Look at README.auth for further details + +2013-01-28 +fixed an issue on qmail-pop3d which was causing a double +OK after the pass command (thanks to Rakesh, Orbit +and Simplex for helping in testing and troubleshooting) + +2013-01-06 +environment variable GREETDELAY renamed to SMTPD_GREETDELAY + +2012-10-31 +qmail-auth updated to latest v. 0.7.5. Look at README.auth for further details +The qmail-forcetls patch was simplyfied accordingly. +You MUST export SMTPAUTH="" in your run file now. + +2012-04-25 +-added qmail-remote CRLF (thanks to Pierre Lauriente for the help on testing and troubleshooting) +The qmail-remote CRLF patch solved a problem of broken headers after sieve forwarding that was +caused by a bad handling of the CR (carriage return) by qmail-remote. +The issue is also reported here http://www.dt.e-technik.uni-dortmund.de/~ma/qmail-bugs.html + +2012.04.16 +-added qmail-tap + +2012.02.08 +-added smtp-size patch + +2012.01.29 +-added doublebounce-trim patch + +2011.12.12 +-file update_tmprsadh modified to chown the .pem files to vpopmail to avoid hang-ups during the smtp +conversation on port 587 caused by permission problems. + +2011.10.06 +-qmail-remote.c: fixed. It was not going into tls on authentication (thanks to Krzysztof Gajdemski) +-force-tls now quits if the starttls command is not provided when required (thanks to Jacekalex) diff -ruN ../netqmail-1.06-original/README.auth netqmail-1.06/README.auth --- ../netqmail-1.06-original/README.auth 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.auth 2019-02-27 20:57:13.379025191 +0100 @@ -0,0 +1,154 @@ +README qmail SMTP Authentication +================================ + +Scope: +------ + +This patch supports RFC 2554 "SMTP Service Extension for Authentication" and +RFC 4409 "Message Submission for Mail" for + +* qmail-smtpd and +* qmail-remote + +and supports commonly the AUTH methods + +- CRAM-MD5 +- LOGIN (unsecure) +- PLAIN (unsecure) + +Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration"). +For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html. + + +Installation: +------------- + +* Untar the source in the qmail-1.03 home direcotry. +* Run ./install_auth. +* Re-make qmail. + + +Setup for qmail-smtpd: +---------------------- + +1. Prereqs: + +In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module' +PAM to be called by qmail-smtpd; typically + + /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1 + +Since qmail-smtpd does not run as root, checkpassword has to be made sticky. +There is no need to include additionally the hostname in the call. +In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information. + +2. Invocation: + +In order activate SMTP authentication, you need to provide the environment +variable 'SMTPAUTH' to qmail-smtpd. + +Possible choices: + + a) SMTPAUTH=""; qmail-smtpd supports auth of type PLAIN and/or LOGIN. + b) SMTPAUTH="+cram"; qmail-smtpd will additionally annonce CRAM-MD5, + this requires a CRAM-MD5 supporting PAM. + c) SMTPAUTH="cram"; qmail-smtpd will only annonce CRAM-MD5. + d) SMTPAUTH="!"; this instructs qmail-smtpd to require (any type) authentication for this connection. + This behavior is equivalent to the Submission feaure. + e) SMTPAUTH="!cram"; same as d) but now CRAM-MD5 is the only method instead. + + +Setup for qmail-remote: +----------------------- + +SMTP Authentication with qmail-remote is faclitated by two means: + +a) SMTP Authentication by sender/sending domain as provided in the 'Mail From:' + +The control file 'authsenders' which works similar to 'control/smtproutes' +but with additional authentication information (username + password): + + @example.com:relay.example.com|user|passwd + info@example.com:relay.example.com:26|infouser|infopasswd + :mailrelay.example.com:587|e=mc2|testpass + +Note: The choice of the AUTH method depends on the capabilities of the server. + +b) SMTP Authentication by recipient domain: + +The control file 'smtproutes' is enhanced with the authentication information: + + authdomain.com:mx.authdomain.com:125|myuserid|mypassword + :mailrelay.example.com:587|e=mc2|testpass + + +Historical Notes: +----------------- + +SMTP authentication for qmail-smtpd was initially provided by Krysztof Dabrowski (version 0.31): + + +Changes wrt. Krysztof Dabrowski's patch: + +* Avoid the 'hostname' in the call of the PAM. +* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5. +* Doesn't close FD 2; thus not inhibiting logging to STDERR. +* Fixed bugs in base64.c. +* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'. +* Evaluation of the (informational) Mail From: < > Auth=username. +* Additional support for the advertised "Size" via 'Mail From: SIZE=123456780' (RFC 1870). +* RFC 3848 conformance for Received header in case of SMTP Auth (keyword ESMTPA). +* Added SMTPAUTH environment variable. +* The particular Submission feature has been removed; obsolete. + + +SMTP authentication for qmail-remote is taken from Bjoern Kalkbrenner. + +Changes wrt. Bjoern Kalkbrenner's patch (version 0.0.1 / 20020715): + +* Uniform, modular support for LOGIN and PLAIN. +* Added 'Mail From: < > Auth=username' information in provisionally XTEXT format. +* Added CRAM-MD5 support. +* Added authentication by recipient domain. + + +Release Notes: +-------------- + +Version: Notes: Date: +------------------------------------------------------------------- +0.5.9 qmail-smtpd AUTH (only) 25.3.2008 +0.6.9 qmail-authentication 1.2.2010 +0.7.0 Based on qmail-authentication 0.69 + including now CRAM-MD5 support + for qmail-remote 31.7.2010 +0.7.1 cosmetics for qmail-remote 5.8.2010 +0.7.2 authorization-id = authentication-id + for qmail-remote; + added SMTPAUTH environment variable + for qmail-smtpd; backport from SC 2.7 29.4.2012 +0.7.3 Fixed missing AUTH for qmai-smtpd announcement. + Improved SUBMISSION port handling. 2.5.2012 +0.7.4 Fixed missing 250 statements for AUTH. + Changed SMTPAUTH settings for cram-md5 18.5.2012 +0.7.5 Fixed bug in qmail-remote not respecting + announced AUTH types. Tx. Callum Gibson. + Added '432' server code evaluation for + AUTH password expired in qmail-remote. 23.10.2012 +0.7.6 Fixed order of SMTP commands (tx roberto). + 02.02.2013 +0.8.0 Added authentication by recipient domain + for qmail-remote. + Removed SUBMISSION port feature for + qmail-smtpd. 22.03.2013 +0.8.1 Added SMTPAUTH="!+crom" feature. 24.03.2013 +0.8.2 Bug fix: qmail-smtpd ACCEPTS auth commands + even if SMTPAUTH="-". (tx chris). 21.01.2015 +0.8.3 Fixed bug in MD5 calculation for AMD64. + Fixed 'die_nomem' bug in qmail-smtpd. 23.08.2015 + + + +Erwin Hoffmann - Hoehn 2015-08-23 (www.fehcom.de) + + diff -ruN ../netqmail-1.06-original/README.dnsbl netqmail-1.06/README.dnsbl --- ../netqmail-1.06-original/README.dnsbl 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.dnsbl 2019-02-27 20:57:13.379025191 +0100 @@ -0,0 +1,18 @@ +Code and logic from rblsmtpd and qmail-dnsbl patch http://qmail-dnsbl.sourceforge.net/ + +-added support for whitelists, TXT and A queries, configurable return codes 451 or 553 with custom messages + + # - default file control/dnsbllist can be overridden with env variable DNSBLLIST + # - if DNSBLSKIP env variable is set, qmail-smtpd skips the rbl check + # - if control/dnsblfailclosed or DNSBLFAILCLOSED are defined, qmail-smtpd considers the source ip as blacklisted even in case of lookup failures (check rblsmtpd man page for more details) + # - support for environment variable RBLSMTPD (check rblsmtpd man page for more details) + # - dnsbllist can contain empty lines and comments with '#' at start or end of lines; leading and trailing spaces are automatically removed + +CONTROL FILES + +dnsbllist + A list of dnsbl providers that qmail-smtpd checks to identify blacklisted ip addresses. + + Exception: If the environment variable DNSBLSKIP is set, qmail-smtpd ignores dnsbllist, and the dnsbl check is not performed. The check is skipped even if + some other authentication method succedeed and authorized the client to relay (smtp-auth or tls client certificate), or if RELAYCLIENT enviromnent variable is + set. diff -ruN ../netqmail-1.06-original/README.empf netqmail-1.06/README.empf --- ../netqmail-1.06-original/README.empf 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.empf 2019-02-27 20:57:13.380025180 +0100 @@ -0,0 +1,106 @@ +config file is: /var/qmail/control/policy +more info: http://www.qmailwiki.org/EMPF + +eMail Messaging Policy Framework (eMPF) + +What it does +------------ + + eMPF follows a set of administrator-defined rules describing +who can message whom. With this, companies can segregate various +parts of their organizations email activities, as well as provide +a variety of security-enhancing services. + +How it does it +-------------- + + During an SMTP session, when a sender identifies themselves, either +via SMTP_AUTH, or via the message envelope, as well as a recipient, +eMPF loads applicable message policies to determine if the sender is +allowed to message the sender, and if the recipient is allowed to +receive mail from the sender. + +What it doesn't do +------------------ + + Because mail from outside your mail server cannot be authenticated, +the policy framework cannot be entirely sure about the identities of +senders messaging local users. However, if SMTP_AUTH is enabled, and +required by your local users, it can prevent local users from transmitting +information to parties who should not view it. It is HIGHLY recommended +that a mail server implementing eMPF also require SMTP_AUTH by it's local +users as well as use the POLICY_ENFORCE_AUTHENTICATION configuration so that +senders from the outside cannot masquerade as local users. + +Policy configuration +-------------------- + + A large, complicated policy may be rather intimidating at first, however, +if the rules are documented well, and a basic knowledge of the format of a +policy is known, they are rather simple to set up. + + comment: + # text + ; text + + policy: + :,[,][,] + + user policy: + :[] + + delivery policy: + [(
[,
])] + + delivery types: + L - Local + R - Remote + E - External + I - Internal + +Delivery types specify what types of messaging can take place. An uppercase +delivery type allows a type of delivery, and a lowercase delivery type, +disallows a type of delivery. Delivery types may take a list of addresses. +When a list of addresses is provided after a delivery type, those addresses +are the only addresses covered by that delivery type. + +Certian delivery types apply only to either the sender of the recipient. +Here is a more clear definition of each of the delivery types. + + Local - (Sender only) When sending a message to a user on the same domain + Internal - (Recipient only) When recieving a messages from a user of the + same domain + Remote - (Sender only) When sending a message to a user on another + domain (even if that domain resides on the same mail server). + External - (Recipient only) When receiving a message from a user on + another domain (even if that domain resides on the same server). + +Samples +------- + +In this example, example.com allows all messaging. In this case, +simply not defining a policy would be more efficient. + + example.com:LREI, + +Now, example.com wishes all mail to stay internal. As stated above, +there are particular cases in which eMPF cannot authenticate a sender. +This only occurs when a remote mail server is transmitting mail to a local +user on your system. In this case, a remote user could pretend to be a local +user, and succesfully deliver mail to another local user. However, the +recipient would be unable to message back. + + example.com:LIre, + +As in the above example, example.com wants all mail to stay internal, however, +a few of their users are allowed to communicate with the outside world. Sales +can communicate with everybody, and Tasks can send messages only to their +sister-site, example.org. + + example.com:LIre,sales:RE,tasks:R(*@example.org)E(*@example.org), + +Something to keep in mind in this scenario, is that if example.org is hosted +on the same system, and has similar policies to example.com, a policy must be +established for example.org which allows messages from example.com. + + example.org:LIre,sales:RE,tasks:E(*@example.com)R(*@example.com), diff -ruN ../netqmail-1.06-original/README.exttodo netqmail-1.06/README.exttodo --- ../netqmail-1.06-original/README.exttodo 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.exttodo 2019-02-27 20:57:13.380025180 +0100 @@ -0,0 +1,114 @@ +EXTTODO by Claudio Jeker and +Andre Oppermann +(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd. + +The EXTTODO patch is a part of the qmail-ldap patch. +This patches for qmail come with NO WARRANTY. + +These patches are under the BSD license. + +RELEASE: 5. Jan. 2003 + +EXTTODO: +====================== + +TOC: + WHAT DOES IT DO + INSTALL + CONFIG FILES + SETUP + BIG PICTURE + +NEWS: + + This is the first release of the EXTTODO patch. + +================================================================================ + +WHAT DOES IT DO + + The exttodo patch addresses a problem known as the silly qmail (queue) + problem. This problem is found only on system with high injection rates. + + qmail with a big local and remote concurrency could deliver a tremendous + amount of messages but normally this can not be achieved because qmail-send + becomes a bottleneck on those high volumes servers. + qmail-send preprocesses all new messages before distributing them for local + or remote delivering. In one run qmail-send does one todo run but has the + ability to close multiple jobs. Because of this layout qmail-send can not + feed all the new available (local/remote) delivery slots and therefor it is + not possible to achieve the maximum throughput. + This would be a minor problem if one qmail-send run could be done in extreme + short time but because of many file system calls (fsync and (un)link) a todo + run is expensive and throttles the throughput. + + The exttodo patch tries to solve the problem by moving the todo routine into + an external program. This reduces the run time in qmail-send. + + exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares + incoming messages for local and remote delivering (by creating info/ + local/ and remote/ and removing todo/). See also + INTERNALS. As next qmail-todo transmits the to qmail-send which will + add this message into the priority queue which schedules the message for + delivery. + +INSTALL + + To enable the exttodo patch you need to define EXTERNAL_TODO while compiling + qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO). + + NOTE: the exttodo patch can also be used on qmail systems without the + qmail-ldap patch. + +================================================================================ + +CONFIG FILES + + No additional control files are used or needed. + +================================================================================ + +SETUP + + qmail-todo will be started by qmail-start and therefor no additional setup + is needed. + + To verify that exttodo is running just check if qmail-todo is running. + +================================================================================ + +BIG PICTURE + + +-------+ +-------+ + | clean | | clean | + +--0-1--+ +--0-1--+ +-----------+ + trigger ^ | ^ | +->0,1 lspawn | + | | v | v / +-----------+ + +-------+ v +--2-3--+ +--5-6--+ / + | | | | 0<--7 1,2<-+ + | queue |--+--| todo | | send | + | | | | 1-->8 3,4<-+ + +-------+ +-------+ +---0---+ \ + | \ +-----------+ + v +->0,1 rspwan | + +---0---+ +-----------+ + | logger| + +-------+ + +Communication between qmail-send and qmail-todo + +todo -> send: + D[LRB]\0 + Start delivery for new message with id . + the character L, R or B defines the type + of delivery, local, remote or both respectively. + L\0 + Dump string to the logger without adding additional \n or similar. +send -> todo: + H Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains + X Quit ASAP. + +qmail-todo sends "\0" terminated messages whereas qmail-send just send one +character to qmail-todo. + + diff -ruN ../netqmail-1.06-original/README.liberal-lf netqmail-1.06/README.liberal-lf --- ../netqmail-1.06-original/README.liberal-lf 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.liberal-lf 2019-02-27 20:57:13.380025180 +0100 @@ -0,0 +1,39 @@ +From dgaudet-list-qmail@arctic.org Fri Jan 17 17:50:19 1997 +Date: Fri, 17 Jan 1997 17:38:33 -0800 (PST) +From: Dean Gaudet +To: Julie Baumler +Cc: qmail list +Subject: [PATCH] qmail and \r\n (was Re: host unable to deliver to host using qmail (summary)) + +On Thu, 16 Jan 1997, Julie Baumler wrote: +> +> The admin on the machine in question added "E=\r\n" this morning and low +> and behold, suddenly they can send us mail. +> +> >From a quick look at the source code, it appears that the remote machine +> is notified of the problem, but sendmail in its infinite wisdom apparently +> doesn't capture the error message. (SIGH) + +I don't know what the sendmail config had wrong with it, but I know that +systems I've set up bounce the error just fine. Check also in the mail +logs. + +qmail by default violates the "be liberal in what you accept" rule when +receiving mail via smtp. It enforces strict \r\n behaviour... which is +exactly what the standard dictates. But I'm not interested in my mailer +telling other admins to fix their setup, because I know they won't. (The +straynewline() function tells people that if they're under Solaris they +need E=\r\n in their Mether mailer.) + +There is also bug in sendmail pre 8.8 that in some instances will emit +\rx\n for some printable x. The case where it occured to me, the message +had been generated by a lame PC mail program. In any event, this causes +qmail to indefinitely defer the incoming message. + +So instead I looked at the sendmail-8.8.4 code and tried to duplicate its +behaviour w.r.t. seeing \n when it wasn't expecting it. That is what this +patch implements. + +Go ahead, shoot me. + +Dean diff -ruN ../netqmail-1.06-original/README.maxrcpt netqmail-1.06/README.maxrcpt --- ../netqmail-1.06-original/README.maxrcpt 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.maxrcpt 2019-02-27 20:57:13.380025180 +0100 @@ -0,0 +1,60 @@ +maxrcpt patch for qmail-smtpd + + To: djb-qmail@xxxxxxxxxxxxxxxxxxxx + Subject: maxrcpt patch for qmail-smtpd + From: Michael Samuel + Date: Wed, 12 Nov 1997 15:18:38 +1100 (EST) + Mailing-list: contact djb-qmail-help@xxxxxxxxxxxxxxxxxxxx; run by ezmlm + +-----BEGIN PGP SIGNED MESSAGE----- + +Here is a patch for qmail-smtpd to read a control file to limit the number +of rcpt to commands. + +In the patch I have a thank-you to Sam, this is referring to +mrsam@xxxxxxxxxxxxx who gave me the ideas on how to start off. + +Anyway, it is a "Do what you like with it, but don't blame me" license, +but if it doesn't work, tell me what went wrong if you like and I will +try to figure it out. + + + +Michael Samuel, + +Surf-Net City - Internet Cafe and Internet Service Providers + + Phone: +61 3 9593-9977 + E-Mail: michael@xxxxxxxxxxxxxxxxxx + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.3ia +Charset: noconv + +iQCVAwUBNGkuIUqgdYLWa7qBAQHEiwP+JqNDMZDLwLY7CUdmkuY0OUHwSaFCJJiS +T853fUkupG2kQz6WU8m0RXWd4Rhr+BT8+hqjDDPQYfWzK6QcEf563D0Mp7nA0ZuQ +s+XHKflwb8PAZBp+lpzkMsgDg/B8mlw9dnJ4pGeP1keWR/5cgBFM78XsthW2rLXd +EIXiZJ7AEhc= +=5RMp +-----END PGP SIGNATURE----- + +Here is a patch I rigged up limit the number of RCPT TO: commands per E-Mail +messages. + +It reads the file control/maxrcpt relative to your qmail directory stucture +(usually /var/qmail/control/maxrcpt). + +In that file you should have a integer, which represents the maximum number +of recipients per E-Mail Messages. + +Apparently one of the SMTP rfcs recommends a minimum of 100 recipients per +message be allowed. Just something to keep in mind anyway. + +If /var/qmail/control/maxrcpt doesn't exist, it does not impose a limit and +skips the rcpt part of the code, so unless I missed something in the source, +you could even have more than MAXINT. + +I would like to thank Sam from the qmail list for giving me a good start to +this patch, and anyone else who offered me suggestions from the qmail list. +(When I refer to qmail list, I'm referring to djb-qmail@xxxxxxxxxxxxxxxxxxxx) + diff -ruN ../netqmail-1.06-original/README.moreipme netqmail-1.06/README.moreipme --- ../netqmail-1.06-original/README.moreipme 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.moreipme 2019-02-27 20:57:13.380025180 +0100 @@ -0,0 +1,154 @@ +########### +### WHO ### +########### + +This patch was written by Scott Gifford . +The design and much of the code for supporting "notipme" was +contributed by Charles Cazabon . + + +############ +### WHAT ### +############ + +This patch may be necessary in some configurations that involve network +address translation or port forwarding. It prevents a problem caused +by an MX or other mail routing directive instructing qmail to connect to +itself without realizing it's connecting to itself. When this happens, +it accepts the message, finds out where to deliver it to (itself), and +promptly reconnects to itself to deliver the message. Eventually, when +it has done this 20 or 30 times, it will give up and bounce the message, +but not before sucking up all of your CPU while it's happening. + +It may also be useful in some configurations that have multiple qmail +servers configured on different interfaces of the same system. qmail +will normally refuse to deliver mail by SMTP to the machine it's +running on, but with multiple copies of qmail, you may want to prevent +this behavior. + +Normally, qmail can detect what IP addresses refer to itself by getting +a list of all network interfaces with IP addresses from the operating +system. It uses this list to determine whether connecting to an address +will cause it to connect to itself, and avoid the situation (it calls +the perm_ambigmx() function, which prints the message: + + Sorry. Although I'm listed as a best-preference MX or A for that host, + it isn't in my control/locals file, so I don't treat it as local. (#5.4.6) + +But in situations where the OS is not aware of all IP addresses that +connect back to itself, this detection fails, causing the CPU-sucking +phenomenon described above. This can happen if there is a network +address translation device in front of the qmail server, such as a +load-balancer or a router which allows you to share one IP address among +several machines; if there is a port forwarder forwarding connections +from some other machine to the SMTP server on the qmail server; or in +configurations where a "dumb" mailserver is configured to use your qmail +server as a "smarthost", delivering all mail to it without inspection. + +To solve this, other IP addresses which will ultimately connect back to +your machine can be added to the file "control/moreipme", one per line. +qmail will treat all addresses in this file exactly as if they were +local, and if it finds an MX record or other mail routing information +which would cause it to connect to any of these addresses, it will call +perm_ambigmx(), and print the above error message. + +Additionally, IP addresses which the system detects but which should +*not* be treated as local can be removed from qmail's ipme list by +adding them to the file "control/notipme". + +IP addresses can be specified as individual addresses in the usual +dotted-quad format, or as entire networks using a slash followed by +the full dotted-quad netmask: + + 127.0.0.1 + 127.0.0.1/255.255.255.255 + 127.0.0.0/255.0.0.0 + 10.0.0.0/255.255.255.0 + +An individual address is treated exactly like a network with a mask of +255.255.255.255. Addresses of interfaces found on the system are +added with their individual addresses. In addition, these addresses +are implicitly added: + + 0.0.0.0 + 127.0.0.0/255.0.0.0 + +So the list of system addresses (the "ipme" list) is, in order, +127.0.0.0/255.0.0.0, 0.0.0.0, then all actual interfaces on the system +in the order they are reported, then the contents of the "moreipme" +file. The list of excluded addresses (the "notipme" list) is just the +contents of the "notipme" file. + +If an address appears in both the ipme list and the notipme list, the +entry with the longest netmask wins. If the netmask lengths are the +same, notipme wins. + +For example, if the ipme list has 127.0.0.0/255.0.0.0 and notipme has +127.0.0.2, then 127.0.0.2 will not be considered me because the entry +in notipme has a 32-bit mask. If the notipme list has +127.0.0.0/255.0.0.0, all of 127.* will not be considered me. + +You can run the program "ipmeprint" from the source directory to see +what interfaces qmail is detecting or finds in moreipme. + +You can run the program "ipmetest" from the source directory to test +your configuration. It takes as its first and only parameter an IP +address to test, and prints either "me" or "not me". + +########### +### HOW ### +########### + +To apply the patch, download and save it somewhere, then cd into your +qmail source directory. + +For stock qmail, download qmail-1.03-moreipme-0.6.patch then run: + + cd qmail-1.03 + patch -p1 .patch". Follow the instructions as per the +included qmail INSTALL file. Once you are done come back to this file and read +the section on the control files. + +If you are using netqmail, then unpack the netqmail archive. Run the collate.sh +script and cd into the resulting netqmail- directory. From there, run +"patch < /path/to/qregex-.patch". Complete the netqmail installation +normally. Once you are done, come back to this file and read the section on the +control files. + +- If this is an existing setup. +FIRST: create your control files (see below). +cd into your existing qmail or netqmail source directory. Run +"patch < /path/to/qregex-.patch" then "make qmail-smtpd". Now run +./qmail-smtpd and test your new rules to make sure they work as expected. + +Install the new binary by cd'ing to /var/qmail/bin and as root (in one command) +copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the +source directory to 'qmail-smtpd'. +(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd) + +You can also optionally just run "make setup check" as it will install the +updated documentation and man pages provided with this patch. Stopping qmail +before doing the "make setup check" is always a good idea. + + +LOGGING: + +qregex will log matches to the patterns in the various control files. Log +messages will take these three forms depending on which control file was +matched: + +badhelo +qmail-smtpd: badhelo: at + +badmailfrom and badmailfromnorelay +qmail-smtpd: badmailfrom: at + +badmailto and badmailtonorelay +qmail-smtpd: badmailto: at + +When the LOGREGEX environment variable is set, the matched pattern will +be included in the log. Log messages will have the regex pattern appended +to them. For example, a badhelo log message will look like this: + +qmail-smtpd: badhelo: at matches pattern: + + +CONTROL FILES: + +qregex provides you with five control files. None of these control files +is mandatory and you can use them in any combination you choose in your setup. + +The "control/badmailfrom" and "control/badmailto" files contain your REs for +matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope +recipient) smtp commands respectively. +The "control/badmailfromnorelay" and "control/badmailtonorelay" match against +the same commands but are read only when the RELAYCLIENT environment variable +is not set. +The "control/badhelo" file matches against the 'helo/ehlo' smtp command. + +If you prefer you can symlink the badmailfrom and badmailto control files +(ln -s badmailfrom badmailto) and maintain fewer sets of rules. Beware +this might cause problems in certain setups. + + Here's an example "badhelo" file. + ----------------------------------- + # block host strings with no dot (not a FQDN) + !\. + ----------------------------------- + + An example "badmailfrom" file. + ----------------------------------- + # this will drop everything containing the string + # bad.domain.com or Bad.Domain.Com or BAD.domain.COM + bad\.domain\.com + # force users to fully qualify themselves + # (i.e. deny "user", accept "user@domain") + !@ + ----------------------------------- + + And "badmailto" (a little more interesting) + ----------------------------------- + # must not contain invalid characters, brakets or multiple @'s + [!%#:*^(){}] + @.*@ + ----------------------------------- + +You can use the non-RE character '!' to start an RE as a signal to qregex to +negate the action. As used above in the badmailfrom file, by negating the '@' +symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever +the address doesn't contain an @ symbol. When used inside a bracket expression, +the '!' character looses this special meaning. This is shown in the badmailto +example. + +The norelay control files follow the same rules as the other control files but +are intended to address two specific scenarios. +The badmailfromnorelay file can be used to block mail trying to spoof a domain +hosted on your mail server. It prevents a mail client that is not allowed to +relay email through your server from using one of your hosted domains as its +envelope sender. +The badmailtonorelay file can be used to create email addresses that cannot +receive mail from any source not allowed to relay email through your server. +This is handy for creating email addresses for use only within your own +domain(s) that can't receive spam from the world at large. + + +INTERNALS: + +qregex (or regexmatch as the function is called) will be called during the +`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c". +When called, it will read the proper control file then one by one compile and +execute the regex on the string passed into qmail-smtpd. If the regex matches +it returns TRUE (1) and the qmail-smtpd process will deny the user the ability +to continue. If you change anything and think it betters this patch please +send me a new diff file so I can take a peek. + + +CONTACT: +qregex is maintained by: + Andrew St. Jean + andrew@arda.homeunix.net + www.arda.homeunix.net/store/qmail/ + +Contributers to qregex: + Jeremy Kitchen + kitchen at scriptkitchen dot com + http://www.scriptkitchen.com/qmail + + Alex Pleiner + alex@zeitform.de + zeitform Internet Dienste + http://www.zeitform.de/ + + Thanos Massias + +Original qregex patch written by: + Evan Borgstrom + evan at unixpimps dot org diff -ruN ../netqmail-1.06-original/README.rfc2821 netqmail-1.06/README.rfc2821 --- ../netqmail-1.06-original/README.rfc2821 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.rfc2821 2019-02-27 20:57:13.380025180 +0100 @@ -0,0 +1,39 @@ +This patch is Copyright (C) 2002 - 2003 by Matthias Andree. License below. + +(this is the 2nd edition of this patch and the 4th edition of the +introductory text) + +This patch changes qmail-remote to skip over MX servers that greet with +codes 400 to 499 and to bounce mail when any MX server that qmail tries +greets with a code 500 to 599. + +If you want qmail-remote to skip over hosts greeting with 5XX-codes and +try the next MX for real RFC-2821 compliance (Sendmail and Postfix do +that), change the + + if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed"); + +to + + if (code >= 500 && code < 600) return; + +License: + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff -ruN ../netqmail-1.06-original/README.srs netqmail-1.06/README.srs --- ../netqmail-1.06-original/README.srs 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.srs 2019-02-27 20:57:13.381025169 +0100 @@ -0,0 +1,93 @@ +qmail SRS patch + +This is a SRS (Sender Rewriting Scheme) implementation for qmail using libsrs2. + +Current version: qmail-srs-0.8.patch +Changes + + 2011-03-30 (0.8): + Fixed bug reading configuration files. + 2007-06-05 (0.7): + New QMAILINJECT_FORCE_SRS and QMAILINJECT_SKIP_SRS environment variables can force or skip envelope rewriting in qmail-inject. + 2007-05-31 (0.6): + qmail-inject only will rewrite envelope if EXT and HOST variables are set. + Fixed bug in qmail-send handling chdir() calls (Special Thanks to Werner Fleck). + 2007-01-11 (0.5): + Added parameters srs_separator and srs_alwaysrewrite from libsrs2. + 2007-01-10 (0.4): + If srs_domain is empty or not set, SRS is disabled. + 2006-12-18 (0.3): + forward and condredirect: modified to work with SRS. + 2006-12-17 (0.2): + srsfilter: now rewrites To header with the SRS decoded address. + srsfilter: only accepts messages from null-sender, Return-Path: <>. + srsfilter: modified to reject messages without body. + qmail-inject: error message detailed. + If optional parameters are not set, will use libsrs2 defaults. + Install instructions revised. + 2006-12-15 (0.1): + First release. + +Install instructions + + Download and install libsrs2 from http://www.libsrs2.org/download.html. + Download qmail-srs-0.8.patch. + Apply this patch: + + tar -xzf /path/to/qmail-1.03.tar.gz + cd qmail-1.03 + patch -p1 < qmail-srs-0.8.patch + And follow your qmail instalation (config, make, make setup check, ...) + Configure some parameters in /var/qmail/control. + + Required parameters: + + echo srs.YOURDOMAIN > /var/qmail/control/srs_domain + echo SECRET > /var/qmail/control/srs_secrets + + YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com + SECRET: Replace with a random string + + Important! You MUST create a MX record for srs.YOURDOMAIN pointing to your server. + + Optional parameters: + + echo 7 > /var/qmail/control/srs_maxage + echo 4 > /var/qmail/control/srs_hashlength + echo 4 > /var/qmail/control/srs_hashmin + echo = > /var/qmail/control/srs_separator + echo 0 > /var/qmail/control/srs_alwaysrewrite + Configure your SRS domain. + + echo srs.YOURDOMAIN >> /var/qmail/control/rcpthosts + echo srs.YOURDOMAIN:srs >> /var/qmail/control/virtualdomains + echo "| /var/qmail/bin/srsfilter" > /var/qmail/alias/.qmail-srs-default + + YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com. + +Configuration Parameters + +Parameters in bold are required. +Parameter Description Example +srs_domain A domain to use in rewritten addresses. If not set, SRS is disabled. srs.foo-bar.com +srs_secrets A random string to generate and check SRS addresses. You can specify a list of secrets (one per line). The first secret in the list is used for generating new SRS addresses. All secrets on the list may be used to verify SRS addresses. foobar123 +srs_maxage The maximum permitted age of a rewritten address. SRS rewritten addresses expire after a specified number of days. libsrs2 default is 21, but I believe that a week is enougth to get all bounces, so I recommend you to use 7. 7 +srs_hashlength The hash length to generate in a rewritten address. The hash length is a measure of security in the SRS system; longer is more secure. 4 +srs_hashmin The hash length to require when checking an address. If the hash length is increased, there may be SRS addresses from your MTA in the wild which use a shorter hash length. This parameter may be set to permit checking of hashes shorter than srs_hashlength. This parameter must be at most srs_hashlength. 4 +srs_separator The separator to appear immediately after SRS[01] in rewritten addresses. This must be -, + or =. Default value is =. = +srs_alwaysrewrite Skip rcpthosts check and perform SRS rewriting for all forwarding, even when not required. This must be 0 (disabled) or 1 (enabled). Default value is 0 (disabled). 0 +Environment Variables (qmail-inject only) + +By default, this patch modifies qmail-inject to rewrite the envelope sender only if EXT and HOST variables are set. + +You can change this behavior using the following environment variables: + + QMAILINJECT_FORCE_SRS: qmail-inject will call srsforward() even if EXT and HOST are not set. + QMAILINJECT_SKIP_SRS: qmail-inject will not call srsforward() even if EXT and HOST are set. + +More about SRS + + http://www.openspf.org/SRS + http://www.libsrs2.org/ + http://wooledge.org/~greg/qmail-srs.html + diff -ruN ../netqmail-1.06-original/README.surbl netqmail-1.06/README.surbl --- ../netqmail-1.06-original/README.surbl 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.surbl 2019-02-27 20:57:13.381025169 +0100 @@ -0,0 +1,34 @@ +SURBL filter for netqmail +========================= +surblfilter reads an rfc822 email on stdin and prints it back on stdout. +It extracts URL and checks them against SURBL. surblfilter can be used +as a filter using qmail-qfilter(1) or as a qmail-queue(8) frontend by +setting QMAILQUEUE environment variable to a wrapper +/var/qmail/bin/surblqueue + +surbfilter caches the result in /var/qmail/control/cache. qmail-smtpd +needs to have write permission for this directory. + +surbfilter does base64 decoding for base64 encoded text/html, text/plain +to extract urls. + +surblfilter requires two control files level2-tlds and level3-tlds in +/var/qmail/control. The same can be obtained from surbl.org website + +http://www.surbl.org/tld/three-level-tlds +http://www.surbl.org/tld/two-level-tlds + +surbfilter is a standone program and does not need you to patch qmail-smtpd +or existing programs. It exits 88 in case it finds a SURBL listed URL. It +exits 111 for temporary errors. It exits 0 if everyting is fine. + +surblfilter has been integrated with my qmail-dkim patch and is avaiable at + +https://sourceforge.net/projects/indimail/files/netqmail-addons/qmail-dkim-1.0/ + +It has been integrated in dkim-netqmail-1.06.patch-1.9.gz. The older patches +will not have surblfilter + +surbfilter is built on djb functions and some functions have been ruthlessly +borrowed from qmail surbl interface by Pieter Droogendijk and the surblhost +program at http://surblhost.sourceforge.net/ diff -ruN ../netqmail-1.06-original/README.tap netqmail-1.06/README.tap --- ../netqmail-1.06-original/README.tap 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.tap 2019-02-27 20:57:13.381025169 +0100 @@ -0,0 +1,34 @@ +qmail provides the ability to make a copy of each email that flows through the system. +This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2 + +The qmail tap patch adds additional functionality: +1) Specify which email addresses to tap using a regex style control file. With the + regex function, you can specify full domains or individual email addresses. + +2) Specify which email address to send the emails to. + +3) Qmail does not need to be restated to when the taps control file is changed. + +The regex match is applied to both the to and from email addresses. So email +sent to or from the addresses will be copied. Matching is case insensitive. +If there are multiple matches, the first match is used. + +The queue tap patch adds a new control file: + +/var/qmail/control/taps +Contains a regex style list of addresses to tap and the email +address of where you want the copy sent to. + +Examples: +a) To tap a whole domain add a line like: +A:.*@domain.com:joe@example.com + + +b) To tap an individual email address add a line like: +A:user@domain.com:other@example.com + +c) To tap messages going to a domain add a line like: +T:.*@domain.com:joe@example.com + +d) To tap messages comming from a domain add a line like: +F:.*@domain.com:joe@example.com diff -ruN ../netqmail-1.06-original/README.tls netqmail-1.06/README.tls --- ../netqmail-1.06-original/README.tls 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/README.tls 2020-01-10 21:46:22.909448586 +0100 @@ -0,0 +1,98 @@ +Frederik Vermeulen 20200107 +http://inoa.net/qmail-tls/ + +This patch implements RFC 3207 in qmail. +This means you can get SSL or TLS encrypted and +authenticated SMTP between the MTAs and from MUA to MTA. +The code is considered experimental (but has worked for +many since its first release on 1999-03-21). + +Usage: - install OpenSSL-1.1.0 http://www.openssl.org/ or later + (any version since 0.9.8 is presumed to work) + - apply patch to netqmail-1.06 http://www.usenix.org.uk/mirrors/qmail/netqmail + The patches to qmail-remote.c and qmail-smtpd.c can be applied + separately. + - provide a server certificate in /var/qmail/control/servercert.pem. + "make cert" makes a self-signed certificate. + "make cert-req" makes a certificate request. + Note: you can add the CA certificate and intermediate + certs to the end of servercert.pem. + - replace qmail-smtpd and/or qmail-remote binary + - verify operation (header information should show + something like + "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;") + +Optional: - when DEBUG is defined, some extra TLS info will be logged + - qmail-remote will authenticate with the certificate in + /var/qmail/control/clientcert.pem. By preference this is + the same as servercert.pem, where nsCertType should be + == server,client or be a generic certificate (no usage specified). + - when a 2048 bit RSA key is provided in /var/qmail/control/rsa2048.pem, + this key will be used instead of (slow) on-the-fly generation by + qmail-smtpd. Idem for 2048 DH param in control/dh2048.pem. + `make tmprsadh` does this. + Periodical replacement can be done by crontab: + 01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1 + - server authentication: + qmail-remote requires authentication from servers for which + /var/qmail/control/tlshosts/host.dom.ain.pem exists. + The .pem file contains the validating CA certificates. + One of the dNSName or the CommonName attributes have to match. + WARNING: this option may cause mail to be delayed, bounced, + doublebounced, and lost. + If /var/qmail/control/tlshosts/exhaustivelist is present, + the lists of hosts in /var/qmail/control/tlshosts is + an exhaustive list of hosts TLS is tried on. + If /var/qmail/control/notlshosts/host.dom.ain is present, + no TLS is tried on this host. + - client authentication: + when relay rules would reject an incoming mail, + qmail-smtpd can allow the mail based on a presented cert. + Certs are verified against a CA list in + /var/qmail/control/clientca.pem (eg. from + http://curl.haxx.se/ca/cacert.pem) + and the cert email-address has to match a line in + /var/qmail/control/tlsclients. This email-address is logged + in the headers. CRLs can be provided through + /var/qmail/control/clientcrl.pem. + - cipher selection: + qmail-remote: + openssl cipher string (`man ciphers`) read from + /var/qmail/control/tlsclientciphers + qmail-smtpd: + openssl cipher string read from TLSCIPHERS environment variable + (can vary based on client IP address e.g.) + or if that is not available /var/qmail/control/tlsserverciphers + - smtps (deprecated SMTP over TLS via port 465): + qmail-remote: when connecting to port 465 + qmail-smtpd: when SMTPS environment variable is not empty + +Caveats: - do a `make clean` after patching + - binaries dynamically linked with current openssl versions need + recompilation when the shared openssl libs are upgraded. + - this patch could conflict with other patches (notably those + replacing \n with \r\n, which is a bad idea on encrypted links). + - needs working /dev/urandom (or EGD for openssl versions >0.9.7) + for seeding random number generator. + - packagers should make sure that installing without a valid + servercert is impossible + - when applied in combination with AUTH patch, AUTH patch + should be applied first and first part of this patch + will fail. This error can be ignored. Packagers should + cut the first 12 lines of this patch to make a happy + patch + - `make tmprsadh` is recommended (or should I say required), + otherwise DH generation can be unpredictably slow + - some need "-I/usr/kerberos/include" to be added in conf-cc + +Copyright: GPL + Links with OpenSSL + Inspiration and code from examples in SSLeay (E. Young + and T. Hudson ), + stunnel (M. Trojnara ), + Postfix/TLS (L. Jaenicke ), + modssl (R. Engelschall ), + openssl examples of E. Rescorla . + +Bug reports: mailto: + diff -ruN ../netqmail-1.06-original/TARGETS netqmail-1.06/TARGETS --- ../netqmail-1.06-original/TARGETS 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/TARGETS 2019-06-26 16:45:45.017718421 +0200 @@ -1,3 +1,4 @@ +dktest auto-ccld.sh make-load find-systype @@ -10,11 +11,20 @@ qmail.o quote.o now.o +base64.o gfrom.o myctime.o slurpclose.o make-makelib makelib +maildirflags.o +maildirparsequota.o +maildiropen.o +maildirgetquota.o +maildirquota.o +overmaildirquota.o +strtimet.o +strpidt.o case_diffb.o case_diffs.o case_lowerb.o @@ -100,11 +110,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 @@ -158,6 +171,8 @@ auto_uids.o qmail-lspawn qmail-getpw.o +qmail-newmvrt.o +qmail-newmvrt auto_break.c auto_break.o auto_usera.c @@ -168,11 +183,19 @@ constmap.o timeoutread.o timeoutwrite.o +tls.o +ssl_timeoutio.o timeoutconn.o tcpto.o dns.o +srsfilter +srsfilter.o +srs +srs.o +spf.o ip.o ipalloc.o +strsalloc.o hassalen.h ipme.o ndelay.o @@ -182,6 +205,8 @@ qmail-remote qmail-rspawn.o tcpto_clean.o +md5c.o +hmac_md5.o qmail-rspawn direntry.h qmail-clean.o @@ -212,6 +237,9 @@ headerbody.o hfield.o token822.o +spf.o +spfquery.o +spfquery qmail-inject predate.o predate @@ -270,12 +298,16 @@ dnsip dnsmxip.o dnsmxip +dnstxt.o +dnstxt dnsfq.o dnsfq hostname.o hostname ipmeprint.o ipmeprint +ipmetest.o +ipmetest qreceipt.o qreceipt qsmhook.o @@ -320,6 +352,7 @@ binm2+df binm3 binm3+df +Makefile-cert it qmail-local.0 qmail-lspawn.0 @@ -382,6 +415,54 @@ addresses.0 envelopes.0 forgeries.0 +policy.o man setup check +qmail-todo.o +qmail-todo +update_tmprsadh +chkuser.o +qmail-dk +qmail-dkim +qmail-dkim.o +qmail-dk.o +libdkim.a +dkimbase.o +dkimdns.o +dkim.o +dkimsign.o +dkimverify.o +dkim +dkim.o +dkim.8 +qmail-dkim.8 +qmail-dkim.0 +str_cpyb.o +dkimfuncs.o +MakeArgs.o +spawn-filter spawn-filter.o qregex.o wildmat.o +spawn-filter.8 +qmail-dk.0 spawn-filter.0 +dk-filter echo.o echo dk-filter.0 dk-filter.8 +case_startb.o +mess822_ok.o +scan_xlong.o +socket_v4mappedprefix.o +socket_v6any.o +str_cspn.o +surblfilter.o +surblfilter +surblfilter.8 +uint64.h +surblqueue +surblfilter.0 +base64sub.o +qmail-dk.8 +dktest.o +dktrace.o +dknewkey +dktest.8 +time_t_size.h +channels.h + diff -ruN ../netqmail-1.06-original/alloc.c netqmail-1.06/alloc.c --- ../netqmail-1.06-original/alloc.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/alloc.c 2020-07-27 13:06:47.995674192 +0200 @@ -1,6 +1,7 @@ +#include #include "alloc.h" #include "error.h" -extern char *malloc(); +extern void *malloc(); extern void free(); #define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */ @@ -15,6 +16,10 @@ unsigned int n; { char *x; + if (n >= (INT_MAX >> 3)) { + errno = error_nomem; + return 0; + } n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */ if (n <= avail) { avail -= n; return space + avail; } x = malloc(n); diff -ruN ../netqmail-1.06-original/base64.c netqmail-1.06/base64.c --- ../netqmail-1.06-original/base64.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/base64.c 2019-02-27 20:57:13.381025169 +0100 @@ -0,0 +1,124 @@ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(in,l,out) +const unsigned char *in; +int l; +stralloc *out; /* not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while(in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + i = (n * 3) - p; + if (!stralloc_ready(out,i)) return -1; + out->len = i; + s = out->s; + + for(i = 0; i < n - 1 ; i++) { + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for(i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + i = in->len / 3 * 4 + 4; + if (!stralloc_ready(out,i)) 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 ../netqmail-1.06-original/base64.h netqmail-1.06/base64.h --- ../netqmail-1.06-original/base64.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/base64.h 2019-02-27 20:57:13.381025169 +0100 @@ -0,0 +1,13 @@ +#ifndef BASE64_H +#define BASE64_H + +/* DKIM-1.10 */ +#include "stralloc.h" +extern int b64decode(const unsigned char *, int, stralloc *); +extern int b64encode(stralloc *, stralloc *); +/* end DKIM-1.10 */ + +extern int b64decode(); +extern int b64encode(); + +#endif diff -ruN ../netqmail-1.06-original/base64sub.c netqmail-1.06/base64sub.c --- ../netqmail-1.06-original/base64sub.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/base64sub.c 2019-02-27 20:57:13.382025158 +0100 @@ -0,0 +1,170 @@ +/* + * $Log: base64sub.c,v $ + * Revision 1.6 2010-03-03 09:33:16+05:30 Cprogrammer + * renamed base64 to base64sub + * + * Revision 1.5 2004-10-22 20:18:37+05:30 Cprogrammer + * added RCS id + * + * Revision 1.4 2004-09-19 14:36:11+05:30 Cprogrammer + * corrected number of bytes in stralloc variable 'out' + * + * Revision 1.3 2004-07-30 17:36:47+05:30 Cprogrammer + * fixed bugs in b64decode() + * + * Revision 1.2 2004-07-17 21:16:27+05:30 Cprogrammer + * added RCS log + * + */ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* + * returns 0 ok, 1 illegal, -1 problem + */ + +int +b64decode(in, l, out) + const unsigned char *in; + int l; + stralloc *out; /*- not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) + { + if (!stralloc_copys(out, "")) + return -1; + return 0; + } + while (in[l - 1] == B64PAD) + { + p++; + l--; + } + n = (l + p) / 4; + out->len = (n * 3) - p; + if (!stralloc_ready(out, out->len)) + return -1; + s = out->s; + for (i = 0; i < n - 1; i++) + { + x = 0; + for (j = 0; j < 4; j++) + { + if (in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int) (in[j] - 'A' + 0); + else + if (in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int) (in[j] - 'a' + 26); + else + if (in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int) (in[j] - '0' + 52); + else + if (in[j] == '+') + x = (x << 6) + 62; + else + if (in[j] == '/') + x = (x << 6) + 63; + else + if (in[j] == '=') + x = (x << 6); + } + s[2] = (unsigned char) (x & 255); + x >>= 8; + s[1] = (unsigned char) (x & 255); + x >>= 8; + s[0] = (unsigned char) (x & 255); + x >>= 8; + s += 3; + in += 4; + } + x = 0; + for (j = 0; j < 4; j++) + { + if (in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int) (in[j] - 'A' + 0); + else + if (in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int) (in[j] - 'a' + 26); + else + if (in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int) (in[j] - '0' + 52); + else + if (in[j] == '+') + x = (x << 6) + 62; + else + if (in[j] == '/') + x = (x << 6) + 63; + else + if (in[j] == '=') + x = (x << 6); + } + b[2] = (unsigned char) (x & 255); + x >>= 8; + b[1] = (unsigned char) (x & 255); + x >>= 8; + b[0] = (unsigned char) (x & 255); + x >>= 8; + for (i = 0; i < 3 - p; i++) + s[i] = b[i]; + out->len = (n * 3) - p; + 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; +} + +void +getversion_base64sub_c() +{ + static char *x = "$Id: base64sub.c,v 1.6 2010-03-03 09:33:16+05:30 Cprogrammer Stab mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/byte.h netqmail-1.06/byte.h --- ../netqmail-1.06-original/byte.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/byte.h 2019-02-27 20:57:13.382025158 +0100 @@ -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 ../netqmail-1.06-original/byte_cspn.c netqmail-1.06/byte_cspn.c --- ../netqmail-1.06-original/byte_cspn.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/byte_cspn.c 2019-02-27 20:57:13.382025158 +0100 @@ -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 ../netqmail-1.06-original/byte_rcspn.c netqmail-1.06/byte_rcspn.c --- ../netqmail-1.06-original/byte_rcspn.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/byte_rcspn.c 2019-02-27 20:57:13.382025158 +0100 @@ -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 ../netqmail-1.06-original/caldate.h netqmail-1.06/caldate.h --- ../netqmail-1.06-original/caldate.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/caldate.h 2019-02-27 20:57:13.382025158 +0100 @@ -0,0 +1,24 @@ +/* + * $Log: caldate.h,v $ + * Revision 1.3 2004-10-09 23:20:20+05:30 Cprogrammer + * added function prototypes + * + * Revision 1.2 2004-06-18 22:57:44+05:30 Cprogrammer + * added RCS log + * + */ +#ifndef CALDATE_H +#define CALDATE_H + +struct caldate +{ + long year; + int month; + int day; +}; + +void caldate_frommjd(struct caldate *, long, int *, int *); +long caldate_mjd(struct caldate *); +unsigned int caldate_fmt(char *, struct caldate *); + +#endif diff -ruN ../netqmail-1.06-original/caltime.h netqmail-1.06/caltime.h --- ../netqmail-1.06-original/caltime.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/caltime.h 2019-02-27 20:57:13.382025158 +0100 @@ -0,0 +1,30 @@ +/* + * $Log: caltime.h,v $ + * Revision 1.3 2004-10-09 23:20:26+05:30 Cprogrammer + * added function prototypes + * + * Revision 1.2 2004-06-18 22:57:47+05:30 Cprogrammer + * added RCS log + * + */ +#ifndef CALTIME_H +#define CALTIME_H + +#include "caldate.h" +#include "tai.h" + +struct caltime +{ + struct caldate date; + int hour; + int minute; + int second; + long offset; +}; + +void caltime_tai(struct caltime *, struct tai *); +void caltime_utc(struct caltime *, struct tai *, int *, int *); + +unsigned int caltime_fmt(char *, struct caltime *); + +#endif diff -ruN ../netqmail-1.06-original/case_startb.c netqmail-1.06/case_startb.c --- ../netqmail-1.06-original/case_startb.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/case_startb.c 2019-02-27 20:57:13.382025158 +0100 @@ -0,0 +1,31 @@ +#include "case.h" + +int case_startb(s,len,t) +register char *s; +unsigned int len; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (!len) return 0; + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + if (x != y) return 0; + } +} + +/* DKIM 1.10 */ +void +getversion_case_startb_c() +{ + static char *x = "$Id: case_startb.c,v 1.3 2004-10-22 20:23:18+05:30 Cprogrammer Stab mbhangui $"; + + x++; +} +/* end DKIM 1.10 */ diff -ruN ../netqmail-1.06-original/channels.g netqmail-1.06/channels.g --- ../netqmail-1.06-original/channels.g 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/channels.g 2019-06-26 16:39:31.572826981 +0200 @@ -0,0 +1,18 @@ +#ifndef CHANNELS_H +#define CHANNELS_H + +/* total number of channels including canonical "local" and "remote" channels */ +#define CHANNELS NUMCHANNELS + +/* supplemental channels are all channels less the canonical "local" and "remote" channels */ +#define SUPPL_CHANNELS (CHANNELS - 2) + +/* Not longer than 80 bytes, must also change qmail-upq.sh */ +#define QDIR_BASENAME "suppl" + +/* start supplemental channel fd numbers here */ +#define CHANNEL_FD_OFFSET 10 + + +#endif + diff -ruN ../netqmail-1.06-original/chkspawn.c netqmail-1.06/chkspawn.c --- ../netqmail-1.06-original/chkspawn.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/chkspawn.c 2019-02-27 20:57:13.382025158 +0100 @@ -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 ../netqmail-1.06-original/chkuser.c netqmail-1.06/chkuser.c --- ../netqmail-1.06-original/chkuser.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/chkuser.c 2019-08-05 19:11:16.583873852 +0200 @@ -0,0 +1,1266 @@ + +/* + * + * 'chkuser.c' v.2.0.9 + * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + * + * Author: Antonio Nati tonix@interazioni.it + * All rights on this software and + * the identifying words chkusr and chkuser reserved by the author + * + * This software may be freely used, modified and distributed, + * but this lines must be kept in every original or derived version. + * Original author "Antonio Nati" and the web URL + * "http://www.interazioni.it/opensource" + * must be indicated in every related work or web page + * + */ + +#include + +/* required by vpopmail */ +#include + +#include +#include +#include + +#include "dns.h" +#include "env.h" +#include "ipme.h" +#include "now.h" +#include "str.h" +#include "open.h" +#include "subfd.h" +#include "substdio.h" +#include "stralloc.h" + +#include "vpopmail.h" +#include "vauth.h" +#include "vpopmail_config.h" + +#include "chkuser.h" +#include "chkuser_settings.h" + +#if defined _exit +#undef _exit +#endif + +extern void flush(); +extern void out (char *s); + +extern char *remotehost; +extern char *remoteip; +extern char *remoteinfo; +extern char *relayclient; +extern char *fakehelo; + +extern void die_nomem(); + +#define DIE_NOMEM() die_nomem() + +#if defined CHKUSER_DEBUG + +#if defined CHKUSER_DEBUG_STDERR + +#define CHKUSER_DBG(a) write (STDERR_FILENO, a, strlen (a)) +#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDERR_FILENO, str, strlen (str));} + +#else + +#define CHKUSER_DBG(a) write (STDOUT_FILENO, a, strlen (a)) +#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDOUT_FILENO, str, strlen (str));} + +#endif +#else + +#define CHKUSER_DBG(a) /* DBG dummy */ +#define CHKUSER_DBG_INT(a) /* DBG dummy */ + +#endif + +static int intrusion_threshold_reached = 0; +static int first_time_init_flag = 1; + +static int recipients = 0; +static int wrong_recipients = 0; + +static stralloc user = {0}; +static stralloc domain = {0}; +static stralloc domain_path = {0}; +static stralloc tmp_path = {0}; +static stralloc alias_path = {0}; + +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + static char *identify_remote; +#endif + +#if defined CHKUSER_ENABLE_EXTENSIONS +#define CHKUSER_ENABLE_USERS_EXTENSIONS +#endif + +#if defined CHKUSER_ENABLE_LISTS +#define CHKUSER_ENABLE_EZMLM_LISTS +#endif + +#if defined CHKUSER_EXTENSION_DASH +#define CHKUSER_USERS_DASH CHKUSER_EXTENSION_DASH +#endif + + +#if defined CHKUSER_ENABLE_VALIAS +#error "chkuser setting error: CHKUSER_ENABLE_VALIAS has been substituted by VALIAS (within vpopmail includes); you don't need anymore this define" +#endif + +#if defined CHKUSER_ENABLE_VAUTH_OPEN +#error "chkuser setting error: CHKUSER_ENABLE_VAUTH_OPEN has been substituted by CHKUSER_ENABLE_VAUTH_OPEN_CALL; edit chkuser_settings.h and change your settings" +#endif + +#if defined CHKUSER_ENABLE_VAUTH_OPEN_CALL + static int db_already_open = 0; +#endif + +#if defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE +#error "chkuser setting error: CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE are mutually esclusive. Edit your chkuser_settings.h and disable one of them" +#endif + + static int starting_value = 0; + +#if defined CHKUSER_STARTING_VARIABLE + static char *starting_string = 0; +#endif + +#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE + static int mustauth_value = 0; +#endif + + +#if defined CHKUSER_RCPT_LIMIT_VARIABLE + static char *maxrcpt_string = 0; + static int maxrcpt_limit = 0; + static int maxrcpt_limit_reached = 0; +#endif + +#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE + static char *maxwrongrcpt_string = 0; + static int maxwrongrcpt_limit = 0; + static int maxwrongrcpt_limit_reached = 0; +#endif + +#if defined CHKUSER_MBXQUOTA_VARIABLE + static char *maxmbxquota_string = 0; + static int maxmbxquota_limit = 0; +#endif + + static unsigned int sender_nocheck = 0; + +#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX +static stralloc sender_user = {0}; +static stralloc sender_domain = {0}; +#endif + +#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE +static unsigned int enable_doublebounce = 0; +#endif + +#if defined CHKUSER_ERROR_DELAY + + static int chkuser_delay_interval = CHKUSER_ERROR_DELAY * 1000; + +#define CHKUSER_DELAY() chkuser_delay() + +void chkuser_delay (void) { + + usleep (chkuser_delay_interval); + +#if defined CHKUSER_ERROR_DELAY_INCREASE + chkuser_delay_interval += CHKUSER_ERROR_DELAY_INCREASE * 1000; +#endif +} + +#if defined CHKUSER_RCPT_DELAY_ANYERROR +#define CHKUSER_RCPT_DELAY_ANY() chkuser_delay() +#else +#define CHKUSER_RCPT_DELAY_ANY() /* no delay for any error */ +#endif + +#if defined CHKUSER_SENDER_DELAY_ANYERROR +#define CHKUSER_SENDER_DELAY_ANY() chkuser_delay() +#else +#define CHKUSER_SENDER_DELAY_ANY() /* no delay for any error */ +#endif + + +#else +#define CHKUSER_DELAY() /* no delay */ +#define CHKUSER_RCPT_DELAY_ANY() /* no delay */ +#define CHKUSER_SENDER_DELAY_ANY() /* no delay */ +#endif + +#if defined CHKUSER_ENABLE_LOGGING + +static stralloc logstr = { 0 }; + +static void chkuser_commonlog (char *sender, char *rcpt, char *title, char *description) { + + substdio_puts (subfderr, "CHKUSER "); + substdio_puts (subfderr, title); + substdio_puts (subfderr, ": from <"); + substdio_puts (subfderr, sender); + substdio_puts (subfderr, "|remoteinfo/auth:" ); + if (remoteinfo) { + substdio_puts (subfderr, remoteinfo); + } + substdio_puts (subfderr, "|chkuser-identify:" ); +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + if (identify_remote) substdio_puts (subfderr, identify_remote); +#endif + substdio_puts (subfderr, "> remote rcpt <"); + substdio_puts (subfderr, rcpt); + substdio_puts (subfderr, "> : "); + substdio_puts (subfderr, description); + substdio_puts (subfderr, "\n"); + substdio_flush (subfderr); +} + +#else +#define chkuser_commonlog(a,b,c,d) /* no log */ +#endif + +#if defined CHKUSER_SENDER_FORMAT + +static int check_sender_address_format (stralloc *user, stralloc *domain) { + + int x; + + for (x = 0; x < (user->len -1); ++x) { + if ((!isalnum (user->s[x])) + +#if defined CHKUSER_ALLOW_SENDER_SRS + && (user->s[x] != '#') + && (user->s[x] != '+') +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_1 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_2 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_3 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_4 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_5 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_6 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_6) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_7 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_7) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_8 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_8) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_9 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_9) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_10 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_10) +#endif + && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) { + return 0; + } + } + +/* + * Be careful, this is a base check + * Minimum is x.xx + ending \0 + * Minimum characters needed are 5 + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) { + return 0; + } +#endif + +/* + * This is a safety check + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < 2) { + return 0; + } +#endif + + for (x = 0; x < (domain->len -1); ++x) { + if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) { + return 0; + } + } + + if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) { + return 0; + } + if (strstr (domain->s, "..") != NULL) { + return 0; + } + if (strncmp (domain->s, "xn--", 4) == 0) { + if (strstr (&domain->s[4], "--") != NULL) + return 0; + } else { + if (strstr (domain->s, "--") != NULL) + return 0; + } + if (strstr (domain->s, ".-") != NULL) { + return 0; + } + if (strstr (domain->s, "-.") != NULL) { + return 0; + } + if (strchr (domain->s, '.') == NULL) { + return 0; + } + + return 1; +} + +#endif + +#if defined CHKUSER_RCPT_FORMAT + +static int check_rcpt_address_format (stralloc *user, stralloc *domain) { + + int x; + + for (x = 0; x < (user->len -1); ++x) { + if ((!isalnum (user->s[x])) +#if defined CHKUSER_ALLOW_RCPT_SRS + && (user->s[x] != '#') + && (user->s[x] != '+') +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_1 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_1) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_2 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_2) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_3 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_3) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_4 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_4) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_5 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_5) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_6 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_6) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_7 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_7) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_8 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_8) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_9 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_9) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_10 + && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_10) +#endif + && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) { + return 0; + } + } + +/* + * Be careful, this is a base check + * Minimum is x.xx + ending \0 + * Minimum characters needed are 5 + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) { + return 0; + } +#endif + +/* + * This is a safety check + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < 2) { + return 0; + } +#endif + for (x = 0; x < (domain->len -1); ++x) { + if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) { + return 0; + } + } + + if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) { + return 0; + } + if (strstr (domain->s, "..") != NULL) { + return 0; + } + if (strncmp (domain->s, "xn--", 4) == 0) { + if (strstr (&domain->s[4], "--") != NULL) + return 0; + } else { + if (strstr (domain->s, "--") != NULL) + return 0; + } + if (strstr (domain->s, ".-") != NULL) { + return 0; + } + if (strstr (domain->s, "-.") != NULL) { + return 0; + } + if (strchr (domain->s, '.') == NULL) { + return 0; + } + + return 1; +} + +#endif + +#if defined CHKUSER_SENDER_MX || defined CHKUSER_RCPT_MX + +static unsigned long mx_random; +static ipalloc mx_ip = {0}; + +static int chkuser_mx_lookup (stralloc *domain) { + + int status; + + mx_random = now() + getpid(); + dns_init(0); + status = dns_mxip (&mx_ip, domain, mx_random); + + if (status == DNS_MEM) DIE_NOMEM(); + + return status; +} + +#endif + + +void chkuser_cleanup (int exit_value) { + +#if defined CHKUSER_DB_CLEANUP + vclose (); +#endif + _exit (exit_value); +} + +static void first_time_init (void) { + + starting_value = 0; + +#if defined CHKUSER_ALWAYS_ON + starting_value = 1; +#endif + +#if defined CHKUSER_STARTING_VARIABLE + starting_string = env_get (CHKUSER_STARTING_VARIABLE); + if (starting_string) { + if (strcasecmp(starting_string, "ALWAYS") == 0) { + starting_value = 1; + } else if (strcasecmp(starting_string, "DOMAIN") == 0) { + starting_value = 0; +/* + Edit by Roberto Puzzanghera + It seems like any other definition of starting_string ends up as "DOMAIN". + Instead, if starting_string is otherwise defined, we want to turn off chkuser, + just like if the starting_string is "NONE". + */ + } else { + starting_value = -1; + } + } else { + starting_string = ""; + starting_value = -1; + } +#endif + +#if defined CHKUSER_DISABLE_VARIABLE + if (env_get (CHKUSER_DISABLE_VARIABLE)) { + starting_value = -1; + } +#endif + +#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE + if (env_get (CHKUSER_EXTRA_MUSTAUTH_VARIABLE)) { + if (relayclient) { + mustauth_value = 0; + } else { + mustauth_value = 1; + } + } +#endif + + +#if defined CHKUSER_RCPT_LIMIT_VARIABLE + maxrcpt_string = env_get (CHKUSER_RCPT_LIMIT_VARIABLE); + if (maxrcpt_string) { + maxrcpt_limit = atoi (maxrcpt_string); + if (maxrcpt_limit < 1) { + maxrcpt_limit = 0; + } + } else { + maxrcpt_string = "";; + } +#endif + +#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE + maxwrongrcpt_string = env_get (CHKUSER_WRONGRCPT_LIMIT_VARIABLE); + if (maxwrongrcpt_string) { + maxwrongrcpt_limit = atoi (maxwrongrcpt_string); + if (maxwrongrcpt_limit < 1) { + maxwrongrcpt_limit = 0; + } + } else { + maxwrongrcpt_string = ""; + } +#endif + +#if defined CHKUSER_MBXQUOTA_VARIABLE + maxmbxquota_string = env_get (CHKUSER_MBXQUOTA_VARIABLE); + if (maxmbxquota_string) { + maxmbxquota_limit = atoi (maxmbxquota_string); + if (maxmbxquota_limit < 1) { + maxmbxquota_limit = 0; + } + } else { + maxmbxquota_string = ""; + } +#endif + +#if defined CHKUSER_SENDER_NOCHECK_VARIABLE + + if (env_get (CHKUSER_SENDER_NOCHECK_VARIABLE)) { + sender_nocheck = 1; + } else { + sender_nocheck = 0; + } +#endif + +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + + identify_remote = env_get (CHKUSER_IDENTIFY_REMOTE_VARIABLE); +#endif + + +#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE + + if (env_get (CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE)) { + enable_doublebounce = 1; + } else { + enable_doublebounce = 0; + } +#endif + + if (!stralloc_ready (&user, 300)) DIE_NOMEM(); + if (!stralloc_ready (&domain, 500)) DIE_NOMEM(); + if (!stralloc_ready (&domain_path, 1000)) DIE_NOMEM(); + if (!stralloc_ready (&tmp_path, 1000)) DIE_NOMEM(); + if (!stralloc_ready (&alias_path, 1000)) DIE_NOMEM(); + + first_time_init_flag = 0; + +} + +/* + * realrcpt () + * + * Returns: + * + * CHKUSER_OK = 1 = Ok, recipients does exists + * + * 0 = Not in rcpthosts + * + * < 0 various errors + * + * + * Parameters: + * stralloc *sender = sender address + * stralloc *rcpt = rcpt address to check + * + * +*/ + +static int realrcpt (stralloc *sender, stralloc *rcpt) +{ + int count; + int retstat = CHKUSER_KO; + struct vqpasswd *user_passwd = NULL; + int fd_file = -1; + int read_char; + int offset; + char read_buf[1024]; + +#if defined CHKUSER_ENABLE_UIDGID + uid_t eff_uid; + gid_t eff_gid; +#endif + +#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE + if (mustauth_value == 1) { + return CHKUSER_ERR_MUSTAUTH; + } +#endif + + + if (starting_value == -1) { + if (addrallowed()) { + return CHKUSER_OK_NOCHECKALL; + } else { + if (relayclient) { + return CHKUSER_RELAYING; + } + return CHKUSER_NORCPTHOSTS; + } + } + + if (intrusion_threshold_reached == 1) { + return CHKUSER_ERR_INTRUSION_THRESHOLD; + } + +#if defined CHKUSER_RCPT_LIMIT_VARIABLE + + ++recipients; + if ((maxrcpt_limit > 0) && (recipients >= maxrcpt_limit)) { + chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed rcpt"); + intrusion_threshold_reached = 1; + return CHKUSER_ERR_MAXRCPT; + } +#endif + +/* Search the '@' character */ + count = byte_rchr(rcpt->s,rcpt->len,'@'); + + if (count < rcpt->len) { + if (!stralloc_copyb (&user, rcpt->s, count)) DIE_NOMEM(); + if (!stralloc_copys (&domain, rcpt->s + count + 1)) DIE_NOMEM(); + } + else { + if (!stralloc_copys (&user, rcpt->s)) DIE_NOMEM(); + domain.len = 0; + } + if (!stralloc_0 (&user)) DIE_NOMEM(); + if (!stralloc_0 (&domain)) DIE_NOMEM(); + +#if defined CHKUSER_ENABLE_UIDGID + +/* qmail-smtpd is running now as (effective) qmaild:nofiles */ +/* Save the effective UID & GID (qmaild:nofiles) */ + eff_uid = geteuid (); + eff_gid = getegid (); + +/* Now set new effective UID & GID, getting it from real UID & GID (vpopmail:vchkpw) */ + setegid (getgid()); + seteuid (getuid()); + +/* qmail-smtpd is running now as effective vpopmail:vchkpw */ +#endif + + +/* + * + * Now let's start the test/setting suite + * + **/ + + switch (0) { + + case 0: +/* These are some preliminary settings */ + case_lowers (user.s); + case_lowers (domain.s); + + case 1: + + if (domain.len == 1) { +#if defined CHKUSER_DOMAIN_WANTED + retstat = CHKUSER_ERR_DOMAIN_MISSING; + break; +#else + if (!stralloc_copys (&domain, DEFAULT_DOMAIN)) DIE_NOMEM(); + if (!stralloc_0 (&domain)) DIE_NOMEM(); +#endif + } + + case 2: + +#if defined CHKUSER_RCPT_FORMAT + if (check_rcpt_address_format (&user, &domain) == 0) { + retstat = CHKUSER_ERR_RCPT_FORMAT; + break; + } +#endif + + case 3: + + if (!addrallowed()) { + +#if defined CHKUSER_RCPT_MX + switch (chkuser_mx_lookup(&domain)) { + + case DNS_HARD: + retstat = CHKUSER_ERR_RCPT_MX; + break; + + case DNS_SOFT: + retstat = CHKUSER_ERR_RCPT_MX_TMP; + break; + } + + if (retstat != CHKUSER_KO) { + break; + } +#endif + if (relayclient) { + retstat = CHKUSER_RELAYING; + break; + } + + retstat = CHKUSER_NORCPTHOSTS; + break; + } + + case 4: + +#if defined CHKUSER_ENABLE_VGET_REAL_DOMAIN +/* Check if domain is a real domain */ + + vget_real_domain(domain.s, domain.a); + + domain.len = strlen (domain.s) +1; + if (domain.len > (domain.a - 1)) DIE_NOMEM(); +#endif + +/* Let's get domain's real path */ + if (vget_assign(domain.s, domain_path.s, domain_path.a -1, NULL, NULL) == NULL) { + retstat = CHKUSER_OK; + break; + } + + domain_path.len = strlen (domain_path.s); + + case 5: + +/* Check if domain has bouncing enabled */ + + if (starting_value == 0) { + + if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM(); + +#if defined CHKUSER_SPECIFIC_BOUNCING + if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, CHKUSER_SPECIFIC_BOUNCING)) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + fd_file = open_read (tmp_path.s); + if (fd_file != -1) { + close (fd_file); + } else { + retstat = CHKUSER_OK_NOCHECKDOMAIN; + break; + } +#else + if (!stralloc_cats (&tmp_path, "/.qmail-default")) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + + read_char = 0; + fd_file = open_read (tmp_path.s); + if (fd_file != -1) { + read_char = read (fd_file, read_buf, sizeof(read_buf) - 1); + close (fd_file); + if (read_char < 0) read_char = 0; + } + read_buf[read_char] = 0; + + if ( strstr(read_buf, CHKUSER_BOUNCE_STRING) == NULL ) { + retstat = CHKUSER_OK_NOCHECKDOMAIN; + break; + } +#endif + } + + + case 6: + +#if defined CHKUSER_ENABLE_VAUTH_OPEN_CALL + if (db_already_open != 1) { + if (CHKUSER_VAUTH_OPEN_CALL () == 0) { + db_already_open == 1; + } else { + retstat = CHKUSER_ERR_AUTH_RESOURCE; + break; + } + } +#endif + + + case 7: +#if defined VALIAS +/* Check for aliases/forwards - valias*/ + + if (valias_select (user.s, domain.s) != NULL) { + retstat = CHKUSER_OK; + break; + } +#endif + + case 8: +#if defined CHKUSER_ENABLE_ALIAS +/* Check for aliases/forwards - .qmail.x files */ + + if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM(); + /* Change all '.' in ':' before continuing on aliases */ + for (count = 0; count < tmp_path.len; ++count) + if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':'; + + if (!stralloc_copy (&alias_path, &domain_path)) DIE_NOMEM(); + if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM(); + if (!stralloc_cats (&alias_path, tmp_path.s)) DIE_NOMEM(); + if (!stralloc_0 (&alias_path)) DIE_NOMEM(); + + fd_file = open_read (alias_path.s); + if (fd_file != -1) { + close (fd_file); + retstat = CHKUSER_OK; + break; + } +#endif + + case 9: + +#if defined CHKUSER_ENABLE_ALIAS_DEFAULT + + if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM(); + /* Change all '.' in ':' before continuing on aliases */ + for (count = 0; count < tmp_path.len; ++count) + if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':'; + + /* Search for the outer '-' character */ + for (offset = user.len - 1; offset > 0; --offset) { + if (*(user.s + offset) == CHKUSER_USERS_DASH) { + if (!stralloc_copy (&alias_path, &domain_path)) die_nomem(); + if (!stralloc_cats (&alias_path, "/.qmail-")) die_nomem(); + if (!stralloc_catb (&alias_path, user.s, offset)) die_nomem(); + if (!stralloc_cats (&alias_path, "-default")) die_nomem(); + if (!stralloc_0 (&alias_path)) die_nomem(); + + fd_file = open_read (alias_path.s); + if (fd_file != -1) { + close (fd_file); + retstat = CHKUSER_OK; + break; + } + } + } + if (retstat != CHKUSER_KO) { + break; + } + +#endif + + case 10: +#if defined CHKUSER_ENABLE_USERS +/* User control: check the existance of a real user */ + + user_passwd = vauth_getpw (user.s, domain.s); + +#if defined CHKUSER_ENABLE_USERS_EXTENSIONS + if (user_passwd == NULL) { + count = 0; + while ((count < (user.len -1)) && (user_passwd == NULL)) { + count += byte_chr(&user.s[count], user.len - count, CHKUSER_USERS_DASH); + if (count < user.len) { + if (!stralloc_copyb (&tmp_path, user.s, count)) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + user_passwd = vauth_getpw (tmp_path.s, domain.s); + ++count; + } + } + } + +#endif + if (user_passwd != NULL) { + + /* If user exists check if he has BOUNCE_MAIL flag set */ + + if (user_passwd->pw_gid & BOUNCE_MAIL) + retstat = CHKUSER_KO; + else { + retstat = CHKUSER_OK; +#if defined CHKUSER_MBXQUOTA_VARIABLE + if ((maxmbxquota_limit > 0) && (strcasecmp(user_passwd->pw_shell, "NOQUOTA") != 0)) { + if (!stralloc_copys (&tmp_path, user_passwd->pw_dir)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/Maildir")) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + + if (vmaildir_readquota(tmp_path.s,format_maildirquota(user_passwd->pw_shell)) + >= maxmbxquota_limit) { + retstat = CHKUSER_ERR_MBXFULL; + } + } +#endif + } + break; + } +#endif + + case 11: +#if defined CHKUSER_ENABLE_EZMLM_LISTS +/* Let's check for mailing lists */ + + /* Search for the outer CHKUSER_EZMLM_DASH character */ + for (offset = user.len - 2; offset > 0; --offset) { + if (*(user.s + offset) == CHKUSER_EZMLM_DASH) { + if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM(); + if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/editor")) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + fd_file = open_read (tmp_path.s); + if (fd_file != -1) { + close (fd_file); + retstat = CHKUSER_OK; + break; + } + } + } + if (retstat != CHKUSER_KO) { + break; + } +#endif + + case 12: +#if defined CHKUSER_ENABLE_MAILMAN_LISTS +/* Let's check for mailing lists */ + + /* Search for the outer CHKUSER_MAILMAN_DASH character */ + for (offset = user.len - 2; offset > 0; --offset) { + if (*(user.s + offset) == CHKUSER_MAILMAN_DASH) { + if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM(); + if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM(); + if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + fd_file = open_read (tmp_path.s); + read_char = 0; + if (fd_file != -1) { + read_char = read (fd_file, read_buf, sizeof(read_buf) - 1); + close (fd_file); + if (read_char < 0) read_char = 0; + } + read_buf[read_char] = 0; + + if ( strstr(read_buf, CHKUSER_MAILMAN_STRING) == NULL ) { + retstat = CHKUSER_OK; + break; + } + + } + } + if (retstat != CHKUSER_KO) { + break; + } +#endif + +/* + * Add this code if another case is following + case xx: + code .... + code .... + code .... + code .... + + if (xxxxxxxx) { + retstat != CHKUSER_KO) + break; + } +*/ + + default: + retstat = CHKUSER_KO; + + } /* end switch */ + +#if defined CHKUSER_ENABLE_UIDGID +/* Now switch back effective to saved UID & GID (qmaild:nofiles) */ + + setegid (eff_gid); + seteuid (eff_uid); + +/* qmail-smtpd is running again as (effective) qmaild:nofiles */ +#endif + + return retstat; + +} + + + +/* + * chkuser_realrcpt () + * + * Returns a simple status: + * + * CHKUSER_OK = 1 = Ok, recipients does exists + * + * CHKUSER_NORCPTHOSTS = Not in rcpthosts + * + * CHKUSER_KO = ERROR + * + * + * Parameters: + * stralloc *sender = sender address + * stralloc *rcpt = rcpt address to check + * + * +*/ + +int chkuser_realrcpt (stralloc *sender, stralloc *rcpt) { + +int retstat; + + if (first_time_init_flag) { + first_time_init (); + } + + + retstat = realrcpt (sender, rcpt); + + switch (retstat) { + + case CHKUSER_OK: +#if defined CHKUSER_LOG_VALID_RCPT + chkuser_commonlog (sender->s, rcpt->s, "accepted rcpt", "found existing recipient"); +#endif + return CHKUSER_OK; + break; + + case CHKUSER_OK_NOCHECKALL: +#if defined CHKUSER_LOG_VALID_RCPT + chkuser_commonlog (sender->s, rcpt->s, "accepted any rcpt", "accepted any recipient for any rcpt domain"); +#endif + return CHKUSER_OK; + break; + + case CHKUSER_OK_NOCHECKDOMAIN: +#if defined CHKUSER_LOG_VALID_RCPT + chkuser_commonlog (sender->s, rcpt->s, "accepted any rcpt", "accepted any recipient for this domain"); +#endif + return CHKUSER_OK; + break; + + case CHKUSER_RELAYING: +#if defined CHKUSER_LOG_VALID_RCPT + chkuser_commonlog (sender->s, rcpt->s, "relaying rcpt", "client allowed to relay"); +#endif + return CHKUSER_RELAYING; + break; + + case CHKUSER_NORCPTHOSTS: + chkuser_commonlog (sender->s, rcpt->s, "rejected relaying", "client not allowed to relay"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_NORELAY_STRING); + break; + + case CHKUSER_KO: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "not existing recipient"); + CHKUSER_DELAY(); + out(CHKUSER_NORCPT_STRING); + break; + + case CHKUSER_ERR_AUTH_RESOURCE: + chkuser_commonlog (sender->s, rcpt->s, "no auth resource", "no auth resource available"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RESOURCE_STRING); + break; + + case CHKUSER_ERR_MUSTAUTH: + chkuser_commonlog (sender->s, rcpt->s, "must auth", "sender not authenticated/authorized"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_MUSTAUTH_STRING); + break; + + case CHKUSER_ERR_MBXFULL: + chkuser_commonlog (sender->s, rcpt->s, "mbx overquota", "rcpt mailbox is overquota"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_MBXFULL_STRING); + break; + + case CHKUSER_ERR_MAXRCPT: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of recipients"); + CHKUSER_DELAY (); + out(CHKUSER_MAXRCPT_STRING); + break; + + case CHKUSER_ERR_MAXWRONGRCPT: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of invalid recipients"); + CHKUSER_DELAY (); + out(CHKUSER_MAXWRONGRCPT_STRING); + break; + + case CHKUSER_ERR_INTRUSION_THRESHOLD: + chkuser_commonlog (sender->s, rcpt->s, "rejected intrusion", "rcpt ignored, session over intrusion threshold"); + CHKUSER_DELAY (); + out(CHKUSER_INTRUSIONTHRESHOLD_STRING); + break; + + case CHKUSER_ERR_DOMAIN_MISSING: + CHKUSER_DELAY (); + out(CHKUSER_DOMAINMISSING_STRING); + break; + + case CHKUSER_ERR_RCPT_FORMAT: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt address format"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RCPTFORMAT_STRING); + break; + + case CHKUSER_ERR_RCPT_MX: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt MX domain"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RCPTMX_STRING); + break; + + case CHKUSER_ERR_RCPT_MX_TMP: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "temporary DNS problem"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RCPTMX_TMP_STRING); + break; + } + + + +#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE + if ((retstat == CHKUSER_KO) || (retstat == CHKUSER_ERR_DOMAIN_MISSING)) { + ++wrong_recipients; + if ((intrusion_threshold_reached == 0) && (maxwrongrcpt_limit > 0) && (wrong_recipients >= maxwrongrcpt_limit)) { + chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed invalid rcpt"); + intrusion_threshold_reached = 1; + } + } +#endif + + return retstat; +} + + +/* + * + * This routine checks for sender format and MX + * + */ + + +int chkuser_sender (stralloc *sender) { + +int count; + + if (first_time_init_flag) { + first_time_init (); + } + +#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE + if (mustauth_value == 1) { + out(CHKUSER_MUSTAUTH_STRING); +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "must auth", "sender not authenticated/authorized"); + CHKUSER_SENDER_DELAY_ANY(); +#endif + return CHKUSER_ERR_MUSTAUTH; + } +#endif + + if (sender->len <= 1) { +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted sender", "accepted null sender always"); +#endif + return CHKUSER_OK; + } + + if ((starting_value == -1) || (sender_nocheck == 1)) { +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted sender", "accepted any sender always"); +#endif + return CHKUSER_OK; + } + +#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE + if ((enable_doublebounce) && str_equal(sender->s,"#@[]")) { +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted doublebounce", "accepted qmail doublebounce #@[]"); +#endif + return CHKUSER_OK; + } +#endif + +#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX + count = byte_rchr(sender->s,sender->len,'@'); + if (count < sender->len) { + if (!stralloc_copyb (&sender_user, sender->s, count)) DIE_NOMEM(); + if (!stralloc_copys (&sender_domain, sender->s + count + 1)) DIE_NOMEM(); + } else { + if (!stralloc_copys (&sender_user, sender->s)) DIE_NOMEM(); + sender_domain.len = 0; + } + if (!stralloc_0 (&sender_user)) DIE_NOMEM(); + if (!stralloc_0 (&sender_domain)) DIE_NOMEM(); + +#if defined CHKUSER_SENDER_FORMAT + if (check_sender_address_format (&sender_user, &sender_domain) == 0) { + chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender address format"); + CHKUSER_SENDER_DELAY_ANY(); + out(CHKUSER_SENDERFORMAT_STRING); + return CHKUSER_ERR_SENDER_FORMAT; + } + +#endif + +#if defined CHKUSER_SENDER_MX + + switch (chkuser_mx_lookup(&sender_domain)) { + + case DNS_HARD: + CHKUSER_SENDER_DELAY_ANY(); + out(CHKUSER_SENDERMX_STRING); + chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender MX domain"); + return CHKUSER_ERR_SENDER_MX; + break; + + case DNS_SOFT: + CHKUSER_SENDER_DELAY_ANY(); + out(CHKUSER_SENDERMX_TMP_STRING); + chkuser_commonlog (sender->s, "", "rejected sender", "temporary DNS problem"); + return CHKUSER_ERR_SENDER_MX_TMP; + break; + } + +#endif +#endif + +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted sender", "sender accepted"); +#endif + + return CHKUSER_OK; + +} + + diff -ruN ../netqmail-1.06-original/chkuser.h netqmail-1.06/chkuser.h --- ../netqmail-1.06-original/chkuser.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/chkuser.h 2019-02-27 20:57:13.383025147 +0100 @@ -0,0 +1,55 @@ + +/* + * + * 'chkuser.h' v.2.0.9 + * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + * + * Author: Antonio Nati tonix@interazioni.it + * All rights on this software and + * the identifying words chkusr and chkuser reserved by the author + * + * This software may be freely used, modified and distributed, + * but this lines must be kept in every original or derived version. + * Original author "Antonio Nati" and the web URL + * "http://www.interazioni.it/opensource" + * must be indicated in every related work or web page + * + */ + +#define CHKUSER +#define CHKUSER_VERSION "2.0.9" +#define CHKUSER_VERSION_RL 2 +#define CHKUSER_VERSION_MJ 0 +#define CHKUSER_VERSION_MN 9 + +#define CHKUSER_OK_NOCHECKALL 11 +#define CHKUSER_OK_NOCHECKDOMAIN 10 +#define CHKUSER_OK 1 +#define CHKUSER_RELAYING 0 +#define CHKUSER_KO -1 +#define CHKUSER_NORCPTHOSTS -10 +#define CHKUSER_ERR_MUSTAUTH -15 +#define CHKUSER_ERR_AUTH_RESOURCE -20 +#define CHKUSER_ERR_MBXFULL -30 +#define CHKUSER_ERR_MAXRCPT -40 +#define CHKUSER_ERR_MAXWRONGRCPT -50 +#define CHKUSER_ERR_DOMAIN_MISSING -60 +#define CHKUSER_ERR_RCPT_FORMAT -70 +#define CHKUSER_ERR_RCPT_MX -75 +#define CHKUSER_ERR_RCPT_MX_TMP -76 +#define CHKUSER_ERR_SENDER_FORMAT -80 +#define CHKUSER_ERR_SENDER_MX -85 +#define CHKUSER_ERR_SENDER_MX_TMP -86 +#define CHKUSER_ERR_INTRUSION_THRESHOLD -90 + + +void chkuser_cleanup (int exit_value); +int chkuser_realrcpt (stralloc *sender, stralloc *rcpt); +int chkuser_sender (stralloc *sender); + +#ifdef TLS_H +#undef _exit +#define _exit(value) { if (ssl) ssl_free(ssl); chkuser_cleanup(value); } +#else +#define _exit(value) chkuser_cleanup(value); +#endif diff -ruN ../netqmail-1.06-original/chkuser_settings.h netqmail-1.06/chkuser_settings.h --- ../netqmail-1.06-original/chkuser_settings.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/chkuser_settings.h 2020-06-16 22:27:56.782600304 +0200 @@ -0,0 +1,468 @@ +/* + * + * 'chkuser_settings.h' v.2.0.9 + * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + * + * Author: Antonio Nati tonix@interazioni.it + * All rights on this software and + * the identifying words chkusr and chkuser reserved by the author + * + * This software may be freely used, modified and distributed, + * but this lines must be kept in every original or derived version. + * Original author "Antonio Nati" and the web URL + * "http://www.interazioni.it/opensource" + * must be indicated in every related work or web page + * + */ + +/* + * the following line enables debugging of chkuser + */ +#define CHKUSER_DEBUG + +/* + * The following line moves DEBUG output from STDOUT (default) to STDERR + * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log + */ +#define CHKUSER_DEBUG_STDERR + +/* + * Uncomment the following define if you want chkuser ALWAYS enabled. + * If uncommented, it will check for rcpt existance despite any .qmail-default + * setting. + * So, unsomments this if you are aware that ALL rcpt in all domains will be + * ALWAYS checked. + */ +/* #define CHKUSER_ALWAYS_ON */ + +/* + * The following defines which virtual manager is used. + * Up to know, only vpopmail, but versions with pure qmail are in the mind. + */ +#define CHKUSER_VPOPMAIL + +/* + * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting + * VALUE HERE DEFINED is the name of the variable + * Values admitted inside the variable: NONE | ALWAYS | DOMAIN + * NONE = chkuser will not work + * ALWAYS = chkuser will work always + * DOMAIN = chkuser will work depending by single domain settings + * CHKUSER_STARTING_VARIABLE cannot be defined together with CHKUSER_ALWAYS_ON + * if CHKUSER_STARTING_VARIABLE is defined, and no variable or no value is set, then chkuser is disabled + */ +#define CHKUSER_STARTING_VARIABLE "CHKUSER_START" + +/* + * Uncomment this to enable uid/gid changing + * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS) + */ +/* #define CHKUSER_ENABLE_UIDGID */ + +/* + * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses + */ +#define CHKUSER_DOMAIN_WANTED + +/* + * Uncomment this to check for vpopmail users + */ +#define CHKUSER_ENABLE_USERS + +/* + * Uncomment this to check for alias + */ +#define CHKUSER_ENABLE_ALIAS + +/* + * The following #define set the character used for lists extensions + * be careful: this is a single char '-' definition, not a "string" + */ +#define CHKUSER_EZMLM_DASH '-' + +/* + * Uncomment this to set an alternative way to check for bouncing enabling; + * with this option enabled, the file here defined + * will be searched, inside the domain dir, in order to check if bouncing is enabled + * The content of this file is not important, just it's existence is enough + */ +/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */ + +/* + * This is the string to look for inside .qmail-default + * Be careful, chkuser looks within the first 1023 characters of .qmail-default for + * this string (despite the line containing the string is working or commented). + */ +#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" + + +/* + * Uncomment to enable logging of rejected recipients and variuos limits reached + */ +#define CHKUSER_ENABLE_LOGGING + +/* + * Uncomment to enable logging of "good" rcpts + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +#define CHKUSER_LOG_VALID_RCPT + +/* + * Uncomment to enable usage of a variable escluding any check on the sender. + * The variable should be set in tcp.smtp for clients, with static IP, whose mailer + * is composing bad sender addresses + * Defining it as "RELAYCLIENT" will avoid sender checking for authenticated/authorized users. + * Senders will be logged anyway if CHKUSER_LOG_VALID_SENDER is defined. + */ +#define CHKUSER_SENDER_NOCHECK_VARIABLE "RELAYCLIENT" + +/* + * Uncomment to enable usage of "#" and "+" characters within sender address + * This is used by SRS (Sender Rewriting Scheme) products + */ +#define CHKUSER_ALLOW_SENDER_SRS + +/* + * The following #define sets the minimum length of a domain: + * as far as I know, "k.st" is the shortest domain, so 4 characters is the + * minimum length. + * This value is used to check formally a domain name validity. + * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done. + * If you comment this define, no check on length is done. + */ +#define CHKUSER_MIN_DOMAIN_LEN 4 + +/* + * Uncomment to enable logging of "good" senders + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +#define CHKUSER_LOG_VALID_SENDER + +/* + * Uncomment to define a variable which contains the max recipients number + * this will return always error if total recipients exceed this limit. + * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, + * makes chkuser rejecting everything else + */ +#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" + +/* + * Uncomment to define a variable which contains the max unknown recipients number + * this will return always error if not existing recipients exceed this limit. + * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, + * makes chkuser rejecting everything else + */ +#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" + +/* + * Uncomment to define the variable containing the percent to check for. + * Remember to define externally (i.e. in tcp.smtp) the environment variable containing + * the limit percent. + * If the variable is not defined, or it is <= 0, quota checking is not performed. + */ +#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" + +/* + * Delay to wait for each not existing recipient + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY 1000 + +/* + * Uncomment to consider rcpt errors on address format and MX as intrusive + * + */ +#define CHKUSER_RCPT_DELAY_ANYERROR + +/* + * Uncomment to consider sender errors on address format and MX as intrusive + * + */ +#define CHKUSER_SENDER_DELAY_ANYERROR + + +/*************************************************** + * + * new/modified defines in/from 2.0.6 + * + **************************************************/ + +/* + * Before version 5.3.25, vpopmail used the function vget_real_domain() + * to get the real name of a domain (useful if rcpt domain is aliasing + * another domain). + * From version 5.3.25, this call is not available and has been + * substituted by other calls. + * + * must be enabled if vpopmail version< 5.3.5 + * must be disabled if vpopmail version => 5.3.5 * + */ +/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */ + +/*************************************************** + * + * new/modified defines in/from 2.0.7 + * + **************************************************/ + +/* + * Uncomment next define to accept recipients for + * aliases that have a -default extension + */ +#define CHKUSER_ENABLE_ALIAS_DEFAULT + + +/* + * Uncomment to enable usage of "#" and "+" characters within rcpt address + * This is used by SRS (Sender Rewriting Scheme) products + */ +#define CHKUSER_ALLOW_RCPT_SRS + +/* + * This define has been eliminated and its usage will generate an error. + * Turning it ON or OFF has no effect, as we consider the existence + * of #define VALIAS inside ~vpopmail/include/vpopmail_config.h + */ +/* #define CHKUSER_ENABLE_VALIAS */ + +/* + * Uncomment this to enable user extension on names (i.e. TMDA) + * (for mailing lists this is done without checking this define) + * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS + */ +/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */ + +/* + * Enables checking for EZMLM lists + * this define substitutes #define CHKUSER_ENABLE_LISTS + * + */ +#define CHKUSER_ENABLE_EZMLM_LISTS + +/* + * Help identifying remote authorized IPs giving them a descriptive name + * Can be put in tcp.smtp, and will be displayed inside chkuser log + */ +#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" + +/* + * The following #define set the character used for users extensions + * be careful: this is a single char '-' definition, not a "string" + * this define substitutes #define CHKUSER_EXTENSION_DASH + * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined + */ +#define CHKUSER_USERS_DASH '-' + +/* + * Enables checking for mailman lists + * + */ +/* #define CHKUSER_ENABLE_MAILMAN_LISTS */ + +/* + * Identifies the pattern string to be searched within mailman aliases + * + */ +#define CHKUSER_MAILMAN_STRING "mailman" + +/* + * The following #define set the character used for mailman lists extensions + * be careful: this is a single char '-' definition, not a "string" + */ +#define CHKUSER_MAILMAN_DASH '-' + + +/* + * Enables final clean-up routine of chkuser + * This routine cleans open DB connections used for checking users and valiases + */ +#define CHKUSER_DB_CLEANUP + +/*************************************************** + * + * new/modified defines in/from 2.0.8 + * + **************************************************/ + +/* + * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC + * compatibility, and makes harder to handle e-mail receipts. + * Please comment or delete them from your chkuser_settings.h. + */ +/* #define CHKUSER_ACCEPT_NULL_SENDER */ +/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ + +/* + * Uncomment to enable checking of user and domain format for rcpt addresses + * user = [a-z0-9_-] + * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." + */ +#define CHKUSER_RCPT_FORMAT + +/* + * Uncomment to enable checking of domain MX for rcpt addresses + * It works on any rcpt address domain that is not inside rcpthosts + */ +#define CHKUSER_RCPT_MX + +/* + * Uncomment to enable checking of user and domain format for sender address + * user = [a-z0-9_-] + * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." + */ +#define CHKUSER_SENDER_FORMAT + +/* + * Uncomment to enable checking of domain MX for sender address + * it works on the first rcpt address, despite of any domain setting on chkuser + */ +#define CHKUSER_SENDER_MX + +/* + * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY_INCREASE 300 + +/*************************************************** + * + * new/modified defines in/from 2.0.9 + * + **************************************************/ + +/* + * A new class of defines is introduced + * CHKUSER_EXTRA_xxxxx + * + * These defines will be used for features/behaviours that may work despite of other CHKUSER enable/disable settings + * + */ + +/* + * If you want to accept only authenticated/authorized users you MUST enable this define and set the related variable. + * + * if this define is uncommented and the variable is set (to whatever value) then RELAYCLIENT must be set + * otherwise any message will be rejected giving "not authorized" error. + * + */ +/* #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH" */ + + +/* + * This is to check DB availability + * It avoids bouncing messages with wrong codes if MySQL/LDAP/PostGRES/etc are down or not reachable + * + * If you are using MySQL in normal installation use #define CHKUSER_VAUTH_OPEN_CALL vauth_open_update + * If you are using MySQL with separate servers for read and write use #define CHKUSER_VAUTH_OPEN_CALL vauth_open + * If you are using other DB, check the most appropriate function for your DB within dedicated vpopmail module + * + * This define substitutes CHKUSER_ENABLE_VAUTH_OPEN + */ + +/* #define CHKUSER_VAUTH_OPEN_CALL vauth_open */ +#define CHKUSER_VAUTH_OPEN_CALL vauth_open_update + +/* + * Variable to be set in order to disable chkuser + * You may set it to any value you like. If it exists chkuser will be disabled. + * Setting it to RELAYCLIENT helps disabling chkuser when sender is a known/authenticated mail client + * This is useful because Outlook/Eudora and other clients are not able to handle a KO when multiple recipients + * are present in the message. They should always relay to a SMTP service accepting all. + * + * Recipients will be logged anyway if CHKUSER_LOG_VALID_RCPT is defined. + * + * Important changes from 2.0.9 + * CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together and in such a case a fatal error is displayed + * (in the previous versions CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition) + * + * CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or CHKUSER_STARTING_VARIABLE is evaluated, so + * CHKUSER_ALWAYS_ON or CHKUSER_STARTING_VARIABLE can set the general behaviour, while CHKUSER_DISABLE_VARIABLE + * should be invoked to handle exceptions. + * + */ +#define CHKUSER_DISABLE_VARIABLE "RELAYCLIENT" + + +/* + * Error strings (SMTP error answers) + * If you don't like these definitions you can change them here + * + */ +#define CHKUSER_NORCPT_STRING "550 5.1.1 sorry, no mailbox here by that name (chkuser)\r\n" +#define CHKUSER_RESOURCE_STRING "451 4.3.0 system temporary unavailable, try again later (chkuser)\r\n" +#define CHKUSER_MBXFULL_STRING "552 5.2.2 sorry, recipient mailbox is full (chkuser)\r\n" +#define CHKUSER_MAXRCPT_STRING "550 5.5.3 sorry, reached maximum number of recipients allowed in one session (chkuser)\r\n" +#define CHKUSER_MAXWRONGRCPT_STRING "550 5.5.3 sorry, you are violating our security policies (chkuser)\r\n" +#define CHKUSER_DOMAINMISSING_STRING "550 5.1.2 sorry, you must specify a domain (chkuser)\r\n" +#define CHKUSER_RCPTFORMAT_STRING "553 5.1.3 sorry, mailbox syntax not allowed (chkuser)\r\n" +#define CHKUSER_RCPTMX_STRING "550 5.1.2 sorry, can't find a valid MX for rcpt domain (chkuser)\r\n" +#define CHKUSER_SENDERFORMAT_STRING "553 5.1.7 sorry, mailbox syntax not allowed (chkuser)\r\n" +#define CHKUSER_SENDERMX_STRING "550 5.1.8 sorry, can't find a valid MX for sender domain (chkuser)\r\n" +#define CHKUSER_INTRUSIONTHRESHOLD_STRING "550 5.7.1 sorry, you are violating our security policies (chkuser)\r\n" +#define CHKUSER_NORELAY_STRING "553 5.7.1 sorry, that domain isn't in my list of allowed rcpthosts (chkuser)\r\n" + +#define CHKUSER_RCPTMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n" +#define CHKUSER_SENDERMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n" + +#define CHKUSER_MUSTAUTH_STRING "530 5.7.0 Authentication required (chkuser)\r\n" + +/* + * No more used defines + * Following defines are eliminated since 2.0.9 + * They will make compilation errors and must be deleted/commented + * + * #define CHKUSER_ENABLE_VAUTH_OPEN -> Substituted by CHKUSER_VAUTH_OPEN_CALL + */ + + +/* + * If you need more additional characters to be accepted within sender address + * uncomment one of the following #define and edit the character value. + * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the + * wanted char. + * + * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_SENDER_SRS + * + */ +#define CHKUSER_ALLOW_SENDER_CHAR_1 "'" +#define CHKUSER_ALLOW_SENDER_CHAR_2 '=' +/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_6 '^' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_7 '~' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_8 '&' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_9 '#' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_10 '=' */ /* available for other characters */ + + +/* + * If you need more additional characters to be accepted within rcpt address + * uncomment one of the following #define and edit the character value. + * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the + * wanted char. + * + * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_RCPT_SRS + * + */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_3 '£' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_6 '^' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_7 '~' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_8 '&' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_9 '#' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_10 '=' */ /* available for other characters */ + + +/* + * This define tells chkuser which variable must be set to accept a <#@[]> sender + * This kind of sender is usually generated from qmail when there is a doublebounce + * and all the job is done within the same system. + * You may need to accept double bounces from outside when you are migrating servers and + * doublebounces are forwarded between systems + */ +#define CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE "CHKUSER_DOUBLEBOUNCE" diff -ruN ../netqmail-1.06-original/condredirect.c netqmail-1.06/condredirect.c --- ../netqmail-1.06-original/condredirect.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/condredirect.c 2019-02-27 20:57:13.384025136 +0100 @@ -10,6 +10,8 @@ #include "strerr.h" #include "substdio.h" #include "fmt.h" +#include "stralloc.h" +#include "srs.h" #define FATAL "condredirect: fatal: " @@ -68,6 +70,16 @@ dtline = env_get("DTLINE"); if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set"); + if (str_len(sender)) { + switch(srsforward(sender)) { + case -3: strerr_die2x(100,FATAL,srs_error.s); break; + case -2: strerr_die2x(111,FATAL,"out of memory"); break; + case -1: strerr_die2x(111,FATAL,"unable to read controls"); break; + case 0: break; // nothing + case 1: sender = srs_result.s; break; + } + } + if (qmail_open(&qqt) == -1) strerr_die2sys(111,FATAL,"unable to fork: "); qmail_puts(&qqt,dtline); diff -ruN ../netqmail-1.06-original/conf-cc netqmail-1.06/conf-cc --- ../netqmail-1.06-original/conf-cc 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/conf-cc 2020-01-10 21:52:13.080721081 +0100 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -g -DEXTERNAL_TODO -DTLS=20200107 -I/usr/local/ssl/include -I/home/vpopmail/include This will be used to compile .c files. diff -ruN ../netqmail-1.06-original/conf-channels netqmail-1.06/conf-channels --- ../netqmail-1.06-original/conf-channels 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/conf-channels 2019-06-26 19:12:46.033685227 +0200 @@ -0,0 +1,4 @@ +2 + +Total number of channels (queues) available for delivery. Must be at +least 2, and anything above 2 are considered supplemental channels. diff -ruN ../netqmail-1.06-original/conf-domainkeys netqmail-1.06/conf-domainkeys --- ../netqmail-1.06-original/conf-domainkeys 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/conf-domainkeys 2019-02-27 20:57:13.384025136 +0100 @@ -0,0 +1 @@ +-DDOMAIN_KEYS diff -ruN ../netqmail-1.06-original/conf-ld netqmail-1.06/conf-ld --- ../netqmail-1.06-original/conf-ld 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/conf-ld 2019-02-27 20:57:13.384025136 +0100 @@ -1,3 +1,3 @@ -cc -s +cc -g This will be used to link .o files into an executable. diff -ruN ../netqmail-1.06-original/conf-policy netqmail-1.06/conf-policy --- ../netqmail-1.06-original/conf-policy 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/conf-policy 2019-02-27 20:57:13.384025136 +0100 @@ -0,0 +1,17 @@ +-DPOLICY_FILENAME="/var/qmail/control/policy" -DPOLICY_DEALLOCATE -DPOLICY_ENFORCE_AUTHENTICATION + +POLICY_FILENAME + Sets where the policy file is located + +POLICY_ENFORCE_AUTHENTICATION + Require that senders who use a local name for envelope + authenticate. This is the recommended setting. + +POLICY_DEALLOCATE + For every MAIL FROM, RCPT TO combination, re-read all + policy information. This makes policies more dynamic, + and stops remote users from causing lots of memory usage, + however, it also increases disk i/o, and slows down + policy enforcement. If this is not defined, domain policies + will stay in memory until the SMTP session is ended. + diff -ruN ../netqmail-1.06-original/conf-spawn netqmail-1.06/conf-spawn --- ../netqmail-1.06-original/conf-spawn 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/conf-spawn 2019-02-27 20:57:13.384025136 +0100 @@ -1,4 +1,4 @@ -120 +1000 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 ../netqmail-1.06-original/config.h netqmail-1.06/config.h --- ../netqmail-1.06-original/config.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/config.h 2019-02-27 20:57:13.384025136 +0100 @@ -0,0 +1,10 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ + +/* Define to 1 if you have the header file. */ + +/* HAVE_EVP_SHA256 */ +#define HAVE_EVP_SHA256 1 + diff -ruN ../netqmail-1.06-original/control.c netqmail-1.06/control.c --- ../netqmail-1.06-original/control.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/control.c 2019-02-27 20:57:13.384025136 +0100 @@ -85,6 +85,82 @@ return 1; } +int +control_readulong(i, fn) + unsigned long *i; + char *fn; +{ + unsigned long u; + + switch (control_readline(&line, fn)) + { + case 0: + return 0; + case -1: + return -1; + } + if (!stralloc_0(&line)) + return -1; + if (!scan_ulong(line.s, &u)) + return 0; + *i = u; + return 1; +} + +/* + * read entire file in variable sa + * without any interpretation (e.g. comments) + * To be used in case a file contains '#' character + * in the first column (which control_readfile() will + * skip + */ +int +control_readnativefile(sa, fn, mode) + stralloc *sa; + char *fn; + int mode; +{ + substdio ss; + int fd, match; + + if (!stralloc_copys(sa, "")) + return -1; + if ((fd = open_read(fn)) == -1) + { + if (errno == error_noent) + return(0); + return -1; + } + substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf)); + for (;;) + { + if (getln(&ss, &line, &match, '\n') == -1) + break; + if (!match && !line.len) + { + close(fd); + return 1; + } + if (mode) /* for qmail-dk */ + { + striptrailingwhitespace(&line); + if (!stralloc_0(&line)) + break; + if (line.s[0] && !stralloc_cat(sa, &line)) + break; + } else + if (!stralloc_cat(sa, &line)) + break; + if (!match) + { + close(fd); + return 1; + } + } + close(fd); + return -1; +} + int control_readfile(sa,fn,flagme) stralloc *sa; char *fn; diff -ruN ../netqmail-1.06-original/control.h netqmail-1.06/control.h --- ../netqmail-1.06-original/control.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/control.h 2019-02-27 20:57:13.384025136 +0100 @@ -3,8 +3,10 @@ extern int control_init(); extern int control_readline(); +extern int control_readulong(); extern int control_rldef(); extern int control_readint(); +extern int control_readnativefile(); extern int control_readfile(); #endif diff -ruN ../netqmail-1.06-original/date822fmt.c netqmail-1.06/date822fmt.c --- ../netqmail-1.06-original/date822fmt.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/date822fmt.c 2019-02-27 20:57:13.385025125 +0100 @@ -1,3 +1,4 @@ +#include #include "datetime.h" #include "fmt.h" #include "date822fmt.h" @@ -12,18 +13,51 @@ { unsigned int i; unsigned int len; + time_t now; + datetime_sec utc; + datetime_sec local; + struct tm *tm; + struct datetime new_dt; + int minutes; + + utc = datetime_untai(dt); + now = (time_t)utc; + tm = localtime(&now); + new_dt.year = tm->tm_year; + new_dt.mon = tm->tm_mon; + new_dt.mday = tm->tm_mday; + new_dt.hour = tm->tm_hour; + new_dt.min = tm->tm_min; + new_dt.sec = tm->tm_sec; + local = datetime_untai(&new_dt); + len = 0; - i = fmt_uint(s,dt->mday); len += i; if (s) s += i; + i = fmt_uint(s,new_dt.mday); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i; + i = fmt_str(s,montab[new_dt.mon]); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i; + i = fmt_uint(s,new_dt.year + 1900); len += i; if (s) s += i; i = fmt_str(s," "); len += i; if (s) s += i; - i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.hour,2); len += i; if (s) s += i; i = fmt_str(s,":"); len += i; if (s) s += i; - i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.min,2); len += i; if (s) s += i; i = fmt_str(s,":"); len += i; if (s) s += i; - i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i; - i = fmt_str(s," -0000\n"); len += i; if (s) s += i; + i = fmt_uint0(s,new_dt.sec,2); len += i; if (s) s += i; + + if (local < utc) { + minutes = (utc - local + 30) / 60; + i = fmt_str(s," -"); len += i; if (s) s += i; + i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i; + i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i; + } + else { + minutes = (local - utc + 30) / 60; + i = fmt_str(s," +"); len += i; if (s) s += i; + i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i; + i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i; + } + + i = fmt_str(s,"\n"); len += i; if (s) s += i; + return len; } diff -ruN ../netqmail-1.06-original/dk-filter.9 netqmail-1.06/dk-filter.9 --- ../netqmail-1.06-original/dk-filter.9 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dk-filter.9 2020-04-10 11:08:43.313802048 +0200 @@ -0,0 +1,98 @@ +.TH dk-filter 8 +.SH NAME +dk-filter \- sign/verify using DK/DKIM (SSP/ADSP optionally) and deliver a mail message for delivery +.SH SYNOPSIS +.B FILTERARGS=QMAILHOME/bin/dk-filter +.SH DESCRIPTION +.B dk-filter +is a qfilter which can be set as a filter for +.BR spawn-filter(8) . +The filter can be set either as +.B FILTERARGS +or in the control file +.BR filterargs . + +.B dk-filter +supports DK/DKIM signing and verification and can optionally use +.B Sender Signing Practice (SSP) +or +.B Author Domain Signing Practice. +It uses the libdkim and OpenSSL libraries. To sign a message, set the +.B DKIMSIGN +environment variable (for DKIM signing) or +.B DKSIGN +environment variable (for DK signing) to the pathname of the private key that will be +used to sign the message. If there is a % character in the environment +variable, it is removed and replaced by the domain name in the From: header. +If, after substituting the %, that file does not exist, QMAILHOME/control/domainkeys/%/default +will be used as the key. If again, after substituting the % sign, the file does not exist, +QMAILHOME/control/domainkeys/default will be used as the key. +After all substitutions, if the key file does not exist, the message will not be signed. +If there is no % and the file does not exist, the message will be rejected with error 35. +The default private key QMAILHOME/control/domainkeys/default can be overriden by the +\fBDKIM_DEFAULT_KEY\fR environment variable. + +The selector (s=) will be taken from the basename of the file. +The private key should be created by +.BR dknewkey(8) . + +You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS + b Length of Advice + c simple, nofws + s + h show headers included + r Skip Duplicate Headers +.EX + DKSIGNOPTIONS="-h -r -c nofws" + sets the h= tag, skips duplicate headers and sets nofws canonicalization +.EE + +You can set various DKIM options in getopt style, by setting the environment variable DKIMSIGNOPTIONS + + b 1 - allman, 2 - ietf or 3 - both + c r for relaxed [DEFAULT], s - simple, + t relaxed/simple, u - simple/relaxed + l include body length tag + q include query method tag; + t include a timestamp tag + h + i the identity, if not provided it will not be included + x the expire time in seconds since epoch + ( DEFAULT = current time + 604800) + if set to - then it will not be included + z 1 for sha1, 2 for sha256, 3 for both + s + y + +.EX + DKIMSIGNOPTIONS="-b 1 -c r -q" + sets allman standard, with relaxed canonicalization and include query method tag +.EE + +when signing \fBdk-filter\fR uses the domain found in the Return-Path, Sender, From headers to set +the domain tag. If not it uses the value of \fBDKIMDOMAIN\fR environment +variable. \fBDKIMDOMAIN\fR can be set to an email address or a domain (without the at sign). + +To verify a message, set the \fBDKIMVERIFY\fR or \fBDKVERIFY\fR environment variables. +\fBdk-filter\fR always inserts the \fBDKIM-Status\fR or \fBDomainKey-Status\fR header, so +that messages can be rejected later at delivery time, or in the mail reader. In that case +you may set \fBDKIMVERIFY\fR or \fBDKVERIFY\fR to an empty string. The exit code of \fBdk-filter\fR +can be fine tuned by setting \fBDKIMVERIFY\fR environment variable. See \fBdkim(8)\fR for a detailed +description on setting the \fBDKIMVERIFY\fR environment variable. + +\fBdk-filter\fR does not use any signing practice by default. You can override this by setting +the \fBSIGN_PRACTICE\fR to ssp or adsp (lowercase). + +If neither of these environment variables (\fBDKIMSIGN\fR, \fBDKSIGN\fR, \fBDKIMVERIFY\fR, \fBDKVERIFY\fR) are defined, \fBdk-filter\fR +will do signing by default. + +You can set environment variable \fBNODK\fR to disable domainkeys and \fBNODKIM\fR to disable \fBDKIM\fR. + +.SH "EXIT CODES" +0 for success, non-zero failure + +.SH "SEE ALSO" +dknewkey(8), +dktest(8), +dkim(8), +spawn-filter(8) diff -ruN ../netqmail-1.06-original/dk-filter.sh netqmail-1.06/dk-filter.sh --- ../netqmail-1.06-original/dk-filter.sh 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dk-filter.sh 2020-07-29 15:59:13.351023490 +0200 @@ -0,0 +1,367 @@ +# +# $Log: dk-filter.sh,v $ +# Revision 1.22 2019-06-24 23:19:57+05:30 Cprogrammer +# added code for -d option in DKIMSIGNOPTIONS +# +# Revision 1.21 2019-01-14 00:10:00+05:30 Cprogrammer +# added -S, -f option to verify signatures with unsigned subject, unsigned from +# +# Revision 1.20 2017-03-09 16:38:15+05:30 Cprogrammer +# FHS changes +# +# Revision 1.19 2016-05-17 23:11:42+05:30 Cprogrammer +# fix for configurable control directory +# +# Revision 1.18 2014-03-12 08:50:48+05:30 Cprogrammer +# bug - fixed signing when env variables DKSIGN or DKIMSIGN were set +# +# Revision 1.17 2013-09-03 23:04:30+05:30 Cprogrammer +# set signing as default if both DKSIGN and DKIMSIGN are not defined +# +# Revision 1.16 2013-08-17 15:59:21+05:30 Cprogrammer +# do not treat duplicate DomainKey-Signature as an error +# +# Revision 1.15 2013-08-17 15:02:06+05:30 Cprogrammer +# fixed syntax errors and private key lookup +# +# Revision 1.14 2011-02-10 22:47:01+05:30 Cprogrammer +# fixed exit code of dk-filter when doing verification +# +# Revision 1.13 2011-02-08 22:02:29+05:30 Cprogrammer +# use sender domain when replacing '%' in private key +# +# Revision 1.12 2010-05-04 08:37:42+05:30 Cprogrammer +# do DK signing before DKIM signing to prevent DK_SYNTAX error +# +# Revision 1.11 2009-12-10 19:25:13+05:30 Cprogrammer +# added RCS id +# +# Revision 1.10 2009-12-10 16:41:14+05:30 Cprogrammer +# continue of message gives DK_SYNTAX_ERR +# +# Revision 1.9 2009-05-04 10:30:32+05:30 Cprogrammer +# fixed argument expected error +# +# Revision 1.8 2009-04-21 20:42:44+05:30 Cprogrammer +# added check for dktest, dkim executables +# +# Revision 1.7 2009-04-20 10:06:58+05:30 Cprogrammer +# added DKSIGNOPTS +# +# Revision 1.6 2009-04-19 13:38:24+05:30 Cprogrammer +# added full set of dkim options +# replaced indimail/bin/echo with echo 1>&2 +# +# Revision 1.5 2009-04-06 16:37:50+05:30 Cprogrammer +# added SIGN_PRACTICE +# use ietf standard insted of allman so that Yahoo verification does not fail +# +# Revision 1.4 2009-04-03 14:39:00+05:30 Cprogrammer +# added return status +# +# Revision 1.3 2009-04-03 08:55:29+05:30 Cprogrammer +# print error messages to stderr +# +# Revision 1.2 2009-04-02 20:36:25+05:30 Cprogrammer +# added -h option to dktest +# added -x - option to dkim +# +# Revision 1.1 2009-04-02 14:52:27+05:30 Cprogrammer +# Initial revision +# +# $Id: dk-filter.sh,v 1.22 2019-06-24 23:19:57+05:30 Cprogrammer Exp mbhangui $ +# +if [ -z "$QMAILREMOTE" -a -z "$QMAILLOCAL" ]; then + echo "dk-filter should be run by spawn-filter" 1>&2 + exit 1 +fi +dksign=0 +dkimsign=0 +dkverify=0 +dkimverify=0 +if [ -z "$DEFAULT_DKIM_KEY" ] ; then + default_key=QMAILHOME/control/domainkeys/default +else + default_key=$DEFAULT_DKIM_KEY +fi +if [ -z "$NODK" -a -x QMAILHOME/bin/dktest -a -z "$DKVERIFY" ] ; then + if [ -z "$DKSIGN" ] ; then + DKSIGN=QMAILHOME/control/domainkeys/%/default + dksign=2 + elif [ " $DKSIGN" = " QMAILHOME/control/domainkeys/%/default" ] ; then + dksign=2 + fi +fi +if [ -z "$NODKIM" -a -x QMAILHOME/bin/dkim -a -z "$DKIMVERIFY" ] ; then + if [ -z "$DKIMSIGN" ] ; then + DKIMSIGN=QMAILHOME/control/domainkeys/%/default + dkimsign=2 + elif [ " $DKIMSIGN" = " QMAILHOME/control/domainkeys/%/default" ] ; then + dkimsign=2 + fi +fi +if [ -z "$NODK" -a -n "$DKSIGN" ] ; then + if [ ! -f QMAILHOME/bin/dktest ] ; then + echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2 + exit 1 + fi + percent_found=0 + echo $DKSIGN|grep "%" >/dev/null 2>&1 + if [ $? -eq 0 ] ; then + percent_found=1 + fi + if [ -n "$DKIMDOMAIN" ] && [ -z "$_SENDER" ] ; then + dkkeyfn=`echo $DKSIGN | sed s{%{$DKIMDOMAIN{g` + elif [ ! " $_SENDER" = " " ] ; then + # replace '%' in filename with domain + domain=`echo $_SENDER | cut -d@ -f2` + dkkeyfn=`echo $DKSIGN | sed s{%{$domain{g` + else + dkkeyfn=$DKSIGN + fi + if [ $dksign -eq 2 -a ! -f $dkkeyfn ] ; then + dkkeyfn=$default_key + fi + if [ -f $dkkeyfn ] ; then + dksign=1 + else + dksign=0 + fi + if [ $dksign -eq 0 -a $percent_found -ne 1 ] ; then + exit 32 + fi + dkselector=`basename $dkkeyfn` +fi +if [ -z "$NODKIM" -a -n "$DKIMSIGN" ] ; then + if [ ! -f QMAILHOME/bin/dkim ] ; then + echo "QMAILHOME/bin/dkim: No such file or directory" 1>&2 + exit 1 + fi + percent_found=0 + echo $DKIMSIGN|grep "%" >/dev/null 2>&1 + if [ $? -eq 0 ] ; then + percent_found=1 + fi + if [ -n "$DKIMDOMAIN" ] && [ -z "$_SENDER" ] ; then + dkimkeyfn=`echo $DKIMSIGN | sed s{%{$DKIMDOMAIN{g` + elif [ ! " $_SENDER" = " " ] ; then + # replace '%' in filename with domain + domain=`echo $_SENDER | cut -d@ -f2` + dkimkeyfn=`echo $DKIMSIGN | sed s{%{$domain{g` + else + dkimkeyfn=$DKIMSIGN + fi + if [ $dkimsign -eq 2 -a ! -f $dkimkeyfn ] ; then + dkimkeyfn=$default_key + fi + if [ -f $dkimkeyfn ] ; then + dkimsign=1 + else + dkimsign=0 + fi + if [ $dkimsign -eq 0 -a $percent_found -ne 1 ] ; then + exit 32 # private key does not exist + fi + dkimselector=`basename $dkimkeyfn` +fi +if [ -z "$NODK" -a -n "$DKVERIFY" ] ; then + if [ ! -f QMAILHOME/bin/dktest ] ; then + echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2 + exit 1 + fi + dkverify=1 +fi +if [ -z "$NODKIM" -a -n "$DKIMVERIFY" ] ; then + if [ ! -f QMAILHOME/bin/dkim ] ; then + echo "QMAILHOME/bin/dkim: No such file or directory" 1>&2 + exit 1 + fi + dkimverify=1 +fi +cat > /tmp/dk.$$ +if [ $dkimsign -eq 1 ] ; then + # DKIMSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn" + set -- `getopt lqthb:c:d:i:x:z:y:s: $DKIMSIGNOPTIONS` + bopt=0 + xopt=0 + zopt=0 + yopt=0 + sopt=0 + dkimopts="QMAILHOME/bin/dkim" + while [ $1 != -- ] + do + case $1 in + -l) + dkimopts="$dkimopts -l" + ;; + -q) + dkimopts="$dkimopts -q" + ;; + -t) + dkimopts="$dkimopts -t" + ;; + -h) + dkimopts="$dkimopts -h" + ;; + + -b) + bopt=1 + dkimopts="$dkimopts -b $2" + shift + ;; + + -c) + dkimopts="$dkimopts -c $2" + shift + ;; + + -d) + dkimopts="$dkimopts -d $2" + shift + ;; + + -i) + dkimopts="$dkimopts -i $2" + shift + ;; + + -x) + xopt=1 + dkimopts="$dkimopts -x $2" + shift + ;; + + -z) + zopt=1 + dkimopts="$dkimopts -z $2" + shift + ;; + + -y) + yopt=1 + dkimopts="$dkimopts -y $2" + shift + ;; + + -s) + sopt=1 + dkimopts="$dkimopts -s $2" + shift + ;; + esac + shift # next flag + done + if [ $zopt -eq 0 ] ; then + dkimopts="$dkimopts -z 1" + fi + if [ $bopt -eq 0 ] ; then + dkimopts="$dkimopts -b 2" + fi + if [ $xopt -eq 0 ] ; then + dkimopts="$dkimopts -x -" + fi + if [ $yopt -eq 0 ] ; then + dkimopts="$dkimopts -y $dkimselector" + fi + if [ $sopt -eq 0 ] ; then + dkimopts="$dkimopts -s $dkimkeyfn" + fi + exec 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include "dkim.h" +#include "dkimdns.h" + +#ifdef HAVE_OPENSSL_EVP_H +#include +#define DKIM_MALLOC(s) OPENSSL_malloc(s) +#define DKIM_MFREE(s) OPENSSL_free(s); s = NULL; +#else +#define DKIM_MALLOC(s) malloc(s) +#define DKIM_MFREE(s) free(s); s = NULL; +#endif + +int DKIM_CALL +SignThisHeader(const char *szHeader) +{ + if ((!strncasecmp(szHeader, "X-", 2) && strncasecmp(szHeader, "X-Mailer", 8)) + || !strncasecmp(szHeader, "Received:", 9) + || !strncasecmp(szHeader, "DKIM-Signature:", 15) + || !strncasecmp(szHeader, "Authentication-Results:", 23) + || !strncasecmp(szHeader, "DomainKey-Signature", 19) + || !strncasecmp(szHeader, "Return-Path:", 12)) + { + return 0; + } + return 1; +} + +char *program; + +void +usage() +{ +#ifdef HAVE_EVP_SHA256 + fprintf(stderr, "usage: %s [-lqthvH] [-p <0|1|2>] [-b <1|2|3>] [-c ]\n\t[-d domain] [-i you@domain] [-x expire_time] [-z hash] [-y selector] -s privkeyfile\n", program); +#else + fprintf(stderr, "usage: %s [-lqthvH] [-p <0|1|2>] [-b <1|2|3>] [-c ]\n\t[-d domain] [-i you@domain] [-x expire_time] [-y selector] -s privkeyfile\n", program); +#endif + fprintf(stderr, "l include body length tag\n"); + fprintf(stderr, "q include query method tag\n"); + fprintf(stderr, "t include a timestamp tag\n"); + fprintf(stderr, "h include Copied Headers\n"); + fprintf(stderr, "f allow Unsigned From (default is to reject if From field is not signed)\n"); + fprintf(stderr, "S allow Unsigned Subject (default is to reject if Subject field is not signed)\n"); + fprintf(stderr, "v verify the message\n"); + fprintf(stderr, "p 0 - disable practice (default), 1- SSP, or 2 - ADSP verification\n"); + fprintf(stderr, "b 1 - allman, 2 - ietf or 3 - both\n"); + fprintf(stderr, "c r for relaxed [DEFAULT], s - simple, t relaxed/simple, u - simple/relaxed\n"); + fprintf(stderr, "d the domain tag, if not provided, determined from the sender/from header\n"); + fprintf(stderr, "i the identity, if not provided it will not be included\n"); + fprintf(stderr, "x the expire time in seconds since epoch ( DEFAULT = current time + 604800)\n"); + fprintf(stderr, " if set to - then it will not be included\n"); +#ifdef HAVE_EVP_SHA256 + fprintf(stderr, "z 1 for sha1, 2 for sha256, 3 for both\n"); +#endif + fprintf(stderr, "y the selector tag DEFAULT=private\n"); + fprintf(stderr, "s sign the message using the private key in privkeyfile\n"); + fprintf(stderr, "V set verbose mode\n"); + fprintf(stderr, "H this help\n"); + exit(1); +} + +unsigned int str_chr(char *s, int c) +{ + register char ch; + register char *t; + + ch = c; + t = s; + for (;;) { + if (!*t) + break; + if (*t == ch) + break; + ++t; + if (!*t) + break; + if (*t == ch) + break; + ++t; + if (!*t) + break; + if (*t == ch) + break; + ++t; + if (!*t) + break; + if (*t == ch) + break; + ++t; + } + return t - s; +} + +void +dkim_error(int e) +{ + switch (e) + { + case DKIM_OUT_OF_MEMORY: + fprintf(stderr, "memory allocation failed\n"); + break; + case DKIM_INVALID_CONTEXT: + fprintf(stderr, "DKIMContext structure invalid for this operation\n"); + break; + case DKIM_NO_SENDER: + fprintf(stderr, "Could not find From: or Sender: header in message\n"); + break; + case DKIM_BAD_PRIVATE_KEY: + fprintf(stderr, "Could not parse private key\n"); + break; + case DKIM_BUFFER_TOO_SMALL: + fprintf(stderr, "Buffer passed in is not large enough"); + break; + } +} + +/* + * Allows you to add the headers contain the results and DKIM ADSP + */ +void writeHeader(int ret, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP ) +{ + char *dkimStatus, *sspStatus, *adspStatus; + + dkimStatus = sspStatus = adspStatus = (char *) ""; + switch (ret) + { + case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */ + dkimStatus = (char *) "signature result: signature verified but it did not include all of the body"; + break; + case DKIM_NEUTRAL: /*- 3 verify result: no signatures verified but message is not suspicious */ + dkimStatus = (char *) "verify result: no signatures verified but message is not suspicious"; + break; + case DKIM_PARTIAL_SUCCESS: /*- 2 verify result: at least one but not all signatures verified */ + dkimStatus = (char *) "verify result: at least none but not all signatures verified"; + break; + case DKIM_FINISHED_BODY: /*- 1 process result: no more message body is needed */ + dkimStatus = (char *) "process result: no more message body is needed"; + break; + case DKIM_SUCCESS: + dkimStatus = (char *) "good "; + break; + case DKIM_FAIL: + dkimStatus = (char *) "failed "; + break; + case DKIM_BAD_SYNTAX: + dkimStatus = (char *) "signature error: DKIM-Signature could not parse or has bad tags/values"; + break; + case DKIM_SIGNATURE_BAD: + dkimStatus = (char *) "signature error: RSA verify failed"; + break; + case DKIM_SIGNATURE_BAD_BUT_TESTING: + dkimStatus = (char *) "signature error: RSA verify failed but testing"; + break; + case DKIM_SIGNATURE_EXPIRED: + dkimStatus = (char *) "signature error: x= is old"; + break; + case DKIM_SELECTOR_INVALID: + dkimStatus = (char *) "signature error: selector doesn't parse or contains invalid values"; + break; + case DKIM_SELECTOR_GRANULARITY_MISMATCH: + dkimStatus = (char *) "signature error: selector g= doesn't match i="; + break; + case DKIM_SELECTOR_KEY_REVOKED: + dkimStatus = (char *) "signature error: selector p= empty"; + break; + case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG: + dkimStatus = (char *) "signature error: selector domain name too long to request"; + break; + case DKIM_SELECTOR_DNS_TEMP_FAILURE: + dkimStatus = (char *) "signature error: temporary dns failure requesting selector"; + break; + case DKIM_SELECTOR_DNS_PERM_FAILURE: + dkimStatus = (char *) "signature error: permanent dns failure requesting selector"; + break; + case DKIM_SELECTOR_PUBLIC_KEY_INVALID: + dkimStatus = (char *) "signature error: selector p= value invalid or wrong format"; + break; + case DKIM_NO_SIGNATURES: + dkimStatus = (char *) "process error, no sigs"; + break; + case DKIM_NO_VALID_SIGNATURES: + dkimStatus = (char *) "process error, no valid sigs"; + break; + case DKIM_BODY_HASH_MISMATCH: + dkimStatus = (char *) "sigature verify error: message body does not hash to bh value"; + break; + case DKIM_SELECTOR_ALGORITHM_MISMATCH: + dkimStatus = (char *) "signature error: selector h= doesn't match signature a="; + break; + case DKIM_STAT_INCOMPAT: + dkimStatus = (char *) "signature error: incompatible v="; + break; + default: + dkimStatus = (char *) "error"; + break; + } + if (useSSP && resDKIMSSP != -1) + { + switch(resDKIMSSP) + { + case DKIM_SSP_ALL: + sspStatus = (char *) "all;"; + break; + case DKIM_SSP_STRICT: + sspStatus = (char *) "strict;"; + break; + case DKIM_SSP_SCOPE: + sspStatus = (char *) "out of scope;"; + break; + case DKIM_SSP_TEMPFAIL: + sspStatus = (char *) "temporary failure;"; + break; + case DKIM_SSP_UNKNOWN: + default: + sspStatus = (char *) "unknown;"; + break; + } + } + if (useADSP && resDKIMADSP != -1) { + switch(resDKIMADSP) + { + case DKIM_ADSP_ALL: + adspStatus = (char *) "all;"; + break; + case DKIM_ADSP_DISCARDABLE: + adspStatus = (char *) "discardable;"; + break; + case DKIM_ADSP_SCOPE: + adspStatus = (char *) "out of scope;"; + break; + case DKIM_ADSP_TEMPFAIL: + adspStatus = (char *) "temporary failure;"; + break; + case DKIM_ADSP_UNKNOWN: + default: + adspStatus = (char *) "unknown ;"; + break; + } + } + printf("DKIM-Status: %s\n", dkimStatus); + if (useSSP && *sspStatus) + printf("X-DKIM-SSP: %s\n", sspStatus); + if (useADSP && *adspStatus) + printf("X-DKIM-ADSP: %s\n", adspStatus); +} + +int +ParseTagValues(char *list, char *letters[], char *values[]) +{ + char *tmp, *ptr, *key; + int i; + + /*- start with all args unset */ + for (i = 0; letters[i]; i++) + values[i] = 0; + key = 0; + for(ptr = list;*ptr;) { + if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */ + *ptr++ = 0; + if (!key) + key = ptr; + if (*ptr == '=') { + *ptr = 0; + for (i = 0;letters[i];i++) { + if (!strcmp(letters[i], key)) { + ptr++; + for (;*ptr;) { + if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) { + ptr++; + continue; + } + break; + } + values[i] = ptr; + for(;*ptr && *ptr != ';';ptr++); + tmp = ptr; + if (*ptr) + *ptr++ = 0; + for(;tmp != values[i];tmp--) /*- RFC 4871 3.2 */ { + if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n')) { + *tmp = 0; + continue; + } + break; + } + key = 0; + break; + } + } + } else + ptr++; + } + return (0); +} + +int +GetSSP(char *domain, int *bTesting) +{ + char *query, *results; + char *tags[] = { (char *) "dkim", (char *) "t", (char *) 0}; + char *values[2]; + int bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN; + + *bTesting = 0; + if (!(query = (char *) DKIM_MALLOC(strlen("_ssp._domainkey.") + strlen(domain) + 1))) { + fprintf(stderr, "malloc: %d: %s\n", strlen("_ssp._domainkey.") + strlen(domain) + 1, + strerror(errno)); + exit(1); + } + sprintf(query, "_ssp._domainkey.%s", domain); + results = dns_text(query); + DKIM_MFREE(query); + if (!strcmp(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_SSP_TEMPFAIL; + } else + if (!strcmp(results, "e=perm;")) { + DKIM_MFREE(results); + results = dns_text(domain); + if (!strcmp(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_SSP_TEMPFAIL; + } else + if (!strcmp(results, "e=perm;")) { + DKIM_MFREE(results); + return DKIM_SSP_SCOPE; + } + bIsParentSSP = 1; + } + if (!ParseTagValues(results, tags, values)) { + DKIM_MFREE(results); + return DKIM_SSP_UNKNOWN; + } + DKIM_MFREE(results); + if (values[0] != NULL) { + if (strcasecmp(values[0], "all") == 0) + iSSP = DKIM_SSP_ALL; + else + if (strcasecmp(values[0], "strict") == 0) + iSSP = DKIM_SSP_STRICT; + } + // flags + if (values[1] != NULL) { + char *s, *p; + for (p = values[1], s = values[1]; *p; p++) { + if (*p == '|') + *p = 0; + else + continue; + if (!strcmp(s, "y")) + *bTesting = 1; + else + if (!strcmp(s, "s")) { + if (bIsParentSSP) { + /* + * this is a parent's SSP record that should not apply to subdomains + * the message is non-suspicious + */ + *bTesting = 0; + return (DKIM_SSP_UNKNOWN); + } + } + s = p + 1; + } + } + return iSSP; /*- No ADSP Record */ +} + +int +GetADSP(char *domain) +{ + char *query, *results; + char *tags[] = {(char *) "dkim", (char *) 0}; + char *values[1]; + + results = dns_text(domain); + if (!strcmp(results, "e=perm;")) { + DKIM_MFREE(results); + return DKIM_ADSP_SCOPE; + } else + if (!strcmp(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_ADSP_TEMPFAIL; + } + if (!(query = (char *) DKIM_MALLOC(strlen((char *) "_adsp._domainkey.") + strlen(domain) + 1))) { + fprintf(stderr, "malloc: %d: %s\n", strlen("_adsp._domainkey.") + strlen(domain) + 1, + strerror(errno)); + exit(1); + } + sprintf(query, "_adsp._domainkey.%s", domain); + results = dns_text(query); + DKIM_MFREE(query); + if (!strcmp(results, "e=perm;")) { + DKIM_MFREE(results); + return DKIM_ADSP_SCOPE; + } else + if (!strcmp(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_ADSP_TEMPFAIL; + } + if (!ParseTagValues(results, tags, values)) { + DKIM_MFREE(results); + return DKIM_ADSP_UNKNOWN; + } + DKIM_MFREE(results); + if (values[0] != NULL) { + if (strcasecmp(values[0], "all") == 0) + return (DKIM_ADSP_ALL); + else + if (strcasecmp(values[0], "discardable") == 0) + return (DKIM_ADSP_DISCARDABLE); + } + return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */ +} + +int +main(int argc, char **argv) +{ + char *PrivKey, *PrivKeyFile = NULL, *pSig = NULL, *dkimverify; + int i, ret, ch, nPrivKeyLen, PrivKeyFD, verbose = 0; + int bSign = 1, nSigCount = 0, useSSP = 0, useADSP = 0, accept3ps = 0; + int sCount = 0, sSize = 0, resDKIMSSP = -1, resDKIMADSP = -1; + int nAllowUnsignedFromHeaders = 0; + int nAllowUnsignedSubject = 1; + char Buffer[4096], szPolicy[512]; + time_t t; + struct stat statbuf; + DKIMContext ctxt; + DKIMSignOptions opts = { 0 }; + DKIMVerifyDetails *pDetails; + DKIMVerifyOptions vopts = { 0 }; + + if (!(program = strrchr(argv[0], '/'))) + program = argv[0]; + else + program++; + t = time(0); +#ifdef HAVE_EVP_SHA256 + opts.nHash = DKIM_HASH_SHA1_AND_256; +#else + opts.nHash = DKIM_HASH_SHA1; +#endif + opts.nCanon = DKIM_SIGN_RELAXED; + opts.nIncludeBodyLengthTag = 0; + opts.nIncludeQueryMethod = 0; + opts.nIncludeTimeStamp = 0; + opts.expireTime = t + 604800; // expires in 1 week + opts.nIncludeCopiedHeaders = 0; + opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH; + strcpy(opts.szRequiredHeaders, "NonExistent"); + opts.pfnHeaderCallback = SignThisHeader; + while (1) { + if ((ch = getopt(argc, argv, "lqtfhHSvVp:b:c:d:i:s:x:y:z:")) == -1) + break; + switch (ch) + { + case 'l': /*- body length tag */ + vopts.nHonorBodyLengthTag = 1; + opts.nIncludeBodyLengthTag = 1; + break; + case 'q': /*- query method tag */ + opts.nIncludeQueryMethod = 1; + break; + case 'S': + nAllowUnsignedSubject = 0; + break; + case 'f': + nAllowUnsignedFromHeaders = 1; + case 't': /*- timestamp tag */ + opts.nIncludeTimeStamp = 1; + break; + case 'h': + opts.nIncludeCopiedHeaders = 1; + break; + case 'H': + usage(); + break; + case 'v': /*- verify */ + bSign = 0; + break; + case 'V': /*- verbose */ + verbose = 1; + break; + case 'p': + switch(*optarg) + { + case '1': + accept3ps = 1; + useSSP = 1; + useADSP = 0; + break; + case '2': + accept3ps = 1; + useSSP = 0; + useADSP = 1; + break; + case '3': + accept3ps = 1; + useSSP = 0; + useADSP = 0; + break; + case '0': + accept3ps = 0; + useSSP = 0; + useADSP = 0; + break; + default: + fprintf(stderr, "%s: unrecognized practice %c.\n", program, *optarg); + return (1); + } + break; + case 'b': /*- allman or ietf draft 1 or both */ + switch (*optarg) + { + case '1': + opts.nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1; + break; + case '2': + opts.nIncludeBodyHash = DKIM_BODYHASH_IETF_1; + break; + case '3': + opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH; + break; + default: + fprintf(stderr, "%s: unrecognized standard %c.\n", program, *optarg); + return (1); + } + break; + case 'c': + switch (*optarg) + { + case 'r': + opts.nCanon = DKIM_SIGN_RELAXED; + break; + case 's': + opts.nCanon = DKIM_SIGN_SIMPLE; + break; + case 't': + opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE; + break; + case 'u': + opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED; + break; + default: + fprintf(stderr, "%s: unrecognized canonicalization.\n", program); + return (1); + } + break; + case 'd': + strncpy(opts.szDomain, optarg, sizeof (opts.szDomain) - 1); + break; + case 'i': /*- identity */ + if (*optarg == '-') + opts.szIdentity[0] = '\0'; + else + strncpy(opts.szIdentity, optarg, sizeof (opts.szIdentity) - 1); + break; + case 's': /*- sign */ + bSign = 1; + PrivKeyFile = optarg; + break; + case 'x': /*- expire time */ + if (*optarg == '-') + opts.expireTime = 0; + else + opts.expireTime = t + atoi(optarg); + break; + case 'y': + strncpy(opts.szSelector, optarg, sizeof (opts.szSelector) - 1); + break; + case 'z': /*- sign w/ sha1, sha256 or both */ +#ifdef HAVE_EVP_SHA256 + switch (*optarg) + { + case '1': + opts.nHash = DKIM_HASH_SHA1; + break; + case '2': + opts.nHash = DKIM_HASH_SHA256; + break; + case '3': + opts.nHash = DKIM_HASH_SHA1_AND_256; + break; + default: + fprintf(stderr, "%s: unrecognized hash.\n", program); + return (1); + } +#else + opts.nHash = DKIM_HASH_SHA1; +#endif + break; + } /*- switch (ch) */ + } + if (bSign) { /*- sign */ + if (!PrivKeyFile) { + fprintf(stderr, "Private Key not provided\n"); + usage(); + return (1); + } + if (!opts.szSelector[0]) { + if ((pSig = strrchr(PrivKeyFile, '/'))) { + pSig++; + strcpy(opts.szSelector, pSig); + } else + strcpy(opts.szSelector, "private"); + } + if ((PrivKeyFD = open(PrivKeyFile, O_RDONLY)) == -1) { + fprintf(stderr, "%s: %s\n", PrivKeyFile, strerror(errno)); + return (1); + } + if (fstat(PrivKeyFD, &statbuf) == -1) { + fprintf(stderr, "fstat: %s: %s\n", PrivKeyFile, strerror(errno)); + return (1); + } + if (!(PrivKey = (char *) DKIM_MALLOC(sizeof(char) * ((nPrivKeyLen = statbuf.st_size) + 1)))) { + fprintf(stderr, "malloc: %ld bytes: %s\n", statbuf.st_size + 1, strerror(errno)); + return (1); + } + if (read(PrivKeyFD, PrivKey, nPrivKeyLen) != nPrivKeyLen) { + fprintf(stderr, "%s: read: %s\n", strerror(errno), program); + return (1); + } + close(PrivKeyFD); + PrivKey[nPrivKeyLen] = '\0'; + if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS) { + fprintf(stderr, "DKIMSignInit: failed to initialize signature %s\n", PrivKeyFile); + return (1); + } + for (;;) { + if ((ret = read(0, Buffer, sizeof(Buffer) - 1)) == -1) { + fprintf(stderr, "read: %s\n", strerror(errno)); + DKIMSignFree(&ctxt); + return (1); + } else + if (!ret) + break; + if (DKIMSignProcess(&ctxt, Buffer, ret) == DKIM_INVALID_CONTEXT) { + fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n"); + DKIMSignFree(&ctxt); + return (1); + } + } + if (DKIMSignGetSig2(&ctxt, PrivKey, &pSig) == DKIM_INVALID_CONTEXT) { + fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n"); + DKIMSignFree(&ctxt); + return (1); + } + if (pSig) { + fwrite(pSig, 1, strlen(pSig), stdout); + fwrite("\n", 1, 1, stdout); + } + DKIMSignFree(&ctxt); + return 0; + } else { /*- verify */ + if (useADSP) + vopts.nCheckPractices = useADSP; + else + if (useSSP) + vopts.nCheckPractices = useSSP; + else + vopts.nCheckPractices = 0; + vopts.nAccept3ps = accept3ps; + vopts.pfnSelectorCallback = NULL; /*- SelectorCallback; */ + vopts.nAllowUnsignedFromHeaders = nAllowUnsignedFromHeaders; + vopts.nSubjectRequired = nAllowUnsignedSubject; + DKIMVerifyInit(&ctxt, &vopts); /*- this is always successful */ + for (;;) { + if ((i = read(0, Buffer, sizeof(Buffer) - 1)) == -1) { + fprintf(stderr, "read: %s\n", strerror(errno)); + DKIMVerifyFree(&ctxt); + return (1); + } else + if (!i) + break; + ret = DKIMVerifyProcess(&ctxt, Buffer, i); + dkim_error(ret); + if (ret > 0 && ret < DKIM_FINISHED_BODY) + ret = DKIM_FAIL; + if (ret) + break; + } + if (!ret) { + ret = DKIMVerifyResults(&ctxt, &sCount, &sSize); + if (ret != DKIM_SUCCESS && ret != DKIM_3PS_SIGNATURE && ret != DKIM_NEUTRAL) + dkim_error(ret); + if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS) + dkim_error(ret); + else { + for (ret = DKIM_FAIL, i = 0; i < nSigCount; i++) { + if (verbose) + printf("Signature # %02d: ", i + 1); + if (pDetails[i].nResult >= 0) { + ret = 0; + if (verbose) + printf("Success\n"); + continue; + } else { + if (ret == DKIM_FAIL) + ret = pDetails[i].nResult; + if (verbose) + printf("Failure %d\n", pDetails[i].nResult); + } + } + if (!nSigCount) + ret = DKIM_NO_SIGNATURES; + } + } + if (ret < 0 || ret == DKIM_3PS_SIGNATURE) { + if (useADSP) { + char *domain; + + if ((domain = DKIMVerifyGetDomain(&ctxt))) + resDKIMADSP = GetADSP(domain); + if (sCount > 0) { + if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL) + ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS); + } + /* if the message should be signed, return fail */ + if (resDKIMADSP == DKIM_ADSP_DISCARDABLE) + ret = DKIM_FAIL; + ret = DKIM_NEUTRAL; + } else + if (useSSP) { + int bTestingPractices = 0; + char *domain; + + if ((domain = DKIMVerifyGetDomain(&ctxt))) + resDKIMSSP = GetSSP(domain, &bTestingPractices); + if (sCount > 0) { + if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL)) + ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS); + } + // if the SSP is testing, return neutral + if (bTestingPractices) + return(DKIM_NEUTRAL); + /* if the message should be signed, return fail */ + if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT) + return(DKIM_FAIL); + ret = DKIM_NEUTRAL; + } + } + DKIMVerifyFree(&ctxt); + writeHeader(ret, resDKIMSSP, resDKIMADSP, useSSP, useADSP); + if ((dkimverify = getenv("DKIMVERIFY"))) { + if (ret < 0) { + if (dkimverify[str_chr(dkimverify, 'F' - ret)]) + ret = 14; /*- return permanent error */ + if (dkimverify[str_chr(dkimverify, 'f' - ret)]) + ret = 88; /*- return temporary error */ + } else { + if (dkimverify[str_chr(dkimverify, 'A' + ret)]) + ret = 14; /*- return permanent error */ + if (dkimverify[str_chr(dkimverify, 'a' + ret)]) + ret = 88; /*- return temporary error */ + } + } + return (ret); + } + /*- Not Reached */ + _exit(0); +} + +void +getversion_dkim_c() +{ + static char *x = (char *) "$Id: dkim.c,v 1.23 2019-06-14 21:24:59+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dkim.h netqmail-1.06/dkim.h --- ../netqmail-1.06-original/dkim.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkim.h 2019-02-27 20:57:13.386025114 +0100 @@ -0,0 +1,193 @@ +/* + * $Log: dkim.h,v $ + * Revision 1.8 2015-12-15 16:03:18+05:30 Cprogrammer + * use time_t for expireTime + * + * Revision 1.7 2011-06-04 13:56:06+05:30 Cprogrammer + * corrected return codes + * + * Revision 1.6 2011-06-04 10:04:00+05:30 Cprogrammer + * unified error code for signing & verifcation + * added signature and identity domain information to + * DKIMVerifyDetails structure + * + * Revision 1.5 2009-03-27 20:19:05+05:30 Cprogrammer + * major changes made for incorporating ADSP + * + * Revision 1.4 2009-03-26 19:28:15+05:30 Cprogrammer + * removed DKIM_3PS_PARTIAL_SUCCESS + * + * Revision 1.3 2009-03-26 15:11:33+05:30 Cprogrammer + * added ADSP + * + * Revision 1.2 2009-03-25 08:37:58+05:30 Cprogrammer + * changed definitions of constants to avoid clash between error and success + * + * Revision 1.1 2009-03-21 08:50:19+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define DKIM_CALL +#define MAKELONG(a,b) ((long)(((unsigned)(a) & 0xffff) | (((unsigned)(b) & 0xffff) << 16))) + +#ifdef __cplusplus +extern "C" { +#endif + +#include +// DKIM Body hash versions +#define DKIM_BODYHASH_ALLMAN_1 1 +#define DKIM_BODYHASH_IETF_1 2 +#define DKIM_BODYHASH_BOTH DKIM_BODYHASH_ALLMAN_1 | DKIM_BODYHASH_IETF_1 + +// DKIM hash algorithms +#define DKIM_HASH_SHA1 1 +#define DKIM_HASH_SHA256 2 +#define DKIM_HASH_SHA1_AND_256 DKIM_HASH_SHA1 | DKIM_HASH_SHA256 + +// DKIM canonicalization methods +#define DKIM_CANON_SIMPLE 1 +#define DKIM_CANON_NOWSP 2 +#define DKIM_CANON_RELAXED 3 + +#define DKIM_SIGN_SIMPLE MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_SIMPLE) +#define DKIM_SIGN_SIMPLE_RELAXED MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_SIMPLE) +#define DKIM_SIGN_RELAXED MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_RELAXED) +#define DKIM_SIGN_RELAXED_SIMPLE MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_RELAXED) + +// DKIM_SUCCESS // verify result: all signatures verified +// signature result: signature verified +#define DKIM_SUCCESS 0 // operation successful +#define DKIM_FINISHED_BODY 1 // process result: no more message body is needed +#define DKIM_PARTIAL_SUCCESS 2 // verify result: at least one but not all signatures verified +#define DKIM_NEUTRAL 3 // verify result: no signatures verified but message is not suspicous +#define DKIM_SUCCESS_BUT_EXTRA 4 // signature result: signature verified but it did not include all of the body +#define DKIM_3PS_SIGNATURE 5 // 3rd-party signature + +// DKIM Error codes +#define DKIM_FAIL -1 // verify error: message is suspicious +#define DKIM_BAD_SYNTAX -2 // signature error: DKIM-Signature could not parse or has bad tags/values +#define DKIM_SIGNATURE_BAD -3 // signature error: RSA verify failed +#define DKIM_SIGNATURE_BAD_BUT_TESTING -4 // signature error: RSA verify failed but testing +#define DKIM_SIGNATURE_EXPIRED -5 // signature error: x= is old +#define DKIM_SELECTOR_INVALID -6 // signature error: selector doesn't parse or contains invalid values +#define DKIM_SELECTOR_GRANULARITY_MISMATCH -7 // signature error: selector g= doesn't match i= +#define DKIM_SELECTOR_KEY_REVOKED -8 // signature error: selector p= empty +#define DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG -9 // signature error: selector domain name too long to request +#define DKIM_SELECTOR_DNS_TEMP_FAILURE -10 // signature error: temporary dns failure requesting selector +#define DKIM_SELECTOR_DNS_PERM_FAILURE -11 // signature error: permanent dns failure requesting selector +#define DKIM_SELECTOR_PUBLIC_KEY_INVALID -12 // signature error: selector p= value invalid or wrong format +#define DKIM_NO_SIGNATURES -13 // process error, no sigs +#define DKIM_NO_VALID_SIGNATURES -14 // process error, no valid sigs +#define DKIM_BODY_HASH_MISMATCH -15 // sigature verify error: message body does not hash to bh value +#define DKIM_SELECTOR_ALGORITHM_MISMATCH -16 // signature error: selector h= doesn't match signature a= +#define DKIM_STAT_INCOMPAT -17 // signature error: incompatible v= +#define DKIM_UNSIGNED_FROM -18 // signature error: not all message's From headers in signature +#define DKIM_OUT_OF_MEMORY -19 // memory allocation failed +#define DKIM_INVALID_CONTEXT -20 // DKIMContext structure invalid for this operation +#define DKIM_NO_SENDER -21 // signing error: Could not find From: or Sender: header in message +#define DKIM_BAD_PRIVATE_KEY -22 // signing error: Could not parse private key +#define DKIM_BUFFER_TOO_SMALL -23 // signing error: Buffer passed in is not large enough +#define DKIM_MAX_ERROR -24 // set this to 1 greater than the highest error code (but negative) + +#define DKIM_SSP_UNKNOWN 1 /*- some messages may be signed */ +#define DKIM_SSP_ALL 2 /*- all messages are signed, 3rd party allowed */ +#define DKIM_SSP_STRICT 3 /*- all messages are signed, no 3rd party allowed */ +#define DKIM_SSP_SCOPE 4 /*- the domain should be considered invalid */ +#define DKIM_SSP_TEMPFAIL 5 /*- Temporary Error */ + +#define DKIM_ADSP_UNKNOWN 1 /*- some messages may be signed */ +#define DKIM_ADSP_ALL 2 /*- All message are signed with author signature */ +#define DKIM_ADSP_DISCARDABLE 3 /*- messages which fail verification are Discardable */ +#define DKIM_ADSP_SCOPE 4 /*- domain is out of scope */ +#define DKIM_ADSP_TEMPFAIL 5 /*- Temporary Error */ + + +// This function is called once for each header in the message +// return 1 to include this header in the signature and 0 to exclude. +typedef int (DKIM_CALL * DKIMHEADERCALLBACK) (const char *szHeader); + +// This function is called to retrieve a TXT record from DNS +typedef int (DKIM_CALL * DKIMDNSCALLBACK) (const char *szFQDN, char *szBuffer, int nBufLen); + +typedef struct DKIMContext_t { + unsigned int reserved1; + unsigned int reserved2; + void *reserved3; +} DKIMContext; + +typedef struct DKIMSignOptions_t { + int nCanon; // canonization + int nIncludeBodyLengthTag; // 0 = don't include l= tag, 1 = include l= tag + int nIncludeTimeStamp; // 0 = don't include t= tag, 1 = include t= tag + int nIncludeQueryMethod; // 0 = don't include q= tag, 1 = include q= tag + char szSelector[80]; // selector - required + char szDomain[256]; // domain - optional - if empty, domain is computed from sender + char szIdentity[256]; // for i= tag, if empty tag will not be included in sig + time_t expireTime; // for x= tag, if 0 tag will not be included in sig + DKIMHEADERCALLBACK pfnHeaderCallback; // header callback + char szRequiredHeaders[256]; // colon-separated list of headers that must be signed + int nHash; // use one of the DKIM_HASH_xx constants here + // even if not present in the message + int nIncludeCopiedHeaders; // 0 = don't include z= tag, 1 = include z= tag + int nIncludeBodyHash; // use one of the DKIM_BODYHASH_xx constants here +} DKIMSignOptions; + +typedef struct DKIMVerifyOptions_t { + DKIMDNSCALLBACK pfnSelectorCallback; // selector record callback + DKIMDNSCALLBACK pfnPracticesCallback; // SSP record callback + int nHonorBodyLengthTag; // 0 = ignore l= tag, 1 = use l= tag to limit the amount of body verified + int nCheckPractices; // 0 = use default (unknown) practices, 1 = request and use sender's signing practices + int nSubjectRequired; // 0 = subject is required to be signed, 1 = not required + int nSaveCanonicalizedData; // 0 = canonicalized data is not saved, 1 = canonicalized data is saved + int nAllowUnsignedFromHeaders; // 0 = From headers not included in the signature are not allowed, 1 = allowed + int nAccept3ps; // 0 = don't check 3rd party signature(s), 1 = check 3rd party signature(s) +} DKIMVerifyOptions; + +typedef struct DKIMVerifyDetails_t { + char *szSignature; + char *DNS; + char *szSignatureDomain; + char *szIdentityDomain; + char *szCanonicalizedData; + int nResult; +} DKIMVerifyDetails; + +int DKIM_CALL DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions * pOptions); +int DKIM_CALL DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength); +int DKIM_CALL DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, int nSigLength); +int DKIM_CALL DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature); +void DKIM_CALL DKIMSignFree(DKIMContext *pSignContext); +char *DKIM_CALL DKIMSignGetDomain(DKIMContext *pSignContext); + +int DKIM_CALL DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions * pOptions); +int DKIM_CALL DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength); +int DKIM_CALL DKIMVerifyResults(DKIMContext *pVerifyContext , int *sCount, int *sSize); +int DKIM_CALL DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices); +char *DKIM_CALL DKIMVerifyGetDomain(DKIMContext *pVerifyContext); +void DKIM_CALL DKIMVerifyFree(DKIMContext *pVerifyContext); +char *DKIM_CALL DKIMVersion(); +char *DKIM_CALL DKIMGetErrorString(int ErrorCode); +#include "macros.h" +#ifdef __cplusplus +} +#endif diff -ruN ../netqmail-1.06-original/dkimbase.cpp netqmail-1.06/dkimbase.cpp --- ../netqmail-1.06-original/dkimbase.cpp 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimbase.cpp 2019-06-19 09:46:20.131250010 +0200 @@ -0,0 +1,339 @@ +/* + * $Log: dkimbase.cpp,v $ + * Revision 1.5 2019-06-14 21:24:03+05:30 Cprogrammer + * BUG - honor body length tag in verification + * + * Revision 1.4 2017-09-05 10:58:26+05:30 Cprogrammer + * removed compiler warnings + * + * Revision 1.3 2009-03-26 15:10:32+05:30 Cprogrammer + * fixed indentation + * + * Revision 1.2 2009-03-25 08:37:27+05:30 Cprogrammer + * fixed indentation + * + * Revision 1.1 2009-03-21 08:43:08+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "dkim.h" +#include "dkimbase.h" +#include +#include + +CDKIMBase::CDKIMBase() +{ + m_From = NULL; + m_Sender = NULL; + m_hTag = NULL; + m_hTagSize = 0; + m_hTagPos = 0; + m_Line = NULL; + m_LineSize = 0; + m_LinePos = 0; + m_InHeaders = true; +} + +CDKIMBase::~CDKIMBase() +{ + Free(m_Line); + Free(m_From); + Free(m_Sender); + Free(m_hTag); +} + +int +CDKIMBase::Init(void) +{ + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Alloc - allocate buffer +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::Alloc(char *&szBuffer, int nRequiredSize) +{ + szBuffer = new char[nRequiredSize]; + + return (szBuffer == NULL) ? DKIM_OUT_OF_MEMORY : DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// ReAlloc - extend buffer if necessary, leaving room for future expansion +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ReAlloc(char *&szBuffer, int &nBufferSize, int nRequiredSize) +{ + if (nRequiredSize > nBufferSize) { + char * + newp; + int + nNewSize = nRequiredSize + BUFFER_ALLOC_INCREMENT; + + if (Alloc(newp, nNewSize) == DKIM_SUCCESS) { + if (szBuffer != NULL && nBufferSize > 0) { + memcpy(newp, szBuffer, nBufferSize); + delete[]szBuffer; + } + szBuffer = newp; + nBufferSize = nNewSize; + } else { + return DKIM_OUT_OF_MEMORY; // memory alloc error! + } + } + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Process - split buffers into lines without any CRs or LFs at the end. +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMBase::Free(char *szBuffer) +{ + if (szBuffer) + delete[]szBuffer; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Process - split buffers into lines without any CRs or LFs at the end. +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::Process(char *szBuffer, int nBufLength, bool bEOF) +{ + char *p = szBuffer; + char *e = szBuffer + nBufLength; + + while (p < e) { + if (*p != '\n' && *p != '\r') { + if (m_LinePos >= m_LineSize) { + int nRet = ReAlloc(m_Line, m_LineSize, m_LinePos + 1); + if (nRet != DKIM_SUCCESS) + /* + * How to distinguish between + * DKIM_FINISHED_BODY & DKIM_OUT_OF_MEMORY + */ + return nRet; + } + m_Line[m_LinePos++] = *p; + } else { + if (*p == '\r' && p + 1 < e && *(p + 1) == '\n') + p++; + if (m_InHeaders) { + // process header line + if (m_LinePos == 0) { + m_InHeaders = false; + int Result = ProcessHeaders(); + if (Result != DKIM_SUCCESS) + return Result; + } else { + // append the header to the headers list + if (m_Line[0] != ' ' && m_Line[0] != '\t') { + HeaderList.push_back(string(m_Line, m_LinePos)); + } else { + if (!HeaderList.empty()) { + HeaderList.back().append("\r\n", 2).append(m_Line, m_LinePos); + } else { + // no header to append to... + } + } + } + } else { + // process body line + int Result = ProcessBody(m_Line, m_LinePos, bEOF); + if (Result != DKIM_SUCCESS && Result != DKIM_FINISHED_BODY) { + m_LinePos = 0; + return Result; + } + } + m_LinePos = 0; + } + p++; + } + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessFinal - process leftovers if stopping before the body or mid-line +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ProcessFinal(void) +{ + if (m_LinePos > 0) { + Process((char *) "\r\n", 2, true); + } + + if (m_InHeaders) { + m_InHeaders = false; + ProcessHeaders(); + ProcessBody((char *) "", 0, true); + } + + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessHeaders - process the headers (to be implemented by derived class) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ProcessHeaders() +{ + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessBody - process body line (to be implemented by derived class) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ProcessBody(char *szBuffer, int nBufLength, bool bEOF) +{ + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// RemoveSWSP - remove streaming white space from buffer/string inline +// +//////////////////////////////////////////////////////////////////////////////// + +struct isswsp { + bool + operator() (char ch) { + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); + } +}; + +void CDKIMBase::RemoveSWSP(char *szBuffer) +{ + *remove_if(szBuffer, szBuffer + strlen(szBuffer), isswsp()) = '\0'; +} + +void CDKIMBase::RemoveSWSP(char *pBuffer, int &nBufLength) +{ + nBufLength = remove_if(pBuffer, pBuffer + nBufLength, isswsp()) - pBuffer; +} + +void CDKIMBase::RemoveSWSP(string &sBuffer) +{ + sBuffer.erase(remove_if(sBuffer.begin(), sBuffer.end(), isswsp()), sBuffer.end()); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CompressSWSP - compress streaming white space into single spaces from buffer/string inline +// +////////////////////////////////////////////////////////////////////////////////////////// + +void CDKIMBase::CompressSWSP(char *pBuffer, int &nBufLength) +{ + char *pSrc = pBuffer; + char *pDst = pBuffer; + char *pEnd = pBuffer + nBufLength; + + while (pSrc != pEnd) { + if (isswsp()(*pSrc)) { + do { + ++pSrc; + } while (pSrc != pEnd && isswsp()(*pSrc)); + if (pSrc == pEnd) + break; + *pDst++ = ' '; + } + *pDst++ = *pSrc++; + } + nBufLength = pDst - pBuffer; +} + +void CDKIMBase::CompressSWSP(string &sBuffer) +{ + string::iterator iSrc = sBuffer.begin(); + string::iterator iDst = sBuffer.begin(); + string::iterator iEnd = sBuffer.end(); + + while (iSrc != iEnd) { + if (isswsp()(*iSrc)) { + do { + ++iSrc; + } while (iSrc != iEnd && isswsp()(*iSrc)); + + if (iSrc == iEnd) + break; + *iDst++ = ' '; + } + *iDst++ = *iSrc++; + } + sBuffer.erase(iDst, iEnd); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// RelaxHeader - relax a header field (lower case the name, remove swsp before and after :) +// +// modified 4/21/06 STB to remove white space before colon +// +////////////////////////////////////////////////////////////////////////////////////////// + +string CDKIMBase::RelaxHeader(const string &sHeader) +{ + string sTemp = sHeader; + + CompressSWSP(sTemp); + + int cpos = sTemp.find(':'); + if (cpos == -1) { + // no colon?! + } else { + // lower case the header field name + for (int i = 0; i < cpos; i++) { + if (sTemp[i] >= 'A' && sTemp[i] <= 'Z') + sTemp[i] += 'a' - 'A'; + } + // remove the space after the : + if ((unsigned int) (cpos + 1) < sTemp.length() && sTemp[cpos + 1] == ' ') + sTemp.erase(cpos + 1, 1); + // remove the space before the : + if (cpos > 0 && sTemp[cpos - 1] == ' ') + sTemp.erase(cpos - 1, 1); + } + return sTemp; +} + +void +getversion_dkimbase_cpp() +{ + static char *x = (char *) "$Id: dkimbase.cpp,v 1.5 2019-06-14 21:24:03+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dkimbase.h netqmail-1.06/dkimbase.h --- ../netqmail-1.06-original/dkimbase.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimbase.h 2019-02-27 20:57:13.386025114 +0100 @@ -0,0 +1,72 @@ +/* + * $Log: dkimbase.h,v $ + * Revision 1.1 2009-03-21 08:50:18+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef DKIMBASE_H +#define DKIMBASE_H + +#include +#include +#include + +#define BUFFER_ALLOC_INCREMENT 256 + +#include +#include + +using namespace std; +class CDKIMBase { +public: + + CDKIMBase(); + ~CDKIMBase(); + + int Init(void); + int Process(char *szBuffer, int nBufLength, bool bEOF); + int ProcessFinal(void); + int Alloc(char *&szBuffer, int nRequiredSize); + int ReAlloc(char *&szBuffer, int &nBufferLength, int nRequiredSize); + void Free(char *szBuffer); + static void RemoveSWSP(char *szBuffer); + static void RemoveSWSP(char *pBuffer, int &nBufLength); + static void RemoveSWSP(string & sBuffer); + static void CompressSWSP(char *pBuffer, int &nBufLength); + static void CompressSWSP(string & sBuffer); + static string RelaxHeader(const string & sHeader); + virtual int ProcessHeaders(void); + virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF); + +protected: + char *m_From; + char *m_Sender; + char *m_hTag; + int m_hTagSize; + int m_hTagPos; + char *m_Line; + int m_LineSize; + int m_LinePos; + bool m_InHeaders; + list < string > HeaderList; +}; +#endif /*- DKIMBASE_H */ diff -ruN ../netqmail-1.06-original/dkimdns.cpp netqmail-1.06/dkimdns.cpp --- ../netqmail-1.06-original/dkimdns.cpp 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimdns.cpp 2019-05-23 15:12:30.128092884 +0200 @@ -0,0 +1,329 @@ +/* + * $Log: dns.cpp,v $ + * Revision 1.10 2017-09-05 11:01:06+05:30 Cprogrammer + * removed unused variables + * + * Revision 1.9 2017-09-01 12:46:27+05:30 Cprogrammer + * fixed double free() of dnresult + * + * Revision 1.8 2017-08-09 22:06:33+05:30 Cprogrammer + * fixed resolve() function + * + * Revision 1.7 2017-05-16 12:40:23+05:30 Cprogrammer + * refactored dns_text() function + * + * Revision 1.6 2017-05-10 14:58:06+05:30 Cprogrammer + * increase responselen to 1024 for long text records + * + * Revision 1.5 2017-05-10 12:27:49+05:30 Cprogrammer + * use packetsize > 512 to avoid dkim failures for sites having long txt records (hotmail.com) + * + * Revision 1.4 2009-06-11 13:58:39+05:30 Cprogrammer + * port for DARWIN + * + * Revision 1.3 2009-03-27 19:22:45+05:30 Cprogrammer + * dns functions + * + */ +#include +#include +#include +#include +#include +#ifdef DARWIN +#include +#endif +#include +#include +#include +#include "dkimdns.h" + +static unsigned short +getshort(unsigned char *cp) +{ + return (cp[0] << 8) | cp[1]; +} + +static struct +{ + unsigned char *buf; +} response; +static int responsebuflen = 0; +static int responselen; +static unsigned char *responseend; +static unsigned char *responsepos; +static u_long saveresoptions; +static int numanswers; +static char name[MAXDNAME]; + +static int +resolve(char *domain, int type) +{ + int n, i; + unsigned char *ptr; + + errno = 0; + if (!responsebuflen) { + if ((response.buf = (unsigned char *) malloc(PACKETSZ + 1))) + responsebuflen = PACKETSZ + 1; + else + return DNS_MEM; + } + responselen = res_query(domain, C_IN, type, response.buf, responsebuflen); + if ((responselen >= responsebuflen) || (responselen > 0 && (((HEADER *) response.buf)->tc))) { + if (responsebuflen < 65536) { + if ((ptr = (unsigned char *) realloc((void *) response.buf, 65536))) { + response.buf = ptr; + responsebuflen = 65536; + } else { + free(response.buf); + responsebuflen = 0; + return DNS_MEM; + } + } + saveresoptions = _res.options; + _res.options |= RES_USEVC; + responselen = res_query(domain, C_IN, type, response.buf, responsebuflen); + _res.options = saveresoptions; + } + if (responselen <= 0) { + if (errno == ECONNREFUSED) + return DNS_SOFT; + if (h_errno == TRY_AGAIN) + return DNS_SOFT; + return DNS_HARD; + } + responseend = response.buf + responselen; + responsepos = response.buf + sizeof(HEADER); + n = ntohs(((HEADER *) response.buf)->qdcount); + while (n-- > 0) { + if ((i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME)) < 0) + return DNS_SOFT; + responsepos += i; + i = responseend - responsepos; + if (i < QFIXEDSZ) + return DNS_SOFT; + responsepos += QFIXEDSZ; + } + numanswers = ntohs(((HEADER *) response.buf)->ancount); + return 0; +} + +void +byte_copy(register char *to, register unsigned int n, register char *from) +{ + for (;;) { + if (!n) + return; + *to++ = *from++; + --n; + if (!n) + return; + *to++ = *from++; + --n; + if (!n) + return; + *to++ = *from++; + --n; + if (!n) + return; + *to++ = *from++; + --n; + } +} + +static char *txt; +static int txtlen; + +static int +findtxt(int wanttype, int *txt_strlen) +{ + unsigned short rrtype, rrdlen; + char *ptr; + int i; + + if (numanswers <= 0) + return 2; + --numanswers; + if (responsepos == responseend) + return DNS_SOFT; + + if ((i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME)) < 0) + return DNS_SOFT; + responsepos += i; + + if ((i = responseend - responsepos) < 4 + 3 * 3) + return DNS_SOFT; + + rrtype = getshort(responsepos); + rrdlen = getshort(responsepos + 8); + responsepos += 10; + + *txt_strlen = 0; + if (!txtlen) { + if (!(txt = (char *) malloc(PACKETSZ * 2 * sizeof(char)))) + return DNS_MEM; + txtlen = PACKETSZ * 2; + } + if (rrtype == wanttype) { + unsigned short txtpos; + unsigned char n; + + *txt = 0; + for (txtpos = 0; txtpos < rrdlen; txtpos += n) { + n = responsepos[txtpos++]; + if (n > rrdlen - txtpos) + n = rrdlen - txtpos; + if ((*txt_strlen + n + 1) > txtlen) { + if (!(ptr = (char *) realloc(txt, (*txt_strlen + n) * 2))) { + free(txt); + txtlen = 0; + *txt_strlen = 0; + return DNS_MEM; + } + txt = ptr; + txtlen = (*txt_strlen + n) * 2; + } + byte_copy(txt + *txt_strlen, n, (char *) &responsepos[txtpos]); + *txt_strlen += n; + } + responsepos += rrdlen; + txt[*txt_strlen] = 0; + return 1; + } + responsepos += rrdlen; + return 0; +} + +static char *dnresult; +static int dnresultlen; + +static int +dns_txtplus(char *domain) +{ + int r, len, total; + char *ptr; + + switch (resolve(domain, T_TXT)) + { + case DNS_MEM: + return DNS_MEM; + case DNS_SOFT: + return DNS_SOFT; + case DNS_HARD: + return DNS_HARD; + } + total = 0; + if (!dnresultlen) { + if (!(dnresult = (char *) malloc((2 * PACKETSZ) * sizeof(char)))) + return DNS_MEM; + dnresultlen = 2 * PACKETSZ; + } + while ((r = findtxt(T_TXT, &len)) != 2) { + if (r == DNS_SOFT) { + if (txtlen) { + txtlen = 0; + free(txt); + } + return DNS_SOFT; + } + if (r == 1) { + if ((total + len + 1) >= dnresultlen) { + if (!(ptr = (char *) realloc(dnresult, (total + len) * 2))) { + free(dnresult); + dnresultlen = 0; + if (txtlen) { + txtlen = 0; + free(txt); + } + return DNS_MEM; + } + dnresult = ptr; + dnresultlen = (total + len) * 2; + } + byte_copy(dnresult + total, len, txt); + total += len; + } + } + if (txtlen) { + txtlen = 0; + free(txt); + } + if (total) { + dnresult[total] = 0; + return (0); + } + return DNS_HARD; +} + +/* + * we always return a null-terminated string which has been malloc'ed. The string + * is always in the tag=value form. If a temporary or permanent error occurs, + * the string will be exactly "e=perm;" or "e=temp;". + * Note that it never returns NULL. + */ +char * +dns_text(char *dn) +{ + int r; + + switch (r = dns_txtplus(dn)) + { + case DNS_MEM: + case DNS_SOFT: + if (responsebuflen) { + free(response.buf); + responsebuflen = 0; + } + return strdup("e=temp;"); + case DNS_HARD: + if (responsebuflen) { + free(response.buf); + responsebuflen = 0; + } + return strdup("e=perm;"); + } + if (responsebuflen) { + free(response.buf); + responsebuflen = 0; + } + return dnresult; +} + + +int +DNSGetTXT(const char *domain, char *buffer, int maxlen) +{ + char *results; + int len; + + results = dns_text((char *) domain); + if (!strcmp(results, "e=perm;")) { + free(results); + dnresultlen = 0; + return DNSRESP_PERM_FAIL; + } else + if (!strcmp(results, "e=temp;")) { + free(results); + dnresultlen = 0; + return DNSRESP_TEMP_FAIL; + } + if ((len = strlen(results)) > maxlen - 1) { + free(results); + dnresultlen = 0; + return DNSRESP_DOMAIN_NAME_TOO_LONG; + } + byte_copy(buffer, len, results); + buffer[len] = 0; + free(results); + dnresultlen = 0; + return DNSRESP_SUCCESS; +} + +void +getversion_dkimdns_cpp() +{ + static char *x = (char *) "$Id: dns.cpp,v 1.10 2017-09-05 11:01:06+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dkimdns.h netqmail-1.06/dkimdns.h --- ../netqmail-1.06-original/dkimdns.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimdns.h 2019-02-27 20:57:13.387025103 +0100 @@ -0,0 +1,54 @@ +/* + * $Log: dns.h,v $ + * Revision 1.4 2017-05-16 12:40:39+05:30 Cprogrammer + * define DNS_SOFT, DNS_HARD and DNS_MEM + * + * Revision 1.3 2009-03-27 20:40:16+05:30 Cprogrammer + * removed windows definitions + * + * Revision 1.2 2009-03-27 19:23:01+05:30 Cprogrammer + * added dns_text() + * + * Revision 1.1 2009-03-21 08:50:24+05:30 Cprogrammer + * Initial revision + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// These DNS resolution routines are encapsulated by the API below + +// return values for DNS functions: + + +#define MAX_DOMAIN 254 + +#define DNSRESP_SUCCESS 0 // DNS lookup returned sought after records +#define DNSRESP_TEMP_FAIL 1 // No response from DNS server +#define DNSRESP_PERM_FAIL 2 // DNS server returned error or no records +#define DNSRESP_DOMAIN_NAME_TOO_LONG 3 // Domain name too long +#define DNSRESP_NXDOMAIN 4 // DNS server returned Name Error +#define DNSRESP_EMPTY 5 // DNS server returned successful response but no records + +#define DNS_SOFT -1 +#define DNS_HARD -2 +#define DNS_MEM -3 + +// Pass in the FQDN to get the TXT record +int DNSGetTXT(const char *szFQDN, char *Buffer, int nBufLen); +char *dns_text(char *szFQDN); diff -ruN ../netqmail-1.06-original/dkimfuncs.cpp netqmail-1.06/dkimfuncs.cpp --- ../netqmail-1.06-original/dkimfuncs.cpp 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimfuncs.cpp 2019-02-27 20:57:13.387025103 +0100 @@ -0,0 +1,236 @@ +/* + * $Log: dkimfuncs.cpp,v $ + * Revision 1.4 2011-06-04 10:06:33+05:30 Cprogrammer + * unified error strings for signing & verification + * + * Revision 1.3 2009-04-15 20:45:29+05:30 Cprogrammer + * code beautified + * + * Revision 1.2 2009-03-26 15:11:12+05:30 Cprogrammer + * added GetDomain(), ADSP + * + * Revision 1.1 2009-03-21 08:43:10+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "dkim.h" +#include "dkimsign.h" +#include "dkimverify.h" +#include + +#define DKIMID ('D' | 'K'<<8 | 'I'<<16 | 'M'<<24) + +static void +InitContext(DKIMContext *pContext, bool bSign, void *pObject) +{ + pContext->reserved1 = DKIMID; + pContext->reserved2 = bSign ? 1 : 0; + pContext->reserved3 = pObject; +} + +static void * +ValidateContext(DKIMContext *pContext, bool bSign) +{ + if (pContext->reserved1 != DKIMID) + return NULL; + if (pContext->reserved2 != (unsigned int) (bSign ? 1 : 0)) + return NULL; + return pContext->reserved3; +} + +int DKIM_CALL +DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions) +{ + int nRet = DKIM_OUT_OF_MEMORY; + CDKIMSign *pSign = new CDKIMSign; + + if (pSign) { + nRet = pSign->Init(pOptions); + if (nRet != DKIM_SUCCESS) + delete pSign; + } + if (nRet == DKIM_SUCCESS) + InitContext(pSignContext, true, pSign); + return nRet; +} + +int DKIM_CALL +DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength) +{ + CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true); + if (pSign) + return pSign->Process(szBuffer, nBufLength, false); + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL +DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, int nSigLength) +{ + CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true); + if (pSign) + return pSign->GetSig(szPrivKey, szSignature, nSigLength); + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL +DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature) +{ + CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true); + if (pSign) + return pSign->GetSig2(szPrivKey, pszSignature); + return DKIM_INVALID_CONTEXT; +} + +char *DKIM_CALL +DKIMSignGetDomain(DKIMContext *pSignContext) +{ + CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true); + if (pSign) + return pSign->GetDomain(); + return ((char *) 0); +} + +void DKIM_CALL +DKIMSignFree(DKIMContext *pSignContext) +{ + CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true); + if (pSign) { + delete pSign; + pSignContext->reserved3 = NULL; + } +} + +int DKIM_CALL +DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions) +{ + int nRet = DKIM_OUT_OF_MEMORY; + CDKIMVerify *pVerify = new CDKIMVerify; + if (pVerify) { + nRet = pVerify->Init(pOptions); + if (nRet != DKIM_SUCCESS) + delete pVerify; + } + if (nRet == DKIM_SUCCESS) + InitContext(pVerifyContext, false, pVerify); + return nRet; +} + +int DKIM_CALL +DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength) +{ + CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false); + if (pVerify) + return pVerify->Process(szBuffer, nBufLength, false); + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL +DKIMVerifyResults( DKIMContext *pVerifyContext , int *sCount, int *sSize) +{ + CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false); + if (pVerify) + return pVerify->GetResults(sCount, sSize); + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL +DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices) +{ + szPractices[0] = '\0'; + CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false); + if (pVerify) { + strcpy(szPractices, pVerify->GetPractices()); + return pVerify->GetDetails(nSigCount, pDetails); + } + return DKIM_INVALID_CONTEXT; +} + +void DKIM_CALL +DKIMVerifyFree(DKIMContext *pVerifyContext) +{ + CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false); + if (pVerify) { + delete pVerify; + pVerifyContext->reserved3 = NULL; + } +} + +char *DKIM_CALL +DKIMVerifyGetDomain(DKIMContext *pVerifyContext) +{ + CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false); + if (pVerify) + return pVerify->GetDomain(); + return ((char *) 0); +} + +char *DKIM_CALL +DKIMVersion() +{ + return (char *) "1.5"; +} + +static char *DKIMErrorStrings[-1 - DKIM_MAX_ERROR] = { + (char *) "DKIM_FAIL", + (char *) "DKIM_BAD_SYNTAX", + (char *) "DKIM_SIGNATURE_BAD", + (char *) "DKIM_SIGNATURE_BAD_BUT_TESTING", + (char *) "DKIM_SIGNATURE_EXPIRED", + (char *) "DKIM_SELECTOR_INVALID", + (char *) "DKIM_SELECTOR_GRANULARITY_MISMATCH", + (char *) "DKIM_SELECTOR_KEY_REVOKED", + (char *) "DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG", + (char *) "DKIM_SELECTOR_DNS_TEMP_FAILURE", + (char *) "DKIM_SELECTOR_DNS_PERM_FAILURE", + (char *) "DKIM_SELECTOR_PUBLIC_KEY_INVALID", + (char *) "DKIM_NO_SIGNATURES", + (char *) "DKIM_NO_VALID_SIGNATURES", + (char *) "DKIM_BODY_HASH_MISMATCH", + (char *) "DKIM_SELECTOR_ALGORITHM_MISMATCH", + (char *) "DKIM_STAT_INCOMPAT", + (char *) "DKIM_UNSIGNED_FROM", + (char *) "DKIM_OUT_OF_MEMORY", + (char *) "DKIM_INVALID_CONTEXT", + (char *) "DKIM_NO_SENDER", + (char *) "DKIM_BAD_PRIVATE_KEY", + (char *) "DKIM_BUFFER_TOO_SMALL" +}; + +char *DKIM_CALL +DKIMGetErrorString(int ErrorCode) +{ + if (ErrorCode >= 0 || ErrorCode <= DKIM_MAX_ERROR) + return (char *) "Unknown"; + + else + return DKIMErrorStrings[-1 - ErrorCode]; +} + +void +getversion_dkimfuncs_cpp() +{ + static char *x = (char *) "$Id: dkimfuncs.cpp,v 1.4 2011-06-04 10:06:33+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dkimsign.cpp netqmail-1.06/dkimsign.cpp --- ../netqmail-1.06-original/dkimsign.cpp 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimsign.cpp 2020-04-10 18:20:19.279077900 +0200 @@ -0,0 +1,1029 @@ +/* + * $Log: dkimsign.cpp,v $ + * Revision 1.17 2020-04-10 21:36:20+05:30 Cprogrammer + * fixed BUG with domain assignment + * + * Revision 1.16 2020-04-09 21:21:04+05:30 Cprogrammer + * check for null domain after DKIMDOMAIN replacement + * + * Revision 1.15 2019-06-26 19:08:18+05:30 Cprogrammer + * added sBouncedAddr variable for X-Bounced-Address header added by qmail-send for bounces + * + * Revision 1.14 2019-06-24 22:22:15+05:30 Cprogrammer + * use DKIMDOMAIN only if Return-Path, From, Sender header are empty + * + * Revision 1.13 2018-08-25 18:01:59+05:30 Cprogrammer + * fixed dkim signing for From address containing company name + * + * Revision 1.12 2018-05-23 13:07:58+05:30 Cprogrammer + * fixed compiler warnings + * + * Revision 1.11 2017-09-05 10:59:03+05:30 Cprogrammer + * removed compiler warnings + * + * Revision 1.10 2017-08-09 22:02:13+05:30 Cprogrammer + * replaced EVP_MD_CTX_free() with EVP_MD_CTX_reset() + * + * Revision 1.9 2017-08-08 23:50:19+05:30 Cprogrammer + * openssl 1.1.0 port + * + * Revision 1.8 2013-07-16 20:18:03+05:30 Cprogrammer + * replace '%' with domain name in selector + * + * Revision 1.7 2013-06-11 00:02:39+05:30 Cprogrammer + * removed header iostream + * + * Revision 1.6 2013-06-09 16:41:28+05:30 Cprogrammer + * parse address properly from From and Sender header + * + * Revision 1.5 2009-04-16 10:32:38+05:30 Cprogrammer + * added DKIMDOMAIN env variable + * + * Revision 1.4 2009-04-15 21:32:12+05:30 Cprogrammer + * added DKIM-Signature, Received to list of excluded headers + * + * Revision 1.3 2009-03-26 15:11:46+05:30 Cprogrammer + * added GetDomain + * + * Revision 1.2 2009-03-21 11:57:19+05:30 Cprogrammer + * fixed indentation + * + * Revision 1.1 2009-03-21 08:43:11+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#define _strnicmp strncasecmp +#define _stricmp strcasecmp +#define LOWORD(l) ((unsigned)(l) & 0xffff) +#define HIWORD(l) ((unsigned)(l) >> 16) + +#include +#include +#include "dkim.h" +#include "dkimsign.h" + +CDKIMSign::CDKIMSign() +{ + m_EmptyLineCount = 0; + m_pfnHdrCallback = NULL; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (!m_allman_sha1ctx) + m_allman_sha1ctx = EVP_MD_CTX_new(); + EVP_SignInit(m_allman_sha1ctx, EVP_sha1()); + if (!m_Hdr_ietf_sha1ctx) + m_Hdr_ietf_sha1ctx = EVP_MD_CTX_new(); + EVP_SignInit(m_Hdr_ietf_sha1ctx, EVP_sha1()); + if (!m_Bdy_ietf_sha1ctx) + m_Bdy_ietf_sha1ctx = EVP_MD_CTX_new(); + EVP_DigestInit(m_Bdy_ietf_sha1ctx, EVP_sha1()); +#ifdef HAVE_EVP_SHA256 + if (!m_Hdr_ietf_sha256ctx) + m_Hdr_ietf_sha256ctx = EVP_MD_CTX_new(); + EVP_SignInit(m_Hdr_ietf_sha256ctx, EVP_sha256()); + if (!m_Bdy_ietf_sha256ctx) + m_Bdy_ietf_sha256ctx = EVP_MD_CTX_new(); + EVP_DigestInit(m_Bdy_ietf_sha256ctx, EVP_sha256()); +#endif +#else + EVP_SignInit(&m_allman_sha1ctx, EVP_sha1()); + EVP_SignInit(&m_Hdr_ietf_sha1ctx, EVP_sha1()); + EVP_DigestInit(&m_Bdy_ietf_sha1ctx, EVP_sha1()); +#ifdef HAVE_EVP_SHA256 + EVP_SignInit(&m_Hdr_ietf_sha256ctx, EVP_sha256()); + EVP_DigestInit(&m_Bdy_ietf_sha256ctx, EVP_sha256()); +#endif +#endif +} + +CDKIMSign::~CDKIMSign() +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_MD_CTX_reset(m_allman_sha1ctx); + EVP_MD_CTX_reset(m_Hdr_ietf_sha1ctx); + EVP_MD_CTX_reset(m_Bdy_ietf_sha1ctx); +#ifdef HAVE_EVP_SHA256 + EVP_MD_CTX_reset(m_Hdr_ietf_sha256ctx); + EVP_MD_CTX_reset(m_Bdy_ietf_sha256ctx); +#endif +#else + EVP_MD_CTX_cleanup(&m_allman_sha1ctx); + EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha1ctx); + EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha1ctx); +#ifdef HAVE_EVP_SHA256 + EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha256ctx); + EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha256ctx); +#endif +#endif +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Init - save the options +// +//////////////////////////////////////////////////////////////////////////////// +int +CDKIMSign::Init(DKIMSignOptions * pOptions) +{ + int nRet = CDKIMBase::Init(); + m_Canon = pOptions->nCanon; + +// as of draft 01, these are the only allowed signing types: + if ((m_Canon != DKIM_SIGN_SIMPLE_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED_SIMPLE)) { + m_Canon = DKIM_SIGN_SIMPLE; + } + sSelector.assign(pOptions->szSelector); + m_pfnHdrCallback = pOptions->pfnHeaderCallback; + sDomain.assign(pOptions->szDomain); + m_IncludeBodyLengthTag = (pOptions->nIncludeBodyLengthTag != 0); + m_nBodyLength = 0; + m_ExpireTime = pOptions->expireTime; + sIdentity.assign(pOptions->szIdentity); + m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp; + m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod; + m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders; + m_nIncludeBodyHash = pOptions->nIncludeBodyHash; + +// NOTE: the following line is not backwards compatible with MD 8.0.3 +// because the szRequiredHeaders member was added after the release +//sRequiredHeaders.assign( pOptions->szRequiredHeaders ); + +//make sure there is a colon after the last header in the list + if ((sRequiredHeaders.size() > 0) && sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':') + sRequiredHeaders.append(":"); + m_nHash = pOptions->nHash; + m_bReturnedSigAssembled = false; + m_sCopiedHeaders.erase(); + return nRet; +} + + +// Hash - update the hash +void +CDKIMSign::Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly) +{ + EVP_MD_CTX *p1, *p2, *p3, *p4, *p5; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + p1 = m_allman_sha1ctx; + p2 = m_Hdr_ietf_sha1ctx; + p3 = m_Hdr_ietf_sha256ctx; + p4 = m_Bdy_ietf_sha1ctx; + p5 = m_Bdy_ietf_sha256ctx; +#else + p1 = &m_allman_sha1ctx; + p2 = &m_Hdr_ietf_sha1ctx; + p3 = &m_Hdr_ietf_sha256ctx; + p4 = &m_Bdy_ietf_sha1ctx; + p5 = &m_Bdy_ietf_sha256ctx; +#endif + if (bAllmanOnly) { + if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1) + EVP_SignUpdate(p1, szBuffer, nBufLength); + } else { + if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1) + EVP_SignUpdate(p1, szBuffer, nBufLength); + else + if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) { + if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1) + EVP_SignUpdate(p1, szBuffer, nBufLength); +#ifdef HAVE_EVP_SHA256 + if (m_nHash & DKIM_HASH_SHA256) { + if (bHdr) + EVP_SignUpdate(p3, szBuffer, nBufLength); + else + EVP_DigestUpdate(p5, szBuffer, nBufLength); + } + if (m_nHash != DKIM_HASH_SHA256) { + if (bHdr) + EVP_SignUpdate(p2, szBuffer, nBufLength); + else + EVP_DigestUpdate(p4, szBuffer, nBufLength); + } +#else + if (bHdr) + EVP_SignUpdate(p2, szBuffer, nBufLength); + else + EVP_DigestUpdate(p4, szBuffer, nBufLength); +#endif + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// SignThisTag - return boolean whether or not to sign this tag +// +//////////////////////////////////////////////////////////////////////////////// +bool CDKIMSign::SignThisTag(const string &sTag) +{ + bool bRet = true; + + if (_strnicmp(sTag.c_str(), "X-", 2) == 0 + || _stricmp(sTag.c_str(), "Authentication-Results:") == 0 + || _stricmp(sTag.c_str(), "DKIM-Signature:") == 0 + || _stricmp(sTag.c_str(), "Received:") == 0 + || _stricmp(sTag.c_str(), "Return-Path:") == 0) + { + bRet = false; + } + return bRet; +} + +bool +ConvertHeaderToQuotedPrintable(const char *source, char *dest) +{ + bool bConvert = false; + +// do quoted printable + static unsigned char hexchars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + unsigned char *d = (unsigned char *) dest; + for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++) { + if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') { + *d++ = *s; + } + + else { + bConvert = true; + *d++ = '='; + *d++ = hexchars[*s >> 4]; + *d++ = hexchars[*s & 15]; + } + } + *d = '\0'; + return bConvert; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// GetHeaderParams - Extract any needed header parameters +// +//////////////////////////////////////////////////////////////////////////////// +void +CDKIMSign::GetHeaderParams(const string & sHdr) +{ + if (_strnicmp(sHdr.c_str(), "X-Bounced-Address:", 18) == 0) + sBouncedAddr.assign(sHdr.c_str() + 21); + else + if (_strnicmp(sHdr.c_str(), "X", 1) == 0) + return; + if (_strnicmp(sHdr.c_str(), "From:", 5) == 0) + sFrom.assign(sHdr.c_str() + 5); + if (_strnicmp(sHdr.c_str(), "Sender:", 7) == 0) + sSender.assign(sHdr.c_str() + 7); + if (_strnicmp(sHdr.c_str(), "Return-Path:", 12) == 0) + sReturnPath.assign(sHdr.c_str() + 12); + if (m_nIncludeCopiedHeaders) { + string::size_type pos = sHdr.find(':'); + if (pos != string::npos) { + string sTag, sValue; + char *workBuffer = new char[sHdr.size() * 3 + 1]; + sTag.assign(sHdr.substr(0, pos)); + sValue.assign(sHdr.substr(pos + 1, string::npos)); + ConvertHeaderToQuotedPrintable(sTag.c_str(), workBuffer); + if (!m_sCopiedHeaders.empty()) { + m_sCopiedHeaders.append("|"); + } + m_sCopiedHeaders.append(workBuffer); + m_sCopiedHeaders.append(":"); + ConvertHeaderToQuotedPrintable(sValue.c_str(), workBuffer); + m_sCopiedHeaders.append(workBuffer); + delete[]workBuffer; + } + } +} + +// ProcessHeaders - sign headers and save needed parameters +int +CDKIMSign::ProcessHeaders(void) +{ + map ::reverse_iterator> IterMap; + map ::reverse_iterator>::iterator IterMapIter; + list ::reverse_iterator riter; + list ::iterator iter; + string sTag; + bool bFromHeaderFound = false; + + // walk the header list + for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) { + sTag.assign(*iter); + // look for a colon + string::size_type pos = sTag.find(':'); + if (pos != string::npos) { + int nSignThisTag = 1; + // hack off anything past the colon + sTag.erase(pos + 1, string::npos); + // is this the From: header? + if (_stricmp(sTag.c_str(), "From:") == 0) { + bFromHeaderFound = true; + nSignThisTag = 1; + IsRequiredHeader(sTag); // remove from required header list + } + // is this in the list of headers that must be signed? + else + if (IsRequiredHeader(sTag)) + nSignThisTag = 1; + else { + if (m_pfnHdrCallback) + nSignThisTag = m_pfnHdrCallback(iter->c_str()); + else + nSignThisTag = SignThisTag(sTag) ? 1 : 0; + } + // save header parameters + GetHeaderParams(*iter); + if (nSignThisTag > 0) { + // add this tag to h= + hParam.append(sTag); + IterMapIter = IterMap.find(sTag); + riter = (IterMapIter == IterMap.end())? HeaderList.rbegin() : IterMapIter->second; + // walk the list in reverse looking for the last instance of this header + while (riter != HeaderList.rend()) { + if (_strnicmp(riter->c_str(), sTag.c_str(), sTag.size()) == 0) { + ProcessHeader(*riter); + // save the reverse iterator position for this tag + riter++; + IterMap[sTag] = riter; + break; + } + riter++; + } + } + } + } + Hash("\r\n", 2, true, true); // only for Allman sig + if (!bFromHeaderFound) { + string sFrom("From:"); + hParam.append(sFrom); + IsRequiredHeader(sFrom); // remove from required header list + } + hParam.append(sRequiredHeaders); + if (hParam.at(hParam.size() - 1) == ':') + hParam.erase(hParam.size() - 1, string::npos); + return DKIM_SUCCESS; +} + +char *DKIM_CALL +CDKIMSign::GetDomain(void) +{ + if (ParseFromAddress() == false) + return ((char *) 0); + return ((char *) sDomain.c_str()); +} + +void +CDKIMSign::ProcessHeader(const string & sHdr) +{ + switch (HIWORD(m_Canon)) { + case DKIM_CANON_SIMPLE: + Hash(sHdr.c_str(), sHdr.size(), true); + Hash("\r\n", 2, true); + break; + case DKIM_CANON_NOWSP: + { + string sTemp = sHdr; + RemoveSWSP(sTemp); + // convert characters before ':' to lower case + for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) { + if (*s >= 'A' && *s <= 'Z') + *s += 'a' - 'A'; + } + Hash(sTemp.c_str(), sTemp.size(), true); + Hash("\r\n", 2, true); + } + break; + case DKIM_CANON_RELAXED: + { + string sTemp = RelaxHeader(sHdr); + Hash(sTemp.c_str(), sTemp.length(), true); + Hash("\r\n", 2, true); + } + break; + } +} + +int CDKIMSign::ProcessBody(char *szBuffer, int nBufLength, bool bEOF) +{ + switch (LOWORD(m_Canon)) { + case DKIM_CANON_SIMPLE: + if (nBufLength > 0) { + while (m_EmptyLineCount > 0) { + Hash("\r\n", 2, false); + m_nBodyLength += 2; + m_EmptyLineCount--; + } + Hash(szBuffer, nBufLength, false); + Hash("\r\n", 2, false); + m_nBodyLength += nBufLength + 2; + } else { + m_EmptyLineCount++; + if (bEOF) { + Hash("\r\n", 2, false); + m_nBodyLength += 2; + } + } + break; + case DKIM_CANON_NOWSP: + RemoveSWSP(szBuffer, nBufLength); + if (nBufLength > 0) { + Hash(szBuffer, nBufLength, false); + m_nBodyLength += nBufLength; + } + break; + case DKIM_CANON_RELAXED: + CompressSWSP(szBuffer, nBufLength); + if (nBufLength > 0) { + while (m_EmptyLineCount > 0) { + Hash("\r\n", 2, false); + m_nBodyLength += 2; + m_EmptyLineCount--; + } + Hash(szBuffer, nBufLength, false); + m_nBodyLength += nBufLength; + if (!bEOF) { + Hash("\r\n", 2, false); + m_nBodyLength += 2; + } + } else + m_EmptyLineCount++; + break; + } + return DKIM_SUCCESS; +} + +bool CDKIMSign::ParseFromAddress(void) +{ + string::size_type pos; + string sAddress; + char *p, *at; + + /* thanks to fred */ + if (!sReturnPath.empty()) + sAddress.assign(sReturnPath); + else + if (!sSender.empty()) + sAddress.assign(sSender); + else + if (!sFrom.empty()) + sAddress.assign(sFrom); + else /* use indimail's X-Bounced-Address header to find the domain that injected the bounce */ + if (!sBouncedAddr.empty()) + sAddress.assign(sBouncedAddr); + else + return false; + /*- + * simple for now, beef it up later + * remove '<' and anything before it + */ + pos = sAddress.find('<'); + if (pos != string::npos) + sAddress.erase(0, pos + 1); + + /* remove '>' and anything after it */ + pos = sAddress.find('>'); + if (pos != string::npos) + sAddress.erase(pos, string::npos); + /* look for '@' symbol */ + if (sDomain.empty()) { + pos = sAddress.find('@'); + if (pos != string::npos) + sDomain.assign(sAddress.c_str() + pos + 1); + if (sDomain.empty()) { + p = getenv("DKIMDOMAIN"); + if (p && *p) { + if (!(at = strchr(p, '@'))) + at = p; + else + at++; + sDomain.assign(at); + } + } + } + RemoveSWSP(sDomain); + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// InitSig - initialize signature folding algorithm +// +//////////////////////////////////////////////////////////////////////////////// +void +CDKIMSign::InitSig(void) +{ + m_sSig.reserve(1024); + m_sSig.assign("DKIM-Signature:"); + m_nSigPos = m_sSig.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AddTagToSig - add tag and value to signature folding if necessary +// if bFold, fold at cbrk char +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold) +{ + int + nTagLen = strlen(Tag); + AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2); + m_sSig.append(Tag); + m_sSig.append("="); + m_nSigPos += 1 + nTagLen; + if (!bFold) { + m_sSig.append(sValue); + m_nSigPos += sValue.size(); + } + + else { + AddFoldedValueToSig(sValue, cbrk); + } + m_sSig.append(";"); + m_nSigPos++; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// AddTagToSig - add tag and numeric value to signature folding if necessary +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddTagToSig(char *Tag, unsigned long nValue) +{ + char szValue[64]; + sprintf(szValue, "%lu", nValue); + AddTagToSig(Tag, szValue, 0, false); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AddInterTagSpace - add space or fold here +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag) +{ + if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) { + m_sSig.append("\n\t"); + m_nSigPos = 1; + } + + else { + m_sSig.append(" "); + m_nSigPos++; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// AddTagToSig - add value to signature folding if necessary +// if cbrk == 0 fold anywhere, otherwise fold only at cbrk +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddFoldedValueToSig(const string & sValue, char cbrk) +{ + string::size_type pos = 0; + if (cbrk == 0) { + + // fold anywhere + while (pos < sValue.size()) { + string::size_type len = OptimalHeaderLineLength - m_nSigPos; + if (len > sValue.size() - pos) + len = sValue.size() - pos; + m_sSig.append(sValue.substr(pos, len)); + m_nSigPos += len; + pos += len; + if (pos < sValue.size()) { + m_sSig.append("\n\t"); + m_nSigPos = 1; + } + } + } + + else { + + // fold only at cbrk + while (pos < sValue.size()) { + string::size_type len = OptimalHeaderLineLength - m_nSigPos; + string::size_type brkpos; + if (sValue.size() - pos < len) { + brkpos = sValue.size() - 1; + } + + else { + brkpos = sValue.rfind(cbrk, pos + len); + } + if (brkpos == string::npos || brkpos < pos) { + brkpos = sValue.find(cbrk, pos); + if (brkpos == string::npos) { + brkpos = sValue.size(); + } + } + len = brkpos - pos + 1; + m_sSig.append(sValue.substr(pos, len)); + m_nSigPos += len; + pos += len; + if (pos < sValue.size()) { + + //m_sSig.append( "\r\n\t" ); + m_sSig.append("\n\t"); + m_nSigPos = 1; + } + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// GetSig - compute hash and return signature header in szSignature +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::GetSig(char *szPrivKey, char *szSignature, unsigned int nSigLength) +{ + if (szPrivKey == NULL) { + return DKIM_BAD_PRIVATE_KEY; + } + if (szSignature == NULL) { + return DKIM_BUFFER_TOO_SMALL; + } + int nRet = AssembleReturnedSig(szPrivKey); + if (nRet != DKIM_SUCCESS) + return nRet; + if (m_sReturnedSig.size() + 1 < nSigLength) + strcpy(szSignature, m_sReturnedSig.c_str()); + else + return DKIM_BUFFER_TOO_SMALL; + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// GetSig - compute hash and return signature header in szSignature +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::GetSig2(char *szPrivKey, char **pszSignature) +{ + if (szPrivKey == NULL) { + return DKIM_BAD_PRIVATE_KEY; + } + if (pszSignature == NULL) { + return DKIM_BUFFER_TOO_SMALL; + } + int nRet = AssembleReturnedSig(szPrivKey); + if (nRet != DKIM_SUCCESS) + return nRet; + *pszSignature = (char *) m_sReturnedSig.c_str(); + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// IsRequiredHeader - Check if header in required list. If so, delete +// header from list. +// +//////////////////////////////////////////////////////////////////////////////// +bool CDKIMSign::IsRequiredHeader(const string & sTag) +{ + string::size_type start = 0; + string::size_type end = sRequiredHeaders.find(':'); + while (end != string::npos) { + + // check for a zero-length header + if (start == end) { + sRequiredHeaders.erase(start, 1); + } + + else { + if (_stricmp(sTag.c_str(), sRequiredHeaders.substr(start, end - start + 1).c_str()) == 0) { + sRequiredHeaders.erase(start, end - start + 1); + return true; + } + + else { + start = end + 1; + } + } + end = sRequiredHeaders.find(':', start); + } + return false; +} + +int CDKIMSign::ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256) +{ + string sSignedSig; + unsigned char *sig; + EVP_PKEY *pkey; + BIO *bio, *b64; + unsigned int siglen; + int size, len; + char *buf, *cptr; + const char *ptr, *dptr, *sptr; + EVP_MD_CTX *p1, *p2, *p3, *p4, *p5; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + p1 = m_allman_sha1ctx; + p2 = m_Hdr_ietf_sha1ctx; + p3 = m_Hdr_ietf_sha256ctx; + p4 = m_Bdy_ietf_sha1ctx; + p5 = m_Bdy_ietf_sha256ctx; +#else + p1 = &m_allman_sha1ctx; + p2 = &m_Hdr_ietf_sha1ctx; + p3 = &m_Hdr_ietf_sha256ctx; + p4 = &m_Bdy_ietf_sha1ctx; + p5 = &m_Bdy_ietf_sha256ctx; +#endif + +// construct the DKIM-Signature: header and add to hash + InitSig(); + if (bUseIetfBodyHash) { + AddTagToSig((char *) "v", (char *) "1", 0, false); + } +#ifdef HAVE_EVP_SHA256 + AddTagToSig((char *) "a", bUseSha256 ? "rsa-sha256" : "rsa-sha1", 0, false); +#else + AddTagToSig((char *) "a", "rsa-sha1", 0, false); +#endif + switch (m_Canon) { + case DKIM_SIGN_SIMPLE: + AddTagToSig((char *) "c", "simple", 0, false); + break; + case DKIM_SIGN_SIMPLE_RELAXED: + AddTagToSig((char *) "c", "simple/relaxed", 0, false); + break; + case DKIM_SIGN_RELAXED: + AddTagToSig((char *) "c", "relaxed/relaxed", 0, false); + break; + case DKIM_SIGN_RELAXED_SIMPLE: + AddTagToSig((char *) "c", "relaxed", 0, false); + break; + } + AddTagToSig((char *) "d", sDomain, 0, false); + /*- replace % with domain name */ + ptr = sSelector.c_str(); + if ((sptr = strchr(ptr, '%'))) { + dptr = sDomain.c_str(); + for (sptr = ptr, len = 0;*sptr;sptr++) { + if (*sptr == '%') + len += (int) strlen(dptr) + 1; + else + len++; + } + if (!(buf = new char[len])) + return DKIM_OUT_OF_MEMORY; + for (cptr = buf, sptr = ptr; *sptr; sptr++) { + if (*sptr == '%') { + memcpy(cptr, dptr, (len = strlen(dptr))); + cptr += len; + } else + *cptr++ = *sptr; + } + *cptr = 0; + sSelector.assign(buf); + delete[]buf; + } + AddTagToSig((char *) "s", sSelector, 0, false); + if (m_IncludeBodyLengthTag) { + AddTagToSig((char *) "l", m_nBodyLength); + } + if (m_nIncludeTimeStamp != 0) { + time_t t; + time(&t); + AddTagToSig((char *) "t", t); + } + if (m_ExpireTime != 0) { + AddTagToSig((char *) "x", m_ExpireTime); + } + if (!sIdentity.empty()) { + AddTagToSig((char *) "i", sIdentity, 0, false); + } + if (m_nIncludeQueryMethod) { + AddTagToSig((char *) "q", bUseIetfBodyHash ? "dns/txt" : "dns", 0, false); + } + AddTagToSig((char *) "h", hParam, ':', true); + if (m_nIncludeCopiedHeaders) { + AddTagToSig((char *) "z", m_sCopiedHeaders, 0, true); + } + if (bUseIetfBodyHash) { + unsigned char Hash[EVP_MAX_MD_SIZE]; + unsigned int nHashLen = 0; +#ifdef HAVE_EVP_SHA256 + EVP_DigestFinal(bUseSha256 ? p5 : p4, Hash, &nHashLen); +#else + EVP_DigestFinal(p4, Hash, &nHashLen); +#endif + bio = BIO_new(BIO_s_mem()); + if (!bio) { + return DKIM_OUT_OF_MEMORY; + } + b64 = BIO_new(BIO_f_base64()); + if (!b64) { + BIO_free(bio); + return DKIM_OUT_OF_MEMORY; + } + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(b64, bio); + if (BIO_write(b64, Hash, nHashLen) < (int) nHashLen) { + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + BIO_flush(b64); + len = nHashLen * 2; + buf = new char[len]; + if (buf == NULL) { + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + size = BIO_read(bio, buf, len); + BIO_free_all(b64); + // this should never happen + if (size >= len) { + delete[]buf; + return DKIM_OUT_OF_MEMORY; + } + buf[size] = '\0'; + AddTagToSig((char *) "bh", buf, 0, true); + delete[]buf; + } + AddInterTagSpace(3); + m_sSig.append("b="); + m_nSigPos += 2; + // Force a full copy - no reference copies please + sSignedSig.assign(m_sSig.c_str()); + // note that since we're not calling hash here, need to dump this + // to the debug file if you want the full canonical form + string sTemp; + if (HIWORD(m_Canon) == DKIM_CANON_RELAXED) + sTemp = RelaxHeader(sSignedSig); + else + sTemp = sSignedSig.c_str(); + if (bUseIetfBodyHash) { +#ifdef HAVE_EVP_SHA256 + EVP_SignUpdate(bUseSha256 ? p3 : p2, sTemp.c_str(), sTemp.size()); +#else + EVP_SignUpdate(p2, sTemp.c_str(), sTemp.size()); +#endif + } else + EVP_SignUpdate(p1, sTemp.c_str(), sTemp.size()); + if (!(bio = BIO_new_mem_buf(szPrivKey, -1))) + return DKIM_OUT_OF_MEMORY; + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!pkey) { + return DKIM_BAD_PRIVATE_KEY; + } + siglen = EVP_PKEY_size(pkey); + int nSignRet; + sig = (unsigned char *) OPENSSL_malloc(siglen); + if (sig == NULL) { + EVP_PKEY_free(pkey); + return DKIM_OUT_OF_MEMORY; + } + if (bUseIetfBodyHash) { +#ifdef HAVE_EVP_SHA256 + nSignRet = EVP_SignFinal(bUseSha256 ? p3 : p2, sig, &siglen, pkey); +#else + nSignRet = EVP_SignFinal(p2, sig, &siglen, pkey); +#endif + } else + nSignRet = EVP_SignFinal(p1, sig, &siglen, pkey); + EVP_PKEY_free(pkey); + if (!nSignRet) { + OPENSSL_free(sig); + return DKIM_BAD_PRIVATE_KEY; // key too small + } + bio = BIO_new(BIO_s_mem()); + if (!bio) { + return DKIM_OUT_OF_MEMORY; + } + b64 = BIO_new(BIO_f_base64()); + if (!b64) { + BIO_free(bio); + return DKIM_OUT_OF_MEMORY; + } + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_push(b64, bio); + if (BIO_write(b64, sig, siglen) < (int) siglen) { + OPENSSL_free(sig); + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + BIO_flush(b64); + OPENSSL_free(sig); + len = siglen * 2; + buf = new char[len]; + if (buf == NULL) { + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + size = BIO_read(bio, buf, len); + BIO_free_all(b64); + // this should never happen + if (size >= len) { + delete[]buf; + return DKIM_OUT_OF_MEMORY; + } + buf[size] = '\0'; + AddFoldedValueToSig(buf, 0); + delete[]buf; + return DKIM_SUCCESS; +} + +int CDKIMSign::AssembleReturnedSig(char *szPrivKey) +{ + int nRet; + if (m_bReturnedSigAssembled) + return DKIM_SUCCESS; + ProcessFinal(); + if (ParseFromAddress() == false) { + //return DKIM_NO_SENDER; + } + Hash("\r\n", 2, true, true); // only for Allman sig + string allmansha1sig, +#ifdef HAVE_EVP_SHA256 + ietfsha256Sig, +#endif + ietfsha1Sig; + if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1) { + nRet = ConstructSignature(szPrivKey, false, false); + if (nRet == DKIM_SUCCESS) + allmansha1sig.assign(m_sSig); + else + return nRet; + } else + if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) { + if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1) { + if ((nRet = ConstructSignature(szPrivKey, false, false)) == DKIM_SUCCESS) + allmansha1sig.assign(m_sSig); + else + return nRet; + } +#ifdef HAVE_EVP_SHA256 + if (m_nHash & DKIM_HASH_SHA256) { + if ((nRet = ConstructSignature(szPrivKey, true, true)) == DKIM_SUCCESS) + ietfsha256Sig.assign(m_sSig); + else + return nRet; + } + if (m_nHash != DKIM_HASH_SHA256) { + if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS) + ietfsha1Sig.assign(m_sSig); + else + return nRet; + } +#else + if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS) + ietfsha1Sig.assign(m_sSig); + else + return nRet; +#endif + } + m_sReturnedSig.assign(allmansha1sig); + if (!ietfsha1Sig.empty()) { + if (!m_sReturnedSig.empty()) + m_sReturnedSig.append("\n"); + m_sReturnedSig.append(ietfsha1Sig); + } +#ifdef HAVE_EVP_SHA256 + if (!ietfsha256Sig.empty()) { + if (!m_sReturnedSig.empty()) + m_sReturnedSig.append("\n"); + m_sReturnedSig.append(ietfsha256Sig); + } +#endif + m_bReturnedSigAssembled = true; + return DKIM_SUCCESS; +} + +void +getversion_dkimsign_cpp() +{ + static char *x = (char *) "$Id: dkimsign.cpp,v 1.17 2020-04-10 21:36:20+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dkimsign.h netqmail-1.06/dkimsign.h --- ../netqmail-1.06-original/dkimsign.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimsign.h 2020-04-10 18:39:39.483427628 +0200 @@ -0,0 +1,115 @@ +/* + * $Log: dkimsign.h,v $ + * Revision 1.5 2019-06-26 19:09:07+05:30 Cprogrammer + * added sBouncedAddr variable for X-Bounced-Address header added by qmail-send for bounces + * + * Revision 1.4 2017-09-05 10:59:20+05:30 Cprogrammer + * removed compiler warnings + * + * Revision 1.3 2017-08-09 22:03:09+05:30 Cprogrammer + * initialized EVP_MD_CTX variables + * + * Revision 1.2 2017-08-08 23:50:33+05:30 Cprogrammer + * openssl 1.1.0 port + * + * Revision 1.1 2009-04-16 10:34:02+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef DKIMSIGN_H +#define DKIMSIGN_H + +#include "dkimbase.h" + +class CDKIMSign:public CDKIMBase { +public: + + CDKIMSign(); + ~CDKIMSign(); + int Init(DKIMSignOptions * pOptions); + int GetSig(char *szPrivKey, char *szSignature, unsigned int nSigLength); + int GetSig2(char *szPrivKey, char **pszSignature); + virtual int ProcessHeaders(void); + virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF); + enum CKDKIMConstants { OptimalHeaderLineLength = 65 }; + char *DKIM_CALL GetDomain(void); + +protected: + void Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly = false); + bool SignThisTag(const string & sTag); + void GetHeaderParams(const string & sHdr); + void ProcessHeader(const string & sHdr); + bool ParseFromAddress(void); + void InitSig(void); + void AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold); + void AddTagToSig(char *Tag, unsigned long nValue); + void AddInterTagSpace(int nSizeOfNextTag); + void AddFoldedValueToSig(const string & sValue, char cbrk); + bool IsRequiredHeader(const string & sTag); + int ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256); + int AssembleReturnedSig(char *szPrivKey); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_MD_CTX *m_Hdr_ietf_sha1ctx = NULL; /* the header hash for ietf sha1 */ + EVP_MD_CTX *m_Bdy_ietf_sha1ctx = NULL; /* the body hash for ietf sha1 */ +#ifdef HAVE_EVP_SHA256 + EVP_MD_CTX *m_Hdr_ietf_sha256ctx = NULL; /* the header hash for ietf sha256 */ + EVP_MD_CTX *m_Bdy_ietf_sha256ctx = NULL; /* the body hash for ietf sha256 */ +#endif + EVP_MD_CTX *m_allman_sha1ctx = NULL; /* the hash for allman sha1 */ +#else + EVP_MD_CTX m_Hdr_ietf_sha1ctx; /* the header hash for ietf sha1 */ + EVP_MD_CTX m_Bdy_ietf_sha1ctx; /* the body hash for ietf sha1 */ +#ifdef HAVE_EVP_SHA256 + EVP_MD_CTX m_Hdr_ietf_sha256ctx; /* the header hash for ietf sha256 */ + EVP_MD_CTX m_Bdy_ietf_sha256ctx; /* the body hash for ietf sha256 */ +#endif + EVP_MD_CTX m_allman_sha1ctx; /* the hash for allman sha1 */ +#endif + int m_Canon; // canonization method + int m_EmptyLineCount; + string hParam; + string sFrom; + string sSender; + string sSelector; + string sReturnPath; + string sBouncedAddr; /*- used for bounces */ + string sDomain; + string sIdentity; // for i= tag, if empty tag will not be included in sig + string sRequiredHeaders; + bool m_IncludeBodyLengthTag; + int m_nBodyLength; + time_t m_ExpireTime; + int m_nIncludeTimeStamp; // 0 = don't include t= tag, 1 = include t= tag + int m_nIncludeQueryMethod; // 0 = don't include q= tag, 1 = include q= tag + int m_nHash; // use one of the DKIM_HASH_xx constants here + int m_nIncludeCopiedHeaders; // 0 = don't include z= tag, 1 = include z= tag + int m_nIncludeBodyHash; // 0 = calculate sig using draft 0, 1 = include bh= tag and + // use new signature computation algorithm + DKIMHEADERCALLBACK m_pfnHdrCallback; + string m_sSig; + int m_nSigPos; + string m_sReturnedSig; + bool m_bReturnedSigAssembled; + string m_sCopiedHeaders; +}; + +#endif /*- DKIMSIGN_H */ diff -ruN ../netqmail-1.06-original/dkimverify.cpp netqmail-1.06/dkimverify.cpp --- ../netqmail-1.06-original/dkimverify.cpp 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimverify.cpp 2019-06-19 09:47:20.017583230 +0200 @@ -0,0 +1,1303 @@ +/* + * $Log: dkimverify.cpp,v $ + * Revision 1.23 2019-05-22 11:29:09+05:30 Cprogrammer + * fix for 32 bit systems where time_t is 4 bytes & encounters year 2038 issue + * + * Revision 1.22 2019-05-21 22:27:17+05:30 Cprogrammer + * increased buffer size + * + * Revision 1.21 2019-02-17 11:32:05+05:30 Cprogrammer + * made scope of sFromDomain static + * + * Revision 1.20 2018-12-14 11:05:20+05:30 Cprogrammer + * fixed 'conversion from 'int' to 'char' inside { }†for cross compiling on arm + * + * Revision 1.19 2018-08-08 23:56:27+05:30 Cprogrammer + * changed comment style + * + * Revision 1.18 2017-09-05 11:00:33+05:30 Cprogrammer + * removed extra whitespace + * + * Revision 1.17 2017-09-03 14:02:04+05:30 Cprogrammer + * call EVP_MD_CTX_init() only once + * + * Revision 1.16 2017-09-01 12:46:05+05:30 Cprogrammer + * removed dkimd2i_PUBKEY function + * + * Revision 1.15 2017-08-31 17:04:34+05:30 Cprogrammer + * replaced d2i_PUBKEY() with dkimd2i_PUBKEY() to avoid SIGSEGV on X509_PUBKEY_free() + * + * Revision 1.14 2017-08-09 21:59:39+05:30 Cprogrammer + * fixed segmentation fault. Use EVP_MD_CTX_reset() instead of EVP_MD_CTX_free() + * + * Revision 1.13 2017-08-08 23:50:41+05:30 Cprogrammer + * openssl 1.1.0 port + * + * Revision 1.12 2017-05-23 09:23:45+05:30 Cprogrammer + * use strtok_r instead of strtok() for thread safe operation + * + * Revision 1.11 2016-03-01 16:24:00+05:30 Cprogrammer + * reverse value of m_SubjectIsRequired + * + * Revision 1.10 2015-12-15 16:05:00+05:30 Cprogrammer + * fixed issue with time comparision. Use time_t for time variables + * + * Revision 1.9 2011-06-04 10:05:01+05:30 Cprogrammer + * added signature and identity domain information to + * DKIMVerifyDetails structure + * + * Revision 1.8 2011-06-04 09:37:13+05:30 Cprogrammer + * added AllowUnsignedFromHeaders + * + * Revision 1.7 2009-06-11 13:58:34+05:30 Cprogrammer + * port for DARWIN + * + * Revision 1.6 2009-05-31 21:09:29+05:30 Cprogrammer + * changed cast + * + * Revision 1.5 2009-03-27 20:19:58+05:30 Cprogrammer + * added ADSP code + * + * Revision 1.4 2009-03-26 15:12:05+05:30 Cprogrammer + * added ADSP code + * + * Revision 1.3 2009-03-25 08:38:20+05:30 Cprogrammer + * fixed indentation + * + * Revision 1.2 2009-03-21 11:57:40+05:30 Cprogrammer + * fixed indentation + * + * Revision 1.1 2009-03-21 08:43:13+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "time_t_size.h" +#define _strnicmp strncasecmp +#define _stricmp strcasecmp +#include +#include +#include +#include +#include +#include "dkim.h" +#include "dkimverify.h" +#include "dkimdns.h" + +#define MAX_SIGNATURES 10 /*- maximum number of DKIM signatures to process in a message */ + +SignatureInfo::SignatureInfo(bool s) +{ + VerifiedBodyCount = 0; + UnverifiedBodyCount = 0; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (m_Hdr_ctx) + EVP_MD_CTX_init(m_Hdr_ctx); + else + m_Hdr_ctx = EVP_MD_CTX_new(); + if (m_Bdy_ctx) + EVP_MD_CTX_init(m_Bdy_ctx); + else + m_Bdy_ctx = EVP_MD_CTX_new(); +#else + EVP_MD_CTX_init(&m_Hdr_ctx); + EVP_MD_CTX_init(&m_Bdy_ctx); +#endif + m_pSelector = NULL; + Status = DKIM_SUCCESS; + m_nHash = 0; + EmptyLineCount = 0; + m_SaveCanonicalizedData = s; +} + +SignatureInfo::~SignatureInfo() +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (m_Hdr_ctx) + EVP_MD_CTX_reset(m_Hdr_ctx); + if (m_Bdy_ctx) + EVP_MD_CTX_reset(m_Bdy_ctx); +#else + EVP_MD_CTX_cleanup(&m_Hdr_ctx); + EVP_MD_CTX_cleanup(&m_Bdy_ctx); +#endif +} + +inline bool +isswsp(char ch) +{ + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Parse a DKIM tag-list. Returns true for success +// +//////////////////////////////////////////////////////////////////////////////// +bool +ParseTagValueList(char *tagvaluelist, const char *wanted[], char *values[]) +{ + char *s = tagvaluelist; + + for (;;) { + /* skip whitespace */ + while (isswsp(*s)) + s++; + /* if at the end of the string, return success. note: this allows a list with no entries */ + if (*s == '\0') + return true; + /*- get tag name -*/ + if (!isalpha(*s)) + return false; + char *tag = s; + do { + s++; + } while (isalnum(*s) || *s == '-'); + char *endtag = s; + /*- skip whitespace before equals -*/ + while (isswsp(*s)) + s++; + /*- next character must be equals -*/ + if (*s != '=') + return false; + s++; + /*- null-terminate tag name -*/ + *endtag = '\0'; + /*- skip whitespace after equals -*/ + while (isswsp(*s)) + s++; + /*- get tag value -*/ + char *value = s; + while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~'))) + s++; + char *e = s; + /*- make sure the next character is the null terminator (which means we're done) or a semicolon (not done) -*/ + bool done = false; + if (*s == '\0') + done = true; + else { + if (*s != ';') + return false; + s++; + } + /*- skip backwards past any trailing whitespace -*/ + while (e > value && isswsp(e[-1])) + e--; + /*- null-terminate tag value -*/ + *e = '\0'; + /*- check to see if we want this tag -*/ + for (unsigned i = 0; wanted[i] != NULL; i++) { + if (strcmp(wanted[i], tag) == 0) { + /*- return failure if we already have a value for this tag (duplicates not allowed) -*/ + if (values[i] != NULL) + return false; + values[i] = value; + break; + } + } + if (done) + return true; + } +} + +/*- Convert hex char to value (0-15) -*/ +char +tohex(char ch) +{ + if (ch >= '0' && ch <= '9') + return (ch - '0'); + else + if (ch >= 'A' && ch <= 'F') + return (ch - 'A' + 10); + else + if (ch >= 'a' && ch <= 'f') + return (ch - 'a' + 10); + else { + assert(0); + return 0; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Decode quoted printable string in-place +// +//////////////////////////////////////////////////////////////////////////////// +void +DecodeQuotedPrintable(char *ptr) +{ + char *s = ptr; + while (*s != '\0' && *s != '=') + s++; + if (*s == '\0') + return; + char *d = s; + do { + if (*s == '=' && isxdigit(s[1]) && isxdigit(s[2])) { + *d++ = (tohex(s[1]) << 4) | tohex(s[2]); + s += 3; + } else { + *d++ = *s++; + } + } while (*s != '\0'); + *d = '\0'; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Decode base64 string in-place, returns number of bytes output +// +//////////////////////////////////////////////////////////////////////////////// +unsigned +DecodeBase64(char *ptr) +{ + static const signed char base64_table[256] = + { -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, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, + -1, -1, + -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, + -1, -1, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -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, -1, -1, -1, -1, -1 + }; + unsigned char *s = (unsigned char *) ptr; + unsigned char *d = (unsigned char *) ptr; + unsigned b64accum = 0; + unsigned char b64shift = 0; + while (*s != '\0') { + unsigned char value = base64_table[*s++]; + if ((signed char) value >= 0) { + b64accum = (b64accum << 6) | value; + b64shift += 6; + if (b64shift >= 8) { + b64shift -= 8; + *d++ = (b64accum >> b64shift); + } + } + } + return (char *) d - ptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Match a string with a pattern (used for g= value) +// Supports a single, optional "*" wildcard character. +// +//////////////////////////////////////////////////////////////////////////////// +bool +WildcardMatch(const char *p, const char *s) +{ + /*- special case: An empty "g=" value never matches any addresses -*/ + if (*p == '\0') + return false; + const char *wildcard = strchr(p, '*'); + if (wildcard == NULL) + return strcmp(s, p) == 0; + else { + unsigned beforewildcardlen = wildcard - p; + unsigned afterwildcardlen = strlen(wildcard + 1); + unsigned slen = strlen(s); + return (slen >= beforewildcardlen + afterwildcardlen) && (strncmp(s, p, beforewildcardlen) == 0) + && strcmp(s + slen - afterwildcardlen, wildcard + 1) == 0; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Parse addresses from a string. Returns true if at least one address found +// +//////////////////////////////////////////////////////////////////////////////// +bool +ParseAddresses(string str, vector < string > &Addresses) +{ + char *s = (char *) str.c_str(); + while (*s != '\0') { + char *start = s; + char *from = s; + char *to = s; + char *lt = NULL; /*- pointer to less than character (<) which starts the address if found */ + while (*from != '\0') { + if (*from == '(') { + /*- skip over comment -*/ + from++; + for (int depth = 1; depth != 0; from++) { + if (*from == '\0') + break; + else + if (*from == '(') + depth++; + else + if (*from == ')') + depth--; + else + if (*from == '\\' && from[1] != '\0') + from++; + } + } + else + if (*from == ')') /*- ignore closing parenthesis outside of comment -*/ + from++; + else + if (*from == ',' || *from == ';') { + /*- comma/selicolon ends the address -*/ + from++; + break; + } + else + if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') /*- ignore whitespace -*/ + from++; + else + if (*from == '"') { + /*- copy the contents of a quoted string -*/ + from++; + while (*from != '\0') { + if (*from == '"') { + from++; + break; + } + else + if (*from == '\\' && from[1] != '\0') + *to++ = *from++; + *to++ = *from++; + } + } + else + if (*from == '\\' && from[1] != '\0') { + /*- copy quoted-pair -*/ + *to++ = *from++; + *to++ = *from++; + } else { + /*- copy any other char -*/ + *to = *from++; + // save pointer to '<' for later... + if (*to == '<') + lt = to; + to++; + } + } + *to = '\0'; + /*- if there's < > get what's inside -*/ + if (lt != NULL) { + start = lt + 1; + char *gt = strchr(start, '>'); + if (gt != NULL) + *gt = '\0'; + } else { + /*- look for and strip group name -*/ + char *colon = strchr(start, ':'); + if (colon != NULL) { + char *at = strchr(start, '@'); + if (at == NULL || colon < at) + start = colon + 1; + } + } + if (*start != '\0' && strchr(start, '@') != NULL) + Addresses.push_back(start); + s = from; + } + return !Addresses.empty(); +} + +CDKIMVerify::CDKIMVerify() +{ + m_pfnSelectorCallback = NULL; + m_pfnPracticesCallback = NULL; + m_HonorBodyLengthTag = false; + m_CheckPractices = false; + m_Accept3ps = false; + m_SubjectIsRequired = true; + m_SaveCanonicalizedData = false; + m_AllowUnsignedFromHeaders = false; +} + +CDKIMVerify::~CDKIMVerify() +{ +} + +/*- Init - save the options -*/ +int +CDKIMVerify::Init(DKIMVerifyOptions *pOptions) +{ + int nRet = CDKIMBase::Init(); + m_pfnSelectorCallback = pOptions->pfnSelectorCallback; + m_pfnPracticesCallback = pOptions->pfnPracticesCallback; + + m_HonorBodyLengthTag = pOptions->nHonorBodyLengthTag != 0; + m_CheckPractices = pOptions->nCheckPractices != 0; + m_SubjectIsRequired = pOptions->nSubjectRequired != 0; + m_Accept3ps = pOptions->nAccept3ps != 0; //TBS(Luc) + m_SaveCanonicalizedData = pOptions->nSaveCanonicalizedData != 0; + m_AllowUnsignedFromHeaders = pOptions->nAllowUnsignedFromHeaders != 0; + return nRet; +} + +/*- GetResults - return the pass/fail/neutral verification result -*/ +int +CDKIMVerify::GetResults(int *sCount, int *sSize) +{ + ProcessFinal(); + unsigned int SuccessCount = 0; + int TestingFailures = 0; + int RealFailures = 0; + list SuccessfulDomains; /* can contain duplicates */ + string sFromDomain; /*- get the From address's domain if we might need it -*/ + + for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + if (i->Status == DKIM_SUCCESS) { + if (!i->BodyHashData.empty()) { /*- check the body hash -*/ + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned len = 0; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int res = EVP_DigestFinal(i->m_Bdy_ctx, md, &len); +#else + int res = EVP_DigestFinal(&i->m_Bdy_ctx, md, &len); +#endif + if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(), md, len) != 0) { + /* body hash mismatch */ + if (i->m_pSelector->Testing) { /* if the selector is in testing mode... */ + i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING; /* todo: make a new error code for this? */ + TestingFailures++; + } else { + i->Status = DKIM_BODY_HASH_MISMATCH; + RealFailures++; + } + continue; + } + } else { + /* hash CRLF separating the body from the signature */ + i->Hash("\r\n", 2); + } + /*- check the header hash -*/ + string sSignedSig = i->Header; + string sSigValue = sSignedSig.substr(sSignedSig.find(':') + 1); + static const char *tags[] = { "b", NULL }; + char *values[sizeof (tags) / sizeof (tags[0])] = { NULL }; + char *pSigValue = (char *) sSigValue.c_str(); + if (ParseTagValueList(pSigValue, tags, values) && values[0] != NULL) { + sSignedSig.erase(15 + values[0] - pSigValue, strlen(values[0])); + } + if (i->HeaderCanonicalization == DKIM_CANON_RELAXED) { + sSignedSig = RelaxHeader(sSignedSig); + } else + if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) { + RemoveSWSP(sSignedSig); + /* convert "DKIM-Signature" to lower case */ + sSignedSig.replace(0, 14, "dkim-signature", 14); + } + i->Hash(sSignedSig.c_str(), sSignedSig.length()); + assert(i->m_pSelector != NULL); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int res = EVP_VerifyFinal(i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(), + i->SignatureData.length(), i->m_pSelector->PublicKey); +#else + int res = EVP_VerifyFinal(&i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(), + i->SignatureData.length(), i->m_pSelector->PublicKey); +#endif + if (res == 1) { + if (i->UnverifiedBodyCount == 0) + i->Status = DKIM_SUCCESS; + else + i->Status = DKIM_SUCCESS_BUT_EXTRA; + SuccessCount++; + SuccessfulDomains.push_back(i->Domain); + } else { + /* if the selector is in testing mode... */ + if (i->m_pSelector->Testing) { + i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING; + TestingFailures++; + } else { + i->Status = DKIM_SIGNATURE_BAD; + RealFailures++; + } + } + } else + if (i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH + || i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH + || i->Status == DKIM_SELECTOR_KEY_REVOKED) { + /*- treat these as failures -*/ + /*- todo: maybe see if the selector is in testing mode? -*/ + RealFailures++; + } + } + if (SuccessCount > 0 || m_CheckPractices) { + for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { + if (_strnicmp(i->c_str(), "From", 4) == 0) { + /*- skip over whitespace between the header name and : -*/ + const char *s = i->c_str() + 4; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + vector Addresses; + if (ParseAddresses(s + 1, Addresses)) { + unsigned atpos = Addresses[0].find('@'); + sFromDomain = Addresses[0].substr(atpos + 1); + break; + } + } + } + } + } + /*- + * if a signature from the From domain verified successfully, + * return success now without checking the sender signing practices + */ + if (SuccessCount > 0 && !sFromDomain.empty()) { + for (list < string >::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) { + /* see if the successful domain is the same as or a parent of the From domain */ + if (i->length() > sFromDomain.length()) + continue; + if (_stricmp(i->c_str(), sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0) + continue; + if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.') + return ((SuccessCount == Signatures.size()) ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS); + } + } + if (!m_Accept3ps) + return DKIM_NEUTRAL; + *sCount = SuccessCount; + *sSize = Signatures.size(); + return DKIM_3PS_SIGNATURE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Hash - update the hash +// +//////////////////////////////////////////////////////////////////////////////// +void +SignatureInfo::Hash(const char *szBuffer, unsigned nBufLength, bool IsBody) +{ + + if (IsBody && BodyLength != -1) { + VerifiedBodyCount += nBufLength; + if (VerifiedBodyCount > BodyLength) { + nBufLength = BodyLength - (VerifiedBodyCount - nBufLength); + UnverifiedBodyCount += VerifiedBodyCount - BodyLength; + VerifiedBodyCount = BodyLength; + if (nBufLength == 0) + return; + } + } + if (IsBody && !BodyHashData.empty()) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_DigestUpdate(m_Bdy_ctx, szBuffer, nBufLength); +#else + EVP_DigestUpdate(&m_Bdy_ctx, szBuffer, nBufLength); +#endif + } else { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_VerifyUpdate(m_Hdr_ctx, szBuffer, nBufLength); +#else + EVP_VerifyUpdate(&m_Hdr_ctx, szBuffer, nBufLength); +#endif + } + if (m_SaveCanonicalizedData) { + CanonicalizedData.append(szBuffer, nBufLength); + } +} + + +/*- ProcessHeaders - Look for DKIM-Signatures and start processing them -*/ +int +CDKIMVerify::ProcessHeaders(void) +{ + + /*- look for DKIM-Signature header(s) -*/ + for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { + if (_strnicmp(i->c_str(), "DKIM-Signature", 14) == 0) { + /*- skip over whitespace between the header name and : -*/ + const char *s = i->c_str() + 14; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + // found + SignatureInfo sig(m_SaveCanonicalizedData); + sig.Status = ParseDKIMSignature(*i, sig); + Signatures.push_back(sig); + if (Signatures.size() >= MAX_SIGNATURES) + break; + } + } + } + if (Signatures.empty()) + return DKIM_NO_SIGNATURES; + bool ValidSigFound = false; + for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { + SignatureInfo &sig = *s; + if (sig.Status != DKIM_SUCCESS) + continue; + SelectorInfo &sel = GetSelector(sig.Selector, sig.Domain); + sig.m_pSelector = &sel; + if (sel.Status != DKIM_SUCCESS) { + sig.Status = sel.Status; + return (sig.Status); + } else { + /*- check the granularity -*/ + if (!WildcardMatch(sel.Granularity.c_str(), sig.IdentityLocalPart.c_str())) + sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH; /* this error causes the signature to fail */ + /*- check the hash algorithm -*/ +#ifdef HAVE_EVP_SHA256 + if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) || (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256)) +#else + if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1)) +#endif + sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH; /* causes signature to fail */ + /*- check for same domain -*/ + if (sel.SameDomain && _stricmp(sig.Domain.c_str(), sig.IdentityDomain.c_str()) != 0) + sig.Status = DKIM_BAD_SYNTAX; + } + if (sig.Status != DKIM_SUCCESS) + continue; + /*- initialize the hashes -*/ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#ifdef HAVE_EVP_SHA256 + if (sig.m_nHash == DKIM_HASH_SHA256) { + EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha256()); + EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha256()); + } else { + EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha1()); + EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha1()); + } +#else + EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha1()); + EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha1()); +#endif +#else +#ifdef HAVE_EVP_SHA256 + if (sig.m_nHash == DKIM_HASH_SHA256) { + EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha256()); + EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha256()); + } else { + EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1()); + EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1()); + } +#else + EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1()); + EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1()); +#endif +#endif + /*- compute the hash of the header -*/ + vector < list < string >::reverse_iterator > used; + for (vector < string >::iterator x = sig.SignedHeaders.begin(); x != sig.SignedHeaders.end(); ++x) { + list < string >::reverse_iterator i; + for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) { + if (_strnicmp(i->c_str(), x->c_str(), x->length()) == 0) { + /*- skip over whitespace between the header name and : -*/ + const char *s = i->c_str() + x->length(); + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':' && find(used.begin(), used.end(), i) == used.end()) + break; + } + } + if (i != HeaderList.rend()) { + used.push_back(i); + /*- hash this header -*/ + if (sig.HeaderCanonicalization == DKIM_CANON_SIMPLE) + sig.Hash(i->c_str(), i->length()); + else + if (sig.HeaderCanonicalization == DKIM_CANON_RELAXED) { + string sTemp = RelaxHeader(*i); + sig.Hash(sTemp.c_str(), sTemp.length()); + } else + if (sig.HeaderCanonicalization == DKIM_CANON_NOWSP) { + string sTemp = *i; + RemoveSWSP(sTemp); + /*- convert characters before ':' to lower case -*/ + for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) { + if (*s >= 'A' && *s <= 'Z') + *s += 'a' - 'A'; + } + sig.Hash(sTemp.c_str(), sTemp.length()); + } + sig.Hash("\r\n", 2); + } + } + if (sig.BodyHashData.empty()) /*- hash CRLF separating headers from body -*/ + sig.Hash("\r\n", 2); + if (!m_AllowUnsignedFromHeaders) { + /*- make sure the message has no unsigned From headers -*/ + list::reverse_iterator i; + for( i = HeaderList.rbegin(); i != HeaderList.rend(); ++i ) { + if( _strnicmp(i->c_str(), "From", 4 ) == 0 ) { + /*- skip over whitespace between the header name and : -*/ + const char *s = i->c_str()+4; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + if (find(used.begin(), used.end(), i) == used.end()) { + /*- this From header was not signed -*/ + break; + } + } + } + } + if (i != HeaderList.rend()) { + /*- treat signature as invalid -*/ + sig.Status = DKIM_UNSIGNED_FROM; + continue; + } + } + ValidSigFound = true; + } /*- for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { */ + if (!ValidSigFound) + return DKIM_NO_VALID_SIGNATURES; + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Strictly parse an unsigned integer. Don't allow spaces, negative sign, +// 0x prefix, etc. Values greater than 2^32-1 are capped at 2^32-1 +// +//////////////////////////////////////////////////////////////////////////////// +bool +ParseUnsigned(const char *s, unsigned long *result) +{ + unsigned temp = 0, last = 0; + bool overflowed = false; + + do { + if (*s < '0' || *s > '9') + return false; /*- returns false for an initial '\0' */ + temp = temp * 10 + (*s - '0'); + if (temp < last) + overflowed = true; + last = temp; + s++; + } while (*s != '\0'); + if (overflowed) + *result = -1; + else + *result = temp; + return true; +} + + +/*- ParseDKIMSignature - Parse a DKIM-Signature header field -*/ +int +CDKIMVerify::ParseDKIMSignature(const string &sHeader, SignatureInfo &sig) +{ + + /*- save header for later -*/ + sig.Header = sHeader; + string sValue = sHeader.substr(sHeader.find(':') + 1); + static const char *tags[] = { "v", "a", "b", "d", "h", "s", "c", "i", "l", "q", "t", "x", "bh", NULL }; + char *values[sizeof (tags) / sizeof (tags[0])] = { NULL }; + char *saveptr; + + if (!ParseTagValueList((char *) sValue.c_str(), tags, values)) + return DKIM_BAD_SYNTAX; + /*- check signature version -*/ + if (values[0] != NULL) { + if (strcmp(values[0], "1") == 0 || strcmp(values[0], "0.5") == 0 || strcmp(values[0], "0.4") == 0 + || strcmp(values[0], "0.3") == 0 || strcmp(values[0], "0.2") == 0) { + sig.Version = DKIM_SIG_VERSION_02_PLUS; + } else /*- unknown version -*/ + return DKIM_STAT_INCOMPAT; + } else { + /*- + * Note: DKIM Interop 1 pointed out that v= is now required, but we do + * not enforce that in order to verify signatures made by older drafts. + * prior to 0.2, there MUST NOT have been a v= + * (optionally) support these signatures, for backwards compatibility + */ + if (true) { + sig.Version = DKIM_SIG_VERSION_PRE_02; + } else { + return DKIM_BAD_SYNTAX; + } + } + /*- signature MUST have a=, b=, d=, h=, s= -*/ + if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL) + return DKIM_BAD_SYNTAX; + /*- algorithm can be "rsa-sha1" or "rsa-sha256" -*/ + if (strcmp(values[1], "rsa-sha1") == 0) { + sig.m_nHash = DKIM_HASH_SHA1; + } +#ifdef HAVE_EVP_SHA256 + else + if (strcmp(values[1], "rsa-sha256") == 0) + sig.m_nHash = DKIM_HASH_SHA256; +#endif + else + return DKIM_BAD_SYNTAX; /* todo: maybe create a new error code for unknown algorithm */ + /*- make sure the signature data is not empty -*/ + unsigned SigDataLen = DecodeBase64(values[2]); + if (SigDataLen == 0) + return DKIM_BAD_SYNTAX; + sig.SignatureData.assign(values[2], SigDataLen); + /*- check for body hash -*/ + if (values[12] == NULL) { + /*- use the old single hash way for backwards compatibility -*/ + if (sig.Version != DKIM_SIG_VERSION_PRE_02) + return DKIM_BAD_SYNTAX; + } else { + unsigned BodyHashLen = DecodeBase64(values[12]); + if (BodyHashLen == 0) + return DKIM_BAD_SYNTAX; + sig.BodyHashData.assign(values[12], BodyHashLen); + } + /*- domain must not be empty -*/ + if (*values[3] == '\0') + return DKIM_BAD_SYNTAX; + sig.Domain = values[3]; + /*- signed headers must not be empty (more verification is done later) -*/ + if (*values[4] == '\0') + return DKIM_BAD_SYNTAX; + /*- selector must not be empty -*/ + if (*values[5] == '\0') + return DKIM_BAD_SYNTAX; + sig.Selector = values[5]; + /*- canonicalization -*/ + if (values[6] == NULL) { + sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_SIMPLE; + } + else + if (sig.Version == DKIM_SIG_VERSION_PRE_02 && strcmp(values[6], "nowsp") == 0) /*- for backwards compatibility -*/ + sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_NOWSP; + else { + char *slash = strchr(values[6], '/'); + if (slash != NULL) + *slash = '\0'; + if (strcmp(values[6], "simple") == 0) + sig.HeaderCanonicalization = DKIM_CANON_SIMPLE; + else + if (strcmp(values[6], "relaxed") == 0) + sig.HeaderCanonicalization = DKIM_CANON_RELAXED; + else + return DKIM_BAD_SYNTAX; + if (slash == NULL || strcmp(slash + 1, "simple") == 0) + sig.BodyCanonicalization = DKIM_CANON_SIMPLE; + else + if (strcmp(slash + 1, "relaxed") == 0) + sig.BodyCanonicalization = DKIM_CANON_RELAXED; + else + return DKIM_BAD_SYNTAX; + } + /*- identity -*/ + if (values[7] == NULL) { + sig.IdentityLocalPart.erase(); + sig.IdentityDomain = sig.Domain; + } else { + /*- quoted-printable decode the value -*/ + DecodeQuotedPrintable(values[7]); + /*- must have a '@' separating the local part from the domain -*/ + char *at = strchr(values[7], '@'); + if (at == NULL) + return DKIM_BAD_SYNTAX; + *at = '\0'; + char *ilocalpart = values[7]; + char *idomain = at + 1; + /*- i= domain must be the same as or a subdomain of the d= domain -*/ + int idomainlen = strlen(idomain); + int ddomainlen = strlen(values[3]); + + /*- todo: maybe create a new error code for invalid identity domain -*/ + if (idomainlen < ddomainlen) + return DKIM_BAD_SYNTAX; + if (_stricmp(idomain + idomainlen - ddomainlen, values[3]) != 0) + return DKIM_BAD_SYNTAX; + if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.') + return DKIM_BAD_SYNTAX; + sig.IdentityLocalPart = ilocalpart; + sig.IdentityDomain = idomain; + } + /*- body count -*/ + if (values[8] == NULL || !m_HonorBodyLengthTag) { + sig.BodyLength = -1; + } else { + if (!ParseUnsigned(values[8], (unsigned long *) &sig.BodyLength)) + return DKIM_BAD_SYNTAX; + } + /*- query methods -*/ + if (values[9] != NULL) { + + /*- make sure "dns" is in the list -*/ + bool HasDNS = false; + char *s = strtok_r(values[9], ":", &saveptr); + while (s != NULL) { + if (strncmp(s, "dns", 3) == 0 && (s[3] == '\0' || s[3] == '/')) { + HasDNS = true; + break; + } + s = strtok_r(NULL, ": \t", &saveptr); + } + if (!HasDNS) + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method + } +#if SIZEOF_TIME_T == 8 + /*- signature time -*/ + time_t SignedTime = -1; +#else + long long SignedTime = -1; +#endif + if (values[10] != NULL) { + if (!ParseUnsigned(values[10], (unsigned long *) &SignedTime)) + return DKIM_BAD_SYNTAX; + } + /*- expiration time -*/ + if (values[11] == NULL) { + sig.ExpireTime = -1; + } else { + if (!ParseUnsigned(values[11], (unsigned long *) &sig.ExpireTime)) + return DKIM_BAD_SYNTAX; + if (sig.ExpireTime != -1) { + /*- the value of x= MUST be greater than the value of t= if both are present -*/ +#if SIZEOF_TIME_T == 8 + if (SignedTime != -1 && sig.ExpireTime <= SignedTime) + return DKIM_BAD_SYNTAX; +#else + if (SignedTime != -1 && (long long) sig.ExpireTime <= SignedTime) + return DKIM_BAD_SYNTAX; +#endif + /*- todo: if possible, use the received date/time instead of the current time -*/ + time_t curtime = time(NULL); +#if SIZEOF_TIME_T == 8 + if (curtime > sig.ExpireTime) + return DKIM_SIGNATURE_EXPIRED; +#else /*- handle year 2038 the best we can, beyond which one has to upgrade to a 64 bit os */ + if (curtime < 2147483648 && curtime > sig.ExpireTime) + return DKIM_SIGNATURE_EXPIRED; +#endif + } + } + /*- parse the signed headers list -*/ + bool HasFrom = false, HasSubject = false; + RemoveSWSP(values[4]); /*- header names shouldn't have spaces in them so this should be ok... */ + char *s = strtok_r(values[4], ":", &saveptr); + while (s != NULL) { + if (_stricmp(s, "From") == 0) + HasFrom = true; + else + if (_stricmp(s, "Subject") == 0) + HasSubject = true; + sig.SignedHeaders.push_back(s); + s = strtok_r(NULL, ":", &saveptr); + } + if (!HasFrom) + return DKIM_BAD_SYNTAX; /*- todo: maybe create a new error code for h= missing From */ + if (m_SubjectIsRequired && !HasSubject) + return DKIM_BAD_SYNTAX; /*- todo: maybe create a new error code for h= missing Subject */ + return DKIM_SUCCESS; +} + + +/*- ProcessBody - Process message body data -*/ +int +CDKIMVerify::ProcessBody(char *szBuffer, int nBufLength, bool bEOF) +{ + bool MoreBodyNeeded = false; + + for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + if (i->Status == DKIM_SUCCESS) { + if (i->BodyCanonicalization == DKIM_CANON_SIMPLE) { + if (nBufLength > 0) { + while (i->EmptyLineCount > 0) { + i->Hash("\r\n", 2, true); + i->EmptyLineCount--; + } + i->Hash(szBuffer, nBufLength, true); + i->Hash("\r\n", 2, true); + } else { + i->EmptyLineCount++; + if (bEOF) + i->Hash("\r\n", 2, true); + } + } else + if (i->BodyCanonicalization == DKIM_CANON_RELAXED) { + CompressSWSP(szBuffer, nBufLength); + if (nBufLength > 0) { + while (i->EmptyLineCount > 0) { + i->Hash("\r\n", 2, true); + i->EmptyLineCount--; + } + i->Hash(szBuffer, nBufLength, true); + if (!bEOF) + i->Hash("\r\n", 2, true); + } else + i->EmptyLineCount++; + } else + if (i->BodyCanonicalization == DKIM_CANON_NOWSP) { + RemoveSWSP(szBuffer, nBufLength); + i->Hash(szBuffer, nBufLength, true); + } + if (i->UnverifiedBodyCount == 0) + MoreBodyNeeded = true; + } + } + if (!MoreBodyNeeded) + return DKIM_FINISHED_BODY; + return DKIM_SUCCESS; +} + +SelectorInfo::SelectorInfo(const string &sSelector, const string &sDomain):Selector(sSelector), Domain(sDomain) +{ + AllowSHA1 = true; +#ifdef HAVE_EVP_SHA256 + AllowSHA256 = true; +#else + AllowSHA256 = false; +#endif + PublicKey = NULL; + Testing = false; + SameDomain = false; + Status = DKIM_SUCCESS; +} SelectorInfo::~SelectorInfo() +{ + if (PublicKey != NULL) { + EVP_PKEY_free(PublicKey); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Parse - Parse a DKIM selector +// +//////////////////////////////////////////////////////////////////////////////// +int +SelectorInfo::Parse(char *Buffer) +{ + static const char *tags[] = { "v", "g", "h", "k", "p", "s", "t", "n", NULL }; + char *values[sizeof (tags) / sizeof (tags[0])] = { NULL }; + char *saveptr; + + if (!ParseTagValueList(Buffer, tags, values)) + return DKIM_SELECTOR_INVALID; + if (values[0] != NULL) { + /*- make sure the version is "DKIM1" -*/ + if (strcmp(values[0], "DKIM1") != 0) + return DKIM_SELECTOR_INVALID; /*- todo: maybe create a new error code for unsupported selector version */ + /*- make sure v= is the first tag in the response */ + /*- todo: maybe don't enforce this, it seems unnecessary */ + for (unsigned int j = 1; j < sizeof (values) / sizeof (values[0]); j++) { + if (values[j] != NULL && values[j] < values[0]) { + return DKIM_SELECTOR_INVALID; + } + } + } + /*- selector MUST have p= tag -*/ + if (values[4] == NULL) + return DKIM_SELECTOR_INVALID; + /*- granularity -*/ + if (values[1] == NULL) + Granularity = "*"; + + else + Granularity = values[1]; + /*- hash algorithm -*/ + if (values[2] == NULL) { + AllowSHA1 = true; +#ifdef HAVE_EVP_SHA256 + AllowSHA256 = true; +#else + AllowSHA256 = false; +#endif + } else { + /*- MUST include "sha1" or "sha256" -*/ + char *s = strtok_r(values[2], ":", &saveptr); + while (s != NULL) { + if (strcmp(s, "sha1") == 0) + AllowSHA1 = true; +#ifdef HAVE_EVP_SHA256 + else if (strcmp(s, "sha256") == 0) + AllowSHA256 = true; +#endif + s = strtok_r(NULL, ":", &saveptr); + } +#ifdef HAVE_EVP_SHA256 + if (!(AllowSHA1 || AllowSHA256)) +#else + if (!AllowSHA1) +#endif + return DKIM_SELECTOR_INVALID; /*- todo: maybe create a new error code for unsupported hash algorithm */ + } + /*- key type -*/ + if (values[3] != NULL) { + /*- key type MUST be "rsa" -*/ + if (strcmp(values[3], "rsa") != 0) + return DKIM_SELECTOR_INVALID; + } + /*- service type -*/ + if (values[5] != NULL) { + /*- make sure "*" or "email" is in the list -*/ + bool ServiceTypeMatch = false; + char *s = strtok_r(values[5], ":", &saveptr); + while (s != NULL) { + if (strcmp(s, "*") == 0 || strcmp(s, "email") == 0) { + ServiceTypeMatch = true; + break; + } + s = strtok_r(NULL, ":", &saveptr); + } + if (!ServiceTypeMatch) + return DKIM_SELECTOR_INVALID; + } + /*- flags -*/ + if (values[6] != NULL) { + char *s = strtok_r(values[6], ":", &saveptr); + while (s != NULL) { + if (strcmp(s, "y") == 0) { + Testing = true; + } else + if (strcmp(s, "s") == 0) { + SameDomain = true; + } + s = strtok_r(NULL, ":", &saveptr); + } + } +#define M_ToConstUCharPtr(p) reinterpret_cast(p) /* Cast to unsigned char* */ + /*- public key data */ + unsigned PublicKeyLen = DecodeBase64(values[4]); + if (PublicKeyLen == 0) + return DKIM_SELECTOR_KEY_REVOKED; /*- this error causes the signature to fail */ + else { + EVP_PKEY *pkey; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + int rtype; +#endif + const unsigned char *qq; /*- public key data */ + + qq = M_ToConstUCharPtr(values[4]); +#ifdef DARWIN + pkey = d2i_PUBKEY(NULL, (unsigned char **) &qq, PublicKeyLen); +#else + pkey = d2i_PUBKEY(NULL, &qq, PublicKeyLen); +#endif + if (!pkey) + return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + /*- make sure public key is the correct type (we only support rsa) */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + rtype = EVP_PKEY_base_id(pkey); + if (rtype == EVP_PKEY_RSA || rtype == EVP_PKEY_RSA2) +#else + if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) +#endif + PublicKey = pkey; + else { + EVP_PKEY_free(pkey); + return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + } + } + return DKIM_SUCCESS; +} + +/*- GetSelector - Get a DKIM selector for a domain -*/ +SelectorInfo &CDKIMVerify::GetSelector(const string &sSelector, const string &sDomain) +{ + /*- see if we already have this selector -*/ + for (list < SelectorInfo >::iterator i = Selectors.begin(); i != Selectors.end(); ++i) { + if (_stricmp(i->Selector.c_str(), sSelector.c_str()) == 0 && _stricmp(i->Domain.c_str(), sDomain.c_str()) == 0) { + return *i; + } + } + Selectors.push_back(SelectorInfo(sSelector, sDomain)); + SelectorInfo &sel = Selectors.back(); + string sFQDN = sSelector; + sFQDN += "._domainkey."; + sFQDN += sDomain; + char Buffer[4096]; + int DNSResult; + + if (m_pfnSelectorCallback) + DNSResult = m_pfnSelectorCallback(sFQDN.c_str(), Buffer, sizeof(Buffer)); + else + DNSResult = DNSGetTXT(sFQDN.c_str(), Buffer, sizeof(Buffer)); + switch (DNSResult) { + case DNSRESP_SUCCESS: + sel.Status = sel.Parse(Buffer); + break; + case DNSRESP_TEMP_FAIL: + sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE; + break; + case DNSRESP_PERM_FAIL: + default: + sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE; + break; + case DNSRESP_DOMAIN_NAME_TOO_LONG: + sel.Status = DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG; + break; + } + return sel; +} + +/*- GetDetails - Get DKIM verification details (per signature) -*/ +int +CDKIMVerify::GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails) +{ + Details.clear(); + for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + DKIMVerifyDetails d; + d.szSignature = (char *) i->Header.c_str(); + d.szSignatureDomain = (char*)i->Domain.c_str(); + d.szIdentityDomain = (char*)i->IdentityDomain.c_str(); + d.nResult = i->Status; + d.szCanonicalizedData = (char *) i->CanonicalizedData.c_str(); + Details.push_back(d); + } *nSigCount = Details.size(); + *pDetails = (*nSigCount != 0) ? &Details[0] : NULL; + return DKIM_SUCCESS; +} + +char *DKIM_CALL +CDKIMVerify::GetDomain(void) +{ + static string sFromDomain; + for (list ::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { + if (_strnicmp(i->c_str(), "From", 4) == 0) { + /*- skip over whitespace between the header name and : -*/ + const char *s = i->c_str() + 4; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + vector Addresses; + if (ParseAddresses(s + 1, Addresses)) { + unsigned atpos = Addresses[0].find('@'); + sFromDomain = Addresses[0].substr(atpos + 1); + break; + } + } + } + } + return ((char *) sFromDomain.c_str()); +} + +void +getversion_dkimverify_cpp() +{ + static char *x = (char *) "$Id: dkimverify.cpp,v 1.23 2019-05-22 11:29:09+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dkimverify.h netqmail-1.06/dkimverify.h --- ../netqmail-1.06-original/dkimverify.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dkimverify.h 2019-06-19 09:47:30.665464671 +0200 @@ -0,0 +1,150 @@ +/* + * $Log: dkimverify.h,v $ + * Revision 1.9 2019-06-14 21:25:11+05:30 Cprogrammer + * BUG - honor body length tag in verification. Changed data type for BodyLength + * + * Revision 1.8 2019-05-22 11:30:06+05:30 Cprogrammer + * fix for 32 bit systems where time_t is 4 bytes & encounters year 2038 issue + * + * Revision 1.7 2017-08-31 17:07:45+05:30 Cprogrammer + * fixed g++ compiler warning + * + * Revision 1.6 2017-08-09 22:03:46+05:30 Cprogrammer + * initialized EVP_MD_CTX variables + * + * Revision 1.5 2017-08-08 23:50:47+05:30 Cprogrammer + * openssl 1.1.0 port + * + * Revision 1.4 2015-12-15 16:03:09+05:30 Cprogrammer + * use time_t for ExpireTime + * + * Revision 1.3 2011-06-04 09:37:25+05:30 Cprogrammer + * added AllowUnsignedFromHeaders + * + * Revision 1.2 2009-03-26 15:12:15+05:30 Cprogrammer + * changes for ADSP + * + * Revision 1.1 2009-03-21 08:50:22+05:30 Cprogrammer + * Initial revision + * + * + * Copyright 2005 Alt-N Technologies, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This code incorporates intellectual property owned by Yahoo! and licensed + * pursuant to the Yahoo! DomainKeys Patent License Agreement. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef DKIMVERIFY_H +#define DKIMVERIFY_H + +#include "dkimbase.h" +#include "time_t_size.h" +#include + +#define DKIM_SIG_VERSION_PRE_02 0 +#define DKIM_SIG_VERSION_02_PLUS 1 + +class SelectorInfo { + public: + SelectorInfo(const string & sSelector, const string & sDomain); + ~SelectorInfo(); + + string Domain; + string Selector; + string Granularity; + bool AllowSHA1; + bool AllowSHA256; + EVP_PKEY *PublicKey; /* the public key */ + bool Testing; + bool SameDomain; + int Status; + int Parse(char *Buffer); +}; + +class SignatureInfo { +public: + SignatureInfo(bool SaveCanonicalizedData); + ~SignatureInfo(); + + void Hash(const char *szBuffer, unsigned nBufLength, bool IsBody = false); + string Header; + unsigned Version; + string Domain; + string Selector; + string SignatureData; + string BodyHashData; + string IdentityLocalPart; + string IdentityDomain; + string CanonicalizedData; + vector SignedHeaders; + long BodyLength; + unsigned HeaderCanonicalization; + unsigned BodyCanonicalization; +#if SIZEOF_TIME_T == 8 + time_t ExpireTime; +#else + long long ExpireTime; +#endif + long VerifiedBodyCount; + long UnverifiedBodyCount; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_MD_CTX *m_Hdr_ctx = NULL; + EVP_MD_CTX *m_Bdy_ctx = NULL; +#else + EVP_MD_CTX m_Hdr_ctx; + EVP_MD_CTX m_Bdy_ctx; +#endif + SelectorInfo *m_pSelector; + int Status; + int m_nHash; // use one of the DKIM_HASH_xxx constants here + unsigned EmptyLineCount; + bool m_SaveCanonicalizedData; +}; + +class CDKIMVerify:public CDKIMBase { +public: + + CDKIMVerify(); + ~CDKIMVerify(); + + int Init(DKIMVerifyOptions * pOptions); + int GetResults(int *sCount, int *sSize); + int GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails); + virtual int ProcessHeaders(void); + virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF); + const char *GetPractices() {return Practices.c_str();} + char *DKIM_CALL GetDomain(void); + +protected: + int ParseDKIMSignature(const string & sHeader, SignatureInfo & sig); + SelectorInfo &GetSelector(const string & sSelector, const string & sDomain); + int GetADSP(const string &sDomain, int &iADSP); + int GetSSP(const string &sDomain, int &iSSP, bool & bTesting); + list Signatures; + list Selectors; + DKIMDNSCALLBACK m_pfnSelectorCallback; // selector record callback + DKIMDNSCALLBACK m_pfnPracticesCallback; // SSP record callback + bool m_HonorBodyLengthTag; + bool m_CheckPractices; + bool m_Accept3ps; //TBS(Luc) : accept 3rd party signature(s) + bool m_SubjectIsRequired; + bool m_SaveCanonicalizedData; + bool m_AllowUnsignedFromHeaders; + vector Details; + string Practices; +}; + +#endif /*- DKIMVERIFY_H */ diff -ruN ../netqmail-1.06-original/dknewkey.sh netqmail-1.06/dknewkey.sh --- ../netqmail-1.06-original/dknewkey.sh 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dknewkey.sh 2019-02-27 20:57:13.389025081 +0100 @@ -0,0 +1,27 @@ +# +# $Log: dknewkey.sh,v $ +# Revision 1.4 2010-05-16 19:59:48+05:30 Cprogrammer +# fix for Mac OS X +# +# Revision 1.3 2004-11-02 20:48:31+05:30 Cprogrammer +# fixed error when dknewkey was called without arguments +# +# Revision 1.2 2004-10-21 21:54:25+05:30 Cprogrammer +# create public key file +# +# Revision 1.1 2004-10-20 20:40:56+05:30 Cprogrammer +# Initial revision +# +# +if [ $# -lt 1 ] ; then + echo "USAGE: dknewkey keyfile [bits]" + exit 1 +fi +BITS=384 +if test -n "$2"; then BITS=$2; fi + +openssl genrsa -out $1 $BITS 2>/dev/null +openssl rsa -in $1 -out /tmp/dknewkey.$$ -pubout -outform PEM 2>/dev/null +printf "%s._domainkey\tIN\tTXT\t\"k=rsa; p=%s\"\n" `basename $1` `grep -v ^-- /tmp/dknewkey.$$ | tr -d '\n'` > $1.pub +/bin/cat $1.pub +/bin/rm -f /tmp/dknewkey.$$ diff -ruN ../netqmail-1.06-original/dktest.9 netqmail-1.06/dktest.9 --- ../netqmail-1.06-original/dktest.9 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dktest.9 2019-02-27 20:57:13.389025081 +0100 @@ -0,0 +1,86 @@ +.TH dktest 8 +.SH NAME +dktest \- exercise the domainkeys library +.SH SYNOPSIS +.B dktest +.I opts + +.I opts +is a series of getopt-style options. + +.SH DESCRIPTION +.B dktest +exercises the domainkeys library. Both signing and verification merely print out the DK header. +They do not keep a copy of the input file. You will need to do something like this: + +.EX + (./dktest -s QMAILHOME/control/domainkeys/dog +#include +#include +#include +#include "domainkeys.h" + +#ifdef DOMAIN_KEYS +int optf = 0; + +void +errorout(DK *dk, DK_STAT st) +{ + if (optf && dk) + fprintf(stderr, "%s(%d):", dk_errfile(dk), dk_errline(dk)); + fprintf(stderr, "dktest: %s\n", DK_STAT_to_string(st)); + exit(st); +} + +int +main(int argc, char *argv[]) +{ + char inbuf[1024]; + char advice[2048]; + char trace_count[BUFSIZ]; + size_t inlen; + size_t advicelen = sizeof(advice); + DK *dk; + DK_LIB *dklib; + DK_STAT st, dkt_st; + signed char ch; + int opts = 0, optv = 0, optt = 0, opth = 0, optr = 0, optT = 0, + optc = DK_CANON_SIMPLE; + char *canon = "simple"; + char *keyfn = 0, *selector = 0; + char *txtrec, *cp, *from, *dkdomain; + char privkey[2048]; + FILE *privkeyf = 0; + size_t privkeylen; + DK_FLAGS dkf = 0; + int i; + DK_TRACE_TYPE dk_trace_tag[4] = { + DKT_RAW_HEADER, + DKT_CANON_HEADER, + DKT_RAW_BODY, + DKT_CANON_BODY + }; + + for (dkdomain = (char *) 0;;) + { + ch = getopt(argc, argv, "s:vt:fb:c:hrTd:D:"); + if (ch == -1) + break; + switch (ch) + { + case 'D': + dkdomain = optarg; + break; + case 'd': /*- optD */ + txtrec = dns_text(optarg); + cp = txtrec; + printf("%ld\n", (long) strlen(cp)); + while (*cp) { + printf("%02x ", *cp++); + if ((cp - txtrec) % 16 == 0) + printf("\n"); + } + printf("\n"); + if (!strcmp(txtrec, "e=perm;")) + exit(0); + if (!strcmp(txtrec, "e=temp;")) + exit(0); + free(txtrec); + return (0); + break; + case 'T': + optT = 1; + break; + case 'v': + optv = 1; + break; + case 'f': + optf = 1; + break; + case 'r': + optr = 1; + opth = 1; + break; + case 's': + opts = 1; + keyfn = optarg; + selector = optarg; + while (*optarg) + { + if (*optarg == '/') + selector = optarg + 1; + optarg++; + } + break; + case 't': + optt = atoi(optarg); + break; + case 'h': + opth = 1; + break; + case 'b': + advicelen = atoi(optarg); + if (advicelen > sizeof(advice)) + advicelen = sizeof(advice); + break; + case 'c': + if (!strcmp(optarg, "simple")) + optc = DK_CANON_SIMPLE, canon = "simple"; + else + if (!strcmp(optarg, "nofws")) + optc = DK_CANON_NOFWS, canon = "nofws"; + else + { + fprintf(stderr, "dktest: unrecognized canonicalization.\n"); + exit(1); + } + } + } + if (opts) + { + if (!(privkeyf = fopen(keyfn, "r"))) /*- TC10 */ + { + fprintf(stderr, "dktest: can't open private key file %s\n", keyfn); + exit(1); + } + if ((privkeylen = fread(privkey, 1, sizeof(privkey), privkeyf)) == sizeof(privkey)) + /*- TC9 */ + { + fprintf(stderr, "dktest: private key buffer isn't big enough, use a smaller private key or recompile.\n"); + exit(1); + } + privkey[privkeylen] = '\0'; + fclose(privkeyf); + } + if (optt == 1) + errorout(NULL, 0); /*- TC2 */ + if (optt == 2) + errorout(NULL, 32767); /*- TC3 */ + dklib = dk_init(&st); + if (st != DK_STAT_OK) + errorout(NULL, st); + if (!dklib) + errorout(NULL, 200); + if (optv) + { + dk = dk_verify(dklib, &st); + if (st != DK_STAT_OK) + errorout(dk, st); + } else + if (opts) + { + dk = dk_sign(dklib, &st, optc); + if (st != DK_STAT_OK) + errorout(dk, st); + if (optr) + st = dk_setopts(dk, DKOPT_RDUPE); + if (st != DK_STAT_OK) + errorout(dk, st); + } else + { + fprintf(stderr, "dktest: [-f] [-b#] [-c nofws|simple] [-v|-s selector] [-h] [-t#] [-r] [-T][-d dnsrecord]\n"); /* TC1 */ + exit(1); + } + if (optT) /*- trace */ + { + st = dk_setopts(dk, (DKOPT_TRACE_h|DKOPT_TRACE_H|DKOPT_TRACE_b|DKOPT_TRACE_B)); + if (st != DK_STAT_OK) + errorout(dk, st); + } + if (optt == 3) + errorout(dk, dk_message(NULL, (const unsigned char *) "", 1)); /*- TC4 */ + if (optt == 4) + errorout(dk, dk_message(dk, (const unsigned char *) NULL, 1)); /*- TC5 */ + if (optt == 5) + errorout(dk, dk_message(dk, (const unsigned char *) "", 0)); /*- TC6 */ + if (optt >= 100 && optt <= 140) + errorout(dk, optt - 100); /*- TC53 */ + st = DK_STAT_OK; + /* + * This should work with DOS or UNIX text files -Tim + * Reduced calls to dk_message, in lib dkhash called for EVERY char + * DOS formatted input (CRLF line terminated) will have fewer calls + * to dk_message() than UNIX (LF line terminated) input. + */ + while (1) + { + char *inp; + + inlen = fread(inbuf, 1, sizeof(inbuf), stdin); + inp = inbuf; + while (inlen--) + { + if (*inp == '\n') + st = dk_message(dk, (const unsigned char *) "\r\n", 2); + else + st = dk_message(dk, (const unsigned char *) inp, 1); + if (st != DK_STAT_OK) + break; //stop looping if there was an error + inp++; + } + if ((inp-inbuf < sizeof(inbuf)) || (st != DK_STAT_OK)) + break; /*- if we read in the entire message or encountered an error */ + } + if (st == DK_STAT_OK) + { + if (optt == 10) + st = dk_end(dk, &dkf); + else + st = dk_eom(dk, &dkf); + } + if (optT) + { + printf("DomainKey-Trace: U=http://domainkeys.sourceforge.net; V=TESTING;\n"); + for (i = 0; i < 4; i++) + { + if (dk_get_trace(dk, dk_trace_tag[i], trace_count, sizeof (trace_count)) != DK_STAT_OK) + { + fprintf(stderr, "dktest: Not enough resources for trace buffer output\n"); + break; + } else + printf(" %s\n", trace_count); + } + if (optv) + { + printf("DomainKey-Trace-Diff:\n"); + for (i = 0; i < 4; i++) { + dkt_st = dk_compare_trace(dk,dk_trace_tag[i],trace_count,sizeof(trace_count)); + if (dkt_st == DK_STAT_NOSIG) + { + printf(" No DK-Trace: header found\n"); + break; + } else + if (dkt_st != DK_STAT_OK) + { + fprintf(stderr,"dktest: Not enough resources for trace buffer output\n"); + break; + } else + printf(" %s\n",trace_count); + } + } + } + if ((optt == 6 || optt == 10) && optv) + { + printf("flags: "); + if (dkf & DK_FLAG_SET) + printf("+"); + if (dkf & DK_FLAG_TESTING) + printf("t"); + if (dkf & DK_FLAG_SIGNSALL) + printf("s"); /*- wont be set if dk_end() is sucessful */ + if (dkf & DK_FLAG_G) + printf("g"); + printf("\n"); + } else + if (optt == 6 && opts) + errorout(dk, dk_getsig(dk, NULL, NULL, advicelen)); /*- TC14 */ + else + if (optt == 7) + { + from = dk_from(dk); + if (!from) + from = ""; + printf("%s\n", from); /*- TC14-1, TC14-2 */ + } else + if (optt == 11) + { + from = dk_address(dk); + printf("%s\n", from); /*- TC14-3, TC14-4 */ + } else + if (optt == 9) + { + char *s; + + s = malloc(dk_headers(dk, NULL)); + dk_headers(dk, s); + printf("%s\n", s); + free(s); + } else + if (optt == 8 && opts) + { + dk_getsig(dk, privkey, (unsigned char *) advice, advicelen); + if (st != DK_STAT_OK) + errorout(dk, st); + printf("%d %d\n", (int) dk_siglen(privkey), (int) strlen(advice)); /*- TC39 */ + } else + if (opts) + { + if (st != DK_STAT_OK) + errorout(dk, st); + st = dk_getsig(dk, privkey, (unsigned char *) advice, advicelen); + if (st != DK_STAT_OK) + errorout(dk, st); +#if 0 + printf("Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n"); +#endif + from = dk_from(dk); + printf("DomainKey-Signature: a=rsa-sha1; q=dns; c=%s;\n" + " s=%s; d=%s;\n" " b=%s;\n", canon, selector, dkdomain ? dkdomain : from, advice); + if (opth == 1) + { + if (dk_headers(dk, NULL) < sizeof(inbuf)) + { + dk_headers(dk, inbuf); + printf(" h=%s;\n", inbuf); + } + } + } else + if (optv) + { + char *status = 0; + + switch (st) + { + case DK_STAT_OK: + status = "good"; + break; + case DK_STAT_BADSIG: + status = "bad"; + break; + case DK_STAT_NOSIG: + status = "no signature"; + break; + case DK_STAT_NOKEY: + case DK_STAT_CANTVRFY: + status = "no key"; + break; + case DK_STAT_BADKEY: + status = "bad key"; + break; + case DK_STAT_INTERNAL: + case DK_STAT_ARGS: + case DK_STAT_SYNTAX: + status = "bad format"; + break; + case DK_STAT_NORESOURCE: + status = "no resources"; + break; + case DK_STAT_REVOKED: + status = "revoked"; + break; + case DK_STAT_GRANULARITY: + status = "bad sender (g=)"; + break; + case DK_STAT_DUPLICATE: + status = "duplicate signature"; + break; + } +#if 0 + printf("Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n"); +#endif + printf("DomainKey-Status: %s\n", status); + rewind(stdin); + } + if (st != DK_STAT_OK) + errorout(dk, st); + dk_free(dk, 1);//cleanup properly (not really necessary for single run process) + dk_shutdown(dklib); + return(0); +} +#else +#warning "not compiled with -DDOMAIN_KEYS" +int +main(int argc, char *argv[]) +{ + fprintf(stderr, "not compiled with -DDOMAIN_KEYS\n"); + return(1); +} +#endif + +void +getversion_dktest_c() +{ + static char *x = "$Id: dktest.c,v 1.16 2013-08-17 16:01:08+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dktrace.c netqmail-1.06/dktrace.c --- ../netqmail-1.06-original/dktrace.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dktrace.c 2019-02-27 20:57:13.389025081 +0100 @@ -0,0 +1,277 @@ +/* + * $Log: dktrace.c,v $ + * Revision 1.1 2009-03-14 09:00:52+05:30 Cprogrammer + * Initial revision + * + */ +/* + * $Id: dktrace.c,v 1.1 2009-03-14 09:00:52+05:30 Cprogrammer Stab mbhangui $ + */ + +#ifdef DOMAIN_KEYS +#include +#include +#include +#include "str.h" +#include "case.h" +#include "dktrace.h" + +#define strncasecmp(x,y,z) case_diffb((x), (z), (y)) + +static int * +getPointer(DK_TRACE * dkp, DK_TRACE_TYPE type) +{ + switch (type) { + case DKT_RAW_HEADER: + return dkp->ccounts_h; + case DKT_CANON_HEADER: + return dkp->ccounts_H; + case DKT_RAW_BODY: + return dkp->ccounts_b; + case DKT_CANON_BODY: + return dkp->ccounts_B; + default: + return 0; + } +} + +//Modified version of dkparselist() +static int +dkt_parselist(char *list, char *letters, char *values[]) +{ + char key; + int i; + char *value; + + /*- start with all args unset */ + for (i = 0; letters[i]; i++) { + values[i] = NULL; + } + key = 0; + while (*list) { + if ((*list == ' ') || (*list == '\t') || (*list == '\r') || (*list == '\n')) { + list++; + } else if (*list == '=') { + char *ws; + + ++list; + value = list; + ws = list; + while (1) { + /* + * copy up to null or semicolon, deleting whitespace as we go + */ + *ws = *list; + if ((*list == ' ') || (*list == '\t') || (*list == '\r') || (*list == '\n')) { + /* + * ignore + */ + } else if (!*list) { + break; + } else if (*list == ';') { + *ws = '\0'; + list++; + break; + } else { + ws++; + } + list++; + } + if (!key) { + return 0; //No key + } + /* + * if we find a matching letter, remember the value + */ + for (i = 0; letters[i]; i++) { + if (key == letters[i]) { + if (values[i]) { + return 0; /* no duplicate keys. TC23 */ + } + values[i] = value; + } + } + key = 0; + } else { + if (key) { + return 0; /* they already gave us a key. TC24 */ + } + key = *list++; + } + } + return 1; +} + +extern void +dkt_add(DK_TRACE * dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength) +{ + int *ip; + ip = getPointer(dkp, type); + if (!ip) + return; + + while (dataLength-- > 0) + ip[*data++]++; +} + +//useful for building table directly +extern void +dkt_quickadd(DK_TRACE * dkp, DK_TRACE_TYPE type, int index, int count) +{ + int *ip; + ip = getPointer(dkp, type); + if (!ip) + return; + if ((index < 256) && (index >= 0)) + ip[index] = ip[index] + count; +} + +//reverse of dkt_quickadd, reads data from table and returns the int count +extern int +dkt_getcount(DK_TRACE * dkp, DK_TRACE_TYPE type, int index, int count) +{ + int *ip; + ip = getPointer(dkp, type); + if (!ip) + return 0; + if ((index < 256) && (index >= 0)) + return ip[index]; + return 0; +} + + +/* + * Fills in DK_TRACE *diff_table with the differences between + * *dka (before) and *dkb (after), (after - before = diff) + */ +extern int +dkt_diff(DK_TRACE * dka, DK_TRACE * dkb, DK_TRACE_TYPE type, DK_TRACE * diff_table) +{ + int *inputa, *inputb, *output; + int i; + inputa = getPointer(dka, type); + if (!inputa) + return 0; + inputb = getPointer(dkb, type); + if (!inputb) + return 0; + output = getPointer(diff_table, type); + if (!output) + return 0; + + for (i = 0; i < 256; i++) { + output[i] = (inputb[i] - inputa[i]); + } + return 1; +} + +/* + * Generate the tag=value; data for a particular trace type + * returns length of generated C string including ending '\0' + */ + +extern int +dkt_generate(DK_TRACE * dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize) +{ + int *ip; + char *cp; + int ix; + int len; + int highest; + + if (maxBufferSize < 20) + return 0; /* Getting too close, you lose */ + cp = buffer; + ip = getPointer(dkp, type); + if (!ip) + return 0; + *buffer++ = (char) type; + --maxBufferSize; + *buffer++ = '='; + --maxBufferSize; + +/* + * Only produce as many entries as needed, rather than the full 256 + */ + + for (ix = 0, highest = 0; ix < 256; ++ix) { + if (ip[ix] != 0) + highest = ix; + } + + for (ix = 0; ix <= highest; ++ix) { + if (ip[ix] != 0) { + len = snprintf(buffer, maxBufferSize, "%d", ip[ix]); + buffer += len; + maxBufferSize -= len; + } + if (maxBufferSize < 10) + return 0; /* Getting too close, you lose */ + *buffer++ = ':'; + --maxBufferSize; + } + /* + * Finish up the tag with a semi-colon and turn it into a C string + */ + --buffer; + *buffer++ = ';'; //replace last ':' + *buffer++ = '\0'; + --maxBufferSize; + return buffer - cp; +} + +//converts a header to to a DK_TRACE table +extern int +dkt_hdrtotrace(char *ptr, DK_TRACE * store) +{ + char *values[4]; // hHbB + int idx; + int delim_count; + char *sptr, *eptr; + DK_TRACE_TYPE dk_trace_tag[4] = { + DKT_RAW_HEADER, + DKT_CANON_HEADER, + DKT_RAW_BODY, + DKT_CANON_BODY + }; + int *ip; + + if ((strncasecmp(ptr, "DomainKey-Trace:", 16)) || !store || (!dkt_parselist(ptr + 16, "hHbB", values))) { + return 0; + } + for (idx = 0; idx < 4; idx++) { + if (!values[idx]) + continue; + ip = getPointer(store, dk_trace_tag[idx]); + if (!ip) + return 0; + sptr = values[idx]; + for (delim_count = 0; ((delim_count < 256) && (*sptr != '\0')); sptr++) { + if (*sptr == ':') { + delim_count++; + continue; + } + //find the end of the int + for (eptr = sptr + 1; ((*eptr != ':') && (*eptr != '\0')); eptr++); + if (*eptr == '\0') //if end of values for key finish up + { + ip[delim_count] = atoi(sptr); + break; + } + *eptr = '\0'; + ip[delim_count] = atoi(sptr); + delim_count++; + sptr = eptr; + } + } + return 1; +} +#endif + +void +getversion_dktrace_c() +{ + static char *x = "$Id: dktrace.c,v 1.1 2009-03-14 09:00:52+05:30 Cprogrammer Stab mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/dktrace.h netqmail-1.06/dktrace.h --- ../netqmail-1.06-original/dktrace.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dktrace.h 2019-02-27 20:57:13.389025081 +0100 @@ -0,0 +1,26 @@ +/* $Id: dktrace.h,v 1.3 2005/06/27 18:47:57 ted46045 Exp $ */ + +#ifndef _DK_TRACE_H +#define _DK_TRACE_H + +typedef struct { + int ccounts_h[256]; + int ccounts_H[256]; + int ccounts_b[256]; + int ccounts_B[256]; +} DK_TRACE; + +typedef enum { DKT_RAW_HEADER='h', DKT_CANON_HEADER='H', + DKT_RAW_BODY='b', DKT_CANON_BODY='B' } DK_TRACE_TYPE; + +#define dkt_init(s) memset(s,0,sizeof(DK_TRACE)) + +//extern void dkt_init(DK_TRACE *dkp); +extern void dkt_add(DK_TRACE *dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength); +extern int dkt_diff(DK_TRACE *dka, DK_TRACE *dkb, DK_TRACE_TYPE type, DK_TRACE *table); +extern void dkt_quickadd(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count); +extern int dkt_getcount(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count); +extern int dkt_generate(DK_TRACE *dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize); +extern int dkt_hdrtotrace(char *ptr, DK_TRACE *store); + +#endif diff -ruN ../netqmail-1.06-original/dns.c netqmail-1.06/dns.c --- ../netqmail-1.06-original/dns.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/dns.c 2019-04-09 20:53:51.981528338 +0200 @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,6 +8,7 @@ extern int res_search(); #include "ip.h" #include "ipalloc.h" +#include "strsalloc.h" #include "fmt.h" #include "alloc.h" #include "str.h" @@ -19,14 +19,17 @@ 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 stralloc txt = {0}; unsigned short pref; static stralloc glue = {0}; @@ -43,18 +46,33 @@ errno = 0; if (!stralloc_copy(&glue,domain)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; - responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response)); + if (!responsebuflen) + if (response.buf = (unsigned char *)alloc(PACKETSZ+1)) + responsebuflen = PACKETSZ+1; + else return DNS_MEM; + + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + if ((responselen >= responsebuflen) || + (responselen > 0 && (((HEADER *)response.buf)->tc))) + { + if (responsebuflen < 65536) + if (alloc_re(&response.buf, responsebuflen, 65536)) + responsebuflen = 65536; + else return DNS_MEM; + saveresoptions = _res.options; + _res.options |= RES_USEVC; + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + _res.options = saveresoptions; + } if (responselen <= 0) { if (errno == ECONNREFUSED) return DNS_SOFT; if (h_errno == TRY_AGAIN) return DNS_SOFT; return DNS_HARD; } - if (responselen >= sizeof(response)) - responselen = sizeof(response); responseend = response.buf + responselen; responsepos = response.buf + sizeof(HEADER); - n = ntohs(response.hdr.qdcount); + n = ntohs(((HEADER *)response.buf)->qdcount); while (n-- > 0) { i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); @@ -64,7 +82,7 @@ if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } - numanswers = ntohs(response.hdr.ancount); + numanswers = ntohs(((HEADER *)response.buf)->ancount); return 0; } @@ -177,6 +195,49 @@ 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) + { + unsigned short txtpos; + unsigned char txtlen; + + txt.len = 0; + for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen) + { + txtlen = responsepos[txtpos++]; + if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos; + if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM; + } + + responsepos += rrdlen; + return 1; + } + + responsepos += rrdlen; + return 0; +} + void dns_init(flagsearch) int flagsearch; { @@ -194,7 +255,7 @@ if (!sa->len) return loop; if (sa->s[sa->len - 1] == ']') return loop; if (sa->s[sa->len - 1] == '.') { --sa->len; continue; } - switch(resolve(sa,T_ANY)) + switch(resolve(sa,T_CNAME)) { case DNS_MEM: return DNS_MEM; case DNS_SOFT: return DNS_SOFT; @@ -235,15 +296,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; @@ -254,25 +318,49 @@ 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; int pref; { int r; - struct ip_mx ix; + struct ip_mx ix = {0}; if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { +#ifndef IX_FQDN ix.pref = 0; +#endif if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; @@ -291,9 +379,16 @@ ix.ip = ip; ix.pref = pref; if (r == DNS_SOFT) return DNS_SOFT; - if (r == 1) + if (r == 1) { +#ifdef IX_FQDN + ix.fqdn = glue.s; +#endif if (!ipalloc_append(ia,&ix)) return DNS_MEM; } + } +#ifdef IX_FQDN + glue.s = 0; +#endif return 0; } @@ -313,7 +408,7 @@ { int r; struct mx { stralloc sa; unsigned short p; } *mx; - struct ip_mx ix; + struct ip_mx ix = {0}; int nummx; int i; int j; @@ -325,7 +420,9 @@ if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { +#ifndef IX_FQDN ix.pref = 0; +#endif if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; @@ -396,3 +493,49 @@ 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_copy(&sa,&txt)) 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 ../netqmail-1.06-original/dns.h netqmail-1.06/dns.h --- ../netqmail-1.06-original/dns.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/dns.h 2019-02-27 20:57:13.390025070 +0100 @@ -10,5 +10,6 @@ int dns_mxip(); int dns_ip(); int dns_ptr(); +int dns_txt(); #endif diff -ruN ../netqmail-1.06-original/dnsfq.c netqmail-1.06/dnsfq.c --- ../netqmail-1.06-original/dnsfq.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/dnsfq.c 2019-02-27 20:57:13.390025070 +0100 @@ -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 ../netqmail-1.06-original/dnsptr.c netqmail-1.06/dnsptr.c --- ../netqmail-1.06-original/dnsptr.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/dnsptr.c 2019-02-27 20:57:13.390025070 +0100 @@ -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 ../netqmail-1.06-original/dnstxt.c netqmail-1.06/dnstxt.c --- ../netqmail-1.06-original/dnstxt.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/dnstxt.c 2019-02-27 20:57:13.390025070 +0100 @@ -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 ../netqmail-1.06-original/domainkeys.h netqmail-1.06/domainkeys.h --- ../netqmail-1.06-original/domainkeys.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/domainkeys.h 2020-04-09 19:45:10.589742433 +0200 @@ -0,0 +1,379 @@ +/* This file is automatically created from the corresponding .c file */ +/* Do not change this file; change the .c file instead. */ +/* This is libdomainkeys. It's Copyright (c) 2004 Yahoo, Inc. + * This code incorporates intellectual property owned by + * Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License + * Agreement: http://domainkeys.sourceforge.net/license/softwarelicense1-0.html + */ +#include +#include +#include + +#ifdef SWIG +%module domainkeys +%{ +#include "domainkeys.h" +%} +#endif + +#include "dktrace.h" + +/* Performance/Debug options. + * Uncomment below or use -D switch in gcc + * DK_DEBUG Dumps whatever dkhash() hashes in to stderr and turns on + * some debug warnings that should never happen + * DK_HASH_BUFF Enables code that uses a buffer when processing the + * canocalized message, reducing calls to the crypto library (from dkhash()), + * but can use up slightly more memory +*/ +//#define DK_DEBUG 1 +#define DK_HASH_BUFF 1 + + +#define DKMARK ('D' | 'K'<<8 | 'E'<<16 | 'Y'<<24) +#define DK_SIGNING_SIGN 0 +#define DK_SIGNING_VERIFY 1 +#define DK_SIGNING_NOSIGN 2 +#define DK_SIGNING_NOVERIFY 3 +#define DK_MALLOC(s) OPENSSL_malloc(s) +#define DK_MFREE(s) OPENSSL_free(s); s = NULL; +#define DKERR(x) ((dk->errline=__LINE__),(dk->errfile=__FILE__),(x)) +#define DK_BLOCK 1024 //default size of malloc'd block + +/* + * Option Flags for dk_setopts + * OR together or run dk_setopts several times + * All option flags are OFF by default +*/ +#define DKOPT_TRACE_h 0x01 //enables tracking character count in pre-canon header +#define DKOPT_TRACE_H 0x02 //enables tracking character count in post-canon header +#define DKOPT_TRACE_b 0x04 //enables tracking character count in pre-canon body +#define DKOPT_TRACE_B 0x08 //enables tracking character count in post-canon header +#define DKOPT_RDUPE 0x10 //enables skipping duplicate headers when generateing a signature + +typedef enum +{ + DK_STAT_OK, /* Function completed successfully */ + DK_STAT_BADSIG, /* Signature was available but failed to verify against domain specified key */ + DK_STAT_NOSIG, /* No signature available in message */ + DK_STAT_NOKEY, /* No public key available (permanent failure) */ + DK_STAT_BADKEY, /* Unusable key, public if verifying, private if signing */ + DK_STAT_CANTVRFY, /* Cannot get domain key to verify signature (temporary failure) */ + DK_STAT_SYNTAX, /* Message is not valid syntax. Signature could not be created/checked */ + DK_STAT_NORESOURCE, /* Could not get critical resource (temporary failure) */ + DK_STAT_ARGS, /* Arguments are not usable. */ + DK_STAT_REVOKED, /* Key has been revoked. */ + DK_STAT_INTERNAL, /* cannot call this routine in this context. Internal error. */ + DK_STAT_GRANULARITY, /* Granularity mismatch: sender doesn't match g= option. */ + DK_STAT_DUPLICATE, /* Duplicate Domainkey-Header */ +} DK_STAT; + +typedef enum +{ + DK_FLAG_TESTING = 1, /* set when in testing mode. */ + DK_FLAG_SIGNSALL = 2, /* domain signs all outgoing email. */ + DK_FLAG_SET = 4, /* flags set from a successful DNS query */ + DK_FLAG_G = 8, /* g tag was present in the selector. */ +} DK_FLAGS; +typedef enum +{ + DK_TXT_KEY = 0, + DK_TXT_POLICY +} DK_TXT; + +typedef enum +{ + DK_CANON_SIMPLE = 0, + DK_CANON_NOFWS = 1, +} DK_CANON; +/* STARTSTRUCT */ +typedef struct +{ + +} DK_LIB; +/* STOPSTRUCT */ + +//UnixWare Fix -Tim +/* STARTSTRUCT */ +typedef struct +{ +} DK; +/* STOPSTRUCT */ + + +/* returns the source file from which an error was returned. */ +char * dk_errfile(DK *dk) +; + + +/* returns the source line number from which an error was returned. */ +int dk_errline(DK *dk) +; + + +/* Per-process, one-time initialization + * Returns library structure for subsequent dk_sign or dk_verify calls. + * Consult statp before using. + * + * When terminating the PROCESS its a good idea to call dk_shutdown() + * When terminating a THREAD it's a good idea to call ERR_remove_state(0); defined in + * NOTE: DK_LIB pointers are safe to use over multiple threads + * DK pointers are NOT safe to use over multiple threads + */ +DK_LIB *dk_init(DK_STAT *statp) +; + + +/* Per-process, one-time cleanup + * Should be called just before the application ends. + * the dklib pointer is not valid anymore after this call + * This function should be called even if dk_init failed. + * It's safe to call dk_shutdown with a NULL pointer + */ +void dk_shutdown(DK_LIB * dklib) +; + + +/* Set dk options, use instead of dk_remdupe and dk_enable_trace + * Can be called multiple times. + * use after dk_sign()/dk_verify() + * + * the bits field can be an OR of any of the following + *DKOPT_TRACE_h Trace pre-canon header + *DKOPT_TRACE_H Trace post-canon header + *DKOPT_TRACE_b Trace pre-canon body + *DKOPT_TRACE_B Trace post-canon body + *DKOPT_RDUPE Exclude duplicate headers from hash (Signing only) + */ +DK_STAT dk_setopts(DK *dk, int bits) +; + + +/* returns the int holding the options set + * See dk_setopts for bit flags + */ +int dk_getopts(DK *dk) +; + + +/* DEPRECATED in favor of calling dk_setopts(). + * Enables character trace tracking + * + * use after dk_sign()/dk_verify() + */ +DK_STAT dk_enable_trace(DK *dk) +; + + +/* Prints trace table to *store variable (char string) + * *dk is the container for the table + * *store is a pointer to a character array to output to + * store_size is the size of the character array *store + * + */ +DK_STAT dk_get_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size) +; + + +/* Prints difference trace table to *store variable (char string) + * *dk is the container for the table + * *store is a pointer to a character array to output to + * store_size is the size of the character array *store + * return DK_STAT_NOSIG if no DK-Trace header was found + */ +DK_STAT dk_compare_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size) +; + + +/* Sets the DNS key/policy record manually (no DNS lookup) + * txtrecord needs to be set to "e=perm;" to force a permanent DNS failure + * txtrecord needs to be set to "e=temp;" to force a temporary DNS failure + * Valid DK_TXT types are: + * DK_TXT_KEY (normal selector record; for ._domainkey.) + * DK_TXT_POLICY (domain policy record; for _domainkey.) + */ +DK_STAT dk_settxt(DK *dk, DK_TXT recordtype, const char *txtrecord) +; + + +/* Per-message, may be threaded. + * canon is one of DK_CANON_*. + * Returns state structure for operation. Consult statp before using. + */ +DK *dk_sign(DK_LIB *dklib, DK_STAT *statp, int canon) +; + + +/* Per-message, may be threaded. + * Returns state structure for operation. Consult statp before using. + */ +DK *dk_verify(DK_LIB *dklib, DK_STAT *statp) +; + + +/* DEPRECATED in favor of calling dk_setopts() + * set option to remove dupe headers + * should be called after dk_sign(); + * any int NOT 0 turns dupe removal on + */ +DK_STAT dk_remdupe(DK *dk,int i) +; + + +/* Returns the policy flags belonging to the signing domain. + * Sender: overrides From:, and the d= entry in the DK-Sig overrides both. + * If the policy flags were not successfully fetched, DK_FLAG_SET will not + * be set. + */ +DK_FLAGS dk_policy(DK *dk) +; + + +/* Copies the header names that were signed into the pointer. + * Returns the number of bytes copied. + * ptr may be NULL, in which case the bytes are just counted, not copied. + * Feel free to call this twice; once to get the length, and again to + * copy the data. + * NOTE: If the return value is 0 then an error occured. + * It's a good idea to check for this + */ +int dk_headers(DK *dk, char *ptr) +; + + +/* Must NOT include dots inserted for SMTP encapsulation. + * Must NOT include CRLF.CRLF which terminates the message. + * Otherwise must be exactly that which is sent or received over the SMTP session. + * May be called multiple times (not necessary to read an entire message into memory). + */ +DK_STAT dk_message(DK *dk, const unsigned char *ptr, size_t len) +; + + +/* DEPRECATED in favor of calling dk_address(). + * Returns a pointer to a null-terminated domain name portion of an RFC 2822 address. + * If a Sender: was encountered, it returns that domain. Otherwise, + * if a From: was encountered, it returns that domain. Otherwise, + * return NULL. + * return NULL if no domain name found in the address. + * return NULL if the dk is unusable for any reason. + * return NULL if the address is unusable for any reason. + */ +char *dk_from(DK *dk) +; + + +/* Returns a pointer to the selector name used or NULL if there isn't one + * Added by rjp + */ +const char *dk_selector(DK *dk) +; + + +/* Returns a pointer to the domain name used or NULL if there isn't one + */ +const char *dk_domain(DK *dk) +; + + +/* + * Returns a pointer to a string which begins with "N", "S", or "F", + * corresponding to None, Sender: and From:, respectively. + * This single character is followed by a null-terminated RFC 2822 address. + * The first character is "N" if no valid address has been seen yet, + * "S" if the address came from the Sender: field, and "F" if the + * address came from the From: field. + */ +char *dk_address(DK *dk) +; + + +/* + * Returns a pointer to a null-terminated string containing the granularity + * value found in the selector DNS record, if any, but only after dk_end + * has been called. Otherwise returns NULL. + */ +char *dk_granularity(DK *dk) +; + + +/* + * Called at end-of-message (before response to DATA-dot, if synchronous with SMTP session). + * If verifying, returns signature validity. + * This does not calculate the signature. Call dk_getsig() for that. + * Flags are returned indirectly through dkf. + * If you pass in NULL for dkf, the flags will not be fetched. + * If there is a DK-Sig line, the d= entry will be used to fetch the flags. + * Otherwise the Sender: domain will be used to fetch the flags. + * Otherwise the From: domain will be used to fetch the flags. + * + * NOTE: If for some reason dk_end() returns an error (!DK_STAT_OK) dk_policy() should be called + * to get the domain signing policy (o=) and handle accordingly. + * dkf (selector flags) wont be set if dk_end() returns + * DK_STAT_NOSIG + * DK_STAT_NOKEY + * DK_STAT_SYNTAX + * DK_STAT_NORESOURCE + * DK_STAT_BADKEY + * DK_STAT_CANTVERIFY + */ +DK_STAT dk_end(DK *dk, DK_FLAGS *dkf) +; + + +/* + * DEPRECATED in favor of calling dk_end and dk_policy() directly. + * If you pass in NULL for dkf, the policy flags will not be fetched. + * If the message verified okay, the policy flags will not be fetched. + */ +DK_STAT dk_eom(DK *dk, DK_FLAGS *dkf) +; + + +/* + * + * privatekey is the private key used to create the signature; It should contain + * the entire contents of a PEM-format private key file, thusly it will begin with + * -----BEGIN RSA PRIVATE KEY-----. It should be null-terminated. + */ +size_t dk_siglen(void *privatekey) +; + + +/* + * Sets buf to a null-terminated string. + * If the message is being signed, signature is stored in the buffer. + * If the message is being verified, returns DK_STAT_INTERNAL. + * privatekey is the private key used to create the signature; It should contain + * the entire contents of a PEM-format private key file, thus it will begin with + * -----BEGIN RSA PRIVATE KEY-----. It should be null-terminated. + * If you pass in NULL for buf, you'll get back DK_STAT_NORESOURCE. + * If len is not big enough, you'll get back DK_STAT_NORESOURCE. + */ +DK_STAT dk_getsig(DK *dk, void *privatekey, unsigned char buf[], size_t len) +; + + +/* + * Free all resources associated with this message. + * dk is no longer usable. + * if doClearErrState != 0, the OpenSSL ErrorState is freed. + * Set clearErrState=0 if you use other openssl functions and + * want to call openssl's ERR_remove_state(0) by yourself + * ERR_remove_state(0) is declared in + */ +DK_STAT dk_free(DK *dk, int doClearErrState) +; + + +/* + * return a pointer to a string which describes st. + * The string is structured. All the characters up to the first colon + * contain the name of the DK_STAT constant. From there to the end of + * string is a human-readable description of the error. + */ +const char *DK_STAT_to_string(DK_STAT st) +; + + +char *dns_text(char *); diff -ruN ../netqmail-1.06-original/forward.c netqmail-1.06/forward.c --- ../netqmail-1.06-original/forward.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/forward.c 2019-02-27 20:57:13.391025058 +0100 @@ -6,11 +6,11 @@ #include "strerr.h" #include "substdio.h" #include "fmt.h" +#include "stralloc.h" +#include "srs.h" #define FATAL "forward: fatal: " -void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); } - struct qmail qqt; int mywrite(fd,buf,len) int fd; char *buf; int len; @@ -42,6 +42,16 @@ dtline = env_get("DTLINE"); if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set"); + + if (str_len(sender)) { + switch(srsforward(sender)) { + case -3: strerr_die2x(100,FATAL,srs_error.s); break; + case -2: strerr_die2x(111,FATAL,"out of memory"); break; + case -1: strerr_die2x(111,FATAL,"unable to read controls"); break; + case 0: break; // nothing + case 1: sender = srs_result.s; break; + } + } if (qmail_open(&qqt) == -1) strerr_die2sys(111,FATAL,"unable to fork: "); diff -ruN ../netqmail-1.06-original/global.h netqmail-1.06/global.h --- ../netqmail-1.06-original/global.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/global.h 2019-02-27 20:57:13.391025058 +0100 @@ -0,0 +1,51 @@ +/* GLOBAL.H - RSAREF types and constants */ + +#include +#include "uint32.h" + +/* Copyright (C) RSA Laboratories, a division of RSA Data Security, + Inc., created 1991. All rights reserved. + */ + +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ 1 + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 1 if it has not already been + defined as 0 with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 1 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +#ifdef UINT32_H +#define UINT4 uint32 +#endif + +#ifndef NULL_PTR +#define NULL_PTR ((POINTER)0) +#endif + +#ifndef UNUSED_ARG +#define UNUSED_ARG(x) x = *(&x); +#endif + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +#endif /* end _GLOBAL_H_ */ diff -ruN ../netqmail-1.06-original/hier.c netqmail-1.06/hier.c --- ../netqmail-1.06-original/hier.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/hier.c 2019-06-26 16:39:31.573826970 +0200 @@ -4,6 +4,9 @@ #include "fmt.h" #include "fifo.h" +#include +#include "channels.h" + char buf[100 + FMT_ULONG]; void dsplit(base,uid,mode) @@ -29,9 +32,12 @@ void hier() { + int cc; + h(auto_qmail,auto_uido,auto_gidq,0755); d(auto_qmail,"control",auto_uido,auto_gidq,0755); + d(auto_qmail,"control/cache",89,89,0755); d(auto_qmail,"users",auto_uido,auto_gidq,0755); d(auto_qmail,"bin",auto_uido,auto_gidq,0755); d(auto_qmail,"boot",auto_uido,auto_gidq,0755); @@ -55,10 +61,20 @@ d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700); dsplit("queue/mess",auto_uidq,0750); + dsplit("queue/todo",auto_uidq,0750); + dsplit("queue/intd",auto_uidq,0700); dsplit("queue/info",auto_uids,0700); dsplit("queue/local",auto_uids,0700); dsplit("queue/remote",auto_uids,0700); + for (cc = 0;cc < SUPPL_CHANNELS;++cc) + { + char adbuf[100]; + + sprintf(adbuf,"queue/" QDIR_BASENAME "%d", cc); + dsplit(adbuf,auto_uids,0700); + } + d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750); z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644); z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600); @@ -89,6 +105,7 @@ c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644); @@ -104,13 +121,26 @@ c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","spawn-filter",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","surblfilter",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","dk-filter",auto_uido,auto_gidq,0555); + c(auto_qmail,"bin","dknewkey", auto_uido, auto_gidq, 0711); + c(auto_qmail,"bin","dktest",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","surblqueue",auto_uido,auto_gidq,0555); c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); +#ifdef EXTERNAL_TODO + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); +#endif + c(auto_qmail,"bin","qmail-dk",auto_uidq,auto_gidq,0711); + c(auto_qmail,"bin","qmail-dkim",auto_uidq,auto_gidq,0711); + c(auto_qmail,"bin","dkim",auto_uidq,auto_gidq,0711); c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmvrt",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755); @@ -127,6 +157,7 @@ c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); @@ -143,6 +174,9 @@ c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755); +#ifdef TLS + c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755); +#endif c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644); @@ -198,6 +232,18 @@ c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-dk.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-dk.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","dkim.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-dkim.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-dkim.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","dk-filter.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","dk-filter.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","dktest.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","surblfilter.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","surblfilter.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","spawn-filter.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","spawn-filter.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644); diff -ruN ../netqmail-1.06-original/hmac_md5.c netqmail-1.06/hmac_md5.c --- ../netqmail-1.06-original/hmac_md5.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/hmac_md5.c 2019-02-27 20:57:13.391025058 +0100 @@ -0,0 +1,76 @@ +#include "global.h" +#include "md5.h" + +/* +** Function: hmac_md5 +*/ + +void hmac_md5(text, text_len, key, key_len, digest) +unsigned char* text; /* pointer to data stream */ +int text_len; /* length of data stream */ +unsigned char* key; /* pointer to authentication key */ +int key_len; /* length of authentication key */ +unsigned char *digest; /* caller digest to be filled in */ + +{ + MD5_CTX context; + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + bzero( k_ipad, sizeof k_ipad); + bzero( k_opad, sizeof k_opad); + bcopy( key, k_ipad, key_len); + bcopy( key, k_opad, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ +} diff -ruN ../netqmail-1.06-original/hmac_md5.h netqmail-1.06/hmac_md5.h --- ../netqmail-1.06-original/hmac_md5.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/hmac_md5.h 2019-02-27 20:57:13.391025058 +0100 @@ -0,0 +1,11 @@ + +/* prototypes */ + +void hmac_md5( unsigned char* text, int text_len, unsigned char* key, int key_len, unsigned char* digest); + +/* pointer to data stream */ +/* length of data stream */ +/* pointer to authentication key */ +/* length of authentication key */ +/* caller digest to be filled in */ + diff -ruN ../netqmail-1.06-original/install-big.c netqmail-1.06/install-big.c --- ../netqmail-1.06-original/install-big.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/install-big.c 2019-06-26 16:39:31.574826959 +0200 @@ -4,6 +4,9 @@ #include "fmt.h" #include "fifo.h" +#include +#include "channels.h" + char buf[100 + FMT_ULONG]; void dsplit(base,uid,mode) @@ -29,6 +32,8 @@ void hier() { + int cc; + h(auto_qmail,auto_uido,auto_gidq,0755); d(auto_qmail,"control",auto_uido,auto_gidq,0755); @@ -59,6 +64,14 @@ dsplit("queue/local",auto_uids,0700); dsplit("queue/remote",auto_uids,0700); + for (cc = 0;cc < SUPPL_CHANNELS;++cc) + { + char adbuf[100]; + + sprintf(adbuf,"queue/" QDIR_BASENAME "%d", cc); + dsplit(adbuf,auto_uids,0700); + } + d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750); z(auto_qmail,"queue/lock/tcpto",1024,auto_uidr,auto_gidq,0644); z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600); @@ -89,6 +102,7 @@ c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644); + c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644); c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644); @@ -108,9 +122,13 @@ c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); +#ifdef EXTERNAL_TODO + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); +#endif c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmvrt",auto_uido,auto_gidq,0700); c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711); c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755); @@ -133,6 +151,7 @@ c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755); diff -ruN ../netqmail-1.06-original/ip.h netqmail-1.06/ip.h --- ../netqmail-1.06-original/ip.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/ip.h 2019-02-27 20:57:13.392025048 +0100 @@ -2,6 +2,7 @@ #define IP_H struct ip_address { unsigned char d[4]; } ; +typedef struct ip_address ip_addr; extern unsigned int ip_fmt(); #define IPFMT 19 diff -ruN ../netqmail-1.06-original/ipalloc.h netqmail-1.06/ipalloc.h --- ../netqmail-1.06-original/ipalloc.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/ipalloc.h 2019-02-27 20:57:13.392025048 +0100 @@ -3,7 +3,15 @@ #include "ip.h" +#ifdef TLS +# define IX_FQDN 1 +#endif + +#ifdef IX_FQDN +struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ; +#else struct ip_mx { struct ip_address ip; int pref; } ; +#endif #include "gen_alloc.h" diff -ruN ../netqmail-1.06-original/ipme.c netqmail-1.06/ipme.c --- ../netqmail-1.06-original/ipme.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/ipme.c 2019-02-27 20:57:13.392025048 +0100 @@ -14,23 +14,65 @@ #include "ipalloc.h" #include "stralloc.h" #include "ipme.h" +#include "substdio.h" +#include "readwrite.h" static int ipmeok = 0; ipalloc ipme = {0}; +ipalloc ipme_mask = {0}; +ipalloc notipme = {0}; +ipalloc notipme_mask = {0}; int ipme_is(ip) struct ip_address *ip; { - int i; if (ipme_init() != 1) return -1; - for (i = 0;i < ipme.len;++i) - if (byte_equal(&ipme.ix[i].ip,4,ip)) - return 1; - return 0; + return ipme_match(&ipme,&ipme_mask,ip) > ipme_match(¬ipme,¬ipme_mask,ip); } +int ipme_match(ipa, ipa_mask, ip) +struct ipalloc *ipa, *ipa_mask; +struct ip_address *ip; +{ + int i,j; + struct ip_address masked; + int masklen, longest_masklen=-1; + + for(i=0;i < ipa->len;++i) + { + masklen = 0; + for(j=0;j<4;++j) + { + switch(ipa_mask->ix[i].ip.d[j]) + { + case 255: masklen += 8; break; + case 254: masklen += 7; break; + case 252: masklen += 6; break; + case 248: masklen += 5; break; + case 240: masklen += 4; break; + case 224: masklen += 3; break; + case 192: masklen += 2; break; + case 128: masklen += 1; break; + default: masklen += 0; break; + } + if (ipa->ix[i].ip.d[j] != (ip->d[j] & ipa_mask->ix[i].ip.d[j])) + break; + } + if ( (j == 4) && (masklen > longest_masklen) ) + { + longest_masklen = masklen; + } + } + return longest_masklen; +} static stralloc buf = {0}; +#define ipme_init_retclean(ret) { \ + if (moreipme.ix) alloc_free(moreipme.ix); \ + if (moreipme_mask.ix) alloc_free(moreipme_mask.ix); \ + if (buf.s) alloc_free(buf.s); \ + return ret; } + int ipme_init() { struct ifconf ifc; @@ -39,23 +81,45 @@ struct sockaddr_in *sin; int len; int s; - struct ip_mx ix; - + struct ip_mx ix, ix_mask; + ipalloc moreipme = {0}; + ipalloc moreipme_mask = {0}; + int i; + if (ipmeok) return 1; - if (!ipalloc_readyplus(&ipme,0)) return 0; + if (!ipalloc_readyplus(&ipme,0)) ipme_init_retclean(0); + if (!ipalloc_readyplus(&ipme_mask,0)) ipme_init_retclean(0); + if (!ipalloc_readyplus(¬ipme,0)) ipme_init_retclean(0); + if (!ipalloc_readyplus(¬ipme_mask,0)) ipme_init_retclean(0); + if (!ipalloc_readyplus(&moreipme,0)) ipme_init_retclean(0); + if (!ipalloc_readyplus(&moreipme_mask,0)) ipme_init_retclean(0); + 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. + ix.pref = ix_mask.pref = 0; + + if (!ipme_readipfile(¬ipme, ¬ipme_mask, "control/notipme")) ipme_init_retclean(0); + + /* 127.0.0.0/255.0.0.0 is the localhost network. Linux will treat + every address in this range as a local interface, even if it + isn't explicitly configured. */ + byte_copy(&ix.ip,4,"\x7f\0\0\0"); + byte_copy(&ix_mask.ip,4,"\xff\0\0\0"); + if (!ipalloc_append(&ipme,&ix)) ipme_init_retclean(0); + if (!ipalloc_append(&ipme_mask,&ix_mask)) ipme_init_retclean(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; - + byte_copy(&ix_mask.ip,4,"\xff\xff\xff\xff"); + if (!ipalloc_append(&ipme,&ix)) ipme_init_retclean(0); + if (!ipalloc_append(&ipme_mask,&ix_mask)) ipme_init_retclean(0); + + if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) ipme_init_retclean(-1); + len = 256; for (;;) { - if (!stralloc_ready(&buf,len)) { close(s); return 0; } + if (!stralloc_ready(&buf,len)) { close(s); ipme_init_retclean(0); } buf.len = 0; ifc.ifc_buf = buf.s; ifc.ifc_len = len; @@ -64,7 +128,7 @@ buf.len = ifc.ifc_len; break; } - if (len > 200000) { close(s); return -1; } + if (len > 200000) { close(s); ipme_init_retclean(-1); } len += 100 + (len >> 2); } x = buf.s; @@ -79,7 +143,10 @@ byte_copy(&ix.ip,4,&sin->sin_addr); if (ioctl(s,SIOCGIFFLAGS,x) == 0) if (ifr->ifr_flags & IFF_UP) - if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; } + { + if (!ipalloc_append(&ipme,&ix)) { close(s); ipme_init_retclean(0); } + if (!ipalloc_append(&ipme_mask,&ix_mask)) { close(s); ipme_init_retclean(0); } + } } #else len = sizeof(*ifr); @@ -89,12 +156,60 @@ if (ifr->ifr_addr.sa_family == AF_INET) { sin = (struct sockaddr_in *) &ifr->ifr_addr; byte_copy(&ix.ip,4,&sin->sin_addr); - if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; } + if (!ipalloc_append(&ipme,&ix)) { close(s); ipme_init_retclean(0); } + if (!ipalloc_append(&ipme_mask,&ix_mask)) { close(s); ipme_init_retclean(0); } } #endif x += len; } close(s); + + if (!ipme_readipfile(&moreipme, &moreipme_mask, "control/moreipme")) ipme_init_retclean(0); + for(i = 0;i < moreipme.len;++i) + { + if (!ipalloc_append(&ipme,&moreipme.ix[i])) ipme_init_retclean(0); + if (!ipalloc_append(&ipme_mask,&moreipme_mask.ix[i])) ipme_init_retclean(0); + } ipmeok = 1; - return 1; + ipme_init_retclean(1); } + + +int ipme_readipfile(ipa, ipa_mask, fn) + ipalloc *ipa, *ipa_mask; + char *fn; +{ + int fd = -1; + char inbuf[1024]; + substdio ss; + stralloc l = {0}; + int match; + struct ip_mx ix, ix_mask; + int ret = 1; + int slash = 0; + + if ( (fd = open_read(fn)) != -1) { + substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf)); + while ( (getln(&ss,&l,&match,'\n') != -1) && (match || l.len) ) { + l.len--; + if (!stralloc_0(&l)) { ret = 0; break; } + if (l.s[slash=str_chr(l.s,'/')]!='\0') + { + l.s[slash]='\0'; + if (!ip_scan(l.s+slash+1,&ix_mask.ip)) + continue; + } + else + if (!ip_scan("255.255.255.255",&ix_mask.ip)) { ret = 0; break; } + + if (!ip_scan(l.s, &ix.ip)) continue; + if (!ipalloc_append(ipa,&ix)) { ret = 0; break; } + if (!ipalloc_append(ipa_mask,&ix_mask.ip)) { ret = 0; break; } + } + if (l.s) alloc_free(l.s); + if ( (fd >= 0) && (close(fd) == -1) ) + ret = 0; + } + return ret; +} + diff -ruN ../netqmail-1.06-original/ipme.h netqmail-1.06/ipme.h --- ../netqmail-1.06-original/ipme.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/ipme.h 2019-02-27 20:57:13.392025048 +0100 @@ -4,7 +4,7 @@ #include "ip.h" #include "ipalloc.h" -extern ipalloc ipme; +extern ipalloc ipme, ipme_mask, notipme, notipme_mask; extern int ipme_init(); extern int ipme_is(); diff -ruN ../netqmail-1.06-original/ipmeprint.c netqmail-1.06/ipmeprint.c --- ../netqmail-1.06-original/ipmeprint.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/ipmeprint.c 2019-02-27 20:57:13.392025048 +0100 @@ -3,12 +3,15 @@ #include "ip.h" #include "ipme.h" #include "exit.h" +#include "auto_qmail.h" char temp[IPFMT]; void main() { - int j; + int j,k; + + chdir(auto_qmail); switch(ipme_init()) { case 0: substdio_putsflush(subfderr,"out of memory\n"); _exit(111); @@ -17,8 +20,18 @@ for (j = 0;j < ipme.len;++j) { substdio_put(subfdout,temp,ip_fmt(temp,&ipme.ix[j].ip)); - substdio_puts(subfdout,"\n"); + substdio_puts(subfdout,"/"); + substdio_put(subfdout,temp,ip_fmt(temp,&ipme_mask.ix[j].ip)); + substdio_puts(subfdout," is me\n"); + } + for (j = 0;j < notipme.len;++j) + { + substdio_put(subfdout,temp,ip_fmt(temp,¬ipme.ix[j].ip)); + substdio_puts(subfdout,"/"); + substdio_put(subfdout,temp,ip_fmt(temp,¬ipme_mask.ix[j].ip)); + substdio_puts(subfdout," is not me\n"); } + substdio_flush(subfdout); _exit(0); } diff -ruN ../netqmail-1.06-original/ipmetest.c netqmail-1.06/ipmetest.c --- ../netqmail-1.06-original/ipmetest.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/ipmetest.c 2019-02-27 20:57:13.392025048 +0100 @@ -0,0 +1,38 @@ +#include "subfd.h" +#include "substdio.h" +#include "ip.h" +#include "ipme.h" +#include "exit.h" +#include "auto_qmail.h" +#include "env.h" + +void main(int argc, char *argv[]) +{ + struct ip_address ip; + + if (!env_get("IPMETEST_HERE")) + chdir(auto_qmail); + + if (argc < 2) + { + substdio_puts(subfdout,"invalid usage\n"); + substdio_flush(subfdout); + exit(1); + } + if (!ip_scan(argv[1],&ip)) + { + substdio_puts(subfdout,"invalid IP address\n"); + substdio_flush(subfdout); + exit(1); + } + if (ipme_is(&ip)) + { + substdio_puts(subfdout,"me\n"); + } + else + { + substdio_puts(subfdout,"not me\n"); + } + substdio_flush(subfdout); + exit(0); +} diff -ruN ../netqmail-1.06-original/macros.h netqmail-1.06/macros.h --- ../netqmail-1.06-original/macros.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/macros.h 2019-02-27 20:57:13.392025048 +0100 @@ -0,0 +1,25 @@ +/* + * $Log: macros.h,v $ + * Revision 1.1 2009-03-21 08:50:25+05:30 Cprogrammer + * Initial revision + * + * + * macros.h: Useful macros + * + * Author: + * Dick Porter (dick@ximian.com) + * + * (C) 2002 Ximian, Inc. + */ + +#ifndef _WAPI_MACROS_H_ +#define _WAPI_MACROS_H_ + +#include + +#define MAKEWORD(low, high) ((__uint16_t)(((__uint8_t)(low)) | \ + ((__uint16_t)((__uint8_t)(high))) << 8)) +#define LOBYTE(i16) ((__uint8_t)((i16) & 0xFF)) +#define HIBYTE(i16) ((__uint8_t)(((__uint16_t)(i16) >> 8) & 0xFF)) + +#endif /* _WAPI_MACROS_H_ */ diff -ruN ../netqmail-1.06-original/maildirflags.c netqmail-1.06/maildirflags.c --- ../netqmail-1.06-original/maildirflags.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildirflags.c 2019-02-27 20:57:13.392025048 +0100 @@ -0,0 +1,23 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include +#include + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +int maildir_hasflag(const char *filename, char flag) +{ + const char *p=strrchr(filename, '/'); + + if (p) + filename=p+1; + + p=strrchr(p, ':'); + if (p && strncmp(p, ":2,", 3) == 0 && + strchr(p+3, flag)) + return (1); + return (0); +} diff -ruN ../netqmail-1.06-original/maildirgetquota.c netqmail-1.06/maildirgetquota.c --- ../netqmail-1.06-original/maildirgetquota.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildirgetquota.c 2019-02-27 20:57:13.393025036 +0100 @@ -0,0 +1,50 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include "maildirgetquota.h" +#include "maildirmisc.h" +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +int maildir_getquota(const char *dir, char buf[QUOTABUFSIZE]) +{ +char *p; +struct stat stat_buf; +int n; +int l; + + p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + if (!p) return (-1); + + strcat(strcpy(p, dir), "/maildirfolder"); + if (stat(p, &stat_buf) == 0) + { + strcat(strcpy(p, dir), "/.."); + n=maildir_getquota(p, buf); + free(p); + return (n); + } + + strcat(strcpy(p, dir), "/maildirsize"); + n=maildir_safeopen(p, O_RDONLY, 0); + free(p); + if (n < 0) return (n); + if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0) + { + close(n); + return (-1); + } + close(n); + for (n=0; n +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static const char maildirgetquota_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +#define QUOTABUFSIZE 256 + +int maildir_getquota(const char *, char [QUOTABUFSIZE]); + +#ifdef __cplusplus +} +#endif + +#endif diff -ruN ../netqmail-1.06-original/maildirmisc.h netqmail-1.06/maildirmisc.h --- ../netqmail-1.06-original/maildirmisc.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildirmisc.h 2019-02-27 20:57:13.393025036 +0100 @@ -0,0 +1,145 @@ +#ifndef maildirmisc_h +#define maildirmisc_h + +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +static const char maildirmisc_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +/* +** +** Miscellaneous maildir-related code +** +*/ + +/* Some special folders */ + +#define INBOX "INBOX" +#define DRAFTS "Drafts" +#define SENT "Sent" +#define TRASH "Trash" + +#define SHAREDSUBDIR "shared-folders" + +char *maildir_folderdir(const char *, /* maildir */ + const char *); /* folder name */ + /* Returns the directory corresponding to foldername (foldername is + ** checked to make sure that it's a valid name, else we set errno + ** to EINVAL, and return (0). + */ + +char *maildir_filename(const char *, /* maildir */ + const char *, /* folder */ + const char *); /* filename */ + /* + ** Builds the filename to this message, suitable for opening. + ** If the file doesn't appear to be there, search the maildir to + ** see if someone changed the flags, and return the current filename. + */ + +int maildir_safeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + +/* +** Same arguments as open(). When we're accessing a shared maildir, +** prevent someone from playing cute and dumping a bunch of symlinks +** in there. This function will open the indicate file only if the +** last component is not a symlink. +** This is implemented by opening the file with O_NONBLOCK (to prevent +** a DOS attack of someone pointing the symlink to a pipe, causing +** the open to hang), clearing O_NONBLOCK, then stat-int the file +** descriptor, lstating the filename, and making sure that dev/ino +** match. +*/ + +int maildir_semisafeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + +/* +** Same thing, except that we allow ONE level of soft link indirection, +** because we're reading from our own maildir, which points to the +** message in the sharable maildir. +*/ + +int maildir_mkdir(const char *); /* directory */ +/* +** Create maildir including all subdirectories in the path (like mkdir -p) +*/ + +void maildir_purgetmp(const char *); /* maildir */ + /* purges old stuff out of tmp */ + +void maildir_purge(const char *, /* directory */ + unsigned); /* time_t to purge */ + +void maildir_getnew(const char *, /* maildir */ + const char *); /* folder */ + /* move messages from new to cur */ + +int maildir_deletefolder(const char *, /* maildir */ + const char *); /* folder */ + /* deletes a folder */ + +int maildir_mddelete(const char *); /* delete a maildir folder by path */ + +void maildir_list_sharable(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to callback func */ + /* list sharable folders */ + +int maildir_shared_subscribe(const char *, /* maildir */ + const char *); /* folder */ + /* subscribe to a shared folder */ + +void maildir_list_shared(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to the callback func */ + /* list subscribed folders */ + +int maildir_shared_unsubscribe(const char *, /* maildir */ + const char *); /* folder */ + /* unsubscribe from a shared folder */ + +char *maildir_shareddir(const char *, /* maildir */ + const char *); /* folder */ + /* + ** Validate and return a path to a shared folder. folderdir must be + ** a name of a valid shared folder. + */ + +void maildir_shared_sync(const char *); /* maildir */ + /* "sync" the shared folder */ + +int maildir_sharedisro(const char *); /* maildir */ + /* maildir is a shared read-only folder */ + +int maildir_unlinksharedmsg(const char *); /* filename */ + /* Remove a message from a shared folder */ + +/* Internal function that reads a symlink */ + +char *maildir_getlink(const char *); + + /* Determine whether the maildir filename has a certain flag */ + +int maildir_hasflag(const char *filename, char); + +#define MAILDIR_DELETED(f) maildir_hasflag((f), 'T') + +#ifdef __cplusplus +} +#endif + +#endif diff -ruN ../netqmail-1.06-original/maildiropen.c netqmail-1.06/maildiropen.c --- ../netqmail-1.06-original/maildiropen.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildiropen.c 2019-02-27 20:57:13.393025036 +0100 @@ -0,0 +1,133 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include + +#include "maildirmisc.h" + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +char *maildir_getlink(const char *filename) +{ +#if HAVE_READLINK +size_t bufsiz; +char *buf; + + bufsiz=0; + buf=0; + + for (;;) + { + int n; + + if (buf) free(buf); + bufsiz += 256; + if ((buf=malloc(bufsiz)) == 0) + { + perror("malloc"); + return (0); + } + if ((n=readlink(filename, buf, bufsiz)) < 0) + { + free(buf); + return (0); + } + if (n < bufsiz) + { + buf[n]=0; + break; + } + } + return (buf); +#else + return (0); +#endif +} + +int maildir_semisafeopen(const char *path, int mode, int perm) +{ + +#if HAVE_READLINK + +char *l=maildir_getlink(path); + + if (l) + { + int f; + + if (*l != '/') + { + char *q=malloc(strlen(path)+strlen(l)+2); + char *s; + + if (!q) + { + free(l); + return (-1); + } + + strcpy(q, path); + if ((s=strchr(q, '/')) != 0) + s[1]=0; + else *q=0; + strcat(q, l); + free(l); + l=q; + } + + f=maildir_safeopen(l, mode, perm); + + free(l); + return (f); + } +#endif + + return (maildir_safeopen(path, mode, perm)); +} + +int maildir_safeopen(const char *path, int mode, int perm) +{ +struct stat stat1, stat2; + +int fd=open(path, mode +#ifdef O_NONBLOCK + | O_NONBLOCK +#else + | O_NDELAY +#endif + , perm); + + if (fd < 0) return (fd); + if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1) + || lstat(path, &stat2)) + { + close(fd); + return (-1); + } + + if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino) + { + close(fd); + errno=ENOENT; + return (-1); + } + + return (fd); +} diff -ruN ../netqmail-1.06-original/maildirparsequota.c netqmail-1.06/maildirparsequota.c --- ../netqmail-1.06-original/maildirparsequota.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildirparsequota.c 2019-02-27 20:57:13.393025036 +0100 @@ -0,0 +1,44 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "maildirquota.h" +#include +#include + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +int maildir_parsequota(const char *n, unsigned long *s) +{ +const char *o; +int yes; + + if ((o=strrchr(n, '/')) == 0) o=n; + + for (; *o; o++) + if (*o == ':') break; + yes=0; + for ( ; o >= n; --o) + { + if (*o == '/') break; + + if (*o == ',' && o[1] == 'S' && o[2] == '=') + { + yes=1; + o += 3; + break; + } + } + if (yes) + { + *s=0; + while (*o >= '0' && *o <= '9') + *s= *s*10 + (*o++ - '0'); + return (0); + } + return (-1); +} diff -ruN ../netqmail-1.06-original/maildirquota.c netqmail-1.06/maildirquota.c --- ../netqmail-1.06-original/maildirquota.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildirquota.c 2019-02-27 20:57:13.393025036 +0100 @@ -0,0 +1,685 @@ +/* +** Copyright 1998 - 2002 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +/* #if HAVE_DIRENT_H */ +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +/* #else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#if HAVE_SYS_NDIR_H +#include +#endif +#if HAVE_SYS_DIR_H +#include +#endif +#if HAVE_NDIR_H +#include +#endif +#endif */ +#include +/* #if HAVE_SYS_STAT_H */ +#include +/* #endif */ +#include + +#include "maildirquota.h" +#include "maildirmisc.h" +#include +#include +#include +#include +/* #if HAVE_FCNTL_H */ +#include +/* #endif */ +#if HAVE_UNISTD_H +#include +#endif +#include +#include "numlib.h" + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +/* Read the maildirsize file */ + +int maildirsize_read(const char *filename, /* The filename */ + int *fdptr, /* Keep the file descriptor open */ + off_t *sizeptr, /* Grand total of maildir size */ + unsigned *cntptr, /* Grand total of message count */ + unsigned *nlines, /* # of lines in maildirsize */ + struct stat *statptr) /* The stats on maildirsize */ +{ +char buf[5120]; +int f; +char *p; +unsigned l; +int n; +int first; + + if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0) + return (-1); + p=buf; + l=sizeof(buf); + + while (l) + { + n=read(f, p, l); + if (n < 0) + { + close(f); + return (-1); + } + if (n == 0) break; + p += n; + l -= n; + } + if (l == 0 || fstat(f, statptr)) /* maildir too big */ + { + close(f); + return (-1); + } + + *sizeptr=0; + *cntptr=0; + *nlines=0; + *p=0; + p=buf; + first=1; + while (*p) + { + long n=0; + int c=0; + char *q=p; + + while (*p) + if (*p++ == '\n') + { + p[-1]=0; + break; + } + + if (first) + { + first=0; + continue; + } + sscanf(q, "%ld %d", &n, &c); + *sizeptr += n; + *cntptr += c; + ++ *nlines; + } + *fdptr=f; + return (0); +} + +static char *makenewmaildirsizename(const char *, int *); +static int countcurnew(const char *, time_t *, off_t *, unsigned *); +static int countsubdir(const char *, const char *, + time_t *, off_t *, unsigned *); +static int statcurnew(const char *, time_t *); +static int statsubdir(const char *, const char *, time_t *); + +#define MDQUOTA_SIZE 'S' /* Total size of all messages in maildir */ +#define MDQUOTA_BLOCKS 'B' /* Total # of blocks for all messages in + maildir -- NOT IMPLEMENTED */ +#define MDQUOTA_COUNT 'C' /* Total number of messages in maildir */ + +static int qcalc(off_t s, unsigned n, const char *quota, int *percentage) +{ +off_t i; +int spercentage=0; +int npercentage=0; + + errno=ENOSPC; + while (quota && *quota) + { + int x=1; + + if (*quota < '0' || *quota > '9') + { + ++quota; + continue; + } + i=0; + while (*quota >= '0' && *quota <= '9') + i=i*10 + (*quota++ - '0'); + switch (*quota) { + default: + if (i < s) + { + *percentage=100; + return (-1); + } + + /* + ** For huge quotas, over 20mb, + ** divide numerator & denominator by 1024 to prevent + ** an overflow when multiplying by 100 + */ + + x=1; + if (i > 20000000) x=1024; + + spercentage = i ? (s/x) * 100 / (i/x):100; + break; + case 'C': + + if (i < n) + { + *percentage=100; + return (-1); + } + + /* Ditto */ + + x=1; + if (i > 20000000) x=1024; + + npercentage = i ? ((off_t)n/x) * 100 / (i/x):100; + break; + } + } + *percentage = spercentage > npercentage ? spercentage:npercentage; + return (0); +} + +static int doaddquota(const char *, int, const char *, long, int, int); + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, int *percentage); + + +int maildir_checkquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt) +{ +int dummy; + + return (docheckquota(dir, maildirsize_fdptr, quota_type, + xtra_size, xtra_cnt, &dummy)); +} + +int maildir_readquota(const char *dir, const char *quota_type) +{ +int percentage=0; +int fd=-1; + + (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage); + if (fd >= 0) + close(fd); + return (percentage); +} + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, + int *percentage) +{ +char *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); +char *newmaildirsizename; +struct stat stat_buf; +int maildirsize_fd; +off_t maildirsize_size; +unsigned maildirsize_cnt; +unsigned maildirsize_nlines; +int n; +time_t tm; +time_t maxtime; +DIR *dirp; +struct dirent *de; + + if (checkfolder == 0) return (-1); + *maildirsize_fdptr= -1; + strcat(strcpy(checkfolder, dir), "/maildirfolder"); + if (stat(checkfolder, &stat_buf) == 0) /* Go to parent */ + { + strcat(strcpy(checkfolder, dir), "/.."); + n=docheckquota(checkfolder, maildirsize_fdptr, + quota_type, xtra_size, xtra_cnt, percentage); + free(checkfolder); + return (n); + } + if (!quota_type || !*quota_type) return (0); + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + time(&tm); + if (maildirsize_read(checkfolder, &maildirsize_fd, + &maildirsize_size, &maildirsize_cnt, + &maildirsize_nlines, &stat_buf) == 0) + { + n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage); + + if (n == 0) + { + free(checkfolder); + *maildirsize_fdptr=maildirsize_fd; + return (0); + } + close(maildirsize_fd); + + if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60) + return (n); + } + + maxtime=0; + maildirsize_size=0; + maildirsize_cnt=0; + + if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt)) + { + free(checkfolder); + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size, + &maildirsize_cnt)) + { + free(checkfolder); + closedir(dirp); + return (-1); + } + } + if (dirp) + { +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + { + free(checkfolder); + return (-1); + } +#endif + } + + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(checkfolder); + return (-1); + } + + *maildirsize_fdptr=maildirsize_fd; + + if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 1)) + { + unlink(newmaildirsizename); + free(newmaildirsizename); + close(maildirsize_fd); + *maildirsize_fdptr= -1; + free(checkfolder); + return (-1); + } + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + + if (rename(newmaildirsizename, checkfolder)) + { + /* free(checkfolder); */ + unlink(newmaildirsizename); + close(maildirsize_fd); + *maildirsize_fdptr= -1; + } + free(checkfolder); + free(newmaildirsizename); + + tm=0; + + if (statcurnew(dir, &tm)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (statsubdir(dir, de->d_name, &tm)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + closedir(dirp); + return (-1); + } + } + if (dirp) + { +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + { + close(maildirsize_fd); + *maildirsize_fdptr= -1; + return (-1); + } +#endif + } + + if (tm != maxtime) /* Race condition, someone changed something */ + { + errno=EAGAIN; + return (-1); + } + + return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage)); +} + +int maildir_addquota(const char *dir, int maildirsize_fd, + const char *quota_type, long maildirsize_size, int maildirsize_cnt) +{ + if (!quota_type || !*quota_type) return (0); + return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 0)); +} + +static int doaddquota(const char *dir, int maildirsize_fd, + const char *quota_type, long maildirsize_size, int maildirsize_cnt, + int isnew) +{ +union { + char buf[100]; + struct stat stat_buf; + } u; /* Scrooge */ +char *newname2=0; +char *newmaildirsizename=0; +struct iovec iov[3]; +int niov; +struct iovec *p; +int n; + + niov=0; + if ( maildirsize_fd < 0) + { + newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); + if (!newname2) return (-1); + strcat(strcpy(newname2, dir), "/maildirfolder"); + if (stat(newname2, &u.stat_buf) == 0) + { + strcat(strcpy(newname2, dir), "/.."); + n=doaddquota(newname2, maildirsize_fd, quota_type, + maildirsize_size, maildirsize_cnt, + isnew); + free(newname2); + return (n); + } + + strcat(strcpy(newname2, dir), "/maildirsize"); + + if ((maildirsize_fd=maildir_safeopen(newname2, + O_RDWR|O_APPEND, 0644)) < 0) + { + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(newname2); + return (-1); + } + + maildirsize_fd=maildir_safeopen(newmaildirsizename, + O_CREAT|O_RDWR|O_APPEND, 0644); + + if (maildirsize_fd < 0) + { + free(newname2); + return (-1); + } + isnew=1; + } + } + + if (isnew) + { + iov[0].iov_base=(caddr_t)quota_type; + iov[0].iov_len=strlen(quota_type); + iov[1].iov_base=(caddr_t)"\n"; + iov[1].iov_len=1; + niov=2; + } + + + sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt); + iov[niov].iov_base=(caddr_t)u.buf; + iov[niov].iov_len=strlen(u.buf); + + p=iov; + ++niov; + n=0; + while (niov) + { + if (n) + { + if (n < p->iov_len) + { + p->iov_base= + (caddr_t)((char *)p->iov_base + n); + p->iov_len -= n; + } + else + { + n -= p->iov_len; + ++p; + --niov; + continue; + } + } + + n=writev( maildirsize_fd, p, niov); + + if (n <= 0) + { + if (newname2) + { + close(maildirsize_fd); + free(newname2); + } + return (-1); + } + } + if (newname2) + { + close(maildirsize_fd); + + if (newmaildirsizename) + { + rename(newmaildirsizename, newname2); + free(newmaildirsizename); + } + free(newname2); + } + return (0); +} + +/* New maildirsize is built in the tmp subdirectory */ + +static char *makenewmaildirsizename(const char *dir, int *fd) +{ +char hostname[256]; +struct stat stat_buf; +time_t t; +char *p; + + hostname[0]=0; + hostname[sizeof(hostname)-1]=0; + gethostname(hostname, sizeof(hostname)-1); + p=(char *)malloc(strlen(dir)+strlen(hostname)+130); + if (!p) return (0); + + for (;;) + { + char tbuf[NUMBUFSIZE]; + char pbuf[NUMBUFSIZE]; + + time(&t); + strcat(strcpy(p, dir), "/tmp/"); + sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s", + str_time_t(t, tbuf), + str_pid_t(getpid(), pbuf), hostname); + + if (stat( (const char *)p, &stat_buf) < 0 && + (*fd=maildir_safeopen(p, + O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0) + break; + sleep(3); + } + return (p); +} + +static int statcurnew(const char *dir, time_t *maxtimestamp) +{ +char *p=(char *)malloc(strlen(dir)+5); +struct stat stat_buf; + + if (!p) return (-1); + strcat(strcpy(p, dir), "/cur"); + if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp) + *maxtimestamp=stat_buf.st_mtime; + strcat(strcpy(p, dir), "/new"); + if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp) + *maxtimestamp=stat_buf.st_mtime; + free(p); + return (0); +} + +static int statsubdir(const char *dir, const char *subdir, time_t *maxtime) +{ +char *p; +int n; + + if ( *subdir != '.' || strcmp(subdir, ".") == 0 || + strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0) + return (0); + + p=(char *)malloc(strlen(dir)+strlen(subdir)+2); + if (!p) return (-1); + strcat(strcat(strcpy(p, dir), "/"), subdir); + n=statcurnew(p, maxtime); + free(p); + return (n); +} + +static int docount(const char *, time_t *, off_t *, unsigned *); + +static int countcurnew(const char *dir, time_t *maxtime, + off_t *sizep, unsigned *cntp) +{ +char *p=(char *)malloc(strlen(dir)+5); +int n; + + if (!p) return (-1); + strcat(strcpy(p, dir), "/new"); + n=docount(p, maxtime, sizep, cntp); + if (n == 0) + { + strcat(strcpy(p, dir), "/cur"); + n=docount(p, maxtime, sizep, cntp); + } + free(p); + return (n); +} + +static int countsubdir(const char *dir, const char *subdir, time_t *maxtime, + off_t *sizep, unsigned *cntp) +{ +char *p; +int n; + + if ( *subdir != '.' || strcmp(subdir, ".") == 0 || + strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0) + return (0); + + p=(char *)malloc(strlen(dir)+strlen(subdir)+2); + if (!p) return (2); + strcat(strcat(strcpy(p, dir), "/"), subdir); + n=countcurnew(p, maxtime, sizep, cntp); + free(p); + return (n); +} + +static int docount(const char *dir, time_t *dirstamp, + off_t *sizep, unsigned *cntp) +{ +struct stat stat_buf; +char *p; +DIR *dirp; +struct dirent *de; +unsigned long s; + + if (stat(dir, &stat_buf)) return (0); /* Ignore */ + if (stat_buf.st_mtime > *dirstamp) *dirstamp=stat_buf.st_mtime; + if ((dirp=opendir(dir)) == 0) return (0); + while ((de=readdir(dirp)) != 0) + { + const char *n=de->d_name; + + if (*n == '.') continue; + + /* PATCH - do not count msgs marked as deleted */ + + for ( ; *n; n++) + { + if (n[0] != ':' || n[1] != '2' || + n[2] != ',') continue; + n += 3; + while (*n >= 'A' && *n <= 'Z') + { + if (*n == 'T') break; + ++n; + } + break; + } + if (*n == 'T') continue; + n=de->d_name; + + + if (maildir_parsequota(n, &s) == 0) + stat_buf.st_size=s; + else + { + p=(char *)malloc(strlen(dir)+strlen(n)+2); + if (!p) + { + closedir(dirp); + return (-1); + } + strcat(strcat(strcpy(p, dir), "/"), n); + if (stat(p, &stat_buf)) + { + free(p); + continue; + } + free(p); + } + *sizep += stat_buf.st_size; + ++*cntp; + } + +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + return (-1); +#endif + return (0); +} diff -ruN ../netqmail-1.06-original/maildirquota.h netqmail-1.06/maildirquota.h --- ../netqmail-1.06-original/maildirquota.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/maildirquota.h 2019-02-27 20:57:13.393025036 +0100 @@ -0,0 +1,45 @@ +#ifndef maildirquota_h +#define maildirquota_h + +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static const char maildirquota_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +int maildir_checkquota(const char *, /* Pointer to directory */ + int *, /* Initialized to -1, or opened descriptor for maildirsize */ + const char *, /* The quota */ + long, /* Extra bytes planning to add/remove from maildir */ + int); /* Extra messages planning to add/remove from maildir */ + +int maildir_addquota(const char *, /* Pointer to the maildir */ + int, /* Must be the int pointed to by 2nd arg to checkquota */ + const char *, /* The quota */ + long, /* +/- bytes */ + int); /* +/- files */ + +int maildir_readquota(const char *, /* Directory */ + const char *); /* Quota, from getquota */ + +int maildir_parsequota(const char *, unsigned long *); + /* Attempt to parse file size encoded in filename. Returns 0 if + ** parsed, non-zero if we didn't parse. */ + +#ifdef __cplusplus +} +#endif + +#endif diff -ruN ../netqmail-1.06-original/md5.h netqmail-1.06/md5.h --- ../netqmail-1.06-original/md5.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/md5.h 2019-02-27 20:57:13.394025025 +0100 @@ -0,0 +1,49 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#ifndef _MD5_H_ +#define _MD5_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + +#ifdef __cplusplus +} +#endif + +#endif diff -ruN ../netqmail-1.06-original/md5c.c netqmail-1.06/md5c.c --- ../netqmail-1.06-original/md5c.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/md5c.c 2019-02-27 20:57:13.394025025 +0100 @@ -0,0 +1,334 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#include "global.h" +#include "md5.h" + +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} diff -ruN ../netqmail-1.06-original/mess822.h netqmail-1.06/mess822.h --- ../netqmail-1.06-original/mess822.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/mess822.h 2019-02-27 20:57:13.394025025 +0100 @@ -0,0 +1,55 @@ +/* + * $Log: mess822.h,v $ + * Revision 1.3 2004-10-11 13:55:55+05:30 Cprogrammer + * added function prototypes + * + * Revision 1.2 2004-06-18 23:00:58+05:30 Cprogrammer + * added RCS log + * + */ +#ifndef MESS822_H +#define MESS822_H +#include "stralloc.h" +#include "caltime.h" + +#define MESS822_HEADER { {0} } + +typedef struct +{ + struct caltime ct; + int known; /*- 0 for ct uninitialized; 1 if ok; 2 if ok and right zone */ +} +mess822_time; + +typedef struct +{ + char *name; /*- 0 means all names */ + int *flag; + stralloc *copy; + stralloc *value; + stralloc *addr; + mess822_time *when; +} +mess822_action; + +typedef struct +{ + stralloc inprogress; + mess822_action *action; +} +mess822_header; + +int mess822_quoteplus(stralloc *, char *, char *); +int mess822_quote(stralloc *, char *, char *); +int mess822_quotelist(stralloc *, stralloc *); +int mess822_fold(stralloc *, stralloc *, char *, int); +int mess822_date(stralloc *, mess822_time *); +int mess822_token(stralloc *, char *); +int mess822_addrlist(stralloc *, char *); +int mess822_when(mess822_time *, char *); +int mess822_begin(mess822_header *, mess822_action *); +int mess822_line(mess822_header *, stralloc *); +int mess822_end(mess822_header *); +int mess822_ok(stralloc *); + +#endif diff -ruN ../netqmail-1.06-original/mess822_ok.c netqmail-1.06/mess822_ok.c --- ../netqmail-1.06-original/mess822_ok.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/mess822_ok.c 2019-02-27 20:57:13.394025025 +0100 @@ -0,0 +1,55 @@ +/* + * $Log: mess822_ok.c,v $ + * Revision 1.2 2004-10-22 20:27:30+05:30 Cprogrammer + * added RCS id + * + * Revision 1.1 2004-01-04 23:17:51+05:30 Cprogrammer + * Initial revision + * + */ +#include "mess822.h" +#include "byte.h" + +int +mess822_ok(sa) + stralloc *sa; +{ + int i; + int len; + int colon; + + len = sa->len; + if (len && (sa->s[len - 1] == '\n')) + --len; + if (!len) + return 0; + /* + * if input message is 822-compliant, will return 1 after this + */ + if (sa->s[0] == ' ') + return 1; + if (sa->s[0] == '\t') + return 1; + colon = byte_chr(sa->s, sa->len, ':'); + if (colon >= sa->len) + return 0; + while (colon && ((sa->s[colon - 1] == ' ') || (sa->s[colon - 1] == '\t'))) + --colon; + if (!colon) + return 0; + for (i = 0; i < colon; ++i) + if (sa->s[i] < 33) + return 0; + for (i = 0; i < colon; ++i) + if (sa->s[i] > 126) + return 0; + return 1; +} + +void +getversion_mess822_ok_c() +{ + static char *x = "$Id: mess822_ok.c,v 1.2 2004-10-22 20:27:30+05:30 Cprogrammer Stab mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/numlib.h netqmail-1.06/numlib.h --- ../netqmail-1.06-original/numlib.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/numlib.h 2019-02-27 20:57:13.394025025 +0100 @@ -0,0 +1,45 @@ +#ifndef numlib_h +#define numlib_h + +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +static const char numlib_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define NUMBUFSIZE 60 + +/* Convert various system types to decimal */ + +char *str_time_t(time_t, char *); +char *str_off_t(off_t, char *); +char *str_pid_t(pid_t, char *); +char *str_ino_t(ino_t, char *); +char *str_uid_t(uid_t, char *); +char *str_gid_t(gid_t, char *); +char *str_size_t(size_t, char *); + +char *str_sizekb(unsigned long, char *); /* X Kb or X Mb */ + +/* Convert selected system types to hex */ + +char *strh_time_t(time_t, char *); +char *strh_pid_t(pid_t, char *); +char *strh_ino_t(ino_t, char *); + +#ifdef __cplusplus +} +#endif +#endif diff -ruN ../netqmail-1.06-original/overmaildirquota.c netqmail-1.06/overmaildirquota.c --- ../netqmail-1.06-original/overmaildirquota.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/overmaildirquota.c 2019-02-27 20:57:13.394025025 +0100 @@ -0,0 +1,41 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "maildirquota.h" +#include +#include +#include +#include + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +int user_over_maildirquota( const char *dir, const char *q) +{ +struct stat stat_buf; +int quotafd; +int ret_value; + + if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && + stat_buf.st_size > 0 && *q) + { + if (maildir_checkquota(dir, "afd, q, stat_buf.st_size, 1) + && errno != EAGAIN) + { + if (quotafd >= 0) close(quotafd); + ret_value = 1; + } else { + maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1); + if (quotafd >= 0) close(quotafd); + ret_value = 0; + } + } else { + ret_value = 0; + } + + return(ret_value); +} diff -ruN ../netqmail-1.06-original/policy.c netqmail-1.06/policy.c --- ../netqmail-1.06-original/policy.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/policy.c 2019-02-27 20:57:13.395025014 +0100 @@ -0,0 +1,1210 @@ +/* + * Copyright (C) 2005 Inter7 Internet Technologies, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * + * eMail Messaging Policy Framework + * http://www.inter7.com/?page=empf + * + */ + +#include +#include +#include +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif +#ifndef FNM_CASEFOLD + #include +#endif +#include +#include "stralloc.h" +#include "rcpthosts.h" +#include "policy.h" + +/* + Policy delivery flags +*/ + +#define POLICY_F_NONE 0 +#define POLICY_F_ALLOW 1 /* Allow this delivery type */ +#define POLICY_F_DISALLOW 2 /* Do not allow this delivery */ +#define POLICY_F_LOCAL 4 /* Local -> local delivery */ +#define POLICY_F_REMOTE 8 /* Local -> remote delivery */ +#define POLICY_F_EXTERNAL 16 /* Remote -> local delivery */ +#define POLICY_F_INTERNAL 32 /* Local -> local delivery */ + +/* + Policy +*/ + +typedef struct __policy_ { + int flags, /* Allow or disallow */ + numargs; /* Arguments read */ + char **args; /* Argument array */ + + struct __policy_ *next; +} policy_t; + +/* + Local delivery name +*/ + +typedef struct __local_ { + char *local; /* Local name */ + policy_t *policy; /* Policy */ + + struct __local_ *next; +} local_t; + +/* + Domain name +*/ + +typedef struct __domain_ { + char *domain; /* Domain name */ + policy_t *policy; /* Policy */ + local_t *locals; /* Locals */ + + struct __domain_ *next; +} domain_t; + +extern char *remoteinfo; +extern stralloc mailfrom; +extern stralloc addr; +extern void out(char *); + +static int policy_load(const char *); +static int policy_parse(const char *, char *); +static policy_t *policy_construct(char *); +static int policy_construct_parse_arguments(char **, const int, char *); +static int policy_match(const char *, const char *); +static int policy_applies_to(const policy_t *, const char *); +static int policy_flags(const char); +static policy_t *policy_find(const policy_t *, const int); +static int policy_forbids(void); +static domain_t *domain_find(const char *); +static local_t *local_find(const domain_t *, const char *); +static void policy_free(policy_t *); +static void local_free(local_t *); +static void domains_free(void); + +static domain_t *domains = NULL, *s_domain = NULL, *r_domain = NULL; + +/* + Check policy for delivery restrictions +*/ + +int policy_check(void) +{ + int ret = 0; + const char *p = NULL; + + s_domain = r_domain = NULL; + +#ifdef POLICY_DEALLOCATE + domains_free(); +#endif + + if (remoteinfo) + p = remoteinfo; + else + p = mailfrom.s; + + fprintf(stderr, "policy_check: %s %s -> %s %s (%s)\n", + rcpthosts(p, strlen(p)) ? "local" : "remote", p, + rcpthosts(addr.s, strlen(addr.s)) ? "local" : "remote", addr.s, + remoteinfo ? "AUTHENTICATED SENDER" : "UNAUTHENTICATED SENDER"); + + /* + Load sender-related policy + */ + + for (; ((p) && (*p)); p++) { + if (*p == '@') + break; + } + + if (*p) { + if (rcpthosts(remoteinfo ? remoteinfo : mailfrom.s, + remoteinfo ? strlen(remoteinfo) : strlen(mailfrom.s))) { + ret = policy_load(p + 1); + if (!ret) { + fprintf(stderr, "policy_check: policy_load failed\n"); + return -1; + } + + s_domain = domain_find(p + 1); + +#ifdef POLICY_ENFORCE_AUTHENTICATION + /* + This check is done here in the event that there is + no policy for a domain. In that event, we do not + wish to enforce policy rules + */ + + if ((s_domain) && (remoteinfo == NULL)) { + fprintf(stderr, "policy_check: sender not authenticated\n"); + return 0; + } +#endif + } + +#ifdef DEBUG + else + fprintf(stderr, "policy_check: %s is not local\n", p + 1); +#endif + } + + /* + Load recipient-related policy + */ + + for (p = addr.s; ((p) && (*p)); p++) { + if (*p == '@') + break; + } + + if (*p) { + if (rcpthosts(addr.s, strlen(addr.s))) { + ret = policy_load(p + 1); + if (!ret) { + fprintf(stderr, "policy_check: policy_load failed\n"); + return -1; + } + + r_domain = domain_find(p + 1); + } + +#ifdef DEBUG + else + fprintf(stderr, "policy_check: %s is not local\n", p + 1); +#endif + } + + /* + Policy enforcement + */ + + ret = policy_forbids(); + if (ret == 1) { + fprintf(stderr, "policy_check: policy forbids transmission\n"); + return 0; + } + + else if (ret == 0) { + fprintf(stderr, "policy_check: policy allows transmission\n"); + return 1; + } + + fprintf(stderr, "policy_check: policy_forbids failed\n"); + return -1; +} + +/* + Load policy from policy file +*/ + +static int policy_load(const char *domain) +{ + size_t line = 0; + FILE *stream = NULL; + char b[4096] = { 0 }; + int locald = 0, ret = 0; + + if (domain == NULL) + return 1; + + /* + See if we've already loaded this policy + */ + + if (domain_find(domain)) { +#ifdef DEBUG + fprintf(stderr, "policy_load(%s): already loaded\n", domain); +#endif + return 1; + } + + stream = fopen(POLICY_FILENAME, "r"); + + /* + If policy file doesnt exist, allow all messaging + Otherwise trigger error + */ + + if (stream == NULL) { + if (errno != ENOENT) { + fprintf(stderr, "policy_load(%s): cannot read policy\n", domain); + return 0; + } + +#ifdef DEBUG + fprintf(stderr, "policy_load(%s): no policy\n", domain); +#endif + return 1; + } + + /* + Run through policy line-by-line + */ + + line = 1; + + while(!(feof(stream))) { + memset(b, 0, sizeof(b)); + fgets(b, sizeof(b), stream); + + if ((*b == '#') || (*b == ';') || (*b == '\r') || (*b == '\n') || (*b == '\0')) + continue; + + ret = policy_parse(domain, b); + if (ret == -1) { + fprintf(stderr, "policy_load(%s): policy_parse failed (line %d)\n", domain, line); + fclose(stream); + return 0; + } + + else if (ret == 1) + break; + + line++; + } + + fclose(stream); + +#ifdef DEBUG + fprintf(stderr, "policy_load(%s): loaded\n", domain); +#endif + + return 1; +} + +/* + Parse policy data +*/ + +static int policy_parse(const char *domain, char *data) +{ + int ia = 0; + domain_t *d = NULL; + policy_t *p = NULL, *lp = NULL; + local_t *l_list = NULL, *l = NULL; + char *h = NULL, *t = NULL, *pp = NULL; + + if ((domain == NULL) || (data == NULL)) + return -1; + + /* + Seperate out domain part + */ + + for (h = t = data; *h; h++) { + if (*h == ':') + break; + } + + if (*h != ':') { + fprintf(stderr, "policy_parse: syntax error: no domain seperator\n"); + return -1; + } + + *h = '\0'; + + /* + Determine if this is the correct policy + */ + + if (strcasecmp(domain, t)) + return 0; + + /* + Seperate domain policy + */ + + for (ia = 0, t = ++h; *h; h++) { + if ((*h == '(') && (ia == 0)) + ia = 1; + + else if ((*h == ')') && (ia == 1)) + ia = 0; + + else if ((*h == ',') && (ia == 0)) + break; + } + + if (*h != ',') { + fprintf(stderr, "policy_parse: syntax error: no domain policy seperator\n"); + return -1; + } + + *h = '\0'; + + p = policy_construct(t); + if (p == NULL) { + fprintf(stderr, "policy_parse: policy_construct failed\n"); + return -1; + } + + /* + Parse locals + */ + + h++; + l_list = l = NULL; + + while(*h) { + if ((*h == '\n') || (*h == '\r')) + break; + + for (ia = 0, t = h; *h; h++) { + if ((*h == '(') && (ia == 0)) + ia = 1; + + else if ((*h == ')') && (ia == 1)) + ia = 0; + + else if ((*h == ',') && (ia == 0)) + break; + } + + if (*h != ',') { + policy_free(p); + fprintf(stderr, "policy_parse: syntax error: no local policy seperator\n"); + return -1; + } + + *h = '\0'; + + /* + Seperate local name from local policy + */ + + for (pp = t; *pp; pp++) { + if (*pp == ':') + break; + } + + if (*pp != ':') { + policy_free(p); + fprintf(stderr, "policy_parse: syntax error: no local name, policy seperator\n"); + return -1; + } + + *pp++ = '\0'; + + if (!(*t)) { + policy_free(p); + fprintf(stderr, "policy_parse: syntax error: empty local name\n"); + return -1; + } + + if ((!(*pp)) || (*pp == ',')) { + policy_free(p); + fprintf(stderr, "policy_parse: syntax error: empty local policy\n"); + return -1; + } + + /* + Load local policy + */ + + lp = policy_construct(pp); + if (lp == NULL) { + policy_free(p); + fprintf(stderr, "policy_parse: policy_construct failed\n"); + return -1; + } + + l = (local_t *)malloc(sizeof(local_t)); + if (l == NULL) { + policy_free(p); + policy_free(lp); + fprintf(stderr, "policy_parse: malloc failed\n"); + return -1; + } + + memset(l, 0, sizeof(local_t)); + + ia = strlen(t); + + l->local = (char *)malloc(ia + 1 + strlen(domain) + 1); + if (l->local == NULL) { + policy_free(p); + policy_free(lp); + free(l); + fprintf(stderr, "policy_parse: malloc failed\n"); + return -1; + } + + memset(l->local, 0, ia + 1 + strlen(domain) + 1); + memcpy(l->local, t, ia); + memcpy(l->local + ia, "@", 1); + memcpy(l->local + ia + 1, domain, strlen(domain)); + + l->policy = lp; + l->next = NULL; + + l->next = l_list; + l_list = l; + + t = ++h; + } + + /* + Allocate and fill domain structure + */ + + d = (domain_t *)malloc(sizeof(domain_t)); + if (d == NULL) { + policy_free(p); + local_free(l_list); + fprintf(stderr, "policy_parse: malloc failed\n"); + return -1; + } + + memset(d, 0, sizeof(domain_t)); + + ia = strlen(domain); + + d->domain = (char *)malloc(ia + 1); + if (d->domain == NULL) { + policy_free(p); + local_free(l_list); + free(d); + fprintf(stderr, "policy_parse: malloc failed\n"); + return -1; + } + + memset(d->domain, 0, ia + 1); + memcpy(d->domain, domain, ia); + + d->locals = l_list; + d->policy = p; + + d->next = domains; + domains = d; + + return 1; +} + +/* + Parse policy data into a policy structure +*/ + +static policy_t *policy_construct(char *data) +{ + char pc = 0, **args = NULL; + policy_t *p_list = NULL, *p = NULL; + char *h = NULL, *t = NULL, *hp = NULL; + int flags = POLICY_F_NONE, numargs = 0, i = 0, ret = 0; + + if (data == NULL) + return 0; + + pc = 0; + + for (h = data; *h; h++) { + pc = *h; + + flags = policy_flags(*h); + if (flags == POLICY_F_NONE) { + fprintf(stderr, "policy_construct: unknown identifier\n"); + return NULL; + } + + args = NULL; + + /* + Count, parse and allocate addresses + */ + + if (*(h + 1) == '(') { + t = (h + 2); + + for (h += 2; *h; h++) { + if (*h == ')') + break; + } + + if (*h != ')') { + fprintf(stderr, "policy_construct: no terminating ')'\n"); + return 0; + } + + numargs = 1; + + for (hp = t; hp < h; hp++) { + if (*hp == ',') + numargs++; + } + + /* + No arguments + */ + + if (hp == t) { + fprintf(stderr, "policy_construct: empty argument\n"); + return NULL; + } + + args = (char **)malloc(sizeof(char *) * numargs); + if (args == NULL) { + fprintf(stderr, "policy_construct: malloc failed\n"); + return NULL; + } + + for (i = 0; i < numargs; i++) + args[i] = NULL; + + *h = '\0'; + + ret = policy_construct_parse_arguments(args, numargs, t); + if (!ret) { + fprintf(stderr, "policy_construct: policy_construct_parse_arguments failed\n"); + free(args); + return NULL; + } + } + + /* + Allocate policy structure, + add to linked list + */ + + p = (policy_t *)malloc(sizeof(policy_t)); + if (p == NULL) { + for (i = 0; i < numargs; i++) + free(args[i]); + + free(args); + + fprintf(stderr, "policy_construct: malloc failed\n"); + return NULL; + } + + memset(p, 0, sizeof(policy_t)); + + p->numargs = numargs; + p->args = args; + p->flags = flags; + p->next = NULL; + + p->next = p_list; + p_list = p; + } + + return p_list; +} + +/* + Parse policy arguments, + fill array +*/ + +static int policy_construct_parse_arguments(char **args, const int numargs, char *data) +{ + int len = 0, i = 0; + char *h = NULL, *t = NULL; + + i = 0; + + for (i = 0, h = t = data;;h++) { + if ((*h == ',') || (*h == '\0')) { + len = (h - t); + + if (*h == '\0') + h = NULL; + else + *h = '\0'; + + if (!(*t)) { + for (; i >= 0; i--) + free(args[i]); + + fprintf(stderr, "policy_construct_parse_arguments: empty argument value\n"); + return 0; + } + + if (i >= numargs) { + for (; i >= 0; i--) + free(args[i]); + + fprintf(stderr, "policy_construct_parse_arguments: too many arguments\n"); + return 0; + } + + args[i] = (char *)malloc(len + 1); + if (args[i] == NULL) { + for (; i >= 0; i--) + free(args[i]); + + fprintf(stderr, "policy_construct_parse_arguments: malloc failed\n"); + return 0; + } + + memset(args[i], 0, len + 1); + memcpy(args[i], t, len); + + i++; + + if (!h) + break; + + t = (h + 1); + } + } + + if (i != numargs) { + fprintf(stderr, "policy_construct_parse_arguments: post argument count failed (%d/%d)\n", + i, numargs); + + for (; i >= 0; i--) + free(args[i]); + + return 0; + } + + return 1; +} + +/* + Match a filter against an address string +*/ + +static int policy_match(const char *filter, const char *address) +{ + int ret = 0; + + if ((filter == NULL) || (address == NULL)) + return 0; + +#ifndef FNM_CASEFOLD + int len = 0, flags = 0; + char filt[POLICY_MAX_FILTER] = { 0 }, addr[POLICY_MAX_ADDRESS] = { 0 }, + *p = NULL; + + memset(filt, 0, sizeof(filt)); + + len = strlen(filter); + if (len >= POLICY_MAX_FILTER) + len = (POLICY_MAX_FILTER - 1); + + memcpy(filt, filter, len); + + for (p = filt; *p; p++) + *p = tolower(*p); + + memset(addr, 0, sizeof(addr)); + + len = strlen(address); + if (len >= POLICY_MAX_ADDRESS) + len = (POLICY_MAX_ADDRESS - 1); + + memcpy(addr, address, len); + + for (p = addr; *p; p++) + *p = tolower(*p); + + filter = filt; + address = addr; +#else + int flags = FNM_CASEFOLD; +#endif + + ret = fnmatch(filter, address, flags); + if (ret == 0) + return 1; + + return 0; +} + +/* + See if a given policy applies to a particular + address +*/ + +static int policy_applies_to(const policy_t *p, const char *addr) +{ + int i = 0; + + if ((p == NULL) || (addr == NULL)) + return 0; + + if (p->numargs == 0) { +#ifdef DEBUG + fprintf(stderr, "policy_applies_to: no arguments (yes)\n"); +#endif + return 1; + } + + if (p->args == NULL) { +#ifdef DEBUG + fprintf(stderr, "policy_applies_to: broken arguments (no)\n"); +#endif + return 0; + } + + for (i = 0; i < p->numargs; i++) { + if (policy_match(p->args[i], addr)) { +#ifdef DEBUG + fprintf(stderr, "policy_applies_to: match (yes)\n"); +#endif + return 1; + } + } + +#ifdef DEBUG + fprintf(stderr, "policy_applies_to: no match (no)\n"); +#endif + return 0; +} + +/* + Return flags for policy identifier +*/ + +static int policy_flags(const char c) +{ + int flags = POLICY_F_NONE; + + switch(c) { + case 'l': + flags = (POLICY_F_LOCAL|POLICY_F_DISALLOW); + break; + + case 'r': + flags = (POLICY_F_REMOTE|POLICY_F_DISALLOW); + break; + + case 'e': + flags = (POLICY_F_EXTERNAL|POLICY_F_DISALLOW); + break; + + case 'i': + flags = (POLICY_F_INTERNAL|POLICY_F_DISALLOW); + break; + + case 'L': + flags = (POLICY_F_LOCAL|POLICY_F_ALLOW); + break; + + case 'R': + flags = (POLICY_F_REMOTE|POLICY_F_ALLOW); + break; + + case 'E': + flags = (POLICY_F_EXTERNAL|POLICY_F_ALLOW); + break; + + case 'I': + flags = (POLICY_F_INTERNAL|POLICY_F_ALLOW); + break; + + default: + break; + }; + + return flags; +} + +/* + Find a policy definition +*/ + +static policy_t *policy_find(const policy_t *sp, const int flag) +{ + for (; sp; sp = sp->next) { + if (sp->flags & flag) + return (policy_t *)sp; + } + + return NULL; +} + +/* + Compare policies and determine + if messaging is forbidden +*/ + +static int policy_forbids(void) +{ + policy_t *pl = NULL, *p = NULL; + local_t *s_local = NULL, *r_local = NULL; + int dtype = 0, s_forbid = 0, r_forbid = 0; + + /* + Find local policy if any + */ + + if (s_domain) + s_local = local_find(s_domain, remoteinfo ? remoteinfo : mailfrom.s); + + if (r_domain) + r_local = local_find(r_domain, addr.s); + + /* + Determine type of delivery + (local, remote, external) + */ + + if ((s_domain) && (r_domain) && (s_domain == r_domain)) + dtype = POLICY_F_LOCAL; + + else if ((s_domain == NULL) && (r_domain)) + dtype = POLICY_F_EXTERNAL; + + else if ((s_domain) && (r_domain == NULL)) + dtype = POLICY_F_REMOTE; + + else if ((s_domain) && (r_domain) && (s_domain != r_domain)) + dtype = POLICY_F_REMOTE; + + else if ((s_domain == NULL) && (r_domain == NULL)) { +#ifdef DEBUG + fprintf(stderr, "policy_forbids: no policies for this delivery\n"); +#endif + return 0; + } + + else { + fprintf(stderr, "policy_forbids: unknown delivery type\n"); + return -1; + } + + p = NULL; + s_forbid = r_forbid = 0; + + /* + If there is a local rule for sender, use that to + determine if able to send. If not, check domain + */ + + if (s_local) { + p = policy_find(s_local->policy, dtype); + if (p) { + /* + See if policy matches + */ + + if (p->flags & POLICY_F_DISALLOW) { + if (policy_applies_to(p, addr.s)) { +#ifdef DEBUG + fprintf(stderr, "*** sender local policy disallows\n"); +#endif + s_forbid = 1; + } + +#ifdef DEBUG + else + fprintf(stderr, "*** sender local policy allows\n"); +#endif + } + + else { +#ifdef DEBUG + if (policy_applies_to(p, addr.s)) + fprintf(stderr, "*** sender local policy allows\n"); + + else { + fprintf(stderr, "*** sender local policy denies\n"); + s_forbid = 1; + } +#else + if (!(policy_applies_to(p, addr.s))) + s_forbid = 1; +#endif + } + } + +#ifdef DEBUG + else + fprintf(stderr, "*** no sender local policy\n"); +#endif + } + + if ((p == NULL) && (s_domain)) { + p = policy_find(s_domain->policy, dtype); + if (p) { + if (p->flags & POLICY_F_DISALLOW) { + s_forbid = 1; +#ifdef DEBUG + fprintf(stderr, "--- sender domain policy disallows\n"); +#endif + } + } + +#ifdef DEBUG + else + fprintf(stderr, "--- no sender domain policy\n"); +#endif + } + + /* + Deny messaging + */ + + if (s_forbid) { +#ifdef DEBUG + fprintf(stderr, "policy_forbids: sender policy denies messaging\n"); +#endif + return 1; + } + + /* + Reverse delivery type, and check same above + for recipient unless the recipient is of the + same domain + */ + + /* + A local user to local user delivery is an 'internal' + delivery for the recipient user. + */ + + if (dtype == POLICY_F_LOCAL) + dtype = POLICY_F_INTERNAL; + + /* + Sender on same system, different domain. + This is considered an 'external' delivery + to the recipient + */ + + else if (dtype == POLICY_F_REMOTE) + dtype = POLICY_F_EXTERNAL; + + /* + Sender from off-system remains + as external + */ + + else if (dtype != POLICY_F_EXTERNAL) { + fprintf(stderr, "policy_forbids: unknown recipient delivery type\n"); + return -1; + } + + p = NULL; + r_forbid = 0; + + if (r_local) { + p = policy_find(r_local->policy, dtype); + if (p) { + /* + See if policy matches + */ + + if (p->flags & POLICY_F_DISALLOW) { + if (policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s)) { +#ifdef DEBUG + fprintf(stderr, "*** recipient local policy disallows\n"); +#endif + r_forbid = 1; + } + +#ifdef DEBUG + else + fprintf(stderr, "*** recipient local policy allows\n"); +#endif + } + + else { +#ifdef DEBUG + if (policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s)) + fprintf(stderr, "*** recipient local policy allows\n"); + + else { + fprintf(stderr, "*** recipient local policy denies\n"); + r_forbid = 1; + } +#else + if (!(policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s))) + r_forbid = 1; +#endif + } + } + +#ifdef DEBUG + else + fprintf(stderr, "*** no recipient local policy\n"); +#endif + } + + if ((p == NULL) && (r_domain)) { + p = policy_find(r_domain->policy, dtype); + if (p) { + if (p->flags & POLICY_F_DISALLOW) { + r_forbid = 1; +#ifdef DEBUG + fprintf(stderr, "--- recipient domain policy disallows\n"); +#endif + } + } + +#ifdef DEBUG + else + fprintf(stderr, "--- no recipient domain policy\n"); +#endif + + } + + /* + Deny messaging + */ + + if (r_forbid) { +#ifdef DEBUG + fprintf(stderr, "policy_forbids: recipient policy denies messaging\n"); +#endif + return 1; + } + + /* + Accept message + */ + + return 0; +} + +/* + Search for a domain in the linked list of domains +*/ + +static domain_t *domain_find(const char *domain) +{ + domain_t *d = NULL; + + if (domain == NULL) + return NULL; + + for (d = domains; d; d = d->next) { + if (!(strcasecmp(d->domain, domain))) + return d; + } + + return NULL; +} + +/* + Search for a local under a domain in the locals linked list +*/ + +static local_t *local_find(const domain_t *d, const char *local) +{ + local_t *l = NULL; + + if ((d == NULL) || (local == NULL)) { + fprintf(stderr, "local_find: null argument\n"); + return NULL; + } + + for (l = d->locals; l; l = l->next) { + if (policy_match(l->local, local)) + return l; + } + + return NULL; +} + +/* + Deallocate a policy +*/ + +static void policy_free(policy_t *policy) +{ + int i = 0; + policy_t *p = NULL, *op = NULL; + + if (policy == NULL) + return; + + p = policy; + + while(p) { + op = p; + p = p->next; + + if (op->args) { + if (op->numargs) { + for (i = 0; i < op->numargs; i++) + free(op->args[i]); + + free(op->args); + } + + else + fprintf(stderr, "policy_free: no argument count\n"); + } + + free(op); + } +} + +/* + Deallocate a local +*/ + +static void local_free(local_t *local) +{ + local_t *l = NULL, *ol = NULL; + + if (local == NULL) + return; + + l = local; + + while(l) { + ol = l; + l = l->next; + + if (ol->policy) + policy_free(ol->policy); + + if (ol->local) + free(ol->local); + + free(ol); + } +} + +/* + Deallocate all domains +*/ + +static void domains_free(void) +{ + domain_t *d = NULL, *od = NULL; + + d = domains; + + while(d) { + od = d; + d = d->next; + + if (od->policy) + policy_free(od->policy); + + if (od->locals) + local_free(od->locals); + + if (od->domain) + free(od->domain); + + free(od); + } + + domains = NULL; +} diff -ruN ../netqmail-1.06-original/policy.h netqmail-1.06/policy.h --- ../netqmail-1.06-original/policy.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/policy.h 2019-02-27 20:57:13.395025014 +0100 @@ -0,0 +1,6 @@ +#ifndef __POLICY_H_ + #define __POLICY_H_ + +int policy_check(void); + +#endif diff -ruN ../netqmail-1.06-original/qmail-clean.c netqmail-1.06/qmail-clean.c --- ../netqmail-1.06-original/qmail-clean.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-clean.c 2019-02-27 20:57:13.395025014 +0100 @@ -73,22 +73,26 @@ if (line.len < 7) { respond("x"); continue; } if (line.len > 100) { respond("x"); continue; } if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */ - for (i = 5;i < line.len - 1;++i) + for (i = line.len - 2;i > 4;--i) + { + if (line.s[i] == '/') break; if ((unsigned char) (line.s[i] - '0') > 9) { respond("x"); continue; } - if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; } + } + if (line.s[i] == '/') + if (!scan_ulong(line.s + i + 1,&id)) { respond("x"); continue; } if (byte_equal(line.s,5,"foop/")) { #define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \ if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; } - U("intd/",0) + U("intd/",1) U("mess/",1) respond("+"); } else if (byte_equal(line.s,4,"todo/")) { - U("intd/",0) - U("todo/",0) + U("intd/",1) + U("todo/",1) respond("+"); } else diff -ruN ../netqmail-1.06-original/qmail-control.9 netqmail-1.06/qmail-control.9 --- ../netqmail-1.06-original/qmail-control.9 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-control.9 2019-02-27 20:57:13.395025014 +0100 @@ -20,7 +20,11 @@ Comments are allowed in +.IR badhelo , .IR badmailfrom , +.IR badmailfromnorelay , +.IR badmailto , +.IR badmailtonorelay , .IR locals , .IR percenthack , .IR qmqpservers , @@ -40,14 +44,25 @@ .ta 5c 10c control default used by +.I authsender \fR(none) \fRqmail-remote +.I badhelo \fR(none) \fRqmail-smtpd +.I badhelonorelay \fR(none) \fRqmail-smtpd .I badmailfrom \fR(none) \fRqmail-smtpd +.I badmailfromnorelay \fR(none) \fRqmail-smtpd +.I badmailto \fR(none) \fRqmail-smtpd +.I badmailtonorelay \fR(none) \fRqmail-smtpd .I bouncefrom \fRMAILER-DAEMON \fRqmail-send .I bouncehost \fIme \fRqmail-send +.I brtlimit \fR0 \fRqmail-smtpd +.I clientca.pem \fR(none) \fRqmail-smtpd +.I clientcert.pem \fR(none) \fRqmail-remote .I concurrencylocal \fR10 \fRqmail-send .I concurrencyremote \fR20 \fRqmail-send .I defaultdomain \fIme \fRqmail-inject .I defaulthost \fIme \fRqmail-inject .I databytes \fR0 \fRqmail-smtpd +.I dnsbllist \fR(none) \fRqmail-smtpd +.I dh2048.pem \fR(none) \fRqmail-smtpd .I doublebouncehost \fIme \fRqmail-send .I doublebounceto \fRpostmaster \fRqmail-send .I envnoathost \fIme \fRqmail-send @@ -56,22 +71,35 @@ .I localiphost \fIme \fRqmail-smtpd .I locals \fIme \fRqmail-send .I morercpthosts \fR(none) \fRqmail-smtpd +.I outgoingip \fR0.0.0.0 \fRqmail-remote .I percenthack \fR(none) \fRqmail-send .I plusdomain \fIme \fRqmail-inject .I qmqpservers \fR(none) \fRqmail-qmqpc .I queuelifetime \fR604800 \fRqmail-send .I rcpthosts \fR(none) \fRqmail-smtpd +.I rsa2048.pem \fR(none) \fRqmail-smtpd +.I servercert.pem \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 taps \fR(none) \fRqmail-queue .I timeoutconnect \fR60 \fRqmail-remote .I timeoutremote \fR1200 \fRqmail-remote .I timeoutsmtpd \fR1200 \fRqmail-smtpd +.I tlsclients \fR(none) \fRqmail-smtpd +.I tlsclientciphers \fR(none) \fRqmail-remote +.I tlshosts/FQDN.pem \fR(none) \fRqmail-remote +.I tlsserverciphers \fR(none) \fRqmail-smtpd .I virtualdomains \fR(none) \fRqmail-send .fi .RE .SH "SEE ALSO" qmail-inject(8), qmail-qmqpc(8), +qmail-queue(8), qmail-remote(8), qmail-send(8), qmail-showctl(8), diff -ruN ../netqmail-1.06-original/qmail-dk.9 netqmail-1.06/qmail-dk.9 --- ../netqmail-1.06-original/qmail-dk.9 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qmail-dk.9 2019-02-27 20:57:13.395025014 +0100 @@ -0,0 +1,164 @@ +.TH qmail-dk 8 +.SH NAME +qmail-dk \- sign/verify and queue a mail message for delivery +.SH SYNOPSIS +.B qmail-dk +.SH DESCRIPTION +.B qmail-dk +has the same interface as +.B qmail-queue +except that it inserts an appropriate DomainKeys header before it +queues the message. To invoke +.BR qmail-dk , +set QMAILQUEUE to point to qmail-dk in the environment when +you send or receive email. qmail-dk will call +.BR qmail-multi . +To invoke an executable other than +.B qmail-multi +set DKQUEUE=bin/qmail-queue for example. + +.B qmail-dk +supports DomainKey signing and verification. It uses the libdomainkey +and OpenSSL libraries. To sign a message, set the +.B DKSIGN +environment variable to the pathname to the private key that will be +used to sign the message. If there is a % character in the environment +variable, it is removed and replaced by the domain name in the From: header. +If, after substituting the %, that file does not exist, the % will be removed. +If the private key file does not exist and does not have a % character, the message will +be rejected with error 32. The selector will be taken from the basename of the file. +The private key should be created by +.BR dknewkey(8) . + +In the absense of DKSIGN and DKVERIFY environment variable, qmail-dk will sign the +message if RELAYCLIENT environment variable is set and verify the message if RELAYCLIENT +environment variable is not set. If DKVERIFY is set, you can disable dk verification, if +RELAYCLIENT is set by setting RELAYCLIENT_NODKVERIFY environment variable. + +You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS + b Length of Advice + c simple, nofws + s + h include h= tag + r Skip Duplicate Headers +.EX + DKSIGNOPTIONS="-h -r -c nofws" + sets the h= tag, skips duplicate headers and sets nofws canonicalization +.EE + +To verify a message, set the +.B DKVERIFY +environment variable to a desired set of letters. Precisely, if you +want a libdomainkey return status to generate an error, include that +letter, where A is the first return status (DK_STAT_OK), B is the +second (DK_STAT_BADSIG), etc. The letter should be uppercase if you +want a permanent error to be returned and lowercase if you want a temporary error +to be returned. + +The complete set of letters with the corresponding return status is given below + + A - DK_STAT_OK - Function completed successfully + B - DK_STAT_BADSIG - Signature was available but failed to verify against + domain specified key + C - DK_STAT_NOSIG - No signature available in message + D - DK_STAT_NOKEY - No public key available (permanent failure) + E - DK_STAT_BADKEY - Unusable key, public if verifying, private if signing + F - DK_STAT_CANTVRFY - Cannot get domain key to verify signature + (temporary failure) + G - DK_STAT_SYNTAX - Message is not valid syntax. Signature could not be + created/checked + H - DK_STAT_NORESOURCE - Could not get critical resource (temporary failure) + I - DK_STAT_ARGS - Arguments are not usable. + J - DK_STAT_REVOKED - Key has been revoked. + K - DK_STAT_INTERNAL - cannot call this routine in this context. Internal error. + L - DK_STAT_GRANULARITY - Granularity mismatch: sender doesn't match g= option. + M - DK_STAT_DUPLICATE - duplicate Domainkey-Signature in message. + +For example, if you want to permanently reject messages that have a +signature that has been revoked, include the letter 'K' in the +.B DKVERIFY +environment variable. A conservative set of letters is +.BR DEGIJKfh . +Reject permanently BADSIG, NOKEY, BADKEY, SYNTAX, ARGS, REVOKED, and +INTERNAL errors, and temporarily CANTVRFY and NORESOURCE. Add in +.B B +if you want to reject messages that have a signature that doesn't +verify (presumably because the message is a forgery or has been +damaged in transit. Note that +.B qmail-dk +always inserts the +.B DomainKey-Status +header, so that messages can be +rejected at delivery time, or in the mail reader. + +Typically, you would sign messages generated on-host by setting +.B DKSIGN +in the environment before running an email program. DKSIGN will be carried +through qmail's sendmail emulation through +.B qmail-inject +to +.BR qmail-dk . +You would also set it for +.B qmail-smtpd +at the same time +.B RELAYCLIENT +is set, most often in the tcpserver cdb file. If a host is authorized +to relay, you probably want to sign messages sent by that host. +.B DKVERIFY +should be set for all other hosts. + +If neither +.B DKSIGN +nor +.B DKVERIFY +are set, then +.B DKSIGN +will be set to QMAILHOME/control/domainkeys/%/default. The % will be replaced by the domain in the +From: header. If such a file does not exist, then it will be set to +QMAILHOME/control/domainkeys/default. If such a private key exists, it will be used to sign the +domain. You can also set \fBDKKEY\fR to chose a key different from +QMAILHOME/control/domainkeys/%/default. \fBDKKEY\fR can also have % character that will be +replaced by the domain in the From: header. + +By default +.B qmail-dk +will use all of the headers when signing a message. +The environment variable +.B DKEXCLUDEHEADERS +may be set to a colon-separated list of headers that are to be excluded from signing. + +.B qmail-dk +will ordinarily spawn +.BR qmail-multi , +but if DKQUEUE is set in the environment, +the program that it points to will be executed instead. + +.SH "EXIT CODES" +.B qmail-dk +returns the same exit codes as qmail-queue with these additions: +.TP 5 +.B 35 +The private key file does not exist. +.TP 5 +.B 57 +Trouble waiting for qmail-queue to exit. +.TP 5 +.B 58 +Unable to vfork. +.TP 5 +.B 59 +Unable to create a pipe to qmail-queue. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-header(5), +dknewkey(8), +dktest(8), +qmail-inject(8), +qmail-qmqpc(8), +qmail-queue(8), +qmail-send(8), +qmail-smtpd(8), +qmail-dkim(8), +domain-keys(5) + diff -ruN ../netqmail-1.06-original/qmail-dk.c netqmail-1.06/qmail-dk.c --- ../netqmail-1.06-original/qmail-dk.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qmail-dk.c 2019-02-27 20:57:13.396025003 +0100 @@ -0,0 +1,866 @@ +/* + * $Log: qmail-dk.c,v $ + * Revision 1.37 2013-01-24 22:42:07+05:30 Cprogrammer + * alternate code for chosing DKSIGN selector filename + * + * Revision 1.36 2011-11-10 14:31:42+05:30 Cprogrammer + * BUG ssout to be assigned only after pidopen() + * + * Revision 1.35 2011-11-07 09:35:25+05:30 Cprogrammer + * set ssout, sserr, ssin before executing other functions + * + * Revision 1.34 2011-07-29 09:28:48+05:30 Cprogrammer + * fixed key file name + * + * Revision 1.33 2011-07-28 19:34:45+05:30 Cprogrammer + * BUG - fixed opening of private key with absolute path + * + * Revision 1.32 2011-07-22 19:29:06+05:30 Cprogrammer + * fixed compilation error + * + * Revision 1.31 2011-07-22 14:39:53+05:30 Cprogrammer + * added DKDOMAIN feature to set d= tag + * + * Revision 1.30 2011-06-04 17:44:16+05:30 Cprogrammer + * remove % sign from private key if file not found + * + * Revision 1.29 2010-07-21 08:59:14+05:30 Cprogrammer + * use CONTROLDIR environment variable instead of a hardcoded control directory + * + * Revision 1.28 2009-08-13 18:36:39+05:30 Cprogrammer + * for verification continue in case of DK_SYNTAX errors + * + * Revision 1.27 2009-04-22 13:42:47+05:30 Cprogrammer + * made fd for custom error configurable through env variable ERROR_FD + * + * Revision 1.26 2009-04-21 09:05:14+05:30 Cprogrammer + * return relevant error message for reading private key + * + * Revision 1.25 2009-04-21 08:56:03+05:30 Cprogrammer + * return temp errors for temporary failures + * + * Revision 1.24 2009-04-21 08:15:39+05:30 Cprogrammer + * moved stralloc variable outside block + * + * Revision 1.23 2009-04-20 22:20:40+05:30 Cprogrammer + * added DKSIGNOPTIONS + * + * Revision 1.22 2009-04-07 11:38:02+05:30 Cprogrammer + * use TMPDIR env variable for tmp directory + * + * Revision 1.21 2009-04-05 12:52:10+05:30 Cprogrammer + * added preprocessor warning + * + * Revision 1.20 2009-03-31 08:21:12+05:30 Cprogrammer + * set dksign when RELAYCLIENT is defined when both dksign, dkverify are undefined + * + * Revision 1.19 2009-03-28 22:26:35+05:30 Cprogrammer + * use DKSIGN,DKVERIFY env variables if RELAYCLIENT is not set + * + * Revision 1.18 2009-03-28 22:02:32+05:30 Cprogrammer + * removed extra white space + * + * Revision 1.17 2009-03-28 11:34:51+05:30 Cprogrammer + * BUG fix. corrected setting of dksign, dkverify variables + * + * Revision 1.16 2009-03-22 16:58:13+05:30 Cprogrammer + * report custom errors to qmail-queue through custom error interface + * + * Revision 1.15 2009-03-21 15:15:56+05:30 Cprogrammer + * improved logic + * + * Revision 1.14 2009-03-20 22:35:24+05:30 Cprogrammer + * fix for multi-line headers + * + * Revision 1.13 2009-03-19 08:28:12+05:30 Cprogrammer + * added EXCLUDEHEADERS + * + * Revision 1.12 2009-03-14 17:11:32+05:30 Cprogrammer + * added DK_STAT_GRANULARITY + * + * Revision 1.11 2009-03-14 08:52:54+05:30 Cprogrammer + * look for domainkey in control/domainkeys + * + * Revision 1.10 2005-08-23 17:33:37+05:30 Cprogrammer + * gcc 4 compliance + * + * Revision 1.9 2005-04-01 21:42:04+05:30 Cprogrammer + * added DK_STAT_SYNTAX + * changed error codes + * + * Revision 1.8 2004-11-02 09:15:53+05:30 Cprogrammer + * commented out writing of Comments: header + * + * Revision 1.7 2004-10-24 21:32:00+05:30 Cprogrammer + * removed unecessary header files + * + * Revision 1.6 2004-10-22 20:28:18+05:30 Cprogrammer + * added RCS id + * + * Revision 1.5 2004-10-22 15:36:45+05:30 Cprogrammer + * removed readwrite.h + * + * Revision 1.4 2004-10-22 14:44:26+05:30 Cprogrammer + * use control_readnativefile to avoid skipping signure with '#' char + * + * Revision 1.3 2004-10-20 20:08:53+05:30 Cprogrammer + * libdomainkeys-0.62 + * + * Revision 1.2 2004-09-23 22:55:32+05:30 Cprogrammer + * removed uneccessary header files + * + * Revision 1.1 2004-08-28 01:02:16+05:30 Cprogrammer + * Initial revision + * + */ +#include +#include +#include +#include +#include "sgetopt.h" +#include "substdio.h" +#include "open.h" +#include "qmail.h" +#include "sig.h" +#include "fmt.h" +#include "fd.h" +#include "alloc.h" +#include "str.h" +#include "getln.h" +#include "case.h" +#include "stralloc.h" +#include "datetime.h" +#include "now.h" +#include "wait.h" +#include "auto_qmail.h" +#include "env.h" +#include "scan.h" +#include "mess822.h" +#include "control.h" +#include "domainkeys.h" + +#define DEATH 86400 /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ +#define ADDR 1003 +#define ADVICE_BUF 2048 + +char inbuf[2048]; +struct substdio ssin; +char outbuf[256]; +struct substdio ssout; +struct substdio sserr; +char errbuf[256]; + +datetime_sec starttime; +struct datetime dt; +unsigned long mypid; +unsigned long uid; +char *pidfn; +int messfd; +int readfd; +char *dksign = 0; +char *dkverify = 0; + +char **MakeArgs(char *); +void FreeMakeArgs(char **); + +void +die(e) + int e; +{ + _exit(e); +} + +void +die_write() +{ + die(53); +} + +void +die_read() +{ + die(54); +} + +void +sigalrm() +{ + /*- thou shalt not clean up here */ + die(52); +} + +void +sigbug() +{ + die(81); +} + +void +custom_error(char *flag, char *status, char *code) +{ + char *c; + + if (substdio_put(&sserr, flag, 1) == -1) + die_write(); + if (substdio_put(&sserr, "qmail-dk: ", 10) == -1) + die_write(); + if (substdio_puts(&sserr, status) == -1) + die_write(); + if (code) + { + if (substdio_put(&sserr, " (#", 3) == -1) + die_write(); + c = (*flag == 'Z') ? "4" : "5"; + if (substdio_put(&sserr, c, 1) == -1) + die_write(); + if (substdio_put(&sserr, code + 1, 4) == -1) + die_write(); + if (substdio_put(&sserr, ")", 1) == -1) + die_write(); + } + if (substdio_flush(&sserr) == -1) + die_write(); + return; +} + +void +maybe_die_dk(e) + DK_STAT e; +{ + switch (e) + { + case DK_STAT_NORESOURCE: + _exit(51); + case DK_STAT_INTERNAL: + _exit(81); + case DK_STAT_ARGS: + custom_error("Z", "Arguments are not usable. (#4.3.5)", 0); + _exit(88); + case DK_STAT_SYNTAX: + if (!dksign && dkverify) + return; + custom_error("Z", "Message is not valid syntax. Signature could not be created/checked (#4.6.0)", 0); + _exit(88); + case DK_STAT_CANTVRFY: + custom_error("Z", "Cannot get domainkeys to verify signature (#5.7.5)", 0); + _exit(88); + case DK_STAT_BADKEY: + if (env_get("DKVERIFY")) + custom_error("Z", "Unusable public key for verifying (#5.7.5)", 0); + else + custom_error("Z", "Unusable private key for signing (#5.7.15", 0); + _exit(88); + default: + return; + } +} + +unsigned int +pidfmt(s, seq) + char *s; + unsigned long seq; +{ + unsigned int i; + unsigned int len; + char *tmpdir; + + if (!(tmpdir = env_get("TMPDIR"))) + tmpdir = "/tmp"; + len = 0; + i = fmt_str(s, tmpdir); + len += i; + if (s) + s += i; + i = fmt_str(s, "/qmail-dk."); + len += i; + if (s) + s += i; + i = fmt_ulong(s, mypid); + len += i; + if (s) + s += i; + i = fmt_str(s, "."); + len += i; + if (s) + s += i; + i = fmt_ulong(s, starttime); + len += i; + if (s) + s += i; + i = fmt_str(s, "."); + len += i; + if (s) + s += i; + i = fmt_ulong(s, seq); + len += i; + if (s) + s += i; + ++len; + if (s) + *s++ = 0; + + return len; +} + +void +pidopen() +{ + unsigned int len; + unsigned long seq; + + seq = 1; + len = pidfmt((char *) 0, seq); + if (!(pidfn = alloc(len))) + die(51); + for (seq = 1; seq < 10; ++seq) + { + if (pidfmt((char *) 0, seq) > len) + die(81); /*- paranoia */ + pidfmt(pidfn, seq); + if ((messfd = open_excl(pidfn)) != -1) + return; + } + die(63); +} + +char tmp[FMT_ULONG]; +DK_LIB *dklib; +DK *dk; +DK_STAT st; +stralloc dkoutput = { 0 }; /*- Domainkey-Signature */ +stralloc dksignature = { 0 }; /*- content of private signature */ +stralloc dkopts = { 0 }; +char *dkqueue = 0; +char *dkexcludeheaders; + +static void +write_signature(DK *dk, char *dk_selector, char *keyfn, + int advicelen, int opth, char *canon) +{ + unsigned char advice[ADVICE_BUF]; + char *selector, *from, *tmp; + static stralloc keyfnfrom = { 0 }; + int i; + + from = dk_from(dk); + if (keyfn[0] != '/') + { + if (!stralloc_copys(&keyfnfrom, "control/")) + die(51, 1); + } + i = str_chr(keyfn, '%'); + if (keyfn[i]) + { + if (keyfn[0] == '/') + { + if (!stralloc_copyb(&keyfnfrom, keyfn, i)) + die(51, 1); + } else + if (!stralloc_catb(&keyfnfrom, keyfn, i)) + die(51, 1); + if (!stralloc_cats(&keyfnfrom, from)) + die(51); + if (!stralloc_cats(&keyfnfrom, keyfn + i + 1)) + die(51); + if (!stralloc_0(&keyfnfrom)) + die(51); + if (access(keyfnfrom.s, F_OK)) + { + /*- since file is not found remove '%' sign */ + keyfnfrom.len = 8; + if (keyfn[0] == '/') + { + if (!stralloc_copyb(&keyfnfrom, keyfn, i)) + die(51, 1); + } else + if (!stralloc_catb(&keyfnfrom, keyfn, i)) + die(51, 1); + if ((i - 1) > 0 && keyfn[i - 1] == '/' && keyfn[i + 1] == '/') + i++; + if (!stralloc_cats(&keyfnfrom, keyfn + i + 1)) + die(51); + if (!stralloc_0(&keyfnfrom)) + die(51); + } + } else + { + if (keyfn[0] == '/') + { + if (!stralloc_copys(&keyfnfrom, keyfn)) + die(51, 1); + } else + if (!stralloc_cats(&keyfnfrom, keyfn)) + die(51, 1); + if (!stralloc_0(&keyfnfrom)) + die(51); + } + switch (control_readnativefile(&dksignature, keyfnfrom.s, 1)) + { + case 0: /*- file not present */ + /* + * You may have multiple domains, but may chose to sign + * only for few domains which have the key present. Do not + * treat domains with missing key as an error. + */ + if (keyfn[i]) + return; + die(32); + case 1: + break; + default: + custom_error("Z", "Unable to read private key. (#4.3.0)", 0); + _exit(88); + } + for (i = 0; i < dksignature.len; i++) + { + if (dksignature.s[i] == '\0') + dksignature.s[i] = '\n'; + } + if (!stralloc_0(&dksignature)) + die(51); + st = dk_getsig(dk, dksignature.s, advice, advicelen); + maybe_die_dk(st); + if (!dk_selector) + { + selector = keyfn; + while (*keyfn) + { + if (*keyfn == '/') + selector = keyfn + 1; + keyfn++; + } + } else + selector = dk_selector; + if (!stralloc_cats(&dkoutput, +#if 0 + "Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n" +#endif + "DomainKey-Signature: a=rsa-sha1; q=dns; c=")) + die(51); + if (!stralloc_cats(&dkoutput, canon)) + die(51); + if (!stralloc_cats(&dkoutput, ";\n")) + die(51); + if (!stralloc_cats(&dkoutput, " s=")) + die(51); + if (!stralloc_cats(&dkoutput, selector)) + die(51); + if (!stralloc_cats(&dkoutput, "; d=")) + die(51); + tmp = env_get("DKDOMAIN"); + if (from || tmp) + { + if (!stralloc_cats(&dkoutput, tmp ? tmp : from)) + die(51); + } else + if (!stralloc_cats(&dkoutput, "unknown")) + die(51); + if (!stralloc_cats(&dkoutput, ";\n")) + die(51); + if (!stralloc_cats(&dkoutput, " b=")) + die(51); + if (!stralloc_cats(&dkoutput, (char *) advice)) + die(51); + if (dkexcludeheaders || opth) + { + if ((i = dk_headers(dk, NULL)) > 0) + { + if (!(tmp = alloc(i))) + die(51); + if (!dk_headers(dk, tmp)) + die(51); + if (!stralloc_cats(&dkoutput, ";\n h=")) + die(51); + if (!stralloc_cats(&dkoutput, tmp)) + die(51); + alloc_free(tmp); + } + } + if (!stralloc_cats(&dkoutput, ";\n")) + die(51); +} + +int +find_header(stralloc *line) +{ + static stralloc headers = { 0 }; + int n, i, j; + + for (n = 0; n < line->len; ++n) + { + if (line->s[n] == ':') + break; + } + if (n == line->len) + return -1; + if (!headers.len) + { + if (!stralloc_copys(&headers, "")) + die(51); + if (dkexcludeheaders) + { + if (!stralloc_cats(&headers, dkexcludeheaders)) + die(51); + if (!stralloc_append(&headers, ":")) + die(51); + } + } + if (!headers.len) + return 0; + for (i = j = 0; i < headers.len; ++i) + { + if (headers.s[i] != ':') + continue; + if (i - j == n && !case_diffb(headers.s + j, n, line->s)) + return 1; + j = i + 1; + } + return 0; +} + +int +dk_setoptions(char **selector, int *advicelen, int *opth, int *optr, int *optc, + char **canon, char *signOptions) +{ + char **argv; + int ch, argc; + + *opth = 0; + *optr = 0; + *optc = DK_CANON_NOFWS; + *canon = "nofws"; + *selector = 0; + if (!signOptions) + return (0); + if (!stralloc_copys(&dkopts, "qmail-dk ")) + die(51); + if (!stralloc_cats(&dkopts, signOptions)) + die(51); + if (!stralloc_0(&dkopts)) + die(51); + if (!(argv = MakeArgs(dkopts.s))) + die(51); + for (argc = 0;argv[argc];argc++); + while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone) + { + switch (ch) + { + case 'h': + *opth = 1; + break; + case 'r': + *optr = 1; + *opth = 1; + break; + case 'b': + *advicelen = atoi(optarg); + if (*advicelen > ADVICE_BUF); + *advicelen = ADVICE_BUF; + break; + case 'c': + if (!str_diffn("simple\0", optarg, 7)) + { + *optc = DK_CANON_SIMPLE; + *canon = "simple"; + } + break; + case 's': + *selector = optarg; + break; + default: + FreeMakeArgs(argv); + return (1); + } + } /*- while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone) */ + FreeMakeArgs(argv); + return (0); +} + +static char *binqqargs[2] = { "bin/qmail-queue", 0 }; + +int +main(int argc, char *argv[]) +{ + int errfd, pim[2]; + int wstat, match, opth = 0, optr = 0, optc = DK_CANON_NOFWS, + advicelen = ADVICE_BUF; + char *x, *relayclient, *canon = "nofws", *selector = 0; + stralloc line = {0}, dkfn = {0}; + unsigned long pid; + + sig_blocknone(); + umask(033); + if (!(x = env_get("ERROR_FD"))) + errfd = CUSTOM_ERR_FD; + else + scan_int(x, &errfd); + substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf)); + if (chdir(auto_qmail) == -1) + die(61); + dkqueue = env_get("DKQUEUE"); + if (dkqueue && *dkqueue) + binqqargs[0] = dkqueue; + dksign = env_get("DKSIGN"); + dkverify = env_get("DKVERIFY"); + relayclient = env_get("RELAYCLIENT"); + if (dkverify && relayclient && env_get("RELAYCLIENT_NODKVERIFY")) { + execv(*binqqargs, binqqargs); + die(120, 0); + } + if (!dksign && !dkverify && relayclient) + { + if (!(dksign = env_get("DKKEY"))) + { + if (!stralloc_copys(&dkfn, "domainkeys/%/default")) + die(51); + if (!stralloc_0(&dkfn)) + die(51); + dksign = dkfn.s; + } + } + if (dksign || dkverify) + { + if (!(dklib = dk_init(&st))) + { + maybe_die_dk(st); + custom_error("Z", "dk initialization failed (#4.3.0)", 0); + _exit(88); + } + } + /*- Initialization */ + if (dksign) + { + if (dk_setoptions(&selector, &advicelen, &opth, &optr, &optc, &canon, env_get("DKSIGNOPTIONS"))) + { + custom_error("Z", "Invalid DKSIGNOPTIONS (#4.3.0)", 0); + _exit(88); + } + if (!(dk = dk_sign(dklib, &st, optc))) + { + maybe_die_dk(st); + custom_error("Z", "dk_sign failed (#4.3.0)", 0); + _exit(88); + } + if (optr && dk_setopts(dk, DKOPT_RDUPE) != DK_STAT_OK) + { + custom_error("Z", "DKOPT_RDUPE failed (#4.3.0)", 0); + _exit(88); + } + } else + if (dkverify) + { + if (!(dk = dk_verify(dklib, &st))) + { + maybe_die_dk(st); + custom_error("Z", "dk_verify failed (#4.3.0)", 0); + _exit(88); + } + } + mypid = getpid(); + uid = getuid(); + starttime = now(); + datetime_tai(&dt, starttime); + sig_pipeignore(); + sig_miscignore(); + sig_alarmcatch(sigalrm); + sig_bugcatch(sigbug); + alarm(DEATH); + pidopen(); + if ((readfd = open_read(pidfn)) == -1) + die(63); + if (unlink(pidfn) == -1) + die(63); + substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf)); + substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf)); + dkexcludeheaders = env_get("DKEXCLUDEHEADERS"); + if (dkexcludeheaders) + { + int hdr_continue, in_header = 1; + + hdr_continue = 0; + for (;;) + { + + if (getln(&ssin, &line, &match, '\n') == -1) + die_read(); + if (!match && line.len == 0) + break; + if (substdio_put(&ssout, line.s, line.len) == -1) + die_write(); + if (!dksign && !dkverify) + continue; + if (in_header && !mess822_ok(&line)) + in_header = 0; + if (in_header) + { + if (line.s[0] == ' ' || line.s[0] == '\t') + { + if (hdr_continue) + continue; + } else + if (find_header(&line) == 1) { + hdr_continue = 1; + continue; + } else + hdr_continue = 0; + } + if (match) + { + st = dk_message(dk, (unsigned char *) line.s, line.len - 1); + maybe_die_dk(st); + st = dk_message(dk, (unsigned char *) "\r\n", 2); + } else + st = dk_message(dk, (unsigned char *) line.s, line.len); + maybe_die_dk(st); + } + } else + for (;;) + { + register int n; + register char *x; + int i; + + if ((n = substdio_feed(&ssin)) < 0) + die_read(); + if (!n) + break; + x = substdio_PEEK(&ssin); + if (dksign || dkverify) + { + for (i = 0; i < n; i++) + { + if (x[i] == '\n') + st = dk_message(dk, (unsigned char *) "\r\n", 2); + else + st = dk_message(dk, (unsigned char *) x + i, 1); + maybe_die_dk(st); + } + } + if (substdio_put(&ssout, x, n) == -1) + die_write(); + substdio_SEEK(&ssin, n); + } + if (substdio_flush(&ssout) == -1) + die_write(); + if (dksign || dkverify) + { + st = dk_eom(dk, (void *) 0); + maybe_die_dk(st); + if (dksign) + write_signature(dk, selector, dksign, advicelen, opth, canon); + else + if (dkverify) + { + char *status = 0, *code = 0; + + if (!stralloc_copys(&dkoutput, "DomainKey-Status: ")) + die(51); + switch (st) + { + case DK_STAT_OK: + status = "good "; + break; + case DK_STAT_BADSIG: + status = "bad "; + code = "X.7.5"; + break; + case DK_STAT_NOSIG: + status = "no signature"; + code = "X.7.5"; + break; + case DK_STAT_NOKEY: + case DK_STAT_CANTVRFY: + status = "no key "; + code = "X.7.0"; + break; + case DK_STAT_BADKEY: + status = "bad key "; + code = "X.7.5"; + break; + case DK_STAT_INTERNAL: + status = "bad format "; + code = "X.3.0"; + break; + case DK_STAT_ARGS: + status = "bad format "; + code = "X.3.5"; + break; + case DK_STAT_SYNTAX: + status = "bad format "; + code = "X.6.0"; + break; + case DK_STAT_NORESOURCE: + status = "no resources"; + code = "X.3.0"; + break; + case DK_STAT_REVOKED: + status = "revoked "; + code = "X.7.5"; + break; + case DK_STAT_GRANULARITY: + status = "bad sender "; + code = "X.7.5"; + break; + } + if (!stralloc_cats(&dkoutput, status)) + die(51); + if (!stralloc_cats(&dkoutput, "\n")) + die(51); + if (dkverify[str_chr(dkverify, 'A' + st)]) + { + custom_error("D", status, code); /*- return permanent error */ + die(88); + } + if (dkverify[str_chr(dkverify, 'a' + st)]) + { + custom_error("Z", status, code); /*- return temporary error */ + die(88); + } + } + } + if (pipe(pim) == -1) + die(59); + switch (pid = vfork()) + { + case -1: + close(pim[0]); + close(pim[1]); + die(58); + case 0: + close(pim[1]); + if (fd_move(0, pim[0]) == -1) + die(120); + execv(*binqqargs, binqqargs); + die(120); + } + close(pim[0]); + substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf)); + substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf)); + if (substdio_bput(&ssout, dkoutput.s, dkoutput.len) == -1) /*- write DK signature */ + die_write(); + switch (substdio_copy(&ssout, &ssin)) + { + case -2: + die_read(); + case -3: + die_write(); + } + if (substdio_flush(&ssout) == -1) + die_write(); + close(pim[1]); + if (wait_pid(&wstat, pid) != pid) + die(57); + if (wait_crashed(wstat)) + die(57); + die(wait_exitcode(wstat)); + /*- Not Reached */ + exit(0); +} + +void +getversion_qmail_dk_c() +{ + static char *x = "$Id: qmail-dk.c,v 1.37 2013-01-24 22:42:07+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/qmail-dkim.9 netqmail-1.06/qmail-dkim.9 --- ../netqmail-1.06-original/qmail-dkim.9 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qmail-dkim.9 2020-04-09 19:45:28.421566373 +0200 @@ -0,0 +1,322 @@ +.TH qmail-dkim 8 +.SH NAME +qmail-dkim \- sign/verify using DKIM (SSP/ADSP optionally) and queue a mail message for delivery +.SH SYNOPSIS +.B qmail-dkim +.SH DESCRIPTION +.B qmail-dkim +has the same interface as +.B qmail-queue +except that it inserts an appropriate DKIM header (rfc4871) before it +queues the message. To invoke +.BR qmail-dkim , +set QMAILQUEUE to point to qmail-dkim in the environment when +you send or receive email. qmail-dkim will call +.BR qmail-queue . +To invoke an executable other than +.B qmail-queue +set DKIMQUEUE=bin/qmail-dk for example. + +.B qmail-dkim +supports DKIM signing and verification and can optionally use +.B Sender Signing Practice (SSP) +or +.B Author Domain Signing Practice. +It uses the libdkim and OpenSSL libraries. To sign a message, set the +.B DKIMSIGN +environment variable to the pathname of the private key that will be +used to sign the message. If there is a % character in the environment +variable, it is removed and replaced by the domain name in the From: header. +If, after substituting the %, that file does not exist, the % character will be +removed. If a private key file does not exist and does not have a % character, +the message will be rejected with error 35. The selector (s=) will be taken from +the basename of the file. The private key should be created by +.BR dknewkey(8) . + +In the absense of DKSIGN and DKVERIFY environment variable, qmail-dkim will sign the +message if RELAYCLIENT or AUTHINFO environment variable is set. It will verify the message +if RELAYCLIENT or AUTHINFO environment variable is not set. Even if DKVERIFY is set, you can disable dkim +verification, if RELAYCLIENT or AUTHINFO is set, by setting RELAYCLIENT_NODKVERIFY environment variable. + +You can set various DKIM options in getopt style, by setting the environment variable +.B DKIMSIGNOPTIONS + + b 1 - allman, 2 - ietf or 3 - both + c r for relaxed [DEFAULT], s - simple, + t relaxed/simple, u - simple/relaxed + l include body length tag + q include query method tag; + t include a timestamp tag + h include copied headers + i the identity, if not provided it will not be included + x the expire time in seconds since epoch + ( DEFAULT = current time + 604800) + if set to - then it will not be included + z 1 for sha1, 2 for sha256, 3 for both + +.EX + DKIMSIGNOPTIONS="-b 1 -c r -q" + sets allman standard, with relaxed canonicalization and include query method tag +.EE + +Apart from setting +.BR DKIMSIGNOPTIONS , +you can set the identity and the expire time by setting +.B DKIMIDENTITY +and +.B DKIMEXPIRE +respectively. +.B DKIMIDENTITY +takes precedence over -i option specified in +.BR DKIMSIGNOPTIONS. +Similarly, +.B DKIMEXPIRE +takes precedence over -x option specifed in +.BR DKIMSIGNOPTIONS. +.B qmail-dkim +uses the domain found in the Return-Path, Sender, From headers to set the domain tag. +If not it uses the \fBDKIMDOMAIN\fR environment variable. \fBDKIMDOMAIN\fR +can be set to an email address or a domain (without the at sign). + +To verify a message, set the +.B DKIMVERIFY +environment variable to a desired set of letters. Precisely, if you +want a libdkim return status to generate an error, include that +letter, where A is the first return status (DKIM_SUCCESS), B is the +second (DKIM_FINISHED_BODY), etc. The letter should be uppercase if you +want a permanent error to be returned, and lowercase if you want a temporary +error to be returned (exit code 88). If you omit the letter, qmail-dkim will +not issue any error inspite of DKIM verification failure. It will return +success and the email will get delivered. + +The complete set of letters with the corresponding return status is given below + + A - DKIM_SUCCESS - Function executed successfully + B - DKIM_FINISHED_BODY - process result: no more message + body is needed + C - DKIM_PARTIAL_SUCCESS - verify result: at least one + but not all signatures verified + D - DKIM_NEUTRAL - verify result: no signatures + verified but message is + not suspicious + E - DKIM_SUCCESS_BUT_EXTRA - signature result: signature + verified but it did not + include all of the body + F - DKIM_3PS_SIGNATURE - 3rd-party signature + G - DKIM_FAIL - Function failed to execute + H - DKIM_BAD_SYNTAX - signature error: DKIM-Signature + could not parse or has bad + tags/values + I - DKIM_SIGNATURE_BAD - signature error: RSA verify + failed + J - DKIM_SIGNATURE_BAD_BUT_TESTING - signature error: RSA verify + failed but testing + K - DKIM_SIGNATURE_EXPIRED - signature error: x= is old + L - DKIM_SELECTOR_INVALID - signature error: selector doesn't + parse or contains invalid values + M - DKIM_SELECTOR_GRANULARITY_MISMATCH - signature error: selector + g= doesn't match i= + N - DKIM_SELECTOR_KEY_REVOKED - signature error: selector + p= empty + O - DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG - signature error: selector domain + name too long to request + P - DKIM_SELECTOR_DNS_TEMP_FAILURE - signature error: temporary dns + failure requesting selector + Q - DKIM_SELECTOR_DNS_PERM_FAILURE - signature error: permanent dns + failure requesting selector + R - DKIM_SELECTOR_PUBLIC_KEY_INVALID - signature error: selector + p= value invalid or wrong format + S - DKIM_NO_SIGNATURES - no signatures + T - DKIM_NO_VALID_SIGNATURES - no valid signatures + U - DKIM_BODY_HASH_MISMATCH - sigature verify error: message + body does not hash to bh value + V - DKIM_SELECTOR_ALGORITHM_MISMATCH - signature error: selector + h= doesn't match signature a= + W - DKIM_STAT_INCOMPAT - signature error: incompatible v= + X - DKIM_UNSIGNED_FROM - signature error: not all message's + From headers in signature + +For example, if you want to permanently reject messages that have a +signature that is expired, include the letter 'K' in the +.B DKIMVERIFY +environment variable. A conservative set of letters is +.BR FGHIKLMNOQRTUVWjp . +Reject permanently 3PS, FAILURE, SYNTAX, SIGNATURE_BAD, SIGNATURE_EXPIRED, SELECTOR_INVALID, +GRANULARITY_MISMATCH, SELECTOR_KEY_REVOKED, DOMAIN_NAME_TOO_LONG, SELECTOR_PUBLIC_KEY_INVALID, +NO_VALID_SIGNATURES and BODY_HASH_MISMATCH errors, and temporarily SIGNATURE_BAD_BUT_TESTING and DNS_TEMP_FAILURE . +Add in +.B S +if you want to reject messages that do not have a DKIM signature. You can use the control files +signaturedomains and nosignature domains (See Below) to further fine tune the action to be +taken when a mail arrives with no DKIM signature. Note that +.B qmail-dkim +always inserts the +.B DKIM-Status +header, so that messages can be +rejected later at delivery time, or in the mail reader. In that case you may set +.B DKIMVERIFY +to an empty string. If you want to check all message's From header in signature set the +\fBUNSIGNED_FROM\fR environment variable to an empty string. If you want to check messages +without signed subject header, set \fBUNSIGNED_SUBJECT\fR environment variable. If you want +to honor body lengh tag (l=), set \fBHONOR_BODYLENGTHTAG\fR environment variable. + +qmail-dkim supports signing practice which can be additonall checked when a signature +verifcation fails - + +.BR "SSP - Sender Signing Practice" + +and + +.BR "ADSP - Author Domain Signing Practice" . + +When a signature fails to verify for a message, you can use SSP/ADSP to determine if the message is suspicious or not. +To verify a message against SSP/ADSP, set the +.B DKIMPRACTICE +environment variable to the desired set of letters allowed for DKIMVERIFY environment variable. +SSP/ADSP should be used only when signature verification fails. SSP/ADSP should be invoked only when libdkim returns the +error codes (F,G,H,I,J,K,L,M,N,P,Q,R,S,T,U,V,W,X) for signature verification. In case you want to test +against SSP/ADSP only for DKIM_NO_SIGNATURE and DKIM_NO_VALID_SIGNATURE +set the environment variable +.BR DKIMPRACTICE="ST" . +If you want automatic behaviour, set DKIMPRACTICE to an empty string. In this case ADSP/SSP will be used when return code +matches "FGHIJKLMNPQRSTUVWX". +.B qmail-dkim +uses ADSP as the default signing practice. You can override this by setting the SIGN_PRACTICE to ssp, adsp, local (lowercase). +if you set SIGN_PRACTICE to \fIlocal\fB, +.B qmail-dkim +will check the domain against the control file +.I signaturedomains +(See Below). If the domain is found listed in +.I signaturedomains +.B qmail-dkim +will bypass ADSP/SSP and return DKIM_FAIL if signature fails to verify. Setting SIGN_PRACTICE +to anything else will cause +.B qmail-dkim +to disable Signing Practice. + +If ADSP or SSP is checked, +.B qmail-dkim +will insert the +.B X-DKIM-ADSP +or +.B X-DKIM-SSP +header as given below + + A - DKIM_SUCCESS - Message passes ADSP test + B - DKIM_ADSP_UNKNOWN - some messages may be signed + C - DKIM_ADSP_ALL - All message are signed with author signature + D - DKIM_ADSP_DISCARDABLE - messages which fail verification are Discardable + E - DKIM_ADSP_SCOPE - domain is out of scope + F - DKIM_ADSP_TEMPFAIL - Temporary Error + + or + + A - DKIM_SUCCESS - Message passes ADSP test + B - DKIM_SSP_UNKNOWN - some messages may be signed + C - DKIM_SSP_ALL - All message are signed with author signature + D - DKIM_SSP_STRICT - messages which fail verification are Discardable + E - DKIM_SSP_SCOPE - domain is out of scope + F - DKIM_SSP_TEMPFAIL - Temporary Error + +You can have a control file +.I signaturedomains +containing a list of domains which you know are sure to sign messages using DKIM. If a message comes +from a domain listed in +.IR signaturedomains , +and the signature fails verification (any of DKIM failure status), +.B qmail-dkim +will bypass ADSP/SSP checks and return DKIM_FAIL. The name of this control file can be +overriden by the environment variable +.BR SIGNATUREDOMAINS . + +You can have a control file +.I nosignaturedomains +containing a list of domains which you know are sure not to sign messages using DKIM. +If a message comes from a domain listed in +.IR nosignaturedomains , +and does not have a DKIM-Signature header, +.B qmail-dkim +will bypass ADSP/SSP checks and return DKIM_NEUTRAL. The wildcard entry '*' in this file, will +result in all mails which do not have a signature to pass DKIM test (unless the domain is listed +in the control file +.BR signaturedomains ). +The name of this control file can be overriden by the environment variable +.BR NOSIGNATUREDOMAINS . + +Typically, you would sign messages generated on-host by setting +.B DKIMSIGN +in the environment before running an \fBqmail-smtpd\fR(8) or \fBsendmail(1)\fR / \fBqmail-inject\fR(8). DKIMSIGN will be carried +through qmail-smtpd or through qmail's sendmail emulation through +.B qmail-inject +to +.BR qmail-dkim . +You would also set it for +.B qmail-smtpd +at the same time +.B RELAYCLIENT +is set, most often in the tcpserver cdb file. If a host is authorized +to relay, you probably want to sign messages sent by that host. +.B DKIMVERIFY +should be set for all other hosts. + +If neither +.B DKIMSIGN +nor +.B DKIMVERIFY +are set, then +.B DKIMSIGN +will be set to QMAILHOME/control/domainkeys/%/default. The % will be replaced by the domain in the +From: header. If such a file does not exist, then it will be set to +QMAILHOME/control/domainkeys/default. If such a private key exists, it will be used to sign the +domain. You can also set \fBDKIMKEY\fR to chose a key different from +QMAILHOME/control/domainkeys/%/default. \fBDKIMKEY\fR can also have % character that will be +replaced by the domain in the From: header. If the private key does not exist, qmail-dkim +will exit with return code 32. + +By default +.B qmail-dkim +will use all of the headers when signing a message. + +.B qmail-dkim +will ordinarily spawn +.BR qmail-queue , +but if DKIMQUEUE is set in the environment, +the program that it points to will be executed instead. + +.SH "EXIT CODES" +.B qmail-dkim +returns the same exit codes as qmail-queue with these additions: +.TP 5 +.B 32 +The private key file does not exist. +.TP 5 +.B 57 +Trouble waiting for qmail-queue to exit. +.TP 5 +.B 58 +Unable to vfork. +.TP 5 +.B 59 +Unable to create a pipe to qmail-queue. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-header(5), +dknewkey(8), +dkim(8), +dktest(8), +qmail-inject(8), +qmail-qmqpc(8), +qmail-queue(8), +qmail-send(8), +qmail-smtpd(8), +qmail-dk(8), +domain-keys(5) + +.SH "AUTHORS" + +Manvendra Bhangui. +.SH PROBLEMS +Problems with +.B qmail-dkim +should be forwarded to "Manvendra Bhangui" diff -ruN ../netqmail-1.06-original/qmail-dkim.c netqmail-1.06/qmail-dkim.c --- ../netqmail-1.06-original/qmail-dkim.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qmail-dkim.c 2019-06-19 09:48:44.632641078 +0200 @@ -0,0 +1,1417 @@ +/* + * $Log: qmail-dkim.c,v $ + * Revision 1.53 2019-06-14 21:26:37+05:30 Cprogrammer + * added env variable HONOR_BODYLENGTHTAG to honor body length tag during verification + * + * Revision 1.52 2019-02-18 22:18:12+05:30 Cprogrammer + * allow DKIMVERIFY env variable in place of DKIMPRACTICE when SIGN_PRACTICE="local" + * + * Revision 1.51 2019-02-17 11:38:51+05:30 Cprogrammer + * set original DKIM error for SIGN_PRACTICE=local + * + * Revision 1.50 2019-02-15 21:25:04+05:30 Cprogrammer + * skip nosignaturedomains if domain is present in signaturedomains + * + * Revision 1.49 2018-08-08 23:58:01+05:30 Cprogrammer + * issue success if at lease one one good signature is found + * + * Revision 1.48 2017-09-05 12:37:16+05:30 Cprogrammer + * added missing DKIM_MFREE() + * + * Revision 1.47 2016-06-03 09:57:59+05:30 Cprogrammer + * moved qmail-multi to sbin + * + * Revision 1.46 2016-05-17 19:44:58+05:30 Cprogrammer + * use auto_control, set by conf-control to set control directory + * + * Revision 1.45 2016-03-01 18:48:02+05:30 Cprogrammer + * added env variable UNSIGNED_SUBJECT to verify dkim without subject field + * + * Revision 1.44 2015-12-15 16:05:58+05:30 Cprogrammer + * increased buffer size for long header issue + * + * Revision 1.43 2014-01-22 22:45:01+05:30 Cprogrammer + * treat AUTHINFO environment like RELAYCLIENT environment variable + * + * Revision 1.42 2013-10-01 17:11:24+05:30 Cprogrammer + * fixed QMAILQUEUE recursion + * + * Revision 1.41 2013-09-16 22:16:35+05:30 Cprogrammer + * corrected logic for RELAYCLIENT_NODKIMVERIFY + * + * Revision 1.40 2013-09-13 16:34:35+05:30 Cprogrammer + * turn off verification if RELAYCLIENT, DKIMVERIFY and RELAYCLIENT_NODKIMVERIFY is set + * + * Revision 1.39 2013-08-18 15:53:30+05:30 Cprogrammer + * revert back to default verification mode if both dksign, dkverify are not set + * + * Revision 1.38 2013-08-17 15:00:33+05:30 Cprogrammer + * BUG - corrected location of private key when % sign is removed + * + * Revision 1.37 2013-01-24 22:37:22+05:30 Cprogrammer + * BUG (fix by Piotr Gronek) - DKIM_FREE(results) called before call to ParseTagValues() + * alternate code for DKIMSIGN selector file name + * + * Revision 1.36 2012-08-16 08:01:46+05:30 Cprogrammer + * do not skip X-Mailer headers + * + * Revision 1.35 2011-11-10 14:32:08+05:30 Cprogrammer + * BUG ssout to be assigned only after pidopen() + * + * Revision 1.34 2011-11-07 09:35:59+05:30 Cprogrammer + * set ssout, sserr, ssin before executing other functions + * + * Revision 1.33 2011-07-29 09:29:17+05:30 Cprogrammer + * fixed key file name + * + * Revision 1.32 2011-07-28 19:36:36+05:30 Cprogrammer + * BUG - fixed opening of private key with absolute path + * + * Revision 1.31 2011-07-22 14:40:05+05:30 Cprogrammer + * fixed checking of private key file + * + * Revision 1.30 2011-06-04 14:49:48+05:30 Cprogrammer + * remove '%' sign from private key if key not found + * + * Revision 1.29 2011-06-04 14:22:29+05:30 Cprogrammer + * added DKIM_UNSIGNED_FROM error code for dkimadspverify + * + * Revision 1.28 2011-06-04 14:07:41+05:30 Cprogrammer + * added DKIM_UNSIGNED_FROM + * + * Revision 1.27 2011-02-10 23:39:59+05:30 Cprogrammer + * use DKIMKEY to override defult control/domainkeys/%/default + * + * Revision 1.26 2011-02-06 10:13:50+05:30 Cprogrammer + * BUG - signature was wrongly freed before being accessed. + * + * Revision 1.25 2011-02-05 09:47:47+05:30 Cprogrammer + * fixed SIGSEGV occuring for messages without body + * + * Revision 1.24 2010-11-02 18:45:14+05:30 Cprogrammer + * Improve DKIM signing/verification speed + * + * Revision 1.23 2010-07-21 08:59:57+05:30 Cprogrammer + * use CONTROLDIR environment variable instead of a hardcoded control directory + * + * Revision 1.22 2009-04-22 13:42:51+05:30 Cprogrammer + * made fd for custom error configurable through env variable ERROR_FD + * + * Revision 1.21 2009-04-21 09:05:48+05:30 Cprogrammer + * return relevant error message for reading private key + * + * Revision 1.20 2009-04-21 08:55:41+05:30 Cprogrammer + * return temporary error for temp failures + * + * Revision 1.19 2009-04-20 22:19:01+05:30 Cprogrammer + * made dkimopts global + * + * Revision 1.18 2009-04-16 13:48:32+05:30 Cprogrammer + * added dkim_setoptions() to set all DKIM options + * + * Revision 1.17 2009-04-07 11:36:56+05:30 Cprogrammer + * use TMPDIR env variable for tmp directory + * + * Revision 1.16 2009-04-05 12:52:17+05:30 Cprogrammer + * added preprocessor warning + * + * Revision 1.15 2009-04-04 00:33:44+05:30 Cprogrammer + * removed dk_strdup() + * + * Revision 1.14 2009-03-31 08:21:58+05:30 Cprogrammer + * set dkimsign when RELAYCLIENT is defined when both dkimsign and dkimverify are undefined + * + * Revision 1.13 2009-03-30 22:25:54+05:30 Cprogrammer + * made DKIM messages friendlier + * + * Revision 1.12 2009-03-30 14:47:59+05:30 Cprogrammer + * added descriptive text for original dkim error + * + * Revision 1.11 2009-03-29 19:20:43+05:30 Cprogrammer + * added nosignaturedomains + * + * Revision 1.10 2009-03-28 22:27:02+05:30 Cprogrammer + * use DKIMSIGN, DKIMVERIFY if RELAYCLIENT is not set + * + * Revision 1.9 2009-03-28 22:03:05+05:30 Cprogrammer + * fixed DKIM return codes + * + * Revision 1.8 2009-03-28 13:37:37+05:30 Cprogrammer + * call DKIMVerifyGetDetails() always + * + * Revision 1.7 2009-03-28 11:39:23+05:30 Cprogrammer + * set automatic setting of dkimsign, dkimverify variables based on RELAYCLIENT + * + * Revision 1.6 2009-03-28 11:35:58+05:30 Cprogrammer + * added ADSP/SSP + * + * Revision 1.5 2009-03-22 17:39:38+05:30 Cprogrammer + * set identity using basename of signature or environment variable DKIMIDENTITY + * + * Revision 1.4 2009-03-22 16:58:38+05:30 Cprogrammer + * fixed bug with verification + * report custom errors to qmail-queue through custom error interface + * + * Revision 1.3 2009-03-21 12:34:38+05:30 Cprogrammer + * use hasdkim.h for conditional compilation of dkim + * + * Revision 1.2 2009-03-20 22:35:57+05:30 Cprogrammer + * set error to DKIM_NO_SIGNATURE when DKIM-Signature is not present + * + * Revision 1.1 2009-03-18 13:54:49+05:30 Cprogrammer + * Initial revision + * + */ +#include +#include +#include +#include +#include +#include "sgetopt.h" +#include "substdio.h" +#include "open.h" +#include "qmail.h" +#include "sig.h" +#include "scan.h" +#include "case.h" +#include "fmt.h" +#include "fd.h" +#include "alloc.h" +#include "str.h" +#include "stralloc.h" +#include "datetime.h" +#include "now.h" +#include "wait.h" +#include "auto_qmail.h" +#include "env.h" +#include "control.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "dkim.h" + +#define DEATH 86400 /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ +#define ADDR 1003 +#define HAVE_EVP_SHA256 +#define strncasecmp(x,y,z) case_diffb((x), (z), (y)) +#define strcasecmp(x,y) case_diffs((x), (y)) + +char inbuf[4096]; +char outbuf[256]; +char errbuf[256]; +struct substdio ssin; +struct substdio ssout; +struct substdio sserr; + +datetime_sec starttime; +struct datetime dt; +unsigned long mypid; +unsigned long uid; +char *pidfn; +int messfd; +int readfd; +DKIMContext ctxt; + +char **MakeArgs(char *); +void FreeMakeArgs(char **); + + +void +die(int e, int what) +{ + if (!what) + _exit(e); + (what == 1 ? DKIMSignFree : DKIMVerifyFree) (&ctxt); + _exit(e); +} + +void +die_write() +{ + die(53, 0); +} + +void +die_read() +{ + die(54, 0); +} + +void +sigalrm() +{ + /*- thou shalt not clean up here */ + die(52, 0); +} + +void +sigbug() +{ + die(81, 0); +} + +void +custom_error(char *flag, char *status, char *code) +{ + char *c; + + if (substdio_put(&sserr, flag, 1) == -1) + die_write(); + if (substdio_put(&sserr, "qmail-dkim: ", 12) == -1) + die_write(); + if (substdio_puts(&sserr, status) == -1) + die_write(); + if (code) { + if (substdio_put(&sserr, " (#", 3) == -1) + die_write(); + c = (*flag == 'Z') ? "4" : "5"; + if (substdio_put(&sserr, c, 1) == -1) + die_write(); + if (substdio_put(&sserr, code + 1, 4) == -1) + die_write(); + if (substdio_put(&sserr, ")", 1) == -1) + die_write(); + } + if (substdio_flush(&sserr) == -1) + die_write(); + return; +} + +int DKIM_CALL +SignThisHeader(const char *szHeader) +{ + if ((!strncasecmp((char *) szHeader, "X-", 2) + && strncasecmp((char *) szHeader, "X-Mailer", 8)) + || strncasecmp((char *) szHeader, "Received:", 9) == 0 + || strncasecmp((char *) szHeader, "Authentication-Results:", 23) == 0 + || strncasecmp((char *) szHeader, "Return-Path:", 12) == 0) { + return 0; + } + return 1; +} + +void +maybe_die_dkim(e) + int e; +{ + switch (e) + { + case DKIM_OUT_OF_MEMORY: + case DKIM_BUFFER_TOO_SMALL: + _exit (51); + case DKIM_INVALID_CONTEXT: + custom_error("Z", "DKIMContext structure invalid for this operation (#4.3.0)", 0); + _exit(88); + case DKIM_NO_SENDER: + custom_error("Z", "Could not find From: or Sender: header in message (#5.1.7)", 0); + _exit(88); + case DKIM_BAD_PRIVATE_KEY: + custom_error("D", "Could not parse private key (#5.7.5)", 0); + _exit(88); + default: + return; + } +} + +unsigned int +pidfmt(s, seq) + char *s; + unsigned long seq; +{ + unsigned int i; + unsigned int len; + char *tmpdir; + + if (!(tmpdir = env_get("TMPDIR"))) + tmpdir = "/tmp"; + len = 0; + i = fmt_str(s, tmpdir); + len += i; + if (s) + s += i; + i = fmt_str(s, "/qmail-dkim."); + len += i; + if (s) + s += i; + i = fmt_ulong(s, mypid); + len += i; + if (s) + s += i; + i = fmt_str(s, "."); + len += i; + if (s) + s += i; + i = fmt_ulong(s, starttime); + len += i; + if (s) + s += i; + i = fmt_str(s, "."); + len += i; + if (s) + s += i; + i = fmt_ulong(s, seq); + len += i; + if (s) + s += i; + ++len; + if (s) + *s++ = 0; + + return len; +} + +void +pidopen() +{ + unsigned int len; + unsigned long seq; + + seq = 1; + len = pidfmt((char *) 0, seq); + if (!(pidfn = alloc(len))) + die(51, 0); + for (seq = 1; seq < 10; ++seq) { + if (pidfmt((char *) 0, seq) > len) + die(81, 0); /*- paranoia */ + pidfmt(pidfn, seq); + if ((messfd = open_excl(pidfn)) != -1) + return; + } + die(63, 0); +} + +char tmp[FMT_ULONG]; +char *dkimsign = 0; +char *dkimverify = 0; +char *dkimadspverify = 0, *dkimpractice = "FGHIJKLMNPQRSTUVWX"; +stralloc dkimoutput = { 0 }; /*- DKIM-Signature */ +stralloc dksignature = { 0 }; /*- content of private signature */ +stralloc sigdomains = { 0 }; /*- domains which must have signatures */ +stralloc nsigdomains = { 0 }; /*- domains which do not have signatures */ +stralloc dkimopts = { 0 }; +char *dkimqueue = 0; + +static void +write_signature(char *domain, char *keyfn) +{ + char *pSig; + int i; + static stralloc keyfnfrom = { 0 }; + + if (keyfn[0] != '/') { + if (!stralloc_copys(&keyfnfrom, "control/")) + die(51, 1); + } + i = str_chr(keyfn, '%'); + if (keyfn[i]) { + if (keyfn[0] == '/') { + if (!stralloc_copyb(&keyfnfrom, keyfn, i)) + die(51, 1); + } else + if (!stralloc_catb(&keyfnfrom, keyfn, i)) + die(51, 1); + if (!stralloc_cats(&keyfnfrom, domain)) + die(51, 1); + if (!stralloc_cats(&keyfnfrom, keyfn + i + 1)) + die(51, 1); + if (!stralloc_0(&keyfnfrom)) + die(51, 1); + if (access(keyfnfrom.s, F_OK)) { + /*- since file does not exists remove '%' sign */ + keyfnfrom.len = 8; + if (keyfn[0] == '/') { + if (!stralloc_copyb(&keyfnfrom, keyfn, i)) + die(51, 1); + } else + if (!stralloc_catb(&keyfnfrom, keyfn, i)) + die(51, 1); + if ((i - 1) > 0 && keyfn[i - 1] == '/' && keyfn[i + 1] == '/') + i++; + if (!stralloc_cats(&keyfnfrom, keyfn + i + 1)) + die(51, 1); + if (!stralloc_0(&keyfnfrom)) + die(51, 1); + } + } else { + if (keyfn[0] == '/') { + if (!stralloc_copys(&keyfnfrom, keyfn)) + die(51, 1); + } else + if (!stralloc_cats(&keyfnfrom, keyfn)) + die(51, 1); + if (!stralloc_0(&keyfnfrom)) + die(51, 1); + } + switch (control_readnativefile(&dksignature, keyfnfrom.s, 1)) + { + case 0: /*- missing signature file */ + DKIMSignFree(&ctxt); + /* + * You may have multiple domains, but may chose to sign + * only for few domains which have the key present. Do not + * treat domains with missing key as an error. + */ + if (keyfn[i]) + return; + die(32, 0); + case 1: + break; + default: + custom_error("Z", "Unable to read private key. (#4.3.0)", 0); + DKIMSignFree(&ctxt); + _exit(88); + } + for (i = 0; i < dksignature.len; i++) { + if (dksignature.s[i] == '\0') + dksignature.s[i] = '\n'; + } + if (!stralloc_0(&dksignature)) + die(51, 1); + i = DKIMSignGetSig2(&ctxt, dksignature.s, &pSig); + maybe_die_dkim(i); + if (pSig) { + if (!stralloc_catb(&dkimoutput, pSig, str_len(pSig))) + die(51, 1); + if (!stralloc_cats(&dkimoutput, "\n")) + die(51, 1); + } + DKIMSignFree(&ctxt); +} + +#include +#define DKIM_MALLOC(n) OPENSSL_malloc(n) +#define DKIM_MFREE(s) OPENSSL_free(s); s = NULL; +char *dns_text(char *); + +int +ParseTagValues(char *list, char *letters[], char *values[]) +{ + char *tmp, *ptr, *key; + int i; + + /*- start with all args unset */ + for (i = 0; letters[i]; i++) + values[i] = 0; + key = 0; + for(ptr = list;*ptr;) { + if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */ + *ptr++ = 0; + if (!key) + key = ptr; + if (*ptr == '=') { + *ptr = 0; + for (i = 0;letters[i];i++) { + if (!str_diff(letters[i], key)) { + ptr++; + for (;*ptr;) { + if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) { + ptr++; + continue; + } + break; + } + values[i] = ptr; + for(;*ptr && *ptr != ';';ptr++); + tmp = ptr; + if (*ptr) + *ptr++ = 0; + for(;tmp != values[i];tmp--) { /*- RFC 4871 3.2 */ + if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n')) { + *tmp = 0; + continue; + } + break; + } + key = 0; + break; + } + } + } else + ptr++; + } + return (0); +} + +int +checkSSP(char *domain, int *bTesting) +{ + char *query, *results; + char *tags[] = { "dkim", "t", 0}; + char *values[2]; + int bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN; + + *bTesting = 0; + if (!(query = DKIM_MALLOC(str_len("_ssp._domainkey.") + str_len(domain) + 1))) + die(51, 0); + sprintf(query, "_ssp._domainkey.%s", domain); + results = dns_text(query); + DKIM_MFREE(query); + if (!str_diff(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_SSP_TEMPFAIL; + } else + if (!str_diff(results, "e=perm;")) { + results = dns_text(domain); + if (!str_diff(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_SSP_TEMPFAIL; + } else + if (!str_diff(results, "e=perm;")) { + DKIM_MFREE(results); + return DKIM_SSP_SCOPE; + } + bIsParentSSP = 1; + } + /* + * PG.1 2013-01-03 + * Bug fix by Piotr Gronek, Faculy of Physics & Applied Computer Science, Poland 2013-01-03 + * Deallocating storage for 'results' here is premature - moved beyond last reference to it. + * + */ + if (!ParseTagValues(results, tags, values)) { + DKIM_MFREE(results); + return DKIM_SSP_UNKNOWN; + } + DKIM_MFREE(results); + if (values[0] != NULL) { + if (strcasecmp(values[0], "all") == 0) + iSSP = DKIM_SSP_ALL; + else + if (strcasecmp(values[0], "strict") == 0) + iSSP = DKIM_SSP_STRICT; + } + // flags + if (values[1] != NULL) { + char *s, *p; + for (p = values[1], s = values[1]; *p; p++) { + if (*p == '|') + *p = 0; + else + continue; + if (!str_diff(s, "y")) + *bTesting = 1; + else + if (!str_diff(s, "s")) { + if (bIsParentSSP) { + /* + * this is a parent's SSP record that should not apply to subdomains + * the message is non-suspicious + */ + *bTesting = 0; + return (DKIM_SSP_UNKNOWN); + } + } + s = p + 1; + } + } + return iSSP; +} + +int +checkADSP(char *domain) +{ + char *query, *results; + char *tags[] = { "dkim", 0}; + char *values[1]; + + results = dns_text(domain); + if (!str_diff(results, "e=perm;")) { + DKIM_MFREE(results); + return DKIM_ADSP_SCOPE; + } else + if (!str_diff(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_ADSP_TEMPFAIL; + } + if (!(query = DKIM_MALLOC(str_len("_adsp._domainkey.") + str_len(domain) + 1))) { + DKIM_MFREE(results); + die(51, 0); + } + sprintf(query, "_adsp._domainkey.%s", domain); + results = dns_text(query); + DKIM_MFREE(query); + if (!str_diff(results, "e=perm;")) { + DKIM_MFREE(results); + return DKIM_ADSP_SCOPE; + } else + if (!str_diff(results, "e=temp;")) { + DKIM_MFREE(results); + return DKIM_ADSP_TEMPFAIL; + } + /* + * PG.1 2013-01-03 + * Bug fix by Piotr Gronek, Faculy of Physics & Applied Computer Science, Poland 2013-01-03 + * + * Deallocating storage for 'results' here is premature - moved beyond last reference to it. + * + */ + if (!ParseTagValues(results, tags, values)) { + DKIM_MFREE(results); + return DKIM_SSP_UNKNOWN; + } + DKIM_MFREE(results); + if (values[0] != NULL) { + if (strcasecmp(values[0], "all") == 0) + return (DKIM_ADSP_ALL); + else + if (strcasecmp(values[0], "discardable") == 0) + return (DKIM_ADSP_DISCARDABLE); + } + return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */ +} + +void +dkimverify_exit(int dkimRet, char *status, char *code) +{ + if (dkimRet < 0) { + if (dkimverify[str_chr(dkimverify, 'F' - dkimRet)]) { + custom_error("D", status, code); + die(88, 0); + } + if (dkimverify[str_chr(dkimverify, 'f' - dkimRet)]) { + custom_error("Z", status, code); + die(88, 0); + } + } else { + if (dkimverify[str_chr(dkimverify, 'A' + dkimRet)]) { + custom_error("D", status, code); + die(88, 0); + } + if (dkimverify[str_chr(dkimverify, 'a' + dkimRet)]) { + custom_error("Z", status, code); + die(88, 0); + } + } +} + +void +writeHeaderNexit(int ret, int origRet, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP) +{ + char *dkimStatus = 0, *sspStatus = 0, *adspStatus = 0, *code = 0, *orig = 0; + char strnum[FMT_ULONG]; + + switch (ret) + { + case DKIM_SUCCESS: /*- 0 */ /*- A */ + dkimStatus = "good "; + code = "X.7.0"; + break; + case DKIM_FINISHED_BODY: /*- 1 process result: no more message body is needed */ + dkimStatus = "process result: no more message body is needed"; + code = "X.7.0"; + break; + case DKIM_PARTIAL_SUCCESS: /*- 2 verify result: at least one but not all signatures verified */ + dkimStatus = "verify result: at least none but not all signatures verified"; + code = "X.7.0"; + break; + case DKIM_NEUTRAL: /*- 3 verify result: no signatures verified but message is not suspicious */ + dkimStatus = "verify result: no signatures verified but message is not suspicious"; + code = "X.7.0"; + break; + case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */ + dkimStatus = "signature result: signature verified but it did not include all of the body"; + code = "X.7.0"; + break; + case DKIM_FAIL: /*- -1 */ /*- F */ + dkimStatus = "DKIM Signature verification failed"; + code = "X.7.0"; + break; + case DKIM_BAD_SYNTAX: /*- -2 */ /*- G */ + dkimStatus = "signature error: DKIM-Signature could not parse or has bad tags/values"; + code = "X.7.5"; + break; + case DKIM_SIGNATURE_BAD: /*- -3 */ + dkimStatus = "signature error: RSA verify failed"; + code = "X.7.5"; + break; + case DKIM_SIGNATURE_BAD_BUT_TESTING: + dkimStatus = "signature error: RSA verify failed but testing"; + code = "X.7.5"; + break; + case DKIM_SIGNATURE_EXPIRED: + dkimStatus = "signature error: x= is old"; + code = "X.7.5"; + break; + case DKIM_SELECTOR_INVALID: + dkimStatus = "signature error: selector doesn't parse or contains invalid values"; + code = "X.7.5"; + break; + case DKIM_SELECTOR_GRANULARITY_MISMATCH: + dkimStatus = "signature error: selector g= doesn't match i="; + code = "X.7.5"; + break; + case DKIM_SELECTOR_KEY_REVOKED: + dkimStatus = "signature error: selector p= empty"; + code = "X.7.5"; + break; + case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG: + dkimStatus = "signature error: selector domain name too long to request"; + code = "X.7.0"; + break; + case DKIM_SELECTOR_DNS_TEMP_FAILURE: + dkimStatus = "signature error: temporary dns failure requesting selector"; + code = "X.7.0"; + break; + case DKIM_SELECTOR_DNS_PERM_FAILURE: + dkimStatus = "signature error: permanent dns failure requesting selector"; + code = "X.7.0"; + break; + case DKIM_SELECTOR_PUBLIC_KEY_INVALID: + dkimStatus = "signature error: selector p= value invalid or wrong format"; + code = "X.7.5"; + break; + case DKIM_NO_SIGNATURES: + dkimStatus = "no signatures"; + code = "X.7.5"; + break; + case DKIM_NO_VALID_SIGNATURES: + dkimStatus = "no valid signatures"; + code = "X.7.5"; + break; + case DKIM_BODY_HASH_MISMATCH: + dkimStatus = "signature verify error: message body does not hash to bh value"; + code = "X.7.7"; + break; + case DKIM_SELECTOR_ALGORITHM_MISMATCH: + dkimStatus = "signature error: selector h= doesn't match signature a="; + code = "X.7.7"; + break; + case DKIM_STAT_INCOMPAT: + dkimStatus = "signature error: incompatible v="; + code = "X.7.6"; + break; + case DKIM_UNSIGNED_FROM: + dkimStatus = "signature error: not all message's From headers in signature"; + code = "X.7.7"; + break; + default: + dkimStatus = "error"; + code = "X.3.0"; + break; + } + if (useSSP && resDKIMSSP != -1) { + switch(resDKIMSSP) + { + case DKIM_SSP_ALL: + sspStatus = (char *) "all;"; + break; + case DKIM_SSP_STRICT: + sspStatus = (char *) "strict;"; + break; + case DKIM_SSP_SCOPE: + sspStatus = (char *) "out of scope;"; + break; + case DKIM_SSP_TEMPFAIL: + sspStatus = (char *) "temporary failure;"; + break; + case DKIM_SSP_UNKNOWN: + default: + sspStatus = (char *) "unknown;"; + break; + } + } + if (useADSP && resDKIMADSP != -1) { + switch(resDKIMADSP) + { + case DKIM_ADSP_ALL: + adspStatus = (char *) "all;"; + break; + case DKIM_ADSP_DISCARDABLE: + adspStatus = (char *) "discardable;"; + break; + case DKIM_ADSP_SCOPE: + adspStatus = (char *) "out of scope;"; + break; + case DKIM_ADSP_TEMPFAIL: + adspStatus = (char *) "temporary failure;"; + break; + case DKIM_ADSP_UNKNOWN: + default: + adspStatus = (char *) "unknown ;"; + break; + } + } + if (!stralloc_copys(&dkimoutput, "DKIM-Status: ")) + die(51, 0); + if (!stralloc_cats(&dkimoutput, dkimStatus)) + die(51, 0); + if (origRet != DKIM_MAX_ERROR && ret != origRet) { + if (!stralloc_cats(&dkimoutput, "\n\t(old=")) + die(51, 0); + switch (origRet) + { + case DKIM_SUCCESS: /*- 0 */ /*- A */ + orig = "SUCCESS"; + break; + case DKIM_FINISHED_BODY: /*- 1 process result: no more message body is needed */ + orig = "FINISHED BODY"; + break; + case DKIM_PARTIAL_SUCCESS: /*- 2 verify result: at least one but not all signatures verified */ + orig = "PARTIAL SUCCESS"; + break; + case DKIM_NEUTRAL: /*- 3 verify result: no signatures verified but message is not suspicious */ + orig = "NEUTRAL"; + break; + case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */ + orig = "SUCCESS(BUT EXTRA)"; + break; + case DKIM_FAIL: /*- -1 */ /*- F */ + orig = "FAIL"; + break; + case DKIM_BAD_SYNTAX: /*- -2 */ /*- G */ + orig = "BAD SYNTAX"; + break; + case DKIM_SIGNATURE_BAD: /*- -3 */ + orig = "SIGNATURE BAD"; + break; + case DKIM_SIGNATURE_BAD_BUT_TESTING: + orig = "SIGNATURE BAD (TESTING)"; + break; + case DKIM_SIGNATURE_EXPIRED: + orig = "SIGNATURE EXPIRED"; + break; + case DKIM_SELECTOR_INVALID: + orig = "SELECTOR INVALID"; + break; + case DKIM_SELECTOR_GRANULARITY_MISMATCH: + orig = "SELECTOR GRANULARITY MISMATCH"; + break; + case DKIM_SELECTOR_KEY_REVOKED: + orig = "SELECTOR KEY REVOKED"; + break; + case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG: + orig = "DOMAIN NAME TOO LONG"; + break; + case DKIM_SELECTOR_DNS_TEMP_FAILURE: + orig = "DNS TEMP FAILURE"; + break; + case DKIM_SELECTOR_DNS_PERM_FAILURE: + orig = "DNS PERM FAILURE"; + break; + case DKIM_SELECTOR_PUBLIC_KEY_INVALID: + orig = "PUBLIC KEY INVALID"; + break; + case DKIM_NO_SIGNATURES: + orig = "NO SIGNATURES"; + break; + case DKIM_NO_VALID_SIGNATURES: + orig = "NO VALID SIGNATURES"; + break; + case DKIM_BODY_HASH_MISMATCH: + orig = "BODY HASH MISMATCH"; + break; + case DKIM_SELECTOR_ALGORITHM_MISMATCH: + orig = "ALGORITHM MISMATCH"; + break; + case DKIM_STAT_INCOMPAT: + orig = "STAT INCOMPAT"; + break; + case DKIM_UNSIGNED_FROM: + orig = "UNSIGNED FROM"; + break; + default: + orig = "Unkown error"; + break; + } + if (!stralloc_cats(&dkimoutput, orig)) + die(51, 0); + if (!stralloc_cats(&dkimoutput, ":")) + die(51, 0); + if (origRet < 0) { + if (!stralloc_cats(&dkimoutput, "-")) + die(51, 0); + strnum[fmt_ulong(strnum, 0 - origRet)] = 0; + } else + strnum[fmt_ulong(strnum, origRet)] = 0; + if (!stralloc_cats(&dkimoutput, strnum)) + die(51, 0); + if (!stralloc_cats(&dkimoutput, ")")) + die(51, 0); + } + if (!stralloc_cats(&dkimoutput, "\n")) + die(51, 0); + if (useSSP && sspStatus) { + if (!stralloc_cats(&dkimoutput, "X-DKIM-SSP: ")) + die(51, 0); + if (!stralloc_cats(&dkimoutput, sspStatus)) + die(51, 0); + if (!stralloc_cats(&dkimoutput, "\n")) + die(51, 0); + } + if (useADSP && adspStatus) { + if (!stralloc_cats(&dkimoutput, "X-DKIM-ADSP: ")) + die(51, 0); + if (!stralloc_cats(&dkimoutput, adspStatus)) + die(51, 0); + if (!stralloc_cats(&dkimoutput, "\n")) + die(51, 0); + } + dkimverify_exit(ret, dkimStatus, code); + return; +} + +int +checkPractice(int dkimRet, int useADSP, int useSSP) +{ + char *ptr; + + if (!(ptr = env_get("DKIMPRACTICE"))) { + /*- if SIGN_PRACTICE="local" then you can use DKIMVERIFY env variable too */ + if (!useADSP && !useSSP) + dkimpractice = dkimverify; /*- DKIMVERIFY env variable */ + else + return (0); + } else + dkimpractice = ptr; + if (!*dkimpractice) { + if (dkimRet < 0 || dkimRet == DKIM_3PS_SIGNATURE) + return (1); + return (0); + } + if (dkimRet < 0) { + if (dkimpractice[str_chr(dkimpractice, 'F' - dkimRet)]) + return (1); + if (dkimpractice[str_chr(dkimpractice, 'f' - dkimRet)]) + return (1); + } else { + if (dkimpractice[str_chr(dkimpractice, 'A' + dkimRet)]) + return (1); + if (dkimpractice[str_chr(dkimpractice, 'a' + dkimRet)]) + return (1); + } + return (0); +} + +static char *binqqargs[2] = { "bin/qmail-queue", 0 }; + +int +dkim_setoptions(DKIMSignOptions *opts, char *signOptions) +{ + int ch, argc; + char **argv; + + opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1; + opts->nCanon = DKIM_SIGN_RELAXED; /*- c */ + opts->nIncludeBodyLengthTag = 0; /*- l */ + opts->nIncludeQueryMethod = 0; /*- q */ + opts->nIncludeTimeStamp = 0; /*- t */ + opts->nIncludeCopiedHeaders = 0; /*- h */ + opts->szIdentity[0] = '\0'; + opts->expireTime = starttime + 604800; // expires in 1 week + opts->nHash = DKIM_HASH_SHA1; + str_copy(opts->szRequiredHeaders, "NonExistent"); + if (!signOptions) + return (0); + if (!stralloc_copys(&dkimopts, "dkim ")) + die(51, 0); + if (!stralloc_cats(&dkimopts, signOptions)) + die(51, 0); + if (!stralloc_0(&dkimopts)) + die(51, 0); + if (!(argv = MakeArgs(dkimopts.s))) + die(51, 0); + for (argc = 0;argv[argc];argc++); +#ifdef HAVE_EVP_SHA256 + while ((ch = sgopt(argc, argv, "b:c:li:qthx:z:")) != sgoptdone) { +#else + while ((ch = sgopt(argc, argv, "b:c:li:qthx:")) != sgoptdone) { +#endif + switch (ch) + { + case 'b': + switch (*optarg) + { + case '1': + opts->nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1; + break; + case '2': + opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1; + break; + case '3': + opts->nIncludeBodyHash = DKIM_BODYHASH_BOTH; + break; + default: + FreeMakeArgs(argv); + return (1); + } + break; + case 'c': + switch (*optarg) + { + case 'r': + opts->nCanon = DKIM_SIGN_RELAXED; + break; + case 's': + opts->nCanon = DKIM_SIGN_SIMPLE; + break; + case 't': + opts->nCanon = DKIM_SIGN_RELAXED_SIMPLE; + break; + case 'u': + opts->nCanon = DKIM_SIGN_SIMPLE_RELAXED; + break; + default: + FreeMakeArgs(argv); + return (1); + } + break; + case 'l': /*- body length tag */ + opts->nIncludeBodyLengthTag = 1; + break; + case 'q': /*- query method tag */ + opts->nIncludeQueryMethod = 1; + break; + case 't': /*- timestamp tag */ + opts->nIncludeTimeStamp = 1; + break; + case 'h': + opts->nIncludeCopiedHeaders = 1; + break; + case 'i': /*- identity */ + if (*optarg == '-') /* do not use i= tag */ + opts->szIdentity[0] = '\0'; + else + str_copyb(opts->szIdentity, optarg, sizeof(opts->szIdentity) - 1); + break; + case 'x': /*- expire time */ + if (*optarg == '-') + opts->expireTime = 0; + else + opts->expireTime = starttime + atoi(optarg); + break; +#ifdef HAVE_EVP_SHA256 + case 'z': /*- sign w/ sha1, sha256 or both */ + switch (*optarg) + { + case '1': + opts->nHash = DKIM_HASH_SHA1; + break; + case '2': + opts->nHash = DKIM_HASH_SHA256; + break; + case '3': + opts->nHash = DKIM_HASH_SHA1_AND_256; + break; + default: + FreeMakeArgs(argv); + return (1); + } + break; +#endif + default: + FreeMakeArgs(argv); + return (1); + } /*- switch (ch) */ + } /*- while (1) */ + FreeMakeArgs(argv); + return (0); +} + +int +main(int argc, char *argv[]) +{ + int errfd, pim[2]; + int wstat; + int resDKIMSSP = -1, resDKIMADSP = -1, useSSP = 0, useADSP = 0, accept3ps = 0; + int sCount = 0, sSize = 0; + int ret = 0, origRet = DKIM_MAX_ERROR, i, nSigCount = 0, len, token_len; + unsigned long pid; + char *selector, *p; + stralloc dkimfn = {0}; + DKIMSignOptions opts = { 0 }; + DKIMVerifyDetails *pDetails; + DKIMVerifyOptions vopts = { 0 }; + + starttime = now(); + sig_blocknone(); + umask(033); + if (!(p = env_get("ERROR_FD"))) + errfd = CUSTOM_ERR_FD; + else + scan_int(p, &errfd); + substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf)); + if (chdir(auto_qmail) == -1) + die(61, 0); + dkimqueue = env_get("DKIMQUEUE"); + if (dkimqueue && *dkimqueue) + binqqargs[0] = dkimqueue; + dkimsign = env_get("DKIMSIGN"); + dkimverify = env_get("DKIMVERIFY"); + p = (env_get("RELAYCLIENT") || env_get("AUTHINFO")) ? "" : 0; + if (dkimverify && p && env_get("RELAYCLIENT_NODKIMVERIFY")) { + execv(*binqqargs, binqqargs); + die(120, 0); + } + if (!dkimsign && !dkimverify && p) { + if (!(dkimsign = env_get("DKIMKEY"))) { + if (!stralloc_copys(&dkimfn, "domainkeys/%/default")) + die(51, 0); + if (!stralloc_0(&dkimfn)) + die(51, 0); + dkimsign = dkimfn.s; + } + } + if (dkimsign) { + /* selector */ + p = dkimsign; + selector = p; + while (*p) { + if (*p == '/' && *(p + 1)) + selector = p + 1; + p++; + } + str_copyb(opts.szSelector, selector, sizeof(opts.szSelector) - 1); + + if (dkim_setoptions(&opts, env_get("DKIMSIGNOPTIONS"))) { + custom_error("Z", "Invalid DKIMSIGNOPTIONS (#4.3.0)", 0); + _exit(88); + } + p = env_get("DKIMIDENTITY"); + if (p && *p) + str_copyb(opts.szIdentity, p, sizeof(opts.szIdentity) - 1); + p = env_get("DKIMEXPIRE"); + if (p && *p) + opts.expireTime = starttime + atol(p); + else + if (p) + opts.expireTime = 0; + opts.pfnHeaderCallback = SignThisHeader; + if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS) { /*- failed to initialize signature */ + custom_error("Z", "dkim initialization failed (#4.3.0)", 0); + _exit(88); + } + } else { + char *x; + + if (!dkimverify) + dkimverify = ""; + if (!(x = env_get("SIGN_PRACTICE"))) + x = "adsp"; + if (!str_diffn("adsp", x, 4)) { + useADSP = 1; + accept3ps = 1; + } else + if (!str_diffn("ssp", x, 3)) { + useSSP = 1; + accept3ps = 1; + } else + if (!str_diffn("local", x, 5)) { + useSSP = 0; + useADSP = 0; + accept3ps = 1; + } + if (useADSP) + vopts.nCheckPractices = useADSP; + else + if (useSSP) + vopts.nCheckPractices = useSSP; + else + vopts.nCheckPractices = 0; + vopts.nAccept3ps = accept3ps; + vopts.pfnSelectorCallback = NULL; /*- SelectorCallback; */ + if (env_get("UNSIGNED_FROM")) + vopts.nAllowUnsignedFromHeaders = 1; + vopts.nSubjectRequired = env_get("UNSIGNED_SUBJECT") ? 0 : 1; + vopts.nHonorBodyLengthTag = env_get("HONOR_BODYLENGTHTAG") ? 0 : 1; + DKIMVerifyInit(&ctxt, &vopts); /*- this is always successful */ + } + /*- Initialization */ + mypid = getpid(); + uid = getuid(); + datetime_tai(&dt, starttime); + sig_pipeignore(); + sig_miscignore(); + sig_alarmcatch(sigalrm); + sig_bugcatch(sigbug); + alarm(DEATH); + pidopen(); /*- fd = messfd */ + if ((readfd = open_read(pidfn)) == -1) + die(63, dkimsign ? 1 : 2); + if (unlink(pidfn) == -1) + die(63, dkimsign ? 1 : 2); + substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf)); + substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf)); + for (ret = 0;;) { + register int n; + register char *x; + + if ((n = substdio_feed(&ssin)) < 0) { + (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt); + die_read(); + } + if (!n) + break; + x = substdio_PEEK(&ssin); + if (!ret) { + if ((ret = (dkimsign ? DKIMSignProcess : DKIMVerifyProcess) (&ctxt, x, n)) == DKIM_INVALID_CONTEXT) + (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt); + maybe_die_dkim(ret); + } + if (substdio_put(&ssout, x, n) == -1) { + (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt); + die_write(); + } + substdio_SEEK(&ssin, n); + } + if (substdio_flush(&ssout) == -1) { + (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt); + die_write(); + } + if (dkimsign || dkimverify) { + if (dkimsign) { + char *p; + + if (!(p = DKIMSignGetDomain(&ctxt))) { + DKIMSignFree(&ctxt); + maybe_die_dkim(DKIM_INVALID_CONTEXT); + } + write_signature(p, dkimsign); /*- calls DKIMSignFree(&ctxt) */ + } else + if (dkimverify) { + char szPolicy[512]; + + if (!ret) { + if ((ret = DKIMVerifyResults(&ctxt, &sCount, &sSize)) != DKIM_SUCCESS) + maybe_die_dkim(ret); + if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS) + maybe_die_dkim(ret); + else + for (ret = DKIM_FAIL,i = 0; i < nSigCount; i++) { + if (pDetails[i].nResult >= 0) { + ret = 0; + } else { + if (ret == DKIM_FAIL) + ret = pDetails[i].nResult; + } + } + if (!nSigCount) + ret = DKIM_NO_SIGNATURES; + } + /*- what to do if DKIM Verification fails */ + if (checkPractice(ret, useADSP, useSSP)) { + char *domain; + int skip_nosignature_domain = 0; + + origRet = ret; + if ((domain = DKIMVerifyGetDomain(&ctxt))) { + if (!(p = env_get("SIGNATUREDOMAINS"))) { + if (control_readfile(&sigdomains, "signaturedomains", 0) == -1) + die(55, 2); + } else + if (!stralloc_copys(&sigdomains, p)) + die(51, 2); + for (len = 0, p = sigdomains.s;len < sigdomains.len;) { + len += ((token_len = str_len(p)) + 1); /*- next domain */ + if (!case_diffb(p, token_len, domain)) { + ret = origRet; + skip_nosignature_domain = 1; + useADSP = 0; + useSSP = 0; + break; + } + p = sigdomains.s + len; + } + if (!skip_nosignature_domain) { + if (!(p = env_get("NOSIGNATUREDOMAINS"))) { + if (control_readfile(&nsigdomains, "nosignaturedomains", 0) == -1) + die(55, 2); + } else + if (!stralloc_copys(&nsigdomains, p)) + die(51, 2); + for (len = 0, p = nsigdomains.s;len < nsigdomains.len;) { + len += ((token_len = str_len(p)) + 1); /*- next domain */ + if (*p == '*' || !case_diffb(p, token_len, domain)) { + ret = DKIM_NEUTRAL; + useADSP = 0; + useSSP = 0; + break; + } + p = nsigdomains.s + len; + } + } + } + if (!domain || !*domain) + ; /*- do nothing ? */ + else + if (useADSP) { + resDKIMADSP = checkADSP(domain); + if (sCount > 0) { + if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL) + ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS); + } + /* if the message should be signed, return fail */ + if (resDKIMADSP == DKIM_ADSP_DISCARDABLE) + ret = DKIM_FAIL; + else + ret = DKIM_NEUTRAL; + } else + if (useSSP) { + int bTestingPractices = 0; + char *domain; + + if ((domain = DKIMVerifyGetDomain(&ctxt))) + resDKIMSSP = checkSSP(domain, &bTestingPractices); + if (sCount > 0) { + if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL)) + ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS); + } + // if the SSP is testing, return neutral + if (bTestingPractices) + ret = DKIM_NEUTRAL; + /* if the message should be signed, return fail */ + if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT) + ret = DKIM_FAIL; + else + ret = DKIM_NEUTRAL; + } + } + DKIMVerifyFree(&ctxt); + writeHeaderNexit(ret, origRet, resDKIMSSP, resDKIMADSP, useSSP, useADSP); + } /*- if (dkimverify) */ + } + if (pipe(pim) == -1) + die(59, 0); + switch (pid = vfork()) + { + case -1: + close(pim[0]); + close(pim[1]); + die(58, 0); + case 0: + close(pim[1]); + if (fd_move(0, pim[0]) == -1) + die(120, 0); + execv(*binqqargs, binqqargs); + die(120, 0); + } + close(pim[0]); + substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf)); + substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf)); + if (substdio_bput(&ssout, dkimoutput.s, dkimoutput.len) == -1) /*- write DKIM signature */ + die_write(); + switch (substdio_copy(&ssout, &ssin)) + { + case -2: + die_read(); + case -3: + die_write(); + } + if (substdio_flush(&ssout) == -1) + die_write(); + close(pim[1]); + if (wait_pid(&wstat, pid) != pid) + die(57, 0); + if (wait_crashed(wstat)) + die(57, 0); + die(wait_exitcode(wstat), 0); + /*- Not Reached */ + exit(0); +} + +void +getversion_qmail_dkim_c() +{ + static char *x = "$Id: qmail-dkim.c,v 1.49 2018-08-08 23:58:01+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/qmail-inject.c netqmail-1.06/qmail-inject.c --- ../netqmail-1.06-original/qmail-inject.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-inject.c 2019-02-27 20:57:13.397024992 +0100 @@ -22,6 +22,7 @@ #include "auto_qmail.h" #include "newfield.h" #include "constmap.h" +#include "srs.h" #define LINELEN 80 @@ -61,6 +62,11 @@ void temp() { _exit(111); } void die_nomem() { substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); } +void die_srs() { + substdio_puts("qmail-inject: fatal: "); + substdio_puts(subfderr,srs_error.s); + substdio_putsflush(subfderr,"\n"); + perm(); } void die_invalid(sa) stralloc *sa; { substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: "); substdio_putflush(subfderr,sa->s,sa->len); perm(); } @@ -99,6 +105,17 @@ int i; if (!stralloc_0(&sender)) die_nomem(); + + if (!env_get("QMAILINJECT_SKIP_SRS") && (env_get("QMAILINJECT_FORCE_SRS") || (env_get("EXT") && env_get("HOST")))) { + switch(srsforward(sender.s)) { + case -3: die_srs(); break; + case -2: die_nomem(); break; + case -1: die_read(); break; + case 0: break; + case 1: if (!stralloc_copy(&sender,&srs_result)) die_nomem(); break; + } + } + qmail_from(&qqt,sender.s); for (i = 0;i < reciplist.len;++i) @@ -269,6 +286,10 @@ token822_alloc *addr; { if (!addr->len) return; /* don't rewrite <> */ + if (addr->len == 1 && str_equal(addr->t[0].s,"<>")) { + addr->len = 0; + return; + } if (addr->len >= 2) if (addr->t[1].type == TOKEN822_AT) if (addr->t[0].type == TOKEN822_LITERAL) diff -ruN ../netqmail-1.06-original/qmail-local.c netqmail-1.06/qmail-local.c --- ../netqmail-1.06-original/qmail-local.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/qmail-local.c 2019-02-27 20:57:13.398024981 +0100 @@ -28,6 +28,7 @@ #include "myctime.h" #include "gfrom.h" #include "auto_patrn.h" +#include "srs.h" void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); } @@ -66,6 +67,15 @@ char buf[1024]; char outbuf[1024]; +#define QUOTABUFSIZE 256 + +void die_control() { strerr_die1x(111,"Unable to read controls (#4.3.0)"); } +void die_srs() { + if (!stralloc_copys(&foo,srs_error.s)) temp_nomem(); + if (!stralloc_cats(&foo," (#4.3.0)")) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + strerr_die1x(111,foo.s); +} /* child process */ @@ -86,9 +96,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 +115,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+rpline.len+dtline.len); *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 +178,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)"); @@ -282,6 +302,15 @@ qmail_put(&qqt,messline.s,messline.len); } while (match); + + switch(srsforward(ueo.s)) { + case -3: die_srs(); break; + case -2: temp_nomem(); break; + case -1: die_control(); break; + case 0: break; + case 1: if (!stralloc_copy(&ueo,&srs_result)) temp_nomem(); break; + } + qmail_from(&qqt,ueo.s); while (*recips) qmail_to(&qqt,*recips++); qqx = qmail_close(&qqt); diff -ruN ../netqmail-1.06-original/qmail-lspawn.c netqmail-1.06/qmail-lspawn.c --- ../netqmail-1.06-original/qmail-lspawn.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-lspawn.c 2019-02-27 20:57:13.398024981 +0100 @@ -1,4 +1,5 @@ #include "fd.h" +#include "env.h" #include "wait.h" #include "prot.h" #include "substdio.h" @@ -170,6 +171,7 @@ char *s; char *r; int at; { int f; + char *ptr; if (!(f = fork())) { @@ -226,7 +228,10 @@ if (prot_uid(uid) == -1) _exit(QLX_USAGE); if (!getuid()) _exit(QLX_ROOT); - execv(*args,args); + if(!(ptr = env_get("QMAILLOCAL"))) + execv(*args, args); + else + execv(ptr, args); if (error_temp(errno)) _exit(QLX_EXECSOFT); _exit(QLX_EXECHARD); } diff -ruN ../netqmail-1.06-original/qmail-newmvrt.c netqmail-1.06/qmail-newmvrt.c --- ../netqmail-1.06-original/qmail-newmvrt.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qmail-newmvrt.c 2019-02-27 20:57:13.398024981 +0100 @@ -0,0 +1,70 @@ +#include "strerr.h" +#include "stralloc.h" +#include "substdio.h" +#include "getln.h" +#include "exit.h" +#include "readwrite.h" +#include "open.h" +#include "auto_qmail.h" +#include "cdbmss.h" + +#define FATAL "qmail-newmvrt: fatal: " + +void die_read() +{ + strerr_die2sys(111,FATAL,"unable to read control/morevalidrcptto: "); +} +void die_write() +{ + strerr_die2sys(111,FATAL,"unable to write to control/morevalidrcptto.tmp: "); +} + +char inbuf[1024]; +substdio ssin; + +int fd; +int fdtemp; + +struct cdbmss cdbmss; +stralloc line = {0}; +int match; + +void main() +{ + umask(033); + if (chdir(auto_qmail) == -1) + strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": "); + + fd = open_read("control/morevalidrcptto"); + if (fd == -1) die_read(); + + substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf); + + fdtemp = open_trunc("control/morevalidrcptto.tmp"); + if (fdtemp == -1) die_write(); + + if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write(); + + for (;;) { + if (getln(&ssin,&line,&match,'\n') != 0) die_read(); + case_lowerb(line.s,line.len); + while (line.len) { + if (line.s[line.len - 1] == ' ') { --line.len; continue; } + if (line.s[line.len - 1] == '\n') { --line.len; continue; } + if (line.s[line.len - 1] == '\t') { --line.len; continue; } + if (line.s[0] != '#') + if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1) + die_write(); + break; + } + if (!match) break; + } + + if (cdbmss_finish(&cdbmss) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("control/morevalidrcptto.tmp","control/morevalidrcptto.cdb") == -1) + strerr_die2sys(111,FATAL,"unable to move control/morevalidrcptto.tmp to control/morevalidrcptto.cdb"); + + _exit(0); +} diff -ruN ../netqmail-1.06-original/qmail-pop3d.c netqmail-1.06/qmail-pop3d.c --- ../netqmail-1.06-original/qmail-pop3d.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/qmail-pop3d.c 2019-02-27 20:57:13.398024981 +0100 @@ -16,6 +16,11 @@ #include "readwrite.h" #include "timeoutread.h" #include "timeoutwrite.h" +#include +#include "maildirquota.h" +#include "maildirmisc.h" + +#define QUOTABUFSIZE 256 void die() { _exit(0); } @@ -45,19 +50,15 @@ { substdio_put(&ssout,buf,len); } -void puts(s) char *s; -{ - substdio_puts(&ssout,s); -} void flush() { substdio_flush(&ssout); } void err(s) char *s; { - puts("-ERR "); - puts(s); - puts("\r\n"); + substdio_puts(&ssout,"-ERR "); + substdio_puts(&ssout,s); + substdio_puts(&ssout,"\r\n"); flush(); } @@ -73,7 +74,7 @@ void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } -void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); } void printfn(fn) char *fn; { @@ -153,11 +154,11 @@ total = 0; for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,numm)); - puts(" "); + substdio_puts(&ssout," "); put(strnum,fmt_ulong(strnum,total)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } @@ -171,18 +172,41 @@ void pop3_last(arg) char *arg; { - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,last)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } void pop3_quit(arg) char *arg; { int i; + char quotabuf[QUOTABUFSIZE]; + int has_quota=maildir_getquota(".", quotabuf); + + long deleted_bytes=0; + long deleted_messages=0; + for (i = 0;i < numm;++i) if (m[i].flagdeleted) { - if (unlink(m[i].fn) == -1) err_nounlink(); + unsigned long un=0; + const char *filename=m[i].fn; + if (has_quota == 0 && !MAILDIR_DELETED(filename)) { + if (maildir_parsequota(filename, &un)) { + struct stat stat_buf; + + if (stat(filename, &stat_buf) == 0) + un=stat_buf.st_size; + } + } + if (unlink(m[i].fn) == -1) { + err_nounlink(); + un=0; + } + if (un) { + deleted_bytes -= un; + deleted_messages -= 1; + } } else if (str_start(m[i].fn,"new/")) { @@ -192,6 +216,21 @@ if (!stralloc_0(&line)) die_nomem(); rename(m[i].fn,line.s); /* if it fails, bummer */ } + + if (deleted_messages < 0) { + int quotafd; + + if (maildir_checkquota(".", "afd, quotabuf, deleted_bytes, + deleted_messages) && errno != EAGAIN && + deleted_bytes >= 0) + { + if (quotafd >= 0) close (quotafd); + } else { + maildir_addquota(".", quotafd, quotabuf, + deleted_bytes, deleted_messages); + if (quotafd >= 0) close(quotafd); + } + } okay(0); die(); } @@ -222,10 +261,10 @@ int flaguidl; { put(strnum,fmt_uint(strnum,i + 1)); - puts(" "); + substdio_puts(&ssout," "); if (flaguidl) printfn(m[i].fn); else put(strnum,fmt_ulong(strnum,m[i].size)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); } void dolisting(arg,flaguidl) char *arg; int flaguidl; @@ -234,7 +273,7 @@ if (*arg) { i = msgno(arg); if (i == -1) return; - puts("+OK "); + substdio_puts(&ssout,"+OK "); list(i,flaguidl); } else { @@ -242,7 +281,7 @@ for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); - puts(".\r\n"); + substdio_puts(&ssout,".\r\n"); } flush(); } diff -ruN ../netqmail-1.06-original/qmail-pw2u.c netqmail-1.06/qmail-pw2u.c --- ../netqmail-1.06-original/qmail-pw2u.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-pw2u.c 2019-02-27 20:57:13.398024981 +0100 @@ -1,3 +1,4 @@ +#include #include #include #include "substdio.h" diff -ruN ../netqmail-1.06-original/qmail-qmqpc.c netqmail-1.06/qmail-qmqpc.c --- ../netqmail-1.06-original/qmail-qmqpc.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-qmqpc.c 2019-02-27 20:57:13.398024981 +0100 @@ -102,6 +102,8 @@ char *server; { struct ip_address ip; + struct ip_address outip; + outip.d[0]=outip.d[1]=outip.d[2]=outip.d[3]=(unsigned char) 0; char ch; if (!ip_scan(server,&ip)) return; @@ -109,7 +111,7 @@ qmqpfd = socket(AF_INET,SOCK_STREAM,0); if (qmqpfd == -1) die_socket(); - if (timeoutconn(qmqpfd,&ip,PORT_QMQP,10) != 0) { + if (timeoutconn(qmqpfd,&ip,&outip,PORT_QMQP,10) != 0) { lasterror = 73; if (errno == error_timeout) lasterror = 72; close(qmqpfd); diff -ruN ../netqmail-1.06-original/qmail-qmtpd.c netqmail-1.06/qmail-qmtpd.c --- ../netqmail-1.06-original/qmail-qmtpd.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-qmtpd.c 2019-02-27 20:57:13.399024970 +0100 @@ -1,3 +1,5 @@ +#include +#include #include "stralloc.h" #include "substdio.h" #include "qmail.h" diff -ruN ../netqmail-1.06-original/qmail-qstat.sh netqmail-1.06/qmail-qstat.sh --- ../netqmail-1.06-original/qmail-qstat.sh 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-qstat.sh 2019-02-27 20:57:13.399024970 +0100 @@ -1,7 +1,7 @@ cd QMAIL messdirs=`echo queue/mess/* | wc -w` messfiles=`find queue/mess/* -print | wc -w` -tododirs=`echo queue/todo | wc -w` -todofiles=`find queue/todo -print | wc -w` +tododirs=`echo queue/todo/* | wc -w` +todofiles=`find queue/todo/* -print | wc -w` echo messages in queue: `expr $messfiles - $messdirs` echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs` diff -ruN ../netqmail-1.06-original/qmail-queue.8 netqmail-1.06/qmail-queue.8 --- ../netqmail-1.06-original/qmail-queue.8 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/qmail-queue.8 2019-02-27 20:57:13.399024970 +0100 @@ -46,6 +46,13 @@ will invoke the contents of .B $QMAILQUEUE instead, if that environment variable is set. +.SH "CONTROL FILES" +.TP 5 +.I taps +Should contain source address (T for To,F for From, +A for Any), regex syntax of email addresses to tap +and the associated email address to send the copy to. +The fields should be separated by colon. .SH "FILESYSTEM RESTRICTIONS" .B qmail-queue imposes two constraints on the queue structure: diff -ruN ../netqmail-1.06-original/qmail-queue.c netqmail-1.06/qmail-queue.c --- ../netqmail-1.06-original/qmail-queue.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-queue.c 2019-02-27 20:57:13.399024970 +0100 @@ -16,6 +16,8 @@ #include "auto_uids.h" #include "date822fmt.h" #include "fmtqfn.h" +#include "stralloc.h" +#include "constmap.h" #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ #define ADDR 1003 @@ -25,6 +27,14 @@ char outbuf[256]; struct substdio ssout; +int tapok = 0; +stralloc tap = {0}; +struct constmap maptap; +stralloc chkaddr = {0}; +int tapped; +stralloc tapaddr = {0}; +stralloc controlfile = {0}; + datetime_sec starttime; struct datetime dt; unsigned long mypid; @@ -175,13 +185,20 @@ alarm(DEATH); + stralloc_copys( &controlfile, auto_qmail); + stralloc_cats( &controlfile, "/control/taps"); + stralloc_0( &controlfile); + tapok = control_readfile(&tap,controlfile.s,0); + if (tapok == -1) die(65); + if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65); + pidopen(); if (fstat(messfd,&pidst) == -1) die(63); messnum = pidst.st_ino; messfn = fnnum("mess/",1); - todofn = fnnum("todo/",0); - intdfn = fnnum("intd/",0); + todofn = fnnum("todo/",1); + intdfn = fnnum("intd/",1); if (link(pidfn,messfn) == -1) die(64); if (unlink(pidfn) == -1) die(63); @@ -219,14 +236,28 @@ if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (ch != 'F') die(91); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); + stralloc_0(&chkaddr); for (len = 0;len < ADDR;++len) { + if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1); + else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_put(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } if (len >= ADDR) die(11); + /* check the from address */ + stralloc_0(&chkaddr); + if (tapped == 0 && tapcheck('F')==1 ) { + tapped = 1; + if ( tapaddr.len > 0 ) { + if (substdio_bput(&ssout,"T",1) == -1) die_write(); + if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + } + } + if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write(); for (;;) @@ -237,10 +268,24 @@ if (substdio_bput(&ssout,&ch,1) == -1) die_write(); for (len = 0;len < ADDR;++len) { + if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1); + else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1); if (substdio_get(&ssin,&ch,1) < 1) die_read(); if (substdio_bput(&ssout,&ch,1) == -1) die_write(); if (!ch) break; } + + /* check the to address */ + stralloc_0(&chkaddr); + if (tapped == 0 && tapcheck('T')==1 ) { + tapped = 1; + if ( tapaddr.len > 0 ) { + if (substdio_bput(&ssout,"T",1) == -1) die_write(); + if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write(); + if (substdio_bput(&ssout,"",1) == -1) die_write(); + } + } + if (len >= ADDR) die(11); } @@ -252,3 +297,47 @@ triggerpull(); die(0); } + +int tapcheck(t) +char t; +{ + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + stralloc curregex = {0}; + char tmpbuf[200]; + + while (j < tap.len) { + i = j; + if ( tap.s[i]==t || tap.s[i]=='A'){ + while ((tap.s[i] != ':') && (i < tap.len)) i++; + i++; + j=i; + while ((tap.s[i] != ':') && (i < tap.len)) i++; + if (tap.s[j] == '!') { + negate = 1; + j++; + } + stralloc_copys(&tapaddr, &tap.s[i+1]); + + stralloc_copyb(&curregex,tap.s + j,(i - j)); + stralloc_0(&curregex); + x = matchregex(chkaddr.s, curregex.s, tmpbuf); + + + if ((negate) && (x == 0)) { + return 1; + } + if (!(negate) && (x > 0)) { + return 1; + } + } + while ((tap.s[i] != '\0') && (i < tap.len)) i++; + j = i + 1; + negate = 0; + + + } + return 0; +} diff -ruN ../netqmail-1.06-original/qmail-remote.8 netqmail-1.06/qmail-remote.8 --- ../netqmail-1.06-original/qmail-remote.8 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-remote.8 2019-02-27 20:57:13.399024970 +0100 @@ -100,6 +100,73 @@ After this letter comes a human-readable description of what happened. +.B qmail-remote +may use SMTP Authenticaton of type CRAM-MD5, PLAIN, or LOGIN +(in this order) to connect to remote hosts. +The following reports are provided: +.TP 5 +K +no supported AUTH method found, continuing without authentication. +.TP 5 +Z +Connected to +.I host +but password expired. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (AUTH PLAIN). +.TP 5 +Z +Connected to +.I host +but unable to base64encode (plain). +.TP 5 +Z +Connected to +.I host +but authentication was rejected (plain)." +.TP 5 +Z +Connected to +.I host +but authentication was rejected (AUTH LOGIN). +.TP 5 +Z +Connected to +.I host +but unable to base64encode user. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (username). +.TP 5 +Z +Connected to +.I host +but unable to base64encode pass. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (AUTH CRAM-MD5). +Z +Connected to +.I host +but unable to base64decode challenge. +.TP 5 +Z +Connected to +.I host +but unable to base64encode username+digest. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (username+digest). +.PP The recipient reports will always be printed in the same order as .BR qmail-remote 's .I recip @@ -114,6 +181,55 @@ always exits zero. .SH "CONTROL FILES" .TP 5 +.I authsenders +Authenticated sender. +For each +.I sender +included in +.IR authsenders : +.I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword +.B qmail-remote +will try SMTP Authentication +of type CRAM-MD5, LOGIN, or PLAIN +with the provided user name +.I user +and password +.I password +(the authentication information) +and eventually relay the +mail through +.I relay +on port +.IR port . +The use of +.I relay +and +.I port +follows the same rules as for +.IR smtproutes +Note: In case +.I sender +is empty, +.B qmail-remote +will try to deliver each outgoing mail +SMTP authenticated. If the authentication +information is missing, the mail is +delivered none-authenticated. +.I authsenders +can be constructed as follows: + +.EX + @example.com|generic|passwd + .subdomain.example.com|other|otherpw + mail@example.com|test|testpass + info@example.com:smtp.example.com:26|other|otherpw + :mailrelay.example.com:587|e=mc2|testpass +.EE +.TP 5 +.I clientcert.pem +SSL certificate that is used to authenticate with the remote server +during a TLS session. +.TP 5 .I helohost Current host name, for use solely in saying hello to the remote SMTP server. @@ -123,12 +239,31 @@ otherwise .B qmail-remote refuses to run. + +.TP 5 +.I notlshosts/ +.B qmail-remote +will not try TLS on servers for which this file exists +.RB ( +is the fully-qualified domain name of the server). +.IR (tlshosts/.pem +takes precedence over this file however). + +.TP 5 +.I outgoingip +IP address to be used on outgoing connections. +Default: system-defined. +The value +.IR 0.0.0.0 +is equivalent to the system default. .TP 5 .I smtproutes Artificial SMTP routes. Each route has the form .IR domain\fB:\fIrelay , -without any extra spaces. +or +.IR domain\fB:\fIrelay\fB|\fIuser\fB|\fIpassword +in case of authenticated routes without any extra spaces. If .I domain matches @@ -149,6 +284,7 @@ .EX inside.af.mil:firewall.af.mil:26 + :submission.myrelay.com:587|myuserid|mypasswd .EE .I relay @@ -156,6 +292,8 @@ this tells .B qmail-remote to look up MX records as usual. +.I port +value of 465 (deprecated smtps port) causes TLS session to be started. .I smtproutes may include wildcards: @@ -182,6 +320,10 @@ you are always safe using .I smtproutes if you do not accept mail from the network. +Note: +.I authsender +routes have precedence over +.IR smtproutes . .TP 5 .I timeoutconnect Number of seconds @@ -195,6 +337,88 @@ .B qmail-remote will wait for each response from the remote SMTP server. Default: 1200. + +.TP 5 +.I tlsclientciphers +A set of OpenSSL client cipher strings. Multiple ciphers +contained in a string should be separated by a colon. + +.TP 5 +.I tlshosts/.pem +.B qmail-remote +requires TLS authentication from servers for which this file exists +.RB ( +is the fully-qualified domain name of the server). One of the +.I dNSName +or the +.I CommonName +attributes have to match. The file contains the trusted CA certificates. + +.B WARNING: +this option may cause mail to be delayed, bounced, doublebounced, or lost. + +.TP 5 +.I tlshosts/exhaustivelist +if this file exists +no TLS will be tried on hosts other than those for which a file +.B tlshosts/.pem +exists. + +.SH "ENVIRONMENT VARIABLES READ" +Environment variables may be defined globally in the +.B qmail-smtpd +startup script and/or individually as part of the +.B tcpserver's +cdb database. +The environment variables may be quoted ("variable", or 'variable') and +in case of global use, have to be exported. +.B qmail-smtpd +supports the following legacy environment variables, typically +provided by +.B tcpserver +or +.B sslserver +or +.BR tcp-env : +.IR TCPREMOTEIP , +.IR TCPREMOTEHOST +.IR TCPREMOTEINFO +and +.IR TCPLOCALPORT +as well as +.IR RELAYCLIENT . + +.B qmail-smtpd +may use the following environment variables for SMTP authentication: +.TP 5 +.IR SMTPAUTH +is used to enable SMTP Authentication for the AUTH types +LOGIN and PLAIN. +In case +.TP 5 +.IR SMTPAUTH='+cram' +is defined, +.B qmail-smtpd +honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication. +Simply +.TP 5 +.IR SMTPAUTH='cram' +restricts authentication just to CRAM-MD5. +If however +.TP 5 +.IR SMTPAUTH='!' +starts with an exclamation mark, AUTH is required. In particular, +.TP 5 +.IR SMTPAUTH='!cram' +may be useful. +In opposite, if +.TP 5 +.IR SMTPAUTH='-' +starts with a dash, AUTH is disabled for particular +connections. + +Note: The use of 'cram' requires a CRAM-MD5 enabled PAM. + .SH "SEE ALSO" addresses(5), envelopes(5), diff -ruN ../netqmail-1.06-original/qmail-remote.c netqmail-1.06/qmail-remote.c --- ../netqmail-1.06-original/qmail-remote.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-remote.c 2020-04-16 11:23:31.333050380 +0200 @@ -28,6 +28,7 @@ #include "timeoutconn.h" #include "timeoutread.h" #include "timeoutwrite.h" +#include "base64.h" #define HUGESMTPTEXT 5000 @@ -39,14 +40,37 @@ static stralloc sauninit = {0}; stralloc helohost = {0}; +stralloc outgoingip = {0}; stralloc routes = {0}; struct constmap maproutes; stralloc host = {0}; stralloc sender = {0}; +stralloc authsenders = {0}; +struct constmap mapauthsenders; +stralloc user = {0}; +stralloc pass = {0}; +stralloc auth = {0}; +stralloc plain = {0}; +stralloc chal = {0}; +stralloc slop = {0}; +char *authsender; + saa reciplist = {0}; struct ip_address partner; +struct ip_address outip; + +#ifdef TLS +# include +# include "tls.h" +# include "ssl_timeoutio.h" +# include +# define EHLO 1 + +int tls_init(); +const char *ssl_err_str = 0; +#endif void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } @@ -56,6 +80,7 @@ ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?'; if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } } +void temp_noip() { out("Zinvalid ipaddr in control/outgoingip (#4.3.0)\n"); zerodie(); } void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); } void temp_oserr() { out("Z\ System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); } @@ -86,6 +111,12 @@ it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); zerodie(); } +void err_authprot() { + out("Kno supported AUTH method found, continuing without authentication.\n"); + zero(); + substdio_flush(subfdoutsmall); +} + void outhost() { char x[IPFMT]; @@ -99,6 +130,9 @@ outhost(); out(" but connection died. "); if (flagcritical) out("Possible duplicate! "); +#ifdef TLS + if (ssl_err_str) { out((char *)ssl_err_str); out(" "); } +#endif out("(#4.4.2)\n"); zerodie(); } @@ -110,6 +144,12 @@ int saferead(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + if (ssl) { + r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len); + if (r < 0) ssl_err_str = ssl_error_str(); + } else +#endif r = timeoutread(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -117,6 +157,12 @@ int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + if (ssl) { + r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); + if (r < 0) ssl_err_str = ssl_error_str(); + } else +#endif r = timeoutwrite(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -163,6 +209,65 @@ return code; } +#ifdef EHLO +saa ehlokw = {0}; /* list of EHLO keywords and parameters */ +int maxehlokwlen = 0; + +unsigned long ehlo() +{ + stralloc *sa; + char *s, *e, *p; + unsigned long code; + + if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; + ehlokw.len = 0; + +# ifdef MXPS + if (type == 's') return 0; +# endif + + substdio_puts(&smtpto, "EHLO "); + substdio_put(&smtpto, helohost.s, helohost.len); + substdio_puts(&smtpto, "\r\n"); + substdio_flush(&smtpto); + + code = smtpcode(); + if (code != 250) return code; + + s = smtptext.s; + while (*s++ != '\n') ; /* skip the first line: contains the domain */ + + e = smtptext.s + smtptext.len - 6; /* 250-?\n */ + while (s <= e) + { + int wasspace = 0; + + if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); + sa = ehlokw.sa + ehlokw.len++; + if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; + + /* smtptext is known to end in a '\n' */ + for (p = (s += 4); ; ++p) + if (*p == '\n' || *p == ' ' || *p == '\t') { + if (!wasspace) + if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); + if (*p == '\n') break; + wasspace = 1; + } else if (wasspace == 1) { + wasspace = 0; + s = p; + } + s = ++p; + + /* keyword should consist of alpha-num and '-' + * broken AUTH might use '=' instead of space */ + for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } + } + + return 250; +} +#endif + void outsmtptext() { int i; @@ -179,6 +284,16 @@ char *prepend; char *append; { +#ifdef TLS + /* shouldn't talk to the client unless in an appropriate state */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + OSSL_HANDSHAKE_STATE state = ssl ? SSL_get_state(ssl) : TLS_ST_BEFORE; + if (state & TLS_ST_OK || (!smtps && state & TLS_ST_BEFORE)) +#else + int state = ssl ? ssl->state : SSL_ST_BEFORE; + if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE)) +#endif +#endif substdio_putsflush(&smtpto,"QUIT\r\n"); /* waiting for remote side is just too ridiculous */ out(prepend); @@ -186,6 +301,30 @@ out(append); out(".\n"); outsmtptext(); + +#if defined(TLS) && defined(DEBUG) + if (ssl) { + X509 *peercert; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_get_cipher(ssl)); + + /* we want certificate details */ + if (peercert = SSL_get_peer_certificate(ssl)) { + char *str; + + str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0); + out("; subject="); out(str); OPENSSL_free(str); + + str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0); + out("; issuer="); out(str); OPENSSL_free(str); + + X509_free(peercert); + } + out(";\n"); + } +#endif + zerodie(); } @@ -201,6 +340,16 @@ if (ch == '.') substdio_put(&smtpto,".",1); while (ch != '\n') { + if (ch == '\r') { + r = substdio_get(&ssin, &ch, 1); + if (r == 0) + break; + if (r == -1) temp_read(); + if (ch != '\n') { + substdio_put(&smtpto, "\r\n", 2); + } else + break; + } substdio_put(&smtpto,&ch,1); r = substdio_get(&ssin,&ch,1); if (r == 0) perm_partialline(); @@ -214,30 +363,436 @@ substdio_flush(&smtpto); } -stralloc recip = {0}; +#ifdef TLS +char *partner_fqdn = 0; -void smtp() +# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") +void tls_quit(const char *s1, const char *s2) +{ + out((char *)s1); if (s2) { out(": "); out((char *)s2); } TLS_QUIT; +} +# define tls_quit_error(s) tls_quit(s, ssl_error()) + +int match_partner(const char *s, int len) +{ + if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; + /* we also match if the name is *.domainname */ + if (*s == '*') { + const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); + if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; + } + return 0; +} + +/* don't want to fail handshake if certificate can't be verified */ +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } + +int tls_init() { - unsigned long code; - int flagbother; int i; + SSL *myssl; + SSL_CTX *ctx; + stralloc saciphers = {0}; + const char *ciphers, *servercert = 0; + + if (partner_fqdn) { + struct stat st; + stralloc tmp = {0}; + if (!stralloc_copys(&tmp, "control/tlshosts/") + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) + || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); + if (stat(tmp.s, &st) == 0) + servercert = tmp.s; + else { + if (!stralloc_copys(&tmp, "control/notlshosts/") + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1)) + temp_nomem(); + if ((stat("control/tlshosts/exhaustivelist", &st) == 0) || + (stat(tmp.s, &st) == 0)) { + alloc_free(tmp.s); + return 0; + } + alloc_free(tmp.s); + } + } - if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); - - substdio_puts(&smtpto,"HELO "); - substdio_put(&smtpto,helohost.s,helohost.len); - substdio_puts(&smtpto,"\r\n"); - substdio_flush(&smtpto); - if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); - + if (!smtps) { + stralloc *sa = ehlokw.sa; + unsigned int len = ehlokw.len; + /* look for STARTTLS among EHLO keywords */ + for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; + if (!len) { + if (!servercert) return 0; + out("ZNo TLS achieved while "); out((char *)servercert); + out(" exists"); smtptext.len = 0; TLS_QUIT; + } + } + + SSL_library_init(); + ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ctx) { + if (!smtps && !servercert) return 0; + smtptext.len = 0; + tls_quit_error("ZTLS error initializing ctx"); + } + + /* POODLE vulnerability */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + if (servercert) { + if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { + SSL_CTX_free(ctx); + smtptext.len = 0; + out("ZTLS unable to load "); tls_quit_error(servercert); + } + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + } + + /* let the other side complain if it needs a cert and we don't have one */ +# define CLIENTCERT "control/clientcert.pem" + if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) + SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); +# undef CLIENTCERT + +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + SSL_CTX_set_post_handshake_auth(ctx, 1); +#endif + + myssl = SSL_new(ctx); + SSL_CTX_free(ctx); + if (!myssl) { + if (!smtps && !servercert) return 0; + smtptext.len = 0; + tls_quit_error("ZTLS error initializing ssl"); + } + + if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); + + /* while the server is preparing a responce, do something else */ + if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) + { SSL_free(myssl); temp_control(); } + if (saciphers.len) { + for (i = 0; i < saciphers.len - 1; ++i) + if (!saciphers.s[i]) saciphers.s[i] = ':'; + ciphers = saciphers.s; + } + else ciphers = "DEFAULT"; + SSL_set_cipher_list(myssl, ciphers); + alloc_free(saciphers.s); + + SSL_set_fd(myssl, smtpfd); + + /* read the response to STARTTLS */ + if (!smtps) { + if (smtpcode() != 220) { + SSL_free(myssl); + if (!servercert) return 0; + out("ZSTARTTLS rejected while "); + out((char *)servercert); out(" exists"); TLS_QUIT; + } + smtptext.len = 0; + } + + ssl = myssl; + if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) + tls_quit("ZTLS connect failed", ssl_error_str()); + + if (servercert) { + X509 *peercert; + STACK_OF(GENERAL_NAME) *gens; + int found_gen_dns = 0; + int matched_gen_dns = 0; + + int r = SSL_get_verify_result(ssl); + if (r != X509_V_OK) { + out("ZTLS unable to verify server with "); + tls_quit(servercert, X509_verify_cert_error_string(r)); + } + alloc_free(servercert); + + peercert = SSL_get_peer_certificate(ssl); + if (!peercert) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "no certificate provided"); + } + + /* RFC 2595 section 2.4: find a matching name + * first find a match among alternative names */ + gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); + if (gens) { + for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) + { + const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); + if (gn->type == GEN_DNS){ + found_gen_dns = 1; + if (match_partner(gn->d.ia5->data, gn->d.ia5->length)){ + matched_gen_dns = 1; + break; + } + } + } + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + } + + /* no SubjectAltName of type DNS found, look up commonName */ + if (!found_gen_dns) { + stralloc peer = {0}; + X509_NAME *subj = X509_get_subject_name(peercert); + i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); + if (i >= 0) { + const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, i)); + if (s) { peer.len = s->length; peer.s = s->data; } + } + if (peer.len <= 0) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "certificate contains no valid commonName"); + } + if (!match_partner(peer.s, peer.len)) { + out("ZTLS unable to verify server "); out(partner_fqdn); + out(": received certificate for "); outsafe(&peer); TLS_QUIT; + } + } else if (!matched_gen_dns) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "certificate contains no matching dNSNnames"); + } + + X509_free(peercert); + } + + if (smtps) if (smtpcode() != 220) + quit("ZTLS Connected to "," but greeting failed"); + + return 1; +} +#endif + +stralloc recip = {0}; + +void mailfrom() +{ substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); substdio_flush(&smtpto); +} + +stralloc xuser = {0}; + +int xtext(sa,s,len) +stralloc *sa; +char *s; +int len; +{ + int i; + + if(!stralloc_copys(sa,"")) temp_nomem(); + + for (i = 0; i < len; i++) { + if (s[i] == '=') { + if (!stralloc_cats(sa,"+3D")) temp_nomem(); + } else if (s[i] == '+') { + if (!stralloc_cats(sa,"+2B")) temp_nomem(); + } else if ((int) s[i] < 33 || (int) s[i] > 126) { + if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */ + } else if (!stralloc_catb(sa,s+i,1)) { + temp_nomem(); + } + } + + return sa->len; +} + +void mailfrom_xtext() +{ + if (!xtext(&xuser,user.s,user.len)) temp_nomem(); + substdio_puts(&smtpto,"MAIL FROM:<"); + substdio_put(&smtpto,sender.s,sender.len); + substdio_puts(&smtpto,"> AUTH="); + substdio_put(&smtpto,xuser.s,xuser.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); +} + +int mailfrom_plain() +{ + substdio_puts(&smtpto,"AUTH PLAIN\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH PLAIN)."); return -1; } + if (!stralloc_cat(&plain,&user)) temp_nomem(); /* */ + if (!stralloc_0(&plain)) temp_nomem(); + if (!stralloc_cat(&plain,&user)) temp_nomem(); /* */ + if (!stralloc_0(&plain)) temp_nomem(); + if (!stralloc_cat(&plain,&pass)) temp_nomem(); /* password */ + if (b64encode(&plain,&auth)) quit("ZConnected to "," but unable to base64encode (plain)."); + substdio_put(&smtpto,auth.s,auth.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 235) { mailfrom_xtext(); return 0; } + else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; } + else { quit("ZConnected to "," but authentication was rejected (plain)."); return 1; } + + return 0; +} + +int mailfrom_login() +{ + substdio_puts(&smtpto,"AUTH LOGIN\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)."); return -1; } + + if (!stralloc_copys(&auth,"")) temp_nomem(); + if (b64encode(&user,&auth)) quit("ZConnected to "," but unable to base64encode user."); + substdio_put(&smtpto,auth.s,auth.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username)."); + + if (!stralloc_copys(&auth,"")) temp_nomem(); + if (b64encode(&pass,&auth)) quit("ZConnected to "," but unable to base64encode pass."); + substdio_put(&smtpto,auth.s,auth.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 235) { mailfrom_xtext(); return 0; } + else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; } + else { quit("ZConnected to "," but authentication was rejected (password)."); return 1; } +} + +int mailfrom_cram() +{ + int j; + unsigned char h; + unsigned char digest[16]; + unsigned char digascii[33]; + static char hextab[]="0123456789abcdef"; + + substdio_puts(&smtpto,"AUTH CRAM-MD5\r\n"); + substdio_flush(&smtpto); + if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH CRAM-MD5)."); return -1; } + + if (str_chr(smtptext.s+4,' ')) { /* Challenge */ + if(!stralloc_copys(&slop,"")) temp_nomem(); + if (!stralloc_copyb(&slop,smtptext.s+4,smtptext.len-5)) temp_nomem(); + if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge."); + } + + hmac_md5(chal.s,chal.len,pass.s,pass.len,digest); + + for (j = 0;j < 16;j++) /* HEX => ASCII */ + { + digascii[2*j] = hextab[digest[j] >> 4]; + digascii[2*j+1] = hextab[digest[j] & 0xf]; + } + digascii[32]=0; + + slop.len = 0; + if (!stralloc_copys(&slop,"")) temp_nomem(); + if (!stralloc_cat(&slop,&user)) temp_nomem(); /* user-id */ + if (!stralloc_cats(&slop," ")) temp_nomem(); + if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */ + + if (!stralloc_copys(&auth,"")) temp_nomem(); + if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest."); + substdio_put(&smtpto,auth.s,auth.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + if (smtpcode() == 235) { mailfrom_xtext(); return 0; } + else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; } + else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; } +} + +void smtp_auth() +{ + int i, j; + + for (i = 0; i + 8 < smtptext.len; i += str_chr(smtptext.s+i,'\n')+1) + if (!str_diffn(smtptext.s+i+4,"AUTH",4)) { + if (j = str_chr(smtptext.s+i+8,'C') > 0) + if (case_starts(smtptext.s+i+8+j,"CRAM")) + if (mailfrom_cram() >= 0) return; + + if (j = str_chr(smtptext.s+i+8,'P') > 0) + if (case_starts(smtptext.s+i+8+j,"PLAIN")) + if (mailfrom_plain() >= 0) return; + + if (j = str_chr(smtptext.s+i+8,'L') > 0) + if (case_starts(smtptext.s+i+8+j,"LOGIN")) + if (mailfrom_login() >= 0) return; + + err_authprot(); + mailfrom(); + } +} + +void smtp() +{ + unsigned long code; + int flagbother; + int i; + + #ifndef PORT_SMTP + /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */ + # define port smtp_port + #endif + + #ifdef TLS + # ifdef MXPS + if (type == 'S') smtps = 1; + else if (type != 's') + # endif + if (port == 465) smtps = 1; + if (!smtps) + #endif + + code = smtpcode(); + if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed"); + if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */ + if (code != 220) quit("ZConnected to "," but greeting failed"); + +#ifdef EHLO +# ifdef TLS + if (!smtps) +# endif + code = ehlo(); + +# ifdef TLS + if (tls_init()) + /* RFC2487 says we should issue EHLO (even if we might not need + * extensions); at the same time, it does not prohibit a server + * to reject the EHLO and make us fallback to HELO */ + code = ehlo(); +# endif + + if (code == 250) { + /* add EHLO response checks here */ + + /* and if EHLO failed, use HELO */ + } else { +#endif + +/* if (smtpcode() != 250) { */ + substdio_puts(&smtpto,"HELO "); + substdio_put(&smtpto,helohost.s,helohost.len); + substdio_puts(&smtpto,"\r\n"); + substdio_flush(&smtpto); + code = smtpcode(); + if (code >= 500) quit("DConnected to "," but my name was rejected"); + if (code != 250) quit("ZConnected to "," but my name was rejected"); +/* } */ + +#ifdef EHLO + } +#endif + + if (user.len && pass.len) + smtp_auth(); + else + mailfrom(); + code = smtpcode(); if (code >= 500) quit("DConnected to "," but sender was rejected"); if (code >= 400) quit("ZConnected to "," but sender was rejected"); - + flagbother = 0; for (i = 0;i < reciplist.len;++i) { substdio_puts(&smtpto,"RCPT TO:<"); @@ -246,15 +801,23 @@ substdio_flush(&smtpto); code = smtpcode(); if (code >= 500) { - out("h"); outhost(); out(" does not like recipient.\n"); + /* added by Endersys R&D Team */ + out("h "); outhost(); out(" does not like recipient.\n"); outsmtptext(); zero(); } else if (code >= 400) { - out("s"); outhost(); out(" does not like recipient.\n"); + /* added by Endersys R&D Team */ + out("s "); outhost(); out(" does not like recipient.\n"); outsmtptext(); zero(); } else { - out("r"); zero(); + /* + * James Raftery + * Log _real_ envelope recipient, post canonicalisation. + * and modified by Endersys R&D Team + */ + + out("r "); zero(); flagbother = 1; } } @@ -297,19 +860,14 @@ if (!stralloc_cats(saout,"@")) temp_nomem(); if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem(); - if (flagcname) - switch(dns_cname(&canonhost)) { - case 0: *flagalias = 0; break; - case DNS_MEM: temp_nomem(); - case DNS_SOFT: temp_dnscanon(); - case DNS_HARD: ; /* alias loop, not our problem */ - } + if (flagcname) *flagalias = 0; if (!stralloc_cat(saout,&canonhost)) temp_nomem(); } void getcontrols() { + int r; if (control_init() == -1) temp_control(); if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) @@ -324,48 +882,108 @@ case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } + + switch(control_readfile(&authsenders,"control/authsenders",0)) { + case -1: + temp_control(); + case 0: + if (!constmap_init(&mapauthsenders,"",0,1)) temp_nomem(); break; + case 1: + if (!constmap_init(&mapauthsenders,authsenders.s,authsenders.len,1)) temp_nomem(); break; + } + r = control_readline(&outgoingip,"control/outgoingip"); + if (-1 == r) { if (errno == error_nomem) temp_nomem(); temp_control(); } + if (0 == r && !stralloc_copys(&outgoingip, "0.0.0.0")) temp_nomem(); + if (str_equal(outgoingip.s, "0.0.0.0")) + { outip.d[0]=outip.d[1]=outip.d[2]=outip.d[3]=(unsigned long) 0; } + else if (!ip_scan(outgoingip.s, &outip)) temp_noip(); } -void main(argc,argv) +int main(argc,argv) int argc; char **argv; { static ipalloc ip = {0}; - int i; + int i, j; unsigned long random; char **recips; unsigned long prefme; int flagallaliases; int flagalias; char *relayhost; - + sig_pipeignore(); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); getcontrols(); - if (!stralloc_copys(&host,argv[1])) temp_nomem(); - + + authsender = 0; relayhost = 0; - for (i = 0;i <= host.len;++i) - if ((i == 0) || (i == host.len) || (host.s[i] == '.')) - if (relayhost = constmap(&maproutes,host.s + i,host.len - i)) + + addrmangle(&sender,argv[2],&flagalias,0); + + for (i = 0;i <= sender.len;++i) + if ((i == 0) || (i == sender.len) || (sender.s[i] == '.') || (sender.s[i] == '@')) + if (authsender = constmap(&mapauthsenders,sender.s + i,sender.len - i)) break; - if (relayhost && !*relayhost) relayhost = 0; - - if (relayhost) { - i = str_chr(relayhost,':'); - if (relayhost[i]) { - scan_ulong(relayhost + i + 1,&port); - relayhost[i] = 0; + + if (authsender && !*authsender) authsender = 0; + + if (authsender) { + i = str_chr(authsender,'|'); + if (authsender[i]) { + j = str_chr(authsender + i + 1,'|'); + if (authsender[j]) { + authsender[i] = 0; + authsender[i + j + 1] = 0; + if (!stralloc_copys(&user,"")) temp_nomem(); + if (!stralloc_copys(&user,authsender + i + 1)) temp_nomem(); + if (!stralloc_copys(&pass,"")) temp_nomem(); + if (!stralloc_copys(&pass,authsender + i + j + 2)) temp_nomem(); + } + } + i = str_chr(authsender,':'); + if (authsender[i]) { + scan_ulong(authsender + i + 1,&port); + authsender[i] = 0; } - if (!stralloc_copys(&host,relayhost)) temp_nomem(); - } + if (!stralloc_copys(&relayhost,authsender)) temp_nomem(); + if (!stralloc_copys(&host,authsender)) temp_nomem(); + + } + else { /* default smtproutes -- authenticated */ + for (i = 0;i <= host.len;++i) + if ((i == 0) || (i == host.len) || (host.s[i] == '.')) + if (relayhost = constmap(&maproutes,host.s + i,host.len - i)) + break; + + if (relayhost && !*relayhost) relayhost = 0; + + if (relayhost) { + i = str_chr(relayhost,'|'); + if (relayhost[i]) { + j = str_chr(relayhost + i + 1,'|'); + if (relayhost[j]) { + relayhost[i] = 0; + relayhost[i + j + 1] = 0; + if (!stralloc_copys(&user,"")) temp_nomem(); + if (!stralloc_copys(&user,relayhost + i + 1)) temp_nomem(); + if (!stralloc_copys(&pass,"")) temp_nomem(); + if (!stralloc_copys(&pass,relayhost + i + j + 2)) temp_nomem(); + } + } + i = str_chr(relayhost,':'); + if (relayhost[i]) { + scan_ulong(relayhost + i + 1,&port); + relayhost[i] = 0; + } + if (!stralloc_copys(&host,relayhost)) temp_nomem(); + } + } - addrmangle(&sender,argv[2],&flagalias,0); - if (!saa_readyplus(&reciplist,0)) temp_nomem(); if (ipme_init() != 1) temp_oserr(); @@ -414,10 +1032,13 @@ smtpfd = socket(AF_INET,SOCK_STREAM,0); if (smtpfd == -1) temp_oserr(); - if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { + if (timeoutconn(smtpfd,&ip.ix[i].ip,&outip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; - smtp(); /* does not return */ +#ifdef TLS + partner_fqdn = ip.ix[i].fqdn; +#endif + smtp(); /* only returns when the next MX is to be tried */ } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); diff -ruN ../netqmail-1.06-original/qmail-rspawn.c netqmail-1.06/qmail-rspawn.c --- ../netqmail-1.06-original/qmail-rspawn.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-rspawn.c 2019-02-27 20:57:13.400024959 +0100 @@ -1,3 +1,4 @@ +#include "env.h" #include "fd.h" #include "wait.h" #include "substdio.h" @@ -82,7 +83,7 @@ char *s; char *r; int at; { int f; - char *(args[5]); + char *ptr, *(args[5]); args[0] = "qmail-remote"; args[1] = r + at + 1; @@ -95,7 +96,10 @@ if (fd_move(0,fdmess) == -1) _exit(111); if (fd_move(1,fdout) == -1) _exit(111); if (fd_copy(2,1) == -1) _exit(111); - execvp(*args,args); + if(!(ptr = env_get("QMAILREMOTE"))) + execvp(*args, args); + else + execvp(ptr, args); if (error_temp(errno)) _exit(111); _exit(100); } diff -ruN ../netqmail-1.06-original/qmail-send.9 netqmail-1.06/qmail-send.9 --- ../netqmail-1.06-original/qmail-send.9 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-send.9 2019-06-26 16:45:05.514152928 +0200 @@ -16,6 +16,15 @@ .B qmail-send leaves it in the queue and tries the addresses again later. +.B Supplemental queues +allow more than one queue for remote recipients. (CHANNELS - 2) supplemental queues total, because one queue is always +designated for local deliveries and a second queue is always available for remote deliveries that +don't match any of the domains listed in the supplemental queue control files. +This makes it possible to divide remote deliveries into distinct queues at different concurrency +levels and can be used as a throttling mechanism based on domain. +Supplemental queues are managed by the supplsX and concurrencysupplX control files, where X is an integer from +0 to (CHANNELS - 3). + .B qmail-send prints a readable record of its activities to descriptor 0. It writes commands to @@ -51,7 +60,13 @@ .B qmail-send receives a HUP signal, it will reread -.I locals +.IR concurrencylocal , +.IR concurrencyremote , +.IR locals, +.IR supplsX, +.IR concurrencylocal, +.IR concurrencyremote, +.IR concurrencysupplX and .IR virtualdomains . .TP 5 @@ -93,6 +108,15 @@ is limited at compile time to SPAWN. .TP 5 +.I concurrencysupplX +Maximum number of simultaneous delivery attempts via supplemental +channel X, where X is an integer starting at 0. +Default: 20. +If 0, deliveries via channel X will be put on hold. +.I concurrencysupplX +is limited at compile time to +SPAWN. +.TP 5 .I doublebouncehost Double-bounce host. Default: @@ -115,6 +139,10 @@ (If that bounces, .B qmail-send gives up.) +As a special case, if the first line of +.IR doublebounceto +is blank (contains a single linefeed), qmail-send will not queue +the double-bounce at all. .TP 5 .I envnoathost Presumed domain name for addresses without @ signs. @@ -147,6 +175,12 @@ is listed in .IR locals . .TP 5 +.I supplsX +List of domain names that the current host +will deliver on supplemental channel X where X is an integer starting at 0, +one per line. +No default. +.TP 5 .I percenthack List of domain names where the percent hack is applied. If @@ -164,7 +198,9 @@ handles .I percenthack before -.IR locals . +.I locals +and +.IR supplsX. .TP 5 .I queuelifetime Number of seconds diff -ruN ../netqmail-1.06-original/qmail-send.c netqmail-1.06/qmail-send.c --- ../netqmail-1.06-original/qmail-send.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-send.c 2019-06-26 16:42:40.004753618 +0200 @@ -31,6 +31,11 @@ #include "constmap.h" #include "fmtqfn.h" #include "readsubdir.h" +#include "srs.h" + +#include "auto_spawn.h" + +#include "channels.h" /* critical timing feature #1: if not triggered, do not busy-loop */ /* critical timing feature #2: if triggered, respond within fixed time */ @@ -44,6 +49,8 @@ int lifetime = 604800; +int bouncemaxbytes = 50000; + stralloc percenthack = {0}; struct constmap mappercenthack; stralloc locals = {0}; @@ -55,17 +62,20 @@ stralloc bouncehost = {0}; stralloc doublebounceto = {0}; stralloc doublebouncehost = {0}; +stralloc srs_domain = {0}; char strnum2[FMT_ULONG]; char strnum3[FMT_ULONG]; -#define CHANNELS 2 -char *chanaddr[CHANNELS] = { "local/", "remote/" }; -char *chanstatusmsg[CHANNELS] = { " local ", " remote " }; -char *tochan[CHANNELS] = { " to local ", " to remote " }; -int chanfdout[CHANNELS] = { 1, 3 }; -int chanfdin[CHANNELS] = { 2, 4 }; -int chanskip[CHANNELS] = { 10, 20 }; +char *chanaddr[CHANNELS]; +char *chanstatusmsg[CHANNELS]; +char *tochan[CHANNELS]; +int chanfdout[CHANNELS]; +int chanfdin[CHANNELS]; +int chanskip[CHANNELS]; +struct constmap mapsuppl[SUPPL_CHANNELS]; +stralloc suppls[SUPPL_CHANNELS]; +stralloc newsuppls[SUPPL_CHANNELS]; int flagexitasap = 0; void sigterm() { flagexitasap = 1; } int flagrunasap = 0; void sigalrm() { flagrunasap = 1; } @@ -82,12 +92,10 @@ datetime_sec recent; - -/* this file is too long ----------------------------------------- FILENAMES */ - stralloc fn = {0}; stralloc fn2 = {0}; char fnmake_strnum[FMT_ULONG]; +stralloc fname = {0}; void fnmake_init() { @@ -96,7 +104,7 @@ } void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); } -void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); } +void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,1); } void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); } void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); } void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); } @@ -117,6 +125,7 @@ { int i; int j; + int c; char *x; static stralloc addr = {0}; int at; @@ -159,6 +168,13 @@ if (!stralloc_cat(&rwline,&addr)) return 0; if (!stralloc_0(&rwline)) return 0; + + for (c = 0;c < SUPPL_CHANNELS;++c) + { + if (constmap(&mapsuppl[c],addr.s + at + 1,addr.len - at - 1)) + return c + 3; + } + return 2; } @@ -228,7 +244,8 @@ substdio sstoqc; char sstoqcbuf[1024]; substdio ssfromqc; char ssfromqcbuf[1024]; -stralloc comm_buf[CHANNELS] = { {0}, {0} }; + +stralloc comm_buf[CHANNELS]; int comm_pos[CHANNELS]; void comm_init() @@ -262,6 +279,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(); @@ -382,7 +401,7 @@ /* this file is too long ----------------------------------- PRIORITY QUEUES */ prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */ -prioq pqchan[CHANNELS] = { {0}, {0} }; +prioq pqchan[CHANNELS]; /* pqchan 0: -todo +info +local ?remote */ /* pqchan 1: -todo +info ?local +remote */ prioq pqfail = {0}; /* stat() failure; has to be pqadded again */ @@ -683,15 +702,39 @@ } if (str_equal(sender.s,"#@[]")) log3("triple bounce: discarding ",fn2.s,"\n"); + else if (!*sender.s && *doublebounceto.s == '@') + log3("double bounce: discarding ",fn2.s,"\n"); else { if (qmail_open(&qqt) == -1) { log1("warning: unable to start qmail-queue, will try later\n"); return 0; } qp = qmail_qp(&qqt); - if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; } - else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; } - + if (*sender.s) { + if (srs_domain.len) { + int j = 0; + j = byte_rchr(sender.s, sender.len, '@'); + if (j < sender.len) { + if (srs_domain.len == sender.len - j - 1 && stralloc_starts(&srs_domain, sender.s + j + 1)) { + switch(srsreverse(sender.s)) { + case -3: log1(srs_error.s); log1("\n"); _exit(111); break; + case -2: nomem(); break; + case -1: log1("alert: unable to read controls\n"); _exit(111); break; + case 0: break; + case 1: if (!stralloc_copy(&sender,&srs_result)) nomem(); break; + } + if (chdir(auto_qmail) == -1) { log1("alert: unable to switch to home directory\n"); _exit(111); } + if (chdir("queue") == -1) { log1("alert: unable to switch to queue directory\n"); _exit(111); } + } + } + } + bouncesender = ""; + bouncerecip = sender.s; + } else { + bouncesender = "#@[]"; + bouncerecip = doublebounceto.s; + } + while (!newfield_datemake(now())) nomem(); qmail_put(&qqt,newfield_date.s,newfield_date.len); qmail_puts(&qqt,"From: "); @@ -740,9 +783,17 @@ qmail_fail(&qqt); else { + int bytestogo = bouncemaxbytes; + int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf)); - while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0) + while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) { qmail_put(&qqt,buf,r); + bytestogo -= bytestoget; + bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf; + } + if (r > 0) { + qmail_puts(&qqt,"\n\n--- End of message stripped.\n"); + } close(fd); if (r == -1) qmail_fail(&qqt); @@ -780,8 +831,8 @@ ; unsigned long masterdelid = 1; -unsigned int concurrency[CHANNELS] = { 10, 20 }; -unsigned int concurrencyused[CHANNELS] = { 0, 0 }; +unsigned int concurrency[CHANNELS]; +unsigned int concurrencyused[CHANNELS]; struct del *d[CHANNELS]; stralloc dline[CHANNELS]; char delbuf[2048]; @@ -808,9 +859,9 @@ for (c = 0;c < CHANNELS;++c) { flagspawnalive[c] = 1; - while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del)))) + while (!(d[c] = (struct del *) alloc(auto_spawn * sizeof(struct del)))) nomem(); - for (i = 0;i < concurrency[c];++i) + for (i = 0;i < auto_spawn;++i) { d[c][i].used = 0; d[c][i].recip.s = 0; } dline[c].s = 0; while (!stralloc_copys(&dline[c],"")) nomem(); @@ -906,41 +957,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]; - if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) + delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8; + if ((delnum < 0) || (delnum >= auto_spawn) || !d[c][delnum].used) log1("warning: internal error: delivery report out of range\n"); else { strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; - if (dline[c].s[1] == 'Z') + if (dline[c].s[2] == 'Z') if (jo[d[c][delnum].j].flagdying) { - dline[c].s[1] = 'D'; + dline[c].s[2] = 'D'; --dline[c].len; while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); while (!stralloc_0(&dline[c])) nomem(); } - switch(dline[c].s[1]) + switch(dline[c].s[2]) { case 'K': log3("delivery ",strnum3,": success: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; case 'Z': log3("delivery ",strnum3,": deferral: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); break; case 'D': log3("delivery ",strnum3,": failure: "); - logsafe(dline[c].s + 2); + logsafe(dline[c].s + 3); log1("\n"); - addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); + addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3); markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); --jo[d[c][delnum].j].numtodo; break; @@ -1215,8 +1267,10 @@ /* this file is too long ---------------------------------------------- TODO */ +#ifndef EXTERNAL_TODO datetime_sec nexttodorun; -DIR *tododir; /* if 0, have to opendir again */ +int flagtododir = 0; /* if 0, have to readsubdir_init again */ +readsubdir todosubdir; stralloc todoline = {0}; char todobuf[SUBSTDIO_INSIZE]; char todobufinfo[512]; @@ -1224,7 +1278,7 @@ void todo_init() { - tododir = 0; + flagtododir = 0; nexttodorun = now(); trigger_set(); } @@ -1236,7 +1290,7 @@ { if (flagexitasap) return; trigger_selprep(nfds,rfds); - if (tododir) *wakeup = 0; + if (flagtododir) *wakeup = 0; if (*wakeup > nexttodorun) *wakeup = nexttodorun; } @@ -1253,8 +1307,7 @@ char ch; int match; unsigned long id; - unsigned int len; - direntry *d; + int z; int c; unsigned long uid; unsigned long pid; @@ -1265,32 +1318,26 @@ if (flagexitasap) return; - if (!tododir) + if (!flagtododir) { if (!trigger_pulled(rfds)) if (recent < nexttodorun) return; trigger_set(); - tododir = opendir("todo"); - if (!tododir) - { - pausedir("todo"); - return; - } + readsubdir_init(&todosubdir, "todo", pausedir); + flagtododir = 1; nexttodorun = recent + SLEEP_TODO; } - d = readdir(tododir); - if (!d) + switch(readsubdir_next(&todosubdir, &id)) { - closedir(tododir); - tododir = 0; + case 1: + break; + case 0: + flagtododir = 0; + default: return; } - if (str_equal(d->d_name,".")) return; - if (str_equal(d->d_name,"..")) return; - len = scan_ulong(d->d_name,&id); - if (!len || d->d_name[len]) return; fnmake_todo(id); @@ -1363,12 +1410,9 @@ log1("\n"); break; case 'T': - switch(rewrite(todoline.s + 1)) - { - case 0: nomem(); goto fail; - case 2: c = 1; break; - default: c = 0; break; - } + c = rewrite(todoline.s + 1); + if (c == 0) { nomem(); goto fail; } + c--; if (fdchan[c] == -1) { fnmake_chanaddr(id,c); @@ -1438,17 +1482,175 @@ if (fdchan[c] != -1) close(fdchan[c]); } +#endif + +/* this file is too long ------------------------------------- EXTERNAL TODO */ + +#ifdef EXTERNAL_TODO +stralloc todoline = {0}; +char todobuf[2048]; +int todofdin; +int todofdout; +int flagtodoalive; + +void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n"); + flagexitasap = 1; flagtodoalive = 0; } + +void todo_init() +{ + todofdout = 7; + todofdin = 8; + flagtodoalive = 1; + /* sync with external todo */ + if (write(todofdout, "S", 1) != 1) tododied(); + + return; +} + +void todo_selprep(nfds,rfds,wakeup) +int *nfds; +fd_set *rfds; +datetime_sec *wakeup; +{ + if (flagexitasap) { + if (flagtodoalive) { + write(todofdout, "X", 1); + } + } + if (flagtodoalive) { + FD_SET(todofdin,rfds); + if (*nfds <= todofdin) + *nfds = todofdin + 1; + } +} + +void todo_del(char* s) +{ + int flagchan[CHANNELS]; + struct prioq_elt pe; + unsigned long id; + unsigned int len; + int c; + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + + for (c = 0;c < CHANNELS;++c) + { + if (!*s) + { + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + switch(*s++) { + case '0': + flagchan[c] = 0; + break; + case '1': + flagchan[c] = 1; + break; + default: + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + } + + len = scan_ulong(s,&id); + if (!len || s[len]) { + log1("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + pe.id = id; pe.dt = now(); + for (c = 0;c < CHANNELS;++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pe)) nomem(); + + for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; + if (c == CHANNELS) + while (!prioq_insert(&pqdone,&pe)) nomem(); + + return; +} + +void todo_do(rfds) +fd_set *rfds; +{ + int r; + char ch; + int i; + + if (!flagtodoalive) return; + if (!FD_ISSET(todofdin,rfds)) return; + + r = read(todofdin,todobuf,sizeof(todobuf)); + if (r == -1) return; + if (r == 0) { + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + return; + } + for (i = 0;i < r;++i) { + ch = todobuf[i]; + while (!stralloc_append(&todoline,&ch)) nomem(); + if (todoline.len > REPORTMAX) + todoline.len = REPORTMAX; + /* qmail-todo is responsible for keeping it short */ + if (!ch && (todoline.len > 1)) { + switch (todoline.s[0]) { + case 'D': + if (flagexitasap) break; + todo_del(todoline.s + 1); + break; + case 'L': + log1(todoline.s + 1); + break; + case 'X': + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + break; + default: + log1("warning: qmail-send unable to understand qmail-todo: report mangled\n"); + break; + } + todoline.len = 0; + } + } +} + +#endif /* this file is too long ---------------------------------------------- MAIN */ -int getcontrols() { if (control_init() == -1) return 0; +int getcontrols() { + int c; + int ck = 0; + + if (control_init() == -1) return 0; + if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0; if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; + + for (c = 2,ck = 0;c < CHANNELS;++c) + { + strnum2[fmt_uint(strnum2,ck++)] = 0; + if (!stralloc_copys(&fname,"control/concurrencysuppl")) return 0; + if (!stralloc_cats(&fname,strnum2)) return 0; + if (!stralloc_0(&fname)) return 0; + if (control_readint(&concurrency[c],fname.s) == -1) return 0; + } + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0; if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0; if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0; + if (control_readline(&srs_domain,"control/srs_domain") == -1) return 0; + if (srs_domain.len && !stralloc_0(&srs_domain)) return 0; if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0; if (!stralloc_cats(&doublebounceto,"@")) return 0; if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0; @@ -1467,6 +1669,21 @@ case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; } + + for (c = 0;c < SUPPL_CHANNELS;++c) + { + strnum2[fmt_uint(strnum2,c)] = 0; + if (!stralloc_copys(&fname,"control/suppls")) return 0; + if (!stralloc_cats(&fname,strnum2)) return 0; + if (!stralloc_0(&fname)) return 0; + switch (control_readfile(&suppls[c],fname.s,0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mapsuppl[c],"",0,0)) return 0; break; + case 1: if (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) return 0; break; + } + } + return 1; } stralloc newlocals = {0}; @@ -1475,9 +1692,33 @@ void regetcontrols() { int r; + int c; + int ck = 0; + + if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) + { log1("alert: unable to reread control/concurrencylocal\n"); return; } + if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) + { log1("alert: unable to reread control/concurrencyremote\n"); return; } + + for (c = 2,ck = 0;c < CHANNELS;++c) + { + strnum2[fmt_uint(strnum2,ck++)] = 0; + if (!stralloc_copys(&fname,"control/concurrencysuppl")) + { log3("alert: unable to reread ",fname.s,"\n"); return; } + if (!stralloc_cats(&fname,strnum2)) + { log3("alert: unable to reread ",fname.s,"\n"); return; } + if (!stralloc_0(&fname)) + { log3("alert: unable to reread ",fname.s,"\n"); return; } + if (control_readint(&concurrency[c],fname.s) == -1) + { log3("alert: unable to reread ",fname.s,"\n"); return; } + } if (control_readfile(&newlocals,"control/locals",1) != 1) { log1("alert: unable to reread control/locals\n"); return; } + if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) + { log1("alert: unable to reread control/concurrencylocal\n",0); return; } + if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) + { log1("alert: unable to reread control/concurrencyremote\n",0); return; } r = control_readfile(&newvdoms,"control/virtualdomains",0); if (r == -1) { log1("alert: unable to reread control/virtualdomains\n"); return; } @@ -1495,6 +1736,28 @@ } else while (!constmap_init(&mapvdoms,"",0,1)) nomem(); + + for (c = 0;c < SUPPL_CHANNELS;++c) + { + strnum2[fmt_uint(strnum2,c)] = 0; + if (!stralloc_copys(&fname,"control/suppls")) nomem(); + if (!stralloc_cats(&fname,strnum2)) nomem(); + if (!stralloc_0(&fname)) nomem(); + r = control_readfile(&newsuppls[c],fname.s,0); + if (r == -1) + { log3("alert: qmail-todo: unable to reread ", fname.s, "\n"); return; } + + constmap_free(&mapsuppl[c]); + + if (r) + { + while (!stralloc_copy(&suppls[c],&newsuppls[c])) nomem(); + while (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) nomem(); + } + else + while (!constmap_init(&mapsuppl[c],"",0,0)) nomem(); + } + } void reread() @@ -1504,6 +1767,9 @@ log1("alert: unable to reread controls: unable to switch to home directory\n"); return; } +#ifdef EXTERNAL_TODO + write(todofdout, "H", 1); +#endif regetcontrols(); while (chdir("queue") == -1) { @@ -1512,6 +1778,104 @@ } } + +static int static_i = 0; +static int static_j = 0; +static void channels_init(void) +{ + chanaddr[0] = "local/"; + chanaddr[1] = "remote/"; + for (static_i=2,static_j=0;static_i u) concurrency[c] = u; numjobs += concurrency[c]; } @@ -1568,7 +1940,11 @@ todo_init(); cleanup_init(); +#ifdef EXTERNAL_TODO + while (!flagexitasap || !del_canexit() || flagtodoalive) +#else while (!flagexitasap || !del_canexit()) +#endif { recent = now(); diff -ruN ../netqmail-1.06-original/qmail-showctl.c netqmail-1.06/qmail-showctl.c --- ../netqmail-1.06-original/qmail-showctl.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-showctl.c 2019-02-27 20:57:13.401024948 +0100 @@ -15,6 +15,7 @@ #include "auto_patrn.h" #include "auto_spawn.h" #include "auto_split.h" +#include "spf.h" stralloc me = {0}; int meok; @@ -112,7 +113,7 @@ substdio_puts(subfdout,"\n"); substdio_puts(subfdout,fn); substdio_puts(subfdout,": "); - switch(control_readfile(&line,fn)) { + switch(control_readfile(&line,fn, 0)) { case 0: substdio_puts(subfdout,"(Default.) "); substdio_puts(subfdout,def); @@ -214,14 +215,22 @@ _exit(111); } - do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM."); + do_lst("authsenders","No authenticated SMTP sender.","Authenicated SMTP sender: ",""); + do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern."); + do_lst("badhelonorelay","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern and RELAYCLIENT is not set."); + do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern."); + do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set."); + do_lst("badmailto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern."); + do_lst("badmailtonorelay","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern and RELAYCLIENT is not set."); do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); do_str("bouncehost",1,"bouncehost","Bounce host name is "); + do_int("brtlimit","0","The brtlimit is ",""); do_int("concurrencylocal","10","Local concurrency is ",""); do_int("concurrencyremote","20","Remote concurrency is ",""); do_int("databytes","0","SMTP DATA limit is "," bytes"); do_str("defaultdomain",1,"defaultdomain","Default domain name is "); do_str("defaulthost",1,"defaulthost","Default host name is "); + do_lst("dnsbllist","No dnsbl list configured.","List at "," configured for dnsbl check."); do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: "); do_str("doublebounceto",0,"postmaster","2B recipient user: "); do_str("envnoathost",1,"envnoathost","Presumed domain name is "); @@ -230,6 +239,9 @@ do_str("localiphost",1,"localiphost","Local IP address becomes "); do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally."); do_str("me",0,"undefined! Uh-oh","My name is "); + do_lst("moreipme","No additional IP addresses are me.","IP address "," is me."); + do_lst("notipme","All of my IP addresses are me.","IP address "," is not me."); + do_str("outgoingip",0,"0.0.0.0","Outgoing IP address is "); do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@","."); do_str("plusdomain",1,"plusdomain","Plus domain name is "); do_lst("qmqpservers","No QMQP servers.","QMQP server: ","."); @@ -257,6 +269,15 @@ 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("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 "); + do_str("spfguess",0,"","The guess SPF rules are: "); + do_str("spfrules",0,"","The local SPF rules are: "); + do_str("srs_domain",0,"","SRS domain name is "); + do_lst("srs_secrets","No secrets","",""); + do_int("srs_maxage","21","SRS maxage is ",""); + do_int("srs_hashlength","4","SRS hashlength is ",""); + do_int("srs_hashmin","4","SRS hashmin is ",""); 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"); @@ -265,9 +286,12 @@ while (d = readdir(dir)) { if (str_equal(d->d_name,".")) continue; if (str_equal(d->d_name,"..")) continue; - if (str_equal(d->d_name,"bouncefrom")) continue; - if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"authsenders")) continue; if (str_equal(d->d_name,"badmailfrom")) continue; + if (str_equal(d->d_name,"badhelo")) continue; + if (str_equal(d->d_name,"badmailfromnorelay")) continue; + if (str_equal(d->d_name,"badmailto")) continue; + if (str_equal(d->d_name,"badmailtonorelay")) continue; if (str_equal(d->d_name,"bouncefrom")) continue; if (str_equal(d->d_name,"bouncehost")) continue; if (str_equal(d->d_name,"concurrencylocal")) continue; @@ -275,6 +299,7 @@ if (str_equal(d->d_name,"databytes")) continue; if (str_equal(d->d_name,"defaultdomain")) continue; if (str_equal(d->d_name,"defaulthost")) continue; + if (str_equal(d->d_name,"dnsbllist")) continue; if (str_equal(d->d_name,"doublebouncehost")) continue; if (str_equal(d->d_name,"doublebounceto")) continue; if (str_equal(d->d_name,"envnoathost")) continue; @@ -283,8 +308,10 @@ if (str_equal(d->d_name,"localiphost")) continue; if (str_equal(d->d_name,"locals")) continue; if (str_equal(d->d_name,"me")) continue; + if (str_equal(d->d_name,"moreipme")) continue; if (str_equal(d->d_name,"morercpthosts")) continue; if (str_equal(d->d_name,"morercpthosts.cdb")) continue; + if (str_equal(d->d_name,"notipme")) continue; if (str_equal(d->d_name,"percenthack")) continue; if (str_equal(d->d_name,"plusdomain")) continue; if (str_equal(d->d_name,"qmqpservers")) continue; @@ -292,6 +319,15 @@ 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,"srs_domain")) continue; + if (str_equal(d->d_name,"srs_secrets")) continue; + if (str_equal(d->d_name,"srs_maxage")) continue; + if (str_equal(d->d_name,"srs_hashlength")) continue; + if (str_equal(d->d_name,"srs_hashmin")) 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 ../netqmail-1.06-original/qmail-smtpd.8 netqmail-1.06/qmail-smtpd.8 --- ../netqmail-1.06-original/qmail-smtpd.8 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-smtpd.8 2019-02-27 20:57:13.402024937 +0100 @@ -14,6 +14,15 @@ see .BR tcp-environ(5) . +If the environment variable +.B SMTPS +is non-empty, +.B qmail-smtpd +starts a TLS session (to support the deprecated SMTPS protocol, +normally on port 465). Otherwise, +.B qmail-smtpd +offers the STARTTLS extension to ESMTP. + .B qmail-smtpd is responsible for counting hops. It rejects any message with 100 or more @@ -23,7 +32,30 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options. +.B qmail-smtpd +includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements. +.B qmail-smtpd +can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes +.IR checkprogram , +which reads on file descriptor 3 the username, a 0 byte, the password +or CRAM-MD5 digest/response derived from the SMTP client, +another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type), +and a final 0 byte. +.I checkprogram +invokes +.I subprogram +upon successful authentication, which should in turn return 0 to +.BR qmail-smtpd , +effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO +(any supplied value replaced with the authenticated username). +.B qmail-smtpd +will reject the authentication attempt if it receives a nonzero return +value from +.I checkprogram +or +.IR subprogram . + .SH TRANSPARENCY .B qmail-smtpd converts the SMTP newline convention into the UNIX newline convention @@ -37,11 +69,34 @@ even though such messages violate the SMTP protocol. .SH "CONTROL FILES" .TP 5 +.I badhelo +Unacceptable HELO/EHLO host names. +.B qmail-smtpd +will reject every recipient address for a message if +the host name is listed in, +or matches a POSIX regular expression pattern listed in, +.IR badhelo . +If the +.B NOBADHELO +environment variable is set, then the contents of +.IR badhelo +will be ignored. +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badhelonorelay +Functions the same as the +.IR badhelo +control file but is read only if the +.B RELAYCLIENT +environment variable is not set. +For more information, please have a look at doc/README.qregex. +.TP 5 .I badmailfrom Unacceptable envelope sender addresses. .B qmail-smtpd will reject every recipient address for a message -if the envelope sender address is listed in +if the envelope sender address is listed in, , or matches a POSIX regular expression +pattern listed in, .IR badmailfrom . A line in .I badmailfrom @@ -49,6 +104,61 @@ .BR @\fIhost , meaning every address at .IR host . +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badmailfromnorelay +Functions the same as the +.IR badmailfrom +control file but is read only if the +.B RELAYCLIENT +environment variable is not set. +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badmailtonorelay +Functions the same as the +.IR badmailto +control file but is read only if the +.B RELAYCLIENT +environment variable is not set. +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badrcptto +Unacceptable envelope recipient addresses. +.B qmail-smtpd +will reject every recipient address for a message if the recipient address +is listed in, +or matches a POSIX regular expression pattern listed in, +.IR badrcptto . +For more information, please have a look at doc/README.qregex. + +.TP 5 +.I brtlimit +Number of bad recipients before closing the transmission channel. +.B qmail-smtpd +will close the transmission channel after +reaching the number of bad recipients in +.IR brtlimit . +Both badrcptto, chkuser and validrcptto failures are counted. + +If the environment variable +.B BRTLIMIT +is set, it overrides +.IR brtlimit . + +Default and minimum: 0. + +.TP 5 +.I clientca.pem +A list of Certifying Authority (CA) certificates that are used to verify +the client-presented certificates during a TLS-encrypted session. + +.TP 5 +.I clientcrl.pem +A list of Certificate Revocation Lists (CRLs). If present it +should contain the CRLs of the CAs in +.I clientca.pem +and client certs will be checked for revocation. + .TP 5 .I databytes Maximum number of bytes allowed in a message, @@ -76,6 +186,34 @@ .B DATABYTES is set, it overrides .IR databytes . + +.TP 5 +.I dh2048.pem +If these 2048 bit DH parameters are provided, +.B qmail-smtpd +will use them for TLS sessions instead of generating one on-the-fly +(which is very timeconsuming). + +.TP 5 +.I dnsbllist +A list of dnsbl providers that +.B qmail-smtpd +checks to identify blacklisted ip addresses. + +Exception: +If the environment variable +.B DNSBLSKIP +is set, +.B qmail-smtpd +ignores +.IR dnsbllist , +and the dnsbl check is not performed. +The check is skipped even if some other authentication method succedeed +and authorized the client to relay (smtp-auth or tls client certificate), +or if +.B RELAYCLIENT +enviromnent variable is set. + .TP 5 .I localiphost Replacement host name for local IP addresses. @@ -151,6 +289,19 @@ Envelope recipient addresses without @ signs are always allowed through. + +.TP 5 +.I rsa2048.pem +If this 2048 bit RSA key is provided, +.B qmail-smtpd +will use it for TLS sessions instead of generating one on-the-fly. + +.TP 5 +.I servercert.pem +SSL certificate to be presented to clients in TLS-encrypted sessions. +Should contain both the certificate and the private key. Certifying Authority +(CA) and intermediate certificates can be added at the end of the file. + .TP 5 .I smtpgreeting SMTP greeting message. @@ -169,6 +320,119 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. + +.SH "ENVIRONMENT VARIABLES READ" +Environment variables may be defined globally in the +.B qmail-smtpd +startup script and/or individually as part of the +.B tcpserver's +cdb database. +The environment variables may be quoted ("variable", or 'variable') and +in case of global use, have to be exported. +.B qmail-smtpd +supports the following legacy environment variables, typically +provided by +.B tcpserver +or +.B sslserver +or +.BR tcp-env : +.IR TCPREMOTEIP , +.IR TCPREMOTEHOST +.IR TCPREMOTEINFO +and +.IR TCPLOCALPORT +as well as +.IR RELAYCLIENT . + +.B qmail-smtpd +may use the following environment variables for SMTP authentication: +.TP 5 +.IR SMTPAUTH +is used to enable SMTP Authentication for the AUTH types +LOGIN and PLAIN. +In case +.TP 5 +.IR SMTPAUTH='+cram' +is defined, +.B qmail-smtpd +honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication. +Simply +.TP 5 +.IR SMTPAUTH='cram' +restricts authentication just to CRAM-MD5. +If however +.TP 5 +.IR SMTPAUTH='!' +starts with an exclamation mark, AUTH is required. +You can enforce 'Submission' using this option +and binding +.B qmail-smtpd +to the SUBMISSION port \'587'\. +In particular, +.TP 5 +.IR SMTPAUTH='!cram' +may be useful. +In opposite, if +.TP 5 +.IR SMTPAUTH='-' +starts with a dash, AUTH is disabled for particular +connections. + +Note: The use of 'cram' requires a CRAM-MD5 enabled PAM. + +.TP 5 +.I tlsclients +A list of email addresses. When relay rules would reject an incoming message, +.B qmail-smtpd +can allow it if the client presents a certificate that can be verified against +the CA list in +.I clientca.pem +and the certificate email address is in +.IR tlsclients . + +.TP 5 +.I tlsserverciphers +A set of OpenSSL cipher strings. Multiple ciphers contained in a +string should be separated by a colon. If the environment variable +.B TLSCIPHERS +is set to such a string, it takes precedence. + +.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), diff -ruN ../netqmail-1.06-original/qmail-smtpd.c netqmail-1.06/qmail-smtpd.c --- ../netqmail-1.06-original/qmail-smtpd.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/qmail-smtpd.c 2020-04-25 15:15:28.957053097 +0200 @@ -12,6 +12,9 @@ #include "ip.h" #include "qmail.h" #include "str.h" +#include "strerr.h" +#include "qregex.h" +#include "cdb.h" #include "fmt.h" #include "scan.h" #include "byte.h" @@ -23,44 +26,180 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "dns.h" +#include "wait.h" + +/* chkuser.h will check if TLS_H is defined, so this has to come before chkuser.h */ +#ifdef TLS +#include +#include "tls.h" +#include "ssl_timeoutio.h" +void tls_init(); +int tls_verify(); +void tls_nogateway(); +int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ +int forcetls = 1; +#endif + +/* start chkuser code */ +#include "chkuser.h" +/* end chkuser code */ +#include "spf.h" +/* rbl: start */ +#include "strsalloc.h" +/* rbl: end */ + +#define AUTHSLEEP 5 #define MAXHOPS 100 + +#define BMCHECK_BMF 0 +#define BMCHECK_BMFNR 1 +#define BMCHECK_BMT 2 +#define BMCHECK_BMTNR 3 +#define BMCHECK_BHELO 4 +#define BMCHECK_BHELONR 5 + +static char strnum[FMT_ULONG]; unsigned int databytes = 0; +unsigned int greetdelay = 0; +unsigned int drop_pre_greet = 0; int timeout = 1200; +int maxrcpt = -1; +unsigned int spfbehavior = 0; + +/* rejectrelaytest: start */ +unsigned int rejectrelaytest = 0; +/* rejecrelayttest: end */ +/* rejectnullsenders: start */ +unsigned int rejnsmf = 0; +/* rejectnullsenders: end */ + +static const char *protocol = "SMTP"; + +/* spf ipv6 fix */ +char *remoteip4; +/* end spf ipv6 fix */ int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + if (ssl && fd == ssl_wfd) + r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); + else +#endif r = timeoutwrite(timeout,fd,buf,len); if (r <= 0) _exit(1); return r; } char ssoutbuf[512]; +char sslogbuf[512]; substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); +substdio sslog = SUBSTDIO_FDBUF(safewrite,2,sslogbuf,sizeof sslogbuf); +char sserrbuf[512]; +substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf,sizeof sserrbuf); + +int addrinrcpthosts = 0; +int envelopepos = 0; // 1: ehlo/helo, 2: mailfrom, 3: rcptto: 4: data +void qsmtpdlog(const char *head, const char *result, const char *reason, const char *detail, const char *statuscode); +void qlogenvelope(char *result, char *reason, char *detail, char *statuscode) { qsmtpdlog("qlogenvelope",result,reason,detail,statuscode); } +void qlogreceived(char *result, char *reason, char *detail, char *statuscode) { qsmtpdlog("qlogreceived",result,reason,detail,statuscode); } +void logit(const char* message); +void logit2(const char* message, const char* reason); void flush() { substdio_flush(&ssout); } void out(s) char *s; { substdio_puts(&ssout,s); } -void die_read() { _exit(1); } -void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } -void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } -void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } -void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } -void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } +void die_read(char *reason) { logit2("read failed", reason); flush(); _exit(1); } +void die_alarm() { qlogenvelope("rejected","alarmtimeout","","451"); logit("timeout"); out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } +void die_nomem() { qlogenvelope("rejected","outofmemory","","421"); out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } +void die_control() { qlogenvelope("rejected","cannotreadcontrols","","421"); logit("unable to read controls"); out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } +void die_ipme() { qlogenvelope("rejected","unknownipme","","553"); logit("unable to figure out my IP addresses"); out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } +/* rbl: start */ +/* +void die_dnsbl(arg) +char *arg; +{ + out("421 your ip is currently blacklisted, try to auth first ("); out(arg); out(")\r\n"); + logit2("message rejected (qmail-dnsbl)", arg); + flush(); + _exit(1); +} +*/ +/* rbl: end */ +void err_maxrcpt() +{ + out("553 max rcpt limit exceeded (#5.7.1)\r\n"); + logit("max rcpt limit exceeded (qmail-maxrcpt)"); + qlogenvelope("rejected","maxrcpt","","553"); + flush(); +} +void straynewline() { qlogenvelope("rejected","badnewlines","","451"); logit("bad newlines"); out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } +void die_pre_greet() { qlogenvelope("rejected","pregreet","","554"); out("554 SMTP protocol violation\r\n"); flush(); _exit(1); } -void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } -void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +void err_size() { qlogreceived("rejected","size","","552"); out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } +#ifndef TLS +void err_nogateway() { qlogenvelope("rejected","notinrcpthosts","","553"); out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +#else +void err_nogateway() +{ + qlogenvelope("rejected","notinrcpthosts","","553"); out("553 sorry, that domain isn't in my list of allowed rcpthosts"); + tls_nogateway(); + out(" (#5.7.1)\r\n"); +} +#endif void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } +void err_unrecog() { out("500 unrecognised (#5.5.2)\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(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"); } +void err_qqt() { qlogenvelope("rejected","qqtfailure","","451"); out("451 qqt failure (#4.3.0)\r\n"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } +int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } +void err_authfail() { qlogenvelope("rejected","authfailed","","535"); out("535 authentication failed (#5.7.1)\r\n"); } +void err_submission() { qlogenvelope("rejected","authrequired","","530"); out("530 Authorization required (#5.7.1) \r\n"); } +void err_vrt() { qlogenvelope("rejected","validrcptto","","553"); out("553 sorry, this recipient is not in my validrcptto list (#5.7.1)\r\n"); } +void die_brtlimit() { qlogenvelope("rejected","brtlimit","","421"); out("421 too many invalid addresses, goodbye (#4.3.0)\r\n"); flush(); _exit(1); } +void err_rcpt() { qlogenvelope("rejected","nomailbox","","550"); out("550 sorry, no mailbox here by that name (#5.1.1)\r\n"); } +/* rcptcheck: start */ +void die_fork() { qlogenvelope("rejected","rcptcheck","cannotfork","421"); out("421 unable to fork (#4.3.0)\r\n"); flush(); _exit(1); } +void die_rcpt() { qlogenvelope("rejected","rcptcheck","cannotverify","421"); out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1); } +void die_rcpt2() { qlogenvelope("rejected","rcptcheck","cannotexecute","421"); out("421 unable to execute recipient check (#4.3.0)\r\n"); flush(); _exit(1); } +/* rcptcheck: end */ +/* qregex: start */ +/* +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_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } +void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); } +/* qregex: end */ +/* rejectnullsenders: start */ +void die_nullsender() { qlogenvelope("rejected","nullsenderdenied","","421"); out("421 null senders temporarily denied (#4.3.0)\r\n"); flush(); _exit(1); } +/* rejectnullsenders: end */ +/* rejectrelaytest: start */ +void err_relay() { qlogenvelope("rejected","dontrelay","","553"); out("553 we don't relay (#5.7.1)\r\n"); } +/* rejectrelaytest: end */ +/* authtlsvariables: start */ +void err_authmismatch() { qlogenvelope("rejected","authnotmailfrom","","503"); out("503 from and auth not the same (#5.5.1)\r\n"); } +/* authtlsvariables: end */ stralloc greeting = {0}; +stralloc spflocal = {0}; +stralloc spfguess = {0}; +stralloc spfexp = {0}; void smtp_greet(code) char *code; { @@ -76,11 +215,33 @@ smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } +/* char *protocol; */ char *remoteip; char *remotehost; char *remoteinfo; char *local; +char *localport; +char *submission; char *relayclient; +char *dnsblskip; +char *auth; +/* authtlsvariables: start */ +int flagtls = 0; +int forceauthmailfrom = 0; +int disabletls = 0; +/* authtlsvariables: end */ + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; /* input from SMTP client */ +static stralloc user = {0}; /* authorization user-id */ +static stralloc pass = {0}; /* plain passwd or digest */ +static stralloc resp = {0}; /* b64 response */ +static stralloc chal = {0}; /* plain challenge */ +static stralloc slop = {0}; /* b64 challenge */ + +char **childargs; +char ssauthbuf[512]; +substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -91,11 +252,101 @@ fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } +int smtpauth = 0; int liphostok = 0; stralloc liphost = {0}; int bmfok = 0; stralloc bmf = {0}; -struct constmap mapbmf; + +/* rbl: start */ +int flagrbldns = 0; +int flagrbldelay = 0; +int flagrblfailclosed = 0; +int flagmustnotbounce = 0; +static stralloc rbldnslist = {0}; +static stralloc rblhost = {0}; +int rblhosterror = 0; +int rbllistok = 0; +int rblok = 0; +char *ip_env; +static stralloc ip_reverse; +int rbldecision = 0; /* 0 undecided, 1 accept, 2 reject (451), 3 bounce (553) */ +static stralloc rbltext = {0}; /* defined if rbldecision is 2 or 3 */ +static stralloc rblmessage = {0}; +static stralloc rblserver = {0}; + +void err_rblreject() { + if (env_get("RBLSMTPD")) { + qlogenvelope("rejected","rblreject","rblsmtpd","553"); + } + else { + if (rblserver.len) qlogenvelope("rejected","rblreject",rblserver.s,"553"); + else qlogenvelope("rejected","rblreject","","553"); + } + substdio_put(&ssout,rblmessage.s,rblmessage.len); + flush(); +} + +void die_rbldelay() { + if (env_get("RBLSMTPD")) { + qlogenvelope("rejected","rbldelay","rblsmtpd","451"); + } + else { + if (rblserver.len) qlogenvelope("rejected","rbldelay",rblserver.s,"451"); + else qlogenvelope("rejected","rbldelay","","451"); + } + substdio_put(&ssout,rblmessage.s,rblmessage.len); flush(); + _exit(1); +} +/* rbl: end */ + +/* qregex: start */ +/* + struct constmap mapbmf; +*/ +int bmfnrok = 0; +stralloc bmfnr = {0}; + +int bmtok = 0; +stralloc bmt = {0}; + +int bmtnrok = 0; +stralloc bmtnr = {0}; + +int bhelook = 0; +stralloc bhelo = {0}; + +int bhelonrok = 0; +stralloc bhelonr = {0}; + +int logregex = 0; +stralloc matchedregex = {0}; +/* qregex: end */ + +/* validrcptto.cdb: start */ +int vrtok = 0; +stralloc vrt = {0}; +struct constmap mapvrt; + +int vrtfd = -1; +int vrtcount = 0; +int vrtlog_do = 0; + +stralloc title = {0}; +char pid_buf[FMT_ULONG]; +/* validrcptto.cdb: end */ + +/* realbadrcpt: start */ +int brtlimit = 0; +static char strnumpid[FMT_ULONG]; +static char strnumqp[FMT_ULONG]; +/* realbadrcpt: end */ + +/* rcptcheck: start */ +static char *rcptcheck[2] = { 0, 0 }; +char rcptcheck_err[1024]; +int rcptcheckrelayclient = 0; +/* rcptcheck: end */ void setup() { @@ -109,21 +360,110 @@ if (liphostok == -1) die_control(); if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); if (timeout <= 0) timeout = 1; + if (control_readint(&maxrcpt,"control/maxrcpt") == -1) die_control(); if (rcpthosts_init() == -1) die_control(); bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); +/* qregex: start */ +/* if (bmfok) if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); +*/ + strnumpid[fmt_uint(strnumpid,(unsigned int) getpid())] = 0; + + bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0); + if (bmfnrok == -1) die_control(); + + bmtok = control_readfile(&bmt,"control/badrcptto",0); + if (bmtok == -1) die_control(); + + bmtnrok = control_readfile(&bmtnr,"control/badrcpttonorelay",0); + if (bmtnrok == -1) die_control(); + + bhelook = control_readfile(&bhelo, "control/badhelo",0); + if (bhelook == -1) die_control(); + + bhelonrok = control_readfile(&bhelonr, "control/badhelonorelay",0); + if (bhelonrok == -1) die_control(); + + if (env_get("LOGREGEX")) logregex = 1; +/* qregex: end */ + +/* validrcptto.cdb: start */ + x = env_get("VALIDRCPTTO"); + if (x) { vrtok = control_readfile(&vrt,x,0); } + else vrtok = control_readfile(&vrt,"control/validrcptto",0); + if (vrtok == -1) die_control(); + if (vrtok) + if (!constmap_init(&mapvrt,vrt.s,vrt.len,0)) die_nomem(); + + x = env_get("MOREVALIDRCPTTO_CDB"); + if (x) { vrtfd = open_read(x); } + else vrtfd = open_read("control/morevalidrcptto.cdb"); + if (-1 == vrtfd) if (errno != error_noent) die_control(); + + x = env_get("VALIDRCPTTO_LOG"); + if(x) { scan_ulong(x,&u); vrtlog_do = (int) u; } +/* validrcptto.cdb: end */ + +/* realbadrcpt: start */ + if (control_readint(&brtlimit,"control/brtlimit") == -1) die_control(); + x = env_get("BRTLIMIT"); + if (x) { scan_ulong(x,&u); brtlimit = u; }; + /* Disable limits check, defaults to 0 */ +/* if (brtlimit <= 1) brtlimit = 2; */ +/* realbadrcpt: end */ + +/* rcptcheck: start */ + rcptcheck[0] = env_get("RCPTCHECK"); + + x = env_get("RCPTCHECKRELAYCLIENT"); + if (x) { scan_ulong(x,&u); rcptcheckrelayclient = u; }; +/* rcptcheck: end */ + +/* rejectrelaytest: start */ + if (control_readint(&rejectrelaytest,"control/rejectrelaytest") == -1) die_control(); +/* rejectrelaytest: end */ + +/* rejectnullsenders: start */ + if (control_readint(&rejnsmf,"control/rejectnullsenders") == -1) die_control(); +/* rejectnullsenders: end */ 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; - + + x = env_get("SMTPD_GREETDELAY"); + if (x) { scan_ulong(x, &u); greetdelay = u; } + x = env_get("DROP_PRE_GREET"); + if (x) { scan_ulong(x, &u); drop_pre_greet = u; } + + protocol = "SMTP"; + + 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(); + + /* spf ipv6 fix */ + if (!(remoteip4 = env_get("TCPREMOTEIP"))) + remoteip4 = "unknown"; + /* end spf ipv6 fix */ remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; + localport = env_get("TCPLOCALPORT"); + if (!localport) localport = "0"; local = env_get("TCPLOCALHOST"); if (!local) local = env_get("TCPLOCALIP"); if (!local) local = "unknown"; @@ -131,10 +471,67 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + dnsblskip = env_get("DNSBLSKIP"); +/* rbl: start */ + x = env_get("DNSBLLIST"); + if (x) { + rbllistok = control_readfile(&rbldnslist,x,0); + if (rbllistok == -1) die_control(); + if (rbllistok) rblok = 1; + } + else { + rbllistok = control_readfile(&rbldnslist,"control/dnsbllist",0); + if (rbllistok == -1) die_control(); + if (rbllistok) rblok = 1; + } + + /* from rblsmtpd.c, if RBLSMTPD is defined and empty then accept mail, if defined and string begins with '-' then + block mail using error code 553 + string without hyphen, else (if defined, not null and not beginning with '-') + reject mail using error code 451 + string */ + x = env_get("RBLSMTPD"); + if (x) { + if (!*x) + rbldecision = 1; + else if (*x == '-') { + if (!stralloc_copys(&rbltext,x + 1)) die_nomem(); + rbldecision = 3; + } + else { + if (!stralloc_copys(&rbltext,x)) die_nomem(); + rbldecision = 2; + } + rblok = 1; + } + + if (control_readint(&flagrblfailclosed,"control/dnsblfailclosed") == -1) die_control(); + x = env_get("DNSBLFAILCLOSED"); + if (x) { scan_ulong(x,&u); flagrblfailclosed = u; } +/* rbl: end */ + auth = env_get("SMTPAUTH"); + if (auth) { + smtpauth = 1; + case_lowers(auth); + if (!case_diffs(auth,"-") || !case_diffs(auth,"0")) smtpauth = 0; + if (!case_diffs(auth,"!")) smtpauth = 11; + if (case_starts(auth,"cram")) smtpauth = 2; + if (case_starts(auth,"+cram")) smtpauth = 3; + if (case_starts(auth,"!cram")) smtpauth = 12; + if (case_starts(auth,"!+cram")) smtpauth = 13; + } +/* authtlsvariables: start */ + x = env_get("FORCEAUTHMAILFROM"); if (x) if (!str_diff(x,"1")) { forceauthmailfrom = 1; } + #ifdef TLS + x = env_get("DISABLETLS"); if (x) if (!str_diff(x,"1")) { disabletls = 1; } + #endif +/* authtlsvariables: end */ + #ifdef TLS + x = env_get("FORCETLS"); if (x) if (!str_diff(x, "0")) forcetls = 0; + if (env_get("SMTPS")) { smtps = 1; tls_init(); } + else + #endif dohelo(remotehost); } - stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ int addrparse(arg) @@ -155,6 +552,7 @@ terminator = ' '; arg += str_chr(arg,':'); if (*arg == ':') ++arg; + if (*arg == '\0') return 0; while (*arg == ' ') ++arg; } @@ -197,6 +595,8 @@ return 1; } +/* qregex: start */ +/* int bmfcheck() { int j; @@ -207,76 +607,1108 @@ if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; return 0; } +*/ +int bmcheck(which) int which; +{ + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + static stralloc bmb = {0}; + static stralloc curregex = {0}; + + if (which == BMCHECK_BMF) { + if (!stralloc_copy(&bmb,&bmf)) die_nomem(); + } else if (which == BMCHECK_BMFNR) { + if (!stralloc_copy(&bmb,&bmfnr)) die_nomem(); + } else if (which == BMCHECK_BMT) { + if (!stralloc_copy(&bmb,&bmt)) die_nomem(); + } else if (which == BMCHECK_BMTNR) { + if (!stralloc_copy(&bmb,&bmtnr)) die_nomem(); + } else if (which == BMCHECK_BHELO) { + if (!stralloc_copy(&bmb,&bhelo)) die_nomem(); + } else if (which == BMCHECK_BHELONR) { + if (!stralloc_copy(&bmb,&bhelonr)) die_nomem(); + } else { + die_control(); + } + + while (j < bmb.len) { + i = j; + while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; + if (bmb.s[j] == '!') { + negate = 1; + j++; + } + if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&curregex)) die_nomem(); + if (which == BMCHECK_BHELO) { + x = matchregex(helohost.s, curregex.s); + } else { + x = matchregex(addr.s, curregex.s); + } + if ((negate) && (x == 0)) { + if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); + return 1; + } + if (!(negate) && (x > 0)) { + if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&matchedregex)) die_nomem(); + return 1; + } + j = i + 1; + negate = 0; + } + return 0; +} +/* qregex: end */ + +/* validrcptto.cdb: start */ +void vrtlog(l,a,b) +int l; +const char *a; +const char *b; +{ +/* if (l <= vrtlog_do) + strerr_warn6(title.s,"validrcptto [",remoteip,"] ",a,b,0);*/ +} + +int vrtcheck() +{ + static char *rcptto = "RCPT TO: "; + static char *trying = "trying: "; + static char *found = "found: "; + static char *reject = "reject: "; + char *f = 0; + int j,k,r; + uint32 dlen; + stralloc laddr = {0}; + + stralloc luser = {0}; + stralloc adom = {0}; + stralloc utry = {0}; + stralloc stnoaddr = {0}; + stralloc stnodom = {0}; + + int atfound, okaddr, okdom, noaddr, nodom; + + /* if both validrcptto and morevalidrcptto.cdb are missing, consider valid the recipient */ + if (!((vrtok) || (vrtfd != -1))) return 1; + + okaddr = 0; okdom = 0; noaddr = 0; nodom = 0; atfound = 0; + + /* lowercase whatever we were sent */ + if (!stralloc_copy(&laddr,&addr)) die_nomem() ; + case_lowerb(laddr.s,laddr.len); + + /* split user/domain parts, create negated stralloc */ + j = byte_rchr(laddr.s,laddr.len,'@'); + if (j < laddr.len) { + atfound = 1; + if (!stralloc_copyb(&luser,laddr.s,j)) die_nomem(); + if (!stralloc_copyb(&adom,laddr.s+j,laddr.len-j-1)) die_nomem(); + + if (!stralloc_copys(&stnodom,"-")) die_nomem(); + if (!stralloc_cat(&stnodom,&adom)) die_nomem(); + if (!stralloc_0(&stnodom)) die_nomem(); + + if (!stralloc_copys(&stnoaddr,"-")) die_nomem(); + if (!stralloc_cat(&stnoaddr,&luser)) die_nomem(); + if (!stralloc_cat(&stnoaddr,&adom)) die_nomem(); + if (!stralloc_0(&stnoaddr)) die_nomem(); + } + /* validrcptto */ + if (vrtok) { + vrtlog(rcptto,laddr.s,0); + if (constmap(&mapvrt,laddr.s,laddr.len - 1)) { okaddr = 1; vrtlog(found,laddr.s,0); } + if (atfound) { + if (constmap(&mapvrt,stnoaddr.s,stnoaddr.len-1)) { noaddr= 1; vrtlog(reject,stnoaddr.s,0); } + if (constmap(&mapvrt,laddr.s+j,laddr.len-j-1)) { okdom = 1; vrtlog(found,laddr.s+j,0); } + if (constmap(&mapvrt,stnodom.s,stnodom.len-1)) { nodom = 1; vrtlog(reject,stnodom.s,0); } + } + } + + /* morevalidrcptto.cdb */ + if ((vrtfd != -1)) { + vrtlog(rcptto,laddr.s,0); + + if (cdb_seek(vrtfd,laddr.s,laddr.len-1,&dlen) > 0) { okaddr = 1; vrtlog(found,laddr.s,0); } + if (atfound) { + if (cdb_seek(vrtfd,stnoaddr.s,stnoaddr.len-1,&dlen) > 0) { noaddr = 1; vrtlog(reject,stnoaddr.s,0); } + if (cdb_seek(vrtfd,laddr.s+j,laddr.len-j-1,&dlen) > 0) { okdom = 1; vrtlog(found,laddr.s+j,0); } + if (cdb_seek(vrtfd,stnodom.s,stnodom.len-1,&dlen) > 0) { nodom = 1; vrtlog(reject,stnodom.s,0); } + } + } + + if (okaddr) return 1; + else if (noaddr) return -1; + else if (okdom) return 1; + else if (nodom) return -1; + else return 0; +} +/* validrcptto.cdb: end */ + +/* rbl: start */ +void rbl(char *base) +{ + int i,j; + int whitelisted = 0; + int altmustbounce = 0; + char *altreply = 0; + strsalloc ssa = {0}; + + if (!str_len(base)) return; + if (!stralloc_copys(&rbltext,"")) die_nomem(); + if (!stralloc_copys(&rblhost,"")) die_nomem(); + if (!stralloc_copys(&rblserver,"")) die_nomem(); + + if (!stralloc_copy(&rblhost,&ip_reverse)) die_nomem(); + i = str_chr(base, ':'); + if (base[i]) { + if (base[i+1] == '-') { /* if reply begins with '-', message must bounce (check rblsmtpd man page) */ + altreply = base+i+2; + altmustbounce = 1; + } + else altreply = base+i+1; + } + + if (base[0] == '+') { /* entries beginning with '+' are for whitelistedlists */ + whitelisted = 1; + if (!stralloc_catb(&rblhost,base+1,i-1)) die_nomem(); + if (!stralloc_catb(&rblserver,base+1,i-1)) die_nomem(); + } + else if (base[0] == '-') { /* force bounce (553 error message), instead of default reject (451) */ + whitelisted = 0; + if (!stralloc_catb(&rblhost,base+1,i-1)) die_nomem(); + if (!stralloc_catb(&rblserver,base+1,i-1)) die_nomem(); + altmustbounce = 1; + } + else { + if (!stralloc_catb(&rblhost,base,i)) die_nomem(); + if (!stralloc_catb(&rblserver,base,i)) die_nomem(); + } + if (!stralloc_0(&rblhost)) die_nomem(); + if (!stralloc_0(&rblserver)) die_nomem(); + + rblhosterror = 0; /* set in case of dns errors */ + + if (altreply) { /* if text response is defined in control file, query A records */ + if (dns_ip(&rbltext,&rblhost) == -1) { + flagmustnotbounce = 1; + rblhosterror = 1; + if (flagrblfailclosed) { + if (!stralloc_copys(&rbltext,"temporary RBL lookup error")) die_nomem(); + if (whitelisted) rbldecision = 1; else rbldecision = 2; + } + return; + } + if (rbltext.len) { + if(!stralloc_copys(&rbltext, "")) die_nomem(); + while(*altreply) { + i = str_chr(altreply, '%'); + if(!stralloc_catb(&rbltext, altreply, i)) die_nomem(); + if(altreply[i] && + altreply[i+1]=='I' && + altreply[i+2]=='P' && + altreply[i+3]=='%') { + if(!stralloc_catb(&rbltext, ip_env, str_len(ip_env))) die_nomem(); + altreply+=i+4; + } else if(altreply[i]) { + if(!stralloc_cats(&rbltext, "%")) die_nomem(); + altreply+=i+1; + } else { + altreply+=i; + } + } + } + } else { /* normal rbl query looks for TXT record */ + if (dns_txt(&ssa,&rblhost) == -1) { /* DNS_SOFT = -1, DNS_HARD = -2, DNS_MEM = -3 */ + flagmustnotbounce = 1; + rblhosterror = 1; + if (flagrblfailclosed) { + if (!stralloc_copys(&rbltext,"temporary RBL lookup error")) die_nomem(); + if (whitelisted) rbldecision = 1; else rbldecision = 2; + } + return; + } + else { + /* in case of multiple records, take only the first */ + if (ssa.len > 0) + if (!stralloc_cat(&rbltext,&ssa.sa[0])) die_nomem(); + /* in case of multiple records, append results to rbltext */ + /*for (j = 0;j < ssa.len;++j) if (!stralloc_cat(&rbltext,&ssa.sa[j])) die_nomem();*/ + } + } + if (rbltext.len) { + if (whitelisted) { + rbldecision = 1; + } + else { + if (altmustbounce) + rbldecision = 3; + else + rbldecision = 2; + } + } + else rbldecision = 0; +} + +int rblcheck() +{ + char *ch; + unsigned int i; + unsigned int j; + stralloc sar = {0}; + + if (rbldecision) return rbldecision; /* rbldecision already set in case of RBLSMTPD or if rblcheck was executed previously */ + if (!rbllistok) return 0; + + ip_env = env_get("TCPREMOTEIP"); + if (!ip_env) ip_env = ""; + if (!stralloc_copys(&ip_reverse,"")) die_nomem(); + + i = str_len(ip_env); + while (i) { + for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break; + if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) die_nomem(); + if (!stralloc_cats(&ip_reverse,".")) die_nomem(); + if (!j) break; + i = j - 1; + } + + ch = rbldnslist.s; + while (ch < (rbldnslist.s + rbldnslist.len)) { + rbl(ch); + /* debug log */ + if (!stralloc_copys(&sar,title.s)) die_nomem(); + if (!stralloc_cats(&sar,"rbl: ip=")) die_nomem(); + if (!stralloc_cats(&sar,ip_env)) die_nomem(); + if (!stralloc_cats(&sar," query=")) die_nomem(); + if (!stralloc_cats(&sar,rblhost.s)) die_nomem(); + if (rblhosterror) { + if (!stralloc_cats(&sar," result=dnserr")) die_nomem(); + } + else { + if (!stralloc_cats(&sar," result=")) die_nomem(); + switch (rbldecision) { + case 0: if (!stralloc_cats(&sar,"ignore")) die_nomem(); break; + case 1: if (!stralloc_cats(&sar,"accept")) die_nomem(); break; + case 2: if (!stralloc_cats(&sar,"delay")) die_nomem(); break; + case 3: if (!stralloc_cats(&sar,"reject")) die_nomem(); break; + } + } + if (!stralloc_cats(&sar," message='")) die_nomem(); + if (!stralloc_catb(&sar,rbltext.s,rbltext.len)) die_nomem(); + if (!stralloc_cats(&sar,"'")) die_nomem(); + if (!stralloc_0(&sar)) die_nomem(); + strerr_warn1(sar.s,0); + /* end debug log */ + if (rbldecision) break; + while (*ch++); + } + return rbldecision; +} +/* rbl: end */ + +int sizelimit(arg) +char *arg; +{ + int i; + long r; + unsigned long sizebytes = 0; + + i = str_chr(arg,'<'); + if (arg[i]) + arg += i + 1; + else { + arg += str_chr(arg,':'); + if (*arg == ':') ++arg; + while (*arg == ' ') ++arg; + } + + arg += str_chr(arg,' '); + if (*arg == ' ') while (*arg == ' ') ++arg; + else return 1; + + i = str_chr(arg,'='); + arg[i] = 0; + if (case_equals(arg,"SIZE")) { + arg += i; + while (*++arg && *arg > 47 && *arg < 58) { + sizebytes *= 10; + sizebytes += *arg - 48; + } + r = databytes - sizebytes; + if (r < 0) return 0; + } + return 1; +} int addrallowed() { int r; r = rcpthosts(addr.s,str_len(addr.s)); if (r == -1) die_control(); +#ifdef TLS + if (r == 0) if (tls_verify()) r = -2; +#endif return r; } +/* rejectrelaytest: start */ +int addrrelay() +{ + if (!rejectrelaytest) { return 0; } + else + { + int j; + j = addr.len; + while(--j >= 0) + if (addr.s[j] == '@') break; + if (j < 0) j = addr.len; + while(--j >= 0) { + if (addr.s[j] == '@') return 1; + if (addr.s[j] == '%') return 1; + if (addr.s[j] == '!') return 1; + } + return 0; + } +} +/* rejectrelaytest: end */ +int seenauth = 0; int seenmail = 0; -int flagbarf; /* defined if seenmail */ +int rcptcount = 0; + +/* qregex: start */ +/* +int flagbarf; +*/ +int flagbarfbmf; /* defined if seenmail */ +int flagbarfbmt; +int flagbarfbhelo; +/* qregex: end */ + +int flagsize; +int flagbarfspf; +stralloc spfbarfmsg = {0}; stralloc mailfrom = {0}; stralloc rcptto = {0}; +stralloc fuser = {0}; +stralloc mfparms = {0}; +stralloc log_buf = {0}; + +/* realbadrcpt: start */ +int flagvrt; /* defined if valid rcpt */ +int brtcount = 0; /* for brtlimit count */ +/* realbadrcpt: end */ + +/* rcptcheck: start */ +int addrvalid() +{ + int pid; + int wstat; + int pierr[2] ; + substdio ss; + char ssbuf[sizeof(rcptcheck_err)]; + int len = 0 ; + char ch; + + if (!rcptcheck[0]) return 1; + if (pipe(pierr) == -1) die_rcpt2(); + + switch(pid = fork()) { + case -1: + close(pierr[0]); + close(pierr[1]); + die_fork(); + case 0: + if (!env_put2("SENDER",mailfrom.s)) die_nomem(); + if (!env_put2("RECIPIENT",addr.s)) die_nomem(); + if (!env_put2("HELO",helohost.s)) die_nomem(); + if (!env_put2("USE_FD4","1")) die_nomem(); + close(1); + dup2(2,1); + close(pierr[0]); + if (fd_move(4,pierr[1]) == -1) die_rcpt2(); + execv(*rcptcheck,rcptcheck); + _exit(120); + } + + close(pierr[1]); + if (wait_pid(&wstat,pid) == -1) die_rcpt2(); + if (wait_crashed(wstat)) die_rcpt2(); + + substdio_fdbuf(&ss,read,pierr[0],ssbuf,sizeof(ssbuf)); + while ( substdio_bget(&ss,&ch,1) && len < (sizeof(ssbuf)-3) ) + rcptcheck_err[len++] = ch; + close(pierr[0]); + + while (len&&((rcptcheck_err[len-1]=='\n')||(rcptcheck_err[len-1]=='\r'))) + len -- ; + if (len) { + rcptcheck_err[len] = '\0'; + strerr_warn3(title.s,"RCPTCHECK error: ",rcptcheck_err,0); + rcptcheck_err[len++] = '\r'; + rcptcheck_err[len++] = '\n'; + } + rcptcheck_err[len] = '\0'; + + switch(wait_exitcode(wstat)) { + case 100: + return 0; + case 111: + die_rcpt(); + case 112: + return 2; // ignore + case 113: + return 3; // overlimit + case 120: + die_rcpt2(); + } + return 1; +} +/* rcptcheck: end */ + +int checkrcptcount() { + if (maxrcpt == -1) {return 0;} + else if (rcptcount > maxrcpt) {return 1;} + else {return 0;} +} + +/* logging patch */ + +void safeloglen(const char* string, const int len) { + if (string && len) { + if (!stralloc_catb(&log_buf, string, len-1)) die_nomem(); + } else { + if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem(); + } +} + +void safelog(const char* string) { + if (string) { + if (!stralloc_cats(&log_buf, string)) die_nomem(); + } else { + if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem(); + } +} + +void logit(const char* message) { + logit2(message, (const char*)0); +} + +void logit2(const char* message, const char* reason) +{ + if (!stralloc_copys(&log_buf, "qmail-smtpd: ")) die_nomem(); + safelog(message); + if (reason) { + if (!stralloc_cats(&log_buf, " (")) die_nomem(); + if (!stralloc_cats(&log_buf, reason)) die_nomem(); + if (!stralloc_cats(&log_buf, ")")) die_nomem(); + } + if (!stralloc_catb(&log_buf, ": ", 2)) die_nomem(); + safeloglen(mailfrom.s, mailfrom.len); + if (!stralloc_catb(&log_buf, " from ", 6)) die_nomem(); + safelog(remoteip); + if (!stralloc_catb(&log_buf, " to ", 4)) die_nomem(); + safeloglen(addr.s, addr.len); + if (!stralloc_catb(&log_buf, " helo ", 6)) die_nomem(); + safeloglen(helohost.s, helohost.len); + if (!stralloc_catb(&log_buf, "\n", 1)) die_nomem(); + substdio_putflush(&sserr, log_buf); +} + +/* end logging patch */ + +int mailfrom_size(arg) char *arg; +{ + long r; + unsigned long sizebytes = 0; + + scan_ulong(arg,&r); + sizebytes = r; + if (databytes) if (sizebytes > databytes) return 1; + return 0; +} + +void mailfrom_auth(arg,len) +char *arg; +int len; +{ + if (!stralloc_copys(&fuser,"")) die_nomem(); + if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } + else + while (len) { + if (*arg == '+') { + if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } + if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } + } + else + if (!stralloc_catb(&fuser,arg,1)) die_nomem(); + arg++; len--; + } + if(!stralloc_0(&fuser)) die_nomem(); + if (!remoteinfo) { + remoteinfo = fuser.s; + if (!env_unset("TCPREMOTEINFO")) die_read("TCPREMOTEINFO"); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + } +} + +void mailfrom_parms(arg) char *arg; +{ + int i; + int len; + + len = str_len(arg); + if (!stralloc_copys(&mfparms,"")) die_nomem(); + i = byte_chr(arg,len,'>'); + if (i > 4 && i < len) { + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); + if (!stralloc_copys(&mfparms,"")) die_nomem(); + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem(); + } + } +} void smtp_helo(arg) char *arg; { + envelopepos = 1; smtp_greet("250 "); out("\r\n"); seenmail = 0; dohelo(arg); + if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); + if ((!flagbarfbhelo) && (bhelonrok) && (!relayclient)) flagbarfbhelo = bmcheck(BMCHECK_BHELONR); } +char size_buf[FMT_ULONG]; +void smtp_size() +{ + size_buf[fmt_ulong(size_buf,(unsigned long) databytes)] = 0; + out("250 SIZE "); out(size_buf); out("\r\n"); +} + +/* ESMTP extensions are published here */ void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + char size[FMT_ULONG]; +#ifdef TLS + struct stat st; +#endif + size[fmt_ulong(size,(unsigned int) databytes)] = 0; + envelopepos = 1; + smtp_greet("250-"); + #ifdef TLS + if (!disabletls && !ssl && (stat("control/servercert.pem",&st) == 0)) + out("\r\n250-STARTTLS"); + #endif + out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); +#ifdef TLS + if (!forcetls || ssl) { +#endif + if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n"); + if (smtpauth == 3 || smtpauth == 13) out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n"); + if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n"); +#ifdef TLS + } +#endif + smtp_size(); seenmail = 0; dohelo(arg); + if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); + if ((!flagbarfbhelo) && (bhelonrok) && (!relayclient)) flagbarfbhelo = bmcheck(BMCHECK_BHELONR); } void smtp_rset(arg) char *arg; { - seenmail = 0; + seenmail = 0; /* seenauth = 0; RFC 5321: retain authentication */ + mailfrom.len = 0; rcptto.len = 0; + /* prevents the maxrcpto error if control/maxrcpt limit has been exceeded in the same email, but not in multiple messages sequentially */ + rcptcount = 0; + envelopepos = 1; + /* end rcptcount adjustment */ out("250 flushed\r\n"); } + void smtp_mail(arg) char *arg; { + int r; + + envelopepos = 2; + if (smtpauth) + if (smtpauth > 10 && !seenauth) { err_submission(); return; } if (!addrparse(arg)) { err_syntax(); return; } + if (databytes && !sizelimit(arg)) { err_size(); return; } +/* start chkuser code */ + switch (chkuser_sender (&addr)) { + case CHKUSER_OK: + break; + case CHKUSER_ERR_MUSTAUTH: + qlogenvelope("rejected","chkusersender","mustauth","530"); + return; + break; + case CHKUSER_ERR_SENDER_FORMAT: + qlogenvelope("rejected","chkusersender","senderformat","553"); + return; + break; + case CHKUSER_ERR_SENDER_MX: + qlogenvelope("rejected","chkusersender","sendermxinvalid","550"); + return; + break; + case CHKUSER_ERR_SENDER_MX_TMP: + qlogenvelope("rejected","chkusersender","sendermxdnstmpfail","451"); + return; + break; + default: + qlogenvelope("rejected","chkusersender","invalid","550"); + return; + break; + } +/* end chkuser code */ +/* authtlsvariables: start */ + /* if it is authenticated but MAIL FROM and AUTH USER are different */ + if (smtpauth && seenauth && forceauthmailfrom) { + if (strcmp(addr.s,user.s)) { err_authmismatch(); return; } + } +/* authtlsvariables: end */ +/* rejectnullsenders: start */ + if ((rejnsmf) && (addr.len <= 1)) { die_nullsender(); return; } +/* rejectnullsenders: end */ +/* qregex: start */ + /* flagbarf = bmfcheck(); + */ + flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ + if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); + if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { + flagbarfbmf = bmcheck(BMCHECK_BMFNR); + } +/* qregex: end */ + flagsize = 0; + mailfrom_parms(arg); + if (flagsize) { err_size(); return; } + +/* qregex: start */ + /* + flagbarf = bmfcheck(); + */ + flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ + if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); + if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { + flagbarfbmf = bmcheck(BMCHECK_BMFNR); + } +/* qregex: end */ + + flagbarfspf = 0; + if (spfbehavior && !relayclient) + { + switch(r = spfcheck(remoteip4)) { + 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 < 2) break; + qlogenvelope("rejected","spf","lookupfailure","451"); + 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(); 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"); + } + } +} + +int flagdnsbl = 0; +stralloc dnsblhost = {0}; + void smtp_rcpt(arg) char *arg; { + int flagrcptmatch = 0; /* 0 undefined, 1 validrcptto, 2 chkuser, 3 chkuserrelay, 4 rcptcheck */ +/* added by empf patch */ + int ret = 0; +/* end of empf pacth */ + envelopepos = 3; if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } +/* rejectrelaytest: start */ + if (addrrelay()) { err_relay(); return; } +/* rejectrelaytest: end */ + if (addr.len) addrinrcpthosts = addrallowed(); + else addrinrcpthosts = 0; +/* qregex: start */ + /* if (flagbarf) { err_bmf(); return; } - if (relayclient) { + */ + if (flagbarfbhelo) { + if (logregex) { + strerr_warn5(title.s,"badhelo: <",helohost.s,"> matches pattern: ",matchedregex.s,0); + } else { + strerr_warn5(title.s,"badhelo: <",helohost.s,"> at ",remoteip,0); + } + qlogenvelope("rejected","qregexbhelo",matchedregex.s,"553"); + err_bhelo(); + return; + } + + if (flagbarfbmf) { + if (logregex) { + strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> matches pattern: ",matchedregex.s,0); + } else { + strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip,0); + } + qlogenvelope("rejected","qregexbmf",matchedregex.s,"553"); + err_bmf(); + return; + } +/* qregex: end */ + + if (flagbarfspf) { qlogenvelope("rejected","spf",env_get("SPFRESULT"),"550"); err_spf(); return; } + +/* dnsbl: start */ +/* + if (!(relayclient || dnsblskip || flagdnsbl)) + if (dnsblcheck()) die_dnsbl(dnsblhost.s); +*/ +/* dnsbl: end */ +/* start chkuser code */ +/* if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } else if (!addrallowed()) { err_nogateway(); return; } +*/ + +/* qregex: start */ + if (brtlimit && (brtcount >= brtlimit)) { + strerr_warn3(title.s,"badrcptto: excessive rcptto violations hanging up on ",remoteip,0); + die_brtlimit(); + } + + if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT); + if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) { + flagbarfbmt = bmcheck(BMCHECK_BMTNR); + } + if (flagbarfbmt) { + if (logregex) { + strerr_warn5(title.s,"badrcptto: <",addr.s,"> matches pattern: ",matchedregex.s,0); + } else { + strerr_warn5(title.s,"badrcptto: <",addr.s,"> at ",remoteip,0); + } + qlogenvelope("rejected","qregexbmt",matchedregex.s,"553"); + ++brtcount; + err_bmt(); + return; + } +/* qregex: end */ + +/* realbadrcpt: start */ + if (!relayclient) { /* if relayclient is defined, skip valid recipient checking */ + /* validrcptto */ + flagvrt = 0; + int vrtres = 0; + if ((vrtok) || (vrtfd != -1)) { /* run check only if validrcptto or morevalidrcptto.cdb exist */ + vrtres = vrtcheck(); + if (vrtres > 0) { + flagvrt = 1; + flagrcptmatch = 1; + strerr_warn5(title.s,"validrcptto: accepted address <",addr.s,"> at ",remoteip,0); + } + else if (vrtres < 0) { + strerr_warn5(title.s,"validrcptto: drop address <",addr.s,"> at ",remoteip,0); + ++brtcount; + err_vrt(); + /*err_rcpt();*/ + return; + } + } + + if (!flagvrt) { + switch (chkuser_realrcpt (&mailfrom, &addr)) { + case CHKUSER_OK: + flagrcptmatch = 2; + break; + case CHKUSER_RELAYING: + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + flagrcptmatch = 3; + break; + case CHKUSER_NORCPTHOSTS: + qlogenvelope("rejected","chkuser","notinrcpthosts","553"); + ++brtcount; + return; + break; + case CHKUSER_KO: + qlogenvelope("rejected","chkuser","nomailbox","550"); + ++brtcount; + return; + break; + case CHKUSER_ERR_AUTH_RESOURCE: + qlogenvelope("rejected","chkuser","noauthresource","451"); + return; + break; + case CHKUSER_ERR_MUSTAUTH: + qlogenvelope("rejected","chkuser","mustauth","530"); + return; + break; + case CHKUSER_ERR_MBXFULL: + qlogenvelope("rejected","chkuser","mailboxfull","552"); + return; + break; + case CHKUSER_ERR_MAXRCPT: + qlogenvelope("rejected","chkuser","maxrcpt","550"); + return; + break; + case CHKUSER_ERR_MAXWRONGRCPT: + qlogenvelope("rejected","chkuser","maxwrongrcpt","550"); + return; + break; + case CHKUSER_ERR_INTRUSION_THRESHOLD: + qlogenvelope("rejected","chkuser","instrusionthreshold","550"); + ++brtcount; + return; + break; + case CHKUSER_ERR_DOMAIN_MISSING: + qlogenvelope("rejected","chkuser","domainmissing","550"); + ++brtcount; + return; + break; + case CHKUSER_ERR_RCPT_FORMAT: + qlogenvelope("rejected","chkuser","rcptformat","553"); + ++brtcount; + return; + break; + case CHKUSER_ERR_RCPT_MX: + qlogenvelope("rejected","chkuser","rcptmxinvalid","550"); + ++brtcount; + return; + break; + case CHKUSER_ERR_RCPT_MX_TMP: + qlogenvelope("rejected","chkuser","rcptmxdnstmpfail","451"); + return; + break; + default: + qlogenvelope("rejected","chkuser","invalid","550"); + return; + break; + } + } + } // if (!relayclient) + + + /* rcptcheck */ + if ( (rcptcheck[0]) && (!relayclient || rcptcheckrelayclient) ) { // if RCPTCHECK is not defined, addrvalid returns 1 (rcpt ok),check before calling + strerr_warn5(title.s,"rcptcheck: checking <",addr.s,"> at ",remoteip,0); + if (flagrcptmatch) { + if (!env_put2("RCPTFOUND","1")) die_nomem(); + } + else { + if (!env_unset("RCPTFOUND")) die_nomem(); + } + if (addrinrcpthosts) { + if (!env_put2("RCPTHOSTS","1")) die_nomem(); + } + else { + if (!env_unset("RCPTHOSTS")) die_nomem(); + } + + int rcres = 0; + rcres = addrvalid(); + + char smtperrcode[4]; + char *smtperrstrptr; + long smtperrcodenum = 0; + int len = 0; + int closesession = 0; + + if ((rcptcheck_err[0]) && (sizeof(rcptcheck_err) > 3)) { + strncpy(smtperrcode,rcptcheck_err,3); + smtperrcode[3] = '\0'; + smtperrcodenum = strtoul(smtperrcode, &smtperrstrptr, 10); + if ((smtperrcodenum >= 400) && (smtperrcodenum <=599)) { + if (smtperrcodenum == 421) closesession = 1; + } + else { + len = str_copy(rcptcheck_err,"451 temporary problem (#4.4.2)\r\n"); + rcptcheck_err[len] = '\0' ; + } + qlogenvelope("rejected","rcptcheck","custom",smtperrcode); + } + else { + switch (rcres) { + case 0: + strerr_warn5(title.s,"rcptcheck: drop address <",addr.s,"> at ",remoteip,0); + qlogenvelope("rejected","rcptcheck","nomailbox","550"); + len = str_copy(rcptcheck_err,"550 sorry, no mailbox here by that name. (#5.1.1)\r\n"); + rcptcheck_err[len] = '\0'; + break; + case 1: + strerr_warn5(title.s,"rcptcheck: accepted address <",addr.s,"> at ",remoteip,0); + flagrcptmatch = 4; + break; + case 2: + strerr_warn5(title.s,"rcptcheck: ignore address <",addr.s,"> at ",remoteip,0); + break; + case 3: + strerr_warn5(title.s,"rcptcheck: overlimit sender <",addr.s,"> at ",remoteip,0); + qlogenvelope("rejected","rcptcheck","overlimit","421"); + len = str_copy(rcptcheck_err,"421 you have exceeded your messaging limits (#4.3.0)\r\n"); + rcptcheck_err[len] = '\0'; + closesession = 1; + break; + } + } + + if ( (rcres == 0) || (rcres == 3) ) { + out(rcptcheck_err); flush(); + if (closesession) { + _exit(1); + } + return; + } + } // if rcptcheck[0] +/* realbadrcpt: end */ + +/* end chkuser code */ +/* rbl: start */ + if ((rblok) && !(relayclient || seenauth || dnsblskip || flagrbldns)) { + flagrbldns = 1; + rblcheck(); + } + if (rbldecision >= 2) { + if (!stralloc_ready(&rblmessage,0)) die_nomem(); + if (flagmustnotbounce || (rbldecision == 2)) { + if (!stralloc_copys(&rblmessage,"451 ")) die_nomem(); + } + else + if (!stralloc_copys(&rblmessage,"553 ")) die_nomem(); + if (rbltext.len > 500) rbltext.len = 500; + if (!stralloc_cat(&rblmessage,&rbltext)) die_nomem(); + int i; + for (i = 0;i < rblmessage.len;++i) + if ((rblmessage.s[i] < 32) || (rblmessage.s[i] > 126)) + rblmessage.s[i] = '?'; + if (!stralloc_cats(&rblmessage,"\r\n")) die_nomem(); + if (flagmustnotbounce || (rbldecision == 2)) die_rbldelay(); + else err_rblreject(); + return; + } +/* rbl: end */ + +/* start empf code */ + ret = policy_check(); + + if (ret == 1) { + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + rcptcount++; + if (checkrcptcount() == 1) { err_maxrcpt(); return; } + if (flagrcptmatch == 1) { qlogenvelope("accepted","rcptto","validrcptto","250"); } + else if (flagrcptmatch == 2) { qlogenvelope("accepted","rcptto","chkuser","250"); } + else if (flagrcptmatch == 3) { qlogenvelope("accepted","rcptto","chkuserrelay","250"); } + else if (flagrcptmatch == 4) { qlogenvelope("accepted","rcptto","rcptcheck","250"); } + else { + if (relayclient) { qlogenvelope("accepted","relayclient","","250"); } + else { qlogenvelope("accepted","rcpthosts","","250"); } + } + out("250 ok\r\n"); + } + + else if (ret == 0) { + qlogenvelope("rejected","empf","","550"); + out("550 cannot message "); + out(addr.s); + out(" (#5.0.0 denied by policy)\r\n"); + } + + else { + qlogenvelope("rejected","empf","","454"); + out("454 cannot message "); + out(addr.s); + out(" (#4.3.0 broken policy)\r\n"); + } +/* end of empf code */ + +/* + * code substituted by empf code if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); + rcptcount++; + if (checkrcptcount() == 1) { err_maxrcpt(); return; } out("250 ok\r\n"); + */ } - int saferead(fd,buf,len) int fd; char *buf; int len; { int r; flush(); +#ifdef TLS + if (ssl && fd == ssl_rfd) + r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); + else +#endif r = timeoutread(timeout,fd,buf,len); if (r == -1) if (errno == error_timeout) die_alarm(); - if (r <= 0) die_read(); + if (r <= 0) die_read("hang up before quit cmd"); return r; } char ssinbuf[1024]; substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); +#ifdef TLS +void flush_io() { ssin.p = 0; flush(); } +#endif struct qmail qqt; unsigned int bytestooverflow = 0; @@ -300,7 +1732,7 @@ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ int flagmaybey; /* 1 if this line might match \r\n, if fih */ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ - + state = 1; *hops = 0; flaginheader = 1; @@ -322,17 +1754,16 @@ } switch(state) { case 0: - if (ch == '\n') straynewline(); + if (ch == '\n') { state = 1; break; } if (ch == '\r') { state = 4; continue; } break; case 1: /* \r\n */ - if (ch == '\n') straynewline(); if (ch == '.') { state = 2; continue; } if (ch == '\r') { state = 4; continue; } - state = 0; + if (ch != '\n') state = 0; break; case 2: /* \r\n + . */ - if (ch == '\n') straynewline(); + if (ch == '\n') return; /* this is what sendmail-8.8.4 does -djg */ if (ch == '\r') { state = 3; continue; } state = 0; break; @@ -351,10 +1782,73 @@ } } +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); +} + +/* rbl: start */ +/* +int dnsblcheck() +{ + char *ch; + static stralloc dnsblbyte = {0}; + static stralloc dnsblrev = {0}; + static ipalloc dnsblip = {0}; + static stralloc dnsbllist = {0}; + + ch = remoteip; + if(control_readfile(&dnsbllist,"control/dnsbllist",0) != 1) return 0; + + if (!stralloc_copys(&dnsblrev,"")) return 0; + for (;;) { + if (!stralloc_copys(&dnsblbyte,"")) return 0; + while (ch[0] && (ch[0] != '.')) { + if (!stralloc_append(&dnsblbyte,ch)) return 0; + ch++; + } + if (!stralloc_append(&dnsblbyte,".")) return 0; + if (!stralloc_cat(&dnsblbyte,&dnsblrev)) return 0; + if (!stralloc_copy(&dnsblrev,&dnsblbyte)) return 0; + + if (!ch[0]) break; + ch++; + } + + flagdnsbl = 1; + ch = dnsbllist.s; + while (ch < (dnsbllist.s + dnsbllist.len)) { + if (!stralloc_copy(&dnsblhost,&dnsblrev)) return 0; + if (!stralloc_cats(&dnsblhost,ch)) return 0; + if (!stralloc_0(&dnsblhost)) return 0; + + if (!dns_ip(&dnsblip,&dnsblhost)) return 1; + while (*ch++); + } + + return 0; +} +*/ +/* rbl:end */ + char accept_buf[FMT_ULONG]; void acceptmessage(qp) unsigned long qp; { datetime_sec when; + strnum[fmt_uint(strnum,(unsigned int) getpid())] = 0; when = now(); out("250 ok "); accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; @@ -363,22 +1857,32 @@ accept_buf[fmt_ulong(accept_buf,qp)] = 0; out(accept_buf); out("\r\n"); + substdio_puts(&sslog, "mail recv: pid "); + substdio_puts(&sslog, strnum); + substdio_puts(&sslog, " from <"); + substdio_puts(&sslog, mailfrom.s); + substdio_puts(&sslog, "> qp "); + substdio_puts(&sslog, accept_buf); + substdio_putsflush(&sslog, "\n"); } void smtp_data(arg) char *arg; { int hops; unsigned long qp; char *qqx; - + if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } + envelopepos = 4; seenmail = 0; if (databytes) bytestooverflow = databytes + 1; if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); + strnumqp[fmt_ulong(strnumqp,qp)] = 0; /* qp for qlog */ out("354 go ahead\r\n"); - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); + spfreceived(); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -386,36 +1890,662 @@ qmail_put(&qqt,rcptto.s,rcptto.len); qqx = qmail_close(&qqt); - if (!*qqx) { acceptmessage(qp); return; } - if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } - if (*qqx == 'D') out("554 "); else out("451 "); + if (!*qqx) { acceptmessage(qp); logit("message accepted"); qlogreceived("accepted","queueaccept","","250"); return; } + if (hops) { + out("554 too many hops, this message is looping (#5.4.6)\r\n"); + logit("message looping"); + qlogreceived("rejected","mailloop","","554"); + return; + } + if (databytes) if (!bytestooverflow) { + err_size(); + logit("message too big"); + return; + } + if (*qqx == 'D') { + out("554 "); + qlogreceived("rejected","queuereject",qqx + 1,"554"); + logit2("message rejected", qqx + 1); + } else { + out("451 "); + qlogreceived("rejected","queuedelay",qqx + 1,"451"); + logit2("message delayed", qqx + 1); + } out(qqx + 1); out("\r\n"); } + +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("authgetl"); + 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(&chal)) die_nomem(); + + if (pipe(pi) == -1) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if(fd_copy(3,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); + if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); + if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13) + if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); + if (substdio_flush(&ssauth) == -1) return err_write(); + + close(pi[1]); + if (!stralloc_copys(&chal,"")) die_nomem(); + if (!stralloc_copys(&slop,"")) die_nomem(); + byte_zero(ssauthbuf,sizeof ssauthbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ + + if (resp.len > id + 1) + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); + if (resp.len > id + user.len + 2) + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_cram() +{ + int i, r; + char *s; + + s = unique; /* generate challenge */ + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (!stralloc_copys(&chal,"<")) die_nomem(); + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,local)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); /* "334 base64_challenge \r\n" */ + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; /* got response */ + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + + i = str_rchr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login",auth_login } +, { "plain",auth_plain } +, { "cram-md5",auth_cram } +, { 0,err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + if (!smtpauth || !*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } + if (seenauth) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } +#ifdef TLS + if (forcetls && !ssl) { out("538 auth not available without TLS (#5.3.3)\r\n"); return; } +#endif + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); + if (!stralloc_copys(&chal,"")) 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: + seenauth = 1; + protocol = "ESMTPA"; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read("TCPREMOTEINFO"); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); + + if (!env_unset("SMTPAUTHMETHOD")) die_read("SMTPAUTHMETHOD"); + if (!env_put2("SMTPAUTHMETHOD", authcmds[i].text)) die_nomem(); + if (!env_unset("SMTPAUTHUSER")) die_read("SMTPAUTHUSER"); + if (!env_put2("SMTPAUTHUSER",user.s)) die_nomem(); + if (!env_unset("SMTP_AUTH_USER")) die_read("SMTP_AUTH_USER"); + if (!env_put2("SMTP_AUTH_USER",user.s)) die_nomem(); + + strerr_warn4(title.s,"auth: auth-success type=login user=<",user.s,">",0); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + strerr_warn4(title.s,"auth: auth-failed type=login user=<",user.s,">",0); + err_authfail(user.s,authcmds[i].text); + } +} + +#ifdef TLS +stralloc proto = {0}; +int ssl_verified = 0; +const char *ssl_verify_err = 0; + +void smtp_tls(char *arg) +{ + if (ssl || disabletls) err_unimpl(); + else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + else tls_init(); +} + +RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) +{ + RSA *rsa; + + if (!export) keylen = 2048; + if (keylen == 2048) { + FILE *in = fopen("control/rsa2048.pem", "r"); + if (in) { + rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); + fclose(in); + if (rsa) return rsa; + } + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BIGNUM *e; /*exponent */ + e = BN_new(); + BN_set_word(e, RSA_F4); + if (RSA_generate_key_ex(rsa, keylen, e, NULL) == 1) + return rsa; + return NULL; +#else + return RSA_generate_key(keylen, RSA_F4, NULL, NULL); +#endif +} + +DH *tmp_dh_cb(SSL *ssl, int export, int keylen) +{ + DH *dh; + + if (!export) keylen = 2048; + if (keylen == 2048) { + FILE *in = fopen("control/dh2048.pem", "r"); + if (in) { + dh = PEM_read_DHparams(in, NULL, NULL, NULL); + fclose(in); + if (dh) return dh; + } + } + if (keylen == 1024) { + FILE *in = fopen("control/dh1024.pem", "r"); + if (in) { + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); + fclose(in); + if (dh) return dh; + } + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if((dh = DH_new()) && (DH_generate_parameters_ex(dh, keylen, DH_GENERATOR_2, NULL) == 1)) + return dh; + return NULL; +#else + return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); +#endif +} + +/* don't want to fail handshake if cert isn't verifiable */ +int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { return 1; } + +void tls_nogateway() +{ + /* there may be cases when relayclient is set */ + if (!ssl || relayclient) return; + out("; no valid cert for gatewaying"); + if (ssl_verify_err) { out(": "); out(ssl_verify_err); } +} +void tls_out(const char *s1, const char *s2) +{ + out("454 TLS "); out(s1); + if (s2) { out(": "); out(s2); } + out(" (#4.3.0)\r\n"); flush(); +} +void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read("tls_err"); } + +# define CLIENTCA "control/clientca.pem" +# define CLIENTCRL "control/clientcrl.pem" +# define SERVERCERT "control/servercert.pem" + +int tls_verify() +{ + stralloc clients = {0}; + struct constmap mapclients; + + if (!ssl || relayclient || ssl_verified) return 0; + ssl_verified = 1; /* don't do this twice */ + + /* request client cert to see if it can be verified by one of our CAs + * and the associated email address matches an entry in tlsclients */ + switch (control_readfile(&clients, "control/tlsclients", 0)) + { + case 1: + if (constmap_init(&mapclients, clients.s, clients.len, 0)) { + /* if CLIENTCA contains all the standard root certificates, a + * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; + * it is probably due to 0.9.6b supporting only 8k key exchange + * data while the 0.9.6c release increases that limit to 100k */ + STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); + if (sk) { + SSL_set_client_CA_list(ssl, sk); + SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_cb); + break; + } + constmap_free(&mapclients); + } + case 0: alloc_free(clients.s); return 0; + case -1: die_control(); + } + + if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { + const char *err = ssl_error_str(); + tls_out("rehandshake failed", err); die_read("rehandshake failed"); + } + + do { /* one iteration */ + X509 *peercert; + X509_NAME *subj; + stralloc email = {0}; + + int n = SSL_get_verify_result(ssl); + if (n != X509_V_OK) + { ssl_verify_err = X509_verify_cert_error_string(n); break; } + peercert = SSL_get_peer_certificate(ssl); + if (!peercert) break; + + subj = X509_get_subject_name(peercert); + n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); + if (n >= 0) { + const ASN1_STRING *s = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subj, n)); + if (s) { email.len = s->length; email.s = s->data; } + } + + if (email.len <= 0) + ssl_verify_err = "contains no email address"; + else if (!constmap(&mapclients, email.s, email.len)) + ssl_verify_err = "email address not in my list of tlsclients"; + else { + /* add the cert email to the proto if it helped allow relaying */ + --proto.len; + if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ + || !stralloc_catb(&proto, email.s, email.len) + || !stralloc_cats(&proto, ")") + || !stralloc_0(&proto)) die_nomem(); + relayclient = ""; + /* also inform qmail-queue */ + if (!env_put("RELAYCLIENT=")) die_nomem(); + protocol = proto.s; + } + + X509_free(peercert); + } while (0); + constmap_free(&mapclients); alloc_free(clients.s); + + /* we are not going to need this anymore: free the memory */ + SSL_set_client_CA_list(ssl, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + + return relayclient ? 1 : 0; +} + +void tls_init() +{ + SSL *myssl; + SSL_CTX *ctx; + const char *ciphers; + stralloc saciphers = {0}; + X509_STORE *store; + X509_LOOKUP *lookup; + int session_id_context = 1; /* anything will do */ + + SSL_library_init(); + + /* a new SSL context with the bare minimum of options */ + ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ctx) { tls_err("unable to initialize ctx"); return; } + + /* POODLE vulnerability */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + /* renegotiation should include certificate request */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + /* never bother the application with retries if the transport is blocking */ + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + + /* relevant in renegotiation */ + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + if (!SSL_CTX_set_session_id_context(ctx, (void *)&session_id_context, + sizeof(session_id_context))) + { SSL_CTX_free(ctx); tls_err("failed to set session_id_context"); return; } + + if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) + { SSL_CTX_free(ctx); tls_err("missing certificate"); return; } + SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); + + /* crl checking */ + store = SSL_CTX_get_cert_store(ctx); + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && + (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + /* support ECDH */ + SSL_CTX_set_ecdh_auto(ctx,1); +#endif + + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + + /* a new SSL object, with the rest added to it directly to avoid copying */ + myssl = SSL_new(ctx); + SSL_CTX_free(ctx); + if (!myssl) { tls_err("unable to initialize ssl"); return; } + + /* this will also check whether public and private keys match */ + if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) + { SSL_free(myssl); tls_err("no valid RSA private key"); return; } + + ciphers = env_get("TLSCIPHERS"); + if (!ciphers) { + if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1) + { SSL_free(myssl); die_control(); } + if (saciphers.len) { /* convert all '\0's except the last one to ':' */ + int i; + for (i = 0; i < saciphers.len - 1; ++i) + if (!saciphers.s[i]) saciphers.s[i] = ':'; + ciphers = saciphers.s; + } + } + if (!ciphers || !*ciphers) ciphers = "DEFAULT"; + SSL_set_cipher_list(myssl, ciphers); + alloc_free(saciphers.s); + + SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); + SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); + SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); + SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); + + if (!smtps) { flagtls = 1; out("220 ready for tls\r\n"); flush(); } + + if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { + /* neither cleartext nor any other response here is part of a standard */ + const char *err = ssl_error_str(); + tls_out("connection failed", err); ssl_free(myssl); die_read("tls connection failed"); + } + ssl = myssl; + + /* populate the protocol string, used in Received */ + if (!stralloc_copys(&proto, "ESMTPS (") + || !stralloc_cats(&proto, SSL_get_cipher(ssl)) + || !stralloc_cats(&proto, " encrypted)")) die_nomem(); + if (!stralloc_0(&proto)) die_nomem(); + protocol = proto.s; + + /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ + dohelo(remotehost); +} + +# undef SERVERCERT +# undef CLIENTCA + +#endif + 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_io } +#endif , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } -, { 0, err_unimpl, flush } +, { 0, err_unrecog, flush } } ; -void main() +/* qsmtpdlog: start */ +void qsmtpdlog(const char *head, const char *result, const char *reason, const char *detail, const char *statuscode) { + char *x; + char *ch; + int i, r; + stralloc lst = {0}; + int isenvelope = 0; + + void outqlog(char *s, unsigned int n) { + while (n > 0) { + substdio_put(&sslog,((*s > 32) && (*s <= 126)) ? s : "_",1); + --n; + ++s; + } + } + void outsqlog(s) char *s; { outqlog(s,str_len(s)); } + + stralloc_copys(&lst,head); + if (stralloc_starts(&lst,"qlogenvelope")) isenvelope = 1; + substdio_puts(&sslog, head); + substdio_puts(&sslog, ":"); + + substdio_puts(&sslog, " result="); if (result) outsqlog(result); + substdio_puts(&sslog, " code="); if (detail) outsqlog(statuscode); + substdio_puts(&sslog, " reason="); if (reason) outsqlog(reason); + substdio_puts(&sslog, " detail="); if (detail) outsqlog(detail); + substdio_puts(&sslog, " helo="); if (helohost.len) outsqlog(helohost.s); + substdio_puts(&sslog, " mailfrom="); + if (mailfrom.len) outsqlog(mailfrom.s); + else if ( (envelopepos==2) && (addr.len) ) outsqlog(addr.s); // qlog called in smtp_mail() doesn't have mailfrom.s defined yet + + substdio_puts(&sslog, " rcptto="); + if ((rcptto.len) && (!isenvelope)) { + ch = rcptto.s; + outsqlog(ch+1); + while (*ch++); + while (ch < (rcptto.s + rcptto.len)) { + outsqlog(","); + outsqlog(ch+1); + while (*ch++); + } + } + else if ( (envelopepos==3) && (addr.len) ) outsqlog(addr.s); // qlog was probably called at the beginning of smtp_rcpt and addr.s contains the recipient + + substdio_puts(&sslog, " relay="); if (relayclient) outsqlog("yes"); else outsqlog("no"); + + // only log rcpthosts value in smtp_rcpt(), that is for a single recipient, this field is meaningless for multiple recipients + substdio_puts(&sslog, " rcpthosts="); if (isenvelope && addr.len && (envelopepos==3)) { if (addrinrcpthosts) outsqlog("yes"); else outsqlog("no"); } + + substdio_puts(&sslog, " size="); + if (bytestooverflow) { + char *p,text[20]; + if ((databytes - bytestooverflow) >= 0) + sprintf(text,"%d",databytes - bytestooverflow); + else + sprintf(text,""); + p = text; + outsqlog(p); + } + + substdio_puts(&sslog, " authuser="); if (user.len) outsqlog(user.s); + substdio_puts(&sslog, " authtype="); x = env_get("SMTPAUTHMETHOD"); if (x) outsqlog(x); + substdio_puts(&sslog, " encrypted="); if (smtps) outsqlog("ssl"); else if (flagtls) outsqlog("tls"); + + substdio_puts(&sslog, " sslverified="); +#ifdef TLS + if (ssl_verified) outsqlog("yes"); else outsqlog("no"); +#endif +/* + substdio_puts(&sslog, " sslproto="); +#ifdef TLS + if (proto.len) outsqlog(proto.s); +#endif +*/ + substdio_puts(&sslog, " localip="); x = env_get("TCPLOCALIP"); if (x) outsqlog(x); + substdio_puts(&sslog, " localport="); x = env_get("TCPLOCALPORT"); if (x) outsqlog(x); + substdio_puts(&sslog, " remoteip="); x = env_get("TCPREMOTEIP"); if (x) outsqlog(x); + substdio_puts(&sslog, " remoteport="); x = env_get("TCPREMOTEPORT"); if (x) outsqlog(x); + substdio_puts(&sslog, " remotehost="); x = env_get("TCPREMOTEHOST"); if (x) outsqlog(x); + substdio_puts(&sslog, " qp="); if (strnumqp) outsqlog(strnumqp); + substdio_puts(&sslog, " pid="); if (strnumpid) outsqlog(strnumpid); + substdio_putsflush(&sslog, "\n"); +} +/* qsmtpdlog: end */ + +void main(argc,argv) +int argc; +char **argv; { + int n, m; + childargs = argv + 1; sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); + + pid_buf[fmt_ulong(pid_buf,getpid())]=0; + if (!stralloc_copys(&title,"qmail-smtpd[")) die_nomem(); + if (!stralloc_cats(&title,pid_buf)) die_nomem(); + if (!stralloc_cats(&title,"]: ")) die_nomem(); + if (!stralloc_0(&title)) die_nomem(); + setup(); if (ipme_init() != 1) die_ipme(); + if (!relayclient && greetdelay) { + if (drop_pre_greet) { + n = timeoutread(greetdelay ? greetdelay : 1, 0, ssinbuf, sizeof(ssinbuf)); + if(n == -1) { + if (errno != error_timeout) + strerr_die3sys(1, "GREETDELAY from ", remoteip, ": "); + } else if (n == 0) { + strerr_die3x(1, "GREETDELAY from ", remoteip, ": client disconnected"); + } else { + strerr_warn3("GREETDELAY from ", remoteip, ": client sent data before greeting", 0); + die_pre_greet(); + } + } + else { + strerr_warn3("GREETDELAY: ", greetdelay, "s", 0); + sleep(greetdelay); + m = 0; + for (;;) { + n = timeoutread(0, 0, ssinbuf, sizeof(ssinbuf)); + if (n <= 0) + break; + if (n > 0 && m == 0) { + strerr_warn3("GREETDELAY from ", remoteip, ": client sent data before greeting. ignoring", 0); + m = 1; + } + } + } + } + smtp_greet("220 "); out(" ESMTP\r\n"); - if (commands(&ssin,&smtpcommands) == 0) die_read(); + if (commands(&ssin,&smtpcommands) == 0) die_read("commands"); die_nomem(); } diff -ruN ../netqmail-1.06-original/qmail-start.c netqmail-1.06/qmail-start.c --- ../netqmail-1.06-original/qmail-start.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail-start.c 2019-06-26 16:39:31.578826915 +0200 @@ -4,10 +4,15 @@ #include "fork.h" #include "auto_uids.h" +#include "channels.h" + char *(qsargs[]) = { "qmail-send", 0 }; char *(qcargs[]) = { "qmail-clean", 0 }; char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; char *(qrargs[]) = { "qmail-rspawn", 0 }; +#ifdef EXTERNAL_TODO +char *(qtargs[]) = { "qmail-todo", 0}; +#endif void die() { _exit(111); } @@ -18,19 +23,51 @@ int pi4[2]; int pi5[2]; int pi6[2]; - -void close23456() { close(2); close(3); close(4); close(5); close(6); } +#ifdef EXTERNAL_TODO +int pi7[2]; +int pi8[2]; +int pi9[2]; +int pi10[2]; +#endif + +int suppl_pi[SUPPL_CHANNELS*2][2]; + +void close23456() { + int c; + close(2); close(3); close(4); close(5); close(6); +#ifdef EXTERNAL_TODO + close(7); close(8); +#endif + for (c=1+CHANNEL_FD_OFFSET;c<=SUPPL_CHANNELS*2+CHANNEL_FD_OFFSET;c++) + { + close(c); + } +} void closepipes() { + int c; + close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]); close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]); close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]); +#ifdef EXTERNAL_TODO + close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]); + close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]); +#endif + + for (c=0;c +#include +#include "alloc.h" +#include "auto_qmail.h" +#include "byte.h" +#include "constmap.h" +#include "control.h" +#include "direntry.h" +#include "error.h" +#include "exit.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "getln.h" +#include "open.h" +#include "ndelay.h" +#include "now.h" +#include "readsubdir.h" +#include "readwrite.h" +#include "scan.h" +#include "select.h" +#include "str.h" +#include "stralloc.h" +#include "substdio.h" +#include "trigger.h" + +#include "channels.h" + +/* critical timing feature #1: if not triggered, do not busy-loop */ +/* critical timing feature #2: if triggered, respond within fixed time */ +/* important timing feature: when triggered, respond instantly */ +#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ +#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ +#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */ +#define SLEEP_SYSFAIL 123 + + +stralloc percenthack = {0}; +struct constmap mappercenthack; +stralloc locals = {0}; +struct constmap maplocals; +stralloc vdoms = {0}; +struct constmap mapvdoms; +stralloc envnoathost = {0}; +stralloc fname = {0}; + +char strnum[FMT_ULONG]; + +struct constmap mapsuppl[SUPPL_CHANNELS]; +stralloc suppls[SUPPL_CHANNELS]; +stralloc newsuppls[SUPPL_CHANNELS]; +char *chanaddr[CHANNELS]; + +datetime_sec recent; + +void log1(char *x); +void log3(char* x, char* y, char* z); + +int flagstopasap = 0; +void sigterm(void) +{ + if (flagstopasap == 0) + log1("status: qmail-todo stop processing asap\n"); + flagstopasap = 1; +} + +int flagreadasap = 0; void sighup(void) { flagreadasap = 1; } +int flagsendalive = 1; void senddied(void) { flagsendalive = 0; } + +void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); } +void pausedir(dir) char *dir; +{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); } + +void cleandied() +{ + log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n"); + flagstopasap = 1; +} + + +/* this file is not so long ------------------------------------- FILENAMES */ + +stralloc fn = {0}; + +void fnmake_init(void) +{ + while (!stralloc_ready(&fn,FMTQFN)) nomem(); +} + +void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); } +void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_chanaddr(unsigned long id, int c) +{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } + + +/* this file is not so long ------------------------------------- REWRITING */ + +stralloc rwline = {0}; + +/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ +/* may trash recip. must set up rwline, between a T and a \0. */ +int rewrite(char *recip) +{ + int i; + int j; + int c; + char *x; + static stralloc addr = {0}; + int at; + + if (!stralloc_copys(&rwline,"T")) return 0; + if (!stralloc_copys(&addr,recip)) return 0; + + i = byte_rchr(addr.s,addr.len,'@'); + if (i == addr.len) { + if (!stralloc_cats(&addr,"@")) return 0; + if (!stralloc_cat(&addr,&envnoathost)) return 0; + } + + while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { + j = byte_rchr(addr.s,i,'%'); + if (j == i) break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s,addr.len,'@'); + + if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + for (i = 0;i <= addr.len;++i) + if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) + if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) { + if (!*x) break; + if (!stralloc_cats(&rwline,x)) return 0; + if (!stralloc_cats(&rwline,"-")) return 0; + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + + for (c = 0;c < SUPPL_CHANNELS;++c) + { + if (constmap(&mapsuppl[c],addr.s + at + 1,addr.len - at - 1)) + return c + 3; + } + + return 2; +} + +/* this file is not so long --------------------------------- COMMUNICATION */ + +substdio sstoqc; char sstoqcbuf[1024]; +substdio ssfromqc; char ssfromqcbuf[1024]; +stralloc comm_buf = {0}; +int comm_pos; +int fdout = -1; +int fdin = -1; + +void comm_init(void) +{ + substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf)); + substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf)); + + fdout = 1; /* stdout */ + fdin = 0; /* stdin */ + if (ndelay_on(fdout) == -1) + /* this is so stupid: NDELAY semantics should be default on write */ + senddied(); /* drastic, but better than risking deadlock */ + + while (!stralloc_ready(&comm_buf,1024)) nomem(); +} + +int comm_canwrite(void) +{ + /* XXX: could allow a bigger buffer; say 10 recipients */ + /* XXX: returns true if there is something in the buffer */ + if (!flagsendalive) return 0; + if (comm_buf.s && comm_buf.len) return 1; + return 0; +} + +void log1(char* x) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto fail; + if (!stralloc_cats(&comm_buf,x)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void log3(char* x, char *y, char *z) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto fail; + if (!stralloc_cats(&comm_buf,x)) goto fail; + if (!stralloc_cats(&comm_buf,y)) goto fail; + if (!stralloc_cats(&comm_buf,z)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_write(unsigned long id, int flagchan[]) +{ + int pos; + char s[CHANNELS+1]; + int c; + + for (c = 0;c < CHANNELS;++c) + { + if (flagchan[c]) + { + s[c] = '1'; + } + else + { + s[c] = '0'; + } + } + s[c] = 0; + + pos = comm_buf.len; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,"D")) goto fail; + if (!stralloc_cats(&comm_buf,s)) goto fail; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +static int issafe(char ch) +{ + if (ch == '%') return 0; /* general principle: allman's code is crap */ + if (ch < 33) return 0; + if (ch > 126) return 0; + return 1; +} + +void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid) +{ + int pos; + int i; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf,": bytes ")) goto fail; + strnum[fmt_ulong(strnum,size)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf," from <")) goto fail; + i = comm_buf.len; + if (!stralloc_cats(&comm_buf,from)) goto fail; + for (;i < comm_buf.len;++i) + if (comm_buf.s[i] == '\n') + comm_buf.s[i] = '/'; + else + if (!issafe(comm_buf.s[i])) + comm_buf.s[i] = '_'; + if (!stralloc_cats(&comm_buf,"> qp ")) goto fail; + strnum[fmt_ulong(strnum,pid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf," uid ")) goto fail; + strnum[fmt_ulong(strnum,uid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto fail; + if (!stralloc_cats(&comm_buf,"\n")) goto fail; + if (!stralloc_0(&comm_buf)) goto fail; + return; + +fail: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_exit(void) +{ + int w; + + /* if it fails exit, we have already stoped */ + if (!stralloc_cats(&comm_buf,"X")) _exit(1); + if (!stralloc_0(&comm_buf)) _exit(1); +} + +void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds) +{ + if (flagsendalive) { + if (flagstopasap && comm_canwrite() == 0) + comm_exit(); + if (comm_canwrite()) { + FD_SET(fdout,wfds); + if (*nfds <= fdout) + *nfds = fdout + 1; + } + FD_SET(fdin,rfds); + if (*nfds <= fdin) + *nfds = fdin + 1; + } +} + +void comm_do(fd_set *wfds, fd_set *rfds) +{ + /* first write then read */ + if (flagsendalive) + if (comm_canwrite()) + if (FD_ISSET(fdout,wfds)) { + int w; + int len; + len = comm_buf.len; + w = write(fdout,comm_buf.s + comm_pos,len - comm_pos); + if (w <= 0) { + if ((w == -1) && (errno == error_pipe)) + senddied(); + } else { + comm_pos += w; + if (comm_pos == len) { + comm_buf.len = 0; + comm_pos = 0; + } + } + } + if (flagsendalive) + if (FD_ISSET(fdin,rfds)) { + /* there are only two messages 'H' and 'X' */ + char c; + int r; + r = read(fdin, &c, 1); + if (r <= 0) { + if ((r == -1) && (errno != error_intr)) + senddied(); + } else { + switch(c) { + case 'H': + sighup(); + break; + case 'X': + sigterm(); + break; + default: + log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n"); + break; + } + } + } +} + +/* this file is not so long ------------------------------------------ TODO */ + +datetime_sec nexttodorun; +/* DIR *tododir; if 0, have to opendir again */ +int flagtododir = 0; /* if 0, have to readsubdir_init again */ +readsubdir todosubdir; +stralloc todoline = {0}; +char todobuf[SUBSTDIO_INSIZE]; +char todobufinfo[512]; +char todobufchan[CHANNELS][1024]; + +void todo_init(void) +{ +/* tododir = 0; */ + flagtododir = 0; + nexttodorun = now(); + trigger_set(); +} + +void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup) +{ + if (flagstopasap) return; + trigger_selprep(nfds,rfds); +/* if (tododir) *wakeup = 0; */ + if (flagtododir) *wakeup = 0; + if (*wakeup > nexttodorun) *wakeup = nexttodorun; +} + +void todo_do(fd_set *rfds) +{ + struct stat st; + substdio ss; int fd; + substdio ssinfo; int fdinfo; + substdio sschan[CHANNELS]; + int fdchan[CHANNELS]; + int flagchan[CHANNELS]; + char ch; + int match; + unsigned long id; +/* unsigned int len; + direntry *d; */ + int z; + int c; + unsigned long uid; + unsigned long pid; + + fd = -1; + fdinfo = -1; + for (c = 0;c < CHANNELS;++c) fdchan[c] = -1; + + if (flagstopasap) return; + +/* if (!tododir) */ + if (!flagtododir) + { + if (!trigger_pulled(rfds)) + if (recent < nexttodorun) + return; + trigger_set(); +/* tododir = opendir("todo"); + if (!tododir) + { + pausedir("todo"); + return; + } */ + readsubdir_init(&todosubdir, "todo", pausedir); + flagtododir = 1; + nexttodorun = recent + SLEEP_TODO; + } + +/* d = readdir(tododir); + if (!d) */ + switch(readsubdir_next(&todosubdir, &id)) + { +/* closedir(tododir); + tododir = 0; + return; */ + case 1: + break; + case 0: + flagtododir = 0; + default: + return; + } +/* if (str_equal(d->d_name,".")) return; + if (str_equal(d->d_name,"..")) return; + len = scan_ulong(d->d_name,&id); + if (!len || d->d_name[len]) return; +*/ + fnmake_todo(id); + + fd = open_read(fn.s); + if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; } + + fnmake_mess(id); + /* just for the statistics */ + if (stat(fn.s,&st) == -1) + { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; } + + for (c = 0;c < CHANNELS;++c) + { + fnmake_chanaddr(id,c); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } + } + + fnmake_info(id); + if (unlink(fn.s) == -1) if (errno != error_noent) + { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } + + fdinfo = open_excl(fn.s); + if (fdinfo == -1) + { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } + + strnum[fmt_ulong(strnum,id)] = 0; + log3("new msg ",strnum,"\n"); + + for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; + + substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf)); + substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo)); + + uid = 0; + pid = 0; + + for (;;) + { + if (getln(&ss,&todoline,&match,'\0') == -1) + { + /* perhaps we're out of memory, perhaps an I/O error */ + fnmake_todo(id); + log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail; + } + if (!match) break; + + switch(todoline.s[0]) + { + case 'u': + scan_ulong(todoline.s + 1,&uid); + break; + case 'p': + scan_ulong(todoline.s + 1,&pid); + break; + case 'F': + if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1) + { + fnmake_info(id); + log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; + } + comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid); + break; + case 'T': + c = rewrite(todoline.s + 1); + if (c == 0) { nomem(); goto fail; } + c--; + if (fdchan[c] == -1) + { + fnmake_chanaddr(id,c); + fdchan[c] = open_excl(fn.s); + if (fdchan[c] == -1) + { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } + substdio_fdbuf(&sschan[c] + ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); + flagchan[c] = 1; + } + if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1) + { + fnmake_chanaddr(id,c); + log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; + } + break; + default: + fnmake_todo(id); + log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail; + } + } + + close(fd); fd = -1; + + fnmake_info(id); + if (substdio_flush(&ssinfo) == -1) + { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdinfo) == -1) + { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdinfo); fdinfo = -1; + + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) + { + fnmake_chanaddr(id,c); + if (substdio_flush(&sschan[c]) == -1) + { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } + if (fsync(fdchan[c]) == -1) + { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } + close(fdchan[c]); fdchan[c] = -1; + } + + fnmake_todo(id); + if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + { + log3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + return; + } + + comm_write(id, flagchan); + + return; + + fail: + if (fd != -1) close(fd); + if (fdinfo != -1) close(fdinfo); + for (c = 0;c < CHANNELS;++c) + if (fdchan[c] != -1) close(fdchan[c]); +} + +/* this file is too long ---------------------------------------------- MAIN */ + +int getcontrols(void) +{ + int c; + + if (control_init() == -1) return 0; + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; + if (control_readfile(&locals,"control/locals",1) != 1) return 0; + if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; + switch(control_readfile(&percenthack,"control/percenthack",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + switch(control_readfile(&vdoms,"control/virtualdomains",0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; + } + + for (c = 0;c < SUPPL_CHANNELS;++c) + { + strnum[fmt_uint(strnum,c)] = 0; + if (!stralloc_copys(&fname,"control/suppls")) return 0; + if (!stralloc_cats(&fname,strnum)) return 0; + if (!stralloc_0(&fname)) return 0; + switch (control_readfile(&suppls[c],fname.s,0)) + { + case -1: return 0; + case 0: if (!constmap_init(&mapsuppl[c],"",0,0)) return 0; break; + case 1: if (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) return 0; break; + } + } + + return 1; +} + +stralloc newlocals = {0}; +stralloc newvdoms = {0}; + +void regetcontrols(void) +{ + int r; + int c; + + if (control_readfile(&newlocals,"control/locals",1) != 1) + { log1("alert: qmail-todo: unable to reread control/locals\n"); return; } + r = control_readfile(&newvdoms,"control/virtualdomains",0); + if (r == -1) + { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals,&newlocals)) nomem(); + while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); + + if (r) + { + while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); + while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); + } + else + while (!constmap_init(&mapvdoms,"",0,1)) nomem(); + + for (c = 0;c < SUPPL_CHANNELS;++c) + { + strnum[fmt_uint(strnum,c)] = 0; + if (!stralloc_copys(&fname,"control/suppls")) nomem(); + if (!stralloc_cats(&fname,strnum)) nomem(); + if (!stralloc_0(&fname)) nomem(); + r = control_readfile(&newsuppls[c],fname.s,0); + if (r == -1) + { log3("alert: qmail-todo: unable to reread ", fname.s, "\n"); return; } + + constmap_free(&mapsuppl[c]); + + if (r) + { + while (!stralloc_copy(&suppls[c],&newsuppls[c])) nomem(); + while (!constmap_init(&mapsuppl[c],suppls[c].s,suppls[c].len,0)) nomem(); + } + else + while (!constmap_init(&mapsuppl[c],"",0,0)) nomem(); + } + +} + +void reread(void) +{ + if (chdir(auto_qmail) == -1) + { + log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n"); + return; + } + regetcontrols(); + while (chdir("queue") == -1) + { + log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } +} + +static int static_i = 0; +static int static_j = 0; +void channels_init(void) +{ + for (static_i=0;static_ipid = vfork()) { case -1: close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); - return -1; + close(pic[0]); close(pic[1]); + return -1; case 0: close(pim[1]); close(pie[1]); + close(pic[0]); /*- we want to receive data */ if (fd_move(0,pim[0]) == -1) _exit(120); if (fd_move(1,pie[0]) == -1) _exit(120); + if (!(x = env_get("ERROR_FD"))) + errfd = CUSTOM_ERR_FD; + else + scan_int(x, &errfd); + if (fd_move(errfd, pic[1]) == -1) _exit(120); if (chdir(auto_qmail) == -1) _exit(61); execv(*binqqargs,binqqargs); _exit(120); @@ -46,6 +56,7 @@ qq->fdm = pim[1]; close(pim[0]); qq->fde = pie[1]; close(pie[0]); + qq->fdc = pic[0]; close(pic[1]); substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf)); qq->flagerr = 0; return 0; @@ -93,11 +104,27 @@ { int wstat; int exitcode; + char ch; + static char errstr[256]; + int len = 0; qmail_put(qq,"",1); if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1; close(qq->fde); + substdio_fdbuf(&qq->ss, read, qq->fdc, qq->buf, sizeof(qq->buf)); + + while (substdio_bget(&qq->ss, &ch, 1) && len < 255) + { + errstr[len]=ch; + len++; + } + + if (len > 0) errstr[len]='\0'; /* add str-term */ + /* dkim + errstr[len] = 0; */ /* add str-term */ + + close(qq->fdc); + if (wait_pid(&wstat,qq->pid) != qq->pid) return "Zqq waitpid surprise (#4.3.0)"; if (wait_crashed(wstat)) @@ -108,6 +135,7 @@ case 115: /* compatibility */ case 11: return "Denvelope address too long for qq (#5.1.3)"; case 31: return "Dmail server permanently rejected message (#5.3.0)"; + case 32: return "DPrivate key file does not exist (#5.3.5)"; case 51: return "Zqq out of memory (#4.3.0)"; case 52: return "Zqq timeout (#4.3.0)"; case 53: return "Zqq write error or disk full (#4.3.0)"; @@ -127,10 +155,14 @@ case 74: return "Zcommunication with mail server failed (#4.4.2)"; case 91: /* fall through */ case 81: return "Zqq internal bug (#4.3.0)"; + case 82: /*- simscan exits with 82 */ + case 88: /*- custom error */ + if (len > 2) + return errstr; case 120: return "Zunable to exec qq (#4.3.0)"; default: if ((exitcode >= 11) && (exitcode <= 40)) - return "Dqq permanent problem (#5.3.0)"; + return "Dqq permanent problem (#5.3.0)"; return "Zqq temporary problem (#4.3.0)"; } } diff -ruN ../netqmail-1.06-original/qmail.h netqmail-1.06/qmail.h --- ../netqmail-1.06-original/qmail.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/qmail.h 2019-02-27 20:57:13.404024915 +0100 @@ -3,11 +3,13 @@ #include "substdio.h" +#define CUSTOM_ERR_FD 4 struct qmail { int flagerr; unsigned long pid; int fdm; int fde; + int fdc; substdio ss; char buf[1024]; } ; diff -ruN ../netqmail-1.06-original/qregex.c netqmail-1.06/qregex.c --- ../netqmail-1.06-original/qregex.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qregex.c 2019-02-27 20:57:13.405024904 +0100 @@ -0,0 +1,239 @@ +/* + * $Log: qregex.c,v $ + * Revision 1.13 2007-12-20 13:51:04+05:30 Cprogrammer + * removed compiler warning + * + * Revision 1.12 2005-08-23 17:41:36+05:30 Cprogrammer + * regex to be turned on only of QREGEX is defined to non-zero value + * + * Revision 1.11 2005-04-02 19:07:25+05:30 Cprogrammer + * use internal wildmat version + * + * Revision 1.10 2005-01-22 00:39:04+05:30 Cprogrammer + * added missing error handling + * + * Revision 1.9 2004-10-22 20:29:45+05:30 Cprogrammer + * added RCS id + * + * Revision 1.8 2004-09-21 23:48:18+05:30 Cprogrammer + * made matchregex() visible + * introduced dotChar (configurable dot char) + * + * Revision 1.7 2004-02-05 18:48:48+05:30 Cprogrammer + * changed curregex to static + * + * Revision 1.6 2003-12-23 23:22:53+05:30 Cprogrammer + * implicitly use wildcard if address starts with '@' + * + * Revision 1.5 2003-12-22 18:33:12+05:30 Cprogrammer + * added address_match() + * + * Revision 1.4 2003-12-22 13:21:08+05:30 Cprogrammer + * added text and pattern as part of error message + * + * Revision 1.3 2003-12-22 10:04:04+05:30 Cprogrammer + * conditional compilation of qregex + * + * Revision 1.2 2003-12-21 15:32:18+05:30 Cprogrammer + * added regerror + * + * Revision 1.1 2003-12-20 13:17:16+05:30 Cprogrammer + * Initial revision + * + * qregex (v2) + * $Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $ + * + * Author : Evan Borgstrom (evan at unixpimps dot org) + * Created : 2001/12/14 23:08:16 + * Modified: $Date: 2007-12-20 13:51:04+05:30 $ + * Revision: $Revision: 1.13 $ + * + * 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 "case.h" +#include "scan.h" +#include "stralloc.h" +#include "constmap.h" +#include "substdio.h" +#include "byte.h" +#include "env.h" +#include +#include +#include + +static int wildmat_match(stralloc *, int, struct constmap *, int, stralloc *); +static int regex_match(stralloc *, int, stralloc *); +int wildmat_internal(char *, char *); + +static char sserrbuf[512]; +static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf)); +static char dotChar = '@'; + +int +address_match(stralloc *addr, int bhfok, stralloc *bhf, + struct constmap *mapbhf, int bhpok, stralloc *bhp) +{ + char *ptr; + int x = 0; + + case_lowerb(addr->s, addr->len); /*- convert into lower case */ + if ((ptr = env_get("QREGEX"))) + scan_int(ptr, &x); + if (ptr && x) + return (regex_match(addr, bhfok, bhf)); + else + return (wildmat_match(addr, bhfok, mapbhf, bhpok, bhp)); +} + +int +matchregex(char *text, char *regex) +{ + regex_t qreg; + char errbuf[512]; + int retval = 0; + +#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE) + /*- build the regex */ + if ((retval = REGCOMP(qreg, regex)) != 0) + { + regerror(retval, &qreg, errbuf, sizeof(errbuf)); + regfree(&qreg); + if (substdio_puts(&sserr, text) == -1) + return (-retval); + if (substdio_puts(&sserr, ": ") == -1) + return (-retval); + if (substdio_puts(&sserr, regex) == -1) + return (-retval); + if (substdio_puts(&sserr, ": ") == -1) + return (-retval); + if (substdio_puts(&sserr, errbuf) == -1) + return (-retval); + if (substdio_puts(&sserr, "\n") == -1) + return (-retval); + if (substdio_flush(&sserr) == -1) + return (-retval); + return (-retval); + } + /*- execute the regex */ +#define REGEXEC(X,Y) regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0) + retval = REGEXEC(qreg, text); + regfree(&qreg); + return (retval == REG_NOMATCH ? 0 : 1); +} + +static int +wildmat_match(stralloc * addr, int mapfile, struct constmap *ptrmap, int patfile, stralloc *wildcard) +{ + int i = 0; + int j = 0; + int k = 0; + char subvalue; + + if (mapfile) + { + if (constmap(ptrmap, addr->s, addr->len - 1)) + return 1; + if ((j = byte_rchr(addr->s, addr->len, dotChar)) < addr->len) + { + if (constmap(ptrmap, addr->s + j, addr->len - j - 1)) + return 1; + } + } + /*- Include control file control/xxxxpatterns and evaluate with Wildmat check */ + if (patfile && wildcard) + { + i = 0; + for (j = 0; j < wildcard->len; ++j) + { + if (!wildcard->s[j]) + { + subvalue = wildcard->s[i] != '!'; + if (!subvalue) + i++; + if ((k != subvalue) && wildmat_internal(addr->s, wildcard->s + i)) + k = subvalue; + i = j + 1; + } + } + return k; + } + return (0); +} + +static int +regex_match(stralloc * addr, int mapfile, stralloc *map) +{ + int i = 0; + int j = 0; + int k = 0; + int negate = 0, match; + static stralloc curregex = { 0 }; + + match = 0; + if (mapfile) + { + while (j < map->len) + { + i = j; + while ((map->s[i] != '\0') && (i < map->len)) + i++; + if (map->s[j] == '!') + { + negate = 1; + j++; + } + if (*(map->s + j) == dotChar) + { + if (!stralloc_copys(&curregex, ".*")) + return(-1); + if (!stralloc_catb(&curregex, map->s + j, (i - j))) + return(-1); + } else + if (!stralloc_copyb(&curregex, map->s + j, (i - j))) + return(-1); + if (!stralloc_0(&curregex)) + return(-1); + if((k = matchregex(addr->s, curregex.s)) == 1) + { + if (negate) + return(0); + match = 1; + } + j = i + 1; + negate = 0; + } + } + return (match); +} + +void +setdotChar(c) + char c; +{ + dotChar = c; + return; +} + +void +getversion_qregex_c() +{ + static char *x = "$Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $"; + +#ifdef INDIMAIL + x = sccsidh; +#else + x++; +#endif +} diff -ruN ../netqmail-1.06-original/qregex.h netqmail-1.06/qregex.h --- ../netqmail-1.06-original/qregex.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/qregex.h 2019-02-27 20:57:13.405024904 +0100 @@ -0,0 +1,24 @@ +/* + * $Log: qregex.h,v $ + * Revision 1.3 2004-09-21 23:49:02+05:30 Cprogrammer + * added matchregex() and setdotChar() + * + * Revision 1.2 2003-12-22 18:35:26+05:30 Cprogrammer + * added address_match() function + * + * Revision 1.1 2003-12-20 13:17:45+05:30 Cprogrammer + * Initial revision + * + */ +/* + * simple header file for the matchregex prototype + */ +#ifndef _QREGEX_H_ +#define _QREGEX_H_ +#include "constmap.h" +#include "stralloc.h" + +int address_match(stralloc *, int, stralloc *, struct constmap *, int, stralloc *); +int matchregex(char *, char *); +void setdotChar(char); +#endif diff -ruN ../netqmail-1.06-original/readwrite.h netqmail-1.06/readwrite.h --- ../netqmail-1.06-original/readwrite.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/readwrite.h 2019-02-27 20:57:13.405024904 +0100 @@ -1,7 +1,6 @@ #ifndef READWRITE_H #define READWRITE_H -extern int read(); -extern int write(); +#include #endif diff -ruN ../netqmail-1.06-original/received.c netqmail-1.06/received.c --- ../netqmail-1.06-original/received.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/received.c 2019-02-27 20:57:13.405024904 +0100 @@ -21,6 +21,9 @@ return 0; } +char *relayclient; +int relayclientlen; + void safeput(qqt,s) struct qmail *qqt; char *s; @@ -58,9 +61,12 @@ qmail_puts(qqt," ("); if (remoteinfo) { safeput(qqt,remoteinfo); - qmail_puts(qqt,"@"); } - safeput(qqt,remoteip); + relayclient = env_get("RELAYCLIENT"); + if (!relayclient) { + if (remoteinfo) { qmail_puts(qqt,"@"); } + safeput(qqt,remoteip); + } qmail_puts(qqt,")\n by "); safeput(qqt,local); qmail_puts(qqt," with "); diff -ruN ../netqmail-1.06-original/remoteinfo.c netqmail-1.06/remoteinfo.c --- ../netqmail-1.06-original/remoteinfo.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/remoteinfo.c 2019-02-27 20:57:13.405024904 +0100 @@ -44,12 +44,12 @@ s = socket(AF_INET,SOCK_STREAM,0); if (s == -1) return 0; - byte_zero(&sin,sizeof(sin)); +/* byte_zero(&sin,sizeof(sin)); sin.sin_family = AF_INET; byte_copy(&sin.sin_addr,4,ipl); sin.sin_port = 0; - if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } - if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; } + if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } */ + if (timeoutconn(s,ipr,ipl,113,timeout) == -1) { close(s); return 0; } fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY); len = 0; diff -ruN ../netqmail-1.06-original/scan.h netqmail-1.06/scan.h --- ../netqmail-1.06-original/scan.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/scan.h 2019-02-27 20:57:13.405024904 +0100 @@ -2,6 +2,7 @@ #define SCAN_H extern unsigned int scan_uint(); +extern unsigned int scan_int(); extern unsigned int scan_xint(); extern unsigned int scan_nbbint(); extern unsigned int scan_ushort(); diff -ruN ../netqmail-1.06-original/scan_ulong.c netqmail-1.06/scan_ulong.c --- ../netqmail-1.06-original/scan_ulong.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/scan_ulong.c 2019-02-27 20:57:13.405024904 +0100 @@ -9,3 +9,43 @@ { result = result * 10 + c; ++pos; } *u = result; return pos; } + +unsigned int +scan_int(s, i) + register char *s; + register int *i; +{ + register unsigned int pos; + register int result; + register unsigned char c; + int sign; + + pos = 0; + result = 0; + sign = 1; + /*- + * determine sign of the number + */ + switch (s[0]) + { + case '\0': + return 0; + case '-': + ++pos; + sign = -1; + break; + case '+': + ++pos; + sign = 1; + break; + default: + break; + } + while ((c = (unsigned char)(s[pos] - '0')) < 10) + { + result = result * 10 + c; + ++pos; + } + *i = result * sign; + return pos; +} diff -ruN ../netqmail-1.06-original/scan_xlong.c netqmail-1.06/scan_xlong.c --- ../netqmail-1.06-original/scan_xlong.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/scan_xlong.c 2019-02-27 20:57:13.405024904 +0100 @@ -0,0 +1,47 @@ +/* + * $Log: scan_xlong.c,v $ + * Revision 1.2 2005-06-15 22:35:48+05:30 Cprogrammer + * added RCS version information + * + * Revision 1.1 2005-06-15 22:11:59+05:30 Cprogrammer + * Initial revision + * + */ +#include "scan.h" + +static int +fromhex(unsigned char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +unsigned int +scan_xlong(char *src, unsigned long *dest) +{ + register const char *tmp = src; + register int l = 0; + register unsigned char c; + while ((c = fromhex(*tmp)) < 16) + { + l = (l << 4) + c; + ++tmp; + } + *dest = l; + return tmp - src; +} + +void +getversion_scan_xlong_c() +{ + static char *x = "$Id: scan_xlong.c,v 1.2 2005-06-15 22:35:48+05:30 Cprogrammer Stab mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/select.h2 netqmail-1.06/select.h2 --- ../netqmail-1.06-original/select.h2 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/select.h2 2019-02-27 20:57:13.405024904 +0100 @@ -1,6 +1,12 @@ #ifndef SELECT_H #define SELECT_H +#include +#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) +# include +# undef __FD_SETSIZE +# define __FD_SETSIZE 131077 +#endif #include #include #include diff -ruN ../netqmail-1.06-original/socket_v4mappedprefix.c netqmail-1.06/socket_v4mappedprefix.c --- ../netqmail-1.06-original/socket_v4mappedprefix.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/socket_v4mappedprefix.c 2019-02-27 20:57:13.406024893 +0100 @@ -0,0 +1,9 @@ +/* + * $Log: socket_v4mappedprefix.c,v $ + * Revision 1.1 2005-06-15 22:12:51+05:30 Cprogrammer + * Initial revision + * + */ +#ifdef IPV6 +unsigned char V4mappedprefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; +#endif diff -ruN ../netqmail-1.06-original/socket_v6any.c netqmail-1.06/socket_v6any.c --- ../netqmail-1.06-original/socket_v6any.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/socket_v6any.c 2019-02-27 20:57:13.406024893 +0100 @@ -0,0 +1,9 @@ +/* + * $Log: socket_v6loopback.c,v $ + * Revision 1.1 2005-06-15 22:13:07+05:30 Cprogrammer + * Initial revision + * + */ +#ifdef IPV6 +const unsigned char V6any[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#endif diff -ruN ../netqmail-1.06-original/softwarelicense1-1.html netqmail-1.06/softwarelicense1-1.html --- ../netqmail-1.06-original/softwarelicense1-1.html 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/softwarelicense1-1.html 2019-02-27 20:57:13.406024893 +0100 @@ -0,0 +1,59 @@ + + + +Yahoo! DomainKeys Public License Agreement v1.0 + + + + +Yahoo! DomainKeys Public License Agreement v1.1
+(this "Agreement") +

Copyright (c) 2004, Yahoo! Inc.
+ All rights reserved.

+ +
+(Available online) +
+ + +

This Agreement is between Licensor and You. You agree to be bound by all the terms and conditions set forth below, and, subject to those terms and conditions, You may use the intellectual property described below.

+

1. LICENSE GRANT.

+

1.1. Subject to the terms and conditions of this Agreement, each DomainKeys Developer hereby grants You a royalty-free, perpetual, worldwide, sublicensable, non-exclusive license to use, reproduce, modify, publicly display, publicly perform, and distribute the Licensed Code.

+

1.2. Subject to the terms and conditions of this Agreement, Licensor hereby grants You a royalty-free, perpetual, worldwide, sublicensable, non-exclusive license under its rights to the Yahoo! Patent Claims to make, use, sell, offer for sale, and/or import the Licensed Code for the sole purpose of implementing a sender verification solution in connection with e-mail.

+

2. DEFINITIONS.

+

2.1. "Contributions" means any modifications to the Licensed Code, and/or any portions thereof, that are distributed under this Agreement. A Contribution includes, without limitation, any addition to or deletion from the contents of a file containing any Licensed Code, or any new file that contains any part of the Licensed Code.

+

2.2. "DomainKeys Developer(s)" means Yahoo, Inc. ("Yahoo!"), Licensor, and/or any other individual or entity who distributes code under this Agreement.

+

2.3. "Licensed Code" means the Original Code, any Contributions (whether made by You or any DomainKeys Developer other than You), and the combination of Original Code and any such Contributions.

+

2.4. "Licensor" means Yahoo! or any other individual or entity that elects to use this Agreement to license intellectual property to any licensee.

+

2.5. "Original Code" means the source code and binary code that is based on the Specifications and distributed by or on behalf of Yahoo! under this Agreement for the sole purpose of implementing a sender verification solution in connection with e-mail, including any updates or upgrades to such code made available by Yahoo!.

+

2.6. "Specifications" means the specification having submission ID "draft-delany-domainkeys-base-01.txt" dated Aug 2004 published through the IETF (Internet Engineering Task Force). The Specifications may be found at the following link:
+http://antispam.yahoo.com/domainkeys/draft-delany-domainkeys-base-02.txt

+

+ 2.7. "Yahoo! Patent Claims" shall mean those claims of all Yahoo! foreign and domestic patents and patent applications that base their priority on U.S. Provisional Patent Application Ser. Nos. 60/497,794, filed Aug. 26, 2003, or 60/553,300, filed Mar. 15, 2004, or U.S. Patent Application Ser. Nos. 10/671,319, filed Sep. 24, 2003, or 10/805,181, filed Mar. 19, 2004.

+

2.8. "You" or "Your" means an individual, company, or other legal entity exercising any rights under this Agreement. Any individual who accepts the terms and conditions of this Agreement on behalf of a company or other legal entity represents and warrants that the individual has the authority to enter into this Agreement on behalf of the company or other legal entity.

+

3. TERMS.

+

3.1. You agree not to assert against Yahoo!, any other DomainKeys Developer or any of their respective licensees under Section 3.4, a patent infringement claim based on the manufacture, use, sale, offer for sale and/or importation of any of the specific portions of a hardware or software implementation expressly required to be compliant with the Specifications for the sole purpose of implementing a sender verification solution in connection with e-mail ("Licensed Code IP Claim").

+

3.2. To indicate your assent to the terms and conditions of this Agreement and in order to obtain a license to use, reproduce, modify, publicly display, publicly perform, distribute, and sublicense Licensed Code, You must:

+

(a) include, attach or preserve the following prominently displayed statement in the Licensed Code: "This code incorporates intellectual property owned by Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License Agreement.";

+

(b) preserve the copyright and other proprietary notices and disclaimers of DomainKeys Developers as they appear in the Licensed Code; and

+

(c) if the Licensed Code developed by You is distributed in source form, You must identify Yourself, in the source code of such Licensed Code, as the originator of any modifications in a manner that reasonably allows subsequent DomainKeys Developers or their licensees to identify the originator of the modifications.

+

3.3. You will not use the name of Yahoo! to endorse or promote any products, services, or Licensed Code without specific prior written permission of Yahoo!. "DomainKeys" is a trademark of Yahoo!. However, You may state Your Licensed Code is "DomainKeys compliant", "supports DomainKeys", or is "DomainKeys-enabled", without citation to Yahoo!. You must create Your own product or service names or trademarks for Your Licensed Code and You agree not to use the term "DomainKeys" in or as part of a name or trademark for Your Licensed Code.

+

3.4. You may choose to distribute Licensed Code or modifications under this Agreement or a different agreement, provided that:

+

(a) a copy of this Agreement or the different agreement is included with each copy of the Licensed Code or modifications along with the following prominently displayed statement: "By using, reproducing, modifying, publicly displaying, publicly performing, distributing, and/or sublicensing this code as permitted, you agree to the terms and conditions of the Yahoo! DomainKeys Public License Agreement or other agreement contained herein."; and

+

(b) if distributed under a different agreement, such different agreement contains terms and conditions that (i) provide no fewer rights, privileges and immunities to DomainKeys Developers than the terms and conditions of this Agreement, including, without limitation, Sections 1.2, 3.1, 3.4, 3.7, 4.1, 4.2, and 4.3, except that You may alter the terms and conditions of Section 1.1 and (ii) apply such terms and conditions to the Licensed Code and/or modifications made by You.

+

3.5. You acknowledge that Licensed Code may be subject to U.S. export restriction and other applicable national and international laws. You agree to comply with all export, re-export, or import restrictions, laws, or regulations.

+

3.6. Yahoo!, and only Yahoo!, may, from time to time and at its sole discretion, update or modify the terms of this Agreement. If Yahoo! makes any such modifications, You may continue under the terms and conditions of this Agreement or agree to the updated or modified terms and conditions. For the most recent version of this Agreement please contact Yahoo!.

+

3.7. This Agreement and the rights hereunder will terminate:
+ (a) automatically without notice from Yahoo!, if You at any time during the term of this Agreement assert any Licensed Code IP Claim against Yahoo!;

+

(b) upon written notice from Yahoo!, if You at any time during the term of this Agreement assert any Licensed Code IP Claim against any DomainKeys Developer (other than Yahoo!) or any licensees of any DomainKeys Developer; or

+

(c) where (a) or (b) do not apply, automatically without notice from Yahoo!, if You fail to comply with any term(s) of this Agreement and fail to cure such breach within 30 days of You becoming aware of such breach.

+

3.8. This Agreement constitutes the entire agreement between the parties with respect to the subject matter hereof. This Agreement shall be governed by and construed under the laws of the United States and the State of California without giving effect to California conflict of law provisions or to construction provisions favoring either party. All actions arising out of or relating to this Agreement that involve Yahoo! as a party will be adjudicated exclusively by the Superior Court of the State of California for the County of Santa Clara or the United States District Court for the Northern District of California.

+

3.9. In the event that any provision of this Agreement is deemed to be invalid, such invalidity shall not affect the remainder of this Agreement.

+

4. LEGAL DISCLAIMERS.

+

4.1. THE YAHOO! PATENT CLAIMS, THIS AGREEMENT, LICENSED CODE, THE DOMAINKEYS TRADEMARK, AND THE SPECIFICATIONS ARE PROVIDED ON AN "AS IS" BASIS, WITHOUT REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of exercising any rights under this Agreement and using the Specifications, Licensed Code, and the DomainKeys trademark and assume all risks associated in connection therewith, including, but not limited to, the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.

+

4.2. You expressly acknowledge and agree that no assurances are provided by DomainKeys Developers with respect to the validity of the Yahoo! Patent Claims or that the Specifications, Licensed Code, the DomainKeys trademark or any implementations related to the Specifications, Licensed Code or the DomainKeys trademark do not infringe or misappropriate the patent, trademark or other intellectual property rights of any other entity. DomainKeys Developers disclaim any liability to You for claims brought by any other person or entity based on infringement or misappropriation of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed.

+

4.3. DOMAINKEYS DEVELOPERS SHALL NOT HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 EXERCISE OF ANY RIGHTS UNDER THIS AGREEMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND EVEN IF THE REMEDIES PROVIDED FOR IN THIS AGREEMENT FAIL OF THEIR ESSENTIAL PURPOSE.

+ + + + diff -ruN ../netqmail-1.06-original/spawn-filter.9 netqmail-1.06/spawn-filter.9 --- ../netqmail-1.06-original/spawn-filter.9 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/spawn-filter.9 2019-02-27 20:57:13.406024893 +0100 @@ -0,0 +1,103 @@ +.TH spawn-filter 8 +.SH NAME +spawn-filter \- Helper for running filters for qmail-local and qmail-remote +.SH SYNOPSIS +.B spawn-filter args +.SH DESCRIPTION +.B spawn-filter +is a utility to help qmail run any filter during local or remote delivery. It +can run any filter which expects to read mess on fd 0 and writes back the message on fd 1. +The filter can be turned on individually for local and remote mails by defining +.B QMAILLOCAL +and +.B QMAILREMOTE +environment variables respectively in +.B qmail-send +supervise or rc script. If spawn-filter is invoked as qmail-local, it executes the +original +.B qmail-local +after runing the mail through the filter. If spawn-filter is invoked as qmail-remote, it +executes the original +.B qmail-remote +after running the mail through the filter. Hence QMAILLOCAL should be set as QMAILHOME/bin/spawn-filter +for filtering local mails and QMAILREMOTE as QMAILHOME/bin/spawn-filter for filtering +remote mails. + +Filters can be run by setting the environment variable +.B FILTERARGS +or by using a control file +.BR filterargs. +The environment variable overrides the control file. +.B spawn-filter +uses /bin/sh to run the filter (with arguments) specified by the FILTERARGS environment variable or the control file +.BR filterargs . +The environment variable FILTERARGS apply to both local and remote mails. For individual domain level control, +it is best to set using the control file filterargs. + +.TP 5 +.I filterargs +The format of this file is of the form +.B domain:args +for both local and remote mails. +.B domain:remote:args +for remote mails and +.B domain:local:args +for local mails. + +.EX +indimail.org:remote:QMAILHOME/bin/dk-filter +.EE + +.TP 0 +The sequence in which the filter program is run is given below + +.TP 5 +1. create two pipes and fork +.TP 5 +2. dup write end of the first pipe to descriptor 1, dup write end of the second pipe to descriptor 2 in the child and exec the filter program +.TP 5 +3. dup read end of the pipe to descriptor 0 in parent and exec qmail-local for local mails and qmail-remote for remote mails. +.TP 5 +4. Wait for filter to exit and read read end of second pipe for any error messages. +.TP 5 +5. Report success or failure +.TP 0 + +This gives the ability for the any filter program to read the mail message from descriptor 0 before +passing it to qmail-local/qmail-remote through the pipe. + +.B spawn-filter +will attempt to make the descriptor 0 seekable if the environment variable MAKE_SEEKABLE +is defined. This may be necessary for certain filter programs which could do lseek(). + +.B spawn-filter +sets the environment variable +.B DOMAIN +to the recipient domain. This can be conveniently used in programs/scripts which get invoked by +setting +.B FILTERARGS +environment variable or by rules in the control file +.BR filterargs . + +.SH "EXIT CODES" +.B spawn-filter +exits 111 for any error or if it is not able to exec +QMAILHOME/bin/qmail-local (for local mails) or +QMAILHOME/bin/qmail-remote (for remote mails). + +.SH "SEE ALSO" +qmail-lspawn(8), +qmail-rspawn(8), +qmail-local(8), +qmail-remote(8), +qmail-smtpd(8), +qmail-control(5), +qmail-queue(8) + +.SH "AUTHORS" + +Manvendra Bhangui. +.SH PROBLEMS +Problems with +.B spawn-filter +should be forwarded to "Manvendra Bhangui" diff -ruN ../netqmail-1.06-original/spawn-filter.c netqmail-1.06/spawn-filter.c --- ../netqmail-1.06-original/spawn-filter.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/spawn-filter.c 2020-04-09 19:45:45.390398843 +0200 @@ -0,0 +1,506 @@ +/* + * netqmail-version without spam filter + * + * $Log: spawn-filter.c,v $ + * Revision 1.41 2009-04-03 11:42:48+05:30 Cprogrammer + * create pipe for error messages + * + * Revision 1.40 2009-04-02 15:17:54+05:30 Cprogrammer + * unset QMAILLOCAL in qmail-remote and unset QMAILREMOTE in qmail-local + * + * Revision 1.39 2008-06-12 08:40:55+05:30 Cprogrammer + * added rulesfile argument + * + * Revision 1.38 2008-05-25 17:16:43+05:30 Cprogrammer + * made message more readable by adding a blank space + * + * Revision 1.37 2007-12-20 13:51:54+05:30 Cprogrammer + * avoid loops with FILTERARGS, SPAMFILTERARGS + * removed compiler warning + * + * Revision 1.36 2006-06-07 14:11:28+05:30 Cprogrammer + * added SPAMEXT, SPAMHOST, SPAMSENDER, QQEH environment variable + * unset FILTERARGS before calling filters + * + * Revision 1.35 2006-01-22 10:14:45+05:30 Cprogrammer + * BUG fix for spam mails wrongly getting blackholed + * + * Revision 1.34 2005-08-23 17:36:48+05:30 Cprogrammer + * gcc 4 compliance + * delete sender in spam notification + * + * Revision 1.33 2005-04-02 19:07:47+05:30 Cprogrammer + * use internal wildmat version + * + * Revision 1.32 2004-11-22 19:50:53+05:30 Cprogrammer + * include regex.h after sys/types.h to avoid compilation prob on RH 7.3 + * + * Revision 1.31 2004-10-22 20:30:35+05:30 Cprogrammer + * added RCS id + * + * Revision 1.30 2004-10-21 21:56:21+05:30 Cprogrammer + * change for two additional arguments to strerr_die() + * + * Revision 1.29 2004-10-11 14:06:14+05:30 Cprogrammer + * use control_readulong instead of control_readint + * + * Revision 1.28 2004-09-22 23:14:20+05:30 Cprogrammer + * replaced atoi() with scan_int() + * + * Revision 1.27 2004-09-08 10:54:49+05:30 Cprogrammer + * incorrect exit code in report() function for remote + * mails. Caused qmail-rspawn to report "Unable to run qmail-remote" + * + * Revision 1.26 2004-07-17 21:23:31+05:30 Cprogrammer + * change qqeh code in qmail-remote + * + * Revision 1.25 2004-07-15 23:40:46+05:30 Cprogrammer + * fixed compilation warning + * + * Revision 1.24 2004-07-02 16:15:25+05:30 Cprogrammer + * override control files rejectspam, spamredirect by + * environment variables REJECTSPAM and SPAMREDIRECT + * allow patterns in domain specification in the control files + * spamfilterargs, filterargs, rejectspam and spamredirect + * + * Revision 1.23 2004-06-03 22:58:34+05:30 Cprogrammer + * fixed compilation problem without indimail + * + * Revision 1.22 2004-05-23 22:18:17+05:30 Cprogrammer + * added envrules filename as argument + * + * Revision 1.21 2004-05-19 23:15:07+05:30 Cprogrammer + * added comments + * + * Revision 1.20 2004-05-12 22:37:47+05:30 Cprogrammer + * added check DATALIMIT check + * + * Revision 1.19 2004-05-03 22:17:36+05:30 Cprogrammer + * use QUEUE_BASE instead of auto_qmail + * + * Revision 1.18 2004-02-13 14:51:24+05:30 Cprogrammer + * added envrules + * + * Revision 1.17 2004-01-20 06:56:56+05:30 Cprogrammer + * unset FILTERARGS for notifications + * + * Revision 1.16 2004-01-20 01:52:08+05:30 Cprogrammer + * report string length corrected + * + * Revision 1.15 2004-01-10 09:44:36+05:30 Cprogrammer + * added comment for exit codes of bogofilter + * + * Revision 1.14 2004-01-08 00:32:49+05:30 Cprogrammer + * use TMPDIR environment variable for temporary directory + * send spam reports to central spam logger + * + * Revision 1.13 2003-12-30 00:44:42+05:30 Cprogrammer + * set argv[0] from spamfilterprog + * + * Revision 1.12 2003-12-22 18:34:25+05:30 Cprogrammer + * replaced spfcheck() with address_match() + * + * Revision 1.11 2003-12-20 01:35:06+05:30 Cprogrammer + * added wait_pid to prevent zombies + * + * Revision 1.10 2003-12-17 23:33:39+05:30 Cprogrammer + * improved logic for getting remote/local tokens + * + * Revision 1.9 2003-12-16 10:38:24+05:30 Cprogrammer + * fixed incorrect address being returned if filterargs contained local: or + * remote: directives + * + * Revision 1.8 2003-12-15 20:46:19+05:30 Cprogrammer + * added case 100 to bounce mail + * + * Revision 1.7 2003-12-15 13:51:44+05:30 Cprogrammer + * code to run additional filters using /bin/sh + * + * Revision 1.6 2003-12-14 11:36:18+05:30 Cprogrammer + * added option to blackhole spammers + * + * Revision 1.5 2003-12-13 21:08:46+05:30 Cprogrammer + * extensive rewrite + * common report() function for local/remote mails to report errors + * + * Revision 1.4 2003-12-12 20:20:55+05:30 Cprogrammer + * use -a option to prevent using header addresses + * + * Revision 1.3 2003-12-09 23:37:16+05:30 Cprogrammer + * change for spawn-filter to be called as qmail-local or qmail-remote + * + * Revision 1.2 2003-12-08 23:48:23+05:30 Cprogrammer + * new function getDomainToken() to retrieve domain specific values + * read rejectspam and spamredirect only if SPAMEXITCODE is set + * + * Revision 1.1 2003-12-07 13:02:00+05:30 Cprogrammer + * Initial revision + * + */ +#include "fmt.h" +#include "str.h" +#include "strerr.h" +#include "env.h" +#include "substdio.h" +#include "subfd.h" +#include "stralloc.h" +#include "error.h" +#include "control.h" +#include "wait.h" +#include "qregex.h" +#include "auto_qmail.h" +#include +#include +#include +#include +#include + +#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE) +#define REGEXEC(X,Y) regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0) + +static int mkTempFile(int); +static void report(int, char *, char *, char *, char *, char *, char *); +char *getDomainToken(char *, stralloc *); +static int run_mailfilter(char *, char *, char **); +int wildmat_internal(char *, char *); + +static int remotE; +stralloc sender = { 0 }; +stralloc recipient = { 0 }; + +static void +report(int errCode, char *s1, char *s2, char *s3, char *s4, char *s5, char *s6) +{ + if (!remotE) /*- strerr_die does not return */ + strerr_die(errCode, s1, s2, s3, s4, s5, s6, 0, 0, (struct strerr *) 0); + /*- h - hard, s - soft */ + if (substdio_put(subfdoutsmall, errCode == 111 ? "s" : "h", 1) == -1) + _exit(111); + if (s1 && substdio_puts(subfdoutsmall, s1) == -1) + _exit(111); + if (s2 && substdio_puts(subfdoutsmall, s2) == -1) + _exit(111); + if (s3 && substdio_puts(subfdoutsmall, s3) == -1) + _exit(111); + if (s4 && substdio_puts(subfdoutsmall, s4) == -1) + _exit(111); + if (s5 && substdio_puts(subfdoutsmall, s5) == -1) + _exit(111); + if (s6 && substdio_puts(subfdoutsmall, s6) == -1) + _exit(111); + if (substdio_put(subfdoutsmall, "\0", 1) == -1) + _exit(111); + if (substdio_puts(subfdoutsmall, + errCode == 111 ? "Zspawn-filter said: Message deferred" : "DGiving up on spawn-filter\n") == -1) + _exit(111); + if (substdio_put(subfdoutsmall, "\0", 1) == -1) + _exit(111); + substdio_flush(subfdoutsmall); + /*- For qmail-rspawn to stop complaining unable to run qmail-remote */ + _exit(0); +} + +void +set_environ(char *host, char *sender, char *recipient) +{ + if (!env_put2("DOMAIN", host)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!env_put2("_SENDER", sender)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!env_put2("_RECIPIENT", recipient)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + return; +} + +static int +run_mailfilter(char *domain, char *mailprog, char **argv) +{ + char strnum[FMT_ULONG]; + pid_t filt_pid; + int pipefd[2], pipefe[2]; + int wstat, filt_exitcode, len = 0; + char *filterargs; + static stralloc filterdefs = { 0 }; + static char errstr[1024]; + char inbuf[1024]; + char ch; + static substdio errbuf; + + if (!(filterargs = env_get("FILTERARGS"))) + { + if (control_readfile(&filterdefs, "control/filterargs", 0) == -1) + report(111, "spawn-filter: Unable to read filterargs: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + filterargs = getDomainToken(domain, &filterdefs); + } + if (!filterargs) + { + execv(mailprog, argv); + report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0); + _exit(111); /*- To make compiler happy */ + } + if (pipe(pipefd) == -1) + report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (pipe(pipefe) == -1) + report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + switch ((filt_pid = fork())) + { + case -1: + report(111, "spawn-filter: Trouble creating child filter: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + case 0: /*- Filter Program */ + set_environ(domain, sender.s, recipient.s); + /*- Mail content read from fd 0 */ + if (mkTempFile(0)) + report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + /*- stdout will go here */ + if (dup2(pipefd[1], 1) == -1 || close(pipefd[0]) == -1) + report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (pipefd[1] != 1) + close(pipefd[1]); + /*- stderr will go here */ + if (dup2(pipefe[1], 2) == -1 || close(pipefe[0]) == -1) + report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (pipefe[1] != 2) + close(pipefe[1]); + /*- Avoid loop if program(s) defined by FILTERARGS call qmail-inject, etc */ + if (!env_unset("FILTERARGS") || !env_unset("SPAMFILTER")) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + execl("/bin/sh", "IndiMailfilter", "-c", filterargs, (char *) 0); + report(111, "spawn-filter: could not exec /bin/sh: ", filterargs, ": ", error_str(errno), ". (#4.3.0)", 0); + default: + close(pipefe[1]); + close(pipefd[1]); + if (dup2(pipefd[0], 0)) + { + close(pipefd[0]); + close(pipefe[0]); + wait_pid(&wstat, filt_pid); + report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } + if (pipefd[0] != 0) + close(pipefd[0]); + if (mkTempFile(0)) + { + close(0); + close(pipefe[0]); + wait_pid(&wstat, filt_pid); + report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } + break; + } + /*- Process message if exit code is 0, bounce if 100 */ + if (wait_pid(&wstat, filt_pid) != filt_pid) + { + close(0); + close(pipefe[0]); + report(111, "spawn-filter: waitpid surprise: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } + if (wait_crashed(wstat)) + { + close(0); + close(pipefe[0]); + report(111, "spawn-filter: filter crashed: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } + switch (filt_exitcode = wait_exitcode(wstat)) + { + case 0: + execv(mailprog, argv); + report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0); + case 100: + report(100, "Mail Rejected (#5.7.1)", 0, 0, 0, 0, 0); + default: + substdio_fdbuf(&errbuf, read, pipefe[0], inbuf, sizeof(inbuf)); + for (len = 0; substdio_bget(&errbuf, &ch, 1) && len < (sizeof(errstr) - 1); len++) + errstr[len] = ch; + errstr[len] = 0; + strnum[fmt_ulong(strnum, filt_exitcode)] = 0; + report(111, filterargs, ": (spawn-filter) exit code: ", strnum, *errstr ? ": " : 0, *errstr ? errstr : 0, ". (#4.3.0)"); + } + /*- Not reached */ + return(111); +} + +char * +getDomainToken(char *domain, stralloc *sa) +{ + regex_t qreg; + int len, n, retval; + char *ptr, *p; + char errbuf[512]; + + for (len = 0, ptr = sa->s;len < sa->len;) + { + len += ((n = str_len(ptr)) + 1); + for (p = ptr;*p && *p != ':';p++); + if (*p == ':') + { + *p = 0; + /*- build the regex */ + if ((retval = str_diff(ptr, domain))) + { + if (env_get("QREGEX")) + { + if ((retval = REGCOMP(qreg, ptr)) != 0) + { + regerror(retval, &qreg, errbuf, sizeof(errbuf)); + regfree(&qreg); + report(111, "spawn-filter: ", ptr, ": ", errbuf, ". (#4.3.0)", 0); + } + retval = REGEXEC(qreg, domain); + regfree(&qreg); + } else + retval = !wildmat_internal(domain, ptr); + } + *p = ':'; + if (!retval) /*- match occurred for domain or wildcard */ + { + /* check for local/remote directives */ + if (remotE) + { + if (!str_diffn(p + 1, "remote:", 7)) + return (p + 8); + if (!str_diffn(p + 1, "local:", 6)) + { + ptr = sa->s + len; + continue; /*- skip local directives for remote mails */ + } + } else + { + if (!str_diffn(p + 1, "local:", 6)) + return (p + 7); + if (!str_diffn(p + 1, "remote:", 7)) + { + ptr = sa->s + len; + continue; /*- skip remote directives for remote mails */ + } + } + return (p + 1); + } + } + ptr = sa->s + len; + } + return ((char *) 0); +} + +int +mkTempFile(int seekfd) +{ + char inbuf[2048], outbuf[2048], strnum[FMT_ULONG]; + char *tmpdir; + static stralloc tmpFile = {0}; + struct substdio _ssin; + struct substdio _ssout; + int fd; + + if (lseek(seekfd, 0, SEEK_SET) == 0) + return (0); + if (errno == EBADF) + { + strnum[fmt_ulong(strnum, seekfd)] = 0; + report(111, "spawn-filter: fd ", strnum, ": ", error_str(errno), ". (#4.3.0)", 0); + } + if (!(tmpdir = env_get("TMPDIR"))) + tmpdir = "/tmp"; + if (!stralloc_copys(&tmpFile, tmpdir)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_cats(&tmpFile, "/qmailFilterXXX")) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_catb(&tmpFile, strnum, fmt_ulong(strnum, (unsigned long) getpid()))) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_0(&tmpFile)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if ((fd = open(tmpFile.s, O_RDWR | O_EXCL | O_CREAT, 0600)) == -1) + report(111, "spawn-filter: ", tmpFile.s, ": ", error_str(errno), ". (#4.3.0)", 0); + unlink(tmpFile.s); + substdio_fdbuf(&_ssout, write, fd, outbuf, sizeof(outbuf)); + substdio_fdbuf(&_ssin, read, seekfd, inbuf, sizeof(inbuf)); + switch (substdio_copy(&_ssout, &_ssin)) + { + case -2: /*- read error */ + report(111, "spawn-filter: read error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + case -3: /*- write error */ + report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } + if (substdio_flush(&_ssout) == -1) + report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (dup2(fd, seekfd) == -1) + report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (lseek(seekfd, 0, SEEK_SET) != 0) + report(111, "spawn-filter: lseek: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + return (0); +} + +int +main(int argc, char **argv) +{ + char *ptr, *mailprog, *domain, *ext; + int len; + + len = str_len(argv[0]); + for (ptr = argv[0] + len;*ptr != '/' && ptr != argv[0];ptr--); + if (*ptr && *ptr == '/') + ptr++; + ptr += 6; + if (*ptr == 'l') /*- qmail-local Filter */ + { + mailprog = "bin/qmail-local"; + ext = argv[6]; + domain = argv[7]; + remotE = 0; + if (!env_unset("QMAILREMOTE")) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + /*- sender */ + if (!stralloc_copys(&sender, argv[8])) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_0(&sender)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + /*- recipient */ + if (*ext) { /*- EXT */ + if (!stralloc_copys(&recipient, ext)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } else /*- user */ + if (!stralloc_copys(&recipient, argv[2])) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_cats(&recipient, "@")) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_cats(&recipient, domain)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_0(&recipient)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } else + if (*ptr == 'r') /*- qmail-remote Filter */ + { + mailprog = "bin/qmail-remote"; + domain = argv[1]; + remotE = 1; + if (!env_unset("QMAILLOCAL")) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + /*- sender */ + if (!stralloc_copys(&sender, argv[2])) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_0(&sender)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + /*- recipient */ + if (!stralloc_copys(&recipient, argv[3])) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + if (!stralloc_0(&recipient)) + report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0); + } else + { + report(111, "spawn-filter: Incorrect usage. ", argv[0], " (#4.3.0)", 0, 0, 0); + _exit(111); + } + if (chdir(auto_qmail) == -1) + report(111, "spawn-filter: Unable to switch to ", auto_qmail, ": ", error_str(errno), ". (#4.3.0)", 0); + run_mailfilter(domain, mailprog, argv); + report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0); + /*- Not reached */ + return(0); +} + +void +getversion_qmail_spawn_filter_c() +{ + static char *x = "$Id: spawn-filter.c,v 1.41 2009-04-03 11:42:48+05:30 Cprogrammer Stab mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/spawn.c netqmail-1.06/spawn.c --- ../netqmail-1.06-original/spawn.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06/spawn.c 2019-02-27 20:57:13.407024882 +0100 @@ -1,4 +1,4 @@ -#include +#include "select.h" #include #include "sig.h" #include "wait.h" @@ -7,7 +7,6 @@ #include "str.h" #include "alloc.h" #include "stralloc.h" -#include "select.h" #include "exit.h" #include "coe.h" #include "open.h" @@ -64,7 +63,7 @@ int flagreading = 1; char outbuf[1024]; substdio ssout; -int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ +int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */ int flagabort = 0; /* if 1, everything except delnum is garbage */ int delnum; stralloc messid = {0}; @@ -74,6 +73,7 @@ void err(s) char *s; { char ch; ch = delnum; substdio_put(&ssout,&ch,1); + ch = delnum >> 8; substdio_put(&ssout,&ch,1); substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1); } @@ -156,16 +156,19 @@ { case 0: delnum = (unsigned int) (unsigned char) ch; - messid.len = 0; stage = 1; break; + stage = 1; break; case 1: + delnum += (unsigned int) ((unsigned int) ch) << 8; + messid.len = 0; stage = 2; break; + case 2: if (!stralloc_append(&messid,&ch)) flagabort = 1; if (ch) break; - sender.len = 0; stage = 2; break; - case 2: + sender.len = 0; stage = 3; break; + case 3: if (!stralloc_append(&sender,&ch)) flagabort = 1; if (ch) break; - recip.len = 0; stage = 3; break; - case 3: + recip.len = 0; stage = 4; break; + case 4: if (!stralloc_append(&recip,&ch)) flagabort = 1; if (ch) break; docmd(); @@ -202,7 +205,8 @@ initialize(argc,argv); - ch = auto_spawn; substdio_putflush(&ssout,&ch,1); + ch = auto_spawn; substdio_put(&ssout,&ch,1); + ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1); for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; } @@ -237,7 +241,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 ../netqmail-1.06-original/spf.c netqmail-1.06/spf.c --- ../netqmail-1.06-original/spf.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/spf.c 2019-02-27 20:57:13.407024882 +0100 @@ -0,0 +1,877 @@ +#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 "case.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 *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; + int guessing = 0; + 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 */ + guessing = 1; + 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_ERROR: + if (!guessing) + break; + if (local_pos >= 0 && begin >= local_pos) + break; + hdr_none(); + q = SPF_NONE; + 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(char *remoteip) +{ + stralloc domain = {0}; + 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 ../netqmail-1.06-original/spf.h netqmail-1.06/spf.h --- ../netqmail-1.06-original/spf.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/spf.h 2019-02-27 20:57:13.407024882 +0100 @@ -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 ../netqmail-1.06-original/spfquery.c netqmail-1.06/spfquery.c --- ../netqmail-1.06-original/spfquery.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/spfquery.c 2019-02-27 20:57:13.407024882 +0100 @@ -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 ../netqmail-1.06-original/srs.c netqmail-1.06/srs.c --- ../netqmail-1.06-original/srs.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/srs.c 2019-02-27 20:57:13.408024870 +0100 @@ -0,0 +1,166 @@ +#include +#include +#include +#include "auto_qmail.h" +#include "stralloc.h" +#include "srs.h" + +static stralloc srs_domain = {0}; +static stralloc srs_secrets = {0}; +static unsigned int srs_maxage = 0; +static unsigned int srs_hashlength = 0; +static unsigned int srs_hashmin = 0; +static unsigned int srs_alwaysrewrite = 0; +static stralloc srs_separator = {0}; + +stralloc srs_result = {0}; +stralloc srs_error = {0}; + +static int setup_ok = 0; +static int srs_secrets_ok = 0; + +static int setup(int with_rcpthosts) { + + if (setup_ok == 1) return 1; + + if (chdir(auto_qmail) == -1) return -1; + if (control_init() == -1) return -1; + + if (control_readline(&srs_domain,"control/srs_domain") == -1) return -1; + if (srs_domain.len) { + if (!stralloc_0(&srs_domain)) return -2; + } else { + return 0; + } + + srs_secrets_ok = control_readfile(&srs_secrets,"control/srs_secrets",0); + if (srs_secrets_ok == -1) return -1; + + if (control_readint(&srs_maxage,"control/srs_maxage") == -1) return 0; + if (control_readint(&srs_hashlength,"control/srs_hashlength") == -1) return 0; + if (control_readint(&srs_hashmin,"control/srs_hashmin") == -1) return 0; + if (srs_hashmin > srs_hashlength) srs_hashmin = srs_hashlength; + + if (control_readint(&srs_alwaysrewrite,"control/srs_alwaysrewrite") == -1) return 0; + + if (control_readline(&srs_separator,"control/srs_separator") == -1) return -1; + if (srs_separator.len && !stralloc_0(&srs_separator)) return -2; + if (srs_separator.len && srs_separator.s[0] != '-' && srs_separator.s[0] != '+' && srs_separator.s[0] != '=') { + if (!stralloc_copys(&srs_separator,"")) return -2; + } + + if (!srs_alwaysrewrite) { + if (with_rcpthosts && rcpthosts_init() == -1) return -1; + } + + setup_ok = 1; + return 1; + +} + +static int srs_error_str(int code) { + if (!stralloc_copys(&srs_error,"SRS: ")) return -2; + if (!stralloc_cats(&srs_error,srs_strerror(code))) return -2; + if (!stralloc_0(&srs_error)) return -2; + return -3; +} + + +int srsforward(char *address) { + int x = 0; + + /* Return if setup was unsucessfull */ + x = setup(1); + if (x < 1) return(x); + + /* Return zero if null-sender */ + x = str_len(address); + if (x <= 1) return 0; + + /* Return zero if local address */ + if (!srs_alwaysrewrite && rcpthosts(address,x) == 1) return 0; + + /* Now it's time to rewrite the envelope */ + char srsaddress[1000]; + + srs_t *srs; + srs = srs_new(); + if (srs_maxage > 0) srs->maxage = srs_maxage; + if (srs_hashlength > 0) srs->hashlength = srs_hashlength; + if (srs_hashmin > 0) srs->hashmin = srs_hashmin; + + if (srs_alwaysrewrite){ + x = srs_set_alwaysrewrite(srs, TRUE); + if (x != SRS_SUCCESS) return srs_error_str(x); + } + + if (srs_separator.len) { + x = srs_set_separator(srs, srs_separator.s[0]); + if (x != SRS_SUCCESS) return srs_error_str(x); + } + + int i = 0; + int j = 0; + for (j = 0;j < srs_secrets.len;++j) + if (!srs_secrets.s[j]) { + x = srs_add_secret(srs, srs_secrets.s + i); + if (x != SRS_SUCCESS) return srs_error_str(x); + i = j + 1; + } + + x = srs_forward(srs, srsaddress, 1000, address, srs_domain.s); + if (x != SRS_SUCCESS) return srs_error_str(x); + + if (!stralloc_copys(&srs_result,srsaddress)) return -2; + if (!stralloc_0(&srs_result)) return -2; + + srs_free(srs); + + return 1; +} + +int srsreverse(char *srsaddress) { + int x = 0; + + /* Return if setup was unsucessfull */ + x = setup(0); + if (x < 1) return(x); + + /* Return error if null-sender */ + x = str_len(srsaddress); + if (x <= 1) return -3; + + /* Now it's time to rewrite the envelope */ + char address[1000]; + + srs_t *srs; + srs = srs_new(); + if (srs_maxage > 0) srs->maxage = srs_maxage; + if (srs_hashlength > 0) srs->hashlength = srs_hashlength; + if (srs_hashmin > 0) srs->hashmin = srs_hashmin; + + if (srs_separator.len) { + x = srs_set_separator(srs, srs_separator.s[0]); + if (x != SRS_SUCCESS) return srs_error_str(x); + } + + int i = 0; + int j = 0; + for (j = 0;j < srs_secrets.len;++j) + if (!srs_secrets.s[j]) { + x = srs_add_secret(srs, srs_secrets.s + i); + if (x != SRS_SUCCESS) return srs_error_str(x); + i = j + 1; + } + + x = srs_reverse(srs, address, 1000, srsaddress); + if (x != SRS_SUCCESS) return srs_error_str(x); + + if (!stralloc_copys(&srs_result,address)) return -2; + if (!stralloc_0(&srs_result)) return -2; + + srs_free(srs); + + return 1; +} + diff -ruN ../netqmail-1.06-original/srs.h netqmail-1.06/srs.h --- ../netqmail-1.06-original/srs.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/srs.h 2019-02-27 20:57:13.408024870 +0100 @@ -0,0 +1,9 @@ +#ifndef SRS_H +#define SRS_H + +extern stralloc srs_result; +extern stralloc srs_error; +extern int srsforward(char *); +extern int srsreverse(char *); + +#endif diff -ruN ../netqmail-1.06-original/srsfilter.c netqmail-1.06/srsfilter.c --- ../netqmail-1.06-original/srsfilter.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/srsfilter.c 2019-02-27 20:57:13.408024870 +0100 @@ -0,0 +1,137 @@ +#include "sig.h" +#include "readwrite.h" +#include "exit.h" +#include "env.h" +#include "qmail.h" +#include "strerr.h" +#include "substdio.h" +#include "fmt.h" +#include "stralloc.h" +#include "srs.h" + +#define FATAL "srsfilter: fatal: " +#define IGNORE "srsfilter: ignore: " + +void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); } + +struct qmail qqt; + +stralloc line = {0}; +int flagbody = 0; +int flagnewline = 0; +int flagto = 0; +int seento = 0; + +void newheader() { + if (!stralloc_copyb(&line,"To: ",4)) die_nomem(); + if (!stralloc_cat(&line,&srs_result)) die_nomem(); + ++flagto; ++seento; +} + +void skipheader() { + if (!stralloc_copys(&line,"")) die_nomem(); +} + +void printheader() { + qmail_put(&qqt, line.s, line.len); + qmail_put(&qqt,"\n",1); + if (!stralloc_copys(&line,"")) die_nomem(); +} + +int mywrite(fd,buf,len) int fd; char *buf; int len; +{ + int i; + if (flagbody) { + qmail_put(&qqt,buf,len); + return len; + } else { + i = 0; + while (buf[i]) { + if (buf[i] == '\n') { + if (flagnewline) { + if (!seento) { newheader(); printheader(); } + qmail_put(&qqt,"\n",1); i++; flagbody = 1; continue; + } + if (flagto && (line.s[0] == ' ' || line.s[0] == '\t')) { + skipheader(); i++; continue; + } + if (line.len > 2 && line.s[2] == ':' && (line.s[1] == 'o' || + line.s[1] == 'O') && (line.s[0] == 'T' || line.s[0] == 't')) { + if (seento) { skipheader(); i++; continue; } + newheader(); + } else { flagto = 0; } + printheader(); + flagnewline = 1; + } else { + if (!stralloc_append(&line,&buf[i])) die_nomem(); + flagnewline = 0; + } + ++i; + } + return len; + } +} + +char inbuf[SUBSTDIO_INSIZE]; +char outbuf[1]; +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf); +substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf); + +char num[FMT_ULONG]; + +void main(argc,argv) +int argc; +char **argv; +{ + char *ext2; + char *host; + char *sender; + char *qqx; + + sig_pipeignore(); + + sender = env_get("SENDER"); + if (!sender) + strerr_die2x(100,FATAL,"SENDER not set"); + if (str_len(sender)) { + /* Return zero, the message will not bounce back */ + strerr_die2x(0,IGNORE,"SENDER must be empty"); + } + ext2 = env_get("EXT2"); + if (!ext2) + strerr_die2x(100,FATAL,"EXT2 not set"); + host = env_get("HOST"); + if (!host) + strerr_die2x(100,FATAL,"HOST not set"); + + switch(srsreverse(ext2)) { + case -3: strerr_die2x(100,FATAL,srs_error.s); break; + case -2: die_nomem(); break; + case -1: strerr_die2x(111,FATAL,"unable to read controls"); break; + case 0: strerr_die2x(100,FATAL,"unable to rewrite envelope"); break; + } + + if (qmail_open(&qqt) == -1) + strerr_die2x(111,FATAL,"unable to fork"); + if (substdio_copy(&ssout,&ssin) != 0) + strerr_die2x(111,FATAL,"unable to read message"); + substdio_flush(&ssout); + + if (!flagbody) { + qmail_fail(&qqt); + strerr_die2x(100,FATAL,"unable to read message body"); + } + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + /* Always from nullsender */ + qmail_from(&qqt,""); + + qmail_to(&qqt,srs_result.s); + + qqx = qmail_close(&qqt); + if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + strerr_die2x(0,"srsfilter: qp ",num); + +} + diff -ruN ../netqmail-1.06-original/ssl_timeoutio.c netqmail-1.06/ssl_timeoutio.c --- ../netqmail-1.06-original/ssl_timeoutio.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/ssl_timeoutio.c 2020-01-10 21:51:32.057157279 +0100 @@ -0,0 +1,126 @@ +#ifdef TLS +#include "select.h" +#include "error.h" +#include "ndelay.h" +#include "now.h" +#include "ssl_timeoutio.h" + +int ssl_timeoutio(int (*fun)(), + int t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + int n; + const datetime_sec end = (datetime_sec)t + now(); + + do { + fd_set fds; + struct timeval tv; + + const int r = buf ? fun(ssl, buf, len) : fun(ssl); + if (r > 0) return r; + + t = end - now(); + if (t < 0) break; + tv.tv_sec = (time_t)t; tv.tv_usec = 0; + + FD_ZERO(&fds); + switch (SSL_get_error(ssl, r)) + { + default: return r; /* some other error */ + case SSL_ERROR_WANT_READ: + FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv); + break; + case SSL_ERROR_WANT_WRITE: + FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv); + break; + } + + /* n is the number of descriptors that changed status */ + } while (n > 0); + + if (n != -1) errno = error_timeout; + return -1; +} + +int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl) +{ + int r=0; + +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + if (SSL_version(ssl) >= TLS1_3_VERSION){ + if(SSL_verify_client_post_handshake(ssl) != 1) + return -EPROTO; + } else +#endif + { + r = SSL_renegotiate(ssl); + if (r<=0) return r; + } + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + char buf[1]; /* dummy read buffer */ + struct timeval tv; + fd_set fds; + r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); + if (r <=0) return r; +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + if (SSL_version(ssl) >= TLS1_3_VERSION) return r; +#endif + + tv.tv_sec = (time_t)t; tv.tv_usec = 0; + FD_ZERO(&fds); FD_SET(rfd, &fds); + if ((r = select(rfd + 1, &fds, NULL, NULL, &tv)>0) && FD_ISSET(rfd, &fds)){ + r = SSL_read(ssl, buf, 1); + if (SSL_get_error(ssl, r) == SSL_ERROR_WANT_READ) r = 1; /*ignore */ + } + if (r <=0) return r; +#else + r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); + if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r; + + /* this is for the server only */ + ssl->state = SSL_ST_ACCEPT; +#endif + return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); +} + +int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) return 0; + if (SSL_pending(ssl)) return SSL_read(ssl, buf, len); + return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); +} + +int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) return 0; + return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); +} +#endif diff -ruN ../netqmail-1.06-original/ssl_timeoutio.h netqmail-1.06/ssl_timeoutio.h --- ../netqmail-1.06-original/ssl_timeoutio.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/ssl_timeoutio.h 2019-04-07 13:05:52.192763950 +0200 @@ -0,0 +1,21 @@ +#ifndef SSL_TIMEOUTIO_H +#define SSL_TIMEOUTIO_H + +#include + +/* the version is like this: 0xMNNFFPPS: major minor fix patch status */ +#if OPENSSL_VERSION_NUMBER < 0x00908000L +# error "Need OpenSSL version at least 0.9.8" +#endif + +int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl); +int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl); +int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl); + +int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); +int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len); + +int ssl_timeoutio( + int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len); + +#endif diff -ruN ../netqmail-1.06-original/str.h netqmail-1.06/str.h --- ../netqmail-1.06-original/str.h 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/str.h 2019-02-27 20:57:13.408024870 +0100 @@ -2,6 +2,11 @@ #define STR_H extern unsigned int str_copy(); + +/* DKIM 1.10 +extern unsigned int str_copyb(); + end DKIM 1.10 */ + extern int str_diff(); extern int str_diffn(); extern unsigned int str_len(); @@ -9,6 +14,11 @@ extern unsigned int str_rchr(); extern int str_start(); +/* DKIM 1.10 */ +#include +extern size_t str_cspn(); +/* end DKIM 1.10 */ + #define str_equal(s,t) (!str_diff((s),(t))) #endif diff -ruN ../netqmail-1.06-original/str_cpyb.c netqmail-1.06/str_cpyb.c --- ../netqmail-1.06-original/str_cpyb.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/str_cpyb.c 2019-02-27 20:57:13.408024870 +0100 @@ -0,0 +1,53 @@ +/* + * $Log: str_cpyb.c,v $ + * Revision 1.2 2004-10-22 20:30:54+05:30 Cprogrammer + * added RCS id + * + * Revision 1.1 2004-08-15 19:52:35+05:30 Cprogrammer + * Initial revision + * + */ +#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; +} + +void +getversion_str_cpyb_c() +{ + static char *x = "$Id: str_cpyb.c,v 1.2 2004-10-22 20:30:54+05:30 Cprogrammer Stab mbhangui $"; + x++; +} + diff -ruN ../netqmail-1.06-original/str_cspn.c netqmail-1.06/str_cspn.c --- ../netqmail-1.06-original/str_cspn.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/str_cspn.c 2019-02-27 20:57:13.408024870 +0100 @@ -0,0 +1,40 @@ +/* + * $Log: str_cspn.c,v $ + * Revision 1.1 2011-07-12 20:42:00+05:30 Cprogrammer + * Initial revision + * + */ +#include "str.h" +/* + * Span the complement of string s2. + */ +size_t +str_cspn(s1, s2) + const char *s1; + register const char *s2; +{ + register const char *p, *spanp; + register char c, sc; + + /* + * Stop as soon as we find any character from s2. Note that there + * must be a NUL in s2; it suffices to stop when we find that, too. + */ + for (p = s1;;) { + c = *p++; + spanp = s2; + do { + if ((sc = *spanp++) == c) + return (p - 1 - s1); + } while (sc != 0); + } + /* NOTREACHED */ +} + +void +getversion_str_cspn_c() +{ + static char *x = "$Id: str_cspn.c,v 1.1 2011-07-12 20:42:00+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} diff -ruN ../netqmail-1.06-original/strpidt.c netqmail-1.06/strpidt.c --- ../netqmail-1.06-original/strpidt.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/strpidt.c 2019-02-27 20:57:13.408024870 +0100 @@ -0,0 +1,26 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "numlib.h" +#include + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +char *str_pid_t(pid_t t, char *arg) +{ +char buf[NUMBUFSIZE]; +char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); +} diff -ruN ../netqmail-1.06-original/strsalloc.c netqmail-1.06/strsalloc.c --- ../netqmail-1.06-original/strsalloc.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/strsalloc.c 2019-02-27 20:57:13.409024860 +0100 @@ -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 ../netqmail-1.06-original/strsalloc.h netqmail-1.06/strsalloc.h --- ../netqmail-1.06-original/strsalloc.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/strsalloc.h 2019-02-27 20:57:13.409024860 +0100 @@ -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 ../netqmail-1.06-original/strtimet.c netqmail-1.06/strtimet.c --- ../netqmail-1.06-original/strtimet.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/strtimet.c 2019-02-27 20:57:13.409024860 +0100 @@ -0,0 +1,26 @@ +/* +** Copyright 1998 - 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "numlib.h" +#include + +static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $"; + +char *str_time_t(time_t t, char *arg) +{ +char buf[NUMBUFSIZE]; +char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); +} diff -ruN ../netqmail-1.06-original/surblfilter.9 netqmail-1.06/surblfilter.9 --- ../netqmail-1.06-original/surblfilter.9 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/surblfilter.9 2019-06-19 09:49:02.553441531 +0200 @@ -0,0 +1,82 @@ +.TH surblfilter 8 +.SH NAME +surblfilter \- qmail SURBL blocklist interface +.SH SYNOPSIS +.B surblfilter +[ +.B \-v +] +[ +.B \-c +] +[ +.B \-t +] +.SH DESCRIPTION +\fBSURBL\fRs are lists of web sites that have appeared in unsolicited messages. Unlike most lists, +SURBLs are not lists of message senders. + +Web sites seen in unsolicited messages tend to be more stable than the rapidly changing botnet +IP addresses used to send the vast majority of them. Sender lists like zen.spamhaus.org can be +used in a first stage filter to help identify 80% to 90% of unsolicited messages. SURBLs can +help find about 75% of the otherwise difficult, remaining unsolicited messages in a second +stage filter. Used together with sender lists, SURBLs have proven to be a highly-effective way +to detect 95% of unsolicited messages. + +\fBsurblfilter\fR reads an rfc822 email on stdin, extracts URL and checks them against SURBL. +\fBsurblfilter\fR can be used as a filter using \fBqmail-qfilter(1)\fR. It can also be used +as a qmail-queue(8) frontend by setting QMAILQUEUE environment variable to a wrapper +QMAILHOME/bin/surblqueue. You can define \fBSURBLQUEUE\fR environment variable to make +\fBsurblqueue\fR execute something other than \fBqmail-queue\fR(8). + +\fBsurblfilter\fR uses \fBQMAILRCPTS\fR environment variable to get the recipient list. You can +whitelist recipients by having the email addresses in \fIsurblrcpt\fR control file. You can +change the name of this control file by setting \fBSURBLRCPT\fR environment variable. + +.PP +\fBsurblfilter\fR uses the control file \fIsurbldomainwhite\fR to whitelist a domain. + +.PP +The default SURBL list that is used is \fBmulti.surbl.org\fR. You can use a different list +by setting the \fIsurbldomain\fR control file. + +.PP +\fBsurblfilter\fR caches results in @controldir@/cache directory. The filename of files in this +directory represents the domain. If a file has permission 0600, it means that the domain was +blacklisted. The entries are cached for a default of 300 seconds. You can change this by +setting \fBCACHELIFETIME\fR environment variable or setting this value in \fBcacheliftime\fR +control file. The cache directory should be owned by the uid of the running qmail-smtpd(8). + +\fBsurblfilter\fR removes all leading host names, subdomains, www., randomized subdomains, etc. In +order to determine the level of domain check, it uses the control files \fBlevel3-tlds\fR and +\fBlevel2-tlds\fR. For any domain found in \fBlevel3-tlds\fR, it checks the domain at level4. For any +domain found in \fBlevel2-tlds\fR, it checks the domain at level3. For all domains not found in +these two control files, it checks the domain at level2. Please look at http://www.surbl.org/guidelines + +for more details. + +.SH OPTIONS +.PP +.TP +\fB-v\fR +Use debug mode + +.TP +\fB-c\fR +Do not cache results + +.TP +\fB-t\fR +Do dns text query to get the reason. This option will slow \fBsurblfilter\fR. Remember that +last octet of the IP address obtained for the domain gives you the reason for the block + +.SH RETURN VALUE +\fBsurbfilter\fR returns 88 if the domain is blocked and prints the reason on standard error. +This allows for qmail-smtpd(8) to print a permanent error during a SMTP session. +It returns 111 for all temporary errors. It returns 0 if the message does not contain any +domain blocked by SURBL + +.SH "SEE ALSO" +qmail-smtpd(8), +qmail-queue(8), +qmail-qfilter(1) diff -ruN ../netqmail-1.06-original/surblfilter.c netqmail-1.06/surblfilter.c --- ../netqmail-1.06-original/surblfilter.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/surblfilter.c 2020-04-09 19:46:02.374231173 +0200 @@ -0,0 +1,882 @@ +/* + * $Log: surblfilter.c,v $ + * Revision 1.3 2011-07-13 22:11:13+05:30 Cprogrammer + * skip surblrcpt if QMAILRCPTS is not defined + * + * Revision 1.2 2011-07-13 22:02:13+05:30 Cprogrammer + * added surblrcpt functionality + * + * Revision 1.1 2011-07-13 20:56:34+05:30 Cprogrammer + * Initial revision + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef DARWIN +#include +#endif +#include +#include +#include + +#include "alloc.h" +#include "sgetopt.h" +#include "error.h" +#include "scan.h" +#include "str.h" +#include "case.h" +#include "constmap.h" +#include "auto_qmail.h" +#include "stralloc.h" +#include "env.h" +#include "control.h" +#include "strerr.h" +#include "substdio.h" +#include "getln.h" +#include "byte.h" +#include "dns.h" +#include "ip.h" +#include "ipalloc.h" +#include "mess822.h" +#include "base64.h" + +#define FATAL "surblfilter: fatal: " + +char *dns_text(char *); + +stralloc line = { 0 }; +int debug = 0, do_text = 0, do_cache = 1; +static int cachelifetime = 300; +stralloc whitelist = { 0 }; +stralloc surbldomain = { 0 }; + +/*- SURBL: RCPT whitelist. */ +stralloc srw = { 0 }; +int srwok = 0; +struct constmap mapsrw; + +/*- 2 level tld */ +stralloc l2 = { 0 }; +int l2ok = 0; +struct constmap mapl2; +/*- 3 level tld */ +stralloc l3 = { 0 }; +int l3ok = 0; +struct constmap mapl3; + +static char ssinbuf[1024]; +static substdio ssin = SUBSTDIO_FDBUF(read, 0, ssinbuf, sizeof ssinbuf); +static char ssoutbuf[512]; +static substdio ssout = SUBSTDIO_FDBUF(write, 1, ssoutbuf, sizeof ssoutbuf); +static char sserrbuf[512]; +static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf)); + +void +out(char *str) +{ + if (!str || !*str) + return; + if (substdio_puts(&ssout, str) == -1) + strerr_die2sys(111, FATAL, "write: "); + return; +} + +void +print_debug(char *arg1, char *arg2, char *arg3) +{ + if (!debug) + return; + if (arg1 && substdio_puts(&sserr, arg1) == -1) + _exit(1); + if (arg2 && substdio_puts(&sserr, arg2) == -1) + _exit(1); + if (arg3 && substdio_puts(&sserr, arg3) == -1) + _exit(1); + if ((arg1 || arg2 || arg3) && substdio_puts(&sserr, "\n")) + _exit(1); + if (substdio_flush(&sserr) == -1) + _exit(1); +} + +void +die_write() +{ + strerr_die2sys(111, FATAL, "write: "); + return; +} + +void +flush() +{ + if (substdio_flush(&ssout) == -1) + strerr_die2sys(111, FATAL, "write: "); + return; +} + +void +logerr(char *s) +{ + if (substdio_puts(&sserr, s) == -1) + _exit(1); +} + +void +logerrf(char *s) +{ + if (substdio_puts(&sserr, s) == -1) + _exit(1); + if (substdio_flush(&sserr) == -1) + _exit(1); +} + +void +my_error(char *s1, char *s2, int exit_val) +{ + logerr(s1); + if (s2) { + logerr(": "); + logerr(s2); + } + if (exit_val > 0) { + logerr(": "); + logerr(error_str(errno)); + } + logerrf("\n"); + _exit(exit_val > 0 ? exit_val : 0 - exit_val); +} + +void +die_nomem() +{ + substdio_flush(&ssout); + substdio_puts(&sserr, "surblfilter: out of memory\n"); + substdio_flush(&sserr); + _exit(1); +} + +void +die_soft() +{ + substdio_flush(&ssout); + substdio_puts(&sserr, "surblfilter: DNS temporary failure\n"); + substdio_flush(&sserr); + _exit(1); +} + +void +die_hard() +{ + substdio_flush(&ssout); + substdio_puts(&sserr, "surblfilter: DNS permanent failure\n"); + substdio_flush(&sserr); + _exit(1); +} + +void +die_control() +{ + substdio_flush(&ssout); + substdio_puts(&sserr, "surblfilter: unable to read controls\n"); + substdio_flush(&sserr); + _exit(1); +} + +static unsigned short +getshort(unsigned char *cp) +{ + return (cp[0] << 8) | cp[1]; +} + +static char * +strdup(const char *str) +{ + size_t siz; + char *copy; + + siz = str_len((char *) str) + 1; + if (!(copy = alloc(siz))) + return((char *) 0); + byte_copy(copy, siz, (char *) str); + return(copy); +} + +/* + * we always return a null-terminated string which has been malloc'ed. The string + * is always in the tag=value form. If a temporary or permanent error occurs, + * the string will be exactly "e=perm;" or "e=temp;". + * Note that it never returns NULL. + */ +char * +dns_text(char *dn) +{ + u_char response[PACKETSZ + PACKETSZ + 1]; /* response */ + int responselen; /* buffer length */ + int rc; /* misc variables */ + int ancount, qdcount; /* answer count and query count */ + u_short type, rdlength; /* fields of records returned */ + u_char *eom, *cp; + u_char buf[PACKETSZ + PACKETSZ + 1]; /* we're storing a TXT record here, not just a DNAME */ + u_char *bufptr; + + for (rc = 0, responselen = PACKETSZ;rc < 2;rc++) { + if ((responselen = res_query(dn, C_IN, T_TXT, response, responselen)) < 0) { + if (h_errno == TRY_AGAIN) + return strdup("e=temp;"); + else + return strdup("e=perm;"); + } + if (responselen <= PACKETSZ) + break; + else + if (responselen >= (2 * PACKETSZ)) + return strdup("e=perm;"); + } + qdcount = getshort(response + 4); /* http://crynwr.com/rfc1035/rfc1035.html#4.1.1. */ + ancount = getshort(response + 6); + eom = response + responselen; + cp = response + HFIXEDSZ; + while (qdcount-- > 0 && cp < eom) { + rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME); + if (rc < 0) + return strdup("e=perm;"); + cp += rc + QFIXEDSZ; + } + while (ancount-- > 0 && cp < eom) { + if ((rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME)) < 0) + return strdup("e=perm;"); + cp += rc; + if (cp + RRFIXEDSZ >= eom) + return strdup("e=perm;"); + type = getshort(cp + 0); /* http://crynwr.com/rfc1035/rfc1035.html#4.1.3. */ + rdlength = getshort(cp + 8); + cp += RRFIXEDSZ; + if (type != T_TXT) { + cp += rdlength; + continue; + } + bufptr = buf; + while (rdlength && cp < eom) { + unsigned int cnt; + + cnt = *cp++; /* http://crynwr.com/rfc1035/rfc1035.html#3.3.14. */ + if (bufptr - buf + cnt + 1 >= (2 * PACKETSZ)) + return strdup("e=perm;"); + if (cp + cnt > eom) + return strdup("e=perm;"); + byte_copy((char *) bufptr, cnt, (char *) cp); + rdlength -= cnt + 1; + bufptr += cnt; + cp += cnt; + *bufptr = '\0'; + } + return (char *) strdup((char *) buf); + } + return strdup("e=perm;"); +} + +static char * +uri_decode(char *str, size_t str_len, char **strend) +{ + size_t i = 0, j = 0, found; + int pasthostname = 0; + char *str_bits = "\r\n\t \'\"<>()"; + + for (i = 0; i < str_len; i++, j++) { + if (str[i] == '%' || (!pasthostname && str[i] == '=')) { + if (i + 2 < str_len) { + if (isxdigit(str[i + 1]) && isxdigit(str[i + 2])) { + int c1 = str[i + 1]; + int c2 = str[i + 2]; + int num = ( /* first character */ + ((c1 & 0xF) /* take right half */ + +(9 * (c1 >> 6))) /* add 9 if character is a-f or A-F */ + <<4 /* pack into the left half of the byte */ + ) | ( /* second character */ + (c2 & 0xF) + + (9 * (c2 >> 6)) + ); /* leave it as the left half */ + str[j] = tolower(num); + i += 2; + continue; + } + } + } + if (!pasthostname && (str[i] == '?' || str[i] == '/' || str[i] == '\\')) + pasthostname = 1; + if (i + 1 < str_len) { + if (str[i] == '=' && str[i + 1] == '\n') { + j -= 1; + i += 1; + continue; + } + } + if (i + 2 < str_len) { + if (str[i] == '=' && str[i + 1] == '\r' && str[i + 2] == '\n') { + j -= 1; + i += 2; + continue; + } + } + found = str_chr(str_bits, str[i]); + if (str_bits[found]) + break; + str[j] = tolower(str[i]); + } + str[j] = '\0'; + *strend = str + j + 1; + return str; +} + +/* + * Returns: + * -1 on error + * 0 if domain wasn't cached + * 1 if domain was cached, and not blacklisted + * 2 if domain was cached, and blacklisted. + * + * text != NULL: host blacklisted, text == reason. + */ +static int +cachefunc(char *uri, size_t urilen, char **text, int flag) +{ + static char inbuf[2048]; + static stralloc cachefile = { 0 }, reason = { 0 }; + int fd, i, n, textlen, match; + struct stat st; + substdio ss; + + if (!do_cache) + return (0); + if (uri[i = str_chr(uri, '/')]) { + errno = EINVAL; + return (-1); + } + if (!stralloc_copyb(&cachefile, "control/cache", 13)) + die_nomem(); + if (!stralloc_0(&cachefile)) + die_nomem(); + if (access(cachefile.s, F_OK)) + return (0); + cachefile.len--; + if (!stralloc_append(&cachefile, "/")) + die_nomem(); + if (!stralloc_cats(&cachefile, uri)) + die_nomem(); + if (!stralloc_0(&cachefile)) + die_nomem(); + if (flag) { /*- add the cache */ + if (!access(cachefile.s, F_OK)) + return (0); + if ((fd = open(cachefile.s, O_CREAT|O_WRONLY, *text ? 0600 : 0644)) == -1) + my_error(cachefile.s, 0, 2); + if (*text) { + textlen = str_len(*text); + if ((n = write(fd, *text, textlen)) == -1) { + close(fd); + my_error("write", 0, 1); + } + } + if (close(fd)) + my_error(cachefile.s, 0, 1); + } else { + if (stat(cachefile.s, &st) == -1) { + if (errno == ENOENT) + return (0); + my_error("stat", 0, 1); + return -1; + } + if (time(0) > st.st_mtime + cachelifetime) { + if (unlink(cachefile.s)) { + my_error("unlink", 0, 1); + return -1; + } + return (0); + } + if ((fd = open(cachefile.s, O_RDONLY)) == -1) + my_error(cachefile.s, 0, 2); + substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf)); + if (getln(&ss, &reason, &match, '\n') == -1) { + close(fd); + return -1; + } + *text = reason.s; + close(fd); + return (((st.st_mode & 07777) == 0600) ? 2 : 1); + } + return (0); +} + +static int +getdnsip(stralloc *ip, stralloc *domain, int *code) +{ + char x[IPFMT]; + ipalloc ia = { 0 }; + int len; + + if (!stralloc_copys(ip, "")) + die_nomem(); + switch(dns_ip(&ia, domain)) + { + case DNS_MEM: + die_nomem(); + case DNS_SOFT: + die_soft(); + case DNS_HARD: + return 0; + case 1: + if (ia.len <= 0) + die_soft(); + } + if (code) + *code = *(&ia.ix->ip.d[3]); + len = ip_fmt(x, &ia.ix->ip); + if (!stralloc_copyb(ip, x, len)) + die_nomem(); + return 0; +} + +/*- SURBL: Check surbl rcpt whitelist. */ +int +srwcheck(char *arg, int len) +{ + int j; + + if (!srwok) + return 0; + if (constmap(&mapsrw, arg, len)) + return 1; + if ((j = byte_rchr(arg, len, '@')) < (len - 1)) { + if (constmap(&mapsrw, arg + j, len - j)) + return 1; + } + return 0; +} + +int +l2check(char *arg, int len) +{ + if (!l2ok) + return (0); + if (constmap(&mapl2, arg, len)) + return 1; + return (0); +} + +int +l3check(char *arg, int len) +{ + if (!l3ok) + return (0); + if (constmap(&mapl3, arg, len)) + return 1; + return (0); +} + +/* + * Returns -1 on error. + * Returns 0 if host does not exist. + * Returns 1 if host exists. + */ +static int +checkwhitelist(char *hostname, int hostlen) +{ + int len; + char *ptr; + + for (ptr = whitelist.s, len = 0;len < whitelist.len;) { + if (!str_diffn(hostname, ptr, hostlen)) + return (1); + len += (str_len(ptr) + 1); + ptr = whitelist.s + len; + } + return (0); +} + +static int +getreason(int code, char **text) +{ + static stralloc reason = { 0 }; + + if (!stralloc_copyb(&reason, "blacklisted by ", 15)) + die_nomem(); + if (code & 64 && !stralloc_cats(&reason, debug ? "prolocation/jwspamspy" : "[jp]")) + die_nomem(); + if (code & 32 && !stralloc_cats(&reason, debug ? "abusebutler " : "[ab]")) + die_nomem(); + if (code & 16 && !stralloc_cats(&reason, debug ? "outblaze " : "[ob]")) + die_nomem(); + if (code & 8 && !stralloc_cats(&reason, debug ? "phising " : "[ph]")) + die_nomem(); + if (code & 2 && !stralloc_cats(&reason, debug ? "spamcop " : "[sc]")) + die_nomem(); + if (code & 4 && !stralloc_cats(&reason, debug ? "w.stearns " : "[ws]")) + die_nomem(); + if (!stralloc_0(&reason)) + die_nomem(); + *text = reason.s; + return (code >= 2); +} + +static int +checksurbl(char *uri, int urilen, char *surbldomain, char **text) +{ + static stralloc ip = { 0 }; + static stralloc host = { 0 }; + int i, code = 0; + + if ((i = checkwhitelist(uri, urilen)) == -1) + return -1; + else + if (i) + return (0); + if (stralloc_copys(&host, uri) == 0) + die_nomem(); + if (stralloc_append(&host, ".") == 0) + die_nomem(); + if (stralloc_cats(&host, surbldomain) == 0) + die_nomem(); + if (!stralloc_0(&host)) + die_nomem(); + if (getdnsip(&ip, &host, &code) == -1) + return -1; + if (do_text && ip.len > 0) { + if (text) { + if ((*text = dns_text(host.s))) + return 2; + } + return 1; + } + if (code > 1) + return (getreason(code, text) ? 2 : 0); + return 0; +} + +static int +num_domains(const char *s) +{ + int r = *s ? 1 : 0; + + while (*s) { + if (*s++ == '.') + ++r; + } + return r; +} + +static char * +remove_subdomains(char *orig, int output_domains) +{ + char *s = orig + str_len((char *) orig); + int dots = 0; + + while (s > orig) { + if (*s == '.') + ++dots; + if (dots == output_domains) { + ++s; + break; + } + --s; + } + return s; +} + +/* + * Returns 0 if URI was erronous. + * 1 if URI was not blacklisted. + * 2 if URI was blacklisted. + */ +static int +checkuri(char **ouri, char **text, size_t textlen) +{ + char *uri = *ouri, *uriend, *ptr; + char ipuri[IPFMT]; + size_t urilen = 0; + ip_addr ip; + int cached, blacklisted, i, level; + + if (case_diffb(uri, 4, "http")) + return 0; + uri += 4; + + /*- Check and skip http[s]?:[/\\][/\\]? */ + if (*uri == 's') + uri++; + if (*uri == ':' && (uri[1] == '/' || uri[1] == '\\')) + uri += 2; + else + return 0; + if (*uri == '/' || *uri == '\\') + uri++; + if (!isalpha(*uri) && !isdigit(*uri)) + return 0; + uri_decode(uri, textlen, &uriend); + *ouri = uriend; + print_debug("Full URI: ", uri, 0); + uri[(urilen = str_cspn(uri, "/\\?"))] = '\0'; + if (uri[i = str_chr(uri, '@')]) + uri += (i + 1); + uri[i = str_chr(uri, ':')] = 0; + if (ip_scan(uri, &ip)) { + ip_fmt(ipuri, &ip); + uri = ipuri; + print_debug("Proper IP: ", uri, 0); + } else { + urilen = str_len(uri); + print_debug("Full domain: ", uri, 0); + level = num_domains(uri); + if (level > 2) { + ptr = remove_subdomains(uri, 3); + if (l3check(ptr, str_len(ptr))) + uri = remove_subdomains(uri, 4); + else { + ptr = remove_subdomains(uri, 2); + if (l2check(ptr, str_len(ptr))) + uri = remove_subdomains(uri, 3); + else + uri = remove_subdomains(uri, 2); + } + } else + if (level > 1) { + ptr = remove_subdomains(uri, 2); + if (l2check(ptr, str_len(ptr))) + uri = remove_subdomains(uri, 3); + else + uri = remove_subdomains(uri, 2); + } + print_debug(" Part: ", uri, 0); + } + urilen = str_len(uri); + cached = 1; + blacklisted = 0; + switch (cachefunc(uri, urilen, text, 0)) + { + case 0: + cached = 0; + break; + case 1: + blacklisted = 0; + break; + case 2: + blacklisted = 1; + break; + } + if (cached == 0) { + switch (checksurbl(uri, urilen, surbldomain.s, text)) + { + case -1: + return -1; + case 0: + blacklisted = 0; + *text = (char *) 0; + print_debug(uri, ": not blacklisted", 0); + break; + case 1: + *text = "No reason given"; + blacklisted = 1; + print_debug(uri, ": blacklisted. reason - ", *text); + break; + case 2: + blacklisted = 2; + print_debug(uri, ": blacklisted. reason - ", *text); + break; + } + cachefunc(uri, urilen, text, 1); + } + return (blacklisted); +} + +#define DEF_SURBL_DOMAIN "multi.surbl.org" + +static int do_surbl = 1; + +static void +setup() +{ + char *x, *y, *rcpt; + int i; + + if ((rcpt = env_get("QMAILRCPTS"))) { + if ((srwok = control_readfile(&srw, "control/surblrcpt", 0)) == -1) + die_control(); + if (srwok && !constmap_init(&mapsrw, srw.s, srw.len, 0)) + die_nomem(); + } + for (x = y = rcpt, i = 0;rcpt && *x;x++, i++) { + if (*x == '\n') { + *x = 0; + if (srwcheck(y, i)) { + do_surbl = 0; + return; + } + y = x + 1; + *x = '\n'; + i = 0; + } + } + if ((l2ok = control_readfile(&l2, "control/level2-tlds", 0)) == -1) + die_control(); + if (l2ok && !constmap_init(&mapl2, l2.s, l2.len, 0)) + die_nomem(); + if ((l3ok = control_readfile(&l3, "control/level3-tlds", 0)) == -1) + die_control(); + if (l3ok && !constmap_init(&mapl3, l3.s, l3.len, 0)) + die_nomem(); + switch (control_readline(&surbldomain, "control/surbldomain")) + { + case -1: + die_control(); + case 0: + if (!stralloc_copys(&surbldomain, DEF_SURBL_DOMAIN)) + die_nomem(); + /*- flow through */ + case 1: + if (!stralloc_0(&surbldomain)) + die_nomem(); + } + if ((x = env_get("CACHELIFETIME"))) + scan_int(x, &cachelifetime); + else + if (control_readint(&cachelifetime, "control/cachelifetime") == -1) + die_control(); + if (control_readfile(&whitelist, "control/surbldomainwhite", 0) == -1) + die_control(); + return; +} + +int +main(int argc, char **argv) +{ + stralloc base64out = { 0 }, boundary = { 0 }; + stralloc *ptr; + char *x, *reason = 0; + int opt, in_header = 1, i, total_bl = 0, blacklisted, match, html_plain_text, + base64_decode, found_content_type = 0; + + if (!(x = env_get("SURBL"))) + do_surbl = 0; + while ((opt = getopt(argc, argv, "vtc")) != opteof) { + switch (opt) { + case 'c': + do_cache = 0; + break; + case 'v': + debug = 1; + break; + case 't': + do_text = 1; + break; + } + } + if (chdir(auto_qmail) == -1) + die_control(); + if (do_surbl) + setup(); + for (html_plain_text = base64_decode = 0;;) { + if (getln(&ssin, &line, &match, '\n') == -1) + my_error("getln: ", 0, 1); + if (!match && line.len == 0) + break; + if (substdio_put(&ssout, line.s, line.len)) + die_write(); + if (!do_surbl) + continue; + if (in_header) { + if (!str_diffn(line.s, "Content-Type: ", 14)) { + found_content_type = 1; + } + if (found_content_type) { + for (i = 0;i < line.len; i++) { + if (case_startb(line.s + i, line.len - i, "boundary=")) { + if (line.s[i + 9] == '\"' && line.s[line.len -2] == '\"') + { + if (!stralloc_copyb(&boundary, line.s + i + 10, line.len -i - 12)) + die_nomem(); + } else + if (!stralloc_copyb(&boundary, line.s + i + 9, line.len - i - 10)) + die_nomem(); + if (!stralloc_0(&boundary)) + die_nomem(); + boundary.len--; + } + } + } + if (!mess822_ok(&line)) + in_header = 0; + } else { + if (!str_diffn(line.s, "Content-Type: ", 14)) { + if (!str_diffn(line.s + 14, "message/rfc822", 14) || + !str_diffn(line.s + 14, "text/html", 9) || + !str_diffn(line.s + 14, "text/plain", 10)) + html_plain_text = 1; + else + html_plain_text = 0; + } + if (html_plain_text && !str_diffn(line.s, "Content-Transfer-Encoding: ", 27)) { + if (!str_diffn(line.s + 27, "base64", 6)) + base64_decode = 1; + else + base64_decode = 0; + } + if (line.len == 1) + continue; + if (base64_decode) { + if (!str_diffn(line.s, "Content-", 8)) + continue; + if (!str_diffn(line.s + 2, boundary.s, boundary.len)) { + base64_decode = 0; + continue; + } + if (b64decode((const unsigned char *) line.s, line.len - 1, &base64out) == -1) + die_nomem(); + ptr = &base64out; + } else + ptr = &line; + for (blacklisted = -1, i = 0;i < ptr->len; i++) { + if (case_startb(line.s + i, ptr->len - i, "http:")) { + x = ptr->s + i; + switch (checkuri(&x, &reason, ptr->len - i)) + { + case -1: + my_error("checkuri", 0, 111); + case 0: /*- no valid uri in line */ + blacklisted = 0; + break; + case 1: + case 2: + blacklisted = 1; + break; + } + } + if (blacklisted == 1) { + total_bl++; + break; + } + } + } + } /*- for (html_plain_text = base64_decode = 0;;) { */ + if (substdio_flush(&ssout) == -1) + die_write(); + if (do_surbl && total_bl) { + logerrf("Dmessage contains an URL listed in SURBL blocklist"); + _exit (88); /*- custom error */ + } + return (0); +} + +void +getversion_surblfilter_c() +{ + static char *x = "$Id: surblfilter.c,v 1.4 2011-07-13 22:28:32+05:30 Cprogrammer Exp mbhangui $"; + + x++; +} + diff -ruN ../netqmail-1.06-original/surblqueue.sh netqmail-1.06/surblqueue.sh --- ../netqmail-1.06-original/surblqueue.sh 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/surblqueue.sh 2019-02-27 20:57:13.409024860 +0100 @@ -0,0 +1,33 @@ +#!/bin/sh +# I should be called by qmail-smtpd or anything that calls qmail-queue +# +if [ -f /bin/mktemp ] ; then + MKTEMP=/bin/mktemp +elif [ -f /usr/bin/mktemp ] ; then + MKTEMP=/usr/bin/mktemp +else + MKTEMP=mktemp +fi +out=`$MKTEMP -t surblXXXXXXXXXX` +if [ $? -ne 0 ] ; then + echo "mktemp: unable to create temp files" 1>&2 + exit 111 +fi +# +# Redirect standard error to 4 so that qmail_open() will pick up the error +# +QMAIL/bin/surblfilter > $out 2>&4 +status=$? +if [ $status -eq 0 ] ; then + exec 0<$out + /bin/rm -f $out + # use SURBLQUEUE to execute queue program (thanks Roberto Puzzanghera) + if [ "$SURBLQUEUE" != "" -a -x "$SURBLQUEUE" ]; then + exec $SURBLQUEUE + else + exec QMAIL/bin/qmail-queue + fi +else + /bin/rm -f $out + exit $status +fi diff -ruN ../netqmail-1.06-original/tai.h netqmail-1.06/tai.h --- ../netqmail-1.06-original/tai.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/tai.h 2019-02-27 20:57:13.409024860 +0100 @@ -0,0 +1,34 @@ +/* + * $Log: tai.h,v $ + * Revision 1.3 2004-10-11 14:15:10+05:30 Cprogrammer + * added function prototypes + * + * Revision 1.2 2004-09-19 22:49:23+05:30 Cprogrammer + * added tai_unix macro + * + * Revision 1.1 2004-06-16 01:20:25+05:30 Cprogrammer + * Initial revision + * + */ +#ifndef TAI_H +#define TAI_H + +#include "uint64.h" + +struct tai +{ + uint64 x; +}; + +#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u))) +#define tai_approx(t) ((double) ((t)->x)) +#define tai_less(t,u) ((t)->x < (u)->x) +#define TAI_PACK 8 + +void tai_now(struct tai *); +void tai_add(); +void tai_sub(struct tai *, struct tai *, struct tai *); +void tai_pack(char *, struct tai *); +void tai_unpack(char *, struct tai *); + +#endif diff -ruN ../netqmail-1.06-original/tcp-env.c netqmail-1.06/tcp-env.c --- ../netqmail-1.06-original/tcp-env.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/tcp-env.c 2019-02-27 20:57:13.410024849 +0100 @@ -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(); diff -ruN ../netqmail-1.06-original/time_t_size.c netqmail-1.06/time_t_size.c --- ../netqmail-1.06-original/time_t_size.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/time_t_size.c 2019-05-23 15:12:30.140092743 +0200 @@ -0,0 +1,8 @@ +#include +#include +int +main() +{ + printf("#define SIZEOF_TIME_T %d\n", sizeof(time_t)); + return (0); +} diff -ruN ../netqmail-1.06-original/timeoutconn.c netqmail-1.06/timeoutconn.c --- ../netqmail-1.06-original/timeoutconn.c 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06/timeoutconn.c 2019-02-27 20:57:13.410024849 +0100 @@ -10,9 +10,10 @@ #include "byte.h" #include "timeoutconn.h" -int timeoutconn(s,ip,port,timeout) +int timeoutconn(s,ip,outip,port,timeout) int s; struct ip_address *ip; +struct ip_address *outip; unsigned int port; int timeout; { @@ -22,6 +23,13 @@ fd_set wfds; struct timeval tv; + /* bind() an outgoing ipaddr */ + byte_zero(&sin,sizeof(sin)); + byte_copy(&sin.sin_addr.s_addr,4,outip); + sin.sin_family = AF_INET; + + if (-1 == bind(s,(struct sockaddr *) &sin,sizeof(sin))) return -1; + byte_zero(&sin,sizeof(sin)); byte_copy(&sin.sin_addr,4,ip); x = (char *) &sin.sin_port; diff -ruN ../netqmail-1.06-original/tls.c netqmail-1.06/tls.c --- ../netqmail-1.06-original/tls.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/tls.c 2019-04-09 20:56:31.139694929 +0200 @@ -0,0 +1,27 @@ +#ifdef TLS +#include "exit.h" +#include "error.h" +#include +#include + +int smtps = 0; +SSL *ssl = NULL; + +void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); } +void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); } + +const char *ssl_error() +{ + int r = ERR_get_error(); + if (!r) return NULL; + SSL_load_error_strings(); + return ERR_error_string(r, NULL); +} +const char *ssl_error_str() +{ + const char *err = ssl_error(); + if (err) return err; + if (!errno) return 0; + return (errno == error_timeout) ? "timed out" : error_str(errno); +} +#endif diff -ruN ../netqmail-1.06-original/tls.h netqmail-1.06/tls.h --- ../netqmail-1.06-original/tls.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/tls.h 2019-02-27 20:57:13.410024849 +0100 @@ -0,0 +1,16 @@ +#ifndef TLS_H +#define TLS_H + +#include + +extern int smtps; +extern SSL *ssl; + +void ssl_free(SSL *myssl); +void ssl_exit(int status); +# define _exit ssl_exit + +const char *ssl_error(); +const char *ssl_error_str(); + +#endif diff -ruN ../netqmail-1.06-original/tryulong64.c netqmail-1.06/tryulong64.c --- ../netqmail-1.06-original/tryulong64.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/tryulong64.c 2019-02-27 20:57:13.410024849 +0100 @@ -0,0 +1,47 @@ +/* + * $Log: tryulong64.c,v $ + * Revision 1.1 2004-05-14 00:45:23+05:30 Cprogrammer + * Initial revision + * + */ +void +main() +{ + unsigned long u; + u = 1; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + u += u; + if (!u) + _exit(1); + _exit(0); +} diff -ruN ../netqmail-1.06-original/uint64.h1 netqmail-1.06/uint64.h1 --- ../netqmail-1.06-original/uint64.h1 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/uint64.h1 2019-02-27 20:57:13.410024849 +0100 @@ -0,0 +1,12 @@ +/* + * $Log: uint64.h1,v $ + * Revision 1.1 2004-10-22 15:00:08+05:30 Cprogrammer + * Initial revision + * + */ +#ifndef UINT64_H +#define UINT64_H + +typedef unsigned long uint64; + +#endif diff -ruN ../netqmail-1.06-original/uint64.h2 netqmail-1.06/uint64.h2 --- ../netqmail-1.06-original/uint64.h2 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/uint64.h2 2019-02-27 20:57:13.410024849 +0100 @@ -0,0 +1,12 @@ +/* + * $Log: uint64.h2,v $ + * Revision 1.1 2004-10-22 15:00:36+05:30 Cprogrammer + * Initial revision + * + */ +#ifndef UINT64_H +#define UINT64_H + +typedef unsigned long long uint64; + +#endif diff -ruN ../netqmail-1.06-original/update_tmprsadh.sh netqmail-1.06/update_tmprsadh.sh --- ../netqmail-1.06-original/update_tmprsadh.sh 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/update_tmprsadh.sh 2019-02-27 20:57:13.410024849 +0100 @@ -0,0 +1,22 @@ +#!/bin/sh + +# Update temporary RSA and DH keys +# Frederik Vermeulen 2004-05-31 GPL +# +# Slightly modified by Roberto Puzzanghera +# to chown the .pem files to vpopmail + +umask 0077 || exit 0 + +export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin" + +openssl genrsa -out QMAIL/control/rsa2048.new 2048 && +chmod 600 QMAIL/control/rsa2048.new && +chown vpopmail:vchkpw QMAIL/control/rsa2048.new && +mv -f QMAIL/control/rsa2048.new QMAIL/control/rsa2048.pem + +openssl dhparam -2 -out QMAIL/control/dh2048.new 2048 && +chmod 600 QMAIL/control/dh2048.new && +chown vpopmail:vchkpw QMAIL/control/dh2048.new && +mv -f QMAIL/control/dh2048.new QMAIL/control/dh2048.pem + diff -ruN ../netqmail-1.06-original/wildmat.c netqmail-1.06/wildmat.c --- ../netqmail-1.06-original/wildmat.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06/wildmat.c 2019-02-27 20:57:13.410024849 +0100 @@ -0,0 +1,173 @@ +/*-** wildmat.c.orig Wed Dec 3 11:46:31 1997 + * $Revision: 1.6 $ + * Do shell-style pattern matching for ?, \, [], and * characters. + * Might not be robust in face of malformed patterns; e.g., "foo[a-" + * could cause a segmentation violation. It is 8bit clean. + * + * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. + * Rich $alz is now . + * April, 1991: Replaced mutually-recursive calls with in-line code + * for the star character. + * + * Special thanks to Lars Mathiesen for the ABORT code. + * This can greatly speed up failing wildcard patterns. For example: + * pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* + * text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 + * text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 + * Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without + * the ABORT code, it takes 22310 calls to fail. Ugh. The following + * explanation is from Lars: + * The precondition that must be fulfilled is that DoMatch will consume + * at least one character in text. This is true if *p is neither '*' no + * '\0'.) The last return has ABORT instead of FALSE to avoid quadratic + * behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With + * FALSE, each star-loop has to run to the end of the text; with ABORT + * only the last one does. + * + * Once the control of one instance of DoMatch enters the star-loop, that + * instance will return either TRUE or ABORT, and any calling instance + * will therefore return immediately after (without calling recursively + * again). In effect, only one star-loop is ever active. It would be + * possible to modify the code to maintain this context explicitly, + * eliminating all recursive calls at the cost of some complication and + * loss of clarity (and the ABORT stuff seems to be unclear enough by + * itself). I think it would be unwise to try to get this into a + * released version unless you have a good test data base to try it out + * on. + */ +#define TRUE 1 +#define FALSE 0 +#define ABORT -1 + + +/*- What character marks an inverted character class? */ +#define NEGATE_CLASS '^' +/*- Is "*" a common pattern? */ +#define OPTIMIZE_JUST_STAR +/*- Do tar(1) matching rules, which ignore a trailing slash? */ +#undef MATCH_TAR_PATTERN + + +/*- Match text and p, return TRUE, FALSE, or ABORT. */ +static int +DoMatch(text, p) + register char *text; + register char *p; +{ + register int last; + register int matched; + register int reverse; + + for (; *p; text++, p++) + { + if (*text == '\0' && *p != '*') + return ABORT; + switch (*p) + { + case '\\': /*- Literal match with following character. */ + p++; + /*- FALLTHROUGH */ + default: + if (*text != *p) + return FALSE; + continue; + case '?': /*- Match anything. */ + continue; + case '*': + /*- Consecutive stars act just like one. */ + while (*++p == '*') + continue; + /*- Trailing star matches everything. */ + if (*p == '\0') + return TRUE; + while (*text) + if ((matched = DoMatch(text++, p)) != FALSE) + return matched; + return ABORT; + case '[': + reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; + /*- Inverted character class. */ + if (reverse) + p++; + matched = FALSE; + if (p[1] == ']' || p[1] == '-') + { + if (*++p == *text) + matched = TRUE; + } + for (last = *p; *++p && *p != ']'; last = *p) + { + /*- This next line requires a good C compiler. */ + if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p) + matched = TRUE; + } + if (matched == reverse) + return FALSE; + continue; + } + } + +#ifdef MATCH_TAR_PATTERN + if (*text == '/') + return TRUE; +#endif /*- MATCH_TAR_ATTERN */ + return *text == '\0'; +} + + +/*- User-level routine. Returns TRUE or FALSE. */ +int +wildmat_internal(text, p) + char *text; + char *p; +{ +#ifdef OPTIMIZE_JUST_STAR + if (p[0] == '*' && p[1] == '\0') + return TRUE; +#endif /*- OPTIMIZE_JUST_STAR */ + return DoMatch(text, p) == TRUE; +} + +#if defined(TEST) +include < stdio.h > +/*- Yes, we use gets not fgets. Sue me. */ + +int +main() +{ + char p[80]; + char text[80]; + + printf("Wildmat tester. Enter pattern, then strings to test.\n"); + printf("A blank line gets prompts for a new pattern; a blank pattern\n"); + printf("exits the program.\n"); + for (;;) + { + printf("\nEnter pattern: "); + (void) fflush(stdout); + if (gets(p) == NULL || p[0] == '\0') + break; + for (;;) + { + printf("Enter text: "); + (void) fflush(stdout); + if (gets(text) == NULL) + exit(0); + /*- Blank line; go back and get a new pattern. */ + if (text[0] == '\0') + break; + printf(" %s\n", wildmat_internal(text, p) ? "YES" : "NO"); + } + } + exit(0); + /*- NOTREACHED */ +} +#endif /*- defined(TEST) */ + +void +getversion_wildmat_internal_c() +{ + static char *x = "$Id: wildmat.c,v 1.6 2008-08-03 18:26:33+05:30 Cprogrammer Stab mbhangui $"; + x++; + x--; +}