X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/02b41d7106c67bbe862d6a44373034684a03c00e..cf0c61644d7dd2dfb29f6418d95bf4d8cae199ea:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 79a1ee02b..6963e7da2 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 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2017 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -44,11 +44,11 @@ The maximum size of a Kerberos ticket under Windows 2003 is 12000 bytes, and we need room to handle large base64-encoded AUTHs for GSSAPI. */ -#define smtp_cmd_buffer_size 16384 +#define SMTP_CMD_BUFFER_SIZE 16384 /* Size of buffer for reading SMTP incoming packets */ -#define in_buffer_size 8192 +#define IN_BUFFER_SIZE 8192 /* Structure for SMTP command list */ @@ -305,7 +305,7 @@ static int smtp_had_error; /* forward declarations */ int bdat_ungetc(int ch); -static int smtp_read_command(BOOL check_sync); +static int smtp_read_command(BOOL check_sync, unsigned buffer_lim); static int synprot_error(int type, int code, uschar *data, uschar *errmess); static void smtp_quit_handler(uschar **, uschar **); static void smtp_rset_handler(void); @@ -352,7 +352,7 @@ tzero.tv_usec = 0; rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero); if (rc <= 0) return TRUE; /* Not ready to read */ -rc = smtp_getc(); +rc = smtp_getc(GETC_BUFFER_UNLIMITED); if (rc < 0) return TRUE; /* End of file or error */ smtp_ungetc(rc); @@ -410,12 +410,12 @@ it flushes the output, and refills the buffer, with a timeout. The signal handler is set appropriately by the calling function. This function is not used after a connection has negotiated itself into an TLS/SSL state. -Arguments: none +Arguments: lim Maximum amount to read/buffer Returns: the next character or EOF */ int -smtp_getc(void) +smtp_getc(unsigned lim) { if (smtp_inptr >= smtp_inend) { @@ -423,7 +423,10 @@ if (smtp_inptr >= smtp_inend) if (!smtp_out) return EOF; fflush(smtp_out); if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); - rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size); + + /* Limit amount read, so non-message data is not fed to DKIM */ + + rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE, lim)); save_errno = errno; alarm(0); if (rc <= 0) @@ -471,23 +474,26 @@ to handle the BDAT command/response. Placed here due to the correlation with the above smtp_getc(), which it wraps, and also by the need to do smtp command/response handling. -Arguments: none +Arguments: lim (ignored) Returns: the next character or ERR, EOD or EOF */ int -bdat_getc(void) +bdat_getc(unsigned lim) { uschar * user_msg = NULL; uschar * log_msg; for(;;) { - if (chunking_data_left-- > 0) - return lwr_receive_getc(); + if (chunking_data_left > 0) + return lwr_receive_getc(chunking_data_left--); receive_getc = lwr_receive_getc; receive_ungetc = lwr_receive_ungetc; +#ifndef DISABLE_DKIM + dkim_collect_input = FALSE; +#endif /* Unless PIPELINING was offered, there should be no next command until after we ack that chunk */ @@ -516,21 +522,22 @@ for(;;) return EOD; } - chunking_state = CHUNKING_OFFERED; smtp_printf("250 %u byte chunk received\r\n", chunking_datasize); + chunking_state = CHUNKING_OFFERED; + DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); /* Expect another BDAT cmd from input. RFC 3030 says nothing about QUIT, RSET or NOOP but handling them seems obvious */ next_cmd: - switch(smtp_read_command(TRUE)) + switch(smtp_read_command(TRUE, 1)) { default: (void) synprot_error(L_smtp_protocol_error, 503, NULL, US"only BDAT permissible after non-LAST BDAT"); repeat_until_rset: - switch(smtp_read_command(TRUE)) + switch(smtp_read_command(TRUE, 1)) { case QUIT_CMD: smtp_quit_handler(&user_msg, &log_msg); /*FALLTHROUGH */ case EOF_CMD: return EOF; @@ -569,6 +576,8 @@ next_cmd: chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 ? CHUNKING_LAST : CHUNKING_ACTIVE; chunking_data_left = chunking_datasize; + DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", + (int)chunking_state, chunking_data_left); if (chunking_datasize == 0) if (chunking_state == CHUNKING_LAST) @@ -582,24 +591,30 @@ next_cmd: receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; +#ifndef DISABLE_DKIM + dkim_collect_input = TRUE; +#endif break; /* to top of main loop */ } } } } -static void +void bdat_flush_data(void) { -while (chunking_data_left-- > 0) - if (lwr_receive_getc() < 0) +while (chunking_data_left > 0) + if (lwr_receive_getc(chunking_data_left--) < 0) break; receive_getc = lwr_receive_getc; receive_ungetc = lwr_receive_ungetc; if (chunking_state != CHUNKING_LAST) + { chunking_state = CHUNKING_OFFERED; + DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state); + } } @@ -882,7 +897,8 @@ if (get_ok == 0) * Check if host is required proxy host * *************************************************/ /* The function determines if inbound host will be a regular smtp host -or if it is configured that it must use Proxy Protocol. +or if it is configured that it must use Proxy Protocol. A local +connection cannot. Arguments: none Returns: bool @@ -892,12 +908,10 @@ static BOOL check_proxy_protocol_host() { int rc; -/* Cannot configure local connection as a proxy inbound */ -if (sender_host_address == NULL) return proxy_session; -rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL, - sender_host_address, NULL); -if (rc == OK) +if ( sender_host_address + && (rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL, + sender_host_address, NULL)) == OK) { DEBUG(D_receive) debug_printf("Detected proxy protocol configured host\n"); @@ -907,6 +921,69 @@ return proxy_session; } +/************************************************* +* Read data until newline or end of buffer * +*************************************************/ +/* While SMTP is server-speaks-first, TLS is client-speaks-first, so we can't +read an entire buffer and assume there will be nothing past a proxy protocol +header. Our approach normally is to use stdio, but again that relies upon +"STARTTLS\r\n" and a server response before the client starts TLS handshake, or +reading _nothing_ before client TLS handshake. So we don't want to use the +usual buffering reads which may read enough to block TLS starting. + +So unfortunately we're down to "read one byte at a time, with a syscall each, +and expect a little overhead", for all proxy-opened connections which are v1, +just to handle the TLS-on-connect case. Since SSL functions wrap the +underlying fd, we can't assume that we can feed them any already-read content. + +We need to know where to read to, the max capacity, and we'll read until we +get a CR and one more character. Let the caller scream if it's CR+!LF. + +Return the amount read. +*/ + +static int +swallow_until_crlf(int fd, uschar *base, int already, int capacity) +{ + uschar *to = base + already; + uschar *cr; + int have = 0; + int ret; + int last = 0; + + /* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read + up through the \r; for the _normal_ case, we haven't yet seen the \r. */ + cr = memchr(base, '\r', already); + if (cr != NULL) + { + if ((cr - base) < already - 1) + { + /* \r and presumed \n already within what we have; probably not + actually proxy protocol, but abort cleanly. */ + return 0; + } + /* \r is last character read, just need one more. */ + last = 1; + } + + while (capacity > 0) + { + do { ret = recv(fd, to, 1, 0); } while (ret == -1 && errno == EINTR); + if (ret == -1) + return -1; + have++; + if (last) + return have; + if (*to == '\r') + last = 1; + capacity--; + to++; + } + // reached end without having room for a final newline, abort + errno = EOVERFLOW; + return -1; +} + /************************************************* * Setup host for proxy protocol * *************************************************/ @@ -919,7 +996,7 @@ Arguments: none Returns: Boolean success */ -static BOOL +static void setup_proxy_protocol_host() { union { @@ -959,14 +1036,45 @@ struct sockaddr_in tmpaddr; char tmpip6[INET6_ADDRSTRLEN]; struct sockaddr_in6 tmpaddr6; +/* We can't read "all data until end" because while SMTP is +server-speaks-first, the TLS handshake is client-speaks-first, so for +TLS-on-connect ports the proxy protocol header will usually be immediately +followed by a TLS handshake, and with N TLS libraries, we can't reliably +reinject data for reading by those. So instead we first read "enough to be +safely read within the header, and figure out how much more to read". +For v1 we will later read to the end-of-line, for v2 we will read based upon +the stated length. + +The v2 sig is 12 octets, and another 4 gets us the length, so we know how much +data is needed total. For v1, where the line looks like: +PROXY TCPn L3src L3dest SrcPort DestPort \r\n + +However, for v1 there's also `PROXY UNKNOWN\r\n` which is only 15 octets. +We seem to support that. So, if we read 14 octets then we can tell if we're +v2 or v1. If we're v1, we can continue reading as normal. + +If we're v2, we can't slurp up the entire header. We need the length in the +15th & 16th octets, then to read everything after that. + +So to safely handle v1 and v2, with client-sent-first supported correctly, +we have to do a minimum of 3 read calls, not 1. Eww. +*/ + +#define PROXY_INITIAL_READ 14 +#define PROXY_V2_HEADER_SIZE 16 +#if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE +# error Code bug in sizes of data to read for proxy usage +#endif + int get_ok = 0; int size, ret; int fd = fileno(smtp_in); const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; -uschar *iptype; /* To display debug info */ +uschar * iptype; /* To display debug info */ struct timeval tv; 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); @@ -976,23 +1084,37 @@ get_ok = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tvtmp, &vslen); 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) - return FALSE; + goto bad; do { /* The inbound host was declared to be a Proxy Protocol host, so - don't do a PEEK into the data, actually slurp it up. */ - ret = recv(fd, &hdr, sizeof(hdr), 0); + 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); } while (ret == -1 && errno == EINTR); if (ret == -1) goto proxyfail; -if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) +/* For v2, handle reading the length, and then the rest. */ +if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) { - uint8_t ver = (hdr.v2.ver_cmd & 0xf0) >> 4; - uint8_t cmd = (hdr.v2.ver_cmd & 0x0f); + int retmore; + uint8_t ver; + + /* 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); + if (retmore == -1) + goto proxyfail; + ret += retmore; + + ver = (hdr.v2.ver_cmd & 0xf0) >> 4; /* May 2014: haproxy combined the version and command into one byte to allow two full bytes for the length field in order to proxy SSL @@ -1004,15 +1126,43 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) DEBUG(D_receive) debug_printf("Invalid Proxy Protocol version: %d\n", ver); goto proxyfail; } - DEBUG(D_receive) debug_printf("Detected PROXYv2 header\n"); + /* The v2 header will always be 16 bytes per the spec. */ size = 16 + ntohs(hdr.v2.len); - if (ret < size) + DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n", + size, (int)sizeof(hdr)); + + /* We should now have 16 octets (PROXY_V2_HEADER_SIZE), and we know the total + amount that we need. Double-check that the size is not unreasonable, then + get the rest. */ + if (size > sizeof(hdr)) { - DEBUG(D_receive) debug_printf("Truncated or too large PROXYv2 header (%d/%d)\n", - ret, size); + DEBUG(D_receive) debug_printf("PROXYv2 header size unreasonably large; security attack?\n"); goto proxyfail; } + + do + { + do + { + retmore = recv(fd, (uschar*)&hdr + ret, size-ret, 0); + } while (retmore == -1 && errno == EINTR); + if (retmore == -1) + goto proxyfail; + ret += retmore; + DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size); + } while (ret < size); + + } /* end scope for getting rest of data for v2 */ + +/* At this point: if PROXYv2, we've read the exact size required for all data; +if PROXYv1 then we've read "less than required for any valid line" and should +read the rest". */ + +if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) + { + uint8_t cmd = (hdr.v2.ver_cmd & 0x0f); + switch (cmd) { case 0x01: /* PROXY command */ @@ -1080,6 +1230,7 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) break; case 0x00: /* LOCAL command */ /* Keep local connection address for LOCAL */ + iptype = US"local"; break; default: DEBUG(D_receive) @@ -1089,20 +1240,31 @@ if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) } else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) { - uschar *p = string_copy(hdr.v1.line); - uschar *end = memchr(p, '\r', ret - 1); + uschar *p; + uschar *end; uschar *sp; /* Utility variables follow */ int tmp_port; + int r2; char *endc; - if (!end || end[1] != '\n') + /* get the rest of the line */ + r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); + if (r2 == -1) + goto proxyfail; + ret += r2; + + p = string_copy(hdr.v1.line); + end = memchr(p, '\r', ret - 1); + + if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n') { DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n"); goto proxyfail; } *end = '\0'; /* Terminate the string */ - size = end + 2 - hdr.v1.line; /* Skip header + CRLF */ + size = end + 2 - p; /* Skip header + CRLF */ DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n"); + DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size); /* Step through the string looking for the required fields. Ensure strict adherence to required formatting, exit for any error. */ p += 5; @@ -1194,26 +1356,41 @@ else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) } proxy_external_port = tmp_port; /* Already checked for /r /n above. Good V1 header received. */ - goto done; } else { /* Wrong protocol */ DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n"); + (void) swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); goto proxyfail; } +done: + DEBUG(D_receive) + debug_printf("Valid %s sender from Proxy Protocol header\n", iptype); + yield = proxy_session; + +/* Don't flush any potential buffer contents. Any input on proxyfail +should cause a synchronization failure */ + proxyfail: -restore_socket_timeout(fd, get_ok, &tvtmp, vslen); -/* Don't flush any potential buffer contents. Any input should cause a - synchronization failure */ -return FALSE; + restore_socket_timeout(fd, get_ok, &tvtmp, vslen); -done: -restore_socket_timeout(fd, get_ok, &tvtmp, vslen); -DEBUG(D_receive) - debug_printf("Valid %s sender from Proxy Protocol header\n", iptype); -return proxy_session; +bad: + if (yield) + { + sender_host_name = NULL; + (void) host_name_lookup(); + host_build_sender_fullhost(); + } + else + { + proxy_session_failed = TRUE; + DEBUG(D_receive) + debug_printf("Failure to extract proxied host, only QUIT allowed\n"); + } + +return; } #endif @@ -1234,13 +1411,14 @@ signal handler that closes down the session on a timeout. Control does not return when it runs. Arguments: - check_sync if TRUE, check synchronization rules if global option is TRUE + check_sync if TRUE, check synchronization rules if global option is TRUE + buffer_lim maximum to buffer in lower layer Returns: a code identifying the command (enumerated above) */ static int -smtp_read_command(BOOL check_sync) +smtp_read_command(BOOL check_sync, unsigned buffer_lim) { int c; int ptr = 0; @@ -1249,9 +1427,9 @@ BOOL hadnull = FALSE; os_non_restarting_signal(SIGALRM, command_timeout_handler); -while ((c = (receive_getc)()) != '\n' && c != EOF) +while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF) { - if (ptr >= smtp_cmd_buffer_size) + if (ptr >= SMTP_CMD_BUFFER_SIZE) { os_non_restarting_signal(SIGALRM, sigalrm_handler); return OTHER_CMD; @@ -1290,14 +1468,11 @@ if required. */ for (p = cmd_list; p < cmd_list_end; p++) { - #ifdef SUPPORT_PROXY +#ifdef SUPPORT_PROXY /* Only allow QUIT command if Proxy Protocol parsing failed */ - if (proxy_session && proxy_session_failed) - { - if (p->cmd != QUIT_CMD) - continue; - } - #endif + if (proxy_session && proxy_session_failed && p->cmd != QUIT_CMD) + continue; +#endif if ( p->len && strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0 && ( smtp_cmd_buffer[p->len-1] == ':' /* "mail from:" or "rcpt to:" */ @@ -1391,7 +1566,7 @@ if (smtp_in == NULL || smtp_batched_input) return; receive_swallow_smtp(); smtp_printf("421 %s\r\n", message); -for (;;) switch(smtp_read_command(FALSE)) +for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { case EOF_CMD: return; @@ -1658,7 +1833,13 @@ uschar *n; uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1; while (isspace(*v)) v--; v[1] = 0; -while (v > smtp_cmd_data && *v != '=' && !isspace(*v)) v--; +while (v > smtp_cmd_data && *v != '=' && !isspace(*v)) + { + /* Take care to not stop at a space embedded in a quoted local-part */ + + if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1); + v--; + } n = v; if (*v == '=') @@ -1697,7 +1878,6 @@ Returns: nothing static void smtp_reset(void *reset_point) { -store_reset(reset_point); recipients_list = NULL; rcpt_count = rcpt_defer_count = rcpt_fail_count = raw_recipients_count = recipients_count = recipients_list_max = 0; @@ -1720,7 +1900,12 @@ submission_mode = FALSE; /* Can be set by ACL */ suppress_local_fixups = suppress_local_fixups_default; /* Can be set by ACL */ active_local_from_check = local_from_check; /* Can be set by ACL */ active_local_sender_retain = local_sender_retain; /* Can be set by ACL */ -sender_address = NULL; +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; +callout_address = NULL; 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 */ @@ -1733,6 +1918,7 @@ authenticated_sender = NULL; bmi_run = 0; bmi_verdicts = NULL; #endif +dnslist_domain = dnslist_matched = NULL; #ifndef DISABLE_DKIM dkim_signers = NULL; dkim_disable_verify = FALSE; @@ -1740,6 +1926,7 @@ dkim_collect_input = FALSE; #endif dsn_ret = 0; dsn_envid = NULL; +deliver_host = deliver_host_address = NULL; /* Can be set by ACL */ #ifndef DISABLE_PRDR prdr_requested = FALSE; #endif @@ -1766,13 +1953,13 @@ acl_var_m = NULL; not the first message in an SMTP session and the previous message caused them to be referenced in an ACL. */ -if (message_body != NULL) +if (message_body) { store_free(message_body); message_body = NULL; } -if (message_body_end != NULL) +if (message_body_end) { store_free(message_body_end); message_body_end = NULL; @@ -1782,12 +1969,13 @@ if (message_body_end != NULL) repetition in the same message, but it seems right to repeat them for different messages. */ -while (acl_warn_logged != NULL) +while (acl_warn_logged) { string_item *this = acl_warn_logged; acl_warn_logged = acl_warn_logged->next; store_free(this); } +store_reset(reset_point); } @@ -1835,7 +2023,7 @@ while (done <= 0) uschar *recipient = NULL; int start, end, sender_domain, recipient_domain; - switch(smtp_read_command(FALSE)) + switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { /* The HELO/EHLO commands set sender_address_helo if they have valid data; otherwise they are ignored, except that they do @@ -2030,6 +2218,19 @@ return done - 2; /* Convert yield values */ +static BOOL +smtp_log_tls_fail(uschar * errstr) +{ +uschar * conn_info = smtp_get_connection_info(); + +if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; +/* I'd like to get separated H= here, but too hard for now */ + +log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr); +return FALSE; +} + + /************************************************* * Start an SMTP session * *************************************************/ @@ -2094,12 +2295,12 @@ acl_var_c = NULL; /* Allow for trailing 0 in the command and data buffers. */ -if (!(smtp_cmd_buffer = US malloc(2*smtp_cmd_buffer_size + 2))) +if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP command buffer"); smtp_cmd_buffer[0] = 0; -smtp_data_buffer = smtp_cmd_buffer + smtp_cmd_buffer_size + 1; +smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1; /* For batched input, the protocol setting can be overridden from the command line by a trusted caller. */ @@ -2119,7 +2320,7 @@ else /* Set up the buffer for inputting using direct read() calls, and arrange to call the local functions instead of the standard C ones. */ -if (!(smtp_inbuffer = (uschar *)malloc(in_buffer_size))) +if (!(smtp_inbuffer = (uschar *)malloc(IN_BUFFER_SIZE))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer"); receive_getc = smtp_getc; @@ -2382,14 +2583,6 @@ if (!sender_host_unknown) "bad value for smtp_receive_timeout: '%s'", exp ? exp : US""); } - /* Start up TLS if tls_on_connect is set. This is for supporting the legacy - smtps port for use with older style SSL MTAs. */ - - #ifdef SUPPORT_TLS - if (tls_in.on_connect && tls_server_start(tls_require_ciphers) != OK) - return FALSE; - #endif - /* Test for explicit connection rejection */ if (verify_check_host(&host_reject_connection) == OK) @@ -2409,19 +2602,17 @@ if (!sender_host_unknown) value of errno is 0 or ENOENT (which happens if /etc/hosts.{allow,deny} does not exist). */ - #ifdef USE_TCP_WRAPPERS +#ifdef USE_TCP_WRAPPERS errno = 0; - tcp_wrappers_name = expand_string(tcp_wrappers_daemon_name); - if (tcp_wrappers_name == NULL) - { + if (!(tcp_wrappers_name = expand_string(tcp_wrappers_daemon_name))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" " "(tcp_wrappers_name) failed: %s", string_printing(tcp_wrappers_name), expand_string_message); - } + if (!hosts_ctl(tcp_wrappers_name, - (sender_host_name == NULL)? STRING_UNKNOWN : CS sender_host_name, - (sender_host_address == NULL)? STRING_UNKNOWN : CS sender_host_address, - (sender_ident == NULL)? STRING_UNKNOWN : CS sender_ident)) + sender_host_name ? CS sender_host_name : STRING_UNKNOWN, + sender_host_address ? CS sender_host_address : STRING_UNKNOWN, + sender_ident ? CS sender_ident : STRING_UNKNOWN)) { if (errno == 0 || errno == ENOENT) { @@ -2443,7 +2634,7 @@ if (!sender_host_unknown) } return FALSE; } - #endif +#endif /* Check for reserved slots. The value of smtp_accept_count has already been incremented to include this process. */ @@ -2514,27 +2705,25 @@ if (!sender_host_unknown) if (smtp_batched_input) return TRUE; -#ifdef SUPPORT_PROXY /* If valid Proxy Protocol source is connecting, set up session. * Failure will not allow any SMTP function other than QUIT. */ + +#ifdef SUPPORT_PROXY proxy_session = FALSE; proxy_session_failed = FALSE; if (check_proxy_protocol_host()) - if (!setup_proxy_protocol_host()) - { - proxy_session_failed = TRUE; - DEBUG(D_receive) - debug_printf("Failure to extract proxied host, only QUIT allowed\n"); - } - else - { - sender_host_name = NULL; - (void)host_name_lookup(); - host_build_sender_fullhost(); - } + setup_proxy_protocol_host(); #endif -/* Run the ACL if it exists */ + /* Start up TLS if tls_on_connect is set. This is for supporting the legacy + 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); +#endif + +/* Run the connect ACL if it exists */ user_msg = NULL; if (acl_smtp_connect) @@ -3567,7 +3756,7 @@ while (done <= 0) US &off, sizeof(off)); #endif - switch(smtp_read_command(TRUE)) + switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED)) { /* The AUTH command is not permitted to occur inside a transaction, and may occur successfully only once per connection. Actually, that isn't quite @@ -4300,9 +4489,13 @@ while (done <= 0) case ENV_MAIL_OPT_UTF8: if (smtputf8_advertised) { + int old_pool = store_pool; + DEBUG(D_receive) debug_printf("smtputf8 requested\n"); message_smtputf8 = allow_utf8_domains = TRUE; + store_pool = POOL_PERM; received_protocol = string_sprintf("utf8%s", received_protocol); + store_pool = old_pool; } break; #endif @@ -4768,14 +4961,14 @@ while (done <= 0) chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 ? CHUNKING_LAST : CHUNKING_ACTIVE; chunking_data_left = chunking_datasize; + DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", + (int)chunking_state, chunking_data_left); lwr_receive_getc = receive_getc; lwr_receive_ungetc = receive_ungetc; receive_getc = bdat_getc; receive_ungetc = bdat_ungetc; - DEBUG(D_any) - debug_printf("chunking state %d\n", (int)chunking_state); goto DATA_BDAT; } @@ -4991,7 +5184,7 @@ while (done <= 0) It seems safest to just wipe away the content rather than leave it as a target to jump to. */ - memset(smtp_inbuffer, 0, in_buffer_size); + memset(smtp_inbuffer, 0, IN_BUFFER_SIZE); /* Attempt to start up a TLS session, and if successful, discard all knowledge that was obtained previously. At least, that's what the RFC says, @@ -5001,7 +5194,8 @@ while (done <= 0) We must allow for an extra EHLO command and an extra AUTH command after STARTTLS that don't add to the nonmail command count. */ - if ((rc = tls_server_start(tls_require_ciphers)) == OK) + s = NULL; + if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK) { if (!tls_remember_esmtp) helo_seen = esmtp = auth_advertised = pipelining_advertised = FALSE; @@ -5030,11 +5224,13 @@ while (done <= 0) DEBUG(D_tls) debug_printf("TLS active\n"); break; /* Successful STARTTLS */ } + else + (void) smtp_log_tls_fail(s); /* Some local configuration problem was discovered before actually trying to do a TLS handshake; give a temporary error. */ - else if (rc == DEFER) + if (rc == DEFER) { smtp_printf("454 TLS currently unavailable\r\n"); break; @@ -5045,7 +5241,7 @@ while (done <= 0) set, but we must still reject all incoming commands. */ DEBUG(D_tls) debug_printf("TLS failed to start\n"); - while (done <= 0) switch(smtp_read_command(FALSE)) + while (done <= 0) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { case EOF_CMD: log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF", @@ -5333,8 +5529,8 @@ while (done <= 0) case BADSYN_CMD: SYNC_FAILURE: - if (smtp_inend >= smtp_inbuffer + in_buffer_size) - smtp_inend = smtp_inbuffer + in_buffer_size - 1; + 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; smtp_inptr[c] = 0;