1*4768c36cSeric /* $OpenBSD: smtp_session.c,v 1.329 2018/05/04 10:49:49 eric Exp $ */ 21f3c2bcfSsobrado 33ef9cbf7Sgilles /* 465c4fdfbSgilles * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 53ef9cbf7Sgilles * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6b8989c65Sjacekm * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 7b04e6f27Seric * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 83ef9cbf7Sgilles * 93ef9cbf7Sgilles * Permission to use, copy, modify, and distribute this software for any 103ef9cbf7Sgilles * purpose with or without fee is hereby granted, provided that the above 113ef9cbf7Sgilles * copyright notice and this permission notice appear in all copies. 123ef9cbf7Sgilles * 133ef9cbf7Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 143ef9cbf7Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 153ef9cbf7Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 163ef9cbf7Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 173ef9cbf7Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 183ef9cbf7Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 193ef9cbf7Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 203ef9cbf7Sgilles */ 213ef9cbf7Sgilles 223ef9cbf7Sgilles #include <sys/types.h> 233ef9cbf7Sgilles #include <sys/queue.h> 243ef9cbf7Sgilles #include <sys/tree.h> 253ef9cbf7Sgilles #include <sys/socket.h> 2665c4fdfbSgilles #include <sys/uio.h> 273ef9cbf7Sgilles 283ef9cbf7Sgilles #include <netinet/in.h> 299e096db0Sgilles 303ef9cbf7Sgilles #include <ctype.h> 3182614934Seric #include <errno.h> 323ef9cbf7Sgilles #include <event.h> 335eb8dddaSgilles #include <imsg.h> 34b9fc9a72Sderaadt #include <limits.h> 3582614934Seric #include <inttypes.h> 3665c4fdfbSgilles #include <openssl/ssl.h> 370767a313Sgilles #include <resolv.h> 383ef9cbf7Sgilles #include <stdio.h> 393ef9cbf7Sgilles #include <stdlib.h> 40a2bd1240Schl #include <string.h> 413ef9cbf7Sgilles #include <unistd.h> 4282614934Seric #include <vis.h> 433ef9cbf7Sgilles 443ef9cbf7Sgilles #include "smtpd.h" 455eb8dddaSgilles #include "log.h" 4665c4fdfbSgilles #include "ssl.h" 473ef9cbf7Sgilles 486162865dSeric #define SMTP_LINE_MAX 16384 49b34f8648Sgilles #define DATA_HIWAT 65535 506162865dSeric #define APPEND_DOMAIN_BUFFER_SIZE SMTP_LINE_MAX 5127b32957Sgilles 5265c4fdfbSgilles enum smtp_state { 5365c4fdfbSgilles STATE_NEW = 0, 5465c4fdfbSgilles STATE_CONNECTED, 5565c4fdfbSgilles STATE_TLS, 5665c4fdfbSgilles STATE_HELO, 5765c4fdfbSgilles STATE_AUTH_INIT, 5865c4fdfbSgilles STATE_AUTH_USERNAME, 5965c4fdfbSgilles STATE_AUTH_PASSWORD, 6065c4fdfbSgilles STATE_AUTH_FINALIZE, 6165c4fdfbSgilles STATE_BODY, 6265c4fdfbSgilles STATE_QUIT, 6365c4fdfbSgilles }; 6465c4fdfbSgilles 6565c4fdfbSgilles enum session_flags { 6665c4fdfbSgilles SF_EHLO = 0x0001, 6765c4fdfbSgilles SF_8BITMIME = 0x0002, 6865c4fdfbSgilles SF_SECURE = 0x0004, 6965c4fdfbSgilles SF_AUTHENTICATED = 0x0008, 7065c4fdfbSgilles SF_BOUNCE = 0x0010, 71b78cbe77Sgilles SF_VERIFIED = 0x0020, 72b78cbe77Sgilles SF_BADINPUT = 0x0080, 7365c4fdfbSgilles }; 7465c4fdfbSgilles 75f299ff90Seric enum { 76f299ff90Seric TX_OK = 0, 77f299ff90Seric TX_ERROR_ENVELOPE, 78f299ff90Seric TX_ERROR_SIZE, 79f299ff90Seric TX_ERROR_IO, 80f299ff90Seric TX_ERROR_LOOP, 81f299ff90Seric TX_ERROR_MALFORMED, 82f299ff90Seric TX_ERROR_RESOURCES 8365c4fdfbSgilles }; 8465c4fdfbSgilles 8565c4fdfbSgilles enum smtp_command { 8665c4fdfbSgilles CMD_HELO = 0, 8765c4fdfbSgilles CMD_EHLO, 8865c4fdfbSgilles CMD_STARTTLS, 8965c4fdfbSgilles CMD_AUTH, 9065c4fdfbSgilles CMD_MAIL_FROM, 9165c4fdfbSgilles CMD_RCPT_TO, 9265c4fdfbSgilles CMD_DATA, 9365c4fdfbSgilles CMD_RSET, 9465c4fdfbSgilles CMD_QUIT, 9565c4fdfbSgilles CMD_HELP, 96fe95d8d0Seric CMD_WIZ, 9765c4fdfbSgilles CMD_NOOP, 9865c4fdfbSgilles }; 9965c4fdfbSgilles 100ee3e6bebSeric struct smtp_rcpt { 101ee3e6bebSeric TAILQ_ENTRY(smtp_rcpt) entry; 102ee3e6bebSeric struct mailaddr maddr; 103ee3e6bebSeric size_t destcount; 104ee3e6bebSeric }; 105ee3e6bebSeric 1060a80c58aSeric struct smtp_tx { 1070a80c58aSeric struct smtp_session *session; 108763ff2e3Seric uint32_t msgid; 1090a80c58aSeric 1100a80c58aSeric struct envelope evp; 1110a80c58aSeric size_t rcptcount; 1120a80c58aSeric size_t destcount; 1130a80c58aSeric TAILQ_HEAD(, smtp_rcpt) rcpts; 1140a80c58aSeric 115d7b42ce8Seric time_t time; 116f299ff90Seric int error; 117f71caa29Seric size_t datain; 1180a80c58aSeric size_t odatalen; 11984639b21Seric FILE *ofile; 1200a80c58aSeric int hdrdone; 1210a80c58aSeric int rcvcount; 1220a80c58aSeric int skiphdr; 1230a80c58aSeric struct rfc2822_parser rfc2822_parser; 1240a80c58aSeric }; 1250a80c58aSeric 12665c4fdfbSgilles struct smtp_session { 12765c4fdfbSgilles uint64_t id; 1288d3f7f0dSeric struct io *io; 12965c4fdfbSgilles struct listener *listener; 1301c3ac238Seric void *ssl_ctx; 13165c4fdfbSgilles struct sockaddr_storage ss; 132953aae25Sderaadt char hostname[HOST_NAME_MAX+1]; 133953aae25Sderaadt char smtpname[HOST_NAME_MAX+1]; 13465c4fdfbSgilles 13565c4fdfbSgilles int flags; 13665c4fdfbSgilles enum smtp_state state; 13765c4fdfbSgilles 138953aae25Sderaadt char helo[LINE_MAX]; 139953aae25Sderaadt char cmd[LINE_MAX]; 1406dfe9135Sgilles char username[SMTPD_MAXMAILADDRSIZE]; 14165c4fdfbSgilles 14265c4fdfbSgilles size_t mailcount; 143299c4efeSeric struct event pause; 1446bc0eb6bSgilles 1450a80c58aSeric struct smtp_tx *tx; 14665c4fdfbSgilles }; 1472a4d7b1bSeric 148f4807aa6Seric #define ADVERTISE_TLS(s) \ 14965c4fdfbSgilles ((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE)) 150f4807aa6Seric 151f4807aa6Seric #define ADVERTISE_AUTH(s) \ 15265c4fdfbSgilles ((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \ 15365c4fdfbSgilles !((s)->flags & SF_AUTHENTICATED)) 154f4807aa6Seric 155ad286364Seric #define ADVERTISE_EXT_DSN(s) \ 156ad286364Seric ((s)->listener->flags & F_EXT_DSN) 157ad286364Seric 158cc81b7c6Seric static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *); 15965c4fdfbSgilles static void smtp_session_init(void); 1601c3ac238Seric static int smtp_lookup_servername(struct smtp_session *); 16165c4fdfbSgilles static void smtp_connected(struct smtp_session *); 162cc81b7c6Seric static void smtp_send_banner(struct smtp_session *); 1631ddc3febSeric static void smtp_tls_verified(struct smtp_session *); 164b556a8d3Seric static void smtp_io(struct io *, int, void *); 16565c4fdfbSgilles static void smtp_enter_state(struct smtp_session *, int); 16665c4fdfbSgilles static void smtp_reply(struct smtp_session *, char *, ...); 16765c4fdfbSgilles static void smtp_command(struct smtp_session *, char *); 16865c4fdfbSgilles static int smtp_parse_mail_args(struct smtp_session *, char *); 169fe95d8d0Seric static int smtp_parse_rcpt_args(struct smtp_session *, char *); 17065c4fdfbSgilles static void smtp_rfc4954_auth_plain(struct smtp_session *, char *); 17165c4fdfbSgilles static void smtp_rfc4954_auth_login(struct smtp_session *, char *); 17265c4fdfbSgilles static void smtp_free(struct smtp_session *, const char *); 17365c4fdfbSgilles static const char *smtp_strstate(int); 1747627c963Seric static void smtp_tls_init(struct smtp_session *); 17565c4fdfbSgilles static int smtp_verify_certificate(struct smtp_session *); 176299c4efeSeric static void smtp_auth_failure_pause(struct smtp_session *); 177299c4efeSeric static void smtp_auth_failure_resume(int, short, void *); 178b04e6f27Seric 1790a80c58aSeric static int smtp_tx(struct smtp_session *); 1800a80c58aSeric static void smtp_tx_free(struct smtp_tx *); 181008f3f72Seric static void smtp_tx_create_message(struct smtp_tx *); 18272cde3cbSeric static void smtp_tx_mail_from(struct smtp_tx *, char *); 18372cde3cbSeric static void smtp_tx_rcpt_to(struct smtp_tx *, char *); 184008f3f72Seric static void smtp_tx_open_message(struct smtp_tx *); 185008f3f72Seric static void smtp_tx_commit(struct smtp_tx *); 186008f3f72Seric static void smtp_tx_rollback(struct smtp_tx *); 187008f3f72Seric static int smtp_tx_dataline(struct smtp_tx *, const char *); 18855042ccbSeric static void smtp_message_fd(struct smtp_tx *, int); 18955042ccbSeric static void smtp_message_end(struct smtp_tx *); 19055042ccbSeric static int smtp_message_printf(struct smtp_tx *, const char *, ...); 19108becfa3Seric 19265c4fdfbSgilles static struct { int code; const char *cmd; } commands[] = { 19365c4fdfbSgilles { CMD_HELO, "HELO" }, 19465c4fdfbSgilles { CMD_EHLO, "EHLO" }, 19565c4fdfbSgilles { CMD_STARTTLS, "STARTTLS" }, 19665c4fdfbSgilles { CMD_AUTH, "AUTH" }, 19765c4fdfbSgilles { CMD_MAIL_FROM, "MAIL FROM" }, 19865c4fdfbSgilles { CMD_RCPT_TO, "RCPT TO" }, 19965c4fdfbSgilles { CMD_DATA, "DATA" }, 20065c4fdfbSgilles { CMD_RSET, "RSET" }, 20165c4fdfbSgilles { CMD_QUIT, "QUIT" }, 20265c4fdfbSgilles { CMD_HELP, "HELP" }, 203fe95d8d0Seric { CMD_WIZ, "WIZ" }, 20465c4fdfbSgilles { CMD_NOOP, "NOOP" }, 20565c4fdfbSgilles { -1, NULL }, 2063ef9cbf7Sgilles }; 2073ef9cbf7Sgilles 20865c4fdfbSgilles static struct tree wait_lka_ptr; 209cc81b7c6Seric static struct tree wait_lka_helo; 210a1bd4550Sgilles static struct tree wait_lka_mail; 21165c4fdfbSgilles static struct tree wait_lka_rcpt; 21265c4fdfbSgilles static struct tree wait_parent_auth; 21365c4fdfbSgilles static struct tree wait_queue_msg; 21465c4fdfbSgilles static struct tree wait_queue_fd; 21565c4fdfbSgilles static struct tree wait_queue_commit; 21665c4fdfbSgilles static struct tree wait_ssl_init; 21765c4fdfbSgilles static struct tree wait_ssl_verify; 2183ef9cbf7Sgilles 21965c4fdfbSgilles static void 2206bc0eb6bSgilles header_default_callback(const struct rfc2822_header *hdr, void *arg) 2216bc0eb6bSgilles { 222eb95ffd1Seric struct smtp_tx *tx = arg; 2236bc0eb6bSgilles struct rfc2822_line *l; 2246bc0eb6bSgilles 22555042ccbSeric if (smtp_message_printf(tx, "%s:", hdr->name) == -1) 2266bc0eb6bSgilles return; 2276bc0eb6bSgilles 2281b33e826Seric TAILQ_FOREACH(l, &hdr->lines, next) 22955042ccbSeric if (smtp_message_printf(tx, "%s\n", l->buffer) == -1) 2306bc0eb6bSgilles return; 2316bc0eb6bSgilles } 2326bc0eb6bSgilles 2336bc0eb6bSgilles static void 2346bc0eb6bSgilles dataline_callback(const char *line, void *arg) 2356bc0eb6bSgilles { 236eb95ffd1Seric struct smtp_tx *tx = arg; 2376bc0eb6bSgilles 238eb95ffd1Seric smtp_message_printf(tx, "%s\n", line); 2396bc0eb6bSgilles } 2406bc0eb6bSgilles 2416bc0eb6bSgilles static void 2426bc0eb6bSgilles header_bcc_callback(const struct rfc2822_header *hdr, void *arg) 2436bc0eb6bSgilles { 2446bc0eb6bSgilles } 2456bc0eb6bSgilles 2466bc0eb6bSgilles static void 24727b32957Sgilles header_append_domain_buffer(char *buffer, char *domain, size_t len) 24827b32957Sgilles { 24927b32957Sgilles size_t i; 25027b32957Sgilles int escape, quote, comment, bracket; 25127b32957Sgilles int has_domain, has_bracket, has_group; 25227b32957Sgilles int pos_bracket, pos_component, pos_insert; 25327b32957Sgilles char copy[APPEND_DOMAIN_BUFFER_SIZE]; 25427b32957Sgilles 25527b32957Sgilles i = 0; 25627b32957Sgilles escape = quote = comment = bracket = 0; 25727b32957Sgilles has_domain = has_bracket = has_group = 0; 25827b32957Sgilles pos_bracket = pos_insert = pos_component = 0; 25927b32957Sgilles for (i = 0; buffer[i]; ++i) { 26027b32957Sgilles if (buffer[i] == '(' && !escape && !quote) 26127b32957Sgilles comment++; 26227b32957Sgilles if (buffer[i] == '"' && !escape && !comment) 26327b32957Sgilles quote = !quote; 26427b32957Sgilles if (buffer[i] == ')' && !escape && !quote && comment) 26527b32957Sgilles comment--; 26627b32957Sgilles if (buffer[i] == '\\' && !escape && !comment && !quote) 26727b32957Sgilles escape = 1; 26827b32957Sgilles else 26927b32957Sgilles escape = 0; 27027b32957Sgilles if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { 27127b32957Sgilles bracket++; 27227b32957Sgilles has_bracket = 1; 27327b32957Sgilles } 27427b32957Sgilles if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { 27527b32957Sgilles bracket--; 27627b32957Sgilles pos_bracket = i; 27727b32957Sgilles } 27827b32957Sgilles if (buffer[i] == '@' && !escape && !comment && !quote) 27927b32957Sgilles has_domain = 1; 28027b32957Sgilles if (buffer[i] == ':' && !escape && !comment && !quote) 28127b32957Sgilles has_group = 1; 282cb2a19a6Sgilles 283cb2a19a6Sgilles /* update insert point if not in comment and not on a whitespace */ 284cb2a19a6Sgilles if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i])) 28527b32957Sgilles pos_component = i; 28627b32957Sgilles } 28727b32957Sgilles 28827b32957Sgilles /* parse error, do not attempt to modify */ 28927b32957Sgilles if (escape || quote || comment || bracket) 29027b32957Sgilles return; 29127b32957Sgilles 29227b32957Sgilles /* domain already present, no need to modify */ 29327b32957Sgilles if (has_domain) 29427b32957Sgilles return; 29527b32957Sgilles 29627b32957Sgilles /* address is group, skip */ 29727b32957Sgilles if (has_group) 29827b32957Sgilles return; 29927b32957Sgilles 30027b32957Sgilles /* there's an address between brackets, just append domain */ 30127b32957Sgilles if (has_bracket) { 30227b32957Sgilles pos_bracket--; 303cb2a19a6Sgilles while (isspace((unsigned char)buffer[pos_bracket])) 30427b32957Sgilles pos_bracket--; 30527b32957Sgilles if (buffer[pos_bracket] == '<') 30627b32957Sgilles return; 30727b32957Sgilles pos_insert = pos_bracket + 1; 30827b32957Sgilles } 30927b32957Sgilles else { 31027b32957Sgilles /* otherwise append address to last component */ 31127b32957Sgilles pos_insert = pos_component + 1; 31227b32957Sgilles 31327b32957Sgilles /* empty address */ 314cb2a19a6Sgilles if (buffer[pos_component] == '\0' || 315cb2a19a6Sgilles isspace((unsigned char)buffer[pos_component])) 31627b32957Sgilles return; 31727b32957Sgilles } 31827b32957Sgilles 31927b32957Sgilles if (snprintf(copy, sizeof copy, "%.*s@%s%s", 32027b32957Sgilles (int)pos_insert, buffer, 32127b32957Sgilles domain, 32227b32957Sgilles buffer+pos_insert) >= (int)sizeof copy) 32327b32957Sgilles return; 32427b32957Sgilles 32527b32957Sgilles memcpy(buffer, copy, len); 32627b32957Sgilles } 32727b32957Sgilles 32827b32957Sgilles static void 3294d531edbSgilles header_address_rewrite_buffer(char *buffer, const char *address, size_t len) 3304d531edbSgilles { 3314d531edbSgilles size_t i; 3324d531edbSgilles int address_len; 3334d531edbSgilles int escape, quote, comment, bracket; 3344d531edbSgilles int has_bracket, has_group; 3354d531edbSgilles int pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end; 3364d531edbSgilles int insert_beg, insert_end; 3374d531edbSgilles char copy[APPEND_DOMAIN_BUFFER_SIZE]; 3384d531edbSgilles 3394d531edbSgilles escape = quote = comment = bracket = 0; 3404d531edbSgilles has_bracket = has_group = 0; 3414d531edbSgilles pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0; 3424d531edbSgilles for (i = 0; buffer[i]; ++i) { 3434d531edbSgilles if (buffer[i] == '(' && !escape && !quote) 3444d531edbSgilles comment++; 3454d531edbSgilles if (buffer[i] == '"' && !escape && !comment) 3464d531edbSgilles quote = !quote; 3474d531edbSgilles if (buffer[i] == ')' && !escape && !quote && comment) 3484d531edbSgilles comment--; 3494d531edbSgilles if (buffer[i] == '\\' && !escape && !comment && !quote) 3504d531edbSgilles escape = 1; 3514d531edbSgilles else 3524d531edbSgilles escape = 0; 3534d531edbSgilles if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { 3544d531edbSgilles bracket++; 3554d531edbSgilles has_bracket = 1; 3564d531edbSgilles pos_bracket_beg = i+1; 3574d531edbSgilles } 3584d531edbSgilles if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { 3594d531edbSgilles bracket--; 3604d531edbSgilles pos_bracket_end = i; 3614d531edbSgilles } 3624d531edbSgilles if (buffer[i] == ':' && !escape && !comment && !quote) 3634d531edbSgilles has_group = 1; 3644d531edbSgilles 3654d531edbSgilles /* update insert point if not in comment and not on a whitespace */ 3664d531edbSgilles if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i])) 3674d531edbSgilles pos_component_end = i; 3684d531edbSgilles } 3694d531edbSgilles 3704d531edbSgilles /* parse error, do not attempt to modify */ 3714d531edbSgilles if (escape || quote || comment || bracket) 3724d531edbSgilles return; 3734d531edbSgilles 3744d531edbSgilles /* address is group, skip */ 3754d531edbSgilles if (has_group) 3764d531edbSgilles return; 3774d531edbSgilles 3784d531edbSgilles /* there's an address between brackets, just replace everything brackets */ 3794d531edbSgilles if (has_bracket) { 3804d531edbSgilles insert_beg = pos_bracket_beg; 3814d531edbSgilles insert_end = pos_bracket_end; 3824d531edbSgilles } 3834d531edbSgilles else { 3844d531edbSgilles if (pos_component_end == 0) 3854d531edbSgilles pos_component_beg = 0; 3864d531edbSgilles else { 3874d531edbSgilles for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg) 3884d531edbSgilles if (buffer[pos_component_beg] == ')' || isspace(buffer[pos_component_beg])) 3894d531edbSgilles break; 3904d531edbSgilles pos_component_beg += 1; 3914d531edbSgilles pos_component_end += 1; 3924d531edbSgilles } 3934d531edbSgilles insert_beg = pos_component_beg; 3944d531edbSgilles insert_end = pos_component_end; 3954d531edbSgilles } 3964d531edbSgilles 3974d531edbSgilles /* check that masquerade won' t overflow */ 3984d531edbSgilles address_len = strlen(address); 3994d531edbSgilles if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len) 4004d531edbSgilles return; 4014d531edbSgilles 4024d531edbSgilles (void)strlcpy(copy, buffer, sizeof copy); 4034d531edbSgilles (void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg); 4044d531edbSgilles (void)strlcat(copy, buffer+insert_end, sizeof (copy)); 4054d531edbSgilles memcpy(buffer, copy, len); 4064d531edbSgilles } 4074d531edbSgilles 4084d531edbSgilles static void 4094ea1d675Seric header_domain_append_callback(const struct rfc2822_header *hdr, void *arg) 4104d531edbSgilles { 411eb95ffd1Seric struct smtp_session *s; 412eb95ffd1Seric struct smtp_tx *tx = arg; 4134d531edbSgilles struct rfc2822_line *l; 4144d531edbSgilles size_t i, j; 4154d531edbSgilles int escape, quote, comment, skip; 4164d531edbSgilles char buffer[APPEND_DOMAIN_BUFFER_SIZE]; 4174d531edbSgilles 418eb95ffd1Seric s = tx->session; 41955042ccbSeric 42055042ccbSeric if (smtp_message_printf(tx, "%s:", hdr->name) == -1) 4214d531edbSgilles return; 4224d531edbSgilles 4234d531edbSgilles j = 0; 4244d531edbSgilles escape = quote = comment = skip = 0; 4254d531edbSgilles memset(buffer, 0, sizeof buffer); 4264d531edbSgilles 4274d531edbSgilles TAILQ_FOREACH(l, &hdr->lines, next) { 4284d531edbSgilles for (i = 0; i < strlen(l->buffer); ++i) { 4294d531edbSgilles if (l->buffer[i] == '(' && !escape && !quote) 4304d531edbSgilles comment++; 4314d531edbSgilles if (l->buffer[i] == '"' && !escape && !comment) 4324d531edbSgilles quote = !quote; 4334d531edbSgilles if (l->buffer[i] == ')' && !escape && !quote && comment) 4344d531edbSgilles comment--; 4354d531edbSgilles if (l->buffer[i] == '\\' && !escape && !comment && !quote) 4364d531edbSgilles escape = 1; 4374d531edbSgilles else 4384d531edbSgilles escape = 0; 4394d531edbSgilles 4404d531edbSgilles /* found a separator, buffer contains a full address */ 4414d531edbSgilles if (l->buffer[i] == ',' && !escape && !quote && !comment) { 4424d531edbSgilles if (!skip && j + strlen(s->listener->hostname) + 1 < sizeof buffer) { 4434d531edbSgilles header_append_domain_buffer(buffer, s->listener->hostname, sizeof buffer); 4444ea1d675Seric if (s->flags & SF_AUTHENTICATED && 4454ea1d675Seric s->listener->sendertable[0] && 4464ea1d675Seric s->listener->flags & F_MASQUERADE && 4474ea1d675Seric !(strcasecmp(hdr->name, "From"))) 44855042ccbSeric header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender), 4494d531edbSgilles sizeof buffer); 4504d531edbSgilles } 45155042ccbSeric if (smtp_message_printf(tx, "%s,", buffer) == -1) 4524d531edbSgilles return; 4534d531edbSgilles j = 0; 4544d531edbSgilles skip = 0; 4554d531edbSgilles memset(buffer, 0, sizeof buffer); 4564d531edbSgilles } 4574d531edbSgilles else { 4584d531edbSgilles if (skip) { 45955042ccbSeric if (smtp_message_printf(tx, "%c", l->buffer[i]) == -1) 4604d531edbSgilles return; 4614d531edbSgilles } 4624d531edbSgilles else { 4634d531edbSgilles buffer[j++] = l->buffer[i]; 4644d531edbSgilles if (j == sizeof (buffer) - 1) { 46555042ccbSeric if (smtp_message_printf(tx, "%s", buffer) == -1) 4664d531edbSgilles return; 4674d531edbSgilles skip = 1; 4684d531edbSgilles j = 0; 4694d531edbSgilles memset(buffer, 0, sizeof buffer); 4704d531edbSgilles } 4714d531edbSgilles } 4724d531edbSgilles } 4734d531edbSgilles } 4744d531edbSgilles if (skip) { 47555042ccbSeric if (smtp_message_printf(tx, "\n") == -1) 4764d531edbSgilles return; 4774d531edbSgilles } 4784d531edbSgilles else { 4794d531edbSgilles buffer[j++] = '\n'; 4804d531edbSgilles if (j == sizeof (buffer) - 1) { 48155042ccbSeric if (smtp_message_printf(tx, "%s", buffer) == -1) 4824d531edbSgilles return; 4834d531edbSgilles skip = 1; 4844d531edbSgilles j = 0; 4854d531edbSgilles memset(buffer, 0, sizeof buffer); 4864d531edbSgilles } 4874d531edbSgilles } 4884d531edbSgilles } 4894d531edbSgilles 4904d531edbSgilles /* end of header, if buffer is not empty we'll process it */ 4914d531edbSgilles if (buffer[0]) { 4924d531edbSgilles if (j + strlen(s->listener->hostname) + 1 < sizeof buffer) { 4934d531edbSgilles header_append_domain_buffer(buffer, s->listener->hostname, sizeof buffer); 4944ea1d675Seric if (s->flags & SF_AUTHENTICATED && 4954ea1d675Seric s->listener->sendertable[0] && 4964ea1d675Seric s->listener->flags & F_MASQUERADE && 4974ea1d675Seric !(strcasecmp(hdr->name, "From"))) 49855042ccbSeric header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender), 4994d531edbSgilles sizeof buffer); 5004d531edbSgilles } 50155042ccbSeric smtp_message_printf(tx, "%s", buffer); 5024d531edbSgilles } 5034d531edbSgilles } 5044d531edbSgilles 5054d531edbSgilles static void 5062e02d062Sgilles header_missing_callback(const char *header, void *arg) 5072e02d062Sgilles { 508eb95ffd1Seric struct smtp_tx *tx = arg; 5092e02d062Sgilles 5101b33e826Seric if (strcasecmp(header, "message-id") == 0) 51155042ccbSeric smtp_message_printf(tx, "Message-Id: <%016"PRIx64"@%s>\n", 512eb95ffd1Seric generate_uid(), tx->session->listener->hostname); 5137fc7f1aaSgilles 5141b33e826Seric if (strcasecmp(header, "date") == 0) 51555042ccbSeric smtp_message_printf(tx, "Date: %s\n", time_to_text(tx->time)); 5162e02d062Sgilles } 5172e02d062Sgilles 5182e02d062Sgilles static void 51965c4fdfbSgilles smtp_session_init(void) 5203ef9cbf7Sgilles { 52165c4fdfbSgilles static int init = 0; 522ef9f3596Sjacekm 52365c4fdfbSgilles if (!init) { 52465c4fdfbSgilles tree_init(&wait_lka_ptr); 525cc81b7c6Seric tree_init(&wait_lka_helo); 526a1bd4550Sgilles tree_init(&wait_lka_mail); 52765c4fdfbSgilles tree_init(&wait_lka_rcpt); 52865c4fdfbSgilles tree_init(&wait_parent_auth); 52965c4fdfbSgilles tree_init(&wait_queue_msg); 53065c4fdfbSgilles tree_init(&wait_queue_fd); 53165c4fdfbSgilles tree_init(&wait_queue_commit); 53265c4fdfbSgilles tree_init(&wait_ssl_init); 53365c4fdfbSgilles tree_init(&wait_ssl_verify); 53465c4fdfbSgilles init = 1; 53565c4fdfbSgilles } 5363ef9cbf7Sgilles } 5373ef9cbf7Sgilles 53865c4fdfbSgilles int 53965c4fdfbSgilles smtp_session(struct listener *listener, int sock, 54065c4fdfbSgilles const struct sockaddr_storage *ss, const char *hostname) 5413ef9cbf7Sgilles { 54265c4fdfbSgilles struct smtp_session *s; 5433ef9cbf7Sgilles 54465c4fdfbSgilles log_debug("debug: smtp: new client on listener: %p", listener); 54565c4fdfbSgilles 54665c4fdfbSgilles smtp_session_init(); 54765c4fdfbSgilles 54865c4fdfbSgilles if ((s = calloc(1, sizeof(*s))) == NULL) 54965c4fdfbSgilles return (-1); 5500a80c58aSeric 55165c4fdfbSgilles s->id = generate_uid(); 55265c4fdfbSgilles s->listener = listener; 55365c4fdfbSgilles memmove(&s->ss, ss, sizeof(*ss)); 5548d3f7f0dSeric s->io = io_new(); 5558d3f7f0dSeric io_set_callback(s->io, smtp_io, s); 5568d3f7f0dSeric io_set_fd(s->io, sock); 5578d3f7f0dSeric io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000); 5588d3f7f0dSeric io_set_write(s->io); 55965c4fdfbSgilles 56065c4fdfbSgilles s->state = STATE_NEW; 56165c4fdfbSgilles 562c51a7fb7Sgilles (void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname)); 563cc81b7c6Seric 56465195c54Sgilles log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p " 56565195c54Sgilles "[hostname=%s, port=%d, tag=%s]", s, listener, 56665195c54Sgilles listener->hostname, ntohs(listener->port), listener->tag); 56765195c54Sgilles 56865c4fdfbSgilles /* For local enqueueing, the hostname is already set */ 56965c4fdfbSgilles if (hostname) { 57065c4fdfbSgilles s->flags |= SF_AUTHENTICATED; 57165c4fdfbSgilles /* A bit of a hack */ 57265c4fdfbSgilles if (!strcmp(hostname, "localhost")) 57365c4fdfbSgilles s->flags |= SF_BOUNCE; 574c51a7fb7Sgilles (void)strlcpy(s->hostname, hostname, sizeof(s->hostname)); 5751c3ac238Seric if (smtp_lookup_servername(s)) 57665c4fdfbSgilles smtp_connected(s); 57765c4fdfbSgilles } else { 578aa1d5973Seric m_create(p_lka, IMSG_SMTP_DNS_PTR, 0, 0, -1); 579aa1d5973Seric m_add_id(p_lka, s->id); 580aa1d5973Seric m_add_sockaddr(p_lka, (struct sockaddr *)&s->ss); 581aa1d5973Seric m_close(p_lka); 58265c4fdfbSgilles tree_xset(&wait_lka_ptr, s->id, s); 5837219d653Sjacekm } 584ef9f3596Sjacekm 585cb2a19a6Sgilles /* session may have been freed by now */ 5866bc0eb6bSgilles 58765c4fdfbSgilles return (0); 5883ef9cbf7Sgilles } 5893ef9cbf7Sgilles 59065c4fdfbSgilles void 59165c4fdfbSgilles smtp_session_imsg(struct mproc *p, struct imsg *imsg) 59265c4fdfbSgilles { 59365c4fdfbSgilles struct ca_cert_resp_msg *resp_ca_cert; 59465c4fdfbSgilles struct ca_vrfy_resp_msg *resp_ca_vrfy; 59565c4fdfbSgilles struct smtp_session *s; 596ee3e6bebSeric struct smtp_rcpt *rcpt; 59765c4fdfbSgilles void *ssl; 598953aae25Sderaadt char user[LOGIN_NAME_MAX]; 59965c4fdfbSgilles struct msg m; 600cc81b7c6Seric const char *line, *helo; 60165c4fdfbSgilles uint64_t reqid, evpid; 60208becfa3Seric uint32_t msgid; 60365c4fdfbSgilles int status, success, dnserror; 6041c3ac238Seric void *ssl_ctx; 60565c4fdfbSgilles 60665c4fdfbSgilles switch (imsg->hdr.type) { 607aa1d5973Seric case IMSG_SMTP_DNS_PTR: 60865c4fdfbSgilles m_msg(&m, imsg); 60965c4fdfbSgilles m_get_id(&m, &reqid); 61065c4fdfbSgilles m_get_int(&m, &dnserror); 61165c4fdfbSgilles if (dnserror) 61265c4fdfbSgilles line = "<unknown>"; 61365c4fdfbSgilles else 61465c4fdfbSgilles m_get_string(&m, &line); 61565c4fdfbSgilles m_end(&m); 61665c4fdfbSgilles s = tree_xpop(&wait_lka_ptr, reqid); 6175dc5ee61Sgilles (void)strlcpy(s->hostname, line, sizeof s->hostname); 6181c3ac238Seric if (smtp_lookup_servername(s)) 61965c4fdfbSgilles smtp_connected(s); 62065c4fdfbSgilles return; 62165c4fdfbSgilles 62206405042Ssunil case IMSG_SMTP_CHECK_SENDER: 62306405042Ssunil m_msg(&m, imsg); 62406405042Ssunil m_get_id(&m, &reqid); 62506405042Ssunil m_get_int(&m, &status); 62606405042Ssunil m_end(&m); 62706405042Ssunil s = tree_xpop(&wait_lka_mail, reqid); 62806405042Ssunil switch (status) { 62906405042Ssunil case LKA_OK: 630008f3f72Seric smtp_tx_create_message(s->tx); 63106405042Ssunil break; 63206405042Ssunil 63306405042Ssunil case LKA_PERMFAIL: 6340490754bSeric smtp_tx_free(s->tx); 63506405042Ssunil smtp_reply(s, "%d %s", 530, "Sender rejected"); 63606405042Ssunil break; 63706405042Ssunil case LKA_TEMPFAIL: 6380490754bSeric smtp_tx_free(s->tx); 63906405042Ssunil smtp_reply(s, "421 %s: Temporary Error", 64006405042Ssunil esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 64106405042Ssunil break; 64206405042Ssunil } 64306405042Ssunil return; 64406405042Ssunil 645aa1d5973Seric case IMSG_SMTP_EXPAND_RCPT: 64665c4fdfbSgilles m_msg(&m, imsg); 64765c4fdfbSgilles m_get_id(&m, &reqid); 64865c4fdfbSgilles m_get_int(&m, &status); 649299c4efeSeric m_get_string(&m, &line); 65065c4fdfbSgilles m_end(&m); 65165c4fdfbSgilles s = tree_xpop(&wait_lka_rcpt, reqid); 65265c4fdfbSgilles switch (status) { 65365c4fdfbSgilles case LKA_OK: 65465c4fdfbSgilles fatalx("unexpected ok"); 65565c4fdfbSgilles case LKA_PERMFAIL: 656299c4efeSeric smtp_reply(s, "%s", line); 65765c4fdfbSgilles break; 65865c4fdfbSgilles case LKA_TEMPFAIL: 659299c4efeSeric smtp_reply(s, "%s", line); 66065c4fdfbSgilles } 66165c4fdfbSgilles return; 66265c4fdfbSgilles 663aa1d5973Seric case IMSG_SMTP_LOOKUP_HELO: 664cc81b7c6Seric m_msg(&m, imsg); 665cc81b7c6Seric m_get_id(&m, &reqid); 666cc81b7c6Seric s = tree_xpop(&wait_lka_helo, reqid); 667cc81b7c6Seric m_get_int(&m, &status); 668cc81b7c6Seric if (status == LKA_OK) { 669cc81b7c6Seric m_get_string(&m, &helo); 670d429e01dSgilles (void)strlcpy(s->smtpname, helo, sizeof(s->smtpname)); 671cc81b7c6Seric } 672cc81b7c6Seric m_end(&m); 6731c3ac238Seric smtp_connected(s); 674cc81b7c6Seric return; 675cc81b7c6Seric 676aa1d5973Seric case IMSG_SMTP_MESSAGE_CREATE: 67765c4fdfbSgilles m_msg(&m, imsg); 67865c4fdfbSgilles m_get_id(&m, &reqid); 67965c4fdfbSgilles m_get_int(&m, &success); 68065c4fdfbSgilles s = tree_xpop(&wait_queue_msg, reqid); 68165c4fdfbSgilles if (success) { 68265c4fdfbSgilles m_get_msgid(&m, &msgid); 683763ff2e3Seric s->tx->msgid = msgid; 6840a80c58aSeric s->tx->evp.id = msgid_to_evpid(msgid); 6850a80c58aSeric s->tx->rcptcount = 0; 686fe95d8d0Seric smtp_reply(s, "250 %s: Ok", 687fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 68865c4fdfbSgilles } else { 6890490754bSeric smtp_tx_free(s->tx); 690fe95d8d0Seric smtp_reply(s, "421 %s: Temporary Error", 691fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 692299c4efeSeric smtp_enter_state(s, STATE_QUIT); 69365c4fdfbSgilles } 69465c4fdfbSgilles m_end(&m); 69565c4fdfbSgilles return; 69665c4fdfbSgilles 697aa1d5973Seric case IMSG_SMTP_MESSAGE_OPEN: 69865c4fdfbSgilles m_msg(&m, imsg); 69965c4fdfbSgilles m_get_id(&m, &reqid); 70065c4fdfbSgilles m_get_int(&m, &success); 70165c4fdfbSgilles m_end(&m); 70265c4fdfbSgilles 70365c4fdfbSgilles s = tree_xpop(&wait_queue_fd, reqid); 704f7c2eb5aSgilles if (!success || imsg->fd == -1) { 70565c4fdfbSgilles if (imsg->fd != -1) 70665c4fdfbSgilles close(imsg->fd); 707fe95d8d0Seric smtp_reply(s, "421 %s: Temporary Error", 708fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 70965c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 71065c4fdfbSgilles return; 71165c4fdfbSgilles } 71265c4fdfbSgilles 713f7c2eb5aSgilles log_debug("smtp: %p: fd %d from queue", s, imsg->fd); 71465c4fdfbSgilles 71555042ccbSeric smtp_message_fd(s->tx, imsg->fd); 71665c4fdfbSgilles return; 71765c4fdfbSgilles 718aa1d5973Seric case IMSG_QUEUE_ENVELOPE_SUBMIT: 71965c4fdfbSgilles m_msg(&m, imsg); 72065c4fdfbSgilles m_get_id(&m, &reqid); 72165c4fdfbSgilles m_get_int(&m, &success); 72265c4fdfbSgilles s = tree_xget(&wait_lka_rcpt, reqid); 72365c4fdfbSgilles if (success) { 72465c4fdfbSgilles m_get_evpid(&m, &evpid); 7250a80c58aSeric s->tx->destcount++; 72665c4fdfbSgilles } 72765c4fdfbSgilles else 728f299ff90Seric s->tx->error = TX_ERROR_ENVELOPE; 72965c4fdfbSgilles m_end(&m); 73065c4fdfbSgilles return; 73165c4fdfbSgilles 732aa1d5973Seric case IMSG_QUEUE_ENVELOPE_COMMIT: 73365c4fdfbSgilles m_msg(&m, imsg); 73465c4fdfbSgilles m_get_id(&m, &reqid); 73565c4fdfbSgilles m_get_int(&m, &success); 73665c4fdfbSgilles m_end(&m); 73765c4fdfbSgilles if (!success) 73865c4fdfbSgilles fatalx("commit evp failed: not supposed to happen"); 73965c4fdfbSgilles s = tree_xpop(&wait_lka_rcpt, reqid); 740f299ff90Seric if (s->tx->error) { 74165c4fdfbSgilles /* 74265c4fdfbSgilles * If an envelope failed, we can't cancel the last 74365c4fdfbSgilles * RCPT only so we must cancel the whole transaction 74465c4fdfbSgilles * and close the connection. 74565c4fdfbSgilles */ 746fe95d8d0Seric smtp_reply(s, "421 %s: Temporary failure", 747fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 74865c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 74965c4fdfbSgilles } 75065c4fdfbSgilles else { 751ee3e6bebSeric rcpt = xcalloc(1, sizeof(*rcpt), "smtp_rcpt"); 7520a80c58aSeric rcpt->destcount = s->tx->destcount; 7530a80c58aSeric rcpt->maddr = s->tx->evp.rcpt; 7540a80c58aSeric TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry); 755ee3e6bebSeric 7560a80c58aSeric s->tx->destcount = 0; 7570a80c58aSeric s->tx->rcptcount++; 758fe95d8d0Seric smtp_reply(s, "250 %s %s: Recipient ok", 759fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID), 760fe95d8d0Seric esc_description(ESC_DESTINATION_ADDRESS_VALID)); 76165c4fdfbSgilles } 76265c4fdfbSgilles return; 76365c4fdfbSgilles 764aa1d5973Seric case IMSG_SMTP_MESSAGE_COMMIT: 76565c4fdfbSgilles m_msg(&m, imsg); 76665c4fdfbSgilles m_get_id(&m, &reqid); 76765c4fdfbSgilles m_get_int(&m, &success); 76865c4fdfbSgilles m_end(&m); 76965c4fdfbSgilles s = tree_xpop(&wait_queue_commit, reqid); 77065c4fdfbSgilles if (!success) { 7710490754bSeric smtp_tx_free(s->tx); 772fe95d8d0Seric smtp_reply(s, "421 %s: Temporary failure", 773fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 77465c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 77565c4fdfbSgilles return; 77665c4fdfbSgilles } 77765c4fdfbSgilles 778fe95d8d0Seric smtp_reply(s, "250 %s: %08x Message accepted for delivery", 779fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS), 780763ff2e3Seric s->tx->msgid); 78165c4fdfbSgilles 7820a80c58aSeric TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) { 783a206140dSgiovanni log_info("%016"PRIx64" smtp event=message address=%s host=%s " 784a206140dSgiovanni "msgid=%08x from=<%s%s%s> to=<%s%s%s> size=%zu ndest=%zu proto=%s", 78565c4fdfbSgilles s->id, 786a206140dSgiovanni ss_to_text(&s->ss), s->hostname, 787763ff2e3Seric s->tx->msgid, 7880a80c58aSeric s->tx->evp.sender.user, 7890a80c58aSeric s->tx->evp.sender.user[0] == '\0' ? "" : "@", 7900a80c58aSeric s->tx->evp.sender.domain, 791ee3e6bebSeric rcpt->maddr.user, 792ee3e6bebSeric rcpt->maddr.user[0] == '\0' ? "" : "@", 793ee3e6bebSeric rcpt->maddr.domain, 7940a80c58aSeric s->tx->odatalen, 795ee3e6bebSeric rcpt->destcount, 79665c4fdfbSgilles s->flags & SF_EHLO ? "ESMTP" : "SMTP"); 797ee3e6bebSeric } 7980490754bSeric smtp_tx_free(s->tx); 79965c4fdfbSgilles s->mailcount++; 80065c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 80165c4fdfbSgilles return; 80265c4fdfbSgilles 803aa1d5973Seric case IMSG_SMTP_AUTHENTICATE: 80465c4fdfbSgilles m_msg(&m, imsg); 80565c4fdfbSgilles m_get_id(&m, &reqid); 80665c4fdfbSgilles m_get_int(&m, &success); 80765c4fdfbSgilles m_end(&m); 80865c4fdfbSgilles 80965c4fdfbSgilles s = tree_xpop(&wait_parent_auth, reqid); 81065c4fdfbSgilles strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE); 81165c4fdfbSgilles if (success == LKA_OK) { 8127a93bdaaSgilles log_info("%016"PRIx64" smtp " 813a9c4e50dSgiovanni "event=authentication user=%s address=%s " 814a9c4e50dSgiovanni "host=%s result=ok", 815a9c4e50dSgiovanni s->id, user, ss_to_text(&s->ss), s->hostname); 81665c4fdfbSgilles s->flags |= SF_AUTHENTICATED; 817fe95d8d0Seric smtp_reply(s, "235 %s: Authentication succeeded", 818fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 81965c4fdfbSgilles } 82065c4fdfbSgilles else if (success == LKA_PERMFAIL) { 8217a93bdaaSgilles log_info("%016"PRIx64" smtp " 822a9c4e50dSgiovanni "event=authentication user=%s address=%s " 823a9c4e50dSgiovanni "host=%s result=permfail", 824a9c4e50dSgiovanni s->id, user, ss_to_text(&s->ss), s->hostname); 825299c4efeSeric smtp_auth_failure_pause(s); 826299c4efeSeric return; 82765c4fdfbSgilles } 82865c4fdfbSgilles else if (success == LKA_TEMPFAIL) { 8297a93bdaaSgilles log_info("%016"PRIx64" smtp " 830a9c4e50dSgiovanni "event=authentication user=%s address=%s " 831a9c4e50dSgiovanni "host=%s result=tempfail", 832a9c4e50dSgiovanni s->id, user, ss_to_text(&s->ss), s->hostname); 833fe95d8d0Seric smtp_reply(s, "421 %s: Temporary failure", 834fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 83565c4fdfbSgilles } 83665c4fdfbSgilles else 83765c4fdfbSgilles fatalx("bad lka response"); 838299c4efeSeric 83965c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 84065c4fdfbSgilles return; 84165c4fdfbSgilles 842f80060acSgilles case IMSG_SMTP_TLS_INIT: 84365c4fdfbSgilles resp_ca_cert = imsg->data; 84465c4fdfbSgilles s = tree_xpop(&wait_ssl_init, resp_ca_cert->reqid); 84565c4fdfbSgilles 84665c4fdfbSgilles if (resp_ca_cert->status == CA_FAIL) { 847a206140dSgiovanni log_info("%016"PRIx64" smtp event=closed address=%s host=%s " 848a206140dSgiovanni "reason=ca-failure", 849a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname); 85065c4fdfbSgilles smtp_free(s, "CA failure"); 85165c4fdfbSgilles return; 85265c4fdfbSgilles } 85365c4fdfbSgilles 85465c4fdfbSgilles resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert, "smtp:ca_cert"); 85565c4fdfbSgilles resp_ca_cert->cert = xstrdup((char *)imsg->data + 85665c4fdfbSgilles sizeof *resp_ca_cert, "smtp:ca_cert"); 857f7aa1c30Sgilles ssl_ctx = dict_get(env->sc_ssl_dict, resp_ca_cert->name); 858c3e2e87dSgilles ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY); 8598d3f7f0dSeric io_set_read(s->io); 8608d3f7f0dSeric io_start_tls(s->io, ssl); 86165c4fdfbSgilles 862d914c05cSderaadt freezero(resp_ca_cert->cert, resp_ca_cert->cert_len); 86365c4fdfbSgilles free(resp_ca_cert); 86465c4fdfbSgilles return; 86565c4fdfbSgilles 866f80060acSgilles case IMSG_SMTP_TLS_VERIFY: 86765c4fdfbSgilles resp_ca_vrfy = imsg->data; 86865c4fdfbSgilles s = tree_xpop(&wait_ssl_verify, resp_ca_vrfy->reqid); 86965c4fdfbSgilles 87065c4fdfbSgilles if (resp_ca_vrfy->status == CA_OK) 87165c4fdfbSgilles s->flags |= SF_VERIFIED; 872cc81b7c6Seric else if (s->listener->flags & F_TLS_VERIFY) { 8737a93bdaaSgilles log_info("%016"PRIx64" smtp " 874a206140dSgiovanni "event=closed address=%s host=%s reason=cert-check-failed", 875a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname); 876cc81b7c6Seric smtp_free(s, "SSL certificate check failed"); 877cc81b7c6Seric return; 878cc81b7c6Seric } 8791ddc3febSeric smtp_tls_verified(s); 880f80a744fSeric io_resume(s->io, IO_IN); 8811ddc3febSeric return; 8821ddc3febSeric } 8831ddc3febSeric 8841ddc3febSeric log_warnx("smtp_session_imsg: unexpected %s imsg", 8851ddc3febSeric imsg_to_str(imsg->hdr.type)); 8861ddc3febSeric fatalx(NULL); 8871ddc3febSeric } 8881ddc3febSeric 8891ddc3febSeric static void 8901ddc3febSeric smtp_tls_verified(struct smtp_session *s) 8911ddc3febSeric { 8921ddc3febSeric X509 *x; 8934b9ec75fSeric 8948d3f7f0dSeric x = SSL_get_peer_certificate(io_ssl(s->io)); 8954b9ec75fSeric if (x) { 8964b9ec75fSeric log_info("%016"PRIx64" smtp " 8974b9ec75fSeric "event=client-cert-check address=%s host=%s result=\"%s\"", 8984b9ec75fSeric s->id, ss_to_text(&s->ss), s->hostname, 8994b9ec75fSeric (s->flags & SF_VERIFIED) ? "success" : "failure"); 9004b9ec75fSeric X509_free(x); 9014b9ec75fSeric } 9024b9ec75fSeric 9034b9ec75fSeric if (s->listener->flags & F_SMTPS) { 9044b9ec75fSeric stat_increment("smtp.smtps", 1); 9058d3f7f0dSeric io_set_write(s->io); 9064b9ec75fSeric smtp_send_banner(s); 9074b9ec75fSeric } 9084b9ec75fSeric else { 9094b9ec75fSeric stat_increment("smtp.tls", 1); 9104b9ec75fSeric smtp_enter_state(s, STATE_HELO); 9114b9ec75fSeric } 91265c4fdfbSgilles } 91365c4fdfbSgilles 91465c4fdfbSgilles static void 915b556a8d3Seric smtp_io(struct io *io, int evt, void *arg) 91665c4fdfbSgilles { 917b556a8d3Seric struct smtp_session *s = arg; 91865c4fdfbSgilles char *line; 919b251880aSgilles size_t len; 9205bfc3fefSeric int eom; 92165c4fdfbSgilles 92265c4fdfbSgilles log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt), 92365c4fdfbSgilles io_strio(io)); 92465c4fdfbSgilles 92565c4fdfbSgilles switch (evt) { 92665c4fdfbSgilles 92765c4fdfbSgilles case IO_TLSREADY: 928a206140dSgiovanni log_info("%016"PRIx64" smtp event=starttls address=%s host=%s ciphers=\"%s\"", 9298d3f7f0dSeric s->id, ss_to_text(&s->ss), s->hostname, ssl_to_text(io_ssl(s->io))); 93065c4fdfbSgilles 93165c4fdfbSgilles s->flags |= SF_SECURE; 932b937aab1Seric s->helo[0] = '\0'; 93365c4fdfbSgilles 93465c4fdfbSgilles if (smtp_verify_certificate(s)) { 935f80a744fSeric io_pause(s->io, IO_IN); 93665c4fdfbSgilles break; 93765c4fdfbSgilles } 93865c4fdfbSgilles 939cc81b7c6Seric if (s->listener->flags & F_TLS_VERIFY) { 9407a93bdaaSgilles log_info("%016"PRIx64" smtp " 941a206140dSgiovanni "event=closed address=%s host=%s reason=no-client-cert", 942a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname); 943cc81b7c6Seric smtp_free(s, "client did not present certificate"); 944cc81b7c6Seric return; 945cc81b7c6Seric } 946cc81b7c6Seric 9471ddc3febSeric smtp_tls_verified(s); 9481ddc3febSeric break; 94965c4fdfbSgilles 95065c4fdfbSgilles case IO_DATAIN: 95165c4fdfbSgilles nextline: 9528d3f7f0dSeric line = io_getline(s->io, &len); 9536162865dSeric if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) || 9546162865dSeric (line && len >= SMTP_LINE_MAX)) { 955299c4efeSeric s->flags |= SF_BADINPUT; 956fe95d8d0Seric smtp_reply(s, "500 %s: Line too long", 957fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); 95865c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 95965c4fdfbSgilles io_set_write(io); 96065c4fdfbSgilles return; 96165c4fdfbSgilles } 96265c4fdfbSgilles 96365c4fdfbSgilles /* No complete line received */ 9640e198904Seric if (line == NULL) 96565c4fdfbSgilles return; 96665c4fdfbSgilles 96765c4fdfbSgilles /* Message body */ 9685bfc3fefSeric eom = 0; 9695bfc3fefSeric if (s->state == STATE_BODY) { 970008f3f72Seric eom = smtp_tx_dataline(s->tx, line); 9715bfc3fefSeric if (eom == 0) 97265c4fdfbSgilles goto nextline; 97365c4fdfbSgilles } 97465c4fdfbSgilles 97565c4fdfbSgilles /* Pipelining not supported */ 9768d3f7f0dSeric if (io_datalen(s->io)) { 977299c4efeSeric s->flags |= SF_BADINPUT; 978fe95d8d0Seric smtp_reply(s, "500 %s %s: Pipelining not supported", 979fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 980fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 98165c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 98265c4fdfbSgilles io_set_write(io); 98365c4fdfbSgilles return; 98465c4fdfbSgilles } 98565c4fdfbSgilles 9865bfc3fefSeric if (eom) { 98755042ccbSeric smtp_message_end(s->tx); 9885bfc3fefSeric io_set_write(io); 98965c4fdfbSgilles return; 99065c4fdfbSgilles } 99165c4fdfbSgilles 99265c4fdfbSgilles /* Must be a command */ 9936162865dSeric if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) { 9946162865dSeric s->flags |= SF_BADINPUT; 9956162865dSeric smtp_reply(s, "500 %s: Command line too long", 9966162865dSeric esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); 9976162865dSeric smtp_enter_state(s, STATE_QUIT); 9986162865dSeric io_set_write(io); 9996162865dSeric return; 10006162865dSeric } 100165c4fdfbSgilles io_set_write(io); 100265c4fdfbSgilles smtp_command(s, line); 100365c4fdfbSgilles break; 100465c4fdfbSgilles 100565c4fdfbSgilles case IO_LOWAT: 100665c4fdfbSgilles if (s->state == STATE_QUIT) { 1007a206140dSgiovanni log_info("%016"PRIx64" smtp event=closed address=%s host=%s " 1008a206140dSgiovanni "reason=quit", 1009a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname); 101065c4fdfbSgilles smtp_free(s, "done"); 101165c4fdfbSgilles break; 101265c4fdfbSgilles } 101365c4fdfbSgilles 101465c4fdfbSgilles /* Wait for the client to start tls */ 101565c4fdfbSgilles if (s->state == STATE_TLS) { 10167627c963Seric smtp_tls_init(s); 101765c4fdfbSgilles break; 101865c4fdfbSgilles } 101965c4fdfbSgilles 102065c4fdfbSgilles io_set_read(io); 102165c4fdfbSgilles break; 102265c4fdfbSgilles 102365c4fdfbSgilles case IO_TIMEOUT: 1024a206140dSgiovanni log_info("%016"PRIx64" smtp event=closed address=%s host=%s " 1025a206140dSgiovanni "reason=timeout", 1026a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname); 102765c4fdfbSgilles smtp_free(s, "timeout"); 102865c4fdfbSgilles break; 102965c4fdfbSgilles 103065c4fdfbSgilles case IO_DISCONNECTED: 1031a206140dSgiovanni log_info("%016"PRIx64" smtp event=closed address=%s host=%s " 1032a206140dSgiovanni "reason=disconnect", 1033a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname); 103465c4fdfbSgilles smtp_free(s, "disconnected"); 103565c4fdfbSgilles break; 103665c4fdfbSgilles 103765c4fdfbSgilles case IO_ERROR: 1038a206140dSgiovanni log_info("%016"PRIx64" smtp event=closed address=%s host=%s " 1039a206140dSgiovanni "reason=\"io-error: %s\"", 1040219e2fd6Seric s->id, ss_to_text(&s->ss), s->hostname, io_error(io)); 104165c4fdfbSgilles smtp_free(s, "IO error"); 104265c4fdfbSgilles break; 104365c4fdfbSgilles 104465c4fdfbSgilles default: 104565c4fdfbSgilles fatalx("smtp_io()"); 104665c4fdfbSgilles } 104765c4fdfbSgilles } 104865c4fdfbSgilles 104965c4fdfbSgilles static void 105065c4fdfbSgilles smtp_command(struct smtp_session *s, char *line) 105165c4fdfbSgilles { 105265c4fdfbSgilles char *args, *eom, *method; 105365c4fdfbSgilles int cmd, i; 105465c4fdfbSgilles 105565c4fdfbSgilles log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line); 105665c4fdfbSgilles 105765c4fdfbSgilles /* 105865c4fdfbSgilles * These states are special. 105965c4fdfbSgilles */ 106065c4fdfbSgilles if (s->state == STATE_AUTH_INIT) { 106165c4fdfbSgilles smtp_rfc4954_auth_plain(s, line); 106265c4fdfbSgilles return; 106365c4fdfbSgilles } 106465c4fdfbSgilles if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) { 106565c4fdfbSgilles smtp_rfc4954_auth_login(s, line); 106665c4fdfbSgilles return; 106765c4fdfbSgilles } 106865c4fdfbSgilles 106965c4fdfbSgilles /* 107065c4fdfbSgilles * Unlike other commands, "mail from" and "rcpt to" contain a 107165c4fdfbSgilles * space in the command name. 107265c4fdfbSgilles */ 107365c4fdfbSgilles if (strncasecmp("mail from:", line, 10) == 0 || 107465c4fdfbSgilles strncasecmp("rcpt to:", line, 8) == 0) 107565c4fdfbSgilles args = strchr(line, ':'); 107665c4fdfbSgilles else 107765c4fdfbSgilles args = strchr(line, ' '); 107865c4fdfbSgilles 107965c4fdfbSgilles if (args) { 108065c4fdfbSgilles *args++ = '\0'; 1081fc3a8311Seric while (isspace((unsigned char)*args)) 108265c4fdfbSgilles args++; 108365c4fdfbSgilles } 108465c4fdfbSgilles 108565c4fdfbSgilles cmd = -1; 108665c4fdfbSgilles for (i = 0; commands[i].code != -1; i++) 108765c4fdfbSgilles if (!strcasecmp(line, commands[i].cmd)) { 108865c4fdfbSgilles cmd = commands[i].code; 108965c4fdfbSgilles break; 109065c4fdfbSgilles } 109165c4fdfbSgilles 109265c4fdfbSgilles switch (cmd) { 109365c4fdfbSgilles /* 109465c4fdfbSgilles * INIT 109565c4fdfbSgilles */ 109665c4fdfbSgilles case CMD_HELO: 109765c4fdfbSgilles case CMD_EHLO: 1098b937aab1Seric if (s->helo[0]) { 1099af0dba2aSsobrado smtp_reply(s, "503 %s %s: Already identified", 1100fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1101fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 110265c4fdfbSgilles break; 1103b8ff4170Sgilles } 1104b8ff4170Sgilles 11053ef9cbf7Sgilles if (args == NULL) { 1106fe95d8d0Seric smtp_reply(s, "501 %s %s: %s requires domain name", 1107fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1108fe95d8d0Seric esc_description(ESC_INVALID_COMMAND), 110965c4fdfbSgilles (cmd == CMD_HELO) ? "HELO" : "EHLO"); 1110fe95d8d0Seric 111165c4fdfbSgilles break; 111265c4fdfbSgilles } 111365c4fdfbSgilles 111465c4fdfbSgilles if (!valid_domainpart(args)) { 1115fe95d8d0Seric smtp_reply(s, "501 %s %s: Invalid domain name", 1116fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1117fe95d8d0Seric esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 111865c4fdfbSgilles break; 111965c4fdfbSgilles } 1120c51a7fb7Sgilles (void)strlcpy(s->helo, args, sizeof(s->helo)); 112184639b21Seric s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED; 112265c4fdfbSgilles if (cmd == CMD_EHLO) { 112365c4fdfbSgilles s->flags |= SF_EHLO; 112465c4fdfbSgilles s->flags |= SF_8BITMIME; 112565c4fdfbSgilles } 112665c4fdfbSgilles 1127c679e749Seric smtp_enter_state(s, STATE_HELO); 1128c679e749Seric smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you", 1129c679e749Seric (s->flags & SF_EHLO) ? '-' : ' ', 1130c679e749Seric s->smtpname, 1131c679e749Seric s->helo, 1132c679e749Seric ss_to_text(&s->ss)); 1133c679e749Seric 1134c679e749Seric if (s->flags & SF_EHLO) { 1135c679e749Seric smtp_reply(s, "250-8BITMIME"); 1136c679e749Seric smtp_reply(s, "250-ENHANCEDSTATUSCODES"); 1137c679e749Seric smtp_reply(s, "250-SIZE %zu", env->sc_maxsize); 1138c679e749Seric if (ADVERTISE_EXT_DSN(s)) 1139c679e749Seric smtp_reply(s, "250-DSN"); 1140c679e749Seric if (ADVERTISE_TLS(s)) 1141c679e749Seric smtp_reply(s, "250-STARTTLS"); 1142c679e749Seric if (ADVERTISE_AUTH(s)) 1143c679e749Seric smtp_reply(s, "250-AUTH PLAIN LOGIN"); 1144c679e749Seric smtp_reply(s, "250 HELP"); 1145c679e749Seric } 114665c4fdfbSgilles break; 114765c4fdfbSgilles /* 114865c4fdfbSgilles * SETUP 114965c4fdfbSgilles */ 115065c4fdfbSgilles case CMD_STARTTLS: 1151b937aab1Seric if (s->helo[0] == '\0' || s->tx) { 1152fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1153fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1154fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 115565c4fdfbSgilles break; 115665c4fdfbSgilles } 115765c4fdfbSgilles 115865c4fdfbSgilles if (!(s->listener->flags & F_STARTTLS)) { 1159fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not supported", 1160fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1161fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 116265c4fdfbSgilles break; 116365c4fdfbSgilles } 116465c4fdfbSgilles 116565c4fdfbSgilles if (s->flags & SF_SECURE) { 1166fe95d8d0Seric smtp_reply(s, "503 %s %s: Channel already secured", 1167fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1168fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 116965c4fdfbSgilles break; 117065c4fdfbSgilles } 117165c4fdfbSgilles if (args != NULL) { 1172fe95d8d0Seric smtp_reply(s, "501 %s %s: No parameters allowed", 1173fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1174fe95d8d0Seric esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 117565c4fdfbSgilles break; 117665c4fdfbSgilles } 1177fe95d8d0Seric smtp_reply(s, "220 %s: Ready to start TLS", 1178fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 117965c4fdfbSgilles smtp_enter_state(s, STATE_TLS); 118065c4fdfbSgilles break; 118165c4fdfbSgilles 118265c4fdfbSgilles case CMD_AUTH: 1183b937aab1Seric if (s->helo[0] == '\0' || s->tx) { 1184fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1185fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1186fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 118765c4fdfbSgilles break; 118865c4fdfbSgilles } 118965c4fdfbSgilles 119065c4fdfbSgilles if (s->flags & SF_AUTHENTICATED) { 1191fe95d8d0Seric smtp_reply(s, "503 %s %s: Already authenticated", 1192fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1193fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 119465c4fdfbSgilles break; 119565c4fdfbSgilles } 119665c4fdfbSgilles 119765c4fdfbSgilles if (!ADVERTISE_AUTH(s)) { 1198fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not supported", 1199fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1200fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 120165c4fdfbSgilles break; 120265c4fdfbSgilles } 120365c4fdfbSgilles 120465c4fdfbSgilles if (args == NULL) { 1205fe95d8d0Seric smtp_reply(s, "501 %s %s: No parameters given", 1206fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1207fe95d8d0Seric esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 120865c4fdfbSgilles break; 12093ef9cbf7Sgilles } 12103ef9cbf7Sgilles 12113ef9cbf7Sgilles method = args; 12123ef9cbf7Sgilles eom = strchr(args, ' '); 12133ef9cbf7Sgilles if (eom == NULL) 12143ef9cbf7Sgilles eom = strchr(args, '\t'); 12153ef9cbf7Sgilles if (eom != NULL) 12163ef9cbf7Sgilles *eom++ = '\0'; 121785e1f4f6Sgilles if (strcasecmp(method, "PLAIN") == 0) 121865c4fdfbSgilles smtp_rfc4954_auth_plain(s, eom); 121985e1f4f6Sgilles else if (strcasecmp(method, "LOGIN") == 0) 122065c4fdfbSgilles smtp_rfc4954_auth_login(s, eom); 1221e8fa9864Sjacekm else 1222fe95d8d0Seric smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported", 1223fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED), 1224fe95d8d0Seric esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED), 122565c4fdfbSgilles method); 122665c4fdfbSgilles break; 122785e1f4f6Sgilles 122865c4fdfbSgilles case CMD_MAIL_FROM: 1229b937aab1Seric if (s->helo[0] == '\0' || s->tx) { 1230fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1231fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1232fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 1233fe95d8d0Seric 123465c4fdfbSgilles break; 123565c4fdfbSgilles } 123665c4fdfbSgilles 123765c4fdfbSgilles if (s->listener->flags & F_STARTTLS_REQUIRE && 123865c4fdfbSgilles !(s->flags & SF_SECURE)) { 123965c4fdfbSgilles smtp_reply(s, 1240fe95d8d0Seric "530 %s %s: Must issue a STARTTLS command first", 1241fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1242fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 124365c4fdfbSgilles break; 124465c4fdfbSgilles } 124565c4fdfbSgilles 124665c4fdfbSgilles if (s->listener->flags & F_AUTH_REQUIRE && 124765c4fdfbSgilles !(s->flags & SF_AUTHENTICATED)) { 124865c4fdfbSgilles smtp_reply(s, 1249fe95d8d0Seric "530 %s %s: Must issue an AUTH command first", 1250fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1251fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 125265c4fdfbSgilles break; 125365c4fdfbSgilles } 125465c4fdfbSgilles 125552f1cdb1Sjung if (s->mailcount >= env->sc_session_max_mails) { 1256fe95d8d0Seric /* we can pretend we had too many recipients */ 1257fe95d8d0Seric smtp_reply(s, "452 %s %s: Too many messages sent", 1258fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), 1259fe95d8d0Seric esc_description(ESC_TOO_MANY_RECIPIENTS)); 126065c4fdfbSgilles break; 126165c4fdfbSgilles } 126265c4fdfbSgilles 12630490754bSeric if (!smtp_tx(s)) { 12640490754bSeric smtp_reply(s, "421 %s: Temporary Error", 12650490754bSeric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 12660490754bSeric smtp_enter_state(s, STATE_QUIT); 12670490754bSeric break; 12680490754bSeric } 126965c4fdfbSgilles 127072cde3cbSeric smtp_tx_mail_from(s->tx, args); 127165c4fdfbSgilles break; 127265c4fdfbSgilles /* 127365c4fdfbSgilles * TRANSACTION 127465c4fdfbSgilles */ 127565c4fdfbSgilles case CMD_RCPT_TO: 1276b937aab1Seric if (s->tx == NULL) { 1277fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1278fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1279fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 128065c4fdfbSgilles break; 128165c4fdfbSgilles } 128265c4fdfbSgilles 128372cde3cbSeric smtp_tx_rcpt_to(s->tx, args); 128465c4fdfbSgilles break; 128565c4fdfbSgilles 128665c4fdfbSgilles case CMD_RSET: 1287b937aab1Seric if (s->helo[0] == '\0') { 1288fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1289fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1290fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 129165c4fdfbSgilles break; 129265c4fdfbSgilles } 129365c4fdfbSgilles 12940490754bSeric if (s->tx) { 1295763ff2e3Seric if (s->tx->msgid) 1296008f3f72Seric smtp_tx_rollback(s->tx); 12970490754bSeric smtp_tx_free(s->tx); 12980490754bSeric } 129965c4fdfbSgilles 1300fe95d8d0Seric smtp_reply(s, "250 %s: Reset state", 1301fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 130265c4fdfbSgilles break; 130365c4fdfbSgilles 130465c4fdfbSgilles case CMD_DATA: 1305b937aab1Seric if (s->tx == NULL) { 1306fe95d8d0Seric smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1307fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1308fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 130965c4fdfbSgilles break; 131065c4fdfbSgilles } 13110a80c58aSeric if (s->tx->rcptcount == 0) { 1312fe95d8d0Seric smtp_reply(s, "503 %s %s: No recipient specified", 1313fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1314fe95d8d0Seric esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 131565c4fdfbSgilles break; 131665c4fdfbSgilles } 131765c4fdfbSgilles 1318008f3f72Seric smtp_tx_open_message(s->tx); 131965c4fdfbSgilles break; 132065c4fdfbSgilles /* 132165c4fdfbSgilles * ANY 132265c4fdfbSgilles */ 132365c4fdfbSgilles case CMD_QUIT: 1324fe95d8d0Seric smtp_reply(s, "221 %s: Bye", 1325fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 132665c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 132765c4fdfbSgilles break; 132865c4fdfbSgilles 132965c4fdfbSgilles case CMD_NOOP: 1330fe95d8d0Seric smtp_reply(s, "250 %s: Ok", 1331fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 133265c4fdfbSgilles break; 133365c4fdfbSgilles 133465c4fdfbSgilles case CMD_HELP: 133598cf77ddSjung smtp_reply(s, "214- This is " SMTPD_NAME); 133665c4fdfbSgilles smtp_reply(s, "214- To report bugs in the implementation, " 133765c4fdfbSgilles "please contact bugs@openbsd.org"); 133865c4fdfbSgilles smtp_reply(s, "214- with full details"); 1339fe95d8d0Seric smtp_reply(s, "214 %s: End of HELP info", 1340fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 1341fe95d8d0Seric break; 1342fe95d8d0Seric 1343fe95d8d0Seric case CMD_WIZ: 1344fe95d8d0Seric smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)", 1345fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1346fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 134765c4fdfbSgilles break; 134865c4fdfbSgilles 134965c4fdfbSgilles default: 1350fe95d8d0Seric smtp_reply(s, "500 %s %s: Command unrecognized", 1351fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1352fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 135365c4fdfbSgilles break; 135465c4fdfbSgilles } 135585e1f4f6Sgilles } 135685e1f4f6Sgilles 1357be925435Sgilles static void 135865c4fdfbSgilles smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg) 135985e1f4f6Sgilles { 1360e8fa9864Sjacekm char buf[1024], *user, *pass; 1361e8fa9864Sjacekm int len; 1362e8fa9864Sjacekm 136365c4fdfbSgilles switch (s->state) { 136465c4fdfbSgilles case STATE_HELO: 136585e1f4f6Sgilles if (arg == NULL) { 136665c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_INIT); 136765c4fdfbSgilles smtp_reply(s, "334 "); 1368e8fa9864Sjacekm return; 136985e1f4f6Sgilles } 137065c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_INIT); 1371e8fa9864Sjacekm /* FALLTHROUGH */ 137285e1f4f6Sgilles 137365c4fdfbSgilles case STATE_AUTH_INIT: 1374e8fa9864Sjacekm /* String is not NUL terminated, leave room. */ 1375254aed36Seric if ((len = base64_decode(arg, (unsigned char *)buf, 13767bdbba2fSeric sizeof(buf) - 1)) == -1) 1377e8fa9864Sjacekm goto abort; 1378e8fa9864Sjacekm /* buf is a byte string, NUL terminate. */ 1379e8fa9864Sjacekm buf[len] = '\0'; 1380e8fa9864Sjacekm 1381e8fa9864Sjacekm /* 1382e8fa9864Sjacekm * Skip "foo" in "foo\0user\0pass", if present. 1383e8fa9864Sjacekm */ 1384e8fa9864Sjacekm user = memchr(buf, '\0', len); 1385e8fa9864Sjacekm if (user == NULL || user >= buf + len - 2) 1386e8fa9864Sjacekm goto abort; 1387e8fa9864Sjacekm user++; /* skip NUL */ 138865c4fdfbSgilles if (strlcpy(s->username, user, sizeof(s->username)) 138965c4fdfbSgilles >= sizeof(s->username)) 1390e8fa9864Sjacekm goto abort; 1391e8fa9864Sjacekm 1392e8fa9864Sjacekm pass = memchr(user, '\0', len - (user - buf)); 1393e8fa9864Sjacekm if (pass == NULL || pass >= buf + len - 2) 1394e8fa9864Sjacekm goto abort; 1395e8fa9864Sjacekm pass++; /* skip NUL */ 13963ef9cbf7Sgilles 1397aa1d5973Seric m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); 139865c4fdfbSgilles m_add_id(p_lka, s->id); 139965c4fdfbSgilles m_add_string(p_lka, s->listener->authtable); 140065c4fdfbSgilles m_add_string(p_lka, user); 140165c4fdfbSgilles m_add_string(p_lka, pass); 140265c4fdfbSgilles m_close(p_lka); 140365c4fdfbSgilles tree_xset(&wait_parent_auth, s->id, s); 1404e8fa9864Sjacekm return; 1405e8fa9864Sjacekm 1406e8fa9864Sjacekm default: 140765c4fdfbSgilles fatal("smtp_rfc4954_auth_plain: unknown state"); 14083ef9cbf7Sgilles } 14093ef9cbf7Sgilles 1410e8fa9864Sjacekm abort: 1411fe95d8d0Seric smtp_reply(s, "501 %s %s: Syntax error", 1412fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), 1413fe95d8d0Seric esc_description(ESC_SYNTAX_ERROR)); 141465c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 1415e8fa9864Sjacekm } 1416e8fa9864Sjacekm 1417be925435Sgilles static void 141865c4fdfbSgilles smtp_rfc4954_auth_login(struct smtp_session *s, char *arg) 141985e1f4f6Sgilles { 1420953aae25Sderaadt char buf[LINE_MAX]; 142185e1f4f6Sgilles 142265c4fdfbSgilles switch (s->state) { 142365c4fdfbSgilles case STATE_HELO: 142465c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_USERNAME); 1425da29cb6eSgilles if (arg != NULL && *arg != '\0') { 1426da29cb6eSgilles smtp_rfc4954_auth_login(s, arg); 1427da29cb6eSgilles return; 1428da29cb6eSgilles } 142965c4fdfbSgilles smtp_reply(s, "334 VXNlcm5hbWU6"); 1430e8fa9864Sjacekm return; 143185e1f4f6Sgilles 143265c4fdfbSgilles case STATE_AUTH_USERNAME: 1433c1392a69Seric memset(s->username, 0, sizeof(s->username)); 1434254aed36Seric if (base64_decode(arg, (unsigned char *)s->username, 143565c4fdfbSgilles sizeof(s->username) - 1) == -1) 1436e8fa9864Sjacekm goto abort; 143785e1f4f6Sgilles 143865c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_PASSWORD); 143965c4fdfbSgilles smtp_reply(s, "334 UGFzc3dvcmQ6"); 1440e8fa9864Sjacekm return; 144185e1f4f6Sgilles 144265c4fdfbSgilles case STATE_AUTH_PASSWORD: 1443c1392a69Seric memset(buf, 0, sizeof(buf)); 1444254aed36Seric if (base64_decode(arg, (unsigned char *)buf, 1445254aed36Seric sizeof(buf)-1) == -1) 1446e8fa9864Sjacekm goto abort; 144785e1f4f6Sgilles 1448aa1d5973Seric m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); 144965c4fdfbSgilles m_add_id(p_lka, s->id); 145065c4fdfbSgilles m_add_string(p_lka, s->listener->authtable); 145165c4fdfbSgilles m_add_string(p_lka, s->username); 145265c4fdfbSgilles m_add_string(p_lka, buf); 145365c4fdfbSgilles m_close(p_lka); 145465c4fdfbSgilles tree_xset(&wait_parent_auth, s->id, s); 1455e8fa9864Sjacekm return; 1456e8fa9864Sjacekm 145785e1f4f6Sgilles default: 145865c4fdfbSgilles fatal("smtp_rfc4954_auth_login: unknown state"); 145985e1f4f6Sgilles } 146085e1f4f6Sgilles 1461e8fa9864Sjacekm abort: 1462fe95d8d0Seric smtp_reply(s, "501 %s %s: Syntax error", 1463fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), 1464fe95d8d0Seric esc_description(ESC_SYNTAX_ERROR)); 146565c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 146685e1f4f6Sgilles } 146785e1f4f6Sgilles 1468fe95d8d0Seric static int 1469fe95d8d0Seric smtp_parse_rcpt_args(struct smtp_session *s, char *args) 1470fe95d8d0Seric { 1471fe95d8d0Seric char *b, *p; 1472fe95d8d0Seric 1473fe95d8d0Seric while ((b = strsep(&args, " "))) { 1474fe95d8d0Seric if (*b == '\0') 1475fe95d8d0Seric continue; 1476fe95d8d0Seric 1477ad286364Seric if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "NOTIFY=", 7) == 0) { 1478fe95d8d0Seric b += 7; 1479fe95d8d0Seric while ((p = strsep(&b, ","))) { 1480fb24b76bSeric if (strcasecmp(p, "SUCCESS") == 0) 1481fb24b76bSeric s->tx->evp.dsn_notify |= DSN_SUCCESS; 1482fb24b76bSeric else if (strcasecmp(p, "FAILURE") == 0) 1483fb24b76bSeric s->tx->evp.dsn_notify |= DSN_FAILURE; 1484fb24b76bSeric else if (strcasecmp(p, "DELAY") == 0) 1485fb24b76bSeric s->tx->evp.dsn_notify |= DSN_DELAY; 1486fb24b76bSeric else if (strcasecmp(p, "NEVER") == 0) 1487fb24b76bSeric s->tx->evp.dsn_notify |= DSN_NEVER; 1488fe95d8d0Seric } 1489fb24b76bSeric 14900a80c58aSeric if (s->tx->evp.dsn_notify & DSN_NEVER && 14910a80c58aSeric s->tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE | 1492fe95d8d0Seric DSN_DELAY)) { 1493fe95d8d0Seric smtp_reply(s, 1494fe95d8d0Seric "553 NOTIFY option NEVER cannot be \ 1495fe95d8d0Seric combined with other options"); 1496fe95d8d0Seric return (-1); 1497fe95d8d0Seric } 1498ad286364Seric } else if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "ORCPT=", 6) == 0) { 1499fe95d8d0Seric b += 6; 15000a80c58aSeric if (!text_to_mailaddr(&s->tx->evp.dsn_orcpt, b)) { 1501fe95d8d0Seric smtp_reply(s, "553 ORCPT address syntax error"); 1502fe95d8d0Seric return (-1); 1503fe95d8d0Seric } 1504fe95d8d0Seric } else { 1505fe95d8d0Seric smtp_reply(s, "503 Unsupported option %s", b); 1506fe95d8d0Seric return (-1); 1507fe95d8d0Seric } 1508fe95d8d0Seric } 1509fe95d8d0Seric 1510fe95d8d0Seric return (0); 1511fe95d8d0Seric } 1512fe95d8d0Seric 1513be925435Sgilles static int 151465c4fdfbSgilles smtp_parse_mail_args(struct smtp_session *s, char *args) 15153ef9cbf7Sgilles { 151665c4fdfbSgilles char *b; 15173ef9cbf7Sgilles 151865c4fdfbSgilles while ((b = strsep(&args, " "))) { 151965c4fdfbSgilles if (*b == '\0') 15205739f5ceSjacekm continue; 15213ef9cbf7Sgilles 152265c4fdfbSgilles if (strncasecmp(b, "AUTH=", 5) == 0) 152365c4fdfbSgilles log_debug("debug: smtp: AUTH in MAIL FROM command"); 152465c4fdfbSgilles else if (strncasecmp(b, "SIZE=", 5) == 0) 152565c4fdfbSgilles log_debug("debug: smtp: SIZE in MAIL FROM command"); 1526fe95d8d0Seric else if (strcasecmp(b, "BODY=7BIT") == 0) 152765c4fdfbSgilles /* XXX only for this transaction */ 152865c4fdfbSgilles s->flags &= ~SF_8BITMIME; 152965c4fdfbSgilles else if (strcasecmp(b, "BODY=8BITMIME") == 0) 153065c4fdfbSgilles ; 1531ad286364Seric else if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "RET=", 4) == 0) { 1532fe95d8d0Seric b += 4; 1533fe95d8d0Seric if (strcasecmp(b, "HDRS") == 0) 15340a80c58aSeric s->tx->evp.dsn_ret = DSN_RETHDRS; 1535fe95d8d0Seric else if (strcasecmp(b, "FULL") == 0) 15360a80c58aSeric s->tx->evp.dsn_ret = DSN_RETFULL; 1537ad286364Seric } else if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "ENVID=", 6) == 0) { 1538fe95d8d0Seric b += 6; 15390a80c58aSeric if (strlcpy(s->tx->evp.dsn_envid, b, sizeof(s->tx->evp.dsn_envid)) 15400a80c58aSeric >= sizeof(s->tx->evp.dsn_envid)) { 1541351a2600Sgilles smtp_reply(s, "503 %s %s: option too large, truncated: %s", 1542351a2600Sgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1543351a2600Sgilles esc_description(ESC_INVALID_COMMAND_ARGUMENTS), b); 1544351a2600Sgilles return (-1); 1545351a2600Sgilles } 1546fe95d8d0Seric } else { 1547fe95d8d0Seric smtp_reply(s, "503 %s %s: Unsupported option %s", 1548fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1549fe95d8d0Seric esc_description(ESC_INVALID_COMMAND_ARGUMENTS), b); 15503ef9cbf7Sgilles return (-1); 155165c4fdfbSgilles } 155265c4fdfbSgilles } 15533ef9cbf7Sgilles 15543ef9cbf7Sgilles return (0); 15553ef9cbf7Sgilles } 15563ef9cbf7Sgilles 15571c3ac238Seric static int 15581c3ac238Seric smtp_lookup_servername(struct smtp_session *s) 15591c3ac238Seric { 15601c3ac238Seric struct sockaddr *sa; 15611c3ac238Seric socklen_t sa_len; 15621c3ac238Seric struct sockaddr_storage ss; 15631c3ac238Seric 15641c3ac238Seric if (s->listener->hostnametable[0]) { 15651c3ac238Seric sa_len = sizeof(ss); 15661c3ac238Seric sa = (struct sockaddr *)&ss; 15678d3f7f0dSeric if (getsockname(io_fileno(s->io), sa, &sa_len) == -1) { 15681c3ac238Seric log_warn("warn: getsockname()"); 15691c3ac238Seric } 15701c3ac238Seric else { 1571aa1d5973Seric m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1); 15721c3ac238Seric m_add_id(p_lka, s->id); 15731c3ac238Seric m_add_string(p_lka, s->listener->hostnametable); 15741c3ac238Seric m_add_sockaddr(p_lka, sa); 15751c3ac238Seric m_close(p_lka); 15761c3ac238Seric tree_xset(&wait_lka_helo, s->id, s); 15771c3ac238Seric return 0; 15781c3ac238Seric } 15791c3ac238Seric } 15801c3ac238Seric return 1; 15811c3ac238Seric } 15821c3ac238Seric 158365c4fdfbSgilles static void 158465c4fdfbSgilles smtp_connected(struct smtp_session *s) 15853ef9cbf7Sgilles { 158665c4fdfbSgilles struct sockaddr_storage ss; 158765c4fdfbSgilles socklen_t sl; 15883ef9cbf7Sgilles 158965c4fdfbSgilles smtp_enter_state(s, STATE_CONNECTED); 15903ef9cbf7Sgilles 15917a93bdaaSgilles log_info("%016"PRIx64" smtp event=connected address=%s host=%s", 15927a93bdaaSgilles s->id, ss_to_text(&s->ss), s->hostname); 159365c4fdfbSgilles 159465c4fdfbSgilles sl = sizeof(ss); 15958d3f7f0dSeric if (getsockname(io_fileno(s->io), (struct sockaddr*)&ss, &sl) == -1) { 1596316175faSeric smtp_free(s, strerror(errno)); 1597316175faSeric return; 1598316175faSeric } 159965c4fdfbSgilles 1600c679e749Seric if (s->listener->flags & F_SMTPS) { 16017627c963Seric smtp_tls_init(s); 1602c679e749Seric return; 1603c679e749Seric } 1604c679e749Seric 1605c679e749Seric smtp_send_banner(s); 16063ef9cbf7Sgilles } 16073ef9cbf7Sgilles 1608cc81b7c6Seric static void 1609cc81b7c6Seric smtp_send_banner(struct smtp_session *s) 1610cc81b7c6Seric { 161148b77194Seric smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME); 1612cc81b7c6Seric } 1613cc81b7c6Seric 16143ef9cbf7Sgilles void 161565c4fdfbSgilles smtp_enter_state(struct smtp_session *s, int newstate) 161665c4fdfbSgilles { 161765c4fdfbSgilles log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s, 161865c4fdfbSgilles smtp_strstate(s->state), 161965c4fdfbSgilles smtp_strstate(newstate)); 162065c4fdfbSgilles 162165c4fdfbSgilles s->state = newstate; 162265c4fdfbSgilles } 162365c4fdfbSgilles 162465c4fdfbSgilles static void 162565c4fdfbSgilles smtp_reply(struct smtp_session *s, char *fmt, ...) 1626a6ca2cc5Sjacekm { 1627a6ca2cc5Sjacekm va_list ap; 162865c4fdfbSgilles int n; 1629953aae25Sderaadt char buf[LINE_MAX], tmp[LINE_MAX]; 1630a6ca2cc5Sjacekm 1631a6ca2cc5Sjacekm va_start(ap, fmt); 1632b04e6f27Seric n = vsnprintf(buf, sizeof buf, fmt, ap); 1633a6ca2cc5Sjacekm va_end(ap); 1634953aae25Sderaadt if (n == -1 || n >= LINE_MAX) 163565c4fdfbSgilles fatalx("smtp_reply: line too long"); 1636b04e6f27Seric if (n < 4) 163765c4fdfbSgilles fatalx("smtp_reply: response too short"); 1638a6ca2cc5Sjacekm 1639b04e6f27Seric log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf); 1640b04e6f27Seric 16418d3f7f0dSeric io_xprintf(s->io, "%s\r\n", buf); 16425711eb67Sjacekm 1643b04e6f27Seric switch (buf[0]) { 16441ef63b12Sjacekm case '5': 16451ef63b12Sjacekm case '4': 1646299c4efeSeric if (s->flags & SF_BADINPUT) { 16477a93bdaaSgilles log_info("%016"PRIx64" smtp " 1648a206140dSgiovanni "event=bad-input address=%s host=%s result=\"%.*s\"", 1649a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname, n, buf); 1650299c4efeSeric } 16516f197337Sgilles else if (s->state == STATE_AUTH_INIT) { 1652a206140dSgiovanni log_info("%016"PRIx64" smtp " 1653a206140dSgiovanni "event=failed-command address=%s host=%s " 1654a206140dSgiovanni "command=\"AUTH PLAIN (...)\" result=\"%.*s\"", 1655a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname, n, buf); 16566f197337Sgilles } 16576f197337Sgilles else if (s->state == STATE_AUTH_USERNAME) { 1658a206140dSgiovanni log_info("%016"PRIx64" smtp " 1659a206140dSgiovanni "event=failed-command address=%s host=%s " 1660a206140dSgiovanni "command=\"AUTH LOGIN (username)\" result=\"%.*s\"", 1661a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname, n, buf); 16626f197337Sgilles } 16636f197337Sgilles else if (s->state == STATE_AUTH_PASSWORD) { 1664a206140dSgiovanni log_info("%016"PRIx64" smtp " 1665a206140dSgiovanni "event=failed-command address=%s host=%s " 1666a206140dSgiovanni "command=\"AUTH LOGIN (password)\" result=\"%.*s\"", 1667a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname, n, buf); 1668299c4efeSeric } 1669299c4efeSeric else { 167082614934Seric strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE); 16717a93bdaaSgilles log_info("%016"PRIx64" smtp " 1672a206140dSgiovanni "event=failed-command address=%s host=%s command=\"%s\" " 1673a206140dSgiovanni "result=\"%.*s\"", 1674a206140dSgiovanni s->id, ss_to_text(&s->ss), s->hostname, tmp, n, buf); 1675299c4efeSeric } 16761ef63b12Sjacekm break; 16771ef63b12Sjacekm } 16785711eb67Sjacekm } 16795711eb67Sjacekm 1680be925435Sgilles static void 168165c4fdfbSgilles smtp_free(struct smtp_session *s, const char * reason) 16828107cc2fSjacekm { 168365c4fdfbSgilles log_debug("debug: smtp: %p: deleting session: %s", s, reason); 1684b04e6f27Seric 16850490754bSeric if (s->tx) { 16868d3f7f0dSeric if (s->tx->msgid) 1687008f3f72Seric smtp_tx_rollback(s->tx); 16880490754bSeric smtp_tx_free(s->tx); 16890490754bSeric } 1690dcdb4581Seric 169165c4fdfbSgilles if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS) 169265c4fdfbSgilles stat_decrement("smtp.smtps", 1); 169365c4fdfbSgilles if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS) 169465c4fdfbSgilles stat_decrement("smtp.tls", 1); 169565c4fdfbSgilles 16968d3f7f0dSeric io_free(s->io); 169765c4fdfbSgilles free(s); 169865c4fdfbSgilles 169965c4fdfbSgilles smtp_collect(); 170065c4fdfbSgilles } 170165c4fdfbSgilles 170265c4fdfbSgilles static int 1703cc81b7c6Seric smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args, 1704cc81b7c6Seric const char *domain) 170565c4fdfbSgilles { 170665c4fdfbSgilles char *p, *e; 170765c4fdfbSgilles 170865c4fdfbSgilles if (line == NULL) 170965c4fdfbSgilles return (0); 171065c4fdfbSgilles 171165c4fdfbSgilles if (*line != '<') 171265c4fdfbSgilles return (0); 171365c4fdfbSgilles 171465c4fdfbSgilles e = strchr(line, '>'); 171565c4fdfbSgilles if (e == NULL) 171665c4fdfbSgilles return (0); 171765c4fdfbSgilles *e++ = '\0'; 171865c4fdfbSgilles while (*e == ' ') 171965c4fdfbSgilles e++; 172065c4fdfbSgilles *args = e; 172165c4fdfbSgilles 172265c4fdfbSgilles if (!text_to_mailaddr(maddr, line + 1)) 172365c4fdfbSgilles return (0); 172465c4fdfbSgilles 172565c4fdfbSgilles p = strchr(maddr->user, ':'); 172665c4fdfbSgilles if (p != NULL) { 172765c4fdfbSgilles p++; 172865c4fdfbSgilles memmove(maddr->user, p, strlen(p) + 1); 172965c4fdfbSgilles } 173065c4fdfbSgilles 173165c4fdfbSgilles if (!valid_localpart(maddr->user) || 1732299c4efeSeric !valid_domainpart(maddr->domain)) { 1733f3fad8caSgilles /* accept empty return-path in MAIL FROM, required for bounces */ 1734f3fad8caSgilles if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0') 173565c4fdfbSgilles return (1); 1736cc81b7c6Seric 1737f3fad8caSgilles /* no user-part, reject */ 1738f3fad8caSgilles if (maddr->user[0] == '\0') 1739f3fad8caSgilles return (0); 1740f3fad8caSgilles 1741f3fad8caSgilles /* no domain, local user */ 1742f3fad8caSgilles if (maddr->domain[0] == '\0') { 1743cc81b7c6Seric (void)strlcpy(maddr->domain, domain, 1744cc81b7c6Seric sizeof(maddr->domain)); 1745cc81b7c6Seric return (1); 1746cc81b7c6Seric } 174765c4fdfbSgilles return (0); 174865c4fdfbSgilles } 174965c4fdfbSgilles 175065c4fdfbSgilles return (1); 175165c4fdfbSgilles } 175265c4fdfbSgilles 17537627c963Seric static void 17547627c963Seric smtp_tls_init(struct smtp_session *s) 17557627c963Seric { 17567627c963Seric struct ca_cert_req_msg req_ca_cert; 17577627c963Seric 17587627c963Seric req_ca_cert.reqid = s->id; 17597627c963Seric if (s->listener->pki_name[0]) { 17607627c963Seric (void)strlcpy(req_ca_cert.name, s->listener->pki_name, 17617627c963Seric sizeof req_ca_cert.name); 17627627c963Seric req_ca_cert.fallback = 0; 17637627c963Seric } 17647627c963Seric else { 17657627c963Seric (void)strlcpy(req_ca_cert.name, s->smtpname, 17667627c963Seric sizeof req_ca_cert.name); 17677627c963Seric req_ca_cert.fallback = 1; 17687627c963Seric } 17697627c963Seric m_compose(p_lka, IMSG_SMTP_TLS_INIT, 0, 0, -1, 17707627c963Seric &req_ca_cert, sizeof(req_ca_cert)); 17717627c963Seric tree_xset(&wait_ssl_init, s->id, s); 17727627c963Seric } 17737627c963Seric 177465c4fdfbSgilles static int 177565c4fdfbSgilles smtp_verify_certificate(struct smtp_session *s) 177665c4fdfbSgilles { 1777290f027cSgilles #define MAX_CERTS 16 1778290f027cSgilles #define MAX_CERT_LEN (MAX_IMSGSIZE - (IMSG_HEADER_SIZE + sizeof(req_ca_vrfy))) 177965c4fdfbSgilles struct ca_vrfy_req_msg req_ca_vrfy; 178065c4fdfbSgilles struct iovec iov[2]; 178165c4fdfbSgilles X509 *x; 178265c4fdfbSgilles STACK_OF(X509) *xchain; 1783f7aa1c30Sgilles const char *name; 1784290f027cSgilles unsigned char *cert_der[MAX_CERTS]; 1785290f027cSgilles int cert_len[MAX_CERTS]; 1786290f027cSgilles int i, cert_count, res; 1787290f027cSgilles 1788290f027cSgilles res = 0; 1789290f027cSgilles memset(cert_der, 0, sizeof(cert_der)); 1790290f027cSgilles memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy); 1791290f027cSgilles 1792f7aa1c30Sgilles /* Send the client certificate */ 1793f7aa1c30Sgilles if (s->listener->ca_name[0]) { 1794f7aa1c30Sgilles name = s->listener->ca_name; 1795f7aa1c30Sgilles req_ca_vrfy.fallback = 0; 1796f7aa1c30Sgilles } 1797f7aa1c30Sgilles else { 1798f7aa1c30Sgilles name = s->smtpname; 1799f7aa1c30Sgilles req_ca_vrfy.fallback = 1; 1800f7aa1c30Sgilles } 1801f7aa1c30Sgilles 1802f7aa1c30Sgilles if (strlcpy(req_ca_vrfy.name, name, sizeof req_ca_vrfy.name) 1803bc2251d2Sgilles >= sizeof req_ca_vrfy.name) 1804290f027cSgilles return 0; 180565c4fdfbSgilles 18068d3f7f0dSeric x = SSL_get_peer_certificate(io_ssl(s->io)); 180765c4fdfbSgilles if (x == NULL) 180865c4fdfbSgilles return 0; 18098d3f7f0dSeric xchain = SSL_get_peer_cert_chain(io_ssl(s->io)); 181065c4fdfbSgilles 181165c4fdfbSgilles /* 181265c4fdfbSgilles * Client provided a certificate and possibly a certificate chain. 181365c4fdfbSgilles * SMTP can't verify because it does not have the information that 181465c4fdfbSgilles * it needs, instead it will pass the certificate and chain to the 181565c4fdfbSgilles * lookup process and wait for a reply. 181665c4fdfbSgilles * 181765c4fdfbSgilles */ 181865c4fdfbSgilles 1819290f027cSgilles cert_len[0] = i2d_X509(x, &cert_der[0]); 1820290f027cSgilles X509_free(x); 1821290f027cSgilles 1822290f027cSgilles if (cert_len[0] < 0) { 1823290f027cSgilles log_warnx("warn: failed to encode certificate"); 1824290f027cSgilles goto end; 1825290f027cSgilles } 1826290f027cSgilles log_debug("debug: certificate 0: len=%d", cert_len[0]); 1827290f027cSgilles if (cert_len[0] > (int)MAX_CERT_LEN) { 1828290f027cSgilles log_warnx("warn: certificate too long"); 1829290f027cSgilles goto end; 1830290f027cSgilles } 1831290f027cSgilles 1832290f027cSgilles if (xchain) { 1833290f027cSgilles cert_count = sk_X509_num(xchain); 1834290f027cSgilles log_debug("debug: certificate chain len: %d", cert_count); 1835290f027cSgilles if (cert_count >= MAX_CERTS) { 1836290f027cSgilles log_warnx("warn: certificate chain too long"); 1837290f027cSgilles goto end; 1838290f027cSgilles } 1839290f027cSgilles } 1840290f027cSgilles else 1841290f027cSgilles cert_count = 0; 1842290f027cSgilles 1843290f027cSgilles for (i = 0; i < cert_count; ++i) { 1844290f027cSgilles x = sk_X509_value(xchain, i); 1845290f027cSgilles cert_len[i+1] = i2d_X509(x, &cert_der[i+1]); 1846290f027cSgilles if (cert_len[i+1] < 0) { 1847290f027cSgilles log_warnx("warn: failed to encode certificate"); 1848290f027cSgilles goto end; 1849290f027cSgilles } 1850290f027cSgilles log_debug("debug: certificate %i: len=%d", i+1, cert_len[i+1]); 1851290f027cSgilles if (cert_len[i+1] > (int)MAX_CERT_LEN) { 1852290f027cSgilles log_warnx("warn: certificate too long"); 1853290f027cSgilles goto end; 1854290f027cSgilles } 1855290f027cSgilles } 1856290f027cSgilles 185765c4fdfbSgilles tree_xset(&wait_ssl_verify, s->id, s); 185865c4fdfbSgilles 185965c4fdfbSgilles /* Send the client certificate */ 186065c4fdfbSgilles req_ca_vrfy.reqid = s->id; 1861290f027cSgilles req_ca_vrfy.cert_len = cert_len[0]; 1862290f027cSgilles req_ca_vrfy.n_chain = cert_count; 186365c4fdfbSgilles iov[0].iov_base = &req_ca_vrfy; 186465c4fdfbSgilles iov[0].iov_len = sizeof(req_ca_vrfy); 1865290f027cSgilles iov[1].iov_base = cert_der[0]; 1866290f027cSgilles iov[1].iov_len = cert_len[0]; 1867f80060acSgilles m_composev(p_lka, IMSG_SMTP_TLS_VERIFY_CERT, 0, 0, -1, 186865c4fdfbSgilles iov, nitems(iov)); 186965c4fdfbSgilles 1870c1392a69Seric memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy); 187165c4fdfbSgilles req_ca_vrfy.reqid = s->id; 1872290f027cSgilles 1873290f027cSgilles /* Send the chain, one cert at a time */ 1874290f027cSgilles for (i = 0; i < cert_count; ++i) { 1875290f027cSgilles req_ca_vrfy.cert_len = cert_len[i+1]; 1876290f027cSgilles iov[1].iov_base = cert_der[i+1]; 1877290f027cSgilles iov[1].iov_len = cert_len[i+1]; 1878f80060acSgilles m_composev(p_lka, IMSG_SMTP_TLS_VERIFY_CHAIN, 0, 0, -1, 187965c4fdfbSgilles iov, nitems(iov)); 188065c4fdfbSgilles } 188165c4fdfbSgilles 188265c4fdfbSgilles /* Tell lookup process that it can start verifying, we're done */ 1883c1392a69Seric memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy); 188465c4fdfbSgilles req_ca_vrfy.reqid = s->id; 1885f80060acSgilles m_compose(p_lka, IMSG_SMTP_TLS_VERIFY, 0, 0, -1, 188665c4fdfbSgilles &req_ca_vrfy, sizeof req_ca_vrfy); 188765c4fdfbSgilles 1888290f027cSgilles res = 1; 1889290f027cSgilles 1890290f027cSgilles end: 1891290f027cSgilles for (i = 0; i < MAX_CERTS; ++i) 1892290f027cSgilles free(cert_der[i]); 1893290f027cSgilles 1894290f027cSgilles return res; 189565c4fdfbSgilles } 1896b6d81129Seric 1897299c4efeSeric static void 1898299c4efeSeric smtp_auth_failure_resume(int fd, short event, void *p) 1899299c4efeSeric { 1900299c4efeSeric struct smtp_session *s = p; 1901299c4efeSeric 1902299c4efeSeric smtp_reply(s, "535 Authentication failed"); 1903299c4efeSeric smtp_enter_state(s, STATE_HELO); 1904299c4efeSeric } 1905299c4efeSeric 1906299c4efeSeric static void 1907299c4efeSeric smtp_auth_failure_pause(struct smtp_session *s) 1908299c4efeSeric { 1909299c4efeSeric struct timeval tv; 1910299c4efeSeric 1911299c4efeSeric tv.tv_sec = 0; 1912299c4efeSeric tv.tv_usec = arc4random_uniform(1000000); 1913299c4efeSeric log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, " 1914299c4efeSeric "will defer answer for %lu microseconds", tv.tv_usec); 1915299c4efeSeric evtimer_set(&s->pause, smtp_auth_failure_resume, s); 1916299c4efeSeric evtimer_add(&s->pause, &tv); 1917299c4efeSeric } 1918299c4efeSeric 19195e3ff182Seric static int 19205e3ff182Seric smtp_tx(struct smtp_session *s) 19215e3ff182Seric { 19225e3ff182Seric struct smtp_tx *tx; 19235e3ff182Seric 19245e3ff182Seric tx = calloc(1, sizeof(*tx)); 19255e3ff182Seric if (tx == NULL) 19265e3ff182Seric return 0; 19275e3ff182Seric 19285e3ff182Seric TAILQ_INIT(&tx->rcpts); 19295e3ff182Seric 19305e3ff182Seric s->tx = tx; 19315e3ff182Seric tx->session = s; 19325e3ff182Seric 19335e3ff182Seric /* setup the envelope */ 193422135240Seric tx->evp.ss = s->ss; 193522135240Seric (void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag)); 193622135240Seric (void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname)); 193722135240Seric (void)strlcpy(tx->evp.hostname, s->hostname, sizeof tx->evp.hostname); 193822135240Seric (void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo)); 19395e3ff182Seric 19405e3ff182Seric if (s->flags & SF_BOUNCE) 194122135240Seric tx->evp.flags |= EF_BOUNCE; 19425e3ff182Seric if (s->flags & SF_AUTHENTICATED) 194322135240Seric tx->evp.flags |= EF_AUTHENTICATED; 19445e3ff182Seric 19455e3ff182Seric /* Setup parser and callbacks */ 19465e3ff182Seric rfc2822_parser_init(&tx->rfc2822_parser); 19475e3ff182Seric rfc2822_header_default_callback(&tx->rfc2822_parser, 1948eb95ffd1Seric header_default_callback, tx); 19495e3ff182Seric rfc2822_header_callback(&tx->rfc2822_parser, "bcc", 1950eb95ffd1Seric header_bcc_callback, tx); 19515e3ff182Seric rfc2822_header_callback(&tx->rfc2822_parser, "from", 1952eb95ffd1Seric header_domain_append_callback, tx); 19535e3ff182Seric rfc2822_header_callback(&tx->rfc2822_parser, "to", 1954eb95ffd1Seric header_domain_append_callback, tx); 19555e3ff182Seric rfc2822_header_callback(&tx->rfc2822_parser, "cc", 1956eb95ffd1Seric header_domain_append_callback, tx); 19575e3ff182Seric rfc2822_body_callback(&tx->rfc2822_parser, 1958eb95ffd1Seric dataline_callback, tx); 19595e3ff182Seric 19605e3ff182Seric if (s->listener->local || s->listener->port == htons(587)) { 19615e3ff182Seric rfc2822_missing_header_callback(&tx->rfc2822_parser, "date", 1962eb95ffd1Seric header_missing_callback, tx); 19635e3ff182Seric rfc2822_missing_header_callback(&tx->rfc2822_parser, "message-id", 1964eb95ffd1Seric header_missing_callback, tx); 19655e3ff182Seric } 19665e3ff182Seric 19675e3ff182Seric return 1; 19685e3ff182Seric } 19695e3ff182Seric 19705e3ff182Seric static void 19715e3ff182Seric smtp_tx_free(struct smtp_tx *tx) 19725e3ff182Seric { 19735e3ff182Seric struct smtp_rcpt *rcpt; 19745e3ff182Seric 19755e3ff182Seric rfc2822_parser_release(&tx->rfc2822_parser); 19765e3ff182Seric 19775e3ff182Seric while ((rcpt = TAILQ_FIRST(&tx->rcpts))) { 19785e3ff182Seric TAILQ_REMOVE(&tx->rcpts, rcpt, entry); 19795e3ff182Seric free(rcpt); 19805e3ff182Seric } 19815e3ff182Seric 19825e3ff182Seric if (tx->ofile) 19835e3ff182Seric fclose(tx->ofile); 19845e3ff182Seric 19855e3ff182Seric tx->session->tx = NULL; 19865e3ff182Seric 19875e3ff182Seric free(tx); 19885e3ff182Seric } 19895e3ff182Seric 199008becfa3Seric static void 199172cde3cbSeric smtp_tx_mail_from(struct smtp_tx *tx, char *line) 199272cde3cbSeric { 199372cde3cbSeric if (smtp_mailaddr(&tx->evp.sender, line, 1, &line, 199472cde3cbSeric tx->session->smtpname) == 0) { 199572cde3cbSeric smtp_reply(tx->session, "553 %s: Sender address syntax error", 199672cde3cbSeric esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); 1997*4768c36cSeric smtp_tx_free(tx); 199872cde3cbSeric return; 199972cde3cbSeric } 200072cde3cbSeric 200172cde3cbSeric if (line && smtp_parse_mail_args(tx->session, line) == -1) { 200272cde3cbSeric smtp_tx_free(tx); 200372cde3cbSeric return; 200472cde3cbSeric } 200572cde3cbSeric 200672cde3cbSeric /* only check sendertable if defined and user has authenticated */ 200772cde3cbSeric if (tx->session->flags & SF_AUTHENTICATED && 200872cde3cbSeric tx->session->listener->sendertable[0]) { 200972cde3cbSeric m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1); 201072cde3cbSeric m_add_id(p_lka, tx->session->id); 201172cde3cbSeric m_add_string(p_lka, tx->session->listener->sendertable); 201272cde3cbSeric m_add_string(p_lka, tx->session->username); 201372cde3cbSeric m_add_mailaddr(p_lka, &tx->evp.sender); 201472cde3cbSeric m_close(p_lka); 201572cde3cbSeric tree_xset(&wait_lka_mail, tx->session->id, tx->session); 201672cde3cbSeric } 201772cde3cbSeric else 201872cde3cbSeric smtp_tx_create_message(tx); 201972cde3cbSeric } 202072cde3cbSeric 202172cde3cbSeric static void 2022008f3f72Seric smtp_tx_create_message(struct smtp_tx *tx) 2023a2e141b1Seric { 2024a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1); 2025008f3f72Seric m_add_id(p_queue, tx->session->id); 2026a2e141b1Seric m_close(p_queue); 2027008f3f72Seric tree_xset(&wait_queue_msg, tx->session->id, tx->session); 2028a2e141b1Seric } 2029a2e141b1Seric 2030a2e141b1Seric static void 203172cde3cbSeric smtp_tx_rcpt_to(struct smtp_tx *tx, char *line) 203272cde3cbSeric { 203372cde3cbSeric if (tx->rcptcount >= env->sc_session_max_rcpt) { 203472cde3cbSeric smtp_reply(tx->session, "451 %s %s: Too many recipients", 203572cde3cbSeric esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), 203672cde3cbSeric esc_description(ESC_TOO_MANY_RECIPIENTS)); 203772cde3cbSeric return; 203872cde3cbSeric } 203972cde3cbSeric 204072cde3cbSeric if (smtp_mailaddr(&tx->evp.rcpt, line, 0, &line, 204172cde3cbSeric tx->session->smtpname) == 0) { 204272cde3cbSeric smtp_reply(tx->session, 204372cde3cbSeric "501 %s: Recipient address syntax error", 204472cde3cbSeric esc_code(ESC_STATUS_PERMFAIL, 204572cde3cbSeric ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX)); 204672cde3cbSeric return; 204772cde3cbSeric } 204872cde3cbSeric 204972cde3cbSeric if (line && smtp_parse_rcpt_args(tx->session, line) == -1) 205072cde3cbSeric return; 205172cde3cbSeric 205272cde3cbSeric m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1); 205372cde3cbSeric m_add_id(p_lka, tx->session->id); 205472cde3cbSeric m_add_envelope(p_lka, &tx->evp); 205572cde3cbSeric m_close(p_lka); 205672cde3cbSeric tree_xset(&wait_lka_rcpt, tx->session->id, tx->session); 205772cde3cbSeric } 205872cde3cbSeric 205972cde3cbSeric static void 2060008f3f72Seric smtp_tx_open_message(struct smtp_tx *tx) 2061a2e141b1Seric { 2062a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1); 2063008f3f72Seric m_add_id(p_queue, tx->session->id); 2064008f3f72Seric m_add_msgid(p_queue, tx->msgid); 2065a2e141b1Seric m_close(p_queue); 2066008f3f72Seric tree_xset(&wait_queue_fd, tx->session->id, tx->session); 2067a2e141b1Seric } 2068a2e141b1Seric 2069a2e141b1Seric static void 2070008f3f72Seric smtp_tx_commit(struct smtp_tx *tx) 2071a2e141b1Seric { 2072a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1); 2073008f3f72Seric m_add_id(p_queue, tx->session->id); 2074008f3f72Seric m_add_msgid(p_queue, tx->msgid); 2075a2e141b1Seric m_close(p_queue); 2076008f3f72Seric tree_xset(&wait_queue_commit, tx->session->id, tx->session); 2077a2e141b1Seric } 2078a2e141b1Seric 2079a2e141b1Seric static void 2080008f3f72Seric smtp_tx_rollback(struct smtp_tx *tx) 2081a2e141b1Seric { 2082a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1); 2083008f3f72Seric m_add_msgid(p_queue, tx->msgid); 2084a2e141b1Seric m_close(p_queue); 2085a2e141b1Seric } 2086a2e141b1Seric 20875bfc3fefSeric static int 2088008f3f72Seric smtp_tx_dataline(struct smtp_tx *tx, const char *line) 2089a1bd4550Sgilles { 2090f7c2eb5aSgilles int ret; 2091f7c2eb5aSgilles 2092ad48cc20Seric log_trace(TRACE_SMTP, "<<< [MSG] %s", line); 2093ad48cc20Seric 20945bfc3fefSeric if (!strcmp(line, ".")) { 20955bfc3fefSeric log_trace(TRACE_SMTP, "<<< [EOM]"); 2096008f3f72Seric if (!tx->error) 2097008f3f72Seric rfc2822_parser_flush(&tx->rfc2822_parser); 20985bfc3fefSeric return 1; 20995bfc3fefSeric } 21005bfc3fefSeric 2101f299ff90Seric /* ignore data line if an error is set */ 2102008f3f72Seric if (tx->error) 21035bfc3fefSeric return 0; 2104f7c2eb5aSgilles 2105ad48cc20Seric /* escape lines starting with a '.' */ 2106ad48cc20Seric if (line[0] == '.') 2107ad48cc20Seric line += 1; 2108f7c2eb5aSgilles 2109f7c2eb5aSgilles /* account for newline */ 2110008f3f72Seric tx->datain += strlen(line) + 1; 2111008f3f72Seric if (tx->datain > env->sc_maxsize) { 2112008f3f72Seric tx->error = TX_ERROR_SIZE; 21135bfc3fefSeric return 0; 2114f7c2eb5aSgilles } 2115f7c2eb5aSgilles 2116008f3f72Seric if (!tx->hdrdone) { 2117ad48cc20Seric 2118ad48cc20Seric /* folded header that must be skipped */ 2119008f3f72Seric if (isspace((unsigned char)line[0]) && tx->skiphdr) 21205bfc3fefSeric return 0; 2121008f3f72Seric tx->skiphdr = 0; 2122ad48cc20Seric 2123ad48cc20Seric /* BCC should be stripped from headers */ 2124ad48cc20Seric if (strncasecmp("bcc:", line, 4) == 0) { 2125008f3f72Seric tx->skiphdr = 1; 21265bfc3fefSeric return 0; 2127ad48cc20Seric } 2128ad48cc20Seric 2129ad48cc20Seric /* check for loop */ 2130f7c2eb5aSgilles if (strncasecmp("Received: ", line, 10) == 0) 2131008f3f72Seric tx->rcvcount++; 2132008f3f72Seric if (tx->rcvcount == MAX_HOPS_COUNT) { 2133008f3f72Seric tx->error = TX_ERROR_LOOP; 213471130655Smillert log_warnx("warn: loop detected"); 21355bfc3fefSeric return 0; 2136f7c2eb5aSgilles } 2137ad48cc20Seric 2138ad48cc20Seric if (line[0] == '\0') 2139008f3f72Seric tx->hdrdone = 1; 2140f7c2eb5aSgilles } 2141f7c2eb5aSgilles 2142008f3f72Seric ret = rfc2822_parser_feed(&tx->rfc2822_parser, line); 21435bfc3fefSeric if (ret == -1) 2144008f3f72Seric tx->error = TX_ERROR_RESOURCES; 2145f7c2eb5aSgilles 21465bfc3fefSeric if (ret == 0) 2147008f3f72Seric tx->error = TX_ERROR_MALFORMED; 21485bfc3fefSeric 21495bfc3fefSeric return 0; 2150a1bd4550Sgilles } 2151a1bd4550Sgilles 215254355315Seric static void 215354355315Seric smtp_message_fd(struct smtp_tx *tx, int fd) 215454355315Seric { 215554355315Seric struct smtp_session *s; 215654355315Seric X509 *x; 215754355315Seric 215854355315Seric s = tx->session; 215954355315Seric 216054355315Seric log_debug("smtp: %p: message fd %d", s, fd); 216154355315Seric 216222135240Seric if ((tx->ofile = fdopen(fd, "w")) == NULL) { 216354355315Seric close(fd); 216454355315Seric smtp_reply(s, "421 %s: Temporary Error", 216554355315Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 216654355315Seric smtp_enter_state(s, STATE_QUIT); 216754355315Seric return; 216854355315Seric } 216954355315Seric 217054355315Seric smtp_message_printf(tx, "Received: "); 217154355315Seric if (!(s->listener->flags & F_MASK_SOURCE)) { 217254355315Seric smtp_message_printf(tx, "from %s (%s [%s])", 217354355315Seric s->helo, 217454355315Seric s->hostname, 217554355315Seric ss_to_text(&s->ss)); 217654355315Seric } 217754355315Seric smtp_message_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x", 217854355315Seric s->smtpname, 217954355315Seric SMTPD_NAME, 218054355315Seric s->flags & SF_EHLO ? "E" : "", 218154355315Seric s->flags & SF_SECURE ? "S" : "", 218254355315Seric s->flags & SF_AUTHENTICATED ? "A" : "", 218322135240Seric tx->msgid); 218454355315Seric 218554355315Seric if (s->flags & SF_SECURE) { 218654355315Seric x = SSL_get_peer_certificate(io_ssl(s->io)); 218754355315Seric smtp_message_printf(tx, " (%s:%s:%d:%s)", 218854355315Seric SSL_get_version(io_ssl(s->io)), 218954355315Seric SSL_get_cipher_name(io_ssl(s->io)), 219054355315Seric SSL_get_cipher_bits(io_ssl(s->io), NULL), 219154355315Seric (s->flags & SF_VERIFIED) ? "YES" : (x ? "FAIL" : "NO")); 219254355315Seric X509_free(x); 219354355315Seric 219454355315Seric if (s->listener->flags & F_RECEIVEDAUTH) { 219554355315Seric smtp_message_printf(tx, " auth=%s", 219654355315Seric s->username[0] ? "yes" : "no"); 219754355315Seric if (s->username[0]) 219854355315Seric smtp_message_printf(tx, " user=%s", s->username); 219954355315Seric } 220054355315Seric } 220154355315Seric 220254355315Seric if (tx->rcptcount == 1) { 220354355315Seric smtp_message_printf(tx, "\n\tfor <%s@%s>", 220454355315Seric tx->evp.rcpt.user, 220554355315Seric tx->evp.rcpt.domain); 220654355315Seric } 220754355315Seric 220854355315Seric smtp_message_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time))); 220954355315Seric 221054355315Seric smtp_enter_state(s, STATE_BODY); 221154355315Seric smtp_reply(s, "354 Enter mail, end with \".\"" 221254355315Seric " on a line by itself"); 221354355315Seric } 221454355315Seric 221554355315Seric static void 221654355315Seric smtp_message_end(struct smtp_tx *tx) 221754355315Seric { 221854355315Seric struct smtp_session *s; 221954355315Seric 222054355315Seric s = tx->session; 222154355315Seric 222254355315Seric log_debug("debug: %p: end of message, error=%d", s, tx->error); 222354355315Seric 222454355315Seric fclose(tx->ofile); 222554355315Seric tx->ofile = NULL; 222654355315Seric 222754355315Seric switch(tx->error) { 222854355315Seric case TX_OK: 222954355315Seric smtp_tx_commit(tx); 223054355315Seric return; 223154355315Seric 223254355315Seric case TX_ERROR_SIZE: 223354355315Seric smtp_reply(s, "554 %s %s: Transaction failed, message too big", 223454355315Seric esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM), 223554355315Seric esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM)); 223654355315Seric break; 223754355315Seric 223854355315Seric case TX_ERROR_LOOP: 223954355315Seric smtp_reply(s, "500 %s %s: Loop detected", 224054355315Seric esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED), 224154355315Seric esc_description(ESC_ROUTING_LOOP_DETECTED)); 224254355315Seric break; 224354355315Seric 224454355315Seric case TX_ERROR_MALFORMED: 224554355315Seric smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant", 224654355315Seric esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED), 224754355315Seric esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED)); 224854355315Seric break; 224954355315Seric 225054355315Seric case TX_ERROR_IO: 225154355315Seric case TX_ERROR_RESOURCES: 225254355315Seric smtp_reply(s, "421 %s: Temporary Error", 225354355315Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 225454355315Seric break; 225554355315Seric 225654355315Seric default: 225754355315Seric /* fatal? */ 225854355315Seric smtp_reply(s, "421 Internal server error"); 225954355315Seric } 226054355315Seric 226154355315Seric smtp_tx_rollback(tx); 226254355315Seric smtp_tx_free(tx); 226354355315Seric smtp_enter_state(s, STATE_HELO); 226454355315Seric } 226554355315Seric 226654355315Seric static int 226754355315Seric smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...) 226854355315Seric { 226954355315Seric va_list ap; 227054355315Seric int len; 227154355315Seric 227254355315Seric if (tx->error) 227354355315Seric return -1; 227454355315Seric 227554355315Seric va_start(ap, fmt); 227654355315Seric len = vfprintf(tx->ofile, fmt, ap); 227754355315Seric va_end(ap); 227854355315Seric 227954355315Seric if (len < 0) { 228054355315Seric log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id); 228154355315Seric tx->error = TX_ERROR_IO; 228254355315Seric } 228354355315Seric else 228454355315Seric tx->odatalen += len; 228554355315Seric 228654355315Seric return len; 228754355315Seric } 228854355315Seric 2289b6d81129Seric #define CASE(x) case x : return #x 2290b6d81129Seric 2291b6d81129Seric const char * 229265c4fdfbSgilles smtp_strstate(int state) 2293b6d81129Seric { 2294b6d81129Seric static char buf[32]; 2295b6d81129Seric 2296b6d81129Seric switch (state) { 229765c4fdfbSgilles CASE(STATE_NEW); 229865c4fdfbSgilles CASE(STATE_CONNECTED); 229965c4fdfbSgilles CASE(STATE_TLS); 230065c4fdfbSgilles CASE(STATE_HELO); 230165c4fdfbSgilles CASE(STATE_AUTH_INIT); 230265c4fdfbSgilles CASE(STATE_AUTH_USERNAME); 230365c4fdfbSgilles CASE(STATE_AUTH_PASSWORD); 230465c4fdfbSgilles CASE(STATE_AUTH_FINALIZE); 230565c4fdfbSgilles CASE(STATE_BODY); 230665c4fdfbSgilles CASE(STATE_QUIT); 2307b6d81129Seric default: 2308ef75c9feSgilles (void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state); 230965c4fdfbSgilles return (buf); 2310b6d81129Seric } 2311b6d81129Seric } 2312