From: Jeremy Harris Date: Sun, 22 Mar 2020 20:15:33 +0000 (+0000) Subject: Merge branch 'debug_fork' X-Git-Url: https://git.exim.org/users/heiko/exim.git/commitdiff_plain/46d2a5e6f6e7709d172903b13945d23fc0a2c888?hp=8102279385f5f70c959aa219feca37031c0a1828 Merge branch 'debug_fork' --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index b9f012aef..2a3fb6c51 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -6770,7 +6770,12 @@ by default, but has an option to omit them (see section &<>&). whose name is the key by calling the &[lstat()]& function. The key may not contain any forward slash characters. If &[lstat()]& succeeds, the result of the lookup is the name of the entry, which may be a file, directory, -symbolic link, or any other kind of directory entry. An example of how this +symbolic link, or any other kind of directory entry. +.new +.cindex "tainted data" "dsearch result" +It is regarded as untainted. +.wen +An example of how this lookup can be used to support virtual domains is given in section &<>&. .next @@ -36779,12 +36784,18 @@ to a router of this form: virtual: driver = redirect domains = dsearch;/etc/mail/virtual - data = ${lookup{$local_part}lsearch{/etc/mail/virtual/$domain}} + data = ${lookup{$local_part}lsearch{/etc/mail/virtual/$domain_data}} no_more .endd +.new The &%domains%& option specifies that the router is to be skipped, unless there is a file in the &_/etc/mail/virtual_& directory whose name is the same as the -domain that is being processed. When the router runs, it looks up the local +domain that is being processed. +The &(dsearch)& lookup used results in an untainted version of &$domain$& +being placed into the &$domain_data$& variable. +.wen + +When the router runs, it looks up the local part in the file to find a new address (or list of addresses). The &%no_more%& setting ensures that if the lookup fails (leading to &%data%& being an empty string), Exim gives up on the address without trying any subsequent routers. diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 001f919d7..be07ba625 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -147,6 +147,15 @@ JH/30 When an pipelined-connect fails at the first response, assume incorrect cached capability (perhaps the peer reneged?) and immediately retry in non-pipelined mode. +JH/31 Fix spurious detection of timeout while writing to transport filter. + +JH/32 Bug 2541: Fix segfault on bad cmdline -f (sender) argument. Previously + an attempt to copy the string was made before checking it. + +JH/33 Fix the dsearch lookup to return an untainted result. Previously the + taint of the lookup key was maintained; we now regard the presence in the + filesystem as sufficient validation. + Exim version 4.93 ----------------- diff --git a/src/src/exim.c b/src/src/exim.c index 7b1c8ead9..10526ba5f 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -831,7 +831,7 @@ int start, end, domain; uschar *parse_error = NULL; uschar *address = parse_extract_address(s, &parse_error, &start, &end, &domain, FALSE); -if (address == NULL) +if (!address) { fprintf(stdout, "syntax error: %s\n", parse_error); *exit_value = 2; @@ -1955,8 +1955,8 @@ on the second character (the one after '-'), to save some effort. */ for (i = 1; i < argc; i++) { BOOL badarg = FALSE; - uschar *arg = argv[i]; - uschar *argrest; + uschar * arg = argv[i]; + uschar * argrest; int switchchar; /* An argument not starting with '-' is the start of a recipients list; @@ -2035,7 +2035,7 @@ for (i = 1; i < argc; i++) /* sendmail uses -Ac and -Am to control which .cf file is used; we ignore them. */ case 'A': - if (*argrest == '\0') { badarg = TRUE; break; } + if (!*argrest) { badarg = TRUE; break; } else { BOOL ignore = FALSE; @@ -2091,9 +2091,9 @@ for (i = 1; i < argc; i++) /* -bF: Run system filter test */ case 'F': filter_test |= checking = FTEST_SYSTEM; - if (*argrest) { badarg = TRUE; break; } - if (++i < argc) filter_test_sfile = argv[i]; else - exim_fail("exim: file name expected after %s\n", argv[i-1]); + if (*argrest) badarg = TRUE; + else if (++i < argc) filter_test_sfile = argv[i]; + else exim_fail("exim: file name expected after %s\n", argv[i-1]); break; /* -bf: Run user filter test @@ -2126,7 +2126,7 @@ for (i = 1; i < argc; i++) if (!*argrest || Ustrcmp(argrest, "c") == 0) { if (++i >= argc) { badarg = TRUE; break; } - sender_host_address = argv[i]; + sender_host_address = string_copy_taint(argv[i], TRUE); host_checking = checking = f.log_testing_mode = TRUE; f.host_checking_callout = *argrest == 'c'; message_logs = FALSE; @@ -2345,11 +2345,8 @@ for (i = 1; i < argc; i++) a change! Enforce a prefix check if required. */ case 'C': - if (*argrest == 0) - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } if (Ustrcmp(config_main_filelist, argrest) != 0) { #ifdef ALT_CONFIG_PREFIX @@ -2358,14 +2355,14 @@ for (i = 1; i < argc; i++) const uschar *list = argrest; uschar *filename; while((filename = string_nextinlist(&list, &sep, big_buffer, - big_buffer_size)) != NULL) - { - if ((Ustrlen(filename) < len || - Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 || - Ustrstr(filename, "/../") != NULL) && - (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid)) + big_buffer_size))) + if ( ( Ustrlen(filename) < len + || Ustrncmp(filename, ALT_CONFIG_PREFIX, len) != 0 + || Ustrstr(filename, "/../") != NULL + ) + && (Ustrcmp(filename, "/dev/null") != 0 || real_uid != root_uid) + ) exim_fail("-C Permission denied\n"); - } #endif if (real_uid != root_uid) { @@ -2420,7 +2417,7 @@ for (i = 1; i < argc; i++) if (nl) *nl = 0; trusted_configs[nr_configs++] = string_copy(start); - if (nr_configs == 32) + if (nr_configs == nelem(trusted_configs)) break; } fclose(trust_list); @@ -2431,7 +2428,7 @@ for (i = 1; i < argc; i++) const uschar *list = argrest; uschar *filename; while (f.trusted_config && (filename = string_nextinlist(&list, - &sep, big_buffer, big_buffer_size)) != NULL) + &sep, big_buffer, big_buffer_size))) { for (i=0; i < nr_configs; i++) if (Ustrcmp(filename, trusted_configs[i]) == 0) @@ -2533,7 +2530,7 @@ for (i = 1; i < argc; i++) f.debug_daemon = TRUE; argrest++; } - if (*argrest != 0) + if (*argrest) decode_bits(&selector, 1, debug_notall, argrest, debug_options, debug_options_count, US"debug", 0); debug_selector = selector; @@ -2580,12 +2577,9 @@ for (i = 1; i < argc; i++) the -F or be in the next argument. */ case 'F': - if (*argrest == 0) - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } - originator_name = argrest; + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } + originator_name = string_copy_taint(argrest, TRUE); f.sender_name_forced = TRUE; break; @@ -2609,16 +2603,13 @@ for (i = 1; i < argc; i++) { int dummy_start, dummy_end; uschar *errmess; - if (*argrest == 0) - { - if (i+1 < argc) argrest = argv[++i]; else - { badarg = TRUE; break; } - } - if (*argrest == 0) + if (!*argrest) + if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } + if (!*argrest) *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */ else { - uschar *temp = argrest + Ustrlen(argrest) - 1; + uschar * temp = argrest + Ustrlen(argrest) - 1; while (temp >= argrest && isspace(*temp)) temp--; if (temp >= argrest && *temp == '.') f_end_dot = TRUE; allow_domain_literals = TRUE; @@ -2626,8 +2617,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); @@ -2635,8 +2628,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; } @@ -2656,11 +2647,8 @@ for (i = 1; i < argc; i++) To put it in will require a change to the spool header file format. */ case 'h': - if (*argrest == 0) - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } if (!isdigit(*argrest)) badarg = TRUE; break; @@ -2669,7 +2657,7 @@ for (i = 1; i < argc; i++) not to be documented for sendmail but mailx (at least) uses it) */ case 'i': - if (*argrest == 0) f.dot_ends = FALSE; else badarg = TRUE; + if (!*argrest) f.dot_ends = FALSE; else badarg = TRUE; break; @@ -2677,16 +2665,13 @@ for (i = 1; i < argc; i++) syslog_processname in the config file, but needs to be an admin option. */ case 'L': - if (*argrest == '\0') - { - if(++i < argc) argrest = argv[i]; else - { badarg = TRUE; break; } - } + if (!*argrest) + if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } if ((sz = Ustrlen(argrest)) > 32) exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest); if (sz < 1) exim_fail("exim: the -L syslog name is too short\n"); - cmdline_syslog_name = argrest; + cmdline_syslog_name = string_copy_taint(argrest, TRUE); break; case 'M': @@ -2716,9 +2701,9 @@ for (i = 1; i < argc; i++) if (msg_action_arg >= 0) exim_fail("exim: incompatible arguments\n"); - continue_transport = argv[++i]; - continue_hostname = argv[++i]; - continue_host_address = argv[++i]; + continue_transport = string_copy_taint(argv[++i], TRUE); + continue_hostname = string_copy_taint(argv[++i], TRUE); + continue_host_address = string_copy_taint(argv[++i], TRUE); continue_sequence = Uatoi(argv[++i]); msg_action = MSG_DELIVER; msg_action_arg = ++i; @@ -2769,7 +2754,7 @@ for (i = 1; i < argc; i++) /* -MCG: set the queue name, to a non-default value */ - case 'G': if (++i < argc) queue_name = string_copy(argv[i]); + case 'G': if (++i < argc) queue_name = string_copy_taint(argv[i], TRUE); else badarg = TRUE; break; @@ -2803,11 +2788,14 @@ for (i = 1; i < argc; i++) Require three arguments for the proxied local address and port, and the TLS cipher. */ - case 't': if (++i < argc) sending_ip_address = argv[i]; + case 't': if (++i < argc) + sending_ip_address = string_copy_taint(argv[i], TRUE); else badarg = TRUE; - if (++i < argc) sending_port = (int)(Uatol(argv[i])); + if (++i < argc) + sending_port = (int)(Uatol(argv[i])); else badarg = TRUE; - if (++i < argc) continue_proxy_cipher = argv[i]; + if (++i < argc) + continue_proxy_cipher = string_copy_taint(argv[i], TRUE); else badarg = TRUE; /*FALLTHROUGH*/ @@ -2844,7 +2832,7 @@ for (i = 1; i < argc; i++) -Mvl show log */ - else if (*argrest == 0) + else if (!*argrest) { msg_action = MSG_DELIVER; forced_delivery = f.deliver_force_thaw = TRUE; @@ -2869,7 +2857,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "G") == 0) { msg_action = MSG_SETQUEUE; - queue_name_dest = argv[++i]; + queue_name_dest = string_copy_taint(argv[++i], TRUE); } else if (Ustrcmp(argrest, "mad") == 0) { @@ -2942,7 +2930,7 @@ for (i = 1; i < argc; i++) for sendmail it askes for "me too". Exim always does this. */ case 'm': - if (*argrest != 0) badarg = TRUE; + if (*argrest) badarg = TRUE; break; @@ -2950,7 +2938,7 @@ for (i = 1; i < argc; i++) their thing. It implies debugging at the D_v level. */ case 'N': - if (*argrest == 0) + if (!*argrest) { f.dont_deliver = TRUE; debug_selector |= D_v; @@ -2973,7 +2961,7 @@ for (i = 1; i < argc; i++) -O option=value and -Ooption=value. */ case 'O': - if (*argrest == 0) + if (!*argrest) if (++i >= argc) exim_fail("exim: string expected after -O\n"); break; @@ -3081,12 +3069,13 @@ for (i = 1; i < argc; i++) /* -oMa: Set sender host address */ - if (Ustrcmp(argrest, "a") == 0) sender_host_address = argv[++i]; + if (Ustrcmp(argrest, "a") == 0) + sender_host_address = string_copy_taint(argv[++i], TRUE); /* -oMaa: Set authenticator name */ else if (Ustrcmp(argrest, "aa") == 0) - sender_host_authenticated = argv[++i]; + sender_host_authenticated = string_copy_taint(argv[++i], TRUE); /* -oMas: setting authenticated sender */ @@ -3100,7 +3089,8 @@ for (i = 1; i < argc; i++) /* -oMi: Set incoming interface address */ - else if (Ustrcmp(argrest, "i") == 0) interface_address = argv[++i]; + else if (Ustrcmp(argrest, "i") == 0) + interface_address = string_copy_taint(argv[++i], TRUE); /* -oMm: Message reference */ @@ -3120,7 +3110,7 @@ for (i = 1; i < argc; i++) if (received_protocol) exim_fail("received_protocol is set already\n"); else - received_protocol = argv[++i]; + received_protocol = string_copy_taint(argv[++i], TRUE); /* -oMs: Set sender host name */ @@ -3132,7 +3122,7 @@ for (i = 1; i < argc; i++) else if (Ustrcmp(argrest, "t") == 0) { sender_ident_set = TRUE; - sender_ident = argv[++i]; + sender_ident = string_copy_taint(argv[++i], TRUE); } /* Else a bad argument */ @@ -3185,7 +3175,7 @@ for (i = 1; i < argc; i++) case 'X': if (*argrest) badarg = TRUE; - else override_local_interfaces = argv[++i]; + else override_local_interfaces = string_copy_taint(argv[++i], TRUE); break; /* Unknown -o argument */ @@ -3215,13 +3205,10 @@ for (i = 1; i < argc; i++) /* -panythingelse is taken as the Sendmail-compatible argument -prval:sval, which sets the host protocol and host name */ - if (*argrest == 0) - if (i+1 < argc) - argrest = argv[++i]; - else - { badarg = TRUE; break; } + if (!*argrest) + if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } - if (*argrest != 0) + if (*argrest) { uschar *hn; @@ -3229,15 +3216,15 @@ for (i = 1; i < argc; i++) exim_fail("received_protocol is set already\n"); hn = Ustrchr(argrest, ':'); - if (hn == NULL) - received_protocol = argrest; + if (!hn) + received_protocol = string_copy_taint(argrest, TRUE); else { int old_pool = store_pool; store_pool = POOL_PERM; - received_protocol = string_copyn(argrest, hn - argrest); + received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE); store_pool = old_pool; - sender_host_name = hn + 1; + sender_host_name = string_copy_taint(hn + 1, TRUE); } } break; @@ -3300,14 +3287,14 @@ for (i = 1; i < argc; i++) only, optionally named, optionally starting from a given message id. */ if (!(list_queue || count_queue)) - if (*argrest == 0 + if ( !*argrest && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1]))) { queue_interval = 0; if (i+1 < argc && mac_ismsgid(argv[i+1])) - start_queue_run_id = argv[++i]; + start_queue_run_id = string_copy_taint(argv[++i], TRUE); if (i+1 < argc && mac_ismsgid(argv[i+1])) - stop_queue_run_id = argv[++i]; + stop_queue_run_id = string_copy_taint(argv[++i], TRUE); } /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally @@ -3331,7 +3318,7 @@ for (i = 1; i < argc; i++) in all cases provided there are no further characters in this argument. */ - if (*argrest != 0) + if (*argrest) for (int i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { @@ -3345,9 +3332,9 @@ for (i = 1; i < argc; i++) pick out particular messages. */ if (*argrest) - deliver_selectstring = argrest; + deliver_selectstring = string_copy_taint(argrest, TRUE); else if (i+1 < argc) - deliver_selectstring = argv[++i]; + deliver_selectstring = string_copy_taint(argv[++i], TRUE); else exim_fail("exim: string expected after -R\n"); break; @@ -3384,9 +3371,9 @@ for (i = 1; i < argc; i++) pick out particular messages. */ if (*argrest) - deliver_selectstring_sender = argrest; + deliver_selectstring_sender = string_copy_taint(argrest, TRUE); else if (i+1 < argc) - deliver_selectstring_sender = argv[++i]; + deliver_selectstring_sender = string_copy_taint(argv[++i], TRUE); else exim_fail("exim: string expected after -S\n"); break; @@ -3398,7 +3385,7 @@ for (i = 1; i < argc; i++) case 'T': if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) - fudged_queue_times = argv[++i]; + fudged_queue_times = string_copy_taint(argv[++i], TRUE); else badarg = TRUE; break; @@ -3406,7 +3393,7 @@ for (i = 1; i < argc; i++) /* -t: Set flag to extract recipients from body of message. */ case 't': - if (*argrest == 0) extract_recipients = TRUE; + if (!*argrest) extract_recipients = TRUE; /* -ti: Set flag to extract recipients from body of message, and also specify that dot does not end the message. */ @@ -3438,7 +3425,7 @@ for (i = 1; i < argc; i++) /* -v: verify things - this is a very low-level debugging */ case 'v': - if (*argrest == 0) + if (!*argrest) { debug_selector |= D_v; debug_file = stderr; @@ -3458,22 +3445,22 @@ for (i = 1; i < argc; i++) As Exim is 8-bit clean, it just ignores this flag. */ case 'x': - if (*argrest != 0) badarg = TRUE; + if (*argrest) badarg = TRUE; break; /* -X: in sendmail: takes one parameter, logfile, and sends debugging logs to that file. We swallow the parameter and otherwise ignore it. */ case 'X': - if (*argrest == '\0') + if (!*argrest) if (++i >= argc) exim_fail("exim: string expected after -X\n"); break; case 'z': - if (*argrest == '\0') + if (!*argrest) if (++i < argc) - log_oneline = argv[i]; + log_oneline = string_copy_taint(argv[i], TRUE); else exim_fail("exim: file name expected after %s\n", argv[i-1]); break; @@ -4083,11 +4070,12 @@ if ( (debug_selector & D_any || LOGGING(arguments)) p = big_buffer + 3; } printing = string_printing(argv[i]); - if (printing[0] == 0) quote = US"\""; else + if (!*printing) quote = US"\""; + else { const uschar *pp = printing; quote = US""; - while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; } + while (*pp) if (isspace(*pp++)) { quote = US"\""; break; } } p += sprintf(CS p, " %s%.*s%s", quote, (int)(big_buffer_size - (p - big_buffer) - 4), printing, quote); @@ -4123,19 +4111,19 @@ script. */ if (bi_option) { (void)fclose(config_file); - if (bi_command != NULL) + if (bi_command) { int i = 0; uschar *argv[3]; argv[i++] = bi_command; - if (alias_arg != NULL) argv[i++] = alias_arg; + if (alias_arg) argv[i++] = alias_arg; argv[i++] = NULL; setgroups(group_count, group_list); exim_setugid(real_uid, real_gid, FALSE, US"running bi_command"); DEBUG(D_exec) debug_printf("exec %.256s %.256s\n", argv[0], - (argv[1] == NULL)? US"" : argv[1]); + argv[1] ? argv[1] : US""); execv(CS argv[0], (char *const *)argv); exim_fail("exim: exec failed: %s\n", strerror(errno)); @@ -4614,6 +4602,8 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) { int status; pid_t pid; + /*XXX This use of argv[i] for msg_id should really be tainted, but doing + that runs into a later copy into the untainted global message_id[] */ if (i == argc - 1) (void)deliver_message(argv[i], forced_delivery, deliver_give_up); else if ((pid = exim_fork(US"cmdline-delivery")) == 0) @@ -5479,8 +5469,7 @@ while (more) errmess = US"unqualified recipient address not allowed"; } - if (recipient == NULL) - { + if (!recipient) if (error_handling == ERRORS_STDERR) { fprintf(stderr, "exim: bad recipient address \"%s\": %s\n", @@ -5497,7 +5486,6 @@ while (more) moan_to_sender(ERRMESS_BADARGADDRESS, &eblock, NULL, stdin, TRUE)? errors_sender_rc : EXIT_FAILURE; } - } receive_add_recipient(string_copy_taint(recipient, TRUE), -1); s = ss; @@ -5510,8 +5498,8 @@ while (more) DEBUG(D_receive) { - if (sender_address != NULL) debug_printf("Sender: %s\n", sender_address); - if (recipients_list != NULL) + if (sender_address) debug_printf("Sender: %s\n", sender_address); + if (recipients_list) { debug_printf("Recipients:\n"); for (int i = 0; i < recipients_count; i++) diff --git a/src/src/filter.c b/src/src/filter.c index 98b6bc3e8..90e83e6b0 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -1510,7 +1510,7 @@ switch (c->type) parse_extract_address(pp, &error, &start, &end, &domain, FALSE); *p = saveend; - if (filter_thisaddress != NULL) + if (filter_thisaddress) { if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) @@ -1747,11 +1747,11 @@ while (commands) uschar *error; uschar *ss = parse_extract_address(s, &error, &start, &end, &domain, FALSE); - if (ss != NULL) - expargs[i] = ((filter_options & RDO_REWRITE) != 0)? - rewrite_address(ss, TRUE, FALSE, global_rewrite_rules, - rewrite_existflags) : - rewrite_address_qualify(ss, TRUE); + if (ss) + expargs[i] = filter_options & RDO_REWRITE + ? rewrite_address(ss, TRUE, FALSE, global_rewrite_rules, + rewrite_existflags) + : rewrite_address_qualify(ss, TRUE); else { *error_pointer = string_sprintf("malformed address \"%s\" in " diff --git a/src/src/functions.h b/src/src/functions.h index b8bf2a490..035960646 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -153,9 +153,8 @@ extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...); extern pid_t child_open_exim_function(int *, const uschar *); extern pid_t child_open_exim2_function(int *, uschar *, uschar *, const uschar *); -extern pid_t child_open_function(uschar **argv, uschar **envp, int newumask, - int *infdptr, int *outfdptr, BOOL make_leader, - const uschar * purpose); +extern pid_t child_open_function(uschar **, uschar **, int, + int *, int *, BOOL, const uschar *); extern pid_t child_open_uid(const uschar **, const uschar **, int, uid_t *, gid_t *, int *, int *, uschar *, BOOL, const uschar *); extern BOOL cleanup_environment(void); @@ -712,28 +711,44 @@ return chown(CCS name, owner, group) *************************************************/ /* This function assumes that memcpy() is faster than strcpy(). +The result is explicitly nul-terminated. */ static inline uschar * -string_copy_taint_trc(const uschar *s, BOOL tainted, const char * func, int line) +string_copyn_taint_trc(const uschar * s, unsigned len, + BOOL tainted, const char * func, int line) { -int len = Ustrlen(s) + 1; -uschar *ss = store_get_3(len, tainted, func, line); +uschar * ss = store_get_3(len + 1, tainted, func, line); memcpy(ss, s, len); +ss[len] = '\0'; return ss; } -#define string_copy_taint(s, tainted) \ - string_copy_taint_trc((s), tainted, __FUNCTION__, __LINE__) +static inline uschar * +string_copy_taint_trc(const uschar * s, BOOL tainted, const char * func, int line) +{ return string_copyn_taint_trc(s, Ustrlen(s), tainted, func, line); } +static inline uschar * +string_copyn_trc(const uschar * s, unsigned len, const char * func, int line) +{ return string_copyn_taint_trc(s, len, is_tainted(s), func, line); } static inline uschar * string_copy_trc(const uschar * s, const char * func, int line) -{ -return string_copy_taint_trc((s), is_tainted(s), func, line); -} +{ return string_copy_taint_trc(s, is_tainted(s), func, line); } + + +/* String-copy functions explicitly setting the taint status */ +#define string_copyn_taint(s, len, tainted) \ + string_copyn_taint_trc((s), (len), (tainted), __FUNCTION__, __LINE__) +#define string_copy_taint(s, tainted) \ + string_copy_taint_trc((s), (tainted), __FUNCTION__, __LINE__) + +/* Simple string-copy functions maintaining the taint */ + +#define string_copyn(s, len) \ + string_copyn_taint_trc((s), (len), is_tainted(s), __FUNCTION__, __LINE__) #define string_copy(s) \ - string_copy_trc((s), __FUNCTION__, __LINE__) + string_copy_taint_trc((s), is_tainted(s), __FUNCTION__, __LINE__) /************************************************* @@ -757,31 +772,6 @@ return ss; -/************************************************* -* Copy and save string, given length * -*************************************************/ - -/* It is assumed the data contains no zeros. A zero is added -onto the end. - -Arguments: - s string to copy - n number of characters - -Returns: copy of string in new store - -This is an API for local_scan hence not static. -*/ - -static inline uschar * -string_copyn(const uschar *s, int n) -{ -uschar *ss = store_get(n + 1, is_tainted(s)); -Ustrncpy(ss, s, n); -ss[n] = 0; -return ss; -} - /************************************************* * Copy, lowercase, and save string, given length * *************************************************/ diff --git a/src/src/header.c b/src/src/header.c index a6c44fac8..cbfc4f847 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -412,14 +412,13 @@ for (header_line * h = header_list; !yield && h; h = h->next) /* If there is some kind of syntax error, just give up on this header line. */ - if (next == NULL) break; + if (!next) break; /* Otherwise, test for the pattern; a non-regex must be an exact match */ - yield = (re == NULL)? - (strcmpic(next, pattern) == 0) - : - (pcre_exec(re, NULL, CS next, Ustrlen(next), 0, PCRE_EOPT, NULL, 0) + yield = !re + ? (strcmpic(next, pattern) == 0) + : (pcre_exec(re, NULL, CS next, Ustrlen(next), 0, PCRE_EOPT, NULL, 0) >= 0); } } 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; } diff --git a/src/src/parse.c b/src/src/parse.c index 71f48f379..5d50d6862 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -1614,14 +1614,14 @@ for (;;) { recipient = parse_extract_address(s+1, error, &start, &end, &domain, FALSE); - if (recipient != NULL) - recipient = (domain != 0)? NULL : + if (recipient) + recipient = domain != 0 ? NULL : string_sprintf("%s@%s", recipient, incoming_domain); } /* Try parsing the item as an address. */ - if (recipient == NULL) recipient = + if (!recipient) recipient = parse_extract_address(s, error, &start, &end, &domain, FALSE); /* If item starts with / or | and is not a valid address, or there @@ -2127,7 +2127,9 @@ while (Ufgets(buffer, sizeof(buffer), stdin) != NULL) buffer[Ustrlen(buffer) - 1] = 0; if (buffer[0] == 0) break; out = parse_extract_address(buffer, &errmess, &start, &end, &domain, FALSE); - if (out == NULL) printf("*** bad address: %s\n", errmess); else + if (!out) + printf("*** bad address: %s\n", errmess); + else { uschar extract[1024]; Ustrncpy(extract, buffer+start, end-start); @@ -2146,7 +2148,9 @@ while (Ufgets(buffer, sizeof(buffer), stdin) != NULL) buffer[Ustrlen(buffer) - 1] = 0; if (buffer[0] == 0) break; out = parse_extract_address(buffer, &errmess, &start, &end, &domain, FALSE); - if (out == NULL) printf("*** bad address: %s\n", errmess); else + if (!out) + printf("*** bad address: %s\n", errmess); + else { uschar extract[1024]; Ustrncpy(extract, buffer+start, end-start); @@ -2167,7 +2171,7 @@ while (Ufgets(buffer, sizeof(buffer), stdin) != NULL) buffer[Ustrlen(buffer) - 1] = 0; if (buffer[0] == 0) break; s = buffer; - while (*s != 0) + while (*s) { uschar *ss = parse_find_address_end(s, FALSE); int terminator = *ss; @@ -2175,7 +2179,9 @@ while (Ufgets(buffer, sizeof(buffer), stdin) != NULL) out = parse_extract_address(buffer, &errmess, &start, &end, &domain, FALSE); *ss = terminator; - if (out == NULL) printf("*** bad address: %s\n", errmess); else + if (!out) + printf("*** bad address: %s\n", errmess); + else { uschar extract[1024]; Ustrncpy(extract, buffer+start, end-start); diff --git a/src/src/queue.c b/src/src/queue.c index 3235a41ba..bb75c99cd 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -1410,13 +1410,13 @@ switch(action) parse_extract_address(argv[recipients_arg], &errmess, &start, &end, &domain, (action == MSG_EDIT_SENDER)); - if (recipient == NULL) + if (!recipient) { yield = FALSE; printf("- error while %s:\n bad address %s: %s\n", doing, argv[recipients_arg], errmess); } - else if (recipient[0] != 0 && domain == 0) + else if (*recipient && domain == 0) { yield = FALSE; printf("- error while %s:\n bad address %s: " @@ -1546,7 +1546,6 @@ memcpy(buf+1, msgid, MESSAGE_ID_LENGTH+1); if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0) { struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; - int slen; #ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS int len = offsetof(struct sockaddr_un, sun_path) + 1 diff --git a/src/src/receive.c b/src/src/receive.c index 398250ebb..0afb72b8c 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2568,7 +2568,7 @@ if (extract_recip) If there are no recipients at all, an error will occur later. */ - if (recipient == NULL && Ustrcmp(errmess, "empty address") != 0) + if (!recipient && Ustrcmp(errmess, "empty address") != 0) { int len = Ustrlen(s); error_block *b = store_get(sizeof(error_block), FALSE); diff --git a/src/src/sieve.c b/src/src/sieve.c index e07b7da18..446766534 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -328,7 +328,7 @@ if (address->length>0) { ss = parse_extract_address(address->character, &error, &start, &end, &domain, FALSE); - if (ss == NULL) + if (!ss) { filter->errmsg=string_sprintf("malformed address \"%s\" (%s)", address->character, error); diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 2b487b435..90a5aa4be 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -202,7 +202,7 @@ while (*s != 0) /* If there is some kind of syntax error, just give up on this header line. */ - if (next == NULL) break; + if (!next) break; /* See if the address is on the never_mail list */ diff --git a/src/src/verify.c b/src/src/verify.c index deca5bc6c..7b9d006f6 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2294,7 +2294,7 @@ for (header_line * h = header_list; h && yield == OK; h = h->next) { if (!f.allow_unqualified_recipient) recipient = NULL; } - if (recipient == NULL) errmess = US"unqualified address not permitted"; + if (!recipient) errmess = US"unqualified address not permitted"; } /* It's an error if no address could be extracted, except for the special @@ -2608,7 +2608,7 @@ for (int i = 0; i < 3 && !done; i++) /* If we found an empty address, just carry on with the next one, but kill the message. */ - if (address == NULL && Ustrcmp(*log_msgptr, "empty address") == 0) + if (!address && Ustrcmp(*log_msgptr, "empty address") == 0) { *log_msgptr = NULL; s = ss; @@ -2619,7 +2619,7 @@ for (int i = 0; i < 3 && !done; i++) function, and ensure that the failing address gets added to the error message. */ - if (address == NULL) + if (!address) { new_ok = FAIL; while (ss > s && isspace(ss[-1])) ss--; diff --git a/test/aux-fixed/2501.alias.exists b/test/aux-fixed/2501.alias.exists new file mode 100644 index 000000000..2bd57ca95 --- /dev/null +++ b/test/aux-fixed/2501.alias.exists @@ -0,0 +1 @@ +yes: aliased@okdomain diff --git a/test/confs/2501 b/test/confs/2501 index b485b0102..61bf7383b 100644 --- a/test/confs/2501 +++ b/test/confs/2501 @@ -14,10 +14,17 @@ domainlist local_domains = dsearch;DIR/aux-fixed/TESTNUM.domains begin routers r1: - driver = accept - domains = +local_domains - transport = t1 - + driver = accept + domains = +local_domains + transport = t1 + +virtual: + driver = redirect + domains = *.virt.test.ex + address_data = ${lookup {TESTNUM.alias.${extract {1}{.}{$domain}}} \ + dsearch{DIR/aux-fixed} {$value}fail} + data = ${lookup{$local_part}lsearch{DIR/aux-fixed/$address_data}} + no_more # ------ Transports ------ diff --git a/test/scripts/2500-dsearch/2501 b/test/scripts/2500-dsearch/2501 index 44b5308eb..d3b31a668 100644 --- a/test/scripts/2500-dsearch/2501 +++ b/test/scripts/2500-dsearch/2501 @@ -2,3 +2,6 @@ 2 exim -bt xxx@okdomain yyy@notokdomain zzz@dom/mod **** +2 +exim -bv yes@exists.virt.test.ex no@exists.virt.test.ex xx@notexists.virt.test.ex +**** diff --git a/test/stderr/0002 b/test/stderr/0002 index f22614284..995a193fe 100644 --- a/test/stderr/0002 +++ b/test/stderr/0002 @@ -177,12 +177,14 @@ dropping to exim gid; retaining priv uid ╭considering: -oMa sender_host_address = $sender_host_address ├──expanding: -oMa sender_host_address = $sender_host_address ╰─────result: -oMa sender_host_address = V4NET.0.0.1 + ╰──(tainted) ╭considering: sender_host_port = $sender_host_port ├──expanding: sender_host_port = $sender_host_port ╰─────result: sender_host_port = 1234 ╭considering: -oMaa sender_host_authenticated = $sender_host_authenticated ├──expanding: -oMaa sender_host_authenticated = $sender_host_authenticated ╰─────result: -oMaa sender_host_authenticated = AAA + ╰──(tainted) ╭considering: -oMai authenticated_id = $authenticated_id ├──expanding: -oMai authenticated_id = $authenticated_id ╰─────result: -oMai authenticated_id = philip @@ -194,15 +196,18 @@ dropping to exim gid; retaining priv uid ╭considering: -oMi interface_address = $interface_address ├──expanding: -oMi interface_address = $interface_address ╰─────result: -oMi interface_address = 1.1.1.1 + ╰──(tainted) ╭considering: interface_port = $interface_port ├──expanding: interface_port = $interface_port ╰─────result: interface_port = 99 ╭considering: -oMr received_protocol = $received_protocol ├──expanding: -oMr received_protocol = $received_protocol ╰─────result: -oMr received_protocol = special + ╰──(tainted) ╭considering: -oMt sender_ident = $sender_ident ├──expanding: -oMt sender_ident = $sender_ident ╰─────result: -oMt sender_ident = me + ╰──(tainted) >>>>>>>>>>>>>>>> Exim pid=pppp (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>> 1999-03-02 09:44:33 no host name found for IP address V4NET.11.12.13 Exim version x.yz .... @@ -212,12 +217,14 @@ dropping to exim gid; retaining priv uid ╭considering: -oMa sender_host_address = $sender_host_address ├──expanding: -oMa sender_host_address = $sender_host_address ╰─────result: -oMa sender_host_address = V4NET.0.0.1 + ╰──(tainted) ╭considering: sender_host_port = $sender_host_port ├──expanding: sender_host_port = $sender_host_port ╰─────result: sender_host_port = 1234 ╭considering: -oMaa sender_host_authenticated = $sender_host_authenticated ├──expanding: -oMaa sender_host_authenticated = $sender_host_authenticated ╰─────result: -oMaa sender_host_authenticated = AAA + ╰──(tainted) ╭considering: -oMai authenticated_id = $authenticated_id ├──expanding: -oMai authenticated_id = $authenticated_id ╰─────result: -oMai authenticated_id = philip @@ -229,12 +236,14 @@ dropping to exim gid; retaining priv uid ╭considering: -oMi interface_address = $interface_address ├──expanding: -oMi interface_address = $interface_address ╰─────result: -oMi interface_address = 1.1.1.1 + ╰──(tainted) ╭considering: interface_port = $interface_port ├──expanding: interface_port = $interface_port ╰─────result: interface_port = 99 ╭considering: -oMr received_protocol = $received_protocol ├──expanding: -oMr received_protocol = $received_protocol ╰─────result: -oMr received_protocol = special + ╰──(tainted) ╭considering: ----> No lookup yet: ${if eq{black}{white}{$sender_host_name}{No}} ╭considering: black}{white}{$sender_host_name}{No}} ├──expanding: black @@ -276,6 +285,7 @@ sender_rcvhost = ten-1.test.ex ([V4NET.0.0.1] ident=me) ╭considering: -oMt sender_ident = $sender_ident ├──expanding: -oMt sender_ident = $sender_ident ╰─────result: -oMt sender_ident = me + ╰──(tainted) >>>>>>>>>>>>>>>> Exim pid=pppp (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... changed uid/gid: forcing real = effective diff --git a/test/stdout/2501 b/test/stdout/2501 index 1199cdd1d..253466563 100644 --- a/test/stdout/2501 +++ b/test/stdout/2501 @@ -2,3 +2,6 @@ xxx@okdomain router = r1, transport = t1 yyy@notokdomain is undeliverable: Unrouteable address syntax error: malformed address: /mod may not follow zzz@dom +yes@exists.virt.test.ex verified +no@exists.virt.test.ex failed to verify: Unrouteable address +xx@notexists.virt.test.ex failed to verify: Unrouteable address