123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- #include "common.h"
- #include "smtpd.h"
- #include <ip.h>
- enum {
- NORELAY = 0,
- DNSVERIFY,
- SAVEBLOCK,
- DOMNAME,
- OURNETS,
- OURDOMS,
- IP = 0,
- STRING,
- };
- typedef struct Keyword Keyword;
- struct Keyword {
- char *name;
- int code;
- };
- static Keyword options[] = {
- "norelay", NORELAY,
- "verifysenderdom", DNSVERIFY,
- "saveblockedmsg", SAVEBLOCK,
- "defaultdomain", DOMNAME,
- "ournets", OURNETS,
- "ourdomains", OURDOMS,
- 0, NONE,
- };
- static Keyword actions[] = {
- "allow", ACCEPT,
- "block", BLOCKED,
- "deny", DENIED,
- "dial", DIALUP,
- "delay", DELAY,
- 0, NONE,
- };
- static int hisaction;
- static List ourdoms;
- static List badguys;
- static ulong v4peerip;
- static char* getline(Biobuf*);
- static int cidrcheck(char*);
- static int
- findkey(char *val, Keyword *p)
- {
- for(; p->name; p++)
- if(strcmp(val, p->name) == 0)
- break;
- return p->code;
- }
- char*
- actstr(int a)
- {
- static char buf[32];
- Keyword *p;
- for(p=actions; p->name; p++)
- if(p->code == a)
- return p->name;
- if(a==NONE)
- return "none";
- sprint(buf, "%d", a);
- return buf;
- }
- int
- getaction(char *s, char *type)
- {
- char buf[1024];
- Keyword *k;
- if(s == nil || *s == 0)
- return ACCEPT;
- for(k = actions; k->name != 0; k++){
- snprint(buf, sizeof buf, "/mail/ratify/%s/%s/%s", k->name, type, s);
- if(access(buf,0) >= 0)
- return k->code;
- }
- return ACCEPT;
- }
- int
- istrusted(char *s)
- {
- char buf[1024];
- if(s == nil || *s == 0)
- return 0;
- snprint(buf, sizeof buf, "/mail/ratify/trusted/%s", s);
- return access(buf,0) >= 0;
- }
- void
- getconf(void)
- {
- Biobuf *bp;
- char *cp, *p;
- String *s;
- char buf[512];
- uchar addr[4];
- v4parseip(addr, nci->rsys);
- v4peerip = nhgetl(addr);
- trusted = istrusted(nci->rsys);
- hisaction = getaction(nci->rsys, "ip");
- if(debug){
- fprint(2, "istrusted(%s)=%d\n", nci->rsys, trusted);
- fprint(2, "getaction(%s, ip)=%s\n", nci->rsys, actstr(hisaction));
- }
- snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
- bp = sysopen(buf, "r", 0);
- if(bp == 0)
- return;
- for(;;){
- cp = getline(bp);
- if(cp == 0)
- break;
- p = cp+strlen(cp)+1;
- switch(findkey(cp, options)){
- case NORELAY:
- if(fflag == 0 && strcmp(p, "on") == 0)
- fflag++;
- break;
- case DNSVERIFY:
- if(rflag == 0 && strcmp(p, "on") == 0)
- rflag++;
- break;
- case SAVEBLOCK:
- if(sflag == 0 && strcmp(p, "on") == 0)
- sflag++;
- break;
- case DOMNAME:
- if(dom == 0)
- dom = strdup(p);
- break;
- case OURNETS:
- if (trusted == 0)
- trusted = cidrcheck(p);
- break;
- case OURDOMS:
- while(*p){
- s = s_new();
- s_append(s, p);
- listadd(&ourdoms, s);
- p += strlen(p)+1;
- }
- break;
- default:
- break;
- }
- }
- sysclose(bp);
- }
- /*
- * match a user name. the only meta-char is '*' which matches all
- * characters. we only allow it as "*", which matches anything or
- * an * at the end of the name (e.g., "username*") which matches
- * trailing characters.
- */
- static int
- usermatch(char *pathuser, char *specuser)
- {
- int n;
- n = strlen(specuser)-1;
- if(specuser[n] == '*'){
- if(n == 0) /* match everything */
- return 0;
- return strncmp(pathuser, specuser, n);
- }
- return strcmp(pathuser, specuser);
- }
- static int
- dommatch(char *pathdom, char *specdom)
- {
- int n;
- if (*specdom == '*'){
- if (specdom[1] == '.' && specdom[2]){
- specdom += 2;
- n = strlen(pathdom)-strlen(specdom);
- if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
- return strcmp(pathdom+n, specdom);
- return n;
- }
- }
- return strcmp(pathdom, specdom);
- }
- /*
- * figure out action for this sender
- */
- int
- blocked(String *path)
- {
- String *lpath;
- int action;
- if(debug)
- fprint(2, "blocked(%s)\n", s_to_c(path));
- /* if the sender's IP address is blessed, ignore sender email address */
- if(trusted){
- if(debug)
- fprint(2, "\ttrusted => trusted\n");
- return TRUSTED;
- }
- /* if sender's IP address is blocked, ignore sender email address */
- if(hisaction != ACCEPT){
- if(debug)
- fprint(2, "\thisaction=%s => %s\n", actstr(hisaction), actstr(hisaction));
- return hisaction;
- }
- /* convert to lower case */
- lpath = s_copy(s_to_c(path));
- s_tolower(lpath);
- /* classify */
- action = getaction(s_to_c(lpath), "account");
- if(debug)
- fprint(2, "\tgetaction account %s => %s\n", s_to_c(lpath), actstr(action));
- s_free(lpath);
- return action;
- }
- /*
- * get a canonicalized line: a string of null-terminated lower-case
- * tokens with a two null bytes at the end.
- */
- static char*
- getline(Biobuf *bp)
- {
- char c, *cp, *p, *q;
- int n;
- static char *buf;
- static int bufsize;
- for(;;){
- cp = Brdline(bp, '\n');
- if(cp == 0)
- return 0;
- n = Blinelen(bp);
- cp[n-1] = 0;
- if(buf == 0 || bufsize < n+1){
- bufsize += 512;
- if(bufsize < n+1)
- bufsize = n+1;
- buf = realloc(buf, bufsize);
- if(buf == 0)
- break;
- }
- q = buf;
- for (p = cp; *p; p++){
- c = *p;
- if(c == '\\' && p[1]) /* we don't allow \<newline> */
- c = *++p;
- else
- if(c == '#')
- break;
- else
- if(c == ' ' || c == '\t' || c == ',')
- if(q == buf || q[-1] == 0)
- continue;
- else
- c = 0;
- *q++ = tolower(c);
- }
- if(q != buf){
- if(q[-1])
- *q++ = 0;
- *q = 0;
- break;
- }
- }
- return buf;
- }
- static int
- isourdom(char *s)
- {
- Link *l;
- if(strchr(s, '.') == nil)
- return 1;
- for(l = ourdoms.first; l; l = l->next){
- if(dommatch(s, s_to_c(l->p)) == 0)
- return 1;
- }
- return 0;
- }
- int
- forwarding(String *path)
- {
- char *cp, *s;
- String *lpath;
- if(debug)
- fprint(2, "forwarding(%s)\n", s_to_c(path));
- /* first check if they want loopback */
- lpath = s_copy(s_to_c(s_restart(path)));
- if(nci->rsys && *nci->rsys){
- cp = s_to_c(lpath);
- if(strncmp(cp, "[]!", 3) == 0){
- found:
- s_append(path, "[");
- s_append(path, nci->rsys);
- s_append(path, "]!");
- s_append(path, cp+3);
- s_terminate(path);
- s_free(lpath);
- return 0;
- }
- cp = strchr(cp,'!'); /* skip our domain and check next */
- if(cp++ && strncmp(cp, "[]!", 3) == 0)
- goto found;
- }
- /* if mail is from a trusted IP addr, allow it to forward */
- if(trusted) {
- s_free(lpath);
- return 0;
- }
- /* sender is untrusted; ensure receiver is in one of our domains */
- for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
- *cp = tolower(*cp);
- for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
- *cp = 0;
- if(!isourdom(s)){
- s_free(lpath);
- return 1;
- }
- }
- s_free(lpath);
- return 0;
- }
- int
- masquerade(String *path, char *him)
- {
- char *cp, *s;
- String *lpath;
- int rv = 0;
- if(debug)
- fprint(2, "masquerade(%s)\n", s_to_c(path));
- if(trusted)
- return 0;
- if(path == nil)
- return 0;
- lpath = s_copy(s_to_c(path));
- /* sender is untrusted; ensure receiver is in one of our domains */
- for(cp = s_to_c(lpath); *cp; cp++) /* convert receiver lc */
- *cp = tolower(*cp);
- s = s_to_c(lpath);
- /* scan first element of ! or last element of @ paths */
- if((cp = strchr(s, '!')) != nil){
- *cp = 0;
- if(isourdom(s))
- rv = 1;
- } else if((cp = strrchr(s, '@')) != nil){
- if(isourdom(cp+1))
- rv = 1;
- } else {
- if(isourdom(him))
- rv = 1;
- }
- s_free(lpath);
- return rv;
- }
- /* this is a v4 only check */
- static int
- cidrcheck(char *cp)
- {
- char *p;
- ulong a, m;
- uchar addr[IPv4addrlen];
- uchar mask[IPv4addrlen];
- if(v4peerip == 0)
- return 0;
- /* parse a list of CIDR addresses comparing each to the peer IP addr */
- while(cp && *cp){
- v4parsecidr(addr, mask, cp);
- a = nhgetl(addr);
- m = nhgetl(mask);
- /*
- * if a mask isn't specified, we build a minimal mask
- * instead of using the default mask for that net. in this
- * case we never allow a class A mask (0xff000000).
- */
- if(strchr(cp, '/') == 0){
- m = 0xff000000;
- p = cp;
- for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
- m = (m>>8)|0xff000000;
- /* force at least a class B */
- m |= 0xffff0000;
- }
- if((v4peerip&m) == a)
- return 1;
- cp += strlen(cp)+1;
- }
- return 0;
- }
- int
- isbadguy(void)
- {
- Link *l;
- /* check if this IP address is banned */
- for(l = badguys.first; l; l = l->next)
- if(cidrcheck(s_to_c(l->p)))
- return 1;
- return 0;
- }
- void
- addbadguy(char *p)
- {
- listadd(&badguys, s_copy(p));
- };
- char*
- dumpfile(char *sender)
- {
- int i, fd;
- ulong h;
- static char buf[512];
- char *cp;
- if (sflag == 1){
- cp = ctime(time(0));
- cp[7] = 0;
- if(cp[8] == ' ')
- sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
- else
- sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
- cp = buf+strlen(buf);
- if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
- return "/dev/null";
- h = 0;
- while(*sender)
- h = h*257 + *sender++;
- for(i = 0; i < 50; i++){
- h += lrand();
- sprint(cp, "/%lud", h);
- if(access(buf, 0) >= 0)
- continue;
- fd = syscreate(buf, ORDWR, 0666);
- if(fd >= 0){
- if(debug)
- fprint(2, "saving in %s\n", buf);
- close(fd);
- return buf;
- }
- }
- }
- return "/dev/null";
- }
- char *validator = "/mail/lib/validateaddress";
- int
- recipok(char *user)
- {
- char *cp, *p, c;
- char buf[512];
- int n;
- Biobuf *bp;
- int pid;
- Waitmsg *w;
- if(shellchars(user)){
- syslog(0, "smtpd", "shellchars in user name");
- return 0;
- }
- if(access(validator, AEXEC) == 0)
- switch(pid = fork()) {
- case -1:
- break;
- case 0:
- execl(validator, "validateaddress", user, nil);
- exits(0);
- default:
- while(w = wait()) {
- if(w->pid != pid)
- continue;
- if(w->msg[0] != 0){
- /*
- syslog(0, "smtpd", "validateaddress %s: %s", user, w->msg);
- */
- return 0;
- }
- break;
- }
- }
- snprint(buf, sizeof(buf), "%s/names.blocked", UPASLIB);
- bp = sysopen(buf, "r", 0);
- if(bp == 0)
- return 1;
- for(;;){
- cp = Brdline(bp, '\n');
- if(cp == 0)
- break;
- n = Blinelen(bp);
- cp[n-1] = 0;
- while(*cp == ' ' || *cp == '\t')
- cp++;
- for(p = cp; c = *p; p++){
- if(c == '#')
- break;
- if(c == ' ' || c == '\t')
- break;
- }
- if(p > cp){
- *p = 0;
- if(cistrcmp(user, cp) == 0){
- syslog(0, "smtpd", "names.blocked blocks %s", user);
- Bterm(bp);
- return 0;
- }
- }
- }
- Bterm(bp);
- return 1;
- }
- /*
- * a user can opt out of spam filtering by creating
- * a file in his mail directory named 'nospamfiltering'.
- */
- int
- optoutofspamfilter(char *addr)
- {
- char *p, *f;
- int rv;
- p = strchr(addr, '!');
- if(p)
- p++;
- else
- p = addr;
- rv = 0;
- f = smprint("/mail/box/%s/nospamfiltering", p);
- if(f != nil){
- rv = access(f, 0)==0;
- free(f);
- }
- return rv;
- }
|