summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/smtpd/bounce.c48
-rw-r--r--usr.sbin/smtpd/envelope.c122
-rw-r--r--usr.sbin/smtpd/lka.c30
-rw-r--r--usr.sbin/smtpd/lka_session.c54
-rw-r--r--usr.sbin/smtpd/mta.c4
-rw-r--r--usr.sbin/smtpd/mta_session.c55
-rw-r--r--usr.sbin/smtpd/parse.y1043
-rw-r--r--usr.sbin/smtpd/ruleset.c50
-rw-r--r--usr.sbin/smtpd/smtp.c7
-rw-r--r--usr.sbin/smtpd/smtp_session.c114
-rw-r--r--usr.sbin/smtpd/smtpd.c77
-rw-r--r--usr.sbin/smtpd/smtpd.conf.5322
-rw-r--r--usr.sbin/smtpd/smtpd.h41
-rw-r--r--usr.sbin/smtpd/ssl.c76
-rw-r--r--usr.sbin/smtpd/ssl.h17
-rw-r--r--usr.sbin/smtpd/to.c41
16 files changed, 1348 insertions, 753 deletions
diff --git a/usr.sbin/smtpd/bounce.c b/usr.sbin/smtpd/bounce.c
index 39779b6e17e..0daf1bd2bc9 100644
--- a/usr.sbin/smtpd/bounce.c
+++ b/usr.sbin/smtpd/bounce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bounce.c,v 1.58 2013/10/26 12:27:58 eric Exp $ */
+/* $OpenBSD: bounce.c,v 1.59 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -65,12 +65,14 @@ struct bounce_message {
TAILQ_ENTRY(bounce_message) entry;
uint32_t msgid;
struct delivery_bounce bounce;
+ char *smtpname;
char *to;
time_t timeout;
TAILQ_HEAD(, bounce_envelope) envelopes;
};
struct bounce_session {
+ char *smtpname;
struct bounce_message *msg;
FILE *msgfp;
int state;
@@ -139,19 +141,21 @@ bounce_add(uint64_t evpid)
key.msgid = evpid_to_msgid(evpid);
key.bounce = evp.agent.bounce;
+ key.smtpname = evp.smtpname;
msg = SPLAY_FIND(bounce_message_tree, &messages, &key);
if (msg == NULL) {
msg = xcalloc(1, sizeof(*msg), "bounce_add");
msg->msgid = key.msgid;
msg->bounce = key.bounce;
- SPLAY_INSERT(bounce_message_tree, &messages, msg);
TAILQ_INIT(&msg->envelopes);
+ msg->smtpname = xstrdup(evp.smtpname, "bounce_add");
snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user,
evp.sender.domain);
msg->to = xstrdup(buf, "bounce_add");
nmessage += 1;
+ SPLAY_INSERT(bounce_message_tree, &messages, msg);
log_debug("debug: bounce: new message %08" PRIx32,
msg->msgid);
stat_increment("bounce.message", 1);
@@ -168,8 +172,8 @@ bounce_add(uint64_t evpid)
be->id = evpid;
be->report = xstrdup(buf, "bounce_add");
TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
- log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id,
- be->report);
+ buf[strcspn(buf, "\n")] = '\0';
+ log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf);
msg->timeout = time(NULL) + 1;
TAILQ_INSERT_TAIL(&pending, msg, entry);
@@ -182,16 +186,23 @@ void
bounce_fd(int fd)
{
struct bounce_session *s;
+ struct bounce_message *msg;
log_debug("debug: bounce: got enqueue socket %d", fd);
- if (fd == -1) {
+ if (fd == -1 || TAILQ_EMPTY(&pending)) {
+ log_debug("debug: bounce: cancelling");
+ if (fd != -1)
+ close(fd);
running -= 1;
bounce_drain();
return;
}
+ msg = TAILQ_FIRST(&pending);
+
s = xcalloc(1, sizeof(*s), "bounce_fd");
+ s->smtpname = xstrdup(msg->smtpname, "bounce_fd");
s->state = BOUNCE_EHLO;
iobuf_xinit(&s->iobuf, 0, 0, "bounce_run");
io_init(&s->io, fd, s, bounce_io, &s->iobuf);
@@ -323,11 +334,20 @@ bounce_next_message(struct bounce_session *s)
struct bounce_message *msg;
char buf[SMTPD_MAXLINESIZE];
int fd;
+ time_t now;
again:
- msg = TAILQ_FIRST(&pending);
- if (msg == NULL || msg->timeout > time(NULL))
+ now = time(NULL);
+
+ TAILQ_FOREACH(msg, &pending, entry) {
+ if (msg->timeout > now)
+ continue;
+ if (strcmp(msg->smtpname, s->smtpname))
+ continue;
+ break;
+ }
+ if (msg == NULL)
return (0);
TAILQ_REMOVE(&pending, msg, entry);
@@ -360,7 +380,7 @@ bounce_next(struct bounce_session *s)
switch (s->state) {
case BOUNCE_EHLO:
- bounce_send(s, "EHLO %s", env->sc_hostname);
+ bounce_send(s, "EHLO %s", s->smtpname);
s->state = BOUNCE_MAIL;
break;
@@ -401,7 +421,7 @@ bounce_next(struct bounce_session *s)
NOTICE_INTRO
"\n",
(s->msg->bounce.type == B_ERROR) ? "error" : "warning",
- env->sc_hostname,
+ s->smtpname,
s->msg->to,
time_to_text(time(NULL)));
@@ -515,6 +535,7 @@ bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
queue_envelope_delete(be->id);
}
TAILQ_REMOVE(&msg->envelopes, be, entry);
+ free(be->report);
free(be);
n += 1;
}
@@ -522,6 +543,8 @@ bounce_delivery(struct bounce_message *msg, int delivery, const char *status)
nmessage -= 1;
stat_decrement("bounce.message", 1);
stat_decrement("bounce.envelope", n);
+ free(msg->smtpname);
+ free(msg->to);
free(msg);
}
@@ -563,6 +586,8 @@ bounce_free(struct bounce_session *s)
iobuf_clear(&s->iobuf);
io_clear(&s->io);
+
+ free(s->smtpname);
free(s);
running -= 1;
@@ -648,10 +673,15 @@ static int
bounce_message_cmp(const struct bounce_message *a,
const struct bounce_message *b)
{
+ int r;
+
if (a->msgid < b->msgid)
return (-1);
if (a->msgid > b->msgid)
return (1);
+ if ((r = strcmp(a->smtpname, b->smtpname)))
+ return (r);
+
return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
}
diff --git a/usr.sbin/smtpd/envelope.c b/usr.sbin/smtpd/envelope.c
index 92992cafa0e..aa884d97afc 100644
--- a/usr.sbin/smtpd/envelope.c
+++ b/usr.sbin/smtpd/envelope.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: envelope.c,v 1.21 2013/10/26 20:32:48 eric Exp $ */
+/* $OpenBSD: envelope.c,v 1.22 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -44,6 +44,7 @@
#include "smtpd.h"
#include "log.h"
+static int envelope_upgrade_v1(struct dict *);
static int envelope_ascii_load(struct envelope *, struct dict *);
static void envelope_ascii_dump(const struct envelope *, char **, size_t *,
const char *);
@@ -139,8 +140,16 @@ envelope_load_buffer(struct envelope *ep, const char *ibuf, size_t buflen)
switch (version) {
case 1:
- /* very very okd envelopes may still have this */
- dict_pop(&d, "msgid");
+ log_debug("debug: upgrading envelope to version 1");
+ if (!envelope_upgrade_v1(&d)) {
+ log_debug("debug: failed to upgrade envelope to version 1");
+ goto end;
+ }
+ /* FALLTRHOUGH */
+ case 2:
+ /* Can be missing in some v2 envelopes */
+ if (dict_get(&d, "smtpname") == NULL)
+ dict_xset(&d, "smtpname", env->sc_hostname);
break;
default:
log_debug("debug: bad envelope version %lld", version);
@@ -165,6 +174,7 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
envelope_ascii_dump(ep, &dest, &len, "version");
envelope_ascii_dump(ep, &dest, &len, "tag");
envelope_ascii_dump(ep, &dest, &len, "type");
+ envelope_ascii_dump(ep, &dest, &len, "smtpname");
envelope_ascii_dump(ep, &dest, &len, "helo");
envelope_ascii_dump(ep, &dest, &len, "hostname");
envelope_ascii_dump(ep, &dest, &len, "errorline");
@@ -190,7 +200,9 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
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-helo");
+ 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:
@@ -347,6 +359,23 @@ ascii_load_mta_relay_url(struct relayhost *relay, char *buf)
}
static int
+ascii_load_mta_relay_flags(uint16_t *dest, char *buf)
+{
+ char *flag;
+
+ while ((flag = strsep(&buf, " ,|")) != NULL) {
+ if (strcasecmp(flag, "verify") == 0)
+ *dest |= F_TLS_VERIFY;
+ else if (strcasecmp(flag, "tls") == 0)
+ *dest |= F_STARTTLS;
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
ascii_load_bounce_type(enum bounce_type *dest, char *buf)
{
if (strcasecmp(buf, "error") == 0)
@@ -432,7 +461,14 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf)
return ascii_load_string(ep->agent.mta.relay.cert, buf,
sizeof ep->agent.mta.relay.cert);
- if (strcasecmp("mta-relay-helo", field) == 0)
+ if (strcasecmp("mta-relay-flags", field) == 0)
+ return ascii_load_mta_relay_flags(&ep->agent.mta.relay.flags, buf);
+
+ if (strcasecmp("mta-relay-heloname", field) == 0)
+ return ascii_load_string(ep->agent.mta.relay.heloname, buf,
+ sizeof ep->agent.mta.relay.heloname);
+
+ if (strcasecmp("mta-relay-helotable", field) == 0)
return ascii_load_string(ep->agent.mta.relay.helotable, buf,
sizeof ep->agent.mta.relay.helotable);
@@ -449,6 +485,9 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf)
if (strcasecmp("sender", field) == 0)
return ascii_load_mailaddr(&ep->sender, buf);
+ if (strcasecmp("smtpname", field) == 0)
+ return ascii_load_string(ep->smtpname, buf, sizeof(ep->smtpname));
+
if (strcasecmp("sockaddr", field) == 0)
return ascii_load_sockaddr(&ep->ss, buf);
@@ -594,6 +633,28 @@ ascii_dump_mta_relay_url(const struct relayhost *relay, char *buf, size_t len)
}
static int
+ascii_dump_mta_relay_flags(uint16_t flags, char *buf, size_t len)
+{
+ size_t cpylen = 0;
+
+ buf[0] = '\0';
+ if (flags) {
+ if (flags & F_TLS_VERIFY) {
+ if (buf[0] != '\0')
+ strlcat(buf, " ", len);
+ cpylen = strlcat(buf, "verify", len);
+ }
+ if (flags & F_STARTTLS) {
+ if (buf[0] != '\0')
+ strlcat(buf, " ", len);
+ cpylen = strlcat(buf, "tls", len);
+ }
+ }
+
+ return cpylen < len ? 1 : 0;
+}
+
+static int
ascii_dump_bounce_type(enum bounce_type type, char *dest, size_t len)
{
char *p = NULL;
@@ -683,7 +744,15 @@ ascii_dump_field(const char *field, const struct envelope *ep,
return ascii_dump_string(ep->agent.mta.relay.cert,
buf, len);
- if (strcasecmp(field, "mta-relay-helo") == 0)
+ if (strcasecmp(field, "mta-relay-flags") == 0)
+ return ascii_dump_mta_relay_flags(ep->agent.mta.relay.flags,
+ buf, len);
+
+ if (strcasecmp(field, "mta-relay-heloname") == 0)
+ return ascii_dump_string(ep->agent.mta.relay.heloname,
+ buf, len);
+
+ if (strcasecmp(field, "mta-relay-helotable") == 0)
return ascii_dump_string(ep->agent.mta.relay.helotable,
buf, len);
@@ -700,6 +769,9 @@ ascii_dump_field(const char *field, const struct envelope *ep,
if (strcasecmp(field, "sender") == 0)
return ascii_dump_mailaddr(&ep->sender, buf, len);
+ if (strcasecmp(field, "smtpname") == 0)
+ return ascii_dump_string(ep->smtpname, buf, len);
+
if (strcasecmp(field, "sockaddr") == 0)
return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
@@ -740,3 +812,41 @@ envelope_ascii_dump(const struct envelope *ep, char **dest, size_t *len, const c
err:
*dest = NULL;
}
+
+static int
+envelope_upgrade_v1(struct dict *d)
+{
+ static char buf_relay[1024];
+ char *val;
+
+ /*
+ * very very old envelopes had a "msgid" field
+ */
+ dict_pop(d, "msgid");
+
+ /*
+ * rename "mta-relay-helo" field to "mta-relay-helotable"
+ */
+ if ((val = dict_get(d, "mta-relay-helo"))) {
+ dict_xset(d, "mta-relay-helotable", val);
+ dict_xpop(d, "mta-relay-helo");
+ }
+
+ /*
+ * "ssl" becomes "secure" in "mta-relay" scheme
+ */
+ if ((val = dict_get(d, "mta-relay"))) {
+ if (strncasecmp("ssl://", val, 6) == 0) {
+ if (! bsnprintf(buf_relay, sizeof(buf_relay), "secure://%s", val+6))
+ return (0);
+ dict_set(d, "mta-relay", buf_relay);
+ }
+ else if (strncasecmp("ssl+auth://", val, 11) == 0) {
+ if (! bsnprintf(buf_relay, sizeof(buf_relay), "secure+auth://%s", val+11))
+ return (0);
+ dict_set(d, "mta-relay", buf_relay);
+ }
+ }
+
+ return (1);
+}
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index 7a550bbcfea..1f9153566ba 100644
--- a/usr.sbin/smtpd/lka.c
+++ b/usr.sbin/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.157 2013/10/28 17:02:08 eric Exp $ */
+/* $OpenBSD: lka.c,v 1.158 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -105,6 +105,24 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
lka_session(reqid, &evp);
return;
+ case IMSG_LKA_HELO:
+ m_msg(&m, imsg);
+ m_get_id(&m, &reqid);
+ m_get_string(&m, &tablename);
+ m_get_sockaddr(&m, (struct sockaddr *)&ss);
+ m_end(&m);
+
+ ret = lka_addrname(tablename, (struct sockaddr*)&ss,
+ &addrname);
+
+ m_create(p, IMSG_LKA_HELO, 0, 0, -1);
+ m_add_id(p, reqid);
+ m_add_int(p, ret);
+ if (ret == LKA_OK)
+ m_add_string(p, addrname.name);
+ m_close(p);
+ return;
+
case IMSG_LKA_SSL_INIT:
req_ca_cert = imsg->data;
resp_ca_cert.reqid = req_ca_cert->reqid;
@@ -431,6 +449,16 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
env->sc_tables_dict = tmp;
return;
+ case IMSG_CONF_RULE_RECIPIENT:
+ rule = TAILQ_LAST(env->sc_rules_reload, rulelist);
+ tmp = env->sc_tables_dict;
+ env->sc_tables_dict = tables_dict;
+ rule->r_recipients = table_find(imsg->data, NULL);
+ if (rule->r_recipients == NULL)
+ fatalx("lka: tables inconsistency");
+ env->sc_tables_dict = tmp;
+ return;
+
case IMSG_CONF_RULE_DESTINATION:
rule = TAILQ_LAST(env->sc_rules_reload, rulelist);
tmp = env->sc_tables_dict;
diff --git a/usr.sbin/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c
index 4fc3ba044a5..5ebef4b854d 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.59 2013/10/28 09:14:58 eric Exp $ */
+/* $OpenBSD: lka_session.c,v 1.60 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
@@ -142,9 +142,21 @@ lka_session_forward_reply(struct forward_req *fwreq, int fd)
break;
case 1:
if (fd == -1) {
- log_trace(TRACE_EXPAND, "expand: no .forward for "
- "user %s, just deliver", fwreq->user);
- lka_submit(lks, rule, xn);
+ if (lks->expand.rule->r_forwardonly) {
+ 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) {
+ log_trace(TRACE_EXPAND, "expand: no .forward "
+ "for user %s and no default action on rule", fwreq->user);
+ lks->error = LKA_PERMFAIL;
+ }
+ else {
+ log_trace(TRACE_EXPAND, "expand: no .forward for "
+ "user %s, just deliver", fwreq->user);
+ lka_submit(lks, rule, xn);
+ }
}
else {
/* expand for the current user and rule */
@@ -161,9 +173,21 @@ lka_session_forward_reply(struct forward_req *fwreq, int fd)
lks->error = LKA_TEMPFAIL;
}
else if (ret == 0) {
- log_trace(TRACE_EXPAND, "expand: empty .forward "
- "for user %s, just deliver", fwreq->user);
- lka_submit(lks, rule, xn);
+ if (lks->expand.rule->r_forwardonly) {
+ 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) {
+ log_trace(TRACE_EXPAND, "expand: empty .forward "
+ "for user %s and no default action on rule", fwreq->user);
+ lks->error = LKA_PERMFAIL;
+ }
+ else {
+ log_trace(TRACE_EXPAND, "expand: empty .forward "
+ "for user %s, just deliver", fwreq->user);
+ lka_submit(lks, rule, xn);
+ }
}
}
break;
@@ -394,12 +418,22 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
break;
case EXPAND_FILENAME:
+ if (rule->r_forwardonly) {
+ log_trace(TRACE_EXPAND, "expand: filename matched on forward-only rule");
+ lks->error = LKA_TEMPFAIL;
+ break;
+ }
log_trace(TRACE_EXPAND, "expand: lka_expand: filename: %s "
"[depth=%d]", xn->u.buffer, xn->depth);
lka_submit(lks, rule, xn);
break;
case EXPAND_ERROR:
+ if (rule->r_forwardonly) {
+ log_trace(TRACE_EXPAND, "expand: error matched on forward-only rule");
+ lks->error = LKA_TEMPFAIL;
+ break;
+ }
log_trace(TRACE_EXPAND, "expand: lka_expand: error: %s "
"[depth=%d]", xn->u.buffer, xn->depth);
if (xn->u.buffer[0] == '4')
@@ -410,6 +444,11 @@ lka_expand(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
break;
case EXPAND_FILTER:
+ if (rule->r_forwardonly) {
+ log_trace(TRACE_EXPAND, "expand: filter matched on forward-only rule");
+ lks->error = LKA_TEMPFAIL;
+ break;
+ }
log_trace(TRACE_EXPAND, "expand: lka_expand: filter: %s "
"[depth=%d]", xn->u.buffer, xn->depth);
lka_submit(lks, rule, xn);
@@ -459,6 +498,7 @@ lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn)
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:
diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c
index f1d30012db3..7c827ab675a 100644
--- a/usr.sbin/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.170 2013/10/30 21:37:48 eric Exp $ */
+/* $OpenBSD: mta.c,v 1.171 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -1551,6 +1551,8 @@ mta_relay(struct envelope *e)
key.flags |= RELAY_MX;
} else {
key.domain = mta_domain(e->dest.domain, 0);
+ if (!(e->agent.mta.relay.flags & RELAY_STARTTLS))
+ key.flags |= RELAY_TLS_OPTIONAL;
}
key.flags |= e->agent.mta.relay.flags;
diff --git a/usr.sbin/smtpd/mta_session.c b/usr.sbin/smtpd/mta_session.c
index 5616b1d7c23..56442c807b8 100644
--- a/usr.sbin/smtpd/mta_session.c
+++ b/usr.sbin/smtpd/mta_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta_session.c,v 1.45 2013/10/29 17:04:45 eric Exp $ */
+/* $OpenBSD: mta_session.c,v 1.46 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -324,10 +324,19 @@ mta_session_imsg(struct mproc *p, struct imsg *imsg)
return;
if (resp_ca_cert->status == CA_FAIL) {
- log_info("smtp-out: Disconnecting session %016"PRIx64
- ": CA failure", s->id);
- mta_free(s);
- return;
+ if (s->relay->cert) {
+ log_info("smtp-out: Disconnecting session %016"PRIx64
+ ": CA failure", s->id);
+ mta_free(s);
+ return;
+ }
+ else {
+ ssl = ssl_mta_init(NULL, 0, NULL, 0);
+ if (ssl == NULL)
+ fatal("mta: ssl_mta_init");
+ io_start_tls(&s->io, ssl);
+ return;
+ }
}
resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert,
@@ -360,6 +369,12 @@ mta_session_imsg(struct mproc *p, struct imsg *imsg)
if (resp_ca_vrfy->status == CA_OK)
s->flags |= MTA_VERIFIED;
+ else if (s->relay->flags & F_TLS_VERIFY) {
+ errno = 0;
+ mta_error(s, "SSL certificate check failed");
+ mta_free(s);
+ return;
+ }
mta_io(&s->io, IO_TLSVERIFIED);
io_resume(&s->io, IO_PAUSE_IN);
@@ -1495,22 +1510,20 @@ static void
mta_start_tls(struct mta_session *s)
{
struct ca_cert_req_msg req_ca_cert;
- void *ssl;
-
- if (s->relay->cert) {
- req_ca_cert.reqid = s->id;
- strlcpy(req_ca_cert.name, s->relay->cert,
- sizeof req_ca_cert.name);
- m_compose(p_lka, IMSG_LKA_SSL_INIT, 0, 0, -1,
- &req_ca_cert, sizeof(req_ca_cert));
- tree_xset(&wait_ssl_init, s->id, s);
- s->flags |= MTA_WAIT;
- return;
- }
- ssl = ssl_mta_init(NULL, 0, NULL, 0);
- if (ssl == NULL)
- fatal("mta: ssl_mta_init");
- io_start_tls(&s->io, ssl);
+ const char *certname;
+
+ if (s->relay->cert)
+ certname = s->relay->cert;
+ else
+ certname = s->helo;
+
+ req_ca_cert.reqid = s->id;
+ strlcpy(req_ca_cert.name, certname, sizeof req_ca_cert.name);
+ m_compose(p_lka, IMSG_LKA_SSL_INIT, 0, 0, -1,
+ &req_ca_cert, sizeof(req_ca_cert));
+ tree_xset(&wait_ssl_init, s->id, s);
+ s->flags |= MTA_WAIT;
+ return;
}
static int
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index 4b484a31720..83875ee924e 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.125 2013/10/27 11:01:47 eric Exp $ */
+/* $OpenBSD: parse.y,v 1.126 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -50,7 +50,10 @@
#include <unistd.h>
#include <util.h>
+#include <openssl/ssl.h>
+
#include "smtpd.h"
+#include "ssl.h"
#include "log.h"
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
@@ -93,15 +96,30 @@ struct table *table = NULL;
struct rule *rule = NULL;
struct listener l;
struct mta_limits *limits;
+static struct ssl *pki_ssl;
+
+static struct listen_opts {
+ char *ifx;
+ int family;
+ in_port_t port;
+ uint16_t ssl;
+ char *pki;
+ uint16_t auth;
+ struct table *authtable;
+ char *tag;
+ char *hostname;
+ struct table *hostnametable;
+ uint16_t flags;
+} listen_opts;
+
+static void create_listener(struct listenerlist *, struct listen_opts *);
+static void config_listener(struct listener *, struct listen_opts *);
struct listener *host_v4(const char *, in_port_t);
struct listener *host_v6(const char *, in_port_t);
-int host_dns(const char *, const char *, const char *,
- struct listenerlist *, int, in_port_t, uint8_t);
-int host(const char *, const char *, const char *,
- struct listenerlist *, int, in_port_t, const char *, uint8_t, const char *);
-int interface(const char *, int, const char *, const char *,
- struct listenerlist *, int, in_port_t, const char *, uint8_t, const char *);
+int host_dns(struct listenerlist *, struct listen_opts *);
+int host(struct listenerlist *, struct listen_opts *);
+int interface(struct listenerlist *, struct listen_opts *);
void set_localaddrs(void);
int delaytonum(char *);
int is_if_in_group(const char *, const char *);
@@ -124,18 +142,17 @@ typedef struct {
%}
%token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE LISTEN ON ANY PORT EXPIRE
-%token TABLE SSL SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6
-%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HELO
-%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA
-%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER FILTERCHAIN KEY
-%token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER
+%token TABLE SECURE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6
+%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HOSTNAMES
+%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI
+%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER FILTERCHAIN KEY CA DHPARAMS
+%token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER MASK_SOURCE VERIFY FORWARDONLY RECIPIENT
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.table> table
-%type <v.number> port auth ssl size expire address_family
-%type <v.table> tables tablenew tableref destination alias virtual usermapping userbase credentials from sender
-%type <v.maddr> relay_as
-%type <v.string> certificate tag tagged relay_source listen_helo relay_helo relay_backup
+%type <v.number> size negation
+%type <v.table> tables tablenew tableref alias virtual userbase
+%type <v.string> tagged
%%
grammar : /* empty */
@@ -203,7 +220,83 @@ size : NUMBER {
}
;
-port : PORT STRING {
+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;
+ }
+ ;
+
+bouncedelay : STRING {
+ time_t d;
+ int i;
+
+ d = delaytonum($1);
+ if (d < 0) {
+ yyerror("invalid bounce delay: %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ for (i = 0; i < MAX_BOUNCE_WARN; i++) {
+ if (conf->sc_bounce_warn[i] != 0)
+ continue;
+ conf->sc_bounce_warn[i] = d;
+ break;
+ }
+ }
+
+bouncedelays : bouncedelays ',' bouncedelay
+ | bouncedelay
+ | /* EMPTY */
+ ;
+
+opt_limit : INET4 {
+ limits->family = AF_INET;
+ }
+ | INET6 {
+ limits->family = AF_INET6;
+ }
+ | STRING NUMBER {
+ if (!limit_mta_set(limits, $1, $2)) {
+ yyerror("invalid limit keyword");
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+limits : opt_limit limits
+ | /* empty */
+ ;
+
+opt_pki : CERTIFICATE STRING {
+ pki_ssl->ssl_cert_file = $2;
+ }
+ | KEY STRING {
+ pki_ssl->ssl_key_file = $2;
+ }
+ | CA STRING {
+ pki_ssl->ssl_ca_file = $2;
+ }
+ | DHPARAMS STRING {
+ pki_ssl->ssl_dhparams_file = $2;
+ }
+ ;
+
+pki : opt_pki pki
+ | /* empty */
+ ;
+
+opt_listen : INET4 { listen_opts.family = AF_INET; }
+ | INET6 { listen_opts.family = AF_INET6; }
+ | PORT STRING {
struct servent *servent;
servent = getservbyname($2, "tcp");
@@ -213,115 +306,141 @@ port : PORT STRING {
YYERROR;
}
free($2);
- $$ = ntohs(servent->s_port);
+ listen_opts.port = ntohs(servent->s_port);
}
| PORT NUMBER {
if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
yyerror("invalid port: %" PRId64, $2);
YYERROR;
}
- $$ = $2;
+ listen_opts.port = $2;
+ }
+ | SMTPS { listen_opts.ssl = F_SMTPS; }
+ | SMTPS VERIFY { listen_opts.ssl = F_SMTPS|F_TLS_VERIFY; }
+ | TLS { listen_opts.ssl = F_STARTTLS; }
+ | SECURE { listen_opts.ssl = F_SSL; }
+ | TLS_REQUIRE { listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE; }
+ | TLS_REQUIRE VERIFY { listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY; }
+ | PKI STRING { listen_opts.pki = $2; }
+ | AUTH { listen_opts.auth = F_AUTH|F_AUTH_REQUIRE; }
+ | AUTH_OPTIONAL { listen_opts.auth = F_AUTH; }
+ | AUTH tables {
+ listen_opts.authtable = $2;
+ listen_opts.auth = F_AUTH|F_AUTH_REQUIRE;
}
- | /* empty */ {
- $$ = 0;
+ | AUTH_OPTIONAL tables {
+ listen_opts.authtable = $2;
+ listen_opts.auth = F_AUTH;
}
- ;
-
-certificate : CERTIFICATE STRING {
- if (($$ = strdup($2)) == NULL) {
- yyerror("strdup");
+ | TAG STRING {
+ if (strlen($2) >= MAX_TAG_SIZE) {
+ yyerror("tag name too long");
free($2);
YYERROR;
}
- free($2);
+ listen_opts.tag = $2;
}
- | /* empty */ { $$ = NULL; }
+ | HOSTNAME STRING { listen_opts.hostname = $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;
+ }
+ listen_opts.hostnametable = t;
+ }
+ | MASK_SOURCE { listen_opts.flags |= F_MASK_SOURCE; }
;
-ssl : SMTPS { $$ = F_SMTPS; }
- | TLS { $$ = F_STARTTLS; }
- | SSL { $$ = F_SSL; }
- | TLS_REQUIRE { $$ = F_STARTTLS|F_STARTTLS_REQUIRE; }
- | /* Empty */ { $$ = 0; }
+listen : opt_listen listen
+ | /* empty */
;
-auth : AUTH {
- $$ = F_AUTH|F_AUTH_REQUIRE;
- }
- | AUTH_OPTIONAL {
- $$ = F_AUTH;
- }
- | AUTH tables {
- strlcpy(l.authtable, ($2)->t_name, sizeof l.authtable);
- $$ = F_AUTH|F_AUTH_REQUIRE;
- }
- | AUTH_OPTIONAL tables {
- strlcpy(l.authtable, ($2)->t_name, sizeof l.authtable);
- $$ = F_AUTH;
- }
- | /* empty */ { $$ = 0; }
- ;
+opt_relay_common: AS STRING {
+ struct mailaddr maddr, *maddrp;
-tag : TAG STRING {
- if (strlen($2) >= MAX_TAG_SIZE) {
- yyerror("tag name too long");
+ if (! text_to_mailaddr(&maddr, $2)) {
+ yyerror("invalid parameter to AS: %s", $2);
free($2);
YYERROR;
}
+ free($2);
- $$ = $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");
}
- | /* empty */ { $$ = NULL; }
- ;
-
-tagged : TAGGED STRING {
- if (($$ = strdup($2)) == NULL) {
- yyerror("strdup");
- free($2);
+ | 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;
}
+ strlcpy(rule->r_value.relayhost.sourcetable, t->t_name,
+ sizeof rule->r_value.relayhost.sourcetable);
+ }
+ | HOSTNAME STRING {
+ strlcat(rule->r_value.relayhost.heloname, $2,
+ sizeof rule->r_value.relayhost.heloname);
free($2);
}
- | /* empty */ { $$ = NULL; }
- ;
-
-expire : EXPIRE STRING {
- $$ = delaytonum($2);
- if ($$ == -1) {
- yyerror("invalid expire delay: %s", $2);
- 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;
}
+ strlcpy(rule->r_value.relayhost.helotable, t->t_name,
+ sizeof rule->r_value.relayhost.helotable);
+ }
+ | PKI STRING {
+ if (strlcpy(rule->r_value.relayhost.cert, $2,
+ sizeof(rule->r_value.relayhost.cert))
+ >= sizeof(rule->r_value.relayhost.cert))
+ fatal("certificate path too long");
free($2);
}
- | /* empty */ { $$ = conf->sc_qexpire; }
;
-bouncedelay : STRING {
- time_t d;
- int i;
-
- d = delaytonum($1);
- if (d < 0) {
- yyerror("invalid bounce delay: %s", $1);
- free($1);
- YYERROR;
- }
- free($1);
- for (i = 0; i < MAX_BOUNCE_WARN; i++) {
- if (conf->sc_bounce_warn[i] != 0)
- continue;
- conf->sc_bounce_warn[i] = d;
- break;
- }
+opt_relay : BACKUP STRING {
+ rule->r_value.relayhost.flags |= F_BACKUP;
+ strlcpy(rule->r_value.relayhost.hostname, $2,
+ sizeof (rule->r_value.relayhost.hostname));
}
+ | BACKUP {
+ rule->r_value.relayhost.flags |= F_BACKUP;
+ 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;
+ }
+ ;
-bouncedelays : bouncedelays ',' bouncedelay
- | bouncedelay
- | /* EMPTY */
+relay : opt_relay_common relay
+ | opt_relay relay
+ | /* empty */
;
-credentials : AUTH tables {
+opt_relay_via : AUTH tables {
struct table *t = $2;
if (! table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) {
@@ -329,38 +448,20 @@ credentials : AUTH tables {
t->t_name);
YYERROR;
}
-
- $$ = t;
+ strlcpy(rule->r_value.relayhost.authtable, t->t_name,
+ sizeof(rule->r_value.relayhost.authtable));
}
- | /* empty */ { $$ = 0; }
- ;
-
-address_family : INET4 { $$ = AF_INET; }
- | INET6 { $$ = AF_INET6; }
- | /* empty */ { $$ = AF_UNSPEC; }
- ;
-
-listen_helo : HOSTNAME STRING { $$ = $2; }
- | /* empty */ { $$ = NULL; }
- ;
-
-opt_limit : INET4 {
- limits->family = AF_INET;
- }
- | INET6 {
- limits->family = AF_INET6;
- }
- | STRING NUMBER {
- if (!limit_mta_set(limits, $1, $2)) {
- yyerror("invalid limit keyword");
- free($1);
+ | VERIFY {
+ if (!(rule->r_value.relayhost.flags & F_SSL)) {
+ yyerror("cannot \"verify\" with insecure protocol");
YYERROR;
}
- free($1);
+ rule->r_value.relayhost.flags |= F_TLS_VERIFY;
}
;
-limits : opt_limit limits
+relay_via : opt_relay_common relay_via
+ | opt_relay_via relay_via
| /* empty */
;
@@ -370,9 +471,45 @@ main : BOUNCEWARN {
| QUEUE COMPRESSION {
conf->sc_queue_flags |= QUEUE_COMPRESSION;
}
+ | QUEUE ENCRYPTION {
+ char *password;
+
+ password = getpass("queue key: ");
+ if (password == NULL) {
+ yyerror("getpass() error");
+ YYERROR;
+ }
+ conf->sc_queue_key = strdup(password);
+ bzero(password, strlen(password));
+ if (conf->sc_queue_key == NULL) {
+ yyerror("memory exhausted");
+ YYERROR;
+ }
+ conf->sc_queue_flags |= QUEUE_ENCRYPTION;
+
+ }
| QUEUE ENCRYPTION KEY STRING {
+ char *buf;
+ char *lbuf;
+ size_t len;
+
+ if (strcasecmp($4, "stdin") == 0 ||
+ strcasecmp($4, "-") == 0) {
+ lbuf = NULL;
+ buf = fgetln(stdin, &len);
+ if (buf[len - 1] == '\n') {
+ lbuf = calloc(len, 1);
+ memcpy(lbuf, buf, len-1);
+ }
+ else {
+ lbuf = calloc(len+1, 1);
+ memcpy(lbuf, buf, len);
+ }
+ conf->sc_queue_key = lbuf;
+ }
+ else
+ conf->sc_queue_key = $4;
conf->sc_queue_flags |= QUEUE_ENCRYPTION;
- conf->sc_queue_key = $4;
}
| EXPIRE STRING {
conf->sc_qexpire = delaytonum($2);
@@ -403,58 +540,11 @@ main : BOUNCEWARN {
} limits
| LISTEN {
bzero(&l, sizeof l);
- } ON STRING address_family port ssl certificate auth tag listen_helo {
- char *ifx = $4;
- int family = $5;
- in_port_t port = $6;
- uint8_t ssl = $7;
- char *cert = $8;
- uint8_t auth = $9;
- char *tag = $10;
- char *helo = $11;
-
- if (port != 0 && ssl == F_SSL) {
- yyerror("invalid listen option: tls/smtps on same port");
- YYERROR;
- }
-
- if (auth != 0 && !ssl) {
- yyerror("invalid listen option: auth requires tls/smtps");
- YYERROR;
- }
-
- if (port == 0) {
- if (ssl & F_SMTPS) {
- if (! interface(ifx, family, tag, cert, conf->sc_listeners,
- MAX_LISTEN, 465, l.authtable, F_SMTPS|auth, helo)) {
- if (host(ifx, tag, cert, conf->sc_listeners,
- MAX_LISTEN, 465, l.authtable, ssl|auth, helo) <= 0) {
- yyerror("invalid virtual ip or interface: %s", ifx);
- YYERROR;
- }
- }
- }
- if (! ssl || (ssl & ~F_SMTPS)) {
- if (! interface(ifx, family, tag, cert, conf->sc_listeners,
- MAX_LISTEN, 25, l.authtable, (ssl&~F_SMTPS)|auth, helo)) {
- if (host(ifx, tag, cert, conf->sc_listeners,
- MAX_LISTEN, 25, l.authtable, ssl|auth, helo) <= 0) {
- yyerror("invalid virtual ip or interface: %s", ifx);
- YYERROR;
- }
- }
- }
- }
- else {
- if (! interface(ifx, family, tag, cert, conf->sc_listeners,
- MAX_LISTEN, port, l.authtable, ssl|auth, helo)) {
- if (host(ifx, tag, cert, conf->sc_listeners,
- MAX_LISTEN, port, l.authtable, ssl|auth, helo) <= 0) {
- yyerror("invalid virtual ip or interface: %s", ifx);
- YYERROR;
- }
- }
- }
+ bzero(&listen_opts, sizeof listen_opts);
+ listen_opts.family = AF_UNSPEC;
+ } ON STRING listen {
+ listen_opts.ifx = $4;
+ create_listener(conf->sc_listeners, &listen_opts);
}
| FILTER STRING STRING {
if (!create_filter($2, $3)) {
@@ -472,6 +562,16 @@ main : BOUNCEWARN {
}
} filter_list
;
+ | PKI STRING {
+ pki_ssl = dict_get(conf->sc_ssl_dict, $2);
+ if (pki_ssl == NULL) {
+ pki_ssl = xcalloc(1, sizeof *pki_ssl, "parse:pki");
+ xlowercase(pki_ssl->ssl_name, $2, sizeof pki_ssl->ssl_name);
+ dict_set(conf->sc_ssl_dict, pki_ssl->ssl_name, pki_ssl);
+ }
+ free($2);
+ } pki
+ ;
table : TABLE STRING STRING {
char *p, *backend, *config;
@@ -610,332 +710,310 @@ virtual : VIRTUAL tables {
t->t_name);
YYERROR;
}
-
$$ = t;
}
;
usermapping : alias {
+ if (rule->r_mapping) {
+ yyerror("alias specified multiple times");
+ YYERROR;
+ }
rule->r_desttype = DEST_DOM;
- $$ = $1;
+ rule->r_mapping = $1;
}
| virtual {
+ if (rule->r_mapping) {
+ yyerror("virtual specified multiple times");
+ YYERROR;
+ }
rule->r_desttype = DEST_VDOM;
- $$ = $1;
- }
- | /**/ {
- rule->r_desttype = DEST_DOM;
- $$ = 0;
+ rule->r_mapping = $1;
}
;
userbase : USERBASE tables {
struct table *t = $2;
- if (! table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
- yyerror("invalid use of table \"%s\" as USERBASE parameter",
- t->t_name);
+ if (rule->r_userbase) {
+ yyerror("userbase specified multiple times");
YYERROR;
}
-
- $$ = t;
- }
- | /**/ { $$ = table_find("<getpwnam>", NULL); }
- ;
-
-
-
-
-destination : DOMAIN tables {
- struct table *t = $2;
-
- if (! table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
- yyerror("invalid use of table \"%s\" as DOMAIN parameter",
+ if (! table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
+ yyerror("invalid use of table \"%s\" as USERBASE parameter",
t->t_name);
YYERROR;
}
-
- $$ = t;
- }
- | LOCAL { $$ = table_find("<localnames>", NULL); }
- | ANY { $$ = 0; }
- ;
-
-relay_source : 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;
- }
- $$ = t->t_name;
+ rule->r_userbase = t;
}
- | { $$ = NULL; }
;
-relay_helo : HELO tables {
- struct table *t = $2;
- if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
- yyerror("invalid use of table \"%s\" as "
- "HELO parameter", t->t_name);
- YYERROR;
- }
- $$ = t->t_name;
- }
- | { $$ = NULL; }
- ;
-
-relay_backup : BACKUP STRING { $$ = $2; }
- | BACKUP { $$ = NULL; }
- ;
-
-relay_as : 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;
- }
- }
- $$ = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS");
- }
- | /* empty */ { $$ = NULL; }
- ;
-
-action : userbase DELIVER TO MAILDIR {
- rule->r_userbase = $1;
+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");
}
- | userbase DELIVER TO MAILDIR STRING {
- rule->r_userbase = $1;
+ | DELIVER TO MAILDIR STRING {
rule->r_action = A_MAILDIR;
- if (strlcpy(rule->r_value.buffer, $5,
+ if (strlcpy(rule->r_value.buffer, $4,
sizeof(rule->r_value.buffer)) >=
sizeof(rule->r_value.buffer))
fatal("pathname too long");
- free($5);
+ free($4);
}
- | userbase DELIVER TO LMTP STRING {
- rule->r_userbase = $1;
+ | DELIVER TO LMTP STRING {
rule->r_action = A_LMTP;
- if (strchr($5, ':') || $5[0] == '/') {
- if (strlcpy(rule->r_value.buffer, $5,
+ 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($5);
+ free($4);
}
- | userbase DELIVER TO MBOX {
- rule->r_userbase = $1;
+ | 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");
}
- | userbase DELIVER TO MDA STRING {
- rule->r_userbase = $1;
+ | DELIVER TO MDA STRING {
rule->r_action = A_MDA;
- if (strlcpy(rule->r_value.buffer, $5,
+ if (strlcpy(rule->r_value.buffer, $4,
sizeof(rule->r_value.buffer))
>= sizeof(rule->r_value.buffer))
fatal("command too long");
- free($5);
+ free($4);
}
- | RELAY relay_as relay_source relay_helo {
- rule->r_action = A_RELAY;
- rule->r_as = $2;
- if ($3)
- strlcpy(rule->r_value.relayhost.sourcetable, $3,
- sizeof rule->r_value.relayhost.sourcetable);
- if ($4)
- strlcpy(rule->r_value.relayhost.helotable, $4,
- sizeof rule->r_value.relayhost.helotable);
- }
- | RELAY relay_backup relay_as relay_source relay_helo {
- rule->r_action = A_RELAY;
- rule->r_as = $3;
- rule->r_value.relayhost.flags |= F_BACKUP;
-
- if ($2)
- strlcpy(rule->r_value.relayhost.hostname, $2,
- sizeof (rule->r_value.relayhost.hostname));
- else
- strlcpy(rule->r_value.relayhost.hostname,
- env->sc_hostname,
- sizeof (rule->r_value.relayhost.hostname));
- free($2);
+ ;
- if ($4)
- strlcpy(rule->r_value.relayhost.sourcetable, $4,
- sizeof rule->r_value.relayhost.sourcetable);
- if ($5)
- strlcpy(rule->r_value.relayhost.helotable, $5,
- sizeof rule->r_value.relayhost.helotable);
+relay_action : RELAY relay {
+ rule->r_action = A_RELAY;
}
- | RELAY VIA STRING certificate credentials relay_as relay_source relay_helo {
- struct table *t;
-
+ | RELAY VIA STRING {
rule->r_action = A_RELAYVIA;
- rule->r_as = $6;
-
if (! text_to_relayhost(&rule->r_value.relayhost, $3)) {
yyerror("error: invalid url: %s", $3);
free($3);
- free($4);
- free($6);
YYERROR;
}
free($3);
-
+ } relay_via {
/* no worries, F_AUTH cant be set without SSL */
if (rule->r_value.relayhost.flags & F_AUTH) {
- if (! $5) {
+ if (rule->r_value.relayhost.authtable[0] == '\0') {
yyerror("error: auth without auth table");
- free($4);
- free($6);
YYERROR;
}
- t = $5;
- strlcpy(rule->r_value.relayhost.authtable, t->t_name,
- sizeof(rule->r_value.relayhost.authtable));
}
-
- if ($4 != NULL) {
- if (strlcpy(rule->r_value.relayhost.cert, $4,
- sizeof(rule->r_value.relayhost.cert))
- >= sizeof(rule->r_value.relayhost.cert))
- fatal("certificate path too long");
- }
- free($4);
-
- if ($7)
- strlcpy(rule->r_value.relayhost.sourcetable, $7,
- sizeof rule->r_value.relayhost.sourcetable);
- if ($8)
- strlcpy(rule->r_value.relayhost.helotable, $8,
- sizeof rule->r_value.relayhost.helotable);
}
;
-from : FROM tables {
- struct table *t = $2;
+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;
}
-
- $$ = t;
+ 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;
}
- | FROM ANY {
- $$ = table_find("<anyhost>", NULL);
+ ;
+
+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;
}
- | FROM LOCAL {
- $$ = table_find("<localhost>", NULL);
+ | FOR negation ANY {
+ if (rule->r_destination) {
+ yyerror("for specified multiple times");
+ YYERROR;
+ }
+ rule->r_notdestination = $2;
+ rule->r_destination = table_find("<anydestination>", NULL);
}
- | /* empty */ {
- $$ = table_find("<localhost>", 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 tables {
- struct table *t = $2;
+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;
+ }
+ ;
- $$ = 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;
}
- | /* empty */ { $$ = NULL; }
;
-rule : ACCEPT {
- rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT");
- } tagged from sender FOR destination usermapping action expire {
+forwardonly : FORWARDONLY {
+ if (rule->r_forwardonly) {
+ yyerror("forward-only specified multiple times");
+ YYERROR;
+ }
+ rule->r_forwardonly = 1;
+ }
+ ;
- rule->r_decision = R_ACCEPT;
- rule->r_sources = $4;
- rule->r_senders = $5;
- rule->r_destination = $7;
- rule->r_mapping = $8;
- if ($3) {
- 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);
+expire : EXPIRE STRING {
+ if (rule->r_qexpire != -1) {
+ yyerror("expire specified multiple times");
+ YYERROR;
}
- rule->r_qexpire = $10;
+ 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
+ ;
+decision : opt_decision decision
+ |
+ ;
- if (rule->r_mapping && rule->r_desttype == DEST_VDOM) {
- enum table_type type;
+opt_lookup : userbase
+ | usermapping
+ ;
+lookup : opt_lookup lookup
+ |
+ ;
- switch (rule->r_action) {
- case A_RELAY:
- case A_RELAYVIA:
- type = T_LIST;
- break;
- default:
- type = T_HASH;
- break;
+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_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 (! table_check_service(rule->r_mapping, K_ALIAS) &&
- ! table_check_type(rule->r_mapping, type)) {
- yyerror("invalid use of table \"%s\" as VIRTUAL parameter",
- rule->r_mapping->t_name);
+ 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");
- } tagged from sender FOR destination usermapping {
rule->r_decision = R_REJECT;
- rule->r_sources = $4;
- rule->r_senders = $5;
- rule->r_destination = $7;
- rule->r_mapping = $8;
- if ($3) {
- 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_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;
}
@@ -982,18 +1060,21 @@ lookup(char *s)
{ "auth-optional", AUTH_OPTIONAL },
{ "backup", BACKUP },
{ "bounce-warn", BOUNCEWARN },
+ { "ca", CA },
{ "certificate", CERTIFICATE },
{ "compression", COMPRESSION },
{ "deliver", DELIVER },
+ { "dhparams", DHPARAMS },
{ "domain", DOMAIN },
{ "encryption", ENCRYPTION },
{ "expire", EXPIRE },
{ "filter", FILTER },
{ "filterchain", FILTERCHAIN },
{ "for", FOR },
+ { "forward-only", FORWARDONLY },
{ "from", FROM },
- { "helo", HELO },
{ "hostname", HOSTNAME },
+ { "hostnames", HOSTNAMES },
{ "include", INCLUDE },
{ "inet4", INET4 },
{ "inet6", INET6 },
@@ -1003,19 +1084,22 @@ lookup(char *s)
{ "lmtp", LMTP },
{ "local", LOCAL },
{ "maildir", MAILDIR },
+ { "mask-source", MASK_SOURCE },
{ "max-message-size", MAXMESSAGESIZE },
{ "mbox", MBOX },
{ "mda", MDA },
{ "mta", MTA },
{ "on", ON },
+ { "pki", PKI },
{ "port", PORT },
{ "queue", QUEUE },
+ { "recipient", RECIPIENT },
{ "reject", REJECT },
{ "relay", RELAY },
+ { "secure", SECURE },
{ "sender", SENDER },
{ "smtps", SMTPS },
{ "source", SOURCE },
- { "ssl", SSL },
{ "table", TABLE },
{ "tag", TAG },
{ "tagged", TAGGED },
@@ -1023,6 +1107,7 @@ lookup(char *s)
{ "tls-require", TLS_REQUIRE },
{ "to", TO },
{ "userbase", USERBASE },
+ { "verify", VERIFY },
{ "via", VIA },
{ "virtual", VIRTUAL },
};
@@ -1363,6 +1448,8 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
conf = x_conf;
bzero(conf, sizeof(*conf));
+ strlcpy(conf->sc_hostname, hostname, sizeof(conf->sc_hostname));
+
conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE;
conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict));
@@ -1425,6 +1512,10 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
table_add(t, "localhost", NULL);
table_add(t, hostname, NULL);
+ t = table_create("static", "<anydestination>", NULL, NULL);
+ t->t_type = T_LIST;
+ table_add(t, "*", NULL);
+
/* can't truncate here */
(void)strlcpy(hostname_copy, hostname, sizeof hostname_copy);
@@ -1462,9 +1553,6 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
errors++;
}
- if (strlen(conf->sc_hostname) == 0)
- strlcpy(conf->sc_hostname, hostname, sizeof conf->sc_hostname);
-
if (errors) {
purge_config(PURGE_EVERYTHING);
return (-1);
@@ -1547,6 +1635,79 @@ symget(const char *nam)
return (NULL);
}
+static void
+create_listener(struct listenerlist *ll, struct listen_opts *lo)
+{
+ uint16_t flags;
+
+ if (lo->port != 0 && lo->ssl == F_SSL)
+ errx(1, "invalid listen option: tls/smtps on same port");
+
+ if (lo->auth != 0 && !lo->ssl)
+ errx(1, "invalid listen option: auth requires tls/smtps");
+
+ if (lo->pki && !lo->ssl)
+ errx(1, "invalid listen option: pki requires tls/smtps");
+
+ if (lo->ssl && !lo->pki)
+ errx(1, "invalid listen option: tls/smtps requires pki");
+
+ flags = lo->flags;
+
+
+ if (lo->port) {
+ lo->flags = lo->ssl|lo->auth|flags;
+ lo->port = htons(lo->port);
+ if (! interface(ll, lo))
+ if (host(ll, lo) <= 0)
+ errx(1, "invalid virtual ip or interface: %s", lo->ifx);
+ }
+ else {
+ if (lo->ssl & F_SMTPS) {
+ lo->port = htons(465);
+ lo->flags = F_SMTPS|lo->auth|flags;
+ if (! interface(ll, lo))
+ if (host(ll, lo) <= 0)
+ errx(1, "invalid virtual ip or interface: %s", lo->ifx);
+ }
+
+ if (! lo->ssl || (lo->ssl & F_STARTTLS)) {
+ lo->port = htons(25);
+ lo->flags = lo->auth|flags;
+ if (lo->ssl & F_STARTTLS)
+ lo->flags |= F_STARTTLS;
+ if (! interface(ll, lo))
+ if (host(ll, lo) <= 0)
+ errx(1, "invalid virtual ip or interface: %s", lo->ifx);
+ }
+ }
+}
+
+static void
+config_listener(struct listener *h, struct listen_opts *lo)
+{
+ h->fd = -1;
+ h->port = lo->port;
+ h->flags = lo->flags;
+
+ if (lo->hostname == NULL)
+ lo->hostname = conf->sc_hostname;
+
+ h->ssl = NULL;
+ h->ssl_cert_name[0] = '\0';
+
+ if (lo->authtable != NULL)
+ (void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable));
+ if (lo->pki != NULL)
+ (void)strlcpy(h->ssl_cert_name, lo->pki, sizeof(h->ssl_cert_name));
+ if (lo->tag != NULL)
+ (void)strlcpy(h->tag, lo->tag, sizeof(h->tag));
+
+ (void)strlcpy(h->hostname, lo->hostname, sizeof(h->hostname));
+ if (lo->hostnametable)
+ (void)strlcpy(h->hostnametable, lo->hostnametable->t_name, sizeof(h->hostnametable));
+}
+
struct listener *
host_v4(const char *s, in_port_t port)
{
@@ -1590,8 +1751,7 @@ host_v6(const char *s, in_port_t port)
}
int
-host_dns(const char *s, const char *tag, const char *cert,
- struct listenerlist *al, int max, in_port_t port, uint8_t flags)
+host_dns(struct listenerlist *al, struct listen_opts *lo)
{
struct addrinfo hints, *res0, *res;
int error, cnt = 0;
@@ -1602,107 +1762,74 @@ host_dns(const char *s, const char *tag, const char *cert,
bzero(&hints, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
- error = getaddrinfo(s, NULL, &hints, &res0);
+ error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
return (0);
if (error) {
- log_warnx("warn: host_dns: could not parse \"%s\": %s", s,
+ log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
gai_strerror(error));
return (-1);
}
- for (res = res0; res && cnt < max; res = res->ai_next) {
+ for (res = res0; res; res = res->ai_next) {
if (res->ai_family != AF_INET &&
res->ai_family != AF_INET6)
continue;
h = xcalloc(1, sizeof(*h), "host_dns");
- h->port = port;
- h->flags = flags;
h->ss.ss_family = res->ai_family;
- h->ssl = NULL;
- h->ssl_cert_name[0] = '\0';
- if (cert != NULL)
- (void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
- if (tag != NULL)
- (void)strlcpy(h->tag, tag, sizeof(h->tag));
-
if (res->ai_family == AF_INET) {
sain = (struct sockaddr_in *)&h->ss;
sain->sin_len = sizeof(struct sockaddr_in);
sain->sin_addr.s_addr = ((struct sockaddr_in *)
res->ai_addr)->sin_addr.s_addr;
- sain->sin_port = port;
+ sain->sin_port = lo->port;
} else {
sin6 = (struct sockaddr_in6 *)&h->ss;
sin6->sin6_len = sizeof(struct sockaddr_in6);
memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
- sin6->sin6_port = port;
+ sin6->sin6_port = lo->port;
}
+ config_listener(h, lo);
+
TAILQ_INSERT_HEAD(al, h, entry);
cnt++;
}
- if (cnt == max && res) {
- log_warnx("warn: host_dns: %s resolves to more than %d hosts",
- s, max);
- }
+
freeaddrinfo(res0);
return (cnt);
}
int
-host(const char *s, const char *tag, const char *cert, struct listenerlist *al,
- int max, in_port_t port, const char *authtable, uint8_t flags,
- const char *helo)
+host(struct listenerlist *al, struct listen_opts *lo)
{
struct listener *h;
- port = htons(port);
-
- h = host_v4(s, port);
+ h = host_v4(lo->ifx, lo->port);
/* IPv6 address? */
if (h == NULL)
- h = host_v6(s, port);
+ h = host_v6(lo->ifx, lo->port);
if (h != NULL) {
- h->port = port;
- h->flags = flags;
- if (h->flags & F_SSL)
- if (cert == NULL)
- cert = s;
- h->ssl = NULL;
- h->ssl_cert_name[0] = '\0';
- if (authtable != NULL)
- (void)strlcpy(h->authtable, authtable, sizeof(h->authtable));
- if (cert != NULL)
- (void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
- if (tag != NULL)
- (void)strlcpy(h->tag, tag, sizeof(h->tag));
- if (helo != NULL)
- (void)strlcpy(h->helo, helo, sizeof(h->helo));
-
+ config_listener(h, lo);
TAILQ_INSERT_HEAD(al, h, entry);
return (1);
}
- return (host_dns(s, tag, cert, al, max, port, flags));
+ return (host_dns(al, lo));
}
int
-interface(const char *s, int family, const char *tag, const char *cert,
- struct listenerlist *al, int max, in_port_t port, const char *authtable, uint8_t flags,
- const char *helo)
+interface(struct listenerlist *al, struct listen_opts *lo)
{
struct ifaddrs *ifap, *p;
struct sockaddr_in *sain;
struct sockaddr_in6 *sin6;
struct listener *h;
- int ret = 0;
-
- port = htons(port);
+ int ret = 0;
if (getifaddrs(&ifap) == -1)
fatal("getifaddrs");
@@ -1710,10 +1837,10 @@ interface(const char *s, int family, const char *tag, const char *cert,
for (p = ifap; p != NULL; p = p->ifa_next) {
if (p->ifa_addr == NULL)
continue;
- if (strcmp(p->ifa_name, s) != 0 &&
- ! is_if_in_group(p->ifa_name, s))
+ if (strcmp(p->ifa_name, lo->ifx) != 0 &&
+ ! is_if_in_group(p->ifa_name, lo->ifx))
continue;
- if (family != AF_UNSPEC && family != p->ifa_addr->sa_family)
+ if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
continue;
h = xcalloc(1, sizeof(*h), "interface");
@@ -1723,14 +1850,14 @@ interface(const char *s, int family, const char *tag, const char *cert,
sain = (struct sockaddr_in *)&h->ss;
*sain = *(struct sockaddr_in *)p->ifa_addr;
sain->sin_len = sizeof(struct sockaddr_in);
- sain->sin_port = port;
+ sain->sin_port = lo->port;
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&h->ss;
*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
sin6->sin6_len = sizeof(struct sockaddr_in6);
- sin6->sin6_port = port;
+ sin6->sin6_port = lo->port;
break;
default:
@@ -1738,22 +1865,7 @@ interface(const char *s, int family, const char *tag, const char *cert,
continue;
}
- h->fd = -1;
- h->port = port;
- h->flags = flags;
- if (h->flags & F_SSL)
- if (cert == NULL)
- cert = s;
- h->ssl = NULL;
- h->ssl_cert_name[0] = '\0';
- if (authtable != NULL)
- (void)strlcpy(h->authtable, authtable, sizeof(h->authtable));
- if (cert != NULL)
- (void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
- if (tag != NULL)
- (void)strlcpy(h->tag, tag, sizeof(h->tag));
- if (helo != NULL)
- (void)strlcpy(h->helo, helo, sizeof(h->helo));
+ config_listener(h, lo);
ret = 1;
TAILQ_INSERT_HEAD(al, h, entry);
}
@@ -1894,7 +2006,6 @@ end:
return ret;
}
-
struct filter *
create_filter(const char *name, const char *path)
{
diff --git a/usr.sbin/smtpd/ruleset.c b/usr.sbin/smtpd/ruleset.c
index 7042ee090ca..9e8afbde20d 100644
--- a/usr.sbin/smtpd/ruleset.c
+++ b/usr.sbin/smtpd/ruleset.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ruleset.c,v 1.28 2013/05/24 17:03:14 eric Exp $ */
+/* $OpenBSD: ruleset.c,v 1.29 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -35,7 +35,7 @@
static int ruleset_check_source(struct table *,
const struct sockaddr_storage *, int);
-static int ruleset_check_sender(struct table *, const struct mailaddr *);
+static int ruleset_check_mailaddr(struct table *, const struct mailaddr *);
struct rule *
ruleset_match(const struct envelope *evp)
@@ -47,24 +47,39 @@ ruleset_match(const struct envelope *evp)
TAILQ_FOREACH(r, env->sc_rules, r_entry) {
- if (r->r_tag[0] != '\0' && strcmp(r->r_tag, evp->tag) != 0)
- continue;
+ 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;
+ }
ret = ruleset_check_source(r->r_sources, ss, evp->flags);
if (ret == -1) {
errno = EAGAIN;
return (NULL);
}
- if (ret == 0)
+ if ((ret == 0 && !r->r_notsources) || (ret != 0 && r->r_notsources))
continue;
if (r->r_senders) {
- ret = ruleset_check_sender(r->r_senders, &evp->sender);
+ 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)
+ if ((ret == 0 && !r->r_notrecipients) || (ret != 0 && r->r_notrecipients))
continue;
}
@@ -75,19 +90,21 @@ ruleset_match(const struct envelope *evp)
errno = EAGAIN;
return NULL;
}
- if (ret) {
- if (r->r_desttype == DEST_VDOM &&
- (r->r_action == A_RELAY || r->r_action == A_RELAYVIA)) {
- if (! aliases_virtual_check(r->r_mapping,
- &evp->rcpt)) {
- return NULL;
- }
+ if ((ret == 0 && !r->r_notdestination) || (ret != 0 && r->r_notdestination))
+ continue;
+
+ if (r->r_desttype == DEST_VDOM &&
+ (r->r_action == A_RELAY || r->r_action == A_RELAYVIA)) {
+ if (! aliases_virtual_check(r->r_mapping,
+ &evp->rcpt)) {
+ return NULL;
}
- goto matched;
}
+ goto matched;
}
errno = 0;
+ log_trace(TRACE_RULES, "no rule matched");
return (NULL);
matched:
@@ -120,7 +137,7 @@ ruleset_check_source(struct table *table, const struct sockaddr_storage *ss,
}
static int
-ruleset_check_sender(struct table *table, const struct mailaddr *maddr)
+ruleset_check_mailaddr(struct table *table, const struct mailaddr *maddr)
{
const char *key;
@@ -138,6 +155,5 @@ ruleset_check_sender(struct table *table, const struct mailaddr *maddr)
default:
break;
}
-
return 0;
}
diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c
index ed71a943fc4..2d4d8e6943d 100644
--- a/usr.sbin/smtpd/smtp.c
+++ b/usr.sbin/smtpd/smtp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp.c,v 1.129 2013/10/27 11:01:47 eric Exp $ */
+/* $OpenBSD: smtp.c,v 1.130 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -66,6 +66,7 @@ smtp_imsg(struct mproc *p, struct imsg *imsg)
switch (imsg->hdr.type) {
case IMSG_DNS_PTR:
case IMSG_LKA_EXPAND_RCPT:
+ case IMSG_LKA_HELO:
case IMSG_LKA_AUTHENTICATE:
case IMSG_LKA_SSL_INIT:
case IMSG_LKA_SSL_VERIFY:
@@ -296,7 +297,7 @@ smtp_setup_events(void)
TAILQ_FOREACH(l, env->sc_listeners, entry) {
log_debug("debug: smtp: listen on %s port %d flags 0x%01x"
- " cert \"%s\"", ss_to_text(&l->ss), ntohs(l->port),
+ " pki \"%s\"", ss_to_text(&l->ss), ntohs(l->port),
l->flags, l->ssl_cert_name);
session_socket_blockmode(l->fd, BM_NONBLOCK);
@@ -361,6 +362,8 @@ smtp_enqueue(uid_t *euid)
strlcpy(listener->tag, "local", sizeof(listener->tag));
listener->ss.ss_family = AF_LOCAL;
listener->ss.ss_len = sizeof(struct sockaddr *);
+ strlcpy(listener->hostname, "localhost",
+ sizeof(listener->hostname));
}
/*
diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c
index bc59a6e0bf3..0e7563c7bba 100644
--- a/usr.sbin/smtpd/smtp_session.c
+++ b/usr.sbin/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.187 2013/10/28 17:02:08 eric Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.188 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -114,6 +114,7 @@ struct smtp_session {
struct listener *listener;
struct sockaddr_storage ss;
char hostname[SMTPD_MAXHOSTNAMELEN];
+ char smtpname[SMTPD_MAXHOSTNAMELEN];
int flags;
int phase;
@@ -151,9 +152,10 @@ struct smtp_session {
((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \
!((s)->flags & SF_AUTHENTICATED))
-static int smtp_mailaddr(struct mailaddr *, char *, int, char **);
+static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
static void smtp_session_init(void);
static void smtp_connected(struct smtp_session *);
+static void smtp_send_banner(struct smtp_session *);
static void smtp_mfa_response(struct smtp_session *, int, uint32_t,
const char *);
static void smtp_io(struct io *, int);
@@ -189,6 +191,7 @@ static struct { int code; const char *cmd; } commands[] = {
};
static struct tree wait_lka_ptr;
+static struct tree wait_lka_helo;
static struct tree wait_lka_rcpt;
static struct tree wait_mfa_response;
static struct tree wait_mfa_data;
@@ -206,6 +209,7 @@ smtp_session_init(void)
if (!init) {
tree_init(&wait_lka_ptr);
+ tree_init(&wait_lka_helo);
tree_init(&wait_lka_rcpt);
tree_init(&wait_mfa_response);
tree_init(&wait_mfa_data);
@@ -247,6 +251,8 @@ smtp_session(struct listener *listener, int sock,
s->state = STATE_NEW;
s->phase = PHASE_INIT;
+ strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname));
+
/* For local enqueueing, the hostname is already set */
if (hostname) {
s->flags |= SF_AUTHENTICATED;
@@ -273,7 +279,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
void *ssl;
char user[SMTPD_MAXLOGNAME];
struct msg m;
- const char *line;
+ const char *line, *helo;
uint64_t reqid, evpid;
uint32_t code, msgid;
int status, success, dnserror;
@@ -319,6 +325,20 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
io_reload(&s->io);
return;
+ case IMSG_LKA_HELO:
+ m_msg(&m, imsg);
+ m_get_id(&m, &reqid);
+ s = tree_xpop(&wait_lka_helo, reqid);
+ m_get_int(&m, &status);
+ if (status == LKA_OK) {
+ m_get_string(&m, &helo);
+ strlcpy(s->smtpname, helo, sizeof(s->smtpname));
+ }
+ m_end(&m);
+ smtp_reply(s, SMTPD_BANNER, s->smtpname, SMTPD_NAME);
+ io_reload(&s->io);
+ return;
+
case IMSG_MFA_SMTP_RESPONSE:
m_msg(&m, imsg);
m_get_id(&m, &reqid);
@@ -369,13 +389,15 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
return;
}
- fprintf(s->ofile,
- "Received: from %s (%s [%s]);\n"
- "\tby %s (%s) with %sSMTP%s%s id %08x;\n",
- s->evp.helo,
- s->hostname,
- ss_to_text(&s->ss),
- s->listener->helo[0] ? s->listener->helo : env->sc_hostname,
+ fprintf(s->ofile, "Received: ");
+ if (! (s->listener->flags & F_MASK_SOURCE)) {
+ fprintf(s->ofile, "from %s (%s [%s]);\n\t",
+ s->evp.helo,
+ s->hostname,
+ ss_to_text(&s->ss));
+ }
+ fprintf(s->ofile, "by %s (%s) with %sSMTP%s%s id %08x;\n",
+ s->smtpname,
SMTPD_NAME,
s->flags & SF_EHLO ? "E" : "",
s->flags & SF_SECURE ? "S" : "",
@@ -576,7 +598,12 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
if (resp_ca_vrfy->status == CA_OK)
s->flags |= SF_VERIFIED;
-
+ else if (s->listener->flags & F_TLS_VERIFY) {
+ log_info("smtp-in: Disconnecting session %016" PRIx64
+ ": SSL certificate check failed", s->id);
+ smtp_free(s, "SSL certificate check failed");
+ return;
+ }
smtp_io(&s->io, IO_TLSVERIFIED);
io_resume(&s->io, IO_PAUSE_IN);
return;
@@ -621,11 +648,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code,
tree_xset(&wait_ssl_init, s->id, s);
return;
}
- if (s->listener->helo[0])
- smtp_reply(s, SMTPD_BANNER, s->listener->helo, SMTPD_NAME);
- else
- smtp_reply(s, SMTPD_BANNER, env->sc_hostname, SMTPD_NAME);
- io_reload(&s->io);
+ smtp_send_banner(s);
return;
case IMSG_MFA_REQ_HELO:
@@ -640,7 +663,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code,
smtp_enter_state(s, STATE_HELO);
smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you",
(s->flags & SF_EHLO) ? '-' : ' ',
- env->sc_hostname,
+ s->smtpname,
s->evp.helo,
ss_to_text(&s->ss));
@@ -755,6 +778,13 @@ smtp_io(struct io *io, int evt)
break;
}
+ if (s->listener->flags & F_TLS_VERIFY) {
+ log_info("smtp-in: Disconnecting session %016" PRIx64
+ ": client did not present certificate", s->id);
+ smtp_free(s, "client did not present certificate");
+ return;
+ }
+
/* No verification required, cascade */
case IO_TLSVERIFIED:
@@ -769,8 +799,8 @@ smtp_io(struct io *io, int evt)
if (s->listener->flags & F_SMTPS) {
stat_increment("smtp.smtps", 1);
- smtp_reply(s, SMTPD_BANNER, env->sc_hostname, SMTPD_NAME);
io_set_write(&s->io);
+ smtp_send_banner(s);
}
else {
stat_increment("smtp.tls", 1);
@@ -1066,7 +1096,8 @@ smtp_command(struct smtp_session *s, char *line)
smtp_message_reset(s, 1);
- if (smtp_mailaddr(&s->evp.sender, args, 1, &args) == 0) {
+ if (smtp_mailaddr(&s->evp.sender, args, 1, &args,
+ s->smtpname) == 0) {
smtp_reply(s, "553 Sender address syntax error");
break;
}
@@ -1093,7 +1124,8 @@ smtp_command(struct smtp_session *s, char *line)
break;
}
- if (smtp_mailaddr(&s->evp.rcpt, args, 0, &args) == 0) {
+ if (smtp_mailaddr(&s->evp.rcpt, args, 0, &args,
+ s->smtpname) == 0) {
smtp_reply(s,
"553 Recipient address syntax error");
break;
@@ -1329,6 +1361,34 @@ smtp_connected(struct smtp_session *s)
smtp_wait_mfa(s, IMSG_MFA_REQ_CONNECT);
}
+static void
+smtp_send_banner(struct smtp_session *s)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr *sa;
+ socklen_t sa_len;
+
+ if (s->listener->hostnametable[0]) {
+ sa_len = sizeof(ss);
+ sa = (struct sockaddr *)&ss;
+ if (getsockname(s->io.sock, sa, &sa_len) == -1) {
+ log_warn("warn: getsockname()");
+ }
+ else {
+ m_create(p_lka, IMSG_LKA_HELO, 0, 0, -1);
+ m_add_id(p_lka, s->id);
+ m_add_string(p_lka, s->listener->hostnametable);
+ m_add_sockaddr(p_lka, sa);
+ m_close(p_lka);
+ tree_xset(&wait_lka_helo, s->id, s);
+ return;
+ }
+ }
+
+ smtp_reply(s, SMTPD_BANNER, s->smtpname, SMTPD_NAME);
+ io_reload(&s->io);
+}
+
void
smtp_enter_state(struct smtp_session *s, int newstate)
{
@@ -1416,6 +1476,7 @@ smtp_message_reset(struct smtp_session *s, int prepare)
if (prepare) {
s->evp.ss = s->ss;
strlcpy(s->evp.tag, s->listener->tag, sizeof(s->evp.tag));
+ strlcpy(s->evp.smtpname, s->smtpname, sizeof(s->evp.smtpname));
strlcpy(s->evp.hostname, s->hostname, sizeof s->evp.hostname);
strlcpy(s->evp.helo, s->helo, sizeof s->evp.helo);
@@ -1515,7 +1576,8 @@ smtp_free(struct smtp_session *s, const char * reason)
}
static int
-smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args)
+smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
+ const char *domain)
{
char *p, *e;
@@ -1549,6 +1611,16 @@ smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args)
maddr->user[0] == '\0' &&
maddr->domain[0] == '\0')
return (1);
+
+ /* We accept empty domain for RCPT TO if user is postmaster */
+ if (!mailfrom &&
+ strcasecmp(maddr->user, "postmaster") == 0 &&
+ maddr->domain[0] == '\0') {
+ (void)strlcpy(maddr->domain, domain,
+ sizeof(maddr->domain));
+ return (1);
+ }
+
return (0);
}
diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index 4ed2631723d..97586931bd6 100644
--- a/usr.sbin/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.203 2013/10/30 21:37:48 eric Exp $ */
+/* $OpenBSD: smtpd.c,v 1.204 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -75,7 +75,7 @@ static int offline_enqueue(char *);
static void purge_task(int, short, void *);
static void log_imsg(int, int, struct imsg *);
static int parent_auth_user(const char *, const char *);
-static void load_ssl_trees(void);
+static void load_ssl_tree(void);
enum child_type {
CHILD_DAEMON,
@@ -343,8 +343,6 @@ parent_send_config_smtp(void)
m_compose(p_smtp, IMSG_CONF_START, 0, 0, -1, NULL, 0);
while (dict_iter(env->sc_ssl_dict, &iter, NULL, (void **)&s)) {
- if (!(s->flags & F_SCERT))
- continue;
iov[0].iov_base = s;
iov[0].iov_len = sizeof(*s);
iov[1].iov_base = s->ssl_cert;
@@ -455,6 +453,12 @@ parent_send_config_lka()
&r->r_senders->t_name,
sizeof(r->r_senders->t_name));
}
+ if (r->r_recipients) {
+ m_compose(p_lka, IMSG_CONF_RULE_RECIPIENT,
+ 0, 0, -1,
+ &r->r_recipients->t_name,
+ sizeof(r->r_recipients->t_name));
+ }
if (r->r_destination) {
m_compose(p_lka, IMSG_CONF_RULE_DESTINATION,
0, 0, -1,
@@ -711,7 +715,7 @@ main(int argc, char *argv[])
errx(1, "config file exceeds SMTPD_MAXPATHLEN");
if (env->sc_opts & SMTPD_OPT_NOACTION) {
- load_ssl_trees();
+ load_ssl_tree();
fprintf(stderr, "configuration OK\n");
exit(0);
}
@@ -760,7 +764,7 @@ main(int argc, char *argv[])
errx(1, "machine does not have a hostname set");
env->sc_uptime = time(NULL);
- load_ssl_trees();
+ load_ssl_tree();
fork_peers();
@@ -814,44 +818,33 @@ main(int argc, char *argv[])
}
static void
-load_ssl_trees(void)
+load_ssl_tree(void)
{
- struct listener *l;
struct ssl *ssl;
- struct rule *r;
-
- log_debug("debug: init server-ssl tree");
- TAILQ_FOREACH(l, env->sc_listeners, entry) {
- if (!(l->flags & F_SSL))
- continue;
+ void *iter_dict;
+ const char *k;
- ssl = dict_get(env->sc_ssl_dict, l->ssl_cert_name);
- if (ssl == NULL) {
- if (! ssl_load_certfile(&ssl, "/etc/mail/certs",
- l->ssl_cert_name, F_SCERT))
- errx(1, "cannot load certificate: %s",
- l->ssl_cert_name);
- dict_set(env->sc_ssl_dict, ssl->ssl_name, ssl);
- }
- }
-
- log_debug("debug: init client-ssl tree");
- TAILQ_FOREACH(r, env->sc_rules, r_entry) {
- if (r->r_action != A_RELAY && r->r_action != A_RELAYVIA)
- continue;
- if (! r->r_value.relayhost.cert[0])
- continue;
-
- ssl = dict_get(env->sc_ssl_dict, r->r_value.relayhost.cert);
- if (ssl)
- ssl->flags |= F_CCERT;
- else {
- if (! ssl_load_certfile(&ssl, "/etc/mail/certs",
- r->r_value.relayhost.cert, F_CCERT))
- errx(1, "cannot load certificate: %s",
- r->r_value.relayhost.cert);
- dict_set(env->sc_ssl_dict, ssl->ssl_name, ssl);
- }
+ log_debug("debug: init ssl-tree");
+ iter_dict = NULL;
+ while (dict_iter(env->sc_ssl_dict, &iter_dict, &k, (void **)&ssl)) {
+ log_debug("debug: loading pki information for %s", k);
+
+ if (ssl->ssl_cert_file == NULL)
+ errx(1, "load_ssl_tree: missing certificate file for %s", k);
+ if (ssl->ssl_key_file == NULL)
+ errx(1, "load_ssl_tree: missing key file for %s", k);
+
+ if (! ssl_load_certificate(ssl, ssl->ssl_cert_file))
+ errx(1, "load_ssl_tree: failed to load certificate file for %s", k);
+ if (! ssl_load_keyfile(ssl, ssl->ssl_key_file))
+ errx(1, "load_ssl_tree: failed to load certificate file for %s", k);
+
+ if (ssl->ssl_ca_file)
+ if (! ssl_load_cafile(ssl, ssl->ssl_ca_file))
+ errx(1, "load_ssl_tree: failed to load CA file for %s", k);
+ if (ssl->ssl_dhparams_file)
+ if (! ssl_load_dhparams(ssl, ssl->ssl_dhparams_file))
+ errx(1, "load_ssl_tree: failed to load dhparams file for %s", k);
}
}
@@ -1349,7 +1342,7 @@ static void
log_imsg(int to, int from, struct imsg *imsg)
{
- if (to == PROC_CONTROL)
+ if (to == PROC_CONTROL && imsg->hdr.type == IMSG_STAT_SET)
return;
if (imsg->fd != -1)
diff --git a/usr.sbin/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5
index a66250eb2f3..13a2c56dd27 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.106 2013/10/29 14:30:05 eric Exp $
+.\" $OpenBSD: smtpd.conf.5,v 1.107 2013/11/06 10:01:29 eric 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: October 29 2013 $
+.Dd $Mdocdate: November 6 2013 $
.Dt SMTPD.CONF 5
.Os
.Sh NAME
@@ -79,32 +79,50 @@ 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.
.Pp
Following the accept/reject
-decision comes the client's IP address filter:
+decision comes the optional tag matching:
+.Bl -tag -width Ds
+.It Xo
+.Ic tagged
+.Op Ic \!
+.Ic tag
+.Xc
+If specified, the rule will only be matched if the client session was tagged with
+.Ar tag .
+.El
+.Pp
+After that the client's IP address filter is specified:
.Bl -tag -width Ds
.It Ic from any
Make the rule match regardless of the IP of connecting client.
-.It Ic from local
+.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 from Ar network
-The rule matches if the connection is made from the specified
-.Ar network ,
-specified in CIDR notation.
-.It Ic from Aq Ar table
+.It Xo
+.Ic from
+.Op Ic \!
+.Ic source
+.Ic table
+.Xc
The rule matches if the connection is made from a client whose address
is declared in the table
.Ar table .
-.It Ic tagged Ar tag
-If specified, the rule will only be matched if the client session was tagged
-.Ar tag .
.El
.Pp
In addition, finer filtering may be achieved on the sender if desired:
.Bl -tag -width Ds
-.It Ic sender Ar senders
+.It Xo
+.Ic sender
+.Op Ic \!
+.Ic senders
+.Xc
If specified, the rule will only be matched if the sender email address
is found in the table
.Ar senders .
@@ -124,7 +142,13 @@ Make the rule match regardless of the domain it is sent to.
The
.Ar vmap
table will be used as the virtual domain mapping.
-.It Ic for domain Ar domain Op Ic alias Aq Ar aliases
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Ar domain
+.Op Ic alias Aq Ar aliases
+.Xc
This rule applies to mail destined for the specified
.Ar domain .
This parameter supports the
@@ -139,7 +163,13 @@ If specified, the table
.Ar aliases
is used for looking up alternative destinations for addresses in this
.Ar domain .
-.It Ic for domain Aq Ar domains Op Ic alias Aq Ar aliases
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Aq Ar domains
+.Op Ic alias Aq Ar aliases
+.Xc
This rule applies to mail destined to domains which are part of the table
.Ar domains .
.Pp
@@ -147,7 +177,13 @@ If specified, the table
.Ar aliases
is used for looking up alternative destinations for addresses in these
.Ar domains .
-.It Ic for domain Ar domain Ic virtual Aq Ar users
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Ar domain
+.Ic virtual Aq Ar users
+.Xc
This rule applies to mail destined for the specified virtual
.Ar domain .
This parameter supports the
@@ -166,7 +202,13 @@ For an example of how to configure the
.Ar users
table, see
.Xr makemap 8 .
-.It Ic for domain Ao Ar domains Ac Ic virtual Aq Ar users
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Ao Ar domains
+.Ac Ic virtual Aq Ar users
+.Xc
This rule applies to mail destined for the virtual domains specified
in the table
.Ar domains .
@@ -178,7 +220,12 @@ For an example of how to configure the
.Ar users
table, see
.Xr makemap 8 .
-.It Ic for local Op Ic alias Aq Ar aliases
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic local
+.Op Ic alias Aq Ar aliases
+.Xc
This rule applies to mail destined to
.Dq localhost
and to the default server name.
@@ -187,7 +234,12 @@ See the
entry for
.Pa /etc/mail/mailname
below for details of how the server name is determined.
-.It Ic for local virtual Aq Ar vmap
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic local
+.Ic virtual Aq Ar vmap
+.Xc
This rule applies to mail destined to
.Dq localhost
and to the default server name.
@@ -196,6 +248,21 @@ The
table will be used as the virtual domain mapping.
.El
.Pp
+Further filtering may be achieved on specific recipients if desired:
+.Bl -tag -width Ds
+.It Xo
+.Ic recipient
+.Op Ic \&!
+.Ar recipients
+.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
@@ -241,7 +308,12 @@ This parameter may use conversion specifiers that are expanded before use
.Op Ic backup Op Ar mx
.Op Ic as Ar address
.Op Ic source Ar source
-.Op Ic helo Ar names
+.Bk -words
+.Op Ic hostname Ar name
+.Op Ic hostnames Ar names
+.Ek
+.Op Ic pki Ar pkiname
+.Op Ic tls | verify
.Xc
Mail is relayed.
The routing decision is based on the DNS system.
@@ -275,7 +347,7 @@ If the
parameter is specified,
.Xr smtpd 8
will explicitly bind to an address found in the table referenced by
-.Ar table
+.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.
@@ -284,22 +356,61 @@ By default, when connecting to a remote server,
.Xr smtpd 8
advertises its default server name.
A
-.Ic helo
-parameter may be specified to advertise an alternate hostname.
+.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 OpenSMTPD 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, OpenSMTPD will refuse to relay unless remote host provides
+STARTTLS.
+.Pp
+If
+.Ic verify
+is specified, OpenSMTPD will refuse to relay unless remote host provides
+STARTTLS and the certificate it presented has been verified.
+.Pp
+Note that the
+.Ic tls
+and
+.Ic verify
+options are mutually exclusive and 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 certificate Ar name
.Op Ic auth Aq Ar auth
.Op Ic as Ar address
.Op Ic source Ar source
-.Op Ic helo Ar names
+.Op Ic hostname Ar name
+.Op Ic hostnames Ar names
+.Op Ic pki Ar pkiname
.Xc
Mail is relayed through the specified
.Ar host
@@ -319,7 +430,7 @@ For example:
.Bd -literal -offset indent
tls://mx1.example.org # use TLS
smtps://mx1.example.org # use SMTPS
-ssl://mx1.example.org # try SMTPS and \e
+secure://mx1.example.org # try SMTPS and \e
# fallback to TLS
.Ed
.Pp
@@ -327,20 +438,16 @@ 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
-ssl+auth://label@mx.example.org # over either \e
- # SMTPS or TLS
+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 certificate
-.Ar name
-is specified and exists in the
-.Pa /etc/mail/certs
-directory with a .crt extension, it will be used if the remote server
-requests a client certificate.
-Creation of certificates is documented in
-.Xr starttls 8 .
+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
@@ -378,14 +485,26 @@ By default, when connecting to a remote server,
.Xr smtpd 8
advertises its default server name.
A
-.Ic helo
-parameter may be specified to advertise an alternate hostname.
+.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.
.El
.Pp
Additional per-rule adjustments available:
@@ -435,11 +554,14 @@ to MXs for this domain.
.Ic listen on Ar interface
.Op Ar family
.Op Ic port Ar port
-.Op Ic tls | tls-require | smtps
-.Op Ic certificate Ar name
+.Op Ic tls | tls-require | smtps | secure
+.Op Ic pki Ar pkiname
.Op Ic auth | auth-optional
.Op Ic tag Ar tag
.Op Ic hostname Ar hostname
+.Op Ic hostnames Ar names
+.Op Ic mask-source
+.Op Ic verify
.Ek
.Xc
Specify an
@@ -467,36 +589,16 @@ 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.
+.Ic secure
+may be specified to provide both STARTTLS and SMTPS services.
Host certificates may be used for these connections,
-and are searched for in the
-.Pa /etc/mail/certs
-directory.
+and must be priorly declared using the pki directive.
If
-.Ic certificate
-is specified,
-a certificate
-.Ao Ar name Ac Ns .crt ,
-a key
-.Ao Ar name Ac Ns .key
-and Diffie-Hellman parameters
-.Ao Ar name Ac Ns .dh
-are searched for.
-A certificate authority may be appended to the .crt
-file to create a certificate chain.
-If no
-.Ic certificate
+.Ic pki
is specified,
-the default interface name is instead used,
-for example
-.Pa fxp0.crt ,
-.Pa fxp0.key ,
-.Pa fxp0.ca ,
-and
-.Pa fxp0.dh .
-If no DH parameters are provided, smtpd will use
-built-in parameters.
-Creation of certificates is documented in
-.Xr starttls 8 .
+a certificate matching
+.Ic name
+is searched for.
.Pp
If the
.Ic auth
@@ -525,6 +627,27 @@ 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 mask-source
+parameter is used, then the listener will skip the "from" part
+when prepending the "Received" header.
+.Pp
+If the listener is configured to provide SMTPS or STARTTLS and the
+.Ic verify
+parameter is used, then clients will be required to present a
+certificate that can be verified before a SMTP session can be
+initiated.
.It Ic max-message-size Ar n
Specify a maximum message size of
.Ar n
@@ -532,6 +655,35 @@ 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
+Associate the certificate located in
+.Ar certfile
+with
+.Ar hostname .
+.Pp
+A certificate chain may be created by appending one or many certificates,
+including a Certificate Authority certificate,
+to
+.Ar certfile .
+.Pp
+Creation of certificates is documented in
+.Xr starttls 8 .
+.It Ic pki Ar hostname Ic key Ar keyfile
+Associate the key located in
+.Ar keyfile
+with
+.Ar hostname .
+.It Ic pki Ar hostname Ic dhparams Ar dhfile
+Associate the Diffie-Hellman parameters located in
+.Ar dhfile
+with
+.Ar hostname .
+.Pp
+The parameters are used for ephemeral key exchange.
+If not specified, OpenSMTPD will use safely generated builtin parameters.
+.Pp
+Creation of Diffie-Hellman parameters is documented in
+.Xr openssl 1 .
.It Ic queue compression
Enable transparent compression of envelopes and messages.
The only supported algorithm at the moment is gzip.
@@ -540,7 +692,7 @@ Envelopes and messages may be inspected using the
or
.Xr gzcat 1
utilities.
-.It Ic queue encryption key Ar key
+.It Ic queue encryption Op key Ar key
Enable transparent encryption of envelopes and messages.
.Ar key
must be a 16-byte random key in hexadecimal representation.
@@ -551,6 +703,15 @@ utility as follow:
$ 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 "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
@@ -562,6 +723,10 @@ perform compression before encryption.
Tables are used to 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.
+Refer to
+.Xr table 5
+for the exhaustive documentation.
.Pp
The table is identified using table name
.Ar name ;
@@ -712,19 +877,25 @@ The mail server listens on all interfaces the default route(s) point to.
Mail with a local destination should be sent to an external mda.
First, the RSA certificate is created:
.Bd -literal -offset indent
-# openssl genrsa -out /etc/mail/certs/mail.example.com.key 4096
-# openssl req -new -x509 -key /etc/mail/certs/mail.example.com.key \e
- -out /etc/mail/certs/mail.example.com.crt -days 365
-# chmod 600 /etc/mail/certs/mail.example.com.*
+# openssl genrsa -out /etc/ssl/private/mail.example.com.key 4096
+# openssl req -new -x509 -key /etc/ssl/private/mail.example.com.key \e
+ -out /etc/ssl/mail.example.com.crt -days 365
+# chmod 600 /etc/ssl/mail.example.com.crt
+# chmod 600 /etc/ssl/private/mail.example.com.key
.Ed
.Pp
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"
+
listen on lo0
-listen on egress tls certificate mail.example.com auth
+listen on egress tls pki mail.example.com auth
+
table aliases db:/etc/mail/aliases.db
+
accept for local alias <aliases> deliver to mda "/path/to/mda -f -"
accept from any for domain example.org \e
deliver to mda "/path/to/mda -f -"
@@ -732,6 +903,7 @@ accept for any relay
.Ed
.Sh SEE ALSO
.Xr mailer.conf 5 ,
+.Xr table 5 ,
.Xr makemap 8 ,
.Xr smtpd 8
.Sh HISTORY
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 3baddd8f243..782d7ffb71c 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.432 2013/10/30 21:37:48 eric Exp $ */
+/* $OpenBSD: smtpd.h,v 1.433 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -30,24 +30,16 @@
#define CONF_FILE "/etc/mail/smtpd.conf"
#define MAILNAME_FILE "/etc/mail/mailname"
#define CA_FILE "/etc/ssl/cert.pem"
-#define MAX_LISTEN 16
+
#define PROC_COUNT 10
-#define MAX_NAME_SIZE 64
#define MAX_HOPS_COUNT 100
#define DEFAULT_MAX_BODY_SIZE (35*1024*1024)
-
#define MAX_TAG_SIZE 32
-
-#define MAX_TABLE_BACKEND_SIZE 32
-
-/* return and forward path size */
#define MAX_FILTER_NAME 32
#define EXPAND_BUFFER 1024
-#define SMTPD_QUEUE_INTERVAL (15 * 60)
-#define SMTPD_QUEUE_MAXINTERVAL (4 * 60 * 60)
#define SMTPD_QUEUE_EXPIRY (4 * 24 * 60 * 60)
#define SMTPD_USER "_smtpd"
#define SMTPD_QUEUE_USER "_smtpq"
@@ -80,9 +72,8 @@
#define F_STARTTLS_REQUIRE 0x20
#define F_AUTH_REQUIRE 0x40
#define F_LMTP 0x80
-
-#define F_SCERT 0x01
-#define F_CCERT 0x02
+#define F_MASK_SOURCE 0x100
+#define F_TLS_VERIFY 0x200
/* must match F_* for mta */
#define RELAY_STARTTLS 0x01
@@ -93,6 +84,7 @@
#define RELAY_BACKUP 0x10 /* XXX - MUST BE SYNC-ED WITH F_BACKUP */
#define RELAY_MX 0x20
#define RELAY_LMTP 0x80
+#define RELAY_TLS_VERIFY 0x200
struct userinfo {
char username[SMTPD_MAXLOGNAME];
@@ -107,14 +99,14 @@ struct netaddr {
};
struct relayhost {
- uint8_t flags;
+ uint16_t flags;
char hostname[SMTPD_MAXHOSTNAMELEN];
uint16_t port;
char cert[SMTPD_MAXPATHLEN];
char authtable[SMTPD_MAXPATHLEN];
char authlabel[SMTPD_MAXPATHLEN];
char sourcetable[SMTPD_MAXPATHLEN];
- char heloname[SMTPD_MAXPATHLEN];
+ char heloname[SMTPD_MAXHOSTNAMELEN];
char helotable[SMTPD_MAXPATHLEN];
};
@@ -325,6 +317,7 @@ enum dest_type {
};
enum action_type {
+ A_NONE,
A_RELAY,
A_RELAYVIA,
A_MAILDIR,
@@ -342,10 +335,19 @@ enum decision {
struct rule {
TAILQ_ENTRY(rule) r_entry;
enum decision r_decision;
+ uint8_t r_nottag;
char r_tag[MAX_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;
@@ -359,6 +361,7 @@ struct rule {
struct table *r_mapping;
struct table *r_userbase;
time_t r_qexpire;
+ uint8_t r_forwardonly;
};
struct delivery_mda {
@@ -424,7 +427,7 @@ struct expand {
struct expandnode *parent;
};
-#define SMTPD_ENVELOPE_VERSION 1
+#define SMTPD_ENVELOPE_VERSION 2
struct envelope {
TAILQ_ENTRY(envelope) entry;
@@ -434,6 +437,7 @@ struct envelope {
uint64_t id;
enum envelope_flags flags;
+ char smtpname[SMTPD_MAXHOSTNAMELEN];
char helo[SMTPD_MAXHOSTNAMELEN];
char hostname[SMTPD_MAXHOSTNAMELEN];
char errorline[SMTPD_MAXLINESIZE];
@@ -459,7 +463,7 @@ struct envelope {
};
struct listener {
- uint8_t flags;
+ uint16_t flags;
int fd;
struct sockaddr_storage ss;
in_port_t port;
@@ -470,7 +474,8 @@ struct listener {
void *ssl_ctx;
char tag[MAX_TAG_SIZE];
char authtable[SMTPD_MAXLINESIZE];
- char helo[SMTPD_MAXHOSTNAMELEN];
+ char hostname[SMTPD_MAXHOSTNAMELEN];
+ char hostnametable[SMTPD_MAXPATHLEN];
TAILQ_ENTRY(listener) entry;
};
diff --git a/usr.sbin/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c
index f7559a5abab..7b7adb8fd54 100644
--- a/usr.sbin/smtpd/ssl.c
+++ b/usr.sbin/smtpd/ssl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl.c,v 1.55 2013/10/26 12:27:59 eric Exp $ */
+/* $OpenBSD: ssl.c,v 1.56 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -235,75 +235,45 @@ ssl_ctx_create(void)
}
int
-ssl_load_certfile(struct ssl **sp, const char *path, const char *name, uint8_t flags)
+ssl_load_certificate(struct ssl *s, const char *pathname)
{
- struct ssl *s;
- char pathname[PATH_MAX];
- int ret;
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- fatal(NULL);
-
- s->flags = flags;
- (void)strlcpy(s->ssl_name, name, sizeof(s->ssl_name));
-
- ret = snprintf(pathname, sizeof(pathname), "%s/%s.crt",
- path ? path : "/etc/ssl", name);
- if (ret == -1 || (size_t)ret >= sizeof pathname)
- goto err;
s->ssl_cert = ssl_load_file(pathname, &s->ssl_cert_len, 0755);
if (s->ssl_cert == NULL)
- goto err;
+ return 0;
+ return 1;
+}
- ret = snprintf(pathname, sizeof(pathname), "%s/%s.key",
- path ? path : "/etc/ssl/private", name);
- if (ret == -1 || (size_t)ret >= sizeof pathname)
- goto err;
+int
+ssl_load_keyfile(struct ssl *s, const char *pathname)
+{
s->ssl_key = ssl_load_file(pathname, &s->ssl_key_len, 0700);
if (s->ssl_key == NULL)
- goto err;
+ return 0;
+ return 1;
+}
- ret = snprintf(pathname, sizeof(pathname), "%s/%s.ca",
- path ? path : "/etc/ssl", name);
- if (ret == -1 || (size_t)ret >= sizeof pathname)
- goto err;
+int
+ssl_load_cafile(struct ssl *s, const char *pathname)
+{
s->ssl_ca = ssl_load_file(pathname, &s->ssl_ca_len, 0755);
- if (s->ssl_ca == NULL) {
- if (errno == EACCES)
- goto err;
- log_info("info: No CA found in %s", pathname);
- }
+ if (s->ssl_ca == NULL)
+ return 0;
+ return 1;
+}
- ret = snprintf(pathname, sizeof(pathname), "%s/%s.dh",
- path ? path : "/etc/ssl", name);
- if (ret == -1 || (size_t)ret >= sizeof pathname)
- goto err;
+int
+ssl_load_dhparams(struct ssl *s, const char *pathname)
+{
s->ssl_dhparams = ssl_load_file(pathname, &s->ssl_dhparams_len, 0755);
if (s->ssl_dhparams == NULL) {
if (errno == EACCES)
- goto err;
+ return 0;
log_info("info: No DH parameters found in %s: "
"using built-in parameters", pathname);
}
-
- *sp = s;
- return (1);
-
-err:
- if (s->ssl_cert != NULL)
- free(s->ssl_cert);
- if (s->ssl_key != NULL)
- free(s->ssl_key);
- if (s->ssl_ca != NULL)
- free(s->ssl_ca);
- if (s->ssl_dhparams != NULL)
- free(s->ssl_dhparams);
- if (s != NULL)
- free(s);
- return (0);
+ return 1;
}
-
const char *
ssl_to_text(const SSL *ssl)
{
diff --git a/usr.sbin/smtpd/ssl.h b/usr.sbin/smtpd/ssl.h
index 4cbec59458f..9d127beffac 100644
--- a/usr.sbin/smtpd/ssl.h
+++ b/usr.sbin/smtpd/ssl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl.h,v 1.2 2013/07/19 09:04:07 eric Exp $ */
+/* $OpenBSD: ssl.h,v 1.3 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2013 Gilles Chehade <gilles@poolp.org>
*
@@ -21,22 +21,28 @@
struct ssl {
char ssl_name[PATH_MAX];
+
+ char *ssl_ca_file;
char *ssl_ca;
off_t ssl_ca_len;
+
+ char *ssl_cert_file;
char *ssl_cert;
off_t ssl_cert_len;
+
+ char *ssl_key_file;
char *ssl_key;
off_t ssl_key_len;
+
+ char *ssl_dhparams_file;
char *ssl_dhparams;
off_t ssl_dhparams_len;
- uint8_t flags;
};
/* ssl.c */
void ssl_init(void);
int ssl_setup(SSL_CTX **, struct ssl *);
SSL_CTX *ssl_ctx_create(void);
-int ssl_load_certfile(struct ssl **, const char *, const char *, uint8_t);
void *ssl_mta_init(char *, off_t, char *, off_t);
void *ssl_smtp_init(void *, char *, off_t, char *, off_t);
int ssl_cmp(struct ssl *, struct ssl *);
@@ -51,6 +57,11 @@ char *ssl_load_key(const char *, off_t *, char *);
const char *ssl_to_text(const SSL *);
void ssl_error(const char *);
+int ssl_load_certificate(struct ssl *, const char *);
+int ssl_load_keyfile(struct ssl *, const char *);
+int ssl_load_cafile(struct ssl *, const char *);
+int ssl_load_dhparams(struct ssl *, const char *);
+
/* ssl_privsep.c */
int ssl_ctx_use_private_key(SSL_CTX *, char *, off_t);
diff --git a/usr.sbin/smtpd/to.c b/usr.sbin/smtpd/to.c
index 8e3a93f1bf9..b9da9be0274 100644
--- a/usr.sbin/smtpd/to.c
+++ b/usr.sbin/smtpd/to.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: to.c,v 1.11 2013/10/28 10:32:17 eric Exp $ */
+/* $OpenBSD: to.c,v 1.12 2013/11/06 10:01:29 eric Exp $ */
/*
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -321,8 +321,12 @@ text_to_relayhost(struct relayhost *relay, const char *s)
{
static const struct schema {
const char *name;
- uint8_t flags;
+ uint16_t flags;
} schemas [] = {
+ /*
+ * new schemas should be *appended* otherwise the default
+ * schema index needs to be updated later in this function.
+ */
{ "smtp://", 0 },
{ "lmtp://", F_LMTP },
{ "smtp+tls://", F_TLS_OPTIONAL },
@@ -330,8 +334,8 @@ text_to_relayhost(struct relayhost *relay, const char *s)
{ "tls://", F_STARTTLS },
{ "smtps+auth://", F_SMTPS|F_AUTH },
{ "tls+auth://", F_STARTTLS|F_AUTH },
- { "ssl://", F_SMTPS|F_STARTTLS },
- { "ssl+auth://", F_SMTPS|F_STARTTLS|F_AUTH },
+ { "secure://", F_SMTPS|F_STARTTLS },
+ { "secure+auth://", F_SMTPS|F_STARTTLS|F_AUTH },
{ "backup://", F_BACKUP }
};
const char *errstr = NULL;
@@ -414,10 +418,10 @@ relayhost_to_text(const struct relayhost *relay)
bzero(buf, sizeof buf);
switch (relay->flags) {
case F_SMTPS|F_STARTTLS|F_AUTH:
- strlcat(buf, "ssl+auth://", sizeof buf);
+ strlcat(buf, "secure+auth://", sizeof buf);
break;
case F_SMTPS|F_STARTTLS:
- strlcat(buf, "ssl://", sizeof buf);
+ strlcat(buf, "secure://", sizeof buf);
break;
case F_STARTTLS|F_AUTH:
strlcat(buf, "tls+auth://", sizeof buf);
@@ -425,12 +429,18 @@ relayhost_to_text(const struct relayhost *relay)
case F_SMTPS|F_AUTH:
strlcat(buf, "smtps+auth://", sizeof buf);
break;
+ case F_STARTTLS|F_TLS_VERIFY:
+ strlcat(buf, "tls://", sizeof buf);
+ break;
case F_STARTTLS:
strlcat(buf, "tls://", sizeof buf);
break;
case F_SMTPS:
strlcat(buf, "smtps://", sizeof buf);
break;
+ case F_SMTPS|F_TLS_VERIFY:
+ strlcat(buf, "smtps://", sizeof buf);
+ break;
case F_BACKUP:
strlcat(buf, "backup://", sizeof buf);
break;
@@ -501,19 +511,26 @@ rule_to_text(struct rule *r)
bzero(buf, sizeof buf);
strlcpy(buf, r->r_decision == R_ACCEPT ? "accept" : "reject", sizeof buf);
if (r->r_tag[0]) {
- strlcat(buf, " on ", sizeof buf);
+ strlcat(buf, " tagged ", sizeof buf);
+ if (r->r_nottag)
+ strlcat(buf, "! ", sizeof buf);
strlcat(buf, r->r_tag, sizeof buf);
}
strlcat(buf, " from ", sizeof buf);
+ if (r->r_notsources)
+ strlcat(buf, "! ", sizeof buf);
strlcat(buf, r->r_sources->t_name, sizeof buf);
+ strlcat(buf, " for ", sizeof buf);
+ if (r->r_notdestination)
+ strlcat(buf, "! ", sizeof buf);
switch (r->r_desttype) {
case DEST_DOM:
if (r->r_destination == NULL) {
- strlcat(buf, " for any", sizeof buf);
+ strlcat(buf, " any", sizeof buf);
break;
}
- strlcat(buf, " for domain ", sizeof buf);
+ strlcat(buf, " domain ", sizeof buf);
strlcat(buf, r->r_destination->t_name, sizeof buf);
if (r->r_mapping) {
strlcat(buf, " alias ", sizeof buf);
@@ -522,11 +539,11 @@ rule_to_text(struct rule *r)
break;
case DEST_VDOM:
if (r->r_destination == NULL) {
- strlcat(buf, " for any virtual ", sizeof buf);
+ strlcat(buf, " any virtual ", sizeof buf);
strlcat(buf, r->r_mapping->t_name, sizeof buf);
break;
}
- strlcat(buf, " for domain ", sizeof buf);
+ strlcat(buf, " domain ", sizeof buf);
strlcat(buf, r->r_destination->t_name, sizeof buf);
strlcat(buf, " virtual ", sizeof buf);
strlcat(buf, r->r_mapping->t_name, sizeof buf);
@@ -564,6 +581,8 @@ rule_to_text(struct rule *r)
strlcat(buf, r->r_value.buffer, sizeof buf);
strlcat(buf, "\"", sizeof buf);
break;
+ case A_NONE:
+ break;
}
return buf;