summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2009-03-09 01:43:20 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2009-03-09 01:43:20 +0000
commit1d1097165dbba4df7fef4d5a15b3c1436e546db3 (patch)
treec7477765dfd2036c8ffc97fc2a39e9d802e3e87f /usr.sbin/smtpd
parente895cc4f6e609364a7621832f0fc997f2c02e387 (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.c339
-rw-r--r--usr.sbin/smtpd/mta.c322
-rw-r--r--usr.sbin/smtpd/parse.y10
-rw-r--r--usr.sbin/smtpd/runner.c84
-rw-r--r--usr.sbin/smtpd/smtpd.conf12
-rw-r--r--usr.sbin/smtpd/smtpd.h32
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
};