qmail patchset -------------- Robert Nowotniak czw 24 sie 2006 21:02:16 CEST dla qmaila-1.03 z debiana (qmail-src) qmail-smtpd-auth-0.5.7 (wyłączone CRAM-MD5) qmail-queue-custom-error z simscana chkuser 2.0 + drobne poprawki diff -urN qmail-1.03-debian/FILES.auth qmail-1.03/FILES.auth --- qmail-1.03-debian/FILES.auth 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/FILES.auth 2006-08-24 13:18:16.000000000 +0200 @@ -0,0 +1,18 @@ +The qmail-smtpd Auth patch modifies the following QMAIL 1.03 files: + += TARGETS += Makefile += qmail-smtpd.c += qmail-smtpd.8 + +Added files: + ++ base64.c ++ base64.h ++ case_startb.c + +Informational files: + +% install_auth.sh (Installation shell script) +% README.auth +% README.auth.old (old description of SMTP Auth) diff -urN qmail-1.03-debian/Makefile qmail-1.03/Makefile --- qmail-1.03-debian/Makefile 2006-08-24 13:11:23.000000000 +0200 +++ qmail-1.03/Makefile 2006-08-24 13:40:41.000000000 +0200 @@ -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,24 +1548,25 @@ 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 socket.lib - ./load qmail-smtpd qregex.o rcpthosts.o commands.o timeoutread.o \ +fs.a auto_qmail.o base64.o socket.lib $(SMTPD_CHKUSER_OBJ) + ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) qregex.o rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ 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 `cat \ - socket.lib` + alloc.a strerr.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 \ -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 -urN qmail-1.03-debian/README.auth qmail-1.03/README.auth --- qmail-1.03-debian/README.auth 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/README.auth 2006-08-24 13:18:16.000000000 +0200 @@ -0,0 +1,66 @@ +README qmail-smtpd SMTP Authentication +====================================== + + +History: +-------- + +This patch is based on Krzysztof Dabrowski's qmail-smtpd-auth-0.31 patch +which itself uses "Mrs. Brisby's" initial code. +Version 0.41 of this patch fixes the "CAPS-LOCK" typo announcing +'CRAM_MD5' instead of 'CRAM-MD5' (german keyboard) - tx to Mike Garrison. +Version 0.42 fixes the '421 unable to read controls (#4.3.0)' problem +(can't read control/morercpthosts.cdb) because FD 3 was already closed - tx Richard Lyons. +Version 0.43 fixes the ba64decode() failure in case CRAM_MD5 is not enabled - tx Vladimir Zidar. +Version 0.51 includes the evaluation of the 'Auth' and the 'Size' parameter in the 'Mail From:' command. +Version 0.52 uses DJB functions to copy FDs. +Version 0.56 corrects some minor mistakes displaying the 'Auth' userid. +Version 0.57 uses keyword "ESMTPA" in Received header in case of authentication to comply with RFC 3848. + + +Scope: +------ + +This patch supports RFC 2554 "SMTP Service Extension for Authentication" for qmail-smtpd. +Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration"). +For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html. + + +Installation: +------------- + +* Untar the source in the qmail-1.03 home direcotry. +* Run ./install_auth. +* Modify the compile time option "#define CRAM_MD5" to your needs. +* Re-make qmail. + + +Setup: +------ + +In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module' +PAM to be called by qmail-smtpd; typically + + /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1 + +Since qmail-smtpd does not run as root, checkpassword has to be made sticky. +There is no need to include additionally the hostname in the call. +In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information. + + +Changes wrt. Krysztof Dabrowski's patch: +---------------------------------------- + +* Avoid the 'hostname' in the call of the PAM. +* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5. +* Doesn't close FD 2; thus not inhibiting logging to STDERR. +* Fixed bugs in base64.c. +* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'. +* Evaluation of the (informational) Mail From: < > Auth=username. +* Additional support for the advertised "Size" via 'Mail From: SIZE=123456780' (RFC 1870). +* RFC 3848 conformance for Received header in case of SMTP Auth. + + +Erwin Hoffmann - Cologne 2005-01-23 (www.fehcom.de) + + diff -urN qmail-1.03-debian/TARGETS qmail-1.03/TARGETS --- qmail-1.03-debian/TARGETS 2006-08-24 13:11:23.000000000 +0200 +++ qmail-1.03/TARGETS 2006-08-24 13:39:29.000000000 +0200 @@ -10,6 +10,7 @@ qmail.o quote.o now.o +base64.o gfrom.o myctime.o slurpclose.o @@ -386,3 +387,4 @@ man setup check +chkuser.o diff -urN qmail-1.03-debian/base64.c qmail-1.03/base64.c --- qmail-1.03-debian/base64.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/base64.c 2006-08-24 13:18:16.000000000 +0200 @@ -0,0 +1,122 @@ +#include "base64.h" +#include "stralloc.h" +#include "substdio.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(in,l,out) +const unsigned char *in; +int l; +stralloc *out; /* not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while(in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + out->len = (n * 3) - p; + if (!stralloc_ready(out,out->len)) return -1; + s = out->s; + + for(i = 0; i < n - 1 ; i++) { + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for(j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for(i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(in,out) +stralloc *in; +stralloc *out; /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1; + s = out->s; + + for (i = 0;i < in->len;i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + return 0; +} diff -urN qmail-1.03-debian/base64.h qmail-1.03/base64.h --- qmail-1.03-debian/base64.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/base64.h 2006-08-24 13:18:16.000000000 +0200 @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff -urN qmail-1.03-debian/case_startb.c qmail-1.03/case_startb.c --- qmail-1.03-debian/case_startb.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/case_startb.c 2006-08-24 13:18:16.000000000 +0200 @@ -0,0 +1,21 @@ +#include "case.h" + +int case_startb(s,len,t) +register char *s; +unsigned int len; +register char *t; +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (!len) return 0; + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + if (x != y) return 0; + } +} diff -urN qmail-1.03-debian/chkuser.c qmail-1.03/chkuser.c --- qmail-1.03-debian/chkuser.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/chkuser.c 2006-08-24 13:39:29.000000000 +0200 @@ -0,0 +1,1156 @@ + +/* + * + * 'chkuser.c' v.2.0.8 + * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + * + * Author: Antonio Nati tonix@interazioni.it + * All rights on this software and + * the identifying words chkusr and chkuser kept by the author + * + * This software may be freely used, modified and distributed, + * but this lines must be kept in every original or derived version. + * Original author "Antonio Nati" and the web URL + * "http://www.interazioni.it/opensource" + * must be indicated in every related work or web page + * + */ + +#include + +/* required by vpopmail */ +#include + +#include +#include +#include + +#include "dns.h" +#include "env.h" +#include "ipme.h" +#include "now.h" +#include "open.h" +#include "subfd.h" +#include "substdio.h" +#include "stralloc.h" + +#include "vpopmail.h" +#include "vauth.h" +#include "vpopmail_config.h" + +#include "chkuser.h" +#include "chkuser_settings.h" + +#if defined _exit +#undef _exit +#endif + +extern void flush(); +extern void out (char *s); + +extern char *remotehost; +extern char *remoteip; +extern char *remoteinfo; +extern char *relayclient; +extern char *fakehelo; + +extern void die_nomem(); + +#define DIE_NOMEM() die_nomem() + +#if defined CHKUSER_DEBUG + +#if defined CHKUSER_DEBUG_STDERR + +#define CHKUSER_DBG(a) write (STDERR_FILENO, a, strlen (a)) +#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDERR_FILENO, str, strlen (str));} + +#else + +#define CHKUSER_DBG(a) write (STDOUT_FILENO, a, strlen (a)) +#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDOUT_FILENO, str, strlen (str));} + +#endif +#else + +#define CHKUSER_DBG(a) /* DBG dummy */ +#define CHKUSER_DBG_INT(a) /* DBG dummy */ + +#endif + +static int INTRUSION_threshold_reached = 0; +static int first_time_init_flag = 1; + +static int recipients = 0; +static int wrong_recipients = 0; + +static stralloc user = {0}; +static stralloc domain = {0}; +static stralloc domain_path = {0}; +static stralloc tmp_path = {0}; +static stralloc alias_path = {0}; + +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + static char *identify_remote; +#endif + +#if defined CHKUSER_ENABLE_EXTENSIONS +#define CHKUSER_ENABLE_USERS_EXTENSIONS +#endif + +#if defined CHKUSER_ENABLE_LISTS +#define CHKUSER_ENABLE_EZMLM_LISTS +#endif + +#if defined CHKUSER_EXTENSION_DASH +#define CHKUSER_USERS_DASH CHKUSER_EXTENSION_DASH +#endif + + +#if defined CHKUSER_ENABLE_VAUTH_OPEN + static int db_already_open = 0; +#endif + +#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + static char *starting_string = 0; + static int starting_value = -1; +#endif + +#if defined CHKUSER_RCPT_LIMIT_VARIABLE + static char *maxrcpt_string = 0; + static int maxrcpt_limit = 0; + static int maxrcpt_limit_reached = 0; +#endif + +#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE + static char *maxwrongrcpt_string = 0; + static int maxwrongrcpt_limit = 0; + static int maxwrongrcpt_limit_reached = 0; +#endif + +#if defined CHKUSER_MBXQUOTA_VARIABLE + static char *maxmbxquota_string = 0; + static int maxmbxquota_limit = 0; +#endif + +#if defined CHKUSER_SENDER_NOCHECK_VARIABLE + + static unsigned int sender_nocheck = 0; + +#endif + +#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX +static stralloc sender_user = {0}; +static stralloc sender_domain = {0}; +#endif + + +#if defined CHKUSER_ERROR_DELAY + + static int chkuser_delay_interval = CHKUSER_ERROR_DELAY * 1000; + +#define CHKUSER_DELAY() chkuser_delay() + +void chkuser_delay (void) { + + usleep (chkuser_delay_interval); + +#if defined CHKUSER_ERROR_DELAY_INCREASE + chkuser_delay_interval += CHKUSER_ERROR_DELAY_INCREASE * 1000; +#endif +} + +#if defined CHKUSER_RCPT_DELAY_ANYERROR +#define CHKUSER_RCPT_DELAY_ANY() chkuser_delay() +#else +#define CHKUSER_RCPT_DELAY_ANY() /* no delay for any error */ +#endif + +#if defined CHKUSER_SENDER_DELAY_ANYERROR +#define CHKUSER_SENDER_DELAY_ANY() chkuser_delay() +#else +#define CHKUSER_SENDER_DELAY_ANY() /* no delay for any error */ +#endif + + +#else +#define CHKUSER_DELAY() /* no delay */ +#define CHKUSER_RCPT_DELAY_ANY() /* no delay */ +#define CHKUSER_SENDER_DELAY_ANY() /* no delay */ +#endif + +#if defined CHKUSER_ENABLE_LOGGING + +static stralloc logstr = { 0 }; + +static void chkuser_commonlog (char *sender, char *rcpt, char *title, char *description) { + + substdio_puts (subfderr, "CHKUSER "); + substdio_puts (subfderr, title); + substdio_puts (subfderr, ": from <"); + substdio_puts (subfderr, sender); + substdio_puts (subfderr, ":" ); + if (remoteinfo) { + substdio_puts (subfderr, remoteinfo); + } + substdio_puts (subfderr, ":" ); +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + if (identify_remote) substdio_puts (subfderr, identify_remote); +#endif + substdio_puts (subfderr, "> remote <"); + if (fakehelo) substdio_puts (subfderr, fakehelo); + substdio_puts (subfderr, ":" ); + if (remotehost) substdio_puts (subfderr, remotehost); + substdio_puts (subfderr, ":" ); + if (remoteip) substdio_puts (subfderr, remoteip); + substdio_puts (subfderr, "> rcpt <"); + substdio_puts (subfderr, rcpt); + substdio_puts (subfderr, "> : "); + substdio_puts (subfderr, description); + substdio_puts (subfderr, "\n"); + substdio_flush (subfderr); +} + +#else +#define chkuser_commonlog(a,b,c,d) /* no log */ +#endif + +#if defined CHKUSER_SENDER_FORMAT + +static int check_sender_address_format (stralloc *user, stralloc *domain) { + + int x; + + for (x = 0; x < (user->len -1); ++x) { + if ((!isalnum (user->s[x])) + +#if defined CHKUSER_ALLOW_SENDER_SRS + && (user->s[x] != '#') + && (user->s[x] != '+') +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_1 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_2 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_3 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_4 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4) +#endif +#if defined CHKUSER_ALLOW_SENDER_CHAR_5 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5) +#endif + && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) { + return 0; + } + } + +/* + * Be careful, this is a base check + * Minimum is x.xx + ending \0 + * Minimum characters needed are 5 + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) { + return 0; + } +#endif + +/* + * This is a safety check + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < 2) { + return 0; + } +#endif + + for (x = 0; x < (domain->len -1); ++x) { + if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) { + return 0; + } + } + + if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) { + return 0; + } + if (strstr (domain->s, "..") != NULL) { + return 0; + } + if (strncmp (domain->s, "xn--", 4) == 0) { + if (strstr (&domain->s[4], "--") != NULL) + return 0; + } else { + if (strstr (domain->s, "--") != NULL) + return 0; + } + if (strstr (domain->s, ".-") != NULL) { + return 0; + } + if (strstr (domain->s, "-.") != NULL) { + return 0; + } + if (strchr (domain->s, '.') == NULL) { + return 0; + } + + return 1; +} + +#endif + +#if defined CHKUSER_RCPT_FORMAT + +static int check_rcpt_address_format (stralloc *user, stralloc *domain) { + + int x; + + for (x = 0; x < (user->len -1); ++x) { + if ((!isalnum (user->s[x])) +#if defined CHKUSER_ALLOW_RCPT_SRS + && (user->s[x] != '#') + && (user->s[x] != '+') +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_1 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_2 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_3 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_4 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4) +#endif +#if defined CHKUSER_ALLOW_RCPT_CHAR_5 + && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5) +#endif + + && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) { + return 0; + } + } + +/* + * Be careful, this is a base check + * Minimum is x.xx + ending \0 + * Minimum characters needed are 5 + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) { + return 0; + } +#endif + +/* + * This is a safety check + */ +#if defined CHKUSER_MIN_DOMAIN_LEN + if (domain->len < 2) { + return 0; + } +#endif + for (x = 0; x < (domain->len -1); ++x) { + if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) { + return 0; + } + } + + if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) { + return 0; + } + if (strstr (domain->s, "..") != NULL) { + return 0; + } + if (strncmp (domain->s, "xn--", 4) == 0) { + if (strstr (&domain->s[4], "--") != NULL) + return 0; + } else { + if (strstr (domain->s, "--") != NULL) + return 0; + } + if (strstr (domain->s, ".-") != NULL) { + return 0; + } + if (strstr (domain->s, "-.") != NULL) { + return 0; + } + if (strchr (domain->s, '.') == NULL) { + return 0; + } + + return 1; +} + +#endif + +#if defined CHKUSER_SENDER_MX || defined CHKUSER_RCPT_MX + +static unsigned long mx_random; +static ipalloc mx_ip = {0}; + +static int chkuser_mx_lookup (stralloc *domain) { + + int status; + + mx_random = now() + getpid(); + dns_init(0); + status = dns_mxip (&mx_ip, domain, mx_random); + + if (status == DNS_MEM) DIE_NOMEM(); + + return status; +} + +#endif + + +void chkuser_cleanup (int exit_value) { + +#if defined CHKUSER_DB_CLEANUP + vclose (); +#endif + _exit (exit_value); +} + +static void first_time_init (void) { + + char * temp_string; + +#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + starting_string = env_get (CHKUSER_STARTING_VARIABLE); + if (starting_string) { + if (strcasecmp(starting_string, "ALWAYS") == 0) { + starting_value = 1; + } else if (strcasecmp(starting_string, "DOMAIN") == 0) { + starting_value = 0; + } + } else { + starting_string = ""; + } +#endif + +#if defined CHKUSER_RCPT_LIMIT_VARIABLE + maxrcpt_string = env_get (CHKUSER_RCPT_LIMIT_VARIABLE); + if (maxrcpt_string) { + maxrcpt_limit = atoi (maxrcpt_string); + if (maxrcpt_limit < 1) { + maxrcpt_limit = 0; + } + } else { + maxrcpt_string = "";; + } +#endif + +#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE + maxwrongrcpt_string = env_get (CHKUSER_WRONGRCPT_LIMIT_VARIABLE); + if (maxwrongrcpt_string) { + maxwrongrcpt_limit = atoi (maxwrongrcpt_string); + if (maxwrongrcpt_limit < 1) { + maxwrongrcpt_limit = 0; + } + } else { + maxwrongrcpt_string = ""; + } +#endif + +#if defined CHKUSER_MBXQUOTA_VARIABLE + maxmbxquota_string = env_get (CHKUSER_MBXQUOTA_VARIABLE); + if (maxmbxquota_string) { + maxmbxquota_limit = atoi (maxmbxquota_string); + if (maxmbxquota_limit < 1) { + maxmbxquota_limit = 0; + } + } else { + maxmbxquota_string = ""; + } +#endif + +#if defined CHKUSER_SENDER_NOCHECK_VARIABLE + + temp_string = env_get (CHKUSER_SENDER_NOCHECK_VARIABLE); + if (temp_string) { + sender_nocheck = 1; + } else { + sender_nocheck = 0; + } + +#endif + +#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE + + identify_remote = env_get (CHKUSER_IDENTIFY_REMOTE_VARIABLE); + if (identify_remote) { + } + +#endif + + if (!stralloc_ready (&user, 300)) DIE_NOMEM(); + if (!stralloc_ready (&domain, 500)) DIE_NOMEM(); + if (!stralloc_ready (&domain_path, 1000)) DIE_NOMEM(); + if (!stralloc_ready (&tmp_path, 1000)) DIE_NOMEM(); + if (!stralloc_ready (&alias_path, 1000)) DIE_NOMEM(); + + first_time_init_flag = 0; + +} + +/* + * realrcpt () + * + * Returns: + * + * CHKUSER_OK = 1 = Ok, recipients does exists + * + * 0 = Not in rcpthosts + * + * < 0 various errors + * + * + * Parameters: + * stralloc *sender = sender address + * stralloc *rcpt = rcpt address to check + * + * +*/ + +static int realrcpt (stralloc *sender, stralloc *rcpt) +{ + int count; + int retstat = CHKUSER_KO; + struct vqpasswd *user_passwd = NULL; + int fd_file = -1; + int read_char; + int offset; + char read_buf[1024]; + +#if defined CHKUSER_ENABLE_UIDGID + uid_t eff_uid; + gid_t eff_gid; +#endif + +#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + if (starting_value == -1) { + if (addrallowed()) { + return CHKUSER_OK; + } else { + if (relayclient) { + return CHKUSER_RELAYING; + } + + return CHKUSER_NORCPTHOSTS; + } + } +#endif + + if (INTRUSION_threshold_reached == 1) { + return CHKUSER_ERR_INTRUSION_THRESHOLD; + } + +#if defined CHKUSER_RCPT_LIMIT_VARIABLE + + ++recipients; + if ((maxrcpt_limit > 0) && (recipients >= maxrcpt_limit)) { + chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed rcpt"); + INTRUSION_threshold_reached = 1; + return CHKUSER_ERR_MAXRCPT; + } +#endif + +/* Search the '@' character */ + count = byte_rchr(rcpt->s,rcpt->len,'@'); + + if (count < rcpt->len) { + if (!stralloc_copyb (&user, rcpt->s, count)) DIE_NOMEM(); + if (!stralloc_copys (&domain, rcpt->s + count + 1)) DIE_NOMEM(); + } + else { + if (!stralloc_copys (&user, rcpt->s)) DIE_NOMEM(); + domain.len = 0; + } + if (!stralloc_0 (&user)) DIE_NOMEM(); + if (!stralloc_0 (&domain)) DIE_NOMEM(); + +#if defined CHKUSER_ENABLE_UIDGID + +/* qmail-smtpd is running now as (effective) qmaild:nofiles */ +/* Save the effective UID & GID (qmaild:nofiles) */ + eff_uid = geteuid (); + eff_gid = getegid (); + +/* Now set new effective UID & GID, getting it from real UID & GID (vpopmail:vchkpw) */ + setegid (getgid()); + seteuid (getuid()); + +/* qmail-smtpd is running now as effective vpopmail:vchkpw */ +#endif + + +/* + * + * Now let's start the test/setting suite + * + **/ + + switch (0) { + + case 0: +/* These are some preliminary settings */ + case_lowers (user.s); + case_lowers (domain.s); + + case 1: + + if (domain.len == 1) { +#if defined CHKUSER_DOMAIN_WANTED + retstat = CHKUSER_ERR_DOMAIN_MISSING; + break; +#else + if (!stralloc_copys (&domain, DEFAULT_DOMAIN)) DIE_NOMEM(); + if (!stralloc_0 (&domain)) DIE_NOMEM(); +#endif + } + + case 2: + +#if defined CHKUSER_RCPT_FORMAT + + if (check_rcpt_address_format (&user, &domain) == 0) { + retstat = CHKUSER_ERR_RCPT_FORMAT; + break; + } +#endif + + case 3: + + if (!addrallowed()) { + +#if defined CHKUSER_RCPT_MX + switch (chkuser_mx_lookup(&domain)) { + + case DNS_HARD: + retstat = CHKUSER_ERR_RCPT_MX; + break; + + case DNS_SOFT: + retstat = CHKUSER_ERR_RCPT_MX_TMP; + break; + } + + if (retstat != CHKUSER_KO) { + break; + } +#endif + + if (relayclient) { + retstat = CHKUSER_RELAYING; + break; + } + + retstat = CHKUSER_NORCPTHOSTS; + break; + } + + case 4: + +#if defined CHKUSER_ENABLE_VAUTH_OPEN + if (db_already_open != 1) { + if (vauth_open () == 0) { + db_already_open == 1; + } else { + retstat = CHKUSER_ERR_AUTH_RESOURCE; + } + }; +#endif + + case 5: + +#if defined CHKUSER_ENABLE_VGET_REAL_DOMAIN +/* Check if domain is a real domain */ + + vget_real_domain(domain.s, domain.a); + + domain.len = strlen (domain.s) +1; + if (domain.len > (domain.a - 1)) DIE_NOMEM(); +#endif + +/* Let's get domain's real path */ + if (vget_assign(domain.s, domain_path.s, domain_path.a -1, NULL, NULL) == NULL) { + retstat = CHKUSER_OK; + break; + } + + domain_path.len = strlen (domain_path.s); + + case 6: + +/* Check if domain has bouncing enabled */ + +#if !defined CHKUSER_ALWAYS_ON + +#if defined CHKUSER_STARTING_VARIABLE + if (starting_value == 0) { +#endif + + if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM(); + +#if defined CHKUSER_SPECIFIC_BOUNCING + if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, CHKUSER_SPECIFIC_BOUNCING)) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + fd_file = open_read (tmp_path.s); + if (fd_file != -1) { + close (fd_file); + } else { + retstat = CHKUSER_OK; + break; + } +#else + if (!stralloc_cats (&tmp_path, "/.qmail-default")) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + + read_char = 0; + fd_file = open_read (tmp_path.s); + if (fd_file != -1) { + read_char = read (fd_file, read_buf, sizeof(read_buf) - 1); + close (fd_file); + if (read_char < 0) read_char = 0; + } + read_buf[read_char] = 0; + + if ( strstr(read_buf, CHKUSER_BOUNCE_STRING) == NULL ) { + retstat = CHKUSER_OK; + break; + } +#endif +#if defined CHKUSER_STARTING_VARIABLE + } +#endif +#endif + + case 7: +#if defined VALIAS +/* Check for aliases/forwards - valias*/ + + if (valias_select (user.s, domain.s) != NULL) { + retstat = CHKUSER_OK; + break; + } +#endif + + case 8: +#if defined CHKUSER_ENABLE_ALIAS +/* Check for aliases/forwards - .qmail.x files */ + + if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM(); + /* Change all '.' in ':' before continuing on aliases */ + for (count = 0; count < tmp_path.len; ++count) + if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':'; + + if (!stralloc_copy (&alias_path, &domain_path)) DIE_NOMEM(); + if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM(); + if (!stralloc_cats (&alias_path, tmp_path.s)) DIE_NOMEM(); + if (!stralloc_0 (&alias_path)) DIE_NOMEM(); + + fd_file = open_read (alias_path.s); + if (fd_file != -1) { + close (fd_file); + retstat = CHKUSER_OK; + break; + } +#endif + + case 9: + +#if defined CHKUSER_ENABLE_ALIAS_DEFAULT + + if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM(); + /* Change all '.' in ':' before continuing on aliases */ + for (count = 0; count < tmp_path.len; ++count) + if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':'; + + /* Search for the outer '-' character */ + for (offset = user.len - 1; offset > 0; --offset) + if (*(user.s + offset) == CHKUSER_USERS_DASH) { + if (!stralloc_copy (&alias_path, &domain_path)) die_nomem(); + if (!stralloc_cats (&alias_path, "/.qmail-")) die_nomem(); + if (!stralloc_catb (&alias_path, user.s, offset)) die_nomem(); + if (!stralloc_cats (&alias_path, "-default")) die_nomem(); + if (!stralloc_0 (&alias_path)) die_nomem(); + + fd_file = open_read (alias_path.s); + if (fd_file != -1) { + close (fd_file); + retstat = CHKUSER_OK; + break; + } + } + + if (retstat != CHKUSER_KO) { + break; + } + +#endif + + case 10: +#if defined CHKUSER_ENABLE_USERS +/* User control: check the existance of a real user */ + + user_passwd = vauth_getpw (user.s, domain.s); + +#if defined CHKUSER_ENABLE_USERS_EXTENSIONS + if (user_passwd == NULL) { + count = 0; + while ((count < (user.len -1)) && (user_passwd == NULL)) { + count += byte_chr(&user.s[count], user.len - count, CHKUSER_USERS_DASH); + if (count < user.len) { + if (!stralloc_copyb (&tmp_path, user.s, count)) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + user_passwd = vauth_getpw (tmp_path.s, domain.s); + ++count; + } + } + } + +#endif + if (user_passwd != NULL) { + + /* If user exists check if he has BOUNCE_MAIL flag set */ + + if (user_passwd->pw_gid & BOUNCE_MAIL) + retstat = CHKUSER_KO; + else { + retstat = CHKUSER_OK; +#if defined CHKUSER_MBXQUOTA_VARIABLE + if ((maxmbxquota_limit > 0) && (strcasecmp(user_passwd->pw_shell, "NOQUOTA") != 0)) { + if (!stralloc_copys (&tmp_path, user_passwd->pw_dir)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/Maildir")) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + + if (vmaildir_readquota(tmp_path.s,format_maildirquota(user_passwd->pw_shell)) + >= maxmbxquota_limit) { + retstat = CHKUSER_ERR_MBXFULL; + } + } +#endif + } + break; + } +#endif + + case 11: +#if defined CHKUSER_ENABLE_EZMLM_LISTS +/* Let's check for mailing lists */ + + /* Search for the outer CHKUSER_EZMLM_DASH character */ + for (offset = user.len - 2; offset > 0; --offset) { + if (*(user.s + offset) == CHKUSER_EZMLM_DASH) { + if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM(); + if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/mailinglist")) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + fd_file = open_read (tmp_path.s); + if (fd_file != -1) { + close (fd_file); + retstat = CHKUSER_OK; + break; + } + } + } + if (retstat != CHKUSER_KO) { + break; + } +#endif + + case 12: +#if defined CHKUSER_ENABLE_MAILMAN_LISTS +/* Let's check for mailing lists */ + + /* Search for the outer CHKUSER_MAILMAN_DASH character */ + for (offset = user.len - 2; offset > 0; --offset) { + if (*(user.s + offset) == CHKUSER_MAILMAN_DASH) { + if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM(); + if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM(); + if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM(); + if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM(); + if (!stralloc_0 (&tmp_path)) DIE_NOMEM(); + fd_file = open_read (tmp_path.s); + read_char = 0; + if (fd_file != -1) { + read_char = read (fd_file, read_buf, sizeof(read_buf) - 1); + close (fd_file); + if (read_char < 0) read_char = 0; + } + read_buf[read_char] = 0; + + if ( strstr(read_buf, CHKUSER_MAILMAN_STRING) == NULL ) { + retstat = CHKUSER_OK; + break; + } + + } + } + if (retstat != CHKUSER_KO) { + break; + } +#endif + +/* + * Add this code if another case is following + case xx: + code .... + code .... + code .... + code .... + + if (xxxxxxxx) { + retstat != CHKUSER_KO) + break; + } +*/ + + default: + retstat = CHKUSER_KO; + + } /* end switch */ + +#if defined CHKUSER_ENABLE_UIDGID +/* Now switch back effective to saved UID & GID (qmaild:nofiles) */ + + setegid (eff_gid); + seteuid (eff_uid); + +/* qmail-smtpd is running again as (effective) qmaild:nofiles */ +#endif + + return retstat; + +} + + + +/* + * chkuser_realrcpt () + * + * Returns a simple status: + * + * CHKUSER_OK = 1 = Ok, recipients does exists + * + * CHKUSER_NORCPTHOSTS = Not in rcpthosts + * + * CHKUSER_KO = ERROR + * + * + * Parameters: + * stralloc *sender = sender address + * stralloc *rcpt = rcpt address to check + * + * +*/ + +int chkuser_realrcpt (stralloc *sender, stralloc *rcpt) { + +int retstat; + + if (first_time_init_flag) { + first_time_init (); + } + + retstat = realrcpt (sender, rcpt); + + switch (retstat) { + + case CHKUSER_OK: +#if defined CHKUSER_LOG_VALID_RCPT + chkuser_commonlog (sender->s, rcpt->s, "accepted rcpt", "found existing recipient"); +#endif + return CHKUSER_OK; + break; + + case CHKUSER_RELAYING: +#if defined CHKUSER_LOG_VALID_RCPT + chkuser_commonlog (sender->s, rcpt->s, "relaying rcpt", "client allowed to relay"); +#endif + return CHKUSER_RELAYING; + break; + + case CHKUSER_NORCPTHOSTS: + chkuser_commonlog (sender->s, rcpt->s, "rejected relaying", "client not allowed to relay"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_NORELAY_STRING); + break; + + case CHKUSER_KO: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "not existing recipient"); + CHKUSER_DELAY(); + out(CHKUSER_NORCPT_STRING); + break; + + case CHKUSER_ERR_AUTH_RESOURCE: + chkuser_commonlog (sender->s, rcpt->s, "no auth resource", "no auth resource available"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RESOURCE_STRING); + break; + + case CHKUSER_ERR_MBXFULL: + chkuser_commonlog (sender->s, rcpt->s, "mbx overquota", "rcpt mailbox is overquota"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_MBXFULL_STRING); + break; + + case CHKUSER_ERR_MAXRCPT: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of recipients"); + CHKUSER_DELAY (); + out(CHKUSER_MAXRCPT_STRING); + break; + + case CHKUSER_ERR_MAXWRONGRCPT: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of invalid recipients"); + CHKUSER_DELAY (); + out(CHKUSER_MAXWRONGRCPT_STRING); + break; + + case CHKUSER_ERR_INTRUSION_THRESHOLD: + chkuser_commonlog (sender->s, rcpt->s, "rejected intrusion", "rcpt ignored, session over INTRUSION threshold"); + CHKUSER_DELAY (); + out(CHKUSER_INTRUSIONTHRESHOLD_STRING); + break; + + case CHKUSER_ERR_DOMAIN_MISSING: + CHKUSER_DELAY (); + out(CHKUSER_DOMAINMISSING_STRING); + break; + + case CHKUSER_ERR_RCPT_FORMAT: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt address format"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RCPTFORMAT_STRING); + break; + + case CHKUSER_ERR_RCPT_MX: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt MX domain"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RCPTMX_STRING); + break; + + case CHKUSER_ERR_RCPT_MX_TMP: + chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "temporary DNS problem"); + CHKUSER_RCPT_DELAY_ANY(); + out(CHKUSER_RCPTMX_TMP_STRING); + break; + } + + + +#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE + if ((retstat == CHKUSER_KO) || (retstat == CHKUSER_ERR_DOMAIN_MISSING)) { + ++wrong_recipients; + if ((INTRUSION_threshold_reached == 0) && (maxwrongrcpt_limit > 0) && (wrong_recipients >= maxwrongrcpt_limit)) { + chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed invalid rcpt"); + INTRUSION_threshold_reached = 1; + } + } +#endif + + return CHKUSER_KO; +} + + +/* + * + * This routine checks for sender format and MX + * + */ + + +int chkuser_sender (stralloc *sender) { + +int count; + + if (first_time_init_flag) { + first_time_init (); + } + +#if !defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE + if (starting_value == -1) { + return CHKUSER_OK; + } +#endif + +#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX + +#if defined CHKUSER_SENDER_NOCHECK_VARIABLE + + if (sender_nocheck == 1) { + return CHKUSER_OK; + } +#endif + + if (sender->len <= 1) { +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted null sender", "accepted null sender always"); +#endif + return CHKUSER_OK; + } + + count = byte_rchr(sender->s,sender->len,'@'); + if (count < sender->len) { + if (!stralloc_copyb (&sender_user, sender->s, count)) DIE_NOMEM(); + if (!stralloc_copys (&sender_domain, sender->s + count + 1)) DIE_NOMEM(); + } else { + if (!stralloc_copys (&sender_user, sender->s)) DIE_NOMEM(); + sender_domain.len = 0; + } + if (!stralloc_0 (&sender_user)) DIE_NOMEM(); + if (!stralloc_0 (&sender_domain)) DIE_NOMEM(); + +#if defined CHKUSER_SENDER_FORMAT + if (check_sender_address_format (&sender_user, &sender_domain) == 0) { + chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender address format"); + CHKUSER_SENDER_DELAY_ANY(); + out(CHKUSER_SENDERFORMAT_STRING); + return CHKUSER_ERR_SENDER_FORMAT; + } + +#endif + +#if defined CHKUSER_SENDER_MX + + switch (chkuser_mx_lookup(&sender_domain)) { + + case DNS_HARD: + CHKUSER_SENDER_DELAY_ANY(); + out(CHKUSER_SENDERMX_STRING); + chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender MX domain"); + return CHKUSER_ERR_SENDER_MX; + break; + + case DNS_SOFT: + CHKUSER_SENDER_DELAY_ANY(); + out(CHKUSER_SENDERMX_TMP_STRING); + chkuser_commonlog (sender->s, "", "rejected sender", "temporary DNS problem"); + return CHKUSER_ERR_SENDER_MX_TMP; + break; + } + +#if defined CHKUSER_LOG_VALID_SENDER + chkuser_commonlog (sender->s, "", "accepted sender", "sender accepted"); +#endif + + return CHKUSER_OK; +#endif + +#else + + return CHKUSER_OK; + +#endif + +} + + diff -urN qmail-1.03-debian/chkuser.h qmail-1.03/chkuser.h --- qmail-1.03-debian/chkuser.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/chkuser.h 2006-08-24 13:39:29.000000000 +0200 @@ -0,0 +1,51 @@ + +/* + * + * 'chkuser.h' v.2.0.8 + * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x + * + * Author: Antonio Nati tonix@interazioni.it + * All rights on this software and + * the identifying words chkusr and chkuser kept by the author + * + * This software may be freely used, modified and distributed, + * but this lines must be kept in every original or derived version. + * Original author "Antonio Nati" and the web URL + * "http://www.interazioni.it/opensource" + * must be indicated in every related work or web page + * + */ + +#define CHKUSER +#define CHKUSER_VERSION "2.0.8" +#define CHKUSER_VERSION_RL 2 +#define CHKUSER_VERSION_MJ 0 +#define CHKUSER_VERSION_MN 8 + +#define CHKUSER_OK 1 +#define CHKUSER_RELAYING 0 +#define CHKUSER_KO -1 +#define CHKUSER_NORCPTHOSTS -10 +#define CHKUSER_ERR_AUTH_RESOURCE -20 +#define CHKUSER_ERR_MBXFULL -30 +#define CHKUSER_ERR_MAXRCPT -40 +#define CHKUSER_ERR_MAXWRONGRCPT -50 +#define CHKUSER_ERR_DOMAIN_MISSING -60 +#define CHKUSER_ERR_RCPT_FORMAT -70 +#define CHKUSER_ERR_RCPT_MX -75 +#define CHKUSER_ERR_RCPT_MX_TMP -76 +#define CHKUSER_ERR_SENDER_FORMAT -80 +#define CHKUSER_ERR_SENDER_MX -85 +#define CHKUSER_ERR_SENDER_MX_TMP -86 +#define CHKUSER_ERR_INTRUSION_THRESHOLD -90 + +void chkuser_cleanup (int exit_value); +int chkuser_realrcpt (stralloc *sender, stralloc *rcpt); +int chkuser_sender (stralloc *sender); + +#ifdef TLS_H +#undef _exit +#define _exit(value) { if (ssl) ssl_free(ssl); chkuser_cleanup(value); } +#else +#define _exit(value) chkuser_cleanup(value); +#endif diff -urN qmail-1.03-debian/chkuser_settings.h qmail-1.03/chkuser_settings.h --- qmail-1.03-debian/chkuser_settings.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03/chkuser_settings.h 2006-08-24 13:45:30.000000000 +0200 @@ -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 500 + +/* + * Uncomment to consider rcpt errors on address format and MX as intrusive + * + */ +#define CHKUSER_RCPT_DELAY_ANYERROR + +/* + * Uncomment to consider sender errors on address format and MX as intrusive + * + */ +#define CHKUSER_SENDER_DELAY_ANYERROR + +#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 -urN qmail-1.03-debian/conf-cc qmail-1.03/conf-cc --- qmail-1.03-debian/conf-cc 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/conf-cc 2006-08-24 13:39:29.000000000 +0200 @@ -1,3 +1,3 @@ -cc -O2 +cc -O2 -I/home/vpopmail/include This will be used to compile .c files. diff -urN qmail-1.03-debian/qmail-smtpd.8 qmail-1.03/qmail-smtpd.8 --- qmail-1.03-debian/qmail-smtpd.8 2006-08-24 13:11:23.000000000 +0200 +++ qmail-1.03/qmail-smtpd.8 2006-08-24 13:20:43.000000000 +0200 @@ -23,7 +23,30 @@ header fields. .B qmail-smtpd -supports ESMTP, including the 8BITMIME and PIPELINING options. +supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options. +.B qmail-smtpd +includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements. +.B qmail-smtpd +can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes +.IR checkprogram , +which reads on file descriptor 3 the username, a 0 byte, the password +or CRAM-MD5 digest/response derived from the SMTP client, +another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type), +and a final 0 byte. +.I checkprogram +invokes +.I subprogram +upon successful authentication, which should in turn return 0 to +.BR qmail-smtpd , +effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO +(any supplied value replaced with the authenticated username). +.B qmail-smtpd +will reject the authentication attempt if it receives a nonzero return +value from +.I checkprogram +or +.IR subprogram . + .SH TRANSPARENCY .B qmail-smtpd converts the SMTP newline convention into the UNIX newline convention diff -urN qmail-1.03-debian/qmail-smtpd.c qmail-1.03/qmail-smtpd.c --- qmail-1.03-debian/qmail-smtpd.c 2006-08-24 13:11:23.000000000 +0200 +++ qmail-1.03/qmail-smtpd.c 2006-08-24 13:41:46.000000000 +0200 @@ -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" @@ -25,6 +35,10 @@ #include "commands.h" #include "qregex.h" #include "strerr.h" +#include "wait.h" + +// #define CRAM_MD5 +#define AUTHSLEEP 3 #define BMCHECK_BMF 0 #define BMCHECK_BMFNR 1 @@ -33,6 +47,10 @@ #define BMCHECK_BHELO 4 +/* start chkuser code */ +#include "chkuser.h" +/* end chkuser code */ + #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; @@ -59,6 +77,7 @@ 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 has been denied (#5.7.1)\r\n"); } +void err_size() { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); } void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); } void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } @@ -70,6 +89,16 @@ void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } +void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); } +void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); } +int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; } +int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; } +int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; } +void err_authfail() { out("535 authentication failed (#5.7.1)\r\n"); } stralloc greeting = {0}; @@ -87,6 +116,7 @@ smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } +char *protocol; char *remoteip; char *remotehost; char *remoteinfo; @@ -132,7 +162,6 @@ 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); @@ -156,6 +185,7 @@ if (x) { scan_ulong(x,&u); databytes = u; } if (!(databytes + 1)) --databytes; + protocol = "SMTP"; remoteip = env_get("TCPREMOTEIP"); if (!remoteip) remoteip = "unknown"; local = env_get("TCPLOCALHOST"); @@ -287,11 +317,74 @@ int seenmail = 0; int flagbarfbmf; /* defined if seenmail */ +int flagsize; int flagbarfbmt; int flagbarfbhelo; stralloc mailfrom = {0}; stralloc rcptto = {0}; +stralloc fuser = {0}; +stralloc mfparms = {0}; + +int mailfrom_size(arg) char *arg; +{ + long r; + unsigned long sizebytes = 0; + + scan_ulong(arg,&r); + sizebytes = r; + if (databytes) if (sizebytes > databytes) return 1; + return 0; +} + +void mailfrom_auth(arg,len) +char *arg; +int len; +{ + int j; + + if (!stralloc_copys(&fuser,"")) die_nomem(); + if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); } + else + while (len) { + if (*arg == '+') { + if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); } + if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); } + } + else + if (!stralloc_catb(&fuser,arg,1)) die_nomem(); + arg++; len--; + } + if(!stralloc_0(&fuser)) die_nomem(); + if (!remoteinfo) { + remoteinfo = fuser.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + } +} + +void mailfrom_parms(arg) char *arg; +{ + int i; + int len; + + len = str_len(arg); + if (!stralloc_copys(&mfparms,"")) die_nomem(); + i = byte_chr(arg,len,'>'); + if (i > 4 && i < len) { + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; } + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5); + if (!stralloc_copys(&mfparms,"")) die_nomem(); + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem(); + } + } +} + void smtp_helo(arg) char *arg; { smtp_greet("250 "); out("\r\n"); @@ -300,7 +393,16 @@ } void smtp_ehlo(arg) char *arg; { - smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); + char size[FMT_ULONG]; + size[fmt_ulong(size,(unsigned int) databytes)] = 0; + smtp_greet("250-"); + out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n"); + out("250-SIZE "); out(size); out("\r\n"); +#ifdef CRAM_MD5 + out("250 AUTH LOGIN PLAIN CRAM-MD5\r\n"); +#else + out("250 AUTH LOGIN PLAIN\r\n"); +#endif seenmail = 0; dohelo(arg); if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO); } @@ -312,6 +414,12 @@ 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 */ + flagsize = 0; + mailfrom_parms(arg); + if (flagsize) { err_size(); return; } flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */ if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF); if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) { @@ -345,6 +453,8 @@ err_bmt(); return; } + /* + * Original code substituted by chkuser code if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); @@ -352,6 +462,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(); @@ -472,7 +602,7 @@ qp = qmail_qp(&qqt); out("354 go ahead\r\n"); - received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); + received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt); @@ -482,16 +612,242 @@ qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } - if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } + if (databytes) if (!bytestooverflow) { err_size(); return; } if (*qqx == 'D') out("554 "); else out("451 "); out(qqx + 1); out("\r\n"); } +/* this file is too long ----------------------------------------- SMTP AUTH */ + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; /* input from SMTP client */ +static stralloc user = {0}; /* authorization user-id */ +static stralloc pass = {0}; /* plain passwd or digest */ +static stralloc resp = {0}; /* b64 response */ +#ifdef CRAM_MD5 +static stralloc chal = {0}; /* plain challenge */ +static stralloc slop = {0}; /* b64 challenge */ +#endif + +int flagauth = 0; +char **childargs; +char ssauthbuf[512]; +substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf)); + +int authgetl(void) { + int i; + + if (!stralloc_copys(&authin,"")) die_nomem(); + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = substdio_get(&ssin,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); } + if (authin.len == 0) { return err_input(); } + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); +#ifdef CRAM_MD5 + if (!stralloc_0(&chal)) die_nomem(); +#endif + + if (pipe(pi) == -1) return err_pipe(); + switch(child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if(fd_copy(3,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs, childargs); + _exit(1); + } + close(pi[0]); + + substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf); + if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write(); + if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write(); +#ifdef CRAM_MD5 + if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write(); +#endif + if (substdio_flush(&ssauth) == -1) return err_write(); + + close(pi[1]); +#ifdef CRAM_MD5 + if (!stralloc_copys(&chal,"")) die_nomem(); + if (!stralloc_copys(&slop,"")) die_nomem(); +#endif + byte_zero(ssauthbuf,sizeof ssauthbuf); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(arg) char *arg; +{ + int r; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +int auth_plain(arg) char *arg; +{ + int r, id = 0; + + if (*arg) { + if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + } + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ + + if (resp.len > id + 1) + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); + if (resp.len > id + user.len + 2) + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} + +#ifdef CRAM_MD5 +int auth_cram() +{ + int i, r; + char *s; + + s = unique; /* generate challenge */ + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (!stralloc_copys(&chal,"<")) die_nomem(); + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,local)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); /* "334 base64_challenge \r\n" */ + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; /* got response */ + if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input(); + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + + i = str_chr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ + + if (!user.len || !pass.len) return err_input(); + return authenticate(); +} +#endif + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login",auth_login } +, { "plain",auth_plain } +#ifdef CRAM_MD5 +, { "cram-md5",auth_cram } +#endif +, { 0,err_noauth } +}; + +void smtp_auth(arg) +char *arg; +{ + int i; + char *cmd = arg; + + if (!*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; } + if (flagauth) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); +#ifdef CRAM_MD5 + if (!stralloc_copys(&chal,"")) die_nomem(); +#endif + + i = str_chr(cmd,' '); + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0;authcmds[i].text;++i) + if (case_equals(authcmds[i].text,cmd)) break; + + switch (authcmds[i].fun(arg)) { + case 0: + flagauth = 1; + protocol = "ESMTPA"; + relayclient = ""; + remoteinfo = user.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_put2("RELAYCLIENT",relayclient)) die_nomem(); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + err_authfail(user.s,authcmds[i].text); + } +} + + +/* this file is too long --------------------------------------------- GO ON */ + struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 } , { "mail", smtp_mail, 0 } , { "data", smtp_data, flush } +, { "auth", smtp_auth, flush } , { "quit", smtp_quit, flush } , { "helo", smtp_helo, flush } , { "ehlo", smtp_ehlo, flush } @@ -502,8 +858,11 @@ , { 0, err_unimpl, flush } } ; -void main() +void main(argc,argv) +int argc; +char **argv; { + childargs = argv + 1; sig_pipeignore(); if (chdir(auto_qmail) == -1) die_control(); setup(); diff -urN qmail-1.03-debian/qmail.c qmail-1.03/qmail.c --- qmail-1.03-debian/qmail.c 2006-08-24 13:11:23.000000000 +0200 +++ qmail-1.03/qmail.c 2006-08-24 13:38:31.000000000 +0200 @@ -23,22 +23,32 @@ { int pim[2]; int pie[2]; + int pierr[2]; setup_qqargs(); if (pipe(pim) == -1) return -1; if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } + if (pipe(pierr) == -1) { + close(pim[0]); close(pim[1]); + close(pie[0]); close(pie[1]); + close(pierr[0]); close(pierr[1]); + return -1; + } switch(qq->pid = vfork()) { case -1: + close(pierr[0]); close(pierr[1]); close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); return -1; case 0: close(pim[1]); close(pie[1]); + close(pierr[0]); /* we want to receive data */ if (fd_move(0,pim[0]) == -1) _exit(120); if (fd_move(1,pie[0]) == -1) _exit(120); + if (fd_move(4,pierr[1]) == -1) _exit(120); if (chdir(auto_qmail) == -1) _exit(61); execv(*binqqargs,binqqargs); _exit(120); @@ -46,6 +56,7 @@ qq->fdm = pim[1]; close(pim[0]); qq->fde = pie[1]; close(pie[0]); + qq->fderr = pierr[0]; close(pierr[1]); substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf)); qq->flagerr = 0; return 0; @@ -93,10 +104,22 @@ { int wstat; int exitcode; + int match; + char ch; + static char errstr[256]; + int len = 0; qmail_put(qq,"",1); if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1; close(qq->fde); + substdio_fdbuf(&qq->ss,read,qq->fderr,qq->buf,sizeof(qq->buf)); + while( substdio_bget(&qq->ss,&ch,1) && len < 255){ + errstr[len]=ch; + len++; + } + if (len > 0) errstr[len]='\0'; /* add str-term */ + + close(qq->fderr); if (wait_pid(&wstat,qq->pid) != qq->pid) return "Zqq waitpid surprise (#4.3.0)"; @@ -129,8 +152,11 @@ case 81: return "Zqq internal bug (#4.3.0)"; case 120: return "Zunable to exec qq (#4.3.0)"; default: + if (exitcode == 82 && len > 2){ + return errstr; + } if ((exitcode >= 11) && (exitcode <= 40)) - return "Dqq permanent problem (#5.3.0)"; + return "Dqq permanent problem (#5.3.0)"; return "Zqq temporary problem (#4.3.0)"; } } diff -urN qmail-1.03-debian/qmail.h qmail-1.03/qmail.h --- qmail-1.03-debian/qmail.h 1998-06-15 12:53:16.000000000 +0200 +++ qmail-1.03/qmail.h 2006-08-24 13:37:55.000000000 +0200 @@ -8,6 +8,7 @@ unsigned long pid; int fdm; int fde; + int fderr; substdio ss; char buf[1024]; } ;