diff options
Diffstat (limited to 'src/recipients.c')
-rw-r--r-- | src/recipients.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/src/recipients.c b/src/recipients.c new file mode 100644 index 0000000..ce29e19 --- /dev/null +++ b/src/recipients.c @@ -0,0 +1,290 @@ +#include <unistd.h> +#include "cdbread.h" +#include "byte.h" +#include "open.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "recipients.h" +#include "wait.h" +#include "str.h" +#include "fd.h" +#include "sig.h" +#include "case.h" +#include "buffer.h" +#include "auto_break.h" +#include "qmail.h" + +#define FDAUTH 3 + +static stralloc key = {0}; +static stralloc domain = {0}; +static stralloc wildhost = {0}; +static stralloc address = {0}; +static stralloc rcptline = {0}; +static stralloc vkey = {0}; +static stralloc verp = {0}; +static stralloc user = {0}; +static stralloc ukey = {0}; +static int flagrcpts = 0; +static int fdrcps; +static struct cdb cdb; + +/** @file recipients.c + @brief functions recipients_init, recipients, recipients_parse, callapam + @param pointer to address, length of address + @return -3: problem with PAM + -2: out of memory + -1: error reading control file + 0: address not found; unsuccessful + 1: CDB lookup; successful + 2: PAM lookup; successful + 3: USERS lookup; successful + 4: Wildcarded domain; successful + 5: Pass-thru; neutral + 10: none existing control file; pass-thru +*/ + +int recipients_init() +{ + flagrcpts = control_readfile(&rcptline,"control/recipients",0); + if (flagrcpts != 1) return flagrcpts; + return 0; +} + +char rcptbuf[512]; +buffer br = BUFFER_INIT(safewrite,FDAUTH,rcptbuf,sizeof(rcptbuf)); + +int callapam(char *pam,char *addr) +{ + int i; + int j=0; + int wstat; + int pi[2]; + int child; + char ch; + static stralloc mailaddress = {0}; + + char *childargs[7] = {0, 0, 0, 0, 0, 0, 0}; + stralloc pamarg = {0}; + stralloc pamname = {0}; + stralloc pamarg1 = {0}; + stralloc pamarg2 = {0}; + stralloc pamarg3 = {0}; + stralloc pamarg4 = {0}; + stralloc pamarg5 = {0}; + + for (i = 0; (ch = pam[i]); i++) { + if (j < 6) { + if (ch != ' ') + if (!stralloc_append(&pamarg,&ch)) return -2; + if (ch == ' ' || ch == '\n' || i == str_len(pam) - 1) { + if (!stralloc_0(&pamarg)) return -2; + switch (j) { + case 0: + if (!stralloc_copy(&pamname,&pamarg)) return -2; + childargs[0] = pamname.s; + case 1: + if (!stralloc_copy(&pamarg1,&pamarg)) return -2; + childargs[1] = pamarg1.s; + case 2: + if (!stralloc_copy(&pamarg2,&pamarg)) return -2; + childargs[2] = pamarg2.s; + case 3: + if (!stralloc_copy(&pamarg3,&pamarg)) return -2; + childargs[3] = pamarg3.s; + case 4: + if (!stralloc_copy(&pamarg4,&pamarg)) return -2; + childargs[4] = pamarg4.s; + case 5: + if (!stralloc_copy(&pamarg5,&pamarg)) return -2; + childargs[5] = pamarg5.s; + } + j++; + if (!stralloc_copys(&pamarg,"")) return -2; + } + } + } + childargs[j] = 0; + + close(FDAUTH); + if (pipe(pi) == -1) return -3; + if (pi[0] != FDAUTH) return -3; + + switch (child = fork()) { + case -1: + return -3; + case 0: + close(pi[1]); + if (fd_copy(FDAUTH,pi[0]) == -1) return -3; + sig_pipedefault(); + execvp(childargs[0],childargs); + return 111; + } + close(pi[0]); + +/* checkpassword compliant form: address\0\0\0 */ + + if (!stralloc_copys(&mailaddress,addr)) return -2; + if (!stralloc_0(&mailaddress)) return -2; + if (!stralloc_0(&mailaddress)) return -2; + if (!stralloc_0(&mailaddress)) return -2; + + buffer_init(&br,write,pi[1],rcptbuf,sizeof(rcptbuf)); + if (buffer_put(&br,mailaddress.s,mailaddress.len) == -1) return -3; + if (buffer_flush(&br) == -1) return -3; + close(pi[1]); + + if (wait_pid(&wstat,child) == -1) return -3; + if (wait_crashed(wstat)) return -3; + return wait_exitcode(wstat); +} + +int recipients_parse(char *rhost,int rlen,char *addr,char *rkey,int klen,char *vaddr,char *vkey,int vlen,char *ukey,int ulen) +{ + int i; + int r; + int j = 0; + int k = 0; + int u = 0; + static stralloc line = {0}; + int seenhost = 0; + + if (!stralloc_copys(&line,"")) return -2; + if (!stralloc_copys(&wildhost,"!")) return -2; + if (!stralloc_cats(&wildhost,rhost)) return -2; + if (!stralloc_0(&wildhost)) return -2; + + for (i = 0; i < rcptline.len; ++i) { + if (!stralloc_append(&line,&rcptline.s[i])) return -2; + + if (rcptline.s[i] == '\0') { + if (!stralloc_0(&line)) return -2; + + j = byte_chr(line.s,line.len,':'); /* cdb */ + k = byte_chr(line.s,line.len,'|'); /* pam */ + u = byte_chr(line.s,line.len,'='); /* assign users */ + + if (!str_diffn(line.s,wildhost.s,wildhost.len - 1)) return 4; /* wilddomain */ + if ((j && j < line.len) || (k && k < line.len) || (u && u < line.len)) + if (!str_diffn(line.s,"@",1)) /* exact */ + if (!str_diffn(line.s + 1,rhost,rlen - 1)) seenhost = 1; + + if (!seenhost) { /* domain */ + if (j && rlen >= j) + if (!str_diffn(line.s,rhost + rlen - j - 1,j - 1)) seenhost = 2; + if (k && rlen >= k) + if (!str_diffn(line.s,rhost + rlen - k - 1,k - 1)) seenhost = 3; + if (u && rlen >= u) + if (!str_diffn(line.s,rhost + rlen - u - 1,u - 1)) seenhost = 4; + } + if (!seenhost) /* pass-thru */ + if (!str_diffn(line.s,"!*",2)) return 5; + + if (k && k < line.len) /* pam */ + if (seenhost || !str_diffn(line.s,"*",1)) { + r = callapam(line.s + k + 1,addr); + if (vlen > 0 && r != 0) + r = callapam(line.s + k + 1,vaddr); + if (r == 0) return 2; + if (r == 111) return r; + } + + if (u && u < line.len) /* qmail-users */ + if (seenhost || !str_diffn(line.s,"*",1)) { + fdrcps = open_read("users/assign.cdb"); + if (fdrcps != -1) { + cdb_init(&cdb,fdrcps); + r = cdb_find(&cdb,ukey,ulen - 1); + cdb_free(&cdb); + close(fdrcps); + if (r) return 3; + } + } + + if (j && j < line.len) /* cdb */ + if (seenhost || !str_diffn(line.s,"*",1)) { + fdrcps = open_read(line.s + j + 1); + if (fdrcps != -1) { + cdb_init(&cdb,fdrcps); + r = cdb_find(&cdb,rkey,klen - 2); + if (vlen > 0 && r == 0) + r = cdb_find(&cdb,vkey,vlen - 2); + cdb_free(&cdb); + close(fdrcps); + if (r) return 1; + } + } + + if (!seenhost) { + fdrcps = open_read(line.s); /* legacy cdb */ + if (fdrcps != -1) { + cdb_init(&cdb,fdrcps); + r = cdb_find(&cdb,rkey,klen - 2); + if (vlen > 0 && r == 0) + r = cdb_find(&cdb,vkey,vlen - 2); + cdb_free(&cdb); + close(fdrcps); + if (r) return 1; + } + } + + if (!stralloc_copys(&line,"")) return -2; + } + } + return 0; +} + +int recipients(char *buf,int len) +{ + int at; + int i; + int r; + + if (flagrcpts != 1) return 10; + + at = byte_rchr(buf,len,'@'); + if (at && at < len) { + if (!stralloc_copyb(&domain,buf + at + 1,len - at - 1)) return -2; + if (!stralloc_copyb(&address,buf,len)) return -2; + } else { + if (!stralloc_copyb(&address,buf,len)) return -2; + if (!stralloc_append(&address,"@")) return -2; + if (!stralloc_copys(&domain,"localhost")) return -2; + if (!stralloc_cat(&address,&domain)) return -2; + } + if (!stralloc_copyb(&user,buf,at - 1)) return -2; + + if (!stralloc_0(&user)) return -2; + if (!stralloc_0(&address)) return -2; + if (!stralloc_0(&domain)) return -2; + + if (!stralloc_copys(&key,":")) return -2; + if (!stralloc_cat(&key,&address)) return -2; + if (!stralloc_0(&key)) return -2; /* \0\0 terminated */ + case_lowerb(key.s,key.len); + case_lowerb(domain.s,domain.len); + + if (!stralloc_copys(&ukey,"!")) return -2; + if (!stralloc_cat(&ukey,&user)) return -2; + if (!stralloc_0(&ukey)) return -2; /* \0 terminated */ + case_lowerb(ukey.s,ukey.len); + + + for (i = 0; i < at; i++) { /* VERP addresses */ + if (buf[i] == *auto_break || buf[i] == '=' || buf[i] == '+') { /* SRS delimiter */ + if (!stralloc_copyb(&verp,buf,i + 1)) return -2; + if (!stralloc_append(&verp,"@")) return -2; + if (!stralloc_cat(&verp,&domain)) return -2; + if (!stralloc_copys(&vkey,":")) return -2; + if (!stralloc_cat(&vkey,&verp)) return -2; + if (!stralloc_0(&vkey)) return -2; /* \0\0 terminated */ + case_lowerb(vkey.s,vkey.len); + break; + } + } + + r = recipients_parse(domain.s,domain.len,address.s,key.s,key.len,verp.s,vkey.s,vkey.len,ukey.s,ukey.len); + if (r) return r; + return 0; +} |