URI:
       tspam.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tspam.c (10367B)
       ---
            1 #include "common.h"
            2 #include "smtpd.h"
            3 #include <ip.h>
            4 
            5 enum {
            6         NORELAY = 0,
            7         DNSVERIFY,
            8         SAVEBLOCK,
            9         DOMNAME,
           10         OURNETS,
           11         OURDOMS,
           12 
           13         IP = 0,
           14         STRING
           15 };
           16 
           17 
           18 typedef struct Keyword Keyword;
           19 
           20 struct Keyword {
           21         char        *name;
           22         int        code;
           23 };
           24 
           25 static Keyword options[] = {
           26         "norelay",                NORELAY,
           27         "verifysenderdom",        DNSVERIFY,
           28         "saveblockedmsg",        SAVEBLOCK,
           29         "defaultdomain",        DOMNAME,
           30         "ournets",                OURNETS,
           31         "ourdomains",                OURDOMS,
           32         0,                        NONE
           33 };
           34 
           35 static Keyword actions[] = {
           36         "allow",                ACCEPT,
           37         "block",                BLOCKED,
           38         "deny",                        DENIED,
           39         "dial",                        DIALUP,
           40         "delay",                DELAY,
           41         0,                        NONE
           42 };
           43 
           44 static        int        hisaction;
           45 static        List        ourdoms;
           46 static        List         badguys;
           47 static        ulong        v4peerip;
           48 
           49 static        char*        getline(Biobuf*);
           50 static        int        cidrcheck(char*);
           51 
           52 static int
           53 findkey(char *val, Keyword *p)
           54 {
           55 
           56         for(; p->name; p++)
           57                 if(strcmp(val, p->name) == 0)
           58                                 break;
           59         return p->code;
           60 }
           61 
           62 char*
           63 actstr(int a)
           64 {
           65         static char buf[32];
           66         Keyword *p;
           67 
           68         for(p=actions; p->name; p++)
           69                 if(p->code == a)
           70                         return p->name;
           71         if(a==NONE)
           72                 return "none";
           73         sprint(buf, "%d", a);
           74         return buf;
           75 }
           76 
           77 int
           78 getaction(char *s, char *type)
           79 {
           80         char buf[1024];
           81         Keyword *k;
           82 
           83         if(s == nil || *s == 0)
           84                 return ACCEPT;
           85 
           86         for(k = actions; k->name != 0; k++){
           87                 snprint(buf, sizeof buf, "%s/mail/ratify/%s/%s/%s",
           88                         get9root(), k->name, type, s);
           89                 if(access(buf,0) >= 0)
           90                         return k->code;
           91         }
           92         return ACCEPT;
           93 }
           94 
           95 int
           96 istrusted(char *s)
           97 {
           98         char buf[1024];
           99 
          100         if(s == nil || *s == 0)
          101                 return 0;
          102 
          103         snprint(buf, sizeof buf, "%s/mail/ratify/trusted/%s", get9root(), s);
          104         return access(buf,0) >= 0;
          105 }
          106 
          107 void
          108 getconf(void)
          109 {
          110         Biobuf *bp;
          111         char *cp, *p;
          112         String *s;
          113         char buf[512];
          114         uchar addr[4];
          115 
          116         v4parseip(addr, nci->rsys);
          117         v4peerip = nhgetl(addr);
          118 
          119         trusted = istrusted(nci->rsys);
          120         hisaction = getaction(nci->rsys, "ip");
          121         if(debug){
          122                 fprint(2, "istrusted(%s)=%d\n", nci->rsys, trusted);
          123                 fprint(2, "getaction(%s, ip)=%s\n", nci->rsys, actstr(hisaction));
          124         }
          125         snprint(buf, sizeof(buf), "%s/smtpd.conf", UPASLIB);
          126         bp = sysopen(buf, "r", 0);
          127         if(bp == 0)
          128                 return;
          129 
          130         for(;;){
          131                 cp = getline(bp);
          132                 if(cp == 0)
          133                         break;
          134                 p = cp+strlen(cp)+1;
          135                 switch(findkey(cp, options)){
          136                 case NORELAY:
          137                         if(fflag == 0 && strcmp(p, "on") == 0)
          138                                 fflag++;
          139                         break;
          140                 case DNSVERIFY:
          141                         if(rflag == 0 && strcmp(p, "on") == 0)
          142                                 rflag++;
          143                         break;
          144                 case SAVEBLOCK:
          145                         if(sflag == 0 && strcmp(p, "on") == 0)
          146                                 sflag++;
          147                         break;
          148                 case DOMNAME:
          149                         if(dom == 0)
          150                                 dom = strdup(p);
          151                         break;
          152                 case OURNETS:
          153                         if (trusted == 0)
          154                                 trusted = cidrcheck(p);
          155                         break;
          156                 case OURDOMS:
          157                         while(*p){
          158                                 s = s_new();
          159                                 s_append(s, p);
          160                                 listadd(&ourdoms, s);
          161                                 p += strlen(p)+1;
          162                         }
          163                         break;
          164                 default:
          165                         break;
          166                 }
          167         }
          168         sysclose(bp);
          169 }
          170 
          171 #if 0
          172 /*
          173  *        match a user name.  the only meta-char is '*' which matches all
          174  *        characters.  we only allow it as "*", which matches anything or
          175  *        an * at the end of the name (e.g., "username*") which matches
          176  *        trailing characters.
          177  */
          178 static int
          179 usermatch(char *pathuser, char *specuser)
          180 {
          181         int n;
          182 
          183         n = strlen(specuser)-1;
          184         if(specuser[n] == '*'){
          185                 if(n == 0)                /* match everything */
          186                         return 0;
          187                 return strncmp(pathuser, specuser, n);
          188         }
          189         return strcmp(pathuser, specuser);
          190 }
          191 #endif
          192 
          193 static int
          194 dommatch(char *pathdom, char *specdom)
          195 {
          196         int n;
          197 
          198         if (*specdom == '*'){
          199                 if (specdom[1] == '.' && specdom[2]){
          200                         specdom += 2;
          201                         n = strlen(pathdom)-strlen(specdom);
          202                         if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
          203                                 return strcmp(pathdom+n, specdom);
          204                         return n;
          205                 }
          206         }
          207         return strcmp(pathdom, specdom);
          208 }
          209 
          210 /*
          211  *  figure out action for this sender
          212  */
          213 int
          214 blocked(String *path)
          215 {
          216         String *lpath;
          217         int action;
          218 
          219         if(debug)
          220                 fprint(2, "blocked(%s)\n", s_to_c(path));
          221 
          222         /* if the sender's IP address is blessed, ignore sender email address */
          223         if(trusted){
          224                 if(debug)
          225                         fprint(2, "\ttrusted => trusted\n");
          226                 return TRUSTED;
          227         }
          228 
          229         /* if sender's IP address is blocked, ignore sender email address */
          230         if(hisaction != ACCEPT){
          231                 if(debug)
          232                         fprint(2, "\thisaction=%s => %s\n", actstr(hisaction), actstr(hisaction));
          233                 return hisaction;
          234         }
          235 
          236         /* convert to lower case */
          237         lpath = s_copy(s_to_c(path));
          238         s_tolower(lpath);
          239 
          240         /* classify */
          241         action = getaction(s_to_c(lpath), "account");
          242         if(debug)
          243                 fprint(2, "\tgetaction account %s => %s\n", s_to_c(lpath), actstr(action));
          244         s_free(lpath);
          245         return action;
          246 }
          247 
          248 /*
          249  * get a canonicalized line: a string of null-terminated lower-case
          250  * tokens with a two null bytes at the end.
          251  */
          252 static char*
          253 getline(Biobuf *bp)
          254 {
          255         char c, *cp, *p, *q;
          256         int n;
          257 
          258         static char *buf;
          259         static int bufsize;
          260 
          261         for(;;){
          262                 cp = Brdline(bp, '\n');
          263                 if(cp == 0)
          264                         return 0;
          265                 n = Blinelen(bp);
          266                 cp[n-1] = 0;
          267                 if(buf == 0 || bufsize < n+1){
          268                         bufsize += 512;
          269                         if(bufsize < n+1)
          270                                 bufsize = n+1;
          271                         buf = realloc(buf, bufsize);
          272                         if(buf == 0)
          273                                 break;
          274                 }
          275                 q = buf;
          276                 for (p = cp; *p; p++){
          277                         c = *p;
          278                         if(c == '\\' && p[1])        /* we don't allow \<newline> */
          279                                 c = *++p;
          280                         else
          281                         if(c == '#')
          282                                 break;
          283                         else
          284                         if(c == ' ' || c == '\t' || c == ',')
          285                                 if(q == buf || q[-1] == 0)
          286                                         continue;
          287                                 else
          288                                         c = 0;
          289                         *q++ = tolower(c);
          290                 }
          291                 if(q != buf){
          292                         if(q[-1])
          293                                 *q++ = 0;
          294                         *q = 0;
          295                         break;
          296                 }
          297         }
          298         return buf;
          299 }
          300 
          301 static int
          302 isourdom(char *s)
          303 {
          304         Link *l;
          305 
          306         if(strchr(s, '.') == nil)
          307                 return 1;
          308 
          309         for(l = ourdoms.first; l; l = l->next){
          310                 if(dommatch(s, s_to_c(l->p)) == 0)
          311                         return 1;
          312         }
          313         return 0;
          314 }
          315 
          316 int
          317 forwarding(String *path)
          318 {
          319         char *cp, *s;
          320         String *lpath;
          321 
          322         if(debug)
          323                 fprint(2, "forwarding(%s)\n", s_to_c(path));
          324 
          325         /* first check if they want loopback */
          326         lpath = s_copy(s_to_c(s_restart(path)));
          327         if(nci->rsys && *nci->rsys){
          328                 cp = s_to_c(lpath);
          329                 if(strncmp(cp, "[]!", 3) == 0){
          330 found:
          331                         s_append(path, "[");
          332                         s_append(path, nci->rsys);
          333                         s_append(path, "]!");
          334                         s_append(path, cp+3);
          335                         s_terminate(path);
          336                         s_free(lpath);
          337                         return 0;
          338                 }
          339                 cp = strchr(cp,'!');                        /* skip our domain and check next */
          340                 if(cp++ && strncmp(cp, "[]!", 3) == 0)
          341                         goto found;
          342         }
          343 
          344         /* if mail is from a trusted IP addr, allow it to forward */
          345         if(trusted) {
          346                 s_free(lpath);
          347                 return 0;
          348         }
          349 
          350         /* sender is untrusted; ensure receiver is in one of our domains */
          351         for(cp = s_to_c(lpath); *cp; cp++)                /* convert receiver lc */
          352                 *cp = tolower(*cp);
          353 
          354         for(s = s_to_c(lpath); cp = strchr(s, '!'); s = cp+1){
          355                 *cp = 0;
          356                 if(!isourdom(s)){
          357                         s_free(lpath);
          358                         return 1;
          359                 }
          360         }
          361         s_free(lpath);
          362         return 0;
          363 }
          364 
          365 int
          366 masquerade(String *path, char *him)
          367 {
          368         char *cp, *s;
          369         String *lpath;
          370         int rv = 0;
          371 
          372         if(debug)
          373                 fprint(2, "masquerade(%s)\n", s_to_c(path));
          374 
          375         if(trusted)
          376                 return 0;
          377         if(path == nil)
          378                 return 0;
          379 
          380         lpath = s_copy(s_to_c(path));
          381 
          382         /* sender is untrusted; ensure receiver is in one of our domains */
          383         for(cp = s_to_c(lpath); *cp; cp++)                /* convert receiver lc */
          384                 *cp = tolower(*cp);
          385         s = s_to_c(lpath);
          386 
          387         /* scan first element of ! or last element of @ paths */
          388         if((cp = strchr(s, '!')) != nil){
          389                 *cp = 0;
          390                 if(isourdom(s))
          391                         rv = 1;
          392         } else if((cp = strrchr(s, '@')) != nil){
          393                 if(isourdom(cp+1))
          394                         rv = 1;
          395         } else {
          396                 if(isourdom(him))
          397                         rv = 1;
          398         }
          399 
          400         s_free(lpath);
          401         return rv;
          402 }
          403 
          404 /* this is a v4 only check */
          405 static int
          406 cidrcheck(char *cp)
          407 {
          408         char *p;
          409         ulong a, m;
          410         uchar addr[IPv4addrlen];
          411         uchar mask[IPv4addrlen];
          412 
          413         if(v4peerip == 0)
          414                 return 0;
          415 
          416         /* parse a list of CIDR addresses comparing each to the peer IP addr */
          417         while(cp && *cp){
          418                 v4parsecidr(addr, mask, cp);
          419                 a = nhgetl(addr);
          420                 m = nhgetl(mask);
          421                 /*
          422                  * if a mask isn't specified, we build a minimal mask
          423                  * instead of using the default mask for that net.  in this
          424                  * case we never allow a class A mask (0xff000000).
          425                  */
          426                 if(strchr(cp, '/') == 0){
          427                         m = 0xff000000;
          428                         p = cp;
          429                         for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
          430                                         m = (m>>8)|0xff000000;
          431 
          432                         /* force at least a class B */
          433                         m |= 0xffff0000;
          434                 }
          435                 if((v4peerip&m) == a)
          436                         return 1;
          437                 cp += strlen(cp)+1;
          438         }
          439         return 0;
          440 }
          441 
          442 int
          443 isbadguy(void)
          444 {
          445         Link *l;
          446 
          447         /* check if this IP address is banned */
          448         for(l = badguys.first; l; l = l->next)
          449                 if(cidrcheck(s_to_c(l->p)))
          450                         return 1;
          451 
          452         return 0;
          453 }
          454 
          455 void
          456 addbadguy(char *p)
          457 {
          458         listadd(&badguys, s_copy(p));
          459 };
          460 
          461 char*
          462 dumpfile(char *sender)
          463 {
          464         int i, fd;
          465         ulong h;
          466         static char buf[512];
          467         char *cp;
          468 
          469         if (sflag == 1){
          470                 cp = ctime(time(0));
          471                 cp[7] = 0;
          472                 if(cp[8] == ' ')
          473                         sprint(buf, "%s/queue.dump/%s%c", SPOOL, cp+4, cp[9]);
          474                 else
          475                         sprint(buf, "%s/queue.dump/%s%c%c", SPOOL, cp+4, cp[8], cp[9]);
          476                 cp = buf+strlen(buf);
          477                 if(access(buf, 0) < 0 && sysmkdir(buf, 0777) < 0)
          478                         return "/dev/null";
          479                 h = 0;
          480                 while(*sender)
          481                         h = h*257 + *sender++;
          482                 for(i = 0; i < 50; i++){
          483                         h += lrand();
          484                         sprint(cp, "/%lud", h);
          485                         if(access(buf, 0) >= 0)
          486                                 continue;
          487                         fd = syscreate(buf, ORDWR, 0666);
          488                         if(fd >= 0){
          489                                 if(debug)
          490                                         fprint(2, "saving in %s\n", buf);
          491                                 close(fd);
          492                                 return buf;
          493                         }
          494                 }
          495         }
          496         return "/dev/null";
          497 }
          498 
          499 char *validator = "#9/mail/lib/validateaddress";
          500 
          501 int
          502 recipok(char *user)
          503 {
          504         char *cp, *p, c;
          505         char buf[512];
          506         int n;
          507         Biobuf *bp;
          508         int pid;
          509         Waitmsg *w;
          510         static int beenhere;
          511 
          512         if(!beenhere){
          513                 beenhere++;
          514                 validator = unsharp(validator);
          515         }
          516         if(shellchars(user)){
          517                 syslog(0, "smtpd", "shellchars in user name");
          518                 return 0;
          519         }
          520 
          521         if(access(validator, AEXEC) == 0)
          522         switch(pid = fork()) {
          523         case -1:
          524                 break;
          525         case 0:
          526                 execl(validator, "validateaddress", user, nil);
          527                 exits(0);
          528         default:
          529                 while(w = wait()) {
          530                         if(w->pid != pid)
          531                                 continue;
          532                         if(w->msg[0] != 0){
          533                                 syslog(0, "smtpd", "validateaddress %s: %s", user, w->msg);
          534                                 return 0;
          535                         }
          536                         break;
          537                 }
          538         }
          539 
          540         snprint(buf, sizeof(buf), "%s/names.blocked", UPASLIB);
          541         bp = sysopen(buf, "r", 0);
          542         if(bp == 0)
          543                 return 1;
          544         for(;;){
          545                 cp = Brdline(bp, '\n');
          546                 if(cp == 0)
          547                         break;
          548                 n = Blinelen(bp);
          549                 cp[n-1] = 0;
          550 
          551                 while(*cp == ' ' || *cp == '\t')
          552                         cp++;
          553                 for(p = cp; c = *p; p++){
          554                         if(c == '#')
          555                                 break;
          556                         if(c == ' ' || c == '\t')
          557                                 break;
          558                 }
          559                 if(p > cp){
          560                         *p = 0;
          561                         if(cistrcmp(user, cp) == 0){
          562                                 syslog(0, "smtpd", "names.blocked blocks %s", user);
          563                                 Bterm(bp);
          564                                 return 0;
          565                         }
          566                 }
          567         }
          568         Bterm(bp);
          569         return 1;
          570 }
          571 
          572 /*
          573  *  a user can opt out of spam filtering by creating
          574  *  a file in his mail directory named 'nospamfiltering'.
          575  */
          576 int
          577 optoutofspamfilter(char *addr)
          578 {
          579         char *p, *f;
          580         int rv;
          581 
          582         p = strchr(addr, '!');
          583         if(p)
          584                 p++;
          585         else
          586                 p = addr;
          587 
          588 
          589         rv = 0;
          590         f = smprint("%s/mail/box/%s/nospamfiltering", get9root(), p);
          591         if(f != nil){
          592                 rv = access(f, 0)==0;
          593                 free(f);
          594         }
          595 
          596         return rv;
          597 }