--- qregex.c-dist Fri Dec 28 02:08:05 2001 +++ qregex.c Tue Jan 29 18:57:44 2002 @@ -0,0 +1,57 @@ +/* + * qregex (v2) + * $Id: qregex.c,v 2.1 2001/12/28 07:05:21 evan Exp $ + * + * Author : Evan Borgstrom (evan at unixpimps dot org) + * Created : 2001/12/14 23:08:16 + * Modified: $Date: 2001/12/28 07:05:21 $ + * Revision: $Revision: 2.1 $ + * + * Do POSIX regex matching on addresses for anti-relay / spam control. + * It logs to the maillog + * See the qregex-readme file included with this tarball. + * If you didn't get this file in a tarball please see the following URL: + * http://www.unixpimps.org/software/qregex + * + * qregex.c is released under a BSD style copyright. + * See http://www.unixpimps.org/software/qregex/copyright.html + * + * Note: this revision follows the coding guidelines set forth by the rest of + * the qmail code and that described at the following URL. + * http://cr.yp.to/qmail/guarantee.html + * + */ + +#include +#include +#include "qregex.h" + +#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED) +#define REGEXEC(X,Y) regexec(&X, Y, (size_t)0, (regmatch_t *)0, (int)0) + +int matchregex(char *text, char *regex) { + regex_t qreg; + int retval = 0; + + + /* build the regex */ + if ((retval = REGCOMP(qreg, regex)) != 0) { + regfree(&qreg); + return(-retval); + } + + /* execute the regex */ + if ((retval = REGEXEC(qreg, text)) != 0) { + /* did we just not match anything? */ + if (retval == REG_NOMATCH) { + regfree(&qreg); + return(0); + } + regfree(&qreg); + return(-retval); + } + + /* signal the match */ + regfree(&qreg); + return(1); +} --- qregex.h-dist Fri Dec 28 02:08:09 2001 +++ qregex.h Thu Dec 27 13:04:19 2001 @@ -0,0 +1,5 @@ +/* simple header file for the matchregex prototype */ +#ifndef _QREGEX_H_ +#define _QREGEX_H_ +int matchregex(char *text, char *regex); +#endif --- qmail-smtpd.c-dist Fri Dec 28 01:53:50 2001 +++ qmail-smtpd.c Tue Jan 29 18:43:09 2002 @@ -23,6 +23,10 @@ #include "timeoutread.h" #include "timeoutwrite.h" #include "commands.h" +#include "qregex.h" + +#define BMCHECK_BMF 0 +#define BMCHECK_BMT 1 #define MAXHOPS 100 unsigned int databytes = 0; @@ -49,7 +53,8 @@ void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } -void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); } +void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); } +void err_bmt() { out("533 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); } void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); } void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); } void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } @@ -93,10 +98,15 @@ int liphostok = 0; stralloc liphost = {0}; + int bmfok = 0; stralloc bmf = {0}; struct constmap mapbmf; +int bmtok = 0; +stralloc bmt = {0}; +struct constmap mapbmt; + void setup() { char *x; @@ -114,8 +124,11 @@ bmfok = control_readfile(&bmf,"control/badmailfrom",0); if (bmfok == -1) die_control(); - if (bmfok) - if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + bmtok = control_readfile(&bmt,"control/badmailto",0); + if (bmtok == -1) die_control(); + if (!constmap_init(&mapbmt,bmt.s,bmt.len,0)) die_nomem(); if (control_readint(&databytes,"control/databytes") == -1) die_control(); x = env_get("DATABYTES"); @@ -197,14 +210,38 @@ return 1; } -int bmfcheck() +int bmcheck(which) int which; { - int j; - if (!bmfok) return 0; - if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; - j = byte_rchr(addr.s,addr.len,'@'); - if (j < addr.len) - if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; + int i = 0; + int j = 0; + int x = 0; + int negate = 0; + stralloc bmb = {0}; + stralloc curregex = {0}; + + if (which == BMCHECK_BMF) { + if (!stralloc_copy(&bmb,&bmf)) die_nomem(); + } else if (which == BMCHECK_BMT) { + if (!stralloc_copy(&bmb,&bmt)) die_nomem(); + } else { + die_control(); + } + + while (j < bmb.len) { + i = j; + while ((bmb.s[i] != '\0') && (i < bmb.len)) i++; + if (bmb.s[j] == '!') { + negate = 1; + j++; + } + stralloc_copyb(&curregex,bmb.s + j,(i - j)); + stralloc_0(&curregex); + x = matchregex(addr.s, curregex.s); + if ((negate) && (x == 0)) return 1; + if (!(negate) && (x > 0)) return 1; + j = i + 1; + negate = 0; + } return 0; } @@ -218,7 +255,8 @@ int seenmail = 0; -int flagbarf; /* defined if seenmail */ +int flagbarfbmf; /* defined if seenmail */ +int flagbarfbmt; stralloc mailfrom = {0}; stralloc rcptto = {0}; @@ -240,7 +278,7 @@ void smtp_mail(arg) char *arg; { if (!addrparse(arg)) { err_syntax(); return; } - flagbarf = bmfcheck(); + if (bmfok) flagbarfbmf = bmcheck(BMCHECK_BMF); seenmail = 1; if (!stralloc_copys(&rcptto,"")) die_nomem(); if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); @@ -250,7 +288,9 @@ void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } if (!addrparse(arg)) { err_syntax(); return; } - if (flagbarf) { err_bmf(); return; } + if ((!flagbarfbmf) && (bmtok)) { flagbarfbmt = bmcheck(BMCHECK_BMT); } + if (flagbarfbmf) { err_bmf(); return; } + if (flagbarfbmt) { err_bmt(); return; } if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); --- Makefile-dist Mon Jun 15 06:53:16 1998 +++ Makefile Thu Dec 27 14:16:02 2001 @@ -1532,12 +1532,12 @@ ./compile qmail-showctl.c qmail-smtpd: \ -load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ +load qmail-smtpd.o rcpthosts.o qregex.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ fs.a auto_qmail.o socket.lib - ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ + ./load qmail-smtpd 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 \ @@ -1680,6 +1680,10 @@ compile rcpthosts.c cdb.h uint32.h byte.h open.h error.h control.h \ constmap.h stralloc.h gen_alloc.h rcpthosts.h ./compile rcpthosts.c + +qregex.o: \ +compile qregex.c qregex.h + ./compile qregex.c readsubdir.o: \ compile readsubdir.c readsubdir.h direntry.h fmt.h scan.h str.h \ --- TARGETS-dist Mon Jun 15 06:53:16 1998 +++ TARGETS Thu Dec 27 14:16:19 2001 @@ -252,6 +252,7 @@ qmail-qmtpd qmail-smtpd.o qmail-smtpd +qregex.o sendmail.o sendmail tcp-env.o --- README.qregex-dist Fri Dec 28 01:53:13 2001 +++ README.qregex Fri Dec 28 01:48:28 2001 @@ -0,0 +1,110 @@ +QREGEX (v2) - README [12/28/01] +A Regular Expression matching patch for qmail 1.03 + + +OVERVIEW: + +qregex adds the ability to match address evelopes via Regular Expressions (REs) +in the qmail-smtpd process. It has the abiltiy to match both `mail from` and +`rcpt to` commands with no load at all the parent process. It follows all the +base rules that are set out with qmail (ie using control files) so it makes for +easy integretion into an existing setup (see the install instructions for more info). +The v2 noting is because qregex was re-written to better conform to the security +gaurantee set forth by the author. The original version used stdio.h and stdlib.h +for reading the control files where as v2 now uses all stralloc functions which +are much more regulated against buffer overruns and the likes. +See: http://cr.yp.to/qmail/guarantee.html + + + +PLATFORMS: + +qregex has been built and tested on the following platforms. I'm sure it won't have +any problems on any platform that qmail will run on (providing they provide a regex +interface) but if you run into problems let me know. + + - Solaris 2.7 (7, SunOS 5.7) + - Solaris 2.8 (8, SunOS 5.8) + - OpenBSD 2.8 + - OpenBSD 2.9 + - FreeBSD 4.3-RELEASE + - FreeBSD 5.0-CURRENT + - Linux + + + +INSTALLATION INSTRUCTIONS: + +Installation is very simple, there is only one requirement. You need to use the GNU +version of the patch utility (http://www.gnu.org/software/patch/patch.html). +(For Solaris 8 users like me it is installed as 'gpatch') + +- If this is a new setup. +Uncompress and untar the qmail archive, copy the 'qregex.patch' file into the new +qmail-1.03 directory and run "patch < qregex.patch" +Follow the instructions as per the included qmail INSTALL file. +Once you are done come back to this file and read the section on the control files. + +- If this is an existing setup. +FIRST: create your control files (see below). +Copy the 'qregex.patch' file into your existing qmail source directory. +Run "patch < qregex.patch" then "make qmail-smtpd". Now run ./qmail-smtpd and test +your new rules to make sure they work as expected. + +Install the new binary by cd'ing to /var/qmail/bin and as root (in one command) +copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the +source directory to 'qmail-smtpd'. +(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd) + + + +CONTROL FILES: + +qregex provides you with two new control files. +The first (which really isn't new) is "control/badmailfrom". This file used to be +used to statically match addresses and now will contain your REs for matching from +the 'mail from' command. +The second is "control/badmailto", it is the exact same as the first except it matches +against the 'rcpt to' command. + +If you prefer you can symlink the two files (ln -s badmailfrom badmailto) and only +need to maintain one set of rules. Beware this might cause problems in certian +setups. + + Here's an example "badmailfrom" file. + ----------------------------------- + # drop everything containing the word spam + .*spam.* + # force users to fully qualify themselves (ie deny "user", accept "user@domain") + !@ + ----------------------------------- + + And "badmailto" (a litte more interesting) + ----------------------------------- + # must not contain invalid characters, brakets or multiple @'s + [\W\D!%#:\*\^] + [\(\)] + [\{\}] + @.*@ + ----------------------------------- + +Also you can use the non-RE character '!' to start a RE to signal to qregex to negate the +action. As used above in the badmailfrom file, by negating the @ symbol qregex will signal +qmail-smtpd to deny the 'mail from' command whenever the address doesn't contain an @ symbol. + + +INTERNALS: + +qregex (or regexmatch as the function is called) will be called during both the +`rcpt to` and `mail from` handling routenes in "qmail-smtpd.c". When called it will +read the proper control file then one by one compile and execute the regex on the +envelope passed into qmail-smtpd. If the regex matches it returns TRUE (1) and the +qmail-smtpd process will deny the user the ability to continue. +If you change anything and think it betters this patch please send me a new diff file +so I can take a peek. + + +CONTACT: +All comments/questions/critisim welcomed... + www : http://www.unixpimps.org/software/qregex + email: evan at unixpimps dot org