From b0c5a1bf27516611d34b6b7d56a0376e3c64171b Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 5 Feb 2013 23:48:16 +0100 Subject: [PATCH] realrcptto --- realrcptto.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 realrcptto.c (limited to 'realrcptto.c') diff --git a/realrcptto.c b/realrcptto.c new file mode 100644 index 0000000..7ec331a --- /dev/null +++ b/realrcptto.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#include "auto_break.h" +#include "auto_usera.h" +#include "byte.h" +#include "case.h" +#include "cdb.h" +#include "constmap.h" +#include "error.h" +#include "fmt.h" +#include "open.h" +#include "str.h" +#include "stralloc.h" +#include "uint32.h" +#include "substdio.h" +#include "env.h" +#include "slurpclose.h" + +extern void die_nomem(); +extern void die_control(); +extern void die_cdb(); +extern void die_sys(); + +static stralloc envnoathost = {0}; +static stralloc percenthack = {0}; +static stralloc locals = {0}; +static stralloc vdoms = {0}; +static struct constmap mappercenthack; +static struct constmap maplocals; +static struct constmap mapvdoms; + +static char *dash; +static char *extension; +static char *local; +static struct passwd *pw; + +static char errbuf[128]; +static struct substdio sserr = SUBSTDIO_FDBUF(write,2,errbuf,sizeof errbuf); + +static char pidbuf[64]; +static char remoteipbuf[64]=" "; + +static int flagdenyall; +static int flagdenyany; + +void realrcptto_init() +{ + char *x; + + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) + die_control(); + + if (control_readfile(&locals,"control/locals",1) != 1) die_control(); + if (!constmap_init(&maplocals,locals.s,locals.len,0)) die_nomem(); + switch(control_readfile(&percenthack,"control/percenthack",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mappercenthack,"",0,0)) die_nomem(); + case 1: + if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) + die_nomem(); + } + switch(control_readfile(&vdoms,"control/virtualdomains",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mapvdoms,"",0,1)) die_nomem(); + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) die_nomem(); + } + + str_copy(pidbuf + fmt_ulong(pidbuf,getpid())," "); + x=env_get("PROTO"); + if (x) { + static char const remoteip[]="REMOTEIP"; + unsigned int len = str_len(x); + if (len <= sizeof remoteipbuf - sizeof remoteip) { + byte_copy(remoteipbuf,len,x); + byte_copy(remoteipbuf + len,sizeof remoteip,remoteip); + x = env_get(remoteipbuf); + len = str_len(x); + if (len + 1 < sizeof remoteipbuf) { + byte_copy(remoteipbuf,len,x); + remoteipbuf[len]=' '; + remoteipbuf[len + 1]='\0'; + } + } + } + + x = env_get("QMAILRRTDENYALL"); + flagdenyall = (x && x[0]=='1' && x[1]=='\0'); +} + +void realrcptto_start() +{ + flagdenyany = 0; +} + +static int denyaddr(addr, depth) +char *addr; +int depth; +{ + if (depth == 1) + { + substdio_puts(&sserr,"realrcptto "); + substdio_puts(&sserr,pidbuf); + substdio_puts(&sserr,remoteipbuf); + substdio_puts(&sserr,addr); + substdio_puts(&sserr,"\n"); + substdio_flush(&sserr); + flagdenyany = 1; + } + return flagdenyall; +} + +static void stat_error(path,error) +char* path; +int error; +{ + substdio_puts(&sserr,"unable to stat "); + substdio_puts(&sserr,path); + substdio_puts(&sserr,": "); + substdio_puts(&sserr,error_str(error)); + substdio_puts(&sserr,"\n"); + substdio_flush(&sserr); +} + +#define GETPW_USERLEN 32 + +static int userext() +{ + char username[GETPW_USERLEN]; + struct stat st; + + extension = local + str_len(local); + for (;;) { + if (extension - local < sizeof(username)) + if (!*extension || (*extension == *auto_break)) { + byte_copy(username,extension - local,local); + username[extension - local] = 0; + case_lowers(username); + errno = 0; + pw = getpwnam(username); + if (errno == error_txtbsy) die_sys(); + if (pw) + if (pw->pw_uid) + if (stat(pw->pw_dir,&st) == 0) { + if (st.st_uid == pw->pw_uid) { + dash = ""; + if (*extension) { ++extension; dash = "-"; } + return 1; + } + } + else + if (error_temp(errno)) die_sys(); + } + if (extension == local) return 0; + --extension; + } +} + +// max lookups +#define MAXRECURSION 5 + +static int handleqme(qme, depth) +stralloc *qme; +int depth; +{ + stralloc cmds = {0}; + char *username = NULL; + int fd, i, j, k, count; + + if (depth >= MAXRECURSION) return 0; + if (!stralloc_ready(&cmds,0)) die_nomem(); + cmds.len = 0; + + fd = open_read(qme->s); + if (fd == -1) return 0; + if (slurpclose(fd,&cmds,256) == -1) die_nomem(); + if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) + if (!stralloc_cats(&cmds,"\n")) die_nomem(); + + i = count = 0; + for (j = 0;j < cmds.len;++j) + { + if (cmds.s[j] == '\n') + { + cmds.s[j] = 0; + k = j; + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) + cmds.s[--k] = 0; + switch(cmds.s[i]) + { + case 0: + case '#': + case '.': + case '/': + case '|': + case '+': + break; + case '&': + ++i; + default: + count++; + username = cmds.s + i; + break; + } + i = j + 1; + } + } + + if (count == 1 && username) + realrcptto(username, ++depth); + + return 1; +} + +int realrcptto(addr, depth) +char *addr; +int depth; +{ + env_unset("DTUSER"); + return realrcptto_ex(addr, depth); +} + +int realrcptto_ex(addr, depth) +char *addr; +int depth; +{ + char *homedir, *username; + static stralloc localpart = {0}; + static stralloc lower = {0}; + static stralloc nughde = {0}; + static stralloc wildchars = {0}; + static stralloc safeext = {0}; + static stralloc qme = {0}; + unsigned int i,at; + + /* Short circuit, or full logging? Short circuit. */ + if (flagdenyall && flagdenyany) return 1; + + /* qmail-send:rewrite */ + if (!stralloc_copys(&localpart,addr)) die_nomem(); + i = byte_rchr(localpart.s,localpart.len,'@'); + if (i == localpart.len) { + if (!stralloc_cats(&localpart,"@")) die_nomem(); + if (!stralloc_cat(&localpart,&envnoathost)) die_nomem(); + } + while (constmap(&mappercenthack,localpart.s + i + 1,localpart.len - i - 1)) { + unsigned int j = byte_rchr(localpart.s,i,'%'); + if (j == i) break; + localpart.len = i; + i = j; + localpart.s[i] = '@'; + } + at = byte_rchr(localpart.s,localpart.len,'@'); + if (constmap(&maplocals,localpart.s + at + 1,localpart.len - at - 1)) { + localpart.len = at; + localpart.s[at] = '\0'; + } else { + unsigned int xlen,newlen; + char *x; + for (i = 0;;++i) { + if (i > localpart.len) return denyaddr(addr, depth); + if (!i || (i == at + 1) || (i == localpart.len) || + ((i > at) && (localpart.s[i] == '.'))) { + x = constmap(&mapvdoms,localpart.s + i,localpart.len - i); + if (x && i == at + 1) { + // set QMAILQUEUE if catch-all + char *qmailqueue; + qmailqueue = env_get("QMAILQUEUE"); + if (qmailqueue) { + if (!env_put("QMAILQUEUE=bin/qmail-queue")) die_nomem(); + } + } + if (x) break; + } + } + if (!*x) return 1; + xlen = str_len(x) + 1; /* +1 for '-' */ + newlen = xlen + at + 1; /* +1 for \0 */ + if (xlen < 1 || newlen - 1 < xlen || newlen < 1 || + !stralloc_ready(&localpart,newlen)) + die_nomem(); + localpart.s[newlen - 1] = '\0'; + byte_copyr(localpart.s + xlen,at,localpart.s); + localpart.s[xlen - 1] = '-'; + byte_copy(localpart.s,xlen - 1,x); + localpart.len = newlen; + } + + /* qmail-lspawn:nughde_get */ + { + int r,fd,flagwild; + if (!stralloc_copys(&lower,"!")) die_nomem(); + if (!stralloc_cats(&lower,localpart.s)) die_nomem(); + if (!stralloc_0(&lower)) die_nomem(); + case_lowerb(lower.s,lower.len); + if (!stralloc_copys(&nughde,"")) die_nomem(); + fd = open_read("users/cdb"); + if (fd == -1) { + if (errno != error_noent) die_cdb(); + } else { + uint32 dlen; + r = cdb_seek(fd,"",0,&dlen); + if (r != 1) die_cdb(); + if (!stralloc_ready(&wildchars,(unsigned int) dlen)) die_nomem(); + wildchars.len = dlen; + if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) die_cdb(); + i = lower.len; + flagwild = 0; + do { /* i > 0 */ + if (!flagwild || (i == 1) || + (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) + < wildchars.len)) { + r = cdb_seek(fd,lower.s,i,&dlen); + if (r == -1) die_cdb(); + if (r == 1) { + char *x; + if (!stralloc_ready(&nughde,(unsigned int) dlen)) die_nomem(); + nughde.len = dlen; + if (cdb_bread(fd,nughde.s,nughde.len) == -1) die_cdb(); + if (flagwild) + if (!stralloc_cats(&nughde,localpart.s + i - 1)) die_nomem(); + if (!stralloc_0(&nughde)) die_nomem(); + close(fd); + x=nughde.s; + /* skip username */ + username=x; + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return 1; + ++x; + /* skip uid */ + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return 1; + ++x; + /* skip gid */ + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return 1; + ++x; + /* skip homedir */ + homedir=x; + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return 1; + ++x; + /* skip dash */ + dash=x; + x += byte_chr(x,nughde.s + nughde.len - x,'\0'); + if (x == nughde.s + nughde.len) return 1; + ++x; + extension=x; + goto got_nughde; + } + } + --i; + flagwild = 1; + } while (i); + close(fd); + } + } + + /* qmail-getpw */ + local = localpart.s; + if (!userext()) { + extension = local; + dash = "-"; + pw = getpwnam(auto_usera); + } + if (!pw) return denyaddr(addr, depth); + if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem(); + if (!stralloc_0(&nughde)) die_nomem(); + homedir=nughde.s; + username=pw->pw_name; + + got_nughde: + + /* qmail-local:qmesearch */ + //if (!*dash) return 1; + if (!*dash) { env_put2("DTUSER", username); return 1; } + if (!stralloc_copys(&safeext,extension)) die_nomem(); + case_lowerb(safeext.s,safeext.len); + for (i = 0;i < safeext.len;++i) + { + if (safeext.s[i] == '.') + safeext.s[i] = ':'; + } + { + struct stat st; + int i; + if (!stralloc_copys(&qme,homedir)) die_nomem(); + if (!stralloc_cats(&qme,"/.qmail")) die_nomem(); + if (!stralloc_cats(&qme,dash)) die_nomem(); + if (!stralloc_cat(&qme,&safeext)) die_nomem(); + if (!stralloc_0(&qme)) die_nomem(); + //if (stat(qme.s,&st) == 0) return 1; + if (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; } + if (errno != error_noent) { + stat_error(qme.s,errno); + return 1; + } + for (i = safeext.len;i >= 0;--i) + if (!i || (safeext.s[i - 1] == '-')) { + if (!stralloc_copys(&qme,homedir)) die_nomem(); + if (!stralloc_cats(&qme,"/.qmail")) die_nomem(); + if (!stralloc_cats(&qme,dash)) die_nomem(); + if (!stralloc_catb(&qme,safeext.s,i)) die_nomem(); + if (!stralloc_cats(&qme,"default")) die_nomem(); + if (!stralloc_0(&qme)) die_nomem(); + //if (stat(qme.s,&st) == 0) return 1; + if (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; } + if (errno != error_noent) { + stat_error(qme.s,errno); + return 1; + } + } + return denyaddr(addr, depth); + } +} + +int realrcptto_deny() +{ + return flagdenyall && flagdenyany; +} -- cgit v1.2.3