diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2009-03-09 01:43:20 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2009-03-09 01:43:20 +0000 |
commit | 1d1097165dbba4df7fef4d5a15b3c1436e546db3 (patch) | |
tree | c7477765dfd2036c8ffc97fc2a39e9d802e3e87f /usr.sbin/smtpd | |
parent | e895cc4f6e609364a7621832f0fc997f2c02e387 (diff) |
add basic support for outgoing authentication (AUTH PLAIN over ssl) which
can be turned on by adding "enable auth" to a "relay via" rule. this made
me rework the mx resolution so that it is done by the mta process and not
the runner process anymore.
Diffstat (limited to 'usr.sbin/smtpd')
-rw-r--r-- | usr.sbin/smtpd/lka.c | 339 | ||||
-rw-r--r-- | usr.sbin/smtpd/mta.c | 322 | ||||
-rw-r--r-- | usr.sbin/smtpd/parse.y | 10 | ||||
-rw-r--r-- | usr.sbin/smtpd/runner.c | 84 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.conf | 12 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 32 |
6 files changed, 470 insertions, 329 deletions
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c index 521e33d446f..50b8609efc2 100644 --- a/usr.sbin/smtpd/lka.c +++ b/usr.sbin/smtpd/lka.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka.c,v 1.32 2009/03/08 17:54:20 gilles Exp $ */ +/* $OpenBSD: lka.c,v 1.33 2009/03/09 01:43:19 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -30,12 +30,15 @@ #include <event.h> #include <netdb.h> #include <pwd.h> +#include <regex.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <keynote.h> + #include "smtpd.h" __dead void lka_shutdown(void); @@ -45,6 +48,7 @@ void lka_dispatch_mfa(int, short, void *); void lka_dispatch_smtp(int, short, void *); void lka_dispatch_queue(int, short, void *); void lka_dispatch_runner(int, short, void *); +void lka_dispatch_mta(int, short, void *); void lka_setup_events(struct smtpd *); void lka_disable_events(struct smtpd *); int lka_verify_mail(struct smtpd *, struct path *); @@ -64,6 +68,7 @@ void lka_expand_rcpt(struct smtpd *, struct aliaseslist *, struct lkasession *) int lka_expand_rcpt_iteration(struct smtpd *, struct aliaseslist *, struct lkasession *); void lka_rcpt_action(struct smtpd *, struct path *); void lka_clear_aliaseslist(struct aliaseslist *); +int lka_encode_credentials(char *, char *); void lka_sig_handler(int sig, short event, void *p) @@ -293,6 +298,219 @@ lka_dispatch_mfa(int sig, short event, void *p) } void +lka_dispatch_mta(int sig, 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) + fatal("lka_dispatch_mta: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_LKA_MX: { + struct mxreq *mxreq; + struct mxrep mxrep; + struct addrinfo hints, *res, *resp; + char **mx = NULL; + char *lmx[1]; + int len, i; + int mxcnt; + int error; + struct mxhost mxhost; + char *secret; + + mxreq = imsg.data; + mxrep.id = mxreq->id; + mxrep.getaddrinfo_error = 0; + + if (mxreq->rule.r_action == A_RELAY) { + log_debug("attempting to resolve %s", mxreq->hostname); + len = getmxbyname(mxreq->hostname, &mx); + if (len != 0) { + mxrep.getaddrinfo_error = len; + imsg_compose(ibuf, IMSG_LKA_MX_END, 0, 0, -1, + &mxrep, sizeof(struct mxrep)); + break; + } + if (len == 0) { + lmx[0] = mxreq->hostname; + mx = lmx; + len = 1; + } + } + else if (mxreq->rule.r_action == A_RELAYVIA) { + lmx[0] = mxreq->rule.r_value.relayhost.hostname; + log_debug("attempting to resolve %s:%d (forced)", lmx[0], + ntohs(mxreq->rule.r_value.relayhost.port)); + mx = lmx; + len = 1; + + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_protocol = IPPROTO_TCP; + + for (i = 0; i < len; ++i) { + + error = getaddrinfo(mx[i], NULL, &hints, &res); + if (error) { + mxrep.getaddrinfo_error = error; + imsg_compose(ibuf, IMSG_LKA_MX_END, 0, 0, -1, + &mxrep, sizeof(struct mxrep)); + } + + if (mxreq->rule.r_action == A_RELAYVIA) + mxhost.flags = mxreq->rule.r_value.relayhost.flags; + + if (mxhost.flags & F_AUTH) { + secret = map_dblookup(env, "secrets", mx[i]); + if (secret == NULL) { + log_warn("no credentials for relay through \"%s\"", mx[i]); + freeaddrinfo(res); + continue; + } + } + if (secret) + lka_encode_credentials(mxhost.credentials, secret); + + for (resp = res; resp != NULL && mxcnt < MAX_MX_COUNT; resp = resp->ai_next) { + + if (resp->ai_family == PF_INET) { + struct sockaddr_in *ssin; + + mxhost.ss = *(struct sockaddr_storage *)resp->ai_addr; + + ssin = (struct sockaddr_in *)&mxhost.ss; + if (mxreq->rule.r_value.relayhost.port != 0) { + ssin->sin_port = mxreq->rule.r_value.relayhost.port; + mxrep.mxhost = mxhost; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + continue; + } + + switch (mxhost.flags & F_SSL) { + case F_SSMTP: + ssin->sin_port = htons(465); + mxrep.mxhost = mxhost; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + break; + case F_SSL: + ssin->sin_port = htons(465); + mxrep.mxhost = mxhost; + mxrep.mxhost.flags &= ~F_STARTTLS; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + case F_STARTTLS: + ssin->sin_port = htons(25); + mxrep.mxhost = mxhost; + mxrep.mxhost.flags &= ~F_SSMTP; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + break; + default: + ssin->sin_port = htons(25); + mxrep.mxhost = mxhost; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + } + } + + if (resp->ai_family == PF_INET6) { + struct sockaddr_in6 *ssin6; + + mxhost.ss = *(struct sockaddr_storage *)resp->ai_addr; + ssin6 = (struct sockaddr_in6 *)&mxhost.ss; + if (mxreq->rule.r_value.relayhost.port != 0) { + ssin6->sin6_port = mxreq->rule.r_value.relayhost.port; + mxrep.mxhost = mxhost; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + continue; + } + + switch (mxhost.flags & F_SSL) { + case F_SSMTP: + ssin6->sin6_port = htons(465); + mxrep.mxhost = mxhost; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + break; + case F_SSL: + ssin6->sin6_port = htons(465); + mxrep.mxhost = mxhost; + mxrep.mxhost.flags &= ~F_STARTTLS; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + case F_STARTTLS: + ssin6->sin6_port = htons(25); + mxrep.mxhost = mxhost; + mxrep.mxhost.flags &= ~F_SSMTP; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + break; + default: + ssin6->sin6_port = htons(25); + mxrep.mxhost = mxhost; + imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep, + sizeof(struct mxrep)); + } + } + } + freeaddrinfo(res); + free(secret); + bzero(&mxhost.credentials, MAX_LINE_SIZE); + } + + mxrep.getaddrinfo_error = error; + + imsg_compose(ibuf, IMSG_LKA_MX_END, 0, 0, -1, + &mxrep, sizeof(struct mxrep)); + + if (mx != lmx) + free(mx); + + break; + } + + default: + log_debug("lka_dispatch_mta: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void lka_dispatch_smtp(int sig, short event, void *p) { struct smtpd *env = p; @@ -446,93 +664,6 @@ lka_dispatch_runner(int sig, short event, void *p) break; switch (imsg.hdr.type) { - case IMSG_LKA_MX: { - struct batch *batchp; - struct addrinfo hints, *res, *resp; - char **mx = NULL; - char *lmx[1]; - int len, i, j; - int error; - u_int16_t port = htons(25); - - batchp = imsg.data; - - if (! IS_RELAY(batchp->rule.r_action)) - fatalx("lka_dispatch_queue: inconsistent internal state"); - - if (batchp->rule.r_action == A_RELAY) { - log_debug("attempting to resolve %s", batchp->hostname); - len = getmxbyname(batchp->hostname, &mx); - if (len < 0) { - batchp->getaddrinfo_error = len; - imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, - batchp, sizeof(*batchp)); - break; - } - if (len == 0) { - lmx[0] = batchp->hostname; - mx = lmx; - len = 1; - } - } - else if (batchp->rule.r_action == A_RELAYVIA) { - - lmx[0] = batchp->rule.r_value.relayhost.hostname; - port = batchp->rule.r_value.relayhost.port; - log_debug("attempting to resolve %s:%d (forced)", lmx[0], ntohs(port)); - mx = lmx; - len = 1; - - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_protocol = IPPROTO_TCP; - for (i = j = 0; i < len && (j < MXARRAYSIZE * 2); ++i) { - error = getaddrinfo(mx[i], NULL, &hints, &res); - if (error) - continue; - - log_debug("resolving MX: %s", mx[i]); - - for (resp = res; resp != NULL && (j < MXARRAYSIZE * 2); resp = resp->ai_next) { - - if (batchp->rule.r_action == A_RELAYVIA) - batchp->mxarray[j].flags = batchp->rule.r_value.relayhost.flags; - - if (resp->ai_family == PF_INET) { - struct sockaddr_in *ssin; - - batchp->mxarray[j].ss = *(struct sockaddr_storage *)resp->ai_addr; - ssin = (struct sockaddr_in *)&batchp->mxarray[j].ss; - ssin->sin_port = port; - ++j; - } - if (resp->ai_family == PF_INET6) { - struct sockaddr_in6 *ssin6; - - batchp->mxarray[j].ss = *(struct sockaddr_storage *)resp->ai_addr; - ssin6 = (struct sockaddr_in6 *)&batchp->mxarray[j].ss; - ssin6->sin6_port = port; - ++j; - } - } - freeaddrinfo(res); - } - - batchp->mx_cnt = j; - batchp->getaddrinfo_error = 0; - if (j == 0) - batchp->getaddrinfo_error = error; - imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, batchp, - sizeof(*batchp)); - - if (mx != lmx) - free(mx); - - break; - } - default: log_debug("lka_dispatch_runner: unexpected imsg %d", imsg.hdr.type); @@ -575,6 +706,7 @@ lka(struct smtpd *env) { PROC_QUEUE, lka_dispatch_queue }, { PROC_SMTP, lka_dispatch_smtp }, { PROC_RUNNER, lka_dispatch_runner }, + { PROC_MTA, lka_dispatch_mta } }; switch (pid = fork()) { @@ -610,8 +742,8 @@ lka(struct smtpd *env) signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); - config_pipes(env, peers, 5); - config_peers(env, peers, 5); + config_pipes(env, peers, 6); + config_peers(env, peers, 6); lka_setup_events(env); event_dispatch(); @@ -1069,4 +1201,31 @@ lka_clear_aliaseslist(struct aliaseslist *aliaseslist) } } +int +lka_encode_credentials(char *dest, char *src) +{ + size_t len; + char buffer[MAX_LINE_SIZE]; + size_t i; + + len = strlen(src) + 1; + if (len < 1) + return 0; + + bzero(buffer, sizeof (buffer)); + if (strlcpy(buffer + 1, src, sizeof(buffer) - 1) >= + sizeof (buffer) - 1) + return 0; + + while (i++ < len) { + if (buffer[i] == ':') { + buffer[i] = '\0'; + break; + } + } + if (kn_encode_base64(buffer, len, dest, MAX_LINE_SIZE - 1) == -1) + return 0; + return 1; +} + SPLAY_GENERATE(lkatree, lkasession, nodes, lkasession_cmp); diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c index 325dfb21605..c15016b314a 100644 --- a/usr.sbin/smtpd/mta.c +++ b/usr.sbin/smtpd/mta.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta.c,v 1.31 2009/02/22 19:07:33 chl Exp $ */ +/* $OpenBSD: mta.c,v 1.32 2009/03/09 01:43:19 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -44,6 +44,7 @@ void mta_sig_handler(int, short, void *); void mta_dispatch_parent(int, short, void *); void mta_dispatch_queue(int, short, void *); void mta_dispatch_runner(int, short, void *); +void mta_dispatch_lka(int, short, void *); void mta_setup_events(struct smtpd *); void mta_disable_events(struct smtpd *); void mta_timeout(int, short, void *); @@ -54,7 +55,7 @@ void mta_write_handler(struct bufferevent *, void *); void mta_error_handler(struct bufferevent *, short, void *); int mta_reply_handler(struct bufferevent *, void *); void mta_batch_update_queue(struct batch *); -void mta_expand_mxarray(struct session *); +void mta_mxlookup(struct smtpd *, struct session *, char *, struct rule *); void ssl_client_init(struct session *); void @@ -117,6 +118,100 @@ mta_dispatch_parent(int sig, short event, void *p) } void +mta_dispatch_lka(int sig, short event, void *p) +{ + struct smtpd *env = p; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + + ibuf = env->sc_ibufs[PROC_LKA]; + 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) + fatal("mta_dispatch_lka: imsg_read error"); + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_LKA_MX: { + struct session key; + struct mxrep *mxrep; + struct session *s; + struct mxhost *mxhost; + + mxrep = imsg.data; + key.s_id = mxrep->id; + + s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); + if (s == NULL) + fatal("mta_dispatch_lka: session is gone"); + + mxhost = calloc(1, sizeof(struct mxhost)); + if (mxhost == NULL) + fatal("mta_dispatch_lka: calloc"); + + *mxhost = mxrep->mxhost; + TAILQ_INSERT_TAIL(&s->mxhosts, mxhost, entry); + + break; + } + case IMSG_LKA_MX_END: { + struct session key; + struct mxrep *mxrep; + struct session *s; + int ret; + + mxrep = imsg.data; + key.s_id = mxrep->id; + + s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); + if (s == NULL) + fatal("smtp_dispatch_parent: session is gone"); + + s->batch->flags |= F_BATCH_RESOLVED; + + do { + ret = mta_connect(s); + } while (ret == 0); + + if (ret < 0) { + mta_batch_update_queue(s->batch); + session_destroy(s); + } + + break; + } + default: + log_debug("mta_dispatch_lka: unexpected imsg %d", + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + imsg_event_add(ibuf); +} + +void mta_dispatch_queue(int sig, short event, void *p) { struct smtpd *env = p; @@ -229,6 +324,7 @@ mta_dispatch_runner(int sig, short event, void *p) s->s_state = S_INIT; s->s_env = env; s->s_id = queue_generate_id(); + TAILQ_INIT(&s->mxhosts); SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s); /* create the batch for this session */ @@ -238,7 +334,6 @@ mta_dispatch_runner(int sig, short event, void *p) *batchp = *(struct batch *)imsg.data; batchp->session_id = s->s_id; - batchp->mx_off = 0; batchp->env = env; batchp->flags = 0; batchp->sessionp = s; @@ -290,10 +385,8 @@ mta_dispatch_runner(int sig, short event, void *p) s = batchp->sessionp; - mta_expand_mxarray(s); - while (! mta_connect(s)) - if (s->mx_off == s->mx_cnt) - break; + mta_mxlookup(env, s, batchp->hostname, &batchp->rule); + break; } default: @@ -352,7 +445,8 @@ mta(struct smtpd *env) struct peer peers[] = { { PROC_QUEUE, mta_dispatch_queue }, - { PROC_RUNNER, mta_dispatch_runner } + { PROC_RUNNER, mta_dispatch_runner }, + { PROC_LKA, mta_dispatch_lka } }; switch (pid = fork()) { @@ -396,8 +490,8 @@ mta(struct smtpd *env) signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); - config_pipes(env, peers, 2); - config_peers(env, peers, 2); + config_pipes(env, peers, 3); + config_peers(env, peers, 3); SPLAY_INIT(&env->batch_queue); @@ -408,17 +502,34 @@ mta(struct smtpd *env) return (0); } +void +mta_mxlookup(struct smtpd *env, struct session *sessionp, char *hostname, struct rule *rule) +{ + struct mxreq mxreq; + + mxreq.id = sessionp->s_id; + mxreq.rule = *rule; + (void)strlcpy(mxreq.hostname, hostname, MAXHOSTNAMELEN); + imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_LKA_MX, 0, 0, -1, + &mxreq, sizeof(struct mxreq)); +} + /* shamelessly ripped usr.sbin/relayd/check_tcp.c ;) */ int -mta_connect(struct session* sessionp) +mta_connect(struct session *sessionp) { int s; int type; struct linger lng; struct sockaddr_in ssin; struct sockaddr_in6 ssin6; + struct mxhost *mxhost; + + mxhost = TAILQ_FIRST(&sessionp->mxhosts); + if (mxhost == NULL) + return -1; - if ((s = socket(sessionp->mxarray[sessionp->mx_off].ss.ss_family, SOCK_STREAM, 0)) == -1) { + if ((s = socket(mxhost->ss.ss_family, SOCK_STREAM, 0)) == -1) { goto bad; } @@ -434,8 +545,8 @@ mta_connect(struct session* sessionp) session_socket_blockmode(s, BM_NONBLOCK); - if (sessionp->mxarray[sessionp->mx_off].ss.ss_family == PF_INET) { - ssin = *(struct sockaddr_in *)&sessionp->mxarray[sessionp->mx_off].ss; + if (mxhost->ss.ss_family == PF_INET) { + ssin = *(struct sockaddr_in *)&mxhost->ss; if (connect(s, (struct sockaddr *)&ssin, sizeof(struct sockaddr_in)) == -1) { if (errno != EINPROGRESS) { goto bad; @@ -443,15 +554,14 @@ mta_connect(struct session* sessionp) } } - if (sessionp->mxarray[sessionp->mx_off].ss.ss_family == PF_INET6) { - ssin6 = *(struct sockaddr_in6 *)&sessionp->mxarray[sessionp->mx_off].ss; + if (mxhost->ss.ss_family == PF_INET6) { + ssin6 = *(struct sockaddr_in6 *)&mxhost->ss; if (connect(s, (struct sockaddr *)&ssin6, sizeof(struct sockaddr_in6)) == -1) { if (errno != EINPROGRESS) { goto bad; } } } - sessionp->s_tv.tv_sec = SMTPD_CONNECT_TIMEOUT; sessionp->s_tv.tv_usec = 0; sessionp->s_fd = s; @@ -461,7 +571,10 @@ mta_connect(struct session* sessionp) return 1; bad: - sessionp->mx_off++; + if (mxhost) { + TAILQ_REMOVE(&sessionp->mxhosts, mxhost, entry); + free(mxhost); + } close(s); return 0; } @@ -471,11 +584,15 @@ mta_write(int s, short event, void *arg) { struct session *sessionp = arg; struct batch *batchp = sessionp->batch; + struct mxhost *mxhost; int ret; if (event == EV_TIMEOUT) { - sessionp->mx_off++; + + TAILQ_REMOVE(&sessionp->mxhosts, mxhost, entry); + free(mxhost); close(s); + if (sessionp->s_bev) { bufferevent_free(sessionp->s_bev); sessionp->s_bev = NULL; @@ -483,16 +600,15 @@ mta_write(int s, short event, void *arg) strlcpy(batchp->errorline, "connection timed-out.", sizeof(batchp->errorline)); - ret = 0; - while (sessionp->mx_off < sessionp->mx_cnt && - (ret = mta_connect(sessionp)) == 0) { - continue; + do { + ret = mta_connect(sessionp); + } while (ret == 0); + + if (ret < 0) { + mta_batch_update_queue(batchp); + session_destroy(sessionp); } - if (ret) - return; - mta_batch_update_queue(batchp); - session_destroy(sessionp); return; } @@ -505,7 +621,8 @@ mta_write(int s, short event, void *arg) return; } - if (sessionp->mxarray[sessionp->mx_off].flags & F_SSMTP) { + mxhost = TAILQ_FIRST(&sessionp->mxhosts); + if (mxhost->flags & F_SSMTP) { ssl_client_init(sessionp); return; } @@ -534,6 +651,7 @@ mta_reply_handler(struct bufferevent *bev, void *arg) char codebuf[4]; const char *errstr; int flags = 0; + struct mxhost *mxhost = TAILQ_FIRST(&sessionp->mxhosts); line = evbuffer_readline(bev->input); if (line == NULL) @@ -555,6 +673,9 @@ mta_reply_handler(struct bufferevent *bev, void *arg) if (line[3] == '-') { if (strcasecmp(&line[4], "STARTTLS") == 0) sessionp->s_flags |= F_PEERHASTLS; + else if (strncasecmp(&line[4], "AUTH ", 5) == 0 || + strncasecmp(&line[4], "AUTH-", 5) == 0) + sessionp->s_flags |= F_PEERHASAUTH; return 1; } @@ -575,8 +696,17 @@ mta_reply_handler(struct bufferevent *bev, void *arg) } if (sessionp->s_state == S_GREETED && + (sessionp->s_flags & F_PEERHASAUTH) && + (sessionp->s_flags & F_SECURE)) { + log_debug("AUTH PLAIN %s", mxhost->credentials); + session_respond(sessionp, "AUTH PLAIN %s", mxhost->credentials); + sessionp->s_state = S_AUTH_INIT; + return 0; + } + + if (sessionp->s_state == S_GREETED && !(sessionp->s_flags & F_PEERHASTLS) && - sessionp->mxarray[sessionp->mx_off].flags & F_STARTTLS) { + mxhost->flags & F_STARTTLS) { /* PERM - we want TLS but it is not advertised */ batchp->status |= S_BATCH_PERMFAILURE; mta_batch_update_queue(batchp); @@ -584,6 +714,16 @@ mta_reply_handler(struct bufferevent *bev, void *arg) return 0; } + if (sessionp->s_state == S_GREETED && + !(sessionp->s_flags & F_PEERHASAUTH) && + mxhost->flags & F_AUTH) { + /* PERM - we want AUTH but it is not advertised */ + batchp->status |= S_BATCH_PERMFAILURE; + mta_batch_update_queue(batchp); + session_destroy(sessionp); + return 0; + } + break; case 220: @@ -598,6 +738,13 @@ mta_reply_handler(struct bufferevent *bev, void *arg) sessionp->s_state = S_GREETED; return 1; + case 235: + if (sessionp->s_state == S_AUTH_INIT) { + sessionp->s_flags |= F_AUTHENTICATED; + sessionp->s_state = S_GREETED; + break; + } + return 0; case 421: case 450: case 451: @@ -631,6 +778,8 @@ mta_reply_handler(struct bufferevent *bev, void *arg) return 0; } + case 535: + /* Authentication failed*/ case 552: case 553: flags |= F_ISPROTOERROR; @@ -922,118 +1071,3 @@ mta_batch_update_queue(struct batch *batchp) free(batchp); } - -void -mta_expand_mxarray(struct session *sessionp) -{ - int i; - int j; - u_int16_t port; - struct mxhost mxhost; - struct sockaddr_in *ssin; - struct sockaddr_in6 *ssin6; - struct batch *batchp = sessionp->batch; - - /* First pass, we compute the length of the final mxarray */ - for (i = 0; i < batchp->mx_cnt; ++i) { - mxhost = batchp->mxarray[i]; - - if (mxhost.ss.ss_family == AF_INET) { - ssin = (struct sockaddr_in *)&mxhost.ss; - port = ntohs(ssin->sin_port); - } - else if (mxhost.ss.ss_family == AF_INET6) { - ssin6 = (struct sockaddr_in6 *)&mxhost.ss; - port = ntohs(ssin6->sin6_port); - } - - if (port) { - ++sessionp->mx_cnt; - continue; - } - - switch (mxhost.flags & F_SSL) { - case F_SSL: - sessionp->mx_cnt += 2; - break; - case F_SSMTP: - case F_STARTTLS: - default: - ++sessionp->mx_cnt; - } - } - - /* Second pass, we actually fill the array */ - sessionp->mxarray = calloc(sessionp->mx_cnt, sizeof(struct mxhost)); - if (sessionp->mxarray == NULL) - fatal("calloc"); - - for (i = j = 0; i < batchp->mx_cnt; ++i) { - mxhost = batchp->mxarray[i]; - - if (mxhost.ss.ss_family == AF_INET) { - ssin = (struct sockaddr_in *)&mxhost.ss; - port = ntohs(ssin->sin_port); - } - else if (mxhost.ss.ss_family == AF_INET6) { - ssin6 = (struct sockaddr_in6 *)&mxhost.ss; - port = ntohs(ssin6->sin6_port); - } - - if (port) { - sessionp->mxarray[j++] = mxhost; - continue; - } - - switch (mxhost.flags & F_SSL) { - case F_SSL: { - u_int8_t flags = mxhost.flags; - - if (mxhost.ss.ss_family == AF_INET) { - ssin->sin_port = htons(465); - mxhost.ss = *(struct sockaddr_storage *)ssin; - } - else if (mxhost.ss.ss_family == AF_INET6) { - ssin6->sin6_port = htons(465); - mxhost.ss = *(struct sockaddr_storage *)ssin6; - } - mxhost.flags = flags & ~F_STARTTLS; - sessionp->mxarray[j++] = mxhost; - - if (mxhost.ss.ss_family == AF_INET) { - ssin->sin_port = htons(25); - mxhost.ss = *(struct sockaddr_storage *)ssin; - } - else if (mxhost.ss.ss_family == AF_INET6) { - ssin6->sin6_port = htons(25); - mxhost.ss = *(struct sockaddr_storage *)ssin6; - } - mxhost.flags = flags & ~F_SSMTP; - sessionp->mxarray[j++] = mxhost; - break; - } - case F_SSMTP: - if (mxhost.ss.ss_family == AF_INET) { - ssin->sin_port = htons(465); - mxhost.ss = *(struct sockaddr_storage *)ssin; - } - else if (mxhost.ss.ss_family == AF_INET6) { - ssin6->sin6_port = htons(465); - mxhost.ss = *(struct sockaddr_storage *)ssin6; - } - sessionp->mxarray[j++] = mxhost; - break; - case F_STARTTLS: - default: - if (mxhost.ss.ss_family == AF_INET) { - ssin->sin_port = htons(25); - mxhost.ss = *(struct sockaddr_storage *)ssin; - } - else if (mxhost.ss.ss_family == AF_INET6) { - ssin6->sin6_port = htons(25); - mxhost.ss = *(struct sockaddr_storage *)ssin6; - } - sessionp->mxarray[j++] = mxhost; - } - } -} diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y index 6bffefca9cd..63c8d243851 100644 --- a/usr.sbin/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.26 2009/03/08 21:50:33 gilles Exp $ */ +/* $OpenBSD: parse.y,v 1.27 2009/03/09 01:43:19 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -683,7 +683,7 @@ action : DELIVER TO MAILDIR STRING { | RELAY { rule->r_action = A_RELAY; } - | RELAY VIA ssl STRING port { + | RELAY VIA ssl STRING port auth { rule->r_action = A_RELAYVIA; if ($3) @@ -699,6 +699,12 @@ action : DELIVER TO MAILDIR STRING { else rule->r_value.relayhost.port = $5; + if ($6) { + if (! $3) + fatalx("cannot auth over insecure channel"); + rule->r_value.relayhost.flags |= F_AUTH; + } + free($4); } ; diff --git a/usr.sbin/smtpd/runner.c b/usr.sbin/smtpd/runner.c index 38425e0efca..9d46ed1f579 100644 --- a/usr.sbin/smtpd/runner.c +++ b/usr.sbin/smtpd/runner.c @@ -1,4 +1,4 @@ -/* $OpenBSD: runner.c,v 1.33 2009/02/24 12:07:47 gilles Exp $ */ +/* $OpenBSD: runner.c,v 1.34 2009/03/09 01:43:19 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -64,7 +64,6 @@ void runner_process_queue(struct smtpd *); void runner_process_runqueue(struct smtpd *); void runner_process_batchqueue(struct smtpd *); -int runner_batch_resolved(struct smtpd *, struct batch *); void runner_batch_dispatch(struct smtpd *, struct batch *, time_t); int runner_message_schedule(struct message *, time_t); @@ -355,10 +354,6 @@ runner_dispatch_lka(int sig, short event, void *p) break; switch (imsg.hdr.type) { - case IMSG_LKA_MX: { - runner_batch_resolved(env, imsg.data); - break; - } default: log_debug("runner_dispatch_lka: unexpected imsg %d", imsg.hdr.type); @@ -613,10 +608,10 @@ runner_process_batchqueue(struct smtpd *env) batchp != NULL; batchp = nxt) { nxt = SPLAY_NEXT(batchtree, &env->batch_queue, batchp); - if ((batchp->type & T_MTA_BATCH) && - (batchp->flags & F_BATCH_RESOLVED) == 0) { - continue; - } +// if ((batchp->type & T_MTA_BATCH) && +// (batchp->flags & F_BATCH_RESOLVED) == 0) { +// continue; +// } runner_batch_dispatch(env, batchp, curtime); @@ -626,71 +621,6 @@ runner_process_batchqueue(struct smtpd *env) } } -int -runner_batch_resolved(struct smtpd *env, struct batch *lookup) -{ - u_int32_t i; - struct batch *batchp; - - batchp = batch_by_id(env, lookup->id); - batchp->getaddrinfo_error = lookup->getaddrinfo_error; - batchp->mx_cnt = lookup->mx_cnt; - -/* - EAI_NODATA no address associated with hostname - EAI_NONAME hostname or servname not provided, or not known - EAI_PROTOCOL resolved protocol is unknown - EAI_SERVICE servname not supported for ai_socktype - EAI_SOCKTYPE ai_socktype not supported - EAI_SYSTEM system error returned in errno - - - */ - - switch (batchp->getaddrinfo_error) { - case 0: - batchp->flags |= F_BATCH_RESOLVED; - for (i = 0; i < batchp->mx_cnt; ++i) { - batchp->mxarray[i].flags = lookup->mxarray[i].flags; - batchp->mxarray[i].ss = lookup->mxarray[i].ss; - } - break; - case EAI_ADDRFAMILY: - case EAI_BADFLAGS: - case EAI_BADHINTS: - case EAI_FAIL: - case EAI_FAMILY: - case EAI_NODATA: - case EAI_NONAME: - case EAI_SERVICE: - case EAI_SOCKTYPE: - case EAI_SYSTEM: - /* XXX */ - /* - * In the case of a DNS permanent error, do not generate a - * daemon message if the error originates from one already - * as this would cause a loop. Remove the initial batch as - * it will never succeed. - * - */ - return 0; - - case EAI_AGAIN: - case EAI_MEMORY: - /* XXX */ - /* - * Do not generate a daemon message if this error happened - * while processing a daemon message. Do NOT remove batch, - * it may succeed later. - */ - return 0; - - default: - fatalx("runner_batch_resolved: unknown getaddrinfo error."); - } - return 1; -} - void runner_batch_dispatch(struct smtpd *env, struct batch *batchp, time_t curtime) { @@ -927,8 +857,8 @@ batch_record(struct smtpd *env, struct message *messagep) } else { batchp->type |= T_MTA_BATCH; - imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_LKA_MX, - 0, 0, -1, batchp, sizeof(struct batch)); +// imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_LKA_MX, +// 0, 0, -1, batchp, sizeof(struct batch)); } } diff --git a/usr.sbin/smtpd/smtpd.conf b/usr.sbin/smtpd/smtpd.conf index 7da3b09c6ed..07470ad5edc 100644 --- a/usr.sbin/smtpd/smtpd.conf +++ b/usr.sbin/smtpd/smtpd.conf @@ -1,13 +1,17 @@ -# $OpenBSD: smtpd.conf,v 1.5 2009/03/08 21:51:22 gilles Exp $ +# $OpenBSD: smtpd.conf,v 1.6 2009/03/09 01:43:19 gilles Exp $ listen on lo0 port 25 #ssmtp listen on localhost port 465 enable auth hostname localhost -local = "( 127.0.0.1, ::1 )" - #map "aliases" { source db "/etc/mail/aliases.db" } #map "virtual" { source db "/etc/mail/virtual.db" } +#map "secrets" { source db "/etc/mail/secrets.db" } accept for domain "localhost" deliver to mbox -accept from $local for all relay +#accept for domain "localhost" deliver to maildir "~/Maildir" +#accept for domain "localhost" deliver to mda "/usr/local/bin/mymda -u %u" + +accept for all relay +#accept for all relay via "mx.example.org" +#accept for all relay via ssl "mx.example.org" enable auth diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index f86b1cb0bee..75d06d44e8e 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.86 2009/03/08 20:39:49 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.87 2009/03/09 01:43:19 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -68,6 +68,7 @@ /* number of MX records to lookup */ #define MXARRAYSIZE 5 +#define MAX_MX_COUNT 10 /* rfc5321 limits */ #define SMTP_TEXTLINE_MAX 1000 @@ -92,8 +93,10 @@ struct relayhost { }; struct mxhost { + TAILQ_ENTRY(mxhost) entry; u_int8_t flags; struct sockaddr_storage ss; + char credentials[MAX_LINE_SIZE]; }; /* buffer specific headers */ @@ -170,6 +173,7 @@ enum imsg_type { IMSG_LKA_MAIL, IMSG_LKA_RCPT, IMSG_LKA_MX, + IMSG_LKA_MX_END, IMSG_LKA_HOST, IMSG_MDA_MAILBOX_FILE, IMSG_MDA_MESSAGE_FILE, @@ -513,11 +517,6 @@ struct batch { char session_hostname[MAXHOSTNAMELEN]; struct sockaddr_storage session_ss; - int8_t getaddrinfo_error; - struct mxhost mxarray[MXARRAYSIZE*2]; - u_int8_t mx_cnt; - u_int8_t mx_off; - time_t creation; time_t lasttry; u_int8_t retry; @@ -593,7 +592,8 @@ enum session_flags { F_SECURE = 0x8, F_AUTHENTICATED = 0x10, F_PEERHASTLS = 0x20, - F_EVLOCKED = 0x40 + F_PEERHASAUTH = 0x40, + F_EVLOCKED = 0x80 }; struct session { @@ -619,12 +619,8 @@ struct session { struct session_auth_req s_auth; - struct mxhost *mxarray; - u_int8_t mx_cnt; - u_int8_t mx_off; - struct batch *batch; - + TAILQ_HEAD(mxhostlist, mxhost) mxhosts; }; struct smtpd { @@ -727,6 +723,18 @@ struct forward_req { char pw_name[MAXLOGNAME]; }; +struct mxreq { + u_int64_t id; + char hostname[MAXHOSTNAMELEN]; + struct rule rule; +}; + +struct mxrep { + u_int64_t id; + int getaddrinfo_error; + struct mxhost mxhost; +}; + enum lkasession_flags { F_ERROR = 0x1 }; |