X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/0dda43406a4b207504213c58f668de9354ea915e..6d7c6175eda3aaa316d1960a89170a285510ad40:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 548fbfc0e..dc24e6938 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/transports/smtp.c,v 1.44 2009/11/16 19:56:54 nm4 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2009 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -57,6 +55,8 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, dns_qualify_single) }, { "dns_search_parents", opt_bool, (void *)offsetof(smtp_transport_options_block, dns_search_parents) }, + { "dscp", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dscp) }, { "fallback_hosts", opt_stringptr, (void *)offsetof(smtp_transport_options_block, fallback_hosts) }, { "final_timeout", opt_time, @@ -64,6 +64,9 @@ optionlist smtp_transport_options[] = { { "gethostbyname", opt_bool, (void *)offsetof(smtp_transport_options_block, gethostbyname) }, #ifdef SUPPORT_TLS + /* These are no longer honoured, as of Exim 4.80; for now, we silently + ignore; a later release will warn, and a later-still release will remove + these options, so that using them becomes an error. */ { "gnutls_require_kx", opt_stringptr, (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) }, { "gnutls_require_mac", opt_stringptr, @@ -103,6 +106,10 @@ optionlist smtp_transport_options[] = { #endif { "hosts_try_auth", opt_stringptr, (void *)offsetof(smtp_transport_options_block, hosts_try_auth) }, +#ifdef SUPPORT_TLS + { "hosts_verify_avoid_tls", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) }, +#endif { "interface", opt_stringptr, (void *)offsetof(smtp_transport_options_block, interface) }, { "keepalive", opt_bool, @@ -128,10 +135,14 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, tls_certificate) }, { "tls_crl", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_crl) }, + { "tls_dh_min_bits", opt_int, + (void *)offsetof(smtp_transport_options_block, tls_dh_min_bits) }, { "tls_privatekey", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_privatekey) }, - { "tls_require_ciphers", opt_stringptr, + { "tls_require_ciphers", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) }, + { "tls_sni", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, tls_sni) }, { "tls_tempfail_tryclear", opt_bool, (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) }, { "tls_verify_certificates", opt_stringptr, @@ -157,11 +168,13 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* interface */ NULL, /* port */ US"smtp", /* protocol */ + NULL, /* DSCP */ NULL, /* serialize_hosts */ NULL, /* hosts_try_auth */ NULL, /* hosts_require_auth */ NULL, /* hosts_require_tls */ NULL, /* hosts_avoid_tls */ + US"*", /* hosts_verify_avoid_tls */ NULL, /* hosts_avoid_pipelining */ NULL, /* hosts_avoid_esmtp */ NULL, /* hosts_nopass_tls */ @@ -192,7 +205,10 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* gnutls_require_kx */ NULL, /* gnutls_require_mac */ NULL, /* gnutls_require_proto */ + NULL, /* tls_sni */ NULL, /* tls_verify_certificates */ + EXIM_CLIENT_DH_DEFAULT_MIN_BITS, + /* tls_dh_min_bits */ TRUE /* tls_tempfail_tryclear */ #endif #ifndef DISABLE_DKIM @@ -304,7 +320,8 @@ if (tblock->retry_use_local_part == TRUE_UNSET) /* Set the default port according to the protocol */ if (ob->port == NULL) - ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" : US"smtp"; + ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" : + (strcmpic(ob->protocol, US"smtps") == 0)? US"smtps" : US"smtp"; /* Set up the setup entry point, to be called before subprocesses for this transport. */ @@ -845,6 +862,7 @@ time_t start_delivery_time = time(NULL); smtp_transport_options_block *ob = (smtp_transport_options_block *)(tblock->options_block); BOOL lmtp = strcmpic(ob->protocol, US"lmtp") == 0; +BOOL smtps = strcmpic(ob->protocol, US"smtps") == 0; BOOL ok = FALSE; BOOL send_rset = TRUE; BOOL send_quit = TRUE; @@ -889,31 +907,27 @@ outblock.authenticating = FALSE; /* Reset the parameters of a TLS session. */ -tls_cipher = NULL; -tls_peerdn = NULL; +tls_in.bits = 0; +tls_in.cipher = NULL; /* for back-compatible behaviour */ +tls_in.peerdn = NULL; +#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +tls_in.sni = NULL; +#endif -/* If an authenticated_sender override has been specified for this transport -instance, expand it. If the expansion is forced to fail, and there was already -an authenticated_sender for this message, the original value will be used. -Other expansion failures are serious. An empty result is ignored, but there is -otherwise no check - this feature is expected to be used with LMTP and other -cases where non-standard addresses (e.g. without domains) might be required. */ +tls_out.bits = 0; +tls_out.cipher = NULL; /* the one we may use for this transport */ +tls_out.peerdn = NULL; +#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) +tls_out.sni = NULL; +#endif -if (ob->authenticated_sender != NULL) +#ifndef SUPPORT_TLS +if (smtps) { - uschar *new = expand_string(ob->authenticated_sender); - if (new == NULL) - { - if (!expand_string_forcedfail) - { - uschar *message = string_sprintf("failed to expand " - "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); - return ERROR; - } - } - else if (new[0] != 0) local_authenticated_sender = new; + set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE); + return ERROR; } +#endif /* Make a connection to the host if this isn't a continued delivery, and handle the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled @@ -923,7 +937,7 @@ if (continue_hostname == NULL) { inblock.sock = outblock.sock = smtp_connect(host, host_af, port, interface, ob->connect_timeout, - ob->keepalive); /* This puts port into host->port */ + ob->keepalive, ob->dscp); /* This puts port into host->port */ if (inblock.sock < 0) { @@ -942,19 +956,22 @@ if (continue_hostname == NULL) is nevertheless a reasonably clean way of programming this kind of logic, where you want to escape on any error. */ - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + if (!smtps) + { + if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->command_timeout)) goto RESPONSE_FAILED; - /* Now check if the helo_data expansion went well, and sign off cleanly if it - didn't. */ + /* Now check if the helo_data expansion went well, and sign off cleanly if + it didn't. */ - if (helo_data == NULL) - { - uschar *message = string_sprintf("failed to expand helo_data: %s", - expand_string_message); - set_errno(addrlist, 0, message, DEFER, FALSE); - yield = DEFER; - goto SEND_QUIT; + if (helo_data == NULL) + { + uschar *message = string_sprintf("failed to expand helo_data: %s", + expand_string_message); + set_errno(addrlist, 0, message, DEFER, FALSE); + yield = DEFER; + goto SEND_QUIT; + } } /** Debugging without sending a message @@ -995,6 +1012,20 @@ goto SEND_QUIT; esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL, host->name, host->address, NULL) != OK; + /* Alas; be careful, since this goto is not an error-out, so conceivably + we might set data between here and the target which we assume to exist + and be usable. I can see this coming back to bite us. */ + #ifdef SUPPORT_TLS + if (smtps) + { + tls_offered = TRUE; + suppress_tls = FALSE; + ob->tls_tempfail_tryclear = FALSE; + smtp_command = US"SSL-on-connect"; + goto TLS_NEGOTIATE; + } + #endif + if (esmtp) { if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", @@ -1089,6 +1120,7 @@ if (tls_offered && !suppress_tls && /* STARTTLS accepted: try to negotiate a TLS session. */ else + TLS_NEGOTIATE: { int rc = tls_client_start(inblock.sock, host, @@ -1096,12 +1128,11 @@ if (tls_offered && !suppress_tls && NULL, /* No DH param */ ob->tls_certificate, ob->tls_privatekey, + ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl, ob->tls_require_ciphers, - ob->gnutls_require_mac, - ob->gnutls_require_kx, - ob->gnutls_require_proto, + ob->tls_dh_min_bits, ob->command_timeout); /* TLS negotiation failed; give an error. From outside, this function may @@ -1122,21 +1153,26 @@ if (tls_offered && !suppress_tls && { if (addr->transport_return == PENDING_DEFER) { - addr->cipher = tls_cipher; - addr->peerdn = tls_peerdn; + addr->cipher = tls_out.cipher; + addr->peerdn = tls_out.peerdn; } } } } +/* if smtps, we'll have smtp_command set to something else; always safe to +reset it here. */ +smtp_command = big_buffer; + /* If we started TLS, redo the EHLO/LHLO exchange over the secure channel. If helo_data is null, we are dealing with a connection that was passed from another process, and so we won't have expanded helo_data above. We have to expand it here. $sending_ip_address and $sending_port are set up right at the start of the Exim process (in exim.c). */ -if (tls_active >= 0) +if (tls_out.active >= 0) { + char *greeting_cmd; if (helo_data == NULL) { helo_data = expand_string(ob->helo_data); @@ -1150,8 +1186,24 @@ if (tls_active >= 0) } } - if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : "EHLO", - helo_data) < 0) + /* For SMTPS we need to wait for the initial OK response. */ + if (smtps) + { + if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->command_timeout)) goto RESPONSE_FAILED; + } + + if (esmtp) + greeting_cmd = "EHLO"; + else + { + greeting_cmd = "HELO"; + DEBUG(D_transport) + debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n"); + } + + if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", + lmtp? "LHLO" : greeting_cmd, helo_data) < 0) goto SEND_FAILED; if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) @@ -1180,7 +1232,7 @@ we skip this. */ if (continue_hostname == NULL #ifdef SUPPORT_TLS - || tls_active >= 0 + || tls_out.active >= 0 #endif ) { @@ -1413,6 +1465,29 @@ if (smtp_use_size) while (*p) p++; } +/* If an authenticated_sender override has been specified for this transport +instance, expand it. If the expansion is forced to fail, and there was already +an authenticated_sender for this message, the original value will be used. +Other expansion failures are serious. An empty result is ignored, but there is +otherwise no check - this feature is expected to be used with LMTP and other +cases where non-standard addresses (e.g. without domains) might be required. */ + +if (ob->authenticated_sender != NULL) + { + uschar *new = expand_string(ob->authenticated_sender); + if (new == NULL) + { + if (!expand_string_forcedfail) + { + uschar *message = string_sprintf("failed to expand " + "authenticated_sender: %s", expand_string_message); + set_errno(addrlist, 0, message, DEFER, FALSE); + return ERROR; + } + } + else if (new[0] != 0) local_authenticated_sender = new; + } + /* Add the authenticated sender address if present */ if ((smtp_authenticated || ob->authenticated_sender_force) && @@ -1940,7 +2015,7 @@ if (completed_address && ok && send_quit) BOOL more; if (first_addr != NULL || continue_more || ( - (tls_active < 0 || + (tls_out.active < 0 || verify_check_this_host(&(ob->hosts_nopass_tls), NULL, host->name, host->address, NULL) != OK) && @@ -1989,12 +2064,15 @@ if (completed_address && ok && send_quit) don't get a good response, we don't attempt to pass the socket on. */ #ifdef SUPPORT_TLS - if (tls_active >= 0) + if (tls_out.active >= 0) { - tls_close(TRUE); - ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 && - smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout); + tls_close(FALSE, TRUE); + if (smtps) + ok = FALSE; + else + ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 && + smtp_read_response(&inblock, buffer, sizeof(buffer), '2', + ob->command_timeout); } #endif @@ -2038,7 +2116,7 @@ if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); END_OFF: #ifdef SUPPORT_TLS -tls_close(TRUE); +tls_close(FALSE, TRUE); #endif /* Close the socket, and return the appropriate value, first setting @@ -3036,9 +3114,12 @@ for (addr = addrlist; addr != NULL; addr = addr->next) /* Update the database which keeps information about which messages are waiting for which hosts to become available. For some message-specific errors, the update_waiting flag is turned off because we don't want follow-on deliveries in -those cases. */ +those cases. If this transport instance is explicitly limited to one message +per connection then follow-on deliveries are not possible and there's no need +to create/update the per-transport wait- database. */ -if (update_waiting) transport_update_waiting(hostlist, tblock->name); +if (update_waiting && tblock->connection_max_messages != 1) + transport_update_waiting(hostlist, tblock->name); END_TRANSPORT: