X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftransports%2Fsmtp.c;h=bca8c4941c6660e137fb354ed3a811190b551cd7;hb=12263aa86a56d5d6a8aa6a9daaf285e24ca2a448;hp=1631817d948a5b86e6e55f79a5ca833f71c84d9d;hpb=c645dd29e7f9fcb7e3d2fd9f20aa47084ba0221e;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 1631817d9..bca8c4941 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -190,15 +190,18 @@ optionlist smtp_transport_options[] = { { "tls_verify_certificates", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) }, { "tls_verify_hosts", opt_stringptr, - (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) } + (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }, +#endif +#ifdef SUPPORT_I18N + { "utf8_downconvert", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, utf8_downconvert) }, #endif }; /* Size of the options list. An extern variable has to be used so that its address can appear in the tables drtables.c. */ -int smtp_transport_options_count = - sizeof(smtp_transport_options)/sizeof(optionlist); +int smtp_transport_options_count = nelem(smtp_transport_options); #ifdef MACRO_PREDEF @@ -287,6 +290,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { .tls_try_verify_hosts = US"*", .tls_verify_cert_hostnames = US"*", #endif +#ifdef SUPPORT_I18N + .utf8_downconvert = NULL, +#endif #ifndef DISABLE_DKIM .dkim = {.dkim_domain = NULL, @@ -789,6 +795,7 @@ responses before returning, except after I/O errors and timeouts. */ if (sx->pending_MAIL) { + DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__); count--; if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) @@ -840,6 +847,7 @@ while (count-- > 0) /* The address was accepted */ addr->host_used = sx->host; + DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__); if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) { @@ -957,25 +965,28 @@ if (addr) sx->sync_addr = addr->next; /* Handle a response to DATA. If we have not had any good recipients, either previously or in this block, the response is ignored. */ -if (pending_DATA != 0 && - !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), - '3', ob->command_timeout)) +if (pending_DATA != 0) { - int code; - uschar *msg; - BOOL pass_message; - if (pending_DATA > 0 || (yield & 1) != 0) + DEBUG(D_transport) debug_printf("%s expect data\n", __FUNCTION__); + if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), + '3', ob->command_timeout)) { - if (errno == 0 && sx->buffer[0] == '4') + int code; + uschar *msg; + BOOL pass_message; + if (pending_DATA > 0 || (yield & 1) != 0) { - errno = ERRNO_DATA4XX; - sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; + if (errno == 0 && sx->buffer[0] == '4') + { + errno = ERRNO_DATA4XX; + sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; + } + return -3; } - return -3; + (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message); + DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining " + "is in use and there were no good recipients\n", msg); } - (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message); - DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining " - "is in use and there were no good recipients\n", msg); } /* All responses read and handled; MAIL (if present) received 2xx and DATA (if @@ -1336,8 +1347,8 @@ return Ustrcmp(current_local_identity, message_local_identity) == 0; -static uschar -ehlo_response(uschar * buf, uschar checks) +static unsigned +ehlo_response(uschar * buf, unsigned checks) { size_t bsize = Ustrlen(buf); @@ -1345,6 +1356,12 @@ size_t bsize = Ustrlen(buf); if ( checks & OPTION_TLS && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) checks &= ~OPTION_TLS; + +# ifdef EXPERIMENTAL_REQUIRETLS +if ( checks & OPTION_REQUIRETLS + && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0) + checks &= ~OPTION_REQUIRETLS; +# endif #endif if ( checks & OPTION_IGNQ @@ -1533,7 +1550,8 @@ sx->utf8_needed = FALSE; sx->dsn_all_lasthop = TRUE; #if defined(SUPPORT_TLS) && defined(SUPPORT_DANE) sx->dane = FALSE; -sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK; +sx->dane_required = + verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK; #endif if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999; @@ -1626,9 +1644,11 @@ if (!continue_hostname) string_sprintf("DANE error: tlsa lookup %s", rc == DEFER ? "DEFER" : "FAIL"), rc, FALSE); +# ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"dane:fail", sx->dane_required ? US"dane-required" : US"dnssec-invalid"); +# endif return rc; } } @@ -1637,8 +1657,10 @@ if (!continue_hostname) set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name), FAIL, FALSE); +# ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"dane:fail", US"dane-required"); +# endif return FAIL; } } @@ -1908,7 +1930,7 @@ else ) { sx->peer_offered = smtp_peer_options; - pipelining_active = !!(smtp_peer_options & OPTION_PIPE); + sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE); HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n", continue_proxy_cipher ? "proxied" : "verify conn with"); return OK; @@ -1983,8 +2005,10 @@ if ( smtp_peer_options & OPTION_TLS log_write(0, LOG_MAIN, "DANE attempt failed; TLS connection to %s [%s]: %s", sx->host->name, sx->host->address, errstr); +# ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"dane:fail", US"validation-failure"); /* could do with better detail */ +# endif } # endif @@ -2071,15 +2095,22 @@ have one. */ else if ( sx->smtps # ifdef SUPPORT_DANE || sx->dane +# endif +# ifdef EXPERIMENTAL_REQUIRETLS + || tls_requiretls & REQUIRETLS_MSG # endif || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK ) { - errno = ERRNO_TLSREQUIRED; + errno = +# ifdef EXPERIMENTAL_REQUIRETLS + tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS : +# endif + ERRNO_TLSREQUIRED; message = string_sprintf("a TLS session is required, but %s", smtp_peer_options & OPTION_TLS ? "an attempt to start TLS failed" : "the server did not offer TLS support"); -# ifdef SUPPORT_DANE +# if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT) if (sx->dane) (void) event_raise(sx->tblock->event_action, US"dane:fail", smtp_peer_options & OPTION_TLS @@ -2116,6 +2147,9 @@ if (continue_hostname == NULL | OPTION_DSN | OPTION_PIPE | (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0) +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0) +#endif ); /* Set for IGNOREQUOTA if the response to LHLO specifies support and the @@ -2160,6 +2194,16 @@ if (continue_hostname == NULL DEBUG(D_transport) debug_printf("%susing DSN\n", sx->peer_offered & OPTION_DSN ? "" : "not "); +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + if (sx->peer_offered & OPTION_REQUIRETLS) + { + smtp_peer_options |= OPTION_REQUIRETLS; + DEBUG(D_transport) debug_printf( + tls_requiretls & REQUIRETLS_MSG + ? "using REQUIRETLS\n" : "REQUIRETLS offered\n"); + } +#endif + /* Note if the response to EHLO specifies support for the AUTH extension. If it has, check that this host is one we want to authenticate to, and do the business. The host name and address must be available when the @@ -2175,7 +2219,7 @@ if (continue_hostname == NULL } } } -pipelining_active = !!(smtp_peer_options & OPTION_PIPE); +sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE); /* The setting up of the SMTP call is now complete. Any subsequent errors are message-specific. */ @@ -2185,6 +2229,38 @@ sx->setting_up = FALSE; #ifdef SUPPORT_I18N if (sx->addrlist->prop.utf8_msg) { + uschar * s; + + /* If the transport sets a downconversion mode it overrides any set by ACL + for the message. */ + + if ((s = sx->ob->utf8_downconvert)) + { + if (!(s = expand_string(s))) + { + message = string_sprintf("failed to expand utf8_downconvert: %s", + expand_string_message); + set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); + yield = DEFER; + goto SEND_QUIT; + } + switch (*s) + { + case '1': sx->addrlist->prop.utf8_downcvt = TRUE; + sx->addrlist->prop.utf8_downcvt_maybe = FALSE; + break; + case '0': sx->addrlist->prop.utf8_downcvt = FALSE; + sx->addrlist->prop.utf8_downcvt_maybe = FALSE; + break; + case '-': if (s[1] == '1') + { + sx->addrlist->prop.utf8_downcvt = FALSE; + sx->addrlist->prop.utf8_downcvt_maybe = TRUE; + } + break; + } + } + sx->utf8_needed = !sx->addrlist->prop.utf8_downcvt && !sx->addrlist->prop.utf8_downcvt_maybe; DEBUG(D_transport) if (!sx->utf8_needed) @@ -2198,6 +2274,22 @@ if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8)) errno = ERRNO_UTF8_FWD; goto RESPONSE_FAILED; } +#endif /*SUPPORT_I18N*/ + +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) + /*XXX should tls_requiretls actually be per-addr? */ + +if ( tls_requiretls & REQUIRETLS_MSG + && !(sx->peer_offered & OPTION_REQUIRETLS) + ) + { + sx->setting_up = TRUE; + errno = ERRNO_REQUIRETLS; + message = US"REQUIRETLS support is required from the server" + " but it was not offered"; + DEBUG(D_transport) debug_printf("%s\n", message); + goto TLS_FAILED; + } #endif return OK; @@ -2210,6 +2302,7 @@ return OK; message = NULL; sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno, sx->buffer, &code, &message, &pass_message); + yield = DEFER; goto FAILED; SEND_FAILED: @@ -2217,6 +2310,7 @@ return OK; message = US string_sprintf("send() to %s [%s] failed: %s", sx->host->name, sx->host->address, strerror(errno)); sx->send_quit = FALSE; + yield = DEFER; goto FAILED; EHLOHELO_FAILED: @@ -2224,6 +2318,7 @@ return OK; message = string_sprintf("Remote host closed connection in response to %s" " (EHLO response was: %s)", smtp_command, sx->buffer); sx->send_quit = FALSE; + yield = DEFER; goto FAILED; /* This label is jumped to directly when a TLS negotiation has failed, @@ -2233,7 +2328,13 @@ return OK; #ifdef SUPPORT_TLS TLS_FAILED: - code = '4'; +# ifdef EXPERIMENTAL_REQUIRETLS + if (errno == ERRNO_REQUIRETLS) + code = '5', yield = FAIL; + /*XXX DSN will be labelled 500; prefer 530 5.7.4 */ + else +# endif + code = '4', yield = DEFER; goto FAILED; #endif @@ -2266,7 +2367,6 @@ FAILED: , sx->smtp_greeting, sx->helo_response #endif ); - yield = DEFER; } @@ -2372,6 +2472,11 @@ if ( sx->peer_offered & OPTION_UTF8 Ustrcpy(p, " SMTPUTF8"), p += 9; #endif +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) +if (tls_requiretls & REQUIRETLS_MSG) + Ustrcpy(p, " REQUIRETLS") , p += 11; +#endif + /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */ for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0; addr && address_count < sx->max_rcpt; @@ -3220,6 +3325,8 @@ else addr->host_used = host; addr->special_action = flag; addr->message = conf; + + if (sx.pipelining_used) setflag(addr, af_pipelining); #ifndef DISABLE_PRDR if (sx.prdr_active) setflag(addr, af_prdr_used); #endif @@ -3856,6 +3963,12 @@ same one in order to be passed to a single transport - or if the transport has a host list with hosts_override set, use the host list supplied with the transport. It is an error for this not to exist. */ +#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) +if (tls_requiretls & REQUIRETLS_MSG) + ob->tls_tempfail_tryclear = FALSE; /*XXX surely we should have a local for this + rather than modifying the transport? */ +#endif + if (!hostlist || (ob->hosts_override && ob->hosts)) { if (!ob->hosts)