tspam.c - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
tspam.c (10367B)
---
     1 #include "common.h"
     2 #include "smtpd.h"
     3 #include 
     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 \ */
   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 }