X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fsmtp_in.c;h=d1c19ea0d6b15d76c3c4ffd20221e2266856fe04;hb=8ac90765750f87c573300b9e953af3d8090cab8b;hp=36f6856772d0b02f74e2041c6e8b8986b3732fa8;hpb=a2673768b71ee86c71e16e46d53d0ffc4f66b0de;p=exim.git diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 36f685677..d1c19ea0d 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2017 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -135,6 +135,9 @@ static auth_instance *authenticated_by; static BOOL auth_advertised; #ifdef SUPPORT_TLS static BOOL tls_advertised; +# ifdef EXPERIMENTAL_REQUIRETLS +static BOOL requiretls_advertised; +# endif #endif static BOOL dsn_advertised; static BOOL esmtp; @@ -187,7 +190,7 @@ static smtp_cmd_list cmd_list[] = { { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, #ifdef SUPPORT_TLS { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, - { "tls_auth", 0, TLS_AUTH_CMD, FALSE, TRUE }, + { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, #endif /* If you change anything above here, also fix the definitions below. */ @@ -255,6 +258,9 @@ enum { ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID, #ifdef SUPPORT_I18N ENV_MAIL_OPT_UTF8, +#endif +#ifdef EXPERIMENTAL_REQUIRETLS + ENV_MAIL_OPT_REQTLS, #endif }; typedef struct { @@ -274,6 +280,10 @@ static env_mail_type_t env_mail_type_list[] = { { US"ENVID", ENV_MAIL_OPT_ENVID, TRUE }, #ifdef SUPPORT_I18N { US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */ +#endif +#ifdef EXPERIMENTAL_REQUIRETLS + /* https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-03 */ + { US"REQUIRETLS",ENV_MAIL_OPT_REQTLS, FALSE }, #endif /* keep this the last entry */ { US"NULL", ENV_MAIL_OPT_NULL, FALSE }, @@ -340,7 +350,7 @@ fd_set fds; struct timeval tzero; #ifdef SUPPORT_TLS -if (tls_in.active >= 0) +if (tls_in.active.sock >= 0) return !tls_could_read(); #endif @@ -359,16 +369,13 @@ rc = smtp_getc(GETC_BUFFER_UNLIMITED); if (rc < 0) return TRUE; /* End of file or error */ smtp_ungetc(rc); -rc = smtp_inend - smtp_inptr; -if (rc > 150) rc = 150; -smtp_inptr[rc] = 0; return FALSE; } static BOOL check_sync(void) { -if (!smtp_enforce_sync || sender_host_address == NULL || sender_host_notsocket) +if (!smtp_enforce_sync || !sender_host_address || sender_host_notsocket) return TRUE; return wouldblock_reading(); @@ -427,6 +434,53 @@ log_write(L_smtp_incomplete_transaction, LOG_MAIN|LOG_SENDER|LOG_RECIPIENTS, +void +smtp_command_timeout_exit(void) +{ +log_write(L_lost_incoming_connection, + LOG_MAIN, "SMTP command timeout on%s connection from %s", + tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE)); +if (smtp_batched_input) + moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ +smtp_notquit_exit(US"command-timeout", US"421", + US"%s: SMTP command timeout - closing connection", + smtp_active_hostname); +exim_exit(EXIT_FAILURE, US"receiving"); +} + +void +smtp_command_sigterm_exit(void) +{ +log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); +if (smtp_batched_input) + moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ +smtp_notquit_exit(US"signal-exit", US"421", + US"%s: Service not available - closing connection", smtp_active_hostname); +exim_exit(EXIT_FAILURE, US"receiving"); +} + +void +smtp_data_timeout_exit(void) +{ +log_write(L_lost_incoming_connection, + LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>", + sender_fullhost ? sender_fullhost : US"local process", sender_address); +receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout"); +/* Does not return */ +} + +void +smtp_data_sigint_exit(void) +{ +log_write(0, LOG_MAIN, "%s closed after %s", + smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT"); +receive_bomb_out(US"signal-exit", + US"Service not available - SIGTERM or SIGINT received"); +/* Does not return */ +} + + + /* Refill the buffer, and notify DKIM verification code. Return false for error or EOF. */ @@ -439,22 +493,33 @@ if (!smtp_out) return FALSE; fflush(smtp_out); if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); -/* Limit amount read, so non-message data is not fed to DKIM */ +/* Limit amount read, so non-message data is not fed to DKIM. +Take care to not touch the safety NUL at the end of the buffer. */ -rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE, lim)); +rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim)); save_errno = errno; -alarm(0); +if (smtp_receive_timeout > 0) alarm(0); if (rc <= 0) { /* Must put the error text in fixed store, because this might be during header reading, where it releases unused store above the header. */ if (rc < 0) { + if (had_command_timeout) /* set by signal handler */ + smtp_command_timeout_exit(); /* does not return */ + if (had_command_sigterm) + smtp_command_sigterm_exit(); + if (had_data_timeout) + smtp_data_timeout_exit(); + if (had_data_sigint) + smtp_data_sigint_exit(); + smtp_had_error = save_errno; smtp_read_error = string_copy_malloc( string_sprintf(" (error: %s)", strerror(save_errno))); } - else smtp_had_eof = 1; + else + smtp_had_eof = 1; return FALSE; } #ifndef DISABLE_DKIM @@ -540,7 +605,7 @@ uschar * log_msg; for(;;) { #ifndef DISABLE_DKIM - BOOL dkim_save; + unsigned dkim_save; #endif if (chunking_data_left > 0) @@ -551,7 +616,7 @@ for(;;) receive_ungetc = lwr_receive_ungetc; #ifndef DISABLE_DKIM dkim_save = dkim_collect_input; - dkim_collect_input = FALSE; + dkim_collect_input = 0; #endif /* Unless PIPELINING was offered, there should be no next command @@ -565,11 +630,12 @@ for(;;) incomplete_transaction_log(US"sync failure"); log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error " "(next input sent too soon: pipelining was not advertised): " - "rejected \"%s\" %s next input=\"%s\"", + "rejected \"%s\" %s next input=\"%s\"%s", smtp_cmd_buffer, host_and_ident(TRUE), - string_printing(string_copyn(smtp_inptr, n))); - (void) synprot_error(L_smtp_protocol_error, 554, NULL, - US"SMTP synchronization error"); + string_printing(string_copyn(smtp_inptr, n)), + smtp_inend - smtp_inptr > n ? "..." : ""); + (void) synprot_error(L_smtp_protocol_error, 554, NULL, + US"SMTP synchronization error"); goto repeat_until_rset; } @@ -652,7 +718,7 @@ next_cmd: } receive_getc = bdat_getc; - receive_getbuf = bdat_getbuf; + receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */ receive_ungetc = bdat_ungetc; #ifndef DISABLE_DKIM dkim_collect_input = dkim_save; @@ -680,8 +746,11 @@ return buf; void bdat_flush_data(void) { -unsigned n = chunking_data_left; -(void) bdat_getbuf(&n); +while (chunking_data_left) + { + unsigned n = chunking_data_left; + if (!bdat_getbuf(&n)) break; + } receive_getc = lwr_receive_getc; receive_getbuf = lwr_receive_getbuf; @@ -841,7 +910,7 @@ if (!yield) { log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()"); smtp_closedown(US"Unexpected error"); - exim_exit(EXIT_FAILURE); + exim_exit(EXIT_FAILURE, NULL); } /* If this is the first output for a (non-batch) RCPT command, see if all RCPTs @@ -863,9 +932,9 @@ if (rcpt_in_progress) /* Now write the string */ #ifdef SUPPORT_TLS -if (tls_in.active >= 0) +if (tls_in.active.sock >= 0) { - if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer), more) < 0) + if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0) smtp_write_error = -1; } else @@ -892,7 +961,7 @@ Returns: 0 for no error; -1 after an error int smtp_fflush(void) { -if (tls_in.active < 0 && fflush(smtp_out) != 0) smtp_write_error = -1; +if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1; return smtp_write_error; } @@ -912,16 +981,7 @@ Returns: nothing static void command_timeout_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ -log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP command timeout on%s connection from %s", - (tls_in.active >= 0)? " TLS" : "", - host_and_ident(FALSE)); -if (smtp_batched_input) - moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ -smtp_notquit_exit(US"command-timeout", US"421", - US"%s: SMTP command timeout - closing connection", smtp_active_hostname); -exim_exit(EXIT_FAILURE); +had_command_timeout = sig; } @@ -939,13 +999,7 @@ Returns: nothing static void command_sigterm_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ -log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); -if (smtp_batched_input) - moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ -smtp_notquit_exit(US"signal-exit", US"421", - US"%s: Service not available - closing connection", smtp_active_hostname); -exim_exit(EXIT_FAILURE); +had_command_sigterm = sig; } @@ -1505,6 +1559,7 @@ int ptr = 0; smtp_cmd_list *p; BOOL hadnull = FALSE; +had_command_timeout = 0; os_non_restarting_signal(SIGALRM, command_timeout_handler); while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF) @@ -1610,11 +1665,11 @@ if (proxy_session && proxy_session_failed) /* Enforce synchronization for unknown commands */ -if (smtp_inptr < smtp_inend && /* Outstanding input */ - check_sync && /* Local flag set */ - smtp_enforce_sync && /* Global flag set */ - sender_host_address != NULL && /* Not local input */ - !sender_host_notsocket) /* Really is a socket */ +if ( smtp_inptr < smtp_inend /* Outstanding input */ + && check_sync /* Local flag set */ + && smtp_enforce_sync /* Global flag set */ + && sender_host_address /* Not local input */ + && !sender_host_notsocket) /* Really is a socket */ return BADSYN_CMD; return OTHER_CMD; @@ -1642,27 +1697,27 @@ Returns: nothing void smtp_closedown(uschar *message) { -if (smtp_in == NULL || smtp_batched_input) return; +if (!smtp_in || smtp_batched_input) return; receive_swallow_smtp(); smtp_printf("421 %s\r\n", FALSE, message); for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { case EOF_CMD: - return; + return; case QUIT_CMD: - smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); - mac_smtp_fflush(); - return; + smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); + mac_smtp_fflush(); + return; case RSET_CMD: - smtp_printf("250 Reset OK\r\n", FALSE); - break; + smtp_printf("250 Reset OK\r\n", FALSE); + break; default: - smtp_printf("421 %s\r\n", FALSE, message); - break; + smtp_printf("421 %s\r\n", FALSE, message); + break; } } @@ -1710,37 +1765,22 @@ return string_sprintf("SMTP connection from %s", hostname); /* Append TLS-related information to a log line Arguments: - s String under construction: allocated string to extend, or NULL - sizep Pointer to current allocation size (update on return), or NULL - ptrp Pointer to index for new entries in string (update on return), or NULL + g String under construction: allocated string to extend, or NULL Returns: Allocated string or NULL */ -static uschar * -s_tlslog(uschar * s, int * sizep, int * ptrp) +static gstring * +s_tlslog(gstring * g) { - int size = sizep ? *sizep : 0; - int ptr = ptrp ? *ptrp : 0; - - if (LOGGING(tls_cipher) && tls_in.cipher != NULL) - s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher); - if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL) - s = string_append(s, &size, &ptr, 2, US" CV=", - tls_in.certificate_verified? "yes":"no"); - if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL) - s = string_append(s, &size, &ptr, 3, US" DN=\"", - string_printing(tls_in.peerdn), US"\""); - if (LOGGING(tls_sni) && tls_in.sni != NULL) - s = string_append(s, &size, &ptr, 3, US" SNI=\"", - string_printing(tls_in.sni), US"\""); - - if (s) - { - s[ptr] = '\0'; - if (sizep) *sizep = size; - if (ptrp) *ptrp = ptr; - } - return s; +if (LOGGING(tls_cipher) && tls_in.cipher) + g = string_append(g, 2, US" X=", tls_in.cipher); +if (LOGGING(tls_certificate_verified) && tls_in.cipher) + g = string_append(g, 2, US" CV=", tls_in.certificate_verified? "yes":"no"); +if (LOGGING(tls_peerdn) && tls_in.peerdn) + g = string_append(g, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); +if (LOGGING(tls_sni) && tls_in.sni) + g = string_append(g, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); +return g; } #endif @@ -1759,45 +1799,40 @@ Returns: nothing void smtp_log_no_mail(void) { -int size, ptr, i; -uschar *s, *sep; +int i; +uschar * sep, * s; +gstring * g = NULL; if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail)) return; -s = NULL; -size = ptr = 0; - -if (sender_host_authenticated != NULL) +if (sender_host_authenticated) { - s = string_append(s, &size, &ptr, 2, US" A=", sender_host_authenticated); - if (authenticated_id != NULL) - s = string_append(s, &size, &ptr, 2, US":", authenticated_id); + g = string_append(g, 2, US" A=", sender_host_authenticated); + if (authenticated_id) g = string_append(g, 2, US":", authenticated_id); } #ifdef SUPPORT_TLS -s = s_tlslog(s, &size, &ptr); +g = s_tlslog(g); #endif -sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)? - US" C=..." : US" C="; +sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE ? US" C=..." : US" C="; + for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++) - { if (smtp_connection_had[i] != SCH_NONE) { - s = string_append(s, &size, &ptr, 2, sep, - smtp_names[smtp_connection_had[i]]); + g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); sep = US","; } - } for (i = 0; i < smtp_ch_index; i++) { - s = string_append(s, &size, &ptr, 2, sep, smtp_names[smtp_connection_had[i]]); + g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); sep = US","; } -if (s) s[ptr] = 0; else s = US""; +if (!(s = string_from_gstring(g))) s = US""; + log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s", tcp_in_fastopen ? US"TFO " : US"", host_and_ident(FALSE), string_timesince(&smtp_connection_start), s); @@ -1809,17 +1844,19 @@ log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s", uschar * smtp_cmd_hist(void) { -uschar * list = NULL; -int size = 0, len = 0, i; +int i; +gstring * list = NULL; +uschar * s; for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++) if (smtp_connection_had[i] != SCH_NONE) - list = string_append_listele(list, &size, &len, ',', - smtp_names[smtp_connection_had[i]]); + list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]); + for (i = 0; i < smtp_ch_index; i++) - list = string_append_listele(list, &size, &len, ',', - smtp_names[smtp_connection_had[i]]); -return list ? list : US""; + list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]); + +s = string_from_gstring(list); +return s ? s : US""; } @@ -1852,7 +1889,7 @@ BOOL yield = helo_accept_junk; /* Discard any previous helo name */ -if (sender_helo_name != NULL) +if (sender_helo_name) { store_free(sender_helo_name); sender_helo_name = NULL; @@ -1861,7 +1898,7 @@ if (sender_helo_name != NULL) /* Skip tests if junk is permitted. */ if (!yield) - { + /* Allow the new standard form for IPv6 address literals, namely, [IPv6:....], and because someone is bound to use it, allow an equivalent IPv4 form. Allow plain addresses as well. */ @@ -1884,21 +1921,14 @@ if (!yield) /* Non-literals must be alpha, dot, hyphen, plus any non-valid chars that have been configured (usually underscore - sigh). */ - else if (*s != 0) - { - yield = TRUE; - while (*s != 0) - { + else if (*s) + for (yield = TRUE; *s; s++) if (!isalnum(*s) && *s != '.' && *s != '-' && Ustrchr(helo_allow_chars, *s) == NULL) { yield = FALSE; break; } - s++; - } - } - } /* Save argument if OK */ @@ -1968,13 +1998,13 @@ return TRUE; *************************************************/ /* This function is called whenever the SMTP session is reset from -within either of the setup functions. +within either of the setup functions; also from the daemon loop. Argument: the stacking pool storage reset point Returns: nothing */ -static void +void smtp_reset(void *reset_point) { recipients_list = NULL; @@ -2001,8 +2031,8 @@ active_local_sender_retain = local_sender_retain; /* Can be set by ACL */ sending_ip_address = NULL; return_path = sender_address = NULL; sender_data = NULL; /* Can be set by ACL */ -deliver_localpart_orig = NULL; -deliver_domain_orig = NULL; +deliver_localpart_parent = deliver_localpart_orig = NULL; +deliver_domain_parent = deliver_domain_orig = NULL; callout_address = NULL; submission_name = NULL; /* Can be set by ACL */ raw_sender = NULL; /* After SMTP rewrite, before qualifying */ @@ -2017,10 +2047,27 @@ bmi_run = 0; bmi_verdicts = NULL; #endif dnslist_domain = dnslist_matched = NULL; +#ifdef SUPPORT_SPF +spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL; +spf_result_guessed = FALSE; +#endif #ifndef DISABLE_DKIM -dkim_signers = NULL; +dkim_cur_signer = dkim_signers = +dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL; +dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL; dkim_disable_verify = FALSE; -dkim_collect_input = FALSE; +dkim_collect_input = 0; +dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL; +dkim_key_length = 0; +dkim_verify_signers = US"$dkim_signers"; +#endif +#ifdef EXPERIMENTAL_DMARC +dmarc_has_been_checked = dmarc_disable_verify = dmarc_enable_forensic = FALSE; +dmarc_domain_policy = dmarc_status = dmarc_status_text = +dmarc_used_domain = NULL; +#endif +#ifdef EXPERIMENTAL_ARC +arc_state = arc_state_reason = NULL; #endif dsn_ret = 0; dsn_envid = NULL; @@ -2028,12 +2075,6 @@ deliver_host = deliver_host_address = NULL; /* Can be set by ACL */ #ifndef DISABLE_PRDR prdr_requested = FALSE; #endif -#ifdef EXPERIMENTAL_SPF -spf_header_comment = NULL; -spf_received = NULL; -spf_result = NULL; -spf_smtp_comment = NULL; -#endif #ifdef SUPPORT_I18N message_smtputf8 = FALSE; #endif @@ -2131,14 +2172,14 @@ while (done <= 0) case HELO_CMD: case EHLO_CMD: - check_helo(smtp_cmd_data); - /* Fall through */ + check_helo(smtp_cmd_data); + /* Fall through */ case RSET_CMD: - cancel_cutthrough_connection(TRUE, US"RSET received"); - smtp_reset(reset_point); - bsmtp_transaction_linecount = receive_linecount; - break; + cancel_cutthrough_connection(TRUE, US"RSET received"); + smtp_reset(reset_point); + bsmtp_transaction_linecount = receive_linecount; + break; /* The MAIL FROM command requires an address as an operand. All we @@ -2148,53 +2189,53 @@ while (done <= 0) it is the canonical extracted address which is all that is kept. */ case MAIL_CMD: - smtp_mailcmd_count++; /* Count for no-mail log */ - if (sender_address != NULL) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given"); + smtp_mailcmd_count++; /* Count for no-mail log */ + if (sender_address != NULL) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given"); - if (smtp_cmd_data[0] == 0) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "501 MAIL FROM must have an address operand"); + if (smtp_cmd_data[0] == 0) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "501 MAIL FROM must have an address operand"); - /* Reset to start of message */ + /* Reset to start of message */ - cancel_cutthrough_connection(TRUE, US"MAIL received"); - smtp_reset(reset_point); + cancel_cutthrough_connection(TRUE, US"MAIL received"); + smtp_reset(reset_point); - /* Apply SMTP rewrite */ + /* Apply SMTP rewrite */ - raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)? - rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE, - US"", global_rewrite_rules) : smtp_cmd_data; + raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)? + rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE, + US"", global_rewrite_rules) : smtp_cmd_data; - /* Extract the address; the TRUE flag allows <> as valid */ + /* Extract the address; the TRUE flag allows <> as valid */ - raw_sender = - parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain, - TRUE); + raw_sender = + parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain, + TRUE); - if (raw_sender == NULL) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess); + if (!raw_sender) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess); - sender_address = string_copy(raw_sender); + sender_address = string_copy(raw_sender); - /* Qualify unqualified sender addresses if permitted to do so. */ + /* Qualify unqualified sender addresses if permitted to do so. */ - if (sender_domain == 0 && sender_address[0] != 0 && sender_address[0] != '@') - { - if (allow_unqualified_sender) - { - sender_address = rewrite_address_qualify(sender_address, FALSE); - DEBUG(D_receive) debug_printf("unqualified address %s accepted " - "and rewritten\n", raw_sender); - } - /* The function moan_smtp_batch() does not return. */ - else moan_smtp_batch(smtp_cmd_buffer, "501 sender address must contain " - "a domain"); - } - break; + if ( !sender_domain + && sender_address[0] != 0 && sender_address[0] != '@') + if (allow_unqualified_sender) + { + sender_address = rewrite_address_qualify(sender_address, FALSE); + DEBUG(D_receive) debug_printf("unqualified address %s accepted " + "and rewritten\n", raw_sender); + } + /* The function moan_smtp_batch() does not return. */ + else + moan_smtp_batch(smtp_cmd_buffer, "501 sender address must contain " + "a domain"); + break; /* The RCPT TO command requires an address as an operand. All we do @@ -2205,53 +2246,54 @@ while (done <= 0) extracted address. */ case RCPT_CMD: - if (sender_address == NULL) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "503 No sender yet given"); + if (!sender_address) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "503 No sender yet given"); - if (smtp_cmd_data[0] == 0) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "501 RCPT TO must have an address operand"); + if (smtp_cmd_data[0] == 0) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, + "501 RCPT TO must have an address operand"); - /* Check maximum number allowed */ + /* Check maximum number allowed */ - if (recipients_max > 0 && recipients_count + 1 > recipients_max) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients", - recipients_max_reject? "552": "452"); + if (recipients_max > 0 && recipients_count + 1 > recipients_max) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients", + recipients_max_reject? "552": "452"); - /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a - recipient address */ + /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a + recipient address */ - recipient = rewrite_existflags & rewrite_smtp - ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", - global_rewrite_rules) - : smtp_cmd_data; + recipient = rewrite_existflags & rewrite_smtp + ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + global_rewrite_rules) + : smtp_cmd_data; - recipient = parse_extract_address(recipient, &errmess, &start, &end, - &recipient_domain, FALSE); + recipient = parse_extract_address(recipient, &errmess, &start, &end, + &recipient_domain, FALSE); - if (!recipient) - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess); + if (!recipient) + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess); - /* If the recipient address is unqualified, qualify it if permitted. Then - add it to the list of recipients. */ + /* If the recipient address is unqualified, qualify it if permitted. Then + add it to the list of recipients. */ - if (recipient_domain == 0) - { - if (allow_unqualified_recipient) - { - DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", - recipient); - recipient = rewrite_address_qualify(recipient, TRUE); - } - /* The function moan_smtp_batch() does not return. */ - else moan_smtp_batch(smtp_cmd_buffer, "501 recipient address must contain " - "a domain"); - } - receive_add_recipient(recipient, -1); - break; + if (!recipient_domain) + if (allow_unqualified_recipient) + { + DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", + recipient); + recipient = rewrite_address_qualify(recipient, TRUE); + } + /* The function moan_smtp_batch() does not return. */ + else + moan_smtp_batch(smtp_cmd_buffer, + "501 recipient address must contain a domain"); + + receive_add_recipient(recipient, -1); + break; /* The DATA command is legal only if it follows successful MAIL FROM @@ -2259,22 +2301,20 @@ while (done <= 0) command is encountered. */ case DATA_CMD: - if (sender_address == NULL || recipients_count <= 0) - { - /* The function moan_smtp_batch() does not return. */ - if (sender_address == NULL) - moan_smtp_batch(smtp_cmd_buffer, - "503 MAIL FROM: command must precede DATA"); + if (!sender_address || recipients_count <= 0) + /* The function moan_smtp_batch() does not return. */ + if (!sender_address) + moan_smtp_batch(smtp_cmd_buffer, + "503 MAIL FROM: command must precede DATA"); + else + moan_smtp_batch(smtp_cmd_buffer, + "503 RCPT TO: must precede DATA"); else - moan_smtp_batch(smtp_cmd_buffer, - "503 RCPT TO: must precede DATA"); - } - else - { - done = 3; /* DATA successfully achieved */ - message_ended = END_NOTENDED; /* Indicate in middle of message */ - } - break; + { + done = 3; /* DATA successfully achieved */ + message_ended = END_NOTENDED; /* Indicate in middle of message */ + } + break; /* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */ @@ -2284,32 +2324,32 @@ while (done <= 0) case HELP_CMD: case NOOP_CMD: case ETRN_CMD: - bsmtp_transaction_linecount = receive_linecount; - break; + bsmtp_transaction_linecount = receive_linecount; + break; case EOF_CMD: case QUIT_CMD: - done = 2; - break; + done = 2; + break; case BADARG_CMD: - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected argument data"); - break; + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected argument data"); + break; case BADCHAR_CMD: - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected NULL in SMTP command"); - break; + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected NULL in SMTP command"); + break; default: - /* The function moan_smtp_batch() does not return. */ - moan_smtp_batch(smtp_cmd_buffer, "500 Command unrecognized"); - break; + /* The function moan_smtp_batch() does not return. */ + moan_smtp_batch(smtp_cmd_buffer, "500 Command unrecognized"); + break; } } @@ -2346,7 +2386,7 @@ if ( getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0 && tinfo.tcpi_state == TCP_SYN_RECV ) { - DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection\n"); + DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n"); tcp_in_fastopen = TRUE; } # endif @@ -2370,11 +2410,11 @@ Returns: FALSE if the session can not continue; something has BOOL smtp_start_session(void) { -int size = 256; -int ptr, esclen; +int esclen; uschar *user_msg, *log_msg; uschar *code, *esc; -uschar *p, *s, *ss; +uschar *p, *s; +gstring * ss; gettimeofday(&smtp_connection_start, NULL); for (smtp_ch_index = 0; smtp_ch_index < SMTP_HBUFF_SIZE; smtp_ch_index++) @@ -2397,7 +2437,8 @@ smtp_exit_function_called = FALSE; /* For avoiding loop in not-quit exit */ /* If receiving by -bs from a trusted user, or testing with -bh, we allow authentication settings from -oMaa to remain in force. */ -if (!host_checking && !sender_host_notsocket) sender_host_authenticated = NULL; +if (!host_checking && !sender_host_notsocket) + sender_host_auth_pubname = sender_host_authenticated = NULL; authenticated_by = NULL; #ifdef SUPPORT_TLS @@ -2406,6 +2447,9 @@ tls_in.ourcert = tls_in.peercert = NULL; tls_in.sni = NULL; tls_in.ocsp = OCSP_NOT_REQ; tls_advertised = FALSE; +# ifdef EXPERIMENTAL_REQUIRETLS +requiretls_advertised = FALSE; +# endif #endif dsn_advertised = FALSE; #ifdef SUPPORT_I18N @@ -2441,10 +2485,12 @@ else (sender_host_address ? protocols : protocols_local) [pnormal]; /* Set up the buffer for inputting using direct read() calls, and arrange to -call the local functions instead of the standard C ones. */ +call the local functions instead of the standard C ones. Place a NUL at the +end of the buffer to safety-stop C-string reads from it. */ if (!(smtp_inbuffer = US malloc(IN_BUFFER_SIZE))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer"); +smtp_inbuffer[IN_BUFFER_SIZE-1] = '\0'; receive_getc = smtp_getc; receive_getbuf = smtp_getbuf; @@ -2459,7 +2505,7 @@ smtp_had_eof = smtp_had_error = 0; /* Set up the message size limit; this may be host-specific */ thismessage_size_limit = expand_string_integer(message_size_limit, TRUE); -if (expand_string_message != NULL) +if (expand_string_message) { if (thismessage_size_limit == -1) log_write(0, LOG_MAIN|LOG_PANIC, "unable to expand message_size_limit: " @@ -2840,8 +2886,12 @@ if (check_proxy_protocol_host()) smtps port for use with older style SSL MTAs. */ #ifdef SUPPORT_TLS - if (tls_in.on_connect && tls_server_start(tls_require_ciphers, &user_msg) != OK) - return smtp_log_tls_fail(user_msg); + if (tls_in.on_connect) + { + if (tls_server_start(tls_require_ciphers, &user_msg) != OK) + return smtp_log_tls_fail(user_msg); + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + } #endif /* Run the connect ACL if it exists */ @@ -2898,34 +2948,31 @@ command. Sigh. To try to avoid this, build the complete greeting message first, and output it in one fell swoop. This gives a better chance of it ending up as a single packet. */ -ss = store_get(size); -ptr = 0; +ss = string_get(256); p = s; do /* At least once, in case we have an empty string */ { int len; uschar *linebreak = Ustrchr(p, '\n'); - ss = string_catn(ss, &size, &ptr, code, 3); - if (linebreak == NULL) + ss = string_catn(ss, code, 3); + if (!linebreak) { len = Ustrlen(p); - ss = string_catn(ss, &size, &ptr, US" ", 1); + ss = string_catn(ss, US" ", 1); } else { len = linebreak - p; - ss = string_catn(ss, &size, &ptr, US"-", 1); + ss = string_catn(ss, US"-", 1); } - ss = string_catn(ss, &size, &ptr, esc, esclen); - ss = string_catn(ss, &size, &ptr, p, len); - ss = string_catn(ss, &size, &ptr, US"\r\n", 2); + ss = string_catn(ss, esc, esclen); + ss = string_catn(ss, p, len); + ss = string_catn(ss, US"\r\n", 2); p += len; - if (linebreak != NULL) p++; + if (linebreak) p++; } -while (*p != 0); - -ss[ptr] = 0; /* string_cat leaves room for this */ +while (*p); /* Before we write the banner, check that there is no input pending, unless this synchronisation check is disabled. */ @@ -2945,7 +2992,7 @@ if (!check_sync()) /* Now output the banner */ -smtp_printf("%s", FALSE, ss); +smtp_printf("%s", FALSE, string_from_gstring(ss)); /* Attempt to see if we sent the banner before the last ACK of the 3-way handshake arrived. If so we must have managed a TFO. */ @@ -3155,7 +3202,7 @@ return; /* This function is called when acl_check() fails. As well as calls from within this module, it is called from receive.c for an ACL after DATA. It sorts out -logging the incident, and sets up the error response. A message containing +logging the incident, and sends the error response. A message containing newlines is turned into a multiline SMTP response, but for logging, only the first line is used. @@ -3323,7 +3370,8 @@ is closing if required and return 2. */ if (log_reject_target != 0) { #ifdef SUPPORT_TLS - uschar * tls = s_tlslog(NULL, NULL, NULL); + gstring * g = s_tlslog(NULL); + uschar * tls = string_from_gstring(g); if (!tls) tls = US""; #else uschar * tls = US""; @@ -3542,7 +3590,7 @@ else HDEBUG(D_receive) debug_printf("getting IP address for %s\n", sender_helo_name); - rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A | HOST_FIND_BY_AAAA, NULL, NULL, NULL, &d, NULL, NULL); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) for (hh = &h; hh; hh = hh->next) @@ -3645,12 +3693,13 @@ switch(rc) { if (set_id) authenticated_id = string_copy_malloc(set_id); sender_host_authenticated = au->name; + sender_host_auth_pubname = au->public_name; authentication_failed = FALSE; authenticated_fail_id = NULL; /* Impossible to already be set? */ received_protocol = (sender_host_address ? protocols : protocols_local) - [pextend + pauthed + (tls_in.active >= 0 ? pcrpted:0)]; + [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)]; *s = *ss = US"235 Authentication succeeded"; authenticated_by = au; break; @@ -3744,7 +3793,7 @@ else smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); #ifdef SUPPORT_TLS -tls_close(TRUE, TRUE); +tls_close(NULL, TLS_SHUTDOWN_NOWAIT); #endif log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", @@ -3816,11 +3865,11 @@ cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; #ifdef SUPPORT_TLS cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; -cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; #endif /* Set the local signal handler for SIGTERM - it tries to end off tidily */ +had_command_sigterm = 0; os_non_restarting_signal(SIGTERM, command_sigterm_handler); /* Batched SMTP is handled in a different function. */ @@ -3846,39 +3895,40 @@ while (done <= 0) void (*oldsignal)(int); pid_t pid; int start, end, sender_domain, recipient_domain; - int ptr, size, rc; + int rc; int c; auth_instance *au; uschar *orcpt = NULL; int flags; + gstring * g; #ifdef AUTH_TLS /* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */ - if ( tls_in.active >= 0 + if ( tls_in.active.sock >= 0 && tls_in.peercert && tls_in.certificate_verified && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd ) { cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; - if ( acl_smtp_auth - && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, - &user_msg, &log_msg)) != OK - ) - { - done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); - continue; - } for (au = auths; au; au = au->next) if (strcmpic(US"tls", au->driver_name) == 0) { - smtp_cmd_data = NULL; - - if (smtp_in_auth(au, &s, &ss) == OK) - { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); } + if ( acl_smtp_auth + && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth, + &user_msg, &log_msg)) != OK + ) + done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg); else - { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); } + { + smtp_cmd_data = NULL; + + if (smtp_in_auth(au, &s, &ss) == OK) + { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); } + else + { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); } + } break; } } @@ -4060,9 +4110,9 @@ while (done <= 0) /* Force a reverse lookup if HELO quoted something in helo_lookup_domains because otherwise the log can be confusing. */ - if (sender_host_name == NULL && - (deliver_domain = sender_helo_name, /* set $domain */ - match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, + if ( !sender_host_name + && (deliver_domain = sender_helo_name, /* set $domain */ + match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK) (void)host_name_lookup(); @@ -4071,7 +4121,7 @@ while (done <= 0) host_build_sender_fullhost(); /* Rebuild */ set_process_info("handling%s incoming connection from %s", - (tls_in.active >= 0)? " TLS" : "", host_and_ident(FALSE)); + (tls_in.active.sock >= 0)? " TLS" : "", host_and_ident(FALSE)); /* Verify if configured. This doesn't give much security, but it does make some people happy to be able to do it. If helo_required is set, @@ -4102,7 +4152,7 @@ while (done <= 0) } } -#ifdef EXPERIMENTAL_SPF +#ifdef SUPPORT_SPF /* set up SPF context */ spf_init(sender_helo_name, sender_host_address); #endif @@ -4115,7 +4165,11 @@ while (done <= 0) &user_msg, &log_msg)) != OK) { done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg); - sender_helo_name = NULL; + if (sender_helo_name) + { + store_free(sender_helo_name); + sender_helo_name = NULL; + } host_build_sender_fullhost(); /* Rebuild */ break; } @@ -4131,6 +4185,9 @@ while (done <= 0) pipelining_advertised = FALSE; #ifdef SUPPORT_TLS tls_advertised = FALSE; +# ifdef EXPERIMENTAL_REQUIRETLS + requiretls_advertised = FALSE; +# endif #endif dsn_advertised = FALSE; #ifdef SUPPORT_I18N @@ -4146,15 +4203,13 @@ while (done <= 0) sender_ident ? sender_ident : US"", sender_ident ? US" at " : US"", sender_host_name ? sender_host_name : sender_helo_name); - - ptr = Ustrlen(s); - size = ptr + 1; + g = string_cat(NULL, s); if (sender_host_address) { - s = string_catn(s, &size, &ptr, US" [", 2); - s = string_cat (s, &size, &ptr, sender_host_address); - s = string_catn(s, &size, &ptr, US"]", 1); + g = string_catn(g, US" [", 2); + g = string_cat (g, sender_host_address); + g = string_catn(g, US"]", 1); } } @@ -4174,18 +4229,17 @@ while (done <= 0) "newlines: message truncated: %s", string_printing(s)); *ss = 0; } - ptr = Ustrlen(s); - size = ptr + 1; + g = string_cat(NULL, s); } - s = string_catn(s, &size, &ptr, US"\r\n", 2); + g = string_catn(g, US"\r\n", 2); /* If we received EHLO, we must create a multiline response which includes the functions supported. */ if (esmtp) { - s[3] = '-'; + g->s[3] = '-'; /* I'm not entirely happy with this, as an MTA is supposed to check that it has enough room to accept a message of maximum size before @@ -4197,12 +4251,12 @@ while (done <= 0) { sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code, thismessage_size_limit); - s = string_cat(s, &size, &ptr, big_buffer); + g = string_cat(g, big_buffer); } else { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-SIZE\r\n", 7); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-SIZE\r\n", 7); } /* Exim does not do protocol conversion or data conversion. It is 8-bit @@ -4214,15 +4268,15 @@ while (done <= 0) if (accept_8bitmime) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-8BITMIME\r\n", 11); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-8BITMIME\r\n", 11); } /* Advertise DSN support if configured to do so. */ if (verify_check_host(&dsn_advertise_hosts) != FAIL) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-DSN\r\n", 6); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-DSN\r\n", 6); dsn_advertised = TRUE; } @@ -4231,18 +4285,18 @@ while (done <= 0) if (acl_smtp_etrn) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-ETRN\r\n", 7); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-ETRN\r\n", 7); } if (acl_smtp_vrfy) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-VRFY\r\n", 7); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-VRFY\r\n", 7); } if (acl_smtp_expn) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-EXPN\r\n", 7); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-EXPN\r\n", 7); } /* Exim is quite happy with pipelining, so let the other end know that @@ -4251,8 +4305,8 @@ while (done <= 0) if (pipelining_enable && verify_check_host(&pipelining_advertise_hosts) == OK) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-PIPELINING\r\n", 13); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-PIPELINING\r\n", 13); sync_cmd_limit = NON_SYNC_CMD_PIPELINING; pipelining_advertised = TRUE; } @@ -4293,29 +4347,29 @@ while (done <= 0) int saveptr; if (first) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-AUTH", 5); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-AUTH", 5); first = FALSE; auth_advertised = TRUE; } - saveptr = ptr; - s = string_catn(s, &size, &ptr, US" ", 1); - s = string_cat (s, &size, &ptr, au->public_name); - while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]); + saveptr = g->ptr; + g = string_catn(g, US" ", 1); + g = string_cat (g, au->public_name); + while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]); au->advertised = TRUE; } } } - if (!first) s = string_catn(s, &size, &ptr, US"\r\n", 2); + if (!first) g = string_catn(g, US"\r\n", 2); } /* RFC 3030 CHUNKING */ if (verify_check_host(&chunking_advertise_hosts) != FAIL) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-CHUNKING\r\n", 11); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-CHUNKING\r\n", 11); chunking_offered = TRUE; chunking_state = CHUNKING_OFFERED; } @@ -4326,21 +4380,32 @@ while (done <= 0) secure connection. */ #ifdef SUPPORT_TLS - if (tls_in.active < 0 && + if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-STARTTLS\r\n", 11); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-STARTTLS\r\n", 11); tls_advertised = TRUE; } + +# ifdef EXPERIMENTAL_REQUIRETLS + /* Advertise REQUIRETLS only once we are in a secure connection */ + if ( tls_in.active.sock >= 0 + && verify_check_host(&tls_advertise_requiretls) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-REQUIRETLS\r\n", 13); + requiretls_advertised = TRUE; + } +# endif #endif #ifndef DISABLE_PRDR /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-PRDR\r\n", 7); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-PRDR\r\n", 7); } #endif @@ -4348,36 +4413,36 @@ while (done <= 0) if ( accept_8bitmime && verify_check_host(&smtputf8_advertise_hosts) != FAIL) { - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US"-SMTPUTF8\r\n", 11); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US"-SMTPUTF8\r\n", 11); smtputf8_advertised = TRUE; } #endif /* Finish off the multiline reply with one that is always available. */ - s = string_catn(s, &size, &ptr, smtp_code, 3); - s = string_catn(s, &size, &ptr, US" HELP\r\n", 7); + g = string_catn(g, smtp_code, 3); + g = string_catn(g, US" HELP\r\n", 7); } /* Terminate the string (for debug), write it, and note that HELO/EHLO has been seen. */ - s[ptr] = 0; - #ifdef SUPPORT_TLS - if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr, FALSE); else + if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else #endif { - int i = fwrite(s, 1, ptr, smtp_out); i = i; /* compiler quietening */ + int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */ } DEBUG(D_receive) { uschar *cr; - while ((cr = Ustrchr(s, '\r')) != NULL) /* lose CRs */ - memmove(cr, cr + 1, (ptr--) - (cr - s)); - debug_printf("SMTP>> %s", s); + + (void) string_from_gstring(g); + while ((cr = Ustrchr(g->s, '\r')) != NULL) /* lose CRs */ + memmove(cr, cr + 1, (g->ptr--) - (cr - g->s)); + debug_printf("SMTP>> %s", g->s); } helo_seen = TRUE; @@ -4387,7 +4452,7 @@ while (done <= 0) [ (esmtp ? pextend + (sender_host_authenticated ? pauthed : 0) : pnormal) - + (tls_in.active >= 0 ? pcrpted : 0) + + (tls_in.active.sock >= 0 ? pcrpted : 0) ]; cancel_cutthrough_connection(TRUE, US"sent EHLO response"); smtp_reset(reset_point); @@ -4415,14 +4480,14 @@ while (done <= 0) break; } - if (sender_address != NULL) + if (sender_address) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"sender already given"); break; } - if (smtp_cmd_data[0] == 0) + if (!*smtp_cmd_data) { done = synprot_error(L_smtp_protocol_error, 501, NULL, US"MAIL must have an address operand"); @@ -4519,7 +4584,7 @@ while (done <= 0) /* Check if RET has already been set */ if (dsn_ret > 0) { - synprot_error(L_smtp_syntax_error, 501, NULL, + done = synprot_error(L_smtp_syntax_error, 501, NULL, US"RET can be specified once only"); goto COMMAND_LOOP; } @@ -4532,7 +4597,7 @@ while (done <= 0) /* Check for invalid invalid value, and exit with error */ if (dsn_ret == 0) { - synprot_error(L_smtp_syntax_error, 501, NULL, + done = synprot_error(L_smtp_syntax_error, 501, NULL, US"Value for RET is invalid"); goto COMMAND_LOOP; } @@ -4542,9 +4607,9 @@ while (done <= 0) if (dsn_advertised) { /* Check if the dsn envid has been already set */ - if (dsn_envid != NULL) + if (dsn_envid) { - synprot_error(L_smtp_syntax_error, 501, NULL, + done = synprot_error(L_smtp_syntax_error, 501, NULL, US"ENVID can be specified once only"); goto COMMAND_LOOP; } @@ -4575,10 +4640,10 @@ while (done <= 0) US"invalid data for AUTH"); goto COMMAND_LOOP; } - if (acl_smtp_mailauth == NULL) + if (!acl_smtp_mailauth) { ignore_msg = US"client not authenticated"; - rc = (sender_host_authenticated != NULL)? OK : FAIL; + rc = sender_host_authenticated ? OK : FAIL; } else { @@ -4631,18 +4696,50 @@ while (done <= 0) #ifdef SUPPORT_I18N case ENV_MAIL_OPT_UTF8: - if (smtputf8_advertised) + if (!smtputf8_advertised) { - int old_pool = store_pool; + done = synprot_error(L_smtp_syntax_error, 501, NULL, + US"SMTPUTF8 used when not advertised"); + goto COMMAND_LOOP; + } - DEBUG(D_receive) debug_printf("smtputf8 requested\n"); - message_smtputf8 = allow_utf8_domains = TRUE; + DEBUG(D_receive) debug_printf("smtputf8 requested\n"); + message_smtputf8 = allow_utf8_domains = TRUE; + if (Ustrncmp(received_protocol, US"utf8", 4) != 0) + { + int old_pool = store_pool; store_pool = POOL_PERM; received_protocol = string_sprintf("utf8%s", received_protocol); store_pool = old_pool; } break; #endif + +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + case ENV_MAIL_OPT_REQTLS: + { + const uschar * list = value; + int sep = ','; + const uschar * opt; + uschar * r, * t; + + if (!requiretls_advertised) + { + done = synprot_error(L_smtp_syntax_error, 555, NULL, + US"unadvertised MAIL option: REQUIRETLS"); + goto COMMAND_LOOP; + } + + DEBUG(D_receive) debug_printf("requiretls requested\n"); + tls_requiretls = REQUIRETLS_MSG; + + r = string_copy_malloc(received_protocol); + if ((t = Ustrrchr(r, 's'))) *t = 'S'; + received_protocol = r; + } + break; +#endif + /* No valid option. Stick back the terminator characters and break the loop. Do the name-terminator second as extract_option sets value==name when it found no equal-sign. @@ -4660,6 +4757,17 @@ while (done <= 0) if (arg_error) break; } +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + if (tls_requiretls & REQUIRETLS_MSG) + { + /* Ensure headers-only bounces whether a RET option was given or not. */ + + DEBUG(D_receive) if (dsn_ret == dsn_ret_full) + debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n"); + dsn_ret = dsn_ret_hdrs; + } +#endif + /* If we have passed the threshold for rate limiting, apply the current delay, and update it for next time, provided this is a limited host. */ @@ -4736,8 +4844,7 @@ while (done <= 0) in which case just qualify the address. The flag is set above at the start of the SMTP connection. */ - if (sender_domain == 0 && sender_address[0] != 0) - { + if (!sender_domain && *sender_address) if (allow_unqualified_sender) { sender_domain = Ustrlen(sender_address) + 1; @@ -4758,7 +4865,6 @@ while (done <= 0) sender_address = NULL; break; } - } /* Apply an ACL check if one is defined, before responding. Afterwards, when pipelining is not advertised, do another sync check in case the ACL @@ -4863,7 +4969,7 @@ while (done <= 0) /* Check whether orcpt has been already set */ if (orcpt) { - synprot_error(L_smtp_syntax_error, 501, NULL, + done = synprot_error(L_smtp_syntax_error, 501, NULL, US"ORCPT can be specified once only"); goto COMMAND_LOOP; } @@ -4876,7 +4982,7 @@ while (done <= 0) /* Check if the notify flags have been already set */ if (flags > 0) { - synprot_error(L_smtp_syntax_error, 501, NULL, + done = synprot_error(L_smtp_syntax_error, 501, NULL, US"NOTIFY can be specified once only"); goto COMMAND_LOOP; } @@ -4908,7 +5014,7 @@ while (done <= 0) else { /* Catch any strange values */ - synprot_error(L_smtp_syntax_error, 501, NULL, + done = synprot_error(L_smtp_syntax_error, 501, NULL, US"Invalid value for NOTIFY parameter"); goto COMMAND_LOOP; } @@ -5112,17 +5218,24 @@ while (done <= 0) DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", (int)chunking_state, chunking_data_left); + /* push the current receive_* function on the "stack", and + replace them by bdat_getc(), which in turn will use the lwr_receive_* + functions to do the dirty work. */ lwr_receive_getc = receive_getc; lwr_receive_getbuf = receive_getbuf; lwr_receive_ungetc = receive_ungetc; + receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; + dot_ends = FALSE; + goto DATA_BDAT; } case DATA_CMD: HAD(SCH_DATA); + dot_ends = TRUE; DATA_BDAT: /* Common code for DATA and BDAT */ if (!discarded && recipients_count <= 0) @@ -5168,7 +5281,7 @@ while (done <= 0) ACL may have delayed. To handle cutthrough delivery enforce a dummy call to get the DATA command sent. */ - if (acl_smtp_predata == NULL && cutthrough.fd < 0) + if (acl_smtp_predata == NULL && cutthrough.cctx.sock < 0) rc = OK; else { @@ -5322,7 +5435,7 @@ while (done <= 0) { DEBUG(D_any) debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n"); - if (tls_in.active < 0) + if (tls_in.active.sock < 0) smtp_inend = smtp_inptr = smtp_inbuffer; /* and if TLS is already active, tls_server_start() should fail */ } @@ -5352,7 +5465,7 @@ while (done <= 0) cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE; cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; - if (sender_helo_name != NULL) + if (sender_helo_name) { store_free(sender_helo_name); sender_helo_name = NULL; @@ -5365,10 +5478,10 @@ while (done <= 0) [ (esmtp ? pextend + (sender_host_authenticated ? pauthed : 0) : pnormal) - + (tls_in.active >= 0 ? pcrpted : 0) + + (tls_in.active.sock >= 0 ? pcrpted : 0) ]; - sender_host_authenticated = NULL; + sender_host_auth_pubname = sender_host_authenticated = NULL; authenticated_id = NULL; sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING; DEBUG(D_tls) debug_printf("TLS active\n"); @@ -5425,7 +5538,7 @@ while (done <= 0) smtp_printf("554 Security failure\r\n", FALSE); break; } - tls_close(TRUE, TRUE); + tls_close(NULL, TLS_SHUTDOWN_NOWAIT); break; #endif @@ -5467,7 +5580,7 @@ while (done <= 0) buffer[0] = 0; Ustrcat(buffer, " AUTH"); #ifdef SUPPORT_TLS - if (tls_in.active < 0 && + if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) Ustrcat(buffer, " STARTTLS"); #endif @@ -5513,7 +5626,7 @@ while (done <= 0) case ETRN_CMD: HAD(SCH_ETRN); - if (sender_address != NULL) + if (sender_address) { done = synprot_error(L_smtp_protocol_error, 503, NULL, US"ETRN is not permitted inside a transaction"); @@ -5539,7 +5652,7 @@ while (done <= 0) since that is strictly the only kind of ETRN that can be implemented according to the RFC. */ - if (smtp_etrn_command != NULL) + if (smtp_etrn_command) { uschar *error; BOOL rc; @@ -5680,8 +5793,8 @@ while (done <= 0) case BADCHAR_CMD: done = synprot_error(L_smtp_syntax_error, 0, NULL, /* Just logs */ - US"NULL character(s) present (shown as '?')"); - smtp_printf("501 NULL characters are not allowed in SMTP commands\r\n", FALSE); + US"NUL character(s) present (shown as '?')"); + smtp_printf("501 NUL characters are not allowed in SMTP commands\r\n", FALSE); break; @@ -5690,7 +5803,7 @@ while (done <= 0) if (smtp_inend >= smtp_inbuffer + IN_BUFFER_SIZE) smtp_inend = smtp_inbuffer + IN_BUFFER_SIZE - 1; c = smtp_inend - smtp_inptr; - if (c > 150) c = 150; + if (c > 150) c = 150; /* limit logged amount */ smtp_inptr[c] = 0; incomplete_transaction_log(US"sync failure"); log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error " @@ -5755,6 +5868,30 @@ while (done <= 0) return done - 2; /* Convert yield values */ } + + +gstring * +authres_smtpauth(gstring * g) +{ +if (!sender_host_authenticated) + return g; + +g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname); + +if (Ustrcmp(sender_host_auth_pubname, "tls") != 0) + g = string_append(g, 2, US") smtp.auth=", authenticated_id); +else if (authenticated_id) + g = string_append(g, 2, US") x509.auth=", authenticated_id); +else + g = string_catn(g, US") reason=x509.auth", 17); + +if (authenticated_sender) + g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender); +return g; +} + + + /* vi: aw ai sw=2 */ /* End of smtp_in.c */