nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

realrcptto.c (9505B)


      1 #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <unistd.h>
      4 #include <pwd.h>
      5 #include "auto_break.h"
      6 #include "auto_users.h"
      7 #include "byte.h"
      8 #include "case.h"
      9 #include "cdb.h"
     10 #include "constmap.h"
     11 #include "error.h"
     12 #include "fmt.h"
     13 #include "open.h"
     14 #include "str.h"
     15 #include "stralloc.h"
     16 #include "uint32.h"
     17 #include "substdio.h"
     18 #include "env.h"
     19 #include "control.h"
     20 
     21 extern void die_nomem();
     22 extern void die_control();
     23 extern void die_cdb();
     24 extern void die_sys();
     25 
     26 static stralloc envnoathost = {0};
     27 static stralloc percenthack = {0};
     28 static stralloc locals = {0};
     29 static stralloc vdoms = {0};
     30 static struct constmap mappercenthack;
     31 static struct constmap maplocals;
     32 static struct constmap mapvdoms;
     33 
     34 static char *dash;
     35 static char *extension;
     36 static char *local;
     37 static struct passwd *pw;
     38 
     39 static char errbuf[128];
     40 static struct substdio sserr = SUBSTDIO_FDBUF(write,2,errbuf,sizeof errbuf);
     41 
     42 static char pidbuf[64];
     43 static char remoteipbuf[64]=" ";
     44 
     45 static int flagdenyall;
     46 static int flagdenyany;
     47 
     48 void realrcptto_init()
     49 {
     50   char *x;
     51 
     52   if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1)
     53     die_control();
     54 
     55   if (control_readfile(&locals,"control/locals",1) != 1) die_control();
     56   if (!constmap_init(&maplocals,locals.s,locals.len,0)) die_nomem();
     57   switch(control_readfile(&percenthack,"control/percenthack",0)) {
     58     case -1: die_control();
     59     case 0: if (!constmap_init(&mappercenthack,"",0,0)) die_nomem();
     60     case 1:
     61       if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0))
     62         die_nomem();
     63   }
     64   switch(control_readfile(&vdoms,"control/virtualdomains",0)) {
     65     case -1: die_control();
     66     case 0: if (!constmap_init(&mapvdoms,"",0,1)) die_nomem();
     67     case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) die_nomem();
     68   }
     69 
     70   str_copy(pidbuf + fmt_ulong(pidbuf,getpid())," ");
     71   x=env_get("PROTO");
     72   if (x) {
     73     static char const remoteip[]="REMOTEIP";
     74     unsigned int len = str_len(x);
     75     if (len <= sizeof remoteipbuf - sizeof remoteip) {
     76       byte_copy(remoteipbuf,len,x);
     77       byte_copy(remoteipbuf + len,sizeof remoteip,remoteip);
     78       x = env_get(remoteipbuf);
     79       len = str_len(x);
     80       if (len + 1 < sizeof remoteipbuf) {
     81         byte_copy(remoteipbuf,len,x);
     82         remoteipbuf[len]=' ';
     83         remoteipbuf[len + 1]='\0';
     84       }
     85     }
     86   }
     87 
     88   x = env_get("QMAILRRTDENYALL");
     89   flagdenyall = (x && x[0]=='1' && x[1]=='\0');
     90 }
     91 
     92 void realrcptto_start()
     93 {
     94   flagdenyany = 0;
     95 }
     96 
     97 static int denyaddr(addr)
     98 char *addr;
     99 {
    100   substdio_puts(&sserr,"realrcptto ");
    101   substdio_puts(&sserr,pidbuf);
    102   substdio_puts(&sserr,remoteipbuf);
    103   substdio_puts(&sserr,addr);
    104   substdio_puts(&sserr,"\n");
    105   substdio_flush(&sserr);
    106   flagdenyany = 1;
    107   return flagdenyall;
    108 }
    109 
    110 static void stat_error(path,error)
    111 char* path;
    112 int error;
    113 {
    114   substdio_puts(&sserr,"unable to stat ");
    115   substdio_puts(&sserr,path);
    116   substdio_puts(&sserr,": ");
    117   substdio_puts(&sserr,error_str(error));
    118   substdio_puts(&sserr,"\n");
    119   substdio_flush(&sserr);
    120 }
    121 
    122 #define GETPW_USERLEN 32
    123 
    124 static int userext()
    125 {
    126   char username[GETPW_USERLEN];
    127   struct stat st;
    128 
    129   extension = local + str_len(local);
    130   for (;;) {
    131     if (extension - local < sizeof(username))
    132       if (!*extension || (*extension == *auto_break)) {
    133 	byte_copy(username,extension - local,local);
    134 	username[extension - local] = 0;
    135 	case_lowers(username);
    136 	errno = 0;
    137 	pw = getpwnam(username);
    138 	if (errno == error_txtbsy) die_sys();
    139 	if (pw)
    140 	  if (pw->pw_uid)
    141 	    if (stat(pw->pw_dir,&st) == 0) {
    142 	      if (st.st_uid == pw->pw_uid) {
    143 		dash = "";
    144 		if (*extension) { ++extension; dash = "-"; }
    145 		return 1;
    146 	      }
    147 	    }
    148 	    else
    149 	      if (error_temp(errno)) die_sys();
    150       }
    151     if (extension == local) return 0;
    152     --extension;
    153   }
    154 }
    155 
    156 int realrcptto(addr)
    157 char *addr;
    158 {
    159   char *homedir;
    160   static stralloc localpart = {0};
    161   static stralloc lower = {0};
    162   static stralloc nughde = {0};
    163   static stralloc wildchars = {0};
    164   static stralloc safeext = {0};
    165   static stralloc qme = {0};
    166   unsigned int i,at;
    167 
    168   /* Short circuit, or full logging?  Short circuit. */
    169   if (flagdenyall && flagdenyany) return 1;
    170 
    171   /* qmail-send:rewrite */
    172   if (!stralloc_copys(&localpart,addr)) die_nomem();
    173   i = byte_rchr(localpart.s,localpart.len,'@');
    174   if (i == localpart.len) {
    175     if (!stralloc_cats(&localpart,"@")) die_nomem();
    176     if (!stralloc_cat(&localpart,&envnoathost)) die_nomem();
    177   }
    178   while (constmap(&mappercenthack,localpart.s + i + 1,localpart.len - i - 1)) {
    179     unsigned int j = byte_rchr(localpart.s,i,'%');
    180     if (j == i) break;
    181     localpart.len = i;
    182     i = j;
    183     localpart.s[i] = '@';
    184   }
    185   at = byte_rchr(localpart.s,localpart.len,'@');
    186   if (constmap(&maplocals,localpart.s + at + 1,localpart.len - at - 1)) {
    187     localpart.len = at;
    188     localpart.s[at] = '\0';
    189   } else {
    190     unsigned int xlen,newlen;
    191     char *x;
    192     for (i = 0;;++i) {
    193       if (i > localpart.len) return 1;
    194       if (!i || (i == at + 1) || (i == localpart.len) ||
    195           ((i > at) && (localpart.s[i] == '.'))) {
    196         x = constmap(&mapvdoms,localpart.s + i,localpart.len - i);
    197         if (x) break;
    198       }
    199     }
    200     if (!*x) return 1;
    201     xlen = str_len(x) + 1;  /* +1 for '-' */
    202     newlen = xlen + at + 1; /* +1 for \0 */
    203     if (xlen < 1 || newlen - 1 < xlen || newlen < 1 ||
    204         !stralloc_ready(&localpart,newlen))
    205       die_nomem();
    206     localpart.s[newlen - 1] = '\0';
    207     byte_copyr(localpart.s + xlen,at,localpart.s);
    208     localpart.s[xlen - 1] = '-';
    209     byte_copy(localpart.s,xlen - 1,x);
    210     localpart.len = newlen;
    211   }
    212 
    213   /* qmail-lspawn:nughde_get */
    214   {
    215     int r,fd,flagwild;
    216     if (!stralloc_copys(&lower,"!")) die_nomem();
    217     if (!stralloc_cats(&lower,localpart.s)) die_nomem();
    218     if (!stralloc_0(&lower)) die_nomem();
    219     case_lowerb(lower.s,lower.len);
    220     if (!stralloc_copys(&nughde,"")) die_nomem();
    221     fd = open_read("users/cdb");
    222     if (fd == -1) {
    223       if (errno != error_noent) die_cdb();
    224     } else {
    225       uint32 dlen;
    226       r = cdb_seek(fd,"",0,&dlen);
    227       if (r != 1) die_cdb();
    228       if (!stralloc_ready(&wildchars,(unsigned int) dlen)) die_nomem();
    229       wildchars.len = dlen;
    230       if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) die_cdb();
    231       i = lower.len;
    232       flagwild = 0;
    233       do { /* i > 0 */
    234         if (!flagwild || (i == 1) ||
    235             (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1])
    236              < wildchars.len)) {
    237           r = cdb_seek(fd,lower.s,i,&dlen);
    238           if (r == -1) die_cdb();
    239           if (r == 1) {
    240             char *x;
    241             if (!stralloc_ready(&nughde,(unsigned int) dlen)) die_nomem();
    242             nughde.len = dlen;
    243             if (cdb_bread(fd,nughde.s,nughde.len) == -1) die_cdb();
    244             if (flagwild)
    245               if (!stralloc_cats(&nughde,localpart.s + i - 1)) die_nomem();
    246             if (!stralloc_0(&nughde)) die_nomem();
    247             close(fd);
    248             x=nughde.s;
    249             /* skip username */
    250             x += byte_chr(x,nughde.s + nughde.len - x,'\0');
    251             if (x == nughde.s + nughde.len) return 1;
    252             ++x;
    253             /* skip uid */
    254             x += byte_chr(x,nughde.s + nughde.len - x,'\0');
    255             if (x == nughde.s + nughde.len) return 1;
    256             ++x;
    257             /* skip gid */
    258             x += byte_chr(x,nughde.s + nughde.len - x,'\0');
    259             if (x == nughde.s + nughde.len) return 1;
    260             ++x;
    261             /* skip homedir */
    262             homedir=x;
    263             x += byte_chr(x,nughde.s + nughde.len - x,'\0');
    264             if (x == nughde.s + nughde.len) return 1;
    265             ++x;
    266             /* skip dash */
    267             dash=x;
    268             x += byte_chr(x,nughde.s + nughde.len - x,'\0');
    269             if (x == nughde.s + nughde.len) return 1;
    270             ++x;
    271             extension=x;
    272             goto got_nughde;
    273           }
    274         }
    275         --i;
    276         flagwild = 1;
    277       } while (i);
    278       close(fd);
    279     }
    280   }
    281 
    282   /* qmail-getpw */
    283   local = localpart.s;
    284   if (!userext()) {
    285     extension = local;
    286     dash = "-";
    287     pw = getpwnam(auto_usera);
    288   }
    289   if (!pw) return denyaddr(addr);
    290   if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem();
    291   if (!stralloc_0(&nughde)) die_nomem();
    292   homedir=nughde.s;
    293 
    294   got_nughde:
    295 
    296   /* qmail-local:qmesearch */
    297   if (!*dash) return 1;
    298   if (!stralloc_copys(&safeext,extension)) die_nomem();
    299   case_lowerb(safeext.s,safeext.len);
    300   for (i = 0;i < safeext.len;++i)
    301     if (safeext.s[i] == '.')
    302       safeext.s[i] = ':';
    303   {
    304     struct stat st;
    305     int i;
    306     if (!stralloc_copys(&qme,homedir)) die_nomem();
    307     if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
    308     if (!stralloc_cats(&qme,dash)) die_nomem();
    309     if (!stralloc_cat(&qme,&safeext)) die_nomem();
    310     if (!stralloc_0(&qme)) die_nomem();
    311     if (stat(qme.s,&st) == 0) return 1;
    312     if (errno != error_noent) {
    313       stat_error(qme.s,errno);
    314       return 1;
    315     }
    316     for (i = safeext.len;i >= 0;--i)
    317       if (!i || (safeext.s[i - 1] == '-')) {
    318         if (!stralloc_copys(&qme,homedir)) die_nomem();
    319         if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
    320         if (!stralloc_cats(&qme,dash)) die_nomem();
    321         if (!stralloc_catb(&qme,safeext.s,i)) die_nomem();
    322         if (!stralloc_cats(&qme,"default")) die_nomem();
    323         if (!stralloc_0(&qme)) die_nomem();
    324         if (stat(qme.s,&st) == 0) return 1;
    325         if (errno != error_noent) {
    326           stat_error(qme.s,errno);
    327           return 1;
    328         }
    329       }
    330     return denyaddr(addr);
    331   }
    332 }
    333 
    334 int realrcptto_deny()
    335 {
    336   return flagdenyall && flagdenyany;
    337 }