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 }