From cd4ff1f6087887da63195bf2e74dd313bb883770 Mon Sep 17 00:00:00 2001 From: Gilles Chehade Date: Sun, 21 Dec 2008 02:18:47 +0000 Subject: - AUTH PLAIN may receive credentials as a parameter to AUTH or on a following line, this commit brings support for the latter which was not supported yet. - AUTH LOGIN is now supported, allowing smtp auth support on clients that do not support AUTH PLAIN (ie: my mobile phone for instance ;) --- usr.sbin/smtpd/smtp.c | 4 +- usr.sbin/smtpd/smtp_session.c | 138 ++++++++++++++++++++++++++++++++++++------ usr.sbin/smtpd/smtpd.c | 7 ++- usr.sbin/smtpd/smtpd.h | 10 ++- usr.sbin/smtpd/smtpd/Makefile | 4 +- 5 files changed, 137 insertions(+), 26 deletions(-) diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c index be44e27e14d..3219d07e260 100644 --- a/usr.sbin/smtpd/smtp.c +++ b/usr.sbin/smtpd/smtp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp.c,v 1.9 2008/12/13 23:19:34 jacekm Exp $ */ +/* $OpenBSD: smtp.c,v 1.10 2008/12/21 02:18:46 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -178,7 +178,7 @@ smtp_dispatch_parent(int sig, short event, void *p) if (reply->value) s->s_flags |= F_AUTHENTICATED; - session_pickup(s, NULL); + session_auth_pickup(s, NULL); break; } diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index f1b038c94ed..15d30207d60 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.32 2008/12/20 00:18:03 gilles Exp $ */ +/* $OpenBSD: smtp_session.c,v 1.33 2008/12/21 02:18:46 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include #include +#include + #include "smtpd.h" int session_rfc5321_helo_handler(struct session *, char *); @@ -58,6 +61,9 @@ int session_rfc1652_mail_handler(struct session *, char *); int session_rfc3207_stls_handler(struct session *, char *); int session_rfc4954_auth_handler(struct session *, char *); +int session_rfc4954_auth_plain(struct session *, char *, size_t); +int session_rfc4954_auth_login(struct session *, char *, size_t); +void session_auth_pickup(struct session *, char *, size_t); void session_read(struct bufferevent *, void *); int session_read_data(struct session *, char *, size_t); @@ -144,7 +150,6 @@ session_rfc4954_auth_handler(struct session *s, char *args) { char *method; char *eom; - struct session_auth_req req; if (s->s_state == S_GREETED) { session_respond(s, "503 Polite people say HELO first"); @@ -163,25 +168,92 @@ session_rfc4954_auth_handler(struct session *s, char *args) if (eom != NULL) *eom++ = '\0'; - if (eom == NULL) { - /* NEEDS_FIX - unsupported yet */ - session_respond(s, "501 Syntax error"); + if (strcasecmp(method, "PLAIN") == 0) + return session_rfc4954_auth_plain(s, eom, eom ? strlen(eom) : 0); + else if (strcasecmp(method, "LOGIN") == 0) + return session_rfc4954_auth_login(s, eom, eom ? strlen(eom) : 0); + + session_respond(s, "501 Syntax error"); + return 1; + +} + +int +session_rfc4954_auth_plain(struct session *s, char *arg, size_t nr) +{ + if (arg == NULL) { + session_respond(s, "334"); + s->s_state = S_AUTH_INIT; return 1; } - req.session_id = s->s_id; - if (strlcpy(req.buffer, eom, sizeof(req.buffer)) >= - sizeof(req.buffer)) { + s->s_auth.session_id = s->s_id; + if (strlcpy(s->s_auth.buffer, arg, sizeof(s->s_auth.buffer)) >= + sizeof(s->s_auth.buffer)) { session_respond(s, "501 Syntax error"); return 1; } - s->s_state = S_AUTH; + s->s_state = S_AUTH_FINALIZE; + + imsg_compose(s->s_env->sc_ibufs[PROC_PARENT], IMSG_PARENT_AUTHENTICATE, + 0, 0, -1, &s->s_auth, sizeof(s->s_auth)); + bufferevent_disable(s->s_bev, EV_READ); + + return 1; +} + +int +session_rfc4954_auth_login(struct session *s, char *arg, size_t nr) +{ + struct session_auth_req req; + size_t len = 0; + + switch (s->s_state) { + case S_HELO: + session_respond(s, "334 VXNlcm5hbWU6"); + s->s_auth.session_id = s->s_id; + s->s_state = S_AUTH_USERNAME; + return 1; + + case S_AUTH_USERNAME: + bzero(s->s_auth.buffer, sizeof(s->s_auth.buffer)); + if (kn_decode_base64(arg, req.buffer, 1024) == -1 || + ! bsnprintf(s->s_auth.buffer + 1, sizeof(s->s_auth.buffer) - 1, "%s", req.buffer)) + goto err; + + session_respond(s, "334 UGFzc3dvcmQ6"); + s->s_state = S_AUTH_PASSWORD; + + return 1; + + case S_AUTH_PASSWORD: { + len = strlen(s->s_auth.buffer + 1); + if (kn_decode_base64(arg, req.buffer, 1024) == -1 || + ! bsnprintf(s->s_auth.buffer + len + 2, sizeof(s->s_auth.buffer) - len - 2, "%s", req.buffer)) + goto err; + + break; + } + default: + fatal("session_rfc4954_auth_login: unknown state"); + } + + s->s_state = S_AUTH_FINALIZE; + + req = s->s_auth; + len = strlen(s->s_auth.buffer + 1) + strlen(arg) + 2; + if (kn_encode_base64(req.buffer, len, s->s_auth.buffer, sizeof(s->s_auth.buffer)) == -1) + goto err; imsg_compose(s->s_env->sc_ibufs[PROC_PARENT], IMSG_PARENT_AUTHENTICATE, - 0, 0, -1, &req, sizeof(req)); + 0, 0, -1, &s->s_auth, sizeof(s->s_auth)); bufferevent_disable(s->s_bev, EV_READ); + return 1; +err: + s->s_state = S_HELO; + session_respond(s, "535 Authentication failed"); return 1; } @@ -297,7 +369,7 @@ session_rfc5321_ehlo_handler(struct session *s, char *args) /* only advertise auth if session is secure */ if ((s->s_l->flags & F_AUTH) && (s->s_flags & F_SECURE)) - session_respond(s, "250-AUTH %s", "PLAIN"); + session_respond(s, "250-AUTH PLAIN LOGIN"); session_respond(s, "250 HELP"); @@ -522,6 +594,37 @@ rfc5321: session_respond(s, "500 Command unrecognized"); } +void +session_auth_pickup(struct session *s, char *arg, size_t nr) +{ + if (s == NULL) + fatal("session_pickup: desynchronized"); + + bufferevent_enable(s->s_bev, EV_READ); + + switch (s->s_state) { + case S_AUTH_INIT: + session_rfc4954_auth_plain(s, arg, nr); + break; + case S_AUTH_USERNAME: + session_rfc4954_auth_login(s, arg, nr); + break; + case S_AUTH_PASSWORD: + session_rfc4954_auth_login(s, arg, nr); + break; + case S_AUTH_FINALIZE: + if (s->s_flags & F_AUTHENTICATED) + session_respond(s, "235 Authentication succeeded"); + else + session_respond(s, "535 Authentication failed"); + s->s_state = S_HELO; + break; + default: + fatal("session_auth_pickup: unknown state"); + } + return; +} + void session_pickup(struct session *s, struct submit_status *ss) { @@ -550,13 +653,6 @@ session_pickup(struct session *s, struct submit_status *ss) ssl_session_init(s); break; - case S_AUTH: - if (s->s_flags & F_AUTHENTICATED) - session_respond(s, "235 Authentication succeeded"); - else - session_respond(s, "535 Authentication failed"); - break; - case S_MAILREQUEST: /* sender was not accepted, downgrade state */ if (ss->code != 250) { @@ -698,6 +794,12 @@ read: goto read; } + if (IS_AUTH(s->s_state)) { + session_auth_pickup(s, line, nr); + free(line); + return; + } + if (nr > SMTP_CMDLINE_MAX) { session_respond(s, "500 Line too long"); return; diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index a2444f13562..01d0e84b608 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.17 2008/12/19 00:44:40 gilles Exp $ */ +/* $OpenBSD: smtpd.c,v 1.18 2008/12/21 02:18:46 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,8 @@ #include #include +#include + #include "smtpd.h" __dead void usage(void); @@ -388,7 +391,7 @@ parent_dispatch_smtp(int fd, short event, void *p) reply.session_id = req->session_id; reply.value = 0; - if (__b64_pton(req->buffer, buffer, 1024) >= 0) { + if (kn_decode_base64(req->buffer, buffer, sizeof(buffer)) != -1) { pw_name = buffer+1; pw_passwd = pw_name+strlen(pw_name)+1; pw = getpwnam(pw_name); diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 5c4cccf55ce..f6fd4ef357a 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.33 2008/12/20 00:18:03 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.34 2008/12/21 02:18:46 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -499,7 +499,10 @@ enum session_state { S_INIT = 0, S_GREETED, S_TLS, - S_AUTH, + S_AUTH_INIT, + S_AUTH_USERNAME, + S_AUTH_PASSWORD, + S_AUTH_FINALIZE, S_HELO, S_MAILREQUEST, S_MAIL, @@ -511,6 +514,7 @@ enum session_state { S_DONE, S_QUIT }; +#define IS_AUTH(x) ((x) == S_AUTH_INIT || (x) == S_AUTH_USERNAME || (x) == S_AUTH_PASSWORD || (x) == S_AUTH_FINALIZE) struct ssl { SPLAY_ENTRY(ssl) ssl_nodes; @@ -573,6 +577,8 @@ struct session { int s_buflen; struct timeval s_tv; struct message s_msg; + + struct session_auth_req s_auth; }; struct smtpd { diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index 9f5a191e3aa..9c1768cfd24 100644 --- a/usr.sbin/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.3 2008/12/11 22:17:11 gilles Exp $ +# $OpenBSD: Makefile,v 1.4 2008/12/21 02:18:46 gilles Exp $ PROG= smtpd SRCS= parse.y log.c config.c buffer.c imsg.c \ @@ -9,7 +9,7 @@ SRCS= parse.y log.c config.c buffer.c imsg.c \ MAN= smtpd.8 smtpd.conf.5 BINDIR= /usr/sbin -LDADD= -levent -lutil -lssl -lcrypto +LDADD= -levent -lutil -lssl -lcrypto -lkeynote -lm DPADD= ${LIBEVENT} ${LIBSSL} ${LIBCRYPTO} CFLAGS= -g3 -ggdb -I${.CURDIR}/.. CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes -- cgit v1.2.3