#include "../exim.h"
#include "smtp.h"
+#if defined(SUPPORT_DANE) && defined(DISABLE_TLS)
+# error TLS is required for DANE
+#endif
+
/* Options specific to the smtp transport. This transport also supports LMTP
over TCP/IP. The options must be in alphabetic order (note that "_" comes
.gethostbyname = FALSE,
.dns_qualify_single = TRUE,
.dns_search_parents = FALSE,
- .dnssec = { .request=NULL, .require=NULL },
+ .dnssec = { .request= US"*", .require=NULL },
.delay_after_cutoff = TRUE,
.hosts_override = FALSE,
.hosts_randomize = FALSE,
return FALSE;
case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */
- *message = string_sprintf("failed to write a data block");
+ *message = US"failed to write a data block";
return FALSE;
#ifdef SUPPORT_I18N
#ifdef EXPERIMENTAL_DSN_INFO
sx->helo_response = string_copy(sx->buffer);
#endif
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->conn_args.tblock->event_action,
+ US"smtp:ehlo", sx->buffer);
+#endif
return TRUE;
}
else if (errno != 0 || sx->buffer[0] == 0)
{
- string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
+ gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs;
+
+ /* Use taint-unchecked routines for writing into big_buffer, trusting
+ that we'll never expand it. */
+
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>",
transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
+ string_from_gstring(g);
return -2;
}
Return True on error, otherwise buffer has (possibly empty) terminated string
*/
-BOOL
+static BOOL
smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
smtp_transport_options_block *ob)
{
-uschar *local_authenticated_sender = authenticated_sender;
+uschar * local_authenticated_sender = authenticated_sender;
#ifdef notdef
debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
#endif
-if (ob->authenticated_sender != NULL)
+if (ob->authenticated_sender)
{
uschar *new = expand_string(ob->authenticated_sender);
- if (new == NULL)
+ if (!new)
{
if (!f.expand_string_forcedfail)
{
return TRUE;
}
}
- else if (new[0] != 0) local_authenticated_sender = new;
+ else if (*new) local_authenticated_sender = new;
}
/* Add the authenticated sender address if present */
-if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
- local_authenticated_sender != NULL)
+if ( (f.smtp_authenticated || ob->authenticated_sender_force)
+ && local_authenticated_sender)
{
- string_format(buffer, bufsize, " AUTH=%s",
+ string_format_nt(buffer, bufsize, " AUTH=%s",
auth_xtextencode(local_authenticated_sender,
- Ustrlen(local_authenticated_sender)));
+ Ustrlen(local_authenticated_sender)));
client_authenticated_sender = string_copy(local_authenticated_sender);
}
else
-#ifdef SUPPORT_DANE
-/* Lookup TLSA record for host/port.
-Return: OK success with dnssec; DANE mode
- DEFER Do not use this host now, may retry later
- FAIL_FORCED No TLSA record; DANE not usable
- FAIL Do not use this connection
-*/
-
-int
-tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
-{
-/* move this out to host.c given the similarity to dns_lookup() ? */
-uschar buffer[300];
-const uschar * fullname = buffer;
-int rc;
-BOOL sec;
-
-/* TLSA lookup string */
-(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
-
-rc = dns_lookup(dnsa, buffer, T_TLSA, &fullname);
-sec = dns_is_secure(dnsa);
-DEBUG(D_transport)
- debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
-
-switch (rc)
- {
- case DNS_AGAIN:
- return DEFER; /* just defer this TLS'd conn */
-
- case DNS_SUCCEED:
- if (sec)
- {
- DEBUG(D_transport)
- {
- dns_scan dnss;
- for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
- if (rr->type == T_TLSA && rr->size > 3)
- {
- uint16_t payload_length = rr->size - 3;
- uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
-
- sp += sprintf(CS sp, "%d ", *p++); /* usage */
- sp += sprintf(CS sp, "%d ", *p++); /* selector */
- sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
- while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
- sp += sprintf(CS sp, "%02x", *p++);
-
- debug_printf(" %s\n", s);
- }
- }
- return OK;
- }
- log_write(0, LOG_MAIN,
- "DANE error: TLSA lookup for %s not DNSSEC", host->name);
- /*FALLTRHOUGH*/
-
- case DNS_NODATA: /* no TLSA RR for this lookup */
- case DNS_NOMATCH: /* no records at all for this lookup */
- return dane_required ? FAIL : FAIL_FORCED;
-
- default:
- case DNS_FAIL:
- return dane_required ? FAIL : DEFER;
- }
-}
-#endif
-
-
-
typedef struct smtp_compare_s
{
uschar *current_sender_address;
BOOL pass_message = FALSE;
uschar * message = NULL;
int yield = OK;
-int rc;
#ifndef DISABLE_TLS
uschar * tls_errstr;
#endif
if (sx->conn_args.host->dnssec == DS_YES)
{
+ int rc;
if( sx->dane_required
|| verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
)
&& addrlist->prop.utf8_msg
&& !addrlist->prop.utf8_downcvt
)
- Ustrcpy(p, " SMTPUTF8"), p += 9;
+ Ustrcpy(p, US" SMTPUTF8"), p += 9;
#endif
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
{
if (dsn_ret == dsn_ret_hdrs)
- { Ustrcpy(p, " RET=HDRS"); p += 9; }
+ { Ustrcpy(p, US" RET=HDRS"); p += 9; }
else if (dsn_ret == dsn_ret_full)
- { Ustrcpy(p, " RET=FULL"); p += 9; }
+ { Ustrcpy(p, US" RET=FULL"); p += 9; }
if (dsn_envid)
{
{
BOOL first = TRUE;
- Ustrcpy(p, " NOTIFY=");
+ Ustrcpy(p, US" NOTIFY=");
while (*p) p++;
for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
{
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
-if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+testharness_pause_ms(100); /* let parent debug out */
set_process_info("proxying TLS connection for continued transport");
FD_ZERO(&rfds);
FD_SET(tls_out.active.sock, &rfds);
}
done:
- if (f.running_in_test_harness) millisleep(100); /* let logging complete */
+ testharness_pause_ms(100); /* let logging complete */
exim_exit(0, US"TLS proxy");
}
#endif
well as body. Set the appropriate timeout value to be used for each chunk.
(Haven't been able to make it work using select() for writing yet.) */
-if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
+if ( !sx.ok
+ && (!(sx.peer_offered & OPTION_CHUNKING) || !pipelining_active))
{
/* Save the first address of the next batch. */
sx.first_addr = sx.next_addr;
transport_count = 0;
#ifndef DISABLE_DKIM
+ {
+# ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+# endif
dkim_exim_sign_init();
# ifdef EXPERIMENTAL_ARC
{
}
}
# endif
+# ifdef MEASURE_TIMING
+ report_time_since(&t0, US"dkim_exim_sign_init (delta)");
+# endif
+ }
sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
#else
sx.ok = transport_write_message(&tctx, 0);
/* Set up a pipe for proxying TLS for the new transport process */
smtp_peer_options |= OPTION_TLS;
- if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+ if ((sx.ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
socket_fd = pfd[1];
else
set_errno(sx.first_addr, errno, US"internal allocation problem",
int pid = fork();
if (pid == 0) /* child; fork again to disconnect totally */
{
- if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+ testharness_pause_ms(100); /* let parent debug out */
/* does not return */
smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
ob->command_timeout);
if (sx.send_quit)
{
shutdown(sx.cctx.sock, SHUT_WR);
- millisleep(f.running_in_test_harness ? 200 : 20);
+ millisleep(20);
+ testharness_pause_ms(200);
if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
i--; /* drain socket */
else
if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
+ if (is_tainted(s))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted host list '%s' from '%s' in transport %s",
+ s, ob->hosts, tblock->name);
+ /* Avoid leaking info to an attacker */
+ addrlist->message = US"internal configuration error";
+ addrlist->transport_return = PANIC;
+ return FALSE;
+ }
+
host_build_hostlist(&hostlist, s, ob->hosts_randomize);
/* Check that the expansion yielded something useful. */
if (expanded_hosts)
{
- thost = store_get(sizeof(host_item));
+ thost = store_get(sizeof(host_item), FALSE);
*thost = *host;
thost->name = string_copy(host->name);
thost->address = string_copy(host->address);
ob->hosts_max_try_hardlimit);
}
- if (f.running_in_test_harness) millisleep(500); /* let server debug out */
+ testharness_pause_ms(500); /* let server debug out */
} /* End of loop for trying multiple hosts. */
/* If we failed to find a matching host in the list, for an already-open