From: gilles Date: Mon, 3 Sep 2018 19:01:29 +0000 (+0000) Subject: simplify the smtp_command() function by splitting cmd handlers into X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=197a4a5e4a24e0ad65198e67c11555bcdd6d8f77;p=openbsd simplify the smtp_command() function by splitting cmd handlers into two sets of functions smtp_check_ and smtp_proceed_. we use the check functions to validate that verb is acceptable at this point of a session and proceed to actually move the session forward ok eric@ --- diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index d7c8346f110..64323eae183 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.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 @@ -190,6 +190,27 @@ static void smtp_message_fd(struct smtp_tx *, int); 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" }, @@ -1013,7 +1034,7 @@ smtp_io(struct io *io, int evt, void *arg) 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); @@ -1059,255 +1080,69 @@ smtp_command(struct smtp_session *s, char *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: @@ -1318,6 +1153,320 @@ smtp_command(struct smtp_session *s, char *line) } } +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) {