1*d49c07c7Sop /* $OpenBSD: smtp_session.c,v 1.443 2024/08/12 09:32:44 op 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 <ctype.h> 2382614934Seric #include <errno.h> 2482614934Seric #include <inttypes.h> 253ef9cbf7Sgilles #include <stdlib.h> 26a2bd1240Schl #include <string.h> 270dcffd0dSop #include <time.h> 28eed85469Seric #include <tls.h> 293ef9cbf7Sgilles #include <unistd.h> 3082614934Seric #include <vis.h> 313ef9cbf7Sgilles 323ef9cbf7Sgilles #include "smtpd.h" 335eb8dddaSgilles #include "log.h" 3444f4452eSeric #include "rfc5322.h" 353ef9cbf7Sgilles 3695be189aSgilles #define SMTP_LINE_MAX 65535 37b34f8648Sgilles #define DATA_HIWAT 65535 386162865dSeric #define APPEND_DOMAIN_BUFFER_SIZE SMTP_LINE_MAX 3927b32957Sgilles 4065c4fdfbSgilles enum smtp_state { 4165c4fdfbSgilles STATE_NEW = 0, 4265c4fdfbSgilles STATE_CONNECTED, 4365c4fdfbSgilles STATE_TLS, 4465c4fdfbSgilles STATE_HELO, 4565c4fdfbSgilles STATE_AUTH_INIT, 4665c4fdfbSgilles STATE_AUTH_USERNAME, 4765c4fdfbSgilles STATE_AUTH_PASSWORD, 4865c4fdfbSgilles STATE_AUTH_FINALIZE, 4965c4fdfbSgilles STATE_BODY, 5065c4fdfbSgilles STATE_QUIT, 5165c4fdfbSgilles }; 5265c4fdfbSgilles 5365c4fdfbSgilles enum session_flags { 5465c4fdfbSgilles SF_EHLO = 0x0001, 5565c4fdfbSgilles SF_8BITMIME = 0x0002, 5665c4fdfbSgilles SF_SECURE = 0x0004, 5765c4fdfbSgilles SF_AUTHENTICATED = 0x0008, 5865c4fdfbSgilles SF_BOUNCE = 0x0010, 59b78cbe77Sgilles SF_VERIFIED = 0x0020, 60b78cbe77Sgilles SF_BADINPUT = 0x0080, 6165c4fdfbSgilles }; 6265c4fdfbSgilles 63f299ff90Seric enum { 64f299ff90Seric TX_OK = 0, 65f299ff90Seric TX_ERROR_ENVELOPE, 66f299ff90Seric TX_ERROR_SIZE, 67f299ff90Seric TX_ERROR_IO, 68f299ff90Seric TX_ERROR_LOOP, 69f299ff90Seric TX_ERROR_MALFORMED, 7044f4452eSeric TX_ERROR_RESOURCES, 7144f4452eSeric TX_ERROR_INTERNAL, 7265c4fdfbSgilles }; 7365c4fdfbSgilles 7465c4fdfbSgilles enum smtp_command { 7565c4fdfbSgilles CMD_HELO = 0, 7665c4fdfbSgilles CMD_EHLO, 7765c4fdfbSgilles CMD_STARTTLS, 7865c4fdfbSgilles CMD_AUTH, 7965c4fdfbSgilles CMD_MAIL_FROM, 8065c4fdfbSgilles CMD_RCPT_TO, 8165c4fdfbSgilles CMD_DATA, 8265c4fdfbSgilles CMD_RSET, 8365c4fdfbSgilles CMD_QUIT, 8465c4fdfbSgilles CMD_HELP, 85fe95d8d0Seric CMD_WIZ, 8665c4fdfbSgilles CMD_NOOP, 87c8094670Sgilles CMD_COMMIT, 8865c4fdfbSgilles }; 8965c4fdfbSgilles 90ee3e6bebSeric struct smtp_rcpt { 91ee3e6bebSeric TAILQ_ENTRY(smtp_rcpt) entry; 92c6b5e508Sgilles uint64_t evpid; 93ee3e6bebSeric struct mailaddr maddr; 94ee3e6bebSeric size_t destcount; 95ee3e6bebSeric }; 96ee3e6bebSeric 970a80c58aSeric struct smtp_tx { 980a80c58aSeric struct smtp_session *session; 99763ff2e3Seric uint32_t msgid; 1000a80c58aSeric 1010a80c58aSeric struct envelope evp; 1020a80c58aSeric size_t rcptcount; 1030a80c58aSeric size_t destcount; 1040a80c58aSeric TAILQ_HEAD(, smtp_rcpt) rcpts; 1050a80c58aSeric 106d7b42ce8Seric time_t time; 107f299ff90Seric int error; 108f71caa29Seric size_t datain; 1090a80c58aSeric size_t odatalen; 11084639b21Seric FILE *ofile; 111590a6142Sgilles struct io *filter; 11244f4452eSeric struct rfc5322_parser *parser; 1130a80c58aSeric int rcvcount; 11444f4452eSeric int has_date; 11544f4452eSeric int has_message_id; 116f6b0bf9aSgilles 117f6b0bf9aSgilles uint8_t junk; 1180a80c58aSeric }; 1190a80c58aSeric 12065c4fdfbSgilles struct smtp_session { 12165c4fdfbSgilles uint64_t id; 1228d3f7f0dSeric struct io *io; 12365c4fdfbSgilles struct listener *listener; 1241c3ac238Seric void *ssl_ctx; 12565c4fdfbSgilles struct sockaddr_storage ss; 126744e25e4Sgilles char rdns[HOST_NAME_MAX+1]; 127953aae25Sderaadt char smtpname[HOST_NAME_MAX+1]; 128bcbdde81Seric int fcrdns; 12965c4fdfbSgilles 13065c4fdfbSgilles int flags; 13165c4fdfbSgilles enum smtp_state state; 13265c4fdfbSgilles 1330d4adc27Sgilles uint8_t banner_sent; 134953aae25Sderaadt char helo[LINE_MAX]; 135953aae25Sderaadt char cmd[LINE_MAX]; 1366dfe9135Sgilles char username[SMTPD_MAXMAILADDRSIZE]; 13765c4fdfbSgilles 13865c4fdfbSgilles size_t mailcount; 139299c4efeSeric struct event pause; 1406bc0eb6bSgilles 1410a80c58aSeric struct smtp_tx *tx; 142522448a1Sgilles 1437929fb13Sgilles enum smtp_command last_cmd; 144522448a1Sgilles enum filter_phase filter_phase; 145522448a1Sgilles const char *filter_param; 146f6b0bf9aSgilles 147f6b0bf9aSgilles uint8_t junk; 14865c4fdfbSgilles }; 1492a4d7b1bSeric 150f4807aa6Seric #define ADVERTISE_TLS(s) \ 15165c4fdfbSgilles ((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE)) 152f4807aa6Seric 153f4807aa6Seric #define ADVERTISE_AUTH(s) \ 15465c4fdfbSgilles ((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \ 15565c4fdfbSgilles !((s)->flags & SF_AUTHENTICATED)) 156f4807aa6Seric 157ad286364Seric #define ADVERTISE_EXT_DSN(s) \ 158ad286364Seric ((s)->listener->flags & F_EXT_DSN) 159ad286364Seric 1604faa8b13Sgilles #define SESSION_FILTERED(s) \ 1614faa8b13Sgilles ((s)->listener->flags & F_FILTERED) 1624faa8b13Sgilles 1634faa8b13Sgilles #define SESSION_DATA_FILTERED(s) \ 164ec69ed85Sgilles ((s)->listener->flags & F_FILTERED) 1654faa8b13Sgilles 1664faa8b13Sgilles 167cc81b7c6Seric static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *); 16865c4fdfbSgilles static void smtp_session_init(void); 16914b654a8Seric static void smtp_lookup_servername(struct smtp_session *); 17001eba458Seric static void smtp_getnameinfo_cb(void *, int, const char *, const char *); 171bcbdde81Seric static void smtp_getaddrinfo_cb(void *, int, struct addrinfo *); 17265c4fdfbSgilles static void smtp_connected(struct smtp_session *); 173cc81b7c6Seric static void smtp_send_banner(struct smtp_session *); 174eed85469Seric static void smtp_tls_init(struct smtp_session *); 175eed85469Seric static void smtp_tls_started(struct smtp_session *); 176b556a8d3Seric static void smtp_io(struct io *, int, void *); 17765c4fdfbSgilles static void smtp_enter_state(struct smtp_session *, int); 17865c4fdfbSgilles static void smtp_reply(struct smtp_session *, char *, ...); 17965c4fdfbSgilles static void smtp_command(struct smtp_session *, char *); 18065c4fdfbSgilles static void smtp_rfc4954_auth_plain(struct smtp_session *, char *); 18165c4fdfbSgilles static void smtp_rfc4954_auth_login(struct smtp_session *, char *); 18265c4fdfbSgilles static void smtp_free(struct smtp_session *, const char *); 18365c4fdfbSgilles static const char *smtp_strstate(int); 184299c4efeSeric static void smtp_auth_failure_pause(struct smtp_session *); 185299c4efeSeric static void smtp_auth_failure_resume(int, short, void *); 186b04e6f27Seric 1870a80c58aSeric static int smtp_tx(struct smtp_session *); 1880a80c58aSeric static void smtp_tx_free(struct smtp_tx *); 189008f3f72Seric static void smtp_tx_create_message(struct smtp_tx *); 190aaf790e0Sgilles static void smtp_tx_mail_from(struct smtp_tx *, const char *); 191aaf790e0Sgilles static void smtp_tx_rcpt_to(struct smtp_tx *, const char *); 192008f3f72Seric static void smtp_tx_open_message(struct smtp_tx *); 193008f3f72Seric static void smtp_tx_commit(struct smtp_tx *); 194008f3f72Seric static void smtp_tx_rollback(struct smtp_tx *); 195008f3f72Seric static int smtp_tx_dataline(struct smtp_tx *, const char *); 196590a6142Sgilles static int smtp_tx_filtered_dataline(struct smtp_tx *, const char *); 197590a6142Sgilles static void smtp_tx_eom(struct smtp_tx *); 198590a6142Sgilles static void smtp_filter_fd(struct smtp_tx *, int); 199590a6142Sgilles static int smtp_message_fd(struct smtp_tx *, int); 200590a6142Sgilles static void smtp_message_begin(struct smtp_tx *); 20155042ccbSeric static void smtp_message_end(struct smtp_tx *); 202f56ea996Smartijn static int smtp_filter_printf(struct smtp_tx *, const char *, ...) 203f56ea996Smartijn __attribute__((__format__ (printf, 2, 3))); 204f56ea996Smartijn static int smtp_message_printf(struct smtp_tx *, const char *, ...) 205f56ea996Smartijn __attribute__((__format__ (printf, 2, 3))); 20608becfa3Seric 207d7b0dc3bSgilles static int smtp_check_rset(struct smtp_session *, const char *); 208d7b0dc3bSgilles static int smtp_check_helo(struct smtp_session *, const char *); 209d7b0dc3bSgilles static int smtp_check_ehlo(struct smtp_session *, const char *); 210197a4a5eSgilles static int smtp_check_auth(struct smtp_session *s, const char *); 211197a4a5eSgilles static int smtp_check_starttls(struct smtp_session *, const char *); 212d7b0dc3bSgilles static int smtp_check_mail_from(struct smtp_session *, const char *); 213d7b0dc3bSgilles static int smtp_check_rcpt_to(struct smtp_session *, const char *); 214d7b0dc3bSgilles static int smtp_check_data(struct smtp_session *, const char *); 215fbe04ea1Sop static int smtp_check_noop(struct smtp_session *, const char *); 216d7b0dc3bSgilles static int smtp_check_noparam(struct smtp_session *, const char *); 217197a4a5eSgilles 218d7b0dc3bSgilles static void smtp_filter_phase(enum filter_phase, struct smtp_session *, const char *); 219197a4a5eSgilles 220d7b0dc3bSgilles static void smtp_proceed_connected(struct smtp_session *); 221d7b0dc3bSgilles static void smtp_proceed_rset(struct smtp_session *, const char *); 222d7b0dc3bSgilles static void smtp_proceed_helo(struct smtp_session *, const char *); 223d7b0dc3bSgilles static void smtp_proceed_ehlo(struct smtp_session *, const char *); 224d7b0dc3bSgilles static void smtp_proceed_auth(struct smtp_session *, const char *); 225d7b0dc3bSgilles static void smtp_proceed_starttls(struct smtp_session *, const char *); 226d7b0dc3bSgilles static void smtp_proceed_mail_from(struct smtp_session *, const char *); 227d7b0dc3bSgilles static void smtp_proceed_rcpt_to(struct smtp_session *, const char *); 228d7b0dc3bSgilles static void smtp_proceed_data(struct smtp_session *, const char *); 229d7b0dc3bSgilles static void smtp_proceed_noop(struct smtp_session *, const char *); 230d7b0dc3bSgilles static void smtp_proceed_help(struct smtp_session *, const char *); 231d7b0dc3bSgilles static void smtp_proceed_wiz(struct smtp_session *, const char *); 232d7b0dc3bSgilles static void smtp_proceed_quit(struct smtp_session *, const char *); 233c8094670Sgilles static void smtp_proceed_commit(struct smtp_session *, const char *); 234c8094670Sgilles static void smtp_proceed_rollback(struct smtp_session *, const char *); 235c8094670Sgilles 236590a6142Sgilles static void smtp_filter_begin(struct smtp_session *); 237590a6142Sgilles static void smtp_filter_end(struct smtp_session *); 238590a6142Sgilles static void smtp_filter_data_begin(struct smtp_session *); 239590a6142Sgilles static void smtp_filter_data_end(struct smtp_session *); 240d7b0dc3bSgilles 2416771f06dSgilles static void smtp_report_link_connect(struct smtp_session *, const char *, int, 2426771f06dSgilles const struct sockaddr_storage *, 2436771f06dSgilles const struct sockaddr_storage *); 2446771f06dSgilles static void smtp_report_link_greeting(struct smtp_session *, const char *); 2456771f06dSgilles static void smtp_report_link_identify(struct smtp_session *, const char *, const char *); 2466771f06dSgilles static void smtp_report_link_tls(struct smtp_session *, const char *); 2476771f06dSgilles static void smtp_report_link_disconnect(struct smtp_session *); 2486771f06dSgilles static void smtp_report_link_auth(struct smtp_session *, const char *, const char *); 2496771f06dSgilles static void smtp_report_tx_reset(struct smtp_session *, uint32_t); 2506771f06dSgilles static void smtp_report_tx_begin(struct smtp_session *, uint32_t); 2516771f06dSgilles static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int); 2526771f06dSgilles static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int); 2536771f06dSgilles static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t); 2546771f06dSgilles static void smtp_report_tx_data(struct smtp_session *, uint32_t, int); 2556771f06dSgilles static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t); 2566771f06dSgilles static void smtp_report_tx_rollback(struct smtp_session *, uint32_t); 2576771f06dSgilles static void smtp_report_protocol_client(struct smtp_session *, const char *); 2586771f06dSgilles static void smtp_report_protocol_server(struct smtp_session *, const char *); 2596771f06dSgilles static void smtp_report_filter_response(struct smtp_session *, int, int, const char *); 2606771f06dSgilles static void smtp_report_timeout(struct smtp_session *); 2616771f06dSgilles 2626771f06dSgilles 263d7b0dc3bSgilles static struct { 264d7b0dc3bSgilles int code; 265d7b0dc3bSgilles enum filter_phase filter_phase; 266d7b0dc3bSgilles const char *cmd; 267d7b0dc3bSgilles 268d7b0dc3bSgilles int (*check)(struct smtp_session *, const char *); 269d7b0dc3bSgilles void (*proceed)(struct smtp_session *, const char *); 270d7b0dc3bSgilles } commands[] = { 271d7b0dc3bSgilles { CMD_HELO, FILTER_HELO, "HELO", smtp_check_helo, smtp_proceed_helo }, 272d7b0dc3bSgilles { CMD_EHLO, FILTER_EHLO, "EHLO", smtp_check_ehlo, smtp_proceed_ehlo }, 273d7b0dc3bSgilles { CMD_STARTTLS, FILTER_STARTTLS, "STARTTLS", smtp_check_starttls, smtp_proceed_starttls }, 274d7b0dc3bSgilles { CMD_AUTH, FILTER_AUTH, "AUTH", smtp_check_auth, smtp_proceed_auth }, 275d7b0dc3bSgilles { CMD_MAIL_FROM, FILTER_MAIL_FROM, "MAIL FROM", smtp_check_mail_from, smtp_proceed_mail_from }, 276d7b0dc3bSgilles { CMD_RCPT_TO, FILTER_RCPT_TO, "RCPT TO", smtp_check_rcpt_to, smtp_proceed_rcpt_to }, 277d7b0dc3bSgilles { CMD_DATA, FILTER_DATA, "DATA", smtp_check_data, smtp_proceed_data }, 278d7b0dc3bSgilles { CMD_RSET, FILTER_RSET, "RSET", smtp_check_rset, smtp_proceed_rset }, 279d7b0dc3bSgilles { CMD_QUIT, FILTER_QUIT, "QUIT", smtp_check_noparam, smtp_proceed_quit }, 280fbe04ea1Sop { CMD_NOOP, FILTER_NOOP, "NOOP", smtp_check_noop, smtp_proceed_noop }, 281d7b0dc3bSgilles { CMD_HELP, FILTER_HELP, "HELP", smtp_check_noparam, smtp_proceed_help }, 282d7b0dc3bSgilles { CMD_WIZ, FILTER_WIZ, "WIZ", smtp_check_noparam, smtp_proceed_wiz }, 283c8094670Sgilles { CMD_COMMIT, FILTER_COMMIT, ".", smtp_check_noparam, smtp_proceed_commit }, 284d7b0dc3bSgilles { -1, 0, NULL, NULL }, 2853ef9cbf7Sgilles }; 2863ef9cbf7Sgilles 287cc81b7c6Seric static struct tree wait_lka_helo; 288a1bd4550Sgilles static struct tree wait_lka_mail; 28965c4fdfbSgilles static struct tree wait_lka_rcpt; 29065c4fdfbSgilles static struct tree wait_parent_auth; 29165c4fdfbSgilles static struct tree wait_queue_msg; 29265c4fdfbSgilles static struct tree wait_queue_fd; 29365c4fdfbSgilles static struct tree wait_queue_commit; 29465c4fdfbSgilles static struct tree wait_ssl_init; 29565c4fdfbSgilles static struct tree wait_ssl_verify; 296d7b0dc3bSgilles static struct tree wait_filters; 297590a6142Sgilles static struct tree wait_filter_fd; 2983ef9cbf7Sgilles 29965c4fdfbSgilles static void 30027b32957Sgilles header_append_domain_buffer(char *buffer, char *domain, size_t len) 30127b32957Sgilles { 30227b32957Sgilles size_t i; 30327b32957Sgilles int escape, quote, comment, bracket; 30427b32957Sgilles int has_domain, has_bracket, has_group; 30527b32957Sgilles int pos_bracket, pos_component, pos_insert; 30627b32957Sgilles char copy[APPEND_DOMAIN_BUFFER_SIZE]; 30727b32957Sgilles 30827b32957Sgilles escape = quote = comment = bracket = 0; 30927b32957Sgilles has_domain = has_bracket = has_group = 0; 31027b32957Sgilles pos_bracket = pos_insert = pos_component = 0; 31127b32957Sgilles for (i = 0; buffer[i]; ++i) { 31227b32957Sgilles if (buffer[i] == '(' && !escape && !quote) 31327b32957Sgilles comment++; 31427b32957Sgilles if (buffer[i] == '"' && !escape && !comment) 31527b32957Sgilles quote = !quote; 31627b32957Sgilles if (buffer[i] == ')' && !escape && !quote && comment) 31727b32957Sgilles comment--; 31827b32957Sgilles if (buffer[i] == '\\' && !escape && !comment && !quote) 31927b32957Sgilles escape = 1; 32027b32957Sgilles else 32127b32957Sgilles escape = 0; 32227b32957Sgilles if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { 32327b32957Sgilles bracket++; 32427b32957Sgilles has_bracket = 1; 32527b32957Sgilles } 32627b32957Sgilles if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { 32727b32957Sgilles bracket--; 32827b32957Sgilles pos_bracket = i; 32927b32957Sgilles } 33027b32957Sgilles if (buffer[i] == '@' && !escape && !comment && !quote) 33127b32957Sgilles has_domain = 1; 33227b32957Sgilles if (buffer[i] == ':' && !escape && !comment && !quote) 33327b32957Sgilles has_group = 1; 334cb2a19a6Sgilles 335cb2a19a6Sgilles /* update insert point if not in comment and not on a whitespace */ 336cb2a19a6Sgilles if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i])) 33727b32957Sgilles pos_component = i; 33827b32957Sgilles } 33927b32957Sgilles 34027b32957Sgilles /* parse error, do not attempt to modify */ 34127b32957Sgilles if (escape || quote || comment || bracket) 34227b32957Sgilles return; 34327b32957Sgilles 34427b32957Sgilles /* domain already present, no need to modify */ 34527b32957Sgilles if (has_domain) 34627b32957Sgilles return; 34727b32957Sgilles 34827b32957Sgilles /* address is group, skip */ 34927b32957Sgilles if (has_group) 35027b32957Sgilles return; 35127b32957Sgilles 35227b32957Sgilles /* there's an address between brackets, just append domain */ 35327b32957Sgilles if (has_bracket) { 35427b32957Sgilles pos_bracket--; 355cb2a19a6Sgilles while (isspace((unsigned char)buffer[pos_bracket])) 35627b32957Sgilles pos_bracket--; 35727b32957Sgilles if (buffer[pos_bracket] == '<') 35827b32957Sgilles return; 35927b32957Sgilles pos_insert = pos_bracket + 1; 36027b32957Sgilles } 36127b32957Sgilles else { 36227b32957Sgilles /* otherwise append address to last component */ 36327b32957Sgilles pos_insert = pos_component + 1; 36427b32957Sgilles 36527b32957Sgilles /* empty address */ 366cb2a19a6Sgilles if (buffer[pos_component] == '\0' || 367cb2a19a6Sgilles isspace((unsigned char)buffer[pos_component])) 36827b32957Sgilles return; 36927b32957Sgilles } 37027b32957Sgilles 37127b32957Sgilles if (snprintf(copy, sizeof copy, "%.*s@%s%s", 37227b32957Sgilles (int)pos_insert, buffer, 37327b32957Sgilles domain, 37427b32957Sgilles buffer+pos_insert) >= (int)sizeof copy) 37527b32957Sgilles return; 37627b32957Sgilles 37727b32957Sgilles memcpy(buffer, copy, len); 37827b32957Sgilles } 37927b32957Sgilles 38027b32957Sgilles static void 3814d531edbSgilles header_address_rewrite_buffer(char *buffer, const char *address, size_t len) 3824d531edbSgilles { 3834d531edbSgilles size_t i; 3844d531edbSgilles int address_len; 3854d531edbSgilles int escape, quote, comment, bracket; 3864d531edbSgilles int has_bracket, has_group; 3874d531edbSgilles int pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end; 3884d531edbSgilles int insert_beg, insert_end; 3894d531edbSgilles char copy[APPEND_DOMAIN_BUFFER_SIZE]; 3904d531edbSgilles 3914d531edbSgilles escape = quote = comment = bracket = 0; 3924d531edbSgilles has_bracket = has_group = 0; 3934d531edbSgilles pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0; 3944d531edbSgilles for (i = 0; buffer[i]; ++i) { 3954d531edbSgilles if (buffer[i] == '(' && !escape && !quote) 3964d531edbSgilles comment++; 3974d531edbSgilles if (buffer[i] == '"' && !escape && !comment) 3984d531edbSgilles quote = !quote; 3994d531edbSgilles if (buffer[i] == ')' && !escape && !quote && comment) 4004d531edbSgilles comment--; 4014d531edbSgilles if (buffer[i] == '\\' && !escape && !comment && !quote) 4024d531edbSgilles escape = 1; 4034d531edbSgilles else 4044d531edbSgilles escape = 0; 4054d531edbSgilles if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) { 4064d531edbSgilles bracket++; 4074d531edbSgilles has_bracket = 1; 4084d531edbSgilles pos_bracket_beg = i+1; 4094d531edbSgilles } 4104d531edbSgilles if (buffer[i] == '>' && !escape && !comment && !quote && bracket) { 4114d531edbSgilles bracket--; 4124d531edbSgilles pos_bracket_end = i; 4134d531edbSgilles } 4144d531edbSgilles if (buffer[i] == ':' && !escape && !comment && !quote) 4154d531edbSgilles has_group = 1; 4164d531edbSgilles 4174d531edbSgilles /* update insert point if not in comment and not on a whitespace */ 4184d531edbSgilles if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i])) 4194d531edbSgilles pos_component_end = i; 4204d531edbSgilles } 4214d531edbSgilles 4224d531edbSgilles /* parse error, do not attempt to modify */ 4234d531edbSgilles if (escape || quote || comment || bracket) 4244d531edbSgilles return; 4254d531edbSgilles 4264d531edbSgilles /* address is group, skip */ 4274d531edbSgilles if (has_group) 4284d531edbSgilles return; 4294d531edbSgilles 4304d531edbSgilles /* there's an address between brackets, just replace everything brackets */ 4314d531edbSgilles if (has_bracket) { 4324d531edbSgilles insert_beg = pos_bracket_beg; 4334d531edbSgilles insert_end = pos_bracket_end; 4344d531edbSgilles } 4354d531edbSgilles else { 4364d531edbSgilles if (pos_component_end == 0) 4374d531edbSgilles pos_component_beg = 0; 4384d531edbSgilles else { 4394d531edbSgilles for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg) 4403d02c75dSmillert if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg])) 4414d531edbSgilles break; 4424d531edbSgilles pos_component_beg += 1; 4434d531edbSgilles pos_component_end += 1; 4444d531edbSgilles } 4454d531edbSgilles insert_beg = pos_component_beg; 4464d531edbSgilles insert_end = pos_component_end; 4474d531edbSgilles } 4484d531edbSgilles 4494d531edbSgilles /* check that masquerade won' t overflow */ 4504d531edbSgilles address_len = strlen(address); 4514d531edbSgilles if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len) 4524d531edbSgilles return; 4534d531edbSgilles 4544d531edbSgilles (void)strlcpy(copy, buffer, sizeof copy); 4554d531edbSgilles (void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg); 4564d531edbSgilles (void)strlcat(copy, buffer+insert_end, sizeof (copy)); 4574d531edbSgilles memcpy(buffer, copy, len); 4584d531edbSgilles } 4594d531edbSgilles 4604d531edbSgilles static void 46144f4452eSeric header_domain_append_callback(struct smtp_tx *tx, const char *hdr, 46244f4452eSeric const char *val) 4634d531edbSgilles { 46444f4452eSeric size_t i, j, linelen; 4654d531edbSgilles int escape, quote, comment, skip; 4664d531edbSgilles char buffer[APPEND_DOMAIN_BUFFER_SIZE]; 46744f4452eSeric const char *line, *end; 4684d531edbSgilles 46944f4452eSeric if (smtp_message_printf(tx, "%s:", hdr) == -1) 4704d531edbSgilles return; 4714d531edbSgilles 4724d531edbSgilles j = 0; 4734d531edbSgilles escape = quote = comment = skip = 0; 4744d531edbSgilles memset(buffer, 0, sizeof buffer); 4754d531edbSgilles 47644f4452eSeric for (line = val; line; line = end) { 47744f4452eSeric end = strchr(line, '\n'); 47844f4452eSeric if (end) { 47944f4452eSeric linelen = end - line; 48044f4452eSeric end++; 48144f4452eSeric } 48244f4452eSeric else 48344f4452eSeric linelen = strlen(line); 48444f4452eSeric 48544f4452eSeric for (i = 0; i < linelen; ++i) { 48644f4452eSeric if (line[i] == '(' && !escape && !quote) 4874d531edbSgilles comment++; 48844f4452eSeric if (line[i] == '"' && !escape && !comment) 4894d531edbSgilles quote = !quote; 49044f4452eSeric if (line[i] == ')' && !escape && !quote && comment) 4914d531edbSgilles comment--; 4923b3439c3Sop if (line[i] == '\\' && !escape && !comment) 4934d531edbSgilles escape = 1; 4944d531edbSgilles else 4954d531edbSgilles escape = 0; 4964d531edbSgilles 4974d531edbSgilles /* found a separator, buffer contains a full address */ 49844f4452eSeric if (line[i] == ',' && !escape && !quote && !comment) { 49944f4452eSeric if (!skip && j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) { 50044f4452eSeric header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer); 50144f4452eSeric if (tx->session->flags & SF_AUTHENTICATED && 50244f4452eSeric tx->session->listener->sendertable[0] && 50344f4452eSeric tx->session->listener->flags & F_MASQUERADE && 50444f4452eSeric !(strcasecmp(hdr, "From"))) 50555042ccbSeric header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender), 5064d531edbSgilles sizeof buffer); 5074d531edbSgilles } 50855042ccbSeric if (smtp_message_printf(tx, "%s,", buffer) == -1) 5094d531edbSgilles return; 5104d531edbSgilles j = 0; 5114d531edbSgilles skip = 0; 5124d531edbSgilles memset(buffer, 0, sizeof buffer); 5134d531edbSgilles } 5144d531edbSgilles else { 5154d531edbSgilles if (skip) { 51644f4452eSeric if (smtp_message_printf(tx, "%c", line[i]) == -1) 5174d531edbSgilles return; 5184d531edbSgilles } 5194d531edbSgilles else { 52044f4452eSeric buffer[j++] = line[i]; 5214d531edbSgilles if (j == sizeof (buffer) - 1) { 52255042ccbSeric if (smtp_message_printf(tx, "%s", buffer) == -1) 5234d531edbSgilles return; 5244d531edbSgilles skip = 1; 5254d531edbSgilles j = 0; 5264d531edbSgilles memset(buffer, 0, sizeof buffer); 5274d531edbSgilles } 5284d531edbSgilles } 5294d531edbSgilles } 5304d531edbSgilles } 5314d531edbSgilles if (skip) { 53255042ccbSeric if (smtp_message_printf(tx, "\n") == -1) 5334d531edbSgilles return; 5344d531edbSgilles } 5354d531edbSgilles else { 5364d531edbSgilles buffer[j++] = '\n'; 5374d531edbSgilles if (j == sizeof (buffer) - 1) { 53855042ccbSeric if (smtp_message_printf(tx, "%s", buffer) == -1) 5394d531edbSgilles return; 5404d531edbSgilles skip = 1; 5414d531edbSgilles j = 0; 5424d531edbSgilles memset(buffer, 0, sizeof buffer); 5434d531edbSgilles } 5444d531edbSgilles } 5454d531edbSgilles } 5464d531edbSgilles 5474d531edbSgilles /* end of header, if buffer is not empty we'll process it */ 5484d531edbSgilles if (buffer[0]) { 54944f4452eSeric if (j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) { 55044f4452eSeric header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer); 55144f4452eSeric if (tx->session->flags & SF_AUTHENTICATED && 55244f4452eSeric tx->session->listener->sendertable[0] && 55344f4452eSeric tx->session->listener->flags & F_MASQUERADE && 55444f4452eSeric !(strcasecmp(hdr, "From"))) 55555042ccbSeric header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender), 5564d531edbSgilles sizeof buffer); 5574d531edbSgilles } 55855042ccbSeric smtp_message_printf(tx, "%s", buffer); 5594d531edbSgilles } 5604d531edbSgilles } 5614d531edbSgilles 5624d531edbSgilles static void 56365c4fdfbSgilles smtp_session_init(void) 5643ef9cbf7Sgilles { 56565c4fdfbSgilles static int init = 0; 566ef9f3596Sjacekm 56765c4fdfbSgilles if (!init) { 568cc81b7c6Seric tree_init(&wait_lka_helo); 569a1bd4550Sgilles tree_init(&wait_lka_mail); 57065c4fdfbSgilles tree_init(&wait_lka_rcpt); 57165c4fdfbSgilles tree_init(&wait_parent_auth); 57265c4fdfbSgilles tree_init(&wait_queue_msg); 57365c4fdfbSgilles tree_init(&wait_queue_fd); 57465c4fdfbSgilles tree_init(&wait_queue_commit); 57565c4fdfbSgilles tree_init(&wait_ssl_init); 57665c4fdfbSgilles tree_init(&wait_ssl_verify); 577d7b0dc3bSgilles tree_init(&wait_filters); 578590a6142Sgilles tree_init(&wait_filter_fd); 57965c4fdfbSgilles init = 1; 58065c4fdfbSgilles } 5813ef9cbf7Sgilles } 5823ef9cbf7Sgilles 58365c4fdfbSgilles int 58465c4fdfbSgilles smtp_session(struct listener *listener, int sock, 5853685495aSgilles const struct sockaddr_storage *ss, const char *hostname, struct io *io) 5863ef9cbf7Sgilles { 58765c4fdfbSgilles struct smtp_session *s; 5883ef9cbf7Sgilles 58965c4fdfbSgilles smtp_session_init(); 59065c4fdfbSgilles 59165c4fdfbSgilles if ((s = calloc(1, sizeof(*s))) == NULL) 59265c4fdfbSgilles return (-1); 5930a80c58aSeric 59465c4fdfbSgilles s->id = generate_uid(); 59565c4fdfbSgilles s->listener = listener; 59665c4fdfbSgilles memmove(&s->ss, ss, sizeof(*ss)); 5973685495aSgilles 5983685495aSgilles if (io != NULL) 5993685495aSgilles s->io = io; 6003685495aSgilles else 6018d3f7f0dSeric s->io = io_new(); 6023685495aSgilles 6038d3f7f0dSeric io_set_callback(s->io, smtp_io, s); 6048d3f7f0dSeric io_set_fd(s->io, sock); 6058d3f7f0dSeric io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000); 6068d3f7f0dSeric io_set_write(s->io); 60765c4fdfbSgilles s->state = STATE_NEW; 60865c4fdfbSgilles 609c51a7fb7Sgilles (void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname)); 610cc81b7c6Seric 61165195c54Sgilles log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p " 61265195c54Sgilles "[hostname=%s, port=%d, tag=%s]", s, listener, 61365195c54Sgilles listener->hostname, ntohs(listener->port), listener->tag); 61465195c54Sgilles 61565c4fdfbSgilles /* For local enqueueing, the hostname is already set */ 61665c4fdfbSgilles if (hostname) { 61765c4fdfbSgilles s->flags |= SF_AUTHENTICATED; 61865c4fdfbSgilles /* A bit of a hack */ 61965c4fdfbSgilles if (!strcmp(hostname, "localhost")) 62065c4fdfbSgilles s->flags |= SF_BOUNCE; 621744e25e4Sgilles (void)strlcpy(s->rdns, hostname, sizeof(s->rdns)); 622bcbdde81Seric s->fcrdns = 1; 62314b654a8Seric smtp_lookup_servername(s); 62465c4fdfbSgilles } else { 62597556c3cSmartijn resolver_getnameinfo((struct sockaddr *)&s->ss, 62697556c3cSmartijn NI_NAMEREQD | NI_NUMERICSERV, smtp_getnameinfo_cb, s); 6277219d653Sjacekm } 628ef9f3596Sjacekm 629cb2a19a6Sgilles /* session may have been freed by now */ 6306bc0eb6bSgilles 63165c4fdfbSgilles return (0); 6323ef9cbf7Sgilles } 6333ef9cbf7Sgilles 63401eba458Seric static void 63501eba458Seric smtp_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv) 63601eba458Seric { 63701eba458Seric struct smtp_session *s = arg; 638bcbdde81Seric struct addrinfo hints; 63901eba458Seric 640bcbdde81Seric if (gaierrno) { 641744e25e4Sgilles (void)strlcpy(s->rdns, "<unknown>", sizeof(s->rdns)); 6428020a2a3Sgilles 6438020a2a3Sgilles if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME) 6448020a2a3Sgilles s->fcrdns = 0; 6457b3b6902Sgilles else { 6467b3b6902Sgilles log_warnx("getnameinfo: %s: %s", ss_to_text(&s->ss), 6477b3b6902Sgilles gai_strerror(gaierrno)); 648bcbdde81Seric s->fcrdns = -1; 6497b3b6902Sgilles } 6508020a2a3Sgilles 651bcbdde81Seric smtp_lookup_servername(s); 652bcbdde81Seric return; 653bcbdde81Seric } 65401eba458Seric 655744e25e4Sgilles (void)strlcpy(s->rdns, host, sizeof(s->rdns)); 65601eba458Seric 657bcbdde81Seric memset(&hints, 0, sizeof(hints)); 658bcbdde81Seric hints.ai_family = s->ss.ss_family; 659bcbdde81Seric hints.ai_socktype = SOCK_STREAM; 660744e25e4Sgilles resolver_getaddrinfo(s->rdns, NULL, &hints, smtp_getaddrinfo_cb, s); 661bcbdde81Seric } 662bcbdde81Seric 663bcbdde81Seric static void 664bcbdde81Seric smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0) 665bcbdde81Seric { 666bcbdde81Seric struct smtp_session *s = arg; 667bcbdde81Seric struct addrinfo *ai; 668bcbdde81Seric char fwd[64], rev[64]; 669bcbdde81Seric 670bcbdde81Seric if (gaierrno) { 6718020a2a3Sgilles if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME) 6728020a2a3Sgilles s->fcrdns = 0; 6737b3b6902Sgilles else { 6747b3b6902Sgilles log_warnx("getaddrinfo: %s: %s", s->rdns, 6757b3b6902Sgilles gai_strerror(gaierrno)); 676bcbdde81Seric s->fcrdns = -1; 677bcbdde81Seric } 6787b3b6902Sgilles } 679bcbdde81Seric else { 680bcbdde81Seric strlcpy(rev, ss_to_text(&s->ss), sizeof(rev)); 681bcbdde81Seric for (ai = ai0; ai; ai = ai->ai_next) { 682bcbdde81Seric strlcpy(fwd, sa_to_text(ai->ai_addr), sizeof(fwd)); 683bcbdde81Seric if (!strcmp(fwd, rev)) { 684bcbdde81Seric s->fcrdns = 1; 685bcbdde81Seric break; 686bcbdde81Seric } 687bcbdde81Seric } 688bcbdde81Seric freeaddrinfo(ai0); 689bcbdde81Seric } 690bcbdde81Seric 69114b654a8Seric smtp_lookup_servername(s); 69201eba458Seric } 69301eba458Seric 69465c4fdfbSgilles void 69565c4fdfbSgilles smtp_session_imsg(struct mproc *p, struct imsg *imsg) 69665c4fdfbSgilles { 69765c4fdfbSgilles struct smtp_session *s; 698ee3e6bebSeric struct smtp_rcpt *rcpt; 699db893743Sgilles char user[SMTPD_MAXMAILADDRSIZE]; 7008bef9f7cSgilles char tmp[SMTP_LINE_MAX]; 70165c4fdfbSgilles struct msg m; 702cc81b7c6Seric const char *line, *helo; 70365c4fdfbSgilles uint64_t reqid, evpid; 70408becfa3Seric uint32_t msgid; 705510586acSclaudio int status, success, fd; 706d7b0dc3bSgilles int filter_response; 707d7b0dc3bSgilles const char *filter_param; 708d7b0dc3bSgilles uint8_t i; 70965c4fdfbSgilles 71065c4fdfbSgilles switch (imsg->hdr.type) { 71165c4fdfbSgilles 71206405042Ssunil case IMSG_SMTP_CHECK_SENDER: 71306405042Ssunil m_msg(&m, imsg); 71406405042Ssunil m_get_id(&m, &reqid); 71506405042Ssunil m_get_int(&m, &status); 71606405042Ssunil m_end(&m); 71706405042Ssunil s = tree_xpop(&wait_lka_mail, reqid); 71806405042Ssunil switch (status) { 71906405042Ssunil case LKA_OK: 720008f3f72Seric smtp_tx_create_message(s->tx); 72106405042Ssunil break; 72206405042Ssunil 72306405042Ssunil case LKA_PERMFAIL: 7240490754bSeric smtp_tx_free(s->tx); 72506405042Ssunil smtp_reply(s, "%d %s", 530, "Sender rejected"); 72606405042Ssunil break; 72706405042Ssunil case LKA_TEMPFAIL: 7280490754bSeric smtp_tx_free(s->tx); 7295fe3cdcbSgilles smtp_reply(s, "421 %s Temporary Error", 73006405042Ssunil esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 73106405042Ssunil break; 73206405042Ssunil } 73306405042Ssunil return; 73406405042Ssunil 735aa1d5973Seric case IMSG_SMTP_EXPAND_RCPT: 73665c4fdfbSgilles m_msg(&m, imsg); 73765c4fdfbSgilles m_get_id(&m, &reqid); 73865c4fdfbSgilles m_get_int(&m, &status); 739299c4efeSeric m_get_string(&m, &line); 74065c4fdfbSgilles m_end(&m); 74165c4fdfbSgilles s = tree_xpop(&wait_lka_rcpt, reqid); 7428bef9f7cSgilles 7438bef9f7cSgilles tmp[0] = '\0'; 7448bef9f7cSgilles if (s->tx->evp.rcpt.user[0]) { 7458bef9f7cSgilles (void)strlcpy(tmp, s->tx->evp.rcpt.user, sizeof tmp); 7468bef9f7cSgilles if (s->tx->evp.rcpt.domain[0]) { 7478bef9f7cSgilles (void)strlcat(tmp, "@", sizeof tmp); 7488bef9f7cSgilles (void)strlcat(tmp, s->tx->evp.rcpt.domain, 7498bef9f7cSgilles sizeof tmp); 7508bef9f7cSgilles } 7518bef9f7cSgilles } 7528bef9f7cSgilles 75365c4fdfbSgilles switch (status) { 75465c4fdfbSgilles case LKA_OK: 75565c4fdfbSgilles fatalx("unexpected ok"); 75665c4fdfbSgilles case LKA_PERMFAIL: 7578bef9f7cSgilles smtp_reply(s, "%s: <%s>", line, tmp); 75865c4fdfbSgilles break; 75965c4fdfbSgilles case LKA_TEMPFAIL: 7608bef9f7cSgilles smtp_reply(s, "%s: <%s>", line, tmp); 7618bef9f7cSgilles break; 76265c4fdfbSgilles } 76365c4fdfbSgilles return; 76465c4fdfbSgilles 765aa1d5973Seric case IMSG_SMTP_LOOKUP_HELO: 766cc81b7c6Seric m_msg(&m, imsg); 767cc81b7c6Seric m_get_id(&m, &reqid); 768cc81b7c6Seric s = tree_xpop(&wait_lka_helo, reqid); 769cc81b7c6Seric m_get_int(&m, &status); 770cc81b7c6Seric if (status == LKA_OK) { 771cc81b7c6Seric m_get_string(&m, &helo); 772d429e01dSgilles (void)strlcpy(s->smtpname, helo, sizeof(s->smtpname)); 773cc81b7c6Seric } 774cc81b7c6Seric m_end(&m); 7751c3ac238Seric smtp_connected(s); 776cc81b7c6Seric return; 777cc81b7c6Seric 778aa1d5973Seric case IMSG_SMTP_MESSAGE_CREATE: 77965c4fdfbSgilles m_msg(&m, imsg); 78065c4fdfbSgilles m_get_id(&m, &reqid); 78165c4fdfbSgilles m_get_int(&m, &success); 78265c4fdfbSgilles s = tree_xpop(&wait_queue_msg, reqid); 78365c4fdfbSgilles if (success) { 78465c4fdfbSgilles m_get_msgid(&m, &msgid); 785763ff2e3Seric s->tx->msgid = msgid; 7860a80c58aSeric s->tx->evp.id = msgid_to_evpid(msgid); 7870a80c58aSeric s->tx->rcptcount = 0; 7885fe3cdcbSgilles smtp_reply(s, "250 %s Ok", 789fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 79065c4fdfbSgilles } else { 7915fe3cdcbSgilles smtp_reply(s, "421 %s Temporary Error", 792fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 793a06bfb0dSgilles smtp_tx_free(s->tx); 794299c4efeSeric smtp_enter_state(s, STATE_QUIT); 79565c4fdfbSgilles } 79665c4fdfbSgilles m_end(&m); 79765c4fdfbSgilles return; 79865c4fdfbSgilles 799aa1d5973Seric case IMSG_SMTP_MESSAGE_OPEN: 80065c4fdfbSgilles m_msg(&m, imsg); 80165c4fdfbSgilles m_get_id(&m, &reqid); 80265c4fdfbSgilles m_get_int(&m, &success); 80365c4fdfbSgilles m_end(&m); 80465c4fdfbSgilles 805510586acSclaudio fd = imsg_get_fd(imsg); 80665c4fdfbSgilles s = tree_xpop(&wait_queue_fd, reqid); 807510586acSclaudio if (!success || fd == -1) { 808510586acSclaudio if (fd != -1) 809510586acSclaudio close(fd); 8105fe3cdcbSgilles smtp_reply(s, "421 %s Temporary Error", 811fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 81265c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 81365c4fdfbSgilles return; 81465c4fdfbSgilles } 81565c4fdfbSgilles 816510586acSclaudio log_debug("smtp: %p: fd %d from queue", s, fd); 81765c4fdfbSgilles 818510586acSclaudio if (smtp_message_fd(s->tx, fd)) { 8194faa8b13Sgilles if (!SESSION_DATA_FILTERED(s)) 820590a6142Sgilles smtp_message_begin(s->tx); 821590a6142Sgilles else 822590a6142Sgilles smtp_filter_data_begin(s); 823590a6142Sgilles } 824590a6142Sgilles return; 825590a6142Sgilles 8262b1a70ccSgilles case IMSG_FILTER_SMTP_DATA_BEGIN: 827590a6142Sgilles m_msg(&m, imsg); 828590a6142Sgilles m_get_id(&m, &reqid); 829590a6142Sgilles m_get_int(&m, &success); 830590a6142Sgilles m_end(&m); 831590a6142Sgilles 832510586acSclaudio fd = imsg_get_fd(imsg); 833590a6142Sgilles s = tree_xpop(&wait_filter_fd, reqid); 834510586acSclaudio if (!success || fd == -1) { 835510586acSclaudio if (fd != -1) 836510586acSclaudio close(fd); 8375fe3cdcbSgilles smtp_reply(s, "421 %s Temporary Error", 838590a6142Sgilles esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 839590a6142Sgilles smtp_enter_state(s, STATE_QUIT); 840590a6142Sgilles return; 841590a6142Sgilles } 842590a6142Sgilles 843510586acSclaudio log_debug("smtp: %p: fd %d from lka", s, fd); 844590a6142Sgilles 845510586acSclaudio smtp_filter_fd(s->tx, fd); 846590a6142Sgilles smtp_message_begin(s->tx); 84765c4fdfbSgilles return; 84865c4fdfbSgilles 849aa1d5973Seric case IMSG_QUEUE_ENVELOPE_SUBMIT: 85065c4fdfbSgilles m_msg(&m, imsg); 85165c4fdfbSgilles m_get_id(&m, &reqid); 85265c4fdfbSgilles m_get_int(&m, &success); 85365c4fdfbSgilles s = tree_xget(&wait_lka_rcpt, reqid); 85465c4fdfbSgilles if (success) { 85565c4fdfbSgilles m_get_evpid(&m, &evpid); 856c6b5e508Sgilles s->tx->evp.id = evpid; 8570a80c58aSeric s->tx->destcount++; 8586771f06dSgilles smtp_report_tx_envelope(s, s->tx->msgid, evpid); 85965c4fdfbSgilles } 86065c4fdfbSgilles else 861f299ff90Seric s->tx->error = TX_ERROR_ENVELOPE; 86265c4fdfbSgilles m_end(&m); 86365c4fdfbSgilles return; 86465c4fdfbSgilles 865aa1d5973Seric case IMSG_QUEUE_ENVELOPE_COMMIT: 86665c4fdfbSgilles m_msg(&m, imsg); 86765c4fdfbSgilles m_get_id(&m, &reqid); 86865c4fdfbSgilles m_get_int(&m, &success); 86965c4fdfbSgilles m_end(&m); 87065c4fdfbSgilles if (!success) 87165c4fdfbSgilles fatalx("commit evp failed: not supposed to happen"); 87265c4fdfbSgilles s = tree_xpop(&wait_lka_rcpt, reqid); 873f299ff90Seric if (s->tx->error) { 87465c4fdfbSgilles /* 87565c4fdfbSgilles * If an envelope failed, we can't cancel the last 87665c4fdfbSgilles * RCPT only so we must cancel the whole transaction 87765c4fdfbSgilles * and close the connection. 87865c4fdfbSgilles */ 8795fe3cdcbSgilles smtp_reply(s, "421 %s Temporary failure", 880fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 88165c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 88265c4fdfbSgilles } 88365c4fdfbSgilles else { 884118c16f3Sgilles rcpt = xcalloc(1, sizeof(*rcpt)); 885c6b5e508Sgilles rcpt->evpid = s->tx->evp.id; 8860a80c58aSeric rcpt->destcount = s->tx->destcount; 8870a80c58aSeric rcpt->maddr = s->tx->evp.rcpt; 8880a80c58aSeric TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry); 889ee3e6bebSeric 8900a80c58aSeric s->tx->destcount = 0; 8910a80c58aSeric s->tx->rcptcount++; 892fe95d8d0Seric smtp_reply(s, "250 %s %s: Recipient ok", 893fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID), 894fe95d8d0Seric esc_description(ESC_DESTINATION_ADDRESS_VALID)); 89565c4fdfbSgilles } 89665c4fdfbSgilles return; 89765c4fdfbSgilles 898aa1d5973Seric case IMSG_SMTP_MESSAGE_COMMIT: 89965c4fdfbSgilles m_msg(&m, imsg); 90065c4fdfbSgilles m_get_id(&m, &reqid); 90165c4fdfbSgilles m_get_int(&m, &success); 90265c4fdfbSgilles m_end(&m); 90365c4fdfbSgilles s = tree_xpop(&wait_queue_commit, reqid); 90465c4fdfbSgilles if (!success) { 9055fe3cdcbSgilles smtp_reply(s, "421 %s Temporary failure", 906fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 907aebb80a1Sgilles smtp_tx_free(s->tx); 90865c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 90965c4fdfbSgilles return; 91065c4fdfbSgilles } 91165c4fdfbSgilles 9125fe3cdcbSgilles smtp_reply(s, "250 %s %08x Message accepted for delivery", 913fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS), 914763ff2e3Seric s->tx->msgid); 915aebb80a1Sgilles smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen); 916aebb80a1Sgilles smtp_report_tx_reset(s, s->tx->msgid); 91765c4fdfbSgilles 918c6b5e508Sgilles log_info("%016"PRIx64" smtp message " 919c6b5e508Sgilles "msgid=%08x size=%zu nrcpt=%zu proto=%s", 92065c4fdfbSgilles s->id, 921763ff2e3Seric s->tx->msgid, 922c6b5e508Sgilles s->tx->odatalen, 923c6b5e508Sgilles s->tx->rcptcount, 924c6b5e508Sgilles s->flags & SF_EHLO ? "ESMTP" : "SMTP"); 925c6b5e508Sgilles TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) { 926c6b5e508Sgilles log_info("%016"PRIx64" smtp envelope " 927c6b5e508Sgilles "evpid=%016"PRIx64" from=<%s%s%s> to=<%s%s%s>", 928c6b5e508Sgilles s->id, 929c6b5e508Sgilles rcpt->evpid, 9300a80c58aSeric s->tx->evp.sender.user, 9310a80c58aSeric s->tx->evp.sender.user[0] == '\0' ? "" : "@", 9320a80c58aSeric s->tx->evp.sender.domain, 933ee3e6bebSeric rcpt->maddr.user, 934ee3e6bebSeric rcpt->maddr.user[0] == '\0' ? "" : "@", 935c6b5e508Sgilles rcpt->maddr.domain); 936ee3e6bebSeric } 9370490754bSeric smtp_tx_free(s->tx); 93865c4fdfbSgilles s->mailcount++; 93965c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 94065c4fdfbSgilles return; 94165c4fdfbSgilles 942aa1d5973Seric case IMSG_SMTP_AUTHENTICATE: 94365c4fdfbSgilles m_msg(&m, imsg); 94465c4fdfbSgilles m_get_id(&m, &reqid); 94565c4fdfbSgilles m_get_int(&m, &success); 94665c4fdfbSgilles m_end(&m); 94765c4fdfbSgilles 94865c4fdfbSgilles s = tree_xpop(&wait_parent_auth, reqid); 94965c4fdfbSgilles strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE); 95065c4fdfbSgilles if (success == LKA_OK) { 9517a93bdaaSgilles log_info("%016"PRIx64" smtp " 952c6b5e508Sgilles "authentication user=%s " 953c6b5e508Sgilles "result=ok", 954c6b5e508Sgilles s->id, user); 95565c4fdfbSgilles s->flags |= SF_AUTHENTICATED; 9566771f06dSgilles smtp_report_link_auth(s, user, "pass"); 9575fe3cdcbSgilles smtp_reply(s, "235 %s Authentication succeeded", 958fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 95965c4fdfbSgilles } 96065c4fdfbSgilles else if (success == LKA_PERMFAIL) { 9617a93bdaaSgilles log_info("%016"PRIx64" smtp " 962c6b5e508Sgilles "authentication user=%s " 963c6b5e508Sgilles "result=permfail", 964c6b5e508Sgilles s->id, user); 9656771f06dSgilles smtp_report_link_auth(s, user, "fail"); 966299c4efeSeric smtp_auth_failure_pause(s); 967299c4efeSeric return; 96865c4fdfbSgilles } 96965c4fdfbSgilles else if (success == LKA_TEMPFAIL) { 9707a93bdaaSgilles log_info("%016"PRIx64" smtp " 971c6b5e508Sgilles "authentication user=%s " 972c6b5e508Sgilles "result=tempfail", 973c6b5e508Sgilles s->id, user); 9746771f06dSgilles smtp_report_link_auth(s, user, "error"); 9755fe3cdcbSgilles smtp_reply(s, "421 %s Temporary failure", 976fe95d8d0Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 97765c4fdfbSgilles } 97865c4fdfbSgilles else 97965c4fdfbSgilles fatalx("bad lka response"); 980299c4efeSeric 98165c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 98265c4fdfbSgilles return; 98365c4fdfbSgilles 9842b1a70ccSgilles case IMSG_FILTER_SMTP_PROTOCOL: 985d7b0dc3bSgilles m_msg(&m, imsg); 986d7b0dc3bSgilles m_get_id(&m, &reqid); 987d7b0dc3bSgilles m_get_int(&m, &filter_response); 988f6b0bf9aSgilles if (filter_response != FILTER_PROCEED && 989f6b0bf9aSgilles filter_response != FILTER_JUNK) 990d7b0dc3bSgilles m_get_string(&m, &filter_param); 991b0d3076dSgilles else 992b0d3076dSgilles filter_param = NULL; 993d7b0dc3bSgilles m_end(&m); 994d7b0dc3bSgilles 995d7b0dc3bSgilles s = tree_xpop(&wait_filters, reqid); 996d7b0dc3bSgilles 997d7b0dc3bSgilles switch (filter_response) { 998d7b0dc3bSgilles case FILTER_REJECT: 999d7b0dc3bSgilles case FILTER_DISCONNECT: 1000d7b0dc3bSgilles if (!valid_smtp_response(filter_param) || 1001d7b0dc3bSgilles (filter_param[0] != '4' && filter_param[0] != '5')) 1002d7b0dc3bSgilles filter_param = "421 Internal server error"; 1003d7b0dc3bSgilles if (!strncmp(filter_param, "421", 3)) 1004d7b0dc3bSgilles filter_response = FILTER_DISCONNECT; 1005d7b0dc3bSgilles 10066771f06dSgilles smtp_report_filter_response(s, s->filter_phase, 1007b0d3076dSgilles filter_response, filter_param); 1008b0d3076dSgilles 1009d7b0dc3bSgilles smtp_reply(s, "%s", filter_param); 1010d7b0dc3bSgilles 1011d7b0dc3bSgilles if (filter_response == FILTER_DISCONNECT) 1012d7b0dc3bSgilles smtp_enter_state(s, STATE_QUIT); 1013c8094670Sgilles else if (s->filter_phase == FILTER_COMMIT) 1014c8094670Sgilles smtp_proceed_rollback(s, NULL); 1015d7b0dc3bSgilles break; 1016d7b0dc3bSgilles 1017f6b0bf9aSgilles 1018f6b0bf9aSgilles case FILTER_JUNK: 1019f6b0bf9aSgilles if (s->tx) 1020f6b0bf9aSgilles s->tx->junk = 1; 1021f6b0bf9aSgilles else 1022f6b0bf9aSgilles s->junk = 1; 1023f6b0bf9aSgilles /* fallthrough */ 1024f6b0bf9aSgilles 1025d7b0dc3bSgilles case FILTER_PROCEED: 1026522448a1Sgilles filter_param = s->filter_param; 1027522448a1Sgilles /* fallthrough */ 1028522448a1Sgilles 1029*d49c07c7Sop case FILTER_REPORT: 1030d7b0dc3bSgilles case FILTER_REWRITE: 10316771f06dSgilles smtp_report_filter_response(s, s->filter_phase, 1032b0d3076dSgilles filter_response, 1033b0d3076dSgilles filter_param == s->filter_param ? NULL : filter_param); 1034ec69ed85Sgilles if (s->filter_phase == FILTER_CONNECT) { 1035d7b0dc3bSgilles smtp_proceed_connected(s); 1036d7b0dc3bSgilles return; 1037d7b0dc3bSgilles } 1038d7b0dc3bSgilles for (i = 0; i < nitems(commands); ++i) 1039522448a1Sgilles if (commands[i].filter_phase == s->filter_phase) { 1040d7b0dc3bSgilles if (filter_response == FILTER_REWRITE) 1041d7b0dc3bSgilles if (!commands[i].check(s, filter_param)) 1042d7b0dc3bSgilles break; 1043d7b0dc3bSgilles commands[i].proceed(s, filter_param); 1044d7b0dc3bSgilles break; 1045d7b0dc3bSgilles } 1046d7b0dc3bSgilles break; 1047d7b0dc3bSgilles } 1048d7b0dc3bSgilles return; 10491ddc3febSeric } 10501ddc3febSeric 10511ddc3febSeric log_warnx("smtp_session_imsg: unexpected %s imsg", 10521ddc3febSeric imsg_to_str(imsg->hdr.type)); 10531ddc3febSeric fatalx(NULL); 10541ddc3febSeric } 10551ddc3febSeric 10561ddc3febSeric static void 1057eed85469Seric smtp_tls_init(struct smtp_session *s) 10581ddc3febSeric { 1059eed85469Seric io_set_read(s->io); 1060c11a901bSeric if (io_accept_tls(s->io, s->listener->tls) == -1) { 1061c11a901bSeric log_info("%016"PRIx64" smtp disconnected " 1062c11a901bSeric "reason=tls-accept-failed", 1063c11a901bSeric s->id); 1064c11a901bSeric smtp_free(s, "accept failed"); 1065c11a901bSeric } 1066eed85469Seric } 10674b9ec75fSeric 1068eed85469Seric static void 1069eed85469Seric smtp_tls_started(struct smtp_session *s) 1070eed85469Seric { 1071eed85469Seric if (tls_peer_cert_provided(io_tls(s->io))) { 10724b9ec75fSeric log_info("%016"PRIx64" smtp " 1073eed85469Seric "cert-check result=\"%s\" fingerprint=\"%s\"", 1074c6b5e508Sgilles s->id, 1075eed85469Seric (s->flags & SF_VERIFIED) ? "verified" : "unchecked", 1076eed85469Seric tls_peer_cert_hash(io_tls(s->io))); 1077eed85469Seric } 10784b9ec75fSeric 10794b9ec75fSeric if (s->listener->flags & F_SMTPS) { 10804b9ec75fSeric stat_increment("smtp.smtps", 1); 10818d3f7f0dSeric io_set_write(s->io); 10824b9ec75fSeric smtp_send_banner(s); 10834b9ec75fSeric } 10844b9ec75fSeric else { 10854b9ec75fSeric stat_increment("smtp.tls", 1); 10864b9ec75fSeric smtp_enter_state(s, STATE_HELO); 10874b9ec75fSeric } 108865c4fdfbSgilles } 108965c4fdfbSgilles 109065c4fdfbSgilles static void 1091b556a8d3Seric smtp_io(struct io *io, int evt, void *arg) 109265c4fdfbSgilles { 1093b556a8d3Seric struct smtp_session *s = arg; 109465c4fdfbSgilles char *line; 1095b251880aSgilles size_t len; 10965bfc3fefSeric int eom; 109765c4fdfbSgilles 109865c4fdfbSgilles log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt), 109965c4fdfbSgilles io_strio(io)); 110065c4fdfbSgilles 110165c4fdfbSgilles switch (evt) { 110265c4fdfbSgilles 110365c4fdfbSgilles case IO_TLSREADY: 1104c6b5e508Sgilles log_info("%016"PRIx64" smtp tls ciphers=%s", 1105eed85469Seric s->id, tls_to_text(io_tls(s->io))); 110665c4fdfbSgilles 1107eed85469Seric smtp_report_link_tls(s, tls_to_text(io_tls(s->io))); 1108ff05ba49Sgilles 110965c4fdfbSgilles s->flags |= SF_SECURE; 1110eed85469Seric if (s->listener->flags & F_TLS_VERIFY) 1111eed85469Seric s->flags |= SF_VERIFIED; 1112b937aab1Seric s->helo[0] = '\0'; 111365c4fdfbSgilles 1114eed85469Seric smtp_tls_started(s); 11151ddc3febSeric break; 111665c4fdfbSgilles 111765c4fdfbSgilles case IO_DATAIN: 111865c4fdfbSgilles nextline: 11198d3f7f0dSeric line = io_getline(s->io, &len); 11206162865dSeric if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) || 11216162865dSeric (line && len >= SMTP_LINE_MAX)) { 1122299c4efeSeric s->flags |= SF_BADINPUT; 11235fe3cdcbSgilles smtp_reply(s, "500 %s Line too long", 1124fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); 112565c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 112665c4fdfbSgilles io_set_write(io); 112765c4fdfbSgilles return; 112865c4fdfbSgilles } 112965c4fdfbSgilles 1130df8e24afSkili /* No complete line received */ 1131df8e24afSkili if (line == NULL) 1132df8e24afSkili return; 1133df8e24afSkili 11340124f38dSeric /* Strip trailing '\r' */ 11350124f38dSeric if (len && line[len - 1] == '\r') 11360124f38dSeric line[--len] = '\0'; 11370124f38dSeric 113865c4fdfbSgilles /* Message body */ 11395bfc3fefSeric eom = 0; 11405bfc3fefSeric if (s->state == STATE_BODY) { 11413cf20826Sgilles if (strcmp(line, ".")) { 11423cf20826Sgilles s->tx->datain += strlen(line) + 1; 11433cf20826Sgilles if (s->tx->datain > env->sc_maxsize) 11443cf20826Sgilles s->tx->error = TX_ERROR_SIZE; 11453cf20826Sgilles } 1146590a6142Sgilles eom = (s->tx->filter == NULL) ? 1147590a6142Sgilles smtp_tx_dataline(s->tx, line) : 1148590a6142Sgilles smtp_tx_filtered_dataline(s->tx, line); 11495bfc3fefSeric if (eom == 0) 115065c4fdfbSgilles goto nextline; 115165c4fdfbSgilles } 115265c4fdfbSgilles 115365c4fdfbSgilles /* Pipelining not supported */ 11548d3f7f0dSeric if (io_datalen(s->io)) { 1155299c4efeSeric s->flags |= SF_BADINPUT; 1156fe95d8d0Seric smtp_reply(s, "500 %s %s: Pipelining not supported", 1157fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1158fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 115965c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 116065c4fdfbSgilles io_set_write(io); 116165c4fdfbSgilles return; 116265c4fdfbSgilles } 116365c4fdfbSgilles 11645bfc3fefSeric if (eom) { 11655bfc3fefSeric io_set_write(io); 1166590a6142Sgilles if (s->tx->filter == NULL) 1167590a6142Sgilles smtp_tx_eom(s->tx); 116865c4fdfbSgilles return; 116965c4fdfbSgilles } 117065c4fdfbSgilles 117165c4fdfbSgilles /* Must be a command */ 11726162865dSeric if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) { 11736162865dSeric s->flags |= SF_BADINPUT; 11745fe3cdcbSgilles smtp_reply(s, "500 %s Command line too long", 11756162865dSeric esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS)); 11766162865dSeric smtp_enter_state(s, STATE_QUIT); 11776162865dSeric io_set_write(io); 11786162865dSeric return; 11796162865dSeric } 118065c4fdfbSgilles io_set_write(io); 118165c4fdfbSgilles smtp_command(s, line); 118265c4fdfbSgilles break; 118365c4fdfbSgilles 118465c4fdfbSgilles case IO_LOWAT: 118565c4fdfbSgilles if (s->state == STATE_QUIT) { 1186c6b5e508Sgilles log_info("%016"PRIx64" smtp disconnected " 1187a206140dSgiovanni "reason=quit", 1188c6b5e508Sgilles s->id); 118965c4fdfbSgilles smtp_free(s, "done"); 119065c4fdfbSgilles break; 119165c4fdfbSgilles } 119265c4fdfbSgilles 119365c4fdfbSgilles /* Wait for the client to start tls */ 119465c4fdfbSgilles if (s->state == STATE_TLS) { 1195eed85469Seric smtp_tls_init(s); 119665c4fdfbSgilles break; 119765c4fdfbSgilles } 119865c4fdfbSgilles 119965c4fdfbSgilles io_set_read(io); 120065c4fdfbSgilles break; 120165c4fdfbSgilles 120265c4fdfbSgilles case IO_TIMEOUT: 1203c6b5e508Sgilles log_info("%016"PRIx64" smtp disconnected " 1204a206140dSgiovanni "reason=timeout", 1205c6b5e508Sgilles s->id); 12066771f06dSgilles smtp_report_timeout(s); 120765c4fdfbSgilles smtp_free(s, "timeout"); 120865c4fdfbSgilles break; 120965c4fdfbSgilles 121065c4fdfbSgilles case IO_DISCONNECTED: 1211c6b5e508Sgilles log_info("%016"PRIx64" smtp disconnected " 1212a206140dSgiovanni "reason=disconnect", 1213c6b5e508Sgilles s->id); 121465c4fdfbSgilles smtp_free(s, "disconnected"); 121565c4fdfbSgilles break; 121665c4fdfbSgilles 121765c4fdfbSgilles case IO_ERROR: 1218c6b5e508Sgilles log_info("%016"PRIx64" smtp disconnected " 1219a206140dSgiovanni "reason=\"io-error: %s\"", 1220c6b5e508Sgilles s->id, io_error(io)); 122165c4fdfbSgilles smtp_free(s, "IO error"); 122265c4fdfbSgilles break; 122365c4fdfbSgilles 122465c4fdfbSgilles default: 122565c4fdfbSgilles fatalx("smtp_io()"); 122665c4fdfbSgilles } 122765c4fdfbSgilles } 122865c4fdfbSgilles 122965c4fdfbSgilles static void 123065c4fdfbSgilles smtp_command(struct smtp_session *s, char *line) 123165c4fdfbSgilles { 1232197a4a5eSgilles char *args; 123365c4fdfbSgilles int cmd, i; 123465c4fdfbSgilles 123565c4fdfbSgilles log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line); 123665c4fdfbSgilles 123765c4fdfbSgilles /* 123865c4fdfbSgilles * These states are special. 123965c4fdfbSgilles */ 124065c4fdfbSgilles if (s->state == STATE_AUTH_INIT) { 12416771f06dSgilles smtp_report_protocol_client(s, "********"); 124265c4fdfbSgilles smtp_rfc4954_auth_plain(s, line); 124365c4fdfbSgilles return; 124465c4fdfbSgilles } 124565c4fdfbSgilles if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) { 12466771f06dSgilles smtp_report_protocol_client(s, "********"); 124765c4fdfbSgilles smtp_rfc4954_auth_login(s, line); 124865c4fdfbSgilles return; 124965c4fdfbSgilles } 125065c4fdfbSgilles 12513917e8c7Sgilles if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0) 12526771f06dSgilles smtp_report_protocol_client(s, "AUTH PLAIN ********"); 12533917e8c7Sgilles else 12546771f06dSgilles smtp_report_protocol_client(s, line); 12553f8dc4feSgilles 12563917e8c7Sgilles 125765c4fdfbSgilles /* 125865c4fdfbSgilles * Unlike other commands, "mail from" and "rcpt to" contain a 125965c4fdfbSgilles * space in the command name. 126065c4fdfbSgilles */ 126165c4fdfbSgilles if (strncasecmp("mail from:", line, 10) == 0 || 126265c4fdfbSgilles strncasecmp("rcpt to:", line, 8) == 0) 126365c4fdfbSgilles args = strchr(line, ':'); 126465c4fdfbSgilles else 126565c4fdfbSgilles args = strchr(line, ' '); 126665c4fdfbSgilles 126765c4fdfbSgilles if (args) { 126865c4fdfbSgilles *args++ = '\0'; 1269fc3a8311Seric while (isspace((unsigned char)*args)) 127065c4fdfbSgilles args++; 127165c4fdfbSgilles } 127265c4fdfbSgilles 127365c4fdfbSgilles cmd = -1; 127465c4fdfbSgilles for (i = 0; commands[i].code != -1; i++) 127565c4fdfbSgilles if (!strcasecmp(line, commands[i].cmd)) { 127665c4fdfbSgilles cmd = commands[i].code; 127765c4fdfbSgilles break; 127865c4fdfbSgilles } 127965c4fdfbSgilles 12807929fb13Sgilles s->last_cmd = cmd; 128165c4fdfbSgilles switch (cmd) { 128265c4fdfbSgilles /* 128365c4fdfbSgilles * INIT 128465c4fdfbSgilles */ 128565c4fdfbSgilles case CMD_HELO: 1286d7b0dc3bSgilles if (!smtp_check_helo(s, args)) 1287d7b0dc3bSgilles break; 1288d7b0dc3bSgilles smtp_filter_phase(FILTER_HELO, s, args); 1289d7b0dc3bSgilles break; 1290d7b0dc3bSgilles 129165c4fdfbSgilles case CMD_EHLO: 1292d7b0dc3bSgilles if (!smtp_check_ehlo(s, args)) 1293197a4a5eSgilles break; 1294d7b0dc3bSgilles smtp_filter_phase(FILTER_EHLO, s, args); 1295197a4a5eSgilles break; 1296d7b0dc3bSgilles 1297197a4a5eSgilles /* 1298197a4a5eSgilles * SETUP 1299197a4a5eSgilles */ 1300197a4a5eSgilles case CMD_STARTTLS: 1301197a4a5eSgilles if (!smtp_check_starttls(s, args)) 1302197a4a5eSgilles break; 1303d7b0dc3bSgilles 1304d7b0dc3bSgilles smtp_filter_phase(FILTER_STARTTLS, s, NULL); 1305197a4a5eSgilles break; 1306197a4a5eSgilles 1307197a4a5eSgilles case CMD_AUTH: 1308197a4a5eSgilles if (!smtp_check_auth(s, args)) 1309197a4a5eSgilles break; 1310522448a1Sgilles smtp_filter_phase(FILTER_AUTH, s, args); 1311197a4a5eSgilles break; 1312197a4a5eSgilles 1313197a4a5eSgilles case CMD_MAIL_FROM: 1314d7b0dc3bSgilles if (!smtp_check_mail_from(s, args)) 1315197a4a5eSgilles break; 1316d7b0dc3bSgilles smtp_filter_phase(FILTER_MAIL_FROM, s, args); 1317197a4a5eSgilles break; 1318197a4a5eSgilles 1319197a4a5eSgilles /* 1320197a4a5eSgilles * TRANSACTION 1321197a4a5eSgilles */ 1322197a4a5eSgilles case CMD_RCPT_TO: 1323d7b0dc3bSgilles if (!smtp_check_rcpt_to(s, args)) 1324197a4a5eSgilles break; 1325d7b0dc3bSgilles smtp_filter_phase(FILTER_RCPT_TO, s, args); 1326197a4a5eSgilles break; 1327197a4a5eSgilles 1328197a4a5eSgilles case CMD_RSET: 1329d7b0dc3bSgilles if (!smtp_check_rset(s, args)) 1330197a4a5eSgilles break; 1331d7b0dc3bSgilles smtp_filter_phase(FILTER_RSET, s, NULL); 1332197a4a5eSgilles break; 1333197a4a5eSgilles 1334197a4a5eSgilles case CMD_DATA: 1335d7b0dc3bSgilles if (!smtp_check_data(s, args)) 1336197a4a5eSgilles break; 1337d7b0dc3bSgilles smtp_filter_phase(FILTER_DATA, s, NULL); 1338197a4a5eSgilles break; 1339197a4a5eSgilles 1340197a4a5eSgilles /* 1341197a4a5eSgilles * ANY 1342197a4a5eSgilles */ 1343197a4a5eSgilles case CMD_QUIT: 1344a6b09462Sgilles if (!smtp_check_noparam(s, args)) 1345a6b09462Sgilles break; 1346d7b0dc3bSgilles smtp_filter_phase(FILTER_QUIT, s, NULL); 1347197a4a5eSgilles break; 1348197a4a5eSgilles 1349197a4a5eSgilles case CMD_NOOP: 1350fbe04ea1Sop if (!smtp_check_noop(s, args)) 1351a6b09462Sgilles break; 1352d7b0dc3bSgilles smtp_filter_phase(FILTER_NOOP, s, NULL); 1353197a4a5eSgilles break; 1354197a4a5eSgilles 1355197a4a5eSgilles case CMD_HELP: 1356a6b09462Sgilles if (!smtp_check_noparam(s, args)) 1357a6b09462Sgilles break; 1358d7b0dc3bSgilles smtp_proceed_help(s, NULL); 1359197a4a5eSgilles break; 1360197a4a5eSgilles 1361197a4a5eSgilles case CMD_WIZ: 1362a6b09462Sgilles if (!smtp_check_noparam(s, args)) 1363a6b09462Sgilles break; 1364d7b0dc3bSgilles smtp_proceed_wiz(s, NULL); 1365197a4a5eSgilles break; 1366197a4a5eSgilles 1367197a4a5eSgilles default: 1368197a4a5eSgilles smtp_reply(s, "500 %s %s: Command unrecognized", 1369197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1370197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1371197a4a5eSgilles break; 1372197a4a5eSgilles } 1373197a4a5eSgilles } 1374197a4a5eSgilles 1375197a4a5eSgilles static int 1376d7b0dc3bSgilles smtp_check_rset(struct smtp_session *s, const char *args) 1377197a4a5eSgilles { 1378a6b09462Sgilles if (!smtp_check_noparam(s, args)) 1379a6b09462Sgilles return 0; 1380a6b09462Sgilles 1381197a4a5eSgilles if (s->helo[0] == '\0') { 1382197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1383197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1384197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1385197a4a5eSgilles return 0; 1386197a4a5eSgilles } 1387197a4a5eSgilles return 1; 1388197a4a5eSgilles } 1389197a4a5eSgilles 1390197a4a5eSgilles static int 1391d7b0dc3bSgilles smtp_check_helo(struct smtp_session *s, const char *args) 1392197a4a5eSgilles { 1393d7b0dc3bSgilles if (!s->banner_sent) { 1394d7b0dc3bSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1395d7b0dc3bSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1396d7b0dc3bSgilles esc_description(ESC_INVALID_COMMAND)); 1397d7b0dc3bSgilles return 0; 1398d7b0dc3bSgilles } 1399d7b0dc3bSgilles 1400b937aab1Seric if (s->helo[0]) { 1401af0dba2aSsobrado smtp_reply(s, "503 %s %s: Already identified", 1402fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1403fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 1404197a4a5eSgilles return 0; 1405b8ff4170Sgilles } 1406b8ff4170Sgilles 14073ef9cbf7Sgilles if (args == NULL) { 1408d7b0dc3bSgilles smtp_reply(s, "501 %s %s: HELO requires domain name", 1409fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1410d7b0dc3bSgilles esc_description(ESC_INVALID_COMMAND)); 1411d7b0dc3bSgilles return 0; 1412d7b0dc3bSgilles } 1413d7b0dc3bSgilles 1414d7b0dc3bSgilles if (!valid_domainpart(args)) { 1415d7b0dc3bSgilles smtp_reply(s, "501 %s %s: Invalid domain name", 1416d7b0dc3bSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1417d7b0dc3bSgilles esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 1418d7b0dc3bSgilles return 0; 1419d7b0dc3bSgilles } 1420d7b0dc3bSgilles 1421d7b0dc3bSgilles return 1; 1422d7b0dc3bSgilles } 1423d7b0dc3bSgilles 1424d7b0dc3bSgilles static int 1425d7b0dc3bSgilles smtp_check_ehlo(struct smtp_session *s, const char *args) 1426d7b0dc3bSgilles { 1427d7b0dc3bSgilles if (!s->banner_sent) { 1428d7b0dc3bSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1429d7b0dc3bSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1430d7b0dc3bSgilles esc_description(ESC_INVALID_COMMAND)); 1431d7b0dc3bSgilles return 0; 1432d7b0dc3bSgilles } 1433d7b0dc3bSgilles 1434d7b0dc3bSgilles if (s->helo[0]) { 1435d7b0dc3bSgilles smtp_reply(s, "503 %s %s: Already identified", 1436d7b0dc3bSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1437d7b0dc3bSgilles esc_description(ESC_INVALID_COMMAND)); 1438d7b0dc3bSgilles return 0; 1439d7b0dc3bSgilles } 1440d7b0dc3bSgilles 1441d7b0dc3bSgilles if (args == NULL) { 1442d7b0dc3bSgilles smtp_reply(s, "501 %s %s: EHLO requires domain name", 1443d7b0dc3bSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1444d7b0dc3bSgilles esc_description(ESC_INVALID_COMMAND)); 1445197a4a5eSgilles return 0; 144665c4fdfbSgilles } 144765c4fdfbSgilles 144865c4fdfbSgilles if (!valid_domainpart(args)) { 1449fe95d8d0Seric smtp_reply(s, "501 %s %s: Invalid domain name", 1450fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1451fe95d8d0Seric esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 1452197a4a5eSgilles return 0; 145365c4fdfbSgilles } 1454197a4a5eSgilles 1455197a4a5eSgilles return 1; 1456197a4a5eSgilles } 1457197a4a5eSgilles 1458197a4a5eSgilles static int 1459197a4a5eSgilles smtp_check_auth(struct smtp_session *s, const char *args) 1460197a4a5eSgilles { 1461197a4a5eSgilles if (s->helo[0] == '\0' || s->tx) { 1462197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1463197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1464197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1465197a4a5eSgilles return 0; 1466197a4a5eSgilles } 1467197a4a5eSgilles 1468197a4a5eSgilles if (s->flags & SF_AUTHENTICATED) { 1469197a4a5eSgilles smtp_reply(s, "503 %s %s: Already authenticated", 1470197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1471197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1472197a4a5eSgilles return 0; 1473197a4a5eSgilles } 1474197a4a5eSgilles 1475197a4a5eSgilles if (!ADVERTISE_AUTH(s)) { 1476197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not supported", 1477197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1478197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1479197a4a5eSgilles return 0; 1480197a4a5eSgilles } 1481197a4a5eSgilles 1482197a4a5eSgilles if (args == NULL) { 1483197a4a5eSgilles smtp_reply(s, "501 %s %s: No parameters given", 1484197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1485197a4a5eSgilles esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 1486197a4a5eSgilles return 0; 1487197a4a5eSgilles } 1488197a4a5eSgilles 1489197a4a5eSgilles return 1; 1490197a4a5eSgilles } 1491197a4a5eSgilles 1492197a4a5eSgilles static int 1493197a4a5eSgilles smtp_check_starttls(struct smtp_session *s, const char *args) 1494197a4a5eSgilles { 1495197a4a5eSgilles if (s->helo[0] == '\0' || s->tx) { 1496197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1497197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1498197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1499197a4a5eSgilles return 0; 1500197a4a5eSgilles } 1501197a4a5eSgilles 1502197a4a5eSgilles if (!(s->listener->flags & F_STARTTLS)) { 1503197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not supported", 1504197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1505197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1506197a4a5eSgilles return 0; 1507197a4a5eSgilles } 1508197a4a5eSgilles 1509197a4a5eSgilles if (s->flags & SF_SECURE) { 1510197a4a5eSgilles smtp_reply(s, "503 %s %s: Channel already secured", 1511197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1512197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1513197a4a5eSgilles return 0; 1514197a4a5eSgilles } 1515197a4a5eSgilles 1516197a4a5eSgilles if (args != NULL) { 1517197a4a5eSgilles smtp_reply(s, "501 %s %s: No parameters allowed", 1518197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1519197a4a5eSgilles esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 1520197a4a5eSgilles return 0; 1521197a4a5eSgilles } 1522197a4a5eSgilles 1523197a4a5eSgilles return 1; 1524197a4a5eSgilles } 1525197a4a5eSgilles 1526197a4a5eSgilles static int 1527d7b0dc3bSgilles smtp_check_mail_from(struct smtp_session *s, const char *args) 1528197a4a5eSgilles { 1529a9213e5fSgilles char *copy; 1530a9213e5fSgilles char tmp[SMTP_LINE_MAX]; 153103ee6fe4Sgilles struct mailaddr sender; 1532a9213e5fSgilles 1533a9213e5fSgilles (void)strlcpy(tmp, args, sizeof tmp); 1534a9213e5fSgilles copy = tmp; 1535a9213e5fSgilles 1536197a4a5eSgilles if (s->helo[0] == '\0' || s->tx) { 1537197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1538197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1539197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1540197a4a5eSgilles return 0; 1541197a4a5eSgilles } 1542197a4a5eSgilles 1543197a4a5eSgilles if (s->listener->flags & F_STARTTLS_REQUIRE && 1544197a4a5eSgilles !(s->flags & SF_SECURE)) { 1545197a4a5eSgilles smtp_reply(s, 1546197a4a5eSgilles "530 %s %s: Must issue a STARTTLS command first", 1547197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1548197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1549197a4a5eSgilles return 0; 1550197a4a5eSgilles } 1551197a4a5eSgilles 1552197a4a5eSgilles if (s->listener->flags & F_AUTH_REQUIRE && 1553197a4a5eSgilles !(s->flags & SF_AUTHENTICATED)) { 1554197a4a5eSgilles smtp_reply(s, 1555197a4a5eSgilles "530 %s %s: Must issue an AUTH command first", 1556197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1557197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1558197a4a5eSgilles return 0; 1559197a4a5eSgilles } 1560197a4a5eSgilles 1561197a4a5eSgilles if (s->mailcount >= env->sc_session_max_mails) { 1562197a4a5eSgilles /* we can pretend we had too many recipients */ 1563197a4a5eSgilles smtp_reply(s, "452 %s %s: Too many messages sent", 1564197a4a5eSgilles esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), 1565197a4a5eSgilles esc_description(ESC_TOO_MANY_RECIPIENTS)); 1566197a4a5eSgilles return 0; 1567197a4a5eSgilles } 1568197a4a5eSgilles 156903ee6fe4Sgilles if (smtp_mailaddr(&sender, copy, 1, ©, 157003ee6fe4Sgilles s->smtpname) == 0) { 15715fe3cdcbSgilles smtp_reply(s, "553 %s Sender address syntax error", 1572a9213e5fSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); 1573a9213e5fSgilles return 0; 1574a9213e5fSgilles } 1575a9213e5fSgilles 1576197a4a5eSgilles return 1; 1577197a4a5eSgilles } 1578197a4a5eSgilles 1579197a4a5eSgilles static int 1580d7b0dc3bSgilles smtp_check_rcpt_to(struct smtp_session *s, const char *args) 1581197a4a5eSgilles { 1582a9213e5fSgilles char *copy; 1583a9213e5fSgilles char tmp[SMTP_LINE_MAX]; 1584a9213e5fSgilles 1585a9213e5fSgilles (void)strlcpy(tmp, args, sizeof tmp); 1586a9213e5fSgilles copy = tmp; 1587a9213e5fSgilles 1588197a4a5eSgilles if (s->tx == NULL) { 1589197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1590197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1591197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1592197a4a5eSgilles return 0; 1593197a4a5eSgilles } 1594197a4a5eSgilles 1595a9213e5fSgilles if (s->tx->rcptcount >= env->sc_session_max_rcpt) { 1596a9213e5fSgilles smtp_reply(s->tx->session, "451 %s %s: Too many recipients", 1597a9213e5fSgilles esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), 1598a9213e5fSgilles esc_description(ESC_TOO_MANY_RECIPIENTS)); 1599a9213e5fSgilles return 0; 1600a9213e5fSgilles } 1601a9213e5fSgilles 1602a9213e5fSgilles if (smtp_mailaddr(&s->tx->evp.rcpt, copy, 0, ©, 1603a9213e5fSgilles s->tx->session->smtpname) == 0) { 1604a9213e5fSgilles smtp_reply(s->tx->session, 16055fe3cdcbSgilles "501 %s Recipient address syntax error", 1606a9213e5fSgilles esc_code(ESC_STATUS_PERMFAIL, 1607a9213e5fSgilles ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX)); 1608a9213e5fSgilles return 0; 1609a9213e5fSgilles } 1610a9213e5fSgilles 1611197a4a5eSgilles return 1; 1612197a4a5eSgilles } 1613197a4a5eSgilles 1614197a4a5eSgilles static int 1615d7b0dc3bSgilles smtp_check_data(struct smtp_session *s, const char *args) 1616197a4a5eSgilles { 1617a6b09462Sgilles if (!smtp_check_noparam(s, args)) 1618a6b09462Sgilles return 0; 1619a6b09462Sgilles 1620197a4a5eSgilles if (s->tx == NULL) { 1621197a4a5eSgilles smtp_reply(s, "503 %s %s: Command not allowed at this point.", 1622197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1623197a4a5eSgilles esc_description(ESC_INVALID_COMMAND)); 1624197a4a5eSgilles return 0; 1625197a4a5eSgilles } 1626197a4a5eSgilles 1627197a4a5eSgilles if (s->tx->rcptcount == 0) { 1628197a4a5eSgilles smtp_reply(s, "503 %s %s: No recipient specified", 1629197a4a5eSgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1630197a4a5eSgilles esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 1631197a4a5eSgilles return 0; 1632197a4a5eSgilles } 1633197a4a5eSgilles 1634197a4a5eSgilles return 1; 1635197a4a5eSgilles } 1636197a4a5eSgilles 1637d7b0dc3bSgilles static int 1638fbe04ea1Sop smtp_check_noop(struct smtp_session *s, const char *args) 1639fbe04ea1Sop { 1640fbe04ea1Sop return 1; 1641fbe04ea1Sop } 1642fbe04ea1Sop 1643fbe04ea1Sop static int 1644d7b0dc3bSgilles smtp_check_noparam(struct smtp_session *s, const char *args) 1645d7b0dc3bSgilles { 1646a6b09462Sgilles if (args != NULL) { 1647a6b09462Sgilles smtp_reply(s, "500 %s %s: command does not accept arguments.", 1648a6b09462Sgilles esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 1649a6b09462Sgilles esc_description(ESC_INVALID_COMMAND_ARGUMENTS)); 1650a6b09462Sgilles return 0; 1651a6b09462Sgilles } 1652d7b0dc3bSgilles return 1; 1653d7b0dc3bSgilles } 1654197a4a5eSgilles 1655197a4a5eSgilles static void 1656d7b0dc3bSgilles smtp_query_filters(enum filter_phase phase, struct smtp_session *s, const char *args) 1657d7b0dc3bSgilles { 16582b1a70ccSgilles m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 1659d7b0dc3bSgilles m_add_id(p_lka, s->id); 1660d7b0dc3bSgilles m_add_int(p_lka, phase); 1661d7b0dc3bSgilles m_add_string(p_lka, args); 1662d7b0dc3bSgilles m_close(p_lka); 1663d7b0dc3bSgilles tree_xset(&wait_filters, s->id, s); 1664d7b0dc3bSgilles } 1665d7b0dc3bSgilles 1666d7b0dc3bSgilles static void 1667590a6142Sgilles smtp_filter_begin(struct smtp_session *s) 1668590a6142Sgilles { 16694faa8b13Sgilles if (!SESSION_FILTERED(s)) 1670590a6142Sgilles return; 1671590a6142Sgilles 16722b1a70ccSgilles m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1); 1673590a6142Sgilles m_add_id(p_lka, s->id); 1674ec69ed85Sgilles m_add_string(p_lka, s->listener->filter_name); 1675590a6142Sgilles m_close(p_lka); 1676590a6142Sgilles } 1677590a6142Sgilles 1678590a6142Sgilles static void 1679590a6142Sgilles smtp_filter_end(struct smtp_session *s) 1680590a6142Sgilles { 16814faa8b13Sgilles if (!SESSION_FILTERED(s)) 1682590a6142Sgilles return; 1683590a6142Sgilles 16842b1a70ccSgilles m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1); 1685590a6142Sgilles m_add_id(p_lka, s->id); 1686590a6142Sgilles m_close(p_lka); 1687590a6142Sgilles } 1688590a6142Sgilles 1689590a6142Sgilles static void 1690590a6142Sgilles smtp_filter_data_begin(struct smtp_session *s) 1691590a6142Sgilles { 16924faa8b13Sgilles if (!SESSION_FILTERED(s)) 1693590a6142Sgilles return; 1694590a6142Sgilles 16952b1a70ccSgilles m_create(p_lka, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, -1); 1696590a6142Sgilles m_add_id(p_lka, s->id); 1697590a6142Sgilles m_close(p_lka); 1698590a6142Sgilles tree_xset(&wait_filter_fd, s->id, s); 1699590a6142Sgilles } 1700590a6142Sgilles 1701590a6142Sgilles static void 1702590a6142Sgilles smtp_filter_data_end(struct smtp_session *s) 1703590a6142Sgilles { 17044faa8b13Sgilles if (!SESSION_FILTERED(s)) 1705590a6142Sgilles return; 1706590a6142Sgilles 1707590a6142Sgilles if (s->tx->filter == NULL) 1708590a6142Sgilles return; 1709590a6142Sgilles 1710590a6142Sgilles io_free(s->tx->filter); 1711590a6142Sgilles s->tx->filter = NULL; 1712590a6142Sgilles 17132b1a70ccSgilles m_create(p_lka, IMSG_FILTER_SMTP_DATA_END, 0, 0, -1); 1714590a6142Sgilles m_add_id(p_lka, s->id); 1715590a6142Sgilles m_close(p_lka); 1716590a6142Sgilles } 1717590a6142Sgilles 1718590a6142Sgilles static void 1719d7b0dc3bSgilles smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *param) 1720d7b0dc3bSgilles { 1721486285b1Sgilles uint8_t i; 1722486285b1Sgilles 1723cfb718b6Sgilles s->filter_phase = phase; 1724cfb718b6Sgilles s->filter_param = param; 1725cfb718b6Sgilles 1726373949c1Sgilles if (SESSION_FILTERED(s)) { 1727d7b0dc3bSgilles smtp_query_filters(phase, s, param ? param : ""); 1728486285b1Sgilles return; 1729486285b1Sgilles } 1730486285b1Sgilles 1731ec69ed85Sgilles if (s->filter_phase == FILTER_CONNECT) { 1732486285b1Sgilles smtp_proceed_connected(s); 1733486285b1Sgilles return; 1734486285b1Sgilles } 1735373949c1Sgilles 1736486285b1Sgilles for (i = 0; i < nitems(commands); ++i) 1737486285b1Sgilles if (commands[i].filter_phase == s->filter_phase) { 1738486285b1Sgilles commands[i].proceed(s, param); 1739486285b1Sgilles break; 1740486285b1Sgilles } 1741d7b0dc3bSgilles } 1742d7b0dc3bSgilles 1743d7b0dc3bSgilles static void 1744d7b0dc3bSgilles smtp_proceed_rset(struct smtp_session *s, const char *args) 1745197a4a5eSgilles { 1746aebb80a1Sgilles smtp_reply(s, "250 %s Reset state", 1747aebb80a1Sgilles esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 1748aebb80a1Sgilles 1749197a4a5eSgilles if (s->tx) { 1750197a4a5eSgilles if (s->tx->msgid) 1751197a4a5eSgilles smtp_tx_rollback(s->tx); 1752197a4a5eSgilles smtp_tx_free(s->tx); 1753197a4a5eSgilles } 1754197a4a5eSgilles } 1755197a4a5eSgilles 1756197a4a5eSgilles static void 1757d7b0dc3bSgilles smtp_proceed_helo(struct smtp_session *s, const char *args) 1758197a4a5eSgilles { 1759c51a7fb7Sgilles (void)strlcpy(s->helo, args, sizeof(s->helo)); 176084639b21Seric s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED; 176165c4fdfbSgilles 17626771f06dSgilles smtp_report_link_identify(s, "HELO", s->helo); 17637ec3fc38Sgilles 1764c679e749Seric smtp_enter_state(s, STATE_HELO); 176591affd4dSgilles 176691affd4dSgilles smtp_reply(s, "250 %s Hello %s %s%s%s, pleased to meet you", 1767d7b0dc3bSgilles s->smtpname, 1768d7b0dc3bSgilles s->helo, 176991affd4dSgilles s->ss.ss_family == AF_INET6 ? "" : "[", 177091affd4dSgilles ss_to_text(&s->ss), 177191affd4dSgilles s->ss.ss_family == AF_INET6 ? "" : "]"); 1772d7b0dc3bSgilles } 1773d7b0dc3bSgilles 1774d7b0dc3bSgilles static void 1775d7b0dc3bSgilles smtp_proceed_ehlo(struct smtp_session *s, const char *args) 1776d7b0dc3bSgilles { 1777d7b0dc3bSgilles (void)strlcpy(s->helo, args, sizeof(s->helo)); 1778d7b0dc3bSgilles s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED; 1779d7b0dc3bSgilles s->flags |= SF_EHLO; 1780d7b0dc3bSgilles s->flags |= SF_8BITMIME; 1781d7b0dc3bSgilles 17826771f06dSgilles smtp_report_link_identify(s, "EHLO", s->helo); 17837ec3fc38Sgilles 1784d7b0dc3bSgilles smtp_enter_state(s, STATE_HELO); 178591affd4dSgilles smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you", 1786c679e749Seric s->smtpname, 1787c679e749Seric s->helo, 178891affd4dSgilles s->ss.ss_family == AF_INET6 ? "" : "[", 178991affd4dSgilles ss_to_text(&s->ss), 179091affd4dSgilles s->ss.ss_family == AF_INET6 ? "" : "]"); 1791c679e749Seric 1792c679e749Seric smtp_reply(s, "250-8BITMIME"); 1793c679e749Seric smtp_reply(s, "250-ENHANCEDSTATUSCODES"); 1794c679e749Seric smtp_reply(s, "250-SIZE %zu", env->sc_maxsize); 1795c679e749Seric if (ADVERTISE_EXT_DSN(s)) 1796c679e749Seric smtp_reply(s, "250-DSN"); 1797c679e749Seric if (ADVERTISE_TLS(s)) 1798c679e749Seric smtp_reply(s, "250-STARTTLS"); 1799c679e749Seric if (ADVERTISE_AUTH(s)) 1800c679e749Seric smtp_reply(s, "250-AUTH PLAIN LOGIN"); 1801c679e749Seric smtp_reply(s, "250 HELP"); 1802c679e749Seric } 180365c4fdfbSgilles 1804197a4a5eSgilles static void 1805d7b0dc3bSgilles smtp_proceed_auth(struct smtp_session *s, const char *args) 1806197a4a5eSgilles { 1807d7b0dc3bSgilles char tmp[SMTP_LINE_MAX]; 1808197a4a5eSgilles char *eom, *method; 18093ef9cbf7Sgilles 1810d7b0dc3bSgilles (void)strlcpy(tmp, args, sizeof tmp); 1811d7b0dc3bSgilles 1812d7b0dc3bSgilles method = tmp; 1813d7b0dc3bSgilles eom = strchr(tmp, ' '); 18143ef9cbf7Sgilles if (eom == NULL) 1815d7b0dc3bSgilles eom = strchr(tmp, '\t'); 18163ef9cbf7Sgilles if (eom != NULL) 18173ef9cbf7Sgilles *eom++ = '\0'; 181885e1f4f6Sgilles if (strcasecmp(method, "PLAIN") == 0) 181965c4fdfbSgilles smtp_rfc4954_auth_plain(s, eom); 182085e1f4f6Sgilles else if (strcasecmp(method, "LOGIN") == 0) 182165c4fdfbSgilles smtp_rfc4954_auth_login(s, eom); 1822e8fa9864Sjacekm else 1823fe95d8d0Seric smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported", 1824fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED), 1825fe95d8d0Seric esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED), 182665c4fdfbSgilles method); 182765c4fdfbSgilles } 182865c4fdfbSgilles 1829197a4a5eSgilles static void 1830d7b0dc3bSgilles smtp_proceed_starttls(struct smtp_session *s, const char *args) 1831197a4a5eSgilles { 18325fe3cdcbSgilles smtp_reply(s, "220 %s Ready to start TLS", 1833fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 1834197a4a5eSgilles smtp_enter_state(s, STATE_TLS); 183565c4fdfbSgilles } 183665c4fdfbSgilles 1837197a4a5eSgilles static void 1838d7b0dc3bSgilles smtp_proceed_mail_from(struct smtp_session *s, const char *args) 1839197a4a5eSgilles { 184003ee6fe4Sgilles char *copy; 184103ee6fe4Sgilles char tmp[SMTP_LINE_MAX]; 184203ee6fe4Sgilles 184303ee6fe4Sgilles (void)strlcpy(tmp, args, sizeof tmp); 184403ee6fe4Sgilles copy = tmp; 184503ee6fe4Sgilles 184603ee6fe4Sgilles if (!smtp_tx(s)) { 184703ee6fe4Sgilles smtp_reply(s, "421 %s Temporary Error", 184803ee6fe4Sgilles esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 184903ee6fe4Sgilles smtp_enter_state(s, STATE_QUIT); 185003ee6fe4Sgilles return; 185103ee6fe4Sgilles } 185203ee6fe4Sgilles 185303ee6fe4Sgilles if (smtp_mailaddr(&s->tx->evp.sender, copy, 1, ©, 185403ee6fe4Sgilles s->smtpname) == 0) { 185503ee6fe4Sgilles smtp_reply(s, "553 %s Sender address syntax error", 185603ee6fe4Sgilles esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); 185703ee6fe4Sgilles smtp_tx_free(s->tx); 185803ee6fe4Sgilles return; 185903ee6fe4Sgilles } 186003ee6fe4Sgilles 1861197a4a5eSgilles smtp_tx_mail_from(s->tx, args); 1862197a4a5eSgilles } 1863197a4a5eSgilles 1864197a4a5eSgilles static void 1865d7b0dc3bSgilles smtp_proceed_rcpt_to(struct smtp_session *s, const char *args) 1866197a4a5eSgilles { 1867197a4a5eSgilles smtp_tx_rcpt_to(s->tx, args); 1868197a4a5eSgilles } 1869197a4a5eSgilles 1870197a4a5eSgilles static void 1871d7b0dc3bSgilles smtp_proceed_data(struct smtp_session *s, const char *args) 1872197a4a5eSgilles { 1873008f3f72Seric smtp_tx_open_message(s->tx); 1874197a4a5eSgilles } 1875197a4a5eSgilles 1876197a4a5eSgilles static void 1877d7b0dc3bSgilles smtp_proceed_quit(struct smtp_session *s, const char *args) 1878197a4a5eSgilles { 18795fe3cdcbSgilles smtp_reply(s, "221 %s Bye", 1880fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 188165c4fdfbSgilles smtp_enter_state(s, STATE_QUIT); 1882197a4a5eSgilles } 188365c4fdfbSgilles 1884197a4a5eSgilles static void 1885d7b0dc3bSgilles smtp_proceed_noop(struct smtp_session *s, const char *args) 1886197a4a5eSgilles { 18875fe3cdcbSgilles smtp_reply(s, "250 %s Ok", 1888fe95d8d0Seric esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); 1889197a4a5eSgilles } 189065c4fdfbSgilles 1891197a4a5eSgilles static void 1892d7b0dc3bSgilles smtp_proceed_help(struct smtp_session *s, const char *args) 1893197a4a5eSgilles { 18945fe3cdcbSgilles const char *code = esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS); 18955fe3cdcbSgilles 18965fe3cdcbSgilles smtp_reply(s, "214-%s This is " SMTPD_NAME, code); 18975fe3cdcbSgilles smtp_reply(s, "214-%s To report bugs in the implementation, " 18985fe3cdcbSgilles "please contact bugs@openbsd.org", code); 18995fe3cdcbSgilles smtp_reply(s, "214-%s with full details", code); 19005fe3cdcbSgilles smtp_reply(s, "214 %s End of HELP info", code); 1901197a4a5eSgilles } 1902fe95d8d0Seric 1903197a4a5eSgilles static void 1904d7b0dc3bSgilles smtp_proceed_wiz(struct smtp_session *s, const char *args) 1905197a4a5eSgilles { 1906fe95d8d0Seric smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)", 1907fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND), 1908fe95d8d0Seric esc_description(ESC_INVALID_COMMAND)); 190985e1f4f6Sgilles } 191085e1f4f6Sgilles 1911be925435Sgilles static void 1912c8094670Sgilles smtp_proceed_commit(struct smtp_session *s, const char *args) 1913c8094670Sgilles { 1914c8094670Sgilles smtp_message_end(s->tx); 1915c8094670Sgilles } 1916c8094670Sgilles 1917c8094670Sgilles static void 1918c8094670Sgilles smtp_proceed_rollback(struct smtp_session *s, const char *args) 1919c8094670Sgilles { 1920c8094670Sgilles struct smtp_tx *tx; 1921c8094670Sgilles 1922c8094670Sgilles tx = s->tx; 1923c8094670Sgilles 1924c8094670Sgilles fclose(tx->ofile); 1925c8094670Sgilles tx->ofile = NULL; 1926c8094670Sgilles 1927c8094670Sgilles smtp_tx_rollback(tx); 1928c8094670Sgilles smtp_tx_free(tx); 1929c8094670Sgilles smtp_enter_state(s, STATE_HELO); 1930c8094670Sgilles } 1931c8094670Sgilles 1932c8094670Sgilles static void 193365c4fdfbSgilles smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg) 193485e1f4f6Sgilles { 1935e8fa9864Sjacekm char buf[1024], *user, *pass; 1936e8fa9864Sjacekm int len; 1937e8fa9864Sjacekm 193865c4fdfbSgilles switch (s->state) { 193965c4fdfbSgilles case STATE_HELO: 194085e1f4f6Sgilles if (arg == NULL) { 194165c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_INIT); 194265c4fdfbSgilles smtp_reply(s, "334 "); 1943e8fa9864Sjacekm return; 194485e1f4f6Sgilles } 194565c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_INIT); 1946e8fa9864Sjacekm /* FALLTHROUGH */ 194785e1f4f6Sgilles 194865c4fdfbSgilles case STATE_AUTH_INIT: 1949e8fa9864Sjacekm /* String is not NUL terminated, leave room. */ 1950254aed36Seric if ((len = base64_decode(arg, (unsigned char *)buf, 19517bdbba2fSeric sizeof(buf) - 1)) == -1) 1952e8fa9864Sjacekm goto abort; 1953e8fa9864Sjacekm /* buf is a byte string, NUL terminate. */ 1954e8fa9864Sjacekm buf[len] = '\0'; 1955e8fa9864Sjacekm 1956e8fa9864Sjacekm /* 1957e8fa9864Sjacekm * Skip "foo" in "foo\0user\0pass", if present. 1958e8fa9864Sjacekm */ 1959e8fa9864Sjacekm user = memchr(buf, '\0', len); 1960e8fa9864Sjacekm if (user == NULL || user >= buf + len - 2) 1961e8fa9864Sjacekm goto abort; 1962e8fa9864Sjacekm user++; /* skip NUL */ 196365c4fdfbSgilles if (strlcpy(s->username, user, sizeof(s->username)) 196465c4fdfbSgilles >= sizeof(s->username)) 1965e8fa9864Sjacekm goto abort; 1966e8fa9864Sjacekm 1967e8fa9864Sjacekm pass = memchr(user, '\0', len - (user - buf)); 1968e8fa9864Sjacekm if (pass == NULL || pass >= buf + len - 2) 1969e8fa9864Sjacekm goto abort; 1970e8fa9864Sjacekm pass++; /* skip NUL */ 19713ef9cbf7Sgilles 1972aa1d5973Seric m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); 197365c4fdfbSgilles m_add_id(p_lka, s->id); 197465c4fdfbSgilles m_add_string(p_lka, s->listener->authtable); 197565c4fdfbSgilles m_add_string(p_lka, user); 197665c4fdfbSgilles m_add_string(p_lka, pass); 197765c4fdfbSgilles m_close(p_lka); 197865c4fdfbSgilles tree_xset(&wait_parent_auth, s->id, s); 1979e8fa9864Sjacekm return; 1980e8fa9864Sjacekm 1981e8fa9864Sjacekm default: 198265c4fdfbSgilles fatal("smtp_rfc4954_auth_plain: unknown state"); 19833ef9cbf7Sgilles } 19843ef9cbf7Sgilles 1985e8fa9864Sjacekm abort: 1986fe95d8d0Seric smtp_reply(s, "501 %s %s: Syntax error", 1987fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), 1988fe95d8d0Seric esc_description(ESC_SYNTAX_ERROR)); 198965c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 1990e8fa9864Sjacekm } 1991e8fa9864Sjacekm 1992be925435Sgilles static void 199365c4fdfbSgilles smtp_rfc4954_auth_login(struct smtp_session *s, char *arg) 199485e1f4f6Sgilles { 1995953aae25Sderaadt char buf[LINE_MAX]; 199685e1f4f6Sgilles 199765c4fdfbSgilles switch (s->state) { 199865c4fdfbSgilles case STATE_HELO: 199965c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_USERNAME); 2000da29cb6eSgilles if (arg != NULL && *arg != '\0') { 2001da29cb6eSgilles smtp_rfc4954_auth_login(s, arg); 2002da29cb6eSgilles return; 2003da29cb6eSgilles } 200465c4fdfbSgilles smtp_reply(s, "334 VXNlcm5hbWU6"); 2005e8fa9864Sjacekm return; 200685e1f4f6Sgilles 200765c4fdfbSgilles case STATE_AUTH_USERNAME: 2008c1392a69Seric memset(s->username, 0, sizeof(s->username)); 2009254aed36Seric if (base64_decode(arg, (unsigned char *)s->username, 201065c4fdfbSgilles sizeof(s->username) - 1) == -1) 2011e8fa9864Sjacekm goto abort; 201285e1f4f6Sgilles 201365c4fdfbSgilles smtp_enter_state(s, STATE_AUTH_PASSWORD); 201465c4fdfbSgilles smtp_reply(s, "334 UGFzc3dvcmQ6"); 2015e8fa9864Sjacekm return; 201685e1f4f6Sgilles 201765c4fdfbSgilles case STATE_AUTH_PASSWORD: 2018c1392a69Seric memset(buf, 0, sizeof(buf)); 2019254aed36Seric if (base64_decode(arg, (unsigned char *)buf, 2020254aed36Seric sizeof(buf)-1) == -1) 2021e8fa9864Sjacekm goto abort; 202285e1f4f6Sgilles 2023aa1d5973Seric m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1); 202465c4fdfbSgilles m_add_id(p_lka, s->id); 202565c4fdfbSgilles m_add_string(p_lka, s->listener->authtable); 202665c4fdfbSgilles m_add_string(p_lka, s->username); 202765c4fdfbSgilles m_add_string(p_lka, buf); 202865c4fdfbSgilles m_close(p_lka); 202965c4fdfbSgilles tree_xset(&wait_parent_auth, s->id, s); 2030e8fa9864Sjacekm return; 2031e8fa9864Sjacekm 203285e1f4f6Sgilles default: 203365c4fdfbSgilles fatal("smtp_rfc4954_auth_login: unknown state"); 203485e1f4f6Sgilles } 203585e1f4f6Sgilles 2036e8fa9864Sjacekm abort: 2037fe95d8d0Seric smtp_reply(s, "501 %s %s: Syntax error", 2038fe95d8d0Seric esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR), 2039fe95d8d0Seric esc_description(ESC_SYNTAX_ERROR)); 204065c4fdfbSgilles smtp_enter_state(s, STATE_HELO); 204185e1f4f6Sgilles } 204285e1f4f6Sgilles 204314b654a8Seric static void 20441c3ac238Seric smtp_lookup_servername(struct smtp_session *s) 20451c3ac238Seric { 20461c3ac238Seric if (s->listener->hostnametable[0]) { 2047aa1d5973Seric m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1); 20481c3ac238Seric m_add_id(p_lka, s->id); 20491c3ac238Seric m_add_string(p_lka, s->listener->hostnametable); 2050f2006a45Sgilles m_add_sockaddr(p_lka, (struct sockaddr*)&s->listener->ss); 20511c3ac238Seric m_close(p_lka); 20521c3ac238Seric tree_xset(&wait_lka_helo, s->id, s); 205308ad1f30Seric return; 20541c3ac238Seric } 205514b654a8Seric 205614b654a8Seric smtp_connected(s); 20571c3ac238Seric } 20581c3ac238Seric 205965c4fdfbSgilles static void 206065c4fdfbSgilles smtp_connected(struct smtp_session *s) 20613ef9cbf7Sgilles { 206265c4fdfbSgilles smtp_enter_state(s, STATE_CONNECTED); 20633ef9cbf7Sgilles 2064cf540174Sgilles log_info("%016"PRIx64" smtp connected address=%s host=%s", 2065744e25e4Sgilles s->id, ss_to_text(&s->ss), s->rdns); 206665c4fdfbSgilles 2067ec69ed85Sgilles smtp_filter_begin(s); 2068ec69ed85Sgilles 20696771f06dSgilles smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss, 207046aa809dSgilles &s->listener->ss); 207146aa809dSgilles 2072ec69ed85Sgilles smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss)); 2073d7b0dc3bSgilles } 2074d7b0dc3bSgilles 2075d7b0dc3bSgilles static void 2076d7b0dc3bSgilles smtp_proceed_connected(struct smtp_session *s) 2077d7b0dc3bSgilles { 20785d65917aSeric if (s->listener->flags & F_SMTPS) 2079eed85469Seric smtp_tls_init(s); 20805d65917aSeric else 2081c679e749Seric smtp_send_banner(s); 20823ef9cbf7Sgilles } 20833ef9cbf7Sgilles 2084cc81b7c6Seric static void 2085cc81b7c6Seric smtp_send_banner(struct smtp_session *s) 2086cc81b7c6Seric { 208748b77194Seric smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME); 20880d4adc27Sgilles s->banner_sent = 1; 20896771f06dSgilles smtp_report_link_greeting(s, s->smtpname); 2090cc81b7c6Seric } 2091cc81b7c6Seric 20923ef9cbf7Sgilles void 209365c4fdfbSgilles smtp_enter_state(struct smtp_session *s, int newstate) 209465c4fdfbSgilles { 209565c4fdfbSgilles log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s, 209665c4fdfbSgilles smtp_strstate(s->state), 209765c4fdfbSgilles smtp_strstate(newstate)); 209865c4fdfbSgilles 209965c4fdfbSgilles s->state = newstate; 210065c4fdfbSgilles } 210165c4fdfbSgilles 210265c4fdfbSgilles static void 210365c4fdfbSgilles smtp_reply(struct smtp_session *s, char *fmt, ...) 2104a6ca2cc5Sjacekm { 2105a6ca2cc5Sjacekm va_list ap; 210665c4fdfbSgilles int n; 21076e163f27Sgilles char buf[LINE_MAX*2], tmp[LINE_MAX*2]; 2108a6ca2cc5Sjacekm 2109a6ca2cc5Sjacekm va_start(ap, fmt); 2110b04e6f27Seric n = vsnprintf(buf, sizeof buf, fmt, ap); 2111a6ca2cc5Sjacekm va_end(ap); 21126e163f27Sgilles if (n < 0) 21136e163f27Sgilles fatalx("smtp_reply: response format error"); 2114b04e6f27Seric if (n < 4) 211565c4fdfbSgilles fatalx("smtp_reply: response too short"); 21166e163f27Sgilles if (n >= (int)sizeof buf) { 21176e163f27Sgilles /* only first three bytes are used by SMTP logic, 21186e163f27Sgilles * so if _our_ reply does not fit entirely in the 21196e163f27Sgilles * buffer, it's ok to truncate. 21206e163f27Sgilles */ 21216e163f27Sgilles } 2122a6ca2cc5Sjacekm 2123b04e6f27Seric log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf); 2124aebb80a1Sgilles smtp_report_protocol_server(s, buf); 2125b04e6f27Seric 2126b04e6f27Seric switch (buf[0]) { 21277929fb13Sgilles case '2': 21287b136650Sgilles if (s->tx) { 2129aebb80a1Sgilles if (s->last_cmd == CMD_MAIL_FROM) { 2130aebb80a1Sgilles smtp_report_tx_begin(s, s->tx->msgid); 21316771f06dSgilles smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1); 2132aebb80a1Sgilles } 21337929fb13Sgilles else if (s->last_cmd == CMD_RCPT_TO) 21346771f06dSgilles smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1); 21357b136650Sgilles } 21367929fb13Sgilles break; 2137e7d82dd0Sgilles case '3': 21387b136650Sgilles if (s->tx) { 2139e7d82dd0Sgilles if (s->last_cmd == CMD_DATA) 21406771f06dSgilles smtp_report_tx_data(s, s->tx->msgid, 1); 21417b136650Sgilles } 2142e7d82dd0Sgilles break; 21431ef63b12Sjacekm case '5': 21441ef63b12Sjacekm case '4': 2145b2f40906Sgilles /* do not report smtp_tx_mail/smtp_tx_rcpt errors 2146b2f40906Sgilles * if they happened outside of a transaction. 2147b2f40906Sgilles */ 2148b2f40906Sgilles if (s->tx) { 21497929fb13Sgilles if (s->last_cmd == CMD_MAIL_FROM) 21506771f06dSgilles smtp_report_tx_mail(s, s->tx->msgid, 2151b2f40906Sgilles s->cmd + 10, buf[0] == '4' ? -1 : 0); 21527929fb13Sgilles else if (s->last_cmd == CMD_RCPT_TO) 21536771f06dSgilles smtp_report_tx_rcpt(s, 2154b2f40906Sgilles s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0); 2155e7d82dd0Sgilles else if (s->last_cmd == CMD_DATA && s->tx->rcptcount) 21566771f06dSgilles smtp_report_tx_data(s, s->tx->msgid, 2157e7d82dd0Sgilles buf[0] == '4' ? -1 : 0); 2158b2f40906Sgilles } 21597929fb13Sgilles 2160299c4efeSeric if (s->flags & SF_BADINPUT) { 21617a93bdaaSgilles log_info("%016"PRIx64" smtp " 2162c6b5e508Sgilles "bad-input result=\"%.*s\"", 2163c6b5e508Sgilles s->id, n, buf); 2164299c4efeSeric } 21656f197337Sgilles else if (s->state == STATE_AUTH_INIT) { 2166a206140dSgiovanni log_info("%016"PRIx64" smtp " 2167c6b5e508Sgilles "failed-command " 2168a206140dSgiovanni "command=\"AUTH PLAIN (...)\" result=\"%.*s\"", 2169c6b5e508Sgilles s->id, n, buf); 21706f197337Sgilles } 21716f197337Sgilles else if (s->state == STATE_AUTH_USERNAME) { 2172a206140dSgiovanni log_info("%016"PRIx64" smtp " 2173c6b5e508Sgilles "failed-command " 2174a206140dSgiovanni "command=\"AUTH LOGIN (username)\" result=\"%.*s\"", 2175c6b5e508Sgilles s->id, n, buf); 21766f197337Sgilles } 21776f197337Sgilles else if (s->state == STATE_AUTH_PASSWORD) { 2178a206140dSgiovanni log_info("%016"PRIx64" smtp " 2179c6b5e508Sgilles "failed-command " 2180a206140dSgiovanni "command=\"AUTH LOGIN (password)\" result=\"%.*s\"", 2181c6b5e508Sgilles s->id, n, buf); 2182299c4efeSeric } 2183299c4efeSeric else { 218482614934Seric strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE); 21857a93bdaaSgilles log_info("%016"PRIx64" smtp " 2186c6b5e508Sgilles "failed-command command=\"%s\" " 2187a206140dSgiovanni "result=\"%.*s\"", 2188c6b5e508Sgilles s->id, tmp, n, buf); 2189299c4efeSeric } 21901ef63b12Sjacekm break; 21911ef63b12Sjacekm } 2192e7d82dd0Sgilles 2193e7d82dd0Sgilles io_xprintf(s->io, "%s\r\n", buf); 21945711eb67Sjacekm } 21955711eb67Sjacekm 2196be925435Sgilles static void 219765c4fdfbSgilles smtp_free(struct smtp_session *s, const char * reason) 21988107cc2fSjacekm { 21990490754bSeric if (s->tx) { 22008d3f7f0dSeric if (s->tx->msgid) 2201008f3f72Seric smtp_tx_rollback(s->tx); 22020490754bSeric smtp_tx_free(s->tx); 22030490754bSeric } 2204dcdb4581Seric 22056771f06dSgilles smtp_report_link_disconnect(s); 2206590a6142Sgilles smtp_filter_end(s); 2207ff05ba49Sgilles 220865c4fdfbSgilles if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS) 220965c4fdfbSgilles stat_decrement("smtp.smtps", 1); 221065c4fdfbSgilles if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS) 221165c4fdfbSgilles stat_decrement("smtp.tls", 1); 221265c4fdfbSgilles 22138d3f7f0dSeric io_free(s->io); 221465c4fdfbSgilles free(s); 221565c4fdfbSgilles 221665c4fdfbSgilles smtp_collect(); 221765c4fdfbSgilles } 221865c4fdfbSgilles 221965c4fdfbSgilles static int 2220cc81b7c6Seric smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args, 2221cc81b7c6Seric const char *domain) 222265c4fdfbSgilles { 222365c4fdfbSgilles char *p, *e; 222465c4fdfbSgilles 222565c4fdfbSgilles if (line == NULL) 222665c4fdfbSgilles return (0); 222765c4fdfbSgilles 222865c4fdfbSgilles if (*line != '<') 222965c4fdfbSgilles return (0); 223065c4fdfbSgilles 223165c4fdfbSgilles e = strchr(line, '>'); 223265c4fdfbSgilles if (e == NULL) 223365c4fdfbSgilles return (0); 223465c4fdfbSgilles *e++ = '\0'; 223565c4fdfbSgilles while (*e == ' ') 223665c4fdfbSgilles e++; 223765c4fdfbSgilles *args = e; 223865c4fdfbSgilles 223965c4fdfbSgilles if (!text_to_mailaddr(maddr, line + 1)) 224065c4fdfbSgilles return (0); 224165c4fdfbSgilles 224265c4fdfbSgilles p = strchr(maddr->user, ':'); 224365c4fdfbSgilles if (p != NULL) { 224465c4fdfbSgilles p++; 224565c4fdfbSgilles memmove(maddr->user, p, strlen(p) + 1); 224665c4fdfbSgilles } 224765c4fdfbSgilles 2248f3fad8caSgilles /* accept empty return-path in MAIL FROM, required for bounces */ 2249f3fad8caSgilles if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0') 225065c4fdfbSgilles return (1); 2251cc81b7c6Seric 22529dcfda04Sgilles /* no or invalid user-part, reject */ 22539dcfda04Sgilles if (maddr->user[0] == '\0' || !valid_localpart(maddr->user)) 2254f3fad8caSgilles return (0); 2255f3fad8caSgilles 22569dcfda04Sgilles /* no domain part, local user */ 2257f3fad8caSgilles if (maddr->domain[0] == '\0') { 2258cc81b7c6Seric (void)strlcpy(maddr->domain, domain, 2259cc81b7c6Seric sizeof(maddr->domain)); 2260cc81b7c6Seric } 22619dcfda04Sgilles 22629dcfda04Sgilles if (!valid_domainpart(maddr->domain)) 226365c4fdfbSgilles return (0); 226465c4fdfbSgilles 226565c4fdfbSgilles return (1); 226665c4fdfbSgilles } 226765c4fdfbSgilles 22687627c963Seric static void 2269299c4efeSeric smtp_auth_failure_resume(int fd, short event, void *p) 2270299c4efeSeric { 2271299c4efeSeric struct smtp_session *s = p; 2272299c4efeSeric 2273299c4efeSeric smtp_reply(s, "535 Authentication failed"); 2274299c4efeSeric smtp_enter_state(s, STATE_HELO); 2275299c4efeSeric } 2276299c4efeSeric 2277299c4efeSeric static void 2278299c4efeSeric smtp_auth_failure_pause(struct smtp_session *s) 2279299c4efeSeric { 2280299c4efeSeric struct timeval tv; 2281299c4efeSeric 2282299c4efeSeric tv.tv_sec = 0; 2283299c4efeSeric tv.tv_usec = arc4random_uniform(1000000); 2284299c4efeSeric log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, " 22858c71974fSop "will defer answer for %lu microseconds", (long)tv.tv_usec); 2286299c4efeSeric evtimer_set(&s->pause, smtp_auth_failure_resume, s); 2287299c4efeSeric evtimer_add(&s->pause, &tv); 2288299c4efeSeric } 2289299c4efeSeric 22905e3ff182Seric static int 22915e3ff182Seric smtp_tx(struct smtp_session *s) 22925e3ff182Seric { 22935e3ff182Seric struct smtp_tx *tx; 22945e3ff182Seric 22955e3ff182Seric tx = calloc(1, sizeof(*tx)); 22965e3ff182Seric if (tx == NULL) 22975e3ff182Seric return 0; 22985e3ff182Seric 22995e3ff182Seric TAILQ_INIT(&tx->rcpts); 23005e3ff182Seric 23015e3ff182Seric s->tx = tx; 23025e3ff182Seric tx->session = s; 23035e3ff182Seric 23045e3ff182Seric /* setup the envelope */ 230522135240Seric tx->evp.ss = s->ss; 230622135240Seric (void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag)); 230722135240Seric (void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname)); 2308744e25e4Sgilles (void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname); 230922135240Seric (void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo)); 23107d9f71f9Sgilles (void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username)); 23115e3ff182Seric 23125e3ff182Seric if (s->flags & SF_BOUNCE) 231322135240Seric tx->evp.flags |= EF_BOUNCE; 23145e3ff182Seric if (s->flags & SF_AUTHENTICATED) 231522135240Seric tx->evp.flags |= EF_AUTHENTICATED; 23165e3ff182Seric 231744f4452eSeric if ((tx->parser = rfc5322_parser_new()) == NULL) { 231844f4452eSeric free(tx); 231944f4452eSeric return 0; 23205e3ff182Seric } 23215e3ff182Seric 23225e3ff182Seric return 1; 23235e3ff182Seric } 23245e3ff182Seric 23255e3ff182Seric static void 23265e3ff182Seric smtp_tx_free(struct smtp_tx *tx) 23275e3ff182Seric { 23285e3ff182Seric struct smtp_rcpt *rcpt; 23295e3ff182Seric 233044f4452eSeric rfc5322_free(tx->parser); 23315e3ff182Seric 23325e3ff182Seric while ((rcpt = TAILQ_FIRST(&tx->rcpts))) { 23335e3ff182Seric TAILQ_REMOVE(&tx->rcpts, rcpt, entry); 23345e3ff182Seric free(rcpt); 23355e3ff182Seric } 23365e3ff182Seric 23375e3ff182Seric if (tx->ofile) 23385e3ff182Seric fclose(tx->ofile); 23395e3ff182Seric 23405e3ff182Seric tx->session->tx = NULL; 23415e3ff182Seric 23425e3ff182Seric free(tx); 23435e3ff182Seric } 23445e3ff182Seric 234508becfa3Seric static void 2346aaf790e0Sgilles smtp_tx_mail_from(struct smtp_tx *tx, const char *line) 234772cde3cbSeric { 23486b0385feSeric char *opt; 2349aaf790e0Sgilles char *copy; 2350aaf790e0Sgilles char tmp[SMTP_LINE_MAX]; 23516b0385feSeric 2352aaf790e0Sgilles (void)strlcpy(tmp, line, sizeof tmp); 2353aaf790e0Sgilles copy = tmp; 2354aaf790e0Sgilles 2355aaf790e0Sgilles if (smtp_mailaddr(&tx->evp.sender, copy, 1, ©, 235672cde3cbSeric tx->session->smtpname) == 0) { 23575fe3cdcbSgilles smtp_reply(tx->session, "553 %s Sender address syntax error", 235872cde3cbSeric esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS)); 23594768c36cSeric smtp_tx_free(tx); 236072cde3cbSeric return; 236172cde3cbSeric } 236272cde3cbSeric 2363aaf790e0Sgilles while ((opt = strsep(©, " "))) { 23646b0385feSeric if (*opt == '\0') 23656b0385feSeric continue; 23666b0385feSeric 23676b0385feSeric if (strncasecmp(opt, "AUTH=", 5) == 0) 23686b0385feSeric log_debug("debug: smtp: AUTH in MAIL FROM command"); 23696b0385feSeric else if (strncasecmp(opt, "SIZE=", 5) == 0) 23706b0385feSeric log_debug("debug: smtp: SIZE in MAIL FROM command"); 23716b0385feSeric else if (strcasecmp(opt, "BODY=7BIT") == 0) 23726b0385feSeric /* XXX only for this transaction */ 23736b0385feSeric tx->session->flags &= ~SF_8BITMIME; 23746b0385feSeric else if (strcasecmp(opt, "BODY=8BITMIME") == 0) 23756b0385feSeric ; 23766b0385feSeric else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "RET=", 4) == 0) { 23776b0385feSeric opt += 4; 23786b0385feSeric if (strcasecmp(opt, "HDRS") == 0) 23796b0385feSeric tx->evp.dsn_ret = DSN_RETHDRS; 23806b0385feSeric else if (strcasecmp(opt, "FULL") == 0) 23816b0385feSeric tx->evp.dsn_ret = DSN_RETFULL; 23826b0385feSeric } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ENVID=", 6) == 0) { 23836b0385feSeric opt += 6; 23846b0385feSeric if (strlcpy(tx->evp.dsn_envid, opt, sizeof(tx->evp.dsn_envid)) 23856b0385feSeric >= sizeof(tx->evp.dsn_envid)) { 23866b0385feSeric smtp_reply(tx->session, 23876b0385feSeric "503 %s %s: option too large, truncated: %s", 23886b0385feSeric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 23896b0385feSeric esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt); 239072cde3cbSeric smtp_tx_free(tx); 239172cde3cbSeric return; 239272cde3cbSeric } 23936b0385feSeric } else { 23946b0385feSeric smtp_reply(tx->session, "503 %s %s: Unsupported option %s", 23956b0385feSeric esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS), 23966b0385feSeric esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt); 23976b0385feSeric smtp_tx_free(tx); 23986b0385feSeric return; 23996b0385feSeric } 24006b0385feSeric } 240172cde3cbSeric 240272cde3cbSeric /* only check sendertable if defined and user has authenticated */ 240372cde3cbSeric if (tx->session->flags & SF_AUTHENTICATED && 240472cde3cbSeric tx->session->listener->sendertable[0]) { 240572cde3cbSeric m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1); 240672cde3cbSeric m_add_id(p_lka, tx->session->id); 240772cde3cbSeric m_add_string(p_lka, tx->session->listener->sendertable); 240872cde3cbSeric m_add_string(p_lka, tx->session->username); 240972cde3cbSeric m_add_mailaddr(p_lka, &tx->evp.sender); 241072cde3cbSeric m_close(p_lka); 241172cde3cbSeric tree_xset(&wait_lka_mail, tx->session->id, tx->session); 241272cde3cbSeric } 241372cde3cbSeric else 241472cde3cbSeric smtp_tx_create_message(tx); 241572cde3cbSeric } 241672cde3cbSeric 241772cde3cbSeric static void 2418008f3f72Seric smtp_tx_create_message(struct smtp_tx *tx) 2419a2e141b1Seric { 2420a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1); 2421008f3f72Seric m_add_id(p_queue, tx->session->id); 2422a2e141b1Seric m_close(p_queue); 2423008f3f72Seric tree_xset(&wait_queue_msg, tx->session->id, tx->session); 2424a2e141b1Seric } 2425a2e141b1Seric 2426a2e141b1Seric static void 2427aaf790e0Sgilles smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line) 242872cde3cbSeric { 24296b0385feSeric char *opt, *p; 2430aaf790e0Sgilles char *copy; 2431aaf790e0Sgilles char tmp[SMTP_LINE_MAX]; 2432aaf790e0Sgilles 2433aaf790e0Sgilles (void)strlcpy(tmp, line, sizeof tmp); 2434aaf790e0Sgilles copy = tmp; 24356b0385feSeric 243672cde3cbSeric if (tx->rcptcount >= env->sc_session_max_rcpt) { 243772cde3cbSeric smtp_reply(tx->session, "451 %s %s: Too many recipients", 243872cde3cbSeric esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS), 243972cde3cbSeric esc_description(ESC_TOO_MANY_RECIPIENTS)); 244072cde3cbSeric return; 244172cde3cbSeric } 244272cde3cbSeric 2443aaf790e0Sgilles if (smtp_mailaddr(&tx->evp.rcpt, copy, 0, ©, 244472cde3cbSeric tx->session->smtpname) == 0) { 244572cde3cbSeric smtp_reply(tx->session, 24465fe3cdcbSgilles "501 %s Recipient address syntax error", 244772cde3cbSeric esc_code(ESC_STATUS_PERMFAIL, 244872cde3cbSeric ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX)); 244972cde3cbSeric return; 245072cde3cbSeric } 245172cde3cbSeric 2452aaf790e0Sgilles while ((opt = strsep(©, " "))) { 24536b0385feSeric if (*opt == '\0') 24546b0385feSeric continue; 24556b0385feSeric 24566b0385feSeric if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "NOTIFY=", 7) == 0) { 24576b0385feSeric opt += 7; 24586b0385feSeric while ((p = strsep(&opt, ","))) { 24596b0385feSeric if (strcasecmp(p, "SUCCESS") == 0) 24606b0385feSeric tx->evp.dsn_notify |= DSN_SUCCESS; 24616b0385feSeric else if (strcasecmp(p, "FAILURE") == 0) 24626b0385feSeric tx->evp.dsn_notify |= DSN_FAILURE; 24636b0385feSeric else if (strcasecmp(p, "DELAY") == 0) 24646b0385feSeric tx->evp.dsn_notify |= DSN_DELAY; 24656b0385feSeric else if (strcasecmp(p, "NEVER") == 0) 24666b0385feSeric tx->evp.dsn_notify |= DSN_NEVER; 24676b0385feSeric } 24686b0385feSeric 24696b0385feSeric if (tx->evp.dsn_notify & DSN_NEVER && 24706b0385feSeric tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE | 24716b0385feSeric DSN_DELAY)) { 24726b0385feSeric smtp_reply(tx->session, 24736b0385feSeric "553 NOTIFY option NEVER cannot be" 24746b0385feSeric " combined with other options"); 247572cde3cbSeric return; 24766b0385feSeric } 2477cd8603dbSop } else if (ADVERTISE_EXT_DSN(tx->session) && 2478cd8603dbSop strncasecmp(opt, "ORCPT=", 6) == 0) { 2479cd8603dbSop size_t len = sizeof(tx->evp.dsn_orcpt); 2480cd8603dbSop 24816b0385feSeric opt += 6; 24822d8fd64bSgilles 2483cd8603dbSop if ((p = strchr(opt, ';')) == NULL || 2484cd8603dbSop !valid_xtext(p + 1) || 248590974261Sop strlcpy(tx->evp.dsn_orcpt, opt, len) >= len) { 24866b0385feSeric smtp_reply(tx->session, 24876b0385feSeric "553 ORCPT address syntax error"); 24886b0385feSeric return; 24896b0385feSeric } 24906b0385feSeric } else { 24916b0385feSeric smtp_reply(tx->session, "503 Unsupported option %s", opt); 24926b0385feSeric return; 24936b0385feSeric } 24946b0385feSeric } 249572cde3cbSeric 249672cde3cbSeric m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1); 249772cde3cbSeric m_add_id(p_lka, tx->session->id); 249872cde3cbSeric m_add_envelope(p_lka, &tx->evp); 249972cde3cbSeric m_close(p_lka); 250072cde3cbSeric tree_xset(&wait_lka_rcpt, tx->session->id, tx->session); 250172cde3cbSeric } 250272cde3cbSeric 250372cde3cbSeric static void 2504008f3f72Seric smtp_tx_open_message(struct smtp_tx *tx) 2505a2e141b1Seric { 2506a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1); 2507008f3f72Seric m_add_id(p_queue, tx->session->id); 2508008f3f72Seric m_add_msgid(p_queue, tx->msgid); 2509a2e141b1Seric m_close(p_queue); 2510008f3f72Seric tree_xset(&wait_queue_fd, tx->session->id, tx->session); 2511a2e141b1Seric } 2512a2e141b1Seric 2513a2e141b1Seric static void 2514008f3f72Seric smtp_tx_commit(struct smtp_tx *tx) 2515a2e141b1Seric { 2516a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1); 2517008f3f72Seric m_add_id(p_queue, tx->session->id); 2518008f3f72Seric m_add_msgid(p_queue, tx->msgid); 2519a2e141b1Seric m_close(p_queue); 2520008f3f72Seric tree_xset(&wait_queue_commit, tx->session->id, tx->session); 2521590a6142Sgilles smtp_filter_data_end(tx->session); 2522a2e141b1Seric } 2523a2e141b1Seric 2524a2e141b1Seric static void 2525008f3f72Seric smtp_tx_rollback(struct smtp_tx *tx) 2526a2e141b1Seric { 2527a2e141b1Seric m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1); 2528008f3f72Seric m_add_msgid(p_queue, tx->msgid); 2529a2e141b1Seric m_close(p_queue); 25306771f06dSgilles smtp_report_tx_rollback(tx->session, tx->msgid); 25316771f06dSgilles smtp_report_tx_reset(tx->session, tx->msgid); 2532590a6142Sgilles smtp_filter_data_end(tx->session); 2533a2e141b1Seric } 2534a2e141b1Seric 25355bfc3fefSeric static int 2536008f3f72Seric smtp_tx_dataline(struct smtp_tx *tx, const char *line) 2537a1bd4550Sgilles { 253844f4452eSeric struct rfc5322_result res; 253944f4452eSeric int r; 2540f7c2eb5aSgilles 2541ad48cc20Seric log_trace(TRACE_SMTP, "<<< [MSG] %s", line); 2542ad48cc20Seric 25435bfc3fefSeric if (!strcmp(line, ".")) { 25446771f06dSgilles smtp_report_protocol_client(tx->session, "."); 25455bfc3fefSeric log_trace(TRACE_SMTP, "<<< [EOM]"); 254644f4452eSeric if (tx->error) 25475bfc3fefSeric return 1; 254844f4452eSeric line = NULL; 25495bfc3fefSeric } 255044f4452eSeric else { 2551f299ff90Seric /* ignore data line if an error is set */ 2552008f3f72Seric if (tx->error) 25535bfc3fefSeric return 0; 2554f7c2eb5aSgilles 2555ad48cc20Seric /* escape lines starting with a '.' */ 2556ad48cc20Seric if (line[0] == '.') 2557ad48cc20Seric line += 1; 255844f4452eSeric } 2559f7c2eb5aSgilles 256044f4452eSeric if (rfc5322_push(tx->parser, line) == -1) { 256144f4452eSeric log_warnx("failed to push dataline"); 256244f4452eSeric tx->error = TX_ERROR_INTERNAL; 25635bfc3fefSeric return 0; 2564ad48cc20Seric } 2565ad48cc20Seric 256644f4452eSeric for(;;) { 256744f4452eSeric r = rfc5322_next(tx->parser, &res); 256844f4452eSeric switch (r) { 256944f4452eSeric case -1: 257044f4452eSeric if (errno == ENOMEM) 257144f4452eSeric tx->error = TX_ERROR_INTERNAL; 257244f4452eSeric else 2573008f3f72Seric tx->error = TX_ERROR_MALFORMED; 25745bfc3fefSeric return 0; 257544f4452eSeric 257644f4452eSeric case RFC5322_NONE: 257744f4452eSeric /* Need more data */ 257844f4452eSeric return 0; 257944f4452eSeric 258044f4452eSeric case RFC5322_HEADER_START: 258144f4452eSeric /* ignore bcc */ 258244f4452eSeric if (!strcasecmp("Bcc", res.hdr)) 258344f4452eSeric continue; 258444f4452eSeric 258544f4452eSeric if (!strcasecmp("To", res.hdr) || 258644f4452eSeric !strcasecmp("Cc", res.hdr) || 258744f4452eSeric !strcasecmp("From", res.hdr)) { 258844f4452eSeric rfc5322_unfold_header(tx->parser); 258944f4452eSeric continue; 259044f4452eSeric } 259144f4452eSeric 259244f4452eSeric if (!strcasecmp("Received", res.hdr)) { 259344f4452eSeric if (++tx->rcvcount >= MAX_HOPS_COUNT) { 259444f4452eSeric log_warnx("warn: loop detected"); 259544f4452eSeric tx->error = TX_ERROR_LOOP; 259644f4452eSeric return 0; 259744f4452eSeric } 259844f4452eSeric } 259944f4452eSeric else if (!tx->has_date && !strcasecmp("Date", res.hdr)) 260044f4452eSeric tx->has_date = 1; 260144f4452eSeric else if (!tx->has_message_id && 260244f4452eSeric !strcasecmp("Message-Id", res.hdr)) 260344f4452eSeric tx->has_message_id = 1; 260444f4452eSeric 260544f4452eSeric smtp_message_printf(tx, "%s:%s\n", res.hdr, res.value); 260644f4452eSeric break; 260744f4452eSeric 260844f4452eSeric case RFC5322_HEADER_CONT: 260944f4452eSeric 261044f4452eSeric if (!strcasecmp("Bcc", res.hdr) || 261144f4452eSeric !strcasecmp("To", res.hdr) || 261244f4452eSeric !strcasecmp("Cc", res.hdr) || 261344f4452eSeric !strcasecmp("From", res.hdr)) 261444f4452eSeric continue; 261544f4452eSeric 261644f4452eSeric smtp_message_printf(tx, "%s\n", res.value); 261744f4452eSeric break; 261844f4452eSeric 261944f4452eSeric case RFC5322_HEADER_END: 262044f4452eSeric if (!strcasecmp("To", res.hdr) || 262144f4452eSeric !strcasecmp("Cc", res.hdr) || 262244f4452eSeric !strcasecmp("From", res.hdr)) 262344f4452eSeric header_domain_append_callback(tx, res.hdr, 262444f4452eSeric res.value); 262544f4452eSeric break; 262644f4452eSeric 262744f4452eSeric case RFC5322_END_OF_HEADERS: 262844f4452eSeric if (tx->session->listener->local || 2629ab096723Smillert tx->session->listener->port == htons(587)) { 263044f4452eSeric 263144f4452eSeric if (!tx->has_date) { 263244f4452eSeric log_debug("debug: %p: adding Date", tx); 263344f4452eSeric smtp_message_printf(tx, "Date: %s\n", 263444f4452eSeric time_to_text(tx->time)); 263544f4452eSeric } 263644f4452eSeric 263744f4452eSeric if (!tx->has_message_id) { 263844f4452eSeric log_debug("debug: %p: adding Message-ID", tx); 263944f4452eSeric smtp_message_printf(tx, 264044f4452eSeric "Message-ID: <%016"PRIx64"@%s>\n", 264144f4452eSeric generate_uid(), 264244f4452eSeric tx->session->listener->hostname); 264344f4452eSeric } 264444f4452eSeric } 264544f4452eSeric break; 264644f4452eSeric 264744f4452eSeric case RFC5322_BODY_START: 264844f4452eSeric case RFC5322_BODY: 264944f4452eSeric smtp_message_printf(tx, "%s\n", res.value); 265044f4452eSeric break; 265144f4452eSeric 265244f4452eSeric case RFC5322_END_OF_MESSAGE: 265344f4452eSeric return 1; 265444f4452eSeric 265544f4452eSeric default: 265644f4452eSeric fatalx("%s", __func__); 265744f4452eSeric } 265844f4452eSeric } 2659a1bd4550Sgilles } 2660a1bd4550Sgilles 2661590a6142Sgilles static int 2662590a6142Sgilles smtp_tx_filtered_dataline(struct smtp_tx *tx, const char *line) 2663590a6142Sgilles { 26643cf20826Sgilles if (!strcmp(line, ".")) 2665590a6142Sgilles line = NULL; 2666590a6142Sgilles else { 2667590a6142Sgilles /* ignore data line if an error is set */ 2668590a6142Sgilles if (tx->error) 2669590a6142Sgilles return 0; 2670590a6142Sgilles } 26713835491fSeric io_printf(tx->filter, "%s\n", line ? line : "."); 2672590a6142Sgilles return line ? 0 : 1; 2673590a6142Sgilles } 2674590a6142Sgilles 267554355315Seric static void 2676590a6142Sgilles smtp_tx_eom(struct smtp_tx *tx) 2677590a6142Sgilles { 2678590a6142Sgilles smtp_filter_phase(FILTER_COMMIT, tx->session, NULL); 2679590a6142Sgilles } 2680590a6142Sgilles 2681590a6142Sgilles static int 268254355315Seric smtp_message_fd(struct smtp_tx *tx, int fd) 268354355315Seric { 268454355315Seric struct smtp_session *s; 268554355315Seric 268654355315Seric s = tx->session; 268754355315Seric 268854355315Seric log_debug("smtp: %p: message fd %d", s, fd); 268954355315Seric 269022135240Seric if ((tx->ofile = fdopen(fd, "w")) == NULL) { 269154355315Seric close(fd); 26925fe3cdcbSgilles smtp_reply(s, "421 %s Temporary Error", 269354355315Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 269454355315Seric smtp_enter_state(s, STATE_QUIT); 2695590a6142Sgilles return 0; 2696590a6142Sgilles } 2697590a6142Sgilles return 1; 2698590a6142Sgilles } 2699590a6142Sgilles 2700590a6142Sgilles static void 2701590a6142Sgilles filter_session_io(struct io *io, int evt, void *arg) 2702590a6142Sgilles { 2703590a6142Sgilles struct smtp_tx*tx = arg; 2704590a6142Sgilles char*line = NULL; 2705590a6142Sgilles ssize_t len; 2706590a6142Sgilles 2707ec69ed85Sgilles log_trace(TRACE_IO, "filter session io (smtp): %p: %s %s", tx, io_strevent(evt), 2708590a6142Sgilles io_strio(io)); 2709590a6142Sgilles 2710590a6142Sgilles switch (evt) { 2711590a6142Sgilles case IO_DATAIN: 2712590a6142Sgilles nextline: 2713590a6142Sgilles line = io_getline(tx->filter, &len); 2714590a6142Sgilles /* No complete line received */ 2715590a6142Sgilles if (line == NULL) 2716590a6142Sgilles return; 2717590a6142Sgilles 2718590a6142Sgilles if (smtp_tx_dataline(tx, line)) { 2719590a6142Sgilles smtp_tx_eom(tx); 272054355315Seric return; 272154355315Seric } 272254355315Seric 2723590a6142Sgilles goto nextline; 2724590a6142Sgilles } 2725590a6142Sgilles } 2726590a6142Sgilles 2727590a6142Sgilles static void 2728590a6142Sgilles smtp_filter_fd(struct smtp_tx *tx, int fd) 2729590a6142Sgilles { 2730590a6142Sgilles struct smtp_session *s; 2731590a6142Sgilles 2732590a6142Sgilles s = tx->session; 2733590a6142Sgilles 2734590a6142Sgilles log_debug("smtp: %p: filter fd %d", s, fd); 2735590a6142Sgilles 2736590a6142Sgilles tx->filter = io_new(); 2737590a6142Sgilles io_set_fd(tx->filter, fd); 2738590a6142Sgilles io_set_callback(tx->filter, filter_session_io, tx); 2739590a6142Sgilles } 2740590a6142Sgilles 2741590a6142Sgilles static void 2742590a6142Sgilles smtp_message_begin(struct smtp_tx *tx) 2743590a6142Sgilles { 2744590a6142Sgilles struct smtp_session *s; 27453e47678eSmillert struct smtp_rcpt *rcpt; 2746590a6142Sgilles int (*m_printf)(struct smtp_tx *, const char *, ...); 2747590a6142Sgilles 2748590a6142Sgilles m_printf = smtp_message_printf; 2749590a6142Sgilles if (tx->filter) 2750590a6142Sgilles m_printf = smtp_filter_printf; 2751590a6142Sgilles 2752590a6142Sgilles s = tx->session; 2753590a6142Sgilles 2754590a6142Sgilles log_debug("smtp: %p: message begin", s); 2755590a6142Sgilles 2756e7d82dd0Sgilles smtp_reply(s, "354 Enter mail, end with \".\"" 2757e7d82dd0Sgilles " on a line by itself"); 2758e7d82dd0Sgilles 2759f6b0bf9aSgilles if (s->junk || (s->tx && s->tx->junk)) 2760f6b0bf9aSgilles m_printf(tx, "X-Spam: Yes\n"); 2761f6b0bf9aSgilles 2762590a6142Sgilles m_printf(tx, "Received: "); 276354355315Seric if (!(s->listener->flags & F_MASK_SOURCE)) { 276491affd4dSgilles m_printf(tx, "from %s (%s %s%s%s)", 276554355315Seric s->helo, 2766744e25e4Sgilles s->rdns, 276791affd4dSgilles s->ss.ss_family == AF_INET6 ? "" : "[", 276891affd4dSgilles ss_to_text(&s->ss), 276991affd4dSgilles s->ss.ss_family == AF_INET6 ? "" : "]"); 277054355315Seric } 2771590a6142Sgilles m_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x", 277254355315Seric s->smtpname, 277354355315Seric SMTPD_NAME, 277454355315Seric s->flags & SF_EHLO ? "E" : "", 277554355315Seric s->flags & SF_SECURE ? "S" : "", 277654355315Seric s->flags & SF_AUTHENTICATED ? "A" : "", 277722135240Seric tx->msgid); 277854355315Seric 277954355315Seric if (s->flags & SF_SECURE) { 2780590a6142Sgilles m_printf(tx, " (%s:%s:%d:%s)", 2781eed85469Seric tls_conn_version(io_tls(s->io)), 2782eed85469Seric tls_conn_cipher(io_tls(s->io)), 2783eed85469Seric tls_conn_cipher_strength(io_tls(s->io)), 2784eed85469Seric (s->flags & SF_VERIFIED) ? "YES" : "NO"); 278554355315Seric 278654355315Seric if (s->listener->flags & F_RECEIVEDAUTH) { 2787590a6142Sgilles m_printf(tx, " auth=%s", 278854355315Seric s->username[0] ? "yes" : "no"); 278954355315Seric if (s->username[0]) 2790590a6142Sgilles m_printf(tx, " user=%s", s->username); 279154355315Seric } 279254355315Seric } 279354355315Seric 279454355315Seric if (tx->rcptcount == 1) { 27953e47678eSmillert rcpt = TAILQ_FIRST(&tx->rcpts); 2796590a6142Sgilles m_printf(tx, "\n\tfor <%s@%s>", 27973e47678eSmillert rcpt->maddr.user, 27983e47678eSmillert rcpt->maddr.domain); 279954355315Seric } 280054355315Seric 2801590a6142Sgilles m_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time))); 280254355315Seric 280354355315Seric smtp_enter_state(s, STATE_BODY); 280454355315Seric } 280554355315Seric 280654355315Seric static void 280754355315Seric smtp_message_end(struct smtp_tx *tx) 280854355315Seric { 280954355315Seric struct smtp_session *s; 281054355315Seric 281154355315Seric s = tx->session; 281254355315Seric 281354355315Seric log_debug("debug: %p: end of message, error=%d", s, tx->error); 281454355315Seric 281554355315Seric fclose(tx->ofile); 281654355315Seric tx->ofile = NULL; 281754355315Seric 281854355315Seric switch(tx->error) { 281954355315Seric case TX_OK: 282054355315Seric smtp_tx_commit(tx); 282154355315Seric return; 282254355315Seric 282354355315Seric case TX_ERROR_SIZE: 282454355315Seric smtp_reply(s, "554 %s %s: Transaction failed, message too big", 282554355315Seric esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM), 282654355315Seric esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM)); 282754355315Seric break; 282854355315Seric 282954355315Seric case TX_ERROR_LOOP: 283054355315Seric smtp_reply(s, "500 %s %s: Loop detected", 283154355315Seric esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED), 283254355315Seric esc_description(ESC_ROUTING_LOOP_DETECTED)); 283354355315Seric break; 283454355315Seric 283554355315Seric case TX_ERROR_MALFORMED: 283654355315Seric smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant", 283754355315Seric esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED), 283854355315Seric esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED)); 283954355315Seric break; 284054355315Seric 284154355315Seric case TX_ERROR_IO: 284254355315Seric case TX_ERROR_RESOURCES: 28435fe3cdcbSgilles smtp_reply(s, "421 %s Temporary Error", 284454355315Seric esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); 284554355315Seric break; 284654355315Seric 284754355315Seric default: 284854355315Seric /* fatal? */ 284954355315Seric smtp_reply(s, "421 Internal server error"); 285054355315Seric } 285154355315Seric 285254355315Seric smtp_tx_rollback(tx); 285354355315Seric smtp_tx_free(tx); 285454355315Seric smtp_enter_state(s, STATE_HELO); 285554355315Seric } 285654355315Seric 285754355315Seric static int 2858590a6142Sgilles smtp_filter_printf(struct smtp_tx *tx, const char *fmt, ...) 2859590a6142Sgilles { 2860590a6142Sgilles va_list ap; 2861590a6142Sgilles int len; 2862590a6142Sgilles 2863590a6142Sgilles if (tx->error) 2864590a6142Sgilles return -1; 2865590a6142Sgilles 2866590a6142Sgilles va_start(ap, fmt); 2867590a6142Sgilles len = io_vprintf(tx->filter, fmt, ap); 2868590a6142Sgilles va_end(ap); 2869590a6142Sgilles 2870590a6142Sgilles if (len < 0) { 2871590a6142Sgilles log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id); 2872590a6142Sgilles tx->error = TX_ERROR_IO; 2873590a6142Sgilles } 2874590a6142Sgilles else 2875590a6142Sgilles tx->odatalen += len; 2876590a6142Sgilles 2877590a6142Sgilles return len; 2878590a6142Sgilles } 2879590a6142Sgilles 2880590a6142Sgilles static int 288154355315Seric smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...) 288254355315Seric { 288354355315Seric va_list ap; 288454355315Seric int len; 288554355315Seric 288654355315Seric if (tx->error) 288754355315Seric return -1; 288854355315Seric 288954355315Seric va_start(ap, fmt); 289054355315Seric len = vfprintf(tx->ofile, fmt, ap); 289154355315Seric va_end(ap); 289254355315Seric 2893df69c215Sderaadt if (len == -1) { 289454355315Seric log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id); 289554355315Seric tx->error = TX_ERROR_IO; 289654355315Seric } 289754355315Seric else 289854355315Seric tx->odatalen += len; 289954355315Seric 290054355315Seric return len; 290154355315Seric } 290254355315Seric 2903b6d81129Seric #define CASE(x) case x : return #x 2904b6d81129Seric 2905b6d81129Seric const char * 290665c4fdfbSgilles smtp_strstate(int state) 2907b6d81129Seric { 2908b6d81129Seric static char buf[32]; 2909b6d81129Seric 2910b6d81129Seric switch (state) { 291165c4fdfbSgilles CASE(STATE_NEW); 291265c4fdfbSgilles CASE(STATE_CONNECTED); 291365c4fdfbSgilles CASE(STATE_TLS); 291465c4fdfbSgilles CASE(STATE_HELO); 291565c4fdfbSgilles CASE(STATE_AUTH_INIT); 291665c4fdfbSgilles CASE(STATE_AUTH_USERNAME); 291765c4fdfbSgilles CASE(STATE_AUTH_PASSWORD); 291865c4fdfbSgilles CASE(STATE_AUTH_FINALIZE); 291965c4fdfbSgilles CASE(STATE_BODY); 292065c4fdfbSgilles CASE(STATE_QUIT); 2921b6d81129Seric default: 2922ef75c9feSgilles (void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state); 292365c4fdfbSgilles return (buf); 2924b6d81129Seric } 2925b6d81129Seric } 29266771f06dSgilles 29276771f06dSgilles 29286771f06dSgilles static void 29296771f06dSgilles smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns, 29306771f06dSgilles const struct sockaddr_storage *ss_src, 29316771f06dSgilles const struct sockaddr_storage *ss_dest) 29326771f06dSgilles { 29336771f06dSgilles if (! SESSION_FILTERED(s)) 29346771f06dSgilles return; 29356771f06dSgilles 29366771f06dSgilles report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest); 29376771f06dSgilles } 29386771f06dSgilles 29396771f06dSgilles static void 29406771f06dSgilles smtp_report_link_greeting(struct smtp_session *s, 29416771f06dSgilles const char *domain) 29426771f06dSgilles { 29436771f06dSgilles if (! SESSION_FILTERED(s)) 29446771f06dSgilles return; 29456771f06dSgilles 29466771f06dSgilles report_smtp_link_greeting("smtp-in", s->id, domain); 29476771f06dSgilles } 29486771f06dSgilles 29496771f06dSgilles static void 29506771f06dSgilles smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity) 29516771f06dSgilles { 29526771f06dSgilles if (! SESSION_FILTERED(s)) 29536771f06dSgilles return; 29546771f06dSgilles 29556771f06dSgilles report_smtp_link_identify("smtp-in", s->id, method, identity); 29566771f06dSgilles } 29576771f06dSgilles 29586771f06dSgilles static void 29596771f06dSgilles smtp_report_link_tls(struct smtp_session *s, const char *ssl) 29606771f06dSgilles { 29616771f06dSgilles if (! SESSION_FILTERED(s)) 29626771f06dSgilles return; 29636771f06dSgilles 29646771f06dSgilles report_smtp_link_tls("smtp-in", s->id, ssl); 29656771f06dSgilles } 29666771f06dSgilles 29676771f06dSgilles static void 29686771f06dSgilles smtp_report_link_disconnect(struct smtp_session *s) 29696771f06dSgilles { 29706771f06dSgilles if (! SESSION_FILTERED(s)) 29716771f06dSgilles return; 29726771f06dSgilles 29736771f06dSgilles report_smtp_link_disconnect("smtp-in", s->id); 29746771f06dSgilles } 29756771f06dSgilles 29766771f06dSgilles static void 29776771f06dSgilles smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result) 29786771f06dSgilles { 29796771f06dSgilles if (! SESSION_FILTERED(s)) 29806771f06dSgilles return; 29816771f06dSgilles 29826771f06dSgilles report_smtp_link_auth("smtp-in", s->id, user, result); 29836771f06dSgilles } 29846771f06dSgilles 29856771f06dSgilles static void 29866771f06dSgilles smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid) 29876771f06dSgilles { 29886771f06dSgilles if (! SESSION_FILTERED(s)) 29896771f06dSgilles return; 29906771f06dSgilles 29916771f06dSgilles report_smtp_tx_reset("smtp-in", s->id, msgid); 29926771f06dSgilles } 29936771f06dSgilles 29946771f06dSgilles static void 29956771f06dSgilles smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid) 29966771f06dSgilles { 29976771f06dSgilles if (! SESSION_FILTERED(s)) 29986771f06dSgilles return; 29996771f06dSgilles 30006771f06dSgilles report_smtp_tx_begin("smtp-in", s->id, msgid); 30016771f06dSgilles } 30026771f06dSgilles 30036771f06dSgilles static void 30046771f06dSgilles smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok) 30056771f06dSgilles { 3006a2d3e7c3Sgilles char mailaddr[SMTPD_MAXMAILADDRSIZE]; 3007a2d3e7c3Sgilles char *p; 3008a2d3e7c3Sgilles 30096771f06dSgilles if (! SESSION_FILTERED(s)) 30106771f06dSgilles return; 30116771f06dSgilles 3012a2d3e7c3Sgilles if ((p = strchr(address, '<')) == NULL) 3013a2d3e7c3Sgilles return; 3014a2d3e7c3Sgilles (void)strlcpy(mailaddr, p + 1, sizeof mailaddr); 3015a2d3e7c3Sgilles if ((p = strchr(mailaddr, '>')) == NULL) 3016a2d3e7c3Sgilles return; 3017a2d3e7c3Sgilles *p = '\0'; 3018a2d3e7c3Sgilles 3019a2d3e7c3Sgilles report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok); 30206771f06dSgilles } 30216771f06dSgilles 30226771f06dSgilles static void 30236771f06dSgilles smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok) 30246771f06dSgilles { 3025a2d3e7c3Sgilles char mailaddr[SMTPD_MAXMAILADDRSIZE]; 3026a2d3e7c3Sgilles char *p; 3027a2d3e7c3Sgilles 30286771f06dSgilles if (! SESSION_FILTERED(s)) 30296771f06dSgilles return; 30306771f06dSgilles 3031a2d3e7c3Sgilles if ((p = strchr(address, '<')) == NULL) 3032a2d3e7c3Sgilles return; 3033a2d3e7c3Sgilles (void)strlcpy(mailaddr, p + 1, sizeof mailaddr); 3034a2d3e7c3Sgilles if ((p = strchr(mailaddr, '>')) == NULL) 3035a2d3e7c3Sgilles return; 3036a2d3e7c3Sgilles *p = '\0'; 3037a2d3e7c3Sgilles 3038a2d3e7c3Sgilles report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok); 30396771f06dSgilles } 30406771f06dSgilles 30416771f06dSgilles static void 30426771f06dSgilles smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid) 30436771f06dSgilles { 30446771f06dSgilles if (! SESSION_FILTERED(s)) 30456771f06dSgilles return; 30466771f06dSgilles 30476771f06dSgilles report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid); 30486771f06dSgilles } 30496771f06dSgilles 30506771f06dSgilles static void 30516771f06dSgilles smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok) 30526771f06dSgilles { 30536771f06dSgilles if (! SESSION_FILTERED(s)) 30546771f06dSgilles return; 30556771f06dSgilles 30566771f06dSgilles report_smtp_tx_data("smtp-in", s->id, msgid, ok); 30576771f06dSgilles } 30586771f06dSgilles 30596771f06dSgilles static void 30606771f06dSgilles smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz) 30616771f06dSgilles { 30626771f06dSgilles if (! SESSION_FILTERED(s)) 30636771f06dSgilles return; 30646771f06dSgilles 30656771f06dSgilles report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz); 30666771f06dSgilles } 30676771f06dSgilles 30686771f06dSgilles static void 30696771f06dSgilles smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid) 30706771f06dSgilles { 30716771f06dSgilles if (! SESSION_FILTERED(s)) 30726771f06dSgilles return; 30736771f06dSgilles 30746771f06dSgilles report_smtp_tx_rollback("smtp-in", s->id, msgid); 30756771f06dSgilles } 30766771f06dSgilles 30776771f06dSgilles static void 30786771f06dSgilles smtp_report_protocol_client(struct smtp_session *s, const char *command) 30796771f06dSgilles { 30806771f06dSgilles if (! SESSION_FILTERED(s)) 30816771f06dSgilles return; 30826771f06dSgilles 30836771f06dSgilles report_smtp_protocol_client("smtp-in", s->id, command); 30846771f06dSgilles } 30856771f06dSgilles 30866771f06dSgilles static void 30876771f06dSgilles smtp_report_protocol_server(struct smtp_session *s, const char *response) 30886771f06dSgilles { 30896771f06dSgilles if (! SESSION_FILTERED(s)) 30906771f06dSgilles return; 30916771f06dSgilles 30926771f06dSgilles report_smtp_protocol_server("smtp-in", s->id, response); 30936771f06dSgilles } 30946771f06dSgilles 30956771f06dSgilles static void 30966771f06dSgilles smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param) 30976771f06dSgilles { 30986771f06dSgilles if (! SESSION_FILTERED(s)) 30996771f06dSgilles return; 31006771f06dSgilles 31016771f06dSgilles report_smtp_filter_response("smtp-in", s->id, phase, response, param); 31026771f06dSgilles } 31036771f06dSgilles 31046771f06dSgilles static void 31056771f06dSgilles smtp_report_timeout(struct smtp_session *s) 31066771f06dSgilles { 31076771f06dSgilles if (! SESSION_FILTERED(s)) 31086771f06dSgilles return; 31096771f06dSgilles 31106771f06dSgilles report_smtp_timeout("smtp-in", s->id); 31116771f06dSgilles } 3112