diff -urN ../qmail-1.03_unpatched/CHKUSER.automatic_patching ./CHKUSER.automatic_patching --- ../qmail-1.03_unpatched/CHKUSER.automatic_patching 1970-01-01 01:00:00.000000000 +0100 +++ ./CHKUSER.automatic_patching 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,98 @@ +Chkuser 2.0 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 + - qmail 1.03 or netqmail 1.05 patched with auth-0.4.2 (distribuited with + vpopmail within the contrib dir, author Erwin Hoffmann - www.fehcom.de) + - qmail 1.03 or netqmail 1.05 patched with toaster-0.6-1 (author Bill + Shupp - see www.shupp.org/toaster for more info or newer releases) + +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: 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 + 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 -urN ../qmail-1.03_unpatched/CHKUSER.copyright ./CHKUSER.copyright --- ../qmail-1.03_unpatched/CHKUSER.copyright 1970-01-01 01:00:00.000000000 +0100 +++ ./CHKUSER.copyright 2005-01-21 20:23:49.000000000 +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 -urN ../qmail-1.03_unpatched/CHKUSER.install ./CHKUSER.install --- ../qmail-1.03_unpatched/CHKUSER.install 1970-01-01 01:00:00.000000000 +0100 +++ ./CHKUSER.install 2005-01-21 20:23:49.000000000 +0100 @@ -0,0 +1,76 @@ + + +This 2.0.8b version has been released as update release. + + +New installation +================ +This "update" version must be used to update older chkuser 2.0 installations. + +If you did not install chkuser 2.0 yet, install the complete chkuser package +"chkuser-2.0.8-release". + + +Updating installation from 2.0 up to 2.0.6 +========================================== + +Untar this package, and: + +save your existing chkuser_settings.h + +copy newer chkuser.h, chkuser.c and chkuser_settings.h in your qmail source + directory. + +1) patch Makefile using Makefile.patch + patch < Makefile.patch + +2) patch qmail-smtpd.c + If you are using Bill Shupp's toaster use this command + patch < qmail-smtpd_toaster.patch + otherwise use + patch < qmail-smtpd.patch + +Now, check your old chkuser_settings.h and restore the options you'ld like to enable/disable. + +make + +install & test + + +Updating installation from 2.0.7, 2.0.8, 2.0.8a +=============================================== + +Untar this package, and: + +if you're using Shupp's Toaster, patch qmail-smtpd.c using + qmail-smtpd-toaster-2.0.7.patch + + patch < qmail-smtpd-toaster-2.0.7.patch + +copy newer chkuser.h, chkuser.c, chkuser_settings.h in your qmail source + directory. + + +check defines behaviours in chkuser_settings.h. + +make + +install & test + + +DEFAULT DEFINES +=============== + +Some defines are changed in this version. +Please check carefully what's changed. + + +BUGS AND FEEDBACKS. +=================== + +For bugs and feeedbacks, please use (only) one of these lists: + + vchkpw@inter7.com + mail-toaster@simerson.net + chkuser@interazioni.it + diff -urN ../qmail-1.03_unpatched/CHKUSER.log_format ./CHKUSER.log_format --- ../qmail-1.03_unpatched/CHKUSER.log_format 1970-01-01 01:00:00.000000000 +0100 +++ ./CHKUSER.log_format 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,59 @@ + +chkuser 2.0.5 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 + * rejected sender + + sender sender declared within "mail from" + + remoteinfo the value of "TCPREMOTEINFO" or the autenticated user + + identifyremote the value of "CHKUSER_IDENTIFY" env variable - or the name + you've chosen in chkuser_settings.h - (be careful to set it, + inside your tcp.cdb with a value that let you understand who is sending) + + 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 + 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 + 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 + mbx overquota rcpt mailbox is overquota + rejected sender invalid sender address format + rejected sender invalid sender MX domain + diff -urN ../qmail-1.03_unpatched/CHKUSER.manual_patching ./CHKUSER.manual_patching --- ../qmail-1.03_unpatched/CHKUSER.manual_patching 1970-01-01 01:00:00.000000000 +0100 +++ ./CHKUSER.manual_patching 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,183 @@ +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: \ + -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 -urN ../qmail-1.03_unpatched/CHKUSER.settings ./CHKUSER.settings --- ../qmail-1.03_unpatched/CHKUSER.settings 1970-01-01 01:00:00.000000000 +0100 +++ ./CHKUSER.settings 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,222 @@ + +CHKUSER 2.0.5 - SETTINGS + +#define CHKUSER_DEBUG + Only enables eventual debugging instructions put inside the code. + When enabled, + CHKUSER_DBG ("string") + CHKUSER_DBG_INT (integer) + display informations to terminal. + [default NOT defined] + +#define CHKUSER_DEBUG_STDERR + Enables displaying of debugging informations to STDERR, instead of STDOUT. + [default NOT defined] + +#define CHKUSER_ALWAYS_ON + This enables chkuser ALWAYS, despite of any other global or domain setting. + Be careful, when this define is activated, your .qmail-default is no more + important; if the domain is within rcpthosts it will be checked for rcpts. + [default NOT defined] + +#define CHKUSER_VPOPMAIL + This set the virtual mail manager used. + Up to now only vpopmail is supported, so this define is just here for future use + [default defined - must be defined] + +#define CHKUSER_STARTING_VARIABLE "CHKUSER_START" + If defined, tells which variable must be read, at qmail-smtpd start, in order + to understand how to use chkuser for any domain + The variable must be filled with the following values + NONE = chkuser will not work + ALWAYS = chkuser will work always + DOMAIN = chkuser will work depending on single domain settings + Ony other value, or a missing value, will disable chkuser. + [default NOT defined] + +#define CHKUSER_ENABLE_UIDGID + Used to switch between UIDS/GIDS, used if you want to apply a more safe mechanism, and + if you're NOT using TLS (as TLS seems not to like UID/GID changing) + When not defined, qmail-smtpd must be executed as vpopmail user. + When defined, qmail-smtpd runs as inoffensive qmail user, playing as vpopmail user + only for the needed operations. + [default defined, except for toaster-0.6-1, where default is not defined] + +#define CHKUSER_ENABLE_EXTENSIONS + Enables checking of user's extensions. Used by some customized installations, or some + products (like TMDA) + [default NOT defined] + + +#define CHKUSER_EXTENSION_DASH '-' + Defines the character used to start user's extensions. + Has no effect on mailing lists extensions. + [must always be defined if CHKUSER_ENABLE_EXTENSIONS is defined] + +#define CHKUSER_DOMAIN_WANTED + Display error if a recipient address does not contain a domain. + [default defined] + +#define CHKUSER_ENABLE_USERS + Enables checking on users + [default defined] + +#define CHKUSER_ENABLE_ALIAS + Enables checking on aliases + [default defined] + +#define CHKUSER_ENABLE_VALIAS + Enables checking on valiases + [default defined] + +#define CHKUSER_ENABLE_LISTS + Enables checking on mailing lists + [default defined] + +#define CHKUSER_EZMLM_DASH '-' + Defines the character used to start the extensions of mailing lists. + [must always be defined if CHKUSER_ENABLE_LISTS is defined] + +#define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" + Defines an alternative file to use while checking if bouncing must be + done for a domain. + If defined, this file must exist in the domain dir in order to enable + chkuser checking for that domain. + [default NOT defined] + +#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" + String to look for inside .qmail-default in order to check for bouncing. + [must always be defined if domain checking must be done + and CHKUSER_SPECIFIC_BOUNCING is not defined] + +#define CHKUSER_ENABLE_VAUTH_OPEN + Defines if a call is available for checking MySQL/Postgres/LDAP connect. + Check your vpopmail release, it should be released with 5.5.0. + [default NOT defined] + + +#define CHKUSER_ENABLE_LOGGING + Enables logging + [default defined] + +#define CHKUSER_LOG_VALID_RCPT + Enables logging of valid recipients + [default defined] + +#define CHKUSER_RCPT_FORMAT + Enables checking of valid format for recipient addresses + user = [a-z0-9_-] + domain = [a-z0-9-.] without consecutive "-" or ".", + without leading or ending "-" or "." + [default defined] + +#define CHKUSER_RCPT_MX + Enables checking of valid MX for recipient addresses + [default defined] + +#define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK" + This define 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 + [default NOT defined] + +#define CHKUSER_SENDER_FORMAT + Enables checking of valid format for sender addresses + user = [a-z0-9_-] + domain = [a-z0-9-.] without consecutive "-" or ".", + without leading or ending "-" or "." + [default defined] + +#define CHKUSER_ALLOW_SENDER_SRS + When CHKUSER_SENDER_FORMAT is enabled, this define add "#" and "+" as permitted + characters within sender address + It is used by SRS (Sender Rewriting Scheme) + [default NOT defined] + +/* #define CHKUSER_ALLOW_SENDER_CHAR_1 '$' */ +chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */ +chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */ +chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */ +chkuser_settings.h:/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */ + +#define CHKUSER_ALLOW_SENDER_CHAR_1 '$' +#define CHKUSER_ALLOW_SENDER_CHAR_2 '%' +#define CHKUSER_ALLOW_SENDER_CHAR_3 '£' +#define CHKUSER_ALLOW_SENDER_CHAR_4 '?' +#define CHKUSER_ALLOW_SENDER_CHAR_5 '*' + This defines allow to accept further characters within the sender address. + [default NOT defined] + +#define CHKUSER_SENDER_MX + Enables checking of valid MX for sender address + [default defined] + +#define CHKUSER_MIN_DOMAIN_LEN 4 + This is a sub checking enabled by CHKUSER_SENDER_FORMAT define. + Sets the minimum length of a domain, as formal control of the address. + As far as I know, k.st is the shortest domain, so 4 characters is the + minimum length. + If CHKUSER_SENDER_FORMAT is undefined, no check is done. + If CHKUSER_MIN_DOMAIN_LEN is undefined, no check is done. + [default defined] + +#define CHKUSER_LOG_VALID_SENDER + Enables logging of valid senders + [default defined] + +#define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST + Enables accepting null sender "<>" from hosts which have a name + associated to their IP + [default defined] + +#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" + defines the variable which contains the maximimum number of allowed recipients. + When the first value, between CHKUSER_RCPT_LIMIT_VARIABLE's variable and + CHKUSER_WRONGRCPT_LIMIT_VARIABLE's variable is reached, chkuser rejects everything + [default defined] + +#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" + defines the variable which contains the maximimum number of wrong recipients. + When the first value, between CHKUSER_RCPT_LIMIT_VARIABLE's variable and + CHKUSER_WRONGRCPT_LIMIT_VARIABLE's variable is reached, chkuser rejects everything + [default defined] + +#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" + Enables the usage of the environment variable containing the limit percent of quota. + Here you only define which variable will be used to set the percent limit, then + in tcp.smpt or running tcpserver you must set the environment variable. + If the variable is not found, or variable is <= 0, the quota checking is not performed. + [default defined] + +#define CHKUSER_ERROR_DELAY 1000 + Delay to wait for each not existing recipient + [default defined] + +#define CHKUSER_ERROR_DELAY_INCREASE 100 + Delay to add to the default, for each additional wrong recipient found + [default defined] + +#define CHKUSER_RCPT_DELAY_ANYERROR + Use delay for each error on recipients + [default defined] + +#define CHKUSER_SENDER_DELAY_ANYERROR + Use delay for each error on senders + [default defined] + + +#define CHKUSER_NORCPT_STRING "511 sorry, no mailbox here by that name (#5.1.1 - chkuser)\r\n" +#define CHKUSER_RESOURCE_STRING "430 system temporary unavailable, try again later (#4.3.0 - chkuser)\r\n" +#define CHKUSER_MBXFULL_STRING "522 sorry, recipient mailbox is full (#5.2.2 - chkuser)\r\n" +#define CHKUSER_MAXRCPT_STRING "571 sorry, reached maximum number of recipients for one session (#5.7.1 - chkuser)\r\n" +#define CHKUSER_MAXWRONGRCPT_STRING "571 sorry, you are violating our security policies (#5.1.1 - chkuser)\r\n" +#define CHKUSER_DOMAINMISSING_STRING "511 sorry, you must specify a domain (#5.1.1 - chkuser)\r\n" +#define CHKUSER_RCPTFORMAT_STRING "511 sorry, recipient address has invalid format (#5.1.1 - chkuser)\r\n" +#define CHKUSER_RCPTMX_STRING "511 sorry, can't find a valid MX for rcpt domain (#5.1.1 - chkuser)\r\n" +#define CHKUSER_SENDERFORMAT_STRING "571 sorry, sender address has invalid format (#5.7.1 - chkuser)\r\n" +#define CHKUSER_SENDERMX_STRING "511 sorry, can't find a valid MX for sender domain (#5.1.1 - chkuser)\r\n" +#define CHKUSER_INTRUSIONTHRESHOLD_STRING "571 sorry, you are violating our security policies (#5.7.1 - chkuser)\r\n" +#define CHKUSER_NORELAY_STRING "553 sorry, that domain isn't in my list of allowed rcpthosts (#5.5.3 - chkuser)\r\n" + These are the errors handled by chkuser and emitted by qmail-smtpd as error responses. + You may change them here, if you don't like them. + diff -urN ../qmail-1.03_unpatched/Makefile ./Makefile --- ../qmail-1.03_unpatched/Makefile 1998-06-15 12:53:16.000000000 +0200 +++ ./Makefile 2005-01-21 19:25:03.000000000 +0100 @@ -1,5 +1,9 @@ # 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 default: it @@ -23,7 +27,7 @@ auto-ccld.sh: \ conf-cc conf-ld warn-auto.sh ( cat warn-auto.sh; \ - echo CC=\'`head -1 conf-cc`\'; \ + echo CC=\'`head -1 conf-cc` -I`head -1 conf-vpopmail`/include\'; \ echo LD=\'`head -1 conf-ld`\' \ ) > auto-ccld.sh @@ -136,6 +140,10 @@ compile auto_usera.c ./compile auto_usera.c +base64.o: \ +compile base64.c base64.h stralloc.h substdio.h str.h + ./compile base64.c + binm1: \ binm1.sh conf-qmail cat binm1.sh \ @@ -217,9 +225,9 @@ case.a: \ makelib case_diffb.o case_diffs.o case_lowerb.o case_lowers.o \ -case_starts.o +case_starts.o case_startb.o ./makelib case.a case_diffb.o case_diffs.o case_lowerb.o \ - case_lowers.o case_starts.o + case_lowers.o case_starts.o case_startb.o case_diffb.o: \ compile case_diffb.c case.h @@ -237,6 +245,10 @@ compile case_lowers.c case.h ./compile case_lowers.c +case_startb.o: \ +compile case_startb.c case.h + ./compile case_startb.c + case_starts.o: \ compile case_starts.c case.h ./compile case_starts.c @@ -263,7 +275,7 @@ cdbmake_add.o cdbmake_add.o: \ -compile cdbmake_add.c cdbmake.h uint32.h +compile cdbmake_add.c cdbmake.h alloc.h uint32.h ./compile cdbmake_add.c cdbmake_hash.o: \ @@ -300,6 +312,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` @@ -808,7 +824,7 @@ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ -binm3 binm3+df +binm3 binm3+df update_tmprsadh load: \ make-load warn-auto.sh systype @@ -890,6 +906,38 @@ readwrite.h open.h headerbody.h maildir.h strerr.h ./compile maildirwatch.c +maildirgetquota.o: \ +compile maildirgetquota.c maildirgetquota.h maildirmisc.h + ./compile maildirgetquota.c + +maildirflags.o: \ +compile maildirflags.c + ./compile maildirflags.c + +maildiropen.o: \ +compile maildiropen.c maildirmisc.h + ./compile maildiropen.c + +maildirparsequota.o: \ +compile maildirparsequota.c + ./compile maildirparsequota.c + +maildirquota.o: \ +compile maildirquota.c maildirquota.h maildirmisc.h numlib.h + ./compile maildirquota.c + +overmaildirquota.o: \ +compile overmaildirquota.c + ./compile overmaildirquota.c + +strtimet.o: \ +compile strtimet.c + ./compile strtimet.c + +strpidt.o: \ +compile strpidt.c + ./compile strpidt.c + mailsubj: \ warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split cat warn-auto.sh mailsubj.sh \ @@ -1174,12 +1222,15 @@ load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ -fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib +fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \ +maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \ +strtimet.o strpidt.o ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ - auto_patrn.o `cat socket.lib` + auto_patrn.o `cat socket.lib` maildirquota.o maildirgetquota.o \ + maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o qmail-local.0: \ qmail-local.8 @@ -1269,11 +1320,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 @@ -1289,10 +1342,10 @@ qmail-popup: \ load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \ case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a socket.lib +fs.a base64.o socket.lib ./load qmail-popup commands.o timeoutread.o timeoutwrite.o \ now.o case.a fd.a sig.a wait.a stralloc.a alloc.a \ - substdio.a error.a str.a fs.a `cat socket.lib` + substdio.a error.a str.a fs.a base64.o `cat socket.lib` qmail-popup.0: \ qmail-popup.8 @@ -1301,7 +1354,7 @@ qmail-popup.o: \ compile qmail-popup.c commands.h fd.h sig.h stralloc.h gen_alloc.h \ substdio.h alloc.h wait.h str.h byte.h now.h datetime.h fmt.h exit.h \ -readwrite.h timeoutread.h timeoutwrite.h +readwrite.h timeoutread.h timeoutwrite.h base64.h ./compile qmail-popup.c qmail-pw2u: \ @@ -1444,6 +1497,7 @@ substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib ./load qmail-remote control.o constmap.o timeoutread.o \ timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \ + tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \ ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \ lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \ str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib` @@ -1483,12 +1537,12 @@ trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ -auto_split.o +auto_split.o env.a ./load qmail-send qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ - substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a qmail-send.0: \ qmail-send.8 @@ -1532,28 +1586,31 @@ ./compile qmail-showctl.c qmail-smtpd: \ -load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ +load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ -fs.a auto_qmail.o socket.lib - ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ +fs.a auto_qmail.o base64.o socket.lib $(SMTPD_CHKUSER_OBJ) + ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) qregex.o rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ + $(VPOPMAIL_LIBS) \ + `cat socket.lib` \ + -lcrypt qmail-smtpd.0: \ qmail-smtpd.8 nroff -man qmail-smtpd.8 > qmail-smtpd.0 qmail-smtpd.o: \ -compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \ +compile qmail-smtpd.c chkuser.h 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 +exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h ./compile qmail-smtpd.c qmail-start: \ @@ -1681,6 +1738,10 @@ constmap.h stralloc.h gen_alloc.h rcpthosts.h ./compile rcpthosts.c +qregex.o: \ +compile qregex.c qregex.h + ./compile qregex.c + readsubdir.o: \ compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ auto_split.h @@ -1827,7 +1888,8 @@ ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \ ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \ prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \ -maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c +maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \ +update_tmprsadh shar -m `cat FILES` > shar chmod 400 shar @@ -1892,7 +1954,7 @@ spawn.o: \ compile chkspawn spawn.c sig.h wait.h substdio.h byte.h str.h \ -stralloc.h gen_alloc.h select.h exit.h coe.h open.h error.h \ +stralloc.h gen_alloc.h select.h exit.h alloc.h coe.h open.h error.h \ auto_qmail.h auto_uids.h auto_spawn.h ./chkspawn ./compile spawn.c @@ -2108,6 +2170,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 +2214,25 @@ wait_pid.o: \ compile wait_pid.c error.h haswaitp.h ./compile wait_pid.c + +cert cert-req: \ +Makefile-cert + @$(MAKE) -sf $< $@ + +Makefile-cert: \ +conf-qmail Makefile-cert.mk + @cat Makefile-cert.mk \ + | sed s}QMAIL}"`head -1 conf-qmail`"}g \ + > $@ + +update_tmprsadh: \ +conf-qmail update_tmprsadh.sh + @cat update_tmprsadh.sh\ + | 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 diff -urN ../qmail-1.03_unpatched/Makefile-cert.mk ./Makefile-cert.mk --- ../qmail-1.03_unpatched/Makefile-cert.mk 1970-01-01 01:00:00.000000000 +0100 +++ ./Makefile-cert.mk 2005-01-21 19:25:03.000000000 +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 qmaild.qmail $@ + +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 qmaild.qmail 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 -urN ../qmail-1.03_unpatched/README.auth ./README.auth --- ../qmail-1.03_unpatched/README.auth 1970-01-01 01:00:00.000000000 +0100 +++ ./README.auth 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,64 @@ +README qmail-smtpd SMTP Authentication +====================================== + + +History: +-------- + +This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch +which itself uses "Mrs. Brisby's" initial code. +Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing +'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison. +Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem +(can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons. +Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar. +Version 0.51 includes the evaluation of the 'Auth' and the 'Size' parameter in the 'Mail From:' command. +Version 0.52 uses DJB functions to copy FDs. +Version 0.56 corrects some minor mistakes displaying the 'Auth' userid. + + +Scope: +------ + +This patch supports RFC 2554 "SMTP Service Extension for Authentication" for qmail-smtpd. +Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration"). +For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html. + + +Installation: +------------- + +* Untar the source in the qmail-1.03 home direcotry. +* Run ./install_auth. +* Modify the compile time option "#define CRAM_MD5" to your needs. +* Re-make qmail. + + +Setup: +------ + +In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module' +PAM to be called by qmail-smtpd; typically + + /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1 + +Since qmail-smtpd does not run as root, checkpassword has to be made sticky. +There is no need to include additionally the hostname in the call. +In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information. + + +Changes wrt. Krysztof Dabrowski's patch: +---------------------------------------- + +* Avoid the 'hostname' in the call of the PAM. +* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5. +* Doesn't close FD 2; thus not inhibiting logging to STDERR. +* Fixed bugs in base64.c. +* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'. +* Evaluation of the (informational) Mail From: < > Auth=username. +* Additional support for the advertised "Size" via 'Mail From: SIZE=123456780' (RFC 1870). + + +Erwin Hoffmann - Cologne 2004-09-17 (www.fehcom.de) + + diff -urN ../qmail-1.03_unpatched/README.qregex ./README.qregex --- ../qmail-1.03_unpatched/README.qregex 1970-01-01 01:00:00.000000000 +0100 +++ ./README.qregex 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,155 @@ +QREGEX (v2) 20040315 - README March 15, 2004 +A Regular Expression matching patch for qmail 1.03 + + +OVERVIEW: + +qregex adds the ability to match address evelopes via Regular Expressions (REs) +in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name), +`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands with +no load at all to the parent process. It follows all the base rules that are +set out with qmail (ie using control files) so it makes for easy integretion +into an existing setup (see the install instructions for more info). The v2 is +specified because qregex was re-written to better conform to the security +guarantee set forth by the author of qmail. The original version used stdio.h +and stdlib.h for reading the control files whereas v2 now uses all stralloc +functions which are much more regulated against buffer overruns and the like. +See: http://cr.yp.to/qmail/guarantee.html + +NEW FEATURES: + +Features added to qregex since its creation are: + +1. Performs pattern matching on the helo/ehlo host name. Setting the + NOBADHELO environment variable prevents the host name from being + compared to the patterns in the badhelo control file. + +2. Matches to patterns in the badhelo, badmailfrom, and badmailto control + files are logged. + +3. Matching is case insensitive. + +4. Qregex ignores empty envelope senders. An empty envelope sender is not + compared to the patterns in the badmailfrom control file and is always + accepted. + + + +PLATFORMS: + +Qregex has been built and tested on the following platforms. I'm sure it won't +have any problems on any platform that qmail will run on (providing they have +a regex interface) but if you run into problems let me know. + + - OpenBSD 3.x + - FreeBSD 4.x + - Mandrake Linux 9.x + + + + +INSTALLATION INSTRUCTIONS: + +Installation is very simple, there is only one requirement. You need to use the +GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html). +(For Solaris 8 users like me it is installed as 'gpatch') + +- If this is a new setup. +Uncompress and untar the qmail archive, copy the 'qregex.patch' file into the +new qmail-1.03 directory and run "patch < qregex.patch" +Follow the instructions as per the included qmail INSTALL file. Once you are +done come back to this file and read the section on the control files. + +- If this is an existing setup. +FIRST: create your control files (see below). +Copy the 'qregex.patch' file into your existing qmail source directory. +Run "patch < qregex.patch" then "make qmail-smtpd". Now run ./qmail-smtpd and +test your new rules to make sure they work as expected. + +Install the new binary by cd'ing to /var/qmail/bin and as root (in one command) +copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the +source directory to 'qmail-smtpd'. +(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd) + +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. + + +CONTROL FILES: + +qregex provides you with three new control files. +The first (which really isn't new) is "control/badmailfrom". This file +originally matched addresses statically and now contains your REs for matching +from the 'mail from' command. +The other two are "control/badmailto" and "control/badhelo". They are exactly +the same as the first except that they match against the 'rcpt to' and +'helo/ehlo' commands respectively. + +If you prefer you can symlink the badmailfrom and badmailto control files +(ln -s badmailfrom badmailto) and only maintain two 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 litte more interesting) + ----------------------------------- + # must not contain invalid characters, brakets or multiple @'s + [!%#:*^(){}] + @.*@ + ----------------------------------- + +Also 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 as shown by the badmailto example. + + +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: +This version of qregex was prepared by: + Andrew St. Jean + andrew@arda.homeunix.net + www.arda.homeunix.net/store/qmail/ + +Documentation (manpage updates) and qmail-showctl patch added by: + Jeremy Kitchen + kitchen at scriptkitchen dot com + http://www.scriptkitchen.com/qmail + +Badhelo additions supplied by: + Alex Pleiner + alex@zeitform.de + zeitform Internet Dienste + http://www.zeitform.de/ + +Original qregex patch can be found at: + www : http://www.unixpimps.org/software/qregex + email: evan at unixpimps dot org + + diff -urN ../qmail-1.03_unpatched/README.tls ./README.tls --- ../qmail-1.03_unpatched/README.tls 1970-01-01 01:00:00.000000000 +0100 +++ ./README.tls 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,99 @@ +Frederik Vermeulen 20040419 +http://inoa.net/qmail-tls/ + +This patch implements RFC 3207 (was RFC 2487) 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-0.9.7d http://www.openssl.org/ + (any 0.9.6 and 0.9.7 version is presumed to work) + - apply patch to netqmail-1.05 http://qmail.org/netqmail + (should work on qmail-1.03 too). 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;") + If you don't have a server to test with, you can test + by sending mail to tag-ping@tbs-internet.com, + which will bounce your mail. + +Optional: - when DEBUG is defined, some extra TLS info will be logged + - qmail-remote will authenticate with the certificate in + /var/qmail/control/clientcert.pem. By preference this is + the same as servercert.pem, where nsCertType should be + == server,client or be a generic certificate (no usage specified). + - when a 512 bit RSA key is provided in /var/qmail/control/rsa512.pem, + this key will be used instead of (slow) on-the-fly generation by + qmail-smtpd. Idem for 512 and 1024 DH params in control/dh512.pem + and control/dh1024.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 + (or self-signed server certificate). + CommonName has to match. + WARNING: this option may cause mail to be delayed, bounced, + doublebounced, and lost. + - client authentication: + when relay rules would reject an incoming mail, + qmail-smtpd can allow the mail based on a presented cert. + Certs are verified against a CA list in + /var/qmail/control/clientca.pem (eg. http://www.modssl.org/ + source/cvs/exp/mod_ssl/pkg.mod_ssl/pkg.sslcfg/ca-bundle.crt) + and the cert email-address has to match a line in + /var/qmail/control/tlsclients. This email-address is logged + in the headers. 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). + - some broken servers have a problem with TLSv1 compatibility. + Uncomment the line where we set the SSL_OP_NO_TLSv1 option. + - needs working /dev/urandom (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 -urN ../qmail-1.03_unpatched/README.zeitform ./README.zeitform --- ../qmail-1.03_unpatched/README.zeitform 1970-01-01 01:00:00.000000000 +0100 +++ ./README.zeitform 2005-01-21 20:14:54.000000000 +0100 @@ -0,0 +1,262 @@ +README.zeitform +zeitform-qmail-toaster, version 0.21 +2003-2004 alex pleiner, alex@zeitform.de +(c) zeitform Internet Dienste + +http://alex.zeitform.de/qmail-new/qmail.html + + +Patches for a zeitform qmail toaster based on qmail 1.03 +--------------------------------------------------------- + +USAGE: + +patch -p0 < zeitform-qmail-toaster-x.x.patch + + +OVERVIEW: + +This patch is just a collection of other patches found on the web or +provided by some developers around. This patch-collection +(toaster-patch) has been tested under Linux only. Use with care. + +The following patches are included: + + BUGFIXES + + * errno (Phil Edwards): + fix for the changes in the new glibc (2.3.1 or newer). This patch + is not needed on systems with an older glibc but won't hurt. + http://memoryhole.net/qmail/glibc-2.3.x.patch + http://news.gmane.org/article.php?id=13960&group=gmane.mail.qmail.general + http://www.qmail.org/moni.csi.hu/pub/glibc-2.3.1/ + + * qmail-local-fix (Erik Sjoelund): + fixes a bug (a logic error) in qmail-local. + http://memoryhole.net/qmail/bugfix.qmail-local.patch + http://www.ornl.gov/its/archives/mailing-lists/qmail/2000/10/msg00696.html + + * 0.0.0.0 (Scott Gifford): + Qmail ought to recognize 0.0.0.0 as a local IP address, but + doesn't by default. That may allow spammers to spoof you, and + certainly means that Qmail is incorrect. + http://memoryhole.net/qmail/qmail-0.0.0.0.patch + http://www.suspectclass.com/~sgifford/qmail/qmail-0.0.0.0.patch + + * sendmail-flagf (David Phillips): + sendmail emulation doesn't correctly support the -f flag as it should + http://memoryhole.net/qmail/sendmail-flagf.patch + http://david.acz.org/software/sendmail-flagf.patch + + * link-sync (Frank Denis): + Qmail was designed for BSD-like filesystems. And it is unreliable + under Linux because it assumes that link() is a synchronous + operation. This is not the case with EXT2 and ReiserFS. + http://www.jedi.claranet.fr/qmail-tuning.html + http://www.jedi.claranet.fr/qmail-link-sync.patch + + * isoc (James Craig Burley) + This patch improves ISO C conformance of qmail code -- specifically, + of qmail-lspawn, qmail-newmrh, qmail-newu, qmail-pop3d, qmail-popup, + qmail-rspawn, and qmail-smtpd. + http://www.jcb-sc.com/qmail/patches/qmail-isoc.patch + + EXTENSIONS + + * qmailqueue (Bruce Guenter): + The QMAILQUEUE patch is used as a generic way to insert another + program into the Qmail structure. It is merely a support patch + (i.e. supports other uses), and has no real additional + functionality in and of itself. QMAILQUEUE is needed to run + qmail-scanner (antivirus, anti-spam). + http://memoryhole.net/qmail/qmailqueue-patch + http://www.qmail.org/qmailqueue-patch + + Usage: QMAILQUEUE="/path/to/queue-replacement" + + * oversizedns (Christopher K. Davis): + get qmail to handle oversize DNS packets. (Other DNS servers + sometimes send DNS packets that are bigger than they ought to be.) + http://www.ckdhr.com/ckd/qmail-103.patch + + * qregex (Andrew St. Jean): + With REs (Regular Expresions) it becomes quite easy to filter out + email addresses that contain invalid characters or simply aren't a + real address. + http://www.arda.homeunix.net/store/qmail/ + + Usage: see URL above or README.qregex + + * starttls (Frederik Vereulen): + support for STARTTLS ESMTP extension + old: http://memoryhole.net/qmail/qmail-1.03-tls-20021228.patch + old: http://inoa.net/qmail/qmail-1.03-tls.patch + http://inoa.net/qmail-tls/netqmail-1.04-tls-20040120.patch + + Usage: see documentation in patch file or README.tls + + * maildir++ (Bill Shupp): + This patch adds maildirquota (Maildir++) support to qmail-pop3d + and qmail-local. It was created because when vpopmail switched to + maildirquotas, a user's quota usage was not decreased after + deleting mail via qmail-pop3d. + http://shupp.org/patches/qmail-maildir++.patch + + * rfc2821 (Matthias Andree): + changes qmail-remote to skip over MX servers that greet with + codes 4xx or 5xx and try the next MX for real RFC-2821 compliance + (Sendmail and Postfix do that). + http://www-dt.e-technik.uni-dortmund.de/~ma/qmail/patch-qmail-1.03-rfc2821.diff + + * smtp_auth and size (Erwin Hoffmann): + add SMTP_AUTH to qmail-smtpd (CRAM-MD5, PLAIN and LOGIN). This works + with vpopmail versions above 5.4.0 without modification. Please note that + you must omit the hostname argument in the qmail-smtpd startup file as + documented in README.auth. + ESMTP has a way of explicitly defining this size limit up front, + which allows valid clients and standards-compliant email servers + to avoid spending the time to send the large email before + discovering that it's too big. + http://www.fehcom.de/qmail/smtpauth.html + + Usage: echo 50000000 > /var/qmail/control/databytes + + * pop3_auth_and_capa (Alex Pleiner) + adds AUTH to qmail-popup (CRAM-MD5 only). This is based on Erwin Hoffmann's + code and slightly modified. This patch also adds the CAPA command to + qmail-popup. + http://alex.zeitform.de/qmail/qmail-popup-auth_cram_md5/ + + * capa_pop3d (Tom Clegg): + adds CAPA to qmail-pop3d. Based on qmail-capa-pop3d.patch and the + capa features of pop3_auth_and_capa for qmail-popup. + http://tomclegg.net/qmail/qmail-capa-pop3d.patch + + * pop3d-stat (Dwayne Koonce) + This patch changes number of messages returned in qmail-pop3d's reponse to STAT. + It now returns number of not deleted messages. Returned number of all messages. + http://alex.zeitform.de/qmail/qmail_single_patches/qmail-pop3d-stat.tls.patch + + * viruscan (Russel Nelson, mods by zeitform): + this patch blocks executable attachments at smtp level. The modifications bring + back Charles Cazabon's EXECUTABLEOK from version 1.1 + http://qmail.mirrors.space.net/qmail-smtpd-viruscan-1.3.patch + + Usage: Set EXECUTABLEOK to disable this + Edit /var/qmail/control/signatures (see below) + + * chkuser2 (Antonio Nati): + this patch checks for valid users based on vpopmail before accepting + mails for local domains + http://www.interazioni.it/opensource/chkuser/ + + Usage: Edit chkuser_settings.h and recompile + + * qmail-queue-custom-error (Flavio Curti) + Adds the possibility for a qmail-queue-replacement to offer custom + error (=bounce) messages (used by simscan when configured with + --enable-custom-smtp-reject=y) + Part of simscan (http://www.inter7.com) + + +FURTHER ACTION: + + * modify the file /var/qmail/bin/update_tmprsadh (read README.tls + for further instructions). create crontab entry: + + 01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1 + + * create the signature file like this: + +cat </var/qmail/control/signatures +# Windows executables seen in active virii +TVqQAAMAA +TVpQAAIAA +# Additional windows executable signatures not yet seen in virii +TVpAALQAc +TVpyAXkAX +TVrmAU4AA +TVrhARwAk +TVoFAQUAA +TVoAAAQAA +TVoIARMAA +TVouARsAA +TVrQAT8AA +# .ZIPfile signature seen in SoBig.E and mydoom: +# selective zip blocking is not really working - block all or none +#UEsDBBQAA +#UEsDBAoAAA +# .GIF file found in a previous Microsoft virus making the rounds. +#R0lGODlhaAA7APcAAP///+rp6puSp6GZrDUjUUc6Zn53mFJMdbGvvVtXh2xre8bF1x8cU4yLprOy +EOF + + +HISTORY: + +v0.21: updated maildir++ (duplicate free bug) + updated chkuser 2.0.8b +v0.20: updated chkuser 2.0.7 (removed mfcheck), added qmail-queue-custom-error +v0.19: updated chkuser 2.0.5 (edit chkuser_settings.h) +v0.18: dropped size patch, upgraded smtp_auth by Erwin Hoffmann (0.56, + includes size extension). Take care: smtp_auth versions before 0.43 + are buggy if you disable CRAM-MD5. +v0.17: upgraded TLS patch (20040419) +v0.16: added logging for mfcheck, viruscan, badrcptto + updated mfcheck. +v0.15: change: update of qregex to include bad HELO checks + announce: website for zeitform qmail toaster + http://alex.zeitform.de/qmail-new/qmail.html +v0.14: changes: removed all code from Vladimir Kabanov and reimplemented + AUTH CRAM-MD5 for qmail-popup. Sorry Vlad, your code was very + useful, but we need to upgrade vpopmail. This toaster patch now + supports the current version of vpopmail. SMTP AUTH is now done + by the patch provided by Erwin Hoffmann. + change: chkusr for vpopmail > 5.2.25 + change: modification to chkusr for qmail-ext +v0.13: fix: fixed the CAPA in qmail-pop3d.c that still used puts() + fix: fixed qregex memleak in qmail-smtpd.c (according to + qmail-1.03-qregex-memleak-fix.patch) + added qmail-pop3d-stat patch + updated tls patch and added README.tls +v0.12: updated Russel's viruscan patch (1.3) +v0.11: added isoc patch +v0.10: added chkusr patch (needs vpopmail < 5.3.25) + added documentation for various patches to qmail-showctl and + qmail-control.9 +v0.9 : testing stuff (not published) +v0.8 : added Russel's viruscan patch. +v0.7 : added bugfix for size patch, removed *.orig and *.rej files + created by this patch +v0.6 : added smtpd-auth-close3 (for morercpthosts) +v0.5 : fixed a bug causing smtp to fail. After applying the size + patch qmail-smtp sended "250 8BITMIME" followed by "250 SIZE" + This is wrong. +v0.4 : added rfc2821 patch *** DO NOT USE *** +v0.3 : added some bugfixes and extensions from *** DO NOT USE *** + http://memoryhole.net/qmail/. qmail-local-fix, 0.0.0.0, + sendmail-flagf, size +v0.2 : added link-sync +v0.1 : Initial collection + + +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 -urN ../qmail-1.03_unpatched/TARGETS ./TARGETS --- ../qmail-1.03_unpatched/TARGETS 1998-06-15 12:53:16.000000000 +0200 +++ ./TARGETS 2005-01-21 19:25:03.000000000 +0100 @@ -15,11 +15,20 @@ 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 case_lowers.o case_starts.o +case_startb.o case.a getln.o getln2.o @@ -168,6 +177,8 @@ constmap.o timeoutread.o timeoutwrite.o +tls.o +ssl_timeoutio.o timeoutconn.o tcpto.o dns.o @@ -250,8 +261,10 @@ qmail-qmtpd.o rcpthosts.o qmail-qmtpd +base64.o qmail-smtpd.o qmail-smtpd +qregex.o sendmail.o sendmail tcp-env.o @@ -320,6 +333,7 @@ binm2+df binm3 binm3+df +Makefile-cert it qmail-local.0 qmail-lspawn.0 @@ -385,3 +399,5 @@ man setup check +chkuser.o +update_tmprsadh diff -urN ../qmail-1.03_unpatched/base64.c ./base64.c --- ../qmail-1.03_unpatched/base64.c 1970-01-01 01:00:00.000000000 +0100 +++ ./base64.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,123 @@ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(in,l,out) +const unsigned char *in; +int l; +stralloc *out; /* not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while(in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + out->len = (n * 3) - p; + if (!stralloc_ready(out,out->len)) return -1; + s = out->s; + + for(i = 0; i < n - 1 ; i++) { + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for(i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; + s = out->s; + + for (i = 0;i < in->len;i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + return 0; +} + diff -urN ../qmail-1.03_unpatched/base64.h ./base64.h --- ../qmail-1.03_unpatched/base64.h 1970-01-01 01:00:00.000000000 +0100 +++ ./base64.h 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff -urN ../qmail-1.03_unpatched/case_startb.c ./case_startb.c --- ../qmail-1.03_unpatched/case_startb.c 1970-01-01 01:00:00.000000000 +0100 +++ ./case_startb.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,21 @@ +#include "case.h" + +int case_startb(s,len,t) +register char *s; +unsigned int len; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (!len) return 0; + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + if (x != y) return 0; + } +} diff -urN ../qmail-1.03_unpatched/cdb_seek.c ./cdb_seek.c --- ../qmail-1.03_unpatched/cdb_seek.c 1998-06-15 12:53:16.000000000 +0200 +++ ./cdb_seek.c 2005-01-21 19:25:03.000000000 +0100 @@ -1,6 +1,5 @@ #include #include -extern int errno; #include "cdb.h" #ifndef SEEK_SET diff -urN ../qmail-1.03_unpatched/cdbmake_add.c ./cdbmake_add.c --- ../qmail-1.03_unpatched/cdbmake_add.c 1998-06-15 12:53:16.000000000 +0200 +++ ./cdbmake_add.c 2005-01-21 19:25:03.000000000 +0100 @@ -1,3 +1,4 @@ +#include "alloc.h" #include "cdbmake.h" void cdbmake_init(cdbm) diff -urN ../qmail-1.03_unpatched/chkuser.c ./chkuser.c --- ../qmail-1.03_unpatched/chkuser.c 1970-01-01 01:00:00.000000000 +0100 +++ ./chkuser.c 2005-01-21 20:15:34.000000000 +0100 @@ -0,0 +1,1156 @@ + +/* + * + * 'chkuser.c' v.2.0.8 + * 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 + * + */ + +#include + +/* required by vpopmail */ +#include + +#include +#include +#include + +#include "dns.h" +#include "env.h" +#include "ipme.h" +#include "now.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_VAUTH_OPEN + static int db_already_open = 0; +#endif + +#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + static char *starting_string = 0; + static int starting_value = -1; +#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 + +#if defined CHKUSER_SENDER_NOCHECK_VARIABLE + + static unsigned int sender_nocheck = 0; + +#endif + +#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX +static stralloc sender_user = {0}; +static stralloc sender_domain = {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, ":" ); + if (remoteinfo) { + substdio_puts (subfderr, remoteinfo); + } + substdio_puts (subfderr, ":" ); +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + if (identify_remote) substdio_puts (subfderr, identify_remote); +#endif + substdio_puts (subfderr, "> remote <"); + if (fakehelo) substdio_puts (subfderr, fakehelo); + substdio_puts (subfderr, ":" ); + if (remotehost) substdio_puts (subfderr, remotehost); + substdio_puts (subfderr, ":" ); + if (remoteip) substdio_puts (subfderr, remoteip); + substdio_puts (subfderr, "> 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 + && (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_SENDER_CHAR_1) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_2 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_3 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_4 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_5 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5) +#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) { + + char * temp_string; + +#if !defined CHKUSER_ALWAYS_ON && 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; + } + } else { + starting_string = ""; + } +#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 + + temp_string = env_get (CHKUSER_SENDER_NOCHECK_VARIABLE); + if (temp_string) { + sender_nocheck = 1; + } else { + sender_nocheck = 0; + } + +#endif + +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + + identify_remote = env_get (CHKUSER_IDENTIFY_REMOTE_VARIABLE); + if (identify_remote) { + } + +#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_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + if (starting_value == -1) { + if (addrallowed()) { + return CHKUSER_OK; + } else { + if (relayclient) { + return CHKUSER_RELAYING; + } + + return CHKUSER_NORCPTHOSTS; + } + } +#endif + + 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_VAUTH_OPEN + if (db_already_open != 1) { + if (vauth_open () == 0) { + db_already_open == 1; + } else { + retstat = CHKUSER_ERR_AUTH_RESOURCE; + } + }; +#endif + + case 5: + +#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 6: + +/* Check if domain has bouncing enabled */ + +#if !defined CHKUSER_ALWAYS_ON + +#if defined CHKUSER_STARTING_VARIABLE + if (starting_value == 0) { +#endif + + 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; + 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; + break; + } +#endif +#if defined CHKUSER_STARTING_VARIABLE + } +#endif +#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, "/mailinglist")) 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_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_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 CHKUSER_KO; +} + + +/* + * + * 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_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + if (starting_value == -1) { + return CHKUSER_OK; + } +#endif + +#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX + +#if defined CHKUSER_SENDER_NOCHECK_VARIABLE + + if (sender_nocheck == 1) { + return CHKUSER_OK; + } +#endif + + if (sender->len <= 1) { +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted null sender", "accepted null sender always"); +#endif + return CHKUSER_OK; + } + + 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; + } + +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted sender", "sender accepted"); +#endif + + return CHKUSER_OK; +#endif + +#else + + return CHKUSER_OK; + +#endif + +} + + diff -urN ../qmail-1.03_unpatched/chkuser.h ./chkuser.h --- ../qmail-1.03_unpatched/chkuser.h 1970-01-01 01:00:00.000000000 +0100 +++ ./chkuser.h 2005-01-21 20:15:34.000000000 +0100 @@ -0,0 +1,51 @@ + +/* + * + * 'chkuser.h' v.2.0.8 + * 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 + * + */ + +#define CHKUSER +#define CHKUSER_VERSION "2.0.8" +#define CHKUSER_VERSION_RL 2 +#define CHKUSER_VERSION_MJ 0 +#define CHKUSER_VERSION_MN 8 + +#define CHKUSER_OK 1 +#define CHKUSER_RELAYING 0 +#define CHKUSER_KO -1 +#define CHKUSER_NORCPTHOSTS -10 +#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 -urN ../qmail-1.03_unpatched/chkuser_settings.h ./chkuser_settings.h --- ../qmail-1.03_unpatched/chkuser_settings.h 1970-01-01 01:00:00.000000000 +0100 +++ ./chkuser_settings.h 2005-01-21 20:13:03.000000000 +0100 @@ -0,0 +1,406 @@ + +/* + * + * 'chkuser_settings.h' v.2.0.8b + * 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 + * + */ + +/******************************************************** + * Debugging + ********************************************************/ + +/* + * 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 */ + + +/******************************************************** + * Enabling chkuser + ********************************************************/ + +/* + * 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 */ + +/* + * 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 + * if CHKUSER_ALWAYS_ON is defined, this define is useless + * 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 check for vpopmail users + */ +#define CHKUSER_ENABLE_USERS + +/* + * 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 + +/* + * 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 '-' + +/* + * Uncomment this to check for alias + */ +#define CHKUSER_ENABLE_ALIAS + +/* + * This define has been eliminated. + * 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 */ + +/* + * Enables checking for EZMLM lists + * this define substitutes #define CHKUSER_ENABLE_LISTS + * + */ +#define CHKUSER_ENABLE_EZMLM_LISTS + +/* + * 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 '-' + +/* + * 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 '-' + +/* + * 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 next define to accept recipients for + * aliases that have a -default extension + */ +#define CHKUSER_ENABLE_ALIAS_DEFAULT + + +/******************************************************** + * Vpopmail depepnding features + ********************************************************/ + +/* + * 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 */ + + +/* + * This is to enable auth open checking + * it is useful to avoid bouncing if MySQL/LDAP/PostGRES/etc are down or not reachable + */ +/* #define CHKUSER_ENABLE_VAUTH_OPEN */ + +/* + * 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 + +/* + * Enables final clean-up routine of chkuser + * This routine cleans open DB connections used for checking users and valiases + */ +/* #define CHKUSER_DB_CLEANUP */ + + +/******************************************************** + * UID/GID switching + ********************************************************/ + +/* + * 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 */ + + +/******************************************************** + * Checking addresses format & MX + ********************************************************/ + +/* + * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses + */ +/* #define CHKUSER_DOMAIN_WANTED */ + +/* + * 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 end +ing "-." + */ +/* #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 domain MX for sender address + * it works on the first rcpt address, despite of any domain setting on chkuser + */ +#define CHKUSER_SENDER_MX + + +/* + * 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 + */ +#define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK" + +/* + * 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 usage of "#" and "+" characters within sender address + * This is used by SRS (Sender Rewriting Scheme) products + */ +/* #define CHKUSER_ALLOW_SENDER_SRS */ + +/* + * 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. + */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_1 '$' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */ + +/* + * Uncomment to enable usage of "#" and "+" characters within rcpt address + * This is used by SRS (Sender Rewriting Scheme) products + */ +/* #define CHKUSER_ALLOW_RCPT_SRS */ + +/* + * 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. + */ +/* #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 '*' */ + +/* + * 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 next define to accept always a null sender, despite of + * other settings + * NOT USED!!! + */ +/* #define CHKUSER_ACCEPT_NULL_SENDER */ + +/* + * Default commented from 2.0.7 + * Uncomment to accept <> senders from hosts which have name in DNS (TCPREMOTE + * (should be set, otherwise will not accept any bouncing message) + * This define will not work if CHKUSER_ACCEPT_NULL_SENDER is defined + * NOT USED!!! + */ +/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ + + +/******************************************************** + * Logging + ********************************************************/ + +/* + * 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 logging of "good" senders + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +/* #define CHKUSER_LOG_VALID_SENDER */ + +/* + * Help identifying remote authorized IPs giving them a descriptive name + * Can be put in tcp.smtp, and will be displayed inside chkuser log + * Substitutes RELAYCLIENT in chkuser logging + */ +#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" + + +/******************************************************** + * Tarpitting + ********************************************************/ + +/* + * 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" + +/* + * Delay to wait for each not existing recipient + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY 1000 + +/* + * 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 500 + +/* + * 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 + +/******************************************************** + * Quota Checking + ********************************************************/ + +/* + * 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" */ + + +/******************************************************** + * Error strings + ********************************************************/ + +#define CHKUSER_NORCPT_STRING "511 sorry, no mailbox here by that name (#5.1.1 - chkuser)\r\n" +#define CHKUSER_RESOURCE_STRING "430 system temporary unavailable, try again later (#4.3.0 - chkuser)\r\n" +#define CHKUSER_MBXFULL_STRING "522 sorry, recipient mailbox is full (#5.2.2 - chkuser)\r\n" +#define CHKUSER_MAXRCPT_STRING "571 sorry, reached maximum number of recipients for one session (#5.7.1 - chkuser)\r\n" +#define CHKUSER_MAXWRONGRCPT_STRING "571 sorry, you are violating our security policies (#5.1.1 - chkuser)\r\n" +#define CHKUSER_DOMAINMISSING_STRING "511 sorry, you must specify a domain (#5.1.1 - chkuser)\r\n" +#define CHKUSER_RCPTFORMAT_STRING "511 sorry, recipient address has invalid format (#5.1.1 - chkuser)\r\n" +#define CHKUSER_RCPTMX_STRING "511 sorry, can't find a valid MX for rcpt domain (#5.1.1 - chkuser)\r\n" +#define CHKUSER_SENDERFORMAT_STRING "571 sorry, sender address has invalid format (#5.7.1 - chkuser)\r\n" +#define CHKUSER_SENDERMX_STRING "511 sorry, can't find a valid MX for sender domain (#5.1.1 - chkuser)\r\n" +#define CHKUSER_INTRUSIONTHRESHOLD_STRING "571 sorry, you are violating our security policies (#5.7.1 - chkuser)\r\n" +#define CHKUSER_NORELAY_STRING "553 sorry, that domain isn't in my list of allowed rcpthosts (#5.5.3 - chkuser)\r\n" + +#define CHKUSER_RCPTMX_TMP_STRING "451 DNS temporary failure (#4.5.1 - chkuser)\r\n" +#define CHKUSER_SENDERMX_TMP_STRING "451 DNS temporary failure (#4.5.1 - chkuser)\r\n" + + diff -urN ../qmail-1.03_unpatched/conf-cc ./conf-cc --- ../qmail-1.03_unpatched/conf-cc 1998-06-15 12:53:16.000000000 +0200 +++ ./conf-cc 2005-01-21 19:25:03.000000000 +0100 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -DTLS=20040120 -I/usr/local/ssl/include -I/home/vpopmail/include This will be used to compile .c files. diff -urN ../qmail-1.03_unpatched/dns.c ./dns.c --- ../qmail-1.03_unpatched/dns.c 1998-06-15 12:53:16.000000000 +0200 +++ ./dns.c 2005-01-21 19:25:03.000000000 +0100 @@ -7,8 +7,6 @@ #include extern int res_query(); extern int res_search(); -extern int errno; -extern int h_errno; #include "ip.h" #include "ipalloc.h" #include "fmt.h" @@ -21,10 +19,12 @@ static unsigned short getshort(c) unsigned char *c; { unsigned short u; u = c[0]; return (u << 8) + c[1]; } -static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response; +static struct { unsigned char *buf; } response; +static int responsebuflen = 0; static int responselen; static unsigned char *responseend; static unsigned char *responsepos; +static u_long saveresoptions; static int numanswers; static char name[MAXDNAME]; @@ -45,18 +45,33 @@ errno = 0; if (!stralloc_copy(&glue,domain)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; - responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response)); + if (!responsebuflen) + if (response.buf = (unsigned char *)alloc(PACKETSZ+1)) + responsebuflen = PACKETSZ+1; + else return DNS_MEM; + + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + if ((responselen >= responsebuflen) || + (responselen > 0 && (((HEADER *)response.buf)->tc))) + { + if (responsebuflen < 65536) + if (alloc_re(&response.buf, responsebuflen, 65536)) + responsebuflen = 65536; + else return DNS_MEM; + saveresoptions = _res.options; + _res.options |= RES_USEVC; + responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); + _res.options = saveresoptions; + } if (responselen <= 0) { if (errno == ECONNREFUSED) return DNS_SOFT; if (h_errno == TRY_AGAIN) return DNS_SOFT; return DNS_HARD; } - if (responselen >= sizeof(response)) - responselen = sizeof(response); responseend = response.buf + responselen; responsepos = response.buf + sizeof(HEADER); - n = ntohs(response.hdr.qdcount); + n = ntohs(((HEADER *)response.buf)->qdcount); while (n-- > 0) { i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); @@ -66,7 +81,7 @@ if (i < QFIXEDSZ) return DNS_SOFT; responsepos += QFIXEDSZ; } - numanswers = ntohs(response.hdr.ancount); + numanswers = ntohs(((HEADER *)response.buf)->ancount); return 0; } @@ -269,12 +284,11 @@ int pref; { int r; - struct ip_mx ix; + struct ip_mx ix = {0}; if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { - ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; @@ -293,9 +307,16 @@ ix.ip = ip; ix.pref = pref; if (r == DNS_SOFT) return DNS_SOFT; - if (r == 1) + if (r == 1) { +#ifdef IX_FQDN + ix.fqdn = glue.s; +#endif if (!ipalloc_append(ia,&ix)) return DNS_MEM; } + } +#ifdef IX_FQDN + glue.s = 0; +#endif return 0; } @@ -315,7 +336,7 @@ { int r; struct mx { stralloc sa; unsigned short p; } *mx; - struct ip_mx ix; + struct ip_mx ix = {0}; int nummx; int i; int j; @@ -327,7 +348,6 @@ if (!stralloc_copy(&glue,sa)) return DNS_MEM; if (!stralloc_0(&glue)) return DNS_MEM; if (glue.s[0]) { - ix.pref = 0; if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) { if (!ipalloc_append(ia,&ix)) return DNS_MEM; diff -urN ../qmail-1.03_unpatched/error.3 ./error.3 --- ../qmail-1.03_unpatched/error.3 1998-06-15 12:53:16.000000000 +0200 +++ ./error.3 2005-01-21 19:25:03.000000000 +0100 @@ -3,8 +3,7 @@ error \- syscall error codes .SH SYNTAX .B #include - -extern int \fBerrno\fP; +.B #include extern int \fBerror_intr\fP; .br diff -urN ../qmail-1.03_unpatched/error.h ./error.h --- ../qmail-1.03_unpatched/error.h 1998-06-15 12:53:16.000000000 +0200 +++ ./error.h 2005-01-21 19:25:03.000000000 +0100 @@ -1,7 +1,7 @@ #ifndef ERROR_H #define ERROR_H -extern int errno; +#include extern int error_intr; extern int error_nomem; diff -urN ../qmail-1.03_unpatched/hier.c ./hier.c --- ../qmail-1.03_unpatched/hier.c 1998-06-15 12:53:16.000000000 +0200 +++ ./hier.c 2005-01-21 19:25:03.000000000 +0100 @@ -143,6 +143,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); diff -urN ../qmail-1.03_unpatched/ipalloc.h ./ipalloc.h --- ../qmail-1.03_unpatched/ipalloc.h 1998-06-15 12:53:16.000000000 +0200 +++ ./ipalloc.h 2005-01-21 19:25:03.000000000 +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 -urN ../qmail-1.03_unpatched/ipme.c ./ipme.c --- ../qmail-1.03_unpatched/ipme.c 1998-06-15 12:53:16.000000000 +0200 +++ ./ipme.c 2005-01-21 19:25:03.000000000 +0100 @@ -46,6 +46,11 @@ ipme.len = 0; ix.pref = 0; + /* 0.0.0.0 is a special address which always refers to + * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a. + */ + byte_copy(&ix.ip,4,"\0\0\0\0"); + if (!ipalloc_append(&ipme,&ix)) { return 0; } if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1; len = 256; diff -urN ../qmail-1.03_unpatched/maildirflags.c ./maildirflags.c --- ../qmail-1.03_unpatched/maildirflags.c 1970-01-01 01:00:00.000000000 +0100 +++ ./maildirflags.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,23 @@ +/* +** Copyright 2000 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include +#include + +static const char rcsid[]="$Id: maildirflags.c,v 1.1 2000/10/07 01:10:19 mrsam Exp $"; + +int maildir_hasflag(const char *filename, char flag) +{ + const char *p=strrchr(filename, '/'); + + if (p) + filename=p+1; + + p=strrchr(p, ':'); + if (p && strncmp(p, ":2,", 3) == 0 && + strchr(p+3, flag)) + return (1); + return (0); +} diff -urN ../qmail-1.03_unpatched/maildirgetquota.c ./maildirgetquota.c --- ../qmail-1.03_unpatched/maildirgetquota.c 1970-01-01 01:00:00.000000000 +0100 +++ ./maildirgetquota.c 2005-01-21 19:25:03.000000000 +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: maildirgetquota.h,v 1.5 1999/12/06 13:21:05 mrsam Exp $"; + +#define QUOTABUFSIZE 256 + +int maildir_getquota(const char *, char [QUOTABUFSIZE]); + +#ifdef __cplusplus +} +#endif + +#endif diff -urN ../qmail-1.03_unpatched/maildirmisc.h ./maildirmisc.h --- ../qmail-1.03_unpatched/maildirmisc.h 1970-01-01 01:00:00.000000000 +0100 +++ ./maildirmisc.h 2005-01-21 19:25:03.000000000 +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: maildirmisc.h,v 1.8 2000/12/25 17:33:06 mrsam Exp $"; + +/* +** +** Miscellaneous maildir-related code +** +*/ + +/* Some special folders */ + +#define INBOX "INBOX" +#define DRAFTS "Drafts" +#define SENT "Sent" +#define TRASH "Trash" + +#define SHAREDSUBDIR "shared-folders" + +char *maildir_folderdir(const char *, /* maildir */ + const char *); /* folder name */ + /* Returns the directory corresponding to foldername (foldername is + ** checked to make sure that it's a valid name, else we set errno + ** to EINVAL, and return (0). + */ + +char *maildir_filename(const char *, /* maildir */ + const char *, /* folder */ + const char *); /* filename */ + /* + ** Builds the filename to this message, suitable for opening. + ** If the file doesn't appear to be there, search the maildir to + ** see if someone changed the flags, and return the current filename. + */ + +int maildir_safeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + +/* +** Same arguments as open(). When we're accessing a shared maildir, +** prevent someone from playing cute and dumping a bunch of symlinks +** in there. This function will open the indicate file only if the +** last component is not a symlink. +** This is implemented by opening the file with O_NONBLOCK (to prevent +** a DOS attack of someone pointing the symlink to a pipe, causing +** the open to hang), clearing O_NONBLOCK, then stat-int the file +** descriptor, lstating the filename, and making sure that dev/ino +** match. +*/ + +int maildir_semisafeopen(const char *, /* filename */ + int, /* mode */ + int); /* perm */ + +/* +** Same thing, except that we allow ONE level of soft link indirection, +** because we're reading from our own maildir, which points to the +** message in the sharable maildir. +*/ + +int maildir_mkdir(const char *); /* directory */ +/* +** Create maildir including all subdirectories in the path (like mkdir -p) +*/ + +void maildir_purgetmp(const char *); /* maildir */ + /* purges old stuff out of tmp */ + +void maildir_purge(const char *, /* directory */ + unsigned); /* time_t to purge */ + +void maildir_getnew(const char *, /* maildir */ + const char *); /* folder */ + /* move messages from new to cur */ + +int maildir_deletefolder(const char *, /* maildir */ + const char *); /* folder */ + /* deletes a folder */ + +int maildir_mddelete(const char *); /* delete a maildir folder by path */ + +void maildir_list_sharable(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to callback func */ + /* list sharable folders */ + +int maildir_shared_subscribe(const char *, /* maildir */ + const char *); /* folder */ + /* subscribe to a shared folder */ + +void maildir_list_shared(const char *, /* maildir */ + void (*)(const char *, void *), /* callback function */ + void *); /* 2nd arg to the callback func */ + /* list subscribed folders */ + +int maildir_shared_unsubscribe(const char *, /* maildir */ + const char *); /* folder */ + /* unsubscribe from a shared folder */ + +char *maildir_shareddir(const char *, /* maildir */ + const char *); /* folder */ + /* + ** Validate and return a path to a shared folder. folderdir must be + ** a name of a valid shared folder. + */ + +void maildir_shared_sync(const char *); /* maildir */ + /* "sync" the shared folder */ + +int maildir_sharedisro(const char *); /* maildir */ + /* maildir is a shared read-only folder */ + +int maildir_unlinksharedmsg(const char *); /* filename */ + /* Remove a message from a shared folder */ + +/* Internal function that reads a symlink */ + +char *maildir_getlink(const char *); + + /* Determine whether the maildir filename has a certain flag */ + +int maildir_hasflag(const char *filename, char); + +#define MAILDIR_DELETED(f) maildir_hasflag((f), 'T') + +#ifdef __cplusplus +} +#endif + +#endif diff -urN ../qmail-1.03_unpatched/maildiropen.c ./maildiropen.c --- ../qmail-1.03_unpatched/maildiropen.c 1970-01-01 01:00:00.000000000 +0100 +++ ./maildiropen.c 2005-01-21 19:25:03.000000000 +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: maildiropen.c,v 1.7 2000/12/10 04:43:44 mrsam Exp $"; + +char *maildir_getlink(const char *filename) +{ +#if HAVE_READLINK +size_t bufsiz; +char *buf; + + bufsiz=0; + buf=0; + + for (;;) + { + int n; + + if (buf) free(buf); + bufsiz += 256; + if ((buf=malloc(bufsiz)) == 0) + { + perror("malloc"); + return (0); + } + if ((n=readlink(filename, buf, bufsiz)) < 0) + { + free(buf); + return (0); + } + if (n < bufsiz) + { + buf[n]=0; + break; + } + } + return (buf); +#else + return (0); +#endif +} + +int maildir_semisafeopen(const char *path, int mode, int perm) +{ + +#if HAVE_READLINK + +char *l=maildir_getlink(path); + + if (l) + { + int f; + + if (*l != '/') + { + char *q=malloc(strlen(path)+strlen(l)+2); + char *s; + + if (!q) + { + free(l); + return (-1); + } + + strcpy(q, path); + if ((s=strchr(q, '/')) != 0) + s[1]=0; + else *q=0; + strcat(q, l); + free(l); + l=q; + } + + f=maildir_safeopen(l, mode, perm); + + free(l); + return (f); + } +#endif + + return (maildir_safeopen(path, mode, perm)); +} + +int maildir_safeopen(const char *path, int mode, int perm) +{ +struct stat stat1, stat2; + +int fd=open(path, mode +#ifdef O_NONBLOCK + | O_NONBLOCK +#else + | O_NDELAY +#endif + , perm); + + if (fd < 0) return (fd); + if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1) + || lstat(path, &stat2)) + { + close(fd); + return (-1); + } + + if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino) + { + close(fd); + errno=ENOENT; + return (-1); + } + + return (fd); +} diff -urN ../qmail-1.03_unpatched/maildirparsequota.c ./maildirparsequota.c --- ../qmail-1.03_unpatched/maildirparsequota.c 1970-01-01 01:00:00.000000000 +0100 +++ ./maildirparsequota.c 2005-01-21 19:25:03.000000000 +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: maildirparsequota.c,v 1.2 1999/12/06 13:21:05 mrsam Exp $"; + +int maildir_parsequota(const char *n, unsigned long *s) +{ +const char *o; +int yes; + + if ((o=strrchr(n, '/')) == 0) o=n; + + for (; *o; o++) + if (*o == ':') break; + yes=0; + for ( ; o >= n; --o) + { + if (*o == '/') break; + + if (*o == ',' && o[1] == 'S' && o[2] == '=') + { + yes=1; + o += 3; + break; + } + } + if (yes) + { + *s=0; + while (*o >= '0' && *o <= '9') + *s= *s*10 + (*o++ - '0'); + return (0); + } + return (-1); +} diff -urN ../qmail-1.03_unpatched/maildirquota.c ./maildirquota.c --- ../qmail-1.03_unpatched/maildirquota.c 1970-01-01 01:00:00.000000000 +0100 +++ ./maildirquota.c 2005-01-21 19:30:44.000000000 +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: maildirquota.c,v 1.9 2002/05/01 04:05:33 mrsam Exp $"; + +/* Read the maildirsize file */ + +int maildirsize_read(const char *filename, /* The filename */ + int *fdptr, /* Keep the file descriptor open */ + off_t *sizeptr, /* Grand total of maildir size */ + unsigned *cntptr, /* Grand total of message count */ + unsigned *nlines, /* # of lines in maildirsize */ + struct stat *statptr) /* The stats on maildirsize */ +{ +char buf[5120]; +int f; +char *p; +unsigned l; +int n; +int first; + + if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0) + return (-1); + p=buf; + l=sizeof(buf); + + while (l) + { + n=read(f, p, l); + if (n < 0) + { + close(f); + return (-1); + } + if (n == 0) break; + p += n; + l -= n; + } + if (l == 0 || fstat(f, statptr)) /* maildir too big */ + { + close(f); + return (-1); + } + + *sizeptr=0; + *cntptr=0; + *nlines=0; + *p=0; + p=buf; + first=1; + while (*p) + { + long n=0; + int c=0; + char *q=p; + + while (*p) + if (*p++ == '\n') + { + p[-1]=0; + break; + } + + if (first) + { + first=0; + continue; + } + sscanf(q, "%ld %d", &n, &c); + *sizeptr += n; + *cntptr += c; + ++ *nlines; + } + *fdptr=f; + return (0); +} + +static char *makenewmaildirsizename(const char *, int *); +static int countcurnew(const char *, time_t *, off_t *, unsigned *); +static int countsubdir(const char *, const char *, + time_t *, off_t *, unsigned *); +static int statcurnew(const char *, time_t *); +static int statsubdir(const char *, const char *, time_t *); + +#define MDQUOTA_SIZE 'S' /* Total size of all messages in maildir */ +#define MDQUOTA_BLOCKS 'B' /* Total # of blocks for all messages in + maildir -- NOT IMPLEMENTED */ +#define MDQUOTA_COUNT 'C' /* Total number of messages in maildir */ + +static int qcalc(off_t s, unsigned n, const char *quota, int *percentage) +{ +off_t i; +int spercentage=0; +int npercentage=0; + + errno=ENOSPC; + while (quota && *quota) + { + int x=1; + + if (*quota < '0' || *quota > '9') + { + ++quota; + continue; + } + i=0; + while (*quota >= '0' && *quota <= '9') + i=i*10 + (*quota++ - '0'); + switch (*quota) { + default: + if (i < s) + { + *percentage=100; + return (-1); + } + + /* + ** For huge quotas, over 20mb, + ** divide numerator & denominator by 1024 to prevent + ** an overflow when multiplying by 100 + */ + + x=1; + if (i > 20000000) x=1024; + + spercentage = i ? (s/x) * 100 / (i/x):100; + break; + case 'C': + + if (i < n) + { + *percentage=100; + return (-1); + } + + /* Ditto */ + + x=1; + if (i > 20000000) x=1024; + + npercentage = i ? ((off_t)n/x) * 100 / (i/x):100; + break; + } + } + *percentage = spercentage > npercentage ? spercentage:npercentage; + return (0); +} + +static int doaddquota(const char *, int, const char *, long, int, int); + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, int *percentage); + + +int maildir_checkquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt) +{ +int dummy; + + return (docheckquota(dir, maildirsize_fdptr, quota_type, + xtra_size, xtra_cnt, &dummy)); +} + +int maildir_readquota(const char *dir, const char *quota_type) +{ +int percentage=0; +int fd=-1; + + (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage); + if (fd >= 0) + close(fd); + return (percentage); +} + +static int docheckquota(const char *dir, + int *maildirsize_fdptr, + const char *quota_type, + long xtra_size, + int xtra_cnt, + int *percentage) +{ +char *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder")); +char *newmaildirsizename; +struct stat stat_buf; +int maildirsize_fd; +off_t maildirsize_size; +unsigned maildirsize_cnt; +unsigned maildirsize_nlines; +int n; +time_t tm; +time_t maxtime; +DIR *dirp; +struct dirent *de; + + if (checkfolder == 0) return (-1); + *maildirsize_fdptr= -1; + strcat(strcpy(checkfolder, dir), "/maildirfolder"); + if (stat(checkfolder, &stat_buf) == 0) /* Go to parent */ + { + strcat(strcpy(checkfolder, dir), "/.."); + n=docheckquota(checkfolder, maildirsize_fdptr, + quota_type, xtra_size, xtra_cnt, percentage); + free(checkfolder); + return (n); + } + if (!quota_type || !*quota_type) return (0); + + strcat(strcpy(checkfolder, dir), "/maildirsize"); + time(&tm); + if (maildirsize_read(checkfolder, &maildirsize_fd, + &maildirsize_size, &maildirsize_cnt, + &maildirsize_nlines, &stat_buf) == 0) + { + n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt, + quota_type, percentage); + + if (n == 0) + { + free(checkfolder); + *maildirsize_fdptr=maildirsize_fd; + return (0); + } + close(maildirsize_fd); + + if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60) + return (n); + } + + maxtime=0; + maildirsize_size=0; + maildirsize_cnt=0; + + if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt)) + { + free(checkfolder); + return (-1); + } + + dirp=opendir(dir); + while (dirp && (de=readdir(dirp)) != 0) + { + if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size, + &maildirsize_cnt)) + { + free(checkfolder); + closedir(dirp); + return (-1); + } + } + if (dirp) + { +#if CLOSEDIR_VOID + closedir(dirp); +#else + if (closedir(dirp)) + { + free(checkfolder); + return (-1); + } +#endif + } + + newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd); + if (!newmaildirsizename) + { + free(checkfolder); + return (-1); + } + + *maildirsize_fdptr=maildirsize_fd; + + if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size, + maildirsize_cnt, 1)) + { + 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 -urN ../qmail-1.03_unpatched/maildirquota.h ./maildirquota.h --- ../qmail-1.03_unpatched/maildirquota.h 1970-01-01 01:00:00.000000000 +0100 +++ ./maildirquota.h 2005-01-21 19:25:03.000000000 +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: maildirquota.h,v 1.2 2000/09/04 17:10:28 mrsam Exp $"; + +int maildir_checkquota(const char *, /* Pointer to directory */ + int *, /* Initialized to -1, or opened descriptor for maildirsize */ + const char *, /* The quota */ + long, /* Extra bytes planning to add/remove from maildir */ + int); /* Extra messages planning to add/remove from maildir */ + +int maildir_addquota(const char *, /* Pointer to the maildir */ + int, /* Must be the int pointed to by 2nd arg to checkquota */ + const char *, /* The quota */ + long, /* +/- bytes */ + int); /* +/- files */ + +int maildir_readquota(const char *, /* Directory */ + const char *); /* Quota, from getquota */ + +int maildir_parsequota(const char *, unsigned long *); + /* Attempt to parse file size encoded in filename. Returns 0 if + ** parsed, non-zero if we didn't parse. */ + +#ifdef __cplusplus +} +#endif + +#endif diff -urN ../qmail-1.03_unpatched/numlib.h ./numlib.h --- ../qmail-1.03_unpatched/numlib.h 1970-01-01 01:00:00.000000000 +0100 +++ ./numlib.h 2005-01-21 19:25:03.000000000 +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: numlib.h,v 1.3 2001/08/12 15:46:40 mrsam Exp $"; + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define NUMBUFSIZE 60 + +/* Convert various system types to decimal */ + +char *str_time_t(time_t, char *); +char *str_off_t(off_t, char *); +char *str_pid_t(pid_t, char *); +char *str_ino_t(ino_t, char *); +char *str_uid_t(uid_t, char *); +char *str_gid_t(gid_t, char *); +char *str_size_t(size_t, char *); + +char *str_sizekb(unsigned long, char *); /* X Kb or X Mb */ + +/* Convert selected system types to hex */ + +char *strh_time_t(time_t, char *); +char *strh_pid_t(pid_t, char *); +char *strh_ino_t(ino_t, char *); + +#ifdef __cplusplus +} +#endif +#endif diff -urN ../qmail-1.03_unpatched/overmaildirquota.c ./overmaildirquota.c --- ../qmail-1.03_unpatched/overmaildirquota.c 1970-01-01 01:00:00.000000000 +0100 +++ ./overmaildirquota.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,42 @@ +/* +** Copyright 1998 - 1999 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "maildirquota.h" +#include +#include +#include +#include + +static const char rcsid[]="$Id: overquota.c,v 1.0 2002/06/09 16:21:05 mr sam Exp $"; + + +int user_over_maildirquota( const char *dir, const char *q) +{ +struct stat stat_buf; +int quotafd; +int ret_value; + + if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && + stat_buf.st_size > 0 && *q) + { + if (maildir_checkquota(dir, "afd, q, stat_buf.st_size, 1) + && errno != EAGAIN) + { + if (quotafd >= 0) close(quotafd); + ret_value = 1; + } else { + maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1); + if (quotafd >= 0) close(quotafd); + ret_value = 0; + } + } else { + ret_value = 0; + } + + return(ret_value); +} diff -urN ../qmail-1.03_unpatched/qmail-control.9 ./qmail-control.9 --- ../qmail-1.03_unpatched/qmail-control.9 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-control.9 2005-01-21 19:25:03.000000000 +0100 @@ -20,7 +20,9 @@ Comments are allowed in +.IR badhelo , .IR badmailfrom , +.IR badmailto , .IR locals , .IR percenthack , .IR qmqpservers , @@ -32,7 +34,9 @@ The following table lists all control files other than -.IR me . +.IR me +(rows marked by [*] are provided by patches). + See the corresponding man pages for further details. .RS @@ -40,14 +44,20 @@ .ta 5c 10c control default used by +.I badhelo \fR(none) [*] \fRqmail-smtpd .I badmailfrom \fR(none) \fRqmail-smtpd +.I badmailto \fR(none) [*] \fRqmail-smtpd .I bouncefrom \fRMAILER-DAEMON \fRqmail-send .I bouncehost \fIme \fRqmail-send +.I clientca.pem \fR(none) \fRqmail-smtpd +.I clientcert.pem \fR(none) \fRqmail-remote .I concurrencylocal \fR10 \fRqmail-send .I concurrencyremote \fR20 \fRqmail-send .I defaultdomain \fIme \fRqmail-inject .I defaulthost \fIme \fRqmail-inject .I databytes \fR0 \fRqmail-smtpd +.I dh1024.pem \fR(none) \fRqmail-smtpd +.I dh512.pem \fR(none) \fRqmail-smtpd .I doublebouncehost \fIme \fRqmail-send .I doublebounceto \fRpostmaster \fRqmail-send .I envnoathost \fIme \fRqmail-send @@ -61,11 +71,19 @@ .I qmqpservers \fR(none) \fRqmail-qmqpc .I queuelifetime \fR604800 \fRqmail-send .I rcpthosts \fR(none) \fRqmail-smtpd +.I rsa512.pem \fR(none) \fRqmail-smtpd +.I servercert.pem \fR(none) \fRqmail-smtpd .I smtpgreeting \fIme \fRqmail-smtpd .I smtproutes \fR(none) \fRqmail-remote +.I tarpitcount \fR0 [*] \fRqmail-smtpd +.I tarpitdelay \fR5 [*] \fRqmail-smtpd .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 diff -urN ../qmail-1.03_unpatched/qmail-local.c ./qmail-local.c --- ../qmail-1.03_unpatched/qmail-local.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-local.c 2005-01-21 19:25:03.000000000 +0100 @@ -1,5 +1,6 @@ #include #include +#include #include "readwrite.h" #include "sig.h" #include "env.h" @@ -66,6 +67,7 @@ char buf[1024]; char outbuf[1024]; +#define QUOTABUFSIZE 256 /* child process */ @@ -86,9 +88,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 +107,10 @@ s += fmt_str(s,"tmp/"); s += fmt_ulong(s,time); *s++ = '.'; s += fmt_ulong(s,pid); *s++ = '.'; - s += fmt_strn(s,host,sizeof(host)); *s++ = 0; + s += fmt_strn(s,host,sizeof(host)); + s += fmt_strn(s,",S=",sizeof(",S=")); + if (fstat(0,&st) == -1) if (errno == error_noent) break; + s += fmt_ulong(s,st.st_size); *s++ = 0; if (stat(fntmptph,&st) == -1) if (errno == error_noent) break; /* really should never get to this point */ if (loop == 2) _exit(1); @@ -128,6 +139,9 @@ if (close(fd) == -1) goto fail; /* NFS dorks */ if (link(fntmptph,fnnewtph) == -1) goto fail; + if ((fd = open(fnnewtph, O_RDONLY)) < 0 || + fsync(fd) < 0 || close(fd) < 0) goto fail; + /* if it was error_exist, almost certainly successful; i hate NFS */ tryunlinktmp(); _exit(0); @@ -159,6 +173,7 @@ switch(wait_exitcode(wstat)) { case 0: break; + case 1: strerr_die1x(1, "User over quota. (#5.1.1)"); case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)"); case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)"); case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)"); @@ -645,7 +660,7 @@ { cmds.s[j] = 0; k = j; - while ((k > i) && (cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')) + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) cmds.s[--k] = 0; switch(cmds.s[i]) { diff -urN ../qmail-1.03_unpatched/qmail-pop3d.c ./qmail-pop3d.c --- ../qmail-1.03_unpatched/qmail-pop3d.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-pop3d.c 2005-01-21 19:25:03.000000000 +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(); } @@ -66,14 +67,14 @@ void die_scan() { err("unable to scan $HOME/Maildir"); die(); } void err_syntax() { err("syntax error"); } -void err_unimpl() { err("unimplemented"); } +void err_unimpl(arg) char *arg; { err("unimplemented"); } void err_deleted() { err("already deleted"); } void err_nozero() { err("messages are counted from 1"); } void err_toobig() { err("not that many messages"); } void err_nosuch() { err("unable to open that message"); } void err_nounlink() { err("unable to unlink all deleted messages"); } -void okay() { puts("+OK \r\n"); flush(); } +void okay(arg) char *arg; { substdio_puts(&ssout,"+OK \r\n"); flush(); } void printfn(fn) char *fn; { @@ -146,43 +147,79 @@ } } -void pop3_stat() +void pop3_stat(arg) char *arg; { int i; + int realnumm; unsigned long total; - total = 0; - for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; - puts("+OK "); - put(strnum,fmt_uint(strnum,numm)); - puts(" "); + total = realnumm = 0; + + for (i = 0;i < numm;++i) + if (!m[i].flagdeleted) { + total += m[i].size; + ++realnumm; + } + + substdio_puts(&ssout,"+OK "); + put(strnum,fmt_uint(strnum,realnumm)); + + /* for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size; + substdio_puts(&ssout,"+OK "); + put(strnum,fmt_uint(strnum,numm)); + */ + + substdio_puts(&ssout," "); put(strnum,fmt_ulong(strnum,total)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } -void pop3_rset() +void pop3_rset(arg) char *arg; { int i; for (i = 0;i < numm;++i) m[i].flagdeleted = 0; last = 0; - okay(); + okay(0); } -void pop3_last() +void pop3_last(arg) char *arg; { - puts("+OK "); + substdio_puts(&ssout,"+OK "); put(strnum,fmt_uint(strnum,last)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); flush(); } -void pop3_quit() +void pop3_quit(arg) char *arg; { int i; + char quotabuf[QUOTABUFSIZE]; + int has_quota=maildir_getquota(".", quotabuf); + + long deleted_bytes=0; + long deleted_messages=0; + for (i = 0;i < numm;++i) if (m[i].flagdeleted) { - if (unlink(m[i].fn) == -1) err_nounlink(); + unsigned long un=0; + const char *filename=m[i].fn; + if (has_quota == 0 && !MAILDIR_DELETED(filename)) { + if (maildir_parsequota(filename, &un)) { + struct stat stat_buf; + + if (stat(filename, &stat_buf) == 0) + un=stat_buf.st_size; + } + } + if (unlink(m[i].fn) == -1) { + err_nounlink(); + un=0; + } + if (un) { + deleted_bytes -= un; + deleted_messages -= 1; + } } else if (str_start(m[i].fn,"new/")) { @@ -192,7 +229,22 @@ if (!stralloc_0(&line)) die_nomem(); rename(m[i].fn,line.s); /* if it fails, bummer */ } - okay(); + + if (deleted_messages < 0) { + int quotafd; + + if (maildir_checkquota(".", "afd, quotabuf, deleted_bytes, + deleted_messages) && errno != EAGAIN && + deleted_bytes >= 0) + { + if (quotafd >= 0) close (quotafd); + } else { + maildir_addquota(".", quotafd, quotabuf, + deleted_bytes, deleted_messages); + if (quotafd >= 0) close(quotafd); + } + } + okay(0); die(); } @@ -214,7 +266,7 @@ if (i == -1) return; m[i].flagdeleted = 1; if (i + 1 > last) last = i + 1; - okay(); + okay(0); } void list(i,flaguidl) @@ -222,10 +274,10 @@ int flaguidl; { put(strnum,fmt_uint(strnum,i + 1)); - puts(" "); + substdio_puts(&ssout," "); if (flaguidl) printfn(m[i].fn); else put(strnum,fmt_ulong(strnum,m[i].size)); - puts("\r\n"); + substdio_puts(&ssout,"\r\n"); } void dolisting(arg,flaguidl) char *arg; int flaguidl; @@ -234,15 +286,15 @@ if (*arg) { i = msgno(arg); if (i == -1) return; - puts("+OK "); + substdio_puts(&ssout,"+OK "); list(i,flaguidl); } else { - okay(); + okay(0); for (i = 0;i < numm;++i) if (!m[i].flagdeleted) list(i,flaguidl); - puts(".\r\n"); + substdio_puts(&ssout,".\r\n"); } flush(); } @@ -267,14 +319,28 @@ fd = open_read(m[i].fn); if (fd == -1) { err_nosuch(); return; } - okay(); + okay(0); substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf)); blast(&ssmsg,limit); close(fd); } +void pop3_capa(arg) char *arg; +{ + substdio_puts(&ssout,"+OK Capability list follows\r\n" + "TOP\r\n" + "UIDL\r\n" + "LAST\r\n" + "USER\r\n" + "APOP\r\n" + "SASL CRAM-MD5\r\n" + ".\r\n"); + flush(); +} + struct commands pop3commands[] = { { "quit", pop3_quit, 0 } +, { "capa", pop3_capa, 0 } , { "stat", pop3_stat, 0 } , { "list", pop3_list, 0 } , { "uidl", pop3_uidl, 0 } @@ -299,7 +365,7 @@ getlist(); - okay(); + okay(0); commands(&ssin,pop3commands); die(); } diff -urN ../qmail-1.03_unpatched/qmail-popup.c ./qmail-popup.c --- ../qmail-1.03_unpatched/qmail-popup.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-popup.c 2005-01-21 19:25:03.000000000 +0100 @@ -14,6 +14,9 @@ #include "timeoutread.h" #include "timeoutwrite.h" +#include "case.h" +#include "base64.h" + void die() { _exit(1); } int saferead(fd,buf,len) int fd; char *buf; int len; @@ -64,15 +67,20 @@ void err_syntax() { err("syntax error"); } void err_wantuser() { err("USER first"); } -void err_authoriz() { err("authorization first"); } +void err_authoriz(arg) char *arg; { err("authorization first"); } -void okay() { puts("+OK \r\n"); flush(); } -void pop3_quit() { okay(); die(); } +void okay(arg) char *arg; { puts("+OK \r\n"); flush(); } +void pop3_quit(arg) char *arg; { okay(0); die(); } char unique[FMT_ULONG + FMT_ULONG + 3]; char *hostname; -stralloc username = {0}; +stralloc authin = {0}; /* input from POP3 client */ +stralloc username = {0}; /* username */ +stralloc chal = {0}; /* challenge */ +stralloc slop = {0}; /* b64 challenge */ +stralloc resp = {0}; /* b64 response */ + int seenuser = 0; char **childargs; substdio ssup; @@ -133,10 +141,23 @@ puts(">\r\n"); flush(); } +void pop3_capa(arg) char *arg; +{ + puts("+OK Capability list follows\r\n"); + puts("TOP\r\n"); + puts("UIDL\r\n"); + puts("LAST\r\n"); + puts("USER\r\n"); + puts("APOP\r\n"); + puts("SASL CRAM-MD5\r\n"); + puts(".\r\n"); + flush(); +} + void pop3_user(arg) char *arg; { if (!*arg) { err_syntax(); return; } - okay(); + okay(0); seenuser = 1; if (!stralloc_copys(&username,arg)) die_nomem(); if (!stralloc_0(&username)) die_nomem(); @@ -156,11 +177,100 @@ doanddie(arg,space - arg,space); } +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(); + 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) { die_badauth(); } + if (authin.len == 0) { die_badauth(); } + return authin.len; +} + + +void pop3_auth_cram(arg) char *arg; +{ + int i, r; + char *s; + + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&chal,"<")) die_nomem(); /* generate challenge */ + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,hostname)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + puts("+ "); + puts(slop.s); + puts("\r\n"); + flush(); + + if (authgetl() < 0) die_badauth(); /* got response */ + + if (r = b64decode(authin.s,authin.len,&resp) == 1) die_badauth(); + + /* if (r == -1 || !stralloc_0(&resp)) die_nomem(); */ + if (r == -1) die_nomem(); + + i = str_chr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + + if (!stralloc_copys(&username,resp.s)) die_nomem(); /* userid */ + if (!stralloc_0(&username)) die_nomem(); + if (!username.len || !s) die_badauth(); + + doanddie(username.s,username.len,s); +} +struct authcmd { + char *text; + void (*fun)(); +} authcmds[] = { + { "cram-md5",pop3_auth_cram } +, { 0,die_badauth } +}; + +void pop3_auth(arg) char *arg; +{ + int i; + char *cmd = arg; + + 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)) authcmds[i].fun(arg); + + puts("-ERR unsupported method\r\n"); flush(); return; +} + struct commands pop3commands[] = { { "user", pop3_user, 0 } , { "pass", pop3_pass, 0 } , { "apop", pop3_apop, 0 } +, { "auth", pop3_auth, 0 } , { "quit", pop3_quit, 0 } +, { "capa", pop3_capa, 0 } , { "noop", okay, 0 } , { 0, err_authoriz, 0 } } ; diff -urN ../qmail-1.03_unpatched/qmail-queue.c ./qmail-queue.c --- ../qmail-1.03_unpatched/qmail-queue.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-queue.c 2005-01-21 19:25:03.000000000 +0100 @@ -1,5 +1,6 @@ #include #include +#include #include "readwrite.h" #include "sig.h" #include "exit.h" @@ -155,6 +156,7 @@ { unsigned int len; char ch; + int fd; sig_blocknone(); umask(033); @@ -184,6 +186,7 @@ intdfn = fnnum("intd/",0); if (link(pidfn,messfn) == -1) die(64); + if ((fd = open(messfn, O_RDONLY)) < 0 || fsync(fd) < 0 || close(fd) < 0) die(64); if (unlink(pidfn) == -1) die(63); flagmademess = 1; @@ -248,6 +251,8 @@ if (fsync(intdfd) == -1) die_write(); if (link(intdfn,todofn) == -1) die(66); + if ((fd = open(todofn, O_RDONLY)) < 0 || + fsync(fd) < 0 || close(fd) < 0) die(66); triggerpull(); die(0); diff -urN ../qmail-1.03_unpatched/qmail-remote.8 ./qmail-remote.8 --- ../qmail-1.03_unpatched/qmail-remote.8 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-remote.8 2005-01-21 19:25:03.000000000 +0100 @@ -114,6 +114,10 @@ always exits zero. .SH "CONTROL FILES" .TP 5 +.I clientcert.pem +SSL certificate that is used to authenticate with the remote server +during a TLS session. +.TP 5 .I helohost Current host name, for use solely in saying hello to the remote SMTP server. @@ -156,6 +160,8 @@ this tells .B qmail-remote to look up MX records as usual. +.I port +value of 465 (deprecated smtps port) causes TLS session to be started. .I smtproutes may include wildcards: @@ -195,6 +201,26 @@ .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 authentication from servers for which this certificate exists +.RB ( +is the fully-qualified domain name of the server). One of the +.I dNSName +or the +.I CommonName +attributes have to match. + +.B WARNING: +this option may cause mail to be delayed, bounced, doublebounced, or lost. + .SH "SEE ALSO" addresses(5), envelopes(5), diff -urN ../qmail-1.03_unpatched/qmail-remote.c ./qmail-remote.c --- ../qmail-1.03_unpatched/qmail-remote.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-remote.c 2005-01-21 19:25:03.000000000 +0100 @@ -48,6 +48,17 @@ struct ip_address partner; +#ifdef TLS +# include +# include "tls.h" +# include "ssl_timeoutio.h" +# include +# define EHLO 1 + +int tls_init(); +const char *ssl_err_str = 0; +#endif + void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); } void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); } void zerodie() { zero(); substdio_flush(subfdoutsmall); _exit(0); } @@ -99,6 +110,9 @@ outhost(); out(" but connection died. "); if (flagcritical) out("Possible duplicate! "); +#ifdef TLS + if (ssl_err_str) { out(ssl_err_str); out(" "); } +#endif out("(#4.4.2)\n"); zerodie(); } @@ -110,6 +124,12 @@ int saferead(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + if (ssl) { + r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len); + if (r < 0) ssl_err_str = ssl_strerror(); + } else +#endif r = timeoutread(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -117,6 +137,12 @@ int safewrite(fd,buf,len) int fd; char *buf; int len; { int r; +#ifdef TLS + if (ssl) { + r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len); + if (r < 0) ssl_err_str = ssl_strerror(); + } else +#endif r = timeoutwrite(timeout,smtpfd,buf,len); if (r <= 0) dropped(); return r; @@ -163,6 +189,64 @@ return code; } +#ifdef EHLO +saa ehlokw = {0}; /* list of EHLO keywords and parameters */ +int maxehlokwlen = 0; + +unsigned long ehlo() +{ + stralloc *sa; + char *s, *e, *p; + unsigned long code; + + if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len; + ehlokw.len = 0; + +# ifdef MXPS + if (type == 's') return 0; +# endif + + substdio_puts(&smtpto, "EHLO "); + substdio_put(&smtpto, helohost.s, helohost.len); + substdio_puts(&smtpto, "\r\n"); + substdio_flush(&smtpto); + + code = smtpcode(); + if (code != 250) return code; + + s = smtptext.s; + while (*s++ != '\n') ; /* skip the first line: contains the domain */ + + e = smtptext.s + smtptext.len - 6; /* 250-?\n */ + while (s <= e) + { + int wasspace = 0; + + if (!saa_readyplus(&ehlokw, 1)) temp_nomem(); + sa = ehlokw.sa + ehlokw.len++; + if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0; + + /* smtptext is known to end in a '\n' */ + for (p = (s += 4); ; ++p) + if (*p == '\n' || *p == ' ' || *p == '\t') { + if (!wasspace) + if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem(); + if (*p == '\n') break; + wasspace = 1; + } else if (wasspace == 1) { + wasspace = 0; + s = p; + } + s = p; + /* keyword should consist of alpha-num and '-' + * broken AUTH might use '=' instead of space */ + for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; } + } + + return 250; +} +#endif + void outsmtptext() { int i; @@ -179,6 +263,11 @@ char *prepend; char *append; { +#ifdef TLS + /* shouldn't talk to the client unless in an appropriate state */ + int state = ssl ? ssl->state : SSL_ST_BEFORE; + if (state & SSL_ST_OK || !smtps && state & SSL_ST_BEFORE) +#endif substdio_putsflush(&smtpto,"QUIT\r\n"); /* waiting for remote side is just too ridiculous */ out(prepend); @@ -186,6 +275,30 @@ out(append); out(".\n"); outsmtptext(); + +#if defined(TLS) && defined(DEBUG) + if (ssl) { + X509 *peercert; + + out("STARTTLS proto="); out(SSL_get_version(ssl)); + out("; cipher="); out(SSL_get_cipher(ssl)); + + /* we want certificate details */ + if (peercert = SSL_get_peer_certificate(ssl)) { + char *str; + + str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0); + out("; subject="); out(str); OPENSSL_free(str); + + str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0); + out("; issuer="); out(str); OPENSSL_free(str); + + X509_free(peercert); + } + out(";\n"); + } +#endif + zerodie(); } @@ -214,6 +327,182 @@ substdio_flush(&smtpto); } +#ifdef TLS +char *partner_fqdn = 0; + +# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "") +void tls_quit(const char *s1, const char *s2) +{ + out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT; +} +# define tls_quit_error(s) tls_quit(s, ssl_error()) + +int match_partner(const char *s, int len) +{ + if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1; + /* we also match if the name is *.domainname */ + if (*s == '*') { + const char *domain = partner_fqdn + str_chr(partner_fqdn, '.'); + if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1; + } + return 0; +} + +/* don't want to fail handshake if certificate can't be verified */ +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } + +int tls_init() +{ + int i; + SSL *myssl; + SSL_CTX *ctx; + stralloc saciphers = {0}; + const char *ciphers, *servercert = 0; + + if (partner_fqdn) { + struct stat st; + stralloc tmp = {0}; + if (!stralloc_copys(&tmp, "control/tlshosts/") + || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)) + || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem(); + if (stat(tmp.s, &st)) alloc_free(tmp.s); else servercert = tmp.s; + } + + if (!smtps) { + stralloc *sa = ehlokw.sa; + unsigned int len = ehlokw.len; + /* look for STARTTLS among EHLO keywords */ + for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ; + if (!len) { + if (!servercert) return 0; + out("ZNo TLS achieved while "); out(servercert); + out(" exists"); smtptext.len = 0; TLS_QUIT; + } + } + + SSL_library_init(); + ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ctx) { + if (!smtps && !servercert) return 0; + smtptext.len = 0; + tls_quit_error("ZTLS error initializing ctx"); + } + + if (servercert) { + if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) { + SSL_CTX_free(ctx); + smtptext.len = 0; + out("ZTLS unable to load "); tls_quit_error(servercert); + } + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + } + + /* let the other side complain if it needs a cert and we don't have one */ +# define CLIENTCERT "control/clientcert.pem" + if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT)) + SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM); +# undef CLIENTCERT + + myssl = SSL_new(ctx); + SSL_CTX_free(ctx); + if (!myssl) { + if (!smtps && !servercert) return 0; + smtptext.len = 0; + tls_quit_error("ZTLS error initializing ssl"); + } + + if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n"); + + /* while the server is preparing a responce, do something else */ + if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1) + { SSL_free(myssl); temp_control(); } + if (saciphers.len) { + for (i = 0; i < saciphers.len - 1; ++i) + if (!saciphers.s[i]) saciphers.s[i] = ':'; + ciphers = saciphers.s; + } + else ciphers = "DEFAULT"; + SSL_set_cipher_list(myssl, ciphers); + alloc_free(saciphers.s); + + /* SSL_set_options(myssl, SSL_OP_NO_TLSv1); */ + SSL_set_fd(myssl, smtpfd); + + /* read the responce to STARTTLS */ + if (!smtps) { + if (smtpcode() != 220) { + SSL_free(myssl); + if (!servercert) return 0; + out("ZSTARTTLS rejected while "); + out(servercert); out(" exists"); TLS_QUIT; + } + smtptext.len = 0; + } + + ssl = myssl; + if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0) + tls_quit("ZTLS connect failed", ssl_strerror()); + + if (servercert) { + X509 *peercert; + STACK_OF(GENERAL_NAME) *gens; + + int r = SSL_get_verify_result(ssl); + if (r != X509_V_OK) { + out("ZTLS unable to verify server with "); + tls_quit(servercert, X509_verify_cert_error_string(r)); + } + alloc_free(servercert); + + peercert = SSL_get_peer_certificate(ssl); + if (!peercert) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "no certificate provided"); + } + + /* RFC 2595 section 2.4: find a matching name + * first find a match among alternative names */ + gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); + if (gens) { + for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) + { + const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i); + if (gn->type == GEN_DNS) + if (match_partner(gn->d.ia5->data, gn->d.ia5->length)) break; + } + sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); + } + + /* no alternative name matched, look up commonName */ + if (!gens || i >= r) { + stralloc peer = {0}; + X509_NAME *subj = X509_get_subject_name(peercert); + i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1); + if (i >= 0) { + const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value; + if (s) { peer.len = s->length; peer.s = s->data; } + } + if (peer.len <= 0) { + out("ZTLS unable to verify server "); + tls_quit(partner_fqdn, "certificate contains no valid commonName"); + } + if (!match_partner(peer.s, peer.len)) { + out("ZTLS unable to verify server "); out(partner_fqdn); + out(": received certificate for "); outsafe(&peer); TLS_QUIT; + } + } + + X509_free(peercert); + } + + if (smtps) if (smtpcode() != 220) + quit("ZTLS Connected to "," but greeting failed"); + + return 1; +} +#endif + stralloc recip = {0}; void smtp() @@ -221,8 +510,50 @@ unsigned long code; int flagbother; int i; - - if (smtpcode() != 220) quit("ZConnected to "," but greeting failed"); + +#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(); + /* just bounce on a 5xx error + * if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed"); + * or try next mx --- alex + */ + if (code >= 500 && code < 600) return; + if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */ + if (code != 220) quit("ZConnected to "," but greeting failed"); + +#ifdef EHLO +# ifdef TLS + if (!smtps) +# endif + code = ehlo(); + +# ifdef TLS + if (tls_init()) + /* RFC2487 says we should issue EHLO (even if we might not need + * extensions); at the same time, it does not prohibit a server + * to reject the EHLO and make us fallback to HELO */ + code = ehlo(); +# endif + + if (code == 250) { + /* add EHLO response checks here */ + + /* and if EHLO failed, use HELO */ + } else { +#endif substdio_puts(&smtpto,"HELO "); substdio_put(&smtpto,helohost.s,helohost.len); @@ -230,6 +561,10 @@ substdio_flush(&smtpto); if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected"); +#ifdef EHLO + } +#endif + substdio_puts(&smtpto,"MAIL FROM:<"); substdio_put(&smtpto,sender.s,sender.len); substdio_puts(&smtpto,">\r\n"); @@ -417,7 +752,10 @@ if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) { tcpto_err(&ip.ix[i].ip,0); partner = ip.ix[i].ip; - smtp(); /* does not return */ +#ifdef TLS + partner_fqdn = ip.ix[i].fqdn; +#endif + smtp(); /* only returns when the next MX is to be tried */ } tcpto_err(&ip.ix[i].ip,errno == error_timeout); close(smtpfd); diff -urN ../qmail-1.03_unpatched/qmail-showctl.c ./qmail-showctl.c --- ../qmail-1.03_unpatched/qmail-showctl.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-showctl.c 2005-01-21 19:25:03.000000000 +0100 @@ -214,14 +214,47 @@ _exit(111); } + do_lst("badhelo","Any HELO host name is allowed.",""," not accepted in HELO."); do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM."); + do_lst("badmailto","Any RCPT TO is allowed.",""," not accepted in RCPT TO."); do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); do_str("bouncehost",1,"bouncehost","Bounce host name is "); + + struct stat stcca; + substdio_puts(subfdout,"\nclientca.pem: "); + if (stat("clientca.pem",&stcca) == -1) + substdio_puts(subfdout,"(Default.) No client CA pem file.\n"); + else + substdio_puts(subfdout,"client CA pem file exists.\n"); + + struct stat stccert; + substdio_puts(subfdout,"\nclientcert.pem: "); + if (stat("clientcert.pem",&stccert) == -1) + substdio_puts(subfdout,"(Default.) No client certificate pem file.\n"); + else + substdio_puts(subfdout,"client certificate pem file exists.\n"); + + 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 "); + + struct stat stdh512; + substdio_puts(subfdout,"\ndh512.pem: "); + if (stat("dh512.pem",&stdh512) == -1) + substdio_puts(subfdout,"(Default.) No 512 DH key provided.\n"); + else + substdio_puts(subfdout,"512 DH key exists.\n"); + + struct stat stdh1024; + substdio_puts(subfdout,"\ndh1024.pem: "); + if (stat("dh1024.pem",&stdh1024) == -1) + substdio_puts(subfdout,"(Default.) No 1024 DH key provided.\n"); + else + substdio_puts(subfdout,"1024 DH key exists.\n"); + 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 +263,7 @@ 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_int("mfcheck","0","MAIL FROM DNS check is set to "," (0=disable, 1=enable)"); 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: ","."); @@ -255,11 +289,37 @@ else substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n"); + struct stat strsa; + substdio_puts(subfdout,"\nrsa512.pem: "); + if (stat("rsa512.pem",&strsa) == -1) + substdio_puts(subfdout,"(Default.) No 512 RSA key provided.\n"); + else + substdio_puts(subfdout,"512 RSA key exists.\n"); + + struct stat stscert; + substdio_puts(subfdout,"\nservercert.pem: "); + if (stat("servercert.pem",&stscert) == -1) + substdio_puts(subfdout,"(Default.) No server certificate pem file.\n"); + else + substdio_puts(subfdout,"server certificate pem file exists.\n"); + + struct stat stsig; + substdio_puts(subfdout,"\nsignatures: "); + if (stat("signatures",&stsig) == -1) + substdio_puts(subfdout,"(Default.) No binary signatures for blocking.\n"); + else + substdio_puts(subfdout,"binary signatures for blocking exist.\n"); + do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_int("tarpitcount","0",""," RCPT TOs before tarpitting (0=off)"); + do_int("tarpitdelay","5","tarpit delay is "," seconds"); 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"); + do_lst("tlsclients","No TLS clients","TLS client: ",""); + do_lst("tlsclientciphers","No TLS client ciphers","TLS client cipher:",""); + do_lst("tlsserverciphers","No TLS server ciphers","TLS server cipher:",""); do_lst("virtualdomains","No virtual domains.","Virtual domain: ",""); while (d = readdir(dir)) { @@ -267,14 +327,18 @@ if (str_equal(d->d_name,"..")) continue; if (str_equal(d->d_name,"bouncefrom")) continue; if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"badhelo")) continue; if (str_equal(d->d_name,"badmailfrom")) continue; - if (str_equal(d->d_name,"bouncefrom")) continue; - if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"badmailto")) continue; + if (str_equal(d->d_name,"clientca.pem")) continue; + if (str_equal(d->d_name,"clientcert.pem")) continue; if (str_equal(d->d_name,"concurrencylocal")) continue; if (str_equal(d->d_name,"concurrencyremote")) continue; 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,"dh512.pem")) continue; + if (str_equal(d->d_name,"dh1024.pem")) 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; @@ -282,7 +346,9 @@ if (str_equal(d->d_name,"idhost")) continue; if (str_equal(d->d_name,"localiphost")) continue; if (str_equal(d->d_name,"locals")) continue; + if (str_equal(d->d_name,"locals.lock")) continue; if (str_equal(d->d_name,"me")) continue; + if (str_equal(d->d_name,"mfcheck")) continue; if (str_equal(d->d_name,"morercpthosts")) continue; if (str_equal(d->d_name,"morercpthosts.cdb")) continue; if (str_equal(d->d_name,"percenthack")) continue; @@ -290,12 +356,23 @@ if (str_equal(d->d_name,"qmqpservers")) continue; if (str_equal(d->d_name,"queuelifetime")) continue; if (str_equal(d->d_name,"rcpthosts")) continue; + if (str_equal(d->d_name,"rcpthosts.lock")) continue; + if (str_equal(d->d_name,"rsa512.pem")) continue; + if (str_equal(d->d_name,"servercert.pem")) continue; + if (str_equal(d->d_name,"signatures")) continue; if (str_equal(d->d_name,"smtpgreeting")) continue; if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"tarpitcount")) continue; + if (str_equal(d->d_name,"tarpitdelay")) 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; + if (str_equal(d->d_name,"tlsclients")) continue; + if (str_equal(d->d_name,"tlsclientciphers")) continue; + if (str_equal(d->d_name,"tlshosts")) continue; + if (str_equal(d->d_name,"tlsserverciphers")) continue; if (str_equal(d->d_name,"virtualdomains")) continue; + if (str_equal(d->d_name,"virtualdomains.lock")) continue; substdio_puts(subfdout,"\n"); substdio_puts(subfdout,d->d_name); substdio_puts(subfdout,": I have no idea what this file does.\n"); diff -urN ../qmail-1.03_unpatched/qmail-smtpd.8 ./qmail-smtpd.8 --- ../qmail-1.03_unpatched/qmail-smtpd.8 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-smtpd.8 2005-01-21 19:25:03.000000000 +0100 @@ -3,6 +3,10 @@ qmail-smtpd \- receive mail via SMTP .SH SYNOPSIS .B qmail-smtpd +[ +.I checkprogram +.I subprogram +] .SH DESCRIPTION .B qmail-smtpd receives mail messages via the Simple Mail Transfer Protocol (SMTP) @@ -14,6 +18,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 +36,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 +73,25 @@ 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 NOBADHELO environment variable is set, then the +contents of +.IR badhelo +will be ignored. +For more information, please have a look at doc/README.qregex. +.TP 5 .I badmailfrom Unacceptable envelope sender addresses. .B qmail-smtpd will reject every recipient address for a message -if the envelope sender address is listed in +if the envelope sender address is listed in, or matches a POSIX regular expression +pattern listed in, .IR badmailfrom . A line in .I badmailfrom @@ -49,6 +99,28 @@ .BR @\fIhost , meaning every address at .IR host . +For more information, please have a look at doc/README.qregex. +.TP 5 +.I badmailto +Unacceptable recipient addresses. +.B qmail-smtpd +will reject every recipient address for a message if the recipient address +is listed in, +or matches a POSIX regular expression pattern listed in, +.IR badmailto . +For more information, please have a look at doc/README.qregex. +.TP 5 +.I 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 +148,18 @@ .B DATABYTES is set, it overrides .IR databytes . + +.TP 5 +.I dh1024.pem +If these 1024 bit DH parameters are provided, +.B qmail-smtpd +will use them for TLS sessions instead of generating one on-the-fly +(which is very timeconsuming). +.TP 5 +.I dh512.pem +512 bit counterpart for +.B dh1024.pem. + .TP 5 .I localiphost Replacement host name for local IP addresses. @@ -151,6 +235,19 @@ Envelope recipient addresses without @ signs are always allowed through. + +.TP 5 +.I rsa512.pem +If this 512 bit RSA key is provided, +.B qmail-smtpd +will use it for TLS sessions instead of generating one on-the-fly. + +.TP 5 +.I servercert.pem +SSL certificate to be presented to clients in +TLS-encrypted sessions. Certifying Authority +(CA) and intermediate certificates can be added at the end of the file. + .TP 5 .I smtpgreeting SMTP greeting message. @@ -169,6 +266,24 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. + +.TP 5 +.I tlsclients +A list of email addresses. When relay rules would reject an incoming message, +.B qmail-smtpd +can allow it if the client presents a certificate that can be verified against +the CA list in +.I clientca.pem +and the certificate email address is in +.IR tlsclients . + +.TP 5 +.I tlsserverciphers +A set of OpenSSL cipher strings. Multiple ciphers contained in a +string should be separated by a colon. If the environment variable +.B TLSCIPHERS +is set to such a string, it takes precedence. + .SH "SEE ALSO" tcp-env(1), tcp-environ(5), @@ -177,3 +292,6 @@ qmail-newmrh(8), qmail-queue(8), qmail-remote(8) +.SH "HISTORY" +The patch enabling the ESMTP AUTH option is not part of the standard +qmail-1.03 distribution. diff -urN ../qmail-1.03_unpatched/qmail-smtpd.c ./qmail-smtpd.c --- ../qmail-1.03_unpatched/qmail-smtpd.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail-smtpd.c 2005-01-21 19:25:03.000000000 +0100 @@ -22,15 +22,44 @@ #include "rcpthosts.h" #include "timeoutread.h" #include "timeoutwrite.h" + #include "commands.h" +#include "qregex.h" +#include "wait.h" + +#define CRAM_MD5 +#define AUTHSLEEP 5 + +#define BMCHECK_BMF 0 +#define BMCHECK_BMT 1 +#define BMCHECK_BHELO 2 #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; +const char *protocol = "SMTP"; + +#ifdef TLS +# include "tls.h" +# include "ssl_timeoutio.h" + +void tls_init(); +int tls_verify(); +void tls_nogateway(); +int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */ +#endif + +#include "chkuser.h" + 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; @@ -39,26 +68,63 @@ char ssoutbuf[512]; substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); +char sserrbuf[512]; +substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf,sizeof sserrbuf); + void flush() { substdio_flush(&ssout); } void out(s) char *s; { substdio_puts(&ssout,s); } +char strnum[FMT_ULONG]; +void logerr4 (s1,s2,s3,s4) char *s1; char *s2; char *s3; char *s4; +{ + strnum[fmt_ulong(strnum,getpid())] = 0; + substdio_puts(&sserr,"qmail-smtpd: pid "); /* pid */ + substdio_puts(&sserr,strnum); + substdio_puts(&sserr,": "); + substdio_puts(&sserr,s1); substdio_puts(&sserr,s2); substdio_puts(&sserr,s3); substdio_puts(&sserr,s4); + substdio_puts(&sserr,"\n"); + substdio_flush(&sserr); +} + 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 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("533 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"); } +#ifndef TLS void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } -void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } +#else +void err_nogateway() +{ + out("553 sorry, that domain isn't in my list of allowed rcpthosts"); + tls_nogateway(); + out(" (#5.7.1)\r\n"); +} +#endif +void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } +void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } -void err_noop() { out("250 ok\r\n"); } -void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } +void err_noop(arg) char *arg; { out("250 ok\r\n"); } +void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +void err_noexec() { out("552 we don't accept email with executable content (#5.3.4)\r\n"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } +int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } +void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); } stralloc greeting = {0}; @@ -67,11 +133,11 @@ substdio_puts(&ssout,code); substdio_put(&ssout,greeting.s,greeting.len); } -void smtp_help() +void smtp_help(arg) char *arg; { out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n"); } -void smtp_quit() +void smtp_quit(arg) char *arg; { smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } @@ -91,11 +157,19 @@ fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; } +int checkexecutable = 1; int liphostok = 0; stralloc liphost = {0}; int bmfok = 0; +int bmtok = 0; +int bhelook = 0; +stralloc bmt = {0}; stralloc bmf = {0}; -struct constmap mapbmf; +stralloc bhelo = {0}; +int sigsok = 0; +stralloc sigs = {0}; + +int flagauth = 0; /* moved here to use it in mfcheck and others */ void setup() { @@ -114,9 +188,17 @@ bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); - if (bmfok) - if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + bmtok = control_readfile(&bmt,"control/badmailto",0); + if (bmtok == -1) die_control(); + bhelook = control_readfile(&bhelo, "control/badhelo",0); + if (bhelook == -1) die_control(); + if (env_get("NOBADHELO")) bhelook = 0; + + sigsok = control_readfile(&sigs,"control/signatures",0); + if (sigsok == -1) die_control(); + if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); if (x) { scan_ulong(x,&u); databytes = u; } @@ -131,6 +213,11 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + if (env_get("EXECUTABLEOK")) checkexecutable = 0; +#ifdef TLS + if (env_get("SMTPS")) { smtps = 1; tls_init(); } + else +#endif dohelo(remotehost); } @@ -197,14 +284,57 @@ return 1; } -int bmfcheck() +int bmcheck(which) int which; { - int j; - if (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; - j = byte_rchr(addr.s,addr.len,'@'); - if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + 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_BMT) { + if (!stralloc_copy(&bmb,&bmt)) die_nomem(); + } else if (which == BMCHECK_BHELO) { + if (!stralloc_copy(&bmb,&bhelo)) die_nomem(); + } else { + die_control(); + } + + while (j < bmb.len) { + i = j; + while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; + if (bmb.s[j] == '!') { + negate = 1; + j++; + } + if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem(); + if (!stralloc_0(&curregex)) die_nomem(); + if (which == BMCHECK_BHELO) { + x = matchregex(helohost.s, curregex.s); + } else { + x = matchregex(addr.s, curregex.s); + } + if ((negate) && (x == 0)) return 1; + if (!(negate) && (x > 0)) return 1; + j = i + 1; + negate = 0; + } + return 0; +} + +int sigscheck(stralloc *line) { + int i, j; + + j = 0; + for (i = 0; i < sigs.len; i++) if (!sigs.s[i]) { + if (i-j < line->len) + if (!str_diffn(line->s,sigs.s+j,i-j)) + return 1; + j = i+1; + } return 0; } @@ -213,26 +343,112 @@ int r; r = rcpthosts(addr.s,str_len(addr.s)); if (r == -1) die_control(); +#ifdef TLS + if (r == 0) if (tls_verify()) r = -2; +#endif return r; } int seenmail = 0; -int flagbarf; /* defined if seenmail */ +int flagbarfbmf; /* defined if seenmail */ +int flagbarfbmt; +int flagbarfbhelo; +int flagsize; stralloc mailfrom = {0}; stralloc rcptto = {0}; +stralloc fuser = {0}; +stralloc mfparms = {0}; + +int mailfrom_size(arg) char *arg; +{ + long r; + unsigned long sizebytes = 0; + + scan_ulong(arg,&r); + sizebytes = r; + if (databytes) if (sizebytes > databytes) return 1; + return 0; +} + +void mailfrom_auth(arg,len) +char *arg; +int len; +{ + int j; + + if (!stralloc_copys(&fuser,"")) die_nomem(); + if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } + else + while (len) { + if (*arg == '+') { + if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } + if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } + } + else + if (!stralloc_catb(&fuser,arg,1)) die_nomem(); + arg++; len--; + } + if(!stralloc_0(&fuser)) die_nomem(); + if (!remoteinfo) { + remoteinfo = fuser.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + } +} + +void mailfrom_parms(arg) char *arg; +{ + int i; + int len; + + len = str_len(arg); + if (!stralloc_copys(&mfparms,"")) die_nomem; + i = byte_chr(arg,len,'>'); + if (i > 4 && i < len) { + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); + if (!stralloc_copys(&mfparms,"")) die_nomem; + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem; + } + } +} + void smtp_helo(arg) char *arg; { smtp_greet("250 "); out("\r\n"); seenmail = 0; dohelo(arg); + if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); } + +/* 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]; + size[fmt_ulong(size,(unsigned int) databytes)] = 0; + smtp_greet("250-"); +#ifdef TLS + if (!ssl) out("\r\n250-STARTTLS"); +#endif +#ifdef CRAM_MD5 + out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); + out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); +#else + out("\r\n250-AUTH LOGIN PLAIN"); + out("\r\n250-AUTH=LOGIN PLAIN"); +#endif + out("\r\n250-PIPELINING\r\n250-8BITMIME"); + out("\r\n250 SIZE "); out(size); out("\r\n"); seenmail = 0; dohelo(arg); + if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); } -void smtp_rset() +void smtp_rset(arg) char *arg; { seenmail = 0; out("250 flushed\r\n"); @@ -240,7 +456,15 @@ void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); + flagsize = 0; + mailfrom_parms(arg); + if (flagsize) { err_size(); return; } + /* start chkuser code */ + if (chkuser_sender (&addr) != CHKUSER_OK) { return; } + /* end chkuser code */ + + flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ + if ((addr.len != 1) && (bmfok)) flagbarfbmf = bmcheck(BMCHECK_BMF); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); @@ -250,7 +474,14 @@ void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + if (flagbarfbhelo) { logerr4("bad HELO <",helohost.s,"> at ",remoteip); err_bhelo(); return; } + if (flagbarfbmf) { logerr4("bad MAIL FROM <",mailfrom.s,"> at ",remoteip); err_bmf(); return; } + if (bmtok) { flagbarfbmt = bmcheck(BMCHECK_BMT); } + if (flagbarfbmt) { logerr4("bad RCPT TO <",addr.s,"> at ",remoteip); err_bmt(); return; } + +/* + * Original code substituted by chkuser code + if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); @@ -258,6 +489,26 @@ } else if (!addrallowed()) { err_nogateway(); return; } + + * end of substituted code + */ + +/* 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(); @@ -269,6 +520,11 @@ { int r; flush(); +#ifdef TLS + if (ssl && fd == ssl_rfd) + r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len); + else +#endif r = timeoutread(timeout,fd,buf,len); if (r == -1) if (errno == error_timeout) die_alarm(); if (r <= 0) die_read(); @@ -281,9 +537,149 @@ struct qmail qqt; unsigned int bytestooverflow = 0; +int linespastheader; /* =0 after boundary is found in body, */ + /* until blank line */ +char linetype; +int flagexecutable; +int flagqsbmf; + +stralloc line = {0}; +stralloc content = {0}; +stralloc boundary = {0}; +int boundary_start; + +/* + +def put(ch): + line.append(ch) + if ch == '\n': + if linepastheader == 0: + if line.startswith('Content-Type:'): + content = + + put() puts characters into the queue. We remember those characters + and form them into a line. When we get a newline, we examine the + line. If we're currently in a header (0 linespastheader), we look + for Content-Type. If we're at the newline that ends a header, we + look to see if the content is multipart. If it is, then we push + the current boundary, remember the boundary, otherwise we set the + boundary to the empty string. Set the linespastheader to 1. When + linespastheader is 1, and the boundary is empty, scan the line for + signatures. If the boundary is non-empty, look for a match against + the boundary. If it matches and is followed by two dashes, pop the + boundary, otherwise set linespastheader to 0. +*/ + void put(ch) char *ch; { + char *cp, *cpstart, *cpafter; + unsigned int len; + + if (checkexecutable) { + if (line.len < 1024) + if (!stralloc_catb(&line,ch,1)) die_nomem(); + + if (*ch == '\n') { + if (linespastheader == 0) { + if (line.len == 1) { + linespastheader = 1; + if (flagqsbmf) { + flagqsbmf = 0; + linespastheader = 0; + } + if (content.len) { /* MIME header */ + cp = content.s; + len = content.len; + while (len && (*cp == ' ' || *cp == '\t')) { ++cp; --len; } + cpstart = cp; + if (len && *cp == '"') { /* might be commented */ + ++cp; --len; cpstart = cp; + while (len && *cp != '"') { ++cp; --len; } + } else { + while (len && *cp != ' ' && *cp != '\t' && *cp != ';') { + ++cp; --len; + } + } + if (!case_diffb(cpstart,cp-cpstart,"message/rfc822")) + linespastheader = 0; + + cpafter = content.s+content.len; + while((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) { + ++cp; + while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp; + if (case_startb(cp,cpafter - cp,"boundary=")) { + cp += 9; /* after boundary= */ + if (cp < cpafter && *cp == '"') { + ++cp; + cpstart = cp; + while (cp < cpafter && *cp != '"') ++cp; + } else { + cpstart = cp; + while (cp < cpafter && + *cp != ';' && *cp != ' ' && *cp != '\t') ++cp; + } + /* push the current boundary. Append a null and remember start. */ + if (!stralloc_0(&boundary)) die_nomem(); + boundary_start = boundary.len; + if (!stralloc_cats(&boundary,"--")) die_nomem(); + if (!stralloc_catb(&boundary,cpstart,cp-cpstart)) + die_nomem(); + break; + } + } + } + } else { /* non-blank header line */ + if ((*line.s == ' ' || *line.s == '\t')) { + switch(linetype) { + case 'C': if (!stralloc_catb(&content,line.s,line.len-1)) die_nomem(); break; + default: break; + } + } else { + if (case_startb(line.s,line.len,"content-type:")) { + if (!stralloc_copyb(&content,line.s+13,line.len-14)) die_nomem(); + linetype = 'C'; + } else { + linetype = ' '; + } + } + } + } else { /* non-header line */ + if (boundary.len-boundary_start && *line.s == '-' && line.len > (boundary.len-boundary_start) && + !str_diffn(line.s,boundary.s+boundary_start,boundary.len-boundary_start)) { /* matches a boundary */ + if (line.len > boundary.len-boundary_start + 2 && + line.s[boundary.len-boundary_start+0] == '-' && + line.s[boundary.len-boundary_start+1] == '-') { + /* XXXX - pop the boundary here */ + if (boundary_start) boundary.len = boundary_start - 1; + boundary_start = boundary.len; + while(boundary_start--) if (!boundary.s[boundary_start]) break; + boundary_start++; + linespastheader = 2; + } else { + linespastheader = 0; + } + } else if (linespastheader == 1) { /* first line -- match a signature? */ + if (/*mailfrom.s[0] == '\0' && */ + str_start(line.s,"Hi. This is the ")) + flagqsbmf = 1; + else if (/*mailfrom.s[0] == '\0' && */ + str_start(line.s,"This message was created automatically by mail delivery software")) + flagqsbmf = 1; + else if (sigscheck(&line)) { + flagexecutable = 1; + qmail_fail(&qqt); + } + linespastheader = 2; + } + if (flagqsbmf && str_start(line.s,"---")) { + linespastheader = 0; + } + } + line.len = 0; + } + } + if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qqt); @@ -316,8 +712,8 @@ if (flagmaybex) if (pos == 7) ++*hops; if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; if (flagmaybey) if (pos == 1) flaginheader = 0; + ++pos; } - ++pos; if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } } switch(state) { @@ -365,7 +761,7 @@ out("\r\n"); } -void smtp_data() { +void smtp_data(arg) char *arg; { int hops; unsigned long qp; char *qqx; @@ -374,11 +770,18 @@ if (!rcptto.len) { err_wantrcpt(); return; } seenmail = 0; if (databytes) bytestooverflow = databytes + 1; + boundary.len = 0; + boundary_start = 0; + content.len = 0; + linespastheader = 0; + flagexecutable = 0; + flagqsbmf = 0; + linetype = ' '; if (qmail_open(&qqt) == -1) { err_qqt(); return; } qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -388,28 +791,489 @@ qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } + if (databytes) if (!bytestooverflow) { err_size(); return; } + if (flagexecutable) { logerr4("executable content from <",mailfrom.s,"> at ",remoteip); err_noexec(); return; } if (*qqx == 'D') out("554 "); else out("451 "); out(qqx + 1); out("\r\n"); } +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; /* input from SMTP client */ +static stralloc user = {0}; /* plain userid */ +static stralloc pass = {0}; /* plain passwd or digest */ +#ifdef CRAM_MD5 +static stralloc chal = {0}; /* plain challenge */ +static stralloc slop = {0}; /* b64 challenge */ +#endif +static stralloc resp = {0}; /* b64 response */ + +char **childargs; +char ssauthbuf[512]; +substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin,"")) die_nomem(); + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); +#ifdef CRAM_MD5 + if (!stralloc_0(&chal)) die_nomem(); +#endif + + if (pipe(pi) == -1) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if(fd_copy(3,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); + if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); +#ifdef CRAM_MD5 + if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); +#endif + if (substdio_flush(&ssauth) == -1) return err_write(); + + close(pi[1]); +#ifdef CRAM_MD5 + if (!stralloc_copys(&chal,"")) die_nomem(); + if (!stralloc_copys(&slop,"")) die_nomem(); +#endif + byte_zero(ssauthbuf,sizeof ssauthbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ + + if (resp.len > id + 1) + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); + if (resp.len > id + user.len + 2) + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +#ifdef CRAM_MD5 +int auth_cram() +{ + int i, r; + char *s; + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&chal,"<")) die_nomem(); /* generate challenge */ + 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 mychallenge \r\n" */ + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; /* got response */ + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + + i = str_chr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login",auth_login } +, { "plain",auth_plain } +#ifdef CRAM_MD5 +, { "cram-md5",auth_cram } +#endif +, { 0,err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } + if (flagauth) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); +#ifdef CRAM_MD5 + if (!stralloc_copys(&chal,"")) die_nomem(); +#endif + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + flagauth = 1; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + err_authfail(user.s,authcmds[i].text); + } +} + +#ifdef TLS +stralloc proto = {0}; +int ssl_verified = 0; +const char *ssl_verify_err = 0; + +void smtp_tls(char *arg) +{ + if (ssl) err_unimpl(); + else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n"); + else tls_init(); +} + +RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen) +{ + if (!export) keylen = 512; + if (keylen == 512) { + FILE *in = fopen("control/rsa512.pem", "r"); + if (in) { + RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL); + fclose(in); + if (rsa) return rsa; + } + } + return RSA_generate_key(keylen, RSA_F4, NULL, NULL); +} + +DH *tmp_dh_cb(SSL *ssl, int export, int keylen) +{ + if (!export) keylen = 1024; + if (keylen == 512) { + FILE *in = fopen("control/dh512.pem", "r"); + if (in) { + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); + fclose(in); + if (dh) return dh; + } + } + if (keylen == 1024) { + FILE *in = fopen("control/dh1024.pem", "r"); + if (in) { + DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL); + fclose(in); + if (dh) return dh; + } + } + return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL); +} + +/* don't want to fail handshake if cert isn't verifiable */ +int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; } + +void tls_nogateway() +{ + /* there may be cases when relayclient is set */ + if (!ssl || relayclient) return; + out("; no valid cert for gatewaying"); + if (ssl_verify_err) { out(": "); out(ssl_verify_err); } +} +void tls_out(const char *s1, const char *s2) +{ + out("454 TLS "); out(s1); + if (s2) { out(": "); out(s2); } + out(" (#4.3.0)\r\n"); flush(); +} +void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); } + +# define CLIENTCA "control/clientca.pem" +# define CLIENTCRL "control/clientcrl.pem" +# define SERVERCERT "control/servercert.pem" + +int tls_verify() +{ + stralloc clients = {0}; + struct constmap mapclients; + + if (!ssl || relayclient || ssl_verified) return 0; + ssl_verified = 1; /* don't do this twice */ + + /* request client cert to see if it can be verified by one of our CAs + * and the associated email address matches an entry in tlsclients */ + switch (control_readfile(&clients, "control/tlsclients", 0)) + { + case 1: + if (constmap_init(&mapclients, clients.s, clients.len, 0)) { + /* if CLIENTCA contains all the standard root certificates, a + * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE; + * it is probably due to 0.9.6b supporting only 8k key exchange + * data while the 0.9.6c release increases that limit to 100k */ + STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA); + if (sk) { + SSL_set_client_CA_list(ssl, sk); + SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); + break; + } + constmap_free(&mapclients); + } + case 0: alloc_free(clients.s); return 0; + case -1: die_control(); + } + + if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) { + const char *err = ssl_strerror(); + tls_out("rehandshake failed", err); die_read(); + } + + do { /* one iteration */ + X509 *peercert; + X509_NAME *subj; + stralloc email = {0}; + + int n = SSL_get_verify_result(ssl); + if (n != X509_V_OK) + { ssl_verify_err = X509_verify_cert_error_string(n); break; } + peercert = SSL_get_peer_certificate(ssl); + if (!peercert) break; + + subj = X509_get_subject_name(peercert); + n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1); + if (n >= 0) { + const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value; + if (s) { email.len = s->length; email.s = s->data; } + } + + if (email.len <= 0) + ssl_verify_err = "contains no email address"; + else if (!constmap(&mapclients, email.s, email.len)) + ssl_verify_err = "email address not in my list of tlsclients"; + else { + /* add the cert email to the proto if it helped allow relaying */ + --proto.len; + if (!stralloc_cats(&proto, "\n (cert ") /* continuation line */ + || !stralloc_catb(&proto, email.s, email.len) + || !stralloc_cats(&proto, ")") + || !stralloc_0(&proto)) die_nomem(); + relayclient = ""; + protocol = proto.s; + } + + X509_free(peercert); + } while (0); + constmap_free(&mapclients); alloc_free(clients.s); + + /* we are not going to need this anymore: free the memory */ + SSL_set_client_CA_list(ssl, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + + return relayclient ? 1 : 0; +} + +void tls_init() +{ + SSL *myssl; + SSL_CTX *ctx; + const char *ciphers; + stralloc saciphers = {0}; + X509_STORE *store; + X509_LOOKUP *lookup; + + SSL_library_init(); + + /* a new SSL context with the bare minimum of options */ + ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ctx) { tls_err("unable to initialize ctx"); return; } + + if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT)) + { SSL_CTX_free(ctx); tls_err("missing certificate"); return; } + SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL); + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L + /* crl checking */ + store = SSL_CTX_get_cert_store(ctx); + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) && + (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1)) + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); +#endif + + /* set the callback here; SSL_set_verify didn't work before 0.9.6c */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb); + + /* a new SSL object, with the rest added to it directly to avoid copying */ + myssl = SSL_new(ctx); + SSL_CTX_free(ctx); + if (!myssl) { tls_err("unable to initialize ssl"); return; } + + /* this will also check whether public and private keys match */ + if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM)) + { SSL_free(myssl); tls_err("no valid RSA private key"); return; } + + ciphers = env_get("TLSCIPHERS"); + if (!ciphers) { + if (control_readfile(&saciphers, "control/tlsserverciphers") == -1) + { SSL_free(myssl); die_control(); } + if (saciphers.len) { /* convert all '\0's except the last one to ':' */ + int i; + for (i = 0; i < saciphers.len - 1; ++i) + if (!saciphers.s[i]) saciphers.s[i] = ':'; + ciphers = saciphers.s; + } + } + if (!ciphers || !*ciphers) ciphers = "DEFAULT"; + SSL_set_cipher_list(myssl, ciphers); + alloc_free(saciphers.s); + + SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb); + SSL_set_tmp_dh_callback(myssl, tmp_dh_cb); + SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin)); + SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout)); + + if (!smtps) { out("220 ready for tls\r\n"); flush(); } + + if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) { + /* neither cleartext nor any other response here is part of a standard */ + const char *err = ssl_strerror(); + ssl_free(myssl); tls_out("connection failed", err); die_read(); + } + ssl = myssl; + + /* populate the protocol string, used in Received */ + if (!stralloc_copys(&proto, "(") + || !stralloc_cats(&proto, SSL_get_cipher(ssl)) + || !stralloc_cats(&proto, " encrypted) SMTP")) die_nomem(); + if (!stralloc_0(&proto)) die_nomem(); + protocol = proto.s; + + /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */ + dohelo(remotehost); +} + +# undef SERVERCERT +# undef CLIENTCA + +#endif + struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +, { "auth", smtp_auth, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } , { "rset", smtp_rset, 0 } , { "help", smtp_help, flush } +#ifdef TLS +, { "starttls", smtp_tls, flush } +#endif , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) +int argc; +char **argv; { + childargs = argv + 1; sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); diff -urN ../qmail-1.03_unpatched/qmail.c ./qmail.c --- ../qmail-1.03_unpatched/qmail.c 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail.c 2005-01-21 19:25:03.000000000 +0100 @@ -6,28 +6,49 @@ #include "fd.h" #include "qmail.h" #include "auto_qmail.h" +#include "env.h" -static char *binqqargs[2] = { "bin/qmail-queue", 0 } ; +static char *binqqargs[2] = { 0, 0 } ; + +static void setup_qqargs() +{ + if(!binqqargs[0]) + binqqargs[0] = env_get("QMAILQUEUE"); + if(!binqqargs[0]) + binqqargs[0] = "bin/qmail-queue"; +} int qmail_open(qq) struct qmail *qq; { int pim[2]; int pie[2]; + int pierr[2]; + + setup_qqargs(); if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } + if (pipe(pierr) == -1) { + close(pim[0]); close(pim[1]); + close(pie[0]); close(pie[1]); + close(pierr[0]); close(pierr[1]); + return -1; + } switch(qq->pid = vfork()) { case -1: + close(pierr[0]); close(pierr[1]); close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); return -1; case 0: close(pim[1]); close(pie[1]); + close(pierr[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 (fd_move(4,pierr[1]) == -1) _exit(120); if (chdir(auto_qmail) == -1) _exit(61); execv(*binqqargs,binqqargs); _exit(120); @@ -35,6 +56,7 @@ qq->fdm = pim[1]; close(pim[0]); qq->fde = pie[1]; close(pie[0]); + qq->fderr = pierr[0]; close(pierr[1]); substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf)); qq->flagerr = 0; return 0; @@ -82,10 +104,22 @@ { int wstat; int exitcode; + int match; + 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->fderr,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 */ + + close(qq->fderr); if (wait_pid(&wstat,qq->pid) != qq->pid) return "Zqq waitpid surprise (#4.3.0)"; @@ -118,6 +152,9 @@ case 81: return "Zqq internal bug (#4.3.0)"; case 120: return "Zunable to exec qq (#4.3.0)"; default: + if (exitcode == 82 && len > 2){ + return errstr; + } if ((exitcode >= 11) && (exitcode <= 40)) return "Dqq permanent problem (#5.3.0)"; return "Zqq temporary problem (#4.3.0)"; diff -urN ../qmail-1.03_unpatched/qmail.h ./qmail.h --- ../qmail-1.03_unpatched/qmail.h 1998-06-15 12:53:16.000000000 +0200 +++ ./qmail.h 2005-01-21 19:25:03.000000000 +0100 @@ -8,6 +8,7 @@ unsigned long pid; int fdm; int fde; + int fderr; substdio ss; char buf[1024]; } ; diff -urN ../qmail-1.03_unpatched/qregex.c ./qregex.c --- ../qmail-1.03_unpatched/qregex.c 1970-01-01 01:00:00.000000000 +0100 +++ ./qregex.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,57 @@ +/* + * qregex (v2) + * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $ + * + * Author : Evan Borgstrom (evan at unixpimps dot org) + * Created : 2001/12/14 23:08:16 + * Modified: $Date: 2001/12/28 07:05:21 $ + * Revision: $Revision: 2.1 $ + * + * Do POSIX regex matching on addresses for anti-relay / spam control. + * It logs to the maillog + * See the qregex-readme file included with this tarball. + * If you didn't get this file in a tarball please see the following URL: + * http://www.unixpimps.org/software/qregex + * + * qregex.c is released under a BSD style copyright. + * See http://www.unixpimps.org/software/qregex/copyright.html + * + * Note: this revision follows the coding guidelines set forth by the rest of + * the qmail code and that described at the following URL. + * http://cr.yp.to/qmail/guarantee.html + * + */ + +#include +#include +#include "qregex.h" + +#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE) +#define REGEXEC(X,Y) regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0) + +int matchregex(char *text, char *regex) { + regex_t qreg; + int retval = 0; + + + /* build the regex */ + if ((retval = REGCOMP(qreg, regex)) != 0) { + regfree(&qreg); + return(-retval); + } + + /* execute the regex */ + if ((retval = REGEXEC(qreg, text)) != 0) { + /* did we just not match anything? */ + if (retval == REG_NOMATCH) { + regfree(&qreg); + return(0); + } + regfree(&qreg); + return(-retval); + } + + /* signal the match */ + regfree(&qreg); + return(1); +} diff -urN ../qmail-1.03_unpatched/qregex.h ./qregex.h --- ../qmail-1.03_unpatched/qregex.h 1970-01-01 01:00:00.000000000 +0100 +++ ./qregex.h 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,5 @@ +/* simple header file for the matchregex prototype */ +#ifndef _QREGEX_H_ +#define _QREGEX_H_ +int matchregex(char *text, char *regex); +#endif diff -urN ../qmail-1.03_unpatched/sendmail.c ./sendmail.c --- ../qmail-1.03_unpatched/sendmail.c 1998-06-15 12:53:16.000000000 +0200 +++ ./sendmail.c 2005-01-21 19:25:03.000000000 +0100 @@ -45,6 +45,38 @@ _exit(111); } +void do_sender(s) +const char *s; +{ + char *x; + int n; + int a; + int i; + + env_unset("QMAILNAME"); + env_unset("MAILNAME"); + env_unset("NAME"); + env_unset("QMAILHOST"); + env_unset("MAILHOST"); + + n = str_len(s); + a = str_rchr(s, '@'); + if (a == n) + { + env_put2("QMAILUSER", s); + return; + } + env_put2("QMAILHOST", s + a + 1); + + x = (char *) alloc((a + 1) * sizeof(char)); + if (!x) nomem(); + for (i = 0; i < a; i++) + x[i] = s[i]; + x[i] = 0; + env_put2("QMAILUSER", x); + alloc_free(x); +} + int flagh; char *sender; @@ -118,6 +150,7 @@ if (sender) { *arg++ = "-f"; *arg++ = sender; + do_sender(sender); } *arg++ = "--"; for (i = 0;i < argc;++i) *arg++ = argv[i]; diff -urN ../qmail-1.03_unpatched/spawn.c ./spawn.c --- ../qmail-1.03_unpatched/spawn.c 1998-06-15 12:53:16.000000000 +0200 +++ ./spawn.c 2005-01-21 19:25:03.000000000 +0100 @@ -5,6 +5,7 @@ #include "substdio.h" #include "byte.h" #include "str.h" +#include "alloc.h" #include "stralloc.h" #include "select.h" #include "exit.h" diff -urN ../qmail-1.03_unpatched/ssl_timeoutio.c ./ssl_timeoutio.c --- ../qmail-1.03_unpatched/ssl_timeoutio.c 1970-01-01 01:00:00.000000000 +0100 +++ ./ssl_timeoutio.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,94 @@ +#include "select.h" +#include "error.h" +#include "ndelay.h" +#include "ssl_timeoutio.h" + +int ssl_timeoutio(int (*fun)(), + long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + int n; + const long end = t + time(NULL); + + do { + fd_set fds; + struct timeval tv; + + const int r = buf ? fun(ssl, buf, len) : fun(ssl); + if (r > 0) return r; + + t = end - time(NULL); + if (t < 0) break; + tv.tv_sec = t; tv.tv_usec = 0; + + FD_ZERO(&fds); + switch (SSL_get_error(ssl, r)) + { + default: return r; /* some other error */ + case SSL_ERROR_WANT_READ: + FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv); + break; + case SSL_ERROR_WANT_WRITE: + FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv); + break; + } + + /* n is the number of descriptors that changed status */ + } while (n > 0); + + if (n != -1) errno = error_timeout; + return -1; +} + +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl) +{ + int r; + + SSL_renegotiate(ssl); + r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); + if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r; + + /* this is for the server only */ + ssl->state = SSL_ST_ACCEPT; + return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0); +} + +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) return 0; + if (SSL_pending(ssl)) return SSL_read(ssl, buf, len); + return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len); +} + +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + if (!buf) return 0; + return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len); +} diff -urN ../qmail-1.03_unpatched/ssl_timeoutio.h ./ssl_timeoutio.h --- ../qmail-1.03_unpatched/ssl_timeoutio.h 1970-01-01 01:00:00.000000000 +0100 +++ ./ssl_timeoutio.h 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,21 @@ +#ifndef SSL_TIMEOUTIO_H +#define SSL_TIMEOUTIO_H + +#include + +/* the version is like this: 0xMNNFFPPS: major minor fix patch status */ +#if OPENSSL_VERSION_NUMBER < 0x00906000L +# error "Need OpenSSL version at least 0.9.6" +#endif + +int ssl_timeoutconn(long t, int rfd, int wfd, SSL *ssl); +int ssl_timeoutaccept(long t, int rfd, int wfd, SSL *ssl); +int ssl_timeoutrehandshake(long t, int rfd, int wfd, SSL *ssl); + +int ssl_timeoutread(long t, int rfd, int wfd, SSL *ssl, char *buf, int len); +int ssl_timeoutwrite(long t, int rfd, int wfd, SSL *ssl, char *buf, int len); + +int ssl_timeoutio( + int (*fun)(), long t, int rfd, int wfd, SSL *ssl, char *buf, int len); + +#endif diff -urN ../qmail-1.03_unpatched/strpidt.c ./strpidt.c --- ../qmail-1.03_unpatched/strpidt.c 1970-01-01 01:00:00.000000000 +0100 +++ ./strpidt.c 2005-01-21 19:25:03.000000000 +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: strpidt.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $"; + +char *str_pid_t(pid_t t, char *arg) +{ +char buf[NUMBUFSIZE]; +char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); +} diff -urN ../qmail-1.03_unpatched/strtimet.c ./strtimet.c --- ../qmail-1.03_unpatched/strtimet.c 1970-01-01 01:00:00.000000000 +0100 +++ ./strtimet.c 2005-01-21 19:25:03.000000000 +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: strtimet.c,v 1.3 2000/05/27 04:59:26 mrsam Exp $"; + +char *str_time_t(time_t t, char *arg) +{ +char buf[NUMBUFSIZE]; +char *p=buf+sizeof(buf)-1; + + *p=0; + do + { + *--p= '0' + (t % 10); + t=t / 10; + } while(t); + return (strcpy(arg, p)); +} diff -urN ../qmail-1.03_unpatched/tls.c ./tls.c --- ../qmail-1.03_unpatched/tls.c 1970-01-01 01:00:00.000000000 +0100 +++ ./tls.c 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,26 @@ +#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 *strerror(int); +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_strerror() +{ + const char *err = ssl_error(); + if (err) return err; + if (!errno) return 0; + return errno == error_timeout ? "timed out" : strerror(errno); +} diff -urN ../qmail-1.03_unpatched/tls.h ./tls.h --- ../qmail-1.03_unpatched/tls.h 1970-01-01 01:00:00.000000000 +0100 +++ ./tls.h 2005-01-21 19:25:03.000000000 +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_strerror(); + +#endif diff -urN ../qmail-1.03_unpatched/update_tmprsadh.sh ./update_tmprsadh.sh --- ../qmail-1.03_unpatched/update_tmprsadh.sh 1970-01-01 01:00:00.000000000 +0100 +++ ./update_tmprsadh.sh 2005-01-21 19:25:03.000000000 +0100 @@ -0,0 +1,26 @@ +#!/bin/sh + +# Update temporary RSA and DH keys +# Frederik Vermeulen 2003-12-28 GPL +# modified zeitform Internet Dienste + +umask 0077 || exit 0 + +#export PATH="$PATH:/usr/local/bin/ssl" + +openssl genrsa -out QMAIL/control/rsa512.new 512 +chmod 660 QMAIL/control/rsa512.new +chown vpopmail.qmail QMAIL/control/rsa512.new +mv -f QMAIL/control/rsa512.new QMAIL/control/rsa512.pem +echo + +openssl dhparam -2 -out QMAIL/control/dh512.new 512 +chmod 660 QMAIL/control/dh512.new +chown vpopmail.qmail QMAIL/control/dh512.new +mv -f QMAIL/control/dh512.new QMAIL/control/dh512.pem +echo + +openssl dhparam -2 -out QMAIL/control/dh1024.new 1024 +chmod 660 QMAIL/control/dh1024.new +chown vpopmail.qmail QMAIL/control/dh1024.new +mv -f QMAIL/control/dh1024.new QMAIL/control/dh1024.pem