X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/46d2a5e6f6e7709d172903b13945d23fc0a2c888..a75ebe0dcc5faeb915cacb0d9db66d2475789116:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 9055020e1..b50070cfa 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -226,7 +227,7 @@ static smtp_cmd_list *cmd_list_end = /* This list of names is used for performing the smtp_no_mail logging action. It must be kept in step with the SCH_xxx enumerations. */ -static uschar *smtp_names[] = +uschar * smtp_names[] = { US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN", US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", @@ -1032,25 +1033,6 @@ had_command_sigterm = sig; #ifdef SUPPORT_PROXY -/************************************************* -* Restore socket timeout to previous value * -*************************************************/ -/* If the previous value was successfully retrieved, restore -it before returning control to the non-proxy routines - -Arguments: fd - File descriptor for input - get_ok - Successfully retrieved previous values - tvtmp - Time struct with previous values - vslen - Length of time struct -Returns: none -*/ -static void -restore_socket_timeout(int fd, int get_ok, struct timeval * tvtmp, socklen_t vslen) -{ -if (get_ok == 0) - (void) setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS tvtmp, vslen); -} - /************************************************* * Check if host is required proxy host * *************************************************/ @@ -1127,7 +1109,7 @@ if (cr != NULL) while (capacity > 0) { - do { ret = recv(fd, to, 1, 0); } while (ret == -1 && errno == EINTR); + do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout); if (ret == -1) return -1; have++; @@ -1236,15 +1218,8 @@ struct timeval tvtmp; socklen_t vslen = sizeof(struct timeval); BOOL yield = FALSE; -/* Save current socket timeout values */ -get_ok = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tvtmp, &vslen); - -/* Proxy Protocol host must send header within a short time -(default 3 seconds) or it's considered invalid */ -tv.tv_sec = PROXY_NEGOTIATION_TIMEOUT_SEC; -tv.tv_usec = PROXY_NEGOTIATION_TIMEOUT_USEC; -if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tv, sizeof(tv)) < 0) - goto bad; +os_non_restarting_signal(SIGALRM, command_timeout_handler); +ALARM(proxy_protocol_timeout); do { @@ -1252,9 +1227,9 @@ do don't do a PEEK into the data, actually slurp up enough to be "safe". Can't take it all because TLS-on-connect clients follow immediately with TLS handshake. */ - ret = recv(fd, &hdr, PROXY_INITIAL_READ, 0); + ret = read(fd, &hdr, PROXY_INITIAL_READ); } - while (ret == -1 && errno == EINTR); + while (ret == -1 && errno == EINTR && !had_command_timeout); if (ret == -1) goto proxyfail; @@ -1268,8 +1243,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) /* First get the length fields. */ do { - retmore = recv(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ, 0); - } while (retmore == -1 && errno == EINTR); + retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ); + } while (retmore == -1 && errno == EINTR && !had_command_timeout); if (retmore == -1) goto proxyfail; ret += retmore; @@ -1305,8 +1280,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) { do { - retmore = recv(fd, (uschar*)&hdr + ret, size-ret, 0); - } while (retmore == -1 && errno == EINTR); + retmore = read(fd, (uschar*)&hdr + ret, size-ret); + } while (retmore == -1 && errno == EINTR && !had_command_timeout); if (retmore == -1) goto proxyfail; ret += retmore; @@ -1534,7 +1509,8 @@ done: should cause a synchronization failure */ proxyfail: - restore_socket_timeout(fd, get_ok, &tvtmp, vslen); + DEBUG(D_receive) if (had_command_timeout) + debug_printf("Timeout while reading proxy header\n"); bad: if (yield) @@ -1550,6 +1526,7 @@ bad: debug_printf("Failure to extract proxied host, only QUIT allowed\n"); } +ALARM(0); return; } #endif @@ -1732,6 +1709,7 @@ for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) return; case QUIT_CMD: + f.smtp_in_quit = TRUE; smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); mac_smtp_fflush(); return; @@ -1800,7 +1778,7 @@ s_tlslog(gstring * g) if (LOGGING(tls_cipher) && tls_in.cipher) { g = string_append(g, 2, US" X=", tls_in.cipher); -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED) g = string_catn(g, US"*", 1); #endif @@ -1810,11 +1788,31 @@ if (LOGGING(tls_certificate_verified) && tls_in.cipher) 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"\""); + g = string_append(g, 2, US" SNI=", string_printing2(tls_in.sni, SP_TAB|SP_SPACE)); return g; } #endif + + +static gstring * +s_connhad_log(gstring * g) +{ +const uschar * sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE + ? US" C=..." : US" C="; + +for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++) + if (smtp_connection_had[i] != SCH_NONE) + { + g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); + sep = US","; + } +for (int i = 0; i < smtp_ch_index; i++, sep = US",") + g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); +return g; +} + + /************************************************* * Log lack of MAIL if so configured * *************************************************/ @@ -1830,7 +1828,7 @@ Returns: nothing void smtp_log_no_mail(void) { -uschar * sep, * s; +uschar * s; gstring * g = NULL; if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail)) @@ -1846,20 +1844,7 @@ if (sender_host_authenticated) g = s_tlslog(g); #endif -sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE ? US" C=..." : US" C="; - -for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++) - if (smtp_connection_had[i] != SCH_NONE) - { - g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); - sep = US","; - } - -for (int i = 0; i < smtp_ch_index; i++) - { - g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); - sep = US","; - } +g = s_connhad_log(g); if (!(s = string_from_gstring(g))) s = US""; @@ -2037,29 +2022,32 @@ rcpt_count = rcpt_defer_count = rcpt_fail_count = raw_recipients_count = recipients_count = recipients_list_max = 0; message_linecount = 0; message_size = -1; +message_body = message_body_end = NULL; acl_added_headers = NULL; acl_removed_headers = NULL; f.queue_only_policy = FALSE; rcpt_smtp_response = NULL; fl.rcpt_smtp_response_same = TRUE; fl.rcpt_in_progress = FALSE; -f.deliver_freeze = FALSE; /* Can be set by ACL */ -freeze_tell = freeze_tell_config; /* Can be set by ACL */ -fake_response = OK; /* Can be set by ACL */ +f.deliver_freeze = FALSE; /* Can be set by ACL */ +freeze_tell = freeze_tell_config; /* Can be set by ACL */ +fake_response = OK; /* Can be set by ACL */ #ifdef WITH_CONTENT_SCAN -f.no_mbox_unspool = FALSE; /* Can be set by ACL */ +f.no_mbox_unspool = FALSE; /* Can be set by ACL */ #endif -f.submission_mode = FALSE; /* Can be set by ACL */ +f.submission_mode = FALSE; /* Can be set by ACL */ f.suppress_local_fixups = f.suppress_local_fixups_default; /* Can be set by ACL */ -f.active_local_from_check = local_from_check; /* Can be set by ACL */ -f.active_local_sender_retain = local_sender_retain; /* Can be set by ACL */ +f.active_local_from_check = local_from_check; /* Can be set by ACL */ +f.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_data = deliver_domain_data = +recipient_data = sender_data = NULL; /* Can be set by ACL */ +recipient_verify_failure = 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 */ +submission_name = NULL; /* Can be set by ACL */ raw_sender = NULL; /* After SMTP rewrite, before qualifying */ sender_address_unrewritten = NULL; /* Set only after verify rewrite */ sender_verified_list = NULL; /* No senders verified */ @@ -2113,23 +2101,7 @@ ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ acl_var_m = NULL; -/* The message body variables use malloc store. They may be set if this is -not the first message in an SMTP session and the previous message caused them -to be referenced in an ACL. */ - -if (message_body) - { - store_free(message_body); - message_body = NULL; - } - -if (message_body_end) - { - store_free(message_body_end); - message_body_end = NULL; - } - -/* Warning log messages are also saved in malloc store. They are saved to avoid +/* Warning log messages are saved in malloc store. They are saved to avoid repetition in the same message, but it seems right to repeat them for different messages. */ @@ -2354,8 +2326,9 @@ while (done <= 0) break; - case EOF_CMD: case QUIT_CMD: + f.smtp_in_quit = TRUE; + case EOF_CMD: done = 2; break; @@ -2415,7 +2388,7 @@ TCP_SYN_RCV (as of 12.1) so no idea about data-use. */ if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_FASTOPEN, &is_fastopen, &len) == 0) { - if (is_fastopen) + if (is_fastopen) { DEBUG(D_receive) debug_printf("TFO mode connection (TCP_FASTOPEN getsockopt)\n"); @@ -2937,7 +2910,7 @@ if (check_proxy_protocol_host()) #ifndef DISABLE_TLS if (tls_in.on_connect) { - if (tls_server_start(tls_require_ciphers, &user_msg) != OK) + if (tls_server_start(&user_msg) != OK) return smtp_log_tls_fail(user_msg); cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; } @@ -3039,7 +3012,7 @@ if (!check_sync()) #endif { unsigned n = smtp_inend - smtp_inptr; - if (n > 32) n = 32; + if (n > 128) n = 128; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol " "synchronization error (input sent without waiting for greeting): " @@ -3100,15 +3073,17 @@ synprot_error(int type, int code, uschar *data, uschar *errmess) int yield = -1; log_write(type, LOG_MAIN, "SMTP %s error in \"%s\" %s %s", - (type == L_smtp_syntax_error)? "syntax" : "protocol", + type == L_smtp_syntax_error ? "syntax" : "protocol", string_printing(smtp_cmd_buffer), host_and_ident(TRUE), errmess); if (++synprot_error_count > smtp_max_synprot_errors) { yield = 1; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " - "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); + "syntax or protocol errors (last command was \"%s\", %s)", + host_and_ident(FALSE), string_printing(smtp_cmd_buffer), + string_from_gstring(s_connhad_log(NULL)) + ); } if (code > 0) @@ -3194,7 +3169,7 @@ for (;;) { smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg); msg = nl + 1; - while (isspace(*msg)) msg++; + Uskip_whitespace(&msg); } } } @@ -3838,14 +3813,13 @@ static void smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp) { HAD(SCH_QUIT); +f.smtp_in_quit = TRUE; incomplete_transaction_log(US"QUIT"); -if (acl_smtp_quit) - { - int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp); - if (rc == ERROR) +if ( acl_smtp_quit + && acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp) + == ERROR) log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", *log_msgp); - } #ifdef TCP_CORK (void) setsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_CORK, US &on, sizeof(on)); @@ -4156,8 +4130,10 @@ while (done <= 0) if (++synprot_error_count > smtp_max_synprot_errors) { log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " - "syntax or protocol errors (last command was \"%s\")", - host_and_ident(FALSE), string_printing(smtp_cmd_buffer)); + "syntax or protocol errors (last command was \"%s\", %s)", + host_and_ident(FALSE), string_printing(smtp_cmd_buffer), + string_from_gstring(s_connhad_log(NULL)) + ); done = 1; } @@ -4498,10 +4474,8 @@ while (done <= 0) # endif else #endif + (void) fwrite(g->s, 1, g->ptr, smtp_out); - { - int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */ - } DEBUG(D_receive) { uschar *cr; @@ -4861,8 +4835,8 @@ while (done <= 0) and EXPN etc. to be used when space is short. */ if (!receive_check_fs( - (smtp_check_spool_space && message_size >= 0)? - message_size + 5000 : 0)) + smtp_check_spool_space && message_size >= 0 + ? message_size + 5000 : 0)) { smtp_printf("452 Space shortage, please try later\r\n", FALSE); sender_address = NULL; @@ -5171,9 +5145,9 @@ while (done <= 0) recipients_list[recipients_count-1].orcpt = orcpt; recipients_list[recipients_count-1].dsn_flags = dsn_flags; - DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", + /* DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", recipients_list[recipients_count-1].orcpt, - recipients_list[recipients_count-1].dsn_flags); + recipients_list[recipients_count-1].dsn_flags); */ } /* The recipient was discarded */ @@ -5188,8 +5162,8 @@ while (done <= 0) discarded = TRUE; log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: " "discarded by %s ACL%s%s", host_and_ident(TRUE), - sender_address_unrewritten? sender_address_unrewritten : sender_address, - smtp_cmd_argument, f.recipients_discarded? "MAIL" : "RCPT", + sender_address_unrewritten ? sender_address_unrewritten : sender_address, + smtp_cmd_argument, f.recipients_discarded ? "MAIL" : "RCPT", log_msg ? US": " : US"", log_msg ? log_msg : US""); } @@ -5287,10 +5261,10 @@ while (done <= 0) } if (f.smtp_in_pipelining_advertised && last_was_rcpt) smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE, - smtp_names[smtp_connection_had[smtp_ch_index-1]]); + smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]); else done = synprot_error(L_smtp_protocol_error, 503, NULL, - smtp_connection_had[smtp_ch_index-1] == SCH_DATA + smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)] == SCH_DATA ? US"valid RCPT command must precede DATA" : US"valid RCPT command must precede BDAT"); @@ -5492,7 +5466,7 @@ while (done <= 0) STARTTLS that don't add to the nonmail command count. */ s = NULL; - if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK) + if ((rc = tls_server_start(&s)) == OK) { if (!tls_remember_esmtp) fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE; @@ -5554,6 +5528,7 @@ while (done <= 0) some sense is perhaps "right". */ case QUIT_CMD: + f.smtp_in_quit = TRUE; user_msg = NULL; if ( acl_smtp_quit && ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, @@ -5770,7 +5745,7 @@ while (done <= 0) /* If not serializing, do the exec right away. Otherwise, fork down into another process. */ - if ( !smtp_etrn_serialize + if ( !smtp_etrn_serialize || (pid = exim_fork(US"etrn-serialised-command")) == 0) { DEBUG(D_exec) debug_print_argv(argv); @@ -5919,12 +5894,14 @@ if (!sender_host_authenticated) 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); +if (Ustrcmp(sender_host_auth_pubname, "tls") == 0) + g = authenticated_id + ? string_append(g, 2, US") x509.auth=", authenticated_id) + : string_cat(g, US") reason=x509.auth"); else - g = string_catn(g, US") reason=x509.auth", 17); + g = authenticated_id + ? string_append(g, 2, US") smtp.auth=", authenticated_id) + : string_cat(g, US", no id saved)"); if (authenticated_sender) g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);