From 0a045fc157596a9521a5acff73297cc4a7c6c688 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 6 Feb 2006 19:40:05 +0000 Subject: [PATCH] addrcheck: Optionally invoke userv service to decide address validity. It may be that the existence of a mailbox can be best determined programmatically, rather than by the existence of a .qmail-BLAH file for it. For example, imagine a remailer which uses encrypted email addresses: it'd be better to check the authentication on an address than to have a .qmail file for each one. If a user's .qmail-FOO-default file has a first line of the form `#?ANYTHING then that user's userv service `addrcheck:USER-FOO-default' is invoked, with these arguments: * the remainder of the destination email address (including the domain part); * the envelope sender (which may be useful); * the entire destination email address; and * the key `USER-FOO-default' which the address matched. The service should output a `+' or `-' sign to indicate acceptance or rejection. Failure to do this, or exiting with a nonzero status, causes a temporary error to be reported to the SMTP client. --- addrcheck.c | 109 +++++++++++++++++++++++++++++++++++++------------- addrcheck.h | 2 +- qmail-smtpd.c | 2 +- qmail-valid-addresses | 13 ++++-- 4 files changed, 93 insertions(+), 33 deletions(-) diff --git a/addrcheck.c b/addrcheck.c index 2ff9d13..08d29b9 100644 --- a/addrcheck.c +++ b/addrcheck.c @@ -4,13 +4,13 @@ #include "str.h" #include "addrcheck.h" #include +#include /* #define DEBUG */ #ifdef DEBUG # define D(x) x # include # include -# include #else # define D(x) #endif @@ -18,7 +18,7 @@ #define STRALLOC_INIT { 0 } static int probe(int cdb, int prefix, const char *key, int len, - const char *suffix, uint32 *dlen) + const char *suffix, const char **kp, uint32 *dlen) { static stralloc k = STRALLOC_INIT; char ch = prefix; @@ -27,10 +27,12 @@ static int probe(int cdb, int prefix, const char *key, int len, k.len = 0; if (!stralloc_append(&k, &ch) || !stralloc_catb(&k, key, len) || - (suffix && !stralloc_cats(&k, suffix))) + (suffix && !stralloc_cats(&k, suffix)) || + !stralloc_0(&k)) return (-1); + if (kp) *kp = k.s; D( fprintf(stderr, "*** `%.*s' -> ", k.len, k.s); ) - rc = cdb_seek(cdb, k.s, k.len, dlen); + rc = cdb_seek(cdb, k.s, k.len - 1, dlen); D( if (rc == -1) fprintf(stderr, "error: %s\n", strerror(errno)); else if (rc == 0) @@ -55,34 +57,86 @@ static int probe(int cdb, int prefix, const char *key, int len, return (rc); } -static int localprobe(int cdb, const char *key, int len, - const char *suffix, int *rc) +static int localprobe(int cdb, const char *sender, + const char *key, int len, + const char *suffix, const char *tail, int *rc) { int err; uint32 dlen; char ch; - - if ((err = probe(cdb, 'L', key, len, suffix, &dlen)) < 0) + const char *k; + static stralloc u = STRALLOC_INIT; + static stralloc serv = STRALLOC_INIT; + int kid; + int n; + int wstat; + int p[2]; + + if ((err = probe(cdb, 'L', key, len, suffix, &k, &dlen)) < 0) return (-1); if (!err) { *rc = 0; return (0); } - if (dlen != 1) { errno = EINVAL; return (-1); } + if (!dlen) { errno = EINVAL; return (-1); } if (read(cdb, &ch, 1) != 1) { errno = EIO; return (-1); } + if (ch == '?') { + u.len = 0; + if (!stralloc_ready(&u, dlen - 1)) return (-1); + if (read(cdb, u.s, dlen - 1) != dlen - 1) { errno = EIO; return (-1); } + u.len = dlen - 1; + serv.len = 0; + if (!stralloc_cats(&serv, "addrcheck:") || + !stralloc_cats(&serv, k + 1) || + !stralloc_0(&serv) || + !stralloc_0(&u)) + return (-1); + D( fprintf(stderr, "asking user:\n\ + user = %s; service = %s\n tail = %s; sender = %s\n\ + address = %s; key = %s\n", + u.s, serv.s, tail, sender, key, k + 1); ) + + if (pipe(p) || (kid = fork()) == -1) + return (-1); + if (!kid) { + dup2(p[1], 1); + close(p[0]); + close(p[1]); + execl("/usr/bin/userv", "/usr/bin/userv", + "-f", "stdin=/dev/null", + u.s, serv.s, + tail, sender, key, k + 1, + (char *)0); + _exit(127); + } + close(p[1]); + n = read(p[0], &ch, 1); + close(p[0]); + if (wait_pid(&wstat, kid) < 0) { return (-1); } + D( fprintf(stderr, "userv exited with status %d\n", wstat); ) + if (n != 1 || wstat) { errno = EAGAIN; return (-1); } + D( fprintf(stderr, "userv answer was `%c'\n", ch); ) + } else if (dlen != 1) { + errno = EIO; + return (-1); + } *rc = ch; return (1); } -static int local(int cdb, const char *l, int len, int *rc) +static int local(int cdb, const char *l, int len, + const char *sender, int *rc) { int code; int err = 0; int dash; - if ((err = localprobe(cdb, l, len, 0, &code)) != 0) goto done; + if ((err = localprobe(cdb, sender, l, len, 0, l + len, &code)) != 0) + goto done; for (;;) { dash = byte_rchr(l, len, '-'); if (dash == len) break; - if ((err = localprobe(cdb, l, dash, "-default", &code)) != 0) goto done; + if ((err = localprobe(cdb, sender, + l, dash, "-default", l + dash + 1, &code)) != 0) + goto done; len = dash; } *rc = 0; @@ -100,25 +154,25 @@ done: } static int virt(int cdb, const char *u, int ulen, - const char *addr, int alen, int *rc) + const char *addr, int alen, const char *sender, int *rc) { static stralloc l = STRALLOC_INIT; uint32 dlen; int err; - if ((err = probe(cdb, 'V', addr, alen, 0, &dlen)) <= 0) + if ((err = probe(cdb, 'V', addr, alen, 0, 0, &dlen)) <= 0) return (err); if (!stralloc_ready(&l, dlen + 1)) return (-1); if (read(cdb, l.s, dlen) != dlen) { errno = EIO; return (-1); } l.s[dlen] = '-'; l.len = dlen + 1; - if (!stralloc_catb(&l, u, ulen)) return (-1); - D( printf("*** virtual map -> `%.*s'\n", l.len, l.s); ) - if (local(cdb, l.s, l.len, rc) < 0) return (-1); + if (!stralloc_catb(&l, u, ulen) || !stralloc_0(&l)) return (-1); + D( printf("*** virtual map -> `%s'\n", l.s); ) + if (local(cdb, l.s, l.len - 1, sender, rc) < 0) return (-1); return (1); } -int addrcheck(int cdb, const char *addr, int *rc) +int addrcheck(int cdb, const char *addr, const char *sender, int *rc) { int at, len, dot; int err = 0; @@ -127,26 +181,27 @@ int addrcheck(int cdb, const char *addr, int *rc) len = str_len(addr); at = str_chr(addr, '@'); if (!addr[at]) - return (local(cdb, addr, len, rc)); + return (local(cdb, addr, len, sender, rc)); - if ((err = virt(cdb, addr, at, addr, len, rc)) != 0) + if ((err = virt(cdb, addr, at, addr, len, sender, rc)) != 0) return (err); dot = at + 1; while (addr[dot]) { - if ((err = virt(cdb, addr, at, addr + dot, len - dot, rc)) != 0) + if ((err = virt(cdb, addr, at, addr + dot, len - dot, sender, rc)) != 0) return (err); dot += byte_chr(addr + dot + 1, len - dot - 1, '.') + 1; } - if ((err = probe(cdb, '@', addr + at + 1, len - at - 1, 0, &dlen)) < 0) + if ((err = probe(cdb, '@', addr + at + 1, len - at - 1, 0, 0, &dlen)) < 0) return (-1); if (!err) { *rc = 1; return (0); } if (dlen != 0) { errno = EINVAL; return (-1); } - return (local(cdb, addr, at, rc)); + return (local(cdb, addr, at, sender, rc)); } #ifdef TEST + #include #include #include @@ -160,16 +215,16 @@ int main(int argc, char *argv[]) int rc; int i; - if (argc < 3) { - fprintf(stderr, "usage: addrcheck CDB ADDR...\n"); + if (argc < 4) { + fprintf(stderr, "usage: addrcheck CDB SENDER ADDR...\n"); return (1); } if ((fd = open(argv[1], O_RDONLY)) < 0) { perror(argv[1]); return (1); } - for (i = 2; i < argc; i++) { - if (addrcheck(fd, argv[i], &rc) < 0) { + for (i = 3; i < argc; i++) { + if (addrcheck(fd, argv[i], argv[2], &rc) < 0) { perror("checking"); return (1); } diff --git a/addrcheck.h b/addrcheck.h index 7c51909..b4bc555 100644 --- a/addrcheck.h +++ b/addrcheck.h @@ -1,6 +1,6 @@ #ifndef ADDRCHECK_H #define ADDRCHECK_H -extern int addrcheck(int, const char *, int *); +extern int addrcheck(int, const char *, const char *, int *); #endif diff --git a/qmail-smtpd.c b/qmail-smtpd.c index 82ac46a..d2ad504 100644 --- a/qmail-smtpd.c +++ b/qmail-smtpd.c @@ -300,7 +300,7 @@ void smtp_rcpt(arg) char *arg; { if (!addrallowed()) { err_nogateway(); return; } if (ac_fd != -1) { int rc; - if (addrcheck(ac_fd, addr.s, &rc) < 0) { + if (addrcheck(ac_fd, addr.s, mailfrom.s, &rc) < 0) { if (errno == error_nomem) die_nomem(); else diff --git a/qmail-valid-addresses b/qmail-valid-addresses index 9821394..d43cf85 100644 --- a/qmail-valid-addresses +++ b/qmail-valid-addresses @@ -41,7 +41,7 @@ for k in udb.keys(): del udb map = {} -def addlocal(p, l, forcep = False): +def addlocal(u, p, l, forcep = False): l = 'L' + l if not os.path.exists(p): if forcep: @@ -50,8 +50,13 @@ def addlocal(p, l, forcep = False): f = open(p) top = f.readline() f.close() - if len(top) > 0 and top[0] == '!': + if len(top) > 1 and top[0:2] == '#!': map[l] = '-' + elif len(top) > 1 and top[0:2] == '#?': + name = u.name + if name[-1] == '-': + name = name[:-1] + map[l] = '?' + name else: map[l] = '+' for k in sort(umap.keys()): @@ -63,9 +68,9 @@ for k in sort(umap.keys()): if not p.startswith(qm): continue ext = p[qmlen:] - addlocal(os.path.join(u.home, p), u.name + ext) + addlocal(u, os.path.join(u.home, p), u.name + ext) else: - addlocal(os.path.join(u.home, qm), u.name, u.dash == '') + addlocal(u, os.path.join(u.home, qm), u.name, u.dash == '') me = open('control/me').readline() -- 2.11.0