diff options
-rw-r--r-- | usr.sbin/smtpd/makemap.c | 4 | ||||
-rw-r--r-- | usr.sbin/smtpd/mta.c | 39 | ||||
-rw-r--r-- | usr.sbin/smtpd/parse.y | 28 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.c | 85 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.conf.5 | 13 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 9 | ||||
-rw-r--r-- | usr.sbin/smtpd/ssl.c | 31 |
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; |