diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2018-05-24 11:38:25 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2018-05-24 11:38:25 +0000 |
commit | ace804cafcf33d9452774666bb47840ac1899bd0 (patch) | |
tree | 1b3ee00426c097326c9b50cc3bc062f20c89b0f9 /usr.sbin/smtpd | |
parent | b22354e5b6362967535adf6c2e0d2c0d978e6ee0 (diff) |
switch smtpd to new grammar
ok eric@
Diffstat (limited to 'usr.sbin/smtpd')
36 files changed, 1973 insertions, 2940 deletions
diff --git a/usr.sbin/smtpd/Makefile b/usr.sbin/smtpd/Makefile index e026df770ab..a3dbc9d19b5 100644 --- a/usr.sbin/smtpd/Makefile +++ b/usr.sbin/smtpd/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.17 2018/04/28 16:54:11 eric Exp $ +# $OpenBSD: Makefile,v 1.18 2018/05/24 11:38:24 gilles Exp $ .include <bsd.own.mk> SUBDIR = smtpd SUBDIR+= smtpctl SUBDIR+= smtp -#SUBDIR+= mail +SUBDIR+= mail .include <bsd.subdir.mk> diff --git a/usr.sbin/smtpd/aliases.5 b/usr.sbin/smtpd/aliases.5 index 6f89dc56ff5..802ca9a6b51 100644 --- a/usr.sbin/smtpd/aliases.5 +++ b/usr.sbin/smtpd/aliases.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: aliases.5,v 1.14 2017/05/29 12:16:50 tedu Exp $ +.\" $OpenBSD: aliases.5,v 1.15 2018/05/24 11:38:24 gilles Exp $ .\" .\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 29 2017 $ +.Dd $Mdocdate: May 24 2018 $ .Dt ALIASES 5 .Os .Sh NAME @@ -82,9 +82,6 @@ A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). The message must be present and can be freely chosen. -.It Ar maildir : Ns Ar /path -Deliver messages to Maildir at the -.Ar path . .El .Sh FILES .Bl -tag -width "/etc/mail/aliasesXXX" -compact diff --git a/usr.sbin/smtpd/aliases.c b/usr.sbin/smtpd/aliases.c index b1396512db2..c48108258c5 100644 --- a/usr.sbin/smtpd/aliases.c +++ b/usr.sbin/smtpd/aliases.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aliases.c,v 1.71 2016/08/31 10:18:08 gilles Exp $ */ +/* $OpenBSD: aliases.c,v 1.72 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -44,12 +44,14 @@ aliases_get(struct expand *expand, const char *username) size_t nbaliases; int ret; union lookup lk; + struct dispatcher *dsp; struct table *mapping = NULL; struct table *userbase = NULL; char *pbuf; - mapping = expand->rule->r_mapping; - userbase = expand->rule->r_userbase; + dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher); + userbase = table_find(dsp->u.local.table_userbase, NULL); + mapping = table_find(dsp->u.local.table_alias, NULL); xlowercase(buf, username, sizeof(buf)); @@ -77,8 +79,6 @@ expand: nbaliases += aliases_expand_include(expand, xn->u.buffer); else { - xn->mapping = mapping; - xn->userbase = userbase; expand_insert(expand, xn); nbaliases++; } @@ -102,11 +102,13 @@ aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr) char *pbuf; int nbaliases; int ret; + struct dispatcher *dsp; struct table *mapping = NULL; struct table *userbase = NULL; - mapping = expand->rule->r_mapping; - userbase = expand->rule->r_userbase; + dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher); + userbase = table_find(dsp->u.local.table_userbase, NULL); + mapping = table_find(dsp->u.local.table_virtual, NULL); if (!bsnprintf(user, sizeof(user), "%s", maddr->user)) return 0; @@ -188,8 +190,6 @@ expand: nbaliases += aliases_expand_include(expand, xn->u.buffer); else { - xn->mapping = mapping; - xn->userbase = userbase; expand_insert(expand, xn); nbaliases++; } diff --git a/usr.sbin/smtpd/bounce.c b/usr.sbin/smtpd/bounce.c index dba407a3f3c..a9304471d15 100644 --- a/usr.sbin/smtpd/bounce.c +++ b/usr.sbin/smtpd/bounce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bounce.c,v 1.77 2016/11/30 11:52:48 eric Exp $ */ +/* $OpenBSD: bounce.c,v 1.78 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> @@ -160,7 +160,7 @@ bounce_add(uint64_t evpid) } key.bounce.dsn_ret = evp.dsn_ret; - key.bounce.expire = evp.expire; + key.bounce.ttl = evp.ttl; msg = SPLAY_FIND(bounce_message_tree, &messages, &key); if (msg == NULL) { msg = xcalloc(1, sizeof(*msg), "bounce_add"); @@ -500,7 +500,7 @@ bounce_next(struct bounce_session *s) if (s->msg->bounce.type == B_WARNING) io_xprintf(s->io, notice_warning2, - bounce_duration(s->msg->bounce.expire)); + bounce_duration(s->msg->bounce.ttl)); io_xprintf(s->io, " Below is a copy of the original message:\n" diff --git a/usr.sbin/smtpd/ca.c b/usr.sbin/smtpd/ca.c index e4806726be5..4c3bdded6d5 100644 --- a/usr.sbin/smtpd/ca.c +++ b/usr.sbin/smtpd/ca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ca.c,v 1.28 2017/11/21 12:20:34 eric Exp $ */ +/* $OpenBSD: ca.c,v 1.29 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org> @@ -75,7 +75,7 @@ ca(void) { struct passwd *pw; - purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES); + purge_config(PURGE_LISTENERS|PURGE_TABLES|PURGE_RULES|PURGE_DISPATCHERS); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); diff --git a/usr.sbin/smtpd/config.c b/usr.sbin/smtpd/config.c index b2afca085d9..d894194f559 100644 --- a/usr.sbin/smtpd/config.c +++ b/usr.sbin/smtpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.38 2017/05/17 14:00:06 deraadt Exp $ */ +/* $OpenBSD: config.c,v 1.39 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -39,6 +39,7 @@ void purge_config(uint8_t what) { + struct dispatcher *d; struct listener *l; struct table *t; struct rule *r; @@ -68,6 +69,13 @@ purge_config(uint8_t what) free(env->sc_rules); env->sc_rules = NULL; } + if (what & PURGE_DISPATCHERS) { + while (dict_poproot(env->sc_dispatchers, (void **)&d)) { + free(d); + } + free(env->sc_dispatchers); + env->sc_dispatchers = NULL; + } if (what & PURGE_PKI) { while (dict_poproot(env->sc_pki_dict, (void **)&p)) { freezero(p->pki_cert, p->pki_cert_len); diff --git a/usr.sbin/smtpd/delivery.c b/usr.sbin/smtpd/delivery.c index f2537cd44fc..e69de29bb2d 100644 --- a/usr.sbin/smtpd/delivery.c +++ b/usr.sbin/smtpd/delivery.c @@ -1,61 +0,0 @@ -/* $OpenBSD: delivery.c,v 1.6 2015/01/20 17:37:54 deraadt Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -extern struct delivery_backend delivery_backend_mbox; -extern struct delivery_backend delivery_backend_mda; -extern struct delivery_backend delivery_backend_maildir; -extern struct delivery_backend delivery_backend_filename; -extern struct delivery_backend delivery_backend_lmtp; - -struct delivery_backend * -delivery_backend_lookup(enum action_type type) -{ - switch (type) { - case A_MBOX: - return &delivery_backend_mbox; - case A_MDA: - return &delivery_backend_mda; - case A_MAILDIR: - return &delivery_backend_maildir; - case A_FILENAME: - return &delivery_backend_filename; - case A_LMTP: - return &delivery_backend_lmtp; - default: - break; - } - return NULL; -} diff --git a/usr.sbin/smtpd/delivery_filename.c b/usr.sbin/smtpd/delivery_filename.c index 931034ba612..e69de29bb2d 100644 --- a/usr.sbin/smtpd/delivery_filename.c +++ b/usr.sbin/smtpd/delivery_filename.c @@ -1,117 +0,0 @@ -/* $OpenBSD: delivery_filename.c,v 1.14 2015/12/28 19:47:57 millert Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -extern char **environ; - -/* filename backend */ -static void delivery_filename_open(struct deliver *); - -struct delivery_backend delivery_backend_filename = { - 0, delivery_filename_open -}; - - -static void -delivery_filename_open(struct deliver *deliver) -{ - struct stat sb; - size_t sz = 0; - ssize_t len; - int fd; - FILE *fp; - char *ln = NULL; - char *msg; - int n; - int escape_from; - -#define error(m) { msg = m; goto err; } -#define error2(m) { msg = m; goto err2; } - - setproctitle("file delivery"); - fd = open(deliver->to, O_CREAT | O_APPEND | O_WRONLY, 0600); - if (fd < 0) - error("open"); - if (fstat(fd, &sb) < 0) - error("fstat"); - if (S_ISREG(sb.st_mode) && flock(fd, LOCK_EX) < 0) - error("flock"); - fp = fdopen(fd, "a"); - if (fp == NULL) - error("fdopen"); - - escape_from = 0; - while ((len = getline(&ln, &sz, stdin)) != -1) { - if (ln[len - 1] == '\n') - ln[len - 1] = '\0'; - if (strncmp(ln, "From ", 5) == 0) { - if (escape_from == 0) - escape_from = 1; - else - putc('>', fp); - } - fputs(ln, fp); - putc('\n', fp); - if (ferror(fp)) - break; - } - free(ln); - if (ferror(stdin)) - error2("read error"); - putc('\n', fp); - if (fflush(fp) == EOF || ferror(fp)) - error2("write error"); - if (fsync(fd) == -1) { - if (errno != EINVAL) - error2("fsync"); - } - if (fclose(fp) == EOF) - error2("fclose"); - _exit(0); - -err2: - n = errno; - ftruncate(fd, sb.st_size); - errno = n; - -err: - perror(msg); - _exit(1); -} diff --git a/usr.sbin/smtpd/delivery_lmtp.c b/usr.sbin/smtpd/delivery_lmtp.c index f6e7952ffdc..e69de29bb2d 100644 --- a/usr.sbin/smtpd/delivery_lmtp.c +++ b/usr.sbin/smtpd/delivery_lmtp.c @@ -1,253 +0,0 @@ -/* $OpenBSD: delivery_lmtp.c,v 1.17 2016/06/05 12:10:28 gilles Exp $ */ - -/* - * Copyright (c) 2013 Ashish SHUKLA <ashish.is@lostca.se> - * Copyright (c) 2015 Sunil Nimmagadda <sunil@nimmagadda.net> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/socket.h> -#include <sys/tree.h> -#include <sys/un.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <limits.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "smtpd.h" - -/* should be more than enough for any LMTP server */ -#define MAX_CONTINUATIONS 100 - -static int inet_socket(char *); -static int lmtp_banner(char **buf, size_t *, int, FILE *); -static int lmtp_cmd(char **buf, size_t *, int, FILE *, const char *, ...) - __attribute__((__format__ (printf, 5, 6))) - __attribute__((__nonnull__ (5))); -static void lmtp_open(struct deliver *); -static int unix_socket(char *); - -struct delivery_backend delivery_backend_lmtp = { - 0, lmtp_open -}; - -static int -inet_socket(char *address) -{ - struct addrinfo hints, *res, *res0; - char *hostname, *servname; - const char *cause = NULL; - int n, s = -1, save_errno; - - if ((servname = strchr(address, ':')) == NULL) - errx(1, "invalid address: %s", address); - - *servname++ = '\0'; - hostname = address; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - n = getaddrinfo(hostname, servname, &hints, &res0); - if (n) - errx(1, "%s", gai_strerror(n)); - - for (res = res0; res; res = res->ai_next) { - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s == -1) { - cause = "socket"; - continue; - } - - if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { - cause = "connect"; - save_errno = errno; - close(s); - errno = save_errno; - s = -1; - continue; - } - - break; - } - - freeaddrinfo(res0); - if (s == -1) - errx(1, "%s", cause); - - return s; -} - -static int -unix_socket(char *path) -{ - struct sockaddr_un addr; - int s; - - if ((s = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) - err(1, "socket"); - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - if (strlcpy(addr.sun_path, path, sizeof(addr.sun_path)) - >= sizeof(addr.sun_path)) - errx(1, "socket path too long"); - - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) - err(1, "connect"); - - return s; -} - -static void -lmtp_open(struct deliver *deliver) -{ - FILE *fp; - char *buf = NULL, hn[HOST_NAME_MAX + 1], - *rcpt = deliver->to, *to = deliver->to; - size_t sz = 0; - ssize_t len; - int s; - - strsep(&rcpt, " "); - s = (to[0] == '/') ? unix_socket(to) : inet_socket(to); - if ((fp = fdopen(s, "r+")) == NULL) - err(1, "fdopen"); - - if (lmtp_banner(&buf, &sz, '2', fp) != 0) - errx(1, "Invalid LHLO reply: %s", buf); - - if (gethostname(hn, sizeof hn) == -1) - err(1, "gethostname"); - - if (lmtp_cmd(&buf, &sz, '2', fp, "LHLO %s", hn) != 0) - errx(1, "Invalid LHLO reply: %s", buf); - - if (lmtp_cmd(&buf, &sz, '2', fp, "MAIL FROM:<%s>", deliver->from) != 0) - errx(1, "MAIL FROM rejected: %s", buf); - - if (lmtp_cmd(&buf, &sz, '2', fp, "RCPT TO:<%s>", - rcpt ? deliver->dest : deliver->user) != 0) - errx(1, "RCPT TO rejected: %s", buf); - - if (lmtp_cmd(&buf, &sz, '3', fp, "DATA") != 0) - errx(1, "Invalid DATA reply: %s", buf); - - while ((len = getline(&buf, &sz, stdin)) != -1) { - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - - if (fprintf(fp, "%s%s\r\n", buf[0] == '.' ? "." : "", buf) < 0) - errx(1, "fprintf failed"); - } - - if (lmtp_cmd(&buf, &sz, '2', fp, ".") != 0) - errx(1, "Delivery error: %s", buf); - - if (lmtp_cmd(&buf, &sz, '2', fp, "QUIT") != 0) - errx(1, "Error on QUIT: %s", buf); - - exit(0); -} - -static int -lmtp_banner(char **buf, size_t *sz, int code, FILE *fp) -{ - char *bufp; - ssize_t len; - size_t counter; - - counter = 0; - do { - if ((len = getline(buf, sz, fp)) == -1) - err(1, "getline"); - if (len < 4) - err(1, "line too short"); - - bufp = *buf; - if (len >= 2 && bufp[len - 2] == '\r') - bufp[len - 2] = '\0'; - else if (bufp[len - 1] == '\n') - bufp[len - 1] = '\0'; - - if (bufp[3] == '\0' || bufp[3] == ' ') - break; - else if (bufp[3] == '-') { - if (counter == MAX_CONTINUATIONS) - errx(1, "LMTP server is sending too many continuations"); - counter++; - continue; - } - else - errx(1, "invalid line"); - } while (1); - - return bufp[0] != code; -} - -static int -lmtp_cmd(char **buf, size_t *sz, int code, FILE *fp, const char *fmt, ...) -{ - va_list ap; - char *bufp; - ssize_t len; - size_t counter; - - va_start(ap, fmt); - if (vfprintf(fp, fmt, ap) < 0) - errx(1, "vfprintf failed"); - - va_end(ap); - if (fprintf(fp, "\r\n") < 0) - errx(1, "fprintf failed"); - - if (fflush(fp) != 0) - err(1, "fflush"); - - counter = 0; - do { - if ((len = getline(buf, sz, fp)) == -1) - err(1, "getline"); - if (len < 4) - err(1, "line too short"); - - bufp = *buf; - if (len >= 2 && bufp[len - 2] == '\r') - bufp[len - 2] = '\0'; - else if (bufp[len - 1] == '\n') - bufp[len - 1] = '\0'; - - if (bufp[3] == '\0' || bufp[3] == ' ') - break; - else if (bufp[3] == '-') { - if (counter == MAX_CONTINUATIONS) - errx(1, "LMTP server is sending too many continuations"); - counter++; - continue; - } - else - errx(1, "invalid line"); - } while (1); - - return bufp[0] != code; -} diff --git a/usr.sbin/smtpd/delivery_maildir.c b/usr.sbin/smtpd/delivery_maildir.c index 2990eb61e8d..e69de29bb2d 100644 --- a/usr.sbin/smtpd/delivery_maildir.c +++ b/usr.sbin/smtpd/delivery_maildir.c @@ -1,149 +0,0 @@ -/* $OpenBSD: delivery_maildir.c,v 1.18 2016/08/31 10:18:08 gilles Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -extern char **environ; - -/* maildir backend */ -static void delivery_maildir_open(struct deliver *); -static int mailaddr_tag(const struct mailaddr *, char *, size_t); - -struct delivery_backend delivery_backend_maildir = { - 1, delivery_maildir_open -}; - -static int -mailaddr_tag(const struct mailaddr *maddr, char *dest, size_t len) -{ - char *tag; - char *sanitized; - - if ((tag = strchr(maddr->user, *env->sc_subaddressing_delim))) { - tag++; - while (*tag == '.') - tag++; - } - if (tag == NULL) - return 1; - - if (strlcpy(dest, tag, len) >= len) - return 0; - for (sanitized = dest; *sanitized; sanitized++) - if (strchr(MAILADDR_ESCAPE, *sanitized)) - *sanitized = ':'; - return 1; -} - -static void -delivery_maildir_open(struct deliver *deliver) -{ - char tmp[PATH_MAX], new[PATH_MAX], tag[PATH_MAX]; - int ch, fd; - FILE *fp; - char *msg; - int n; - const char *chd; - struct mailaddr maddr; - struct stat sb; - -#define error(m) { msg = m; goto err; } -#define error2(m) { msg = m; goto err2; } - - setproctitle("maildir delivery"); - - memset(&maddr, 0, sizeof maddr); - if (!text_to_mailaddr(&maddr, deliver->dest)) - error("cannot parse destination address"); - - memset(tag, 0, sizeof tag); - if (!mailaddr_tag(&maddr, tag, sizeof tag)) - error("cannot extract tag from destination address"); - - if (mkdirs(deliver->to, 0700) < 0 && errno != EEXIST) - error("cannot mkdir maildir"); - chd = deliver->to; - - if (tag[0]) { - (void)snprintf(tmp, sizeof tmp, "%s/.%s", deliver->to, tag); - if (stat(tmp, &sb) != -1) - chd = tmp; - } - - if (chdir(chd) < 0) - error("cannot cd to maildir"); - if (mkdir("cur", 0700) < 0 && errno != EEXIST) - error("mkdir cur failed"); - if (mkdir("tmp", 0700) < 0 && errno != EEXIST) - error("mkdir tmp failed"); - if (mkdir("new", 0700) < 0 && errno != EEXIST) - error("mkdir new failed"); - (void)snprintf(tmp, sizeof tmp, "tmp/%lld.%d.%s", - (long long int) time(NULL), - getpid(), env->sc_hostname); - fd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0600); - if (fd < 0) - error("cannot open tmp file"); - fp = fdopen(fd, "w"); - if (fp == NULL) - error2("fdopen"); - while ((ch = getc(stdin)) != EOF) - if (putc(ch, fp) == EOF) - break; - if (ferror(stdin)) - error2("read error"); - if (fflush(fp) == EOF || ferror(fp)) - error2("write error"); - if (fsync(fd) < 0) - error2("fsync"); - if (fclose(fp) == EOF) - error2("fclose"); - (void)snprintf(new, sizeof new, "new/%s", tmp + 4); - if (rename(tmp, new) < 0) - error2("cannot rename tmp->new"); - _exit(0); - -err2: - n = errno; - unlink(tmp); - errno = n; -err: - perror(msg); - _exit(1); -} diff --git a/usr.sbin/smtpd/delivery_mbox.c b/usr.sbin/smtpd/delivery_mbox.c index a915e2be786..e69de29bb2d 100644 --- a/usr.sbin/smtpd/delivery_mbox.c +++ b/usr.sbin/smtpd/delivery_mbox.c @@ -1,67 +0,0 @@ -/* $OpenBSD: delivery_mbox.c,v 1.12 2015/12/22 07:54:57 sunil Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -#define PATH_MAILLOCAL "/usr/libexec/mail.local" - -extern char **environ; - -/* mbox backend */ -static void delivery_mbox_open(struct deliver *); - -struct delivery_backend delivery_backend_mbox = { - 1, delivery_mbox_open -}; - - -static void -delivery_mbox_open(struct deliver *deliver) -{ - char *environ_new[2]; - - environ_new[0] = "PATH=" _PATH_DEFPATH; - environ_new[1] = (char *)NULL; - environ = environ_new; - - if (deliver->from[0] == '\0') - (void)strlcpy(deliver->from, "MAILER-DAEMON", - sizeof deliver->from); - execle(PATH_MAILLOCAL, PATH_MAILLOCAL, "-f", deliver->from, - deliver->to, (char *)NULL, environ_new); - perror("execle"); - _exit(1); -} diff --git a/usr.sbin/smtpd/delivery_mda.c b/usr.sbin/smtpd/delivery_mda.c index d37aac5325c..e69de29bb2d 100644 --- a/usr.sbin/smtpd/delivery_mda.c +++ b/usr.sbin/smtpd/delivery_mda.c @@ -1,61 +0,0 @@ -/* $OpenBSD: delivery_mda.c,v 1.9 2015/01/20 17:37:54 deraadt Exp $ */ - -/* - * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <ctype.h> -#include <err.h> -#include <event.h> -#include <fcntl.h> -#include <imsg.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -#include "smtpd.h" -#include "log.h" - -extern char **environ; - -/* mda backend */ -static void delivery_mda_open(struct deliver *); - -struct delivery_backend delivery_backend_mda = { - 0, delivery_mda_open -}; - - -static void -delivery_mda_open(struct deliver *deliver) -{ - char *environ_new[2]; - - environ_new[0] = "PATH=" _PATH_DEFPATH; - environ_new[1] = (char *)NULL; - environ = environ_new; - execle("/bin/sh", "/bin/sh", "-c", deliver->to, (char *)NULL, - environ_new); - perror("execle"); - _exit(1); -} diff --git a/usr.sbin/smtpd/envelope.c b/usr.sbin/smtpd/envelope.c index 4e0ad6b8549..2f91aea15c7 100644 --- a/usr.sbin/smtpd/envelope.c +++ b/usr.sbin/smtpd/envelope.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envelope.c,v 1.37 2017/08/06 08:35:14 gilles Exp $ */ +/* $OpenBSD: envelope.c,v 1.38 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -151,7 +151,7 @@ envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen) goto end; } - if (version != 2) { + if (version != SMTPD_ENVELOPE_VERSION) { log_debug("debug: bad envelope version %lld", version); goto end; } @@ -172,6 +172,7 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) char *p = dest; envelope_ascii_dump(ep, &dest, &len, "version"); + envelope_ascii_dump(ep, &dest, &len, "dispatcher"); envelope_ascii_dump(ep, &dest, &len, "tag"); envelope_ascii_dump(ep, &dest, &len, "type"); envelope_ascii_dump(ep, &dest, &len, "smtpname"); @@ -185,7 +186,7 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) envelope_ascii_dump(ep, &dest, &len, "ctime"); envelope_ascii_dump(ep, &dest, &len, "last-try"); envelope_ascii_dump(ep, &dest, &len, "last-bounce"); - envelope_ascii_dump(ep, &dest, &len, "expire"); + envelope_ascii_dump(ep, &dest, &len, "ttl"); envelope_ascii_dump(ep, &dest, &len, "retry"); envelope_ascii_dump(ep, &dest, &len, "flags"); envelope_ascii_dump(ep, &dest, &len, "dsn-notify"); @@ -197,24 +198,13 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) switch (ep->type) { case D_MDA: - envelope_ascii_dump(ep, &dest, &len, "mda-buffer"); - envelope_ascii_dump(ep, &dest, &len, "mda-method"); + envelope_ascii_dump(ep, &dest, &len, "mda-exec"); envelope_ascii_dump(ep, &dest, &len, "mda-user"); - envelope_ascii_dump(ep, &dest, &len, "mda-usertable"); - envelope_ascii_dump(ep, &dest, &len, "mda-delivery-user"); break; case D_MTA: - envelope_ascii_dump(ep, &dest, &len, "mta-relay"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-auth"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-cert"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-ca"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-flags"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-heloname"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-helotable"); - envelope_ascii_dump(ep, &dest, &len, "mta-relay-source"); break; case D_BOUNCE: - envelope_ascii_dump(ep, &dest, &len, "bounce-expire"); + envelope_ascii_dump(ep, &dest, &len, "bounce-ttl"); envelope_ascii_dump(ep, &dest, &len, "bounce-delay"); envelope_ascii_dump(ep, &dest, &len, "bounce-type"); break; @@ -324,24 +314,6 @@ ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf) } static int -ascii_load_mda_method(enum action_type *dest, char *buf) -{ - if (strcasecmp(buf, "mbox") == 0) - *dest = A_MBOX; - else if (strcasecmp(buf, "maildir") == 0) - *dest = A_MAILDIR; - else if (strcasecmp(buf, "filename") == 0) - *dest = A_FILENAME; - else if (strcasecmp(buf, "mda") == 0) - *dest = A_MDA; - else if (strcasecmp(buf, "lmtp") == 0) - *dest = A_LMTP; - else - return 0; - return 1; -} - -static int ascii_load_mailaddr(struct mailaddr *dest, char *buf) { if (!text_to_mailaddr(dest, buf)) @@ -423,11 +395,15 @@ ascii_load_dsn_ret(enum dsn_ret *ret, char *buf) static int ascii_load_field(const char *field, struct envelope *ep, char *buf) { + if (strcasecmp("dispatcher", field) == 0) + return ascii_load_string(ep->dispatcher, buf, + sizeof ep->dispatcher); + if (strcasecmp("bounce-delay", field) == 0) return ascii_load_time(&ep->agent.bounce.delay, buf); - if (strcasecmp("bounce-expire", field) == 0) - return ascii_load_time(&ep->agent.bounce.expire, buf); + if (strcasecmp("bounce-ttl", field) == 0) + return ascii_load_time(&ep->agent.bounce.ttl, buf); if (strcasecmp("bounce-type", field) == 0) return ascii_load_bounce_type(&ep->agent.bounce.type, buf); @@ -442,8 +418,8 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf) return ascii_load_string(ep->errorline, buf, sizeof ep->errorline); - if (strcasecmp("expire", field) == 0) - return ascii_load_time(&ep->expire, buf); + if (strcasecmp("ttl", field) == 0) + return ascii_load_time(&ep->ttl, buf); if (strcasecmp("flags", field) == 0) return ascii_load_flags(&ep->flags, buf); @@ -461,25 +437,6 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf) if (strcasecmp("last-try", field) == 0) return ascii_load_time(&ep->lasttry, buf); - if (strcasecmp("mda-buffer", field) == 0) - return ascii_load_string(ep->agent.mda.buffer, buf, - sizeof ep->agent.mda.buffer); - - if (strcasecmp("mda-method", field) == 0) - return ascii_load_mda_method(&ep->agent.mda.method, buf); - - if (strcasecmp("mda-user", field) == 0) - return ascii_load_string(ep->agent.mda.username, buf, - sizeof ep->agent.mda.username); - - if (strcasecmp("mda-usertable", field) == 0) - return ascii_load_string(ep->agent.mda.usertable, buf, - sizeof ep->agent.mda.usertable); - - if (strcasecmp("mda-delivery-user", field) == 0) - return ascii_load_string(ep->agent.mda.delivery_user, buf, - sizeof ep->agent.mda.delivery_user); - if (strcasecmp("mta-relay", field) == 0) { int ret; uint16_t flags = ep->agent.mta.relay.flags; @@ -524,6 +481,12 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf) if (strcasecmp("rcpt", field) == 0) return ascii_load_mailaddr(&ep->rcpt, buf); + if (strcasecmp("mda-exec", field) == 0) + return ascii_load_string(ep->mda_exec, buf, sizeof(ep->mda_exec)); + + if (strcasecmp("mda-user", field) == 0) + return ascii_load_string(ep->mda_user, buf, sizeof(ep->mda_user)); + if (strcasecmp("sender", field) == 0) return ascii_load_mailaddr(&ep->sender, buf); @@ -638,33 +601,6 @@ ascii_dump_type(enum delivery_type type, char *dest, size_t len) } static int -ascii_dump_mda_method(enum action_type type, char *dest, size_t len) -{ - char *p = NULL; - - switch (type) { - case A_LMTP: - p = "lmtp"; - break; - case A_MAILDIR: - p = "maildir"; - break; - case A_MBOX: - p = "mbox"; - break; - case A_FILENAME: - p = "filename"; - break; - case A_MDA: - p = "mda"; - break; - default: - return 0; - } - return bsnprintf(dest, len, "%s", p); -} - -static int ascii_dump_mailaddr(const struct mailaddr *addr, char *dest, size_t len) { return bsnprintf(dest, len, "%s@%s", @@ -763,16 +699,19 @@ static int ascii_dump_field(const char *field, const struct envelope *ep, char *buf, size_t len) { + if (strcasecmp(field, "dispatcher") == 0) + return ascii_dump_string(ep->dispatcher, buf, len); + if (strcasecmp(field, "bounce-delay") == 0) { if (ep->agent.bounce.type != B_WARNING) return (1); return ascii_dump_time(ep->agent.bounce.delay, buf, len); } - if (strcasecmp(field, "bounce-expire") == 0) { + if (strcasecmp(field, "bounce-ttl") == 0) { if (ep->agent.bounce.type != B_WARNING) return (1); - return ascii_dump_time(ep->agent.bounce.expire, buf, len); + return ascii_dump_time(ep->agent.bounce.ttl, buf, len); } if (strcasecmp(field, "bounce-type") == 0) @@ -787,8 +726,8 @@ ascii_dump_field(const char *field, const struct envelope *ep, if (strcasecmp(field, "errorline") == 0) return ascii_dump_string(ep->errorline, buf, len); - if (strcasecmp(field, "expire") == 0) - return ascii_dump_time(ep->expire, buf, len); + if (strcasecmp(field, "ttl") == 0) + return ascii_dump_time(ep->ttl, buf, len); if (strcasecmp(field, "flags") == 0) return ascii_dump_flags(ep->flags, buf, len); @@ -805,21 +744,6 @@ ascii_dump_field(const char *field, const struct envelope *ep, if (strcasecmp(field, "last-try") == 0) return ascii_dump_time(ep->lasttry, buf, len); - if (strcasecmp(field, "mda-buffer") == 0) - return ascii_dump_string(ep->agent.mda.buffer, buf, len); - - if (strcasecmp(field, "mda-method") == 0) - return ascii_dump_mda_method(ep->agent.mda.method, buf, len); - - if (strcasecmp(field, "mda-user") == 0) - return ascii_dump_string(ep->agent.mda.username, buf, len); - - if (strcasecmp(field, "mda-delivery-user") == 0) - return ascii_dump_string(ep->agent.mda.delivery_user, buf, len); - - if (strcasecmp(field, "mda-usertable") == 0) - return ascii_dump_string(ep->agent.mda.usertable, buf, len); - if (strcasecmp(field, "mta-relay") == 0) { if (ep->agent.mta.relay.hostname[0]) return ascii_dump_mta_relay_url(&ep->agent.mta.relay, @@ -861,6 +785,18 @@ ascii_dump_field(const char *field, const struct envelope *ep, if (strcasecmp(field, "rcpt") == 0) return ascii_dump_mailaddr(&ep->rcpt, buf, len); + if (strcasecmp(field, "mda-exec") == 0) { + if (ep->mda_exec[0]) + return ascii_dump_string(ep->mda_exec, buf, len); + return 1; + } + + if (strcasecmp(field, "mda-user") == 0) { + if (ep->mda_user[0]) + return ascii_dump_string(ep->mda_user, buf, len); + return 1; + } + if (strcasecmp(field, "sender") == 0) return ascii_dump_mailaddr(&ep->sender, buf, len); diff --git a/usr.sbin/smtpd/expand.c b/usr.sbin/smtpd/expand.c index 55c043ec3d5..fda95be4432 100644 --- a/usr.sbin/smtpd/expand.c +++ b/usr.sbin/smtpd/expand.c @@ -1,4 +1,4 @@ -/* $OpenBSD: expand.c,v 1.29 2015/12/28 22:08:30 jung Exp $ */ +/* $OpenBSD: expand.c,v 1.30 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> @@ -63,6 +63,7 @@ expand_insert(struct expand *expand, struct expandnode *node) { struct expandnode *xn; + node->rule = expand->rule; node->parent = expand->parent; log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s", @@ -85,7 +86,6 @@ expand_insert(struct expand *expand, struct expandnode *node) xn = xmemdup(node, sizeof *xn, "expand_insert"); xn->rule = expand->rule; xn->parent = expand->parent; - xn->alias = expand->alias; if (xn->parent) xn->depth = xn->parent->depth + 1; else @@ -136,20 +136,15 @@ expand_cmp(struct expandnode *e1, struct expandnode *e2) return -1; if (e1->sameuser > e2->sameuser) return 1; - if (e1->mapping < e2->mapping) + if (e1->realuser < e2->realuser) return -1; - if (e1->mapping > e2->mapping) - return 1; - if (e1->userbase < e2->userbase) - return -1; - if (e1->userbase > e2->userbase) + if (e1->realuser > e2->realuser) return 1; r = memcmp(&e1->u, &e2->u, sizeof(e1->u)); if (r) return (r); - if (e1->parent == e2->parent) return (0); @@ -310,14 +305,14 @@ expandnode_info(struct expandnode *e) if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) return NULL; - if (e->mapping) { - (void)strlcat(buffer, ", mapping=", sizeof buffer); - (void)strlcat(buffer, e->mapping->t_name, sizeof buffer); - } + (void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule); + if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) + return NULL; - if (e->userbase) { - (void)strlcat(buffer, ", userbase=", sizeof buffer); - (void)strlcat(buffer, e->userbase->t_name, sizeof buffer); + if (e->rule) { + (void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher); + if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) + return NULL; } if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer) diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c index 210d41a7b0f..a2fc727d971 100644 --- a/usr.sbin/smtpd/lka.c +++ b/usr.sbin/smtpd/lka.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka.c,v 1.202 2018/01/03 11:12:21 sunil Exp $ */ +/* $OpenBSD: lka.c,v 1.203 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -302,6 +302,37 @@ lka_imsg(struct mproc *p, struct imsg *imsg) m_close(p); return; + case IMSG_MTA_LOOKUP_SMARTHOST: + m_msg(&m, imsg); + m_get_id(&m, &reqid); + m_get_string(&m, &tablename); + m_end(&m); + + table = table_find(tablename, NULL); + + m_create(p, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1); + m_add_id(p, reqid); + + if (table == NULL) { + log_warn("warn: smarthost table %s missing", tablename); + m_add_int(p, LKA_TEMPFAIL); + } + else { + ret = table_fetch(table, NULL, K_RELAYHOST, &lk); + if (ret == -1) + m_add_int(p, LKA_TEMPFAIL); + else if (ret == 0) + m_add_int(p, LKA_PERMFAIL); + else { + snprintf(buf, sizeof(buf), "%s", + relayhost_to_text(&lk.relayhost)); + m_add_int(p, LKA_OK); + m_add_string(p, buf); + } + } + m_close(p); + return; + case IMSG_CONF_START: return; diff --git a/usr.sbin/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c index d12b3c56fda..edc46026d56 100644 --- a/usr.sbin/smtpd/lka_session.c +++ b/usr.sbin/smtpd/lka_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka_session.c,v 1.81 2017/05/26 21:30:00 gilles Exp $ */ +/* $OpenBSD: lka_session.c,v 1.82 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> @@ -93,6 +93,7 @@ lka_session(uint64_t id, struct envelope *envelope) memset(&xn, 0, sizeof xn); xn.type = EXPAND_ADDRESS; xn.u.mailaddr = lks->envelope.rcpt; + lks->expand.parent = NULL; lks->expand.rule = NULL; lks->expand.queue = &lks->nodes; expand_insert(&lks->expand, &xn); @@ -103,6 +104,7 @@ void lka_session_forward_reply(struct forward_req *fwreq, int fd) { struct lka_session *lks; + struct dispatcher *dsp; struct rule *rule; struct expandnode *xn; int ret; @@ -122,12 +124,13 @@ lka_session_forward_reply(struct forward_req *fwreq, int fd) break; case 1: if (fd == -1) { - if (lks->expand.rule->r_forwardonly) { + dsp = dict_get(env->sc_dispatchers, lks->rule->dispatcher); + if (dsp->u.local.forward_only) { log_trace(TRACE_EXPAND, "expand: no .forward " "for user %s on forward-only rule", fwreq->user); lks->error = LKA_TEMPFAIL; } - else if (lks->expand.rule->r_action == A_NONE) { + else if (dsp->u.local.expand_only) { log_trace(TRACE_EXPAND, "expand: no .forward " "for user %s and no default action on rule", fwreq->user); lks->error = LKA_PERMFAIL; @@ -139,12 +142,12 @@ lka_session_forward_reply(struct forward_req *fwreq, int fd) } } else { + dsp = dict_get(env->sc_dispatchers, rule->dispatcher); + /* expand for the current user and rule */ lks->expand.rule = rule; lks->expand.parent = xn; - lks->expand.alias = 0; - xn->mapping = rule->r_mapping; - xn->userbase = rule->r_userbase; + /* forwards_get() will close the descriptor no matter what */ ret = forwards_get(fd, &lks->expand); if (ret == -1) { @@ -153,12 +156,12 @@ lka_session_forward_reply(struct forward_req *fwreq, int fd) lks->error = LKA_TEMPFAIL; } else if (ret == 0) { - if (lks->expand.rule->r_forwardonly) { + if (dsp->u.local.forward_only) { log_trace(TRACE_EXPAND, "expand: empty .forward " "for user %s on forward-only rule", fwreq->user); lks->error = LKA_TEMPFAIL; } - else if (lks->expand.rule->r_action == A_NONE) { + else if (dsp->u.local.expand_only) { log_trace(TRACE_EXPAND, "expand: empty .forward " "for user %s and no default action on rule", fwreq->user); lks->error = LKA_PERMFAIL; @@ -253,6 +256,8 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) struct envelope ep; struct expandnode node; struct mailaddr maddr; + struct dispatcher *dsp; + struct table *userbase; int r; union lookup lk; char *tag; @@ -280,24 +285,22 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) ep.dest = xn->u.mailaddr; if (xn->parent) /* nodes with parent are forward addresses */ ep.flags |= EF_INTERNAL; + rule = ruleset_match(&ep); - if (rule == NULL || rule->r_decision == R_REJECT) { + if (rule == NULL || rule->reject) { lks->error = (errno == EAGAIN) ? LKA_TEMPFAIL : LKA_PERMFAIL; break; } - xn->mapping = rule->r_mapping; - xn->userbase = rule->r_userbase; - - if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { + dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); + if (dsp->type == DISPATCHER_REMOTE) { lka_submit(lks, rule, xn); } - else if (rule->r_desttype == DEST_VDOM) { + else if (dsp->u.local.table_virtual) { /* expand */ lks->expand.rule = rule; lks->expand.parent = xn; - lks->expand.alias = 1; /* temporary replace the mailaddr with a copy where * we eventually strip the '+'-part before lookup. @@ -320,36 +323,33 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) else { lks->expand.rule = rule; lks->expand.parent = xn; - lks->expand.alias = 1; + xn->rule = rule; + memset(&node, 0, sizeof node); node.type = EXPAND_USERNAME; xlowercase(node.u.user, xn->u.mailaddr.user, sizeof node.u.user); - node.mapping = rule->r_mapping; - node.userbase = rule->r_userbase; expand_insert(&lks->expand, &node); } break; case EXPAND_USERNAME: log_trace(TRACE_EXPAND, "expand: lka_expand: username: %s " - "[depth=%d]", xn->u.user, xn->depth); - - if (xn->sameuser) { - log_trace(TRACE_EXPAND, "expand: lka_expand: same " - "user, submitting"); - lka_submit(lks, rule, xn); - break; - } + "[depth=%d, sameuser=%d]", + xn->u.user, xn->depth, xn->sameuser); /* expand aliases with the given rule */ + dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); + lks->expand.rule = rule; lks->expand.parent = xn; - lks->expand.alias = 1; - xn->mapping = rule->r_mapping; - xn->userbase = rule->r_userbase; - if (rule->r_mapping) { - r = aliases_get(&lks->expand, xn->u.user); + + if (!xn->sameuser && + (dsp->u.local.table_alias || dsp->u.local.table_virtual)) { + if (dsp->u.local.table_alias) + r = aliases_get(&lks->expand, xn->u.user); + if (dsp->u.local.table_virtual) + r = aliases_virtual_get(&lks->expand, &xn->u.mailaddr); if (r == -1) { log_trace(TRACE_EXPAND, "expand: lka_expand: " "error in alias lookup"); @@ -363,7 +363,8 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) if ((tag = strchr(xn->u.user, *env->sc_subaddressing_delim)) != NULL) *tag++ = '\0'; - r = table_lookup(rule->r_userbase, NULL, xn->u.user, K_USERINFO, &lk); + userbase = table_find(dsp->u.local.table_userbase, NULL); + r = table_lookup(userbase, NULL, xn->u.user, K_USERINFO, &lk); if (r == -1) { log_trace(TRACE_EXPAND, "expand: lka_expand: " "backend error while searching user"); @@ -376,10 +377,19 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) lks->error = LKA_PERMFAIL; break; } + xn->realuser = 1; + + if (xn->sameuser && xn->parent->forwarded) { + log_trace(TRACE_EXPAND, "expand: lka_expand: same " + "user, submitting"); + lka_submit(lks, rule, xn); + break; + } /* no aliases found, query forward file */ lks->rule = rule; lks->node = xn; + xn->forwarded = 1; memset(&fwreq, 0, sizeof(fwreq)); fwreq.id = lks->id; @@ -394,7 +404,8 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) break; case EXPAND_FILENAME: - if (rule->r_forwardonly) { + dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); + if (dsp->u.local.forward_only) { log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule"); lks->error = LKA_TEMPFAIL; break; @@ -405,7 +416,8 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) break; case EXPAND_ERROR: - if (rule->r_forwardonly) { + dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); + if (dsp->u.local.forward_only) { log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule"); lks->error = LKA_TEMPFAIL; break; @@ -420,7 +432,8 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) break; case EXPAND_FILTER: - if (rule->r_forwardonly) { + dsp = dict_xget(env->sc_dispatchers, rule->dispatcher); + if (dsp->u.local.forward_only) { log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule"); lks->error = LKA_TEMPFAIL; break; @@ -429,27 +442,6 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn) "[depth=%d]", xn->u.buffer, xn->depth); lka_submit(lks, rule, xn); break; - - case EXPAND_MAILDIR: - log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: %s " - "[depth=%d]", xn->u.buffer, xn->depth); - r = table_lookup(rule->r_userbase, NULL, - xn->parent->u.user, K_USERINFO, &lk); - if (r == -1) { - log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: " - "backend error while searching user"); - lks->error = LKA_TEMPFAIL; - break; - } - if (r == 0) { - log_trace(TRACE_EXPAND, "expand: lka_expand: maildir: " - "user-part does not match system user"); - lks->error = LKA_PERMFAIL; - break; - } - - lka_submit(lks, rule, xn); - break; } } @@ -469,101 +461,54 @@ lka_find_ancestor(struct expandnode *xn, enum expand_type type) static void lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) { - union lookup lk; struct envelope *ep; - struct expandnode *xn2; - int r; + struct dispatcher *dsp; + const char *user; + const char *format; ep = xmemdup(&lks->envelope, sizeof *ep, "lka_submit"); - ep->expire = rule->r_qexpire; + (void)strlcpy(ep->dispatcher, rule->dispatcher, sizeof ep->dispatcher); - switch (rule->r_action) { - case A_RELAY: - case A_RELAYVIA: + dsp = dict_xget(env->sc_dispatchers, ep->dispatcher); + + switch (dsp->type) { + case DISPATCHER_REMOTE: if (xn->type != EXPAND_ADDRESS) fatalx("lka_deliver: expect address"); ep->type = D_MTA; ep->dest = xn->u.mailaddr; - ep->agent.mta.relay = rule->r_value.relayhost; - - /* only rewrite if not a bounce */ - if (ep->sender.user[0] && rule->r_as && rule->r_as->user[0]) - (void)strlcpy(ep->sender.user, rule->r_as->user, - sizeof ep->sender.user); - if (ep->sender.user[0] && rule->r_as && rule->r_as->domain[0]) - (void)strlcpy(ep->sender.domain, rule->r_as->domain, - sizeof ep->sender.domain); break; - case A_NONE: - case A_MBOX: - case A_MAILDIR: - case A_FILENAME: - case A_MDA: - case A_LMTP: + + case DISPATCHER_BOUNCE: + case DISPATCHER_LOCAL: + if (xn->type != EXPAND_USERNAME && + xn->type != EXPAND_FILENAME && + xn->type != EXPAND_FILTER) + fatalx("lka_deliver: wrong type: %d", xn->type); + ep->type = D_MDA; ep->dest = lka_find_ancestor(xn, EXPAND_ADDRESS)->u.mailaddr; - - /* set username */ - if ((xn->type == EXPAND_FILTER || xn->type == EXPAND_FILENAME) - && xn->alias) { - (void)strlcpy(ep->agent.mda.username, SMTPD_USER, - sizeof(ep->agent.mda.username)); - } + if (xn->type == EXPAND_USERNAME) + (void)strlcpy(ep->mda_user, xn->u.user, sizeof(ep->mda_user)); else { - xn2 = lka_find_ancestor(xn, EXPAND_USERNAME); - (void)strlcpy(ep->agent.mda.username, xn2->u.user, - sizeof(ep->agent.mda.username)); - } - - r = table_lookup(rule->r_userbase, NULL, ep->agent.mda.username, - K_USERINFO, &lk); - if (r <= 0) { - lks->error = (r == -1) ? LKA_TEMPFAIL : LKA_PERMFAIL; - free(ep); - return; - } - (void)strlcpy(ep->agent.mda.usertable, rule->r_userbase->t_name, - sizeof ep->agent.mda.usertable); - (void)strlcpy(ep->agent.mda.username, lk.userinfo.username, - sizeof ep->agent.mda.username); - strlcpy(ep->agent.mda.delivery_user, rule->r_delivery_user, - sizeof ep->agent.mda.delivery_user); - - if (xn->type == EXPAND_FILENAME) { - ep->agent.mda.method = A_FILENAME; - (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, - sizeof ep->agent.mda.buffer); - } - else if (xn->type == EXPAND_FILTER) { - ep->agent.mda.method = A_MDA; - (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, - sizeof ep->agent.mda.buffer); - } - else if (xn->type == EXPAND_USERNAME) { - ep->agent.mda.method = rule->r_action; - (void)strlcpy(ep->agent.mda.buffer, rule->r_value.buffer, - sizeof ep->agent.mda.buffer); - } - else if (xn->type == EXPAND_MAILDIR) { - ep->agent.mda.method = A_MAILDIR; - (void)strlcpy(ep->agent.mda.buffer, xn->u.buffer, - sizeof ep->agent.mda.buffer); - } - else - fatalx("lka_deliver: bad node type"); - - r = mda_expand_format(ep->agent.mda.buffer, - sizeof(ep->agent.mda.buffer), ep, &lk.userinfo); - if (!r) { - lks->error = LKA_TEMPFAIL; - log_warnx("warn: format string error while" - " expanding for user %s", ep->agent.mda.username); - free(ep); - return; + user = !xn->parent->realuser ? + SMTPD_USER : + xn->parent->u.user; + (void)strlcpy(ep->mda_user, user, sizeof (ep->mda_user)); + + /* this battle needs to be fought ... */ + if (strcmp(ep->mda_user, SMTPD_USER) == 0) + log_warn("commands executed from aliases " + "run with %s privileges", SMTPD_USER); + + if (xn->type == EXPAND_FILENAME) + format = "/bin/cat - >> %s"; + else if (xn->type == EXPAND_FILTER) + format = "%s"; + (void)snprintf(ep->mda_exec, sizeof(ep->mda_exec), + format, xn->u.buffer); } break; - default: - fatalx("lka_submit: bad rule action"); } TAILQ_INSERT_TAIL(&lks->deliverylist, ep, entry); diff --git a/usr.sbin/smtpd/mda.c b/usr.sbin/smtpd/mda.c index b3911359854..2649e452a1a 100644 --- a/usr.sbin/smtpd/mda.c +++ b/usr.sbin/smtpd/mda.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mda.c,v 1.130 2018/04/28 13:54:03 gilles Exp $ */ +/* $OpenBSD: mda.c,v 1.131 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -50,11 +50,11 @@ struct mda_envelope { uint64_t id; time_t creation; char *sender; - char *dest; char *rcpt; - enum action_type method; + char *dest; char *user; - char *buffer; + char *dispatcher; + char *mda_exec; }; #define USER_WAITINFO 0x01 @@ -115,13 +115,11 @@ mda_imsg(struct mproc *p, struct imsg *imsg) struct mda_user *u; struct mda_envelope *e; struct envelope evp; - struct userinfo *userinfo; struct deliver deliver; struct msg m; const void *data; const char *error, *parent_error; uint64_t reqid; - time_t now; size_t sz; char out[256], buf[LINE_MAX]; int n; @@ -239,37 +237,22 @@ mda_imsg(struct mproc *p, struct imsg *imsg) } n = 0; - /* - * prepend "From " separator ... for - * A_MDA and A_FILENAME backends only - */ - if (e->method == A_MDA || e->method == A_FILENAME) { - time(&now); - if (e->sender[0]) - n = io_printf(s->io, "From %s %s", - e->sender, ctime(&now)); - else - n = io_printf(s->io, - "From MAILER-DAEMON@%s %s", - env->sc_hostname, ctime(&now)); - } - if (n != -1) { - /* start queueing delivery headers */ - if (e->sender[0]) - /* - * XXX: remove existing Return-Path, - * if any - */ - n = io_printf(s->io, - "Return-Path: <%s>\n" - "Delivered-To: %s\n", - e->sender, - e->rcpt ? e->rcpt : e->dest); - else - n = io_printf(s->io, - "Delivered-To: %s\n", - e->rcpt ? e->rcpt : e->dest); - } + + /* start queueing delivery headers */ + if (e->sender[0]) + /* + * XXX: remove existing Return-Path, + * if any + */ + n = io_printf(s->io, + "Return-Path: <%s>\n" + "Delivered-To: %s\n", + e->sender, + e->rcpt ? e->rcpt : e->dest); + else + n = io_printf(s->io, + "Delivered-To: %s\n", + e->rcpt ? e->rcpt : e->dest); if (n == -1) { log_warn("warn: mda: " "fail to write delivery info"); @@ -281,113 +264,14 @@ mda_imsg(struct mproc *p, struct imsg *imsg) } /* request parent to fork a helper process */ - userinfo = &s->user->userinfo; memset(&deliver, 0, sizeof deliver); - switch (e->method) { - case A_MDA: - deliver.mode = A_MDA; - deliver.userinfo = *userinfo; - (void)strlcpy(deliver.user, userinfo->username, - sizeof(deliver.user)); - if (strlcpy(deliver.to, e->buffer, - sizeof(deliver.to)) - >= sizeof(deliver.to)) { - mda_queue_tempfail(e->id, - "mda command too long", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", - "mda command too long"); - mda_done(s); - return; - } - break; - - case A_MBOX: - /* - * MBOX is a special case as we MUST - * deliver as root, just override the uid. - */ - deliver.mode = A_MBOX; - deliver.userinfo = *userinfo; - deliver.userinfo.uid = 0; - (void)strlcpy(deliver.user, "root", - sizeof(deliver.user)); - (void)strlcpy(deliver.from, e->sender, - sizeof(deliver.from)); - (void)strlcpy(deliver.to, userinfo->username, - sizeof(deliver.to)); - break; - - case A_MAILDIR: - deliver.mode = A_MAILDIR; - deliver.userinfo = *userinfo; - (void)strlcpy(deliver.user, userinfo->username, - sizeof(deliver.user)); - (void)strlcpy(deliver.dest, e->dest, - sizeof(deliver.dest)); - if (strlcpy(deliver.to, e->buffer, - sizeof(deliver.to)) - >= sizeof(deliver.to)) { - log_warn("warn: mda: " - "deliver buffer too large"); - mda_queue_tempfail(e->id, - "Maildir path too long", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", - "Maildir path too long"); - mda_done(s); - return; - } - break; - - case A_FILENAME: - deliver.mode = A_FILENAME; - deliver.userinfo = *userinfo; - (void)strlcpy(deliver.user, userinfo->username, - sizeof deliver.user); - if (strlcpy(deliver.to, e->buffer, - sizeof(deliver.to)) - >= sizeof(deliver.to)) { - log_warn("warn: mda: " - "deliver buffer too large"); - mda_queue_tempfail(e->id, - "filename path too long", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", - "filename path too long"); - mda_done(s); - return; - } - break; - - case A_LMTP: - deliver.mode = A_LMTP; - deliver.userinfo = *userinfo; - (void)strlcpy(deliver.user, e->user, - sizeof(deliver.user)); - (void)strlcpy(deliver.from, e->sender, - sizeof(deliver.from)); - (void)strlcpy(deliver.dest, e->dest, - sizeof(deliver.dest)); - if (strlcpy(deliver.to, e->buffer, - sizeof(deliver.to)) - >= sizeof(deliver.to)) { - log_warn("warn: mda: " - "deliver buffer too large"); - mda_queue_tempfail(e->id, - "socket path too long", - ESC_OTHER_MAIL_SYSTEM_STATUS); - mda_log(e, "TempFail", - "socket path too long"); - mda_done(s); - return; - } - break; - - default: - errx(1, "mda: unknown delivery method: %d", - e->method); - } + text_to_mailaddr(&deliver.sender, s->evp->sender); + text_to_mailaddr(&deliver.rcpt, s->evp->rcpt); + text_to_mailaddr(&deliver.dest, s->evp->dest); + if (s->evp->mda_exec) + (void)strlcpy(deliver.mda_exec, s->evp->mda_exec, sizeof deliver.mda_exec); + (void)strlcpy(deliver.dispatcher, s->evp->dispatcher, sizeof deliver.dispatcher); + deliver.userinfo = s->user->userinfo; log_debug("debug: mda: querying mda fd " "for session %016"PRIx64 " evpid %016"PRIx64, @@ -752,34 +636,19 @@ static void mda_log(const struct mda_envelope *evp, const char *prefix, const char *status) { char rcpt[LINE_MAX]; - const char *method; rcpt[0] = '\0'; if (evp->rcpt) (void)snprintf(rcpt, sizeof rcpt, "rcpt=<%s> ", evp->rcpt); - if (evp->method == A_MAILDIR) - method = "maildir"; - else if (evp->method == A_MBOX) - method = "mbox"; - else if (evp->method == A_FILENAME) - method = "file"; - else if (evp->method == A_MDA) - method = "mda"; - else if (evp->method == A_LMTP) - method = "lmtp"; - else - method = "???"; - log_info("%016"PRIx64" mda event=delivery evpid=%016" PRIx64 " from=<%s> to=<%s> " - "%suser=%s method=%s delay=%s result=%s stat=%s", + "%suser=%s delay=%s result=%s stat=%s", (uint64_t)0, evp->id, evp->sender ? evp->sender : "", evp->dest, rcpt, evp->user, - method, duration_to_text(time(NULL) - evp->creation), prefix, status); @@ -826,41 +695,40 @@ mda_queue_loop(uint64_t evpid) static struct mda_user * mda_user(const struct envelope *evp) { + struct dispatcher *dsp; struct mda_user *u; void *i; i = NULL; + dsp = dict_xget(env->sc_dispatchers, evp->dispatcher); while (tree_iter(&users, &i, NULL, (void**)(&u))) { - if (!strcmp(evp->agent.mda.username, u->name) && - !strcmp(evp->agent.mda.usertable, u->usertable)) + if (!strcmp(evp->mda_user, u->name) && + !strcmp(dsp->u.local.table_userbase, u->usertable)) return (u); } u = xcalloc(1, sizeof *u, "mda_user"); u->id = generate_uid(); TAILQ_INIT(&u->envelopes); - (void)strlcpy(u->name, evp->agent.mda.username, sizeof(u->name)); - (void)strlcpy(u->usertable, evp->agent.mda.usertable, + (void)strlcpy(u->name, evp->mda_user, sizeof(u->name)); + (void)strlcpy(u->usertable, dsp->u.local.table_userbase, sizeof(u->usertable)); tree_xset(&users, u->id, u); m_create(p_lka, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1); m_add_id(p_lka, u->id); - m_add_string(p_lka, evp->agent.mda.usertable); - if (evp->agent.mda.delivery_user[0]) - m_add_string(p_lka, evp->agent.mda.delivery_user); - else - m_add_string(p_lka, evp->agent.mda.username); + m_add_string(p_lka, dsp->u.local.table_userbase); + m_add_string(p_lka, evp->mda_user); m_close(p_lka); u->flags |= USER_WAITINFO; stat_increment("mda.user", 1); - if (evp->agent.mda.delivery_user[0]) + if (dsp->u.local.user) log_debug("mda: new user %016" PRIx64 " for \"%s\" delivering as \"%s\"", - u->id, mda_user_to_text(u), evp->agent.mda.delivery_user); + u->id, mda_user_to_text(u), dsp->u.local.user); else log_debug("mda: new user %016" PRIx64 " for \"%s\"", u->id, mda_user_to_text(u)); @@ -913,14 +781,14 @@ mda_envelope(const struct envelope *evp) e->dest = xstrdup(buf, "mda_envelope:dest"); (void)snprintf(buf, sizeof buf, "%s@%s", evp->rcpt.user, evp->rcpt.domain); - if (strcmp(buf, e->dest)) - e->rcpt = xstrdup(buf, "mda_envelope:rcpt"); - e->method = evp->agent.mda.method; - e->buffer = xstrdup(evp->agent.mda.buffer, "mda_envelope:buffer"); - e->user = xstrdup(evp->agent.mda.username, "mda_envelope:user"); - + e->rcpt = xstrdup(buf, "mda_envelope:rcpt"); + e->user = evp->mda_user[0] ? + xstrdup(evp->mda_user, "mda_envelope:mda_user") : + xstrdup(evp->dest.user, "mda_envelope:user"); + e->dispatcher = xstrdup(evp->dispatcher, "mda_envelope:user"); + if (evp->mda_exec[0]) + e->mda_exec = xstrdup(evp->mda_exec, "mda_envelope:mda_exec"); stat_increment("mda.envelope", 1); - return (e); } @@ -931,7 +799,7 @@ mda_envelope_free(struct mda_envelope *e) free(e->dest); free(e->rcpt); free(e->user); - free(e->buffer); + free(e->mda_exec); free(e); stat_decrement("mda.envelope", 1); diff --git a/usr.sbin/smtpd/mda_variables.c b/usr.sbin/smtpd/mda_variables.c index aa30e047724..630e6096187 100644 --- a/usr.sbin/smtpd/mda_variables.c +++ b/usr.sbin/smtpd/mda_variables.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mda_variables.c,v 1.1 2017/05/26 21:30:00 gilles Exp $ */ +/* $OpenBSD: mda_variables.c,v 1.2 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2011-2017 Gilles Chehade <gilles@poolp.org> @@ -36,10 +36,10 @@ #define EXPAND_DEPTH 10 -size_t mda_expand_format(char *, size_t, const struct envelope *, +size_t mda_expand_format(char *, size_t, const struct deliver *, const struct userinfo *); static size_t mda_expand_token(char *, size_t, const char *, - const struct envelope *, const struct userinfo *); + const struct deliver *, const struct userinfo *); static int mod_lowercase(char *, size_t); static int mod_uppercase(char *, size_t); static int mod_strip(char *, size_t); @@ -58,7 +58,7 @@ static struct modifiers { static size_t mda_expand_token(char *dest, size_t len, const char *token, - const struct envelope *ep, const struct userinfo *ui) + const struct deliver *dlv, const struct userinfo *ui) { char rtoken[MAXTOKENLEN]; char tmp[EXPAND_BUFFER]; @@ -114,40 +114,40 @@ mda_expand_token(char *dest, size_t len, const char *token, /* token -> expanded token */ if (!strcasecmp("sender", rtoken)) { if (snprintf(tmp, sizeof tmp, "%s@%s", - ep->sender.user, ep->sender.domain) >= (int)sizeof tmp) + dlv->sender.user, dlv->sender.domain) >= (int)sizeof tmp) return 0; string = tmp; } - else if (!strcasecmp("dest", rtoken)) { + else if (!strcasecmp("rcpt", rtoken)) { if (snprintf(tmp, sizeof tmp, "%s@%s", - ep->dest.user, ep->dest.domain) >= (int)sizeof tmp) + dlv->rcpt.user, dlv->rcpt.domain) >= (int)sizeof tmp) return 0; string = tmp; } - else if (!strcasecmp("rcpt", rtoken)) { + else if (!strcasecmp("dest", rtoken)) { if (snprintf(tmp, sizeof tmp, "%s@%s", - ep->rcpt.user, ep->rcpt.domain) >= (int)sizeof tmp) + dlv->dest.user, dlv->dest.domain) >= (int)sizeof tmp) return 0; string = tmp; } else if (!strcasecmp("sender.user", rtoken)) - string = ep->sender.user; + string = dlv->sender.user; else if (!strcasecmp("sender.domain", rtoken)) - string = ep->sender.domain; + string = dlv->sender.domain; else if (!strcasecmp("user.username", rtoken)) string = ui->username; else if (!strcasecmp("user.directory", rtoken)) { string = ui->directory; replace = 0; } - else if (!strcasecmp("dest.user", rtoken)) - string = ep->dest.user; - else if (!strcasecmp("dest.domain", rtoken)) - string = ep->dest.domain; else if (!strcasecmp("rcpt.user", rtoken)) - string = ep->rcpt.user; + string = dlv->rcpt.user; else if (!strcasecmp("rcpt.domain", rtoken)) - string = ep->rcpt.domain; + string = dlv->rcpt.domain; + else if (!strcasecmp("dest.user", rtoken)) + string = dlv->dest.user; + else if (!strcasecmp("dest.domain", rtoken)) + string = dlv->dest.domain; else return 0; @@ -227,7 +227,7 @@ mda_expand_token(char *dest, size_t len, const char *token, size_t -mda_expand_format(char *buf, size_t len, const struct envelope *ep, +mda_expand_format(char *buf, size_t len, const struct deliver *dlv, const struct userinfo *ui) { char tmpbuf[EXPAND_BUFFER], *ptmp, *pbuf, *ebuf; @@ -288,7 +288,7 @@ mda_expand_format(char *buf, size_t len, const struct envelope *ep, return 0; *strchr(token, '}') = '\0'; - exptoklen = mda_expand_token(exptok, sizeof exptok, token, ep, + exptoklen = mda_expand_token(exptok, sizeof exptok, token, dlv, ui); if (exptoklen == 0) return 0; diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c index 2006eac813a..fc2dcd96108 100644 --- a/usr.sbin/smtpd/mta.c +++ b/usr.sbin/smtpd/mta.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta.c,v 1.206 2017/11/21 12:20:34 eric Exp $ */ +/* $OpenBSD: mta.c,v 1.207 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -57,7 +57,9 @@ #define RELAY_ONHOLD 0x01 #define RELAY_HOLDQ 0x02 -static void mta_handle_envelope(struct envelope *); +static void mta_handle_envelope(struct envelope *, const char *); +static void mta_query_smarthost(struct envelope *); +static void mta_on_smarthost(struct envelope *, const char *); static void mta_query_mx(struct mta_relay *); static void mta_query_secret(struct mta_relay *); static void mta_query_preference(struct mta_relay *); @@ -146,6 +148,7 @@ static struct mta_block_tree blocks; static struct tree wait_mx; static struct tree wait_preference; static struct tree wait_secret; +static struct tree wait_smarthost; static struct tree wait_source; static struct tree flush_evp; static struct event ev_flush_evp; @@ -173,7 +176,6 @@ void mta_hoststat_uncache(const char *, uint64_t); void mta_hoststat_reschedule(const char *); static void mta_hoststat_remove_entry(struct hoststat *); - void mta_imsg(struct mproc *p, struct imsg *imsg) { @@ -186,11 +188,12 @@ mta_imsg(struct mproc *p, struct imsg *imsg) struct mta_source *source; struct hoststat *hs; struct sockaddr_storage ss; - struct envelope evp; + struct envelope evp, *e; struct msg m; const char *secret; const char *hostname; const char *dom; + const char *smarthost; uint64_t reqid; time_t t; char buf[LINE_MAX]; @@ -203,7 +206,7 @@ mta_imsg(struct mproc *p, struct imsg *imsg) m_msg(&m, imsg); m_get_envelope(&m, &evp); m_end(&m); - mta_handle_envelope(&evp); + mta_handle_envelope(&evp, NULL); return; case IMSG_MTA_OPEN_MESSAGE: @@ -232,6 +235,19 @@ mta_imsg(struct mproc *p, struct imsg *imsg) mta_source((struct sockaddr *)&ss) : NULL); return; + case IMSG_MTA_LOOKUP_SMARTHOST: + m_msg(&m, imsg); + m_get_id(&m, &reqid); + m_get_int(&m, &status); + smarthost = NULL; + if (status == LKA_OK) + m_get_string(&m, &smarthost); + m_end(&m); + + e = tree_xpop(&wait_smarthost, reqid); + mta_on_smarthost(e, smarthost); + return; + case IMSG_MTA_LOOKUP_HELO: mta_session_imsg(p, imsg); return; @@ -470,6 +486,7 @@ mta_postprivdrop(void) SPLAY_INIT(&blocks); tree_init(&wait_secret); + tree_init(&wait_smarthost); tree_init(&wait_mx); tree_init(&wait_preference); tree_init(&wait_source); @@ -605,12 +622,59 @@ mta_route_next_task(struct mta_relay *relay, struct mta_route *route) } static void -mta_handle_envelope(struct envelope *evp) +mta_handle_envelope(struct envelope *evp, const char *smarthost) { struct mta_relay *relay; struct mta_task *task; struct mta_envelope *e; - char buf[LINE_MAX]; + struct dispatcher *dispatcher; + char buf[LINE_MAX], *backupmx; + + dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher); + if (dispatcher->u.remote.smarthost && smarthost == NULL) { + mta_query_smarthost(evp); + return; + } + + memset(&evp->agent.mta, 0, sizeof(evp->agent.mta)); + + /* dispatcher init */ + if (dispatcher->u.remote.pki) + strlcpy(evp->agent.mta.relay.pki_name, dispatcher->u.remote.pki, + sizeof(evp->agent.mta.relay.pki_name)); + if (dispatcher->u.remote.ca) + strlcpy(evp->agent.mta.relay.ca_name, dispatcher->u.remote.ca, + sizeof(evp->agent.mta.relay.ca_name)); + if (dispatcher->u.remote.auth) + strlcpy(evp->agent.mta.relay.authtable, dispatcher->u.remote.auth, + sizeof(evp->agent.mta.relay.authtable)); + if (dispatcher->u.remote.source) + strlcpy(evp->agent.mta.relay.sourcetable, dispatcher->u.remote.source, + sizeof(evp->agent.mta.relay.sourcetable)); + if (dispatcher->u.remote.helo_source) + strlcpy(evp->agent.mta.relay.helotable, dispatcher->u.remote.helo_source, + sizeof(evp->agent.mta.relay.helotable)); + if (dispatcher->u.remote.helo) + strlcpy(evp->agent.mta.relay.heloname, dispatcher->u.remote.helo, + sizeof(evp->agent.mta.relay.heloname)); + if (dispatcher->u.remote.backup) { + evp->agent.mta.relay.flags |= RELAY_BACKUP; + backupmx = dispatcher->u.remote.backupmx; + if (backupmx == NULL) + backupmx = evp->smtpname; + strlcpy(evp->agent.mta.relay.hostname, backupmx, + sizeof(evp->agent.mta.relay.hostname)); + } + + if (smarthost) { + if (text_to_relayhost(&evp->agent.mta.relay, smarthost) == 0) { + m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); + m_add_evpid(p_queue, evp->id); + m_add_string(p_queue, "Cannot parse smarthost"); + m_add_int(p_queue, ESC_OTHER_STATUS); + m_close(p_queue); + } + } relay = mta_relay(evp); /* ignore if we don't know the limits yet */ @@ -661,6 +725,7 @@ mta_handle_envelope(struct envelope *evp) e = xcalloc(1, sizeof *e, "mta_envelope"); e->id = evp->id; e->creation = evp->creation; + e->smtpname = xstrdup(evp->smtpname, "mta_envelope:smtpname"); (void)snprintf(buf, sizeof buf, "%s@%s", evp->dest.user, evp->dest.domain); e->dest = xstrdup(buf, "mta_envelope:dest"); @@ -730,6 +795,7 @@ mta_delivery_flush_event(int fd, short event, void *arg) log_debug("debug: mta: flush for %016"PRIx64" (-> %s)", e->id, e->dest); + free(e->smtpname); free(e->dest); free(e->rcpt); free(e->dsn_orcpt); @@ -841,6 +907,30 @@ mta_query_secret(struct mta_relay *relay) } static void +mta_query_smarthost(struct envelope *evp0) +{ + struct dispatcher *dispatcher; + struct envelope *evp; + + evp = malloc(sizeof(*evp)); + memmove(evp, evp0, sizeof(*evp)); + + dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher); + + log_debug("debug: mta: querying smarthost for %s:%s...", + evp->dispatcher, dispatcher->u.remote.smarthost); + + tree_xset(&wait_smarthost, evp->id, evp); + + m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1); + m_add_id(p_lka, evp->id); + m_add_string(p_lka, dispatcher->u.remote.smarthost); + m_close(p_lka); + + log_debug("debug: mta: querying smarthost"); +} + +static void mta_query_preference(struct mta_relay *relay) { if (relay->status & RELAY_WAIT_PREFERENCE) @@ -953,6 +1043,23 @@ mta_on_secret(struct mta_relay *relay, const char *secret) } static void +mta_on_smarthost(struct envelope *evp, const char *smarthost) +{ + if (smarthost == NULL) { + log_warnx("warn: Failed to retrieve smarthost " + "for envelope %"PRIx64, evp->id); + m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); + m_add_evpid(p_queue, evp->id); + m_add_string(p_queue, "Cannot retrieve smarthost"); + m_add_int(p_queue, ESC_OTHER_STATUS); + return; + } + + mta_handle_envelope(evp, smarthost); + free(evp); +} + +static void mta_on_preference(struct mta_relay *relay, int preference) { log_debug("debug: mta: ... got preference for %s: %d", diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y index 2ebfe459fa9..2275e6a88df 100644 --- a/usr.sbin/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.200 2018/04/26 14:12:19 krw Exp $ */ +/* $OpenBSD: parse.y,v 1.201 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -93,15 +93,17 @@ char *symget(const char *); struct smtpd *conf = NULL; static int errors = 0; -static uint64_t ruleid = 0; struct filter_conf *filter = NULL; struct table *table = NULL; -struct rule *rule = NULL; struct mta_limits *limits; static struct pki *pki; static struct ca *sca; +struct dispatcher *dispatcher; +struct rule *rule; + + enum listen_options { LO_FAMILY = 0x000001, LO_PORT = 0x000002, @@ -168,28 +170,46 @@ typedef struct { %} -%token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED LISTEN ON ANY PORT EXPIRE -%token TABLE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6 NODSN SESSION -%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX RCPTTO HOSTNAME HOSTNAMES -%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI SCHEDULER -%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY CA DHE -%token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER SENDERS MASK_SOURCE VERIFY FORWARDONLY RECIPIENT -%token CIPHERS RECEIVEDAUTH MASQUERADE SOCKET SUBADDRESSING_DELIM AUTHENTICATED +%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL +%token BACKUP BOUNCE +%token CA CERT CIPHERS COMPRESSION +%token DHE DOMAIN +%token ENCRYPTION ERROR EXPAND_ONLY +%token FILTER FOR FORWARD_ONLY FROM +%token HELO HELO_SRC HOST HOSTNAME HOSTNAMES +%token INCLUDE INET4 INET6 +%token KEY +%token LIMIT LISTEN LOCAL +%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX +%token NODSN +%token ON +%token PKI PORT +%token QUEUE +%token RCPT_TO RECIPIENT RECEIVEDAUTH RELAY REJECT +%token SCHEDULER SENDER SENDERS SET SMTP SMTPS SOCKET SRC SUB_ADDR_DELIM +%token TABLE TAG TAGGED TLS TLS_REQUIRE TO TTL +%token USER USERBASE +%token VERIFY VIRTUAL +%token WARN_INTERVAL + %token <v.string> STRING %token <v.number> NUMBER %type <v.table> table %type <v.number> size negation -%type <v.table> tables tablenew tableref alias virtual userbase -%type <v.string> tagged +%type <v.table> tables tablenew tableref %% grammar : /* empty */ | grammar '\n' | grammar include '\n' | grammar varset '\n' - | grammar main '\n' + | grammar limit '\n' + | grammar listen '\n' + | grammar set '\n' + | grammar pkica '\n' | grammar table '\n' - | grammar rule '\n' + | grammar dispatcher '\n' + | grammar match '\n' | grammar error '\n' { file->errors++; } ; @@ -238,6 +258,532 @@ optnl : '\n' optnl nl : '\n' optnl ; + +dispatcher_local_option: +USER STRING { + if (dispatcher->u.local.requires_root) { + yyerror("user may not be specified for this dispatcher"); + YYERROR; + } + + if (dispatcher->u.local.forward_only) { + yyerror("user may not be specified for forward-only"); + YYERROR; + } + + if (dispatcher->u.local.expand_only) { + yyerror("user may not be specified for expand-only"); + YYERROR; + } + + if (dispatcher->u.local.user) { + yyerror("user already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.local.user = $2; +} +| ALIAS tables { + struct table *t = $2; + + if (dispatcher->u.local.table_alias) { + yyerror("alias mapping already specified for this dispatcher"); + YYERROR; + } + + if (dispatcher->u.local.table_virtual) { + yyerror("virtual mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { + yyerror("table \"%s\" may not be used for alias lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.local.table_alias = strdup(t->t_name); +} +| VIRTUAL tables { + struct table *t = $2; + + if (dispatcher->u.local.table_virtual) { + yyerror("virtual mapping already specified for this dispatcher"); + YYERROR; + } + + if (dispatcher->u.local.table_alias) { + yyerror("alias mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { + yyerror("table \"%s\" may not be used for virtual lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.local.table_virtual = strdup(t->t_name); +} +| USERBASE tables { + struct table *t = $2; + + if (dispatcher->u.local.table_userbase) { + yyerror("userbase mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) { + yyerror("table \"%s\" may not be used for userbase lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.local.table_userbase = strdup(t->t_name); +} +; + +dispatcher_local_options: +dispatcher_local_option dispatcher_local_options +| /* empty */ +; + +dispatcher_local: +MBOX { + dispatcher->u.local.requires_root = 1; + dispatcher->u.local.user = xstrdup("root", "dispatcher_mda"); + asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{sender} %%{user.username}"); +} dispatcher_local_options +| MAILDIR { + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.maildir -p %%{user.directory}/Maildir -r %%{dest.user}"); +} dispatcher_local_options +| MAILDIR STRING { + if (strncmp($2, "~/", 2) == 0) + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.maildir -p %%{user.directory}/%s -r %%{dest.user}}", $2+2); + else + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.maildir -p %s -r %%{dest.user}}", $2); +} dispatcher_local_options +| MDA STRING { + asprintf(&dispatcher->u.local.command, + "/usr/libexec/mail.mda \"%s\"", $2); +} dispatcher_local_options +| FORWARD_ONLY { + dispatcher->u.local.forward_only = 1; +} dispatcher_local_options +| EXPAND_ONLY { + dispatcher->u.local.expand_only = 1; +} dispatcher_local_options + +; + +dispatcher_remote_option: +HELO STRING { + if (dispatcher->u.remote.helo) { + yyerror("helo already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.helo = $2; +} +| HELO_SRC tables { + struct table *t = $2; + + if (dispatcher->u.remote.helo_source) { + yyerror("helo-source mapping already specified for this dispatcher"); + YYERROR; + } + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { + yyerror("table \"%s\" may not be used for helo-source lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.helo_source = strdup(t->t_name); +} +| PKI STRING { + if (dispatcher->u.remote.pki) { + yyerror("pki already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.pki = $2; +} +| CA STRING { + if (dispatcher->u.remote.ca) { + yyerror("ca already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.ca = $2; +} +| SRC tables { + struct table *t = $2; + + if (dispatcher->u.remote.source) { + yyerror("source mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) { + yyerror("table \"%s\" may not be used for source lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.source = strdup(t->t_name); +} +| MAIL_FROM STRING { + if (dispatcher->u.remote.mail_from) { + yyerror("mail-from already specified for this dispatcher"); + YYERROR; + } + + dispatcher->u.remote.mail_from = $2; +} +| BACKUP MX STRING { + if (dispatcher->u.remote.backup) { + yyerror("backup already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.smarthost) { + yyerror("backup and host are mutually exclusive"); + YYERROR; + } + + dispatcher->u.remote.backup = 1; + dispatcher->u.remote.backupmx = $3; +} +| BACKUP { + if (dispatcher->u.remote.backup) { + yyerror("backup already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.smarthost) { + yyerror("backup and host are mutually exclusive"); + YYERROR; + } + + dispatcher->u.remote.backup = 1; +} +| HOST tables { + struct table *t = $2; + + if (dispatcher->u.remote.smarthost) { + yyerror("host mapping already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.backup) { + yyerror("backup and host are mutually exclusive"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_RELAYHOST)) { + yyerror("table \"%s\" may not be used for host lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.smarthost = strdup(t->t_name); +} +| AUTH tables { + struct table *t = $2; + + if (dispatcher->u.remote.smarthost == NULL) { + yyerror("auth may not be specified without host on a dispatcher"); + YYERROR; + } + + if (dispatcher->u.remote.auth) { + yyerror("auth mapping already specified for this dispatcher"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) { + yyerror("table \"%s\" may not be used for auth lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.auth = strdup(t->t_name); +} +; + +dispatcher_remote_options: +dispatcher_remote_option dispatcher_remote_options +| /* empty */ +; + +dispatcher_remote : +RELAY dispatcher_remote_options +; + +dispatcher_type: +dispatcher_local { + dispatcher->type = DISPATCHER_LOCAL; +} +| dispatcher_remote { + dispatcher->type = DISPATCHER_REMOTE; +} +; + +dispatcher_option: +TTL STRING { + if (dispatcher->ttl) { + yyerror("ttl already specified for this dispatcher"); + YYERROR; + } + + dispatcher->ttl = delaytonum($2); + if (dispatcher->ttl == -1) { + yyerror("ttl delay \"%s\" is invalid", $2); + free($2); + YYERROR; + } + free($2); +} +; + +dispatcher_options: +dispatcher_option dispatcher_options +| /* empty */ +; + +dispatcher: +ACTION STRING { + if (dict_get(conf->sc_dispatchers, $2)) { + yyerror("dispatcher already declared with that name: %s", $2); + YYERROR; + } + dispatcher = xcalloc(1, sizeof *dispatcher, "dispatcher"); +} dispatcher_type dispatcher_options { + if (dispatcher->type == DISPATCHER_LOCAL) + if (dispatcher->u.local.table_userbase == NULL) + dispatcher->u.local.table_userbase = "<getpwnam>"; + dict_set(conf->sc_dispatchers, $2, dispatcher); + dispatcher = NULL; +} +; + +match_option: +negation TAG tables { + struct table *t = $3; + + if (rule->flag_tag) { + yyerror("tag already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING)) { + yyerror("table \"%s\" may not be used for tag lookups", + t->t_name); + YYERROR; + } + + rule->flag_tag = $1 ? -1 : 1; + rule->table_tag = strdup(t->t_name); +} +| negation HELO tables { + struct table *t = $3; + + if (rule->flag_smtp_helo) { + yyerror("mail-helo already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { + yyerror("table \"%s\" may not be used for helo lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_helo = $1 ? -1 : 1; + rule->table_smtp_helo = strdup(t->t_name); +} +| negation TLS { + if (rule->flag_smtp_starttls) { + yyerror("tls already specified for this rule"); + YYERROR; + } + rule->flag_smtp_starttls = $1 ? -1 : 1; +} +| negation AUTH { + if (rule->flag_smtp_auth) { + yyerror("auth already specified for this rule"); + YYERROR; + } + rule->flag_smtp_auth = $1 ? -1 : 1; +} +| negation AUTH tables { + struct table *t = $3; + + if (rule->flag_smtp_auth) { + yyerror("auth already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_CREDENTIALS)) { + yyerror("table \"%s\" may not be used for auth lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_auth = $1 ? -1 : 1; + rule->table_smtp_auth = strdup(t->t_name); +} +| negation MAIL_FROM tables { + struct table *t = $3; + + if (rule->flag_smtp_mail_from) { + yyerror("mail-from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { + yyerror("table \"%s\" may not be used for mail-from lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_mail_from = $1 ? -1 : 1; + rule->table_smtp_mail_from = strdup(t->t_name); +} +| negation RCPT_TO tables { + struct table *t = $3; + + if (rule->flag_smtp_rcpt_to) { + yyerror("rcpt-to already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { + yyerror("table \"%s\" may not be used for rcpt-to lookups", + t->t_name); + YYERROR; + } + + rule->flag_smtp_rcpt_to = $1 ? -1 : 1; + rule->table_smtp_rcpt_to = strdup(t->t_name); +} + +| negation FROM SOCKET { + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + rule->flag_from = $1 ? -1 : 1; + rule->flag_from_socket = 1; +} +| negation FROM LOCAL { + struct table *t = table_find("<localhost>", NULL); + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + rule->flag_from = $1 ? -1 : 1; + rule->table_from = strdup(t->t_name); +} +| negation FROM ANY { + struct table *t = table_find("<anyhost>", NULL); + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + rule->flag_from = $1 ? -1 : 1; + rule->table_from = strdup(t->t_name); +} +| negation FROM SRC tables { + struct table *t = $4; + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { + yyerror("table \"%s\" may not be used for from lookups", + t->t_name); + YYERROR; + } + + rule->flag_from = $1 ? -1 : 1; + rule->table_from = strdup(t->t_name); +} + +| negation FOR LOCAL { + struct table *t = table_find("<localnames>", NULL); + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + rule->flag_for = $1 ? -1 : 1; + rule->table_for = strdup(t->t_name); +} +| negation FOR ANY { + struct table *t = table_find("<anydestination>", NULL); + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + rule->flag_for = $1 ? -1 : 1; + rule->table_for = strdup(t->t_name); +} +| negation FOR DOMAIN tables { + struct table *t = $4; + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { + yyerror("table \"%s\" may not be used for 'for' lookups", + t->t_name); + YYERROR; + } + + rule->flag_for = $1 ? -1 : 1; + rule->table_for = strdup(t->t_name); +} +; + +match_options: +match_option match_options +| /* empty */ +; + +match_dispatcher: +STRING { + if (dict_get(conf->sc_dispatchers, $1) == NULL) { + yyerror("no such dispatcher: %s", $1); + YYERROR; + } + rule->dispatcher = $1; +} +; + +action: +REJECT { + rule->reject = 1; +} +| ACTION match_dispatcher +; + +match: +MATCH { + rule = xcalloc(1, sizeof *rule, "rule"); +} match_options action { + TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); + rule = NULL; +} +; + size : NUMBER { if ($1 < 0) { yyerror("invalid size: %" PRId64, $1); @@ -258,24 +804,6 @@ size : NUMBER { } ; -tagged : TAGGED negation STRING { - if (strlcpy(rule->r_tag, $3, sizeof rule->r_tag) - >= sizeof rule->r_tag) { - yyerror("tag name too long: %s", $3); - free($3); - YYERROR; - } - free($3); - rule->r_nottag = $2; - } - ; - -authenticated : negation AUTHENTICATED { - rule->r_wantauth = 1; - rule->r_negwantauth = $1; - } - ; - bouncedelay : STRING { time_t d; int i; @@ -294,10 +822,10 @@ bouncedelay : STRING { break; } } + ; bouncedelays : bouncedelays ',' bouncedelay | bouncedelay - | /* EMPTY */ ; opt_limit_mda : STRING NUMBER { @@ -325,11 +853,11 @@ opt_limit_mda : STRING NUMBER { } ; -limits_session : opt_limit_session limits_session +limits_smtp : opt_limit_smtp limits_smtp | /* empty */ ; -opt_limit_session : STRING NUMBER { +opt_limit_smtp : STRING NUMBER { if (!strcmp($1, "max-rcpt")) { conf->sc_session_max_rcpt = $2; } @@ -395,7 +923,7 @@ limits_scheduler: opt_limit_scheduler limits_scheduler | /* empty */ ; -opt_ca : CERTIFICATE STRING { +opt_ca : CERT STRING { sca->ca_cert_file = $2; } ; @@ -403,7 +931,7 @@ opt_ca : CERTIFICATE STRING { ca : opt_ca ; -opt_pki : CERTIFICATE STRING { +opt_pki : CERT STRING { pki->pki_cert_file = $2; } | KEY STRING { @@ -434,7 +962,7 @@ opt_sock_listen : FILTER STRING { YYERROR; } } - | MASK_SOURCE { + | MASK_SRC { if (config_lo_mask_source(&listen_opts)) { YYERROR; } @@ -622,7 +1150,7 @@ opt_if_listen : INET4 { } listen_opts.hostnametable = t; } - | MASK_SOURCE { + | MASK_SRC { if (config_lo_mask_source(&listen_opts)) { YYERROR; } @@ -704,168 +1232,19 @@ if_listen : opt_if_listen if_listen | /* empty */ ; -opt_relay_common: AS STRING { - struct mailaddr maddr, *maddrp; - - if (!text_to_mailaddr(&maddr, $2)) { - yyerror("invalid parameter to AS: %s", $2); - free($2); - YYERROR; - } - free($2); - - if (maddr.user[0] == '\0' && maddr.domain[0] == '\0') { - yyerror("invalid empty parameter to AS"); - YYERROR; - } - else if (maddr.domain[0] == '\0') { - if (strlcpy(maddr.domain, conf->sc_hostname, - sizeof (maddr.domain)) - >= sizeof (maddr.domain)) { - yyerror("hostname too long for AS parameter: %s", - conf->sc_hostname); - YYERROR; - } - } - rule->r_as = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS"); - } - | SOURCE tables { - struct table *t = $2; - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) { - yyerror("invalid use of table \"%s\" as " - "SOURCE parameter", t->t_name); - YYERROR; - } - (void)strlcpy(rule->r_value.relayhost.sourcetable, t->t_name, - sizeof rule->r_value.relayhost.sourcetable); - } - | HOSTNAME STRING { - (void)strlcpy(rule->r_value.relayhost.heloname, $2, - sizeof rule->r_value.relayhost.heloname); - free($2); - } - | HOSTNAMES tables { - struct table *t = $2; - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) { - yyerror("invalid use of table \"%s\" as " - "HOSTNAMES parameter", t->t_name); - YYERROR; - } - (void)strlcpy(rule->r_value.relayhost.helotable, t->t_name, - sizeof rule->r_value.relayhost.helotable); - } - | PKI STRING { - if (!lowercase(rule->r_value.relayhost.pki_name, $2, - sizeof(rule->r_value.relayhost.pki_name))) { - yyerror("pki name too long: %s", $2); - free($2); - YYERROR; - } - if (dict_get(conf->sc_pki_dict, - rule->r_value.relayhost.pki_name) == NULL) { - log_warnx("pki name not found: %s", $2); - free($2); - YYERROR; - } - free($2); - } - | CA STRING { - if (!lowercase(rule->r_value.relayhost.ca_name, $2, - sizeof(rule->r_value.relayhost.ca_name))) { - yyerror("ca name too long: %s", $2); - free($2); - YYERROR; - } - if (dict_get(conf->sc_ca_dict, - rule->r_value.relayhost.ca_name) == NULL) { - log_warnx("ca name not found: %s", $2); - free($2); - YYERROR; - } - free($2); - } - ; - -opt_relay : BACKUP STRING { - rule->r_value.relayhost.flags |= F_BACKUP; - if (strlcpy(rule->r_value.relayhost.hostname, $2, - sizeof (rule->r_value.relayhost.hostname)) - >= sizeof (rule->r_value.relayhost.hostname)) { - log_warnx("hostname too long: %s", $2); - free($2); - YYERROR; - } - free($2); - } - | BACKUP { - rule->r_value.relayhost.flags |= F_BACKUP; - (void)strlcpy(rule->r_value.relayhost.hostname, - conf->sc_hostname, - sizeof (rule->r_value.relayhost.hostname)); - } - | TLS { - rule->r_value.relayhost.flags |= F_STARTTLS; - } - | TLS VERIFY { - rule->r_value.relayhost.flags |= F_STARTTLS|F_TLS_VERIFY; - } - ; - -relay : opt_relay_common relay - | opt_relay relay - | /* empty */ - ; - -opt_relay_via : AUTH tables { - struct table *t = $2; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) { - yyerror("invalid use of table \"%s\" as AUTH parameter", - t->t_name); - YYERROR; - } - (void)strlcpy(rule->r_value.relayhost.authtable, t->t_name, - sizeof(rule->r_value.relayhost.authtable)); - } - | VERIFY { - if (!(rule->r_value.relayhost.flags & F_SSL)) { - yyerror("cannot \"verify\" with insecure protocol"); - YYERROR; - } - rule->r_value.relayhost.flags |= F_TLS_VERIFY; - } - ; - -relay_via : opt_relay_common relay_via - | opt_relay_via relay_via - | /* empty */ - ; - -main : BOUNCEWARN { +set : SET BOUNCE WARN_INTERVAL { memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn); } bouncedelays - | SUBADDRESSING_DELIM STRING { - if (strlen($2) != 1) { - yyerror("subaddressing-delimiter must be one character"); - free($2); - YYERROR; - } - - if (isspace((int)*$2) || !isprint((int)*$2) || *$2== '@') { - yyerror("subaddressing-delimiter uses invalid character"); - free($2); - YYERROR; - } - - conf->sc_subaddressing_delim = $2; + | SET MTA MAX_DEFERRED NUMBER { + conf->sc_mta_max_deferred = $4; } - | QUEUE COMPRESSION { + | SET QUEUE COMPRESSION { conf->sc_queue_flags |= QUEUE_COMPRESSION; } - | QUEUE ENCRYPTION { + | SET QUEUE ENCRYPTION { conf->sc_queue_flags |= QUEUE_ENCRYPTION; } - | QUEUE ENCRYPTION KEY STRING { + | SET QUEUE ENCRYPTION STRING { if (strcasecmp($4, "stdin") == 0 || strcasecmp($4, "-") == 0) { conf->sc_queue_key = "stdin"; free($4); @@ -874,22 +1253,37 @@ main : BOUNCEWARN { conf->sc_queue_key = $4; conf->sc_queue_flags |= QUEUE_ENCRYPTION; } - | EXPIRE STRING { - conf->sc_qexpire = delaytonum($2); - if (conf->sc_qexpire == -1) { - yyerror("invalid expire delay: %s", $2); - free($2); + | SET QUEUE TTL STRING { + conf->sc_ttl = delaytonum($4); + if (conf->sc_ttl == -1) { + yyerror("invalid ttl delay: %s", $4); + free($4); YYERROR; } - free($2); + free($4); + } + | SET SMTP CIPHERS STRING { + conf->sc_tls_ciphers = $4; } - | MAXMESSAGESIZE size { - conf->sc_maxsize = $2; + | SET SMTP MAX_MESSAGE_SIZE size { + conf->sc_maxsize = $4; } - | MAXMTADEFERRED NUMBER { - conf->sc_mta_max_deferred = $2; + | SET SMTP SUB_ADDR_DELIM STRING { + if (strlen($4) != 1) { + yyerror("subaddressing-delimiter must be one character"); + free($4); + YYERROR; + } + if (isspace((int)*$4) || !isprint((int)*$4) || *$4== '@') { + yyerror("sub-addr-delim uses invalid character"); + free($4); + YYERROR; + } + conf->sc_subaddressing_delim = $4; } - | LIMIT SESSION limits_session + ; + +limit : LIMIT SMTP limits_smtp | LIMIT MDA limits_mda | LIMIT MTA FOR DOMAIN STRING { struct mta_limits *d; @@ -907,12 +1301,9 @@ main : BOUNCEWARN { limits = dict_get(conf->sc_limits_dict, "default"); } limits_mta | LIMIT SCHEDULER limits_scheduler - | LISTEN { - memset(&listen_opts, 0, sizeof listen_opts); - listen_opts.family = AF_UNSPEC; - listen_opts.flags |= F_EXT_DSN; - } ON listener_type - | PKI STRING { + ; + +pkica : PKI STRING { char buf[HOST_NAME_MAX+1]; /* if not catchall, check that it is a valid domain */ @@ -952,9 +1343,13 @@ main : BOUNCEWARN { dict_set(conf->sc_ca_dict, sca->ca_name, sca); } } ca - | CIPHERS STRING { - conf->sc_tls_ciphers = $2; - } + ; + +listen : LISTEN { + memset(&listen_opts, 0, sizeof listen_opts); + listen_opts.family = AF_UNSPEC; + listen_opts.flags |= F_EXT_DSN; + } ON listener_type ; table : TABLE STRING STRING { @@ -1064,363 +1459,10 @@ tables : tablenew { $$ = $1; } | tableref { $$ = $1; } ; -alias : ALIAS tables { - struct table *t = $2; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { - yyerror("invalid use of table \"%s\" as ALIAS parameter", - t->t_name); - YYERROR; - } - - $$ = t; - } - ; - -virtual : VIRTUAL tables { - struct table *t = $2; - - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) { - yyerror("invalid use of table \"%s\" as VIRTUAL parameter", - t->t_name); - YYERROR; - } - $$ = t; - } - ; - -usermapping : alias { - if (rule->r_mapping) { - yyerror("alias specified multiple times"); - YYERROR; - } - rule->r_desttype = DEST_DOM; - rule->r_mapping = $1; - } - | virtual { - if (rule->r_mapping) { - yyerror("virtual specified multiple times"); - YYERROR; - } - rule->r_desttype = DEST_VDOM; - rule->r_mapping = $1; - } - ; - -userbase : USERBASE tables { - struct table *t = $2; - - if (rule->r_userbase) { - yyerror("userbase specified multiple times"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) { - yyerror("invalid use of table \"%s\" as USERBASE parameter", - t->t_name); - YYERROR; - } - rule->r_userbase = t; - } - ; - -deliver_as : AS STRING { - if (strlcpy(rule->r_delivery_user, $2, - sizeof(rule->r_delivery_user)) - >= sizeof(rule->r_delivery_user)) - fatal("username too long"); - free($2); - } - | /* empty */ {} - ; - -deliver_action : DELIVER TO MAILDIR { - rule->r_action = A_MAILDIR; - if (strlcpy(rule->r_value.buffer, "~/Maildir", - sizeof(rule->r_value.buffer)) >= - sizeof(rule->r_value.buffer)) - fatal("pathname too long"); - } - | DELIVER TO MAILDIR STRING { - rule->r_action = A_MAILDIR; - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) >= - sizeof(rule->r_value.buffer)) - fatal("pathname too long"); - free($4); - } - | DELIVER TO MBOX { - rule->r_action = A_MBOX; - if (strlcpy(rule->r_value.buffer, _PATH_MAILDIR "/%u", - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("pathname too long"); - } - | DELIVER TO LMTP STRING deliver_as { - rule->r_action = A_LMTP; - if (strchr($4, ':') || $4[0] == '/') { - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("lmtp destination too long"); - } else - fatal("invalid lmtp destination"); - free($4); - } - | DELIVER TO LMTP STRING RCPTTO deliver_as { - rule->r_action = A_LMTP; - if (strchr($4, ':') || $4[0] == '/') { - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("lmtp destination too long"); - if (strlcat(rule->r_value.buffer, " rcpt-to", - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("lmtp recipient too long"); - } else - fatal("invalid lmtp destination"); - free($4); - } - | DELIVER TO MDA STRING deliver_as { - rule->r_action = A_MDA; - if (strlcpy(rule->r_value.buffer, $4, - sizeof(rule->r_value.buffer)) - >= sizeof(rule->r_value.buffer)) - fatal("command too long"); - free($4); - } - ; - -relay_action : RELAY relay { - rule->r_action = A_RELAY; - } - | RELAY VIA STRING { - rule->r_action = A_RELAYVIA; - if (!text_to_relayhost(&rule->r_value.relayhost, $3)) { - yyerror("error: invalid url: %s", $3); - free($3); - YYERROR; - } - free($3); - } relay_via { - /* no worries, F_AUTH cant be set without SSL */ - if (rule->r_value.relayhost.flags & F_AUTH) { - if (rule->r_value.relayhost.authtable[0] == '\0') { - yyerror("error: auth without auth table"); - YYERROR; - } - } - } - ; - negation : '!' { $$ = 1; } | /* empty */ { $$ = 0; } ; -from : FROM negation SOURCE tables { - struct table *t = $4; - - if (rule->r_sources) { - yyerror("from specified multiple times"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) { - yyerror("invalid use of table \"%s\" as FROM parameter", - t->t_name); - YYERROR; - } - rule->r_notsources = $2; - rule->r_sources = t; - } - | FROM negation ANY { - if (rule->r_sources) { - yyerror("from specified multiple times"); - YYERROR; - } - rule->r_sources = table_find("<anyhost>", NULL); - rule->r_notsources = $2; - } - | FROM negation LOCAL { - if (rule->r_sources) { - yyerror("from specified multiple times"); - YYERROR; - } - rule->r_sources = table_find("<localhost>", NULL); - rule->r_notsources = $2; - } - ; - -for : FOR negation DOMAIN tables { - struct table *t = $4; - - if (rule->r_destination) { - yyerror("for specified multiple times"); - YYERROR; - } - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) { - yyerror("invalid use of table \"%s\" as DOMAIN parameter", - t->t_name); - YYERROR; - } - rule->r_notdestination = $2; - rule->r_destination = t; - } - | FOR negation ANY { - if (rule->r_destination) { - yyerror("for specified multiple times"); - YYERROR; - } - rule->r_notdestination = $2; - rule->r_destination = table_find("<anydestination>", NULL); - } - | FOR negation LOCAL { - if (rule->r_destination) { - yyerror("for specified multiple times"); - YYERROR; - } - rule->r_notdestination = $2; - rule->r_destination = table_find("<localnames>", NULL); - } - ; - -sender : SENDER negation tables { - struct table *t = $3; - - if (rule->r_senders) { - yyerror("sender specified multiple times"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("invalid use of table \"%s\" as SENDER parameter", - t->t_name); - YYERROR; - } - rule->r_notsenders = $2; - rule->r_senders = t; - } - ; - -recipient : RECIPIENT negation tables { - struct table *t = $3; - - if (rule->r_recipients) { - yyerror("recipient specified multiple times"); - YYERROR; - } - - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { - yyerror("invalid use of table \"%s\" as RECIPIENT parameter", - t->t_name); - YYERROR; - } - rule->r_notrecipients = $2; - rule->r_recipients = t; - } - ; - -forwardonly : FORWARDONLY { - if (rule->r_forwardonly) { - yyerror("forward-only specified multiple times"); - YYERROR; - } - rule->r_forwardonly = 1; - } - ; - -expire : EXPIRE STRING { - if (rule->r_qexpire != -1) { - yyerror("expire specified multiple times"); - YYERROR; - } - rule->r_qexpire = delaytonum($2); - if (rule->r_qexpire == -1) { - yyerror("invalid expire delay: %s", $2); - free($2); - YYERROR; - } - free($2); - } - ; - -opt_decision : sender - | recipient - | from - | for - | tagged - | authenticated - ; -decision : opt_decision decision - | - ; - -opt_lookup : userbase - | usermapping - ; -lookup : opt_lookup lookup - | - ; - -action : deliver_action - | relay_action - | - ; - -opt_accept : expire - | forwardonly - ; - -accept_params : opt_accept accept_params - | - ; - -rule : ACCEPT { - rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT"); - rule->r_id = ++ruleid; - rule->r_action = A_NONE; - rule->r_decision = R_ACCEPT; - rule->r_desttype = DEST_DOM; - rule->r_qexpire = -1; - } decision lookup action accept_params { - if (!rule->r_sources) - rule->r_sources = table_find("<localhost>", NULL); - if (!rule->r_destination) - rule->r_destination = table_find("<localnames>", NULL); - if (!rule->r_userbase) - rule->r_userbase = table_find("<getpwnam>", NULL); - if (rule->r_qexpire == -1) - rule->r_qexpire = conf->sc_qexpire; - if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) { - if (rule->r_userbase != table_find("<getpwnam>", NULL)) { - yyerror("userbase may not be used with a relay rule"); - YYERROR; - } - if (rule->r_mapping) { - yyerror("aliases/virtual may not be used with a relay rule"); - YYERROR; - } - } - if (rule->r_forwardonly && rule->r_action != A_NONE) { - yyerror("forward-only may not be used with a default action"); - YYERROR; - } - TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); - rule = NULL; - } - | REJECT { - rule = xcalloc(1, sizeof(*rule), "parse rule: REJECT"); - rule->r_id = ++ruleid; - rule->r_decision = R_REJECT; - rule->r_desttype = DEST_DOM; - } decision { - if (!rule->r_sources) - rule->r_sources = table_find("<localhost>", NULL); - if (!rule->r_destination) - rule->r_destination = table_find("<localnames>", NULL); - TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry); - rule = NULL; - } - ; %% struct keywords { @@ -1455,28 +1497,28 @@ lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { - { "accept", ACCEPT }, + { "action", ACTION }, { "alias", ALIAS }, { "any", ANY }, - { "as", AS }, { "auth", AUTH }, { "auth-optional", AUTH_OPTIONAL }, - { "authenticated", AUTHENTICATED }, { "backup", BACKUP }, - { "bounce-warn", BOUNCEWARN }, + { "bounce", BOUNCE }, { "ca", CA }, - { "certificate", CERTIFICATE }, + { "cert", CERT }, { "ciphers", CIPHERS }, { "compression", COMPRESSION }, - { "deliver", DELIVER }, { "dhe", DHE }, { "domain", DOMAIN }, { "encryption", ENCRYPTION }, - { "expire", EXPIRE }, + { "expand-only", EXPAND_ONLY }, { "filter", FILTER }, { "for", FOR }, - { "forward-only", FORWARDONLY }, + { "forward-only", FORWARD_ONLY }, { "from", FROM }, + { "helo", HELO }, + { "helo-src", HELO_SRC }, + { "host", HOST }, { "hostname", HOSTNAME }, { "hostnames", HOSTNAMES }, { "include", INCLUDE }, @@ -1485,44 +1527,48 @@ lookup(char *s) { "key", KEY }, { "limit", LIMIT }, { "listen", LISTEN }, - { "lmtp", LMTP }, { "local", LOCAL }, + { "mail-from", MAIL_FROM }, { "maildir", MAILDIR }, - { "mask-source", MASK_SOURCE }, + { "mask-src", MASK_SRC }, { "masquerade", MASQUERADE }, - { "max-message-size", MAXMESSAGESIZE }, - { "max-mta-deferred", MAXMTADEFERRED }, + { "match", MATCH }, + { "max-deferred", MAX_DEFERRED }, + { "max-message-size", MAX_MESSAGE_SIZE }, { "mbox", MBOX }, { "mda", MDA }, { "mta", MTA }, + { "mx", MX }, { "no-dsn", NODSN }, { "on", ON }, { "pki", PKI }, { "port", PORT }, { "queue", QUEUE }, - { "rcpt-to", RCPTTO }, + { "rcpt-to", RCPT_TO }, { "received-auth", RECEIVEDAUTH }, { "recipient", RECIPIENT }, { "reject", REJECT }, { "relay", RELAY }, { "scheduler", SCHEDULER }, - { "sender", SENDER }, { "senders", SENDERS }, - { "session", SESSION }, + { "set", SET }, + { "smtp", SMTP }, { "smtps", SMTPS }, { "socket", SOCKET }, - { "source", SOURCE }, - { "subaddressing-delimiter", SUBADDRESSING_DELIM }, + { "src", SRC }, + { "sub-addr-delim", SUB_ADDR_DELIM }, { "table", TABLE }, { "tag", TAG }, { "tagged", TAGGED }, { "tls", TLS }, { "tls-require", TLS_REQUIRE }, { "to", TO }, + { "ttl", TTL }, + { "user", USER }, { "userbase", USERBASE }, { "verify", VERIFY }, - { "via", VIA }, { "virtual", VIRTUAL }, + { "warn-interval", WARN_INTERVAL }, }; const struct keywords *p; @@ -1871,6 +1917,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict)); conf->sc_rules = calloc(1, sizeof(*conf->sc_rules)); + conf->sc_dispatchers = calloc(1, sizeof(*conf->sc_dispatchers)); conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners)); conf->sc_ca_dict = calloc(1, sizeof(*conf->sc_ca_dict)); conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict)); @@ -1882,6 +1929,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) if (conf->sc_tables_dict == NULL || conf->sc_rules == NULL || + conf->sc_dispatchers == NULL || conf->sc_listeners == NULL || conf->sc_ca_dict == NULL || conf->sc_pki_dict == NULL || @@ -1890,6 +1938,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) log_warn("warn: cannot allocate memory"); free(conf->sc_tables_dict); free(conf->sc_rules); + free(conf->sc_dispatchers); free(conf->sc_listeners); free(conf->sc_ca_dict); free(conf->sc_pki_dict); @@ -1901,8 +1950,8 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) errors = 0; table = NULL; - rule = NULL; + dict_init(conf->sc_dispatchers); dict_init(conf->sc_ca_dict); dict_init(conf->sc_pki_dict); dict_init(conf->sc_ssl_dict); @@ -1916,7 +1965,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) TAILQ_INIT(conf->sc_listeners); TAILQ_INIT(conf->sc_rules); - conf->sc_qexpire = SMTPD_QUEUE_EXPIRY; + conf->sc_ttl = SMTPD_QUEUE_EXPIRY; conf->sc_opts = opts; conf->sc_mta_max_deferred = 100; @@ -1958,6 +2007,12 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts) table_create("getpwnam", "<getpwnam>", NULL, NULL); + /* bounce dispatcher */ + dispatcher = xcalloc(1, sizeof *dispatcher, "dispatcher"); + dispatcher->type = DISPATCHER_BOUNCE; + conf->sc_dispatcher_bounce = dispatcher; + dispatcher = NULL; + /* * parse configuration */ diff --git a/usr.sbin/smtpd/pony.c b/usr.sbin/smtpd/pony.c index 459a13dd444..f7b3d8e2e57 100644 --- a/usr.sbin/smtpd/pony.c +++ b/usr.sbin/smtpd/pony.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pony.c,v 1.19 2018/03/04 16:49:09 gilles Exp $ */ +/* $OpenBSD: pony.c,v 1.20 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org> @@ -98,6 +98,7 @@ pony_imsg(struct mproc *p, struct imsg *imsg) case IMSG_QUEUE_TRANSFER: case IMSG_MTA_OPEN_MESSAGE: case IMSG_MTA_LOOKUP_CREDENTIALS: + case IMSG_MTA_LOOKUP_SMARTHOST: case IMSG_MTA_LOOKUP_SOURCE: case IMSG_MTA_LOOKUP_HELO: case IMSG_MTA_DNS_HOST: diff --git a/usr.sbin/smtpd/queue.c b/usr.sbin/smtpd/queue.c index 60959aafa57..7c454048152 100644 --- a/usr.sbin/smtpd/queue.c +++ b/usr.sbin/smtpd/queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue.c,v 1.185 2018/05/14 15:23:05 gilles Exp $ */ +/* $OpenBSD: queue.c,v 1.186 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -577,7 +577,7 @@ queue_bounce(struct envelope *e, struct delivery_bounce *d) b.retry = 0; b.lasttry = 0; b.creation = time(NULL); - b.expire = 3600 * 24 * 7; + b.ttl = 3600 * 24 * 7; if (e->dsn_notify & DSN_NEVER) return; @@ -624,7 +624,7 @@ queue(void) struct timeval tv; struct event ev_qload; - purge_config(PURGE_EVERYTHING); + purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS); if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL) if ((pw = getpwnam(SMTPD_USER)) == NULL) @@ -687,6 +687,7 @@ static void queue_timeout(int fd, short event, void *p) { static uint32_t msgid = 0; + struct dispatcher *dsp; struct envelope evp; struct event *ev = p; struct timeval tv; @@ -705,6 +706,13 @@ queue_timeout(int fd, short event, void *p) } if (r) { + dsp = dict_get(env->sc_dispatchers, evp.dispatcher); + if (dsp == NULL) { + log_warnx("warn: queue: missing dispatcher \"%s\"" + " for envelope %016"PRIx64", ignoring", + evp.dispatcher, evp.id); + goto reset; + } if (msgid && evpid_to_msgid(evp.id) != msgid) { m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1); @@ -717,6 +725,7 @@ queue_timeout(int fd, short event, void *p) m_close(p_scheduler); } +reset: tv.tv_sec = 0; tv.tv_usec = 10; evtimer_add(ev, &tv); diff --git a/usr.sbin/smtpd/ruleset.c b/usr.sbin/smtpd/ruleset.c index d46783c843d..1ecaa298458 100644 --- a/usr.sbin/smtpd/ruleset.c +++ b/usr.sbin/smtpd/ruleset.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ruleset.c,v 1.34 2017/02/13 12:23:47 gilles Exp $ */ +/* $OpenBSD: ruleset.c,v 1.35 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> @@ -34,125 +34,211 @@ #include "log.h" -static int ruleset_check_source(struct table *, - const struct sockaddr_storage *, int); -static int ruleset_check_mailaddr(struct table *, const struct mailaddr *); +static int +ruleset_match_table_lookup(struct table *table, const char *key, enum table_service service) +{ + switch (table_lookup(table, NULL, key, service, NULL)) { + case 1: + return 1; + case -1: + log_warnx("warn: failure to perform a table lookup on table %s", + table->t_name); + return -1; + default: + break; + } + return 0; +} -struct rule * -ruleset_match(const struct envelope *evp) +static int +ruleset_match_tag(struct rule *r, const struct envelope *evp) { - const struct mailaddr *maddr = &evp->dest; - const struct sockaddr_storage *ss = &evp->ss; - struct rule *r; - int ret; + int ret; + struct table *table; - TAILQ_FOREACH(r, env->sc_rules, r_entry) { + if (!r->flag_tag) + return 1; - if (r->r_tag[0] != '\0') { - ret = strcmp(r->r_tag, evp->tag); - if (ret != 0 && !r->r_nottag) - continue; - if (ret == 0 && r->r_nottag) - continue; - } - - if ((r->r_wantauth && !r->r_negwantauth) && !(evp->flags & EF_AUTHENTICATED)) - continue; - if ((r->r_wantauth && r->r_negwantauth) && (evp->flags & EF_AUTHENTICATED)) - continue; - - ret = ruleset_check_source(r->r_sources, ss, evp->flags); - if (ret == -1) { - errno = EAGAIN; - return (NULL); - } - if ((ret == 0 && !r->r_notsources) || (ret != 0 && r->r_notsources)) - continue; - - if (r->r_senders) { - ret = ruleset_check_mailaddr(r->r_senders, &evp->sender); - if (ret == -1) { - errno = EAGAIN; - return (NULL); - } - if ((ret == 0 && !r->r_notsenders) || (ret != 0 && r->r_notsenders)) - continue; - } - - if (r->r_recipients) { - ret = ruleset_check_mailaddr(r->r_recipients, &evp->dest); - if (ret == -1) { - errno = EAGAIN; - return (NULL); - } - if ((ret == 0 && !r->r_notrecipients) || (ret != 0 && r->r_notrecipients)) - continue; - } - - ret = r->r_destination == NULL ? 1 : - table_lookup(r->r_destination, NULL, maddr->domain, K_DOMAIN, - NULL); - if (ret == -1) { - errno = EAGAIN; - return NULL; - } - if ((ret == 0 && !r->r_notdestination) || (ret != 0 && r->r_notdestination)) - continue; + table = table_find(r->table_tag, NULL); + if ((ret = ruleset_match_table_lookup(table, evp->tag, K_STRING)) < 0) + return ret; - goto matched; + return r->flag_tag < 0 ? !ret : ret; +} + +static int +ruleset_match_from(struct rule *r, const struct envelope *evp) +{ + int ret; + const char *key; + struct table *table; + + if (!r->flag_from) + return 1; + + if (r->flag_from_socket) { + /* XXX - socket needs to be distinguished from "local" */ + return -1; } - errno = 0; - log_trace(TRACE_RULES, "no rule matched"); - return (NULL); + /* XXX - socket should also be considered local */ + if (evp->flags & EF_INTERNAL) + key = "local"; + else + key = ss_to_text(&evp->ss); -matched: - log_trace(TRACE_RULES, "rule matched: %s", rule_to_text(r)); - return r; + table = table_find(r->table_from, NULL); + if ((ret = ruleset_match_table_lookup(table, key, K_NETADDR)) < 0) + return -1; + + return r->flag_from < 0 ? !ret : ret; } static int -ruleset_check_source(struct table *table, const struct sockaddr_storage *ss, - int evpflags) +ruleset_match_to(struct rule *r, const struct envelope *evp) { - const char *key; + int ret; + struct table *table; - if (evpflags & (EF_AUTHENTICATED | EF_INTERNAL)) - key = "local"; - else - key = ss_to_text(ss); - switch (table_lookup(table, NULL, key, K_NETADDR, NULL)) { - case 1: + if (!r->flag_for) return 1; - case -1: - log_warnx("warn: failure to perform a table lookup on table %s", - table->t_name); + + table = table_find(r->table_for, NULL); + if ((ret = ruleset_match_table_lookup(table, evp->dest.domain, + K_DOMAIN)) < 0) return -1; - default: - break; + + return r->flag_for < 0 ? !ret : ret; +} + +static int +ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp) +{ + int ret; + struct table *table; + + if (!r->flag_smtp_helo) + return 1; + + table = table_find(r->table_smtp_helo, NULL); + if ((ret = ruleset_match_table_lookup(table, evp->helo, K_DOMAIN)) < 0) + return -1; + + return r->flag_smtp_helo < 0 ? !ret : ret; +} + +static int +ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp) +{ + if (!r->flag_smtp_starttls) + return 1; + + /* XXX - not until TLS flag is added to envelope */ + return -1; +} + +static int +ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp) +{ + int ret; + + if (!r->flag_smtp_auth) + return 1; + + if (!(evp->flags & EF_AUTHENTICATED)) + ret = 0; + else if (r->table_smtp_auth) { + /* XXX - not until smtp_session->username is added to envelope */ + /* + * table = table_find(m->from_table, NULL); + * key = evp->username; + * return ruleset_match_table_lookup(table, key, K_CREDENTIALS); + */ + return -1; + } + else + ret = 1; - return 0; + return r->flag_smtp_auth < 0 ? !ret : ret; } static int -ruleset_check_mailaddr(struct table *table, const struct mailaddr *maddr) +ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp) { + int ret; const char *key; + struct table *table; - key = mailaddr_to_text(maddr); - if (key == NULL) + if (!r->flag_smtp_mail_from) + return 1; + + if ((key = mailaddr_to_text(&evp->sender)) == NULL) return -1; - switch (table_lookup(table, NULL, key, K_MAILADDR, NULL)) { - case 1: + table = table_find(r->table_smtp_mail_from, NULL); + if ((ret = ruleset_match_table_lookup(table, key, K_MAILADDR)) < 0) + return -1; + + return r->flag_smtp_mail_from < 0 ? !ret : ret; +} + +static int +ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp) +{ + int ret; + const char *key; + struct table *table; + + if (!r->flag_smtp_rcpt_to) return 1; - case -1: - log_warnx("warn: failure to perform a table lookup on table %s", - table->t_name); + + if ((key = mailaddr_to_text(&evp->dest)) == NULL) return -1; - default: - break; + + table = table_find(r->table_smtp_rcpt_to, NULL); + if ((ret = ruleset_match_table_lookup(table, key, K_MAILADDR)) < 0) + return -1; + + return r->flag_smtp_rcpt_to < 0 ? !ret : ret; +} + +struct rule * +ruleset_match(const struct envelope *evp) +{ + struct rule *r; + int i = 0; + +#define MATCH_EVAL(x) \ + switch ((x)) { \ + case -1: goto tempfail; \ + case 0: continue; \ + default: break; \ } - return 0; + TAILQ_FOREACH(r, env->sc_rules, r_entry) { + ++i; + MATCH_EVAL(ruleset_match_tag(r, evp)); + MATCH_EVAL(ruleset_match_from(r, evp)); + MATCH_EVAL(ruleset_match_to(r, evp)); + MATCH_EVAL(ruleset_match_smtp_helo(r, evp)); + MATCH_EVAL(ruleset_match_smtp_auth(r, evp)); + MATCH_EVAL(ruleset_match_smtp_starttls(r, evp)); + MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp)); + MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp)); + goto matched; + } +#undef MATCH_EVAL + + errno = 0; + log_trace(TRACE_RULES, "no rule matched"); + return (NULL); + +tempfail: + errno = EAGAIN; + log_trace(TRACE_RULES, "temporary failure in processing of a rule"); + return (NULL); + +matched: + log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r)); + return r; } diff --git a/usr.sbin/smtpd/scheduler.c b/usr.sbin/smtpd/scheduler.c index f6c11de853e..e39e5b9d894 100644 --- a/usr.sbin/smtpd/scheduler.c +++ b/usr.sbin/smtpd/scheduler.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scheduler.c,v 1.56 2017/01/09 14:49:22 reyk Exp $ */ +/* $OpenBSD: scheduler.c,v 1.57 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -216,7 +216,7 @@ scheduler_imsg(struct mproc *p, struct imsg *imsg) req.timestamp = timestamp; req.bounce.type = B_WARNING; req.bounce.delay = env->sc_bounce_warn[i]; - req.bounce.expire = si.expire; + req.bounce.ttl = si.ttl; m_compose(p, IMSG_SCHED_ENVELOPE_BOUNCE, 0, 0, -1, &req, sizeof req); break; @@ -433,7 +433,7 @@ scheduler(void) errx(1, "cannot find scheduler backend \"%s\"", backend_scheduler); - purge_config(PURGE_EVERYTHING); + purge_config(PURGE_EVERYTHING & ~PURGE_DISPATCHERS); if ((pw = getpwnam(SMTPD_USER)) == NULL) fatalx("unknown user " SMTPD_USER); diff --git a/usr.sbin/smtpd/scheduler_backend.c b/usr.sbin/smtpd/scheduler_backend.c index 97b15f25006..061f1129595 100644 --- a/usr.sbin/smtpd/scheduler_backend.c +++ b/usr.sbin/smtpd/scheduler_backend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scheduler_backend.c,v 1.15 2015/01/20 17:37:54 deraadt Exp $ */ +/* $OpenBSD: scheduler_backend.c,v 1.16 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> @@ -52,11 +52,28 @@ scheduler_backend_lookup(const char *name) void scheduler_info(struct scheduler_info *sched, struct envelope *evp) { + struct dispatcher *disp; + + disp = evp->type == D_BOUNCE ? + env->sc_dispatcher_bounce : + dict_xget(env->sc_dispatchers, evp->dispatcher); + + switch (disp->type) { + case DISPATCHER_LOCAL: + sched->type = D_MDA; + break; + case DISPATCHER_REMOTE: + sched->type = D_MTA; + break; + case DISPATCHER_BOUNCE: + sched->type = D_BOUNCE; + break; + } + sched->ttl = disp->ttl ? disp->ttl : env->sc_ttl; + sched->evpid = evp->id; - sched->type = evp->type; sched->creation = evp->creation; sched->retry = evp->retry; - sched->expire = evp->expire; sched->lasttry = evp->lasttry; sched->lastbounce = evp->lastbounce; sched->nexttry = 0; diff --git a/usr.sbin/smtpd/scheduler_ramqueue.c b/usr.sbin/smtpd/scheduler_ramqueue.c index ffc0b3e642f..65043b23d92 100644 --- a/usr.sbin/smtpd/scheduler_ramqueue.c +++ b/usr.sbin/smtpd/scheduler_ramqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scheduler_ramqueue.c,v 1.43 2017/01/09 09:53:23 reyk Exp $ */ +/* $OpenBSD: scheduler_ramqueue.c,v 1.44 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> @@ -228,7 +228,7 @@ scheduler_ram_insert(struct scheduler_info *si) envelope->type = si->type; envelope->message = message; envelope->ctime = si->creation; - envelope->expire = si->creation + si->expire; + envelope->expire = si->creation + si->ttl; envelope->sched = scheduler_backoff(si->creation, (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry); tree_xset(&message->envelopes, envelope->evpid, envelope); diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index a59336705bc..541331194fd 100644 --- a/usr.sbin/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpctl.c,v 1.160 2018/05/14 15:23:05 gilles Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.161 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -1212,7 +1212,7 @@ show_queue_envelope(struct envelope *e, int online) e->dest.user, e->dest.domain, (size_t) e->creation, - (size_t) (e->creation + e->expire), + (size_t) (e->creation + e->ttl), (size_t) e->lasttry, (size_t) e->retry, runstate, diff --git a/usr.sbin/smtpd/smtpd-api.h b/usr.sbin/smtpd/smtpd-api.h index 14a57c5e8f5..d36d77ce7be 100644 --- a/usr.sbin/smtpd/smtpd-api.h +++ b/usr.sbin/smtpd/smtpd-api.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd-api.h,v 1.33 2018/05/14 15:23:05 gilles Exp $ */ +/* $OpenBSD: smtpd-api.h,v 1.34 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -111,7 +111,7 @@ struct scheduler_info { enum delivery_type type; uint16_t retry; time_t creation; - time_t expire; + time_t ttl; time_t lasttry; time_t lastbounce; time_t nexttry; @@ -142,6 +142,8 @@ enum table_service { K_MAILADDR = 0x040, /* returns struct mailaddr */ K_ADDRNAME = 0x080, /* returns struct addrname */ K_MAILADDRMAP = 0x100, /* returns struct maddrmap */ + K_RELAYHOST = 0x200, /* returns struct relayhost */ + K_STRING = 0x400, }; #define K_ANY 0xfff diff --git a/usr.sbin/smtpd/smtpd-defines.h b/usr.sbin/smtpd/smtpd-defines.h index 0fc459cb9a7..dcb0024c37c 100644 --- a/usr.sbin/smtpd/smtpd-defines.h +++ b/usr.sbin/smtpd/smtpd-defines.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd-defines.h,v 1.7 2016/08/31 10:18:08 gilles Exp $ */ +/* $OpenBSD: smtpd-defines.h,v 1.8 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org> @@ -35,7 +35,7 @@ #define PATH_CHROOT "/var/empty" #define SMTPD_QUEUE_USER "_smtpq" #define SMTPD_QUEUE_GROUP "_smtpq" -#define PATH_SPOOL "/var/spool/smtpd" +#define PATH_SPOOL "/var/spool/smtpd.new" #define SUBADDRESSING_DELIMITER "+" diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index b144c94e614..056691a40f1 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.294 2018/05/14 15:23:05 gilles Exp $ */ +/* $OpenBSD: smtpd.c,v 1.295 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -1210,27 +1210,54 @@ static void forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) { char ebuf[128], sfn[32]; - struct delivery_backend *db; + struct dispatcher *dsp; struct child *child; pid_t pid; int allout, pipefd[2]; + struct passwd *pw; + uid_t pw_uid; + gid_t pw_gid; + const char *pw_dir; + char *mda_environ[3]; + const char *mda_command; + char mda_exec[LINE_MAX]; + + dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher); log_debug("debug: smtpd: forking mda for session %016"PRIx64 - ": \"%s\" as %s", id, deliver->to, deliver->user); + ": %s as %s", id, deliver->userinfo.username, + dsp->u.local.user ? dsp->u.local.user : deliver->userinfo.username); + + if (dsp->u.local.user) { + if ((pw = getpwnam(dsp->u.local.user)) == NULL) { + (void)snprintf(ebuf, sizeof ebuf, + "delivery user '%s' does not exist", + dsp->u.local.user); + m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); + m_add_id(p_pony, id); + m_add_string(p_pony, ebuf); + m_close(p_pony); + return; + } + pw_uid = pw->pw_uid; + pw_gid = pw->pw_gid; + pw_dir = pw->pw_dir; + } + else { + pw_uid = deliver->userinfo.uid; + pw_gid = deliver->userinfo.gid; + pw_dir = deliver->userinfo.directory; + } - db = delivery_backend_lookup(deliver->mode); - if (db == NULL) { - (void)snprintf(ebuf, sizeof ebuf, "could not find delivery backend"); - m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); - m_add_id(p_pony, id); - m_add_string(p_pony, ebuf); - m_close(p_pony); - return; + if (pw_uid == 0 && deliver->mda_exec[0]) { + pw_uid = deliver->userinfo.uid; + pw_gid = deliver->userinfo.gid; + pw_dir = deliver->userinfo.directory; } - if (deliver->userinfo.uid == 0 && !db->allow_root) { + if (pw_uid == 0 && !dsp->u.local.requires_root) { (void)snprintf(ebuf, sizeof ebuf, "not allowed to deliver to: %s", - deliver->user); + deliver->userinfo.username); m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); m_add_id(p_pony, id); m_add_string(p_pony, ebuf); @@ -1286,12 +1313,11 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) m_close(p); return; } - - if (chdir(deliver->userinfo.directory) < 0 && chdir("/") < 0) + if (chdir(pw_dir) < 0 && chdir("/") < 0) err(1, "chdir"); - if (setgroups(1, &deliver->userinfo.gid) || - setresgid(deliver->userinfo.gid, deliver->userinfo.gid, deliver->userinfo.gid) || - setresuid(deliver->userinfo.uid, deliver->userinfo.uid, deliver->userinfo.uid)) + if (setgroups(1, &pw_gid) || + setresgid(pw_gid, pw_gid, pw_gid) || + setresuid(pw_uid, pw_uid, pw_uid)) err(1, "forkmda: cannot drop privileges"); if (dup2(pipefd[0], STDIN_FILENO) < 0 || dup2(allout, STDOUT_FILENO) < 0 || @@ -1311,7 +1337,29 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) /* avoid hangs by setting 5m timeout */ alarm(300); - db->open(deliver); + if (deliver->mda_exec[0]) + mda_command = deliver->mda_exec; + else + mda_command = dsp->u.local.command; + + if (strlcpy(mda_exec, mda_command, sizeof (mda_exec)) + >= sizeof (mda_exec)) + err(1, "mda command line too long"); + + if (! mda_expand_format(mda_exec, sizeof mda_exec, deliver, + &deliver->userinfo)) + err(1, "mda command line could not be expanded"); + + mda_environ[0] = "PATH=" _PATH_DEFPATH; + + (void)snprintf(ebuf, sizeof ebuf, "HOME=%s", pw_dir); + mda_environ[1] = xstrdup(ebuf, "forkmda"); + + mda_environ[2] = (char *)NULL; + execle("/bin/sh", "/bin/sh", "-c", mda_exec, (char *)NULL, + mda_environ); + perror("execle"); + _exit(1); } static void @@ -1804,6 +1852,7 @@ imsg_to_str(int type) CASE(IMSG_MTA_LOOKUP_CREDENTIALS); CASE(IMSG_MTA_LOOKUP_SOURCE); CASE(IMSG_MTA_LOOKUP_HELO); + CASE(IMSG_MTA_LOOKUP_SMARTHOST); CASE(IMSG_MTA_OPEN_MESSAGE); CASE(IMSG_MTA_SCHEDULE); CASE(IMSG_MTA_TLS_INIT); diff --git a/usr.sbin/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5 index c08e45ee4c4..8c2f1208a6b 100644 --- a/usr.sbin/smtpd/smtpd.conf.5 +++ b/usr.sbin/smtpd/smtpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: smtpd.conf.5,v 1.174 2017/07/11 06:08:40 natano Exp $ +.\" $OpenBSD: smtpd.conf.5,v 1.175 2018/05/24 11:38:24 gilles Exp $ .\" .\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> .\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> @@ -17,7 +17,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" -.Dd $Mdocdate: July 11 2017 $ +.Dd $Mdocdate: May 24 2018 $ .Dt SMTPD.CONF 5 .Os .Sh NAME @@ -41,12 +41,12 @@ must be quoted. Arguments containing whitespace should be surrounded by double quotes .Pq \&" . .Pp -Macros can be defined that will later be expanded in context. +Macros can be defined that are later expanded in context. Macro names must start with a letter, digit, or underscore, and may contain any of those characters. Macro names may not be reserved words (for example .Ar listen , -.Ar accept , +.Ar match , .Ar port ) . Macros are not expanded inside quotes. .Pp @@ -57,771 +57,381 @@ listen on $lan_addr listen on $lan_addr tls auth .Ed .Pp -Additional configuration files can be included with the -.Ic include -keyword, for example: -.Bd -literal -offset indent -include "/etc/mail/smtpd.conf.local" -.Ed -.Pp The syntax of .Nm is described below. .Bl -tag -width Ds -.It Ic accept | reject +.It Ic action Ar name Ar method Op options +When the queue runner processes an envelope from the mail queue, +it uses the +.Ic action +directive matching the dispatcher +.Fa name +that was selected by the +.Ic match action +directive when the message was received. +That +.Ic action +directive provides configuration data for delivery attempts. +Required lookups are performed at the time of each delivery attempt. +Consequently, changing an +.Ic action +directive or the files it references and restarting the .Xr smtpd 8 -accepts and rejects messages -based on information gathered during the SMTP session. +daemon causes the changes to take effect for subsequent delivery +attempts for the respective dispatcher +.Ar name , +even for messages that were already stuck in the queue +prior to the configuration changes. +.Pp +The delivery +.Ar method +parameter may be one of the following: +.Bl -tag -width Ds +.It Ic expand-only +Only accept the message if a delivery method was specified +in an aliases or +.Pa .forward +file. +.It Ic forward-only +Only accept the message if the recipient results in a remote address. +.It Ic maildir Op Ar pathname +Deliver the message to the maildir in +.Ar pathname +if specified, or by default to +.Pa ~/Maildir . .Pp -For each message processed by the daemon, -the rules are evaluated in sequential order, -from first to last. -The first matching rule decides what action is taken. -If no rule matches the message, -the default action is to reject the message. -An exclamation mark may be specified to perform a reverse match. +The +.Ar pathname +may contain format specifiers that are expanded before use +.Pq see Sx FORMAT SPECIFIERS . +.It Ic mbox +Deliver the message to the user's mbox with +.Xr mail.local 8 . +.It Ic mda Ar command +Delegate the delivery to a +.Ar command +that receives the message on its standard input. .Pp -Following the accept/reject -decision comes the matching of optional session related properties: -.Bl -tag -width Ds -.It Xo -.Op Ic \&! -.Ic authenticated -.Xc -If specified, the rule will only be matched if the client session was -authenticated either by requesting authentication over the network or -because the message was submitted over the local enqueuer. -.It Xo -.Ic tagged -.Op Ic \&! -.Ar tag -.Xc -If specified, the rule will only be matched if the client session was tagged with -.Ar tag . +The +.Ar command +may contain format specifiers that are expanded before use +.Pq see Sx FORMAT SPECIFIERS . +.It Ic relay +Relay the message to another SMTP server. .El .Pp -After that the client's IP address rule is specified: +The local delivery methods support additional options: .Bl -tag -width Ds -.It Ic from any -Make the rule match regardless of the IP of connecting client. -.It Xo -.Ic from -.Op Ic \&! -.Ic local -.Xc -The rule matches only locally originating connections. -This is the default, -and may be omitted. +.It Ic alias Pf < Ar table Ns > +Use the mapping +.Ar table +for +.Xr aliases 5 +expansion. .It Xo -.Ic from -.Op Ic \&! -.Ic source -.Pf < Ar table Ns > +.Ic ttl +.Sm off +.Ar n +.Brq Cm s | m | h | d +.Sm on .Xc -The rule matches if the connection is made from a client whose address -is declared in the table -.Ar table . +Specify how long a message may remain in the queue. +.It Ic user Ar username +Specify the +.Ar username +for performing the delivery, to be looked up with +.Xr getpwnam 3 . +.Pp +This is used for virtual hosting where a single username +is in charge of handling delivery for all virtual users. +.Pp +This option is not usable with the +.Ic mbox +delivery method. +.It Ic userbase Pf < Ar table Ns > +Use the mapping +.Ar table +for user lookups instead of the +.Xr getpwnam 3 +function. +.Pp +The +.Ic userbase +does not apply for the +.Ic user +option. +.It Ic virtual Pf < Ar table Ns > +Use the mapping +.Ar table +for +.Xr virtual 5 +expansion. .El .Pp -In addition, finer access control may be achieved on the sender if desired: +The relay delivery methods also support additional options: .Bl -tag -width Ds -.It Xo -.Ic sender -.Op Ic \&! -.Pf < Ar senders Ns > -.Xc -If specified, the rule will only be matched if the sender email address -is found in the table -.Ar senders . -The table may contain complete email addresses or apply to an entire -domain if prefixed with -.Sq @ . +.It Ic backup +Operate as a backup mail exchanger delivering messages to any mail exchanger +with higher priority. +.It Ic backup mx Ar name +Operate as a backup mail exchanger delivering messages to any mail exchanger +with higher priority than mail exchanger identified as +.Ar name . +.It Ic helo Ar heloname +Advertise +.Ar heloname +as the hostname to other mail exchangers during the HELO phase. +.It Ic helo-src Pf < Ar table Ns > +Use the mapping +.Ar table +to look up a hostname matching the source address, +to advertise during the HELO phase. +.It Ic host Ar relay-url +Do not perform MX lookups but relay messages to the relay host described by +.Ar relay-url . +.It Ic mail-from Ar mailaddr +Use +.Ar mailaddr +as the MAIL FROM address within the SMTP transaction. +.It Ic src Ar address | Pf < Ar address Ns > +Use the string or list table +.Ar address +for the source IP address. +If the list contains more than one address, all of them are used +in such a way that traffic is routed as efficiently as possible. .El +.It Ic ca Ar hostname Ic cert Ar cafile +Associate a custom CA certificate located in +.Ar cafile +with +.Ar hostname . +.It Ic include Qq Ar pathname +Replace this directive with the content of the additional configuration +file at the absolute +.Ar pathname . +.It Ic listen on Ar interface Oo Ar family Oc Op Ar options +Listen on the +.Ar interface +for incoming connections, using the same syntax as for +.Xr ifconfig 8 . +The +.Ar interface +parameter may also be an interface group, an IP address, or a domain name. +Listening can optionally be resticted to a specific address +.Ar family , +which can be either +.Ic inet4 +or +.Ic inet6 . .Pp -Next comes the selection based on the domain the message is sent to: +The +.Ar options +are as follows: .Bl -tag -width Ds -.It Ic for any Op Ic alias No < Ns Ar aliases Ns > -Make the rule match regardless of the domain it is sent to. -If specified, the table -.Ar aliases -is used for looking up alternative destinations for all addresses. -.It Ic for any virtual No < Ns Ar vmap Ns > -Make the rule match regardless of the domain it is sent to. +.It Ic auth Op Pf < Ar authtable Ns > +Support SMTPAUTH, and clients may only start SMTP transactions +after successful authentication. +Credentials are looked up in the optional mapping +.Ar authtable . +The credentials format is described in +.Xr table 5 . +.Pp +Any remote sender that passed SMTPAUTH is treated as if +it was the server's local user that was sending the mail. +This means that filter rules using +.Ic from local +are matched. +.It Ic auth-optional Op Pf < Ar authtable Ns > +Like +.Ic auth , +except that authentication is not required +to establish an SMTP transaction. +This is only useful to let a listener accept incoming mail from +untrusted senders and outgoing mail from authenticated users in +situations where it is not possible to listen on the submission +port. +.It Ic ca Ar caname +For secure connections, +use the custom CA certificate previously declared in a +.Ic ca +directive with a matching +.Ar caname . +.It Ic hostname Ar hostname +Use +.Ar hostname +in the greeting banner instead of the default server name. +.It Ic hostnames Pf < Ar names Ns > +Override the server name for specific addresses. The -.Ar vmap -table will be used as the virtual domain mapping. +.Ar names +table contains a mapping of IP addresses to hostnames. +If the address on which the connection arrives appears in the mapping, +the associated hostname is used. +.It Ic mask-src +Omit the +.Ic from +part when prepending +.Dq Received +headers. +.It Ic no-dsn +Disable the DSN (Delivery Status Notification) extension. +.It Ic pki Ar pkiname +For secure connections, use a host certificate previously declared in a +.Ic pki +directive with a matching +.Ar pkiname . +.It Ic port Op Ar port +Listen on the given +.Ar port +instead of the default port 25. +.It Ic received-auth +In +.Dq Received +headers, report whether the session was authenticated +and by which local user. +.It Ic senders Pf < Ar users Ns > Op Cm masquerade +Look up the authenticated user in the +.Ar users +mapping table to find the email addresses that user is allowed +to submit mail as. +In addition, if the +.Cm masquerade +option is provided, +the From header is rewritten +to match the sender provided in the SMTP session. +.It Ic smtps +Support SMTPS, by default on port 465. +Mutually exclusive with +.Ic tls . +.It Ic tag Ar tag +Clients connecting to the listener are tagged with the given +.Ar tag . +.It Ic tls +Support STARTTLS, by default on port 25. +Mutually exclusive with +.Ic smtps . +.It Ic tls-require Op Cm verify +Like +.Ic tls , +and force clients to establish a secure connection +before being allowed to start an SMTP transaction. +With the +.Cm verify +option, clients must also provide a valid certificate +to establish an SMTP session. +.El +.It Ic listen on socket Op Cm mask\-src +Listen for incoming SMTP connections on the Unix domain socket +.Pa /var/run/smtpd.sock . +This is done by default, even if the directive is absent. +If the +.Cm mask\-src +option is specified, printing of the HELO name, hostname, and IP +address of the originating host is suppressed in Received: header lines. +.\" XXX The option +.\" Cm filter Ar string +.\" is parsed, but not implemented, see smtpf_session.c. +.It Ic match Ar options Ic action Ar name +During an incoming SMTP session, each +.Ic RCPT TO: +command generates an envelope. +If at least one envelope matches the +.Ar options +of one +.Ic match action +directive, receive the incoming message, put a copy into each +matching envelope, and atomically save the envelopes to the mail +spool for later processing by the respective dispatcher +.Ar name . +.Pp +The following matching options are supported and can all be negated: +.Bl -tag -width Ds .It Xo -.Ic for .Op Ic \&! -.Ic domain -.Ar domain -.Op Ic alias No < Ns Ar aliases Ns > +.Ic for any .Xc -This rule applies to mail destined for the specified -.Ar domain . -This parameter supports the -.Sq * -wildcard, -so that a single rule for all sub-domains can be used, for example: -.Bd -literal -offset indent -accept for domain "*.example.com" deliver to mbox -.Ed -.Pp -If specified, the table -.Ar aliases -is used for looking up alternative destinations for addresses in this -.Ar domain . +Specify that session may address any destination. .It Xo -.Ic for .Op Ic \&! -.Ic domain -.Pf < Ar domains Ns > -.Op Ic alias No < Ns Ar aliases Ns > +.Ic for local .Xc -This rule applies to mail destined to domains which are part of the table -.Ar domains . -.Pp -If specified, the table -.Ar aliases -is used for looking up alternative destinations for addresses in these -.Ar domains . +Specify that session may address any local domain. .It Xo -.Ic for .Op Ic \&! -.Ic domain -.Ar domain -.Ic virtual No < Ns Ar users Ns > +.Ic for domain +.Ar domain | Pf < Ar domain Ns > .Xc -This rule applies to mail destined for the specified virtual +Specify that session may address the string or list table .Ar domain . -This parameter supports the -.Sq * -wildcard, -so that a single rule for all sub-domains can be used, for example: -.Bd -literal -offset indent -accept for domain "*.example.com" \e - virtual <users> deliver to mbox -.Ed -.Pp -The table -.Ar users -holds a key-value mapping of virtual to system users. -For an example of how to configure the -.Ar users -table, see -.Xr table 5 . .It Xo -.Ic for .Op Ic \&! -.Ic domain -.Pf < Ar domains Ns > Ic virtual No < Ns Ar users Ns > +.Ic from any .Xc -This rule applies to mail destined for the virtual domains specified -in the table -.Ar domains . -.Pp -The table -.Ar users -holds a key-value mapping of virtual to system users. -For an example of how to configure the -.Ar users -table, see -.Xr table 5 . +Specify that session may originate from any source. .It Xo -.Ic for .Op Ic \&! -.Ic local -.Op Ic alias No < Ns Ar aliases Ns > +.Ic from local .Xc -This rule applies to mail destined to -.Dq localhost -and to the default server name -(the -.Sx FILES -entry for -.Pa /etc/mail/mailname -details how the server name is determined). -This is the default, -and may be omitted. -.Pp -If specified, the table -.Ar aliases -is used for looking up alternative destinations for addresses in these -.Ar domains . +Specify that session may only originate from a local IP address, +or from the local enqueuer. .It Xo -.Ic for .Op Ic \&! -.Ic local -.Ic virtual No < Ns Ar vmap Ns > +.Ic from socket .Xc -This rule applies to mail destined to -.Dq localhost -and to the default server name. -The -.Ar vmap -table will be used as the virtual domain mapping. -.El -.Pp -Further access control may be achieved on specific recipients if desired: -.Bl -tag -width Ds +Specify that session may only originate from the local enqueuer. .It Xo -.Ic recipient .Op Ic \&! -.Pf < Ar recipients Ns > -.Xc -If specified, the rule will only be matched if the recipient email address -is found in the table -.Ar recipients . -The table may contain complete email addresses or apply to an entire -domain if prefixed with -.Sq @ . -.El -.Pp -If the method of delivery is local, a user database may be -specified to override the system database: -.Bl -tag -width Ds -.It Op Ic userbase No < Ns Ar table Ns > -Look up users in the table -.Ar table -instead of performing system lookups using the -.Xr getpwnam 3 -function. -.El -.Pp -You can also accept mail just to have it forwarded elsewhere: -.Bl -tag -width Ds -.It Ic forward-only -Mail is accepted for local recipients ONLY if it is redirected to an -external address via an alias or a ~/.forward file. -.Pp -Example: -.Bd -literal -offset indent -accept for domain opensmtpd.org forward-only -.Ed -.El -.Pp -Finally, the method of delivery is specified: -.Bl -tag -width Ds -.It Xo -.Ic deliver to lmtp -.Op Ar host : Ns Ar port | socket -.Op Ic rcpt-to -.Op Ic as Ar user -.Xc -Mail is delivered to -.Ar host : Ns Ar port , -or to the -.Ux -.Ar socket -over LMTP with the privileges of the specified -.Ar user . -.Pp -Optionally, -.Ic rcpt-to -might be specified to use the recipient email address (after expansion) instead -of the local user in the LMTP session as RCPT TO. -.It Ic deliver to maildir Op Ar path -Mail is added to a maildir. -Its location, -.Ar path , -may contain format specifiers that are expanded before use -.Pq see Sx FORMAT SPECIFIERS . -If -.Ar path -is not provided, then -.Pa ~/Maildir -is assumed. -.It Ic deliver to mbox -Mail is delivered to the local user's system mailbox in -.Pa /var/mail . -.It Ic deliver to mda Ar program Op Ic as Ar user -Mail is piped to the specified -.Ar program , -which is run with the privileges of the specified -.Ar user -or the user the message is destined to. -This parameter may use conversion specifiers that are expanded before use -.Pq see Sx FORMAT SPECIFIERS . -.It Xo -.Bk -words -.Ic relay -.Op Ic backup Op Ar mx -.Op Ic as Ar address -.Op Ic source No < Ns Ar source Ns > -.Op Ic hostname Ar name -.Op Ic hostnames No < Ns Ar names Ns > -.Op Ic pki Ar pkiname -.Op Ic tls Op Ic verify -.Ek -.Xc -.Pp -Mail is relayed. -The routing decision is based on the DNS system. -.Pp -If the -.Ic backup -parameter is specified, the current server will act as a backup server -for the target domain. -Accepted mails are only relayed through servers with a lower preference -value in the MX record for the domain than the one specified in -.Ar mx . -If -.Ar mx -is not specified, the default server name will be assumed. -.Pp -If the -.Ic as -parameter is specified, -.Xr smtpd 8 -will rewrite the sender advertised -in the SMTP session. -.Ar address -may be a user, a domain prefixed with -.Sq @ , -or an email address, causing -.Xr smtpd 8 -to rewrite the user-part, the domain-part, or the entire address, respectively. -.Pp -If the -.Ic source -parameter is specified, -.Xr smtpd 8 -will explicitly bind to an address found in the table referenced by -.Ar source -when connecting to the relay. -If the table contains more than one address, they are picked in turn each -time a new connection is opened. -.Pp -By default, when connecting to a remote server, -.Xr smtpd 8 -advertises its default server name. -A -.Ic hostname -parameter may be specified to advertise the alternate hostname -.Ar name . -If the -.Ic source -parameter is used, the -.Ic hostnames -parameter may be specified to advertise a hostname based on -the source address. -Table -.Ar names -contains a mapping of IP addresses to hostnames and -.Xr smtpd 8 -will automatically select the name that matches its source address -when connected to the remote server. -The -.Ic hostname -and -.Ic hostnames -parameters are mutually exclusive. -.Pp -When relaying, STARTTLS is always attempted if available on remote host and -.Xr smtpd 8 -will try to present a certificate matching the outgoing hostname if one is -registered in the pki. -If -.Ic pki -is specified, the certificate registered for -.Ar pkiname -is used instead. -.Pp -If -.Ic tls -is specified, -.Xr smtpd 8 -will refuse to relay unless the remote host provides STARTTLS. -If -.Ic tls verify -is specified, -.Xr smtpd 8 -will refuse to relay unless the remote host provides STARTTLS and the -certificate it presented has been verified. -.Pp -Note that the -.Ic tls -and -.Ic tls verify -options should only be used in private networks -as they will prevent proper relaying on the Internet. -.It Xo -.Ic relay via -.Ar host -.Op Ic auth No < Ns Ar auth Ns > -.Op Ic as Ar address -.Op Ic source No < Ns Ar source Ns > -.Op Ic hostname Ar name -.Op Ic hostnames No < Ns Ar names Ns > -.Op Ic pki Ar pkiname -.Op Ic verify +.Ic from src +.Ar address | Pf < Ar address Ns > .Xc -.Pp -Mail is relayed through the specified -.Ar host -expressed as a URL. -For example: -.Bd -literal -offset indent -smtp://mx1.example.org # use SMTP -smtp://mx1.example.org:4321 # use SMTP \e - # with port 4321 -lmtp://localhost:2026 # use LMTP \e - # with port 2026 -.Ed -.Pp -The communication channel may be secured using one of the secure -schemas. -For example: -.Bd -literal -offset indent -tls://mx1.example.org # use TLS -smtps://mx1.example.org # use SMTPS -secure://mx1.example.org # try SMTPS and \e - # fallback to TLS -.Ed -.Pp -In addition, credentials for authenticated relaying may be provided -when using a secure schema. -For example: -.Bd -literal -offset indent -tls+auth://label@mx.example.org # over TLS -smtps+auth://label@mx.example.org # over SMTPS -secure+auth://label@mx.example.org # over either \e - # SMTPS or TLS -.Ed -.Pp -If a pki entry exists for the outgoing hostname, or one is provided -with -.Ar pkiname , -the associated certificate will be sent to the remote server. -.Pp -If an SMTPAUTH session with -.Ar host -is desired, the -.Ic auth -parameter is used to specify the -.Ar auth -table that holds the credentials. -Credentials will be looked up using the label provided in the URL. -.Pp -If the -.Ic as -parameter is specified, -.Xr smtpd 8 -will rewrite the sender advertised -in the SMTP session. +Specify that session may only originate from string or list table .Ar address -may be a user, a domain prefixed with -.Sq @ , -or an email address, causing -.Xr smtpd 8 -to rewrite the user-part, the domain-part, or the entire address, respectively. -.Pp -If the -.Ic source -parameter is specified, -.Xr smtpd 8 -will explicitly bind to an address found in the table referenced by -.Pf < Ar source Ns > -when connecting to the relay. -If the table contains more than one address, they are picked in turn each -time a new connection is opened. -.Pp -By default, when connecting to a remote server, -.Xr smtpd 8 -advertises its default server name. -A -.Ic hostname -parameter may be specified to advertise the alternate hostname -.Ar name . -If the -.Ic source -parameter is used, the -.Ic hostnames -parameter may be specified to advertise a hostname based on -the source address. -Table -.Ar names -contains a mapping of IP addresses to hostnames and -.Xr smtpd 8 -will automatically select the name that matches its source address -when connected to the remote server. -The -.Ic hostname -and -.Ic hostnames -parameters are mutually exclusive. -.Pp -If -.Ic verify -is specified, -.Xr smtpd 8 -will refuse to relay unless the remote host provides STARTTLS and the -certificate it presented has been verified. -The relay URL must specify TLS for this option to be valid. +which can be a specific address or a subnet expressed in CIDR-notation. .El .Pp -Additional per-rule adjustments are available: +In addition, the following transaction options: .Bl -tag -width Ds .It Xo -.Ic expire -.Sm off -.Ar n -.Brq Cm s | m | h | d -.Sm on -.Xc -Specify how long a message that matched this rule can stay in the queue. -.El -.It Xo -.Ic bounce-warn -.Sm off -.Ar n -.Brq Cm s | m | h | d -.Oo , -.Sm on -.Ar ... -.Oc -.Xc -Specify the delays for which temporary failure reports must be generated -when messages are stuck in the queue. -For example: -.Bd -literal -offset indent -bounce-warn 1h, 6h, 2d -.Ed -.Pp -will generate a failure report when an envelope is in the queue for more -than one hour, six hours and two days. -The default is 4h. -.It Ic ca Ar hostname Ic certificate Ar cafile -Associate a custom CA certificate located in -.Ar cafile -with -.Ar hostname . -.It Ic ciphers Ar cipher-list -Specify an alternate list of ciphers to use when establishing TLS sessions. -It is highly recommended to avoid making use of this option unless there -is a good understanding of the implications. -.Pp -When not specified, only ciphers considered safe are chosen. -.It Xo -.Ic expire -.Sm off -.Ar n -.Brq Cm s | m | h | d -.Sm on +.Op Ic \&! +.Ic auth .Xc -Specify how long a message can stay in the queue. -The default value is 4d. -For example: -.Bd -literal -offset indent -expire 4d # expire after 4 days -expire 10h # expire after 10 hours -.Ed +Specify that transaction should be authenticated. .It Xo -.Ic limit session -.Brq Cm max-rcpt | max-mails -.Ar num +.Op Ic \&! +.Ic helo +.Ar helo-name | Pf < Ar helo-name Ns > .Xc -Instruct -.Xr smtpd 8 -to accept a maximum number of recipients or emails at once -in the receiving queue. -Defaults are 100 for -.Ic max-mails -and 1000 for -.Ic max-rcpt . +Specify that session's HELO / EHLO should match the string or list table +.Ar helo-name . .It Xo -.Ic limit mta -.Op Ic for Ic domain Ar domain -.Ar family +.Op Ic \&! +.Ic mail-from +.Ar sender | Pf < Ar sender Ns > .Xc -Instruct -.Xr smtpd 8 -to only use the specified address -.Ar family -for outgoing connections. -Accepted values are -.Ic inet4 -and -.Ic inet6 . -If a -.Ar domain -is specified, the restriction only applies when connecting -to MXs for this domain. -.It Ic limit scheduler max-inflight Ar num -Suspend the scheduling of envelopes for deliver/relay until the number -of inflight envelopes falls below -.Ar num . -Changing the default value might degrade performance. +Specify that transactions's MAIL FROM should match the string or list table +.Ar sender . .It Xo -.Bk -words -.Ic listen on Ar interface -.Op Ar family -.Op Ic port Ar port -.Op Ic tls | tls-require | tls-require verify | smtps -.Op Ic pki Ar pkiname -.Op Ic ca Ar caname -.Op Ic auth | auth-optional Op < Ns Ar authtable Ns > -.Op Ic tag Ar tag -.Op Ic hostname Ar hostname -.Op Ic hostnames No < Ns Ar names Ns > -.Op Ic senders No < Ns Ar users Ns > Op Cm masquerade -.Op Ic mask-source -.Op Ic received-auth -.Op Ic no-dsn -.Ek +.Op Ic \&! +.Ic rcpt-to +.Ar recipient | Pf < Ar recipient Ns > .Xc -Specify an -.Ar interface -and optional -.Ar port -to listen on for incoming connections. -An interface group, an IP address or a domain name may -be used in place of -.Ar interface . -The -.Ar family -parameter can be used to listen only on specific address family. -Accepted values are -.Ic inet4 -and -.Ic inet6 . -.Pp -Secured connections are provided either using STARTTLS -.Pq Ic tls , -by default on port 25, -or SMTPS -.Pq Ic smtps , -by default on port 465. -.Ic tls-require -may be used to force clients to establish a secure connection -before being allowed to start an SMTP transaction. -.Pp -If -.Ic tls-require verify -is specified, the client must provide a valid certificate to be -able to establish an SMTP session. -.Pp -Host certificates may be used for these connections, -and must be previously declared using the pki directive. -If -.Ic pki -is specified, -a certificate matching -.Ic name -is searched for. -Moreover, a previously declared -.Ic ca -directive may be specified to use a custom CA certificate. -.Pp -If the -.Ic auth -parameter is used, -then a client may only start an SMTP transaction after a -successful authentication. -Any remote sender that passed SMTPAUTH is treated as if -it was the server's local user that was sending the mail. -This means that filter rules using -.Ic from local -will be matched. -If -.Ic auth-optional -is specified, then SMTPAUTH is not required to establish an -SMTP transaction. -This is only useful to let a listener accept incoming mail from -untrusted senders and outgoing mail from authenticated users in -situations where it is not possible to listen on the submission -port. -.Pp -Both -.Ic auth -and -.Ic auth-optional -accept an optional table as a parameter. -When provided, credentials are looked up in this table. -The credentials format is described in -.Xr table 5 . -.Pp -If the -.Ic tag -parameter is used, then clients connecting to the listener will be -tagged -.Ar tag . -.Pp -If the -.Ic hostname -parameter is used, then it will be used in the greeting banner -instead of the default server name. -.Pp -The -.Ic hostnames -parameter overrides the server name for specific addresses. -Table -.Ar names -contains a mapping of IP addresses to hostnames and -.Xr smtpd 8 -will use the hostname that matches the address on which the connection arrives -if it is found in the mapping. -.Pp -If the -.Ic senders -parameter is used, then -.Xr smtpd 8 -will look up a mapping of username to email addresses to see whether -the authenticated user is allowed to submit mail -as the sender that was provided in the SMTP session. -In addition, if the -.Cm masquerade -option is provided, -the From header will be rewritten -to match the sender provided in the SMTP session. -.Pp -If the -.Ic mask-source -parameter is used, then the listener will skip the -.Ic from -part when prepending the -.Dq Received -header. -.Pp -If the -.Ic received-auth -parameter is used, the -.Dq Received -header will display if the session was authenticated and by which local user. -.Pp -If the -.Ic no-dsn -parameter is used, DSN (Delivery Status Notification) extension will not -be enabled. +Specify that transaction's RCPT TO should match the string or list table +.Ar recipient . .It Xo -.Ic listen on socket -.Op Ic mask-source +.Op Ic \&! +.Ic tls .Xc -Modify behaviour for the listener which handles messages -submitted through the local enqueuer, -such as the -.Xr mail 1 -utility. -Clients connecting in this manner are tagged with the "local" -.Ic tag . -.Pp -Parameters available are: -.Bl -tag -width "mask-source" -.It Ic mask-source -Skip the -.Ic from -part when prepending the -.Dq Received -header. +Specify that transaction should take place in a TLS channel. .El -.It Ic max-message-size Ar n -Specify a maximum message size of -.Ar n -bytes. -The argument may contain a multiplier, as documented in -.Xr scan_scaled 3 . -The default maximum message size is 35MB if none is specified. -.It Ic pki Ar hostname Ic certificate Ar certfile +.It Ic match Ar options Ic reject +Reject the incoming message during the SMTP dialogue. +The same +.Ar options +are supported as for the +.Ic match action +directive. +.It Ic pki Ar hostname Ic cert Ar certfile Associate the certificate located in .Ar certfile with @@ -851,58 +461,76 @@ Valid parameter values are none, legacy and auto. For legacy a fixed key length of 1024 bits is used, whereas for auto the key length is determined automatically. The default is none, which disables DHE cipher suites. -.It Ic queue compression -Enable transparent compression of envelopes and messages. -The only supported algorithm at the moment is gzip. -Envelopes and messages may be inspected using the -.Xr smtpctl 8 +.It Ic set bounce warn-interval Ar delay Op , Ar delay ... +Send warning messages to the envelope sender when temporary delivery +failures cause a message to remain on the queue for longer than +.Ar delay . +Each +.Ar delay +parameter consists of a positive decimal integer and a unit +.Cm s , m , h , or -.Xr gzcat 1 -utilities. -.It Ic queue encryption Op key Ar key -Enable transparent encryption of envelopes and messages. +.Cm d . +At most four +.Ar delay +parameters can be specified. +The default is +.Qq Ic set bounce warn-interval No 4h , +sending a single warning after four hours. +.It Ic set mta max-deferred Ar number +When delivery to a given host is suspended due to temporary failures, +cache at most +.Ar number +envelopes for that host such that they can be delivered +as soon as another delivery succeeds to that host. +The default is 100. +.It Ic set queue compression +Store queue files in a compressed format. +This may be useful to save disk space. +.It Ic set queue encryption Op Ar key +Encrypt queue files with +.Xr EVP_aes_256_gcm 3 . +If no .Ar key -must be a 16-byte random key in hexadecimal representation. -It can be obtained using the -.Xr openssl 1 -utility as follow: -.Bd -literal -offset indent -$ openssl rand \-hex 16 -.Ed -.Pp -If the -.Ar key -parameter is not specified, it is read with -.Xr getpass 3 -at startup. -If -.Ar key -is -.Ic stdin , -then it is read from the standard input at startup. -.Pp -The only supported algorithm is AES-256 in GCM mode. -Envelopes and messages may be inspected using the -.Xr smtpctl 8 -utility. -.Pp -Queue encryption can be used with queue compression and will always -perform compression before encryption. -.It Ic subaddressing-delimiter Ar delimiter -Redefine the subaddressing delimiter from the default -.Sq + -to -.Ar delimiter . -.Pp -Any printable character valid in an email address is allowed, -except spaces and -.Sq @ . -.Pp -The first character in the user-part of an email address that matches -.Ar delimiter -is considered to be the subaddressing delimiter. -.It Ic table Ar name Oo Ar type : Oc Ns Ar config -Tables are used to provide additional configuration information for +is specified, it is read with +.Xr getpass 3 . +If the string +.Cm stdin +or a single dash +.Pq Ql \- +is given instead of a +.Ar key , +the key is read from the standard input. +.It Ic set queue ttl Ar delay +Set the default expiration time for temporarily undeliverable +messages, given as a positive decimal integer followed by a unit +.Cm s , m , h , +or +.Cm d . +The default is four days +.Pq 4d . +.It Ic set smtp ciphers Ar control +Set the +.Ar control +string for +.Xr SSL_CTX_set_cipher_list 3 . +The default is +.Qq HIGH:!aNULL:!MD5 . +.It Ic set smtp max-message-size Ar size +Reject messages larger than +.Ar size , +given as a positive number of bytes or as a string to be parsed with +.Xr scan_scaled 3 . +The default is +.Qq 35M . +.It Ic set smtp sub-addr-delim Ar character +When resolving the local part of a local email address, ignore the ASCII +.Ar character +and all characters following it. +The default is +.Ql + . +.It Ic table Ar name Oo Ar type : Oc Ns Ar pathname +Tables provide additional configuration information for .Xr smtpd 8 in the form of lists or key-value mappings. The format of the entries depends on what the table is used for. @@ -910,53 +538,36 @@ Refer to .Xr table 5 for the exhaustive documentation. .Pp -The table is identified using table name -.Ar name ; -the name itself is arbitrarily chosen. +Each table is identified by an arbitrary, unique +.Ar name . .Pp +If the .Ar type -specifies the table backend, -and should be one of the following: -.Pp -.Bl -tag -width "fileXXX" -compact -.It db -Information is stored in a file created using -.Xr makemap 8 . -.It file -Information is stored in a plain text file using the -same format as used to generate -.Xr makemap 8 -mappings. -This is the default. -.El -.Pp -.Ar config -specifies a configuration file for the table data. -It must be an absolute path to a file for the -.Dq file -and -.Dq db -table types. +is +.Cm db , +information is stored in a file created with +.Xr makemap 8 ; +if it is +.Cm file +or omitted, information is stored in a plain text file +using the format described in +.Xr table 5 . +The +.Ar pathname +to the file must be absolute. .It Ic table Ar name Brq Ar value Op , Ar ... -Tables containing list of static values may be declared -using an inlined notation. -.Pp -The table is identified using table name -.Ar name ; -the name itself is arbitrarily chosen. -.Pp +Instead of using a separate file, declare a list table +containing the given static +.Ar value Ns s . The table must contain at least one value and may declare many values as a list of comma-separated strings. .It Ic table Ar name Brq Ar key Ns = Ns Ar value Op , Ar ... -Tables containing static key-value mappings may be declared -using an inlined notation. -.Pp -The table is identified using table name -.Ar name ; -the name itself is arbitrarily chosen. -.Pp -The table must contain at least one key-value mapping and may declare -many mappings as a list of comma-separated +Instead of using a separate file, declare a mapping table +containing the given static +.Ar key Ns - Ns Ar value +pairs. +The table must contain at least one key-value pair and may declare +many pairs as a list of comma-separated .Ar key Ns = Ns Ar value descriptions. .El @@ -965,7 +576,7 @@ Some configuration directives support expansion of their parameters at runtime. Such directives (for example .Ic deliver to maildir , .Ic deliver to mda ) -may use format specifiers which will be expanded before delivery or +may use format specifiers which are expanded before delivery or relaying. The following formats are currently supported: .Bl -column %{user.directory} -offset indent @@ -1031,6 +642,8 @@ Otherwise, the server name is derived from the local hostname returned by either directly if it is a fully qualified domain name, or by retrieving the associated canonical name through .Xr getaddrinfo 3 . +.It Pa /var/run/smtpd.sock +Unix domain socket for incoming SMTP connections. .It Pa /var/spool/smtpd/ Spool directories for mail during processing. .El @@ -1057,14 +670,17 @@ A secrets file is needed to specify a username and password: .Nm would look like this: .Bd -literal -offset indent -table aliases file:/etc/mail/aliases -table secrets file:/etc/mail/secrets +table "aliases" file:/etc/mail/aliases +table "secrets" file:/etc/mail/secrets listen on lo0 -accept for local alias <aliases> deliver to mbox -accept for any relay via tls+auth://label@smtp.example.com \e +action "local" mbox alias <aliases> +action "relay" relay host "tls+auth://label@smtp.example.com" \e auth <secrets> + +match for local action "local" +match for any action "relay" .Ed .Pp In this second example, @@ -1086,18 +702,21 @@ In the example above, a certificate valid for one year was created. The configuration file would look like this: .Bd -literal -offset indent -pki mail.example.com certificate "/etc/ssl/mail.example.com.crt" -pki mail.example.com key "/etc/ssl/private/mail.example.com.key" +pki "mail.example.com" cert "/etc/ssl/mail.example.com.crt" +pki "mail.example.com" key "/etc/ssl/private/mail.example.com.key" -table aliases file:/etc/mail/aliases +table "aliases" file:/etc/mail/aliases listen on lo0 -listen on egress tls pki mail.example.com auth +listen on egress tls pki "mail.example.com" auth + +action "mda_with_aliases" mda "/path/to/mda \-f \-" alias <aliases> +action "mda_without_aliases" mda "/path/to/mda \-f \-" +actuin "relay" relay -accept for local alias <aliases> deliver to mda "/path/to/mda \-f \-" -accept from any for domain example.com \e - deliver to mda "/path/to/mda \-f \-" -accept for any relay +match for local action "mda_with_aliases" +match from any for domain "example.com" action "mda_without_aliases" +match for any relay .Ed .Pp For sites that wish to sign messages using DKIM, the @@ -1108,35 +727,42 @@ but all outgoing mail is passed to dkimproxy_out on port 10027 for signing. The signed messages are received on port 10028 and tagged for relaying. .Bd -literal -offset indent -table aliases file:/etc/mail/aliases +table "aliases" file:/etc/mail/aliases listen on lo0 -listen on lo0 port 10028 tag DKIM +listen on lo0 port 10028 tag "DKIM" -accept for local alias <aliases> deliver to mbox -accept tagged DKIM for any relay -accept from local for any relay via smtp://127.0.0.1:10027 +action "mbox" mbox alias <aliases> +action "relay" relay +action "relay_dkim" relay host smtp://127.0.0.1:10027 + +match for local action "mbox" +match tag "DKIM" for any action "relay" +match for any action "relay_dkim" .Ed .Pp Sites that accept non-local messages may be able to cut down on the volume of spam received by rejecting forged messages that claim to be from the local domain. -The table +The following example uses a list table .Em other-relays -can be used to specify the IP addresses of relays that may legitimately -originate mail with your domain as the sender. +to specify the IP addresses of relays that may legitimately +originate mail with the owner's domain as the sender. .Bd -literal -offset indent -table aliases file:/etc/mail/aliases -table other-relays file:/etc/mail/other-relays +table "aliases" file:/etc/mail/aliases +table "other-relays" file:/etc/mail/other-relays listen on lo0 listen on egress -accept for local alias <aliases> deliver to mbox -accept from local for any relay -reject from ! source <other-relays> sender "@example.com" for any -accept from any for domain example.com \e - alias <aliases> deliver to mbox +action "mbox" mbox alias <aliases> +action "relay" relay + +match for local action "mbox" +match for any action "relay" +match !from src <other-relays> mail-from "@example.com" for any \e + reject +match from any for domain example.com action "mbox" .Ed .Sh SEE ALSO .Xr mailer.conf 5 , diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 10184c10cfa..d884272a029 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.540 2018/05/14 15:23:05 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.541 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -158,6 +158,7 @@ union lookup { struct mailaddr mailaddr; struct addrname addrname; struct maddrmap *maddrmap; + struct relayhost relayhost; }; /* @@ -270,6 +271,7 @@ enum imsg_type { IMSG_MTA_LOOKUP_CREDENTIALS, IMSG_MTA_LOOKUP_SOURCE, IMSG_MTA_LOOKUP_HELO, + IMSG_MTA_LOOKUP_SMARTHOST, IMSG_MTA_OPEN_MESSAGE, IMSG_MTA_SCHEDULE, IMSG_MTA_TLS_INIT, @@ -355,72 +357,6 @@ struct table_backend { }; -enum dest_type { - DEST_DOM, - DEST_VDOM -}; - -enum action_type { - A_NONE, - A_RELAY, - A_RELAYVIA, - A_MAILDIR, - A_MBOX, - A_FILENAME, - A_MDA, - A_LMTP -}; - -enum decision { - R_REJECT, - R_ACCEPT -}; - -struct rule { - uint64_t r_id; - TAILQ_ENTRY(rule) r_entry; - enum decision r_decision; - uint8_t r_nottag; - char r_tag[SMTPD_TAG_SIZE]; - - uint8_t r_notsources; - struct table *r_sources; - - uint8_t r_notsenders; - struct table *r_senders; - - uint8_t r_notrecipients; - struct table *r_recipients; - - uint8_t r_notdestination; - enum dest_type r_desttype; - struct table *r_destination; - - uint8_t r_wantauth; - uint8_t r_negwantauth; - - enum action_type r_action; - union rule_dest { - char buffer[EXPAND_BUFFER]; - struct relayhost relayhost; - } r_value; - - struct mailaddr *r_as; - struct table *r_mapping; - struct table *r_userbase; - time_t r_qexpire; - uint8_t r_forwardonly; - char r_delivery_user[LINE_MAX]; -}; - -struct delivery_mda { - enum action_type method; - char usertable[SMTPD_TABLENAME_SIZE]; - char username[SMTPD_VUSERNAME_SIZE]; - char buffer[EXPAND_BUFFER]; - char delivery_user[SMTPD_VUSERNAME_SIZE]; -}; - struct delivery_mta { struct relayhost relay; }; @@ -439,7 +375,7 @@ enum dsn_ret { struct delivery_bounce { enum bounce_type type; time_t delay; - time_t expire; + time_t ttl; enum dsn_ret dsn_ret; int mta_without_dsn; }; @@ -452,7 +388,6 @@ enum expand_type { EXPAND_INCLUDE, EXPAND_ADDRESS, EXPAND_ERROR, - EXPAND_MAILDIR }; struct expandnode { @@ -460,12 +395,11 @@ struct expandnode { TAILQ_ENTRY(expandnode) tq_entry; enum expand_type type; int sameuser; - int alias; + int realuser; + int forwarded; struct rule *rule; struct expandnode *parent; unsigned int depth; - struct table *mapping; - struct table *userbase; union { /* * user field handles both expansion user and system user @@ -480,7 +414,6 @@ struct expandnode { struct expand { RB_HEAD(expandtree, expandnode) tree; TAILQ_HEAD(xnodes, expandnode) *queue; - int alias; size_t nb_nodes; struct rule *rule; struct expandnode *parent; @@ -502,10 +435,12 @@ struct maddrmap { #define DSN_ENVID_LEN 100 -#define SMTPD_ENVELOPE_VERSION 2 +#define SMTPD_ENVELOPE_VERSION 3 struct envelope { TAILQ_ENTRY(envelope) entry; + char dispatcher[HOST_NAME_MAX+1]; + char tag[SMTPD_TAG_SIZE]; uint32_t version; @@ -522,16 +457,18 @@ struct envelope { struct mailaddr rcpt; struct mailaddr dest; + char mda_user[SMTPD_VUSERNAME_SIZE]; + char mda_exec[LINE_MAX]; + enum delivery_type type; union { - struct delivery_mda mda; struct delivery_mta mta; struct delivery_bounce bounce; } agent; uint16_t retry; time_t creation; - time_t expire; + time_t ttl; time_t lasttry; time_t nexttry; time_t lastbounce; @@ -607,7 +544,7 @@ struct smtpd { size_t sc_scheduler_max_msg_batch_size; size_t sc_scheduler_max_schedule; - int sc_qexpire; + int sc_ttl; #define MAX_BOUNCE_WARN 4 time_t sc_bounce_warn[MAX_BOUNCE_WARN]; char sc_hostname[HOST_NAME_MAX+1]; @@ -622,6 +559,8 @@ struct smtpd { TAILQ_HEAD(listenerlist, listener) *sc_listeners; TAILQ_HEAD(rulelist, rule) *sc_rules; + struct dict *sc_dispatchers; + struct dispatcher *sc_dispatcher_bounce; struct dict *sc_ca_dict; struct dict *sc_pki_dict; @@ -667,11 +606,13 @@ struct forward_req { }; struct deliver { - char to[EXPAND_BUFFER]; - char from[SMTPD_MAXMAILADDRSIZE]; - char dest[SMTPD_MAXMAILADDRSIZE]; - char user[SMTPD_VUSERNAME_SIZE]; - short mode; + char dispatcher[EXPAND_BUFFER]; + + struct mailaddr sender; + struct mailaddr rcpt; + struct mailaddr dest; + + char mda_exec[LINE_MAX]; struct userinfo userinfo; }; @@ -800,6 +741,7 @@ struct mta_relay { SPLAY_ENTRY(mta_relay) entry; uint64_t id; + struct dispatcher *dispatcher; struct mta_domain *domain; struct mta_limits *limits; int flags; @@ -833,7 +775,8 @@ struct mta_relay { #define RELAY_WAIT_LIMITS 0x08 #define RELAY_WAIT_SOURCE 0x10 #define RELAY_WAIT_CONNECTOR 0x20 -#define RELAY_WAITMASK 0x3f +#define RELAY_WAIT_SMARTHOST 0x40 +#define RELAY_WAITMASK 0x7f int status; int refcount; @@ -847,6 +790,7 @@ struct mta_envelope { uint64_t id; uint64_t session; time_t creation; + char *smtpname; char *dest; char *rcpt; struct mta_task *task; @@ -892,13 +836,6 @@ struct auth_backend { int (*authenticate)(char *, char *); }; - -/* delivery_backend */ -struct delivery_backend { - int allow_root; - void (*open)(struct deliver *); -}; - struct scheduler_backend { int (*init)(const char *); @@ -1096,6 +1033,90 @@ struct msg_walkinfo { int done; }; + +enum dispatcher_type { + DISPATCHER_LOCAL, + DISPATCHER_REMOTE, + DISPATCHER_BOUNCE, +}; + +struct dispatcher_local { + uint8_t requires_root; /* only for MBOX */ + + uint8_t expand_only; + uint8_t forward_only; + + char *command; + + char *table_alias; + char *table_virtual; + char *table_userbase; + + char *user; +}; + +struct dispatcher_remote { + char *helo; + char *helo_source; + + char *source; + + char *ca; + char *pki; + + char *mail_from; + + char *smarthost; + char *auth; + + int backup; + char *backupmx; +}; + +struct dispatcher_bounce { +}; + +struct dispatcher { + enum dispatcher_type type; + union dispatcher_agent { + struct dispatcher_local local; + struct dispatcher_remote remote; + struct dispatcher_bounce bounce; + } u; + + time_t ttl; +}; + +struct rule { + TAILQ_ENTRY(rule) r_entry; + + uint8_t reject; + + int8_t flag_tag; + int8_t flag_from; + int8_t flag_for; + int8_t flag_from_socket; + + int8_t flag_smtp_helo; + int8_t flag_smtp_starttls; + int8_t flag_smtp_auth; + int8_t flag_smtp_mail_from; + int8_t flag_smtp_rcpt_to; + + + char *table_tag; + char *table_from; + char *table_for; + + char *table_smtp_helo; + char *table_smtp_auth; + char *table_smtp_mail_from; + char *table_smtp_rcpt_to; + + char *dispatcher; +}; + + /* aliases.c */ int aliases_get(struct expand *, const char *); int aliases_virtual_get(struct expand *, const struct mailaddr *); @@ -1131,6 +1152,7 @@ int uncompress_file(FILE *, FILE *); #define PURGE_RULES 0x04 #define PURGE_PKI 0x08 #define PURGE_PKI_KEYS 0x10 +#define PURGE_DISPATCHERS 0x20 #define PURGE_EVERYTHING 0xff void purge_config(uint8_t); void config_process(enum smtp_proc_type); @@ -1150,10 +1172,6 @@ size_t crypto_encrypt_buffer(const char *, size_t, char *, size_t); size_t crypto_decrypt_buffer(const char *, size_t, char *, size_t); -/* delivery.c */ -struct delivery_backend *delivery_backend_lookup(enum action_type); - - /* dns.c */ void dns_imsg(struct mproc *, struct imsg *); @@ -1221,7 +1239,7 @@ void mda_imsg(struct mproc *, struct imsg *); /* mda_variables.c */ -size_t mda_expand_format(char *, size_t, const struct envelope *, +size_t mda_expand_format(char *, size_t, const struct deliver *, const struct userinfo *); diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index b193c34e17e..135c3e18348 100644 --- a/usr.sbin/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.89 2018/01/06 07:57:53 sunil Exp $ +# $OpenBSD: Makefile,v 1.90 2018/05/24 11:38:24 gilles Exp $ .PATH: ${.CURDIR}/.. @@ -11,7 +11,6 @@ SRCS+= compress_backend.c SRCS+= config.c SRCS+= control.c SRCS+= crypto.c -SRCS+= delivery.c SRCS+= dict.c SRCS+= dns.c SRCS+= unpack_dns.c @@ -58,12 +57,6 @@ SRCS+= rfc2822.c # backends SRCS+= compress_gzip.c -SRCS+= delivery_filename.c -SRCS+= delivery_maildir.c -SRCS+= delivery_mbox.c -SRCS+= delivery_mda.c -SRCS+= delivery_lmtp.c - SRCS+= table_db.c SRCS+= table_getpwnam.c SRCS+= table_proc.c diff --git a/usr.sbin/smtpd/table.c b/usr.sbin/smtpd/table.c index afb71788feb..ca4318e7bb6 100644 --- a/usr.sbin/smtpd/table.c +++ b/usr.sbin/smtpd/table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: table.c,v 1.24 2017/05/01 09:29:07 gilles Exp $ */ +/* $OpenBSD: table.c,v 1.25 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -621,6 +621,11 @@ table_parse_lookup(enum table_service service, const char *key, return (-1); return (1); + case K_RELAYHOST: + if (!text_to_relayhost(&lk->relayhost, line)) + return (-1); + return (1); + default: return (-1); } @@ -693,6 +698,13 @@ table_dump_lookup(enum table_service s, union lookup *lk) goto err; break; + case K_RELAYHOST: + ret = snprintf(buf, sizeof(buf), "%s", + relayhost_to_text(&lk->relayhost)); + if (ret == -1 || (size_t)ret >= sizeof (buf)) + goto err; + break; + default: (void)strlcpy(buf, "???", sizeof(buf)); break; diff --git a/usr.sbin/smtpd/table_static.c b/usr.sbin/smtpd/table_static.c index facb0e4a677..ded0321ae9c 100644 --- a/usr.sbin/smtpd/table_static.c +++ b/usr.sbin/smtpd/table_static.c @@ -1,4 +1,4 @@ -/* $OpenBSD: table_static.c,v 1.17 2017/08/29 07:37:11 eric Exp $ */ +/* $OpenBSD: table_static.c,v 1.18 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -50,7 +50,8 @@ static void table_static_close(void *); struct table_backend table_backend_static = { K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO| - K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP, + K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST| + K_STRING, table_static_config, table_static_open, table_static_update, diff --git a/usr.sbin/smtpd/to.c b/usr.sbin/smtpd/to.c index 14aea652ef4..de9ec657f58 100644 --- a/usr.sbin/smtpd/to.c +++ b/usr.sbin/smtpd/to.c @@ -1,4 +1,4 @@ -/* $OpenBSD: to.c,v 1.28 2016/05/30 12:33:44 mpi Exp $ */ +/* $OpenBSD: to.c,v 1.29 2018/05/24 11:38:24 gilles Exp $ */ /* * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> @@ -50,7 +50,6 @@ #include "log.h" static const char *in6addr_to_text(const struct in6_addr *); -static int alias_is_maildir(struct expandnode *, const char *, size_t); static int alias_is_filter(struct expandnode *, const char *, size_t); static int alias_is_username(struct expandnode *, const char *, size_t); static int alias_is_address(struct expandnode *, const char *, size_t); @@ -319,9 +318,7 @@ text_to_relayhost(struct relayhost *relay, const char *s) { "smtps+auth://", F_SMTPS|F_AUTH }, { "tls+auth://", F_STARTTLS|F_AUTH }, { "secure://", F_SMTPS|F_STARTTLS }, - { "secure+auth://", F_SMTPS|F_STARTTLS|F_AUTH }, - { "backup://", F_BACKUP }, - { "tls+backup://", F_BACKUP|F_STARTTLS } + { "secure+auth://", F_SMTPS|F_STARTTLS|F_AUTH } }; const char *errstr = NULL; char *p, *q; @@ -505,85 +502,92 @@ rule_to_text(struct rule *r) static char buf[4096]; memset(buf, 0, sizeof buf); - (void)strlcpy(buf, r->r_decision == R_ACCEPT ? "accept" : "reject", sizeof buf); - if (r->r_tag[0]) { - (void)strlcat(buf, " tagged ", sizeof buf); - if (r->r_nottag) - (void)strlcat(buf, "! ", sizeof buf); - (void)strlcat(buf, r->r_tag, sizeof buf); - } - (void)strlcat(buf, " from ", sizeof buf); - if (r->r_notsources) - (void)strlcat(buf, "! ", sizeof buf); - (void)strlcat(buf, r->r_sources->t_name, sizeof buf); - - (void)strlcat(buf, " for ", sizeof buf); - if (r->r_notdestination) - (void)strlcat(buf, "! ", sizeof buf); - switch (r->r_desttype) { - case DEST_DOM: - if (r->r_destination == NULL) { - (void)strlcat(buf, " any", sizeof buf); - break; + (void)strlcpy(buf, "match ", sizeof buf); + if (r->flag_tag) { + if (r->flag_tag < 0) + (void)strlcat(buf, "!", sizeof buf); + (void)strlcat(buf, "tag ", sizeof buf); + (void)strlcat(buf, r->table_tag, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); + } + + if (r->flag_from) { + if (r->flag_from < 0) + (void)strlcat(buf, "!", sizeof buf); + if (strcmp(r->table_from, "<anyhost>") == 0) + (void)strlcat(buf, "from any ", sizeof buf); + else if (strcmp(r->table_from, "<localhost>") == 0) + (void)strlcat(buf, "from local", sizeof buf); + else { + (void)strlcat(buf, "from src ", sizeof buf); + (void)strlcat(buf, r->table_from, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); } - (void)strlcat(buf, " domain ", sizeof buf); - (void)strlcat(buf, r->r_destination->t_name, sizeof buf); - if (r->r_mapping) { - (void)strlcat(buf, " alias ", sizeof buf); - (void)strlcat(buf, r->r_mapping->t_name, sizeof buf); + } + + if (r->flag_for) { + if (r->flag_for < 0) + (void)strlcat(buf, "!", sizeof buf); + if (strcmp(r->table_for, "<anydestination>") == 0) + (void)strlcat(buf, "for any ", sizeof buf); + else if (strcmp(r->table_for, "<localnames>") == 0) + (void)strlcat(buf, "for local ", sizeof buf); + else { + (void)strlcat(buf, "for domain ", sizeof buf); + (void)strlcat(buf, r->table_for, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); } - break; - case DEST_VDOM: - if (r->r_destination == NULL) { - (void)strlcat(buf, " any virtual ", sizeof buf); - (void)strlcat(buf, r->r_mapping->t_name, sizeof buf); - break; + } + + if (r->flag_smtp_helo) { + if (r->flag_smtp_helo < 0) + (void)strlcat(buf, "!", sizeof buf); + (void)strlcat(buf, "helo ", sizeof buf); + (void)strlcat(buf, r->table_smtp_helo, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); + } + + if (r->flag_smtp_auth) { + if (r->flag_smtp_auth < 0) + (void)strlcat(buf, "!", sizeof buf); + (void)strlcat(buf, "auth ", sizeof buf); + if (r->table_smtp_auth) { + (void)strlcat(buf, r->table_smtp_auth, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); } - (void)strlcat(buf, " domain ", sizeof buf); - (void)strlcat(buf, r->r_destination->t_name, sizeof buf); - (void)strlcat(buf, " virtual ", sizeof buf); - (void)strlcat(buf, r->r_mapping->t_name, sizeof buf); - break; } - switch (r->r_action) { - case A_RELAY: - (void)strlcat(buf, " relay", sizeof buf); - break; - case A_RELAYVIA: - (void)strlcat(buf, " relay via ", sizeof buf); - (void)strlcat(buf, relayhost_to_text(&r->r_value.relayhost), sizeof buf); - break; - case A_MAILDIR: - (void)strlcat(buf, " deliver to maildir \"", sizeof buf); - (void)strlcat(buf, r->r_value.buffer, sizeof buf); - (void)strlcat(buf, "\"", sizeof buf); - break; - case A_MBOX: - (void)strlcat(buf, " deliver to mbox", sizeof buf); - break; - case A_FILENAME: - (void)strlcat(buf, " deliver to filename \"", sizeof buf); - (void)strlcat(buf, r->r_value.buffer, sizeof buf); - (void)strlcat(buf, "\"", sizeof buf); - break; - case A_MDA: - (void)strlcat(buf, " deliver to mda \"", sizeof buf); - (void)strlcat(buf, r->r_value.buffer, sizeof buf); - (void)strlcat(buf, "\"", sizeof buf); - break; - case A_LMTP: - (void)strlcat(buf, " deliver to lmtp \"", sizeof buf); - (void)strlcat(buf, r->r_value.buffer, sizeof buf); - (void)strlcat(buf, "\"", sizeof buf); - break; - case A_NONE: - break; + if (r->flag_smtp_starttls) { + if (r->flag_smtp_starttls < 0) + (void)strlcat(buf, "!", sizeof buf); + (void)strlcat(buf, "starttls ", sizeof buf); + (void)strlcat(buf, " ", sizeof buf); + } + + if (r->flag_smtp_mail_from) { + if (r->flag_smtp_mail_from < 0) + (void)strlcat(buf, "!", sizeof buf); + (void)strlcat(buf, "mail-from ", sizeof buf); + (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); } + if (r->flag_smtp_rcpt_to) { + if (r->flag_smtp_rcpt_to < 0) + (void)strlcat(buf, "!", sizeof buf); + (void)strlcat(buf, "rcpt-to ", sizeof buf); + (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf); + (void)strlcat(buf, " ", sizeof buf); + } + (void)strlcat(buf, "=> ", sizeof buf); + if (r->reject) + (void)strlcat(buf, "reject", sizeof buf); + else + (void)strlcat(buf, r->dispatcher, sizeof buf); return buf; } + int text_to_userinfo(struct userinfo *userinfo, const char *s) { @@ -677,7 +681,6 @@ text_to_expandnode(struct expandnode *expandnode, const char *s) alias_is_filter(expandnode, s, l) || alias_is_filename(expandnode, s, l) || alias_is_address(expandnode, s, l) || - alias_is_maildir(expandnode, s, l) || alias_is_username(expandnode, s, l)) return (1); @@ -692,8 +695,6 @@ expandnode_to_text(struct expandnode *expandnode) case EXPAND_FILENAME: case EXPAND_INCLUDE: case EXPAND_ERROR: - case EXPAND_MAILDIR: - return expandnode->u.buffer; case EXPAND_USERNAME: return expandnode->u.user; case EXPAND_ADDRESS: @@ -705,22 +706,6 @@ expandnode_to_text(struct expandnode *expandnode) return NULL; } -static int -alias_is_maildir(struct expandnode *alias, const char *line, size_t len) -{ - if (strncasecmp("maildir:", line, 8) != 0) - return (0); - - line += 8; - memset(alias, 0, sizeof *alias); - alias->type = EXPAND_MAILDIR; - if (strlcpy(alias->u.buffer, line, - sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) - return (0); - - return (1); -} - /******/ static int alias_is_filter(struct expandnode *alias, const char *line, size_t len) |