commit 00b35aee4cdc8a002b537d51585f925aba19db09 Author: Fabian Groffen Date: Tue Dec 10 17:32:12 2019 +0000 PAM: fix crash in the pam expansion condition. Bug 2489 Broken-by: f3ebb786e4 (cherry picked from commit dcbfbadac2f557dc73bbcf2b26aadf50f8414ca1) diff --git a/src/src/auths/call_pam.c b/src/src/auths/call_pam.c index 9a46b6b63..c96e146d1 100644 --- a/src/src/auths/call_pam.c +++ b/src/src/auths/call_pam.c @@ -69,9 +69,11 @@ pam_converse (int num_msg, PAM_CONVERSE_ARG2_TYPE **msg, int sep = 0; struct pam_response *reply; -if (pam_arg_ended) return PAM_CONV_ERR; +/* It seems that PAM frees reply[] */ -reply = store_get(sizeof(struct pam_response) * num_msg, FALSE); +if ( pam_arg_ended + || !(reply = malloc(sizeof(struct pam_response) * num_msg))) + return PAM_CONV_ERR; for (int i = 0; i < num_msg; i++) { @@ -80,25 +82,26 @@ for (int i = 0; i < num_msg; i++) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: - arg = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size); - if (arg == NULL) - { - arg = US""; - pam_arg_ended = TRUE; - } - reply[i].resp = CS string_copy_perm(arg, FALSE); /* PAM frees resp */ - reply[i].resp_retcode = PAM_SUCCESS; - break; + arg = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size); + if (!arg) + { + arg = US""; + pam_arg_ended = TRUE; + } + reply[i].resp = CS string_copy_malloc(arg); /* PAM frees resp */ + reply[i].resp_retcode = PAM_SUCCESS; + break; case PAM_TEXT_INFO: /* Just acknowledge messages */ case PAM_ERROR_MSG: - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = NULL; - break; + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = NULL; + break; default: /* Must be an error of some sort... */ - pam_conv_had_error = TRUE; - return PAM_CONV_ERR; + free(reply); + pam_conv_had_error = TRUE; + return PAM_CONV_ERR; } } commit 5cb18fca8873da273fb80931b620892586cbffaf Author: Jeremy Harris Date: Fri Nov 8 22:30:04 2019 +0000 Regard command-line recipients as tainted (cherry picked from commit f0fe22cbc29ee4f887aa254f2590a9e72401e237) diff --git a/src/src/exim.c b/src/src/exim.c index 6a2e7a644..0e839c5d4 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -4822,8 +4822,9 @@ if (verify_address_mode || f.address_test_mode) { while (recipients_arg < argc) { - uschar *s = argv[recipients_arg++]; - while (*s != 0) + /* Supplied addresses are tainted since they come from a user */ + uschar * s = string_copy_taint(argv[recipients_arg++], TRUE); + while (*s) { BOOL finished = FALSE; uschar *ss = parse_find_address_end(s, FALSE); @@ -4831,16 +4832,16 @@ if (verify_address_mode || f.address_test_mode) test_address(s, flags, &exit_value); s = ss; if (!finished) - while (*(++s) != 0 && (*s == ',' || isspace(*s))); + while (*++s == ',' || isspace(*s)) ; } } } else for (;;) { - uschar *s = get_stdinput(NULL, NULL); - if (s == NULL) break; - test_address(s, flags, &exit_value); + uschar * s = get_stdinput(NULL, NULL); + if (!s) break; + test_address(string_copy_taint(s, TRUE), flags, &exit_value); } route_tidyup(); @@ -5334,13 +5335,13 @@ while (more) raw_sender = string_copy(sender_address); - /* Loop for each argument */ + /* Loop for each argument (supplied by user hence tainted) */ for (int i = 0; i < count; i++) { int start, end, domain; - uschar *errmess; - uschar *s = list[i]; + uschar * errmess; + uschar * s = string_copy_taint(list[i], TRUE); /* Loop for each comma-separated address */ commit 0582221c4a875e35b5ccefe9a17eef584819e4f0 Author: Heiko Schlittermann (HS12-RIPE) Date: Tue Dec 10 23:14:35 2019 +0100 TFO: disable for FreeBSD (cherry-picked from 52503083b9eded01c25e1c73f4510963c3cee799) diff --git a/src/src/ip.c b/src/src/ip.c index 19be51a03..70e3e2064 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -245,7 +245,7 @@ callout_address = string_sprintf("[%s]:%d", address, port); sigalrm_seen = FALSE; if (timeout > 0) ALARM(timeout); -#ifdef TCP_FASTOPEN +#if defined(TCP_FASTOPEN) && (defined(MSG_FASTOPEN) || defined(EXIM_TFO_CONNECTX)) /* TCP Fast Open, if the system has a cookie from a previous call to this peer, can send data in the SYN packet. The peer can send data before it gets our ACK of its SYN,ACK - the latter is useful for @@ -255,8 +255,8 @@ possibly use the data-on-syn, so support that too. */ if (fastopen_blob && f.tcp_fastopen_ok) { # ifdef MSG_FASTOPEN - /* This is a Linux implementation. It might be useable on FreeBSD; I have - not checked. */ + /* This is a Linux implementation. FreeBSD does not seem to have MSG_FASTOPEN so + how to get TFO is unknown. */ if ((rc = sendto(sock, fastopen_blob->data, fastopen_blob->len, MSG_FASTOPEN | MSG_DONTWAIT, s_ptr, s_len)) >= 0) commit 6397b292615a5ffb9fdb5bd305736e2ef5f00e0d Author: Samuel Thibault Date: Wed Dec 4 21:30:01 2019 +0000 Hurd: errno really uses more than a short-sized value. Bug 2476 (cherry picked from commit 377da0430697e6bcb8c48744eb5af4272a8f8075) diff --git a/src/src/structs.h b/src/src/structs.h index 3b5818df0..c05778161 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -648,11 +648,11 @@ typedef struct address_item { unsigned int domain_cache[(MAX_NAMED_LIST * 2)/32]; unsigned int localpart_cache[(MAX_NAMED_LIST * 2)/32]; int mode; /* mode for local transporting to a file */ + int basic_errno; /* status after failure */ int more_errno; /* additional error information */ /* (may need to hold a timestamp) */ unsigned int delivery_usec; /* subsecond part of delivery time */ - short int basic_errno; /* status after failure */ unsigned short child_count; /* number of child addresses */ short int return_file; /* fileno of return data file */ short int special_action; /* ( used when when deferred or failed */ commit 8ab104034bd456843cd5a4ab34af6ef339f9ac8d Author: Jeremy Harris Date: Tue Dec 10 20:04:15 2019 +0000 local_scan: align local_scan.h and docs re. store_get() (cherry picked from commit 4ec0fcb6737da9bc2603056d56fbf6e41e061967) diff --git a/src/src/local_scan.h b/src/src/local_scan.h index e0a97088f..da9a8911a 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -193,6 +193,14 @@ extern void smtp_vprintf(const char *, BOOL, va_list); string_sprintf_trc(fmt, US __FUNCTION__, __LINE__, __VA_ARGS__) extern uschar *string_sprintf_trc(const char *, const uschar *, unsigned, ...) ALMOST_PRINTF(1,4); +#define store_get(size, tainted) \ + store_get_3(size, tainted, __FUNCTION__, __LINE__) +extern void *store_get_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +#define store_get_perm(size, tainted) \ + store_get_perm_3(size, tainted, __FUNCTION__, __LINE__) +extern void *store_get_perm_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; + + #if defined(LOCAL_SCAN) || defined(DLFUNC_IMPL) /* When compiling a local_scan() file we want to rename a published API, so that we can use an inlined implementation in the compiles of the main Exim files, diff --git a/src/src/store.h b/src/src/store.h index 52f211ef5..39b0f6ecf 100644 --- a/src/src/store.h +++ b/src/src/store.h @@ -34,10 +34,7 @@ tracing information for debugging. */ #define store_free(addr) \ store_free_3(addr, __FUNCTION__, __LINE__) -#define store_get(size, tainted) \ - store_get_3(size, tainted, __FUNCTION__, __LINE__) -#define store_get_perm(size, tainted) \ - store_get_perm_3(size, tainted, __FUNCTION__, __LINE__) +/* store_get & store_get_perm are in local_scan.h */ #define store_malloc(size) \ store_malloc_3(size, __FUNCTION__, __LINE__) #define store_mark(void) \ @@ -55,8 +52,7 @@ typedef void ** rmark; extern BOOL store_extend_3(void *, BOOL, int, int, const char *, int); extern void store_free_3(void *, const char *, int); -extern void *store_get_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; -extern void *store_get_perm_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +/* store_get_3 & store_get_perm_3 are in local_scan.h */ extern void *store_malloc_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; extern rmark store_mark_3(const char *, int); extern void *store_newblock_3(void *, BOOL, int, int, const char *, int); commit 5169b5fa9104c024a3d3b31b163bfa3b9ec14c9c Author: Jeremy Harris Date: Wed Dec 11 14:32:23 2019 +0000 Fix taint issue in transport with DSN. Bug 2491 (cherry picked from commit c58803fd11185cbcf673e99cebf8397c9194357a) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 3fd94a19c..bb5fda1f4 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -3411,26 +3411,26 @@ BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; -smtp_context sx; +smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ gettimeofday(&start_delivery_time, NULL); suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ *message_defer = FALSE; -sx.addrlist = addrlist; -sx.conn_args.host = host; -sx.conn_args.host_af = host_af, -sx.port = defport; -sx.conn_args.interface = interface; -sx.helo_data = NULL; -sx.conn_args.tblock = tblock; -sx.verify = FALSE; -sx.sync_addr = sx.first_addr = addrlist; +sx->addrlist = addrlist; +sx->conn_args.host = host; +sx->conn_args.host_af = host_af, +sx->port = defport; +sx->conn_args.interface = interface; +sx->helo_data = NULL; +sx->conn_args.tblock = tblock; +sx->verify = FALSE; +sx->sync_addr = sx->first_addr = addrlist; /* Get the channel set up ready for a message (MAIL FROM being the next SMTP command to send */ -if ((rc = smtp_setup_conn(&sx, suppress_tls)) != OK) +if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK) return rc; /* If there is a filter command specified for this transport, we can now @@ -3456,10 +3456,10 @@ if (tblock->filter_command) if ( transport_filter_argv && *transport_filter_argv && **transport_filter_argv - && sx.peer_offered & OPTION_CHUNKING + && sx->peer_offered & OPTION_CHUNKING ) { - sx.peer_offered &= ~OPTION_CHUNKING; + sx->peer_offered &= ~OPTION_CHUNKING; DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n"); } } @@ -3473,11 +3473,11 @@ code was to use a goto to jump back to this point when there is another transaction to handle. */ SEND_MESSAGE: -sx.from_addr = return_path; -sx.sync_addr = sx.first_addr; -sx.ok = FALSE; -sx.send_rset = TRUE; -sx.completed_addr = FALSE; +sx->from_addr = return_path; +sx->sync_addr = sx->first_addr; +sx->ok = FALSE; +sx->send_rset = TRUE; +sx->completed_addr = FALSE; /* If we are a continued-connection-after-verify the MAIL and RCPT @@ -3487,10 +3487,10 @@ always has a sequence number greater than one. */ if (continue_hostname && continue_sequence == 1) { - sx.peer_offered = smtp_peer_options; - sx.pending_MAIL = FALSE; - sx.ok = TRUE; - sx.next_addr = NULL; + sx->peer_offered = smtp_peer_options; + sx->pending_MAIL = FALSE; + sx->ok = TRUE; + sx->next_addr = NULL; for (address_item * addr = addrlist; addr; addr = addr->next) addr->transport_return = PENDING_OK; @@ -3499,7 +3499,7 @@ else { /* Initiate a message transfer. */ - switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) + switch(smtp_write_mail_and_rcpt_cmds(sx, &yield)) { case 0: break; case -1: case -2: goto RESPONSE_FAILED; @@ -3517,13 +3517,13 @@ else address_item * a; unsigned cnt; - for (a = sx.first_addr, cnt = 0; a && cnt < sx.max_rcpt; a = a->next, cnt++) + for (a = sx->first_addr, cnt = 0; a && cnt < sx->max_rcpt; a = a->next, cnt++) if (a->transport_return != PENDING_OK) { /*XXX could we find a better errno than 0 here? */ set_errno_nohost(addrlist, 0, a->message, FAIL, testflag(a, af_pass_message)); - sx.ok = FALSE; + sx->ok = FALSE; break; } } @@ -3537,20 +3537,20 @@ are pipelining. The responses are all handled by sync_responses(). If using CHUNKING, do not send a BDAT until we know how big a chunk we want to send is. */ -if ( !(sx.peer_offered & OPTION_CHUNKING) - && (sx.ok || (pipelining_active && !mua_wrapper))) +if ( !(sx->peer_offered & OPTION_CHUNKING) + && (sx->ok || (pipelining_active && !mua_wrapper))) { - int count = smtp_write_command(&sx, SCMD_FLUSH, "DATA\r\n"); + int count = smtp_write_command(sx, SCMD_FLUSH, "DATA\r\n"); if (count < 0) goto SEND_FAILED; - switch(sync_responses(&sx, count, sx.ok ? +1 : -1)) + switch(sync_responses(sx, count, sx->ok ? +1 : -1)) { - case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ + case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ + case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ break; - case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ + case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: goto END_OFF; /* Timeout on RCPT */ @@ -3573,18 +3573,18 @@ for handling the SMTP dot-handling protocol, flagging to apply to headers as 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.ok - && (!(sx.peer_offered & OPTION_CHUNKING) || !pipelining_active)) +if ( !sx->ok + && (!(sx->peer_offered & OPTION_CHUNKING) || !pipelining_active)) { /* Save the first address of the next batch. */ - sx.first_addr = sx.next_addr; + sx->first_addr = sx->next_addr; - sx.ok = TRUE; + sx->ok = TRUE; } else { transport_ctx tctx = { - .u = {.fd = sx.cctx.sock}, /*XXX will this need TLS info? */ + .u = {.fd = sx->cctx.sock}, /*XXX will this need TLS info? */ .tblock = tblock, .addr = addrlist, .check_string = US".", @@ -3603,32 +3603,32 @@ else of responses. The callback needs a whole bunch of state so set up a transport-context structure to be passed around. */ - if (sx.peer_offered & OPTION_CHUNKING) + if (sx->peer_offered & OPTION_CHUNKING) { tctx.check_string = tctx.escape_string = NULL; tctx.options |= topt_use_bdat; tctx.chunk_cb = smtp_chunk_cmd_callback; - sx.pending_BDAT = FALSE; - sx.good_RCPT = sx.ok; - sx.cmd_count = 0; - tctx.smtp_context = &sx; + sx->pending_BDAT = FALSE; + sx->good_RCPT = sx->ok; + sx->cmd_count = 0; + tctx.smtp_context = sx; } else tctx.options |= topt_end_dot; /* Save the first address of the next batch. */ - sx.first_addr = sx.next_addr; + sx->first_addr = sx->next_addr; /* Responses from CHUNKING commands go in buffer. Otherwise, there has not been a response. */ - sx.buffer[0] = 0; + sx->buffer[0] = 0; sigalrm_seen = FALSE; transport_write_timeout = ob->data_timeout; smtp_command = US"sending data block"; /* For error messages */ DEBUG(D_transport|D_v) - if (sx.peer_offered & OPTION_CHUNKING) + if (sx->peer_offered & OPTION_CHUNKING) debug_printf(" will write message using CHUNKING\n"); else debug_printf(" SMTP>> writing message and terminating \".\"\n"); @@ -3651,7 +3651,7 @@ else if (!f.expand_string_forcedfail) { message = US"failed to expand arc_sign"; - sx.ok = FALSE; + sx->ok = FALSE; goto SEND_FAILED; } } @@ -3668,9 +3668,9 @@ else report_time_since(&t0, US"dkim_exim_sign_init (delta)"); # endif } - sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message); + sx->ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message); #else - sx.ok = transport_write_message(&tctx, 0); + sx->ok = transport_write_message(&tctx, 0); #endif /* transport_write_message() uses write() because it is called from other @@ -3684,7 +3684,7 @@ else or the failure of a transport filter or the expansion of added headers. Or, when CHUNKING, it can be a protocol-detected failure. */ - if (!sx.ok) + if (!sx->ok) if (message) goto SEND_FAILED; else goto RESPONSE_FAILED; @@ -3696,17 +3696,17 @@ else smtp_command = US"end of data"; - if (sx.peer_offered & OPTION_CHUNKING && sx.cmd_count > 1) + if (sx->peer_offered & OPTION_CHUNKING && sx->cmd_count > 1) { /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */ - switch(sync_responses(&sx, sx.cmd_count-1, 0)) + switch(sync_responses(sx, sx->cmd_count-1, 0)) { - case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ - case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ + case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */ + case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ break; - case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ - if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ + case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */ case 0: break; /* No 2xx or 5xx, but no probs */ case -1: goto END_OFF; /* Timeout on RCPT */ @@ -3725,17 +3725,17 @@ else individual responses, before going on with the overall response. If we don't get the warning then deal with per non-PRDR. */ - if(sx.prdr_active) + if(sx->prdr_active) { - sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3', ob->final_timeout); - if (!sx.ok && errno == 0) switch(sx.buffer[0]) + sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '3', ob->final_timeout); + if (!sx->ok && errno == 0) switch(sx->buffer[0]) { - case '2': sx.prdr_active = FALSE; - sx.ok = TRUE; + case '2': sx->prdr_active = FALSE; + sx->ok = TRUE; break; case '4': errno = ERRNO_DATA4XX; addrlist->more_errno |= - ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; break; } } @@ -3745,14 +3745,14 @@ else /* For non-PRDR SMTP, we now read a single response that applies to the whole message. If it is OK, then all the addresses have been delivered. */ - if (!sx.lmtp) + if (!sx->lmtp) { - sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', + sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->final_timeout); - if (!sx.ok && errno == 0 && sx.buffer[0] == '4') + if (!sx->ok && errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; - addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } } @@ -3768,7 +3768,7 @@ else software before the spool gets updated. Also record the final SMTP confirmation if needed (for SMTP only). */ - if (sx.ok) + if (sx->ok) { int flag = '='; struct timeval delivery_time; @@ -3776,7 +3776,7 @@ else uschar * conf = NULL; timesince(&delivery_time, &start_delivery_time); - sx.send_rset = FALSE; + sx->send_rset = FALSE; pipelining_active = FALSE; /* Set up confirmation if needed - applies only to SMTP */ @@ -3785,18 +3785,18 @@ else #ifdef DISABLE_EVENT LOGGING(smtp_confirmation) && #endif - !sx.lmtp + !sx->lmtp ) { - const uschar *s = string_printing(sx.buffer); + const uschar *s = string_printing(sx->buffer); /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ - conf = (s == sx.buffer)? US string_copy(s) : US s; + conf = (s == sx->buffer)? US string_copy(s) : US s; } /* Process all transported addresses - for LMTP or PRDR, read a status for each one. */ - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) { if (addr->transport_return != PENDING_OK) continue; @@ -3806,43 +3806,43 @@ else it doesn't get tried again too soon. */ #ifndef DISABLE_PRDR - if (sx.lmtp || sx.prdr_active) + if (sx->lmtp || sx->prdr_active) #else - if (sx.lmtp) + if (sx->lmtp) #endif { - if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', + if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->final_timeout)) { - if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED; + if (errno != 0 || sx->buffer[0] == 0) goto RESPONSE_FAILED; addr->message = string_sprintf( #ifndef DISABLE_PRDR - "%s error after %s: %s", sx.prdr_active ? "PRDR":"LMTP", + "%s error after %s: %s", sx->prdr_active ? "PRDR":"LMTP", #else "LMTP error after %s: %s", #endif - data_command, string_printing(sx.buffer)); + data_command, string_printing(sx->buffer)); setflag(addr, af_pass_message); /* Allow message to go to user */ - if (sx.buffer[0] == '5') + if (sx->buffer[0] == '5') addr->transport_return = FAIL; else { errno = ERRNO_DATA4XX; - addr->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; addr->transport_return = DEFER; #ifndef DISABLE_PRDR - if (!sx.prdr_active) + if (!sx->prdr_active) #endif retry_add_item(addr, addr->address_retry_key, 0); } continue; } - sx.completed_addr = TRUE; /* NOW we can set this flag */ + sx->completed_addr = TRUE; /* NOW we can set this flag */ if (LOGGING(smtp_confirmation)) { - const uschar *s = string_printing(sx.buffer); + const uschar *s = string_printing(sx->buffer); /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ - conf = (s == sx.buffer) ? US string_copy(s) : US s; + conf = (s == sx->buffer) ? US string_copy(s) : US s; } } @@ -3862,18 +3862,18 @@ else if (tcp_out_fastopen >= TFO_USED_NODATA) setflag(addr, af_tcp_fastopen); if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data); } - if (sx.pipelining_used) setflag(addr, af_pipelining); + if (sx->pipelining_used) setflag(addr, af_pipelining); #ifdef SUPPORT_PIPE_CONNECT - if (sx.early_pipe_active) setflag(addr, af_early_pipe); + if (sx->early_pipe_active) setflag(addr, af_early_pipe); #endif #ifndef DISABLE_PRDR - if (sx.prdr_active) setflag(addr, af_prdr_used); + if (sx->prdr_active) setflag(addr, af_prdr_used); #endif - if (sx.peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used); + if (sx->peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used); flag = '-'; #ifndef DISABLE_PRDR - if (!sx.prdr_active) + if (!sx->prdr_active) #endif { /* Update the journal. For homonymic addresses, use the base address plus @@ -3882,37 +3882,37 @@ else write error, as it may prove possible to update the spool file later. */ if (testflag(addr, af_homonym)) - sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); + sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); else - sprintf(CS sx.buffer, "%.500s\n", addr->unique); + sprintf(CS sx->buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer); - len = Ustrlen(CS sx.buffer); - if (write(journal_fd, sx.buffer, len) != len) + DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx->buffer); + len = Ustrlen(CS sx->buffer); + if (write(journal_fd, sx->buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " - "%s: %s", sx.buffer, strerror(errno)); + "%s: %s", sx->buffer, strerror(errno)); } } #ifndef DISABLE_PRDR - if (sx.prdr_active) + if (sx->prdr_active) { const uschar * overall_message; /* PRDR - get the final, overall response. For any non-success upgrade all the address statuses. */ - sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', + sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->final_timeout); - if (!sx.ok) + if (!sx->ok) { - if(errno == 0 && sx.buffer[0] == '4') + if(errno == 0 && sx->buffer[0] == '4') { errno = ERRNO_DATA4XX; - addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; + addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8; } - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) - if (sx.buffer[0] == '5' || addr->transport_return == OK) + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) + if (sx->buffer[0] == '5' || addr->transport_return == OK) addr->transport_return = PENDING_OK; /* allow set_errno action */ goto RESPONSE_FAILED; } @@ -3920,24 +3920,24 @@ else /* Append the overall response to the individual PRDR response for logging and update the journal, or setup retry. */ - overall_message = string_printing(sx.buffer); - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) + overall_message = string_printing(sx->buffer); + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) if (addr->transport_return == OK) addr->message = string_sprintf("%s\\n%s", addr->message, overall_message); - for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) if (addr->transport_return == OK) { if (testflag(addr, af_homonym)) - sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); + sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name); else - sprintf(CS sx.buffer, "%.500s\n", addr->unique); + sprintf(CS sx->buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx.buffer); - len = Ustrlen(CS sx.buffer); - if (write(journal_fd, sx.buffer, len) != len) + DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx->buffer); + len = Ustrlen(CS sx->buffer); + if (write(journal_fd, sx->buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " - "%s: %s", sx.buffer, strerror(errno)); + "%s: %s", sx->buffer, strerror(errno)); } else if (addr->transport_return == DEFER) retry_add_item(addr, addr->address_retry_key, -2); @@ -3961,7 +3961,7 @@ assumed if errno == 0 and there is no text in the buffer. If control reaches here during the setting up phase (i.e. before MAIL FROM) then always defer, as the problem is not related to this specific message. */ -if (!sx.ok) +if (!sx->ok) { int code, set_rc; uschar * set_message; @@ -3970,8 +3970,8 @@ if (!sx.ok) { save_errno = errno; message = NULL; - sx.send_quit = check_response(host, &save_errno, addrlist->more_errno, - sx.buffer, &code, &message, &pass_message); + sx->send_quit = check_response(host, &save_errno, addrlist->more_errno, + sx->buffer, &code, &message, &pass_message); goto FAILED; } @@ -3981,7 +3981,7 @@ if (!sx.ok) code = '4'; message = string_sprintf("send() to %s [%s] failed: %s", host->name, host->address, message ? message : US strerror(save_errno)); - sx.send_quit = FALSE; + sx->send_quit = FALSE; goto FAILED; } @@ -3989,7 +3989,7 @@ if (!sx.ok) { BOOL message_error; - sx.ok = FALSE; /* For when reached by GOTO */ + sx->ok = FALSE; /* For when reached by GOTO */ set_message = message; /* We want to handle timeouts after MAIL or "." and loss of connection after @@ -4051,7 +4051,7 @@ if (!sx.ok) if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); - write_logs(host, message, sx.first_addr ? sx.first_addr->basic_errno : 0); + write_logs(host, message, sx->first_addr ? sx->first_addr->basic_errno : 0); *message_defer = TRUE; } @@ -4084,7 +4084,7 @@ if (!sx.ok) set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host #ifdef EXPERIMENTAL_DSN_INFO - , sx.smtp_greeting, sx.helo_response + , sx->smtp_greeting, sx->helo_response #endif ); } @@ -4120,10 +4120,10 @@ hosts_nopass_tls. */ DEBUG(D_transport) debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d " - "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit, - sx.send_rset, f.continue_more, yield, sx.first_addr ? "not " : ""); + "yield=%d first_address is %sNULL\n", sx->ok, sx->send_quit, + sx->send_rset, f.continue_more, yield, sx->first_addr ? "not " : ""); -if (sx.completed_addr && sx.ok && sx.send_quit) +if (sx->completed_addr && sx->ok && sx->send_quit) { BOOL more; smtp_compare_t t_compare; @@ -4131,7 +4131,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit) t_compare.tblock = tblock; t_compare.current_sender_address = sender_address; - if ( sx.first_addr != NULL + if ( sx->first_addr != NULL || f.continue_more || ( #ifndef DISABLE_TLS @@ -4148,20 +4148,20 @@ if (sx.completed_addr && sx.ok && sx.send_quit) uschar *msg; BOOL pass_message; - if (sx.send_rset) - if (! (sx.ok = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0)) + if (sx->send_rset) + if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0)) { msg = US string_sprintf("send() to %s [%s] failed: %s", host->name, host->address, strerror(errno)); - sx.send_quit = FALSE; + sx->send_quit = FALSE; } - else if (! (sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), + else if (! (sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout))) { int code; - sx.send_quit = check_response(host, &errno, 0, sx.buffer, &code, &msg, + sx->send_quit = check_response(host, &errno, 0, sx->buffer, &code, &msg, &pass_message); - if (!sx.send_quit) + if (!sx->send_quit) { DEBUG(D_transport) debug_printf("H=%s [%s] %s\n", host->name, host->address, msg); @@ -4170,15 +4170,15 @@ if (sx.completed_addr && sx.ok && sx.send_quit) /* Either RSET was not needed, or it succeeded */ - if (sx.ok) + if (sx->ok) { #ifndef DISABLE_TLS int pfd[2]; #endif - int socket_fd = sx.cctx.sock; + int socket_fd = sx->cctx.sock; - if (sx.first_addr != NULL) /* More addresses still to be sent */ + if (sx->first_addr != NULL) /* More addresses still to be sent */ { /* in this run of the transport */ continue_sequence++; /* Causes * in logging */ goto SEND_MESSAGE; @@ -4200,16 +4200,16 @@ if (sx.completed_addr && sx.ok && sx.send_quit) a new EHLO. If we don't get a good response, we don't attempt to pass the socket on. */ - tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT); - sx.cctx.tls_ctx = NULL; + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); + sx->cctx.tls_ctx = NULL; smtp_peer_options = smtp_peer_options_wrap; - sx.ok = !sx.smtps - && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data) + sx->ok = !sx->smtps + && smtp_write_command(sx, SCMD_FLUSH, "EHLO %s\r\n", sx->helo_data) >= 0 - && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), + && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout); - if (sx.ok && f.continue_more) + if (sx->ok && f.continue_more) return yield; /* More addresses for another run */ } else @@ -4217,13 +4217,13 @@ if (sx.completed_addr && sx.ok && sx.send_quit) /* 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", + set_errno(sx->first_addr, errno, US"internal allocation problem", DEFER, FALSE, host # ifdef EXPERIMENTAL_DSN_INFO - , sx.smtp_greeting, sx.helo_response + , sx->smtp_greeting, sx->helo_response # endif ); } @@ -4238,10 +4238,10 @@ if (sx.completed_addr && sx.ok && sx.send_quit) /*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to propagate it from the initial */ - if (sx.ok && transport_pass_socket(tblock->name, host->name, + if (sx->ok && transport_pass_socket(tblock->name, host->name, host->address, new_message_id, socket_fd)) { - sx.send_quit = FALSE; + sx->send_quit = FALSE; /* We have passed the client socket to a fresh transport process. If TLS is still active, we need to proxy it for the transport we @@ -4256,7 +4256,7 @@ propagate it from the initial { testharness_pause_ms(100); /* let parent debug out */ /* does not return */ - smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd, + smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd, ob->command_timeout); } @@ -4266,10 +4266,10 @@ propagate it from the initial close(pfd[0]); /* tidy the inter-proc to disconn the proxy proc */ waitpid(pid, NULL, 0); - tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN); - sx.cctx.tls_ctx = NULL; - (void)close(sx.cctx.sock); - sx.cctx.sock = -1; + tls_close(sx->cctx.tls_ctx, TLS_NO_SHUTDOWN); + sx->cctx.tls_ctx = NULL; + (void)close(sx->cctx.sock); + sx->cctx.sock = -1; continue_transport = NULL; continue_hostname = NULL; return yield; @@ -4282,9 +4282,9 @@ propagate it from the initial /* If RSET failed and there are addresses left, they get deferred. */ else - set_errno(sx.first_addr, errno, msg, DEFER, FALSE, host + set_errno(sx->first_addr, errno, msg, DEFER, FALSE, host #ifdef EXPERIMENTAL_DSN_INFO - , sx.smtp_greeting, sx.helo_response + , sx->smtp_greeting, sx->helo_response #endif ); } @@ -4310,15 +4310,15 @@ operation, the old commented-out code was removed on 17-Sep-99. */ SEND_QUIT: #ifdef TCP_CORK -(void) setsockopt(sx.cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on)); +(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on)); #endif -if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n"); +if (sx->send_quit) (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n"); END_OFF: #ifndef DISABLE_TLS -tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); -sx.cctx.tls_ctx = NULL; +tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); +sx->cctx.tls_ctx = NULL; #endif /* Close the socket, and return the appropriate value, first setting @@ -4332,16 +4332,16 @@ specified in the transports, and therefore not visible at top level, in which case continue_more won't get set. */ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); -if (sx.send_quit) +if (sx->send_quit) { - shutdown(sx.cctx.sock, SHUT_WR); + shutdown(sx->cctx.sock, SHUT_WR); 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;) + 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 */ } -(void)close(sx.cctx.sock); +(void)close(sx->cctx.sock); #ifndef DISABLE_EVENT (void) event_raise(tblock->event_action, US"tcp:close", NULL); diff --git a/src/src/verify.c b/src/src/verify.c index fc8cd84ea..5c0678c4b 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -625,8 +625,8 @@ coding means skipping this whole loop and doing the append separately. */ { int host_af; int port = 25; - uschar *interface = NULL; /* Outgoing interface to use; NULL => any */ - smtp_context sx; + uschar * interface = NULL; /* Outgoing interface to use; NULL => any */ + smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */ if (!host->address) { @@ -666,14 +666,14 @@ coding means skipping this whole loop and doing the append separately. */ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); - sx.addrlist = addr; - sx.conn_args.host = host; - sx.conn_args.host_af = host_af, - sx.port = port; - sx.conn_args.interface = interface; - sx.helo_data = tf->helo_data; - sx.conn_args.tblock = addr->transport; - sx.verify = TRUE; + sx->addrlist = addr; + sx->conn_args.host = host; + sx->conn_args.host_af = host_af, + sx->port = port; + sx->conn_args.interface = interface; + sx->helo_data = tf->helo_data; + sx->conn_args.tblock = addr->transport; + sx->verify = TRUE; tls_retry_connection: /* Set the address state so that errors are recorded in it */ @@ -686,7 +686,7 @@ tls_retry_connection: SMTP command to send. If we tried TLS but it failed, try again without if permitted */ - yield = smtp_setup_conn(&sx, FALSE); + yield = smtp_setup_conn(sx, FALSE); #ifndef DISABLE_TLS if ( yield == DEFER && addr->basic_errno == ERRNO_TLSFAILURE @@ -698,7 +698,7 @@ tls_retry_connection: "%s: callout unencrypted to %s [%s] (not in hosts_require_tls)", addr->message, host->name, host->address); addr->transport_return = PENDING_DEFER; - yield = smtp_setup_conn(&sx, TRUE); + yield = smtp_setup_conn(sx, TRUE); } #endif if (yield != OK) @@ -728,11 +728,11 @@ tls_retry_connection: addr->authenticator = client_authenticator; addr->auth_id = client_authenticated_id; - sx.from_addr = from_address; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; /*XXX these 3 last might not be needed for verify? */ - sx.send_rset = TRUE; - sx.completed_addr = FALSE; + sx->from_addr = from_address; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; /*XXX these 3 last might not be needed for verify? */ + sx->send_rset = TRUE; + sx->completed_addr = FALSE; new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull ? ccache_reject_mfnull : ccache_accept; @@ -789,12 +789,12 @@ tls_retry_connection: Avoid using a SIZE option on the MAIL for all random-rcpt checks. */ - sx.avoid_option = OPTION_SIZE; + sx->avoid_option = OPTION_SIZE; /* Remember when we last did a random test */ new_domain_record.random_stamp = time(NULL); - if (smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0) + if (smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0) switch(addr->transport_return) { case PENDING_OK: /* random was accepted, unfortunately */ @@ -805,36 +805,36 @@ tls_retry_connection: goto no_conn; case FAIL: /* rejected: the preferred result */ new_domain_record.random_result = ccache_reject; - sx.avoid_option = 0; + sx->avoid_option = 0; /* Between each check, issue RSET, because some servers accept only one recipient after MAIL FROM:<>. XXX We don't care about that for postmaster_full. Should we? */ if ((done = - smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 && - smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout))) + smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0 && + smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', callout))) break; HDEBUG(D_acl|D_v) debug_printf_indent("problem after random/rset/mfrom; reopen conn\n"); random_local_part = NULL; #ifndef DISABLE_TLS - tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); #endif HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); - (void)close(sx.cctx.sock); - sx.cctx.sock = -1; + (void)close(sx->cctx.sock); + sx->cctx.sock = -1; #ifndef DISABLE_EVENT (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); #endif addr->address = main_address; addr->transport_return = PENDING_DEFER; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; - sx.send_rset = TRUE; - sx.completed_addr = FALSE; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; + sx->send_rset = TRUE; + sx->completed_addr = FALSE; goto tls_retry_connection; case DEFER: /* 4xx response to random */ break; /* Just to be clear. ccache_unknown, !done. */ @@ -843,10 +843,10 @@ tls_retry_connection: /* Re-setup for main verify, or for the error message when failing */ addr->address = main_address; addr->transport_return = PENDING_DEFER; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; - sx.send_rset = TRUE; - sx.completed_addr = FALSE; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; + sx->send_rset = TRUE; + sx->completed_addr = FALSE; } else done = TRUE; @@ -857,10 +857,10 @@ tls_retry_connection: if (done) { if (!(options & vopt_is_recipient && options & vopt_callout_no_cache)) - sx.avoid_option = OPTION_SIZE; + sx->avoid_option = OPTION_SIZE; done = FALSE; - switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) + switch(smtp_write_mail_and_rcpt_cmds(sx, &yield)) { case 0: switch(addr->transport_return) /* ok so far */ { @@ -878,7 +878,7 @@ tls_retry_connection: case -1: /* MAIL response error */ *failure_ptr = US"mail"; - if (errno == 0 && sx.buffer[0] == '5') + if (errno == 0 && sx->buffer[0] == '5') { setflag(addr, af_verify_nsfail); if (from_address[0] == 0) @@ -908,8 +908,8 @@ tls_retry_connection: cancel_cutthrough_connection(TRUE, US"postmaster verify"); HDEBUG(D_acl|D_v) debug_printf_indent("Cutthrough cancelled by presence of postmaster verify\n"); - done = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 - && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout); + done = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0 + && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', callout); if (done) { @@ -919,23 +919,23 @@ tls_retry_connection: addr->address = string_sprintf("postmaster@%.1000s", addr->domain); addr->transport_return = PENDING_DEFER; - sx.from_addr = pm_mailfrom; - sx.first_addr = sx.sync_addr = addr; - sx.ok = FALSE; - sx.send_rset = TRUE; - sx.completed_addr = FALSE; - sx.avoid_option = OPTION_SIZE; + sx->from_addr = pm_mailfrom; + sx->first_addr = sx->sync_addr = addr; + sx->ok = FALSE; + sx->send_rset = TRUE; + sx->completed_addr = FALSE; + sx->avoid_option = OPTION_SIZE; - if( smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0 + if( smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0 && addr->transport_return == PENDING_OK ) done = TRUE; else done = (options & vopt_callout_fullpm) != 0 - && smtp_write_command(&sx, SCMD_FLUSH, + && smtp_write_command(sx, SCMD_FLUSH, "RCPT TO:\r\n") >= 0 - && smtp_read_response(&sx, sx.buffer, - sizeof(sx.buffer), '2', callout); + && smtp_read_response(sx, sx->buffer, + sizeof(sx->buffer), '2', callout); /* Sort out the cache record */ @@ -943,7 +943,7 @@ tls_retry_connection: if (done) new_domain_record.postmaster_result = ccache_accept; - else if (errno == 0 && sx.buffer[0] == '5') + else if (errno == 0 && sx->buffer[0] == '5') { *failure_ptr = US"postmaster"; setflag(addr, af_verify_pmfail); @@ -968,7 +968,7 @@ no_conn: { case ETIMEDOUT: HDEBUG(D_verify) debug_printf("SMTP timeout\n"); - sx.send_quit = FALSE; + sx->send_quit = FALSE; break; #ifdef SUPPORT_I18N @@ -986,11 +986,11 @@ no_conn: break; #endif case ECONNREFUSED: - sx.send_quit = FALSE; + sx->send_quit = FALSE; break; case 0: - if (*sx.buffer == 0) Ustrcpy(sx.buffer, US"connection dropped"); + if (*sx->buffer == 0) Ustrcpy(sx->buffer, US"connection dropped"); /*XXX test here is ugly; seem to have a split of responsibility for building this message. Need to rationalise. Where is it done @@ -999,7 +999,7 @@ no_conn: */ if (!addr->message) addr->message = string_sprintf("response to \"%s\" was: %s", - big_buffer, string_printing(sx.buffer)); + big_buffer, string_printing(sx->buffer)); /* RFC 5321 section 4.2: the text portion of the response may have only HT, SP, Printable US-ASCII. Deal with awkward chars by cutting the @@ -1007,14 +1007,14 @@ no_conn: just become a multiline response (but wrapped in the error code we produce). */ - for (uschar * s = sx.buffer; - *s && s < sx.buffer + sizeof(sx.buffer); + for (uschar * s = sx->buffer; + *s && s < sx->buffer + sizeof(sx->buffer); s++) { uschar c = *s; if (c != '\t' && c != '\n' && (c < ' ' || c > '~')) { - if (s - sx.buffer < sizeof(sx.buffer) - 12) + if (s - sx->buffer < sizeof(sx->buffer) - 12) memcpy(s, "(truncated)", 12); else *s = '\0'; @@ -1022,13 +1022,13 @@ no_conn: } } addr->user_message = options & vopt_is_recipient - ? string_sprintf("Callout verification failed:\n%s", sx.buffer) + ? string_sprintf("Callout verification failed:\n%s", sx->buffer) : string_sprintf("Called: %s\nSent: %s\nResponse: %s", - host->address, big_buffer, sx.buffer); + host->address, big_buffer, sx->buffer); /* Hard rejection ends the process */ - if (sx.buffer[0] == '5') /* Address rejected */ + if (sx->buffer[0] == '5') /* Address rejected */ { yield = FAIL; done = TRUE; @@ -1075,7 +1075,7 @@ no_conn: && !random_local_part && !pm_mailfrom && cutthrough.cctx.sock < 0 - && !sx.lmtp + && !sx->lmtp ) { HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n", @@ -1085,7 +1085,7 @@ no_conn: cutthrough.callout_hold_only = !cutthrough.delivery; cutthrough.is_tls = tls_out.active.sock >= 0; /* We assume no buffer in use in the outblock */ - cutthrough.cctx = sx.cctx; + cutthrough.cctx = sx->cctx; cutthrough.nrcpt = 1; cutthrough.transport = addr->transport->name; cutthrough.interface = interface; @@ -1121,23 +1121,23 @@ no_conn: /* Ensure no cutthrough on multiple verifies that were incompatible */ if (options & vopt_callout_recipsender) cancel_cutthrough_connection(TRUE, US"not usable for cutthrough"); - if (sx.send_quit) - if (smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n") != -1) + if (sx->send_quit) + if (smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n") != -1) /* Wait a short time for response, and discard it */ - smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', 1); + smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', 1); - if (sx.cctx.sock >= 0) + if (sx->cctx.sock >= 0) { #ifndef DISABLE_TLS - if (sx.cctx.tls_ctx) + if (sx->cctx.tls_ctx) { - tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); - sx.cctx.tls_ctx = NULL; + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT); + sx->cctx.tls_ctx = NULL; } #endif HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); - (void)close(sx.cctx.sock); - sx.cctx.sock = -1; + (void)close(sx->cctx.sock); + sx->cctx.sock = -1; #ifndef DISABLE_EVENT (void) event_raise(addr->transport->event_action, US"tcp:close", NULL); #endif commit 92a158a6b870886fef89ecc084e6e745f91060e7 Author: Jeremy Harris Date: Thu Dec 12 14:06:54 2019 +0000 ARC: fix crash induced by misordered headers. Bug 2493 (cherry picked from commit e9dfcfb7b87107c2a55441c93aa463f9ea7586e1) diff --git a/src/src/arc.c b/src/src/arc.c index 857e0c046..94365498f 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -381,7 +381,7 @@ adding instances as needed and checking for duplicate lines. static uschar * arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff, - BOOL instance_only) + BOOL instance_only, arc_line ** alp_ret) { unsigned i; arc_set * as; @@ -401,6 +401,7 @@ if (!(as = arc_find_set(ctx, i))) return US"set find"; if (*(alp = (arc_line **)(US as + hoff))) return US"dup hdr"; *alp = al; +if (alp_ret) *alp_ret = al; return NULL; } @@ -424,7 +425,7 @@ if (strncmpic(ARC_HDR_AAR, h->text, ARC_HDRLEN_AAR) == 0) debug_printf("ARC: found AAR: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar), - TRUE))) + TRUE, NULL))) { DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e); return US"inserting AAR"; @@ -443,15 +444,13 @@ else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0) debug_printf("ARC: found AMS: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams), - instance_only))) + instance_only, &ams))) { DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e); return US"inserting AMS"; } /* defaults */ - /*XXX dubious selection of ams here */ - ams = ctx->arcset_chain->hdr_ams; if (!ams->c.data) { ams->c_head.data = US"simple"; ams->c_head.len = 6; @@ -469,7 +468,7 @@ else if (strncmpic(ARC_HDR_AS, h->text, ARC_HDRLEN_AS) == 0) debug_printf("ARC: found AS: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as), - instance_only))) + instance_only, NULL))) { DEBUG(D_acl) debug_printf("inserting AS: %s\n", e); return US"inserting AS"; commit 2529a7a64df7aa824d453c6419af07825409e519 Author: Jeremy Harris Date: Thu Dec 12 23:43:10 2019 +0000 Fix taint issue with retry records. Bug 2492 (cherry picked from commit 5fae29d5b430d6a5f58c6c02cdefbbf307e258a9) diff --git a/src/src/retry.c b/src/src/retry.c index d068f547d..175da216e 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -659,7 +659,8 @@ for (int i = 0; i < 3; i++) /* Read a retry record from the database or construct a new one. Ignore an old one if it is too old since it was last updated. */ - retry_record = dbfn_read(dbm_file, rti->key); + retry_record = dbfn_read_with_length(dbm_file, rti->key, + &message_space); if ( retry_record && now - retry_record->time_stamp > retry_data_expire) retry_record = NULL; @@ -675,7 +676,7 @@ for (int i = 0; i < 3; i++) retry_record->expired = FALSE; retry_record->text[0] = 0; /* just in case */ } - else message_space = Ustrlen(retry_record->text); + else message_space -= sizeof(dbdata_retry); /* Compute how long this destination has been failing */ @@ -806,15 +807,17 @@ for (int i = 0; i < 3; i++) if (next_try - now > retry_interval_max) next_try = now + retry_interval_max; - /* If the new message length is greater than the previous one, we - have to copy the record first. */ - - if (message_length > message_space) - { - dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length, FALSE); - memcpy(newr, retry_record, sizeof(dbdata_retry)); - retry_record = newr; - } + /* If the new message length is greater than the previous one, we have + to copy the record first. If we're using an old one, the read used + tainted memory so we're ok to write into it. */ + + if (message_length > message_space) + { + dbdata_retry * newr = + store_get(sizeof(dbdata_retry) + message_length, is_tainted(message)); + memcpy(newr, retry_record, sizeof(dbdata_retry)); + retry_record = newr; + } /* Set up the retry record; message_length may be less than the string length for very long error strings. */ commit 134c037ed24f77957556f40856659c1456df1bcc Author: Jeremy Harris Date: Fri Dec 13 14:26:17 2019 +0000 DMARC: default dmarc_tld_file to unset. Bug 2494 (cherry picked from commit 39fdec3c4a4b4c1cc60cd17413b096dd07344734) diff --git a/src/src/globals.c b/src/src/globals.c index 358c380a8..85a25a7f2 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -844,7 +844,7 @@ uschar *dmarc_forensic_sender = NULL; uschar *dmarc_history_file = NULL; uschar *dmarc_status = NULL; uschar *dmarc_status_text = NULL; -uschar *dmarc_tld_file = US DMARC_TLD_FILE; +uschar *dmarc_tld_file = NULL; uschar *dmarc_used_domain = NULL; #endif diff --git a/src/src/receive.c b/src/src/receive.c index 31e3f7cbb..4e85ffacf 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1703,10 +1703,6 @@ header_line *msgid_header = NULL; header_line *received_header; BOOL msgid_header_newly_created = FALSE; -#ifdef SUPPORT_DMARC -int dmarc_up = 0; -#endif - /* Variables for use when building the Received: header. */ uschar *timestamp; @@ -1768,7 +1764,7 @@ if (smtp_input && !smtp_batched_input && !f.dkim_disable_verify) #endif #ifdef SUPPORT_DMARC -dmarc_up = dmarc_init(); /* initialize libopendmarc */ +if (sender_host_address) dmarc_init(); /* initialize libopendmarc */ #endif /* Remember the time of reception. Exim uses time+pid for uniqueness of message @@ -3499,7 +3495,7 @@ else #endif /* WITH_CONTENT_SCAN */ #ifdef SUPPORT_DMARC - dmarc_up = dmarc_store_data(from_header); + dmarc_store_data(from_header); #endif #ifndef DISABLE_PRDR commit a0f5040c4d5878342bc6d391d692cf88007d06c0 Author: Jeremy Harris Date: Sat Dec 14 19:39:15 2019 +0000 Zero smtp context structure after allocation diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index bb5fda1f4..8fd9ff94a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -895,8 +895,9 @@ else sx->ehlo_resp = er->data; dbfn_close(dbm_file); DEBUG(D_transport) debug_printf( - "EHLO response bits from cache: cleartext 0x%04x crypted 0x%04x\n", - er->data.cleartext_features, er->data.crypted_features); + "EHLO response bits from cache: cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x\n", + er->data.cleartext_features, er->data.cleartext_auths, + er->data.crypted_features, er->data.crypted_auths); return TRUE; } dbfn_close(dbm_file); @@ -1904,39 +1905,39 @@ sx->conn_args.ob = ob; sx->lmtp = strcmpic(ob->protocol, US"lmtp") == 0; sx->smtps = strcmpic(ob->protocol, US"smtps") == 0; -sx->ok = FALSE; +/* sx->ok = FALSE; */ sx->send_rset = TRUE; sx->send_quit = TRUE; sx->setting_up = TRUE; sx->esmtp = TRUE; -sx->esmtp_sent = FALSE; +/* sx->esmtp_sent = FALSE; */ #ifdef SUPPORT_I18N -sx->utf8_needed = FALSE; +/* sx->utf8_needed = FALSE; */ #endif sx->dsn_all_lasthop = TRUE; #ifdef SUPPORT_DANE -sx->conn_args.dane = FALSE; +/* sx->conn_args.dane = FALSE; */ sx->dane_required = verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK; #endif #ifdef SUPPORT_PIPE_CONNECT -sx->early_pipe_active = sx->early_pipe_ok = FALSE; -sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0; -sx->pending_BANNER = sx->pending_EHLO = FALSE; +/* sx->early_pipe_active = sx->early_pipe_ok = FALSE; */ +/* sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0; */ +/* sx->pending_BANNER = sx->pending_EHLO = sx->pending_MAIL = FALSE; */ #endif if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999; -sx->peer_offered = 0; -sx->avoid_option = 0; +/* sx->peer_offered = 0; */ +/* sx->avoid_option = 0; */ sx->igquotstr = US""; if (!sx->helo_data) sx->helo_data = ob->helo_data; #ifdef EXPERIMENTAL_DSN_INFO -sx->smtp_greeting = NULL; -sx->helo_response = NULL; +/* sx->smtp_greeting = NULL; */ +/* sx->helo_response = NULL; */ #endif smtp_command = US"initial connection"; -sx->buffer[0] = '\0'; +/* sx->buffer[0] = '\0'; */ /* Set up the buffer for reading SMTP response packets. */ @@ -1950,9 +1951,9 @@ sx->inblock.ptrend = sx->inbuffer; sx->outblock.buffer = sx->outbuffer; sx->outblock.buffersize = sizeof(sx->outbuffer); sx->outblock.ptr = sx->outbuffer; -sx->outblock.cmd_count = 0; -sx->outblock.authenticating = FALSE; -sx->outblock.conn_args = NULL; +/* sx->outblock.cmd_count = 0; */ +/* sx->outblock.authenticating = FALSE; */ +/* sx->outblock.conn_args = NULL; */ /* Reset the parameters of a TLS session. */ @@ -3410,13 +3411,13 @@ struct timeval start_delivery_time; BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; - smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ gettimeofday(&start_delivery_time, NULL); suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ *message_defer = FALSE; +memset(sx, 0, sizeof(*sx)); sx->addrlist = addrlist; sx->conn_args.host = host; sx->conn_args.host_af = host_af, @@ -3424,7 +3425,7 @@ sx->port = defport; sx->conn_args.interface = interface; sx->helo_data = NULL; sx->conn_args.tblock = tblock; -sx->verify = FALSE; +/* sx->verify = FALSE; */ sx->sync_addr = sx->first_addr = addrlist; /* Get the channel set up ready for a message (MAIL FROM being the next @@ -3488,9 +3489,9 @@ always has a sequence number greater than one. */ if (continue_hostname && continue_sequence == 1) { sx->peer_offered = smtp_peer_options; - sx->pending_MAIL = FALSE; + /* sx->pending_MAIL = FALSE; */ sx->ok = TRUE; - sx->next_addr = NULL; + /* sx->next_addr = NULL; */ for (address_item * addr = addrlist; addr; addr = addr->next) addr->transport_return = PENDING_OK; diff --git a/src/src/verify.c b/src/src/verify.c index 5c0678c4b..edc4869ac 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -574,6 +574,7 @@ else { smtp_transport_options_block *ob = (smtp_transport_options_block *)addr->transport->options_block; + smtp_context * sx = NULL; /* The information wasn't available in the cache, so we have to do a real callout and save the result in the cache for next time, unless no_cache is set, @@ -626,7 +627,6 @@ coding means skipping this whole loop and doing the append separately. */ int host_af; int port = 25; uschar * interface = NULL; /* Outgoing interface to use; NULL => any */ - smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */ if (!host->address) { @@ -666,6 +666,9 @@ coding means skipping this whole loop and doing the append separately. */ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); + if (!sx) sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */ + memset(sx, 0, sizeof(*sx)); + sx->addrlist = addr; sx->conn_args.host = host; sx->conn_args.host_af = host_af, commit 0445bf5044cafd376d9d788bbf7551e21411a8e6 Author: Jeremy Harris Date: Tue Dec 17 10:46:21 2019 +0000 ARC: Reset received ARC instance counter before next message on a connection. Bug 2498 (cherry picked from commit 1415d1a6c91e66922036079b3e3b481e579cdb00) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index f37b2f6e0..d0fb6b81c 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2087,6 +2087,7 @@ dmarc_used_domain = NULL; #endif #ifdef EXPERIMENTAL_ARC arc_state = arc_state_reason = NULL; +arc_received_instance = 0; #endif dsn_ret = 0; dsn_envid = NULL; commit 1bfc75b19e528a82ce2ff59f7c7c9b91e6e13226 Author: Wolfgang Breyha Date: Fri Dec 20 14:01:23 2019 +0000 SPF: only require "v=spf1" on TXT DNS records during lookups. Bug 2499 (cherry picked from commit 4a3709fb2cfb1ec26ac60bcdb8bbc078d3377ddc) diff --git a/src/src/spf.c b/src/src/spf.c index 1955b5d96..b7041d3e3 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -43,17 +43,15 @@ dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; SPF_dns_rr_t * spfrr; -DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup\n"); +DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain); if (dns_lookup(dnsa, US domain, rr_type, NULL) == DNS_SUCCEED) for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if ( rr->type == rr_type - && Ustrncmp(rr->data+1, "v=spf1", 6) == 0) + && (rr_type != T_TXT || Ustrncmp(rr->data+1, "v=spf1", 6) == 0)) { - gstring * g = NULL; - uschar chunk_len; - uschar * s; + const uschar * s = rr->data; SPF_dns_rr_t srr = { .domain = CS rr->name, /* query information */ .domain_buf_len = DNS_MAXNAME, @@ -71,19 +69,49 @@ if (dns_lookup(dnsa, US domain, rr_type, NULL) == DNS_SUCCEED) .source = spf_dns_server }; - for (int off = 0; off < rr->size; off += chunk_len) + switch(rr_type) { - chunk_len = (rr->data)[off++]; - g = string_catn(g, US ((rr->data)+off), chunk_len); + case T_MX: + s += 2; /* skip the MX precedence field */ + case T_PTR: + { + uschar * buf = store_malloc(256); + (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s, + (DN_EXPAND_ARG4_TYPE)buf, 256); + s = buf; + break; + } + + case T_TXT: + { + gstring * g = NULL; + uschar chunk_len; + for (int off = 0; off < rr->size; off += chunk_len) + { + if (!(chunk_len = s[off++])) break; + g = string_catn(g, s+off, chunk_len); + } + if (!g) + { + HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " + "empty name: treated as non-existent host name\n"); + continue; + } + gstring_release_unused(g); + s = string_copy_malloc(string_from_gstring(g)); + break; + } + + case T_A: + case T_AAAA: + default: + { + uschar * buf = store_malloc(dnsa->answerlen + 1); + s = memcpy(buf, s, dnsa->answerlen + 1); + break; + } } - if (!g) - { - HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " - "empty name: treated as non-existent host name\n"); - continue; - } - gstring_release_unused(g); - s = string_copy_malloc(string_from_gstring(g)); + DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s); srr.rr = (void *) &s; /* spfrr->rr must have been malloc()d for this */ commit f7be88fdcef970d66332dabe702a15fe3e1cdd9a Author: Jeremy Harris Date: Sat Dec 21 20:31:31 2019 +0000 Eximon: fix string-handling. Bug 2500 (cherry picked from commit 8aa16eb712afac844fb28ed465c6076c6b0ea22b) diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 1f42e0ec3..f4977614f 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -868,9 +868,9 @@ em_text.o: ../exim_monitor/em_text.c ../exim_monitor/em_hdr.h em_xs.o: ../exim_monitor/em_xs.c ../exim_monitor/em_hdr.h em_version.o: ../exim_monitor/em_version.c ../exim_monitor/em_hdr.h $(MONBIN): $(HDRS) - @echo "$(CC) exim_monitor/`echo $@ | sed 's/o$$/c/'`" - $(FE)$(CC) -o $@ -c $(CFLAGS) -I. -I../exim_monitor $(INCLUDE) $(XINCLUDE) \ - ../exim_monitor/`echo $@ | sed 's/o$$/c/'` + @echo "$(CC) exim_monitor/$(@:.o=.c)" + $(FE)$(CC) -o $@ -c $(CFLAGS) -DCOMPILE_UTILITY -I. -I../exim_monitor $(INCLUDE) $(XINCLUDE) \ + ../exim_monitor/$(@:.o=.c) # Targets for the various libraries that Exim uses. diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 85bc3fd88..1185cfa44 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -56,6 +56,11 @@ string_vformat_trc(gstring * g, const uschar * func, unsigned line, uschar * string_sprintf_trc(const char * a, const uschar * b, unsigned c, ...) { return NULL; } +BOOL +string_format_trc(uschar * buf, int len, const uschar * func, unsigned line, + const char * fmt, ...) +{ return FALSE; } + struct global_flags f; unsigned int log_selector[1]; diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 80f656530..a86fcbbdc 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -59,6 +59,10 @@ string_vformat_trc(gstring * g, const uschar * func, unsigned line, uschar * string_sprintf_trc(const char * fmt, const uschar * func, unsigned line, ...) { return NULL; } +BOOL +string_format_trc(uschar * buf, int len, const uschar * func, unsigned line, + const char * fmt, ...) +{ return FALSE; } struct global_flags f; unsigned int log_selector[1]; diff --git a/src/src/functions.h b/src/src/functions.h index f4fcd1e19..87d1a04d8 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -902,9 +902,18 @@ return string_sprintf("%s/%s/%s/%s/%s%s", static inline uschar * spool_fname(const uschar * purpose, const uschar * subdir, const uschar * fname, - const uschar * suffix) + const uschar * suffix) { +#ifdef COMPILE_UTILITY /* version avoiding string-extension */ +int len = Ustrlen(spool_directory) + 1 + Ustrlen(queue_name) + 1 + Ustrlen(purpose) + 1 + + Ustrlen(subdir) + 1 + Ustrlen(fname) + Ustrlen(suffix) + 1; +uschar * buf = store_get(len, FALSE); +string_format(buf, len, "%s/%s/%s/%s/%s%s", + spool_directory, queue_name, purpose, subdir, fname, suffix); +return buf; +#else return spool_q_fname(purpose, queue_name, subdir, fname, suffix); +#endif } static inline void diff --git a/src/src/string.c b/src/src/string.c index a20807054..49a028343 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -678,12 +678,20 @@ Returns: pointer to fresh piece of store containing sprintf'ed string uschar * string_sprintf_trc(const char *format, const uschar * func, unsigned line, ...) { -gstring * g; -va_list ap; +#ifdef COMPILE_UTILITY +uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; +gstring gs = { .size = STRING_SPRINTF_BUFFER_SIZE, .ptr = 0, .s = buffer }; +gstring * g = &gs; +unsigned flags = 0; +#else +gstring * g = NULL; +unsigned flags = SVFMT_REBUFFER|SVFMT_EXTEND; +#endif +va_list ap; va_start(ap, line); -g = string_vformat_trc(NULL, func, line, STRING_SPRINTF_BUFFER_SIZE, - SVFMT_REBUFFER|SVFMT_EXTEND, format, ap); +g = string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE, + flags, format, ap); va_end(ap); if (!g) @@ -692,8 +700,12 @@ if (!g) " called from %s %d\n", STRING_SPRINTF_BUFFER_SIZE, format, func, line); +#ifdef COMPILE_UTILITY +return string_copyn(g->s, g->ptr); +#else gstring_release_unused(g); return string_from_gstring(g); +#endif } commit 53afb7482744e6bb127f624d47844d8b6bd54997 Author: Jeremy Harris Date: Sun Dec 22 21:51:53 2019 +0000 Fix build with heimdal-gssapi. Bug 2501 (cherry picked from commit 5031095fd4553935c70e1c24a9936dfc609cdc67) diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index 273d4f47b..3dfcb8c6a 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -415,7 +415,7 @@ while (step < 4) NULL, /* conf_state: no confidentiality applied */ &gbufdesc_out /* output buffer */ ); - if (GSS_ERROR(maj_stat) + if (GSS_ERROR(maj_stat)) { exim_gssapi_error_defer(NULL, maj_stat, min_stat, "gss_wrap(SASL state after auth)"); commit db78a5c40bc27d6f9fe9151442e9dbb9abb11d8a Author: Jeremy Harris Date: Sun Dec 29 14:34:12 2019 +0000 Fix the variables set by gsasl authenticator (cherry picked from commit 98eb95929140ee1e2b2b367b12abb45762d155e9) diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index faf30bb8a..ee6cb4f03 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -461,11 +461,11 @@ switch (prop) case GSASL_VALIDATE_SIMPLE: /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */ propval = US gsasl_property_fast(sctx, GSASL_AUTHID); - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); - auth_vars[1] = expand_nstring[2] = propval ? propval : US""; + auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_PASSWORD); - auth_vars[2] = expand_nstring[3] = propval ? propval : US""; + auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US""; expand_nmax = 3; for (int i = 1; i <= 3; ++i) expand_nlength[i] = Ustrlen(expand_nstring[i]); @@ -483,7 +483,7 @@ switch (prop) } propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); /* We always set $auth1, even if only to empty string. */ - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; expand_nlength[1] = Ustrlen(expand_nstring[1]); expand_nmax = 1; @@ -501,7 +501,7 @@ switch (prop) } propval = US gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN); /* We always set $auth1, even if only to empty string. */ - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; expand_nlength[1] = Ustrlen(expand_nstring[1]); expand_nmax = 1; @@ -521,9 +521,9 @@ switch (prop) to the first release of Exim with this authenticator, they've been switched to match the ordering of GSASL_VALIDATE_SIMPLE. */ propval = US gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME); - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); - auth_vars[1] = expand_nstring[2] = propval ? propval : US""; + auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US""; expand_nmax = 2; for (int i = 1; i <= 2; ++i) expand_nlength[i] = Ustrlen(expand_nstring[i]); @@ -558,11 +558,11 @@ switch (prop) needing to add more glue, since avoiding that is a large part of the point of SASL. */ propval = US gsasl_property_fast(sctx, GSASL_AUTHID); - auth_vars[0] = expand_nstring[1] = propval ? propval : US""; + auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_AUTHZID); - auth_vars[1] = expand_nstring[2] = propval ? propval : US""; + auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US""; propval = US gsasl_property_fast(sctx, GSASL_REALM); - auth_vars[2] = expand_nstring[3] = propval ? propval : US""; + auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US""; expand_nmax = 3; for (int i = 1; i <= 3; ++i) expand_nlength[i] = Ustrlen(expand_nstring[i]); commit bf4d783783e07ca6c3af9546658dbe86769f177c Author: Jeremy Harris Date: Sun Dec 29 14:46:10 2019 +0000 SPF: fix handling mix of spf and other txt records. Bug 2499 (cherry picked from commits 4533e306fc, b17ea87dd9, 44e90dfa83, 10897f5d74) diff --git a/src/src/spf.c b/src/src/spf.c index b7041d3e3..8ead817b9 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -37,90 +37,111 @@ SPF_dns_rr_t * spf_nxdomain = NULL; static SPF_dns_rr_t * SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server, -const char *domain, ns_type rr_type, int should_cache) + const char *domain, ns_type rr_type, int should_cache) { dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; SPF_dns_rr_t * spfrr; +unsigned found = 0; + +SPF_dns_rr_t srr = { + .domain = CS domain, /* query information */ + .domain_buf_len = 0, + .rr_type = rr_type, + + .rr_buf_len = 0, /* answer information */ + .rr_buf_num = 0, /* no free of s */ + .utc_ttl = 0, + + .hook = NULL, /* misc information */ + .source = spf_dns_server +}; +int dns_rc; DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain); -if (dns_lookup(dnsa, US domain, rr_type, NULL) == DNS_SUCCEED) - for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - if ( rr->type == rr_type - && (rr_type != T_TXT || Ustrncmp(rr->data+1, "v=spf1", 6) == 0)) +switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL)) + { + case DNS_SUCCEED: srr.herrno = NETDB_SUCCESS; break; + case DNS_AGAIN: srr.herrno = TRY_AGAIN; break; + case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break; + case DNS_NODATA: srr.herrno = NO_DATA; break; + case DNS_FAIL: + default: srr.herrno = NO_RECOVERY; break; + } + +for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + if (rr->type == rr_type) found++; + +if (found == 0) + { + SPF_dns_rr_dup(&spfrr, &srr); + return spfrr; + } + +srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found); + +found = 0; +for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + if (rr->type == rr_type) + { + const uschar * s = rr->data; + + srr.ttl = rr->ttl; + switch(rr_type) { - const uschar * s = rr->data; - SPF_dns_rr_t srr = { - .domain = CS rr->name, /* query information */ - .domain_buf_len = DNS_MAXNAME, - .rr_type = rr->type, - - .num_rr = 1, /* answer information */ - .rr = NULL, - .rr_buf_len = 0, - .rr_buf_num = 0, - .ttl = rr->ttl, - .utc_ttl = 0, - .herrno = NETDB_SUCCESS, - - .hook = NULL, /* misc information */ - .source = spf_dns_server - }; - - switch(rr_type) + case T_MX: + s += 2; /* skip the MX precedence field */ + case T_PTR: { - case T_MX: - s += 2; /* skip the MX precedence field */ - case T_PTR: - { - uschar * buf = store_malloc(256); - (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s, - (DN_EXPAND_ARG4_TYPE)buf, 256); - s = buf; - break; - } + uschar * buf = store_malloc(256); + (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s, + (DN_EXPAND_ARG4_TYPE)buf, 256); + s = buf; + break; + } - case T_TXT: + case T_TXT: + { + gstring * g = NULL; + uschar chunk_len; + + if (strncmpic(rr->data+1, US"v=spf1", 6) != 0) { - gstring * g = NULL; - uschar chunk_len; - for (int off = 0; off < rr->size; off += chunk_len) - { - if (!(chunk_len = s[off++])) break; - g = string_catn(g, s+off, chunk_len); - } - if (!g) - { - HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " - "empty name: treated as non-existent host name\n"); - continue; - } - gstring_release_unused(g); - s = string_copy_malloc(string_from_gstring(g)); - break; + HDEBUG(D_host_lookup) debug_printf("not an spf record\n"); + continue; } - case T_A: - case T_AAAA: - default: + for (int off = 0; off < rr->size; off += chunk_len) { - uschar * buf = store_malloc(dnsa->answerlen + 1); - s = memcpy(buf, s, dnsa->answerlen + 1); - break; + if (!(chunk_len = s[off++])) break; + g = string_catn(g, s+off, chunk_len); } + if (!g) + continue; + gstring_release_unused(g); + s = string_copy_malloc(string_from_gstring(g)); + break; } - DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s); - srr.rr = (void *) &s; - - /* spfrr->rr must have been malloc()d for this */ - SPF_dns_rr_dup(&spfrr, &srr); - return spfrr; + case T_A: + case T_AAAA: + default: + { + uschar * buf = store_malloc(dnsa->answerlen + 1); + s = memcpy(buf, s, dnsa->answerlen + 1); + break; + } } + DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s); + srr.rr[found++] = (void *) s; + } -SPF_dns_rr_dup(&spfrr, spf_nxdomain); +srr.num_rr = found; +/* spfrr->rr must have been malloc()d for this */ +SPF_dns_rr_dup(&spfrr, &srr); return spfrr; } @@ -129,14 +150,11 @@ return spfrr; SPF_dns_server_t * SPF_dns_exim_new(int debug) { -SPF_dns_server_t *spf_dns_server; +SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t)); DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n"); -if (!(spf_dns_server = malloc(sizeof(SPF_dns_server_t)))) - return NULL; memset(spf_dns_server, 0, sizeof(SPF_dns_server_t)); - spf_dns_server->destroy = NULL; spf_dns_server->lookup = SPF_dns_exim_lookup; spf_dns_server->get_spf = NULL; commit dfb8f72b2237627b26767d1e803e8ed95ad659d2 Author: Wolfgang Breyha Date: Tue Jan 7 13:03:18 2020 +0000 SPF: fix result for case of only non-spf TXT RRs. Bug 2499 (cherry picked from commit 67794d2b830fc580f87b0635718d95e32b467be1) diff --git a/src/src/spf.c b/src/src/spf.c index 8ead817b9..12b756b46 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -139,7 +139,12 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; srr.rr[found++] = (void *) s; } -srr.num_rr = found; +/* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with +empty ANSWER section. */ + +if (!(srr.num_rr = found)) + srr.herrno = NO_DATA; + /* spfrr->rr must have been malloc()d for this */ SPF_dns_rr_dup(&spfrr, &srr); return spfrr; commit 338f36842f10ef84e684dddf59819837fd7792a3 Author: Jeremy Harris Date: Wed Jan 15 10:40:20 2020 +0000 Fix error logging for dynamically-loaded modules. Bug 2507 (cherry picked from commits b1c673ddfa, 3fc07bd570) diff --git a/src/src/drtables.c b/src/src/drtables.c index 059756284..ca051bd20 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -740,10 +740,11 @@ init_lookup_list(void) dl = dlopen(CS big_buffer, RTLD_NOW);// TJ was LAZY if (dl == NULL) { - fprintf(stderr, "Error loading %s: %s\n", name, dlerror()); - moduleerrors++; - log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, dlerror()); - continue; + errormessage = dlerror(); + fprintf(stderr, "Error loading %s: %s\n", name, errormessage); + log_write(0, LOG_MAIN|LOG_PANIC, "Error loading lookup module %s: %s\n", name, errormessage); + moduleerrors++; + continue; } /* FreeBSD nsdispatch() can trigger dlerror() errors about @@ -756,16 +757,16 @@ init_lookup_list(void) info = (struct lookup_module_info*) dlsym(dl, "_lookup_module_info"); if ((errormsg = dlerror()) != NULL) { fprintf(stderr, "%s does not appear to be a lookup module (%s)\n", name, errormsg); + log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg); dlclose(dl); moduleerrors++; - log_write(0, LOG_MAIN|LOG_PANIC, "%s does not appear to be a lookup module (%s)\n", name, errormsg); continue; } if (info->magic != LOOKUP_MODULE_INFO_MAGIC) { fprintf(stderr, "Lookup module %s is not compatible with this version of Exim\n", name); + log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name); dlclose(dl); moduleerrors++; - log_write(0, LOG_MAIN|LOG_PANIC, "Lookup module %s is not compatible with this version of Exim\n", name); continue; } commit bbeab68df3b3c2d5507b1fdca07509fdbb3ec5a1 Author: Jeremy Harris Date: Tue Jan 14 17:48:57 2020 +0000 heimdal auth: fix the increase of big_buffer size. Bug 2501 (cherry picked from commit 7a66b3afa11a70021297c176acf56831692be89a) diff --git a/src/src/auths/README b/src/src/auths/README index d4f125c30..66bdcdcf8 100644 --- a/src/src/auths/README +++ b/src/src/auths/README @@ -34,7 +34,7 @@ instance block for this configured mechanism. It must set the flags called the server and/or client functions are available for this authenticator. Typically this depends on whether server or client configuration options have been set, but it is also possible to have an authenticator that has only one of -the server or client functions. +the server or client functions. The function may not touch big_buffer. SERVER AUTHENTICATION diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index 3dfcb8c6a..523f7c69a 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -200,16 +200,6 @@ if (krc) krb5_free_context(context); -/* RFC 4121 section 5.2, SHOULD support 64K input buffers */ -if (big_buffer_size < (64 * 1024)) - { - uschar *newbuf; - big_buffer_size = 64 * 1024; - newbuf = store_malloc(big_buffer_size); - store_free(big_buffer); - big_buffer = newbuf; - } - ablock->server = TRUE; } diff --git a/src/src/macros.h b/src/src/macros.h index 76913d64e..4e6b1b8a9 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -152,12 +152,19 @@ enough to hold all the headers from a normal kind of message. */ into big_buffer_size and in some circumstances increased. It should be at least as long as the maximum path length. */ -#if defined PATH_MAX && PATH_MAX > 16384 +#ifdef AUTH_HEIMDAL_GSSAPI + /* RFC 4121 section 5.2, SHOULD support 64K input buffers */ +# define __BIG_BUFFER_SIZE 65536 +#else +# define __BIG_BUFFER_SIZE 16384 +#endif + +#if defined PATH_MAX && PATH_MAX > __BIG_BUFFER_SIZE # define BIG_BUFFER_SIZE PATH_MAX -#elif defined MAXPATHLEN && MAXPATHLEN > 16384 +#elif defined MAXPATHLEN && MAXPATHLEN > __BIG_BUFFER_SIZE # define BIG_BUFFER_SIZE MAXPATHLEN #else -# define BIG_BUFFER_SIZE 16384 +# define BIG_BUFFER_SIZE __BIG_BUFFER_SIZE #endif /* header size of pipe content diff --git a/src/src/readconf.c b/src/src/readconf.c index 0233019cf..62cfcfbf9 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3788,6 +3788,7 @@ while ((buffer = get_config_line()) != NULL) if (!d->driver_name) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "no driver defined for %s \"%s\"", class, d->name); + /* s is using big_buffer, so this call had better not */ (d->info->init)(d); d = NULL; } commit 1ccd26e24267ffa0c40b70c2c3282481fe4977c7 Author: Jeremy Harris Date: Thu Jan 16 14:12:56 2020 +0000 Taint: hybrid checking mode (cherry picked from commit 36eb5d3d77426d8cbf4243ea752f8d8cd1d5c682) diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index 52c55a4a3..9b9c7d417 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -5,6 +5,8 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +#define EM_VERSION_C + #include "mytypes.h" #include "store.h" #include "macros.h" diff --git a/src/src/functions.h b/src/src/functions.h index 87d1a04d8..0b5905562 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -187,6 +187,7 @@ extern void deliver_succeeded(address_item *); extern uschar *deliver_get_sender_address (uschar *id); extern void delivery_re_exec(int); +extern void die_tainted(const uschar *, const uschar *, int); extern BOOL directory_make(const uschar *, const uschar *, int, BOOL); #ifndef DISABLE_DKIM extern uschar *dkim_exim_query_dns_txt(const uschar *); @@ -602,6 +603,61 @@ extern BOOL write_chunk(transport_ctx *, uschar *, int); extern ssize_t write_to_fd_buf(int, const uschar *, size_t); +/******************************************************************************/ +/* Predicate: if an address is in a tainted pool. +By extension, a variable pointing to this address is tainted. +*/ + +static inline BOOL +is_tainted(const void * p) +{ +#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) || defined(EM_VERSION_C) +return FALSE; + +#else +extern BOOL is_tainted_fn(const void *); +extern void * tainted_base, * tainted_top; + +return f.taint_check_slow + ? is_tainted_fn(p) : p >= tainted_base && p < tainted_top; +#endif +} + +/******************************************************************************/ +/* String functions */ +static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); +#endif +return US strcat(CS dst, CCS src); +} +static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); +#endif +return US strcpy(CS dst, CCS src); +} +static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); +#endif +return US strncat(CS dst, CCS src, n); +} +static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) +{ +#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) +if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); +#endif +return US strncpy(CS dst, CCS src, n); +} +/*XXX will likely need unchecked copy also */ + + +/******************************************************************************/ + #if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) /* exim_chown - in some NFSv4 setups *seemes* to be an issue with chown(, ). @@ -634,8 +690,8 @@ exim_chown(const uschar *name, uid_t owner, gid_t group) return chown(CCS name, owner, group) ? exim_chown_failure(-1, name, owner, group) : 0; } - #endif /* !MACRO_PREDEF && !COMPILE_UTILITY */ + /******************************************************************************/ /* String functions */ diff --git a/src/src/globals.c b/src/src/globals.c index 85a25a7f2..72449229e 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -311,6 +311,7 @@ struct global_flags f = .synchronous_delivery = FALSE, .system_filtering = FALSE, + .taint_check_slow = FALSE, .tcp_fastopen_ok = FALSE, .tcp_in_fastopen = FALSE, .tcp_in_fastopen_data = FALSE, diff --git a/src/src/globals.h b/src/src/globals.h index ca342acc2..ac7bb8ef3 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -272,6 +272,7 @@ extern struct global_flags { BOOL synchronous_delivery :1; /* TRUE if -odi is set */ BOOL system_filtering :1; /* TRUE when running system filter */ + BOOL taint_check_slow :1; /* malloc/mmap are not returning distinct ranges */ BOOL tcp_fastopen_ok :1; /* appears to be supported by kernel */ BOOL tcp_in_fastopen :1; /* conn usefully used fastopen */ BOOL tcp_in_fastopen_data :1; /* fastopen carried data */ diff --git a/src/src/mytypes.h b/src/src/mytypes.h index ceb9f1b55..e31ee8c1a 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -100,19 +100,15 @@ functions that are called quite often; for other calls to external libraries #define Uread(f,b,l) read(f,CS(b),l) #define Urename(s,t) rename(CCS(s),CCS(t)) #define Ustat(s,t) stat(CCS(s),t) -#define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) #define Ustrchr(s,n) US strchr(CCS(s),n) #define CUstrchr(s,n) CUS strchr(CCS(s),n) #define CUstrerror(n) CUS strerror(n) #define Ustrcmp(s,t) strcmp(CCS(s),CCS(t)) -#define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) #define Ustrcpy_nt(s,t) strcpy(CS s, CCS t) /* no taint check */ #define Ustrcspn(s,t) strcspn(CCS(s),CCS(t)) #define Ustrftime(s,m,f,t) strftime(CS(s),m,f,t) #define Ustrlen(s) (int)strlen(CCS(s)) -#define Ustrncat(s,t,n) __Ustrncat(s, CUS(t),n, __FUNCTION__, __LINE__) #define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n) -#define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t),n, __FUNCTION__, __LINE__) #define Ustrncpy_nt(s,t,n) strncpy(CS s, CCS t, n) /* no taint check */ #define Ustrpbrk(s,t) strpbrk(CCS(s),CCS(t)) #define Ustrrchr(s,n) US strrchr(CCS(s),n) @@ -125,57 +121,17 @@ functions that are called quite often; for other calls to external libraries #define Ustrtoul(s,t,b) strtoul(CCS(s),CSS(t),b) #define Uunlink(s) unlink(CCS(s)) -extern void die_tainted(const uschar *, const uschar *, int); - -/* Predicate: if an address is in a tainted pool. -By extension, a variable pointing to this address is tainted. -*/ - -static inline BOOL -is_tainted(const void * p) -{ -#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) -return FALSE; - -#elif defined(TAINT_CHECK_SLOW) -extern BOOL is_tainted_fn(const void *); -return is_tainted_fn(p); - +#ifdef EM_VERSION_C +# define Ustrcat(s,t) strcat(CS(s), CCS(t)) +# define Ustrcpy(s,t) strcpy(CS(s), CCS(t)) +# define Ustrncat(s,t,n) strncat(CS(s), CCS(t), n) +# define Ustrncpy(s,t,n) strncpy(CS(s), CCS(t), n) #else -extern void * tainted_base, * tainted_top; -return p >= tainted_base && p < tainted_top; -#endif -} - -static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcat", CUS func, line); -#endif -return US strcat(CS dst, CCS src); -} -static inline uschar * __Ustrcpy(uschar * dst, const uschar * src, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrcpy", CUS func, line); -#endif -return US strcpy(CS dst, CCS src); -} -static inline uschar * __Ustrncat(uschar * dst, const uschar * src, size_t n, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncat", CUS func, line); -#endif -return US strncat(CS dst, CCS src, n); -} -static inline uschar * __Ustrncpy(uschar * dst, const uschar * src, size_t n, const char * func, int line) -{ -#if !defined(COMPILE_UTILITY) && !defined(MACRO_PREDEF) -if (!is_tainted(dst) && is_tainted(src)) die_tainted(US"Ustrncpy", CUS func, line); +# define Ustrcat(s,t) __Ustrcat(s, CUS(t), __FUNCTION__, __LINE__) +# define Ustrcpy(s,t) __Ustrcpy(s, CUS(t), __FUNCTION__, __LINE__) +# define Ustrncat(s,t,n) __Ustrncat(s, CUS(t), n, __FUNCTION__, __LINE__) +# define Ustrncpy(s,t,n) __Ustrncpy(s, CUS(t), n, __FUNCTION__, __LINE__) #endif -return US strncpy(CS dst, CCS src, n); -} -/*XXX will likely need unchecked copy also */ #endif /* End of mytypes.h */ diff --git a/src/src/store.c b/src/src/store.c index a06e1c19a..692a993e9 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -162,8 +162,14 @@ static void internal_tainted_free(storeblock *, const char *, int linenumber); /******************************************************************************/ -/* Slower version check, for use when platform intermixes malloc and mmap area -addresses. */ +/* Test if a pointer refers to tainted memory. + +Slower version check, for use when platform intermixes malloc and mmap area +addresses. Test against the current-block of all tainted pools first, then all +blocks of all tainted pools. + +Return: TRUE iff tainted +*/ BOOL is_tainted_fn(const void * p) @@ -171,23 +177,20 @@ is_tainted_fn(const void * p) storeblock * b; int pool; -for (pool = 0; pool < nelem(chainbase); pool++) +for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) if ((b = current_block[pool])) { - char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; - if (CS p >= bc && CS p <= bc + b->length) goto hit; + uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; + if (US p >= bc && US p <= bc + b->length) return TRUE; } -for (pool = 0; pool < nelem(chainbase); pool++) +for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) for (b = chainbase[pool]; b; b = b->next) { - char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; - if (CS p >= bc && CS p <= bc + b->length) goto hit; + uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; + if (US p >= bc && US p <= bc + b->length) return TRUE; } return FALSE; - -hit: -return pool >= POOL_TAINT_BASE; } @@ -198,6 +201,13 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n", msg, func, line); } +static void +use_slow_taint_check(void) +{ +DEBUG(D_any) debug_printf("switching to slow-mode taint checking\n"); +f.taint_check_slow = TRUE; +} + /************************************************* * Get a block from the current pool * @@ -820,6 +830,14 @@ if (!(yield = malloc((size_t)size))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to malloc %d bytes of memory: " "called from line %d in %s", size, linenumber, func); +/* If malloc ever returns apparently tainted memory, which glibc +malloc will as it uses mmap for larger requests, we must switch to +the slower checking for tainting (checking an address against all +the tainted pool block spans, rather than just the mmap span) */ + +if (!f.taint_check_slow && is_tainted(yield)) + use_slow_taint_check(); + return store_alloc_tail(yield, size, func, linenumber, US"Malloc"); } commit ccf4e2396b27b519174aa79552e61d11aafbdc36 Author: Jeremy Harris Date: Fri Jan 17 21:55:11 2020 +0000 Fix taint hybrid-checking on BSD (cherry-picked from commit 677481d4fc) Broken-by: 1ccd26e242 diff --git a/src/src/store.c b/src/src/store.c index 692a993e9..6118ef28d 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -175,16 +175,15 @@ BOOL is_tainted_fn(const void * p) { storeblock * b; -int pool; -for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) +for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) if ((b = current_block[pool])) { uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; if (US p >= bc && US p <= bc + b->length) return TRUE; } -for (pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) +for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) for (b = chainbase[pool]; b; b = b->next) { uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; @@ -204,10 +203,28 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n", static void use_slow_taint_check(void) { +#ifndef COMPILE_UTILITY DEBUG(D_any) debug_printf("switching to slow-mode taint checking\n"); +#endif f.taint_check_slow = TRUE; } +static void +verify_all_untainted(void) +{ +for (int pool = 0; pool < POOL_TAINT_BASE; pool++) + for (storeblock * b = chainbase[pool]; b; b = b->next) + { + uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; + if (is_tainted(bc)) + { + use_slow_taint_check(); + return; + } + } +} + + /************************************************* * Get a block from the current pool * @@ -740,7 +757,7 @@ int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; BOOL release_ok = !tainted && store_last_get[pool] == block; uschar * newtext; -#ifndef MACRO_PREDEF +#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) if (is_tainted(block) != tainted) die_tainted(US"store_newblock", CUS func, linenumber); #endif @@ -799,6 +816,7 @@ if (!(yield = mmap(NULL, (size_t)size, if (yield < tainted_base) tainted_base = yield; if ((top = US yield + size) > tainted_top) tainted_top = top; +if (!f.taint_check_slow) use_slow_taint_check(); return store_alloc_tail(yield, size, func, line, US"Mmap"); } commit 4ce411ffa737df738e18e1e7b008ad3d3ac5c398 Author: Brian Foley Date: Sat Jan 25 15:27:49 2020 +0000 TFO: even in binary built for modern Linux, handle error returned by old Linux kernel. Bug 2518 (cherry picked from commit c3da38a12a2372a7f6a48be97ebfd80aeceda828) diff --git a/src/src/ip.c b/src/src/ip.c index 70e3e2064..43ca6a1c9 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -269,28 +269,34 @@ if (fastopen_blob && f.tcp_fastopen_ok) /*XXX also seen on successful TFO, sigh */ tcp_out_fastopen = fastopen_blob->len > 0 ? TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA; } - else if (errno == EINPROGRESS) /* expected if we had no cookie for peer */ + else switch (errno) + { + case EINPROGRESS: /* expected if we had no cookie for peer */ /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */ /* apparently no visibility of the diffference at this point */ /* seen for with-data, proper TFO opt, cookie-req */ /* with netwk delay, post-conn tcp_info sees unacked 1 for R, 2 for C; code in smtp_out.c */ /* ? older Experimental TFO option behaviour ? */ - { /* queue unsent data */ - DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", - fastopen_blob->len > 0 ? "with" : "no"); - if (!fastopen_blob->data) - { - tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ - rc = 0; - } - else - rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); - } - else if(errno == EOPNOTSUPP) - { - DEBUG(D_transport) - debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); - goto legacy_connect; + DEBUG(D_transport|D_v) debug_printf(" TFO mode sendto, %s data: EINPROGRESS\n", + fastopen_blob->len > 0 ? "with" : "no"); + if (!fastopen_blob->data) + { + tcp_out_fastopen = TFO_ATTEMPTED_NODATA; /* we tried; unknown if useful yet */ + rc = 0; + } + else /* queue unsent data */ + rc = send(sock, fastopen_blob->data, fastopen_blob->len, 0); + break; + + case EOPNOTSUPP: + DEBUG(D_transport) + debug_printf("Tried TCP Fast Open but apparently not enabled by sysctl\n"); + goto legacy_connect; + + case EPIPE: + DEBUG(D_transport) + debug_printf("Tried TCP Fast Open but kernel too old to support it\n"); + goto legacy_connect; } # endif # ifdef EXIM_TFO_CONNECTX commit 69b2f92c0b5da548eaafe4813319f4647fa9c19a Author: Jeremy Harris Date: Thu Jan 30 11:38:30 2020 +0000 Taint: slow-mode checking only (cherry-picked from 4381d60bc9) diff --git a/src/src/functions.h b/src/src/functions.h index 0b5905562..af633851b 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -616,10 +616,7 @@ return FALSE; #else extern BOOL is_tainted_fn(const void *); -extern void * tainted_base, * tainted_top; - -return f.taint_check_slow - ? is_tainted_fn(p) : p >= tainted_base && p < tainted_top; +return is_tainted_fn(p); #endif } diff --git a/src/src/store.c b/src/src/store.c index 6118ef28d..c81744a7b 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -102,13 +102,6 @@ static storeblock *current_block[NPOOLS]; static void *next_yield[NPOOLS]; static int yield_length[NPOOLS] = { -1, -1, -1, -1, -1, -1 }; -/* The limits of the tainted pools. Tracking these on new allocations enables -a fast is_tainted implementation. We assume the kernel only allocates mmaps using -one side or the other of data+heap, not both. */ - -void * tainted_base = (void *)-1; -void * tainted_top = (void *)0; - /* pool_malloc holds the amount of memory used by the store pools; this goes up and down as store is reset or released. nonpool_malloc is the total got by malloc from other calls; this doesn't go down because it is just freed by @@ -200,30 +193,6 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n", msg, func, line); } -static void -use_slow_taint_check(void) -{ -#ifndef COMPILE_UTILITY -DEBUG(D_any) debug_printf("switching to slow-mode taint checking\n"); -#endif -f.taint_check_slow = TRUE; -} - -static void -verify_all_untainted(void) -{ -for (int pool = 0; pool < POOL_TAINT_BASE; pool++) - for (storeblock * b = chainbase[pool]; b; b = b->next) - { - uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; - if (is_tainted(bc)) - { - use_slow_taint_check(); - return; - } - } -} - /************************************************* @@ -814,10 +783,6 @@ if (!(yield = mmap(NULL, (size_t)size, log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to mmap %d bytes of memory: " "called from line %d of %s", size, line, func); -if (yield < tainted_base) tainted_base = yield; -if ((top = US yield + size) > tainted_top) tainted_top = top; -if (!f.taint_check_slow) use_slow_taint_check(); - return store_alloc_tail(yield, size, func, line, US"Mmap"); } @@ -848,14 +813,6 @@ if (!(yield = malloc((size_t)size))) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to malloc %d bytes of memory: " "called from line %d in %s", size, linenumber, func); -/* If malloc ever returns apparently tainted memory, which glibc -malloc will as it uses mmap for larger requests, we must switch to -the slower checking for tainting (checking an address against all -the tainted pool block spans, rather than just the mmap span) */ - -if (!f.taint_check_slow && is_tainted(yield)) - use_slow_taint_check(); - return store_alloc_tail(yield, size, func, linenumber, US"Malloc"); } commit 59bcc75f56ffeb9fa220f1eb53d45bf254258ac7 Author: Jeremy Harris Date: Thu Feb 13 14:08:31 2020 +0000 Auths: fix cyrus-sasl driver for gssapi use. Bug 2524 Broken-by: c0fb53b74e Cherry-picked from: 5c329a4388 diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index 480010bab..19416a1bb 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -347,10 +347,10 @@ for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; ) } else { - /* make sure that we have a null-terminated string */ - out2 = string_copyn(output, outlen); + /* auth_get_data() takes a length-specfied block of binary + which can include zeroes; no terminating NUL is needed */ - if ((rc = auth_get_data(&input, out2, outlen)) != OK) + if ((rc = auth_get_data(&input, output, outlen)) != OK) { /* we couldn't get the data, so free up the library before * returning whatever error we get */ commit 26b045604bd574a6d93868ed437c08503c67d289 Author: Jeremy Harris Date: Thu Feb 13 16:52:52 2020 +0000 GnuTLS: fix hanging callout connections Broken-by: 925ac8e4f1 Cherry-picked from: bd95ffc2ba diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index fc426a251..574dcafd9 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -181,6 +181,10 @@ typedef struct exim_gnutls_state { BOOL peer_dane_verified; BOOL trigger_sni_changes; BOOL have_set_peerdn; +#ifdef SUPPORT_CORK + BOOL corked:1; +#endif + const struct host_item *host; /* NULL if server */ gnutls_x509_crt_t peercert; uschar *peerdn; @@ -3309,9 +3313,8 @@ ssize_t outbytes; size_t left = len; exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server; #ifdef SUPPORT_CORK -static BOOL corked = FALSE; -if (more && !corked) gnutls_record_cork(state->session); +if (more && !state->corked) gnutls_record_cork(state->session); #endif DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__, @@ -3352,10 +3355,10 @@ if (len > INT_MAX) } #ifdef SUPPORT_CORK -if (more != corked) +if (more != state->corked) { if (!more) (void) gnutls_record_uncork(state->session, 0); - corked = more; + state->corked = more; } #endif commit da5bc28fbc92521e5fe5ab65a8bce1a10610b70c Author: Jeremy Harris Date: Wed Feb 26 10:54:56 2020 +0000 Fix ${tr } expansion item. Bug 2533 Broken-by: acec9514b1 (cherry picked from commit 03f110c5d92f3c8aa9dc447253a33e9c039a78b0) diff --git a/src/src/expand.c b/src/src/expand.c index 21686e99c..dea26494a 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5029,7 +5029,7 @@ while (*s != 0) { client_conn_ctx cctx; int timeout = 5; - int save_ptr = yield->ptr; + int save_ptr = yield ? yield->ptr : 0; FILE * fp = NULL; uschar * arg; uschar * sub_arg[4]; @@ -5262,7 +5262,7 @@ while (*s != 0) if (sigalrm_seen) { - yield->ptr = save_ptr; + if (yield) yield->ptr = save_ptr; expand_string_message = US "socket read timed out"; goto SOCK_FAIL; } @@ -5429,7 +5429,7 @@ while (*s != 0) case EITEM_TR: { - int oldptr = yield->ptr; + int oldptr = yield ? yield->ptr : 0; int o2m; uschar *sub[3]; @@ -6170,7 +6170,7 @@ while (*s != 0) case EITEM_REDUCE: { int sep = 0; - int save_ptr = yield->ptr; + int save_ptr = yield ? yield->ptr : 0; uschar outsep[2] = { '\0', '\0' }; const uschar *list, *expr, *temp; uschar *save_iterate_item = iterate_item; @@ -6317,7 +6317,8 @@ while (*s != 0) item of the output list, add in a space if the new item begins with the separator character, or is an empty string. */ - if (yield->ptr != save_ptr && (temp[0] == *outsep || temp[0] == 0)) + if ( yield && yield->ptr != save_ptr + && (temp[0] == *outsep || temp[0] == 0)) yield = string_catn(yield, US" ", 1); /* Add the string in "temp" to the output list that we are building, @@ -6357,7 +6358,7 @@ while (*s != 0) the redundant final separator. Even though an empty item at the end of a list does not count, this is tidier. */ - else if (yield->ptr != save_ptr) yield->ptr--; + else if (yield && yield->ptr != save_ptr) yield->ptr--; /* Restore preserved $item */ @@ -7222,7 +7223,7 @@ while (*s != 0) { uschar outsep[2] = { ':', '\0' }; uschar *address, *error; - int save_ptr = yield->ptr; + int save_ptr = yield ? yield->ptr : 0; int start, end, domain; /* Not really used */ while (isspace(*sub)) sub++; @@ -7253,7 +7254,7 @@ while (*s != 0) if (address) { - if (yield->ptr != save_ptr && address[0] == *outsep) + if (yield && yield->ptr != save_ptr && address[0] == *outsep) yield = string_catn(yield, US" ", 1); for (;;) @@ -7282,7 +7283,7 @@ while (*s != 0) /* If we have generated anything, remove the redundant final separator. */ - if (yield->ptr != save_ptr) yield->ptr--; + if (yield && yield->ptr != save_ptr) yield->ptr--; f.parse_allow_group = FALSE; continue; } commit 1560edd9600ee87a18854a9bf8cdd68d54544205 Author: Jeremy Harris Date: Sun Mar 1 15:19:22 2020 +0000 Fix $mime_part_count for non-mime message on multi-message connection. Bug 2537 Cherry-picked from b273058b34 diff --git a/src/src/receive.c b/src/src/receive.c index 4e85ffacf..350c4eda8 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1756,6 +1756,13 @@ if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX; message_linecount = body_linecount = body_zerocount = max_received_linelength = 0; +#ifdef WITH_CONTENT_SCAN +/* reset non-per-part mime variables */ +mime_is_coverletter = 0; +mime_is_rfc822 = 0; +mime_part_count = -1; +#endif + #ifndef DISABLE_DKIM /* Call into DKIM to set up the context. In CHUNKING mode we clear the dot-stuffing flag */ commit 8d844fbac46c4dcafd507d66b497a537581f0994 Author: Jeremy Harris Date: Thu Mar 5 19:45:04 2020 +0000 Taint: track in ${utf8clean:} operator (cherry picked from commit e68def51cb753d730249565e630b549a73857ec1) diff --git a/src/src/expand.c b/src/src/expand.c index dea26494a..f489c5b8b 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7422,10 +7422,10 @@ while (*s != 0) case EOP_FROM_UTF8: { - while (*sub != 0) + uschar * buff = store_get(4, is_tainted(sub)); + while (*sub) { int c; - uschar buff[4]; GETUTF8INC(c, sub); if (c > 255) c = '_'; buff[0] = c; @@ -7446,7 +7446,17 @@ while (*s != 0) int complete; uschar seq_buff[4]; /* accumulate utf-8 here */ - while (*sub != 0) + /* Manually track tainting, as we deal in individual chars below */ + + if (is_tainted(sub)) + if (yield->s && yield->ptr) + gstring_rebuffer(yield); + else + yield->s = store_get(yield->size = Ustrlen(sub), TRUE); + + /* Check the UTF-8, byte-by-byte */ + + while (*sub) { complete = 0; uschar c = *sub++; diff --git a/src/src/functions.h b/src/src/functions.h index af633851b..a1dab5db0 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -902,6 +902,18 @@ va_end(ap); return g; } + +/* Copy the content of a string to tainted memory */ + +static inline void +gstring_rebuffer(gstring * g) +{ +uschar * s = store_get(g->size, TRUE); +memcpy(s, g->s, g->ptr); +g->s = s; +} + + /******************************************************************************/ #define store_get_dns_answer() store_get_dns_answer_trc(CUS __FUNCTION__, __LINE__) diff --git a/src/src/string.c b/src/src/string.c index 49a028343..6fa01250f 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -12,7 +12,6 @@ utilities and tests, and are cut out by the COMPILE_UTILITY macro. */ #include "exim.h" #include -static void gstring_rebuffer(gstring * g); #ifndef COMPILE_UTILITY /************************************************* @@ -1243,16 +1242,6 @@ return !!gp; -/* Copy the content of a string to tainted memory */ -static void -gstring_rebuffer(gstring * g) -{ -uschar * s = store_get(g->size, TRUE); -memcpy(s, g->s, g->ptr); -g->s = s; -} - - /* Build or append to a growing-string, sprintf-style. commit 7d0615457f5f26bab3900804d60e9b96c09738c9 Author: Jeremy Harris Date: Sun Mar 15 21:08:28 2020 +0000 Fix spurious detection of timeout while writing to transport filter (cherry picked from commit 130212d30d47e588c2bc03edcfce11bf3857da03) diff --git a/src/src/transport.c b/src/src/transport.c index df7fd1628..70e96c065 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -253,6 +253,7 @@ for (int i = 0; i < 100; i++) else { + sigalrm_seen = FALSE; ALARM(local_timeout); rc = commit 768f456c80387efb40ae35d7873d9eee548c4232 Author: Jeremy Harris Date: Fri Mar 20 19:52:14 2020 +0000 Fix segfault on bad cmdline -f (sender) argument. Bug 2541 (cherry picked from commit 5fcc791a74a6f6933b3fb03f36e9ea3553152cf7) diff --git a/src/src/exim.c b/src/src/exim.c index 0e839c5d4..96c020c8e 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2554,8 +2554,10 @@ for (i = 1; i < argc; i++) #ifdef SUPPORT_I18N allow_utf8_domains = TRUE; #endif - sender_address = parse_extract_address(argrest, &errmess, - &dummy_start, &dummy_end, &sender_address_domain, TRUE); + if (!(sender_address = parse_extract_address(argrest, &errmess, + &dummy_start, &dummy_end, &sender_address_domain, TRUE))) + exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); + sender_address = string_copy_taint(sender_address, TRUE); #ifdef SUPPORT_I18N message_smtputf8 = string_is_utf8(sender_address); @@ -2563,8 +2565,6 @@ for (i = 1; i < argc; i++) #endif allow_domain_literals = FALSE; strip_trailing_dot = FALSE; - if (!sender_address) - exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); } f.sender_address_forced = TRUE; } commit 35250a206044733546c5a4ac63cd9b4e3aa13e70 Author: Jeremy Harris Date: Sun Mar 22 11:35:50 2020 +0000 Taint: fix dsearch result to be untainted (cherry-picked from commits 36b600bb77, eb763a7aaf) diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index c27f5d6e6..1eb2924f0 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -28,7 +28,7 @@ static void * dsearch_open(uschar *dirname, uschar **errmsg) { DIR *dp = opendir(CS dirname); -if (dp == NULL) +if (!dp) { int save_errno = errno; *errmsg = string_open_failed(errno, "%s for directory search", dirname); @@ -47,8 +47,8 @@ return (void *)(-1); /* The handle will always be (void *)(-1), but don't try casting it to an integer as this gives warnings on 64-bit systems. */ -BOOL -static dsearch_check(void *handle, uschar *filename, int modemask, uid_t *owners, +static BOOL +dsearch_check(void *handle, uschar *filename, int modemask, uid_t *owners, gid_t *owngroups, uschar **errmsg) { handle = handle; @@ -87,7 +87,9 @@ if (Ustrchr(keystring, '/') != 0) filename = string_sprintf("%s/%s", dirname, keystring); if (Ulstat(filename, &statbuf) >= 0) { - *result = string_copy(keystring); + /* Since the filename exists in the filesystem, we can return a + non-tainted result. */ + *result = string_copy_taint(keystring, FALSE); return OK; } commit 26d90d189dbad5890090340a5d4f478f3b63af0a Author: Jeremy Harris Date: Thu Mar 26 16:05:19 2020 +0000 Fix argument checking for ${readsocket } (cherry picked from commit c06ddd664f4a0f6e485f6f9f6b5086aa4719a441) diff --git a/src/src/expand.c b/src/src/expand.c index f489c5b8b..df3669ca0 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5070,8 +5070,9 @@ while (*s != 0) uschar * item; int sep = 0; - item = string_nextinlist(&list, &sep, NULL, 0); - if ((timeout = readconf_readtime(item, 0, FALSE)) < 0) + if ( !(item = string_nextinlist(&list, &sep, NULL, 0)) + || !*item + || (timeout = readconf_readtime(item, 0, FALSE)) < 0) { expand_string_message = string_sprintf("bad time value %s", item); goto EXPAND_FAILED; commit 6c43594701b8fa2056702b343ad5bfe9dd618ef0 Author: Jeremy Harris Date: Fri Mar 27 21:07:50 2020 +0000 OpenSSL: avoid loading server's CA-list-for-client-notification on client (cherry picked from commit d06f582fd0714042fcc110c9e0f7665657bb9da5) diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 063c23df7..b3c079303 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2480,7 +2480,7 @@ This is inconsistent with the need to verify the OCSP proof of the server cert. #endif } - /* If a certificate file is empty, the next function fails with an + /* If a certificate file is empty, the load function fails with an unhelpful error message. If we skip it, we get the correct behaviour (no certificates are recognized, but the error message is still misleading (it says no certificate was supplied). But this is better. */ @@ -2489,9 +2489,9 @@ This is inconsistent with the need to verify the OCSP proof of the server cert. && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir)) return tls_error(US"SSL_CTX_load_verify_locations", host, NULL, errstr); - /* Load the list of CAs for which we will accept certs, for sending - to the client. This is only for the one-file tls_verify_certificates - variant. + /* On the server load the list of CAs for which we will accept certs, for + sending to the client. This is only for the one-file + tls_verify_certificates variant. If a list isn't loaded into the server, but some verify locations are set, the server end appears to make a wildcard request for client certs. Meanwhile, the client library as default behaviour *ignores* the list @@ -2503,7 +2503,7 @@ This is inconsistent with the need to verify the OCSP proof of the server cert. { STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file); - SSL_CTX_set_client_CA_list(sctx, names); + if (!host) SSL_CTX_set_client_CA_list(sctx, names); DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", sk_X509_NAME_num(names)); } commit 10f790627709d66cb9b08755306de7096c015ef1 Author: Jeremy Harris Date: Sun Apr 12 17:47:27 2020 +0100 Taint: fix parsing of ACL ratelimit condition (cherry picked from commit 0d7a24c68f30ce73df5f0859b2de6a39e74b92bc) diff --git a/src/src/acl.c b/src/src/acl.c index 799af39c3..537514d21 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2259,7 +2259,7 @@ count = 1.0; /* Parse the other options. */ -while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))) +while ((ss = string_nextinlist(&arg, &sep, NULL, 0))) { if (strcmpic(ss, US"leaky") == 0) leaky = TRUE; else if (strcmpic(ss, US"strict") == 0) strict = TRUE; commit 62eab38cc8b135cb2d356f3cae23e005c6b45bcd Author: Jeremy Harris Date: Mon Apr 20 23:32:48 2020 +0100 Fix spool space check to account for SIZE. Bug 2552 (cherry picked from commit dd6711f05c57e1e284306ccc9bb6cd6c926cea01) diff --git a/src/src/receive.c b/src/src/receive.c index 350c4eda8..bafd91b0b 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -270,8 +270,8 @@ if (check_spool_space > 0 || msg_size > 0 || check_spool_inodes > 0) "check_space = " PR_EXIM_ARITH "K inodes = %d msg_size = %d\n", space, inodes, check_spool_space, check_spool_inodes, msg_size); - if ((space >= 0 && space < check_spool_space) || - (inodes >= 0 && inodes < check_spool_inodes)) + if ( space >= 0 && space + msg_size / 1024 < check_spool_space + || inodes >= 0 && inodes < check_spool_inodes) { log_write(0, LOG_MAIN, "spool directory space check failed: space=" PR_EXIM_ARITH " inodes=%d", space, inodes); commit 15d9a1a2ccebc2fe9fdff75309481437df624379 Author: Jeremy Harris Date: Sat Apr 25 21:03:51 2020 +0100 DKIM: fix $dkim_key_length in verify (cherry picked from commit a841a6eca79ff08b36f2225dcf89c1c162bb8777) diff --git a/src/src/arc.c b/src/src/arc.c index 94365498f..56dfa01a0 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -735,7 +735,7 @@ arc_get_verify_hhash(ctx, ams, &hhash); /* Setup the interface to the signing library */ -if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx))) +if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL))) { DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr); as->ams_verify_done = arc_state_reason = US"internal sigverify init error"; @@ -964,7 +964,7 @@ if (!(p = arc_line_to_pubkey(hdr_as))) /* We know the b-tag blob is of a nul-term string, so safe as a string */ pdkim_decode_base64(hdr_as->b.data, &sighash); -if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx))) +if ((errstr = exim_dkim_verify_init(&p->key, KEYFMT_DER, &vctx, NULL))) { DEBUG(D_acl) debug_printf("ARC verify init: %s\n", errstr); return US"fail"; diff --git a/src/src/dkim.c b/src/src/dkim.c index 065170444..89516bf10 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -401,7 +401,7 @@ for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) dkim_cur_sig = sig; dkim_signing_domain = US sig->domain; dkim_signing_selector = US sig->selector; - dkim_key_length = sig->sighash.len * 8; + dkim_key_length = sig->keybits; /* These two return static strings, so we can compare the addr later to see if the ACL overwrote them. Check that when logging */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 7fcfbc76a..0d2674f50 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1430,7 +1430,7 @@ if (sig->keytype == KEYTYPE_ED25519) if ((*errstr = exim_dkim_verify_init(&p->key, sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER, - vctx))) + vctx, &sig->keybits))) { DEBUG(D_acl) debug_printf("verify_init: %s\n", *errstr); sig->verify_status = PDKIM_VERIFY_INVALID; diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h index 0c9d46d01..ee5b9a2c4 100644 --- a/src/src/pdkim/pdkim.h +++ b/src/src/pdkim/pdkim.h @@ -140,8 +140,9 @@ typedef struct pdkim_signature { int version; /* (a=) The signature algorithm. */ - int keytype; /* pdkim_keytypes index */ - int hashtype; /* pdkim_hashes index */ + int keytype; /* pdkim_keytypes index */ + unsigned keybits; /* size of the key */ + int hashtype; /* pdkim_hashes index */ /* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE or PDKIM_CANON_RELAXED */ diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index 102e7bf51..b55bd9f5f 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -155,7 +155,8 @@ return NULL; Return: NULL for success, or an error string */ const uschar * -exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx) +exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx, + unsigned * bits) { gnutls_datum_t k; int rc; @@ -182,6 +183,7 @@ switch(fmt) ret = US"pubkey format not handled"; break; } +if (!ret && bits) gnutls_pubkey_get_pk_algorithm(verify_ctx->key, bits); return ret; } @@ -552,7 +554,8 @@ return NULL; Return: NULL for success, or an error string */ const uschar * -exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx) +exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx, + unsigned * bits) { /* in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi() @@ -560,6 +563,7 @@ in code sequence per b81207d2bfa92 rsa_parse_public_key() and asn1_get_mpi() uschar tag_class; int taglen; long alen; +unsigned nbits; int rc; uschar * errstr; gcry_error_t gerr; @@ -608,10 +612,10 @@ if ((rc = as_tag(pubkey, ASN1_CLASS_STRUCTURED, ASN1_TAG_SEQUENCE, NULL)) /* read two integers */ DEBUG(D_acl) stage = US"MPI"; -if ( (errstr = as_mpi(pubkey, &verify_ctx->n)) - || (errstr = as_mpi(pubkey, &verify_ctx->e)) - ) - return errstr; +nbits = pubkey->len; +if ((errstr = as_mpi(pubkey, &verify_ctx->n))) return errstr; +nbits = (nbits - pubkey->len) * 8; +if ((errstr = as_mpi(pubkey, &verify_ctx->e))) return errstr; #ifdef extreme_debug DEBUG(D_acl) debug_printf_indent("rsa_verify_init:\n"); @@ -624,6 +628,7 @@ DEBUG(D_acl) debug_printf_indent("rsa_verify_init:\n"); } #endif +if (bits) *bits = nbits; return NULL; asn_err: @@ -794,7 +799,8 @@ return US ERR_error_string(ERR_get_error(), NULL); Return: NULL for success, or an error string */ const uschar * -exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx) +exim_dkim_verify_init(blob * pubkey, keyformat fmt, ev_ctx * verify_ctx, + unsigned * bits) { const uschar * s = pubkey->data; uschar * ret = NULL; @@ -818,6 +824,7 @@ switch(fmt) break; } +if (!ret && bits) *bits = EVP_PKEY_bits(verify_ctx->key); return ret; } diff --git a/src/src/pdkim/signing.h b/src/src/pdkim/signing.h index 96a0720fd..6ecde7537 100644 --- a/src/src/pdkim/signing.h +++ b/src/src/pdkim/signing.h @@ -90,7 +90,7 @@ extern gstring * exim_dkim_data_append(gstring *, uschar *); extern const uschar * exim_dkim_signing_init(const uschar *, es_ctx *); extern const uschar * exim_dkim_sign(es_ctx *, hashmethod, blob *, blob *); -extern const uschar * exim_dkim_verify_init(blob *, keyformat, ev_ctx *); +extern const uschar * exim_dkim_verify_init(blob *, keyformat, ev_ctx *, unsigned *); extern const uschar * exim_dkim_verify(ev_ctx *, hashmethod, blob *, blob *); #endif /*DISABLE_DKIM*/ commit 32058c87829578ca77fc53120295c0407f6f9f7a Author: Luca Ceresoli Date: Sun May 3 16:54:28 2020 +0100 Build: fix parallelism problem. Bug 2566 (cherry picked from commit 302e7912253bd1f1a88f04d9a8a71d8a3c9b93fc) diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index f4977614f..6e3efbed1 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -659,11 +659,14 @@ PHDRS = ../config.h \ .c.o:; @echo "$(CC) $*.c" $(FE)$(CC) -c $(CFLAGS) -I. $(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE) $*.c -# Update Exim's version information and build the version object. +# Update Exim's version information and build the version object. The dependency +# chain here avoids problems under parallel-make. -version.h version.sh:: +version.sh: @../scripts/reversion +version.h: version.sh + cnumber.h: version.h version.o: $(HDRS) cnumber.h version.h version.c commit 251dd46714239fb689c3e697c5aa0937f34e323a Author: Jeremy Harris Date: Sun May 3 23:01:59 2020 +0100 tidying diff --git a/src/src/globals.h b/src/src/globals.h index ac7bb8ef3..e8c6652bf 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -340,7 +340,7 @@ extern gid_t *admin_groups; /* List of admin groups */ extern BOOL allow_domain_literals; /* As it says */ extern BOOL allow_mx_to_ip; /* Allow MX records to -> ip address */ #ifdef EXPERIMENTAL_ARC -struct arc_set *arc_received; /* highest ARC instance evaluation struct */ +extern struct arc_set *arc_received; /* highest ARC instance evaluation struct */ extern int arc_received_instance; /* highest ARC instance number in headers */ extern int arc_oldest_pass; /* lowest passing instance number in headers */ extern const uschar *arc_state; /* verification state */ commit 2cce1153f8e41a78b39f612e796422a8ed5bea19 Author: Jeremy Harris Date: Tue May 5 18:47:29 2020 +0100 Ensure lookup-result variables are dropped between messages on a connection (cherry picked from commit 0f9c36e64b3ded09b1972515fc62a673e1be2b8d) diff --git a/src/src/exim.c b/src/src/exim.c index 96c020c8e..ded451db4 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5021,6 +5021,8 @@ if (host_checking) deliver_localpart_orig = NULL; deliver_domain_orig = NULL; callout_address = sending_ip_address = NULL; + deliver_localpart_data = deliver_domain_data = + recipient_data = sender_data = NULL; sender_rate = sender_rate_limit = sender_rate_period = NULL; } smtp_log_no_mail(); @@ -5681,6 +5683,8 @@ moreloop: #endif callout_address = NULL; sending_ip_address = NULL; + deliver_localpart_data = deliver_domain_data = + recipient_data = sender_data = NULL; acl_var_m = NULL; for(int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index d0fb6b81c..b1c8e413c 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2050,7 +2050,8 @@ 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 */ deliver_localpart_parent = deliver_localpart_orig = NULL; deliver_domain_parent = deliver_domain_orig = NULL; callout_address = NULL; commit 6a7edbf6608d10ef0c707c426511e667849518d7 Author: Jeremy Harris Date: Tue May 5 21:15:34 2020 +0100 Fix SPA authenticator, checking client-supplied data before using it. Bug 2571 (cherry picked from commit 57aa14b216432be381b6295c312065b2fd034f86) diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index 97e3b102c..ed9aff23b 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -139,7 +139,7 @@ SPAAuthChallenge challenge; SPAAuthResponse response; SPAAuthResponse *responseptr = &response; uschar msgbuf[2048]; -uschar *clearpass; +uschar *clearpass, *s; /* send a 334, MS Exchange style, and grab the client's request, unless we already have it via an initial response. */ @@ -197,6 +197,13 @@ that causes failure if the size of msgbuf is exceeded. ****/ char *p = ((char*)responseptr) + IVAL(&responseptr->uUser.offset,0); int len = SVAL(&responseptr->uUser.len,0)/2; + if (p + len*2 >= CS (responseptr+1)) + { + DEBUG(D_auth) + debug_printf("auth_spa_server(): bad uUser spec in response\n"); + return FAIL; + } + if (len + 1 >= sizeof(msgbuf)) return FAIL; for (i = 0; i < len; ++i) { @@ -245,14 +252,17 @@ spa_smb_nt_encrypt (clearpass, challenge.challengeData, ntRespData); /* compare NT hash (LM may not be available) */ -if (memcmp(ntRespData, - ((unsigned char*)responseptr)+IVAL(&responseptr->ntResponse.offset,0), - 24) == 0) - /* success. we have a winner. */ +s = (US responseptr) + IVAL(&responseptr->ntResponse.offset,0); +if (s + 24 >= US (responseptr+1)) { - return auth_check_serv_cond(ablock); + DEBUG(D_auth) + debug_printf("auth_spa_server(): bad ntRespData spec in response\n"); + return FAIL; } +if (memcmp(ntRespData, s, 24) == 0) + return auth_check_serv_cond(ablock); /* success. we have a winner. */ + /* Expand server_condition as an authorization check (PH) */ return FAIL; commit 5a41d2c2cd2b28a0d1aea21edeaea02bd6db4984 Author: Jeremy Harris Date: Wed May 6 22:31:25 2020 +0100 Rework SPA fix to avoid overflows. Bug 2571 Amends: 6a7edbf660 (cherry picked from commit a04174dc2a84ae1008c23b6a7109e7fa3fb7b8b0) diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index ed9aff23b..4e3aef808 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -140,6 +140,7 @@ SPAAuthResponse response; SPAAuthResponse *responseptr = &response; uschar msgbuf[2048]; uschar *clearpass, *s; +unsigned off; /* send a 334, MS Exchange style, and grab the client's request, unless we already have it via an initial response. */ @@ -194,10 +195,13 @@ that causes failure if the size of msgbuf is exceeded. ****/ { int i; - char *p = ((char*)responseptr) + IVAL(&responseptr->uUser.offset,0); + char * p; int len = SVAL(&responseptr->uUser.len,0)/2; - if (p + len*2 >= CS (responseptr+1)) + if ( (off = IVAL(&responseptr->uUser.offset,0)) >= sizeof(SPAAuthResponse) + || len >= sizeof(responseptr->buffer)/2 + || (p = (CS responseptr) + off) + len*2 >= CS (responseptr+1) + ) { DEBUG(D_auth) debug_printf("auth_spa_server(): bad uUser spec in response\n"); @@ -252,13 +256,14 @@ spa_smb_nt_encrypt (clearpass, challenge.challengeData, ntRespData); /* compare NT hash (LM may not be available) */ -s = (US responseptr) + IVAL(&responseptr->ntResponse.offset,0); -if (s + 24 >= US (responseptr+1)) +off = IVAL(&responseptr->ntResponse.offset,0); +if (off >= sizeof(SPAAuthResponse) - 24) { DEBUG(D_auth) debug_printf("auth_spa_server(): bad ntRespData spec in response\n"); return FAIL; } +s = (US responseptr) + off; if (memcmp(ntRespData, s, 24) == 0) return auth_check_serv_cond(ablock); /* success. we have a winner. */ commit ee83de04d3087efaf808d1f2235a988275c2ee94 Author: Jeremy Harris Date: Sat May 9 15:22:11 2020 +0100 Fix build with Radius auth expansion condition support. Bug 2572 Cherry-picked from: 8ca559c846 diff --git a/src/src/auths/call_radius.c b/src/src/auths/call_radius.c index c3637436d..ecd098047 100644 --- a/src/src/auths/call_radius.c +++ b/src/src/auths/call_radius.c @@ -2,6 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -115,34 +116,34 @@ if (rc_read_config(RADIUS_CONFIG_FILE) != 0) *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE); else if (rc_read_dictionary(rc_conf_str("dictionary")) != 0) - *errptr = string_sprintf("RADIUS: can't read dictionary"); + *errptr = US"RADIUS: can't read dictionary"; -else if (rc_avpair_add(&send, PW_USER_NAME, user, 0) == NULL) - *errptr = string_sprintf("RADIUS: add user name failed\n"); +else if (!rc_avpair_add(&send, PW_USER_NAME, user, 0)) + *errptr = US"RADIUS: add user name failed"; -else if (rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0) == NULL) - *errptr = string_sprintf("RADIUS: add password failed\n"); +else if (!rc_avpair_add(&send, PW_USER_PASSWORD, CS radius_args, 0)) + *errptr = US"RADIUS: add password failed"); -else if (rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0) == NULL) - *errptr = string_sprintf("RADIUS: add service type failed\n"); +else if (!rc_avpair_add(&send, PW_SERVICE_TYPE, &service, 0)) + *errptr = US"RADIUS: add service type failed"; #else /* RADIUS_LIB_RADIUSCLIENT unset => RADIUS_LIB_RADIUSCLIENT2 */ -if ((h = rc_read_config(RADIUS_CONFIG_FILE)) == NULL) +if (!(h = rc_read_config(RADIUS_CONFIG_FILE))) *errptr = string_sprintf("RADIUS: can't open %s", RADIUS_CONFIG_FILE); else if (rc_read_dictionary(h, rc_conf_str(h, "dictionary")) != 0) - *errptr = string_sprintf("RADIUS: can't read dictionary"); + *errptr = US"RADIUS: can't read dictionary"; -else if (rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0) == NULL) - *errptr = string_sprintf("RADIUS: add user name failed\n"); +else if (!rc_avpair_add(h, &send, PW_USER_NAME, user, Ustrlen(user), 0)) + *errptr = US"RADIUS: add user name failed"; -else if (rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args, - Ustrlen(radius_args), 0) == NULL) - *errptr = string_sprintf("RADIUS: add password failed\n"); +else if (!rc_avpair_add(h, &send, PW_USER_PASSWORD, CS radius_args, + Ustrlen(radius_args), 0)) + *errptr = US"RADIUS: add password failed"; -else if (rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0) == NULL) - *errptr = string_sprintf("RADIUS: add service type failed\n"); +else if (!rc_avpair_add(h, &send, PW_SERVICE_TYPE, &service, 0, 0)) + *errptr = US"RADIUS: add service type failed"; #endif /* RADIUS_LIB_RADIUSCLIENT */ commit df2513020e6ac79c488a8f9e1b508d7c59d57080 Author: Jeremy Harris Date: Wed May 13 00:58:32 2020 +0100 Fix over-long line in DSN (cherry picked from commit a050bbe0d120464d80dab3eb8af420c4ec686ca8) diff --git a/src/src/deliver.c b/src/src/deliver.c index 94bc9a89c..620df86af 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5379,7 +5379,8 @@ Returns: nothing static void print_dsn_diagnostic_code(const address_item *addr, FILE *f) { -uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL; +uschar * s = testflag(addr, af_pass_message) ? addr->message : NULL; +unsigned cnt; /* af_pass_message and addr->message set ? print remote host answer */ if (s) @@ -5391,19 +5392,32 @@ if (s) if (!(s = Ustrstr(addr->message, ": "))) return; /* not found, bail out */ s += 2; /* skip ": " */ - fprintf(f, "Diagnostic-Code: smtp; "); + cnt = fprintf(f, "Diagnostic-Code: smtp; "); } /* no message available. do nothing */ else return; while (*s) + { + if (cnt > 950) /* RFC line length limit: 998 */ + { + DEBUG(D_deliver) debug_printf("print_dsn_diagnostic_code() truncated line\n"); + fputs("[truncated]", f); + break; + } + if (*s == '\\' && s[1] == 'n') { fputs("\n ", f); /* as defined in RFC 3461 */ s += 2; + cnt += 2; } else + { fputc(*s++, f); + cnt++; + } + } fputc('\n', f); } @@ -7809,11 +7823,11 @@ wording. */ fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); } if ((s = addr->smtp_greeting) && *s) - fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s); + fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %.900s\n", s); if ((s = addr->helo_response) && *s) - fprintf(fp, "X-Remote-MTA-helo-response: X-str; %s\n", s); + fprintf(fp, "X-Remote-MTA-helo-response: X-str; %.900s\n", s); if ((s = addr->message) && *s) - fprintf(fp, "X-Exim-Diagnostic: X-str; %s\n", s); + fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s); } #endif print_dsn_diagnostic_code(addr, fp);