diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..8ae1c07 --- /dev/null +++ b/INSTALL @@ -0,0 +1,15 @@ +Written by Cory Wright - cwright /at/ standblue.net + +To install quser just unpack the tarball, cd into the quser src +directory, and do the normal make and make install routine. + + tar xzf quser-0.8.tar.gz + cd quser-0.8 + make + make test (optional) + sudo make install + +cmaildir will be installed in /usr/local/bin/ while validate-sender, +validate-recipient, mybadmailto, and mybadmailfrom will be installed +in /var/qmail/bin/ . If you don't like this then just change the +Makefile, but I feel that these are the best places for these programs. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..97add09 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +# $Id: Makefile,v 1.8 2002/10/01 04:46:56 cwright Exp $ + +all: validate-sender validate-recipient mybadmailfrom mybadrcptto cmaildir + +validate-sender: validate.c quser.o + gcc -Wall -DSENDER -o validate-sender quser.o validate.c + +validate-recipient: validate.c quser.o + gcc -Wall -DRECIPIENT -o validate-recipient quser.o validate.c + +mybadmailfrom: mybadenvelope.c quser.o + gcc -Wall -DMAILFROM -o mybadmailfrom quser.o mybadenvelope.c + +mybadrcptto: mybadenvelope.c quser.o + gcc -Wall -DRCPTTO -o mybadrcptto quser.o mybadenvelope.c + +cmaildir: cmaildir.c quser.o + gcc -o cmaildir quser.o cmaildir.c + +quser.o: quser.c quser.h + gcc -Wall -O1 -c quser.c + +install: validate-sender validate-recipient \ + mybadmailfrom mybadrcptto cmaildir + /usr/bin/install -s -g qmail -c validate-sender /var/qmail/bin/ + /usr/bin/install -s -g qmail -c validate-recipient /var/qmail/bin/ + /usr/bin/install -s -g qmail -c mybadmailfrom /var/qmail/bin/ + /usr/bin/install -s -g qmail -c mybadrcptto /var/qmail/bin/ + /usr/bin/install -s -c cmaildir /usr/local/bin/ + +clean: + rm -f core validate-sender validate-recipient mybadmailfrom \ + mybadrcptto cmaildir quser.o + +test: validate-sender validate-recipient mybadmailfrom mybadrcptto test.sh + bash test.sh diff --git a/README b/README new file mode 100644 index 0000000..3f39315 --- /dev/null +++ b/README @@ -0,0 +1,116 @@ +Written by Cory Wright - cwright /at/ standblue.net + +You can find the most up to date documentation online at +http://projects.standblue.net/software/quser/index.moto + +Tools included with quser. + +* cmaildir +* validate-sender +* validate-recipient +* mybadmailfrom +* mybadmailto + + +cmaildir is a short C program for checking Maildir's for +new mail. If run with no arguments cmaildir checks ~/Maildir/ +for new mail and also reports messages in the inbox. If +arguments are passed to cmaildir then it expects them to be +paths to Maildir style directories, but do not include the +/cur/ and /new/ portions, the Maildir is the set together. +I wrote cmaildir because I use Courier-IMAP and it uses +Maildirs for its folders. I have my list addresses setup to +go straight into my IMAP folders so with cmaildir I can run +the following to get a status of all my folders +(including ~/Maildir/): + + shell$ cmaildir ~/Maildir/.Lists.qmail/ ~/Maildir/.Lists.moto/ + You have 6 new messages, 37 saved + +While this may not be that convenient to type at a command +line, its most useful in a .bash_profile file or as a bash +alias. + + + +validate-sender is a little C program I wrote because I needed +to check a sender against a list before allowing it to post to +an address. I had setup an address for people to add addresses +to badmailfrom but only wanted certain users to be able to have +this ability. validate-sender is designed to be called from a +.qmail file after bouncesaying or condredirect. It takes the +filename of the list file as an argument. The list file should +contain a list of addresses or domains, one per line. Domain +entries can either be in the form @site.dom or site.dom. For +example: + + |bouncesaying "You are not in my list of allowed senders" validate-sender /etc/badmailadders + |/usr/local/bin/addtobadmailfrom + +Or, with condredirect to alert me when somebody tries to submit: + + |condredirect myemailaddress validate-sender /etc/badmailadders + |/usr/local/bin/addtobadmailfrom + +validate-sender returns 1 if a match is found, and 0 if no match +is found. 111 is returned if there is an error. I stick validate-sender +in /var/qmail/bin since the only place it will probably ever be +used is with qmail. + +Please note that no method is fool proof against forgeries, +just see what djb has to say. In other words, dont trust your +system to anything like this, it should only be used for trivial +checks. + + +The validate-recipient program is very similar to the validate-sender +program, except it checks against the value of the SMTP envelope recipient +address. This is mainly for use in .qmail-default files. For example, if +all of your mail passes through a mail gateway that forwards mail using +smtproutes then you probably use a .qmail-default file for this. If you +know in advance the complete list of addresses that should be forwarded +then you can block anything else from being forwarded. + + + |bouncesaying "No mailbox by that name." validate-recipient /path/to/addresses.txt + |forward "$DEFAULT"@someotherserver.example.com + + +validate-recipient uses the same exit codes as validate-sender. + +mybadmailfrom is a program that allows individual users to manage +their own badmailfrom lists. If a user is sick of getting mail from +a certain address or domain then all they need to do is add it to +their personal badmailfrom list. Once the list is ready the user can +add a line to their .qmail file before all the rest: + + |mybadmailfrom ~/Maildir/mybadmailfrom + ./Maildir/ + +Although messages from addresses listed in ~/Maildir/mybadmailfrom will +bounce, I should note that this works in a very different way from the +badmailfrom file that qmail-smtpd uses. Addresses listed in +/var/qmail/control/badmailfrom are blocked at the SMTP connection. +Addresses listed in mybadmailfrom must first be accepted into the +system so that qmail-local can run the .qmail file. + + +mybadrcptto is useful for blocking certain recipient addresses in a +.qmail-default file. For example, if you have a qmail gateway that scans +all mail for spam or viruses and forwards accepted mail onto another server, +you may want to block certain invalid or expired addresses. The badmailrcpto +program will allow you to maintain a list of addresses that you dont want to +pass through: + + |mybadrcptto ~/ex-employees.txt + |forward "$DEFAULT"@internal.example.com + +With this setup any mail sent to an address thats listed in the +~/ex-employees.txt file will bounce. All other mail will pass through +and be forwarded to the internal server. There is a patch available for +qmail that will block certain RCPT TO: addresses at the SMTP conversation. +The advantage to using mybadrcptto is that 1) you do not need to patch qmail, +and 2) individual users can implement their own badrcptto lists. The +disadvantage to using the mybadrcptto program as opposed to the patch is +that all email must first be accepted into the system, and only then can +qmail-local run the mybadrcptto program to check the recipient. diff --git a/SECURITY b/SECURITY new file mode 100644 index 0000000..31aed5c --- /dev/null +++ b/SECURITY @@ -0,0 +1,6 @@ +The validate-sender program (as well as the mybadmailfrom program) +only checks the SENDER environment variable. This variable is set +by qmail-command using the value of MAIL FROM: from the SMTP +conversation. This can easily be forged, so you should not trust +anything mission critical to the validate-sender program. It is +only meant to help with trivial checks. diff --git a/addylist b/addylist new file mode 100644 index 0000000..a0b84d4 --- /dev/null +++ b/addylist @@ -0,0 +1,6 @@ +a@b.com +@c.com +d.com +E@F.COM +f@G.COM +G@h.com diff --git a/cmaildir.c b/cmaildir.c new file mode 100644 index 0000000..4a82583 --- /dev/null +++ b/cmaildir.c @@ -0,0 +1,62 @@ +/******************************************************************************* + * * + * This file is part of quser. http://projects.standblue.net/software/quser * + * * + * $Id: cmaildir.c,v 1.4 2002/09/30 15:04:48 cwright Exp $ * + * * + ******************************************************************************/ + +#include +#include +#include +#include "quser.h" + +int main(int argc, char **argv) { + + char *newdir=NULL; + char *curdir=NULL; + char *home=NULL; + char *homemaildir=NULL; + int strsize=0; + int newmessages=0; + int curmessages=0; + int tnew,tcur; + + if(argc == 1) { + if((home=getenv("HOME")) == NULL) { + fprintf(stderr,"cmaildir: $HOME is undefined\n"); + exit(1); + } + strsize=(strlen(home)*sizeof(char))+9; + homemaildir=malloc(strsize); + sprintf(homemaildir,"%s%s",home,"/Maildir"); + *argv = homemaildir; + } else { + *argv++; + } + + while(*argv != NULL) { + strsize=(strlen(*argv)*sizeof(char))+6; + newdir=malloc(strsize); + curdir=malloc(strsize); + sprintf(newdir,"%s%s",*argv,"/new/"); + sprintf(curdir,"%s%s",*argv,"/cur/"); + tnew = getMessages(newdir); + tcur = getMessages(curdir); + newmessages += (tnew > 0) ? tnew : 0; + curmessages += (tcur > 0) ? tcur : 0; + free(newdir); + free(curdir); + *argv++; + } + + printf("You have "); + if(newmessages > 0) + printf("\033[1;32m%i\033[m",newmessages); + else + printf("%i",newmessages); + + printf(" new messages, %i saved\n",curmessages); + return 0; + +} diff --git a/mybadenvelope.c b/mybadenvelope.c new file mode 100644 index 0000000..089e51f --- /dev/null +++ b/mybadenvelope.c @@ -0,0 +1,55 @@ +/******************************************************************************* + * * + * This file is part of quser. http://projects.standblue.net/software/quser * + * * + * $Id: mybadenvelope.c,v 1.1 2002/10/01 04:01:20 cwright Exp $ * + * * + ******************************************************************************/ + +#include +#include +#include +#include "quser.h" + +#ifdef MAILFROM + #define QUSERPROG "mybadmailfrom" + #define ENVVAR "SENDER" +#endif + +#ifdef RCPTTO + #define QUSERPROG "mybadrcptto" + #define ENVVAR "RECIPIENT" +#endif + +int main(int argc, char **argv) { + FILE *fp; + char match[MAX_ADDRESS]; + char *address; + int accept=1; + + if(argc < 2) { + fprintf(stderr,"%s: No address file given",QUSERPROG); + exit(100); + } + + if((address=getenv(ENVVAR))==NULL) { + fprintf(stderr,"%s: No %s set",QUSERPROG,ENVVAR); + exit(100); + } + + if((fp=fopen(*++argv,"r"))==NULL) { + fprintf(stderr,"%s: Could not open %s",QUSERPROG,*argv); + exit(111); + } + + while(fgets(match,MAX_ADDRESS,fp)) { + if(addressmatch(address,match)) { + accept=0; + break; + } + } + fclose(fp); + + return (accept==1) ? 0 : 100; + +} diff --git a/quser.c b/quser.c new file mode 100644 index 0000000..09c246f --- /dev/null +++ b/quser.c @@ -0,0 +1,60 @@ +/******************************************************************************* + * * + * This file is part of quser. * + * * + * $Id: quser.c,v 1.4 2002/09/30 13:43:15 cwright Exp $ * + * * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include "quser.h" + +int addressmatch(char *sender, char match[MAX_ADDRESS]) { + char *endofline; + char *senderdomain; + int matches=0; + int i=0; + int j=0; + + for(i=0;id_name)[0] != '.') ++mesg; + closedir(dir); + } else { + fprintf(stderr,"cmaildir: cannot open dir: %s\n",fldr); + mesg = -1; + } + + return mesg; +} diff --git a/quser.h b/quser.h new file mode 100644 index 0000000..dc3dcb0 --- /dev/null +++ b/quser.h @@ -0,0 +1,17 @@ +/******************************************************************************* + * * + * This file is part of quser. * + * * + * $Id: quser.h,v 1.4 2002/09/30 13:43:15 cwright Exp $ * + * * + ******************************************************************************/ + +#ifndef QUSER_H +#define QUSER_H + +#define MAX_ADDRESS 1024 + +int addressmatch(char *sender, char match[100]); +int getMessages(char *fldr); + +#endif diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..3cadf5f --- /dev/null +++ b/test.sh @@ -0,0 +1,157 @@ +#!/bin/sh + +echo '$Id: test.sh,v 1.5 2002/10/01 04:01:20 cwright Exp $' +echo + +total=$((0)) +passed=$((0)) +failed=$((0)) + +chret() { + should=$1 + actual=$2 + shift + shift + echo + total=$(($total+1)) + echo -n "Should return $should: " + if [ "$should" = "$actual" ]; then + passed=$((passed+1)) + echo "Passed" + else + failed=$((failed+1)) + echo "Failed ($actual)" + fi + echo +} + +echo " * Testing the validate-sender program" +echo + +echo "Testing empty \$SENDER" +./validate-sender list +chret "0" $? + +echo "Testing with no parameters" +env SENDER="a@b.com" ./validate-sender +chret "0" $? + +echo "Testing nonexistant file" +env SENDER="a@b.com" ./validate-sender nolist +chret "111" $? + +echo "Testing a@a.com" +env SENDER="a@a.com" ./validate-sender addylist +chret "0" $? + +echo "Testing a@b.com" +env SENDER="a@b.com" ./validate-sender addylist +chret "1" $? + +echo "Testing a@c.com" +env SENDER="a@c.com" ./validate-sender addylist +chret "1" $? + +echo "Testing a@d.com" +env SENDER="a@d.com" ./validate-sender addylist +chret "1" $? + +echo " * Testing the validate-recipient program" +echo + +echo "Testing empty \$RECIPIENT" +./validate-recipient list +chret "0" $? + +echo "Testing with no parameters" +env RECIPIENT="a@b.com" ./validate-recipient +chret "0" $? + +echo "Testing nonexistant file" +env RECIPIENT="a@b.com" ./validate-recipient nolist +chret "111" $? + +echo "Testing a@a.com" +env RECIPIENT="a@a.com" ./validate-recipient addylist +chret "0" $? + +echo "Testing a@b.com" +env RECIPIENT="a@b.com" ./validate-recipient addylist +chret "1" $? + +echo "Testing a@c.com" +env RECIPIENT="a@c.com" ./validate-recipient addylist +chret "1" $? + +echo "Testing a@d.com" +env RECIPIENT="a@d.com" ./validate-recipient addylist +chret "1" $? + +echo " * Testing the mybadmailfrom program" +echo + +echo "Testing empty \$SENDER" +./mybadmailfrom list +chret "100" $? + +echo "Testing with no parameters" +env SENDER="a@b.com" ./mybadmailfrom +chret "100" $? + +echo "Testing nonexistant file" +env SENDER="a@b.com" ./mybadmailfrom nolist +chret "111" $? + +echo "Testing a@a.com" +env SENDER="a@a.com" ./mybadmailfrom addylist +chret "0" $? + +echo "Testing a@b.com" +env SENDER="a@b.com" ./mybadmailfrom addylist +chret "100" $? + +echo "Testing a@c.com" +env SENDER="a@c.com" ./mybadmailfrom addylist +chret "100" $? + +echo "Testing a@d.com" +env SENDER="a@d.com" ./mybadmailfrom addylist +chret "100" $? + +echo " * Testing the mybadrcptto program" +echo + +echo "Testing empty \$RECIPIENT" +./mybadrcptto list +chret "100" $? + +echo "Testing with no parameters" +env RECIPIENT="a@b.com" ./mybadrcptto +chret "100" $? + +echo "Testing nonexistant file" +env RECIPIENT="a@b.com" ./mybadrcptto nolist +chret "111" $? + +echo "Testing a@a.com" +env RECIPIENT="a@a.com" ./mybadrcptto addylist +chret "0" $? + +echo "Testing a@b.com" +env RECIPIENT="a@b.com" ./mybadrcptto addylist +chret "100" $? + +echo "Testing a@c.com" +env RECIPIENT="a@c.com" ./mybadrcptto addylist +chret "100" $? + +echo "Testing a@d.com" +env RECIPIENT="a@d.com" ./mybadrcptto addylist +chret "100" $? + + + +echo " * Results *" +echo " Tests Taken: $(($total-0))" +echo " Tests Passed: $(($passed-0))" +echo " Tests Failed: $(($failed-0))" diff --git a/validate.c b/validate.c new file mode 100644 index 0000000..919e0fc --- /dev/null +++ b/validate.c @@ -0,0 +1,58 @@ +/******************************************************************************* + * * + * This file is part of quser. http://projects.standblue.net/software/quser * + * * + * $Id: validate.c,v 1.2 2002/10/01 01:22:19 cwright Exp $ * + * * + ******************************************************************************/ + +#include +#include +#include +#include "quser.h" + +#ifdef SENDER + #define QUSERPROG "validate-sender" + #define ENVVAR "SENDER" +#endif + +#ifdef RECIPIENT + #define QUSERPROG "validate-recipient" + #define ENVVAR "RECIPIENT" +#endif + +int main(int argc, char **argv) { + FILE *fp; + char match[MAX_ADDRESS]; + char *address; + int accept=0; + + if(argc < 2) { + fprintf(stderr,"%s: No address list file given",QUSERPROG); + exit(0); + } + + if((address=getenv(ENVVAR))==NULL) { + fprintf(stderr,"%s: No %s set",QUSERPROG,ENVVAR); + exit(0); + } + + if((fp=fopen(*++argv,"r"))==NULL) { + fprintf(stderr,"%s: Could not open %s",QUSERPROG,*argv); + exit(111); + } + + while(fgets(match,MAX_ADDRESS,fp)) { + if(addressmatch(address,match)) { + accept=1; + break; + } + } + fclose(fp); + + /* output results to go into qmail logs */ + printf("%s: Found %smatch ",QUSERPROG,((accept==1)?"":"no ")); + printf("for address %s, %s",address,((accept==1)?"accepting.":"rejecting. ")); + return (accept==1) ? 1 : 0; + +}