-/* $OpenBSD: smtp_session.c,v 1.336 2018/09/03 17:41:55 gilles Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.337 2018/09/03 19:01:29 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
static void smtp_message_end(struct smtp_tx *);
static int smtp_message_printf(struct smtp_tx *, const char *, ...);
+static int smtp_check_rset(struct smtp_session *);
+static int smtp_check_helo(struct smtp_session *, int, const char *);
+static int smtp_check_auth(struct smtp_session *s, const char *);
+static int smtp_check_starttls(struct smtp_session *, const char *);
+static int smtp_check_mail_from(struct smtp_session *);
+static int smtp_check_rcpt_to(struct smtp_session *);
+static int smtp_check_data(struct smtp_session *);
+
+static void smtp_proceed_rset(struct smtp_session *);
+static void smtp_proceed_helo(struct smtp_session *, int, char *);
+static void smtp_proceed_auth(struct smtp_session *, char *);
+static void smtp_proceed_starttls(struct smtp_session *);
+static void smtp_proceed_mail_from(struct smtp_session *, char *);
+static void smtp_proceed_rcpt_to(struct smtp_session *, char *);
+static void smtp_proceed_data(struct smtp_session *);
+static void smtp_proceed_noop(struct smtp_session *);
+static void smtp_proceed_help(struct smtp_session *);
+static void smtp_proceed_wiz(struct smtp_session *);
+static void smtp_proceed_quit(struct smtp_session *);
+
+
static struct { int code; const char *cmd; } commands[] = {
{ CMD_HELO, "HELO" },
{ CMD_EHLO, "EHLO" },
static void
smtp_command(struct smtp_session *s, char *line)
{
- char *args, *eom, *method;
+ char *args;
int cmd, i;
log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line);
*/
case CMD_HELO:
case CMD_EHLO:
- if (s->helo[0]) {
- smtp_reply(s, "503 %s %s: Already identified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: %s requires domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND),
- (cmd == CMD_HELO) ? "HELO" : "EHLO");
-
+ if (!smtp_check_helo(s, cmd, args))
break;
- }
-
- if (!valid_domainpart(args)) {
- smtp_reply(s, "501 %s %s: Invalid domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- break;
- }
- (void)strlcpy(s->helo, args, sizeof(s->helo));
- s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
- if (cmd == CMD_EHLO) {
- s->flags |= SF_EHLO;
- s->flags |= SF_8BITMIME;
- }
-
- smtp_enter_state(s, STATE_HELO);
- smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you",
- (s->flags & SF_EHLO) ? '-' : ' ',
- s->smtpname,
- s->helo,
- ss_to_text(&s->ss));
-
- if (s->flags & SF_EHLO) {
- smtp_reply(s, "250-8BITMIME");
- smtp_reply(s, "250-ENHANCEDSTATUSCODES");
- smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
- if (ADVERTISE_EXT_DSN(s))
- smtp_reply(s, "250-DSN");
- if (ADVERTISE_TLS(s))
- smtp_reply(s, "250-STARTTLS");
- if (ADVERTISE_AUTH(s))
- smtp_reply(s, "250-AUTH PLAIN LOGIN");
- smtp_reply(s, "250 HELP");
- }
+ smtp_proceed_helo(s, cmd, args);
break;
/*
* SETUP
*/
case CMD_STARTTLS:
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-
- if (!(s->listener->flags & F_STARTTLS)) {
- smtp_reply(s, "503 %s %s: Command not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-
- if (s->flags & SF_SECURE) {
- smtp_reply(s, "503 %s %s: Channel already secured",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ if (!smtp_check_starttls(s, args))
break;
- }
- if (args != NULL) {
- smtp_reply(s, "501 %s %s: No parameters allowed",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- break;
- }
- smtp_reply(s, "220 %s: Ready to start TLS",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_TLS);
+ smtp_proceed_starttls(s);
break;
case CMD_AUTH:
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-
- if (s->flags & SF_AUTHENTICATED) {
- smtp_reply(s, "503 %s %s: Already authenticated",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-
- if (!ADVERTISE_AUTH(s)) {
- smtp_reply(s, "503 %s %s: Command not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ if (!smtp_check_auth(s, args))
break;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: No parameters given",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- break;
- }
-
- method = args;
- eom = strchr(args, ' ');
- if (eom == NULL)
- eom = strchr(args, '\t');
- if (eom != NULL)
- *eom++ = '\0';
- if (strcasecmp(method, "PLAIN") == 0)
- smtp_rfc4954_auth_plain(s, eom);
- else if (strcasecmp(method, "LOGIN") == 0)
- smtp_rfc4954_auth_login(s, eom);
- else
- smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED),
- esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED),
- method);
+ smtp_proceed_auth(s, args);
break;
case CMD_MAIL_FROM:
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
-
- break;
- }
-
- if (s->listener->flags & F_STARTTLS_REQUIRE &&
- !(s->flags & SF_SECURE)) {
- smtp_reply(s,
- "530 %s %s: Must issue a STARTTLS command first",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-
- if (s->listener->flags & F_AUTH_REQUIRE &&
- !(s->flags & SF_AUTHENTICATED)) {
- smtp_reply(s,
- "530 %s %s: Must issue an AUTH command first",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ if (!smtp_check_mail_from(s))
break;
- }
-
- if (s->mailcount >= env->sc_session_max_mails) {
- /* we can pretend we had too many recipients */
- smtp_reply(s, "452 %s %s: Too many messages sent",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- break;
- }
-
- if (!smtp_tx(s)) {
- smtp_reply(s, "421 %s: Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- break;
- }
-
- smtp_tx_mail_from(s->tx, args);
+ smtp_proceed_mail_from(s, args);
break;
+
/*
* TRANSACTION
*/
case CMD_RCPT_TO:
- if (s->tx == NULL) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ if (!smtp_check_rcpt_to(s))
break;
- }
-
- smtp_tx_rcpt_to(s->tx, args);
+ smtp_proceed_rcpt_to(s, args);
break;
case CMD_RSET:
- if (s->helo[0] == '\0') {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ if (!smtp_check_rset(s))
break;
- }
-
- if (s->tx) {
- if (s->tx->msgid)
- smtp_tx_rollback(s->tx);
- smtp_tx_free(s->tx);
- }
-
- smtp_reply(s, "250 %s: Reset state",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+ smtp_proceed_rset(s);
break;
case CMD_DATA:
- if (s->tx == NULL) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ if (!smtp_check_data(s))
break;
- }
- if (s->tx->rcptcount == 0) {
- smtp_reply(s, "503 %s %s: No recipient specified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- break;
- }
-
- smtp_tx_open_message(s->tx);
+ smtp_proceed_data(s);
break;
+
/*
* ANY
*/
case CMD_QUIT:
- smtp_reply(s, "221 %s: Bye",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
+ smtp_proceed_quit(s);
break;
case CMD_NOOP:
- smtp_reply(s, "250 %s: Ok",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+ smtp_proceed_noop(s);
break;
case CMD_HELP:
- smtp_reply(s, "214- This is " SMTPD_NAME);
- smtp_reply(s, "214- To report bugs in the implementation, "
- "please contact bugs@openbsd.org");
- smtp_reply(s, "214- with full details");
- smtp_reply(s, "214 %s: End of HELP info",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+ smtp_proceed_help(s);
break;
case CMD_WIZ:
- smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
+ smtp_proceed_wiz(s);
break;
default:
}
}
+static int
+smtp_check_rset(struct smtp_session *s)
+{
+ if (s->helo[0] == '\0') {
+ smtp_reply(s, "503 %s %s: Command not allowed at this point.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+ return 1;
+}
+
+static int
+smtp_check_helo(struct smtp_session *s, int cmd, const char *args)
+{
+ if (s->helo[0]) {
+ smtp_reply(s, "503 %s %s: Already identified",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (args == NULL) {
+ smtp_reply(s, "501 %s %s: %s requires domain name",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND),
+ (cmd == CMD_HELO) ? "HELO" : "EHLO");
+ return 0;
+ }
+
+ if (!valid_domainpart(args)) {
+ smtp_reply(s, "501 %s %s: Invalid domain name",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
+ esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+smtp_check_auth(struct smtp_session *s, const char *args)
+{
+ if (s->helo[0] == '\0' || s->tx) {
+ smtp_reply(s, "503 %s %s: Command not allowed at this point.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (s->flags & SF_AUTHENTICATED) {
+ smtp_reply(s, "503 %s %s: Already authenticated",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (!ADVERTISE_AUTH(s)) {
+ smtp_reply(s, "503 %s %s: Command not supported",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (args == NULL) {
+ smtp_reply(s, "501 %s %s: No parameters given",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
+ esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+smtp_check_starttls(struct smtp_session *s, const char *args)
+{
+ if (s->helo[0] == '\0' || s->tx) {
+ smtp_reply(s, "503 %s %s: Command not allowed at this point.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (!(s->listener->flags & F_STARTTLS)) {
+ smtp_reply(s, "503 %s %s: Command not supported",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (s->flags & SF_SECURE) {
+ smtp_reply(s, "503 %s %s: Channel already secured",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (args != NULL) {
+ smtp_reply(s, "501 %s %s: No parameters allowed",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
+ esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+smtp_check_mail_from(struct smtp_session *s)
+{
+ if (s->helo[0] == '\0' || s->tx) {
+ smtp_reply(s, "503 %s %s: Command not allowed at this point.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (s->listener->flags & F_STARTTLS_REQUIRE &&
+ !(s->flags & SF_SECURE)) {
+ smtp_reply(s,
+ "530 %s %s: Must issue a STARTTLS command first",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (s->listener->flags & F_AUTH_REQUIRE &&
+ !(s->flags & SF_AUTHENTICATED)) {
+ smtp_reply(s,
+ "530 %s %s: Must issue an AUTH command first",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (s->mailcount >= env->sc_session_max_mails) {
+ /* we can pretend we had too many recipients */
+ smtp_reply(s, "452 %s %s: Too many messages sent",
+ esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
+ esc_description(ESC_TOO_MANY_RECIPIENTS));
+ return 0;
+ }
+
+ if (!smtp_tx(s)) {
+ smtp_reply(s, "421 %s: Temporary Error",
+ esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
+ smtp_enter_state(s, STATE_QUIT);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+smtp_check_rcpt_to(struct smtp_session *s)
+{
+ if (s->tx == NULL) {
+ smtp_reply(s, "503 %s %s: Command not allowed at this point.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+smtp_check_data(struct smtp_session *s)
+{
+ if (s->tx == NULL) {
+ smtp_reply(s, "503 %s %s: Command not allowed at this point.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+ return 0;
+ }
+
+ if (s->tx->rcptcount == 0) {
+ smtp_reply(s, "503 %s %s: No recipient specified",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
+ esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void
+smtp_proceed_rset(struct smtp_session *s)
+{
+ if (s->tx) {
+ if (s->tx->msgid)
+ smtp_tx_rollback(s->tx);
+ smtp_tx_free(s->tx);
+ }
+
+ smtp_reply(s, "250 %s: Reset state",
+ esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+}
+
+static void
+smtp_proceed_helo(struct smtp_session *s, int cmd, char *args)
+{
+ (void)strlcpy(s->helo, args, sizeof(s->helo));
+ s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
+ if (cmd == CMD_EHLO) {
+ s->flags |= SF_EHLO;
+ s->flags |= SF_8BITMIME;
+ }
+
+ smtp_enter_state(s, STATE_HELO);
+ smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you",
+ (s->flags & SF_EHLO) ? '-' : ' ',
+ s->smtpname,
+ s->helo,
+ ss_to_text(&s->ss));
+
+ if (s->flags & SF_EHLO) {
+ smtp_reply(s, "250-8BITMIME");
+ smtp_reply(s, "250-ENHANCEDSTATUSCODES");
+ smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
+ if (ADVERTISE_EXT_DSN(s))
+ smtp_reply(s, "250-DSN");
+ if (ADVERTISE_TLS(s))
+ smtp_reply(s, "250-STARTTLS");
+ if (ADVERTISE_AUTH(s))
+ smtp_reply(s, "250-AUTH PLAIN LOGIN");
+ smtp_reply(s, "250 HELP");
+ }
+}
+
+static void
+smtp_proceed_auth(struct smtp_session *s, char *args)
+{
+ char *eom, *method;
+
+ method = args;
+ eom = strchr(args, ' ');
+ if (eom == NULL)
+ eom = strchr(args, '\t');
+ if (eom != NULL)
+ *eom++ = '\0';
+ if (strcasecmp(method, "PLAIN") == 0)
+ smtp_rfc4954_auth_plain(s, eom);
+ else if (strcasecmp(method, "LOGIN") == 0)
+ smtp_rfc4954_auth_login(s, eom);
+ else
+ smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED),
+ esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED),
+ method);
+}
+
+static void
+smtp_proceed_starttls(struct smtp_session *s)
+{
+ smtp_reply(s, "220 %s: Ready to start TLS",
+ esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+ smtp_enter_state(s, STATE_TLS);
+}
+
+static void
+smtp_proceed_mail_from(struct smtp_session *s, char *args)
+{
+ smtp_tx_mail_from(s->tx, args);
+}
+
+static void
+smtp_proceed_rcpt_to(struct smtp_session *s, char *args)
+{
+ smtp_tx_rcpt_to(s->tx, args);
+}
+
+static void
+smtp_proceed_data(struct smtp_session *s)
+{
+ smtp_tx_open_message(s->tx);
+}
+
+static void
+smtp_proceed_quit(struct smtp_session *s)
+{
+ smtp_reply(s, "221 %s: Bye",
+ esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+ smtp_enter_state(s, STATE_QUIT);
+}
+
+static void
+smtp_proceed_noop(struct smtp_session *s)
+{
+ smtp_reply(s, "250 %s: Ok",
+ esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+}
+
+static void
+smtp_proceed_help(struct smtp_session *s)
+{
+ smtp_reply(s, "214- This is " SMTPD_NAME);
+ smtp_reply(s, "214- To report bugs in the implementation, "
+ "please contact bugs@openbsd.org");
+ smtp_reply(s, "214- with full details");
+ smtp_reply(s, "214 %s: End of HELP info",
+ esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
+}
+
+static void
+smtp_proceed_wiz(struct smtp_session *s)
+{
+ smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
+ esc_description(ESC_INVALID_COMMAND));
+}
+
static void
smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg)
{