diff -Naur qmail-1.03.orig/hier.c qmail-1.03_qv/hier.c --- qmail-1.03.orig/hier.c 1998-06-15 11:53:16.000000000 +0100 +++ qmail-1.03_qv/hier.c 2007-06-30 17:50:50.000000000 +0100 @@ -127,6 +127,7 @@ c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-verify",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755); c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); @@ -247,6 +248,8 @@ c(auto_qmail,"man/cat8","qmail-qmtpd.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-smtpd.8",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/man8","qmail-verify.8",auto_uido,auto_gidq,0644); + c(auto_qmail,"man/cat8","qmail-verify.0",auto_uido,auto_gidq,0644); c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644); c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644); } diff -Naur qmail-1.03.orig/Makefile qmail-1.03_qv/Makefile --- qmail-1.03.orig/Makefile 1998-06-15 11:53:16.000000000 +0100 +++ qmail-1.03_qv/Makefile 2007-07-05 18:40:14.000000000 +0100 @@ -808,7 +808,7 @@ forward preline condredirect bouncesaying except maildirmake \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ -binm3 binm3+df +binm3 binm3+df qmail-verify load: \ make-load warn-auto.sh systype @@ -935,7 +935,7 @@ maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \ qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \ qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \ -envelopes.0 forgeries.0 +envelopes.0 forgeries.0 qmail-verify.0 mbox.0: \ mbox.5 @@ -1533,11 +1533,13 @@ qmail-smtpd: \ load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ +sockbits.o udpbits.o verifyrcpt.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ fs.a auto_qmail.o socket.lib ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + sockbits.o udpbits.o verifyrcpt.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 \ @@ -1553,6 +1555,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 \ +sockbits.h udpbits.h qmail-verify.h \ exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h ./compile qmail-smtpd.c @@ -1627,6 +1630,31 @@ | sed s}SPAWN}"`head -1 conf-spawn`"}g \ > qmail-users.5 +qmail-verify.o: \ +qmail-verify.h compile qmail-verify.c auto_break.h auto_usera.h auto_qmail.h byte.h case.h cdb.h \ +constmap.h error.h fmt.h ip.h open.h str.h stralloc.h uint32.h stderrbits.h + ./compile qmail-verify.c + +qmail-verify: \ +load qmail-verify.o timeoutread.o \ +timeoutwrite.o control.o ip.o constmap.o \ +cdb.a fd.a wait.a getln.a \ +open.a sig.a case.a env.a stralloc.a \ +auto_usera.o auto_break.o auto_qmail.o stderrbits.o \ +alloc.a substdio.a error.a str.a fs.a \ +socket.lib + ./load qmail-verify timeoutread.o \ + timeoutwrite.o control.o ip.o constmap.o \ + cdb.a fd.a wait.a \ + getln.a open.a sig.a case.a env.a stralloc.a \ + auto_usera.o auto_break.o auto_qmail.o stderrbits.o \ + alloc.a substdio.a error.a str.a fs.a `cat \ + socket.lib` + +qmail-verify.0: \ +qmail-verify.8 + nroff -man qmail-verify.8 > qmail-verify.0 + qmail.0: \ qmail.7 nroff -man qmail.7 > qmail.0 @@ -1771,6 +1799,7 @@ qmail-popup.8 qmail-pw2u.9 qmail-qmqpc.8 qmail-qmqpd.8 qmail-qmtpd.8 \ qmail-qread.8 qmail-qstat.8 qmail-queue.8 qmail-remote.8 \ qmail-rspawn.8 qmail-send.9 qmail-showctl.8 qmail-smtpd.8 \ +qmail-verify.8 \ qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-users.9 qmail.7 \ qreceipt.1 splogger.8 tcp-env.1 config.sh config-fast.sh \ qmail-clean.c qmail-getpw.c qmail-inject.c qmail-local.c \ @@ -1883,6 +1912,10 @@ error.h ./compile slurpclose.c +sockbits.o: \ +compile sockbits.c sockbits.h stralloc.h gen_alloc.h + ./compile sockbits.c + socket.lib: \ trylsock.c compile load ( ( ./compile trylsock.c && \ @@ -1911,6 +1944,10 @@ scan.h fmt.h ./compile splogger.c +stderrbits.o: \ +compile stderrbits.c stderrbits.h sockbits.h stralloc.h gen_alloc.h + ./compile stderrbits.c + str.a: \ makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \ str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \ @@ -2121,6 +2158,10 @@ compile triggerpull.c ndelay.h open.h triggerpull.h ./compile triggerpull.c +udpbits.o: \ +compile udpbits.c udpbits.h ip.h + ./compile udpbits.c + uint32.h: \ tryulong32.c compile load uint32.h1 uint32.h2 ( ( ./compile tryulong32.c && ./load tryulong32 && \ @@ -2128,6 +2169,15 @@ && cat uint32.h2 || cat uint32.h1 ) > uint32.h rm -f tryulong32.o tryulong32 +verifyrcpt.o: \ +compile verifyrcpt.c verifyrcpt.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 \ +sockbits.h udpbits.h qmail-verify.h + ./compile verifyrcpt.c + wait.a: \ makelib wait_pid.o wait_nohang.o ./makelib wait.a wait_pid.o wait_nohang.o diff -Naur qmail-1.03.orig/qmail-smtpd.8 qmail-1.03_qv/qmail-smtpd.8 --- qmail-1.03.orig/qmail-smtpd.8 1998-06-15 11:53:16.000000000 +0100 +++ qmail-1.03_qv/qmail-smtpd.8 2007-07-05 20:55:53.000000000 +0100 @@ -169,6 +169,57 @@ .B qmail-smtpd will wait for each new buffer of data from the remote SMTP client. Default: 1200. +.SH "RECIPIENT VERIFICATION" +Optionally, qmail-smtpd can verify local recipient addresses during +the SMTP session. This is enabled if +.B RELAYCLIENT +is not set and the environment variables +.B VERIFY +or +.B VERIFYDEFER +are set to anything, including "" - the actual values are ignored. +Recipient verification causes +.B qmail-smtpd +to query a UDP server ( +.B qmail-verify +) to determine if the address is valid. + +The behaviour of +.B VERIFY +and +.B VERIFYDEFER +is slightly different: +.B VERIFY +causes an immediate response - invalid addresses will generate a +permanent 550 error to the SMTP RCPT command. +.B VERIFYDEFER +causes a delayed response: If any of the addresses given in SMTP +RCPT commands are invalid, the subsequent SMTP DATA command(s) will +result in a permanent 554 error - although the individual RCPTs will +appear to succeed. If both are set, +.B VERIFYDEFER +is assumed. + +The UDP recipient verification server is assumed to be running on +localhost (127.0.0.1) port 11113. Both of these default values can +be altered by specifying the IP address and/or port in the +environment variable +.B VERIFYSVR +- set this to the address of +the address verification server, optionally followed by a colon and +the port, thus for example VERIFYSVR="192.168.1.1:10101". If +the verification server is not running, cannot be reached, or no +reply is received from it, a 451 temporary error is returned +in the SMTP session. + +Addresses with domains appearing in +.B control/rcpthosts +but not in +.B control/locals +or +.B control/virtualdomains +will be considered valid. + .SH "SEE ALSO" tcp-env(1), tcp-environ(5), @@ -176,4 +227,5 @@ qmail-inject(8), qmail-newmrh(8), qmail-queue(8), -qmail-remote(8) +qmail-remote(8), +qmail-verify(8). diff -Naur qmail-1.03.orig/qmail-smtpd.c qmail-1.03_qv/qmail-smtpd.c --- qmail-1.03.orig/qmail-smtpd.c 1998-06-15 11:53:16.000000000 +0100 +++ qmail-1.03_qv/qmail-smtpd.c 2007-07-05 16:57:38.000000000 +0100 @@ -23,6 +23,7 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "verifyrcpt.h" #define MAXHOPS 100 unsigned int databytes = 0; @@ -58,6 +59,12 @@ void err_noop() { out("250 ok\r\n"); } void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } +void die_qvsetup() { out("451 qv setup failure (#4.3.0)\r\n"); flush(); _exit(1); } +/*void die_qvsetup() { out("451 qv setup failure [DEBUG]: "); out(error_str(errno)); out(" (#4.3.0)\r\n"); flush(); _exit(1); }*/ +void die_qvmiscfail() { out("451 qv temporary failure (#4.3.0)\r\n"); flush(); _exit(1); } +/*void die_qvmiscfail() { out("451 qv temporary failure [DEBUG]: "); out(error_str(errno)); out(" (#4.3.0)\r\n"); flush(); _exit(1); }*/ +void err_nosuchuser550() { out("550 sorry, no mailbox here by that name. (#5.1.1)\r\n"); } +void err_nosuchuser554() { out("554 sorry, invalid mailbox name(s). (#5.1.1)\r\n"); } stralloc greeting = {0}; @@ -81,6 +88,8 @@ char *remoteinfo; char *local; char *relayclient; +char *verifysvr; +int flagverify, flagverifydefer; stralloc helohost = {0}; char *fakehelo; /* pointer into helohost, or 0 */ @@ -131,6 +140,10 @@ if (!remotehost) remotehost = "unknown"; remoteinfo = env_get("TCPREMOTEINFO"); relayclient = env_get("RELAYCLIENT"); + flagverifydefer = env_get("VERIFYDEFER")?1:0; + flagverify = flagverifydefer || env_get("VERIFY") ?1:0; /* VERIFYDEFER implies VERIFY */ + verifysvr = env_get("VERIFYSVR"); + if (flagverify) verifyrcpt_init(verifysvr,die_qvsetup); dohelo(remotehost); } @@ -257,7 +270,10 @@ if (!stralloc_0(&addr)) die_nomem(); } else + { if (!addrallowed()) { err_nogateway(); return; } + if (flagverify && verifyrcpt(&addr,flagverifydefer,die_qvmiscfail)) { err_nosuchuser550(); return; } + } if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); @@ -372,6 +388,7 @@ if (!seenmail) { err_wantmail(); return; } if (!rcptto.len) { err_wantrcpt(); return; } + if (flagverifydefer && flagdenyany) { err_nosuchuser554(); return; } seenmail = 0; if (databytes) bytestooverflow = databytes + 1; if (qmail_open(&qqt) == -1) { err_qqt(); return; } diff -Naur qmail-1.03.orig/qmail-verify.8 qmail-1.03_qv/qmail-verify.8 --- qmail-1.03.orig/qmail-verify.8 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/qmail-verify.8 2007-08-23 19:54:43.000000000 +0100 @@ -0,0 +1,135 @@ +.TH qmail-verify 8 "Andrew Richards" "23rd August 2007" "v1.22" +.SH NAME +qmail-verify \- Address verification daemon +.SH SYNOPSIS +.B qmail-verify +.SH DESCRIPTION +.B qmail-verify +receives UDP packets containing local email addresses and returns a +single byte to indicate if the address is valid or invalid to the +sender of the UDP packet. +.B qmail-smtpd +or +.B qmail-qmtpd +are typical clients using the +.B qmail-verify +service, although at present only +.B qmail-smtpd +has had this functionality added. +.B qmail-verify +is based on Paul Jarc's +.B realrcptto +patch for qmail +.I (http://code.dogmap.org/qmail/). + +.B qmail-verify +uses the files +.I control/locals, control/virtualdomains, users/cdb, +the system password file entries (typically in +.B /etc/passwd +) as well as the existence or not of users' home directories and +.B .qmail[-xxx] +files to determine if a given address is valid. + +Where a qmail system uses +.B .qmail-default +files on a per-domain basis in a virtual domains setup, this is +likely to result in all addresses being considered 'valid'. This +may not in fact be the case in certain situations, such as with +extensions/adaptations to qmail like vpopmail which use +.B .qmail-default +files throughout (delivery in this case is subsequently handled +by a vpopmail component). In these cases a replacement for +.B qmail-verify +will be required that can determine address validity. + +Other customised qmail installations that use different methods +to locate users' mailboxes are likely to need alternatives to +.B qmail-verify +or a modified version of it for address verification. + +.SH INVOCATION +.B qmail-verify +should be invoked as user +.I root +to have sufficient privileges to +determine the validity of a given address. In certain single-UID +virtual domains setups, it may be sufficient to run +.B qmail-verify +as the single-UID. + +By default, +.B qmail-verify +listens on localhost (127.0.0.1) on port 11113. This behaviour +can be changed by setting the environment variable +.B LISTEN +to specify the IP address and/or port: Set this to the desired +IP address, optionally followed by a colon and port, thus for +example LISTEN="192.168.1.1:10101". + +.SH ADDRESS VERIFICATION DETAILS +.B qmail-verify +is implemented by taking the various pieces of qmail that parse an +address and combining them in the same executable, +.B qmail-verify. +Thus logic is taken from +.B qmail-send, qmail-lspawn, qmail-getpw +and +.B qmail-local. + +.SH "UDP PACKET DETAILS" +The incoming packet contains just the email address to be checked +as a string. The string is optionally terminated with a 0 byte. + +The response packet contains a single byte to indicate whether the +address is valid. The lowest-order bit of this byte indicates the +result: +.B 0 +for 'valid', +.B 1 +for 'invalid'. Other bits of this response byte are set by +.B qmail-verify +to give further debugging information; these other bits should +generally be disregarded. + +Although not especially designed as a new protocol, extensions +to qmail-verify could require the query string to be 0 terminated +to separate it from other data to follow; the response packet +could clearly hold more information than just the initial response +byte if required. + +.SH CONTROL FILES +At startup +.B qmail-verify +reads the following qmail control files: +.I control/envnoathost, control/locals, control/percenthack, control/virtualdomains. +If changes are made to any of these files, +.B qmail-verify +should be restarted for the changes to take effect in +.B qmail-verify. + +If you are using different machines for +.B qmail-verify +and +.B qmail-smtpd +you should ensure that the machine providing the +.B qmail-verify +service has a full set of control files as well as the mailboxes; the machine +running +.B qmail-smtpd +needs at least +.I control/rcpthosts +to be setup. + +.SH LOGGING +.B qmail-verify +logs each decision it makes to stderr: The address followed by whether +it's valid or not. + +.SH AUTHOR +Andrew Richards, building on the work of Paul Jarc and Dan Bernstein, and with +plenty of help along the way from Russell Nelson, John Levine and Charles Cazabon +amongst others. + +.SH "SEE ALSO" +qmail-smtpd(8). diff -Naur qmail-1.03.orig/qmail-verify.c qmail-1.03_qv/qmail-verify.c --- qmail-1.03.orig/qmail-verify.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/qmail-verify.c 2007-08-23 18:20:13.000000000 +0100 @@ -0,0 +1,437 @@ +/* qmail-verify: Based on Paul Jarc's realrcptto-2006.12.10 patch + * this separates its functionality into a separate + * program that can be invoked by an appropriately + * modified qmail-smtpd. The assumption is that + * qmail-verify will run as root or the UID that + * owns mailboxes in a SingleUID setup, whilst + * qmail-smtpd can continue to run as user qmaild. + * + * Comments have been added to show which parts of + * qmail-1.03 various sections of code relate to. + * + * *This program is written to be used by + * qmail-smtpd communicating with it using UDP. + */ + +#include +#include +#include +#include +#include +#include +#include "auto_break.h" +#include "auto_usera.h" +#include "auto_qmail.h" +#include "byte.h" +#include "case.h" +#include "cdb.h" +#include "constmap.h" +#include "error.h" +#include "fmt.h" +#include "open.h" +#include "str.h" +#include "stralloc.h" +#include "uint32.h" +#include "substdio.h" +#include "getln.h" +#include "env.h" +#include "ip.h" +#include "qmail-verify.h" +#include "stderrbits.h" + +#define GETPW_USERLEN 32 +#define MAXQUERYSIZE 900 +/* 900 derived as max packet size from logic in addrparse() in qmail-smtpd.c */ + +stralloc envnoathost = {0}; +stralloc percenthack = {0}; +stralloc locals = {0}; +stralloc vdoms = {0}; + +struct constmap mappercenthack; +struct constmap maplocals; +struct constmap mapvdoms; + +char *quser; + +stralloc response = {0}; + +char *local, *dash, *extension; +struct passwd *pw; + +char ppid_s[FMT_ULONG]; +char *remoteip; +char inbuf[MAXQUERYSIZE+1]; /* +1 for trailing \0 added after receipt */ + +void die_nomem() { eout("Out of memory, exiting.\n"); eflush(); _exit(1); } +void die_control() { eout("Unable to read controls, exiting.\n"); eflush(); _exit(1); } +void die_cdb() { eout("Unable to read cdb user database, exiting.\n"); eflush(); _exit(1); } +void die_sys() { eout("Unable to read system user database, exiting.\n"); eflush(); _exit(1); } +void die_comms() { eout("Misc. comms problem, exiting.\n"); eflush(); _exit(1); } +void die_inuse() { eout("Port already in use, exiting.\n"); eflush(); _exit(1); } +void die_socket() { eout("Error setting up socket, exiting.\n"); eflush(); _exit(1); } + +char *posstr(buf,status) +char *buf; int status; +{ +int pos; + +pos = status & QVPOSBITS; +if (pos==QVPOS1) str_copy(buf,"1"); +else if (pos==QVPOS2) str_copy(buf,"2"); + else if (pos==QVPOS3) str_copy(buf,"3"); + else if (pos==QVPOS4) str_copy(buf,"4"); + else if (pos==QVPOS5) str_copy(buf,"5"); + else if (pos==QVPOS6) str_copy(buf,"6"); + else if (pos==QVPOS7) str_copy(buf,"7"); + else if (pos==QVPOS8) str_copy(buf,"8"); + else if (pos==QVPOS9) str_copy(buf,"9"); + else if (pos==QVPOS10) str_copy(buf,"10"); + else if (pos==QVPOS11) str_copy(buf,"11"); + else if (pos==QVPOS12) str_copy(buf,"12"); + else if (pos==QVPOS13) str_copy(buf,"13"); + else if (pos==QVPOS14) str_copy(buf,"14"); + else if (pos==QVPOS15) str_copy(buf,"15"); + else str_copy(buf,"??"); +return buf; +} + +char posbuf[10]; /* Large enough for anything posstr() will put in it. */ + +int allowaddr(addr,ret) +char *addr; +int ret; +{ +/*eout3("[DEBUG] Pos ",posstr(posbuf,ret),", ");*/ +/*eout3("qmail-verify: ",addr," permitted.\n");*/ + eout4(addr," permitted for ",quser?quser:"UNKNOWN",".\n"); + eflush(); + return ret; +} + +int denyaddr(addr,ret) +char *addr; +int ret; +{ +/*eout3("[DEBUG] Pos ",posstr(posbuf,ret),", ");*/ +/*eout3("qmail-verify: ",addr," denied.\n");*/ + eout2(addr," denied.\n"); + eflush(); + return ret; +} + +int stat_error(path,staterror,ret) +char* path; +int staterror,ret; +{ +/*eout3("[DEBUG] Pos ",posstr(posbuf,ret),", ");*/ +/*eout5("qmail-verify: Unable to stat ",path,": ",error_str(staterror),".\n");*/ + eout5("Unable to stat ",path,": ",error_str(staterror),".\n"); + eflush(); + return ret; +} + +int userext() /* from qmail-getpw.c */ +{ + char username[GETPW_USERLEN]; + struct stat st; + + extension = local + str_len(local); + for (;;) { + if (extension - local < sizeof(username)) + if (!*extension || (*extension == *auto_break)) { + byte_copy(username,extension - local,local); + username[extension - local] = 0; + case_lowers(username); + errno = 0; + pw = getpwnam(username); + if (errno == error_txtbsy) die_sys(); + if (pw) + if (pw->pw_uid) + if (stat(pw->pw_dir,&st) == 0) { + if (st.st_uid == pw->pw_uid) { + dash = ""; + if (*extension) { ++extension; dash = "-"; } + return 1; + } + } + else + if (error_temp(errno)) die_sys(); + } + if (extension == local) return 0; + --extension; + } +} + +int verifyaddr(addr) +char *addr; +{ + char *homedir; + /* static since they get re-used on each call to verifyaddr(). Note + that they don't need resetting since initial use is always with + stralloc_copys() except wildchars (reset with ...len=0 below). */ + static stralloc localpart = {0}; + static stralloc lower = {0}; + static stralloc nughde = {0}; + static stralloc wildchars = {0}; + static stralloc safeext = {0}; + static stralloc qme = {0}; + unsigned int i,at; + wildchars.len=0; + + /* qmail-send:rewrite */ + if (!stralloc_copys(&localpart,addr)) die_nomem(); + i = byte_rchr(localpart.s,localpart.len,'@'); + if (i == localpart.len) { + if (!stralloc_cats(&localpart,"@")) die_nomem(); + if (!stralloc_cat(&localpart,&envnoathost)) die_nomem(); + } + while (constmap(&mappercenthack,localpart.s + i + 1,localpart.len - i - 1)) { + unsigned int j = byte_rchr(localpart.s,i,'%'); + if (j == i) break; + localpart.len = i; + i = j; + localpart.s[i] = '@'; + } + at = byte_rchr(localpart.s,localpart.len,'@'); + if (constmap(&maplocals,localpart.s + at + 1,localpart.len - at - 1)) { + localpart.len = at; + localpart.s[at] = '\0'; + } else { + unsigned int xlen,newlen; + char *x; + for (i = 0;;++i) { + if (i > localpart.len) return allowaddr(addr,ADDR_OK|QVPOS1); + if (!i || (i == at + 1) || (i == localpart.len) || + ((i > at) && (localpart.s[i] == '.'))) { + x = constmap(&mapvdoms,localpart.s + i,localpart.len - i); + if (x) break; + } + } + if (!*x) return allowaddr(addr,ADDR_OK|QVPOS2); + xlen = str_len(x) + 1; /* +1 for '-' */ + newlen = xlen + at + 1; /* +1 for \0 */ + if (xlen < 1 || newlen - 1 < xlen || newlen < 1 || + !stralloc_ready(&localpart,newlen)) + die_nomem(); + localpart.s[newlen - 1] = '\0'; + byte_copyr(localpart.s + xlen,at,localpart.s); + localpart.s[xlen - 1] = '-'; + byte_copy(localpart.s,xlen - 1,x); + localpart.len = newlen; + } + + /* qmail-lspawn:nughde_get */ + { + /* qmail-lspawn lines 83-128 */ + int r,fd,flagwild; + + if (!stralloc_copys(&lower,"!")) die_nomem(); + if (!stralloc_cats(&lower,localpart.s)) die_nomem(); + if (!stralloc_0(&lower)) die_nomem(); + case_lowerb(lower.s,lower.len); + + if (!stralloc_copys(&nughde,"")) die_nomem(); + + fd = open_read("users/cdb"); + if (fd == -1) { + if (errno != error_noent) die_cdb(); + } + else + { /* This section parses users/cdb file */ + uint32 dlen; + + r = cdb_seek(fd,"",0,&dlen); + if (r != 1) die_cdb(); + if (!stralloc_ready(&wildchars,(unsigned int) dlen)) die_nomem(); + wildchars.len = dlen; + if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) die_cdb(); + + i = lower.len; + flagwild = 0; + + do { /* i > 0 */ + if (!flagwild || (i == 1) || + (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) + { + r = cdb_seek(fd,lower.s,i,&dlen); + if (r == -1) die_cdb(); + if (r == 1) + { + char *x; + if (!stralloc_ready(&nughde,(unsigned int) dlen)) die_nomem(); + nughde.len = dlen; + if (cdb_bread(fd,nughde.s,nughde.len) == -1) die_cdb(); + if (flagwild) + if (!stralloc_cats(&nughde,localpart.s + i - 1)) die_nomem(); + if (!stralloc_0(&nughde)) die_nomem(); + close(fd); + /* maybe based on qmail-lspawn lines 190-214 */ + x=nughde.s; + quser=nughde.s; + /* skip username */ + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return allowaddr(addr,ADDR_OK|QVPOS3); + ++x; + /* skip uid */ + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return allowaddr(addr,ADDR_OK|QVPOS4); + ++x; + /* skip gid */ + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return allowaddr(addr,ADDR_OK|QVPOS5); + ++x; + /* skip homedir */ + homedir=x; + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return allowaddr(addr,ADDR_OK|QVPOS6); + ++x; + /* skip dash */ + dash=x; + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return allowaddr(addr,ADDR_OK|QVPOS7); + ++x; + extension=x; + goto got_nughde; + } + } + /* qmail-lspawn lines 132-137 */ + --i; + flagwild = 1; + } while (i); + close(fd); + } + } + + /* qmail-getpw lines 61-70 */ + local = localpart.s; + quser = local; + if (!userext()) { + extension = local; + dash = "-"; + pw = getpwnam(auto_usera); + quser = auto_usera; + } + if (!pw) return denyaddr(addr,ADDR_NOK|QVPOS8); + if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem(); + if (!stralloc_0(&nughde)) die_nomem(); + homedir=nughde.s; + + got_nughde: + + /* qmail-local:qmesearch, note qmeexists() becomes stralloc_0 + stat() */ + if (!*dash) return allowaddr(addr,ADDR_OK|QVPOS9); + if (!stralloc_copys(&safeext,extension)) die_nomem(); + case_lowerb(safeext.s,safeext.len); + for (i = 0;i < safeext.len;++i) + if (safeext.s[i] == '.') + safeext.s[i] = ':'; + { + /* qmail-local lines 383-388 */ + struct stat st; + int i; + if (!stralloc_copys(&qme,homedir)) die_nomem(); + if (!stralloc_cats(&qme,"/.qmail")) die_nomem(); + if (!stralloc_cats(&qme,dash)) die_nomem(); + if (!stralloc_cat(&qme,&safeext)) die_nomem(); + if (!stralloc_0(&qme)) die_nomem(); +/* e.g. homedir/.qmail-localpart */ + if (stat(qme.s,&st) == 0) return allowaddr(addr,ADDR_OK|QVPOS10); + if (errno != error_noent) { + return stat_error(qme.s,errno, STATERR|QVPOS11); /* Maybe not running as root so access denied */ + } + /* qmail-local lines 398-404 */ + for (i = safeext.len;i >= 0;--i) + if (!i || (safeext.s[i - 1] == '-')) { + if (!stralloc_copys(&qme,homedir)) die_nomem(); + if (!stralloc_cats(&qme,"/.qmail")) die_nomem(); + if (!stralloc_cats(&qme,dash)) die_nomem(); + if (!stralloc_catb(&qme,safeext.s,i)) die_nomem(); + if (!stralloc_cats(&qme,"default")) die_nomem(); + if (!stralloc_0(&qme)) die_nomem(); +/* e.g. homedir/.qmail-[xxx-]default */ + if (stat(qme.s,&st) == 0) return allowaddr(addr,ADDR_OK|QVPOS12); + if (errno != error_noent) /* Maybe not running as root so access denied */ + return stat_error(qme.s,errno,STATERR|QVPOS13); + } + return denyaddr(addr,ADDR_NOK|QVPOS14); + } + return denyaddr(addr,ADDR_NOK|QVPOS15); /* Not sure under what conditions this line triggered, if any */ +} + +int main() +{ + int n, sock; + char result; + socklen_t clientaddrlen; + struct sockaddr_in sin, clientaddr; + unsigned long lport; /* for scan_ulong, scan_uint not in qmail src */ + struct ip_address i; + char *s; + + if (chdir(auto_qmail) == -1) die_control(); + + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) + die_control(); + + if (control_readfile(&locals,"control/locals",1) != 1) die_control(); + if (!constmap_init(&maplocals,locals.s,locals.len,0)) die_nomem(); + switch(control_readfile(&percenthack,"control/percenthack",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mappercenthack,"",0,0)) die_nomem(); + case 1: + if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) + die_nomem(); + } + switch(control_readfile(&vdoms,"control/virtualdomains",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mapvdoms,"",0,1)) die_nomem(); + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) die_nomem(); + } + if (!(s = env_get("LISTEN"))) s=DEFAULTQVIP; + + /* Re-read control files above on SIGHUP? */ + + if (!(n=ip_scan(s,&i))) ip_scan(DEFAULTQVIP,&i); + s+=n; if ((*s==':') && scan_ulong(s+1,&lport)) ; else lport=DEFAULTQVPORT; + + byte_zero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(lport); +/*sin.sin_len = sizeof(sin); (optional, not defined on all systems) */ + byte_copy(&sin.sin_addr, sizeof(i),&i); + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) die_socket(); + if (bind(sock, (struct sockaddr *) &sin, sizeof(sin))< 0) die_inuse(); + + { char tmps1[IPFMT+1], tmps2[FMT_ULONG+1]; + tmps1[ip_fmt(tmps1,&i)]=0; + tmps2[fmt_ulong(tmps2,ntohs(sin.sin_port))]=0; + eout5("Listening on address: ",tmps1,", port ",tmps2,".\n"); eflush(); + } + for (;;) + { + clientaddrlen=sizeof(clientaddr); /* Read & set by recvfrom */ + n = recvfrom(sock,inbuf, MAXQUERYSIZE, 0, (struct sockaddr *)&clientaddr, &clientaddrlen); + if (n<=0) /* <0 for error, 0 for empty packet. */ + { + eout("Error receiving packet.\n"); eflush(); + continue; + } + inbuf[n]='\0'; /* Turn it into a string ('\0' terminated) */ + + quser = 0; + result=(char)verifyaddr(inbuf); + + /* 'short' response packet - just the status byte - use the line below to replace 'long' version */ + /* if (sendto(sock,&result,1,0,(struct sockaddr *)&clientaddr,sizeof(clientaddr)) < 0) die_comms(); */ + + /* 'long' response packet - status byte + controlling user (quser) */ + stralloc_ready(&response, 2); + stralloc_copyb(&response, &result, 1); + if (quser) stralloc_cats(&response, quser); + if (response.len > QVRESPONSELEN) response.len = QVRESPONSELEN; /* Trim oversize response */ + if (sendto(sock,response.s,response.len,0,(struct sockaddr *)&clientaddr,sizeof(clientaddr)) < 0) die_comms(); + + } +} diff -Naur qmail-1.03.orig/qmail-verify.h qmail-1.03_qv/qmail-verify.h --- qmail-1.03.orig/qmail-verify.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/qmail-verify.h 2007-08-23 17:48:41.000000000 +0100 @@ -0,0 +1,34 @@ +#define DEFAULTQVPORT 11113 +#define DEFAULTQVIP "127.0.0.1" +#define DEFAULTQVTIMEOUT 5 + +#define GETPW_USERLEN 32 +/* Response length is status byte + username length */ +#define QVRESPONSELEN (1+GETPW_USERLEN) + +/* ADDR_NOK, ADDR_OK, ADDR_NOK_TEMP get ORed with POSx for possible debugging. */ +#define ADDR_NOK_TEMP 0x02 +#define ADDR_NOK 0x01 +#define ADDR_OK 0x00 +/* Treat a stat() error as 'valid address'; maybe not running with sufficient rights */ +#define STATERR ADDR_OK +/* Which bits show OK / NOK: */ +#define QVRESULTBITS 0x0f + +#define QVPOS1 0x10 +#define QVPOS2 0x20 +#define QVPOS3 0x30 +#define QVPOS4 0x40 +#define QVPOS5 0x50 +#define QVPOS6 0x60 +#define QVPOS7 0x70 +#define QVPOS8 0x80 +#define QVPOS9 0x90 +#define QVPOS10 0xa0 +#define QVPOS11 0xb0 +#define QVPOS12 0xc0 +#define QVPOS13 0xd0 +#define QVPOS14 0xe0 +#define QVPOS15 0xf0 +/* Which bits show QVPOSx: */ +#define QVPOSBITS 0xf0 diff -Naur qmail-1.03.orig/sockbits.c qmail-1.03_qv/sockbits.c --- qmail-1.03.orig/sockbits.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/sockbits.c 2007-08-23 17:17:25.000000000 +0100 @@ -0,0 +1,25 @@ +#include "sockbits.h" +#include "stralloc.h" +#include +#include + +int query_skt(fd,queryp,responsep,maxresponsesize,timeout,errfn) +int fd; +stralloc *queryp; +char *responsep; +int maxresponsesize; +int timeout; +void (*errfn)(); +{ + fd_set rfs; + struct timeval tv; + int nbytes; + + if (write(fd,queryp->s,queryp->len) < 0) (*errfn)(); + tv.tv_sec=timeout; tv.tv_usec=0; + FD_ZERO(&rfs); FD_SET(fd,&rfs); + if (select(fd+1,&rfs,(fd_set *) 0,(fd_set *) 0,&tv) <= 0) (*errfn)(); /* 0 timeout or -1 error */ + nbytes = read(fd,responsep,maxresponsesize); + if (nbytes <= 0) (*errfn)(); /* 0 no output or -1 error */ + return (nbytes); +} diff -Naur qmail-1.03.orig/sockbits.h qmail-1.03_qv/sockbits.h --- qmail-1.03.orig/sockbits.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/sockbits.h 2007-07-04 20:59:42.000000000 +0100 @@ -0,0 +1 @@ +extern int query_skt(); diff -Naur qmail-1.03.orig/stderrbits.c qmail-1.03_qv/stderrbits.c --- qmail-1.03.orig/stderrbits.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/stderrbits.c 2007-07-05 18:41:58.000000000 +0100 @@ -0,0 +1,9 @@ +#include +#include "stderrbits.h" +#include "substdio.h" + +char sserrbuf[128]; +static substdio sserr = SUBSTDIO_FDBUF(write,2,sserrbuf,sizeof sserrbuf); + +void eout(s) char *s; {substdio_puts(&sserr,s);} +void eflush() { substdio_flush(&sserr); } diff -Naur qmail-1.03.orig/stderrbits.h qmail-1.03_qv/stderrbits.h --- qmail-1.03.orig/stderrbits.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/stderrbits.h 2007-07-05 18:46:24.000000000 +0100 @@ -0,0 +1,10 @@ +extern void eout(); +#define eout2(s1,s2) { eout(s1); eout(s2); } +#define eout3(s1,s2,s3) { eout(s1); eout2(s2,s3); } +#define eout4(s1,s2,s3,s4) { eout(s1); eout3(s2,s3,s4); } +#define eout5(s1,s2,s3,s4,s5) { eout(s1); eout4(s2,s3,s4,s5); } +#define eout6(s1,s2,s3,s4,s5,s6) { eout(s1); eout5(s2,s3,s4,s5,s6); } +#define eout7(s1,s2,s3,s4,s5,s6,s7) { eout(s1); eout6(s2,s3,s4,s5,s6,s7); } +#define eout8(s1,s2,s3,s4,s5,s6,s7,s8) { eout(s1); eout7(s2,s3,s4,s5,s6,s7,s8); } +#define eout9(s1,s2,s3,s4,s5,s6,s7,s8,s9) { eout(s1); eout8(s2,s3,s4,s5,s6,s7,s8,s9); } +extern void eflush(); diff -Naur qmail-1.03.orig/TARGETS qmail-1.03_qv/TARGETS --- qmail-1.03.orig/TARGETS 1998-06-15 11:53:16.000000000 +0100 +++ qmail-1.03_qv/TARGETS 2007-07-05 20:18:51.000000000 +0100 @@ -252,6 +252,12 @@ qmail-qmtpd qmail-smtpd.o qmail-smtpd +qmail-verify.o +qmail-verify +verifyrcpt.o +udpbits.o +sockbits.o +stderrbits.o sendmail.o sendmail tcp-env.o @@ -351,6 +357,7 @@ qmail-qmqpd.0 qmail-qmtpd.0 qmail-smtpd.0 +qmail-verify.0 tcp-env.0 qmail-newmrh.8 qmail-newmrh.0 diff -Naur qmail-1.03.orig/udpbits.c qmail-1.03_qv/udpbits.c --- qmail-1.03.orig/udpbits.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/udpbits.c 2007-07-05 19:06:51.000000000 +0100 @@ -0,0 +1,24 @@ +#include "udpbits.h" +#include "ip.h" +#include "byte.h" +#include +#include +#include + +int connect_udp(ip,port,errfn) +struct ip_address ip; +unsigned int port; +void (*errfn)(); +{ + struct sockaddr_in sout; + int fd; + + byte_zero(&sout,sizeof(sout)); + sout.sin_port = htons(port); + sout.sin_family=AF_INET; + byte_copy(&sout.sin_addr,sizeof(ip),&ip); +/*sout.sin_len = sizeof(sout); Commented out since optional & sin_len not defined on all OSes */ + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) (*errfn)(); + if (connect(fd,(struct sockaddr *)&sout,sizeof(sout)) < 0) (*errfn)(); + return fd; +} diff -Naur qmail-1.03.orig/udpbits.h qmail-1.03_qv/udpbits.h --- qmail-1.03.orig/udpbits.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/udpbits.h 2007-07-04 21:35:33.000000000 +0100 @@ -0,0 +1 @@ +extern int connect_udp(); diff -Naur qmail-1.03.orig/verifyrcpt.c qmail-1.03_qv/verifyrcpt.c --- qmail-1.03.orig/verifyrcpt.c 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/verifyrcpt.c 2007-08-23 17:44:57.000000000 +0100 @@ -0,0 +1,54 @@ +#include "verifyrcpt.h" +#include "qmail-verify.h" +#include "ip.h" +#include "stralloc.h" +#include "scan.h" +#include + +static int verifyrcpt_initialised=0; +static int sockfd; + +int flagdenyany=0; + +void verifyrcpt_init(svr,errfn) +char *svr; void (*errfn)(); +{ + struct ip_address i; + unsigned long lport; /* for scan_ulong, scan_uint not in qmail src */ + int n; + + if (!svr) + { + ip_scan(DEFAULTQVIP,&i); + lport = DEFAULTQVPORT; + } + else /* Set IP and/or port if available in string svr */ + { + if (!(n=ip_scan(svr,&i))) ip_scan(DEFAULTQVIP,&i); + svr += n; /* n is 0 if no IP found */ + if (!((*svr==':') && scan_ulong(svr+1,&lport))) lport=DEFAULTQVPORT; + } + sockfd = connect_udp(i,(unsigned int)lport,errfn); + verifyrcpt_initialised=1; +} + +int verifyrcpt(r,defer,errfn) +stralloc *r; int defer; void (*errfn)(); +{ + struct timeval timeout; + char qvresponse[QVRESPONSELEN+1]; /* +1 for '\0' at end */ + int result,n; + + if (!verifyrcpt_initialised) (*errfn)(); + if (defer && flagdenyany) return ADDR_OK; /* Optional short circuit; "Controlling user" not discovered; remove this line if it's needed */ + n = query_skt(sockfd,r,qvresponse,QVRESPONSELEN,DEFAULTQVTIMEOUT,errfn); + result = qvresponse[0] & QVRESULTBITS; + qvresponse[ ( (n > QVRESPONSELEN) ? QVRESPONSELEN : n) ] = '\0'; + /* "Controlling user" available in qvresponse+1 for logging etc. */ + + if (result == ADDR_OK) + return ADDR_OK; + /* NOK: */ + flagdenyany = 1; + return defer?ADDR_OK:ADDR_NOK; /* ADDR_OK if we're rejecting later */ +} diff -Naur qmail-1.03.orig/verifyrcpt.h qmail-1.03_qv/verifyrcpt.h --- qmail-1.03.orig/verifyrcpt.h 1970-01-01 01:00:00.000000000 +0100 +++ qmail-1.03_qv/verifyrcpt.h 2007-07-05 11:44:08.000000000 +0100 @@ -0,0 +1,3 @@ +extern int flagdenyany; +void verifyrcpt_init(); +int verifyrcpt();