diff -Nur netqmail-1.06/base64.c netqmail-1.06.systemadmin/base64.c --- netqmail-1.06/base64.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/base64.c 2008-09-14 20:36:39.000000000 +0200 @@ -0,0 +1,90 @@ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(in,l,out) +const unsigned char *in; +int l; +stralloc *out; /* not null terminated */ +{ + int i, j; + unsigned char a[4]; + unsigned char b[3]; + char *s; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,l + 2)) return -1; /* XXX generous */ + s = out->s; + + for (i = 0;i < l;i += 4) { + for (j = 0;j < 4;j++) + if ((i + j) < l && in[i + j] != B64PAD) + { + a[j] = str_chr(b64alpha,in[i + j]); + if (a[j] > 63) return 1; + } + else a[j] = 0; + + b[0] = (a[0] << 2) | (a[1] >> 4); + b[1] = (a[1] << 4) | (a[2] >> 2); + b[2] = (a[2] << 6) | (a[3]); + + *s++ = b[0]; + + if (in[i + 1] == B64PAD) break; + *s++ = b[1]; + + if (in[i + 2] == B64PAD) break; + *s++ = b[2]; + } + out->len = s - out->s; + while (out->len && !out->s[out->len - 1]) --out->len; /* XXX avoid? */ + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; + s = out->s; + + for (i = 0;i < in->len;i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + return 0; +} diff -Nur netqmail-1.06/base64.h netqmail-1.06.systemadmin/base64.h --- netqmail-1.06/base64.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/base64.h 2008-09-14 20:36:39.000000000 +0200 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff -Nur netqmail-1.06/CHKUSER.automatic_patching netqmail-1.06.systemadmin/CHKUSER.automatic_patching --- netqmail-1.06/CHKUSER.automatic_patching 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.automatic_patching 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,98 @@ +Chkuser 2.0.8 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: + rcpt to: + +You should see error and ok messages, depending on the addresses you typed. + +Install +======= +Copy the new executable in the /var/qmail/bin directory. + +Running +======= +This patched qmail-smtpd must be executed in a different way than the normal one. +See the running pages for detailed instructions. + diff -Nur netqmail-1.06/chkuser.c netqmail-1.06.systemadmin/chkuser.c --- netqmail-1.06/chkuser.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/chkuser.c 2009-02-02 19:31:40.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 -Nur netqmail-1.06/CHKUSER.changelog netqmail-1.06.systemadmin/CHKUSER.changelog --- netqmail-1.06/CHKUSER.changelog 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.changelog 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,141 @@ + +CHKUSER 2.0 change log + +V 2.0.8 - 7 december 2004 + Features + Freeze of new features of 2.0.7, except null senders behaviour. + CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST is no more available + CHKUSER_ENABLE_NULL_SENDER is no more available + NULL SENDERS are now always accepted. No option is available to disable + this behaviour. Previous chkuser versions broke RFC compatibility on + null senders, and complicated real life e-mailing. + Logging of null senders <> is now available. + + Bugs corrected + Sender controls were not executed if CHKUSER_STARTING_VARIABLE was defined + (thanks to Charles Sprickman) + Domains not in control/virtualdomains are now explicitely excluded from + following cascade checks; in previous versions following cascade + checks were done using fake domains paths. + vget_assign is now handled correctly (a domain in rcpthosts but not + in virtualdomains could have an incorrect path in previous versions + (this bug is also in all chkusr versions) + + Defaults changed + CHKUSER_RCPT_FORMAT is now undefined as default + CHKUSER_RCPT_MX is now undefined as default. + CHKUSER_SENDER_FORMAT is now undefined as default + CHKUSER_SENDER_MX is now undefined as default. + CHKUSER_ERROR_DELAY_INCREASE new default is 300 milliseconds + +V 2.0.7 - 25 october 2004 + Features + added vclose() of DB auth connection, overriding + qmail-smtpd _exit call + improved MX checking; now SOFT failure is handled as + temporary error. + added #define CHKUSER_RCPTMX_TMP_STRING + added #define CHKUSER_SENDERMX_TMP_STRING + added handling of mailman mailing lists + (and related #define CHKUSER_ENABLE_MAILMAN_LISTS) + changed order of checking for recipients: + 1 - valias + 2 - alias + 3 - alias extensions + 4 - users + 5 - users extensions + 6 - lists + added #define CHKUSER_ACCEPT_NULL_SENDER (default defined) + added #define CHKUSER_ENABLE_ALIAS_DEFAULT (default not defined) + enables checking of .qmail-alias-default + added #define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" + in order to allow a easy identification of remote IP + (substitutes RELAYCLIENT in chkuser logging) + added #define CHKUSER_ALLOW_RCPT_SRS + enable usage of "#" and "+" characters within rcpt address + added #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' + added #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' + added #define CHKUSER_ALLOW_RCPT_CHAR_3 '£' + added #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' + added #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' + #define CHKUSER_ENABLE_USERS_EXTENSIONS + substitutes #define CHKUSER_ENABLE_EXTENSIONS + #define CHKUSER_ENABLE_EZMLM_LISTS + substitutes #define CHKUSER_ENABLE_LISTS + #define CHKUSER_USERS_DASH + substitutes #define CHKUSER_EXTENSION_DASH + + Bugs + 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 -Nur netqmail-1.06/CHKUSER.copyright netqmail-1.06.systemadmin/CHKUSER.copyright --- netqmail-1.06/CHKUSER.copyright 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.copyright 2009-02-02 19:31:40.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 -Nur netqmail-1.06/chkuser.h netqmail-1.06.systemadmin/chkuser.h --- netqmail-1.06/chkuser.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/chkuser.h 2009-02-02 19:31:40.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 -Nur netqmail-1.06/CHKUSER.log_format netqmail-1.06.systemadmin/CHKUSER.log_format --- netqmail-1.06/CHKUSER.log_format 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.log_format 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,58 @@ + +chkuser 2.0.8 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 + + relayclient the value of RELAYCLIENT env variable (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 -Nur netqmail-1.06/CHKUSER.manual_patching netqmail-1.06.systemadmin/CHKUSER.manual_patching --- netqmail-1.06/CHKUSER.manual_patching 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.manual_patching 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,182 @@ +Chkuser 2.0 manual editing + +Manual editing is a very simple operation. + +Watching the patch design, shown in the patch design page, you may see that +only some simple changes must be done to qmail-smtpd.c and Makefile. + +Backup +====== + +Save you qmail working sources before making any change. + +Basic installation +================== + +Download the newest release.tar package and untar it. It will create a directory +containing all chkuser files and patches. + +Position in the qmail/netqmail source directory: + + $ cd /usr/.../netqmail-1.05 + +Copy all the chkuser sources: + + $ cp /path_to_release_tar/chkuser* . + +edit qmail-smtpd.c + within qmail-smtpd.c, change the following lines: + + At the end of initial #include declarations, add the following (+) lines: + + #include "timeoutwrite.h" + #include "commands.h" + ++ /* start chkuser code */ ++ #include "chkuser.h" ++ /* end chkuser code */ + + #define MAXHOPS 100 + +Within smtp_mail routine, add the following (+) lines + + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ /* start chkuser code */ ++ if (chkuser_sender (&addr) != CHKUSER_OK) { return; } ++ /* end chkuser code */ + flagbarf = bmfcheck(); + + Within smtp_rcpt routine, delete the following (-) lines and substitute + them with the (+) ones: + +- if (relayclient) { +- --addr.len; +- if (!stralloc_cats(&addr,relayclient)) die_nomem(); +- } +- else +- if (!addrallowed()) { err_nogateway(); return; } + ++ /* start chkuser code */ ++ switch (chkuser_realrcpt (&mailfrom, &addr)) { ++ case CHKUSER_KO: ++ return; ++ break; ++ case CHKUSER_RELAYING: ++ --addr.len; ++ if (!stralloc_cats(&addr,relayclient)) die_nomem(); ++ if (!stralloc_0(&addr)) die_nomem(); ++ break; ++ } ++ /* end chkuser code */ + + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + +edit Makefile + Within Makefile, change or add the following lines. + + At the begininng of the file: + + # Don't edit Makefile! Use conf-* for configuration. + ++ VPOPMAIL_HOME=/home/vpopmail ++ SMTPD_CHKUSER_OBJ=chkuser.o dns.o ++ VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib` + + SHELL=/bin/sh + + Be carefule to use the right path, if your vpopmail production home + path is NOT "/home/vpopmail". + + dns.lib is added to qmail-smtpd building instructions, so, if you + have previously patched qmail-smtpd in order to include dns.lib, take + care to delete the duplication from the previous lines. + + Before "clean:" insert the chkuser.o definition: + + exit.h auto_spawn.h + ./compile chkspawn.c ++ chkuser.o: \ ++ compile chkuser.c chkuser.h chkuser_settings.h ++ ./compile chkuser.c + + clean: \ + + Beware: the "./compile chkuser.c" line has an heading TAB. + + Change the qmail-smtpd compiling and linking instructions, + deleting the (-) lines and adding the (+) ones. + + + qmail-smtpd: \ + load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ + date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ + open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ +- fs.a auto_qmail.o socket.lib ++ fs.a auto_qmail.o socket.lib $(SMTPD_CHKUSER_OBJ) +- ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ ++ ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ +- alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ +- socket.lib` ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ ++ $(VPOPMAIL_LIBS) \ ++ `cat socket.lib` + + Beware: all the lines starting from and following "./load" have an heading TAB. + +edit TARGETS + Append the following blue line at the end of TARGETS file: + + man + setup + check ++ chkuser.o + +edit conf-cc + Edit conf-cc, adding the include path of production vpopmail: + + cc -O2 -I/home/vpopmail/include + + Be carefule to use the right path, if your vpopmail production home path + is NOT "/home/vpopmail". + +chkuser settings +================ +Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the +ones you don't want. Default settings should cover the most of situations. + +See the related settings pages for more informations. + +Make +==== +Now, make (or gmake on *BSD) as your usual. No errors (just warnings) +should come out. If you see any error, check carefully edited lines. + +Checking +======== +Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run: + + $ ./qmail-smtpd + mail from + mail from + rcpt to: + rcpt to: + +You should see error and ok messages, depending on the addresses you typed. + +Install +======= +Copy the new executable in the /var/qmail/bin directory. + +Running +======= +This patched qmail-smtpd must be executed in a different way than the normal one. +See the running pages for detailed instructions. + diff -Nur netqmail-1.06/CHKUSER.readme netqmail-1.06.systemadmin/CHKUSER.readme --- netqmail-1.06/CHKUSER.readme 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.readme 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,54 @@ +chkuser 2.0 -README + +Description +=========== +The original qmail-smtpd accepts by default all messages, checking later for +the existence of the recipient. So, if the message is delivered to not existing +recipients a lot of additional system work and network traffic are generated, +with multiple expensive bouncing if the sender is a fake one. + +chkuser has been developed with the goal to improve the acceptance SMTP phase +of qmail-smtpd. qmail-smtpd patched with chkuser may check the existance of +e-mail recipients immediately in the SMTP acceptance phase of a message and +rejects istantly all messages not directed to existing users, avoiding +additional traffic, work and messages bounced more times. + +These goals are achieved enquirying the existing vpopmail archives (each +format is supported: cdb, MySQL, LDAP, etc.) by using standard vpopmail calls, +or using customized chkuser routines. + +Version 2.0 - From chkusr to chkuser +==================================== +Version 2.0 is a lot different from previous versions, so it deserves a more +evident change in the name. + +Version 2.0 has been designed with the goal to be modular, and to make more easy +both adding new features to checkuser code and semplifing code update. + +Patching over original qmail files is done over a few points, while the most of +chkuser code remains ouside, in dedicated chkuser's files. + +Same for settings, that are inside a dedicated chkuser_settings.h file. + +The intention is to semplify upgrading: for future chkuser releases, upgrading +will require only to update chkuser specific files, leaving all the rest +untouched, and changing chkuser_settings.h only if new features must be enabled. + +Logging and SPAM +================ +chkuser 2.0 has detailed logging of accepted and refused recipients and senders, +allowing a deep analysis of "who's sending to who". This can lead to more +sophisticated future enhancements of anti-SPAM features. + +Intrusion rejection +=================== +chkuser 2.0 can be tuned to reject sessions exceeding some recipients limits +(limits can be set for max recipients and max not existing recipients). + +URL Location +============ +For any new release, support, FAQ, mailing lists, or other information, see: + + http://www.interazioni.it/opensource + + diff -Nur netqmail-1.06/CHKUSER.running netqmail-1.06.systemadmin/CHKUSER.running --- netqmail-1.06/CHKUSER.running 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/CHKUSER.running 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,103 @@ + +CHKUSER 2.0.8 - Running instructions + +Chkuser may run using the most of security, following very strictly the sacurity +model used By Dan Berstein. To achieve this goal, chkuser may switch between +differents UID/GID, for differente purposes. + +However this is incompatible with TLS patches (like toaster-0.6-1), as these patches +want to run under a unique UID/GID. Luckily, qmail is enought robust to let us +run this way. + +To achieve both these goals, chkuser uses a #define (CHKUSER_ENABLE_UIDGID) +that indicates if UID/GID switching is wanted, and running instructions must +adapt to this way. + +Instead, when this define is not used, another way of running must be used. +(Just for precision, even if the CHKUSER_ENABLE_UIDGID define is used, chkuser +may be run without switching UID/GID). + +Running with UID/GID switch +=========================== + +If you want the most security when using chkuser, and you have enabled +CHKUSER_ENABLE_UIDGID within chkuser_settings.h (it's enabled by default), use +these instructions. + +Description. + qmail-smtpd-chkusr must be installed (by default in /var/qmail/bin) with + setuid (user qmaild) and setgid (group qnofiles), and executed by tcpserver + with -u vpopmail-user and -g vchkpw-group parameters. + + qmail-smtpd-chkusr starts running with the original qmail-smtpd uid and gid, + switching to needed uid and gid only for vpopmail checks on user existance, + turning back to the starting uid and gid. + +Instructions. + You have to set SUID (set-user-ID-on-execution) and SGID + (set-group-ID-on-execution) bits on qmail-smtpd-chkusr: + chown qmaild qmail-smtpd + chgrp nofiles qmail-smtpd + chmod 6555 qmail-smtpd + + and the result you see should be like (different size and date, of course): + -r-sr-sr-x 1 qmaild nofiles 57056 Feb 14 18:18 qmail-smtpd-chkusr + + Integrate qmail-smtpd in your start files: + + As example, a real start command for qmail-smtpd-chkusr may be + + #!/bin/sh -e + # + # Using splogger to send the log through syslog. + + exec env - PATH="/var/qmail/bin:/usr/local/bin" \ + tcpserver -t 5 -v -p -x \ + -u -g -l 0 smtp \ + qmail-smtpd-chkusr splogger smtpd & + + where + = vpopmail uid + = vchkpw gid + = your host.domain (!) + = your tcp.permission.to.relay cdb + + NOTE: if you are using more system users for your domains, the execution + uid (which I indicated as vpopmail) should be set to root. + + +Running with fixed UID/GID +========================== +You may use these instructions if you've not defined CHKUSER_ENABLE_UIDGID, or if +you want to run qmail-smtpd as unique user, despite of CHKUSER_ENABLE_UIDGID define. +qmail-smtpd is well safe and robust, and there is no risk running it directly as +vpopmail user, unless you use untrusted software layered down. + +Description. + qmail-smtpd must be installed normally (-r-xr-xr-x) and executed by tcpserver + with -u vpopmail-user and -g vchkpw-group parameters. + +Instructions. + Integrate qmail-smtpd-chkusr in your start files: + + As example, a real start command for qmail-smtpd-chkusr may be + + #!/bin/sh -e + # + # Using splogger to send the log through syslog. + + exec env - PATH="/var/qmail/bin:/usr/local/bin" \ + tcpserver -t 5 -v -p -x \ + -u -g -l 0 smtp \ + qmail-smtpd-chkusr splogger smtpd & + + where + = vpopmail uid + = vchkpw gid + = your host.domain (!) + = your tcp.permission.to.relay cdb + + NOTE: if you are using more system users for your domains, the execution user + (which I indicated as vpopmail) should be set to root. + + diff -Nur netqmail-1.06/chkuser_settings.h netqmail-1.06.systemadmin/chkuser_settings.h --- netqmail-1.06/chkuser_settings.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/chkuser_settings.h 2009-02-04 14:43:53.000000000 +0100 @@ -0,0 +1,373 @@ + +/* + * + * 'chkuser_settings.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 + * + */ + +/* + * the following line enables debugging of chkuser + */ +/* #define CHKUSER_DEBUG */ + +/* + * The following line moves DEBUG output from STDOUT (default) to STDERR + * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log + */ +/* #define CHKUSER_DEBUG_STDERR */ + +/* + * Uncomment the following define if you want chkuser ALWAYS enabled. + * If uncommented, it will check for rcpt existance despite any .qmail-default + * setting. + * So, unsomments this if you are aware that ALL rcpt in all domains will be + * ALWAYS checked. + */ +/* #define CHKUSER_ALWAYS_ON */ + +/* + * The following defines which virtual manager is used. + * Up to know, only vpopmail, but versions with pure qmail are in the mind. + */ +#define CHKUSER_VPOPMAIL + +/* + * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting + * VALUE HERE DEFINED is the name of the variable + * Values admitted inside the variable: NONE | ALWAYS | DOMAIN + * NONE = chkuser will not work + * ALWAYS = chkuser will work always + * DOMAIN = chkuser will work depending by single domain settings + * 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 enable uid/gid changing + * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS) + */ +#define CHKUSER_ENABLE_UIDGID + +/* + * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses + */ +#define CHKUSER_DOMAIN_WANTED + +/* + * Uncomment this to check for vpopmail users + */ +#define CHKUSER_ENABLE_USERS + +/* + * Uncomment this to check for alias + */ +#define CHKUSER_ENABLE_ALIAS + +/* + * The following #define set the character used for lists extensions + * be careful: this is a single char '-' definition, not a "string" + */ +#define CHKUSER_EZMLM_DASH '-' + +/* + * Uncomment this to set an alternative way to check for bouncing enabling; + * with this option enabled, the file here defined + * will be searched, inside the domain dir, in order to check if bouncing is enabled + * The content of this file is not important, just it's existence is enough + */ +/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */ + +/* + * This is the string to look for inside .qmail-default + * Be careful, chkuser looks within the first 1023 characters of .qmail-default for + * this string (despite the line containing the string is working or commented). + */ +#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" + +/* + * 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 */ + +/* + * Uncomment to enable logging of rejected recipients and variuos limits reached + */ +//#define CHKUSER_ENABLE_LOGGING + +/* + * Uncomment to enable logging of "good" rcpts + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +#define CHKUSER_LOG_VALID_RCPT + +/* + * Uncomment to enable usage of a variable escluding any check on the sender. + * The variable should be set in tcp.smtp for clients, with static IP, whose mailer + * is composing bad sender addresses + */ +/* #define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK" */ + +/* + * 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 '*' */ + +/* + * The following #define sets the minimum length of a domain: + * as far as I know, "k.st" is the shortest domain, so 4 characters is the + * minimum length. + * This value is used to check formally a domain name validity. + * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done. + * If you comment this define, no check on length is done. + */ +#define CHKUSER_MIN_DOMAIN_LEN 4 + +/* + * Uncomment to enable logging of "good" senders + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +#define CHKUSER_LOG_VALID_SENDER + +/* + * Uncomment to define a variable which contains the max recipients number + * this will return always error if total recipients exceed this limit. + * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, + * makes chkuser rejecting everything else + */ +#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" + +/* + * Uncomment to define a variable which contains the max unknown recipients number + * this will return always error if not existing recipients exceed this limit. + * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, + * makes chkuser rejecting everything else + */ +#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" + +/* + * Uncomment to define the variable containing the percent to check for. + * Remember to define externally (i.e. in tcp.smtp) the environment variable containing + * the limit percent. + * If the variable is not defined, or it is <= 0, quota checking is not performed. + */ +#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" + +/* + * Delay to wait for each not existing recipient + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY 1000 + +/* + * Uncomment to consider rcpt errors on address format and MX as intrusive + * + */ +#define CHKUSER_RCPT_DELAY_ANYERROR + +/* + * Uncomment to consider sender errors on address format and MX as intrusive + * + */ +#define CHKUSER_SENDER_DELAY_ANYERROR + +#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" + +/*************************************************** + * + * new/modified defines in/from 2.0.6 + * + **************************************************/ + +/* + * Before version 5.3.25, vpopmail used the function vget_real_domain() + * to get the real name of a domain (useful if rcpt domain is aliasing + * another domain). + * From version 5.3.25, this call is not available and has been + * substituted by other calls. + * + * must be enabled if vpopmail version< 5.3.5 + * must be disabled if vpopmail version => 5.3.5 * + */ +/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */ + +/*************************************************** + * + * new/modified defines in/from 2.0.7 + * + **************************************************/ + +/* + * Uncomment next define to accept recipients for + * aliases that have a -default extension + */ +/* #define CHKUSER_ENABLE_ALIAS_DEFAULT */ + + +/* + * Uncomment to enable usage of "#" and "+" characters within rcpt address + * This is used by SRS (Sender Rewriting Scheme) products + */ +/* #define CHKUSER_ALLOW_RCPT_SRS */ + +/* + * 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 '*' */ + +/* + * 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 */ + +/* + * Uncomment this to enable user extension on names (i.e. TMDA) + * (for mailing lists this is done without checking this define) + * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS + */ +/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */ + +/* + * Enables checking for EZMLM lists + * this define substitutes #define CHKUSER_ENABLE_LISTS + * + */ +#define CHKUSER_ENABLE_EZMLM_LISTS + +/* + * Help identifying remote authorized IPs giving them a descriptive name + * Can be put in tcp.smtp, and will be displayed inside chkuser log + * Substitutes RELAYCLIENT in chkuser logging + */ +#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" + +/* + * The following #define set the character used for users extensions + * be careful: this is a single char '-' definition, not a "string" + * this define substitutes #define CHKUSER_EXTENSION_DASH + * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined + */ +#define CHKUSER_USERS_DASH '-' + +/* + * New error strings for SOFT DNS problems + */ +#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" + +/* + * Enables checking for mailman lists + * + */ +/* #define CHKUSER_ENABLE_MAILMAN_LISTS */ + +/* + * Identifies the pattern string to be searched within mailman aliases + * + */ +#define CHKUSER_MAILMAN_STRING "mailman" + +/* + * The following #define set the character used for mailman lists extensions + * be careful: this is a single char '-' definition, not a "string" + */ +#define CHKUSER_MAILMAN_DASH '-' + + +/* + * Enables final clean-up routine of chkuser + * This routine cleans open DB connections used for checking users and valiases + */ +#define CHKUSER_DB_CLEANUP + +/*************************************************** + * + * new/modified defines in/from 2.0.8 + * + **************************************************/ + +/* + * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC + * compatibility, and makes harder to handle e-mail receipts. + * Please comment or delete them from your chkuser_settings.h. + */ +/* #define CHKUSER_ACCEPT_NULL_SENDER */ +/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ + +/* + * Uncomment to enable checking of user and domain format for rcpt addresses + * user = [a-z0-9_-] + * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." + */ +/* #define CHKUSER_RCPT_FORMAT */ + +/* + * Uncomment to enable checking of domain MX for rcpt addresses + * It works on any rcpt address domain that is not inside rcpthosts + */ +/* #define CHKUSER_RCPT_MX */ + +/* + * Uncomment to enable checking of user and domain format for sender address + * user = [a-z0-9_-] + * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." + */ +/* #define CHKUSER_SENDER_FORMAT */ + +/* + * Uncomment to enable checking of domain MX for sender address + * it works on the first rcpt address, despite of any domain setting on chkuser + */ +/* #define CHKUSER_SENDER_MX */ + +/* + * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY_INCREASE 300 + diff -Nur netqmail-1.06/chkuser_settings.h.ruben netqmail-1.06.systemadmin/chkuser_settings.h.ruben --- netqmail-1.06/chkuser_settings.h.ruben 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/chkuser_settings.h.ruben 2009-02-04 14:39:43.000000000 +0100 @@ -0,0 +1,468 @@ +/* + * + * 'chkuser_settings.h' v.2.0.9 + * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + * + * Author: Antonio Nati tonix@interazioni.it + * All rights on this software and + * the identifying words chkusr and chkuser reserved by the author + * + * This software may be freely used, modified and distributed, + * but this lines must be kept in every original or derived version. + * Original author "Antonio Nati" and the web URL + * "http://www.interazioni.it/opensource" + * must be indicated in every related work or web page + * + */ + +/* + * the following line enables debugging of chkuser + */ +/* #define CHKUSER_DEBUG */ + +/* + * The following line moves DEBUG output from STDOUT (default) to STDERR + * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log + */ +/* #define CHKUSER_DEBUG_STDERR */ + +/* + * Uncomment the following define if you want chkuser ALWAYS enabled. + * If uncommented, it will check for rcpt existance despite any .qmail-default + * setting. + * So, unsomments this if you are aware that ALL rcpt in all domains will be + * ALWAYS checked. + */ +#define CHKUSER_ALWAYS_ON + +/* + * The following defines which virtual manager is used. + * Up to know, only vpopmail, but versions with pure qmail are in the mind. + */ +#define CHKUSER_VPOPMAIL + +/* + * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting + * VALUE HERE DEFINED is the name of the variable + * Values admitted inside the variable: NONE | ALWAYS | DOMAIN + * NONE = chkuser will not work + * ALWAYS = chkuser will work always + * DOMAIN = chkuser will work depending by single domain settings + * CHKUSER_STARTING_VARIABLE cannot be defined together with CHKUSER_ALWAYS_ON + * if CHKUSER_STARTING_VARIABLE is defined, and no variable or no value is set, then chkuser is disabled + */ +/* #define CHKUSER_STARTING_VARIABLE "CHKUSER_START" */ + +/* + * Uncomment this to enable uid/gid changing + * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS) + */ +/* #define CHKUSER_ENABLE_UIDGID */ + +/* + * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses + */ +#define CHKUSER_DOMAIN_WANTED + +/* + * Uncomment this to check for vpopmail users + */ +#define CHKUSER_ENABLE_USERS + +/* + * Uncomment this to check for alias + */ +#define CHKUSER_ENABLE_ALIAS + +/* + * The following #define set the character used for lists extensions + * be careful: this is a single char '-' definition, not a "string" + */ +#define CHKUSER_EZMLM_DASH '-' + +/* + * Uncomment this to set an alternative way to check for bouncing enabling; + * with this option enabled, the file here defined + * will be searched, inside the domain dir, in order to check if bouncing is enabled + * The content of this file is not important, just it's existence is enough + */ +/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */ + +/* + * This is the string to look for inside .qmail-default + * Be careful, chkuser looks within the first 1023 characters of .qmail-default for + * this string (despite the line containing the string is working or commented). + */ +#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" + + +/* + * Uncomment to enable logging of rejected recipients and variuos limits reached + */ +/* #define CHKUSER_ENABLE_LOGGING */ + +/* + * Uncomment to enable logging of "good" rcpts + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +/* #define CHKUSER_LOG_VALID_RCPT */ + +/* + * Uncomment to enable usage of a variable escluding any check on the sender. + * The variable should be set in tcp.smtp for clients, with static IP, whose mailer + * is composing bad sender addresses + * Defining it as "RELAYCLIENT" will avoid sender checking for authenticated/authorized users. + * Senders will be logged anyway if CHKUSER_LOG_VALID_SENDER is defined. + */ +/* #define CHKUSER_SENDER_NOCHECK_VARIABLE "RELAYCLIENT" */ + +/* + * Uncomment to enable usage of "#" and "+" characters within sender address + * This is used by SRS (Sender Rewriting Scheme) products + */ +/* #define CHKUSER_ALLOW_SENDER_SRS */ + +/* + * The following #define sets the minimum length of a domain: + * as far as I know, "k.st" is the shortest domain, so 4 characters is the + * minimum length. + * This value is used to check formally a domain name validity. + * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done. + * If you comment this define, no check on length is done. + */ +#define CHKUSER_MIN_DOMAIN_LEN 4 + +/* + * Uncomment to enable logging of "good" senders + * valid only if CHKUSER_ENABLE_LOGGING is defined + */ +#define CHKUSER_LOG_VALID_SENDER + +/* + * Uncomment to define a variable which contains the max recipients number + * this will return always error if total recipients exceed this limit. + * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, + * makes chkuser rejecting everything else + */ +#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" + +/* + * Uncomment to define a variable which contains the max unknown recipients number + * this will return always error if not existing recipients exceed this limit. + * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, + * makes chkuser rejecting everything else + */ +#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" + +/* + * Uncomment to define the variable containing the percent to check for. + * Remember to define externally (i.e. in tcp.smtp) the environment variable containing + * the limit percent. + * If the variable is not defined, or it is <= 0, quota checking is not performed. + */ +#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" + +/* + * Delay to wait for each not existing recipient + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY 1000 + +/* + * Uncomment to consider rcpt errors on address format and MX as intrusive + * + */ +#define CHKUSER_RCPT_DELAY_ANYERROR + +/* + * Uncomment to consider sender errors on address format and MX as intrusive + * + */ +#define CHKUSER_SENDER_DELAY_ANYERROR + + +/*************************************************** + * + * new/modified defines in/from 2.0.6 + * + **************************************************/ + +/* + * Before version 5.3.25, vpopmail used the function vget_real_domain() + * to get the real name of a domain (useful if rcpt domain is aliasing + * another domain). + * From version 5.3.25, this call is not available and has been + * substituted by other calls. + * + * must be enabled if vpopmail version< 5.3.5 + * must be disabled if vpopmail version => 5.3.5 * + */ +/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */ + +/*************************************************** + * + * new/modified defines in/from 2.0.7 + * + **************************************************/ + +/* + * Uncomment next define to accept recipients for + * aliases that have a -default extension + */ +/* #define CHKUSER_ENABLE_ALIAS_DEFAULT */ + + +/* + * Uncomment to enable usage of "#" and "+" characters within rcpt address + * This is used by SRS (Sender Rewriting Scheme) products + */ +/* #define CHKUSER_ALLOW_RCPT_SRS */ + +/* + * This define has been eliminated and its usage will generate an error. + * Turning it ON or OFF has no effect, as we consider the existence + * of #define VALIAS inside ~vpopmail/include/vpopmail_config.h + */ +/* #define CHKUSER_ENABLE_VALIAS */ + +/* + * Uncomment this to enable user extension on names (i.e. TMDA) + * (for mailing lists this is done without checking this define) + * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS + */ +/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */ + +/* + * Enables checking for EZMLM lists + * this define substitutes #define CHKUSER_ENABLE_LISTS + * + */ +#define CHKUSER_ENABLE_EZMLM_LISTS + +/* + * Help identifying remote authorized IPs giving them a descriptive name + * Can be put in tcp.smtp, and will be displayed inside chkuser log + */ +#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" + +/* + * The following #define set the character used for users extensions + * be careful: this is a single char '-' definition, not a "string" + * this define substitutes #define CHKUSER_EXTENSION_DASH + * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined + */ +#define CHKUSER_USERS_DASH '-' + +/* + * Enables checking for mailman lists + * + */ +/* #define CHKUSER_ENABLE_MAILMAN_LISTS */ + +/* + * Identifies the pattern string to be searched within mailman aliases + * + */ +#define CHKUSER_MAILMAN_STRING "mailman" + +/* + * The following #define set the character used for mailman lists extensions + * be careful: this is a single char '-' definition, not a "string" + */ +#define CHKUSER_MAILMAN_DASH '-' + + +/* + * Enables final clean-up routine of chkuser + * This routine cleans open DB connections used for checking users and valiases + */ +#define CHKUSER_DB_CLEANUP + +/*************************************************** + * + * new/modified defines in/from 2.0.8 + * + **************************************************/ + +/* + * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC + * compatibility, and makes harder to handle e-mail receipts. + * Please comment or delete them from your chkuser_settings.h. + */ +/* #define CHKUSER_ACCEPT_NULL_SENDER */ +/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ + +/* + * Uncomment to enable checking of user and domain format for rcpt addresses + * user = [a-z0-9_-] + * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." + */ +/* #define CHKUSER_RCPT_FORMAT */ + +/* + * Uncomment to enable checking of domain MX for rcpt addresses + * It works on any rcpt address domain that is not inside rcpthosts + */ +#define CHKUSER_RCPT_MX + +/* + * Uncomment to enable checking of user and domain format for sender address + * user = [a-z0-9_-] + * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." + */ +#define CHKUSER_SENDER_FORMAT + +/* + * Uncomment to enable checking of domain MX for sender address + * it works on the first rcpt address, despite of any domain setting on chkuser + */ +#define CHKUSER_SENDER_MX + +/* + * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value + * value is expressed in milliseconds + */ +#define CHKUSER_ERROR_DELAY_INCREASE 300 + +/*************************************************** + * + * new/modified defines in/from 2.0.9 + * + **************************************************/ + +/* + * A new class of defines is introduced + * CHKUSER_EXTRA_xxxxx + * + * These defines will be used for features/behaviours that may work despite of other CHKUSER enable/disable settings + * + */ + +/* + * If you want to accept only authenticated/authorized users you MUST enable this define and set the related variable. + * + * if this define is uncommented and the variable is set (to whatever value) then RELAYCLIENT must be set + * otherwise any message will be rejected giving "not authorized" error. + * + */ +/* #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH" */ + + +/* + * This is to check DB availability + * It avoids bouncing messages with wrong codes if MySQL/LDAP/PostGRES/etc are down or not reachable + * + * If you are using MySQL in normal installation use #define CHKUSER_VAUTH_OPEN_CALL vauth_open_update + * If you are using MySQL with separate servers for read and write use #define CHKUSER_VAUTH_OPEN_CALL vauth_open + * If you are using other DB, check the most appropriate function for your DB within dedicated vpopmail module + * + * This define substitutes CHKUSER_ENABLE_VAUTH_OPEN + */ + +/* #define CHKUSER_VAUTH_OPEN_CALL vauth_open */ +#define CHKUSER_VAUTH_OPEN_CALL vauth_open_update + +/* + * Variable to be set in order to disable chkuser + * You may set it to any value you like. If it exists chkuser will be disabled. + * Setting it to RELAYCLIENT helps disabling chkuser when sender is a known/authenticated mail client + * This is useful because Outlook/Eudora and other clients are not able to handle a KO when multiple recipients + * are present in the message. They should always relay to a SMTP service accepting all. + * + * Recipients will be logged anyway if CHKUSER_LOG_VALID_RCPT is defined. + * + * Important changes from 2.0.9 + * CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together and in such a case a fatal error is displayed + * (in the previous versions CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition) + * + * CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or CHKUSER_STARTING_VARIABLE is evaluated, so + * CHKUSER_ALWAYS_ON or CHKUSER_STARTING_VARIABLE can set the general behaviour, while CHKUSER_DISABLE_VARIABLE + * should be invoked to handle exceptions. + * + */ +/* #define CHKUSER_DISABLE_VARIABLE "RELAYCLIENT" */ + + +/* + * Error strings (SMTP error answers) + * If you don't like these definitions you can change them here + * + */ +#define CHKUSER_NORCPT_STRING "550 5.1.1 sorry, no mailbox here by that name (chkuser)\r\n" +#define CHKUSER_RESOURCE_STRING "451 4.3.0 system temporary unavailable, try again later (chkuser)\r\n" +#define CHKUSER_MBXFULL_STRING "552 5.2.2 sorry, recipient mailbox is full (chkuser)\r\n" +#define CHKUSER_MAXRCPT_STRING "550 5.5.3 sorry, reached maximum number of recipients allowed in one session (chkuser)\r\n" +#define CHKUSER_MAXWRONGRCPT_STRING "550 5.5.3 sorry, you are violating our security policies (chkuser)\r\n" +#define CHKUSER_DOMAINMISSING_STRING "550 5.1.2 sorry, you must specify a domain (chkuser)\r\n" +#define CHKUSER_RCPTFORMAT_STRING "553 5.1.3 sorry, recipient mailbox syntax not allowed (chkuser)\r\n" +#define CHKUSER_RCPTMX_STRING "550 5.1.2 sorry, can't find a valid MX for rcpt domain (chkuser)\r\n" +#define CHKUSER_SENDERFORMAT_STRING "553 5.1.7 sorry, sender mailbox syntax not allowed (chkuser)\r\n" +#define CHKUSER_SENDERMX_STRING "550 5.1.8 sorry, can't find a valid MX for sender domain (chkuser)\r\n" +#define CHKUSER_INTRUSIONTHRESHOLD_STRING "550 5.7.1 sorry, you are violating our security policies (chkuser)\r\n" +#define CHKUSER_NORELAY_STRING "553 5.7.1 sorry, that domain isn't in my list of allowed rcpthosts (chkuser)\r\n" + +#define CHKUSER_RCPTMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n" +#define CHKUSER_SENDERMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n" + +#define CHKUSER_MUSTAUTH_STRING "530 5.7.0 Authentication required (chkuser)\r\n" + +/* + * No more used defines + * Following defines are eliminated since 2.0.9 + * They will make compilation errors and must be deleted/commented + * + * #define CHKUSER_ENABLE_VAUTH_OPEN -> Substituted by CHKUSER_VAUTH_OPEN_CALL + */ + + +/* + * If you need more additional characters to be accepted within sender address + * uncomment one of the following #define and edit the character value. + * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the + * wanted char. + * + * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_SENDER_SRS + * + */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_1 '$' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_3 'X' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_6 '^' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_7 '~' */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_8 '&' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_9 '#' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_SENDER_CHAR_10 '=' */ /* available for other characters */ + + +/* + * If you need more additional characters to be accepted within rcpt address + * uncomment one of the following #define and edit the character value. + * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the + * wanted char. + * + * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_RCPT_SRS + * + */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_3 'X' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_6 '^' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_7 '~' */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_8 '&' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_9 '#' */ /* available for other characters */ +/* #define CHKUSER_ALLOW_RCPT_CHAR_10 '=' */ /* available for other characters */ + + +/* + * This define tells chkuser which variable must be set to accept a <#@[]> sender + * This kind of sender is usually generated from qmail when there is a doublebounce + * and all the job is done within the same system. + * You may need to accept double bounces from outside when you are migrating servers and + * doublebounces are forwarded between systems + */ +#define CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE "CHKUSER_DOUBLEBOUNCE" + diff -Nur netqmail-1.06/conf-cc netqmail-1.06.systemadmin/conf-cc --- netqmail-1.06/conf-cc 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06.systemadmin/conf-cc 2009-02-02 19:39:05.000000000 +0100 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -I/home/vpopmail/include This will be used to compile .c files. diff -Nur netqmail-1.06/IDS netqmail-1.06.systemadmin/IDS --- netqmail-1.06/IDS 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/IDS 2008-09-12 16:52:53.000000000 +0200 @@ -0,0 +1,11 @@ + + groupadd nofiles + useradd -g nofiles -d /var/qmail/alias alias + useradd -g nofiles -d /var/qmail qmaild + useradd -g nofiles -d /var/qmail qmaill + useradd -g nofiles -d /var/qmail qmailp + groupadd qmail + useradd -g qmail -d /var/qmail qmailq + useradd -g qmail -d /var/qmail qmailr + useradd -g qmail -d /var/qmail qmails + diff -Nur netqmail-1.06/Makefile netqmail-1.06.systemadmin/Makefile --- netqmail-1.06/Makefile 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06.systemadmin/Makefile 2009-02-18 20:45:39.000000000 +0100 @@ -1,5 +1,11 @@ # 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 @@ -136,6 +142,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 \ @@ -300,6 +310,12 @@ 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` @@ -1288,9 +1304,11 @@ qmail-popup: \ load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \ +envread.o ucspitls.o \ case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \ fs.a socket.lib ./load qmail-popup commands.o timeoutread.o timeoutwrite.o \ + envread.o ucspitls.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` @@ -1534,15 +1552,18 @@ 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 \ +ucspitls.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) rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + ucspitls.o \ received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ - alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ + $(VPOPMAIL_LIBS) \ + `cat socket.lib` qmail-smtpd.0: \ qmail-smtpd.8 @@ -1553,7 +1574,8 @@ 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 wait.h \ +fd.h base64.h ./compile qmail-smtpd.c qmail-start: \ diff -Nur netqmail-1.06/netqmail-1.05_auth-0.4.2_chkuser-2.0.8.patch netqmail-1.06.systemadmin/netqmail-1.05_auth-0.4.2_chkuser-2.0.8.patch --- netqmail-1.06/netqmail-1.05_auth-0.4.2_chkuser-2.0.8.patch 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/netqmail-1.05_auth-0.4.2_chkuser-2.0.8.patch 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,1814 @@ +diff -NU3 ./Makefile ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/Makefile +--- ./Makefile Thu Sep 9 22:10:48 2004 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/Makefile Wed Dec 1 18:44:23 2004 +@@ -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 +@@ -304,6 +308,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` +@@ -1540,20 +1548,21 @@ + 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 base64.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) 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 base64.o `cat \ +- socket.lib` ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ ++ $(VPOPMAIL_LIBS) \ ++ `cat socket.lib` + + 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 \ +diff -NU3 ./Makefile.orig ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/Makefile.orig +--- ./Makefile.orig Thu Sep 9 22:09:44 2004 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/Makefile.orig Wed Dec 1 18:37:36 2004 +@@ -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 +@@ -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 \ +@@ -300,6 +308,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` +@@ -1536,13 +1548,14 @@ + 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) rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ +- alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ +- socket.lib` ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ ++ $(VPOPMAIL_LIBS) \ ++ `cat socket.lib` + + qmail-smtpd.0: \ + qmail-smtpd.8 +@@ -1553,7 +1566,7 @@ + substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \ + error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \ + substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \ +-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h ++exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h base64.h + ./compile qmail-smtpd.c + + qmail-start: \ +diff -NU3 ./TARGETS ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/TARGETS +--- ./TARGETS Thu Sep 9 22:10:48 2004 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/TARGETS Wed Dec 1 18:37:36 2004 +@@ -386,3 +386,4 @@ + man + setup + check ++chkuser.o +diff -NU3 ./auth.log ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/auth.log +--- ./auth.log Thu Sep 9 22:10:48 2004 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/auth.log Wed Dec 1 18:37:36 2004 +@@ -1,4 +1,4 @@ +-Installing qmail-smtpd AUTH 041 (Build 2003357161118) at Thu Sep 9 22:10:48 CEST 2004 <<< ++Installing qmail-smtpd AUTH 041 (Build 2003357161118) at Thu Sep 9 13:04:10 CEST 2004 <<< + Targeting file TARGETS ... + --> TARGETS copied to TARGETS.041 + --> Patching qmail source file TARGETS .... +@@ -63,4 +63,4 @@ + done + Copying documentation and samples to /var/qmail/doc/ ... + README.auth -> /var/qmail/doc/README.auth +-Installation of qmail-smtpd AUTH 041 (Build 2003357161118) finished at Thu Sep 9 22:10:48 CEST 2004 <<< ++Installation of qmail-smtpd AUTH 041 (Build 2003357161118) finished at Thu Sep 9 13:04:10 CEST 2004 <<< +diff -NU3 ./chkuser.c ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/chkuser.c +--- ./chkuser.c Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/chkuser.c Tue Dec 7 11:04:35 2004 +@@ -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 -NU3 ./chkuser.h ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/chkuser.h +--- ./chkuser.h Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/chkuser.h Wed Dec 1 23:16:11 2004 +@@ -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 -NU3 ./chkuser_settings.h ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/chkuser_settings.h +--- ./chkuser_settings.h Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/chkuser_settings.h Tue Dec 7 12:00:26 2004 +@@ -0,0 +1,373 @@ ++ ++/* ++ * ++ * 'chkuser_settings.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 ++ * ++ */ ++ ++/* ++ * the following line enables debugging of chkuser ++ */ ++/* #define CHKUSER_DEBUG */ ++ ++/* ++ * The following line moves DEBUG output from STDOUT (default) to STDERR ++ * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log ++ */ ++/* #define CHKUSER_DEBUG_STDERR */ ++ ++/* ++ * Uncomment the following define if you want chkuser ALWAYS enabled. ++ * If uncommented, it will check for rcpt existance despite any .qmail-default ++ * setting. ++ * So, unsomments this if you are aware that ALL rcpt in all domains will be ++ * ALWAYS checked. ++ */ ++/* #define CHKUSER_ALWAYS_ON */ ++ ++/* ++ * The following defines which virtual manager is used. ++ * Up to know, only vpopmail, but versions with pure qmail are in the mind. ++ */ ++#define CHKUSER_VPOPMAIL ++ ++/* ++ * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting ++ * VALUE HERE DEFINED is the name of the variable ++ * Values admitted inside the variable: NONE | ALWAYS | DOMAIN ++ * NONE = chkuser will not work ++ * ALWAYS = chkuser will work always ++ * DOMAIN = chkuser will work depending by single domain settings ++ * 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 enable uid/gid changing ++ * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS) ++ */ ++#define CHKUSER_ENABLE_UIDGID ++ ++/* ++ * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses ++ */ ++#define CHKUSER_DOMAIN_WANTED ++ ++/* ++ * Uncomment this to check for vpopmail users ++ */ ++#define CHKUSER_ENABLE_USERS ++ ++/* ++ * Uncomment this to check for alias ++ */ ++#define CHKUSER_ENABLE_ALIAS ++ ++/* ++ * The following #define set the character used for lists extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ */ ++#define CHKUSER_EZMLM_DASH '-' ++ ++/* ++ * Uncomment this to set an alternative way to check for bouncing enabling; ++ * with this option enabled, the file here defined ++ * will be searched, inside the domain dir, in order to check if bouncing is enabled ++ * The content of this file is not important, just it's existence is enough ++ */ ++/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */ ++ ++/* ++ * This is the string to look for inside .qmail-default ++ * Be careful, chkuser looks within the first 1023 characters of .qmail-default for ++ * this string (despite the line containing the string is working or commented). ++ */ ++#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" ++ ++/* ++ * 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 */ ++ ++/* ++ * Uncomment to enable logging of rejected recipients and variuos limits reached ++ */ ++#define CHKUSER_ENABLE_LOGGING ++ ++/* ++ * Uncomment to enable logging of "good" rcpts ++ * valid only if CHKUSER_ENABLE_LOGGING is defined ++ */ ++#define CHKUSER_LOG_VALID_RCPT ++ ++/* ++ * Uncomment to enable usage of a variable escluding any check on the sender. ++ * The variable should be set in tcp.smtp for clients, with static IP, whose mailer ++ * is composing bad sender addresses ++ */ ++/* #define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK" */ ++ ++/* ++ * 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 '*' */ ++ ++/* ++ * The following #define sets the minimum length of a domain: ++ * as far as I know, "k.st" is the shortest domain, so 4 characters is the ++ * minimum length. ++ * This value is used to check formally a domain name validity. ++ * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done. ++ * If you comment this define, no check on length is done. ++ */ ++#define CHKUSER_MIN_DOMAIN_LEN 4 ++ ++/* ++ * Uncomment to enable logging of "good" senders ++ * valid only if CHKUSER_ENABLE_LOGGING is defined ++ */ ++#define CHKUSER_LOG_VALID_SENDER ++ ++/* ++ * Uncomment to define a variable which contains the max recipients number ++ * this will return always error if total recipients exceed this limit. ++ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, ++ * makes chkuser rejecting everything else ++ */ ++#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" ++ ++/* ++ * Uncomment to define a variable which contains the max unknown recipients number ++ * this will return always error if not existing recipients exceed this limit. ++ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, ++ * makes chkuser rejecting everything else ++ */ ++#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" ++ ++/* ++ * Uncomment to define the variable containing the percent to check for. ++ * Remember to define externally (i.e. in tcp.smtp) the environment variable containing ++ * the limit percent. ++ * If the variable is not defined, or it is <= 0, quota checking is not performed. ++ */ ++#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" ++ ++/* ++ * Delay to wait for each not existing recipient ++ * value is expressed in milliseconds ++ */ ++#define CHKUSER_ERROR_DELAY 1000 ++ ++/* ++ * Uncomment to consider rcpt errors on address format and MX as intrusive ++ * ++ */ ++#define CHKUSER_RCPT_DELAY_ANYERROR ++ ++/* ++ * Uncomment to consider sender errors on address format and MX as intrusive ++ * ++ */ ++#define CHKUSER_SENDER_DELAY_ANYERROR ++ ++#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" ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.6 ++ * ++ **************************************************/ ++ ++/* ++ * Before version 5.3.25, vpopmail used the function vget_real_domain() ++ * to get the real name of a domain (useful if rcpt domain is aliasing ++ * another domain). ++ * From version 5.3.25, this call is not available and has been ++ * substituted by other calls. ++ * ++ * must be enabled if vpopmail version< 5.3.5 ++ * must be disabled if vpopmail version => 5.3.5 * ++ */ ++/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */ ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.7 ++ * ++ **************************************************/ ++ ++/* ++ * Uncomment next define to accept recipients for ++ * aliases that have a -default extension ++ */ ++/* #define CHKUSER_ENABLE_ALIAS_DEFAULT */ ++ ++ ++/* ++ * Uncomment to enable usage of "#" and "+" characters within rcpt address ++ * This is used by SRS (Sender Rewriting Scheme) products ++ */ ++/* #define CHKUSER_ALLOW_RCPT_SRS */ ++ ++/* ++ * 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 '*' */ ++ ++/* ++ * 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 */ ++ ++/* ++ * Uncomment this to enable user extension on names (i.e. TMDA) ++ * (for mailing lists this is done without checking this define) ++ * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS ++ */ ++/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */ ++ ++/* ++ * Enables checking for EZMLM lists ++ * this define substitutes #define CHKUSER_ENABLE_LISTS ++ * ++ */ ++#define CHKUSER_ENABLE_EZMLM_LISTS ++ ++/* ++ * Help identifying remote authorized IPs giving them a descriptive name ++ * Can be put in tcp.smtp, and will be displayed inside chkuser log ++ * Substitutes RELAYCLIENT in chkuser logging ++ */ ++#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" ++ ++/* ++ * The following #define set the character used for users extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ * this define substitutes #define CHKUSER_EXTENSION_DASH ++ * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined ++ */ ++#define CHKUSER_USERS_DASH '-' ++ ++/* ++ * New error strings for SOFT DNS problems ++ */ ++#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" ++ ++/* ++ * Enables checking for mailman lists ++ * ++ */ ++/* #define CHKUSER_ENABLE_MAILMAN_LISTS */ ++ ++/* ++ * Identifies the pattern string to be searched within mailman aliases ++ * ++ */ ++#define CHKUSER_MAILMAN_STRING "mailman" ++ ++/* ++ * The following #define set the character used for mailman lists extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ */ ++#define CHKUSER_MAILMAN_DASH '-' ++ ++ ++/* ++ * Enables final clean-up routine of chkuser ++ * This routine cleans open DB connections used for checking users and valiases ++ */ ++#define CHKUSER_DB_CLEANUP ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.8 ++ * ++ **************************************************/ ++ ++/* ++ * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC ++ * compatibility, and makes harder to handle e-mail receipts. ++ * Please comment or delete them from your chkuser_settings.h. ++ */ ++/* #define CHKUSER_ACCEPT_NULL_SENDER */ ++/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ ++ ++/* ++ * Uncomment to enable checking of user and domain format for rcpt addresses ++ * user = [a-z0-9_-] ++ * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." ++ */ ++/* #define CHKUSER_RCPT_FORMAT */ ++ ++/* ++ * Uncomment to enable checking of domain MX for rcpt addresses ++ * It works on any rcpt address domain that is not inside rcpthosts ++ */ ++/* #define CHKUSER_RCPT_MX */ ++ ++/* ++ * Uncomment to enable checking of user and domain format for sender address ++ * user = [a-z0-9_-] ++ * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." ++ */ ++/* #define CHKUSER_SENDER_FORMAT */ ++ ++/* ++ * Uncomment to enable checking of domain MX for sender address ++ * it works on the first rcpt address, despite of any domain setting on chkuser ++ */ ++/* #define CHKUSER_SENDER_MX */ ++ ++/* ++ * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value ++ * value is expressed in milliseconds ++ */ ++#define CHKUSER_ERROR_DELAY_INCREASE 300 ++ +diff -NU3 ./conf-cc ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/conf-cc +--- ./conf-cc Thu Sep 9 22:09:44 2004 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/conf-cc Wed Dec 1 18:37:36 2004 +@@ -1,3 +1,3 @@ +-cc -O2 ++cc -O2 -I/home/vpopmail/include + + This will be used to compile .c files. +diff -NU3 ./qmail-smtpd.c ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/qmail-smtpd.c +--- ./qmail-smtpd.c Thu Sep 9 22:10:48 2004 ++++ ../netqmail-1.05-auth-0.4.2-chkuser-2.0.8/qmail-smtpd.c Wed Dec 1 18:43:36 2004 +@@ -1,3 +1,14 @@ ++ ++/* ++ * ++ * includes chkuser v.2.0.8 ++ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x ++ * ++ * Author: Antonio Nati tonix@interazioni.it ++ * www.interazioni.it/opensource ++ * ++ */ ++ + #include "sig.h" + #include "readwrite.h" + #include "stralloc.h" +@@ -25,6 +36,10 @@ + #include "commands.h" + #include "wait.h" + ++/* start chkuser code */ ++#include "chkuser.h" ++/* end chkuser code */ ++ + #define CRAM_MD5 + #define AUTHSLEEP 5 + +@@ -260,6 +275,9 @@ + 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(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -271,6 +289,10 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ ++/* ++ * Original code substituted by chkuser code ++ + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); +@@ -278,6 +300,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(); diff -Nur netqmail-1.06/netqmail-1.05_chkuser-2.0.8.patch netqmail-1.06.systemadmin/netqmail-1.05_chkuser-2.0.8.patch --- netqmail-1.06/netqmail-1.05_chkuser-2.0.8.patch 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/netqmail-1.05_chkuser-2.0.8.patch 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,1735 @@ +diff -NU3 ./Makefile ../netqmail-1.05-chkuser-2.0.8-release/Makefile +--- ./Makefile Mon Jul 26 11:05:52 2004 ++++ ../netqmail-1.05-chkuser-2.0.8-release/Makefile Wed Dec 1 18:00:19 2004 +@@ -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 +@@ -300,6 +304,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` +@@ -1536,20 +1544,21 @@ + 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 socket.lib $(SMTPD_CHKUSER_OBJ) ++ ./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` + + 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 \ +diff -NU3 ./TARGETS ../netqmail-1.05-chkuser-2.0.8-release/TARGETS +--- ./TARGETS Mon Jun 15 12:53:16 1998 ++++ ../netqmail-1.05-chkuser-2.0.8-release/TARGETS Wed Dec 1 18:00:19 2004 +@@ -385,3 +385,4 @@ + man + setup + check ++chkuser.o +diff -NU3 ./chkuser.c ../netqmail-1.05-chkuser-2.0.8-release/chkuser.c +--- ./chkuser.c Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-chkuser-2.0.8-release/chkuser.c Tue Dec 7 11:04:27 2004 +@@ -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 -NU3 ./chkuser.h ../netqmail-1.05-chkuser-2.0.8-release/chkuser.h +--- ./chkuser.h Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-chkuser-2.0.8-release/chkuser.h Wed Dec 1 18:34:53 2004 +@@ -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 -NU3 ./chkuser_settings.h ../netqmail-1.05-chkuser-2.0.8-release/chkuser_settings.h +--- ./chkuser_settings.h Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-chkuser-2.0.8-release/chkuser_settings.h Tue Dec 7 12:00:32 2004 +@@ -0,0 +1,373 @@ ++ ++/* ++ * ++ * 'chkuser_settings.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 ++ * ++ */ ++ ++/* ++ * the following line enables debugging of chkuser ++ */ ++/* #define CHKUSER_DEBUG */ ++ ++/* ++ * The following line moves DEBUG output from STDOUT (default) to STDERR ++ * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log ++ */ ++/* #define CHKUSER_DEBUG_STDERR */ ++ ++/* ++ * Uncomment the following define if you want chkuser ALWAYS enabled. ++ * If uncommented, it will check for rcpt existance despite any .qmail-default ++ * setting. ++ * So, unsomments this if you are aware that ALL rcpt in all domains will be ++ * ALWAYS checked. ++ */ ++/* #define CHKUSER_ALWAYS_ON */ ++ ++/* ++ * The following defines which virtual manager is used. ++ * Up to know, only vpopmail, but versions with pure qmail are in the mind. ++ */ ++#define CHKUSER_VPOPMAIL ++ ++/* ++ * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting ++ * VALUE HERE DEFINED is the name of the variable ++ * Values admitted inside the variable: NONE | ALWAYS | DOMAIN ++ * NONE = chkuser will not work ++ * ALWAYS = chkuser will work always ++ * DOMAIN = chkuser will work depending by single domain settings ++ * 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 enable uid/gid changing ++ * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS) ++ */ ++#define CHKUSER_ENABLE_UIDGID ++ ++/* ++ * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses ++ */ ++#define CHKUSER_DOMAIN_WANTED ++ ++/* ++ * Uncomment this to check for vpopmail users ++ */ ++#define CHKUSER_ENABLE_USERS ++ ++/* ++ * Uncomment this to check for alias ++ */ ++#define CHKUSER_ENABLE_ALIAS ++ ++/* ++ * The following #define set the character used for lists extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ */ ++#define CHKUSER_EZMLM_DASH '-' ++ ++/* ++ * Uncomment this to set an alternative way to check for bouncing enabling; ++ * with this option enabled, the file here defined ++ * will be searched, inside the domain dir, in order to check if bouncing is enabled ++ * The content of this file is not important, just it's existence is enough ++ */ ++/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */ ++ ++/* ++ * This is the string to look for inside .qmail-default ++ * Be careful, chkuser looks within the first 1023 characters of .qmail-default for ++ * this string (despite the line containing the string is working or commented). ++ */ ++#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" ++ ++/* ++ * 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 */ ++ ++/* ++ * Uncomment to enable logging of rejected recipients and variuos limits reached ++ */ ++#define CHKUSER_ENABLE_LOGGING ++ ++/* ++ * Uncomment to enable logging of "good" rcpts ++ * valid only if CHKUSER_ENABLE_LOGGING is defined ++ */ ++#define CHKUSER_LOG_VALID_RCPT ++ ++/* ++ * Uncomment to enable usage of a variable escluding any check on the sender. ++ * The variable should be set in tcp.smtp for clients, with static IP, whose mailer ++ * is composing bad sender addresses ++ */ ++/* #define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK" */ ++ ++/* ++ * 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 '*' */ ++ ++/* ++ * The following #define sets the minimum length of a domain: ++ * as far as I know, "k.st" is the shortest domain, so 4 characters is the ++ * minimum length. ++ * This value is used to check formally a domain name validity. ++ * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done. ++ * If you comment this define, no check on length is done. ++ */ ++#define CHKUSER_MIN_DOMAIN_LEN 4 ++ ++/* ++ * Uncomment to enable logging of "good" senders ++ * valid only if CHKUSER_ENABLE_LOGGING is defined ++ */ ++#define CHKUSER_LOG_VALID_SENDER ++ ++/* ++ * Uncomment to define a variable which contains the max recipients number ++ * this will return always error if total recipients exceed this limit. ++ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, ++ * makes chkuser rejecting everything else ++ */ ++#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" ++ ++/* ++ * Uncomment to define a variable which contains the max unknown recipients number ++ * this will return always error if not existing recipients exceed this limit. ++ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, ++ * makes chkuser rejecting everything else ++ */ ++#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" ++ ++/* ++ * Uncomment to define the variable containing the percent to check for. ++ * Remember to define externally (i.e. in tcp.smtp) the environment variable containing ++ * the limit percent. ++ * If the variable is not defined, or it is <= 0, quota checking is not performed. ++ */ ++#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" ++ ++/* ++ * Delay to wait for each not existing recipient ++ * value is expressed in milliseconds ++ */ ++#define CHKUSER_ERROR_DELAY 1000 ++ ++/* ++ * Uncomment to consider rcpt errors on address format and MX as intrusive ++ * ++ */ ++#define CHKUSER_RCPT_DELAY_ANYERROR ++ ++/* ++ * Uncomment to consider sender errors on address format and MX as intrusive ++ * ++ */ ++#define CHKUSER_SENDER_DELAY_ANYERROR ++ ++#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" ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.6 ++ * ++ **************************************************/ ++ ++/* ++ * Before version 5.3.25, vpopmail used the function vget_real_domain() ++ * to get the real name of a domain (useful if rcpt domain is aliasing ++ * another domain). ++ * From version 5.3.25, this call is not available and has been ++ * substituted by other calls. ++ * ++ * must be enabled if vpopmail version< 5.3.5 ++ * must be disabled if vpopmail version => 5.3.5 * ++ */ ++/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */ ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.7 ++ * ++ **************************************************/ ++ ++/* ++ * Uncomment next define to accept recipients for ++ * aliases that have a -default extension ++ */ ++/* #define CHKUSER_ENABLE_ALIAS_DEFAULT */ ++ ++ ++/* ++ * Uncomment to enable usage of "#" and "+" characters within rcpt address ++ * This is used by SRS (Sender Rewriting Scheme) products ++ */ ++/* #define CHKUSER_ALLOW_RCPT_SRS */ ++ ++/* ++ * 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 '*' */ ++ ++/* ++ * 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 */ ++ ++/* ++ * Uncomment this to enable user extension on names (i.e. TMDA) ++ * (for mailing lists this is done without checking this define) ++ * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS ++ */ ++/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */ ++ ++/* ++ * Enables checking for EZMLM lists ++ * this define substitutes #define CHKUSER_ENABLE_LISTS ++ * ++ */ ++#define CHKUSER_ENABLE_EZMLM_LISTS ++ ++/* ++ * Help identifying remote authorized IPs giving them a descriptive name ++ * Can be put in tcp.smtp, and will be displayed inside chkuser log ++ * Substitutes RELAYCLIENT in chkuser logging ++ */ ++#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" ++ ++/* ++ * The following #define set the character used for users extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ * this define substitutes #define CHKUSER_EXTENSION_DASH ++ * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined ++ */ ++#define CHKUSER_USERS_DASH '-' ++ ++/* ++ * New error strings for SOFT DNS problems ++ */ ++#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" ++ ++/* ++ * Enables checking for mailman lists ++ * ++ */ ++/* #define CHKUSER_ENABLE_MAILMAN_LISTS */ ++ ++/* ++ * Identifies the pattern string to be searched within mailman aliases ++ * ++ */ ++#define CHKUSER_MAILMAN_STRING "mailman" ++ ++/* ++ * The following #define set the character used for mailman lists extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ */ ++#define CHKUSER_MAILMAN_DASH '-' ++ ++ ++/* ++ * Enables final clean-up routine of chkuser ++ * This routine cleans open DB connections used for checking users and valiases ++ */ ++#define CHKUSER_DB_CLEANUP ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.8 ++ * ++ **************************************************/ ++ ++/* ++ * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC ++ * compatibility, and makes harder to handle e-mail receipts. ++ * Please comment or delete them from your chkuser_settings.h. ++ */ ++/* #define CHKUSER_ACCEPT_NULL_SENDER */ ++/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ ++ ++/* ++ * Uncomment to enable checking of user and domain format for rcpt addresses ++ * user = [a-z0-9_-] ++ * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." ++ */ ++/* #define CHKUSER_RCPT_FORMAT */ ++ ++/* ++ * Uncomment to enable checking of domain MX for rcpt addresses ++ * It works on any rcpt address domain that is not inside rcpthosts ++ */ ++/* #define CHKUSER_RCPT_MX */ ++ ++/* ++ * Uncomment to enable checking of user and domain format for sender address ++ * user = [a-z0-9_-] ++ * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." ++ */ ++/* #define CHKUSER_SENDER_FORMAT */ ++ ++/* ++ * Uncomment to enable checking of domain MX for sender address ++ * it works on the first rcpt address, despite of any domain setting on chkuser ++ */ ++/* #define CHKUSER_SENDER_MX */ ++ ++/* ++ * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value ++ * value is expressed in milliseconds ++ */ ++#define CHKUSER_ERROR_DELAY_INCREASE 300 ++ +diff -NU3 ./conf-cc ../netqmail-1.05-chkuser-2.0.8-release/conf-cc +--- ./conf-cc Mon Jun 15 12:53:16 1998 ++++ ../netqmail-1.05-chkuser-2.0.8-release/conf-cc Wed Dec 1 18:00:19 2004 +@@ -1,3 +1,3 @@ +-cc -O2 ++cc -O2 -I/home/vpopmail/include + + This will be used to compile .c files. +diff -NU3 ./qmail-smtpd.c ../netqmail-1.05-chkuser-2.0.8-release/qmail-smtpd.c +--- ./qmail-smtpd.c Mon Jul 26 11:05:52 2004 ++++ ../netqmail-1.05-chkuser-2.0.8-release/qmail-smtpd.c Mon Dec 6 13:14:26 2004 +@@ -1,3 +1,13 @@ ++/* ++ * ++ * includes chkuser v.2.0.8 ++ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x ++ * ++ * Author: Antonio Nati tonix@interazioni.it ++ * www.interazioni.it/opensource ++ * ++ */ ++ + #include "sig.h" + #include "readwrite.h" + #include "stralloc.h" +@@ -24,6 +34,10 @@ + #include "timeoutwrite.h" + #include "commands.h" + ++/* start chkuser code */ ++#include "chkuser.h" ++/* end chkuser code */ ++ + #define MAXHOPS 100 + unsigned int databytes = 0; + int timeout = 1200; +@@ -240,6 +254,9 @@ + 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(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); +@@ -251,6 +268,10 @@ + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } ++ ++/* ++ * Original code substituted by chkuser code ++ + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); +@@ -258,6 +279,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(); diff -Nur netqmail-1.06/netqmail-1.05_toaster-0.6-1_chkuser-2.0.8b.patch netqmail-1.06.systemadmin/netqmail-1.05_toaster-0.6-1_chkuser-2.0.8b.patch --- netqmail-1.06/netqmail-1.05_toaster-0.6-1_chkuser-2.0.8b.patch 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/netqmail-1.05_toaster-0.6-1_chkuser-2.0.8b.patch 2009-02-02 19:31:40.000000000 +0100 @@ -0,0 +1,1729 @@ +diff -NU3 ./Makefile ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/Makefile +--- ./Makefile Thu Jul 29 10:03:36 2004 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/Makefile Wed Dec 1 23:16:40 2004 +@@ -2,6 +2,10 @@ + + SHELL=/bin/sh + ++VPOPMAIL_HOME=/home/vpopmail ++SMTPD_CHKUSER_OBJ=chkuser.o ++VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` ++ + default: it + + addresses.0: \ +@@ -304,6 +308,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` +@@ -1578,21 +1586,22 @@ + 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 strerr.a substdio.a error.a str.a \ +-fs.a auto_qmail.o base64.o socket.lib dns.o dns.lib +- ./load qmail-smtpd rcpthosts.o qregex.o commands.o timeoutread.o \ ++fs.a auto_qmail.o base64.o socket.lib dns.o dns.lib $(SMTPD_CHKUSER_OBJ) ++ ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o qregex.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 strerr.a substdio.a error.a str.a fs.a auto_qmail.o base64.o `cat \ +- socket.lib` dns.o `cat dns.lib` ++ alloc.a strerr.a substdio.a error.a str.a fs.a auto_qmail.o base64.o \ ++ $(VPOPMAIL_LIBS) \ ++ `cat socket.lib` dns.o `cat dns.lib` + + 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 \ +diff -NU3 ./TARGETS ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/TARGETS +--- ./TARGETS Thu Jul 29 10:03:36 2004 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/TARGETS Wed Dec 1 23:16:39 2004 +@@ -399,3 +399,4 @@ + setup + check + update_tmprsadh ++chkuser.o +diff -NU3 ./chkuser.c ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/chkuser.c +--- ./chkuser.c Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/chkuser.c Thu Dec 9 00:15:57 2004 +@@ -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 -NU3 ./chkuser.h ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/chkuser.h +--- ./chkuser.h Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/chkuser.h Wed Dec 1 23:16:52 2004 +@@ -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 -NU3 ./chkuser_settings.h ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/chkuser_settings.h +--- ./chkuser_settings.h Thu Jan 1 01:00:00 1970 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/chkuser_settings.h Thu Dec 9 02:21:17 2004 +@@ -0,0 +1,373 @@ ++ ++/* ++ * ++ * 'chkuser_settings.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 ++ * ++ */ ++ ++/* ++ * the following line enables debugging of chkuser ++ */ ++/* #define CHKUSER_DEBUG */ ++ ++/* ++ * The following line moves DEBUG output from STDOUT (default) to STDERR ++ * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log ++ */ ++/* #define CHKUSER_DEBUG_STDERR */ ++ ++/* ++ * Uncomment the following define if you want chkuser ALWAYS enabled. ++ * If uncommented, it will check for rcpt existance despite any .qmail-default ++ * setting. ++ * So, unsomments this if you are aware that ALL rcpt in all domains will be ++ * ALWAYS checked. ++ */ ++/* #define CHKUSER_ALWAYS_ON */ ++ ++/* ++ * The following defines which virtual manager is used. ++ * Up to know, only vpopmail, but versions with pure qmail are in the mind. ++ */ ++#define CHKUSER_VPOPMAIL ++ ++/* ++ * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting ++ * VALUE HERE DEFINED is the name of the variable ++ * Values admitted inside the variable: NONE | ALWAYS | DOMAIN ++ * NONE = chkuser will not work ++ * ALWAYS = chkuser will work always ++ * DOMAIN = chkuser will work depending by single domain settings ++ * 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 enable uid/gid changing ++ * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS) ++ */ ++/* #define CHKUSER_ENABLE_UIDGID */ ++ ++/* ++ * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses ++ */ ++#define CHKUSER_DOMAIN_WANTED ++ ++/* ++ * Uncomment this to check for vpopmail users ++ */ ++#define CHKUSER_ENABLE_USERS ++ ++/* ++ * Uncomment this to check for alias ++ */ ++#define CHKUSER_ENABLE_ALIAS ++ ++/* ++ * The following #define set the character used for lists extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ */ ++#define CHKUSER_EZMLM_DASH '-' ++ ++/* ++ * Uncomment this to set an alternative way to check for bouncing enabling; ++ * with this option enabled, the file here defined ++ * will be searched, inside the domain dir, in order to check if bouncing is enabled ++ * The content of this file is not important, just it's existence is enough ++ */ ++/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */ ++ ++/* ++ * This is the string to look for inside .qmail-default ++ * Be careful, chkuser looks within the first 1023 characters of .qmail-default for ++ * this string (despite the line containing the string is working or commented). ++ */ ++#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox" ++ ++/* ++ * 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 */ ++ ++/* ++ * Uncomment to enable logging of rejected recipients and variuos limits reached ++ */ ++#define CHKUSER_ENABLE_LOGGING ++ ++/* ++ * Uncomment to enable logging of "good" rcpts ++ * valid only if CHKUSER_ENABLE_LOGGING is defined ++ */ ++#define CHKUSER_LOG_VALID_RCPT ++ ++/* ++ * Uncomment to enable usage of a variable escluding any check on the sender. ++ * The variable should be set in tcp.smtp for clients, with static IP, whose mailer ++ * is composing bad sender addresses ++ */ ++/* #define CHKUSER_SENDER_NOCHECK_VARIABLE "SENDER_NOCHECK" */ ++ ++/* ++ * 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 '*' */ ++ ++/* ++ * The following #define sets the minimum length of a domain: ++ * as far as I know, "k.st" is the shortest domain, so 4 characters is the ++ * minimum length. ++ * This value is used to check formally a domain name validity. ++ * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done. ++ * If you comment this define, no check on length is done. ++ */ ++#define CHKUSER_MIN_DOMAIN_LEN 4 ++ ++/* ++ * Uncomment to enable logging of "good" senders ++ * valid only if CHKUSER_ENABLE_LOGGING is defined ++ */ ++#define CHKUSER_LOG_VALID_SENDER ++ ++/* ++ * Uncomment to define a variable which contains the max recipients number ++ * this will return always error if total recipients exceed this limit. ++ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, ++ * makes chkuser rejecting everything else ++ */ ++#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT" ++ ++/* ++ * Uncomment to define a variable which contains the max unknown recipients number ++ * this will return always error if not existing recipients exceed this limit. ++ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE, ++ * makes chkuser rejecting everything else ++ */ ++#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT" ++ ++/* ++ * Uncomment to define the variable containing the percent to check for. ++ * Remember to define externally (i.e. in tcp.smtp) the environment variable containing ++ * the limit percent. ++ * If the variable is not defined, or it is <= 0, quota checking is not performed. ++ */ ++#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA" ++ ++/* ++ * Delay to wait for each not existing recipient ++ * value is expressed in milliseconds ++ */ ++#define CHKUSER_ERROR_DELAY 1000 ++ ++/* ++ * Uncomment to consider rcpt errors on address format and MX as intrusive ++ * ++ */ ++#define CHKUSER_RCPT_DELAY_ANYERROR ++ ++/* ++ * Uncomment to consider sender errors on address format and MX as intrusive ++ * ++ */ ++#define CHKUSER_SENDER_DELAY_ANYERROR ++ ++#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" ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.6 ++ * ++ **************************************************/ ++ ++/* ++ * Before version 5.3.25, vpopmail used the function vget_real_domain() ++ * to get the real name of a domain (useful if rcpt domain is aliasing ++ * another domain). ++ * From version 5.3.25, this call is not available and has been ++ * substituted by other calls. ++ * ++ * must be enabled if vpopmail version< 5.3.5 ++ * must be disabled if vpopmail version => 5.3.5 * ++ */ ++/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */ ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.7 ++ * ++ **************************************************/ ++ ++/* ++ * Uncomment next define to accept recipients for ++ * aliases that have a -default extension ++ */ ++/* #define CHKUSER_ENABLE_ALIAS_DEFAULT */ ++ ++ ++/* ++ * Uncomment to enable usage of "#" and "+" characters within rcpt address ++ * This is used by SRS (Sender Rewriting Scheme) products ++ */ ++/* #define CHKUSER_ALLOW_RCPT_SRS */ ++ ++/* ++ * 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 '*' */ ++ ++/* ++ * 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 */ ++ ++/* ++ * Uncomment this to enable user extension on names (i.e. TMDA) ++ * (for mailing lists this is done without checking this define) ++ * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS ++ */ ++/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */ ++ ++/* ++ * Enables checking for EZMLM lists ++ * this define substitutes #define CHKUSER_ENABLE_LISTS ++ * ++ */ ++#define CHKUSER_ENABLE_EZMLM_LISTS ++ ++/* ++ * Help identifying remote authorized IPs giving them a descriptive name ++ * Can be put in tcp.smtp, and will be displayed inside chkuser log ++ * Substitutes RELAYCLIENT in chkuser logging ++ */ ++#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY" ++ ++/* ++ * The following #define set the character used for users extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ * this define substitutes #define CHKUSER_EXTENSION_DASH ++ * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined ++ */ ++#define CHKUSER_USERS_DASH '-' ++ ++/* ++ * New error strings for SOFT DNS problems ++ */ ++#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" ++ ++/* ++ * Enables checking for mailman lists ++ * ++ */ ++/* #define CHKUSER_ENABLE_MAILMAN_LISTS */ ++ ++/* ++ * Identifies the pattern string to be searched within mailman aliases ++ * ++ */ ++#define CHKUSER_MAILMAN_STRING "mailman" ++ ++/* ++ * The following #define set the character used for mailman lists extensions ++ * be careful: this is a single char '-' definition, not a "string" ++ */ ++#define CHKUSER_MAILMAN_DASH '-' ++ ++ ++/* ++ * Enables final clean-up routine of chkuser ++ * This routine cleans open DB connections used for checking users and valiases ++ */ ++#define CHKUSER_DB_CLEANUP ++ ++/*************************************************** ++ * ++ * new/modified defines in/from 2.0.8 ++ * ++ **************************************************/ ++ ++/* ++ * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC ++ * compatibility, and makes harder to handle e-mail receipts. ++ * Please comment or delete them from your chkuser_settings.h. ++ */ ++/* #define CHKUSER_ACCEPT_NULL_SENDER */ ++/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */ ++ ++/* ++ * Uncomment to enable checking of user and domain format for rcpt addresses ++ * user = [a-z0-9_-] ++ * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." ++ */ ++/* #define CHKUSER_RCPT_FORMAT */ ++ ++/* ++ * Uncomment to enable checking of domain MX for rcpt addresses ++ * It works on any rcpt address domain that is not inside rcpthosts ++ */ ++/* #define CHKUSER_RCPT_MX */ ++ ++/* ++ * Uncomment to enable checking of user and domain format for sender address ++ * user = [a-z0-9_-] ++ * domain = [a-z0-9-.] with not consecutive "-.", not leading or ending "-." ++ */ ++/* #define CHKUSER_SENDER_FORMAT */ ++ ++/* ++ * Uncomment to enable checking of domain MX for sender address ++ * it works on the first rcpt address, despite of any domain setting on chkuser ++ */ ++/* #define CHKUSER_SENDER_MX */ ++ ++/* ++ * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value ++ * value is expressed in milliseconds ++ */ ++#define CHKUSER_ERROR_DELAY_INCREASE 300 ++ +diff -NU3 ./conf-cc ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/conf-cc +--- ./conf-cc Thu Jul 29 10:03:36 2004 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/conf-cc Thu Dec 9 02:03:55 2004 +@@ -1,3 +1,3 @@ +-cc -O2 -DTLS=20040120 -I/usr/local/ssl/include ++cc -O2 -I/home/vpopmail/include -DTLS=20040120 -I/usr/local/ssl/include + + This will be used to compile .c files. +diff -NU3 ./qmail-smtpd.c ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/qmail-smtpd.c +--- ./qmail-smtpd.c Thu Jul 29 10:03:36 2004 ++++ ../netqmail-1.05-toaster-0.6-1-chkuser-2.0.8b/qmail-smtpd.c Thu Dec 9 02:03:09 2004 +@@ -1,3 +1,14 @@ ++ ++/* ++ * ++ * includes chkuser v.2.0.8 ++ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x ++ * ++ * Author: Antonio Nati tonix@interazioni.it ++ * www.interazioni.it/opensource ++ * ++ */ ++ + #include "sig.h" + #include "readwrite.h" + #include "stralloc.h" +@@ -51,6 +62,8 @@ + 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; +@@ -375,12 +388,8 @@ + void smtp_mail(arg) char *arg; + { + if (!addrparse(arg)) { err_syntax(); return; } ++ if (chkuser_sender (&addr) != CHKUSER_OK) { return; } + if (bmfok) flagbarfbmf = bmcheck(BMCHECK_BMF); +- switch(mfcheck()) { +- case DNS_HARD: err_hmf(); return; +- case DNS_SOFT: err_smf(); return; +- case DNS_MEM: die_nomem(); +- } + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); +@@ -402,13 +411,20 @@ + err_bmt(); + return; + } +- if (relayclient) { +- --addr.len; +- if (!stralloc_cats(&addr,relayclient)) die_nomem(); +- if (!stralloc_0(&addr)) die_nomem(); ++ 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; ++ + } +- else +- if (!addrallowed()) { err_nogateway(); return; } ++ + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); diff -Nur netqmail-1.06/netqmail-1.05-ucspitls-0.3.patch netqmail-1.06.systemadmin/netqmail-1.05-ucspitls-0.3.patch --- netqmail-1.06/netqmail-1.05-ucspitls-0.3.patch 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/netqmail-1.05-ucspitls-0.3.patch 2005-11-11 01:30:12.000000000 +0100 @@ -0,0 +1,231 @@ +diff -Nur netqmail-1.05/netqmail-1.05/Makefile netqmail-1.05-tls/netqmail-1.05/Makefile +--- netqmail-1.05/netqmail-1.05/Makefile 2005-09-10 23:27:00.582146440 -0400 ++++ netqmail-1.05-tls/netqmail-1.05/Makefile 2005-09-10 01:58:49.000000000 -0400 +@@ -1288,9 +1288,11 @@ + + qmail-popup: \ + load qmail-popup.o commands.o timeoutread.o timeoutwrite.o now.o \ ++envread.o ucspitls.o \ + case.a fd.a sig.a wait.a stralloc.a alloc.a substdio.a error.a str.a \ + fs.a socket.lib + ./load qmail-popup commands.o timeoutread.o timeoutwrite.o \ ++ envread.o ucspitls.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` + +@@ -1534,11 +1536,13 @@ + 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 \ ++ucspitls.o \ + date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ + open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ + fs.a auto_qmail.o socket.lib + ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ ++ ucspitls.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 \ +diff -Nur netqmail-1.05/netqmail-1.05/qmail-popup.c netqmail-1.05-tls/netqmail-1.05/qmail-popup.c +--- netqmail-1.05/netqmail-1.05/qmail-popup.c 2005-09-10 23:27:00.590145224 -0400 ++++ netqmail-1.05-tls/netqmail-1.05/qmail-popup.c 2005-09-10 23:18:50.653626896 -0400 +@@ -13,6 +13,8 @@ + #include "readwrite.h" + #include "timeoutread.h" + #include "timeoutwrite.h" ++#include "env.h" ++#include "ucspitls.h" + + void die() { _exit(1); } + +@@ -61,6 +63,7 @@ + void die_fork() { err("unable to fork"); die(); } + void die_childcrashed() { err("aack, child crashed"); } + void die_badauth() { err("authorization failed"); } ++void die_tls() { err("TLS startup failed"); die(); } + + void err_syntax() { err("syntax error"); } + void err_wantuser() { err("USER first"); } +@@ -77,6 +80,8 @@ + char **childargs; + substdio ssup; + char upbuf[128]; ++int tls_available = 0; ++int tls_started = 0; + + + void doanddie(user,userlen,pass) +@@ -155,12 +160,36 @@ + *space++ = 0; + doanddie(arg,space - arg,space); + } ++void pop3_capa(arg) char *arg; ++{ ++ puts("+OK capability list follows\r\n"); ++ if (tls_available && !tls_started) ++ puts("STLS\r\n"); ++ puts(".\r\n"); ++ flush(); ++} ++void pop3_stls(arg) char *arg; ++{ ++ if (!tls_available || tls_started) ++ return err("STLS not available"); ++ puts("+OK starting TLS negotiation\r\n"); ++ flush(); ++ ++ if (!ucspitls()) ++ die_tls(); ++ ++ tls_started = 1; ++ /* reset state */ ++ seenuser = 0; ++} + + struct commands pop3commands[] = { + { "user", pop3_user, 0 } + , { "pass", pop3_pass, 0 } + , { "apop", pop3_apop, 0 } + , { "quit", pop3_quit, 0 } ++, { "capa", pop3_capa, 0 } ++, { "stls", pop3_stls, 0 } + , { "noop", okay, 0 } + , { 0, err_authoriz, 0 } + } ; +@@ -177,6 +206,8 @@ + childargs = argv + 2; + if (!*childargs) die_usage(); + ++ tls_available = !!env_get("UCSPITLS"); ++ + pop3_greet(); + commands(&ssin,pop3commands); + die(); +diff -Nur netqmail-1.05/netqmail-1.05/qmail-smtpd.c netqmail-1.05-tls/netqmail-1.05/qmail-smtpd.c +--- netqmail-1.05/netqmail-1.05/qmail-smtpd.c 2005-09-10 23:27:00.609142336 -0400 ++++ netqmail-1.05-tls/netqmail-1.05/qmail-smtpd.c 2005-09-10 01:45:38.000000000 -0400 +@@ -23,10 +23,13 @@ + #include "timeoutread.h" + #include "timeoutwrite.h" + #include "commands.h" ++#include "ucspitls.h" + + #define MAXHOPS 100 + unsigned int databytes = 0; + int timeout = 1200; ++int tls_available = 0; ++int tls_started = 0; + + int safewrite(fd,buf,len) int fd; char *buf; int len; + { +@@ -48,6 +51,7 @@ + void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } + void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } + void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } ++void die_syserr() { out("421 system error (#4.3.0)\r\n"); flush(); _exit(1); } + + void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } + void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +@@ -229,7 +233,10 @@ + } + void smtp_ehlo(arg) char *arg; + { +- smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); ++ smtp_greet("250-"); ++ if (tls_available && !tls_started) ++ out("\r\n250-STARTTLS"); ++ out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); + } + void smtp_rset(arg) char *arg; +@@ -263,7 +270,22 @@ + if (!stralloc_0(&rcptto)) die_nomem(); + out("250 ok\r\n"); + } ++void smtp_starttls(arg) char *arg; { ++ unsigned long long_fd; ++ int fd; ++ char *fdstr; ++ if (!tls_available || tls_started) ++ return err_unimpl(arg); ++ out("220 2.0.0 Ready to start TLS\r\n"); ++ flush(); ++ ++ if (!ucspitls()) ++ die_syserr(); + ++ tls_started = 1; ++ /* reset SMTP state */ ++ seenmail = 0; ++} + + int saferead(fd,buf,len) int fd; char *buf; int len; + { +@@ -405,6 +427,7 @@ + , { "help", smtp_help, flush } + , { "noop", err_noop, flush } + , { "vrfy", err_vrfy, flush } ++, { "starttls", smtp_starttls, flush } + , { 0, err_unimpl, flush } + } ; + +@@ -414,6 +437,7 @@ + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); ++ tls_available = !!env_get("UCSPITLS"); + smtp_greet("220 "); + out(" ESMTP\r\n"); + if (commands(&ssin,&smtpcommands) == 0) die_read(); +diff -Nur netqmail-1.05/netqmail-1.05/TARGETS netqmail-1.05-tls/netqmail-1.05/TARGETS +--- netqmail-1.05/netqmail-1.05/TARGETS 1998-06-15 06:53:16.000000000 -0400 ++++ netqmail-1.05-tls/netqmail-1.05/TARGETS 2005-09-10 02:01:47.000000000 -0400 +@@ -286,6 +286,7 @@ + forward + preline.o + preline ++ucspitls.o + condredirect.o + condredirect + bouncesaying.o +diff -Nur netqmail-1.05/netqmail-1.05/ucspitls.c netqmail-1.05-tls/netqmail-1.05/ucspitls.c +--- netqmail-1.05/netqmail-1.05/ucspitls.c 1969-12-31 19:00:00.000000000 -0500 ++++ netqmail-1.05-tls/netqmail-1.05/ucspitls.c 2005-09-10 01:18:39.000000000 -0400 +@@ -0,0 +1,31 @@ ++#include "scan.h" ++#include "env.h" ++ ++int ucspitls(void) ++{ ++ unsigned long fd; ++ char *fdstr; ++ ++ if (!(fdstr=env_get("SSLCTLFD"))) ++ return 0; ++ if (!scan_ulong(fdstr,&fd)) ++ return 0; ++ if (write((int)fd, "y", 1) < 1) ++ return 0; ++ ++ if (!(fdstr=env_get("SSLREADFD"))) ++ return 0; ++ if (!scan_ulong(fdstr,&fd)) ++ return 0; ++ if (dup2((int)fd,0) == -1) ++ return 0; ++ ++ if (!(fdstr=env_get("SSLWRITEFD"))) ++ return 0; ++ if (!scan_ulong(fdstr,&fd)) ++ return 0; ++ if (dup2((int)fd,1) == -1) ++ return 0; ++ ++ return 1; ++} +diff -Nur netqmail-1.05/netqmail-1.05/ucspitls.h netqmail-1.05-tls/netqmail-1.05/ucspitls.h +--- netqmail-1.05/netqmail-1.05/ucspitls.h 1969-12-31 19:00:00.000000000 -0500 ++++ netqmail-1.05-tls/netqmail-1.05/ucspitls.h 2005-09-10 01:09:23.000000000 -0400 +@@ -0,0 +1 @@ ++int ucspitls(void); diff -Nur netqmail-1.06/qmail-popup.c netqmail-1.06.systemadmin/qmail-popup.c --- netqmail-1.06/qmail-popup.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06.systemadmin/qmail-popup.c 2009-02-18 20:51:50.000000000 +0100 @@ -13,6 +13,8 @@ #include "readwrite.h" #include "timeoutread.h" #include "timeoutwrite.h" +#include "env.h" +#include "ucspitls.h" void die() { _exit(1); } @@ -61,6 +63,7 @@ void die_fork() { err("unable to fork"); die(); } void die_childcrashed() { err("aack, child crashed"); } void die_badauth() { err("authorization failed"); } +void die_tls() { err("TLS startup failed"); die(); } void err_syntax() { err("syntax error"); } void err_wantuser() { err("USER first"); } @@ -78,6 +81,9 @@ substdio ssup; char upbuf[128]; +int tls_available = 0; +int tls_started = 0; + void doanddie(user,userlen,pass) char *user; @@ -156,11 +162,38 @@ doanddie(arg,space - arg,space); } +void pop3_capa(arg) char *arg; +{ + puts("+OK capability list follows\r\n"); + if (tls_available && !tls_started) + puts("STLS\r\n"); + puts(".\r\n"); + flush(); +} + +void pop3_stls(arg) char *arg; +{ + if (!tls_available || tls_started) + return err("STLS not available"); + puts("+OK starting TLS negotiation\r\n"); + flush(); + + if (!ucspitls()) + die_tls(); + + tls_started = 1; + /* reset state */ + seenuser = 0; +} + + struct commands pop3commands[] = { { "user", pop3_user, 0 } , { "pass", pop3_pass, 0 } , { "apop", pop3_apop, 0 } , { "quit", pop3_quit, 0 } +, { "capa", pop3_capa, 0 } +, { "stls", pop3_stls, 0 } , { "noop", okay, 0 } , { 0, err_authoriz, 0 } } ; @@ -176,6 +209,8 @@ if (!hostname) die_usage(); childargs = argv + 2; if (!*childargs) die_usage(); + + tls_available = !!env_get("UCSPITLS"); pop3_greet(); commands(&ssin,pop3commands); diff -Nur netqmail-1.06/qmail-smtpd.8 netqmail-1.06.systemadmin/qmail-smtpd.8 --- netqmail-1.06/qmail-smtpd.8 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06.systemadmin/qmail-smtpd.8 2008-09-14 20:36:58.000000000 +0200 @@ -3,6 +3,11 @@ qmail-smtpd \- receive mail via SMTP .SH SYNOPSIS .B qmail-smtpd +[ +.I hostname +.I checkprogram +.I subprogram +] .SH DESCRIPTION .B qmail-smtpd receives mail messages via the Simple Mail Transfer Protocol (SMTP) @@ -23,7 +28,29 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options. + +.B qmail-smtpd +can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes +.IR checkprogram , +which reads on file descriptor 3 the username, a 0 byte, the password +or challenge derived from +.IR hostname , +another 0 byte, a CRAM-MD5 response (if applicable to the AUTH type), +and a final 0 byte. +.I checkprogram +invokes +.I subprogram +upon successful authentication, which should in turn return 0 to +.BR qmail-smtpd , +effectively setting the environment variables RELAYCLIENT and TCPREMOTEINFO +(any supplied value replaced with the authenticated username). +.B qmail-smtpd +will reject the authentication attempt if it receives a nonzero return +value from +.I checkprogram +or +.IR subprogram . .SH TRANSPARENCY .B qmail-smtpd converts the SMTP newline convention into the UNIX newline convention @@ -177,3 +204,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 -Nur netqmail-1.06/qmail-smtpd.c netqmail-1.06.systemadmin/qmail-smtpd.c --- netqmail-1.06/qmail-smtpd.c 2007-11-30 21:22:54.000000000 +0100 +++ netqmail-1.06.systemadmin/qmail-smtpd.c 2009-02-18 22:31:58.000000000 +0100 @@ -23,10 +23,21 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "wait.h" +#include "fd.h" +#include "ucspitls.h" + +/* start chkuser code */ +#include "chkuser.h" +/* end chkuser code */ +#define AUTHCRAM #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; +int tls_available = 0; +int tls_started = 0; + int safewrite(fd,buf,len) int fd; char *buf; int len; { @@ -48,6 +59,7 @@ void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } +void die_syserr() { out("421 system error (#4.3.0)\r\n"); flush(); _exit(1); } void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } @@ -59,6 +71,15 @@ void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } +int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } stralloc greeting = {0}; @@ -229,17 +250,51 @@ } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + smtp_greet("250-"); + if (tls_available && !tls_started) + out("\r\n250-STARTTLS"); +#ifdef AUTHCRAM + out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN"); + out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN"); +#else + out("\r\n250-AUTH LOGIN PLAIN"); + out("\r\n250-AUTH=LOGIN PLAIN"); +#endif + out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg); } + void smtp_rset(arg) char *arg; { seenmail = 0; out("250 flushed\r\n"); } + +void smtp_starttls(arg) char *arg; { + unsigned long long_fd; + int fd; + char *fdstr; + if (!tls_available || tls_started) + return err_unimpl(arg); + out("220 2.0.0 Ready to start TLS\r\n"); + flush(); + + if (!ucspitls()) + die_syserr(); + + tls_started = 1; + /* reset SMTP state */ + seenmail = 0; +} + 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(); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); @@ -251,6 +306,22 @@ if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } if (flagbarf) { err_bmf(); 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 */ + + +/* cosa sense chkuser if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); @@ -258,6 +329,9 @@ } else if (!addrallowed()) { err_nogateway(); return; } + + end cosa chkuser */ + if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -394,10 +468,226 @@ out("\r\n"); } + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; +static stralloc user = {0}; +static stralloc pass = {0}; +static stralloc resp = {0}; +static stralloc slop = {0}; +char *hostname; +char **childargs; +substdio ssup; +char upbuf[128]; +int authd = 0; + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin, "")) die_nomem(); + + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&resp)) die_nomem(); + + if (fd_copy(2,1) == -1) return err_pipe(); + close(3); + if (pipe(pi) == -1) return err_pipe(); + if (pi[0] != 3) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); + if (substdio_put(&ssup,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write(); + if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write(); + if (substdio_flush(&ssup) == -1) return err_write(); + + close(pi[1]); + byte_zero(pass.s,pass.len); + byte_zero(upbuf,sizeof upbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + while (slop.s[id]) id++; /* ignore authorize-id */ + + if (slop.len > id + 1) + if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem(); + if (slop.len > id + user.len + 2) + if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +#ifdef AUTHCRAM +int auth_cram() +{ + int i, r; + char *s; + + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!stralloc_copys(&pass,"<")) die_nomem(); + if (!stralloc_cats(&pass,unique)) die_nomem(); + if (!stralloc_cats(&pass,hostname)) die_nomem(); + if (!stralloc_cats(&pass,">")) die_nomem(); + if (b64encode(&pass,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input(); + if (r == -1 || !stralloc_0(&slop)) die_nomem(); + + i = str_chr(slop.s,' '); + s = slop.s + i; + while (*s == ' ') ++s; + slop.s[i] = 0; + if (!stralloc_copys(&user,slop.s)) die_nomem(); + if (!stralloc_copys(&resp,s)) die_nomem(); + + if (!user.len || !resp.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login", auth_login } +, { "plain", auth_plain } +#ifdef AUTHCRAM +, { "cram-md5", auth_cram } +#endif +, { 0, err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!hostname || !*childargs) + { + out("503 auth not available (#5.3.3)\r\n"); + return; + } + if (authd) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + authd = 1; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + out("535 authorization failed (#5.7.0)\r\n"); + } +} + 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 } @@ -405,15 +695,22 @@ , { "help", smtp_help, flush } , { "noop", err_noop, flush } , { "vrfy", err_vrfy, flush } +, { "starttls", smtp_starttls, flush } , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) +int argc; +char **argv; { + hostname = argv[1]; + childargs = argv + 2; + sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); if (ipme_init() != 1) die_ipme(); + tls_available = !!env_get("UCSPITLS"); smtp_greet("220 "); out(" ESMTP\r\n"); if (commands(&ssin,&smtpcommands) == 0) die_read(); diff -Nur netqmail-1.06/qmail-smtpd.c.orig netqmail-1.06.systemadmin/qmail-smtpd.c.orig --- netqmail-1.06/qmail-smtpd.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/qmail-smtpd.c.orig 2007-11-30 21:22:54.000000000 +0100 @@ -0,0 +1,421 @@ +#include "sig.h" +#include "readwrite.h" +#include "stralloc.h" +#include "substdio.h" +#include "alloc.h" +#include "auto_qmail.h" +#include "control.h" +#include "received.h" +#include "constmap.h" +#include "error.h" +#include "ipme.h" +#include "ip.h" +#include "qmail.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "case.h" +#include "env.h" +#include "now.h" +#include "exit.h" +#include "rcpthosts.h" +#include "timeoutread.h" +#include "timeoutwrite.h" +#include "commands.h" + +#define MAXHOPS 100 +unsigned int databytes = 0; +int timeout = 1200; + +int safewrite(fd,buf,len) int fd; char *buf; int len; +{ + int r; + r = timeoutwrite(timeout,fd,buf,len); + if (r <= 0) _exit(1); + return r; +} + +char ssoutbuf[512]; +substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf); + +void flush() { substdio_flush(&ssout); } +void out(s) char *s; { substdio_puts(&ssout,s); } + +void die_read() { _exit(1); } +void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } +void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } +void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } +void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } +void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } + +void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } +void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); } +void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } +void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } +void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } +void err_noop(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"); } + + +stralloc greeting = {0}; + +void smtp_greet(code) char *code; +{ + substdio_puts(&ssout,code); + substdio_put(&ssout,greeting.s,greeting.len); +} +void smtp_help(arg) char *arg; +{ + out("214 netqmail home page: http://qmail.org/netqmail\r\n"); +} +void smtp_quit(arg) char *arg; +{ + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); +} + +char *remoteip; +char *remotehost; +char *remoteinfo; +char *local; +char *relayclient; + +stralloc helohost = {0}; +char *fakehelo; /* pointer into helohost, or 0 */ + +void dohelo(arg) char *arg; { + if (!stralloc_copys(&helohost,arg)) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; +} + +int liphostok = 0; +stralloc liphost = {0}; +int bmfok = 0; +stralloc bmf = {0}; +struct constmap mapbmf; + +void setup() +{ + char *x; + unsigned long u; + + if (control_init() == -1) die_control(); + if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) + die_control(); + liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0); + if (liphostok == -1) die_control(); + if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); + if (bmfok) + if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + if (control_readint(&databytes,"control/databytes") == -1) die_control(); + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + + remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); + dohelo(remotehost); +} + + +stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ + +int addrparse(arg) +char *arg; +{ + int i; + char ch; + char terminator; + struct ip_address ip; + int flagesc; + int flagquoted; + + terminator = '>'; + i = str_chr(arg,'<'); + if (arg[i]) + arg += i + 1; + else { /* partner should go read rfc 821 */ + terminator = ' '; + arg += str_chr(arg,':'); + if (*arg == ':') ++arg; + while (*arg == ' ') ++arg; + } + + /* strip source route */ + if (*arg == '@') while (*arg) if (*arg++ == ':') break; + + if (!stralloc_copys(&addr,"")) die_nomem(); + flagesc = 0; + flagquoted = 0; + for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */ + if (flagesc) { + if (!stralloc_append(&addr,&ch)) die_nomem(); + flagesc = 0; + } + else { + if (!flagquoted && (ch == terminator)) break; + switch(ch) { + case '\\': flagesc = 1; break; + case '"': flagquoted = !flagquoted; break; + default: if (!stralloc_append(&addr,&ch)) die_nomem(); + } + } + } + /* could check for termination failure here, but why bother? */ + if (!stralloc_append(&addr,"")) die_nomem(); + + if (liphostok) { + i = byte_rchr(addr.s,addr.len,'@'); + if (i < addr.len) /* if not, partner should go read rfc 821 */ + if (addr.s[i + 1] == '[') + if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)]) + if (ipme_is(&ip)) { + addr.len = i + 1; + if (!stralloc_cat(&addr,&liphost)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } + } + + if (addr.len > 900) return 0; + return 1; +} + +int bmfcheck() +{ + 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; + return 0; +} + +int addrallowed() +{ + int r; + r = rcpthosts(addr.s,str_len(addr.s)); + if (r == -1) die_control(); + return r; +} + + +int seenmail = 0; +int flagbarf; /* defined if seenmail */ +stralloc mailfrom = {0}; +stralloc rcptto = {0}; + +void smtp_helo(arg) char *arg; +{ + smtp_greet("250 "); out("\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_ehlo(arg) char *arg; +{ + smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + seenmail = 0; dohelo(arg); +} +void smtp_rset(arg) char *arg; +{ + seenmail = 0; + out("250 flushed\r\n"); +} +void smtp_mail(arg) char *arg; +{ + if (!addrparse(arg)) { err_syntax(); return; } + flagbarf = bmfcheck(); + seenmail = 1; + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); + if (!stralloc_0(&mailfrom)) die_nomem(); + out("250 ok\r\n"); +} +void smtp_rcpt(arg) char *arg; { + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + if (flagbarf) { err_bmf(); return; } + if (relayclient) { + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } + else + if (!addrallowed()) { err_nogateway(); return; } + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + out("250 ok\r\n"); +} + + +int saferead(fd,buf,len) int fd; char *buf; int len; +{ + int r; + flush(); + r = timeoutread(timeout,fd,buf,len); + if (r == -1) if (errno == error_timeout) die_alarm(); + if (r <= 0) die_read(); + return r; +} + +char ssinbuf[1024]; +substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf); + +struct qmail qqt; +unsigned int bytestooverflow = 0; + +void put(ch) +char *ch; +{ + if (bytestooverflow) + if (!--bytestooverflow) + qmail_fail(&qqt); + qmail_put(&qqt,ch,1); +} + +void blast(hops) +int *hops; +{ + char ch; + int state; + int flaginheader; + int pos; /* number of bytes since most recent \n, if fih */ + int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ + int flagmaybey; /* 1 if this line might match \r\n, if fih */ + int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ + + state = 1; + *hops = 0; + flaginheader = 1; + pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; + for (;;) { + substdio_get(&ssin,&ch,1); + if (flaginheader) { + if (pos < 9) { + if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; + if (flagmaybez) if (pos == 8) ++*hops; + if (pos < 8) + if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; + if (flagmaybex) if (pos == 7) ++*hops; + if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; + if (flagmaybey) if (pos == 1) flaginheader = 0; + ++pos; + } + if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } + } + switch(state) { + case 0: + if (ch == '\n') straynewline(); + if (ch == '\r') { state = 4; continue; } + break; + case 1: /* \r\n */ + if (ch == '\n') straynewline(); + if (ch == '.') { state = 2; continue; } + if (ch == '\r') { state = 4; continue; } + state = 0; + break; + case 2: /* \r\n + . */ + if (ch == '\n') straynewline(); + if (ch == '\r') { state = 3; continue; } + state = 0; + break; + case 3: /* \r\n + .\r */ + if (ch == '\n') return; + put("."); + put("\r"); + if (ch == '\r') { state = 4; continue; } + state = 0; + break; + case 4: /* + \r */ + if (ch == '\n') { state = 1; break; } + if (ch != '\r') { put("\r"); state = 0; } + } + put(&ch); + } +} + +char accept_buf[FMT_ULONG]; +void acceptmessage(qp) unsigned long qp; +{ + datetime_sec when; + when = now(); + out("250 ok "); + accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; + out(accept_buf); + out(" qp "); + accept_buf[fmt_ulong(accept_buf,qp)] = 0; + out(accept_buf); + out("\r\n"); +} + +void smtp_data(arg) char *arg; { + int hops; + unsigned long qp; + char *qqx; + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + 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); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); + qmail_from(&qqt,mailfrom.s); + qmail_put(&qqt,rcptto.s,rcptto.len); + + qqx = qmail_close(&qqt); + if (!*qqx) { acceptmessage(qp); return; } + if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } + if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } + if (*qqx == 'D') out("554 "); else out("451 "); + out(qqx + 1); + out("\r\n"); +} + +struct commands smtpcommands[] = { + { "rcpt", smtp_rcpt, 0 } +, { "mail", smtp_mail, 0 } +, { "data", smtp_data, flush } +, { "quit", smtp_quit, flush } +, { "helo", smtp_helo, flush } +, { "ehlo", smtp_ehlo, flush } +, { "rset", smtp_rset, 0 } +, { "help", smtp_help, flush } +, { "noop", err_noop, flush } +, { "vrfy", err_vrfy, flush } +, { 0, err_unimpl, flush } +} ; + +void main() +{ + sig_pipeignore(); + if (chdir(auto_qmail) == -1) die_control(); + setup(); + if (ipme_init() != 1) die_ipme(); + smtp_greet("220 "); + out(" ESMTP\r\n"); + if (commands(&ssin,&smtpcommands) == 0) die_read(); + die_nomem(); +} diff -Nur netqmail-1.06/README.auth netqmail-1.06.systemadmin/README.auth --- netqmail-1.06/README.auth 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/README.auth 2008-09-14 20:36:39.000000000 +0200 @@ -0,0 +1,175 @@ +*** Warning! Cuidado! Vorsicht! *** +=================================== +*** Version 0.30 of the patch changes the arguments which must be +*** passed to qmail-smtpd. If you are upgrading from a previous +*** version of the patch, take care to ensure your invocation of +*** qmail-smtpd uses the correct arguments. Otherwise, your server +*** may run as an open relay! +=================================== +*** Warning! Cuidado! Vorsicht! *** + + +This patch adds ESMTP AUTH authentication protocol support to +qmail-1.03. It's originally based on Mrs. Brisby's smtp-auth patch +with many enhancements from Krzysztof Dabrowski . + +Beginning with version 0.30, the patch was completely rewritten to +use only djb's string functions by Eric M. Johnston . + +You can always get the newest version from: +http://members.elysium.pl/brush/qmail-smtpd-auth/ + +To use all of it's functionality you will also have to obtain and +install Krzysztof's cmd5checkpw utility available at: +http://members.elysium.pl/brush/cmd5checkpw/ + +If you need more information about SMTP-AUTH itself and the +client/server support and configuration, visit: +http://members.elysium.pl/brush/smtp-auth/ + +--- + +Detailed patch information: + +This patch adds the ESMTP AUTH option to qmail-1.03, allowing the +LOGIN, PLAIN, and CRAM-MD5 AUTH types. An appropriate checkpassword +tool is necessary to support the authentication. See +http://cr.yp.to/checkpwd.html for more information on the interface. +Note that the checkpassword tool should support all of the AUTH types +advertised by qmail-smtpd. + +As reflected in the modified qmail-smtpd(8) man page, qmail-smtpd +must be invoked with three arguments: hostname, checkprogram, and +subprogram. If these arguments are missing, qmail-smtpd will still +advertise availability of AUTH, but will fail with a permanent error +when AUTH is used. + +hostname is simply used to form the CRAM-MD5 challenge. qmail-smtpd +invokes checkprogram, feeding it the username and password, in the +case of LOGIN or PLAIN, or the username, challenge, and response, in +the case of CRAM-MD5. If the user is permitted, checkprogram invokes +subprogram, which just has to exit with a status of 0 for the user to +be authenticated. Otherwise, checkprogram exits with a non-zero +status. subprogram can usually be /usr/bin/true (or /bin/true, +depending on your flavor of OS). + +If the user is successfully authenticated, the RELAYCLIENT +environment variable is effectively set for the SMTP session, and +the TCPREMOTEINFO environment variable is set to the authenticated +username, overriding any value that tcpserver may have set. The +value of TCPREMOTEINFO is reflected in a Received header. + + +How to install it: + +Simply patch your qmail-1.03 distribution with the included patch +file and recompile & install like usual. + +The steps to do this are as follows (assuming your virgin +qmail-1.03 install is in "../qmail-1.03"): + + cp README.auth base64.c base64.h ../qmail-1.03 + patch -d ../qmail-1.03 < auth.patch + +Install qmail normally, with the exception of the new arguments +to qmail-smtpd described elsewhere in this file. + +Also obtain, unpack, compile and install the cmd5checkpw utility +(or some other checkpassword utility) and add a sample account to +/etc/poppasswd file. This file must be readable by the qmail-smtpd +user, usually qmaild. + + +How to use it: + +*** Warning: In version 0.30 the arguments have changed from +*** previous versions of qmail-smtpd-auth. Take care to make sure +*** you update your startup scripts if updating! + +If you're running qmail-smtpd from inetd, you'll want to do the +following: + +smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env tcp-env \ +/var/qmail/bin/qmail-smtpd mail.acme.com /bin/cmd5checkpw /bin/true + +Replace mail.acme.com with your hostname. The second argument to +qmail-smtpd is your checkpassword utility (preferably cmd5checkpw +or some alternative that can handle CRAM-MD5). The third argument +is the executable that the checkpassword utility execs when +authentication is successful. (Note that the location of "true" +is OS dependent: you may need /usr/bin/true.) + +Invocations using tcpserver will require analagous changes. Give +your inetd a kill -HUP or restart tcpserver and away you go. + + +Caveats: + +Please note that as authentication needs vary wildly across +installations, no effort has been made to make this patch work ``out +of the box.'' You'll have to procure or develop your own +checkpassword program. Also note that CRAM-MD5 will require you to +keep plaintext passwords. You'll probably want to disable this AUTH +type if you're just using /etc/passwd (keeping in mind that PLAIN and +LOGIN aren't quite as safe over the wire) -- just undefine AUTHCRAM +in qmail-smtpd. + +Krzysztof Dabrowski's cmd5checkpw tool used as an example in this +document supports the three AUTH types included in this patch. +It's available at http://www.elysium.pl/members/brush/cmd5checkpw/. + +This patch has been generated against the stock qmail 1.03 +distribution. The results of combining this patch with others are +unknown. + + +Features: + +This patch supports the following auth methods: LOGIN, PLAIN and +CRAM-MD5. + + +Compatibility: + +The following MUA's are confirmed to work with this patch: + +Eudora 4.2.2 - CRAM-MD5 +Eudora 5.0.2 - CRAM-MD5 +The Bat 1.39 - LOGIN & CRAM-MD5 +Outlook Express 4 - LOGIN +Outlook Express 5 - LOGIN +Outlook 2000 - LOGIN +Netscape 4.x - LOGIN & PLAIN +Netscape 4.0x - LOGIN +Pegasus Mail 3.1x - CRAM-MD5 + + +Various compatibility issues: + +Testing with Pegasus Mail 3.1 revealed that it requires the new style +(RFC recommended) greeting message. Both styles are now enabled to +maintain the highest degree of compatibility with various clients. +This fix was suggested by David Harris , +the developer of Pegasus Mail. + + +Acknowledgments: + +This patch is based on work by Krzysztof Dabrowski at +http://members.elysium.pl/brush/qmail-smtpd-auth/ and ``Mrs. Brisby'' +at http://www.nimh.org/hacks/qmail-smtpd.c which has been further +developed by Eric M. Johnston . + +--- + +THIS SOFTWARE IS IN THE PUBLIC DOMAIN, IS PROVIDED BY THE AUTHOR +``AS IS,'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -Nur netqmail-1.06/TARGETS netqmail-1.06.systemadmin/TARGETS --- netqmail-1.06/TARGETS 1998-06-15 12:53:16.000000000 +0200 +++ netqmail-1.06.systemadmin/TARGETS 2009-02-18 20:42:17.000000000 +0100 @@ -250,6 +250,7 @@ qmail-qmtpd.o rcpthosts.o qmail-qmtpd +base64.o qmail-smtpd.o qmail-smtpd sendmail.o @@ -286,6 +287,7 @@ forward preline.o preline +ucspitls.o condredirect.o condredirect bouncesaying.o @@ -385,3 +387,5 @@ man setup check +chkuser.o + diff -Nur netqmail-1.06/ucspitls.c netqmail-1.06.systemadmin/ucspitls.c --- netqmail-1.06/ucspitls.c 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/ucspitls.c 2009-02-18 20:41:45.000000000 +0100 @@ -0,0 +1,32 @@ +#include "scan.h" +#include "env.h" + +int ucspitls(void) +{ + unsigned long fd; + char *fdstr; + + if (!(fdstr=env_get("SSLCTLFD"))) + return 0; + if (!scan_ulong(fdstr,&fd)) + return 0; + if (write((int)fd, "y", 1) < 1) + return 0; + + if (!(fdstr=env_get("SSLREADFD"))) + return 0; + if (!scan_ulong(fdstr,&fd)) + return 0; + if (dup2((int)fd,0) == -1) + return 0; + + if (!(fdstr=env_get("SSLWRITEFD"))) + return 0; + if (!scan_ulong(fdstr,&fd)) + return 0; + if (dup2((int)fd,1) == -1) + return 0; + + return 1; +} + diff -Nur netqmail-1.06/ucspitls.h netqmail-1.06.systemadmin/ucspitls.h --- netqmail-1.06/ucspitls.h 1970-01-01 01:00:00.000000000 +0100 +++ netqmail-1.06.systemadmin/ucspitls.h 2009-02-18 20:40:24.000000000 +0100 @@ -0,0 +1,2 @@ +int ucspitls(void); +