summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/smtpd/makemap.c4
-rw-r--r--usr.sbin/smtpd/mta.c39
-rw-r--r--usr.sbin/smtpd/parse.y28
-rw-r--r--usr.sbin/smtpd/smtpd.c85
-rw-r--r--usr.sbin/smtpd/smtpd.conf.513
-rw-r--r--usr.sbin/smtpd/smtpd.h9
-rw-r--r--usr.sbin/smtpd/ssl.c31
7 files changed, 192 insertions, 17 deletions
diff --git a/usr.sbin/smtpd/makemap.c b/usr.sbin/smtpd/makemap.c
index e00c2bdf8b7..bea29b68d2b 100644
--- a/usr.sbin/smtpd/makemap.c
+++ b/usr.sbin/smtpd/makemap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: makemap.c,v 1.17 2009/05/10 11:29:40 jacekm Exp $ */
+/* $OpenBSD: makemap.c,v 1.18 2009/05/30 23:53:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -75,7 +75,7 @@ purge_config(struct smtpd *env, u_int8_t what)
}
int
-ssl_load_certfile(struct smtpd *env, const char *name)
+ssl_load_certfile(struct smtpd *env, const char *name, u_int8_t flags)
{
return (0);
}
diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c
index 36398c793ea..cb5a1a20db5 100644
--- a/usr.sbin/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.52 2009/05/30 16:32:59 gilles Exp $ */
+/* $OpenBSD: mta.c,v 1.53 2009/05/30 23:53:41 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -106,6 +106,40 @@ mta_dispatch_parent(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
+ case IMSG_CONF_START:
+ if (env->sc_flags & SMTPD_CONFIGURING)
+ break;
+ env->sc_flags |= SMTPD_CONFIGURING;
+ break;
+ case IMSG_CONF_SSL: {
+ struct ssl *s;
+ struct ssl *x_ssl;
+
+ if (!(env->sc_flags & SMTPD_CONFIGURING))
+ break;
+
+ if ((s = calloc(1, sizeof(*s))) == NULL)
+ fatal(NULL);
+ x_ssl = imsg.data;
+ (void)strlcpy(s->ssl_name, x_ssl->ssl_name,
+ sizeof(s->ssl_name));
+ s->ssl_cert_len = x_ssl->ssl_cert_len;
+ if ((s->ssl_cert =
+ strdup((char *)imsg.data + sizeof(*s))) == NULL)
+ fatal(NULL);
+ s->ssl_key_len = x_ssl->ssl_key_len;
+ if ((s->ssl_key = strdup((char *)imsg.data +
+ (sizeof(*s) + s->ssl_cert_len))) == NULL)
+ fatal(NULL);
+
+ SPLAY_INSERT(ssltree, &env->sc_ssl, s);
+ break;
+ }
+ case IMSG_CONF_END:
+ if (!(env->sc_flags & SMTPD_CONFIGURING))
+ break;
+ env->sc_flags &= ~SMTPD_CONFIGURING;
+ break;
default:
log_warnx("mta_dispatch_parent: got imsg %d",
imsg.hdr.type);
@@ -476,6 +510,7 @@ mta(struct smtpd *env)
struct event ev_sigterm;
struct peer peers[] = {
+ { PROC_PARENT, mta_dispatch_parent },
{ PROC_QUEUE, mta_dispatch_queue },
{ PROC_RUNNER, mta_dispatch_runner },
{ PROC_LKA, mta_dispatch_lka }
@@ -816,6 +851,8 @@ mta_reply_handler(struct bufferevent *bev, void *arg)
case 535:
/* Authentication failed*/
+ case 554:
+ /* Relaying denied */
case 552:
case 553:
flags |= F_ISPROTOERROR;
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index ab46b2aa5fa..fb2f09dd9f9 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.35 2009/05/27 16:51:26 jacekm Exp $ */
+/* $OpenBSD: parse.y,v 1.36 2009/05/30 23:53:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -266,7 +266,7 @@ main : QUEUE INTERVAL interval {
if ($7)
flags |= F_AUTH;
- if (ssl_load_certfile(conf, cert) < 0) {
+ if (ssl_load_certfile(conf, cert, F_SCERT) < 0) {
log_warnx("warning: could not load cert: %s, "
"no SSL/TLS/AUTH support", cert);
if ($5) {
@@ -745,11 +745,12 @@ action : DELIVER TO MAILDIR STRING {
| RELAY {
rule->r_action = A_RELAY;
}
- | RELAY VIA STRING port ssl auth {
+ | RELAY VIA STRING port ssl certname auth {
rule->r_action = A_RELAYVIA;
- if ($5 == 0 && $6) {
- yyerror("error: auth over insecure channel");
+ if ($5 == 0 && ($6 != NULL || $7)) {
+ yyerror("error: must specify tls, smtps, or ssl");
+ free($6);
free($3);
YYERROR;
}
@@ -762,10 +763,25 @@ action : DELIVER TO MAILDIR STRING {
rule->r_value.relayhost.port = $4;
rule->r_value.relayhost.flags |= $5;
- if ($6)
+ if ($7)
rule->r_value.relayhost.flags |= F_AUTH;
+ if ($6 != NULL) {
+ if (ssl_load_certfile(conf, $6, F_CCERT) < 0) {
+ yyerror("cannot load certificate: %s",
+ $6);
+ free($6);
+ free($3);
+ YYERROR;
+ }
+ if (strlcpy(rule->r_value.relayhost.cert, $6,
+ sizeof(rule->r_value.relayhost.cert))
+ >= sizeof(rule->r_value.relayhost.cert))
+ fatal("certificate path too long");
+ }
+
free($3);
+ free($6);
}
;
diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index 4cdfa20bc83..707be6899e7 100644
--- a/usr.sbin/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.68 2009/05/30 23:28:52 gilles Exp $ */
+/* $OpenBSD: smtpd.c,v 1.69 2009/05/30 23:53:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -55,10 +55,12 @@ __dead void usage(void);
void parent_shutdown(struct smtpd *);
void parent_send_config(int, short, void *);
void parent_send_config_listeners(struct smtpd *);
+void parent_send_config_client_certs(struct smtpd *);
void parent_send_config_ruleset(struct smtpd *, int);
void parent_dispatch_lka(int, short, void *);
void parent_dispatch_mda(int, short, void *);
void parent_dispatch_mfa(int, short, void *);
+void parent_dispatch_mta(int, short, void *);
void parent_dispatch_smtp(int, short, void *);
void parent_dispatch_runner(int, short, void *);
void parent_dispatch_control(int, short, void *);
@@ -117,6 +119,7 @@ void
parent_send_config(int fd, short event, void *p)
{
parent_send_config_listeners(p);
+ parent_send_config_client_certs(p);
parent_send_config_ruleset(p, PROC_MFA);
parent_send_config_ruleset(p, PROC_LKA);
}
@@ -134,6 +137,9 @@ parent_send_config_listeners(struct smtpd *env)
0, 0, -1, NULL, 0);
SPLAY_FOREACH(s, ssltree, &env->sc_ssl) {
+ if (!(s->flags & F_SCERT))
+ continue;
+
iov[0].iov_base = s;
iov[0].iov_len = sizeof(*s);
iov[1].iov_base = s->ssl_cert;
@@ -162,6 +168,35 @@ parent_send_config_listeners(struct smtpd *env)
}
void
+parent_send_config_client_certs(struct smtpd *env)
+{
+ struct ssl *s;
+ struct iovec iov[3];
+
+ log_debug("parent_send_config_client_certs: configuring smtp");
+ imsg_compose(env->sc_ibufs[PROC_MTA], IMSG_CONF_START,
+ 0, 0, -1, NULL, 0);
+
+ SPLAY_FOREACH(s, ssltree, &env->sc_ssl) {
+ if (!(s->flags & F_CCERT))
+ continue;
+
+ iov[0].iov_base = s;
+ iov[0].iov_len = sizeof(*s);
+ iov[1].iov_base = s->ssl_cert;
+ iov[1].iov_len = s->ssl_cert_len;
+ iov[2].iov_base = s->ssl_key;
+ iov[2].iov_len = s->ssl_key_len;
+
+ imsg_composev(env->sc_ibufs[PROC_MTA], IMSG_CONF_SSL, 0, 0, -1,
+ iov, nitems(iov));
+ }
+
+ imsg_compose(env->sc_ibufs[PROC_MTA], IMSG_CONF_END,
+ 0, 0, -1, NULL, 0);
+}
+
+void
parent_send_config_ruleset(struct smtpd *env, int proc)
{
struct rule *r;
@@ -305,6 +340,52 @@ parent_dispatch_mfa(int fd, short event, void *p)
}
void
+parent_dispatch_mta(int fd, short event, void *p)
+{
+ struct smtpd *env = p;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ ibuf = env->sc_ibufs[PROC_MTA];
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read_error");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatalx("parent_dispatch_mta: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ default:
+ log_warnx("parent_dispatch_mta: got imsg %d",
+ imsg.hdr.type);
+ fatalx("parent_dispatch_mta: unexpected imsg");
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
parent_dispatch_mda(int fd, short event, void *p)
{
struct smtpd *env = p;
@@ -615,6 +696,7 @@ parent_dispatch_control(int sig, short event, void *p)
env->sc_rules = newenv.sc_rules;
env->sc_ssl = newenv.sc_ssl;
+ parent_send_config_client_certs(env);
parent_send_config_ruleset(env, PROC_MFA);
parent_send_config_ruleset(env, PROC_LKA);
imsg_compose(env->sc_ibufs[PROC_SMTP],
@@ -734,6 +816,7 @@ main(int argc, char *argv[])
{ PROC_LKA, parent_dispatch_lka },
{ PROC_MDA, parent_dispatch_mda },
{ PROC_MFA, parent_dispatch_mfa },
+ { PROC_MTA, parent_dispatch_mta },
{ PROC_SMTP, parent_dispatch_smtp },
{ PROC_RUNNER, parent_dispatch_runner }
};
diff --git a/usr.sbin/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5
index 59d81b99a87..9d987d418e0 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.11 2009/05/28 08:43:45 jacekm Exp $
+.\" $OpenBSD: smtpd.conf.5,v 1.12 2009/05/30 23:53:41 gilles Exp $
.\"
.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -16,7 +16,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
-.Dd $Mdocdate: May 28 2009 $
+.Dd $Mdocdate: May 30 2009 $
.Dt SMTPD.CONF 5
.Os
.Sh NAME
@@ -245,6 +245,7 @@ The routing decision is based on the DNS system.
.Ar host
.Op Ic port Ar port
.Op Ic tls | smtps | ssl
+.Op Ic certificate Ar name
.Op Ic enable auth
.Xc
Message is relayed through the specified
@@ -261,6 +262,14 @@ Special keyword,
.Ic ssl ,
means any of the two is acceptable:
SMTPS is tried first, STARTTLS second.
+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 .
.Pp
If SMTPAUTH session with
.Ar host
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 98e14e0f3f0..b59fdfc812e 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.119 2009/05/28 08:50:08 jacekm Exp $ */
+/* $OpenBSD: smtpd.h,v 1.120 2009/05/30 23:53:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -85,6 +85,9 @@
#define F_AUTH 0x04
#define F_SSL (F_SMTPS|F_STARTTLS)
+#define F_SCERT 0x01
+#define F_CCERT 0x02
+
#define ADVERTISE_TLS(s) \
((s)->s_l->flags & F_STARTTLS && !((s)->s_flags & F_SECURE))
@@ -101,6 +104,7 @@ struct relayhost {
u_int8_t flags;
char hostname[MAXHOSTNAMELEN];
u_int16_t port;
+ char cert[PATH_MAX];
};
struct mxhost {
@@ -579,6 +583,7 @@ struct ssl {
off_t ssl_cert_len;
char *ssl_key;
off_t ssl_key_len;
+ u_int8_t flags;
};
struct listener {
@@ -979,7 +984,7 @@ void ssl_transaction(struct session *);
void ssl_session_init(struct session *);
void ssl_session_destroy(struct session *);
-int ssl_load_certfile(struct smtpd *, const char *);
+int ssl_load_certfile(struct smtpd *, const char *, u_int8_t);
void ssl_setup(struct smtpd *, struct listener *);
int ssl_cmp(struct ssl *, struct ssl *);
SPLAY_PROTOTYPE(ssltree, ssl, ssl_nodes, ssl_cmp);
diff --git a/usr.sbin/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c
index 6d7b75a6dfa..6164a09d4f3 100644
--- a/usr.sbin/smtpd/ssl.c
+++ b/usr.sbin/smtpd/ssl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl.c,v 1.16 2009/05/24 14:22:24 jacekm Exp $ */
+/* $OpenBSD: ssl.c,v 1.17 2009/05/30 23:53:41 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -337,7 +337,7 @@ ssl_ctx_create(void)
}
int
-ssl_load_certfile(struct smtpd *env, const char *name)
+ssl_load_certfile(struct smtpd *env, const char *name, u_int8_t flags)
{
struct ssl *s;
struct ssl key;
@@ -350,12 +350,15 @@ ssl_load_certfile(struct smtpd *env, const char *name)
}
s = SPLAY_FIND(ssltree, &env->sc_ssl, &key);
- if (s != NULL)
+ if (s != NULL) {
+ s->flags |= flags;
return 0;
+ }
if ((s = calloc(1, sizeof(*s))) == NULL)
fatal(NULL);
+ s->flags = flags;
(void)strlcpy(s->ssl_name, key.ssl_name, sizeof(s->ssl_name));
if (! bsnprintf(certfile, sizeof(certfile),
@@ -561,10 +564,32 @@ void
ssl_client_init(struct session *s)
{
SSL_CTX *ctx;
+ struct ssl key;
+ struct ssl *ssl;
log_debug("ssl_client_init: preparing SSL");
ctx = ssl_ctx_create();
+ if (s->batch->rule.r_value.relayhost.cert[0] != '\0') {
+ if (strlcpy(key.ssl_name,
+ s->batch->rule.r_value.relayhost.cert,
+ sizeof(key.ssl_name)) >= sizeof(key.ssl_name))
+ log_warnx("warning: certificate name too long: %s",
+ s->batch->rule.r_value.relayhost.cert);
+ else if ((ssl = SPLAY_FIND(ssltree, &s->s_env->sc_ssl,
+ &key)) == NULL)
+ log_warnx("warning: failed to find client "
+ "certificate: %s", key.ssl_name);
+ else if (!ssl_ctx_use_certificate_chain(ctx, ssl->ssl_cert,
+ ssl->ssl_cert_len))
+ ssl_error("ssl_client_init");
+ else if (!ssl_ctx_use_private_key(ctx, ssl->ssl_key,
+ ssl->ssl_key_len))
+ ssl_error("ssl_client_init");
+ else if (!SSL_CTX_check_private_key(ctx))
+ ssl_error("ssl_client_init");
+ }
+
s->s_ssl = SSL_new(ctx);
if (s->s_ssl == NULL)
goto err;