summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2018-05-24 11:38:25 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2018-05-24 11:38:25 +0000
commitace804cafcf33d9452774666bb47840ac1899bd0 (patch)
tree1b3ee00426c097326c9b50cc3bc062f20c89b0f9 /usr.sbin/smtpd
parentb22354e5b6362967535adf6c2e0d2c0d978e6ee0 (diff)
switch smtpd to new grammar
ok eric@
Diffstat (limited to 'usr.sbin/smtpd')
-rw-r--r--usr.sbin/smtpd/Makefile4
-rw-r--r--usr.sbin/smtpd/aliases.57
-rw-r--r--usr.sbin/smtpd/aliases.c18
-rw-r--r--usr.sbin/smtpd/bounce.c6
-rw-r--r--usr.sbin/smtpd/ca.c4
-rw-r--r--usr.sbin/smtpd/config.c10
-rw-r--r--usr.sbin/smtpd/delivery.c61
-rw-r--r--usr.sbin/smtpd/delivery_filename.c117
-rw-r--r--usr.sbin/smtpd/delivery_lmtp.c253
-rw-r--r--usr.sbin/smtpd/delivery_maildir.c149
-rw-r--r--usr.sbin/smtpd/delivery_mbox.c67
-rw-r--r--usr.sbin/smtpd/delivery_mda.c61
-rw-r--r--usr.sbin/smtpd/envelope.c142
-rw-r--r--usr.sbin/smtpd/expand.c27
-rw-r--r--usr.sbin/smtpd/lka.c33
-rw-r--r--usr.sbin/smtpd/lka_session.c219
-rw-r--r--usr.sbin/smtpd/mda.c224
-rw-r--r--usr.sbin/smtpd/mda_variables.c38
-rw-r--r--usr.sbin/smtpd/mta.c121
-rw-r--r--usr.sbin/smtpd/parse.y1229
-rw-r--r--usr.sbin/smtpd/pony.c3
-rw-r--r--usr.sbin/smtpd/queue.c15
-rw-r--r--usr.sbin/smtpd/ruleset.c274
-rw-r--r--usr.sbin/smtpd/scheduler.c6
-rw-r--r--usr.sbin/smtpd/scheduler_backend.c23
-rw-r--r--usr.sbin/smtpd/scheduler_ramqueue.c4
-rw-r--r--usr.sbin/smtpd/smtpctl.c4
-rw-r--r--usr.sbin/smtpd/smtpd-api.h6
-rw-r--r--usr.sbin/smtpd/smtpd-defines.h4
-rw-r--r--usr.sbin/smtpd/smtpd.c87
-rw-r--r--usr.sbin/smtpd/smtpd.conf.51290
-rw-r--r--usr.sbin/smtpd/smtpd.h208
-rw-r--r--usr.sbin/smtpd/smtpd/Makefile9
-rw-r--r--usr.sbin/smtpd/table.c14
-rw-r--r--usr.sbin/smtpd/table_static.c5
-rw-r--r--usr.sbin/smtpd/to.c171
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)