From f3ebb786e451da973560f1c9d8cdb151d25108b5 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 25 Jul 2019 12:06:07 +0100 Subject: [PATCH] Track tainted data and refuse to expand it --- doc/doc-docbook/spec.xfpt | 13 +- doc/doc-txt/ChangeLog | 4 + src/OS/unsupported/os.c-IRIX | 2 +- src/OS/unsupported/os.c-IRIX6 | 2 +- src/OS/unsupported/os.c-IRIX632 | 2 +- src/OS/unsupported/os.c-IRIX65 | 2 +- src/OS/unsupported/os.c-cygwin | 4 +- src/exim_monitor/em_log.c | 11 +- src/exim_monitor/em_main.c | 4 +- src/exim_monitor/em_menu.c | 4 +- src/exim_monitor/em_queue.c | 14 +- src/exim_monitor/em_strip.c | 2 +- src/exim_monitor/em_version.c | 6 +- src/exim_monitor/em_xs.c | 4 +- src/src/acl.c | 46 +- src/src/arc.c | 17 +- src/src/auths/call_pam.c | 7 +- src/src/auths/cram_md5.c | 2 +- src/src/auths/cyrus_sasl.c | 5 +- src/src/auths/heimdal_gssapi.c | 10 +- src/src/auths/pwcheck.c | 3 +- src/src/auths/xtextdecode.c | 2 +- src/src/auths/xtextencode.c | 2 +- src/src/base64.c | 4 +- src/src/bmi_spam.c | 13 +- src/src/child.c | 2 +- src/src/daemon.c | 42 +- src/src/dane-openssl.c | 2 +- src/src/dbfn.c | 13 +- src/src/dcc.c | 30 +- src/src/debug.c | 17 +- src/src/deliver.c | 56 +- src/src/dkim.c | 6 +- src/src/dmarc.c | 6 +- src/src/dns.c | 21 +- src/src/drtables.c | 17 +- src/src/dummies.c | 12 +- src/src/environment.c | 15 +- src/src/exim.c | 110 +- src/src/exim_dbmbuild.c | 14 +- src/src/exim_dbutil.c | 29 +- src/src/expand.c | 99 +- src/src/filter.c | 1123 ++++++++--------- src/src/filtertest.c | 4 +- src/src/functions.h | 136 +- src/src/globals.c | 2 +- src/src/globals.h | 2 +- src/src/hash.c | 11 +- src/src/header.c | 13 +- src/src/host.c | 105 +- src/src/ip.c | 2 +- src/src/local_scan.h | 5 +- src/src/log.c | 58 +- src/src/lookups/cdb.c | 54 +- src/src/lookups/dbmdb.c | 2 +- src/src/lookups/dnsdb.c | 2 +- src/src/lookups/ibase.c | 41 +- src/src/lookups/json.c | 6 +- src/src/lookups/ldap.c | 47 +- src/src/lookups/lmdb.c | 2 +- src/src/lookups/lsearch.c | 8 +- src/src/lookups/mysql.c | 8 +- src/src/lookups/nis.c | 2 +- src/src/lookups/nisplus.c | 4 +- src/src/lookups/oracle.c | 16 +- src/src/lookups/pgsql.c | 9 +- src/src/lookups/redis.c | 8 +- src/src/lookups/sqlite.c | 2 +- src/src/macros.h | 13 +- src/src/malware.c | 24 +- src/src/match.c | 13 +- src/src/mime.c | 4 +- src/src/moan.c | 2 +- src/src/mytypes.h | 44 +- src/src/os.c | 8 +- src/src/parse.c | 26 +- src/src/pdkim/pdkim.c | 24 +- src/src/pdkim/signing.c | 6 +- src/src/queue.c | 16 +- src/src/rda.c | 47 +- src/src/readconf.c | 61 +- src/src/receive.c | 42 +- src/src/regex.c | 8 +- src/src/retry.c | 7 +- src/src/rewrite.c | 22 +- src/src/rfc2047.c | 5 +- src/src/route.c | 36 +- src/src/routers/dnslookup.c | 2 +- src/src/routers/ipliteral.c | 2 +- src/src/routers/iplookup.c | 4 +- src/src/routers/manualroute.c | 2 +- src/src/routers/redirect.c | 166 ++- src/src/routers/rf_change_domain.c | 2 +- src/src/routers/rf_get_munge_headers.c | 4 +- src/src/routers/rf_get_transport.c | 19 +- src/src/routers/rf_queue_add.c | 2 +- src/src/search.c | 16 +- src/src/setenv.c | 2 +- src/src/sieve.c | 91 +- src/src/smtp_in.c | 98 +- src/src/smtp_out.c | 21 +- src/src/spam.c | 4 +- src/src/spool_in.c | 117 +- src/src/spool_mbox.c | 11 +- src/src/spool_out.c | 47 +- src/src/store.c | 592 ++++++--- src/src/store.h | 52 +- src/src/string.c | 214 ++-- src/src/tls-gnu.c | 29 +- src/src/tls-openssl.c | 4 +- src/src/tlscert-gnu.c | 22 +- src/src/tlscert-openssl.c | 8 +- src/src/transport.c | 53 +- src/src/transports/appendfile.c | 31 +- src/src/transports/appendfile.h | 2 +- src/src/transports/autoreply.c | 50 +- src/src/transports/lmtp.c | 17 +- src/src/transports/pipe.c | 4 +- src/src/transports/smtp.c | 49 +- src/src/transports/smtp.h | 3 - src/src/transports/tf_maildir.c | 38 +- src/src/tree.c | 18 +- src/src/utf8.c | 4 +- src/src/verify.c | 13 +- src/src/version.c | 6 +- test/confs/0102 | 4 +- test/confs/0137 | 6 +- test/confs/0140 | 2 +- test/confs/0284 | 4 +- test/confs/0386 | 13 +- test/confs/0428 | 100 +- test/confs/0478 | 4 +- test/confs/0504 | 2 +- test/confs/0610 | 10 +- test/confs/1003 | 43 +- test/confs/5000 | 8 +- test/confs/5050 | 4 +- test/confs/5103 | 2 +- test/log/0137 | 8 +- test/log/0428 | 18 +- test/log/0610 | 4 +- test/log/1003 | 10 +- test/log/5000 | 2 +- test/mail/0137.userx | 36 +- test/mail/0428.inbox.JUNK | 56 +- test/mail/0428.someone | 2 +- test/mail/0428.userx | 56 - test/mail/0428.userx-sawsuffix | 13 +- test/mail/0428.userx13 | 17 + test/mail/0428.userx14 | 18 + test/mail/0428.userx9 | 20 + test/mail/5000.new/1.myhost.test.ex | 1 - test/mail/5000.new/2.myhost.test.ex | 1 - test/mail/5000.new/3.myhost.test.ex:S370 | 2 - test/mail/5000.new/4.myhost.test.ex,S=370 | 2 - test/mail/5000.new/5.myhost.test.ex | 2 - test/mail/5000.new/6.myhost.test.ex | 7 - .../5000.new/7.myhost.test.ex,S=10694953:2,S | 2 - test/msglog/5000.10HmaX-0005vi-00 | 2 +- test/paniclog/1003 | 2 +- test/paniclog/5000 | 2 +- test/runtest | 2 +- test/scripts/0000-Basic/0002 | 3 +- test/scripts/0000-Basic/0137 | 20 +- test/scripts/0000-Basic/0428 | 38 +- test/scripts/0000-Basic/0504 | 9 +- test/scripts/1000-Basic-ipv6/1003 | 25 +- test/scripts/5000-maildir/5000 | 41 +- test/scripts/5100-lmtp-transport/5103 | 1 - test/stderr/0002 | 13 +- test/stderr/0023 | 2 +- test/stderr/0037 | 8 +- test/stderr/0084 | 4 +- test/stderr/0085 | 12 +- test/stderr/0123 | 72 +- test/stderr/0279 | 4 +- test/stderr/0297 | 8 +- test/stderr/0360 | 16 +- test/stderr/0361 | 4 +- test/stderr/0364 | 28 +- test/stderr/0370 | 2 +- test/stderr/0377 | 48 +- test/stderr/0378 | 38 +- test/stderr/0379 | 12 +- test/stderr/0380 | 8 +- test/stderr/0382 | 8 +- test/stderr/0386 | 144 ++- test/stderr/0388 | 4 +- test/stderr/0393 | 4 +- test/stderr/0399 | 12 +- test/stderr/0402 | 20 +- test/stderr/0403 | 4 +- test/stderr/0404 | 6 +- test/stderr/0426 | 4 +- test/stderr/0464 | 8 +- test/stderr/0544 | 6 + test/stderr/1003 | 2 +- test/stderr/5000 | 2 +- test/stderr/5004 | 7 +- test/stderr/5204 | 4 +- test/stderr/5410 | 22 + test/stderr/5420 | 22 + test/stdout/0002 | 1 + test/stdout/0035 | 14 +- test/stdout/3415 | 20 +- 205 files changed, 3303 insertions(+), 2489 deletions(-) create mode 100644 test/mail/0428.userx13 create mode 100644 test/mail/0428.userx14 create mode 100644 test/mail/0428.userx9 diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index c4d6112ad..32d57d027 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -9219,7 +9219,13 @@ dependent upon the option for which a value is sought; in this documentation, options for which string expansion is performed are marked with † after the data type. ACL rules always expand strings. A couple of expansion conditions do not expand some of the brace-delimited branches, for security -reasons. +reasons, +.new +.cindex "tainted data" expansion +.cindex expansion "tainted data" +and expansion of data deriving from the sender (&"tainted data"&) +is not permitted. +.wen @@ -39543,6 +39549,11 @@ was received from the client, this records the Distinguished Name from that certificate. .endlist +.new +Any of the above may have an extra hyphen prepended, to indicate the the +corresponding data is untrusted. +.wen + Following the options there is a list of those addresses to which the message is not to be delivered. This set of addresses is initialized from the command line when the &%-t%& option is used and &%extract_addresses_remove_arguments%& diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 2e839039c..78cb12720 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -149,6 +149,10 @@ JH/30 Bug 2411: Fix DSN generation when RFC 3461 failure notification is JH/31 Avoid re-expansion in ${sort } expansion. (CVE-2019-13917) +JH/32 Introduce a general tainting mechanism for values read from the input + channel, and values derived from them. Refuse to expand any tainted + values, to catch one form of exploit. + Exim version 4.92 ----------------- diff --git a/src/OS/unsupported/os.c-IRIX b/src/OS/unsupported/os.c-IRIX index 1f6b0e1b1..c1539cb50 100644 --- a/src/OS/unsupported/os.c-IRIX +++ b/src/OS/unsupported/os.c-IRIX @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed); +buf = store_get(needed, FALSE); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-IRIX6 b/src/OS/unsupported/os.c-IRIX6 index 1f6b0e1b1..c1539cb50 100644 --- a/src/OS/unsupported/os.c-IRIX6 +++ b/src/OS/unsupported/os.c-IRIX6 @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed); +buf = store_get(needed, FALSE); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-IRIX632 b/src/OS/unsupported/os.c-IRIX632 index 1f6b0e1b1..c1539cb50 100644 --- a/src/OS/unsupported/os.c-IRIX632 +++ b/src/OS/unsupported/os.c-IRIX632 @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed); +buf = store_get(needed, FALSE); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-IRIX65 b/src/OS/unsupported/os.c-IRIX65 index 1f6b0e1b1..c1539cb50 100644 --- a/src/OS/unsupported/os.c-IRIX65 +++ b/src/OS/unsupported/os.c-IRIX65 @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed); +buf = store_get(needed, FALSE); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-cygwin b/src/OS/unsupported/os.c-cygwin index c9464aae2..5ca05a8db 100644 --- a/src/OS/unsupported/os.c-cygwin +++ b/src/OS/unsupported/os.c-cygwin @@ -167,10 +167,10 @@ void cygwin_premain2(int argc, char ** argv, struct per_process * ptr) cygwin_debug = TRUE; fprintf(stderr, "CYGWIN = \"%s\".\n", cygenv); if (((size = cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, 0)) > 0) - && ((win32_path = malloc(size)) != NULL) + && ((win32_path = store_malloc(size)) != NULL) && (cygwin_conv_path(CCP_POSIX_TO_WIN_W,"/", win32_path, size) == 0)) { fprintf(stderr, " Root / mapped to %ls.\n", win32_path); - free(win32_path); + store_free(win32_path); } } else if (argv[i][1] == 'b' && argv[i][2] == 'd') { diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index 52eef6b20..1e1dc7c04 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -227,7 +227,7 @@ if (LOG != NULL) { uschar *id; uschar *p = buffer; - void *reset_point; + rmark reset_point; int length = Ustrlen(buffer); int i; @@ -240,7 +240,7 @@ if (LOG != NULL) it for various regular expression matches and take appropriate action. Get the current store point so we can reset to it. */ - reset_point = store_get(0); + reset_point = store_mark(); /* First, update any stripchart data values, noting that the zeroth stripchart is the queue length, which is handled elsewhere, and the @@ -364,9 +364,10 @@ link count of zero on the currently open file. */ if (log_datestamping) { uschar log_file_wanted[256]; - /* Do *not* use "%s" here, we need the %D datestamp in the log_file to - * be expanded! */ - string_format(log_file_wanted, sizeof(log_file_wanted), CS log_file); + /* Do *not* use "%s" here, we need the %D datestamp in the log_file string to + be expanded. The trailing NULL arg is to quieten preprocessors that need at + least one arg for a variadic set in a macro. */ + string_format(log_file_wanted, sizeof(log_file_wanted), CS log_file, NULL); if (Ustrcmp(log_file_wanted, log_file_open) != 0) { if (LOG != NULL) diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 7aa760eb3..9c7f442e5 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -613,7 +613,7 @@ message_subdir[1] = 0; constructing file names and things. This call will initialize the store_get() function. */ -big_buffer = store_get(big_buffer_size); +big_buffer = store_get(big_buffer_size, FALSE); /* Set up the version string and date and output them */ @@ -655,7 +655,7 @@ if (log_file[0] != 0) { /* Do *not* use "%s" here, we need the %D datestamp in the log_file to be expanded! */ - (void)string_format(log_file_open, sizeof(log_file_open), CS log_file); + (void)string_format(log_file_open, sizeof(log_file_open), CS log_file, NULL); log_datestamping = string_datestamp_offset >= 0; LOG = fopen(CS log_file_open, "r"); diff --git a/src/exim_monitor/em_menu.c b/src/exim_monitor/em_menu.c index 31ce1a371..92e0b351b 100644 --- a/src/exim_monitor/em_menu.c +++ b/src/exim_monitor/em_menu.c @@ -651,7 +651,7 @@ static void headersAction(Widget w, XtPointer client_data, XtPointer call_data) uschar buffer[256]; header_line *h, *next; Widget text = text_create(US client_data, text_depth); -void *reset_point; +rmark reset_point; w = w; /* Keep picky compilers happy */ call_data = call_data; @@ -659,7 +659,7 @@ call_data = call_data; /* Remember the point in the dynamic store so we can recover to it afterwards. Then use Exim's function to read the header. */ -reset_point = store_get(0); +reset_point = store_mark(); sprintf(CS buffer, "%s-H", US client_data); if (spool_read_header(buffer, TRUE, FALSE) != spool_read_OK) diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index c8d9a40fc..f121527cc 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -138,7 +138,7 @@ acl_var_create(uschar *name) { tree_node *node, **root; root = (name[0] == 'c')? &acl_var_c : &acl_var_m; -node = store_get(sizeof(tree_node) + Ustrlen(name)); +node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE); Ustrcpy(node->name, name); node->data.ptr = NULL; (void)tree_insertnode(root, node); @@ -156,7 +156,7 @@ set_up(uschar *name, int dir_char) { int i, rc, save_errno; struct stat statdata; -void *reset_point; +rmark reset_point; uschar *p; queue_item *q = (queue_item *)store_malloc(sizeof(queue_item)); uschar buffer[256]; @@ -182,7 +182,7 @@ Before reading the header remember the position in the dynamic store so that we can recover the store into which the header is read. All data read by spool_read_header that is to be preserved is copied into malloc store. */ -reset_point = store_get(0); +reset_point = store_mark(); message_size = 0; message_subdir[0] = dir_char; sprintf(CS buffer, "%s-H", name); @@ -224,9 +224,9 @@ if (rc != spool_read_OK) if (Ustat(big_buffer, &statbuf) == 0) msg = string_sprintf("*** Format error in spool file: size = %d ***", statbuf.st_size); - else msg = string_sprintf("*** Format error in spool file ***"); + else msg = US"*** Format error in spool file ***"; } - else msg = string_sprintf("*** Cannot read spool file ***"); + else msg = US"*** Cannot read spool file ***"; if (rc == spool_read_hdrerror) { @@ -614,7 +614,7 @@ static void update_recipients(queue_item *p) { int i; FILE *jread; -void *reset_point; +rmark reset_point; struct stat statdata; uschar buffer[1024]; @@ -634,7 +634,7 @@ if (!(jread = fopen(CS buffer, "r"))) /* Get the contents of the header file; if any problem, just give up. Arrange to recover the dynamic store afterwards. */ -reset_point = store_get(0); +reset_point = store_mark(); sprintf(CS buffer, "%s-H", p->name); if (spool_read_header(buffer, FALSE, TRUE) != spool_read_OK) { diff --git a/src/exim_monitor/em_strip.c b/src/exim_monitor/em_strip.c index 2a5f0b84e..03864d290 100644 --- a/src/exim_monitor/em_strip.c +++ b/src/exim_monitor/em_strip.c @@ -141,7 +141,7 @@ while (thresholds[i] > 0) thresh : stripchart_midmax[num]; if (newmax == 10) sprintf(CS buffer, "%s", stripchart_name[num]); else sprintf(CS buffer, "%s x%d", stripchart_name[num], newmax/10); - if (size_stripchart != NULL && num == 1) Ustrcat(buffer, "%"); + if (size_stripchart != NULL && num == 1) Ustrcat(buffer, US"%"); xs_SetValues(stripchart_label[num], 1, "label", buffer); oldmax = stripchart_max[num]; stripchart_max[num] = newmax; diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index e5a4ebbb0..52c55a4a3 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -34,7 +34,7 @@ version_date[0] = 0; Ustrncat(version_date, EXIM_BUILD_DATE_OVERRIDE, 31); #else -Ustrcpy(today, __DATE__); +Ustrcpy(today, US __DATE__); if (today[4] == ' ') i = 1; today[3] = today[6] = '-'; @@ -43,8 +43,8 @@ version_date[0] = 0; Ustrncat(version_date, today+4+i, 3-i); Ustrncat(version_date, today, 4); Ustrncat(version_date, today+7, 4); -Ustrcat(version_date, " "); -Ustrcat(version_date, __TIME__); +Ustrcat(version_date, US" "); +Ustrcat(version_date, US __TIME__); #endif } diff --git a/src/exim_monitor/em_xs.c b/src/exim_monitor/em_xs.c index b145fb993..ee91f7c15 100644 --- a/src/exim_monitor/em_xs.c +++ b/src/exim_monitor/em_xs.c @@ -30,7 +30,7 @@ void xs_SetValues(Widget w, Cardinal num_args, ...) { int i; va_list ap; -Arg *aa = (num_args > 15)? (Arg *)malloc(num_args*sizeof(Arg)) : xs_temparg; +Arg *aa = (num_args > 15)? store_malloc(num_args*sizeof(Arg)) : xs_temparg; va_start(ap, num_args); for (i = 0; i < num_args; i++) { @@ -39,7 +39,7 @@ for (i = 0; i < num_args; i++) } va_end(ap); XtSetValues(w, aa, num_args); -if (num_args > 15) free(aa); +if (num_args > 15) store_free(aa); } /* End of em_xs.c */ diff --git a/src/src/acl.c b/src/src/acl.c index 3788612f8..dac2ba570 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -777,7 +777,7 @@ while ((s = (*func)()) != NULL) *error = string_sprintf("malformed ACL line \"%s\"", saveline); return NULL; } - this = store_get(sizeof(acl_block)); + this = store_get(sizeof(acl_block), FALSE); *lastp = this; lastp = &(this->next); this->next = NULL; @@ -824,7 +824,7 @@ while ((s = (*func)()) != NULL) return NULL; } - cond = store_get(sizeof(acl_condition_block)); + cond = store_get(sizeof(acl_condition_block), FALSE); cond->next = NULL; cond->type = c; cond->u.negated = negated; @@ -1022,7 +1022,9 @@ for (p = q; *p; p = q) if (!*hptr) { - header_line *h = store_get(sizeof(header_line)); + /* The header_line struct itself is not tainted, though it points to + tainted data. */ + header_line *h = store_get(sizeof(header_line), FALSE); h->text = hdr; h->next = NULL; h->type = newtype; @@ -1355,7 +1357,7 @@ we return from this function. */ t = tree_search(csa_cache, domain); if (t != NULL) return t->data.val; -t = store_get_perm(sizeof(tree_node) + Ustrlen(domain)); +t = store_get_perm(sizeof(tree_node) + Ustrlen(domain), is_tainted(domain)); Ustrcpy(t->name, domain); (void)tree_insertnode(&csa_cache, t); @@ -1726,7 +1728,7 @@ switch(vp->value) if ((rc = verify_check_notblind(case_sensitive)) != OK) { - *log_msgptr = string_sprintf("bcc recipient detected"); + *log_msgptr = US"bcc recipient detected"; if (smtp_return_error_details) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); } @@ -2168,7 +2170,7 @@ gstring * g = string_cat(NULL, US"error in arguments to \"ratelimit\" condition: "); va_start(ap, format); -g = string_vformat(g, TRUE, format, ap); +g = string_vformat(g, SVFMT_EXTEND|SVFMT_REBUFFER, format, ap); va_end(ap); gstring_release_unused(g); @@ -2455,7 +2457,7 @@ if (!dbdb) /* No Bloom filter. This basic ratelimit block is initialized below. */ HDEBUG(D_acl) debug_printf_indent("ratelimit creating new rate data block\n"); dbdb_size = sizeof(*dbd); - dbdb = store_get(dbdb_size); + dbdb = store_get(dbdb_size, FALSE); /* not tainted */ } else { @@ -2469,7 +2471,7 @@ if (!dbdb) extra = (int)limit * 2 - sizeof(dbdb->bloom); if (extra < 0) extra = 0; dbdb_size = sizeof(*dbdb) + extra; - dbdb = store_get(dbdb_size); + dbdb = store_get(dbdb_size, FALSE); /* not tainted */ dbdb->bloom_epoch = tv.tv_sec; dbdb->bloom_size = sizeof(dbdb->bloom) + extra; memset(dbdb->bloom, 0, dbdb->bloom_size); @@ -2686,9 +2688,10 @@ else dbfn_close(dbm); -/* Store the result in the tree for future reference. */ +/* Store the result in the tree for future reference. Take the taint status +from the key for consistency even though it's unlikely we'll ever expand this. */ -t = store_get(sizeof(tree_node) + Ustrlen(key)); +t = store_get(sizeof(tree_node) + Ustrlen(key), is_tainted(key)); t->data.ptr = dbd; Ustrcpy(t->name, key); (void)tree_insertnode(anchor, t); @@ -2761,7 +2764,7 @@ if (*portend != '\0') } /* Make a single-item host list. */ -h = store_get(sizeof(host_item)); +h = store_get(sizeof(host_item), FALSE); memset(h, 0, sizeof(host_item)); h->name = hostname; h->port = portnum; @@ -3493,7 +3496,7 @@ for (; cb; cb = cb->next) (sender_host_address == NULL)? US"" : sender_host_address, CUSS &host_data); if (rc == DEFER) *log_msgptr = search_error_message; - if (host_data) host_data = string_copy_malloc(host_data); + if (host_data) host_data = string_copy_perm(host_data, TRUE); break; case ACLC_LOCAL_PARTS: @@ -3595,7 +3598,7 @@ for (; cb; cb = cb->next) "Directory separator not permitted in queue name: '%s'", arg); return ERROR; } - queue_name = string_copy_malloc(arg); + queue_name = string_copy_perm(arg, FALSE); break; case ACLC_RATELIMIT: @@ -3995,13 +3998,20 @@ if (Ustrchr(ss, ' ') == NULL) else if (*ss == '/') { struct stat statbuf; + if (is_tainted(ss)) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "attempt to open tainted ACL file name \"%s\"", ss); + /* Avoid leaking info to an attacker */ + *log_msgptr = US"internal configuration error"; + return ERROR; + } if ((fd = Uopen(ss, O_RDONLY, 0)) < 0) { *log_msgptr = string_sprintf("failed to open ACL file \"%s\": %s", ss, strerror(errno)); return ERROR; } - if (fstat(fd, &statbuf) != 0) { *log_msgptr = string_sprintf("failed to fstat ACL file \"%s\": %s", ss, @@ -4009,7 +4019,8 @@ if (Ustrchr(ss, ' ') == NULL) return ERROR; } - acl_text = store_get(statbuf.st_size + 1); + /* If the string being used as a filename is tainted, so is the file content */ + acl_text = store_get(statbuf.st_size + 1, is_tainted(ss)); acl_text_end = acl_text + statbuf.st_size + 1; if (read(fd, acl_text, statbuf.st_size) != statbuf.st_size) @@ -4039,7 +4050,7 @@ if (!acl) if (!acl && *log_msgptr) return ERROR; if (fd >= 0) { - tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss)); + tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss), is_tainted(ss)); Ustrcpy(t->name, ss); t->data.ptr = acl; (void)tree_insertnode(&acl_anchor, t); @@ -4520,7 +4531,7 @@ acl_var_create(uschar * name) tree_node * node, ** root = name[0] == 'c' ? &acl_var_c : &acl_var_m; if (!(node = tree_search(*root, name))) { - node = store_get(sizeof(tree_node) + Ustrlen(name)); + node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name)); Ustrcpy(node->name, name); (void)tree_insertnode(root, node); } @@ -4554,6 +4565,7 @@ void acl_var_write(uschar *name, uschar *value, void *ctx) { FILE *f = (FILE *)ctx; +if (is_tainted(value)) putc('-', f); fprintf(f, "-acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value); } diff --git a/src/src/arc.c b/src/src/arc.c index 7bdf4bfbb..c266849ca 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -143,7 +143,7 @@ for (pas = &ctx->arcset_chain, prev = NULL, next = ctx->arcset_chain; } DEBUG(D_acl) debug_printf("ARC: new instance %u\n", i); -*pas = as = store_get(sizeof(arc_set)); +*pas = as = store_get(sizeof(arc_set), FALSE); memset(as, 0, sizeof(arc_set)); as->next = next; as->prev = prev; @@ -201,7 +201,7 @@ al->complete = h; if (!instance_only) { - al->rawsig_no_b_val.data = store_get(h->slen + 1); + al->rawsig_no_b_val.data = store_get(h->slen + 1, TRUE); /* tainted */ memcpy(al->rawsig_no_b_val.data, h->text, off); /* copy the header name blind */ r = al->rawsig_no_b_val.data + off; al->rawsig_no_b_val.len = off; @@ -387,7 +387,7 @@ arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff, { unsigned i; arc_set * as; -arc_line * al = store_get(sizeof(arc_line)), ** alp; +arc_line * al = store_get(sizeof(arc_line), FALSE), ** alp; uschar * e; memset(al, 0, sizeof(arc_line)); @@ -499,7 +499,7 @@ const uschar * e; DEBUG(D_acl) debug_printf("ARC: collecting arc sets\n"); for (h = header_list; h; h = h->next) { - r = store_get(sizeof(hdr_rlist)); + r = store_get(sizeof(hdr_rlist), FALSE); r->prev = rprev; r->used = FALSE; r->h = h; @@ -1100,7 +1100,7 @@ out: static hdr_rlist * arc_rlist_entry(hdr_rlist * list, const uschar * s, int len) { -hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line)); +hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), FALSE); header_line * h = r->h = (header_line *)(r+1); r->prev = list; @@ -1191,7 +1191,8 @@ arc_sign_append_aar(gstring * g, arc_ctx * ctx, const uschar * identity, int instance, blob * ar) { int aar_off = g ? g->ptr : 0; -arc_set * as = store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line)); +arc_set * as = + store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), FALSE); arc_line * al = (arc_line *)(as+1); header_line * h = (header_line *)(al+1); @@ -1301,7 +1302,7 @@ int col; int hashtype = pdkim_hashname_to_hashtype(US"sha256", 6); /*XXX hardwired */ blob sig; int ams_off; -arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line)); +arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE); header_line * h = (header_line *)(al+1); /* debug_printf("%s\n", __FUNCTION__); */ @@ -1417,7 +1418,7 @@ arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx, gstring * arcset; arc_set * as; uschar * status = arc_ar_cv_status(ar); -arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line)); +arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE); header_line * h = (header_line *)(al+1); gstring * hdata = NULL; diff --git a/src/src/auths/call_pam.c b/src/src/auths/call_pam.c index 204f449d7..9a46b6b63 100644 --- a/src/src/auths/call_pam.c +++ b/src/src/auths/call_pam.c @@ -71,9 +71,7 @@ struct pam_response *reply; if (pam_arg_ended) return PAM_CONV_ERR; -reply = malloc(sizeof(struct pam_response) * num_msg); - -if (reply == NULL) return PAM_CONV_ERR; +reply = store_get(sizeof(struct pam_response) * num_msg, FALSE); for (int i = 0; i < num_msg; i++) { @@ -88,7 +86,7 @@ for (int i = 0; i < num_msg; i++) arg = US""; pam_arg_ended = TRUE; } - reply[i].resp = CS string_copy_malloc(arg); /* PAM frees resp */ + reply[i].resp = CS string_copy_perm(arg, FALSE); /* PAM frees resp */ reply[i].resp_retcode = PAM_SUCCESS; break; @@ -99,7 +97,6 @@ for (int i = 0; i < num_msg; i++) break; default: /* Must be an error of some sort... */ - free (reply); pam_conv_had_error = TRUE; return PAM_CONV_ERR; } diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index b1c5610e7..a60db56e4 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -226,7 +226,7 @@ HDEBUG(D_auth) debug_printf("CRAM-MD5: user name = %s\n", auth_vars[0]); debug_printf(" challenge = %s\n", challenge); debug_printf(" received = %s\n", clear); - Ustrcpy(buff," digest = "); + Ustrcpy(buff, US" digest = "); for (i = 0; i < 16; i++) sprintf(CS buff+22+2*i, "%02x", digest[i]); debug_printf("%.54s\n", buff); } diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index bce420249..480010bab 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -114,7 +114,8 @@ auth_cyrus_sasl_options_block *ob = const uschar *list, *listptr, *buffer; int rc, i; unsigned int len; -uschar *rs_point, *expanded_hostname; +rmark rs_point; +uschar *expanded_hostname; char *realm_expanded; sasl_conn_t *conn; @@ -175,7 +176,7 @@ HDEBUG(D_auth) * the hierarchy is stored for us behind our back. This point * creates a hierarchy point for this function. */ -rs_point = store_get(0); +rs_point = store_mark(); /* loop until either we get to the end of the list, or we match the * public name of this authenticator diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index a70bc8aa3..273d4f47b 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -96,7 +96,7 @@ void auth_heimdal_gssapi_version_report(FILE *f) {} static void exim_heimdal_error_debug(const char *, krb5_context, krb5_error_code); static int - exim_gssapi_error_defer(uschar *, OM_uint32, OM_uint32, const char *, ...) + exim_gssapi_error_defer(rmark, OM_uint32, OM_uint32, const char *, ...) PRINTF_FUNCTION(4, 5); #define EmptyBuf(buf) do { buf.value = NULL; buf.length = 0; } while (0) @@ -255,12 +255,12 @@ uschar *tmp1, *tmp2, *from_client; auth_heimdal_gssapi_options_block *ob = (auth_heimdal_gssapi_options_block *)(ablock->options_block); BOOL handled_empty_ir; -uschar *store_reset_point; +rmark store_reset_point; uschar *keytab; uschar sasl_config[4]; uschar requested_qop; -store_reset_point = store_get(0); +store_reset_point = store_mark(); HDEBUG(D_auth) debug_printf("heimdal: initialising auth context for %s\n", ablock->name); @@ -558,7 +558,7 @@ return auth_check_serv_cond(ablock); static int -exim_gssapi_error_defer(uschar *store_reset_point, +exim_gssapi_error_defer(rmark store_reset_point, OM_uint32 major, OM_uint32 minor, const char *format, ...) { @@ -571,7 +571,7 @@ gstring * g; HDEBUG(D_auth) { va_start(ap, format); - g = string_vformat(NULL, TRUE, format, ap); + g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, format, ap); va_end(ap); } diff --git a/src/src/auths/pwcheck.c b/src/src/auths/pwcheck.c index 54ba80f92..f733b9898 100644 --- a/src/src/auths/pwcheck.c +++ b/src/src/auths/pwcheck.c @@ -296,7 +296,8 @@ static int read_string(int fd, uschar **retval) { if (count > MAX_REQ_LEN) { return -1; } else { - *retval = store_get(count + 1); + /* Assume the file is trusted, so no tainting */ + *retval = store_get(count + 1, FALSE); rc = (retry_read(fd, *retval, count) < (int) count); (*retval)[count] = '\0'; return count; diff --git a/src/src/auths/xtextdecode.c b/src/src/auths/xtextdecode.c index 5312acf35..95cf5dab2 100644 --- a/src/src/auths/xtextdecode.c +++ b/src/src/auths/xtextdecode.c @@ -33,7 +33,7 @@ int auth_xtextdecode(uschar *code, uschar **ptr) { register int x; -uschar *result = store_get(Ustrlen(code) + 1); +uschar *result = store_get(Ustrlen(code) + 1, is_tainted(code)); *ptr = result; while ((x = (*code++)) != 0) diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index 2c00c4a3d..30ff8f11d 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -40,7 +40,7 @@ in order to get the right amount of store. */ while (c -- > 0) count += ((x = *p++) < 33 || x > 127 || x == '+' || x == '=')? 3 : 1; -pp = code = store_get(count); +pp = code = store_get(count, is_tainted(clear)); p = US clear; c = len; diff --git a/src/src/base64.c b/src/src/base64.c index 289383b66..6c8191462 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -158,7 +158,7 @@ uschar *result; { int l = Ustrlen(code); - *ptr = result = store_get(1 + l/4 * 3 + l%4); + *ptr = result = store_get(1 + l/4 * 3 + l%4, is_tainted(code)); } /* Each cycle of the loop handles a quantum of 4 input bytes. For the last @@ -244,7 +244,7 @@ static uschar *enc64table = uschar * b64encode(const uschar * clear, int len) { -uschar *code = store_get(4*((len+2)/3) + 1); +uschar *code = store_get(4*((len+2)/3) + 1, is_tainted(clear)); uschar *p = code; while (len-- >0) diff --git a/src/src/bmi_spam.c b/src/src/bmi_spam.c index 546ac1e36..6651de5ad 100644 --- a/src/src/bmi_spam.c +++ b/src/src/bmi_spam.c @@ -190,8 +190,10 @@ uschar *bmi_process_message(header_line *header_list, int data_fd) { return NULL; }; - /* get store for the verdict string */ - verdicts = store_get(1); + /* Get store for the verdict string. Since we are processing message data, assume that + the verdict is tainted. XXX this should use a growable-string */ + + verdicts = store_get(1, TRUE); *verdicts = '\0'; for ( err = bmiAccessFirstVerdict(message, &verdict); @@ -200,7 +202,8 @@ uschar *bmi_process_message(header_line *header_list, int data_fd) { char *verdict_str; err = bmiCreateStrFromVerdict(verdict,&verdict_str); - if (!store_extend(verdicts, Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) { + if (!store_extend(verdicts, TRUE, + Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) { /* can't allocate more store */ return NULL; }; @@ -299,7 +302,7 @@ uschar *bmi_get_alt_location(uschar *base64_verdict) { } else { /* deliver to alternate location */ - rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1); + rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, TRUE); Ustrcpy(rc, bmiVerdictAccessDestination(verdict)); rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0'; }; @@ -324,7 +327,7 @@ uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) { return NULL; /* allocate room for the b64 verdict string */ - verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1); + verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1, TRUE); /* loop through verdicts */ verdict_ptr = bmi_verdicts; diff --git a/src/src/child.c b/src/src/child.c index e53e448ed..d3cd88201 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -75,7 +75,7 @@ int n = 0; int extra = pcount ? *pcount : 0; uschar **argv; -argv = store_get((extra + acount + MAX_CLMACROS + 18) * sizeof(char *)); +argv = store_get((extra + acount + MAX_CLMACROS + 18) * sizeof(char *), FALSE); /* In all case, the list starts out with the path, any macros, and a changed config file. */ diff --git a/src/src/daemon.c b/src/src/daemon.c index 0b4d347b9..21ce2f03e 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -146,7 +146,7 @@ int max_for_this_host = 0; int save_log_selector = *log_selector; gstring * whofrom; -void *reset_point = store_get(0); +rmark reset_point = store_mark(); /* Make the address available in ASCII representation, and also fish out the remote port. */ @@ -395,7 +395,7 @@ if (pid == 0) "please try again later.\r\n", FALSE); mac_smtp_fflush(); search_tidyup(); - _exit(EXIT_FAILURE); + exim_underbar_exit(EXIT_FAILURE); } } else if (*nah) smtp_active_hostname = nah; @@ -481,14 +481,14 @@ if (pid == 0) { mac_smtp_fflush(); search_tidyup(); - _exit(EXIT_SUCCESS); + exim_underbar_exit(EXIT_SUCCESS); } for (;;) { int rc; message_id[0] = 0; /* Clear out any previous message_id */ - reset_point = store_get(0); /* Save current store high water point */ + reset_point = store_mark(); /* Save current store high water point */ DEBUG(D_any) debug_printf("Process %d is ready for new message\n", (int)getpid()); @@ -509,7 +509,7 @@ if (pid == 0) cancel_cutthrough_connection(TRUE, US"receive dropped"); mac_smtp_fflush(); smtp_log_no_mail(); /* Log no mail if configured */ - _exit(EXIT_SUCCESS); + exim_underbar_exit(EXIT_SUCCESS); } if (message_id[0] == 0) continue; /* No message was accepted */ } @@ -532,7 +532,7 @@ if (pid == 0) /*XXX should we pause briefly, hoping that the client will be the active TCP closer hence get the TCP_WAIT endpoint? */ DEBUG(D_receive) debug_printf("SMTP>>(close on process exit)\n"); - _exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); + exim_underbar_exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); } /* Show the recipients when debugging */ @@ -565,6 +565,7 @@ if (pid == 0) int r = receive_messagecount; BOOL q = f.queue_only_policy; smtp_reset(reset_point); + reset_point = NULL; f.queue_only_policy = q; receive_messagecount = r; } @@ -665,7 +666,7 @@ if (pid == 0) (void) deliver_message(message_id, FALSE, FALSE); search_tidyup(); - _exit(EXIT_SUCCESS); + exim_underbar_exit(EXIT_SUCCESS); } if (dpid > 0) @@ -696,13 +697,14 @@ else if (smtp_slots[i].pid <= 0) { smtp_slots[i].pid = pid; - if (smtp_accept_max_per_host != NULL) + /* Connection closes come asyncronously, so we cannot stack this store */ + if (smtp_accept_max_per_host) smtp_slots[i].host_address = string_copy_malloc(sender_host_address); smtp_accept_count++; break; } DEBUG(D_any) debug_printf("%d SMTP accept process%s running\n", - smtp_accept_count, (smtp_accept_count == 1)? "" : "es"); + smtp_accept_count, smtp_accept_count == 1 ? "" : "es"); } /* Get here via goto in error cases */ @@ -833,7 +835,6 @@ pid_t pid; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - int i; DEBUG(D_any) { debug_printf("child %d ended: status=0x%x\n", (int)pid, status); @@ -851,6 +852,7 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) if (smtp_slots) { + int i; for (i = 0; i < smtp_accept_max; i++) if (smtp_slots[i].pid == pid) { @@ -871,7 +873,7 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) if (queue_pid_slots) { int max = atoi(CS expand_string(queue_run_max)); - for (i = 0; i < max; i++) + for (int i = 0; i < max; i++) if (queue_pid_slots[i] == pid) { queue_pid_slots[i] = 0; @@ -927,7 +929,7 @@ DEBUG(D_any|D_v) debug_selector |= D_pid; if (f.inetd_wait_mode) { listen_socket_count = 1; - listen_sockets = store_get(sizeof(int)); + listen_sockets = store_get(sizeof(int), FALSE); (void) close(3); if (dup2(0, 3) == -1) log_write(0, LOG_MAIN|LOG_PANIC_DIE, @@ -1115,7 +1117,7 @@ if (f.daemon_listen && !f.inetd_wait_mode) sep = 0; while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) pct++; - default_smtp_port = store_get((pct+1) * sizeof(int)); + default_smtp_port = store_get((pct+1) * sizeof(int), FALSE); list = daemon_smtp_port; sep = 0; for (pct = 0; @@ -1203,7 +1205,7 @@ if (f.daemon_listen && !f.inetd_wait_mode) ipa->port = default_smtp_port[0]; for (int i = 1; default_smtp_port[i] > 0; i++) { - ip_address_item *new = store_get(sizeof(ip_address_item)); + ip_address_item *new = store_get(sizeof(ip_address_item), FALSE); memcpy(new->address, ipa->address, Ustrlen(ipa->address) + 1); new->port = default_smtp_port[i]; @@ -1261,7 +1263,7 @@ if (f.daemon_listen && !f.inetd_wait_mode) for (ipa = addresses; ipa; ipa = ipa->next) listen_socket_count++; - listen_sockets = store_get(sizeof(int) * listen_socket_count); + listen_sockets = store_get(sizeof(int) * listen_socket_count, FALSE); } /* daemon_listen but not inetd_wait_mode */ @@ -1284,7 +1286,7 @@ if (f.daemon_listen) if (smtp_accept_max > 0) { - smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot)); + smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot), FALSE); for (int i = 0; i < smtp_accept_max; i++) smtp_slots[i] = empty_smtp_slot; } } @@ -1572,15 +1574,15 @@ coming from Exim, not whoever started the daemon. */ originator_uid = exim_uid; originator_gid = exim_gid; -originator_login = ((pw = getpwuid(exim_uid)) != NULL)? - string_copy_malloc(US pw->pw_name) : US"exim"; +originator_login = (pw = getpwuid(exim_uid)) + ? string_copy_perm(US pw->pw_name, FALSE) : US"exim"; /* Get somewhere to keep the list of queue-runner pids if we are keeping track of them (and also if we are doing queue runs). */ if (queue_interval > 0 && local_queue_run_max > 0) { - queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t)); + queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t), FALSE); for (int i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0; } @@ -1895,7 +1897,7 @@ for (;;) /* No need to re-exec; SIGALRM remains set to the default handler */ queue_run(NULL, NULL, FALSE); - _exit(EXIT_SUCCESS); + exim_underbar_exit(EXIT_SUCCESS); } if (pid < 0) diff --git a/src/src/dane-openssl.c b/src/src/dane-openssl.c index c0bbf3d0d..9b86a488b 100644 --- a/src/src/dane-openssl.c +++ b/src/src/dane-openssl.c @@ -129,7 +129,7 @@ static ERR_STRING_DATA dane_str_reasons[] = { }; #endif -#define DANEerr(f, r) ERR_PUT_error(err_lib_dane, (f), (r), __FILE__, __LINE__) +#define DANEerr(f, r) ERR_PUT_error(err_lib_dane, (f), (r), __FUNCTION__, __LINE__) static int err_lib_dane = -1; static int dane_idx = -1; diff --git a/src/src/dbfn.c b/src/src/dbfn.c index a60775681..63a1aefe3 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -206,7 +206,7 @@ if (created && geteuid() == root_uid) if (Ustrncmp(ent->d_name, name, namelen) == 0) { struct stat statbuf; - Ustrcpy(lastname, ent->d_name); + Ustrcpy(lastname, US ent->d_name); if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid) { DEBUG(D_hints_lookup) debug_printf_indent("ensuring %s is owned by exim\n", filename); @@ -303,7 +303,7 @@ dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) void *yield; EXIM_DATUM key_datum, result_datum; int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen); +uschar * key_copy = store_get(klen, is_tainted(key)); memcpy(key_copy, key, klen); @@ -316,7 +316,10 @@ EXIM_DATUM_SIZE(key_datum) = klen; if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; -yield = store_get(EXIM_DATUM_SIZE(result_datum)); +/* Assume the data store could have been tainted. Properly, we should +store the taint status with the data. */ + +yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE); memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum); @@ -347,7 +350,7 @@ dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) EXIM_DATUM key_datum, value_datum; dbdata_generic *gptr = (dbdata_generic *)ptr; int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen); +uschar * key_copy = store_get(klen, is_tainted(key)); memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); @@ -381,7 +384,7 @@ int dbfn_delete(open_db *dbblock, const uschar *key) { int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen); +uschar * key_copy = store_get(klen, is_tainted(key)); DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key); diff --git a/src/src/dcc.c b/src/src/dcc.c index 5aa2b17aa..4c9c0964f 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -156,11 +156,11 @@ dcc_process(uschar **listptr) debug_printf("DCC: Client IP (default): %s\n", client_ip); } /* strncat(opts, my_request, strlen(my_request)); */ - Ustrcat(opts, "\n"); + Ustrcat(opts, US"\n"); Ustrncat(opts, client_ip, sizeof(opts)-Ustrlen(opts)-1); - Ustrncat(opts, "\nHELO ", sizeof(opts)-Ustrlen(opts)-1); + Ustrncat(opts, US"\nHELO ", sizeof(opts)-Ustrlen(opts)-1); Ustrncat(opts, dcc_helo_option, sizeof(opts)-Ustrlen(opts)-2); - Ustrcat(opts, "\n"); + Ustrcat(opts, US"\n"); /* initialize the other variables */ dcchdr = header_list; @@ -176,8 +176,8 @@ dcc_process(uschar **listptr) if (Ustrlen(sender_address) > 0) Ustrncpy(from, sender_address, sizeof(from)); else - Ustrncpy(from, "<>", sizeof(from)); - Ustrncat(from, "\n", sizeof(from)-Ustrlen(from)-1); + Ustrncpy(from, US"<>", sizeof(from)); + Ustrncat(from, US"\n", sizeof(from)-Ustrlen(from)-1); /************************************** * Now creating the socket connection * @@ -211,7 +211,7 @@ dcc_process(uschar **listptr) /* connecting to the dccifd UNIX socket */ bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; - Ustrncpy(serv_addr.sun_path, sockpath, sizeof(serv_addr.sun_path)); + Ustrncpy(US serv_addr.sun_path, sockpath, sizeof(serv_addr.sun_path)); if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0){ DEBUG(D_acl) debug_printf("DCC: Creating UNIX socket connection failed: %s\n", strerror(errno)); @@ -255,10 +255,10 @@ dcc_process(uschar **listptr) bzero(sendbuf, sizeof(sendbuf)); } Ustrncat(sendbuf, recipients_list[i].address, sizeof(sendbuf)-Ustrlen(sendbuf)-1); - Ustrncat(sendbuf, "\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); + Ustrncat(sendbuf, US"\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); } /* send a blank line between options and message */ - Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); + Ustrncat(sendbuf, US"\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); /* Now we send the input buffer */ DEBUG(D_acl) debug_printf("DCC: %s\nDCC: ****************************\n", sendbuf); @@ -298,7 +298,7 @@ dcc_process(uschar **listptr) } /* a blank line separates header from body */ - Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); + Ustrncat(sendbuf, US"\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1); flushbuffer(sockfd, sendbuf); DEBUG(D_acl) debug_printf("\nDCC: ****************************\n%s", sendbuf); @@ -376,7 +376,7 @@ dcc_process(uschar **listptr) if(recvbuf[i] == 'A') { DEBUG(D_acl) debug_printf("DCC: Overall result = A\treturning OK\n"); - Ustrcpy(dcc_return_text, "Mail accepted by DCC"); + Ustrcpy(dcc_return_text, US"Mail accepted by DCC"); dcc_result = US"A"; retval = OK; } @@ -396,7 +396,7 @@ dcc_process(uschar **listptr) else if(recvbuf[i] == 'S') { DEBUG(D_acl) debug_printf("DCC: Overall result = S\treturning OK\n"); - Ustrcpy(dcc_return_text, "Not all recipients accepted by DCC"); + Ustrcpy(dcc_return_text, US"Not all recipients accepted by DCC"); /* Since we're in an ACL we want a global result * so we accept for all */ dcc_result = US"A"; @@ -405,7 +405,7 @@ dcc_process(uschar **listptr) else if(recvbuf[i] == 'G') { DEBUG(D_acl) debug_printf("DCC: Overall result = G\treturning FAIL\n"); - Ustrcpy(dcc_return_text, "Greylisted by DCC"); + Ustrcpy(dcc_return_text, US"Greylisted by DCC"); dcc_result = US"G"; retval = FAIL; } @@ -414,7 +414,7 @@ dcc_process(uschar **listptr) debug_printf("DCC: Overall result = T\treturning DEFER\n"); retval = DEFER; log_write(0,LOG_MAIN,"Temporary error with DCC: %s\n", recvbuf); - Ustrcpy(dcc_return_text, "Temporary error with DCC"); + Ustrcpy(dcc_return_text, US"Temporary error with DCC"); dcc_result = US"T"; } else { @@ -422,7 +422,7 @@ dcc_process(uschar **listptr) debug_printf("DCC: Overall result = something else\treturning DEFER\n"); retval = DEFER; log_write(0,LOG_MAIN,"Unknown DCC response: %s\n", recvbuf); - Ustrcpy(dcc_return_text, "Unknown DCC response"); + Ustrcpy(dcc_return_text, US"Unknown DCC response"); dcc_result = US"T"; } } @@ -492,7 +492,7 @@ dcc_process(uschar **listptr) if (((xtra_hdrs = expand_string(US"$acl_m_dcc_add_header")) != NULL) && (xtra_hdrs[0] != '\0')) { Ustrncpy(dcc_xtra_hdrs, xtra_hdrs, sizeof(dcc_xtra_hdrs) - 2); if (dcc_xtra_hdrs[Ustrlen(dcc_xtra_hdrs)-1] != '\n') - Ustrcat(dcc_xtra_hdrs, "\n"); + Ustrcat(dcc_xtra_hdrs, US"\n"); header_add(' ', "%s", dcc_xtra_hdrs); DEBUG(D_acl) debug_printf("DCC: adding additional headers in $acl_m_dcc_add_header: %s", dcc_xtra_hdrs); diff --git a/src/src/debug.c b/src/src/debug.c index eb62157de..de9796232 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -230,7 +230,7 @@ if (debug_ptr == debug_buffer) if (host_checking && debug_selector == 0) { - Ustrcpy(debug_ptr, ">>> "); + Ustrcpy(debug_ptr, US">>> "); debug_ptr += 4; } @@ -242,30 +242,33 @@ if (indent > 0) for (int i = indent >> 2; i > 0; i--) DEBUG(D_noutf8) { - Ustrcpy(debug_ptr, " !"); + Ustrcpy(debug_ptr, US" !"); debug_ptr += 4; /* 3 spaces + shriek */ debug_prefix_length += 4; } else { - Ustrcpy(debug_ptr, " " UTF8_VERT_2DASH); + Ustrcpy(debug_ptr, US" " UTF8_VERT_2DASH); debug_ptr += 6; /* 3 spaces + 3 UTF-8 octets */ debug_prefix_length += 6; } - Ustrncpy(debug_ptr, " ", indent &= 3); + Ustrncpy(debug_ptr, US" ", indent &= 3); debug_ptr += indent; debug_prefix_length += indent; } -/* Use the checked formatting routine to ensure that the buffer -does not overflow. Ensure there's space for a newline at the end. */ +/* Use the lengthchecked formatting routine to ensure that the buffer +does not overflow. Ensure there's space for a newline at the end. +However, use taint-unchecked routines for writing into the buffer +so that we can write tainted info into the static debug_buffer - +we trust that we will never expand the results. */ { gstring gs = { .size = (int)sizeof(debug_buffer) - 1, .ptr = debug_ptr - debug_buffer, .s = debug_buffer }; - if (!string_vformat(&gs, FALSE, format, ap)) + if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap)) { uschar * s = US"**** debug string too long - truncated ****\n"; uschar * p = gs.s + gs.ptr; diff --git a/src/src/deliver.c b/src/src/deliver.c index a597c9a88..ba9572e48 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -144,7 +144,7 @@ Returns: a pointer to an initialized address_item address_item * deliver_make_addr(uschar *address, BOOL copy) { -address_item *addr = store_get(sizeof(address_item)); +address_item *addr = store_get(sizeof(address_item), FALSE); *addr = address_defaults; if (copy) address = string_copy(address); addr->address = address; @@ -1023,7 +1023,8 @@ splitting is done; in those cases use the original field. */ else { - uschar * cmp = g->s + g->ptr; + uschar * cmp; + int off = g->ptr; /* start of the "full address" */ if (addr->local_part) { @@ -1045,6 +1046,7 @@ else of all, do a caseless comparison; if this succeeds, do a caseful comparison on the local parts. */ + cmp = g->s + off; /* only now, as rebuffer likely done */ string_from_gstring(g); /* ensure nul-terminated */ if ( strcmpic(cmp, topaddr->address) == 0 && Ustrncmp(cmp, topaddr->address, Ustrchr(cmp, '@') - cmp) == 0 @@ -1137,7 +1139,7 @@ void delivery_log(int flags, address_item * addr, int logchar, uschar * msg) { gstring * g; /* Used for a temporary, expanding buffer, for building log lines */ -void * reset_point; /* released afterwards. */ +rmark reset_point; /* Log the delivery on the main log. We use an extensible string to build up the log line, and reset the store afterwards. Remote deliveries should always @@ -1149,7 +1151,8 @@ pointer to a single host item in their host list, for use by the transport. */ lookup_dnssec_authenticated = NULL; #endif -g = reset_point = string_get(256); +reset_point = store_mark(); +g = string_get_tainted(256, TRUE); /* addrs will be tainted, so avoid copy */ if (msg) g = string_append(g, 2, host_and_ident(TRUE), US" "); @@ -1317,14 +1320,12 @@ static void deferral_log(address_item * addr, uschar * now, int logflags, uschar * driver_name, uschar * driver_kind) { -gstring * g; -void * reset_point; +rmark reset_point = store_mark(); +gstring * g = string_get(256); /* Build up the line that is used for both the message log and the main log. */ -g = reset_point = string_get(256); - /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ @@ -1396,8 +1397,8 @@ return; static void failure_log(address_item * addr, uschar * driver_kind, uschar * now) { -void * reset_point; -gstring * g = reset_point = string_get(256); +rmark reset_point = store_mark(); +gstring * g = string_get(256); #ifndef DISABLE_EVENT /* Message failures for which we will send a DSN get their event raised @@ -1790,7 +1791,7 @@ if (format) gstring * g; va_start(ap, format); - g = string_vformat(NULL, TRUE, CS format, ap); + g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS format, ap); va_end(ap); addr->message = string_from_gstring(g); } @@ -2052,10 +2053,10 @@ Returns: TRUE if previously delivered by the transport static BOOL previously_transported(address_item *addr, BOOL testing) { -(void)string_format(big_buffer, big_buffer_size, "%s/%s", +uschar * s = string_sprintf("%s/%s", addr->unique + (testflag(addr, af_homonym)? 3:0), addr->transport->name); -if (tree_search(tree_nonrecipients, big_buffer) != 0) +if (tree_search(tree_nonrecipients, s) != 0) { DEBUG(D_deliver|D_route|D_transport) debug_printf("%s was previously delivered (%s transport): discarded\n", @@ -2755,7 +2756,7 @@ while (addr_local) f.disable_logging = FALSE; /* Jic */ addr->message = addr->router ? string_sprintf("No transport set by %s router", addr->router->name) - : string_sprintf("No transport set by system filter"); + : US"No transport set by system filter"; post_process_one(addr, DEFER, logflags, EXIM_DTYPE_TRANSPORT, 0); continue; } @@ -3066,7 +3067,7 @@ while (addr_local) else for (addr2 = addr; addr2; addr2 = addr2->next) if (addr2->transport_return == OK) { - addr3 = store_get(sizeof(address_item)); + addr3 = store_get(sizeof(address_item), FALSE); *addr3 = *addr2; addr3->next = NULL; addr3->shadow_message = US &addr2->shadow_message; @@ -3464,7 +3465,7 @@ while (!done) if (!r || !(*ptr & rf_delete)) { - r = store_get(sizeof(retry_item)); + r = store_get(sizeof(retry_item), FALSE); r->next = addr->retries; addr->retries = r; r->flags = *ptr++; @@ -3647,7 +3648,7 @@ while (!done) if (*ptr) { - h = store_get(sizeof(host_item)); + h = store_get(sizeof(host_item), FALSE); h->name = string_copy(ptr); while (*ptr++); h->address = string_copy(ptr); @@ -4231,7 +4232,7 @@ set up, do so. */ if (!parlist) { - parlist = store_get(remote_max_parallel * sizeof(pardata)); + parlist = store_get(remote_max_parallel * sizeof(pardata), FALSE); for (poffset = 0; poffset < remote_max_parallel; poffset++) parlist[poffset].pid = 0; } @@ -5117,7 +5118,7 @@ where they are locally interpreted. [The new draft "821" is more explicit on this, Jan 1999.] We know the syntax is valid, so this can be done by simply removing quoting backslashes and any unquoted doublequotes. */ -t = addr->cc_local_part = store_get(len+1); +t = addr->cc_local_part = store_get(len+1, is_tainted(address)); while(len-- > 0) { int c = *address++; @@ -5160,7 +5161,7 @@ if (percent_hack_domains) if (new_address) { - address_item *new_parent = store_get(sizeof(address_item)); + address_item *new_parent = store_get(sizeof(address_item), FALSE); *new_parent = *addr; addr->parent = new_parent; new_parent->child_count = 1; @@ -6028,8 +6029,8 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) if (addr_new) { - int uid = (system_filter_uid_set)? system_filter_uid : geteuid(); - int gid = (system_filter_gid_set)? system_filter_gid : getegid(); + int uid = system_filter_uid_set ? system_filter_uid : geteuid(); + int gid = system_filter_gid_set ? system_filter_gid : getegid(); /* The text "system-filter" is tested in transport_set_up_command() and in set_up_shell_command() in the pipe transport, to enable them to permit @@ -6103,6 +6104,9 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) if (!tmp) p->message = string_sprintf("failed to expand \"%s\" as a " "system filter transport name", tpname); + if (is_tainted(tmp)) + p->message = string_sprintf("attempt to used tainted value '%s' for" + "transport '%s' as a system filter", tmp, tpname); tpname = tmp; } else @@ -6411,10 +6415,8 @@ while (addr_new) /* Loop until all addresses dealt with */ keep piling '>' characters on the front. */ if (addr->address[0] == '>') - { while (tree_search(tree_duplicates, addr->unique)) addr->unique = string_sprintf(">%s", addr->unique); - } else if ((tnode = tree_search(tree_duplicates, addr->unique))) { @@ -6822,8 +6824,8 @@ while (addr_new) /* Loop until all addresses dealt with */ &addr_succeed, v_none)) == DEFER) retry_add_item(addr, addr->router->retry_use_local_part - ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) - : string_sprintf("R:%s", addr->domain), + ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) + : string_sprintf("R:%s", addr->domain), 0); /* Otherwise, if there is an existing retry record in the database, add @@ -7318,7 +7320,7 @@ for (address_item * a = addr_succeed; a; a = a->next) { /* copy and relink address_item and send report with all of them at once later */ address_item * addr_next = addr_senddsn; - addr_senddsn = store_get(sizeof(address_item)); + addr_senddsn = store_get(sizeof(address_item), FALSE); *addr_senddsn = *a; addr_senddsn->next = addr_next; } diff --git a/src/src/dkim.c b/src/src/dkim.c index 715774c7e..a410ed55b 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -43,8 +43,12 @@ static const uschar * dkim_collect_error = NULL; uschar * dkim_exim_query_dns_txt(uschar * name) { +/*XXX need to always alloc the dnsa, from tainted mem. +Then, we hope, the answers will be tainted */ + dns_answer dnsa; dns_scan dnss; +rmark reset_point = store_mark(); gstring * g = NULL; lookup_dnssec_authenticated = NULL; @@ -84,7 +88,7 @@ for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); } bad: -if (g) store_reset(g); +store_reset(reset_point); return NULL; /*XXX better error detail? logging? */ } diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 5bf33032a..be9c4b384 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -398,11 +398,11 @@ if (!dmarc_abort && !sender_host_authenticated) /* Can't use exim's string manipulation functions so allocate memory for libopendmarc using its max hostname length definition. */ - dmarc_domain = US calloc(DMARC_MAXHOSTNAMELEN, sizeof(uschar)); + dmarc_domain = store_get(DMARC_MAXHOSTNAMELEN, TRUE); libdm_status = opendmarc_policy_fetch_utilized_domain(dmarc_pctx, dmarc_domain, DMARC_MAXHOSTNAMELEN-1); - dmarc_used_domain = string_copy(dmarc_domain); - free(dmarc_domain); + store_release_above(dmarc_domain + Ustrlen(dmarc_domain)+1); + dmarc_used_domain = dmarc_domain; if (libdm_status != DMARC_PARSE_OKAY) log_write(0, LOG_MAIN|LOG_PANIC, diff --git a/src/src/dns.c b/src/src/dns.c index 6ef6b7784..e3845978c 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -40,7 +40,7 @@ fakens_search(const uschar *domain, int type, uschar *answerptr, int size) { int len = Ustrlen(domain); int asize = size; /* Locally modified */ -uschar name[256]; +uschar * name; uschar utilname[256]; uschar *aptr = answerptr; /* Locally modified */ struct stat statbuf; @@ -48,8 +48,7 @@ struct stat statbuf; /* Remove terminating dot. */ if (domain[len - 1] == '.') len--; -Ustrncpy(name, domain, len); -name[len] = 0; +name = string_copyn(domain, len); /* Look for the fakens utility, and if it exists, call it. */ @@ -249,7 +248,7 @@ if (Ustrchr(string, ':') == NULL) *pp++ = '.'; p = ppp - 1; } - Ustrcpy(pp, "in-addr.arpa"); + Ustrcpy(pp, US"in-addr.arpa"); } /* Handle IPv6 address; convert to binary so as to fill out any @@ -268,7 +267,7 @@ else for (int i = 3; i >= 0; i--) for (int j = 0; j < 32; j += 4) pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15); - Ustrcpy(pp, "ip6.arpa."); + Ustrcpy(pp, US"ip6.arpa."); /* Another way of doing IPv6 reverse lookups was proposed in conjunction with A6 records. However, it fell out of favour when they did. The @@ -287,7 +286,7 @@ else sprintf(pp, "%08X", v6[i]); pp += 8; } - Ustrcpy(pp, "].ip6.arpa."); + Ustrcpy(pp, US"].ip6.arpa."); **************************************************/ } @@ -615,7 +614,7 @@ Returns: the return code static int dns_return(const uschar * name, int type, int rc) { -tree_node *node = store_get_perm(sizeof(tree_node) + 290); +tree_node *node = store_get_perm(sizeof(tree_node) + 290, TRUE); dns_fail_tag(node->name, name, type); node->data.val = rc; (void)tree_insertnode(&tree_dns_fails, node); @@ -947,7 +946,8 @@ for (int i = 0; i <= dns_cname_loops; i++) if (!cname_rr.data) return DNS_FAIL; - data = store_get(256); + /* DNS data comes from the outside, hence tainted */ + data = store_get(256, TRUE); if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0) return DNS_FAIL; @@ -1201,7 +1201,8 @@ if (rr->type == T_A) uschar *p = US rr->data; if (p + 4 <= dnsa_lim) { - yield = store_get(sizeof(dns_address) + 20); + /* the IP is not regarded as tainted */ + yield = store_get(sizeof(dns_address) + 20, FALSE); (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); yield->next = NULL; } @@ -1215,7 +1216,7 @@ else { struct in6_addr in6; for (int i = 0; i < 16; i++) in6.s6_addr[i] = rr->data[i]; - yield = store_get(sizeof(dns_address) + 50); + yield = store_get(sizeof(dns_address) + 50, FALSE); inet_ntop(AF_INET6, &in6, CS yield->address, 50); yield->next = NULL; } diff --git a/src/src/drtables.c b/src/src/drtables.c index b7024297d..059756284 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -509,7 +509,7 @@ static struct lookupmodulestr *lookupmodules = NULL; static void addlookupmodule(void *dl, struct lookup_module_info *info) { -struct lookupmodulestr *p = store_malloc(sizeof(struct lookupmodulestr)); +struct lookupmodulestr *p = store_get(sizeof(struct lookupmodulestr), FALSE); p->dl = dl; p->info = info; @@ -620,12 +620,12 @@ init_lookup_list(void) int countmodules = 0; int moduleerrors = 0; #endif - struct lookupmodulestr *p; static BOOL lookup_list_init_done = FALSE; - + rmark reset_point; if (lookup_list_init_done) return; + reset_point = store_mark(); lookup_list_init_done = TRUE; #if defined(LOOKUP_CDB) && LOOKUP_CDB!=2 @@ -787,17 +787,10 @@ init_lookup_list(void) memset(lookup_list, 0, sizeof(lookup_info *) * lookup_list_count); /* now add all lookups to the real list */ - p = lookupmodules; - while (p) { - struct lookupmodulestr *pnext; - + for (struct lookupmodulestr * p = lookupmodules; p; p = p->next) for (int j = 0; j < p->info->lookupcount; j++) add_lookup_to_list(p->info->lookups[j]); - - pnext = p->next; - store_free(p); - p = pnext; - } + store_reset(reset_point); /* just to be sure */ lookupmodules = NULL; } diff --git a/src/src/dummies.c b/src/src/dummies.c index 2e1ad11f5..197415f0c 100644 --- a/src/src/dummies.c +++ b/src/src/dummies.c @@ -20,7 +20,7 @@ alternates. */ /* We don't have the full Exim headers dragged in, but this function is used for debugging output. */ -extern gstring * string_vformat(gstring *, BOOL, const char *, va_list); +extern gstring * string_vformat(gstring *, unsigned, const char *, va_list); /************************************************* @@ -56,7 +56,9 @@ flags = flags; * Handle calls to print debug output * *************************************************/ -/* The message just gets written to stderr +/* The message just gets written to stderr. +We use tainted memory to format into just so that we can handle +tainted arguments. Arguments: format a printf() format @@ -69,12 +71,12 @@ void debug_printf(char *format, ...) { va_list ap; -gstring * g = string_get(1024); -void * reset_point = g; +rmark reset_point = store_mark(); +gstring * g = string_get_tainted(1024, TRUE); va_start(ap, format); -if (!string_vformat(g, FALSE, format, ap)) +if (!string_vformat(g, 0, format, ap)) { char * s = "**** debug string overflowed buffer ****\n"; char * p = CS g->s + g->ptr; diff --git a/src/src/environment.c b/src/src/environment.c index 9d3d126a6..c29cc6c8d 100644 --- a/src/src/environment.c +++ b/src/src/environment.c @@ -37,6 +37,8 @@ if (!keep_environment || *keep_environment == '\0') } else if (Ustrcmp(keep_environment, "*") != 0) + { + rmark reset_point = store_mark(); if (environ) for (uschar ** p = USS environ; *p; /* see below */) { /* It's considered broken if we do not find the '=', according to @@ -53,17 +55,18 @@ else if (Ustrcmp(keep_environment, "*") != 0) if (os_unsetenv(name) < 0) return FALSE; else p = USS environ; /* RESTART from the beginning */ else p++; - store_reset(name); } } + store_reset(reset_point); + } if (add_environment) { - uschar * p; - int sep = 0; - const uschar * envlist = add_environment; + uschar * p; + int sep = 0; + const uschar * envlist = add_environment; - while ((p = string_nextinlist(&envlist, &sep, NULL, 0))) putenv(CS p); + while ((p = string_nextinlist(&envlist, &sep, NULL, 0))) putenv(CS p); } - return TRUE; +return TRUE; } diff --git a/src/src/exim.c b/src/src/exim.c index 7571705d7..f163b1249 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -42,7 +42,9 @@ regular expression for a long time; the other for short-term use. */ static void * function_store_get(size_t size) { -return store_get((int)size); +/* For now, regard all RE results as potentially tainted. We might need +more intelligence on this point. */ +return store_get((int)size, TRUE); } static void @@ -181,7 +183,7 @@ va_list ap; g = string_fmt_append(&gs, "%5d ", (int)getpid()); len = g->ptr; va_start(ap, format); -if (!string_vformat(g, FALSE, format, ap)) +if (!string_vformat(g, 0, format, ap)) { gs.ptr = len; g = string_cat(&gs, US"**** string overflowed buffer ****"); @@ -502,7 +504,7 @@ for (int i = 0; i <= 2; i++) { if (devnull < 0) devnull = open("/dev/null", O_RDWR); if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", - string_open_failed(errno, "/dev/null")); + string_open_failed(errno, "/dev/null", NULL)); if (devnull != i) (void)dup2(devnull, i); } } @@ -666,6 +668,7 @@ void exim_exit(int rc, const uschar * process) { search_tidyup(); +store_exit(); DEBUG(D_any) debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d " ">>>>>>>>>>>>>>>>\n", (int)getpid(), @@ -674,6 +677,14 @@ exit(rc); } +void +exim_underbar_exit(int rc) +{ +store_exit(); +_exit(rc); +} + + /* Print error string, then die */ static void @@ -1254,10 +1265,10 @@ for (int i = 0;; i++) #ifdef USE_READLINE char *readline_line = NULL; - if (fn_readline != NULL) + if (fn_readline) { - if ((readline_line = fn_readline((i > 0)? "":"> ")) == NULL) break; - if (*readline_line != 0 && fn_addhist != NULL) fn_addhist(readline_line); + if (!(readline_line = fn_readline((i > 0)? "":"> "))) break; + if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line); p = US readline_line; } else @@ -1276,9 +1287,7 @@ for (int i = 0;; i++) while (ss > p && isspace(ss[-1])) ss--; if (i > 0) - { while (p < ss && isspace(*p)) p++; /* leading space after cont */ - } g = string_catn(g, p, ss - p); @@ -1375,7 +1384,7 @@ if ( ! ((real_uid == root_uid) } /* Get a list of macros which are whitelisted */ -whitelisted = string_copy_malloc(US WHITELIST_D_MACROS); +whitelisted = string_copy_perm(US WHITELIST_D_MACROS, FALSE); prev_char_item = FALSE; white_count = 0; for (p = whitelisted; *p != '\0'; ++p) @@ -1562,7 +1571,7 @@ uschar *malware_test_file = NULL; uschar *real_sender_address; uschar *originator_home = US"/"; size_t sz; -void *reset_point; +rmark reset_point; struct passwd *pw; struct stat statbuf; @@ -1691,6 +1700,7 @@ big_buffer = store_malloc(big_buffer_size); /* Set up the handler for the data request signal, and set the initial descriptive text. */ +process_info = store_get(PROCESS_INFO_SIZE, TRUE); /* tainted */ set_process_info("initializing"); os_restarting_signal(SIGUSR1, usr1_handler); @@ -2318,7 +2328,7 @@ for (i = 1; i < argc; i++) else { /* Well, the trust list at least is up to scratch... */ - void *reset_point = store_get(0); + rmark reset_point = store_mark(); uschar *trusted_configs[32]; int nr_configs = 0; int i = 0; @@ -2348,30 +2358,22 @@ for (i = 1; i < argc; i++) &sep, big_buffer, big_buffer_size)) != NULL) { for (i=0; i < nr_configs; i++) - { if (Ustrcmp(filename, trusted_configs[i]) == 0) break; - } if (i == nr_configs) { f.trusted_config = FALSE; break; } } - store_reset(reset_point); } - else - { - /* No valid prefixes found in trust_list file. */ + else /* No valid prefixes found in trust_list file. */ f.trusted_config = FALSE; - } + store_reset(reset_point); } } - else - { - /* Could not open trust_list file. */ + else /* Could not open trust_list file. */ f.trusted_config = FALSE; - } } #else /* Not root; don't trust config */ @@ -2426,8 +2428,8 @@ for (i = 1; i < argc; i++) if (clmacro_count >= MAX_CLMACROS) exim_fail("exim: too many -D options on command line\n"); - clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name, - m->replacement); + clmacros[clmacro_count++] = + string_sprintf("-D%s=%s", m->name, m->replacement); } #endif break; @@ -2537,7 +2539,7 @@ for (i = 1; i < argc; i++) { badarg = TRUE; break; } } if (*argrest == 0) - sender_address = string_sprintf(""); /* Ensure writeable memory */ + *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */ else { uschar *temp = argrest + Ustrlen(argrest) - 1; @@ -2550,6 +2552,7 @@ for (i = 1; i < argc; i++) #endif sender_address = parse_extract_address(argrest, &errmess, &dummy_start, &dummy_end, &sender_address_domain, TRUE); + sender_address = string_copy_taint(sender_address, TRUE); #ifdef SUPPORT_I18N message_smtputf8 = string_is_utf8(sender_address); allow_utf8_domains = FALSE; @@ -2995,11 +2998,13 @@ for (i = 1; i < argc; i++) /* -oMas: setting authenticated sender */ - else if (Ustrcmp(argrest, "Mas") == 0) authenticated_sender = argv[++i]; + else if (Ustrcmp(argrest, "Mas") == 0) + authenticated_sender = string_copy_taint(argv[++i], TRUE); /* -oMai: setting authenticated id */ - else if (Ustrcmp(argrest, "Mai") == 0) authenticated_id = argv[++i]; + else if (Ustrcmp(argrest, "Mai") == 0) + authenticated_id = string_copy_taint(argv[++i], TRUE); /* -oMi: Set incoming interface address */ @@ -3027,7 +3032,8 @@ for (i = 1; i < argc; i++) /* -oMs: Set sender host name */ - else if (Ustrcmp(argrest, "Ms") == 0) sender_host_name = argv[++i]; + else if (Ustrcmp(argrest, "Ms") == 0) + sender_host_name = string_copy_taint(argv[++i], TRUE); /* -oMt: Set sender ident */ @@ -3933,7 +3939,7 @@ if ( (debug_selector & D_any || LOGGING(arguments)) && f.really_exim && !list_options && !checking) { uschar *p = big_buffer; - Ustrcpy(p, "cwd= (failed)"); + Ustrcpy(p, US"cwd= (failed)"); if (!initial_cwd) p += 13; @@ -3955,9 +3961,9 @@ if ( (debug_selector & D_any || LOGGING(arguments)) uschar *quote; if (p + len + 8 >= big_buffer + big_buffer_size) { - Ustrcpy(p, " ..."); + Ustrcpy(p, US" ..."); log_write(0, LOG_MAIN, "%s", big_buffer); - Ustrcpy(big_buffer, "..."); + Ustrcpy(big_buffer, US"..."); p = big_buffer + 3; } printing = string_printing(argv[i]); @@ -4290,15 +4296,6 @@ needed in transports so we lost the optimisation. */ readconf_rest(); -/* The configuration data will have been read into POOL_PERM because we won't -ever want to reset back past it. Change the current pool to POOL_MAIN. In fact, -this is just a bit of pedantic tidiness. It wouldn't really matter if the -configuration were read into POOL_MAIN, because we don't do any resets till -later on. However, it seems right, and it does ensure that both pools get used. -*/ - -store_pool = POOL_MAIN; - /* Handle the -brt option. This is for checking out retry configurations. The next three arguments are a domain name or a complete address, and optionally two error numbers. All it does is to call the function that @@ -4495,7 +4492,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) else if ((pid = fork()) == 0) { (void)deliver_message(argv[i], forced_delivery, deliver_give_up); - _exit(EXIT_SUCCESS); + exim_underbar_exit(EXIT_SUCCESS); } else if (pid < 0) { @@ -4664,8 +4661,8 @@ if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0) the caller. This will get overwritten below for an inetd call. If a trusted caller has set it empty, unset it. */ -if (sender_ident == NULL) sender_ident = originator_login; - else if (sender_ident[0] == 0) sender_ident = NULL; +if (!sender_ident) sender_ident = originator_login; +else if (!*sender_ident) sender_ident = NULL; /* Handle the -brw option, which is for checking out rewriting rules. Cause log writes (on errors) to go to stderr instead. Can't do this earlier, as want the @@ -4687,8 +4684,8 @@ if (test_rewrite_arg >= 0) unless a trusted caller supplies a sender address with -f, or is passing in the message via SMTP (inetd invocation or otherwise). */ -if ((sender_address == NULL && !smtp_input) || - (!f.trusted_caller && filter_test == FTEST_NONE)) +if ( !sender_address && !smtp_input + || !f.trusted_caller && filter_test == FTEST_NONE) { f.sender_local = TRUE; @@ -4696,10 +4693,10 @@ if ((sender_address == NULL && !smtp_input) || via -oMas and -oMai and if so, they will already be set. Otherwise, force defaults except when host checking. */ - if (authenticated_sender == NULL && !host_checking) + if (!authenticated_sender && !host_checking) authenticated_sender = string_sprintf("%s@%s", originator_login, qualify_domain_sender); - if (authenticated_id == NULL && !host_checking) + if (!authenticated_id && !host_checking) authenticated_id = originator_login; } @@ -4709,8 +4706,8 @@ is specified is the empty address. However, if a trusted caller does not specify a sender address for SMTP input, we leave sender_address unset. This causes the MAIL commands to be honoured. */ -if ((!smtp_input && sender_address == NULL) || - !receive_check_set_sender(sender_address)) +if ( !smtp_input && !sender_address + || !receive_check_set_sender(sender_address)) { /* Either the caller is not permitted to set a general sender, or this is non-SMTP input and the trusted caller has not set a sender. If there is no @@ -4736,8 +4733,7 @@ f.sender_set_untrusted = sender_address != originator_login && !f.trusted_caller address, which indicates an error message, or doesn't exist (root caller, smtp interface, no -f argument). */ -if (sender_address != NULL && sender_address[0] != 0 && - sender_address_domain == 0) +if (sender_address && *sender_address && sender_address_domain == 0) sender_address = string_sprintf("%s@%s", local_part_quote(sender_address), qualify_domain_sender); @@ -4927,7 +4923,7 @@ if (host_checking) it. The code works for both IPv4 and IPv6, as it happens. */ size = host_aton(sender_host_address, x); - sender_host_address = store_get(48); /* large enough for full IPv6 */ + sender_host_address = store_get(48, FALSE); /* large enough for full IPv6 */ (void)host_nmtoa(size, x, -1, sender_host_address, ':'); /* Now set up for testing */ @@ -4957,7 +4953,7 @@ if (host_checking) if (smtp_start_session()) { - for (reset_point = store_get(0); ; store_reset(reset_point)) + for (; (reset_point = store_mark()); store_reset(reset_point)) { if (smtp_setup_msg() <= 0) break; if (!receive_msg(FALSE)) break; @@ -5200,7 +5196,6 @@ if (!f.synchronous_delivery) /* Save the current store pool point, for resetting at the start of each message, and save the real sender address, if any. */ -reset_point = store_get(0); real_sender_address = sender_address; /* Loop to receive messages; receive_msg() returns TRUE if there are more @@ -5209,6 +5204,7 @@ collapsed). */ while (more) { + reset_point = store_mark(); message_id[0] = 0; /* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP @@ -5358,7 +5354,7 @@ while (more) } } - receive_add_recipient(recipient, -1); + receive_add_recipient(string_copy_taint(recipient, TRUE), -1); s = ss; if (!finished) while (*(++s) != 0 && (*s == ',' || isspace(*s))); @@ -5580,8 +5576,8 @@ while (more) rc = deliver_message(message_id, FALSE, FALSE); search_tidyup(); - _exit((!mua_wrapper || rc == DELIVER_MUA_SUCCEEDED)? - EXIT_SUCCESS : EXIT_FAILURE); + exim_underbar_exit(!mua_wrapper || rc == DELIVER_MUA_SUCCEEDED + ? EXIT_SUCCESS : EXIT_FAILURE); } if (pid < 0) diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 39207be46..311d961e8 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -34,10 +34,10 @@ uschar * spool_directory = NULL; /* dummy for dbstuff.h */ /* dummies needed by Solaris build */ void * -store_get_3(int size, const char *filename, int linenumber) +store_get_3(int size, BOOL tainted, const char *filename, int linenumber) { return NULL; } -void -store_reset_3(void *ptr, const char *filename, int linenumber) +void ** +store_reset_3(void **ptr, int pool, const char *filename, int linenumber) { } @@ -213,14 +213,14 @@ if (strlen(argv[arg+1]) > sizeof(temp_dbmname) - 20) exit(1); } -Ustrcpy(temp_dbmname, argv[arg+1]); -Ustrcat(temp_dbmname, ".dbmbuild_temp"); +Ustrcpy(temp_dbmname, US argv[arg+1]); +Ustrcat(temp_dbmname, US".dbmbuild_temp"); Ustrcpy(dirname, temp_dbmname); if ((bptr = Ustrrchr(dirname, '/'))) *bptr = '\0'; else - Ustrcpy(dirname, "."); + Ustrcpy(dirname, US"."); /* It is apparently necessary to open with O_RDWR for this to work with gdbm-1.7.3, though no reading is actually going to be done. */ @@ -441,7 +441,7 @@ if (yield == 0 || yield == 1) #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) Ustrcpy(real_dbmname, temp_dbmname); - Ustrcpy(buffer, argv[arg+1]); + Ustrcpy(buffer, US argv[arg+1]); if (Urename(real_dbmname, buffer) != 0) { printf("Unable to rename %s as %s\n", real_dbmname, buffer); diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 2c7aad63b..8b71a4140 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -376,7 +376,7 @@ dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length) void *yield; EXIM_DATUM key_datum, result_datum; int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen); +uschar * key_copy = store_get(klen, is_tainted(key)); memcpy(key_copy, key, klen); @@ -387,7 +387,10 @@ EXIM_DATUM_SIZE(key_datum) = klen; if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; -yield = store_get(EXIM_DATUM_SIZE(result_datum)); +/* Assume for now that anything stored could have been tainted. Properly +we should store the taint status along with the data. */ + +yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE); memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum); @@ -420,7 +423,7 @@ dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length) EXIM_DATUM key_datum, value_datum; dbdata_generic *gptr = (dbdata_generic *)ptr; int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen); +uschar * key_copy = store_get(klen, is_tainted(key)); memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); @@ -452,7 +455,7 @@ int dbfn_delete(open_db *dbblock, const uschar *key) { int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen); +uschar * key_copy = store_get(klen, is_tainted(key)); memcpy(key_copy, key, klen); EXIM_DATUM key_datum; @@ -551,6 +554,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); uschar *t; uschar name[MESSAGE_ID_LENGTH + 1]; void *value; + rmark reset_point = store_mark(); /* Keep a copy of the key separate, as in some DBM's the pointer is into data which might change. */ @@ -684,8 +688,8 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor); printf(" %s %.*s\n", keybuffer, length, session->session); break; } - store_reset(value); } + store_reset(reset_point); } dbfn_close(dbm); @@ -735,7 +739,7 @@ int dbdata_type; uschar **argv = USS cargv; uschar buffer[256]; uschar name[256]; -void *reset_point = store_get(0); +rmark reset_point; name[0] = 0; /* No name set */ @@ -745,7 +749,7 @@ user requests */ dbdata_type = check_args(argc, argv, US"fixdb", US""); printf("Modifying Exim hints database %s/db/%s\n", argv[1], argv[2]); -for(;;) +for(; (reset_point = store_mark()); store_reset(reset_point)) { open_db dbblock; open_db *dbm; @@ -760,8 +764,6 @@ for(;;) uschar *t; uschar field[256], value[256]; - store_reset(reset_point); - printf("> "); if (Ufgets(buffer, 256, stdin) == NULL) break; @@ -1100,7 +1102,7 @@ struct stat statbuf; int maxkeep = 30 * 24 * 60 * 60; int dbdata_type, i, oldest, path_len; key_item *keychain = NULL; -void *reset_point; +rmark reset_point; open_db dbblock; open_db *dbm; EXIM_CURSOR *cursor; @@ -1173,7 +1175,7 @@ for (key = dbfn_scan(dbm, TRUE, &cursor); key; key = dbfn_scan(dbm, FALSE, &cursor)) { - key_item *k = store_get(sizeof(key_item) + Ustrlen(key)); + key_item *k = store_get(sizeof(key_item) + Ustrlen(key), is_tainted(key)); k->next = keychain; keychain = k; Ustrcpy(k->key, key); @@ -1182,13 +1184,10 @@ for (key = dbfn_scan(dbm, TRUE, &cursor); /* Now scan the collected keys and operate on the records, resetting the store each time round. */ -reset_point = store_get(0); - -while (keychain) +for (; keychain && (reset_point = store_mark()); store_reset(reset_point)) { dbdata_generic *value; - store_reset(reset_point); key = keychain->key; keychain = keychain->next; value = dbfn_read_with_length(dbm, key, NULL); diff --git a/src/src/expand.c b/src/src/expand.c index 65c585d1c..1bfc75d2b 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1856,22 +1856,17 @@ switch (vp->type) return sender_host_name ? sender_host_name : US""; case vtype_localpart: /* Get local part from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; - domain = Ustrrchr(s, '@'); - if (domain == NULL) return s; + if (!(s = *((uschar **)(val)))) return US""; + if (!(domain = Ustrrchr(s, '@'))) return s; if (domain - s > sizeof(var_buffer) - 1) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT " in string expansion", sizeof(var_buffer)); - Ustrncpy(var_buffer, s, domain - s); - var_buffer[domain - s] = 0; - return var_buffer; + return string_copyn(s, domain - s); case vtype_domain: /* Get domain from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; + if (!(s = *((uschar **)(val)))) return US""; domain = Ustrrchr(s, '@'); - return (domain == NULL)? US"" : domain + 1; + return domain ? domain + 1 : US""; case vtype_msgheaders: return find_header(NULL, newsize, exists_only ? FH_EXISTS_ONLY : 0, NULL); @@ -2354,7 +2349,7 @@ switch(cond_type = identify_operator(&s, &opname)) return NULL; } - s = read_name(name, 256, s+1, US"_"); + s = read_name(name, sizeof(name), s+1, US"_"); /* Test for a header's existence. If the name contains a closing brace character, this may be a user error where the terminating colon has been @@ -2366,7 +2361,7 @@ switch(cond_type = identify_operator(&s, &opname)) && (*++t == '_' || Ustrncmp(t, "eader_", 6) == 0) ) { - s = read_header_name(name, 256, s); + s = read_header_name(name, sizeof(name), s); /* {-for-text-editors */ if (Ustrchr(name, '}') != NULL) malformed_header = TRUE; if (yield) *yield = @@ -2380,9 +2375,9 @@ switch(cond_type = identify_operator(&s, &opname)) { if (!(t = find_variable(name, TRUE, yield == NULL, NULL))) { - expand_string_message = (name[0] == 0)? - string_sprintf("variable name omitted after \"def:\"") : - string_sprintf("unknown variable \"%s\" after \"def:\"", name); + expand_string_message = name[0] + ? string_sprintf("unknown variable \"%s\" after \"def:\"", name) + : US"variable name omitted after \"def:\""; check_variable_error_message(name); return NULL; } @@ -3135,7 +3130,7 @@ switch(cond_type = identify_operator(&s, &opname)) return NULL; } - DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, iterate_item); + DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", opname, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) { expand_string_message = string_sprintf("%s inside \"%s\" condition", @@ -3570,7 +3565,7 @@ Returns: pointer to string containing the last three static uschar * prvs_daystamp(int day_offset) { -uschar *days = store_get(32); /* Need at least 24 for cases */ +uschar *days = store_get(32, FALSE); /* Need at least 24 for cases */ (void)string_format(days, 32, TIME_T_FMT, /* where TIME_T_FMT is %lld */ (time(NULL) + day_offset*86400)/86400); return (Ustrlen(days) >= 3) ? &days[Ustrlen(days)-3] : US"100"; @@ -3607,7 +3602,7 @@ uschar innerhash[20]; uschar finalhash[20]; uschar innerkey[64]; uschar outerkey[64]; -uschar *finalhash_hex = store_get(40); +uschar *finalhash_hex; if (key_num == NULL) key_num = US"0"; @@ -3640,7 +3635,9 @@ chash_start(HMAC_SHA1, &h); chash_mid(HMAC_SHA1, &h, outerkey); chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash); -p = finalhash_hex; +/* Hashing is deemed sufficient to de-taint any input data */ + +p = finalhash_hex = store_get(40, FALSE); for (int i = 0; i < 3; i++) { *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; @@ -4100,6 +4097,7 @@ static uschar * expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, BOOL skipping, BOOL honour_dollar, BOOL *resetok_p) { +rmark reset_point = store_mark(); gstring * yield = string_get(Ustrlen(string) + 64); int item_type; const uschar *s = string; @@ -4122,6 +4120,14 @@ DEBUG(D_expand) f.expand_string_forcedfail = FALSE; expand_string_message = US""; +if (is_tainted(string)) + { + expand_string_message = + string_sprintf("attempt to expand tainted string '%s'", s); + log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); + goto EXPAND_FAILED; + } + while (*s != 0) { uschar *value; @@ -4193,12 +4199,13 @@ while (*s != 0) buffer. */ if (!yield) - g = store_get(sizeof(gstring)); + g = store_get(sizeof(gstring), FALSE); else if (yield->ptr == 0) { - if (resetok) store_reset(yield); + if (resetok) reset_point = store_reset(reset_point); yield = NULL; - g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */ + reset_point = store_mark(); + g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ } /* Header */ @@ -5380,7 +5387,7 @@ while (*s != 0) { if (sigalrm_seen || runrc == -256) { - expand_string_message = string_sprintf("command timed out"); + expand_string_message = US"command timed out"; killpg(pid, SIGKILL); /* Kill the whole process group */ } @@ -5922,8 +5929,8 @@ while (*s != 0) while (isspace(*s)) s++; if (*s != ':') { - expand_string_message = string_sprintf( - "missing object value-separator for extract json"); + expand_string_message = + US"missing object value-separator for extract json"; goto EXPAND_FAILED_CURLY; } s++; @@ -6421,8 +6428,8 @@ while (*s != 0) } xtract = s; - tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); - if (!tmp) goto EXPAND_FAILED; + if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok))) + goto EXPAND_FAILED; xtract = string_copyn(xtract, s - xtract); if (*s++ != '}') @@ -6484,6 +6491,7 @@ while (*s != 0) newlist = string_append_listele(newlist, sep, dstitem); newkeylist = string_append_listele(newkeylist, sep, dstfield); +/*XXX why field-at-a-time copy? Why not just dup the rest of the list? */ while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) { if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) @@ -6579,7 +6587,7 @@ while (*s != 0) log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); goto EXPAND_FAILED; } - t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0])); + t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]), is_tainted(argv[0])); Ustrcpy(t->name, argv[0]); t->data.ptr = handle; (void)tree_insertnode(&dlobj_anchor, t); @@ -7051,7 +7059,7 @@ while (*s != 0) case 'h': t = tree_search(hostlist_anchor, sub); suffix = US"_h"; break; case 'l': t = tree_search(localpartlist_anchor, sub); suffix = US"_l"; break; default: - expand_string_message = string_sprintf("bad suffix on \"list\" operator"); + expand_string_message = US"bad suffix on \"list\" operator"; goto EXPAND_FAILED; } @@ -7875,12 +7883,13 @@ while (*s != 0) gstring * g = NULL; if (!yield) - g = store_get(sizeof(gstring)); + g = store_get(sizeof(gstring), FALSE); else if (yield->ptr == 0) { - if (resetok) store_reset(yield); + if (resetok) reset_point = store_reset(reset_point); yield = NULL; - g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */ + reset_point = store_mark(); + g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ } if (!(value = find_variable(name, FALSE, skipping, &newsize))) { @@ -7934,15 +7943,20 @@ if (left) *left = s; In many cases the final string will be the first one that was got and so there will be optimal store usage. */ -if (resetok) store_reset(yield->s + (yield->size = yield->ptr + 1)); +if (resetok) gstring_release_unused(yield); else if (resetok_p) *resetok_p = FALSE; DEBUG(D_expand) + { + BOOL tainted = is_tainted(yield->s); DEBUG(D_noutf8) { debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); debug_printf_indent("%sresult: %s\n", skipping ? "|-----" : "\\_____", yield->s); + if (tainted) + debug_printf_indent("%s \\__(tainted)\n", + skipping ? "| " : " "); if (skipping) debug_printf_indent("\\___skipping: result is not used\n"); } @@ -7951,15 +7965,19 @@ DEBUG(D_expand) debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ "expanding: %.*s\n", (int)(s - string), string); - debug_printf_indent("%s" - UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "result: %s\n", skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, yield->s); + if (tainted) + debug_printf_indent("%s(tainted)\n", + skipping + ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); if (skipping) debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "skipping: result is not used\n"); } + } expand_level--; return yield->s; @@ -8457,15 +8475,15 @@ if (opt_perl_startup != NULL) } #endif /* EXIM_PERL */ +/* Thie deliberately regards the input as untainted, so that it can be +expanded; only reasonable since this is a test for string-expansions. */ + while (fgets(buffer, sizeof(buffer), stdin) != NULL) { - void *reset_point = store_get(0); + rmark reset_point = store_mark(); uschar *yield = expand_string(buffer); - if (yield != NULL) - { + if (yield) printf("%s\n", yield); - store_reset(reset_point); - } else { if (f.search_find_defer) printf("search_find deferred\n"); @@ -8473,6 +8491,7 @@ while (fgets(buffer, sizeof(buffer), stdin) != NULL) if (f.expand_string_forcedfail) printf("Forced failure\n"); printf("\n"); } + store_reset(reset_point); } search_tidyup(); diff --git a/src/src/filter.c b/src/src/filter.c index a16416c68..3da616700 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -435,7 +435,7 @@ for (;;) if (*ptr == 0) { - *error_pointer = string_sprintf("\"then\" missing at end of filter file"); + *error_pointer = US"\"then\" missing at end of filter file"; break; } @@ -497,7 +497,7 @@ for (;;) /* Build a condition block from the specific word. */ - c = store_get(sizeof(condition_block)); + c = store_get(sizeof(condition_block), FALSE); c->left.u = c->right.u = NULL; c->testfor = testfor; testfor = TRUE; @@ -527,7 +527,7 @@ for (;;) } ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE); if (*error_pointer) break; - aa = store_get(sizeof(string_item)); + aa = store_get(sizeof(string_item), FALSE); aa->text = string_copy(buffer); aa->next = c->left.a; c->left.a = aa; @@ -678,7 +678,7 @@ for (;;) else if (Ustrcmp(buffer, "and") == 0) { - condition_block *andc = store_get(sizeof(condition_block)); + condition_block *andc = store_get(sizeof(condition_block), FALSE); andc->parent = current_parent; andc->type = cond_and; andc->testfor = TRUE; @@ -696,7 +696,7 @@ for (;;) else if (Ustrcmp(buffer, "or") == 0) { - condition_block *orc = store_get(sizeof(condition_block)); + condition_block *orc = store_get(sizeof(condition_block), FALSE); condition_block *or_parent = NULL; if (current_parent) @@ -856,12 +856,12 @@ white space here. */ if (Ustrncmp(ptr, "if(", 3) == 0) { - Ustrcpy(buffer, "if"); + Ustrcpy(buffer, US"if"); ptr += 2; } else if (Ustrncmp(ptr, "elif(", 5) == 0) { - Ustrcpy(buffer, "elif"); + Ustrcpy(buffer, US"elif"); ptr += 4; } else @@ -981,7 +981,7 @@ switch (command) if (command == logwrite_command) { int len = Ustrlen(buffer); - if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, "\n"); + if (len == 0 || buffer[len-1] != '\n') Ustrcat(buffer, US"\n"); } argument.u = string_copy(buffer); @@ -1015,7 +1015,7 @@ switch (command) if (*error_pointer != NULL) yield = FALSE; else { - new = store_get(sizeof(filter_cmd) + sizeof(union argtypes)); + new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), FALSE); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); @@ -1099,7 +1099,7 @@ switch (command) /* Finish has no arguments; fmsg defaults to NULL */ case finish_command: - new = store_get(sizeof(filter_cmd)); + new = store_get(sizeof(filter_cmd), FALSE); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); @@ -1123,7 +1123,7 @@ switch (command) /* Set up the command block for if */ - new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes)); + new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); @@ -1151,7 +1151,7 @@ switch (command) while (had_else_endif == had_elif) { filter_cmd *newnew = - store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes)); + store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); new->args[2].f = newnew; new = newnew; new->next = NULL; @@ -1204,7 +1204,7 @@ switch (command) case mail_command: case vacation_command: - new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes)); + new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), FALSE); new->next = NULL; new->command = command; new->seen = seen_force? seen_value : FALSE; @@ -1516,7 +1516,7 @@ switch (c->type) (debug_selector & D_filter) != 0) { indent(); - debug_printf("Extracted address %s\n", filter_thisaddress); + debug_printf_indent("Extracted address %s\n", filter_thisaddress); } yield = test_condition(c->right.c, FALSE); } @@ -1590,9 +1590,9 @@ switch (c->type) if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { - debug_printf("Match expanded arguments:\n"); - debug_printf(" Subject = %s\n", exp[0]); - debug_printf(" Pattern = %s\n", exp[1]); + debug_printf_indent("Match expanded arguments:\n"); + debug_printf_indent(" Subject = %s\n", exp[0]); + debug_printf_indent(" Pattern = %s\n", exp[1]); } re = pcre_compile(CS exp[1], @@ -1634,11 +1634,11 @@ if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { indent(); - debug_printf("%sondition is %s: ", + debug_printf_indent("%sondition is %s: ", toplevel? "C" : "Sub-c", (yield == c->testfor)? "true" : "false"); print_condition(c, TRUE); - debug_printf("\n"); + debug_printf_indent("\n"); } return yield == c->testfor; @@ -1674,7 +1674,7 @@ int mode; address_item *addr; BOOL condition_value; -while (commands != NULL) +while (commands) { int ff_ret; uschar *fmsg, *ff_name; @@ -1688,21 +1688,16 @@ while (commands != NULL) for (i = 0; i < (command_exparg_count[commands->command] & 15); i++) { uschar *ss = commands->args[i].u; - if (ss == NULL) - { + if (!ss) expargs[i] = NULL; - } else - { - expargs[i] = expand_string(ss); - if (expargs[i] == NULL) + if (!(expargs[i] = expand_string(ss))) { *error_pointer = string_sprintf("failed to expand \"%s\" in " "%s command: %s", ss, command_list[commands->command], expand_string_message); return FF_ERROR; } - } } /* Now switch for each command, setting the "delivered" flag if any of them @@ -1713,640 +1708,640 @@ while (commands != NULL) switch(commands->command) { case add_command: - for (i = 0; i < 2; i++) - { - uschar *ss = expargs[i]; - uschar *end; + for (i = 0; i < 2; i++) + { + uschar *ss = expargs[i]; + uschar *end; - if (i == 1 && (*ss++ != 'n' || ss[1] != 0)) - { - *error_pointer = string_sprintf("unknown variable \"%s\" in \"add\" " - "command", expargs[i]); - return FF_ERROR; - } + if (i == 1 && (*ss++ != 'n' || ss[1] != 0)) + { + *error_pointer = string_sprintf("unknown variable \"%s\" in \"add\" " + "command", expargs[i]); + return FF_ERROR; + } - /* Allow for "--" at the start of the value (from -$n0) for example */ - if (i == 0) while (ss[0] == '-' && ss[1] == '-') ss += 2; + /* Allow for "--" at the start of the value (from -$n0) for example */ + if (i == 0) while (ss[0] == '-' && ss[1] == '-') ss += 2; - n[i] = (int)Ustrtol(ss, &end, 0); - if (*end != 0) - { - *error_pointer = string_sprintf("malformed number \"%s\" in \"add\" " - "command", ss); - return FF_ERROR; - } - } + n[i] = (int)Ustrtol(ss, &end, 0); + if (*end != 0) + { + *error_pointer = string_sprintf("malformed number \"%s\" in \"add\" " + "command", ss); + return FF_ERROR; + } + } - filter_n[n[1]] += n[0]; - if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]); - break; + filter_n[n[1]] += n[0]; + if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]); + break; - /* A deliver command's argument must be a valid address. Its optional - second argument (system filter only) must also be a valid address. */ + /* A deliver command's argument must be a valid address. Its optional + second argument (system filter only) must also be a valid address. */ case deliver_command: - for (i = 0; i < 2; i++) - { - s = expargs[i]; - if (s != NULL) - { - int start, end, domain; - 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); - else - { - *error_pointer = string_sprintf("malformed address \"%s\" in " - "filter file: %s", s, error); - return FF_ERROR; - } - } - } + for (i = 0; i < 2; i++) + { + s = expargs[i]; + if (s != NULL) + { + int start, end, domain; + 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); + else + { + *error_pointer = string_sprintf("malformed address \"%s\" in " + "filter file: %s", s, error); + return FF_ERROR; + } + } + } - /* Stick the errors address into a simple variable, as it will - be referenced a few times. Check that the caller is permitted to - specify it. */ + /* Stick the errors address into a simple variable, as it will + be referenced a few times. Check that the caller is permitted to + specify it. */ - s = expargs[1]; + s = expargs[1]; - if (s != NULL && !f.system_filtering) - { - uschar *ownaddress = expand_string(US"$local_part@$domain"); - if (strcmpic(ownaddress, s) != 0) - { - *error_pointer = US"errors_to must point to the caller's address"; - return FF_ERROR; - } - } + if (s != NULL && !f.system_filtering) + { + uschar *ownaddress = expand_string(US"$local_part@$domain"); + if (strcmpic(ownaddress, s) != 0) + { + *error_pointer = US"errors_to must point to the caller's address"; + return FF_ERROR; + } + } - /* Test case: report what would happen */ + /* Test case: report what would happen */ - if (filter_test != FTEST_NONE) - { - indent(); - printf("%seliver message to: %s%s%s%s\n", - (commands->seen)? "D" : "Unseen d", - expargs[0], - commands->noerror? " (noerror)" : "", - (s != NULL)? " errors_to " : "", - (s != NULL)? s : US""); - } + if (filter_test != FTEST_NONE) + { + indent(); + printf("%seliver message to: %s%s%s%s\n", + (commands->seen)? "D" : "Unseen d", + expargs[0], + commands->noerror? " (noerror)" : "", + (s != NULL)? " errors_to " : "", + (s != NULL)? s : US""); + } - /* Real case. */ + /* Real case. */ - else - { - DEBUG(D_filter) debug_printf("Filter: %sdeliver message to: %s%s%s%s\n", - (commands->seen)? "" : "unseen ", - expargs[0], - commands->noerror? " (noerror)" : "", - (s != NULL)? " errors_to " : "", - (s != NULL)? s : US""); - - /* Create the new address and add it to the chain, setting the - af_ignore_error flag if necessary, and the errors address, which can be - set in a system filter and to the local address in user filters. */ - - addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */ - addr->prop.errors_address = (s == NULL)? - s : string_copy(s); /* Default is NULL */ - if (commands->noerror) addr->prop.ignore_error = TRUE; - addr->next = *generated; - *generated = addr; - } - break; + else + { + DEBUG(D_filter) debug_printf_indent("Filter: %sdeliver message to: %s%s%s%s\n", + (commands->seen)? "" : "unseen ", + expargs[0], + commands->noerror? " (noerror)" : "", + (s != NULL)? " errors_to " : "", + (s != NULL)? s : US""); + + /* Create the new address and add it to the chain, setting the + af_ignore_error flag if necessary, and the errors address, which can be + set in a system filter and to the local address in user filters. */ + + addr = deliver_make_addr(expargs[0], TRUE); /* TRUE => copy s */ + addr->prop.errors_address = (s == NULL)? + s : string_copy(s); /* Default is NULL */ + if (commands->noerror) addr->prop.ignore_error = TRUE; + addr->next = *generated; + *generated = addr; + } + break; case save_command: - s = expargs[0]; - mode = commands->args[1].i; + s = expargs[0]; + mode = commands->args[1].i; - /* Test case: report what would happen */ + /* Test case: report what would happen */ - if (filter_test != FTEST_NONE) - { - indent(); - if (mode < 0) - printf("%save message to: %s%s\n", (commands->seen)? - "S" : "Unseen s", s, commands->noerror? " (noerror)" : ""); - else - printf("%save message to: %s %04o%s\n", (commands->seen)? - "S" : "Unseen s", s, mode, commands->noerror? " (noerror)" : ""); - } + if (filter_test != FTEST_NONE) + { + indent(); + if (mode < 0) + printf("%save message to: %s%s\n", (commands->seen)? + "S" : "Unseen s", s, commands->noerror? " (noerror)" : ""); + else + printf("%save message to: %s %04o%s\n", (commands->seen)? + "S" : "Unseen s", s, mode, commands->noerror? " (noerror)" : ""); + } - /* Real case: Ensure save argument starts with / if there is a home - directory to prepend. */ + /* Real case: Ensure save argument starts with / if there is a home + directory to prepend. */ - else - { - if (s[0] != '/' && (filter_options & RDO_PREPEND_HOME) != 0 && - deliver_home != NULL && deliver_home[0] != 0) - s = string_sprintf("%s/%s", deliver_home, s); - DEBUG(D_filter) debug_printf("Filter: %ssave message to: %s%s\n", - (commands->seen)? "" : "unseen ", s, - commands->noerror? " (noerror)" : ""); - - /* Create the new address and add it to the chain, setting the - af_pfr and af_file flags, the af_ignore_error flag if necessary, and the - mode value. */ - - addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */ - setflag(addr, af_pfr); - setflag(addr, af_file); - if (commands->noerror) addr->prop.ignore_error = TRUE; - addr->mode = mode; - addr->next = *generated; - *generated = addr; - } - break; + else + { + if (s[0] != '/' && (filter_options & RDO_PREPEND_HOME) != 0 && + deliver_home != NULL && deliver_home[0] != 0) + s = string_sprintf("%s/%s", deliver_home, s); + DEBUG(D_filter) debug_printf_indent("Filter: %ssave message to: %s%s\n", + (commands->seen)? "" : "unseen ", s, + commands->noerror? " (noerror)" : ""); + + /* Create the new address and add it to the chain, setting the + af_pfr and af_file flags, the af_ignore_error flag if necessary, and the + mode value. */ + + addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */ + setflag(addr, af_pfr); + setflag(addr, af_file); + if (commands->noerror) addr->prop.ignore_error = TRUE; + addr->mode = mode; + addr->next = *generated; + *generated = addr; + } + break; case pipe_command: - s = string_copy(commands->args[0].u); - if (filter_test != FTEST_NONE) - { - indent(); - printf("%sipe message to: %s%s\n", (commands->seen)? - "P" : "Unseen p", s, commands->noerror? " (noerror)" : ""); - } - else /* Ensure pipe command starts with | */ - { - DEBUG(D_filter) debug_printf("Filter: %spipe message to: %s%s\n", - (commands->seen)? "" : "unseen ", s, - commands->noerror? " (noerror)" : ""); - if (s[0] != '|') s = string_sprintf("|%s", s); - - /* Create the new address and add it to the chain, setting the - af_ignore_error flag if necessary. Set the af_expand_pipe flag so that - each command argument is expanded in the transport after the command - has been split up into separate arguments. */ - - addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */ - setflag(addr, af_pfr); - setflag(addr, af_expand_pipe); - if (commands->noerror) addr->prop.ignore_error = TRUE; - addr->next = *generated; - *generated = addr; - - /* If there are any numeric variables in existence (e.g. after a regex - condition), or if $thisaddress is set, take a copy for use in the - expansion. Note that we can't pass NULL for filter_thisaddress, because - NULL terminates the list. */ - - if (expand_nmax >= 0 || filter_thisaddress != NULL) - { - int i; - int ecount = (expand_nmax >= 0)? expand_nmax : -1; - uschar **ss = store_get(sizeof(uschar *) * (ecount + 3)); - addr->pipe_expandn = ss; - if (filter_thisaddress == NULL) filter_thisaddress = US""; - *ss++ = string_copy(filter_thisaddress); - for (i = 0; i <= expand_nmax; i++) - *ss++ = string_copyn(expand_nstring[i], expand_nlength[i]); - *ss = NULL; - } - } - break; + s = string_copy(commands->args[0].u); + if (filter_test != FTEST_NONE) + { + indent(); + printf("%sipe message to: %s%s\n", (commands->seen)? + "P" : "Unseen p", s, commands->noerror? " (noerror)" : ""); + } + else /* Ensure pipe command starts with | */ + { + DEBUG(D_filter) debug_printf_indent("Filter: %spipe message to: %s%s\n", + commands->seen ? "" : "unseen ", s, + commands->noerror ? " (noerror)" : ""); + if (s[0] != '|') s = string_sprintf("|%s", s); - /* Set up the file name and mode, and close any previously open - file. */ + /* Create the new address and add it to the chain, setting the + af_ignore_error flag if necessary. Set the af_expand_pipe flag so that + each command argument is expanded in the transport after the command + has been split up into separate arguments. */ + + addr = deliver_make_addr(s, TRUE); /* TRUE => copy s */ + setflag(addr, af_pfr); + setflag(addr, af_expand_pipe); + if (commands->noerror) addr->prop.ignore_error = TRUE; + addr->next = *generated; + *generated = addr; + + /* If there are any numeric variables in existence (e.g. after a regex + condition), or if $thisaddress is set, take a copy for use in the + expansion. Note that we can't pass NULL for filter_thisaddress, because + NULL terminates the list. */ + + if (expand_nmax >= 0 || filter_thisaddress != NULL) + { + int ecount = expand_nmax >= 0 ? expand_nmax : -1; + uschar **ss = store_get(sizeof(uschar *) * (ecount + 3), FALSE); + + addr->pipe_expandn = ss; + if (!filter_thisaddress) filter_thisaddress = US""; + *ss++ = string_copy(filter_thisaddress); + for (int i = 0; i <= expand_nmax; i++) + *ss++ = string_copyn(expand_nstring[i], expand_nlength[i]); + *ss = NULL; + } + } + break; + + /* Set up the file name and mode, and close any previously open + file. */ case logfile_command: - log_mode = commands->args[1].i; - if (log_mode == -1) log_mode = 0600; - if (log_fd >= 0) - { - (void)close(log_fd); - log_fd = -1; - } - log_filename = expargs[0]; - if (filter_test != FTEST_NONE) - { - indent(); - printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename); - } - break; + log_mode = commands->args[1].i; + if (log_mode == -1) log_mode = 0600; + if (log_fd >= 0) + { + (void)close(log_fd); + log_fd = -1; + } + log_filename = expargs[0]; + if (filter_test != FTEST_NONE) + { + indent(); + printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename); + } + break; case logwrite_command: - s = expargs[0]; + s = expargs[0]; - if (filter_test != FTEST_NONE) - { - indent(); - printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L", - string_printing(s)); - } + if (filter_test != FTEST_NONE) + { + indent(); + printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L", + string_printing(s)); + } - /* Attempt to write to a log file only if configured as permissible. - Logging may be forcibly skipped for verifying or testing. */ + /* Attempt to write to a log file only if configured as permissible. + Logging may be forcibly skipped for verifying or testing. */ - else if ((filter_options & RDO_LOG) != 0) /* Locked out */ - { - DEBUG(D_filter) - debug_printf("filter log command aborted: euid=%ld\n", - (long int)geteuid()); - *error_pointer = US"logwrite command forbidden"; - return FF_ERROR; - } - else if ((filter_options & RDO_REALLOG) != 0) - { - int len; - DEBUG(D_filter) debug_printf("writing filter log as euid %ld\n", - (long int)geteuid()); - if (log_fd < 0) - { - if (log_filename == NULL) - { - *error_pointer = US"attempt to obey \"logwrite\" command " - "without a previous \"logfile\""; - return FF_ERROR; - } - log_fd = Uopen(log_filename, O_CREAT|O_APPEND|O_WRONLY, log_mode); - if (log_fd < 0) - { - *error_pointer = string_open_failed(errno, "filter log file \"%s\"", - log_filename); - return FF_ERROR; - } - } - len = Ustrlen(s); - if (write(log_fd, s, len) != len) - { - *error_pointer = string_sprintf("write error on file \"%s\": %s", - log_filename, strerror(errno)); - return FF_ERROR; - } - } - else - { - DEBUG(D_filter) debug_printf("skipping logwrite (verifying or testing)\n"); - } - break; + else if ((filter_options & RDO_LOG) != 0) /* Locked out */ + { + DEBUG(D_filter) + debug_printf_indent("filter log command aborted: euid=%ld\n", + (long int)geteuid()); + *error_pointer = US"logwrite command forbidden"; + return FF_ERROR; + } + else if ((filter_options & RDO_REALLOG) != 0) + { + int len; + DEBUG(D_filter) debug_printf_indent("writing filter log as euid %ld\n", + (long int)geteuid()); + if (log_fd < 0) + { + if (log_filename == NULL) + { + *error_pointer = US"attempt to obey \"logwrite\" command " + "without a previous \"logfile\""; + return FF_ERROR; + } + log_fd = Uopen(log_filename, O_CREAT|O_APPEND|O_WRONLY, log_mode); + if (log_fd < 0) + { + *error_pointer = string_open_failed(errno, "filter log file \"%s\"", + log_filename); + return FF_ERROR; + } + } + len = Ustrlen(s); + if (write(log_fd, s, len) != len) + { + *error_pointer = string_sprintf("write error on file \"%s\": %s", + log_filename, strerror(errno)); + return FF_ERROR; + } + } + else + { + DEBUG(D_filter) debug_printf_indent("skipping logwrite (verifying or testing)\n"); + } + break; - /* Header addition and removal is available only in the system filter. The - command is rejected at parse time otherwise. However "headers charset" is - always permitted. */ + /* Header addition and removal is available only in the system filter. The + command is rejected at parse time otherwise. However "headers charset" is + always permitted. */ case headers_command: - { - int subtype = commands->args[1].i; - s = expargs[0]; + { + int subtype = commands->args[1].i; + s = expargs[0]; - if (filter_test != FTEST_NONE) - printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" : - (subtype == FALSE)? "remove" : "charset", string_printing(s)); + if (filter_test != FTEST_NONE) + printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" : + (subtype == FALSE)? "remove" : "charset", string_printing(s)); - if (subtype == TRUE) - { - while (isspace(*s)) s++; - if (s[0] != 0) - { - header_add(htype_other, "%s%s", s, (s[Ustrlen(s)-1] == '\n')? - "" : "\n"); - header_last->type = header_checkname(header_last, FALSE); - if (header_last->type >= 'a') header_last->type = htype_other; - } - } + if (subtype == TRUE) + { + while (isspace(*s)) s++; + if (s[0] != 0) + { + header_add(htype_other, "%s%s", s, (s[Ustrlen(s)-1] == '\n')? + "" : "\n"); + header_last->type = header_checkname(header_last, FALSE); + if (header_last->type >= 'a') header_last->type = htype_other; + } + } - else if (subtype == FALSE) - { - int sep = 0; - uschar *ss; - const uschar *list = s; - uschar buffer[128]; - while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) - != NULL) - header_remove(0, ss); - } + else if (subtype == FALSE) + { + int sep = 0; + uschar *ss; + const uschar *list = s; + uschar buffer[128]; + while ((ss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) + != NULL) + header_remove(0, ss); + } - /* This setting lasts only while the filter is running; on exit, the - variable is reset to the previous value. */ + /* This setting lasts only while the filter is running; on exit, the + variable is reset to the previous value. */ - else headers_charset = s; - } - break; + else headers_charset = s; + } + break; - /* Defer, freeze, and fail are available only when explicitly permitted. - These commands are rejected at parse time otherwise. The message can get - very long by the inclusion of message headers; truncate if it is, and also - ensure printing characters so as not to mess up log files. */ + /* Defer, freeze, and fail are available only when explicitly permitted. + These commands are rejected at parse time otherwise. The message can get + very long by the inclusion of message headers; truncate if it is, and also + ensure printing characters so as not to mess up log files. */ case defer_command: - ff_name = US"defer"; - ff_ret = FF_DEFER; - goto DEFERFREEZEFAIL; + ff_name = US"defer"; + ff_ret = FF_DEFER; + goto DEFERFREEZEFAIL; case fail_command: - ff_name = US"fail"; - ff_ret = FF_FAIL; - goto DEFERFREEZEFAIL; + ff_name = US"fail"; + ff_ret = FF_FAIL; + goto DEFERFREEZEFAIL; case freeze_command: - ff_name = US"freeze"; - ff_ret = FF_FREEZE; + ff_name = US"freeze"; + ff_ret = FF_FREEZE; - DEFERFREEZEFAIL: - fmsg = expargs[0]; - if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, " ... (truncated)"); - fmsg = US string_printing(fmsg); - *error_pointer = fmsg; + DEFERFREEZEFAIL: + fmsg = expargs[0]; + if (Ustrlen(fmsg) > 1024) Ustrcpy(fmsg + 1000, US" ... (truncated)"); + fmsg = US string_printing(fmsg); + *error_pointer = fmsg; - if (filter_test != FTEST_NONE) - { - indent(); - printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg); - } - else DEBUG(D_filter) debug_printf("Filter: %s \"%s\"\n", ff_name, fmsg); - return ff_ret; + if (filter_test != FTEST_NONE) + { + indent(); + printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg); + } + else DEBUG(D_filter) debug_printf_indent("Filter: %s \"%s\"\n", ff_name, fmsg); + return ff_ret; case finish_command: - if (filter_test != FTEST_NONE) - { - indent(); - printf("%sinish\n", (commands->seen)? "Seen f" : "F"); - } - else - { - DEBUG(D_filter) debug_printf("Filter: %sfinish\n", - (commands->seen)? " Seen " : ""); - } - finish_obeyed = TRUE; - return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED; + if (filter_test != FTEST_NONE) + { + indent(); + printf("%sinish\n", (commands->seen)? "Seen f" : "F"); + } + else + { + DEBUG(D_filter) debug_printf_indent("Filter: %sfinish\n", + (commands->seen)? " Seen " : ""); + } + finish_obeyed = TRUE; + return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED; case if_command: - { - uschar *save_address = filter_thisaddress; - int ok = FF_DELIVERED; - condition_value = test_condition(commands->args[0].c, TRUE); - if (*error_pointer != NULL) ok = FF_ERROR; else - { - output_indent += 2; - ok = interpret_commands(commands->args[condition_value? 1:2].f, - generated); - output_indent -= 2; - } - filter_thisaddress = save_address; - if (finish_obeyed || (ok != FF_DELIVERED && ok != FF_NOTDELIVERED)) - return ok; - } - break; + { + uschar *save_address = filter_thisaddress; + int ok = FF_DELIVERED; + condition_value = test_condition(commands->args[0].c, TRUE); + if (*error_pointer != NULL) ok = FF_ERROR; else + { + output_indent += 2; + ok = interpret_commands(commands->args[condition_value? 1:2].f, + generated); + output_indent -= 2; + } + filter_thisaddress = save_address; + if (finish_obeyed || (ok != FF_DELIVERED && ok != FF_NOTDELIVERED)) + return ok; + } + break; - /* To try to catch runaway loops, do not generate mail if the - return path is unset or if a non-trusted user supplied -f <> - as the return path. */ + /* To try to catch runaway loops, do not generate mail if the + return path is unset or if a non-trusted user supplied -f <> + as the return path. */ case mail_command: case vacation_command: - if (return_path == NULL || return_path[0] == 0) - { - if (filter_test != FTEST_NONE) - printf("%s command ignored because return_path is empty\n", - command_list[commands->command]); - else DEBUG(D_filter) debug_printf("%s command ignored because return_path " - "is empty\n", command_list[commands->command]); - break; - } + if (return_path == NULL || return_path[0] == 0) + { + if (filter_test != FTEST_NONE) + printf("%s command ignored because return_path is empty\n", + command_list[commands->command]); + else DEBUG(D_filter) debug_printf_indent("%s command ignored because return_path " + "is empty\n", command_list[commands->command]); + break; + } - /* Check the contents of the strings. The type of string can be deduced - from the value of i. + /* Check the contents of the strings. The type of string can be deduced + from the value of i. - . If i is equal to mailarg_index_text it's a text string for the body, - where anything goes. + . If i is equal to mailarg_index_text it's a text string for the body, + where anything goes. - . If i is > mailarg_index_text, we are dealing with a file name, which - cannot contain non-printing characters. + . If i is > mailarg_index_text, we are dealing with a file name, which + cannot contain non-printing characters. - . If i is less than mailarg_index_headers we are dealing with something - that will go in a single message header line, where newlines must be - followed by white space. + . If i is less than mailarg_index_headers we are dealing with something + that will go in a single message header line, where newlines must be + followed by white space. - . If i is equal to mailarg_index_headers, we have a string that contains - one or more headers. Newlines that are not followed by white space must - be followed by a header name. - */ + . If i is equal to mailarg_index_headers, we have a string that contains + one or more headers. Newlines that are not followed by white space must + be followed by a header name. + */ - for (i = 0; i < MAILARGS_STRING_COUNT; i++) - { - uschar *p; - uschar *s = expargs[i]; + for (i = 0; i < MAILARGS_STRING_COUNT; i++) + { + uschar *p; + uschar *s = expargs[i]; - if (s == NULL) continue; + if (s == NULL) continue; - if (i != mailarg_index_text) for (p = s; *p != 0; p++) - { - int c = *p; - if (i > mailarg_index_text) + if (i != mailarg_index_text) for (p = s; *p != 0; p++) { - if (!mac_isprint(c)) + int c = *p; + if (i > mailarg_index_text) { - *error_pointer = string_sprintf("non-printing character in \"%s\" " - "in %s command", string_printing(s), - command_list[commands->command]); - return FF_ERROR; + if (!mac_isprint(c)) + { + *error_pointer = string_sprintf("non-printing character in \"%s\" " + "in %s command", string_printing(s), + command_list[commands->command]); + return FF_ERROR; + } } - } - /* i < mailarg_index_text */ + /* i < mailarg_index_text */ - else if (c == '\n' && !isspace(p[1])) - { - if (i < mailarg_index_headers) + else if (c == '\n' && !isspace(p[1])) { - *error_pointer = string_sprintf("\\n not followed by space in " - "\"%.1024s\" in %s command", string_printing(s), - command_list[commands->command]); - return FF_ERROR; - } + if (i < mailarg_index_headers) + { + *error_pointer = string_sprintf("\\n not followed by space in " + "\"%.1024s\" in %s command", string_printing(s), + command_list[commands->command]); + return FF_ERROR; + } - /* Check for the start of a new header line within the string */ + /* Check for the start of a new header line within the string */ - else - { - uschar *pp; - for (pp = p + 1;; pp++) + else { - c = *pp; - if (c == ':' && pp != p + 1) break; - if (c == 0 || c == ':' || isspace(*pp)) + uschar *pp; + for (pp = p + 1;; pp++) { - *error_pointer = string_sprintf("\\n not followed by space or " - "valid header name in \"%.1024s\" in %s command", - string_printing(s), command_list[commands->command]); - return FF_ERROR; + c = *pp; + if (c == ':' && pp != p + 1) break; + if (c == 0 || c == ':' || isspace(*pp)) + { + *error_pointer = string_sprintf("\\n not followed by space or " + "valid header name in \"%.1024s\" in %s command", + string_printing(s), command_list[commands->command]); + return FF_ERROR; + } } + p = pp; } - p = pp; } - } - } /* Loop to scan the string */ - - /* The string is OK */ + } /* Loop to scan the string */ - commands->args[i].u = s; - } + /* The string is OK */ - /* Proceed with mail or vacation command */ - - if (filter_test != FTEST_NONE) - { - uschar *to = commands->args[mailarg_index_to].u; - indent(); - printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M", - to ? to : US"", - commands->command == vacation_command ? " (vacation)" : "", - commands->noerror ? " (noerror)" : ""); - for (i = 1; i < MAILARGS_STRING_COUNT; i++) - { - uschar *arg = commands->args[i].u; - if (arg) - { - int len = Ustrlen(mailargs[i]); - int indent = (debug_selector != 0)? output_indent : 0; - while (len++ < 7 + indent) printf(" "); - printf("%s: %s%s\n", mailargs[i], string_printing(arg), - (commands->args[mailarg_index_expand].u != NULL && - Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : ""); - } + commands->args[i].u = s; } - if (commands->args[mailarg_index_return].u) - printf("Return original message\n"); - } - else - { - uschar *tt; - uschar *to = commands->args[mailarg_index_to].u; - gstring * log_addr = NULL; - - if (!to) to = expand_string(US"$reply_address"); - while (isspace(*to)) to++; - for (tt = to; *tt != 0; tt++) /* Get rid of newlines */ - if (*tt == '\n') *tt = ' '; + /* Proceed with mail or vacation command */ - DEBUG(D_filter) + if (filter_test != FTEST_NONE) { - debug_printf("Filter: %smail to: %s%s%s\n", - commands->seen ? "seen " : "", - to, + uschar *to = commands->args[mailarg_index_to].u; + indent(); + printf("%sail to: %s%s%s\n", (commands->seen)? "Seen m" : "M", + to ? to : US"", commands->command == vacation_command ? " (vacation)" : "", commands->noerror ? " (noerror)" : ""); for (i = 1; i < MAILARGS_STRING_COUNT; i++) { uschar *arg = commands->args[i].u; - if (arg != NULL) + if (arg) { int len = Ustrlen(mailargs[i]); - while (len++ < 15) debug_printf(" "); - debug_printf("%s: %s%s\n", mailargs[i], string_printing(arg), + int indent = (debug_selector != 0)? output_indent : 0; + while (len++ < 7 + indent) printf(" "); + printf("%s: %s%s\n", mailargs[i], string_printing(arg), (commands->args[mailarg_index_expand].u != NULL && Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : ""); } } + if (commands->args[mailarg_index_return].u) + printf("Return original message\n"); } - - /* Create the "address" for the autoreply. This is used only for logging, - as the actual recipients are extracted from the To: line by -t. We use the - same logic here to extract the working addresses (there may be more than - one). Just in case there are a vast number of addresses, stop when the - string gets too long. */ - - tt = to; - while (*tt != 0) + else { - uschar *ss = parse_find_address_end(tt, FALSE); - uschar *recipient, *errmess; - int start, end, domain; - int temp = *ss; + uschar *tt; + uschar *to = commands->args[mailarg_index_to].u; + gstring * log_addr = NULL; - *ss = 0; - recipient = parse_extract_address(tt, &errmess, &start, &end, &domain, - FALSE); - *ss = temp; + if (!to) to = expand_string(US"$reply_address"); + while (isspace(*to)) to++; - /* Ignore empty addresses and errors; an error will occur later if - there's something really bad. */ + for (tt = to; *tt != 0; tt++) /* Get rid of newlines */ + if (*tt == '\n') *tt = ' '; - if (recipient) + DEBUG(D_filter) { - log_addr = string_catn(log_addr, log_addr ? US"," : US">", 1); - log_addr = string_cat (log_addr, recipient); + debug_printf_indent("Filter: %smail to: %s%s%s\n", + commands->seen ? "seen " : "", + to, + commands->command == vacation_command ? " (vacation)" : "", + commands->noerror ? " (noerror)" : ""); + for (i = 1; i < MAILARGS_STRING_COUNT; i++) + { + uschar *arg = commands->args[i].u; + if (arg != NULL) + { + int len = Ustrlen(mailargs[i]); + while (len++ < 15) debug_printf_indent(" "); + debug_printf_indent("%s: %s%s\n", mailargs[i], string_printing(arg), + (commands->args[mailarg_index_expand].u != NULL && + Ustrcmp(mailargs[i], "file") == 0)? " (expanded)" : ""); + } + } } - /* Check size */ + /* Create the "address" for the autoreply. This is used only for logging, + as the actual recipients are extracted from the To: line by -t. We use the + same logic here to extract the working addresses (there may be more than + one). Just in case there are a vast number of addresses, stop when the + string gets too long. */ - if (log_addr && log_addr->ptr > 256) + tt = to; + while (*tt != 0) { - log_addr = string_catn(log_addr, US", ...", 5); - break; - } + uschar *ss = parse_find_address_end(tt, FALSE); + uschar *recipient, *errmess; + int start, end, domain; + int temp = *ss; - /* Move on past this address */ + *ss = 0; + recipient = parse_extract_address(tt, &errmess, &start, &end, &domain, + FALSE); + *ss = temp; - tt = ss + (*ss ? 1 : 0); - while (isspace(*tt)) tt++; - } + /* Ignore empty addresses and errors; an error will occur later if + there's something really bad. */ - if (log_addr) - addr = deliver_make_addr(string_from_gstring(log_addr), FALSE); - else - { - addr = deliver_make_addr(US ">**bad-reply**", FALSE); - setflag(addr, af_bad_reply); - } + if (recipient) + { + log_addr = string_catn(log_addr, log_addr ? US"," : US">", 1); + log_addr = string_cat (log_addr, recipient); + } - setflag(addr, af_pfr); - if (commands->noerror) addr->prop.ignore_error = TRUE; - addr->next = *generated; - *generated = addr; + /* Check size */ - addr->reply = store_get(sizeof(reply_item)); - addr->reply->from = NULL; - addr->reply->to = string_copy(to); - addr->reply->file_expand = - commands->args[mailarg_index_expand].u != NULL; - addr->reply->expand_forbid = expand_forbid; - addr->reply->return_message = - commands->args[mailarg_index_return].u != NULL; - addr->reply->once_repeat = 0; - - if (commands->args[mailarg_index_once_repeat].u != NULL) - { - addr->reply->once_repeat = - readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0, - FALSE); - if (addr->reply->once_repeat < 0) + if (log_addr && log_addr->ptr > 256) + { + log_addr = string_catn(log_addr, US", ...", 5); + break; + } + + /* Move on past this address */ + + tt = ss + (*ss ? 1 : 0); + while (isspace(*tt)) tt++; + } + + if (log_addr) + addr = deliver_make_addr(string_from_gstring(log_addr), FALSE); + else { - *error_pointer = string_sprintf("Bad time value for \"once_repeat\" " - "in mail or vacation command: %s", - commands->args[mailarg_index_once_repeat].u); - return FF_ERROR; + addr = deliver_make_addr(US ">**bad-reply**", FALSE); + setflag(addr, af_bad_reply); } - } - /* Set up all the remaining string arguments (those other than "to") */ + setflag(addr, af_pfr); + if (commands->noerror) addr->prop.ignore_error = TRUE; + addr->next = *generated; + *generated = addr; + + addr->reply = store_get(sizeof(reply_item), FALSE); + addr->reply->from = NULL; + addr->reply->to = string_copy(to); + addr->reply->file_expand = + commands->args[mailarg_index_expand].u != NULL; + addr->reply->expand_forbid = expand_forbid; + addr->reply->return_message = + commands->args[mailarg_index_return].u != NULL; + addr->reply->once_repeat = 0; + + if (commands->args[mailarg_index_once_repeat].u != NULL) + { + addr->reply->once_repeat = + readconf_readtime(commands->args[mailarg_index_once_repeat].u, 0, + FALSE); + if (addr->reply->once_repeat < 0) + { + *error_pointer = string_sprintf("Bad time value for \"once_repeat\" " + "in mail or vacation command: %s", + commands->args[mailarg_index_once_repeat].u); + return FF_ERROR; + } + } - for (i = 1; i < mailargs_string_passed; i++) - { - uschar *ss = commands->args[i].u; - *(USS((US addr->reply) + reply_offsets[i])) = - ss ? string_copy(ss) : NULL; + /* Set up all the remaining string arguments (those other than "to") */ + + for (i = 1; i < mailargs_string_passed; i++) + { + uschar *ss = commands->args[i].u; + *(USS((US addr->reply) + reply_offsets[i])) = + ss ? string_copy(ss) : NULL; + } } - } - break; + break; case testprint_command: - if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) - { - const uschar *s = string_printing(expargs[0]); - if (filter_test == FTEST_NONE) - debug_printf("Filter: testprint: %s\n", s); - else - printf("Testprint: %s\n", s); - } + if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) + { + const uschar *s = string_printing(expargs[0]); + if (filter_test == FTEST_NONE) + debug_printf_indent("Filter: testprint: %s\n", s); + else + printf("Testprint: %s\n", s); + } } commands = commands->next; @@ -2376,7 +2371,7 @@ filter_personal(string_item *aliases, BOOL scan_cc) { uschar *self, *self_from, *self_to; uschar *psself = NULL, *psself_from = NULL, *psself_to = NULL; -void *reset_point = store_get(0); +rmark reset_point = store_mark(); BOOL yield; header_line *h; int to_count = 2; @@ -2391,7 +2386,7 @@ defined in RFC 2369. We also scan for "Auto-Submitted"; if it is found to contain any value other than "no", the message is not personal (RFC 3834). Previously the test was for "auto-". */ -for (h = header_list; h != NULL; h = h->next) +for (h = header_list; h; h = h->next) { uschar *s; if (h->type == htype_old) continue; @@ -2515,6 +2510,7 @@ filter_cmd *commands = NULL; filter_cmd **lastcmdptr = &commands; DEBUG(D_route) debug_printf("Filter: start of processing\n"); +acl_level++; /* Initialize "not in an if command", set the global flag that is always TRUE while filtering, and zero the variables. */ @@ -2554,35 +2550,35 @@ if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0) switch(yield) { case FF_DEFER: - s = US"Filtering ended by \"defer\"."; - break; + s = US"Filtering ended by \"defer\"."; + break; case FF_FREEZE: - s = US"Filtering ended by \"freeze\"."; - break; + s = US"Filtering ended by \"freeze\"."; + break; case FF_FAIL: - s = US"Filtering ended by \"fail\"."; - break; + s = US"Filtering ended by \"fail\"."; + break; case FF_DELIVERED: - s = US"Filtering set up at least one significant delivery " - "or other action.\n" - "No other deliveries will occur."; - break; + s = US"Filtering set up at least one significant delivery " + "or other action.\n" + "No other deliveries will occur."; + break; case FF_NOTDELIVERED: - s = US"Filtering did not set up a significant delivery.\n" - "Normal delivery will occur."; - break; + s = US"Filtering did not set up a significant delivery.\n" + "Normal delivery will occur."; + break; case FF_ERROR: - s = string_sprintf("Filter error: %s", *error); - break; + s = string_sprintf("Filter error: %s", *error); + break; } if (filter_test != FTEST_NONE) printf("%s\n", CS s); - else debug_printf("%s\n", s); + else debug_printf_indent("%s\n", s); } /* Close the log file if it was opened, and kill off any numerical variables @@ -2593,6 +2589,7 @@ expand_nmax = -1; f.filter_running = FALSE; headers_charset = save_headers_charset; +acl_level--; DEBUG(D_route) debug_printf("Filter: end of processing\n"); return yield; } diff --git a/src/src/filtertest.c b/src/src/filtertest.c index f3d3acc52..f54cbefb5 100644 --- a/src/src/filtertest.c +++ b/src/src/filtertest.c @@ -112,7 +112,7 @@ if (body_len >= message_body_visible) int above = message_body_visible - below; if (above > 0) { - uschar *temp = store_get(below); + uschar *temp = store_get(below, TRUE); memcpy(temp, message_body_end, below); memmove(message_body_end, s+1, above); memcpy(message_body_end + above, temp, below); @@ -178,7 +178,7 @@ if (fstat(fd, &statbuf) != 0) return FALSE; } -filebuf = store_get(statbuf.st_size + 1); +filebuf = store_get(statbuf.st_size + 1, is_tainted(filename)); rc = read(fd, filebuf, statbuf.st_size); (void)close(fd); diff --git a/src/src/functions.h b/src/src/functions.h index 806ba755d..bcf04c2d2 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -218,6 +218,7 @@ extern const uschar * exim_errstr(int); extern void exim_exit(int, const uschar *) NORETURN; extern void exim_nullstd(void); extern void exim_setugid(uid_t, gid_t, BOOL, uschar *); +extern void exim_underbar_exit(int); extern void exim_wait_tick(struct timeval *, int); extern int exp_bool(address_item *addr, uschar *mtype, uschar *mname, unsigned dgb_opt, uschar *oname, BOOL bvalue, @@ -458,7 +459,7 @@ extern void smtp_log_no_mail(void); extern void smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL); extern void smtp_proxy_tls(void *, uschar *, size_t, int *, int) NORETURN; extern BOOL smtp_read_response(void *, uschar *, int, int, int); -extern void smtp_reset(void *); +extern void *smtp_reset(void *); extern void smtp_respond(uschar *, int, BOOL, uschar *); extern void smtp_notquit_exit(uschar *, uschar *, uschar *, ...); extern void smtp_port_for_connect(host_item *, int); @@ -485,6 +486,8 @@ extern int stdin_getc(unsigned); extern int stdin_feof(void); extern int stdin_ferror(void); extern int stdin_ungetc(int); + +extern void store_exit(void); extern gstring *string_append(gstring *, int, ...) WARN_UNUSED_RESULT; extern gstring *string_append_listele(gstring *, uschar, const uschar *) WARN_UNUSED_RESULT; extern gstring *string_append_listele_n(gstring *, uschar, const uschar *, unsigned) WARN_UNUSED_RESULT; @@ -496,8 +499,6 @@ extern int string_compare_by_pointer(const void *, const void *); extern uschar *string_copy_dnsdomain(uschar *); extern uschar *string_copy_malloc(const uschar *); extern uschar *string_dequote(const uschar **); -extern gstring *string_fmt_append(gstring *, const char *, ...) ALMOST_PRINTF(2,3); -extern BOOL string_format(uschar *, int, const char *, ...) ALMOST_PRINTF(3,4); extern uschar *string_format_size(int, uschar *); extern int string_interpret_escape(const uschar **); extern int string_is_ip_address(const uschar *, int *); @@ -505,7 +506,6 @@ extern int string_is_ip_address(const uschar *, int *); extern BOOL string_is_utf8(const uschar *); #endif extern uschar *string_nextinlist(const uschar **, int *, uschar *, int); -extern uschar *string_open_failed(int, const char *, ...) PRINTF_FUNCTION(2,3); extern const uschar *string_printing2(const uschar *, BOOL); extern uschar *string_split_message(uschar *); extern uschar *string_timediff(struct timeval *); @@ -518,7 +518,23 @@ extern uschar *string_domain_utf8_to_alabel(const uschar *, uschar **); extern uschar *string_localpart_alabel_to_utf8(const uschar *, uschar **); extern uschar *string_localpart_utf8_to_alabel(const uschar *, uschar **); #endif -extern gstring *string_vformat(gstring *, BOOL, const char *, va_list); + +#define string_format(buf, siz, fmt, ...) \ + string_format_trc(buf, siz, US __FUNCTION__, __LINE__, fmt, __VA_ARGS__) +extern BOOL string_format_trc(uschar *, int, const uschar *, unsigned, + const char *, ...) ALMOST_PRINTF(5,6); + +#define string_vformat(g, flgs, fmt, ap) \ + string_vformat_trc(g, US __FUNCTION__, __LINE__, \ + STRING_SPRINTF_BUFFER_SIZE, flgs, fmt, ap) +extern gstring *string_vformat_trc(gstring *, const uschar *, unsigned, + unsigned, unsigned, const char *, va_list); + +#define string_open_failed(eno, fmt, ...) \ + string_open_failed_trc(eno, US __FUNCTION__, __LINE__, fmt, __VA_ARGS__) +extern uschar *string_open_failed_trc(int, const uschar *, unsigned, + const char *, ...) PRINTF_FUNCTION(4,5); + extern int strcmpic(const uschar *, const uschar *); extern int strncmpic(const uschar *, const uschar *, int); extern uschar *strstric(uschar *, uschar *, BOOL); @@ -623,6 +639,7 @@ return chown(CCS name, owner, group) /******************************************************************************/ /* String functions */ +#if !defined(MACRO_PREDEF) /************************************************* * Copy and save string * *************************************************/ @@ -630,16 +647,24 @@ return chown(CCS name, owner, group) /* This function assumes that memcpy() is faster than strcpy(). */ -#if !defined(MACRO_PREDEF) static inline uschar * -string_copy(const uschar *s) +string_copy_taint_trc(const uschar *s, BOOL tainted, const char * func, int line) { int len = Ustrlen(s) + 1; -uschar *ss = store_get(len); +uschar *ss = store_get_3(len, tainted, func, line); memcpy(ss, s, len); return ss; } +#define string_copy_taint(s, tainted) \ + string_copy_taint_trc((s), tainted, __FUNCTION__, __LINE__) + +static inline uschar * +string_copy(const uschar * s) +{ +return string_copy_taint((s), is_tainted(s)); +} + /************************************************* * Copy, lowercase and save string * @@ -653,7 +678,7 @@ Returns: copy of string in new store, with letters lowercased static inline uschar * string_copylc(const uschar *s) { -uschar *ss = store_get(Ustrlen(s) + 1); +uschar *ss = store_get(Ustrlen(s) + 1, is_tainted(s)); uschar *p = ss; while (*s != 0) *p++ = tolower(*s++); *p = 0; @@ -681,7 +706,7 @@ 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); +uschar *ss = store_get(n + 1, is_tainted(s)); Ustrncpy(ss, s, n); ss[n] = 0; return ss; @@ -704,7 +729,7 @@ Returns: copy of string in new store, with letters lowercased static inline uschar * string_copynlc(uschar *s, int n) { -uschar *ss = store_get(n + 1); +uschar *ss = store_get(n + 1, is_tainted(s)); uschar *p = ss; while (n-- > 0) *p++ = tolower(*s++); *p = 0; @@ -712,21 +737,73 @@ return ss; } +/************************************************* +* Copy and save string in longterm store * +*************************************************/ + +/* This function assumes that memcpy() is faster than strcpy(). + +Argument: string to copy +Returns: copy of string in new store +*/ + +static inline uschar * +string_copy_perm(const uschar *s, BOOL force_taint) +{ +int old_pool = store_pool; +int len = Ustrlen(s) + 1; +uschar *ss; + +store_pool = POOL_PERM; +ss = store_get(len, force_taint || is_tainted(s)); +memcpy(ss, s, len); +store_pool = old_pool; +return ss; +} + + + +/* sprintf into a buffer, taint-unchecked */ + +static inline void +string_format_nt(uschar * buf, int siz, const char * fmt, ...) +{ +gstring gs = { .size = siz, .ptr = 0, .s = buf }; +va_list ap; +va_start(ap, fmt); +(void) string_vformat(&gs, SVFMT_TAINT_NOCHK, fmt, ap); +va_end(ap); +} + + + /******************************************************************************/ /* Growable-string functions */ -/* Create a growable-string with some preassigned space, in untainted memory */ +/* Create a growable-string with some preassigned space */ + +#define string_get_tainted(size, tainted) \ + string_get_tainted_trc((size), (tainted), __FUNCTION__, __LINE__) static inline gstring * -string_get(unsigned size) +string_get_tainted_trc(unsigned size, BOOL tainted, const char * func, unsigned line) { -gstring * g = store_get(sizeof(gstring) + size); +gstring * g = store_get_3(sizeof(gstring) + size, tainted, func, line); g->size = size; g->ptr = 0; g->s = US(g + 1); return g; } +#define string_get(size) \ + string_get_trc((size), __FUNCTION__, __LINE__) + +static inline gstring * +string_get_trc(unsigned size, const char * func, unsigned line) +{ +return string_get_tainted_trc(size, FALSE, func, line); +} + /* NUL-terminate the C string in the growable-string, and return it. */ static inline uschar * @@ -737,10 +814,37 @@ g->s[g->ptr] = '\0'; return g->s; } + +#define gstring_release_unused(g) \ + gstring_release_unused_trc(g, __FUNCTION__, __LINE__) + static inline void -gstring_release_unused(gstring * g) +gstring_release_unused_trc(gstring * g, const char * file, unsigned line) { -if (g) store_reset(g->s + (g->size = g->ptr + 1)); +if (g) store_release_above_3(g->s + (g->size = g->ptr + 1), file, line); +} + + +/* sprintf-append to a growable-string */ + +#define string_fmt_append(g, fmt, ...) \ + string_fmt_append_f_trc(g, US __FUNCTION__, __LINE__, \ + SVFMT_EXTEND|SVFMT_REBUFFER, fmt, __VA_ARGS__) + +#define string_fmt_append_f(g, flgs, fmt, ...) \ + string_fmt_append_f_trc(g, US __FUNCTION__, __LINE__, \ + flgs, fmt, __VA_ARGS__) + +static inline gstring * +string_fmt_append_f_trc(gstring * g, const uschar * func, unsigned line, + unsigned flags, const char *format, ...) +{ +va_list ap; +va_start(ap, format); +g = string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE, + flags, format, ap); +va_end(ap); +return g; } /******************************************************************************/ diff --git a/src/src/globals.c b/src/src/globals.c index 742584ed1..15fb0898c 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1169,7 +1169,7 @@ uschar *pipe_connect_advertise_hosts = US"*"; #endif uschar *pipelining_advertise_hosts = US"*"; uschar *primary_hostname = NULL; -uschar process_info[PROCESS_INFO_SIZE]; +uschar *process_info; int process_info_len = 0; uschar *process_log_path = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 80e1764a3..c22600425 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -757,7 +757,7 @@ extern BOOL prdr_requested; /* Connecting mail server wants PRDR */ extern BOOL preserve_message_logs; /* Save msglog files */ extern uschar *primary_hostname; /* Primary name of this computer */ extern BOOL print_topbitchars; /* Topbit chars are printing chars */ -extern uschar process_info[]; /* For SIGUSR1 output */ +extern uschar *process_info; /* For SIGUSR1 output */ extern int process_info_len; extern uschar *process_log_path; /* Alternate path */ extern BOOL prod_requires_admin; /* TRUE if prodding requires admin */ diff --git a/src/src/hash.c b/src/src/hash.c index 1bdeaef5f..f1a6c4096 100644 --- a/src/src/hash.c +++ b/src/src/hash.c @@ -84,7 +84,8 @@ switch (h->method) void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen); +/* Hashing is sufficient to purify any tainted input */ +b->data = store_get(b->len = h->hashlen, FALSE); switch (h->method) { case HASH_SHA1: SHA1_Final (b->data, &h->u.sha1); break; @@ -137,7 +138,7 @@ gnutls_hash(h->sha, data, len); void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen); +b->data = store_get(b->len = h->hashlen, FALSE); gnutls_hash_output(h->sha, b->data); } @@ -174,7 +175,7 @@ gcry_md_write(h->sha, data, len); void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen); +b->data = store_get(b->len = h->hashlen, FALSE); memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen); } @@ -212,7 +213,7 @@ switch (h->method) void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen); +b->data = store_get(b->len = h->hashlen, FALSE); switch (h->method) { case HASH_SHA1: sha1_finish(h->u.sha1, b->data); break; @@ -450,7 +451,7 @@ native_sha1_mid(&h->sha1, US data); /* implicit size always 64 */ void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen); +b->data = store_get(b->len = h->hashlen, FALSE); native_sha1_end(&h->sha1, NULL, 0, b->data); } diff --git a/src/src/header.c b/src/src/header.c index 76ea10f13..a6c44fac8 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -97,14 +97,17 @@ header_line *h, *new = NULL; header_line **hptr; uschar *p, *q; -uschar buffer[HEADER_ADD_BUFFER_SIZE]; -gstring gs = { .size = HEADER_ADD_BUFFER_SIZE, .ptr = 0, .s = buffer }; +uschar * buf = store_get(HEADER_ADD_BUFFER_SIZE, FALSE); +gstring gs = { .size = HEADER_ADD_BUFFER_SIZE, .ptr = 0, .s = buf }; if (!header_last) return NULL; -if (!string_vformat(&gs, FALSE, format, ap)) +if (!string_vformat(&gs, SVFMT_REBUFFER, format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "string too long in header_add: " "%.100s ...", string_from_gstring(&gs)); + +if (gs.s != buf) store_release_above(buf); +gstring_release_unused(&gs); string_from_gstring(&gs); /* Find where to insert this header */ @@ -166,7 +169,7 @@ else point, we have hptr pointing to the link field that will point to the new header, and h containing the following header, or NULL. */ -for (p = q = buffer; *p; p = q) +for (p = q = gs.s; *p; p = q) { for (;;) { @@ -175,7 +178,7 @@ for (p = q = buffer; *p; p = q) if (*(++q) != ' ' && *q != '\t') break; } - new = store_get(sizeof(header_line)); + new = store_get(sizeof(header_line), FALSE); new->text = string_copyn(p, q - p); new->slen = q - p; new->type = type; diff --git a/src/src/host.c b/src/src/host.c index 9d94a2fde..a00d048f6 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -197,9 +197,9 @@ if ((ipa = string_is_ip_address(lname, NULL)) != 0) (ipa == 6 && af == AF_INET6)) { int x[4]; - yield = store_get(sizeof(struct hostent)); - alist = store_get(2 * sizeof(char *)); - adds = store_get(alen); + yield = store_get(sizeof(struct hostent), FALSE); + alist = store_get(2 * sizeof(char *), FALSE); + adds = store_get(alen, FALSE); yield->h_name = CS name; yield->h_aliases = NULL; yield->h_addrtype = af; @@ -251,9 +251,9 @@ else rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == type) count++; - yield = store_get(sizeof(struct hostent)); - alist = store_get((count + 1) * sizeof(char *)); - adds = store_get(count *alen); + yield = store_get(sizeof(struct hostent), FALSE); + alist = store_get((count + 1) * sizeof(char *), FALSE); + adds = store_get(count *alen, FALSE); yield->h_name = CS name; yield->h_aliases = NULL; @@ -325,7 +325,7 @@ while ((name = string_nextinlist(&list, &sep, NULL, 0))) continue; } - h = store_get(sizeof(host_item)); + h = store_get(sizeof(host_item), FALSE); h->name = name; h->address = NULL; h->port = PORT_NONE; @@ -524,12 +524,13 @@ void host_build_sender_fullhost(void) { BOOL show_helo = TRUE; -uschar * address, * fullhost, * rcvhost, * reset_point; +uschar * address, * fullhost, * rcvhost; +rmark reset_point; int len; if (!sender_host_address) return; -reset_point = store_get(0); +reset_point = store_mark(); /* Set up address, with or without the port. After discussion, it seems that the only format that doesn't cause trouble is [aaaa]:pppp. However, we can't @@ -643,10 +644,8 @@ else } } -if (sender_fullhost) store_free(sender_fullhost); -sender_fullhost = string_copy_malloc(fullhost); -if (sender_rcvhost) store_free(sender_rcvhost); -sender_rcvhost = string_copy_malloc(rcvhost); +sender_fullhost = string_copy_perm(fullhost, TRUE); +sender_rcvhost = string_copy_perm(rcvhost, TRUE); store_reset(reset_point); @@ -668,6 +667,8 @@ return depends on whether sender_fullhost and sender_ident are set or not: ident set, no host => U=ident ident set, host set => H=sender_fullhost U=ident +Use taint-unchecked routines on the assumption we'll never expand the results. + Arguments: useflag TRUE if first item to be flagged (H= or U=); if there are two items, the second is always flagged @@ -679,7 +680,7 @@ uschar * host_and_ident(BOOL useflag) { if (!sender_fullhost) - (void)string_format(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "", + string_format_nt(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "", sender_ident ? sender_ident : US"unknown"); else { @@ -688,10 +689,10 @@ else if (LOGGING(incoming_interface) && interface_address) iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port); if (sender_ident) - (void)string_format(big_buffer, big_buffer_size, "%s%s%s U=%s", + string_format_nt(big_buffer, big_buffer_size, "%s%s%s U=%s", flag, sender_fullhost, iface, sender_ident); else - (void)string_format(big_buffer, big_buffer_size, "%s%s%s", + string_format_nt(big_buffer, big_buffer_size, "%s%s%s", flag, sender_fullhost, iface); } return big_buffer; @@ -746,7 +747,7 @@ while ((s = string_nextinlist(&list, &sep, NULL, 0))) address above. The field in the ip_address_item is large enough to hold an IPv6 address. */ - next = store_get(sizeof(ip_address_item)); + next = store_get(sizeof(ip_address_item), FALSE); next->next = NULL; Ustrcpy(next->address, s); next->port = port; @@ -800,7 +801,7 @@ add_unique_interface(ip_address_item *list, ip_address_item *ipa) ip_address_item *ipa2; for (ipa2 = list; ipa2; ipa2 = ipa2->next) if (Ustrcmp(ipa2->address, ipa->address) == 0) return list; -ipa2 = store_get_perm(sizeof(ip_address_item)); +ipa2 = store_get_perm(sizeof(ip_address_item), FALSE); *ipa2 = *ipa; ipa2->next = list; return ipa2; @@ -816,7 +817,7 @@ ip_address_item *running_interfaces = NULL; if (local_interface_data == NULL) { - void *reset_item = store_get(0); + void *reset_item = store_mark(); ip_address_item *dlist = host_build_ifacelist(CUS local_interfaces, US"local_interfaces"); ip_address_item *xlist = host_build_ifacelist(CUS extra_local_interfaces, @@ -1549,7 +1550,7 @@ if ( slow_lookup_log /* Failed to look up the host. */ -if (hosts == NULL) +if (!hosts) { HDEBUG(D_host_lookup) debug_printf("IP address lookup failed: h_errno=%d\n", h_errno); @@ -1560,7 +1561,7 @@ if (hosts == NULL) treat this as non-existent. In some operating systems, this is returned as an empty string; in others as a single dot. */ -if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.') +if (!hosts->h_name || !hosts->h_name[0] || hosts->h_name[0] == '.') { HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an empty name: " "treated as non-existent host name\n"); @@ -1570,29 +1571,29 @@ if (hosts->h_name == NULL || hosts->h_name[0] == 0 || hosts->h_name[0] == '.') /* Copy and lowercase the name, which is in static storage in many systems. Put it in permanent memory. */ -s = US hosts->h_name; -len = Ustrlen(s) + 1; -t = sender_host_name = store_get_perm(len); -while (*s != 0) *t++ = tolower(*s++); -*t = 0; + { + int old_pool = store_pool; + store_pool = POOL_TAINT_PERM; /* names are tainted */ + + sender_host_name = string_copylc(US hosts->h_name); -/* If the host has aliases, build a copy of the alias list */ + /* If the host has aliases, build a copy of the alias list */ -if (hosts->h_aliases) - { - int count = 1; - uschar **ptr; - for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++; - ptr = sender_host_aliases = store_get_perm(count * sizeof(uschar *)); - for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) - { - uschar *s = *aliases; - int len = Ustrlen(s) + 1; - uschar *t = *ptr++ = store_get_perm(len); - while (*s != 0) *t++ = tolower(*s++); - *t = 0; - } - *ptr = NULL; + if (hosts->h_aliases) + { + int count = 1; + uschar **ptr; + + for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++; + store_pool = POOL_PERM; + ptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE); + store_pool = POOL_TAINT_PERM; + + for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) + *ptr++ = string_copylc(*aliases); + *ptr = NULL; + } + store_pool = old_pool; } return OK; @@ -1707,7 +1708,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) /* Get store for the list of aliases. For compatibility with gethostbyaddr, we make an empty list if there are none. */ - aptr = sender_host_aliases = store_get(count * sizeof(uschar *)); + aptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE); /* Re-scan and extract the names */ @@ -1715,7 +1716,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR) { - uschar * s = store_get(ssize); + uschar * s = store_get(ssize, TRUE); /* names are tainted */ /* If an overlong response was received, the data will have been truncated and dn_expand may fail. */ @@ -1728,8 +1729,8 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) break; } - store_reset(s + Ustrlen(s) + 1); - if (s[0] == 0) + store_release_above(s + Ustrlen(s) + 1); + if (!s[0]) { HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " "empty name: treated as non-existent host name\n"); @@ -1737,15 +1738,15 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) } if (!sender_host_name) sender_host_name = s; else *aptr++ = s; - while (*s != 0) { *s = tolower(*s); s++; } + while (*s) { *s = tolower(*s); s++; } } *aptr = NULL; /* End of alias list */ store_pool = old_pool; /* Reset store pool */ - /* If we've found a names, break out of the "order" loop */ + /* If we've found a name, break out of the "order" loop */ - if (sender_host_name != NULL) break; + if (sender_host_name) break; } /* If the DNS lookup deferred, we must also defer. */ @@ -2113,7 +2114,7 @@ for (int i = 1; i <= times; else { - host_item *next = store_get(sizeof(host_item)); + host_item *next = store_get(sizeof(host_item), FALSE); next->name = host->name; next->mx = host->mx; next->address = text_address; @@ -2435,7 +2436,7 @@ for (; i >= 0; i--) /* Not a duplicate */ new_sort_key = host->mx * 1000 + random_number(500) + randoffset; - next = store_get(sizeof(host_item)); + next = store_get(sizeof(host_item), FALSE); /* New address goes first: insert the new block after the first one (so as not to disturb the original pointer) but put the new address @@ -2838,7 +2839,7 @@ for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); /* Make a new host item and seek the correct insertion place */ { int sort_key = precedence * 1000 + weight; - host_item *next = store_get(sizeof(host_item)); + host_item *next = store_get(sizeof(host_item), FALSE); next->name = string_copy_dnsdomain(data); next->address = NULL; next->port = port; diff --git a/src/src/ip.c b/src/src/ip.c index 0f25df1da..19be51a03 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -515,7 +515,7 @@ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) callout_address = string_copy(path); server.sun_family = AF_UNIX; -Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1); +Ustrncpy(US server.sun_path, path, sizeof(server.sun_path)-1); server.sun_path[sizeof(server.sun_path)-1] = '\0'; if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { diff --git a/src/src/local_scan.h b/src/src/local_scan.h index dced8bd52..b250a163b 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -188,7 +188,10 @@ extern uschar *rfc2047_decode(uschar *, BOOL, uschar *, int, int *, uschar **); extern int smtp_fflush(void); extern void smtp_printf(const char *, BOOL, ...) PRINTF_FUNCTION(1,3); extern void smtp_vprintf(const char *, BOOL, va_list); -extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2); + +#define string_sprintf(fmt, ...) \ + string_sprintf_trc(fmt, US __FUNCTION__, __LINE__, __VA_ARGS__) +extern uschar *string_sprintf_trc(const char *, const uschar *, unsigned, ...) ALMOST_PRINTF(1,4); #ifdef LOCAL_SCAN /* When compiling a local_scan() file we want to rename a published API, so that diff --git a/src/src/log.c b/src/src/log.c index 0aaf94a31..e2543a74d 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -865,9 +865,13 @@ DEBUG(D_any|D_v) if (flags & LOG_CONFIG) g = log_config_info(g, flags); + /* We want to be able to log tainted info, but log_buffer is directly + malloc'd. So use deliberately taint-nonchecking routines to build into + it, trusting that we will never expand the results. */ + va_start(ap, format); i = g->ptr; - if (!string_vformat(g, FALSE, format, ap)) + if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap)) { g->ptr = i; g = string_cat(g, US"**** log string overflowed log buffer ****"); @@ -921,7 +925,12 @@ if (flags & LOG_CONFIG) va_start(ap, format); { int i = g->ptr; - if (!string_vformat(g, FALSE, format, ap)) + + /* We want to be able to log tainted info, but log_buffer is directly + malloc'd. So use deliberately taint-nonchecking routines to build into + it, trusting that we will never expand the results. */ + + if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap)) { g->ptr = i; g = string_cat(g, US"**** log string overflowed log buffer ****\n"); @@ -934,7 +943,7 @@ this way because it kind of fits with LOG_RECIPIENTS. */ if ( flags & LOG_SENDER && g->ptr < LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender)) - g = string_fmt_append(g, " from <%s>", raw_sender); + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " from <%s>", raw_sender); /* Add list of recipients to the message if required; the raw list, before rewriting, was saved in raw_recipients. There may be none, if an ACL @@ -945,12 +954,12 @@ if ( flags & LOG_RECIPIENTS && raw_recipients_count > 0) { int i; - g = string_fmt_append(g, " for"); + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL); for (i = 0; i < raw_recipients_count; i++) { uschar * s = raw_recipients[i]; if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break; - g = string_fmt_append(g, " %s", s); + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s); } } @@ -1046,39 +1055,34 @@ if (flags & LOG_REJECT) { if (header_list && LOGGING(rejected_header)) { - uschar * p = g->s + g->ptr; + gstring * g2; int i; if (recipients_count > 0) { /* List the sender */ - string_format(p, LOG_BUFFER_SIZE - g->ptr, - "Envelope-from: <%s>\n", sender_address); - while (*p) p++; - g->ptr = p - g->s; + g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + "Envelope-from: <%s>\n", sender_address); + if (g2) g = g2; /* List up to 5 recipients */ - string_format(p, LOG_BUFFER_SIZE - g->ptr, - "Envelope-to: <%s>\n", recipients_list[0].address); - while (*p) p++; - g->ptr = p - g->s; + g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + "Envelope-to: <%s>\n", recipients_list[0].address); + if (g2) g = g2; for (i = 1; i < recipients_count && i < 5; i++) { - string_format(p, LOG_BUFFER_SIZE - g->ptr, " <%s>\n", - recipients_list[i].address); - while (*p) p++; - g->ptr = p - g->s; + g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + " <%s>\n", recipients_list[i].address); + if (g2) g = g2; } if (i < recipients_count) { - string_format(p, LOG_BUFFER_SIZE - g->ptr, - " ...\n"); - while (*p) p++; - g->ptr = p - g->s; + g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " ...\n", NULL); + if (g2) g = g2; } } @@ -1086,11 +1090,11 @@ if (flags & LOG_REJECT) for (header_line * h = header_list; h; h = h->next) if (h->text) { - BOOL fitted = string_format(p, LOG_BUFFER_SIZE - g->ptr, - "%c %s", h->type, h->text); - while (*p) p++; - g->ptr = p - g->s; - if (!fitted) /* Buffer is full; truncate */ + g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, + "%c %s", h->type, h->text); + if (g2) + g = g2; + else /* Buffer is full; truncate */ { g->ptr -= 100; /* For message and separator */ if (g->s[g->ptr-1] == '\n') g->ptr--; diff --git a/src/src/lookups/cdb.c b/src/src/lookups/cdb.c index 13fe8b7d2..5cae1535f 100644 --- a/src/src/lookups/cdb.c +++ b/src/src/lookups/cdb.c @@ -184,7 +184,7 @@ cdb_open(uschar *filename, } /* Having got a file open we need the structure to put things in */ - cdbp = store_get(sizeof(struct cdb_state)); + cdbp = store_get(sizeof(struct cdb_state), FALSE); /* store_get() does not return if memory was not available... */ /* preload the structure.... */ cdbp->fileno = fileno; @@ -222,7 +222,7 @@ cdb_open(uschar *filename, /* get a buffer to stash the basic offsets in - this should speed * things up a lot - especially on multiple lookups */ - cdbp->cdb_offsets = store_get(CDB_HASH_TABLE); + cdbp->cdb_offsets = store_get(CDB_HASH_TABLE, FALSE); /* now fill the buffer up... */ if (cdb_bread(fileno, cdbp->cdb_offsets, CDB_HASH_TABLE) == -1) { @@ -365,9 +365,10 @@ if (cdbp->cdb_map != NULL) item_ptr += item_key_len; - /* ... and the returned result */ + /* ... and the returned result. Assume it is not + tainted, lacking any way of telling. */ - *result = store_get(item_dat_len + 1); + *result = store_get(item_dat_len + 1, FALSE); memcpy(*result, item_ptr, item_dat_len); (*result)[item_dat_len] = 0; return OK; @@ -410,31 +411,32 @@ for (int loop = 0; (loop < hash_offlen); ++loop) if (item_key_len == key_len) { /* finally check if key matches */ - uschar * item_key = store_get(key_len); + rmark reset_point = store_mark(); + uschar * item_key = store_get(key_len, TRUE); /* keys liable to be tainted */ if (cdb_bread(cdbp->fileno, item_key, key_len) == -1) return DEFER; - if (Ustrncmp(keystring, item_key, key_len) == 0) { - - /* Reclaim some store */ - store_reset(item_key); - - /* matches - get data length */ - item_dat_len = cdb_unpack(packbuf + 4); - - /* then we build a new result string. We know we have enough - memory so disable Coverity errors about the tainted item_dat_ken */ - - *result = store_get(item_dat_len + 1); - /* coverity[tainted_data] */ - if (cdb_bread(cdbp->fileno, *result, item_dat_len) == -1) - return DEFER; - - /* coverity[tainted_data] */ - (*result)[item_dat_len] = 0; - return OK; - } + if (Ustrncmp(keystring, item_key, key_len) == 0) + { + /* Reclaim some store */ + store_reset(reset_point); + + /* matches - get data length */ + item_dat_len = cdb_unpack(packbuf + 4); + + /* then we build a new result string. We know we have enough + memory so disable Coverity errors about the tainted item_dat_ken */ + + *result = store_get(item_dat_len + 1, FALSE); + /* coverity[tainted_data] */ + if (cdb_bread(cdbp->fileno, *result, item_dat_len) == -1) + return DEFER; + + /* coverity[tainted_data] */ + (*result)[item_dat_len] = 0; + return OK; + } /* Reclaim some store */ - store_reset(item_key); + store_reset(reset_point); } } cur_offset += 8; diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index bdc002d4e..5e97009cc 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -154,7 +154,7 @@ int buflen, bufleft, key_item_len, sep = 0; or less than, the length of the delimited list passed in + 1. */ buflen = length + 3; -key_buffer = store_get(buflen); +key_buffer = store_get(buflen, is_tainted(keystring)); key_buffer[0] = '\0'; diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 70203fa6d..5654b5615 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -549,7 +549,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) /* Reclaim unused memory */ -store_reset(yield->s + yield->ptr + 1); +gstring_release_unused(yield); /* If yield NULL we have not found anything. Otherwise, insert the terminating zero and return the result. */ diff --git a/src/src/lookups/ibase.c b/src/src/lookups/ibase.c index eab3c6ad8..f08f503f0 100644 --- a/src/src/lookups/ibase.c +++ b/src/src/lookups/ibase.c @@ -113,6 +113,7 @@ isc_stmt_handle stmth = NULL; XSQLDA *out_sqlda; XSQLVAR *var; int i; +rmark reset_point; char buffer[256]; ISC_STATUS status[20], *statusp = status; @@ -175,7 +176,7 @@ if (cn) } else { - cn = store_get(sizeof(ibase_connection)); + cn = store_get(sizeof(ibase_connection), FALSE); cn->server = server_copy; cn->dbh = NULL; cn->transh = NULL; @@ -248,7 +249,9 @@ if (isc_dsql_allocate_statement(status, &cn->dbh, &stmth)) goto IBASE_EXIT; } -out_sqlda = store_get(XSQLDA_LENGTH(1)); +/* Lacking any information, assume that the data is untainted */ +reset_point = store_mark(); +out_sqlda = store_get(XSQLDA_LENGTH(1), FALSE); out_sqlda->version = SQLDA_VERSION1; out_sqlda->sqln = 1; @@ -256,7 +259,7 @@ if (isc_dsql_prepare (status, &cn->transh, &stmth, 0, query, 1, out_sqlda)) { isc_interprete(buffer, &statusp); - store_reset(out_sqlda); + reset_point = store_reset(reset_point); out_sqlda = NULL; *errmsg = string_sprintf("Interbase prepare_statement() failed: %s", @@ -268,13 +271,13 @@ if (isc_dsql_prepare /* re-allocate the output structure if there's more than one field */ if (out_sqlda->sqln < out_sqlda->sqld) { - XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld)); + XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld), FALSE); if (isc_dsql_describe (status, &stmth, out_sqlda->version, new_sqlda)) { isc_interprete(buffer, &statusp); isc_dsql_free_statement(status, &stmth, DSQL_drop); - store_reset(out_sqlda); + reset_point = store_reset(reset_point); out_sqlda = NULL; *errmsg = string_sprintf("Interbase describe_statement() failed: %s", buffer); @@ -290,46 +293,46 @@ for (i = 0, var = out_sqlda->sqlvar; i < out_sqlda->sqld; i++, var++) switch (var->sqltype & ~1) { case SQL_VARYING: - var->sqldata = CS store_get(sizeof(char) * var->sqllen + 2); + var->sqldata = CS store_get(sizeof(char) * var->sqllen + 2, FALSE); break; case SQL_TEXT: - var->sqldata = CS store_get(sizeof(char) * var->sqllen); + var->sqldata = CS store_get(sizeof(char) * var->sqllen, FALSE); break; case SQL_SHORT: - var->sqldata = CS store_get(sizeof(short)); + var->sqldata = CS store_get(sizeof(short), FALSE); break; case SQL_LONG: - var->sqldata = CS store_get(sizeof(ISC_LONG)); + var->sqldata = CS store_get(sizeof(ISC_LONG), FALSE); break; #ifdef SQL_INT64 case SQL_INT64: - var->sqldata = CS store_get(sizeof(ISC_INT64)); + var->sqldata = CS store_get(sizeof(ISC_INT64), FALSE); break; #endif case SQL_FLOAT: - var->sqldata = CS store_get(sizeof(float)); + var->sqldata = CS store_get(sizeof(float), FALSE); break; case SQL_DOUBLE: - var->sqldata = CS store_get(sizeof(double)); + var->sqldata = CS store_get(sizeof(double), FALSE); break; #ifdef SQL_TIMESTAMP case SQL_DATE: - var->sqldata = CS store_get(sizeof(ISC_QUAD)); + var->sqldata = CS store_get(sizeof(ISC_QUAD), FALSE); break; #else case SQL_TIMESTAMP: - var->sqldata = CS store_get(sizeof(ISC_TIMESTAMP)); + var->sqldata = CS store_get(sizeof(ISC_TIMESTAMP), FALSE); break; case SQL_TYPE_DATE: - var->sqldata = CS store_get(sizeof(ISC_DATE)); + var->sqldata = CS store_get(sizeof(ISC_DATE), FALSE); break; case SQL_TYPE_TIME: - var->sqldata = CS store_get(sizeof(ISC_TIME)); + var->sqldata = CS store_get(sizeof(ISC_TIME), FALSE); break; #endif } if (var->sqltype & 1) - var->sqlind = (short *) store_get(sizeof(short)); + var->sqlind = (short *) store_get(sizeof(short), FALSE); } /* finally, we're ready to execute the statement */ @@ -411,7 +414,7 @@ if (!result) *errmsg = US "Interbase: no data found"; } else - store_reset(result->s + result->ptr + 1); + gstring_release_unused(result); /* Get here by goto from various error checks. */ @@ -514,7 +517,7 @@ static uschar *ibase_quote(uschar * s, uschar * opt) if (count == 0) return s; - t = quoted = store_get(Ustrlen(s) + count + 1); + t = quoted = store_get(Ustrlen(s) + count + 1, FALSE); while ((c = *s++) != 0) { if (Ustrchr("'", c) != NULL) { diff --git a/src/src/lookups/json.c b/src/src/lookups/json.c index a02b7b9bd..15b861751 100644 --- a/src/src/lookups/json.c +++ b/src/src/lookups/json.c @@ -16,12 +16,14 @@ which is freed once by search_tidyup(). Make the free call a dummy. This burns some 300kB in handling a 37kB JSON file, for the benefit of a fast free. The alternative of staying with malloc is nearly as bad, eyeballing the activity there are 20% the number of free vs. alloc -calls (before the big bunch at the end). */ +calls (before the big bunch at the end). + +Assume that the file is trusted, so no tainting */ static void * json_malloc(size_t nbytes) { -void * p = store_get((int)nbytes); +void * p = store_get((int)nbytes, FALSE); /* debug_printf("%s %d: %p\n", __FUNCTION__, (int)nbytes, p); */ return p; } diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 97ee188d2..5b0cffaf8 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -301,24 +301,18 @@ if (!lcp) than the host name + "ldaps:///" plus : and a port number, say 20 + the length of the host name. What we get should accommodate both, easily. */ - uschar *shost = (host == NULL)? US"" : host; - uschar *init_url = store_get(20 + 3 * Ustrlen(shost)); - uschar *init_ptr; + uschar * shost = host ? host : US""; + rmark reset_point = store_mark(); + gstring * g; /* Handle connection via Unix socket ("ldapi"). We build a basic LDAP URI to contain the path name, with slashes escaped as %2F. */ if (ldapi) { - int ch; - init_ptr = init_url + 8; - Ustrcpy(init_url, "ldapi://"); - while ((ch = *shost++)) - if (ch == '/') - { Ustrncpy(init_ptr, "%2F", 3); init_ptr += 3; } - else - *init_ptr++ = ch; - *init_ptr = 0; + g = string_catn(NULL, US"ldapi://", 8); + for (uschar ch; (ch = *shost); shost++) + g = ch == '/' ? string_catn(g, US"%2F", 3) : string_catn(g, shost, 1); } /* This is not an ldapi call. Just build a URI with the protocol type, host @@ -326,22 +320,22 @@ if (!lcp) else { - init_ptr = Ustrchr(ldap_url, '/'); - Ustrncpy(init_url, ldap_url, init_ptr - ldap_url); - init_ptr = init_url + (init_ptr - ldap_url); - sprintf(CS init_ptr, "//%s:%d/", shost, port); + uschar * init_ptr = Ustrchr(ldap_url, '/'); + g = string_catn(NULL, ldap_url, init_ptr - ldap_url); + g = string_fmt_append(g, "//%s:%d/", shost, port); } + string_from_gstring(g); /* Call ldap_initialize() and check the result */ - DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", init_url); - if ((rc = ldap_initialize(&ld, CS init_url)) != LDAP_SUCCESS) + DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", g->s); + if ((rc = ldap_initialize(&ld, CS g->s)) != LDAP_SUCCESS) { *errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n", - rc, init_url); + rc, g->s); goto RETURN_ERROR; } - store_reset(init_url); /* Might as well save memory when we can */ + store_reset(reset_point); /* Might as well save memory when we can */ /* ------------------------- Not OpenLDAP ---------------------- */ @@ -501,8 +495,8 @@ if (!lcp) /* Now add this connection to the chain of cached connections */ - lcp = store_get(sizeof(LDAP_CONNECTION)); - lcp->host = (host == NULL)? NULL : string_copy(host); + lcp = store_get(sizeof(LDAP_CONNECTION), FALSE); + lcp->host = host ? string_copy(host) : NULL; lcp->bound = FALSE; lcp->user = NULL; lcp->password = NULL; @@ -1004,7 +998,7 @@ if (search_type != SEARCH_LDAP_MULTIPLE && rescount > 1) if (rescount < 1) { - *errmsg = string_sprintf("LDAP search: no results"); + *errmsg = US"LDAP search: no results"; error_yield = FAIL; goto RETURN_ERROR_BREAK; } @@ -1156,6 +1150,7 @@ while (strncmpic(url, US"ldap", 4) != 0) else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF; else { + *errmsg = US"LDAP option REFERRALS is not \"follow\" or \"nofollow\""; DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg); return DEFER; } @@ -1481,7 +1476,7 @@ if (count == 0) return s; /* Get sufficient store to hold the quoted string */ -t = quoted = store_get(len + count + 1); +t = quoted = store_get(len + count + 1, is_tainted(s)); /* Handle plain quote_ldap */ @@ -1536,7 +1531,7 @@ else { if (Ustrchr(LDAP_DN_QUOTE, c) != NULL) { - Ustrncpy(t, "%5C", 3); /* insert \ where needed */ + Ustrncpy(t, US"%5C", 3); /* insert \ where needed */ t += 3; /* fall through to check URL */ } if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ @@ -1553,7 +1548,7 @@ else while (*ss++ != 0) { - Ustrncpy(t, "%5C%20", 6); + Ustrncpy(t, US"%5C%20", 6); t += 6; } } diff --git a/src/src/lookups/lmdb.c b/src/src/lookups/lmdb.c index 931ea5bbe..2976cfac5 100644 --- a/src/src/lookups/lmdb.c +++ b/src/src/lookups/lmdb.c @@ -30,7 +30,7 @@ Lmdbstrct * lmdb_p; int ret, save_errno; const uschar * errstr; -lmdb_p = store_get(sizeof(Lmdbstrct)); +lmdb_p = store_get(sizeof(Lmdbstrct), FALSE); lmdb_p->txn = NULL; if ((ret = mdb_env_create(&db_env))) diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index 8b4459ad1..76b76b8f9 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -78,7 +78,7 @@ FILE *f = (FILE *)handle; BOOL last_was_eol = TRUE; BOOL this_is_eol = TRUE; int old_pool = store_pool; -void *reset_point = NULL; +rmark reset_point = NULL; uschar buffer[4096]; /* Wildcard searches may use up some store, because of expansions. We don't @@ -90,7 +90,7 @@ safely stored in the search pool again. */ if(type == LSEARCH_WILD || type == LSEARCH_NWILD) { store_pool = POOL_MAIN; - reset_point = store_get(0); + reset_point = store_mark(); } filename = filename; /* Keep picky compilers happy */ @@ -241,7 +241,7 @@ for (last_was_eol = TRUE; if (reset_point) { - store_reset(reset_point); + reset_point = store_reset(reset_point); store_pool = old_pool; } @@ -294,7 +294,7 @@ for (last_was_eol = TRUE; yield = string_cat(yield, s); } - store_reset(yield->s + yield->ptr + 1); + gstring_release_unused(yield); *result = string_from_gstring(yield); return OK; } diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index 1984e30b2..460ee2973 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -232,7 +232,7 @@ if (!cn) /* Get store for a new handle, initialize it, and connect to the server */ - mysql_handle = store_get(sizeof(MYSQL)); + mysql_handle = store_get(sizeof(MYSQL), FALSE); mysql_init(mysql_handle); mysql_options(mysql_handle, MYSQL_READ_DEFAULT_GROUP, CS group); if (mysql_real_connect(mysql_handle, @@ -248,7 +248,7 @@ if (!cn) /* Add the connection to the cache */ - cn = store_get(sizeof(mysql_connection)); + cn = store_get(sizeof(mysql_connection), FALSE); cn->server = server_copy; cn->handle = mysql_handle; cn->next = mysql_connections; @@ -366,7 +366,7 @@ if (mysql_result) mysql_free_result(mysql_result); if (result) { *resultptr = string_from_gstring(result); - store_reset(result->s + (result->size = result->ptr + 1)); + gstring_release_unused(result); return OK; } else @@ -432,7 +432,7 @@ while ((c = *t++) != 0) if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++; if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1); +t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); while ((c = *s++) != 0) { diff --git a/src/src/lookups/nis.c b/src/src/lookups/nis.c index d3f0480ac..6f5307f43 100644 --- a/src/src/lookups/nis.c +++ b/src/src/lookups/nis.c @@ -24,7 +24,7 @@ nis_open(uschar *filename, uschar **errmsg) char *nis_domain; if (yp_get_default_domain(&nis_domain) != 0) { - *errmsg = string_sprintf("failed to get default NIS domain"); + *errmsg = US"failed to get default NIS domain"; return NULL; } return nis_domain; diff --git a/src/src/lookups/nisplus.c b/src/src/lookups/nisplus.c index 6a3351ecc..98f3df303 100644 --- a/src/src/lookups/nisplus.c +++ b/src/src/lookups/nisplus.c @@ -195,7 +195,7 @@ if (field_name) *errmsg = string_sprintf("NIS+ field %s not found for %s", field_name, query); else - store_reset(yield->s + yield->ptr + 1); + gstring_release_unused(yield); /* Free result store before finishing. */ @@ -240,7 +240,7 @@ if (opt != NULL) return NULL; /* No options recognized */ while (*t != 0) if (*t++ == '\"') count++; if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1); +t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); while (*s != 0) { diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index d106c519f..4e8cba5ca 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -305,8 +305,8 @@ if (!cn) /* Get store for a new connection, initialize it, and connect to the server */ - oracle_handle = store_get(sizeof(struct cda_def)); - hda = store_get(HDA_SIZE); + oracle_handle = store_get(sizeof(struct cda_def), FALSE); + hda = store_get(HDA_SIZE, FALSE); memset(hda,'\0',HDA_SIZE); /* @@ -329,7 +329,7 @@ if (!cn) /* Add the connection to the cache */ - cn = store_get(sizeof(oracle_connection)); + cn = store_get(sizeof(oracle_connection), FALSE); cn->server = server_copy; cn->handle = oracle_handle; cn->next = oracle_connections; @@ -348,7 +348,7 @@ else /* We have a connection. Open a cursor and run the query */ -cda = store_get(sizeof(Cda_Def)); +cda = store_get(sizeof(Cda_Def), FALSE); if (oopen(cda, oracle_handle, (text *)0, -1, -1, (text *)0, -1) != 0) { @@ -369,8 +369,8 @@ if (oparse(cda, (text *)query, (sb4) -1, /* Find the number of fields returned and sort out their types. If the number is one, we don't add field names to the data. Otherwise we do. */ -def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE); -desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE); +def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE, FALSE); +desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE, FALSE); if ((num_fields = describe_define(cda,def,desc)) == -1) { @@ -465,7 +465,7 @@ if (!result) *errmsg = "ORACLE: no data found"; } else - store_reset(result->s + result->ptr + 1); + gstring_release_unused(result); /* Get here by goto from various error checks. */ @@ -561,7 +561,7 @@ while ((c = *t++) != 0) if (strchr("\n\t\r\b\'\"\\", c) != NULL) count++; if (count == 0) return s; -t = quoted = store_get((int)strlen(s) + count + 1); +t = quoted = store_get((int)strlen(s) + count + 1, is_tainted(s)); while ((c = *s++) != 0) { diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index cf1e1743e..b5f60938e 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -128,6 +128,7 @@ gstring * result = NULL; int yield = DEFER; unsigned int num_fields, num_tuples; pgsql_connection *cn; +rmark reset_point = store_mark(); uschar *server_copy = NULL; uschar *sdata[3]; @@ -238,7 +239,7 @@ if (!cn) if(PQstatus(pg_conn) == CONNECTION_BAD) { - store_reset(server_copy); + reset_point = store_reset(reset_point); *errmsg = string_sprintf("PGSQL connection failed: %s", PQerrorMessage(pg_conn)); PQfinish(pg_conn); @@ -259,7 +260,7 @@ if (!cn) /* Add the connection to the cache */ - cn = store_get(sizeof(pgsql_connection)); + cn = store_get(sizeof(pgsql_connection), FALSE); cn->server = server_copy; cn->handle = pg_conn; cn->next = pgsql_connections; @@ -356,7 +357,7 @@ if (pg_result) PQclear(pg_result); if (result) { - store_reset(result->s + result->ptr + 1); + gstring_release_unused(result); *resultptr = string_from_gstring(result); return OK; } @@ -427,7 +428,7 @@ while ((c = *t++) != 0) if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++; if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1); +t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); while ((c = *s++) != 0) { diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index 6de675787..1b53eed69 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -163,13 +163,13 @@ if (!cn) socket ? redisConnectUnix(CCS socket) : redisConnect(CCS server, port); if (!redis_handle) { - *errmsg = string_sprintf("REDIS connection failed"); + *errmsg = US"REDIS connection failed"; *defer_break = FALSE; goto REDIS_EXIT; } /* Add the connection to the cache */ - cn = store_get(sizeof(redis_connection)); + cn = store_get(sizeof(redis_connection), FALSE); cn->server = server_copy; cn->handle = redis_handle; cn->next = redis_connections; @@ -333,7 +333,7 @@ switch (redis_reply->type) if (result) - store_reset(result->s + result->ptr + 1); + gstring_release_unused(result); else { yield = FAIL; @@ -416,7 +416,7 @@ while ((c = *t++) != 0) if (isspace(c) || c == '\\') count++; if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1); +t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); while ((c = *s++) != 0) { diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index 5da2de814..6200d6c82 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -131,7 +131,7 @@ if (opt != NULL) return NULL; /* No options recognized */ while ((c = *t++) != 0) if (c == '\'') count++; if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1); +t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); while ((c = *s++) != 0) { diff --git a/src/src/macros.h b/src/src/macros.h index 2c7900b83..a94a71f7e 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -110,13 +110,6 @@ don't make the file descriptors two-way. */ #define DEBUG(x) if (debug_selector & (x)) #define HDEBUG(x) if (host_checking || (debug_selector & (x))) -#define PTR_CHK(ptr) \ -do { \ -if ((void *)ptr > (void *)store_get(0)) \ - debug_printf("BUG: ptr '%s' beyond arena at %s:%d\n", \ - mac_expanded_string(ptr), __FUNCTION__, __LINE__); \ -} while(0) - /* The default From: text for DSNs */ #define DEFAULT_DSN_FROM "Mail Delivery System " @@ -1097,4 +1090,10 @@ should not be one active. */ ": 0x18 :session resumed unasked: 0x1A :session resumed unasked" \ ": 0x1C :session resumed: 0x1E :session resumed, also new ticket" +/* Flags for string_vformat */ +#define SVFMT_EXTEND BIT(0) +#define SVFMT_REBUFFER BIT(1) +#define SVFMT_TAINT_NOCHK BIT(2) + + /* End of macros.h */ diff --git a/src/src/malware.c b/src/src/malware.c index 91649cf20..481b46acc 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -836,7 +836,7 @@ badseek: err = errno; malware_daemon_ctx.sock); } - if (!(drweb_fbuf = US malloc(fsize_uint))) + if (!(drweb_fbuf = store_malloc(fsize_uint))) { (void)close(drweb_fd); return m_panic_defer_3(scanent, NULL, @@ -849,7 +849,7 @@ badseek: err = errno; { int err = errno; (void)close(drweb_fd); - free(drweb_fbuf); + store_free(drweb_fbuf); return m_panic_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), @@ -860,11 +860,12 @@ badseek: err = errno; /* send file body to socket */ if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0) { - free(drweb_fbuf); + store_free(drweb_fbuf); return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send file body to socket (%s)", scanner_options), malware_daemon_ctx.sock); } + store_free(drweb_fbuf); } else { @@ -917,7 +918,9 @@ badseek: err = errno; return m_panic_defer_3(scanent, CUS callout_address, US"cannot read report size", malware_daemon_ctx.sock); drweb_slen = ntohl(drweb_slen); - tmpbuf = store_get(drweb_slen); + + /* assume tainted, since it is external input */ + tmpbuf = store_get(drweb_slen, TRUE); /* read report body */ if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo)) @@ -1463,7 +1466,7 @@ badseek: err = errno; /* Local file; so we def want to use_scan_command and don't want to try * passing IP/port combinations */ use_scan_command = TRUE; - cd = (clamd_address *) store_get(sizeof(clamd_address)); + cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE); /* extract socket-path part */ sublist = scanner_options; @@ -1497,7 +1500,7 @@ badseek: err = errno; continue; } - cd = (clamd_address *) store_get(sizeof(clamd_address)); + cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE); /* extract host and port part */ sublist = scanner_options; @@ -1666,7 +1669,7 @@ b_seek: err = errno; if (lseek(clam_fd, 0, SEEK_SET) < 0) goto b_seek; - if (!(clamav_fbuf = US malloc(fsize_uint))) + if (!(clamav_fbuf = store_malloc(fsize_uint))) { (void)close(clam_fd); return m_panic_defer_3(scanent, NULL, @@ -1678,7 +1681,7 @@ b_seek: err = errno; if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) { int err = errno; - free(clamav_fbuf); (void)close(clam_fd); + store_free(clamav_fbuf); (void)close(clam_fd); return m_panic_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), @@ -1693,13 +1696,12 @@ b_seek: err = errno; (send(malware_daemon_ctx.sock, clamav_fbuf, fsize_uint, 0) < 0) || (send(malware_daemon_ctx.sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) { - free(clamav_fbuf); + store_free(clamav_fbuf); return m_panic_defer_3(scanent, NULL, string_sprintf("unable to send file body to socket (%s)", hostname), malware_daemon_ctx.sock); } - - free(clamav_fbuf); + store_free(clamav_fbuf); } else { /* use scan command */ diff --git a/src/src/match.c b/src/src/match.c index 43f5912fd..4cd9259bb 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -541,7 +541,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) if (Ustrcmp(ss, "+caseful") == 0) { check_string_block *cb = (check_string_block *)arg; - Ustrcpy(cb->subject, cb->origsubject); + Ustrcpy(US cb->subject, cb->origsubject); cb->caseless = FALSE; continue; } @@ -666,7 +666,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) so we use the permanent store pool */ store_pool = POOL_PERM; - p = store_get(sizeof(namedlist_cacheblock)); + p = store_get(sizeof(namedlist_cacheblock), FALSE); p->key = string_copy(get_check_key(arg, type)); @@ -1277,6 +1277,7 @@ match_address_list(const uschar *address, BOOL caseless, BOOL expand, { check_address_block ab; unsigned int *local_cache_bits = cache_bits; +int len; /* RFC 2505 recommends that for spam checking, local parts should be caselessly compared. Therefore, Exim now forces the entire address into lower case here, @@ -1285,8 +1286,10 @@ patterns.) Otherwise just the domain is lower cases. A magic item "+caseful" in the list can be used to restore a caseful copy of the local part from the original address. */ -sprintf(CS big_buffer, "%.*s", big_buffer_size - 1, address); -for (uschar * p = big_buffer + Ustrlen(big_buffer) - 1; p >= big_buffer; p--) +if ((len = Ustrlen(address)) > 255) len = 255; +ab.address = string_copyn(address, len); + +for (uschar * p = ab.address + len - 1; p >= ab.address; p--) { if (!caseless && *p == '@') break; *p = tolower(*p); @@ -1307,7 +1310,7 @@ if (expand_setup == 0) /* Set up the data to be passed ultimately to check_address. */ ab.origaddress = address; -ab.address = big_buffer; +/* ab.address is above */ ab.expand_setup = expand_setup; ab.caseless = caseless; diff --git a/src/src/mime.c b/src/src/mime.c index cf537d7c1..d47b56982 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -499,8 +499,8 @@ int rc = OK; uschar * header = NULL; struct mime_boundary_context nested_context; -/* reserve a line buffer to work in */ -header = store_get(MIME_MAX_HEADER_SIZE+1); +/* reserve a line buffer to work in. Assume tainted data. */ +header = store_get(MIME_MAX_HEADER_SIZE+1, TRUE); /* Not actually used at the moment, but will be vital to fixing * some RFC 2046 nonconformance later... */ diff --git a/src/src/moan.c b/src/src/moan.c index cdec74586..fea3683ba 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -308,7 +308,7 @@ if (bounce_return_message) if (bounce_return_body && message_file) { BOOL enddot = f.dot_ends && message_file == stdin; - uschar * buf = store_get(bounce_return_linesize_limit+2); + uschar * buf = store_get(bounce_return_linesize_limit+2, TRUE); if (firstline) fprintf(fp, "%s", CS firstline); diff --git a/src/src/mytypes.h b/src/src/mytypes.h index be11240bf..84baa9eea 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -13,6 +13,8 @@ local_scan.h includes it and exim.h includes them both (to get this earlier). */ #ifndef MYTYPES_H #define MYTYPES_H +# include + #ifndef FALSE # define FALSE 0 #endif @@ -98,18 +100,20 @@ 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) strcat(CS(s),CCS(t)) +#define Ustrcat(s,t) __Ustrcat(s,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) strcpy(CS(s),CCS(t)) +#define Ustrcpy(s,t) __Ustrcpy(s,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) strncat(CS(s),CCS(t),n) +#define Ustrncat(s,t,n) __Ustrncat(s,t,n, __FUNCTION__, __LINE__) #define Ustrncmp(s,t,n) strncmp(CCS(s),CCS(t),n) -#define Ustrncpy(s,t,n) strncpy(CS(s),CCS(t),n) +#define Ustrncpy(s,t,n) __Ustrncpy(s,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) #define CUstrrchr(s,n) CUS strrchr(CCS(s),n) @@ -121,6 +125,38 @@ 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 BOOL is_tainted(const void *); +extern void die_tainted(const uschar *, const uschar *, int); + +static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) +{ +#ifndef COMPILE_UTILITY +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) +{ +#ifndef COMPILE_UTILITY +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) +{ +#ifndef COMPILE_UTILITY +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) +{ +#ifndef COMPILE_UTILITY +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 */ +#endif /* End of mytypes.h */ diff --git a/src/src/os.c b/src/src/os.c index 9c1281a78..c44bd3e99 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -508,7 +508,7 @@ for (struct ifaddrs * ifa = ifalist; ifa; ifa = ifa->ifa_next) /* Create a data block for the address, fill in the data, and put it on the chain. */ - next = store_get(sizeof(ip_address_item)); + next = store_get(sizeof(ip_address_item), FALSE); next->next = NULL; next->port = 0; (void)host_ntoa(-1, ifa->ifa_addr, next->address, NULL); @@ -743,7 +743,7 @@ for (char * cp = buf; cp < buf + ifc.V_ifc_len; cp += len) /* Create a data block for the address, fill in the data, and put it on the chain. */ - next = store_get(sizeof(ip_address_item)); + next = store_get(sizeof(ip_address_item), FALSE); next->next = NULL; next->port = 0; (void)host_ntoa(-1, addrp, next->address, NULL); @@ -775,13 +775,13 @@ interfaces. We just return the loopback address(es). */ ip_address_item * os_common_find_running_interfaces(void) { -ip_address_item *yield = store_get(sizeof(address_item)); +ip_address_item *yield = store_get(sizeof(address_item), FALSE); yield->address = US"127.0.0.1"; yield->port = 0; yield->next = NULL; #if HAVE_IPV6 -yield->next = store_get(sizeof(address_item)); +yield->next = store_get(sizeof(address_item), FALSE); yield->next->address = US"::1"; yield->next->port = 0; yield->next->next = NULL; diff --git a/src/src/parse.c b/src/src/parse.c index 4b0efa0e1..e64cb94a1 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -23,7 +23,7 @@ redundant apparatus. */ address_item *deliver_make_addr(uschar *address, BOOL copy) { -address_item *addr = store_get(sizeof(address_item)); +address_item *addr = store_get(sizeof(address_item), FALSE); addr->next = NULL; addr->parent = NULL; addr->address = address; @@ -618,7 +618,7 @@ uschar * parse_extract_address(uschar *mailbox, uschar **errorptr, int *start, int *end, int *domain, BOOL allow_null) { -uschar *yield = store_get(Ustrlen(mailbox) + 1); +uschar *yield = store_get(Ustrlen(mailbox) + 1, is_tainted(mailbox)); uschar *startptr, *endptr; uschar *s = US mailbox; uschar *t = US yield; @@ -1396,7 +1396,7 @@ for (;;) if (flen <= 0) { - *error = string_sprintf("file name missing after :include:"); + *error = US"file name missing after :include:"; return FF_ERROR; } @@ -1547,7 +1547,7 @@ for (;;) return FF_ERROR; } - filebuf = store_get(statbuf.st_size + 1); + filebuf = store_get(statbuf.st_size + 1, is_tainted(filename)); if (fread(filebuf, 1, statbuf.st_size, f) != statbuf.st_size) { *error = string_sprintf("error while reading included file %s: %s", @@ -1623,7 +1623,7 @@ for (;;) if ((*s == '|' || *s == '/') && (recipient == NULL || domain == 0)) { - uschar *t = store_get(Ustrlen(s) + 1); + uschar *t = store_get(Ustrlen(s) + 1, is_tainted(s)); uschar *p = t; uschar *q = s; while (*q != 0) @@ -1662,7 +1662,7 @@ for (;;) if (syntax_errors != NULL) { - error_block *e = store_get(sizeof(error_block)); + error_block *e = store_get(sizeof(error_block), FALSE); error_block *last = *syntax_errors; if (last == NULL) *syntax_errors = e; else { @@ -1730,6 +1730,7 @@ parse_message_id(uschar *str, uschar **yield, uschar **error) { uschar *domain = NULL; uschar *id; +rmark reset_point; str = skip_comment(str); if (*str != '<') @@ -1742,27 +1743,28 @@ if (*str != '<') for the answer, but it may also be very long if we are processing a header line. Therefore, take care to release unwanted store afterwards. */ -id = *yield = store_get(Ustrlen(str) + 1); +reset_point = store_mark(); +id = *yield = store_get(Ustrlen(str) + 1, is_tainted(str)); *id++ = *str++; str = read_addr_spec(str, id, '>', error, &domain); -if (*error == NULL) +if (!*error) { if (*str != '>') *error = US"Missing '>' after message-id"; else if (domain == NULL) *error = US"domain missing in message-id"; } -if (*error != NULL) +if (*error) { - store_reset(*yield); + store_reset(reset_point); return NULL; } -while (*id != 0) id++; +while (*id) id++; *id++ = *str++; *id++ = 0; -store_reset(id); +store_release_above(id); str = skip_comment(str); return str; diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 9ebcfc1b6..239532bb6 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -238,7 +238,7 @@ debug_printf("\n"); static pdkim_stringlist * pdkim_prepend_stringlist(pdkim_stringlist * base, const uschar * str) { -pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist)); +pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist), FALSE); memset(new_entry, 0, sizeof(pdkim_stringlist)); new_entry->value = string_copy(str); @@ -328,7 +328,7 @@ pdkim_relax_header_n(const uschar * header, int len, BOOL append_crlf) { BOOL past_field_name = FALSE; BOOL seen_wsp = FALSE; -uschar * relaxed = store_get(len+3); +uschar * relaxed = store_get(len+3, TRUE); /* tainted */ uschar * q = relaxed; for (const uschar * p = header; p - header < len; p++) @@ -408,7 +408,7 @@ pdkim_decode_qp(const uschar * str) int nchar = 0; uschar * q; const uschar * p = str; -uschar * n = store_get(Ustrlen(str)+1); +uschar * n = store_get(Ustrlen(str)+1, TRUE); *n = '\0'; q = n; @@ -465,7 +465,7 @@ BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; -sig = store_get(sizeof(pdkim_signature)); +sig = store_get(sizeof(pdkim_signature), FALSE); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -474,7 +474,7 @@ sig->version = 0; sig->keytype = -1; sig->hashtype = -1; -q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1); +q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1, TRUE); /* tainted */ for (uschar * p = raw_hdr; ; p++) { @@ -656,7 +656,7 @@ const uschar * ele; int sep = ';'; pdkim_pubkey * pub; -pub = store_get(sizeof(pdkim_pubkey)); +pub = store_get(sizeof(pdkim_pubkey), TRUE); /* tainted */ memset(pub, 0, sizeof(pdkim_pubkey)); while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0))) @@ -1865,11 +1865,12 @@ pdkim_init_verify(uschar * (*dns_txt_callback)(uschar *), BOOL dot_stuffing) { pdkim_ctx * ctx; -ctx = store_get(sizeof(pdkim_ctx)); +ctx = store_get(sizeof(pdkim_ctx), FALSE); memset(ctx, 0, sizeof(pdkim_ctx)); if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM; -ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN); +/* The line-buffer is for message data, hence tainted */ +ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE); ctx->dns_txt_callback = dns_txt_callback; return ctx; @@ -1891,7 +1892,7 @@ if (!domain || !selector || !privkey) /* Allocate & init one signature struct */ -sig = store_get(sizeof(pdkim_signature)); +sig = store_get(sizeof(pdkim_signature), FALSE); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -1977,7 +1978,7 @@ for (b = ctx->bodyhash; b; b = b->next) DEBUG(D_receive) debug_printf("PDKIM: new bodyhash %d/%d/%ld\n", hashtype, canon_method, bodylength); -b = store_get(sizeof(pdkim_bodyhash)); +b = store_get(sizeof(pdkim_bodyhash), FALSE); b->next = ctx->bodyhash; b->hashtype = hashtype; b->canon_method = canon_method; @@ -2021,7 +2022,8 @@ pdkim_init_context(pdkim_ctx * ctx, BOOL dot_stuffed, { memset(ctx, 0, sizeof(pdkim_ctx)); ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN; -ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN); +/* The line buffer is for message data, hence tainted */ +ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE); DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback; } diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index 53a8a7b45..de64ee428 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -499,7 +499,7 @@ switch (hash) } #define SIGSPACE 128 -sig->data = store_get(SIGSPACE); +sig->data = store_get(SIGSPACE, FALSE); if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0) { @@ -755,7 +755,7 @@ switch (hash) if ( (ctx = EVP_MD_CTX_new()) && EVP_DigestSignInit(ctx, NULL, md, NULL, sign_ctx->key) > 0 && EVP_DigestSign(ctx, NULL, &siglen, NULL, 0) > 0 - && (sig->data = store_get(siglen)) + && (sig->data = store_get(siglen, FALSE)) /* Obtain the signature (slen could change here!) */ && EVP_DigestSign(ctx, sig->data, &siglen, data->data, data->len) > 0 @@ -771,7 +771,7 @@ if ( (ctx = EVP_MD_CTX_create()) && EVP_DigestSignInit(ctx, NULL, md, NULL, sign_ctx->key) > 0 && EVP_DigestSignUpdate(ctx, data->data, data->len) > 0 && EVP_DigestSignFinal(ctx, NULL, &siglen) > 0 - && (sig->data = store_get(siglen)) + && (sig->data = store_get(siglen, FALSE)) /* Obtain the signature (slen could change here!) */ && EVP_DigestSignFinal(ctx, sig->data, &siglen) > 0 diff --git a/src/src/queue.c b/src/src/queue.c index f54124c5e..a124782e9 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -239,7 +239,7 @@ for (; i <= *subcount; i++) Ustrcmp(name + SPOOL_NAME_LENGTH - 2, "-H") == 0) { queue_filename *next = - store_get(sizeof(queue_filename) + Ustrlen(name)); + store_get(sizeof(queue_filename) + Ustrlen(name), is_tainted(name)); Ustrcpy(next->text, name); next->dir_uschar = subdirchar; @@ -457,7 +457,7 @@ for (int i = queue_run_in_order ? -1 : 0; i <= (queue_run_in_order ? -1 : subcount); i++) { - void *reset_point1 = store_get(0); + rmark reset_point1 = store_mark(); DEBUG(D_queue_run) { @@ -521,7 +521,7 @@ for (int i = queue_run_in_order ? -1 : 0; { BOOL wanted = TRUE; BOOL orig_dont_deliver = f.dont_deliver; - void *reset_point2 = store_get(0); + rmark reset_point2 = store_mark(); /* Restore the original setting of dont_deliver after reading the header, so that a setting for a particular message doesn't force it for any that @@ -647,7 +647,7 @@ for (int i = queue_run_in_order ? -1 : 0; if (f.running_in_test_harness) millisleep(100); (void)close(pfd[pipe_read]); rc = deliver_message(fq->text, force_delivery, FALSE); - _exit(rc == DELIVER_NOT_ATTEMPTED); + exim_underbar_exit(rc == DELIVER_NOT_ATTEMPTED); } if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork of delivery process from " @@ -816,7 +816,7 @@ queue_list(int option, uschar **list, int count) { int subcount; int now = (int)time(NULL); -void *reset_point; +rmark reset_point; queue_filename * qf = NULL; uschar subdirs[64]; @@ -828,7 +828,7 @@ if (count > 0) for (int i = 0; i < count; i++) { queue_filename *next = - store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2); + store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2, is_tainted(list[i])); sprintf(CS next->text, "%s-H", list[i]); next->dir_uschar = '*'; next->next = NULL; @@ -851,8 +851,8 @@ if (option >= 8) option -= 8; /* Now scan the chain and print information, resetting store used each time. */ -for (reset_point = store_get(0); - qf; +for (; + qf && (reset_point = store_mark()); spool_clear_header_globals(), store_reset(reset_point), qf = qf->next ) { diff --git a/src/src/rda.c b/src/src/rda.c index 228f57409..42b7b14a5 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -96,37 +96,37 @@ static int rda_exists(uschar *filename, uschar **error) { int rc, saved_errno; -uschar *slash; struct stat statbuf; +uschar * s; if ((rc = Ustat(filename, &statbuf)) >= 0) return FILE_EXIST; saved_errno = errno; -Ustrncpy(big_buffer, filename, big_buffer_size - 3); +s = string_copy(filename); sigalrm_seen = FALSE; if (saved_errno == ENOENT) { - slash = Ustrrchr(big_buffer, '/'); - Ustrcpy(slash+1, "."); + uschar * slash = Ustrrchr(s, '/'); + Ustrcpy(slash+1, US"."); ALARM(30); - rc = Ustat(big_buffer, &statbuf); + rc = Ustat(s, &statbuf); if (rc != 0 && errno == EACCES && !sigalrm_seen) { *slash = 0; - rc = Ustat(big_buffer, &statbuf); + rc = Ustat(s, &statbuf); } saved_errno = errno; ALARM_CLR(0); - DEBUG(D_route) debug_printf("stat(%s)=%d\n", big_buffer, rc); + DEBUG(D_route) debug_printf("stat(%s)=%d\n", s, rc); } if (sigalrm_seen || rc != 0) { - *error = string_sprintf("failed to stat %s (%s)", big_buffer, - sigalrm_seen? "timeout" : strerror(saved_errno)); + *error = string_sprintf("failed to stat %s (%s)", s, + sigalrm_seen? "timeout" : strerror(saved_errno)); return FILE_EXIST_UNCLEAR; } @@ -281,7 +281,7 @@ if (statbuf.st_size > MAX_FILTER_SIZE) /* Read the file in one go in order to minimize the time we have it open. */ -filebuf = store_get(statbuf.st_size + 1); +filebuf = store_get(statbuf.st_size + 1, is_tainted(filename)); if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size) { @@ -366,7 +366,7 @@ if (*filtertype != FILTER_FORWARD) int old_expand_forbid = expand_forbid; DEBUG(D_route) debug_printf("data is %s filter program\n", - (*filtertype == FILTER_EXIM)? "an Exim" : "a Sieve"); + *filtertype == FILTER_EXIM ? "an Exim" : "a Sieve"); /* RDO_FILTER is an "allow" bit */ @@ -377,8 +377,7 @@ if (*filtertype != FILTER_FORWARD) } expand_forbid = - (expand_forbid & ~RDO_FILTER_EXPANSIONS) | - (options & RDO_FILTER_EXPANSIONS); + expand_forbid & ~RDO_FILTER_EXPANSIONS | options & RDO_FILTER_EXPANSIONS; /* RDO_{EXIM,SIEVE}_FILTER are forbid bits */ @@ -473,7 +472,8 @@ if (len == 0) else /* We know we have enough memory so disable the error on "len" */ /* coverity[tainted_data] */ - if (read(fd, *sp = store_get(len), len) != len) return FALSE; + /* We trust the data source, so untainted */ + if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE; return TRUE; } @@ -552,13 +552,12 @@ uschar *data; uschar *readerror = US""; void (*oldsignal)(int); -DEBUG(D_route) debug_printf("rda_interpret (%s): %s\n", - (rdata->isfile)? "file" : "string", rdata->string); +DEBUG(D_route) debug_printf("rda_interpret (%s): '%s'\n", + rdata->isfile ? "file" : "string", string_printing(rdata->string)); /* Do the expansions of the file name or data first, while still privileged. */ -data = expand_string(rdata->string); -if (data == NULL) +if (!(data = expand_string(rdata->string))) { if (f.expand_string_forcedfail) return FF_NOTDELIVERED; *error = string_sprintf("failed to expand \"%s\": %s", rdata->string, @@ -567,7 +566,7 @@ if (data == NULL) } rdata->string = data; -DEBUG(D_route) debug_printf("expanded: %s\n", data); +DEBUG(D_route) debug_printf("expanded: '%s'\n", data); if (rdata->isfile && data[0] != '/') { @@ -767,7 +766,7 @@ if ((pid = fork()) == 0) out: (void)close(fd); search_tidyup(); - _exit(0); + exim_underbar_exit(0); bad: DEBUG(D_rewrite) debug_printf("rda_interpret: failed write to pipe\n"); @@ -802,7 +801,7 @@ if (eblockp) uschar *s; if (!rda_read_string(fd, &s)) goto DISASTER; if (!s) break; - e = store_get(sizeof(error_block)); + e = store_get(sizeof(error_block), FALSE); e->next = NULL; e->text1 = s; if (!rda_read_string(fd, &s)) goto DISASTER; @@ -866,7 +865,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || /* First string is the address; NULL => end of addresses */ if (!rda_read_string(fd, &recipient)) goto DISASTER; - if (recipient == NULL) break; + if (!recipient) break; /* Hang on the end of the chain */ @@ -901,7 +900,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || if (i > 0) { - addr->pipe_expandn = store_get((i+1) * sizeof(uschar *)); + addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), FALSE); addr->pipe_expandn[i] = NULL; while (--i >= 0) addr->pipe_expandn[i] = expandn[i]; } @@ -911,7 +910,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || if (read(fd, &reply_options, sizeof(int)) != sizeof(int)) goto DISASTER; if ((reply_options & REPLY_EXISTS) != 0) { - addr->reply = store_get(sizeof(reply_item)); + addr->reply = store_get(sizeof(reply_item), FALSE); addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0; addr->reply->return_message = (reply_options & REPLY_RETURN) != 0; diff --git a/src/src/readconf.c b/src/src/readconf.c index a7cf03dd3..52c64830e 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -630,7 +630,7 @@ Args: macro_item * macro_create(const uschar * name, const uschar * val, BOOL command_line) { -macro_item * m = store_get(sizeof(macro_item)); +macro_item * m = store_get(sizeof(macro_item), FALSE); READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val); m->next = NULL; @@ -1061,7 +1061,7 @@ for (;;) if (config_lines) save_config_position(config_filename, config_lineno); - save = store_get(sizeof(config_file_item)); + save = store_get(sizeof(config_file_item), FALSE); save->next = config_file_stack; config_file_stack = save; save->file = config_file; @@ -1401,7 +1401,7 @@ Returns: the control block for the parsed rule. static rewrite_rule * readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal) { -rewrite_rule *next = store_get(sizeof(rewrite_rule)); +rewrite_rule *next = store_get(sizeof(rewrite_rule), FALSE); next->next = NULL; next->key = string_dequote(&p); @@ -1603,7 +1603,7 @@ BOOL boolvalue = TRUE; BOOL freesptr = TRUE; optionlist *ol, *ol2; struct passwd *pw; -void *reset_point; +rmark reset_point; int intbase = 0; uschar *inttype = US""; uschar *sptr; @@ -1727,7 +1727,8 @@ switch (type) case opt_gidlist: case opt_rewrite: - reset_point = sptr = read_string(s, name); + reset_point = store_mark(); + sptr = read_string(s, name); /* Having read a string, we now have several different ways of using it, depending on the data type, so do another switch. If keeping the actual @@ -1750,10 +1751,11 @@ switch (type) /* We already have a condition, we're conducting a crude hack to let multiple condition rules be chained together, despite storing them in text form. */ - *str_target = string_copy_malloc( (saved_condition = *str_target) + *str_target = string_copy_perm( (saved_condition = *str_target) ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}", saved_condition, sptr) - : sptr); + : sptr, + FALSE); /* TODO(pdp): there is a memory leak here and just below when we set 3 or more conditions; I still don't understand the store mechanism enough to know @@ -1789,7 +1791,7 @@ switch (type) list_o = string_append_listele(list_o, sep_o, s); if (list_o) - *str_target = string_copy_malloc(string_from_gstring(list_o)); + *str_target = string_copy_perm(string_from_gstring(list_o), FALSE); } else { @@ -1891,7 +1893,7 @@ switch (type) ignore. Also ignore if the value is already set. */ if (pw == NULL) break; - Ustrcpy(name+Ustrlen(name)-4, "group"); + Ustrcpy(name+Ustrlen(name)-4, US"group"); ol2 = find_option(name, oltop, last); if (ol2 != NULL && ((ol2->type & opt_mask) == opt_gid || (ol2->type & opt_mask) == opt_expand_gid)) @@ -2031,7 +2033,7 @@ switch (type) /* Release store if the value of the string doesn't need to be kept. */ - if (freesptr) store_reset(reset_point); + if (freesptr) reset_point = store_reset(reset_point); break; /* Expanded boolean: if no characters follow, or if there are no dollar @@ -2042,10 +2044,10 @@ switch (type) if (*s != 0 && Ustrchr(s, '$') != 0) { sprintf(CS name2, "*expand_%.50s", name); - ol2 = find_option(name2, oltop, last); - if (ol2 != NULL) + if ((ol2 = find_option(name2, oltop, last))) { - reset_point = sptr = read_string(s, name); + reset_point = store_mark(); + sptr = read_string(s, name); if (data_block == NULL) *((uschar **)(ol2->value)) = sptr; else @@ -2983,7 +2985,7 @@ read_named_list(tree_node **anchorp, int *numberp, int max, uschar *s, BOOL forcecache = FALSE; uschar *ss; tree_node *t; -namedlist_block *nb = store_get(sizeof(namedlist_block)); +namedlist_block *nb = store_get(sizeof(namedlist_block), FALSE); if (Ustrncmp(s, "_cache", 6) == 0) { @@ -3001,7 +3003,7 @@ if (*numberp >= max) while (isspace(*s)) s++; ss = s; while (isalnum(*s) || *s == '_') s++; -t = store_get(sizeof(tree_node) + s-ss); +t = store_get(sizeof(tree_node) + s-ss, is_tainted(ss)); Ustrncpy(t->name, ss, s-ss); t->name[s-ss] = 0; while (isspace(*s)) s++; @@ -3123,7 +3125,7 @@ if (pid == 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "tls_require_ciphers invalid: %s", errmsg); fflush(NULL); - _exit(0); + exim_underbar_exit(0); } do { @@ -3326,6 +3328,19 @@ if (f.trusted_config && Ustrcmp(filename, US"/dev/null")) "wrong owner, group, or mode", big_buffer); } +/* Do a dummy store-allocation of a size related to the (toplevel) file size. +This assumes we will need this much storage to handle all the allocations +during startup; it won't help when .include is being used. When it does, it +will cut down on the number of store blocks (and malloc calls, and sbrk +syscalls). It also assume we're on the relevant pool. */ + +if (statbuf.st_size > 8192) + { + rmark r = store_mark(); + store_get((int)statbuf.st_size, FALSE); + store_reset(r); + } + /* Process the main configuration settings. They all begin with a lower case letter. If we see something starting with an upper case letter, it is taken as a macro definition. */ @@ -3697,7 +3712,7 @@ for (driver_info * dd = drivers_available; dd->driver_name[0] != 0; { int len = dd->options_len; d->info = dd; - d->options_block = store_get(len); + d->options_block = store_get(len, FALSE); memcpy(d->options_block, dd->options_block, len); for (int i = 0; i < *(dd->options_count); i++) dd->options[i].type &= ~opt_set; @@ -3809,7 +3824,7 @@ while ((buffer = get_config_line()) != NULL) /* Set up a new driver instance data block on the chain, with its default values installed. */ - d = store_get(instance_size); + d = store_get(instance_size, FALSE); memcpy(d, instance_default, instance_size); *p = d; p = &d->next; @@ -4108,7 +4123,7 @@ while ((p = get_config_line())) const uschar *pp; uschar *error; - next = store_get(sizeof(retry_config)); + next = store_get(sizeof(retry_config), FALSE); next->next = NULL; *chain = next; chain = &(next->next); @@ -4152,7 +4167,7 @@ while ((p = get_config_line())) while (*p != 0) { - retry_rule *rule = store_get(sizeof(retry_rule)); + retry_rule *rule = store_get(sizeof(retry_rule), FALSE); *rchain = rule; rchain = &(rule->next); rule->next = NULL; @@ -4302,7 +4317,7 @@ while(acl_line) if (*p != ':' || name[0] == 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name"); - node = store_get(sizeof(tree_node) + Ustrlen(name)); + node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name)); Ustrcpy(node->name, name); if (!tree_insertnode(&acl_anchor, node)) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, @@ -4390,7 +4405,7 @@ while(next_section[0] != 0) int mid = last/2; int n = Ustrlen(next_section); - if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, "s"); + if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, US"s"); for (;;) { @@ -4449,7 +4464,7 @@ save_config_line(const uschar* line) static config_line_item *current; config_line_item *next; -next = (config_line_item*) store_get(sizeof(config_line_item)); +next = (config_line_item*) store_get(sizeof(config_line_item), FALSE); next->line = string_copy(line); next->next = NULL; diff --git a/src/src/receive.c b/src/src/receive.c index ed2afb317..c7553f802 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -489,7 +489,7 @@ if (recipients_count >= recipients_list_max) recipient_item *oldlist = recipients_list; int oldmax = recipients_list_max; recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50; - recipients_list = store_get(recipients_list_max * sizeof(recipient_item)); + recipients_list = store_get(recipients_list_max * sizeof(recipient_item), FALSE); if (oldlist != NULL) memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item)); } @@ -1677,6 +1677,7 @@ uschar *frozen_by = NULL; uschar *queued_by = NULL; uschar *errmsg; +rmark rcvd_log_reset_point; gstring * g; struct stat statbuf; @@ -1687,6 +1688,7 @@ uschar *user_msg, *log_msg; /* Working header pointers */ +rmark reset_point; header_line *next; /* Flags for noting the existence of certain headers (only one left) */ @@ -1727,7 +1729,7 @@ if (extract_recip || !smtp_input) header. Temporarily mark it as "old", i.e. not to be used. We keep header_last pointing to the end of the chain to make adding headers simple. */ -received_header = header_list = header_last = store_get(sizeof(header_line)); +received_header = header_list = header_last = store_get(sizeof(header_line), FALSE); header_list->next = NULL; header_list->type = htype_old; header_list->text = NULL; @@ -1735,8 +1737,9 @@ header_list->slen = 0; /* Control block for the next header to be read. */ -next = store_get(sizeof(header_line)); -next->text = store_get(header_size); +reset_point = store_mark(); +next = store_get(sizeof(header_line), FALSE); /* not tainted */ +next->text = store_get(header_size, TRUE); /* tainted */ /* Initialize message id to be null (indicating no message read), and the header names list to be the normal list. Indicate there is no data file open @@ -1854,8 +1857,10 @@ for (;;) goto OVERSIZE; header_size *= 2; - if (!store_extend(next->text, oldsize, header_size)) - next->text = store_newblock(next->text, header_size, ptr); + /* The data came from the message, so is tainted. */ + + if (!store_extend(next->text, TRUE, oldsize, header_size)) + next->text = store_newblock(next->text, TRUE, header_size, ptr); } /* Cope with receiving a binary zero. There is dispute about whether @@ -1910,7 +1915,7 @@ for (;;) if (ch == '\n') { message_ended = END_DOT; - store_reset(next); + reset_point = store_reset(reset_point); next = NULL; break; /* End character-reading loop */ } @@ -2016,7 +2021,7 @@ OVERSIZE: if (ptr == 1) { - store_reset(next); + reset_point = store_reset(reset_point); next = NULL; break; } @@ -2045,7 +2050,7 @@ OVERSIZE: next->text[ptr] = 0; next->slen = ptr; - store_reset(next->text + ptr + 1); + store_release_above(next->text + ptr + 1); /* Check the running total size against the overall message size limit. We don't expect to fail here, but if the overall limit is set less than MESSAGE_ @@ -2241,9 +2246,10 @@ OVERSIZE: /* Set up for the next header */ + reset_point = store_mark(); header_size = 256; - next = store_get(sizeof(header_line)); - next->text = store_get(header_size); + next = store_get(sizeof(header_line), FALSE); + next->text = store_get(header_size, TRUE); ptr = 0; had_zero = 0; prevlines_length = 0; @@ -2527,7 +2533,7 @@ if (extract_recip) white space that follows the newline must not be removed - it is part of the header. */ - pp = recipient = store_get(ss - s + 1); + pp = recipient = store_get(ss - s + 1, is_tainted(s)); for (uschar * p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; *pp = 0; @@ -2558,7 +2564,7 @@ if (extract_recip) if (recipient == NULL && Ustrcmp(errmess, "empty address") != 0) { int len = Ustrlen(s); - error_block *b = store_get(sizeof(error_block)); + error_block *b = store_get(sizeof(error_block), FALSE); while (len > 0 && isspace(s[len-1])) len--; b->next = NULL; b->text1 = string_printing(string_copyn(s, len)); @@ -2765,7 +2771,7 @@ function may mess with the real recipients. */ if (LOGGING(received_recipients)) { - raw_recipients = store_get(recipients_count * sizeof(uschar *)); + raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE); for (int i = 0; i < recipients_count; i++) raw_recipients[i] = string_copy(recipients_list[i].address); raw_recipients_count = recipients_count; @@ -3968,6 +3974,7 @@ it first! Include any message id that is in the message - since the syntax of a message id is actually an addr-spec, we can use the parse routine to canonicalize it. */ +rcvd_log_reset_point = store_mark(); g = string_get(256); g = string_append(g, 2, @@ -4231,7 +4238,7 @@ if(cutthrough.cctx.sock >= 0 && cutthrough.delivery) case '4': /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode. ... for which, pass back the exact error */ - if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg); + if (cutthrough.defer_pass) smtp_reply = string_copy_perm(msg, TRUE); cutthrough_done = TMP_REJ; /* Avoid the usual immediate delivery attempt */ break; /* message_id needed for SMTP accept below */ @@ -4241,7 +4248,7 @@ if(cutthrough.cctx.sock >= 0 && cutthrough.delivery) break; /* message_id needed for SMTP accept below */ case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */ - smtp_reply = string_copy_malloc(msg); /* Pass on the exact error */ + smtp_reply = string_copy_perm(msg, TRUE); /* Pass on the exact error */ cutthrough_done = PERM_REJ; break; } @@ -4268,7 +4275,8 @@ if(!smtp_reply) } f.receive_call_bombout = FALSE; -store_reset(g); /* The store for the main log message can be reused */ +/* The store for the main log message can be reused */ +rcvd_log_reset_point = store_reset(rcvd_log_reset_point); /* If the message is frozen, and freeze_tell is set, do the telling. */ diff --git a/src/src/regex.c b/src/src/regex.c index 87d03f363..f9c06b9e3 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -53,7 +53,7 @@ while ((regex_string = string_nextinlist(&list, &sep, NULL, 0))) continue; } - ri = store_get(sizeof(pcre_list)); + ri = store_get(sizeof(pcre_list), FALSE); ri->re = re; ri->pcre_text = regex_string; ri->next = re_list_head; @@ -125,7 +125,7 @@ if (!(re_list_head = compile(*listptr))) return FAIL; /* no regexes -> nothing to do */ /* match each line against all regexes */ -linebuffer = store_get(32767); +linebuffer = store_get(32767, TRUE); /* tainted */ while (fgets(CS linebuffer, 32767, mbox_file)) { if ( mime_stream && mime_current_boundary /* check boundary */ @@ -195,8 +195,8 @@ if (!(f = fopen(CS mime_decoded_filename, "rb"))) return DEFER; } -/* get 32k memory */ -mime_subject = store_get(32767); +/* get 32k memory, tainted */ +mime_subject = store_get(32767, TRUE); mime_subject_len = fread(mime_subject, 1, 32766, f); diff --git a/src/src/retry.c b/src/src/retry.c index 509de123c..d068f547d 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -291,7 +291,7 @@ Returns: nothing void retry_add_item(address_item *addr, uschar *key, int flags) { -retry_item *rti = store_get(sizeof(retry_item)); +retry_item *rti = store_get(sizeof(retry_item), FALSE); host_item * host = addr->host_used; rti->next = addr->retries; @@ -666,7 +666,8 @@ for (int i = 0; i < 3; i++) if (!retry_record) { - retry_record = store_get(sizeof(dbdata_retry) + message_length); + retry_record = store_get(sizeof(dbdata_retry) + message_length, + is_tainted(message)); message_space = message_length; retry_record->first_failed = now; retry_record->last_try = now; @@ -810,7 +811,7 @@ for (int i = 0; i < 3; i++) if (message_length > message_space) { - dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length); + dbdata_retry *newr = store_get(sizeof(dbdata_retry) + message_length, FALSE); memcpy(newr, retry_record, sizeof(dbdata_retry)); retry_record = newr; } diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 221b438b8..f942bec05 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -454,7 +454,7 @@ rewrite_one_header(header_line *h, int flag, { int lastnewline = 0; header_line *newh = NULL; -void *function_reset_point = store_get(0); +rmark function_reset_point = store_mark(); uschar *s = Ustrchr(h->text, ':') + 1; while (isspace(*s)) s++; @@ -475,7 +475,7 @@ while (*s) uschar *sprev; uschar *ss = parse_find_address_end(s, FALSE); uschar *recipient, *new, *errmess; - void *loop_reset_point = store_get(0); + rmark loop_reset_point = store_mark(); BOOL changed = FALSE; int terminator = *ss; int start, end, domain; @@ -496,7 +496,7 @@ while (*s) if (!recipient) { - store_reset(loop_reset_point); + loop_reset_point = store_reset(loop_reset_point); continue; } @@ -543,7 +543,7 @@ while (*s) if (changed && ((is_recipient && !f.allow_unqualified_recipient) || (!is_recipient && !f.allow_unqualified_sender))) { - store_reset(loop_reset_point); + loop_reset_point = store_reset(loop_reset_point); continue; } } @@ -575,7 +575,7 @@ while (*s) point, because we may have a rewritten line from a previous time round the loop. */ - if (!changed) store_reset(loop_reset_point); + if (!changed) loop_reset_point = store_reset(loop_reset_point); /* If the address has changed, create a new header containing the rewritten address. We do not need to set the chain pointers at this @@ -592,9 +592,9 @@ while (*s) int newlen = Ustrlen(new); int oldlen = end - start; - header_line *prev = (newh == NULL)? h : newh; - uschar *newt = store_malloc(prev->slen - oldlen + newlen + 4); - uschar *newtstart = newt; + header_line * prev = newh ? newh : h; + uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, TRUE); + uschar * newtstart = newt; int type = prev->type; int slen = prev->slen - oldlen + newlen; @@ -633,7 +633,7 @@ while (*s) if (*p != '\n') { lastnewline = newt - newtstart; - Ustrcat(newt, "\n\t"); + Ustrcat(newt, US"\n\t"); slen += 2; } } @@ -656,11 +656,11 @@ while (*s) rewritten copy from a previous time round this loop. */ store_reset(function_reset_point); - newh = store_get(sizeof(header_line)); + function_reset_point = store_mark(); + newh = store_get(sizeof(header_line), FALSE); newh->type = type; newh->slen = slen; newh->text = string_copyn(newtstart, slen); - store_free(newtstart); /* Set up for scanning the rest of the header */ diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index 5d527d44b..f7085007a 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -46,7 +46,7 @@ rfc2047_qpdecode(uschar *string, uschar **ptrptr) int len = 0; uschar *ptr; -ptr = *ptrptr = store_get(Ustrlen(string) + 1); /* No longer than this */ +ptr = *ptrptr = store_get(Ustrlen(string) + 1, is_tainted(string)); /* No longer than this */ while (*string != 0) { @@ -208,7 +208,7 @@ building the result as we go. The result may be longer than the input if it is translated into a multibyte code such as UTF-8. That's why we use the dynamic string building code. */ -yield = store_get(sizeof(gstring) + ++size); +yield = store_get(sizeof(gstring) + ++size, is_tainted(string)); yield->size = size; yield->ptr = 0; yield->s = US(yield + 1); @@ -222,6 +222,7 @@ while (mimeword) if (mimeword != string) yield = string_catn(yield, string, mimeword - string); +/*XXX that might have to convert an untainted string to a tainted one */ /* Do a charset translation if required. This is supported only on hosts that have the iconv() function. Translation errors set error, but carry on, diff --git a/src/src/route.c b/src/src/route.c index b6930493f..41716bc0e 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -737,21 +737,20 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) { exim_setugid(uid, gid, TRUE, string_sprintf("require_files check, file=%s", ss)); - if (route_check_access(ss, uid, gid, 4)) _exit(0); + if (route_check_access(ss, uid, gid, 4)) + exim_underbar_exit(0); DEBUG(D_route) debug_printf("route_check_access() failed\n"); - _exit(1); + exim_underbar_exit(1); } /* In the parent, wait for the child to finish */ while (waitpid(pid, &status, 0) < 0) - { if (errno != EINTR) /* unexpected error, interpret as failure */ { status = 1; break; } - } signal(SIGCHLD, oldsignal); /* restore */ if ((status == 0) == invert) return SKIP; @@ -1101,7 +1100,7 @@ route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid) BOOL cache_set = (Ustrcmp(lastname, s) == 0); DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s, - cache_set? "using cached result" : "cache not available"); + cache_set ? "using cached result" : "cache not available"); if (!cache_set) { @@ -1115,7 +1114,7 @@ if (!cache_set) return TRUE; } - (void)string_format(lastname, sizeof(lastname), "%s", s); + string_format_nt(lastname, sizeof(lastname), "%s", s); /* Force failure if string length is greater than given maximum */ @@ -1474,13 +1473,15 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) } if (!(node = tree_search(*root, name))) - { - node = store_get(sizeof(tree_node) + Ustrlen(name)); + { /* name should never be tainted */ + node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE); Ustrcpy(node->name, name); (void)tree_insertnode(root, node); } node->data.ptr = US val; - DEBUG(D_route) debug_printf("set r_%s = '%s'\n", name, val); + DEBUG(D_route) debug_printf("set r_%s%s = '%s'%s\n", + name, is_tainted(name)?" (tainted)":"", + val, is_tainted(val)?" (tainted)":""); /* All expansions after this point need visibility of that variable */ router_var = *root; @@ -1789,9 +1790,10 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) /* If succeeded while verifying but fail_verify is set, convert into a failure, and take it off the local or remote delivery list. */ - if (((verify == v_sender && r->fail_verify_sender) || - (verify == v_recipient && r->fail_verify_recipient)) && - (yield == OK || yield == PASS)) + if ( ( verify == v_sender && r->fail_verify_sender + || verify == v_recipient && r->fail_verify_recipient + ) + && (yield == OK || yield == PASS)) { addr->message = string_sprintf("%s router forced verify failure", r->name); if (*paddr_remote == addr) *paddr_remote = addr->next; @@ -1808,7 +1810,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) HDEBUG(D_route) { debug_printf("%s router %s for %s\n", r->name, - (yield == PASS)? "passed" : "declined", addr->address); + yield == PASS ? "passed" : "declined", addr->address); if (Ustrcmp(old_domain, addr->domain) != 0) debug_printf("domain %s rewritten\n", old_domain); } @@ -1872,12 +1874,8 @@ if (!r) if (yield == DEFER) { - HDEBUG(D_route) - { - debug_printf("%s router: defer for %s\n", r->name, addr->address); - debug_printf(" message: %s\n", (addr->message == NULL)? - US"" : addr->message); - } + HDEBUG(D_route) debug_printf("%s router: defer for %s\n message: %s\n", + r->name, addr->address, addr->message ? addr->message : US""); goto ROUTE_EXIT; } diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index 33939be4c..2471f2fb7 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -468,7 +468,7 @@ if (rc != OK) return rc; /* Get store in which to preserve the original host item, chained on to the address. */ -addr->host_list = store_get(sizeof(host_item)); +addr->host_list = store_get(sizeof(host_item), FALSE); addr->host_list[0] = h; /* Fill in the transport and queue the address for delivery. */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index ecc604278..fb25e695b 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -149,7 +149,7 @@ if (verify_check_this_host(CUSS&rblock->ignore_target_hosts, /* Set up a host item */ -h = store_get(sizeof(host_item)); +h = store_get(sizeof(host_item), FALSE); h->next = NULL; h->address = string_copy(ip); diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index 13849f9f1..4ceb1f59a 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -160,7 +160,7 @@ uschar *reply; uschar *hostname, *reroute, *domain; const uschar *listptr; uschar host_buffer[256]; -host_item *host = store_get(sizeof(host_item)); +host_item *host = store_get(sizeof(host_item), FALSE); address_item *new_addr; iplookup_router_options_block *ob = (iplookup_router_options_block *)(rblock->options_block); @@ -176,7 +176,7 @@ pw = pw; DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n", rblock->name, addr->address, addr->domain); -reply = store_get(256); +reply = store_get(256, TRUE); /* tainted data */ /* Build the query string to send. If not explicitly given, a default of "user@domain user@domain" is used. */ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index f7cca3a42..c9ddd2786 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -401,7 +401,7 @@ if (transport && transport->info->local) if (hostlist[0]) { host_item *h; - addr->host_list = h = store_get(sizeof(host_item)); + addr->host_list = h = store_get(sizeof(host_item), FALSE); h->name = string_copy(hostlist); h->address = NULL; h->port = PORT_NONE; diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 09f15d035..6bbbf37e2 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -699,10 +699,10 @@ address. Otherwise, if a local qualify_domain is provided, set that up. */ if (ob->qualify_preserve_domain) qualify_domain_recipient = addr->domain; -else if (ob->qualify_domain != NULL) +else if (ob->qualify_domain) { uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc); - if (new_qdr == NULL) return xrc; + if (!new_qdr) return xrc; qualify_domain_recipient = new_qdr; } @@ -713,16 +713,8 @@ redirect.check_owner = ob->check_owner; redirect.check_group = ob->check_group; redirect.pw = pw; -if (ob->file != NULL) - { - redirect.string = ob->file; - redirect.isfile = TRUE; - } -else - { - redirect.string = ob->data; - redirect.isfile = FALSE; - } +redirect.string = (redirect.isfile = (ob->file != NULL)) + ? ob->file : ob->data; frc = rda_interpret(&redirect, options, ob->include_directory, ob->sieve_vacation_directory, ob->sieve_enotify_mailto_owner, @@ -738,104 +730,104 @@ For FAIL and FREEZE we honour any previously set up deliveries by a filter. */ switch (frc) { case FF_NONEXIST: - addr->message = addr->user_message = NULL; - return DECLINE; + addr->message = addr->user_message = NULL; + return DECLINE; case FF_BLACKHOLE: - DEBUG(D_route) debug_printf("address :blackhole:d\n"); - generated = NULL; - discarded = US":blackhole:"; - frc = FF_DELIVERED; - break; + DEBUG(D_route) debug_printf("address :blackhole:d\n"); + generated = NULL; + discarded = US":blackhole:"; + frc = FF_DELIVERED; + break; - /* FF_DEFER and FF_FAIL can arise only as a result of explicit commands - (:defer: or :fail: in an alias file or "fail" in a filter). If a configured - message was supplied, allow it to be included in an SMTP response after - verifying. Remove any SMTP code if it is not allowed. */ + /* FF_DEFER and FF_FAIL can arise only as a result of explicit commands + (:defer: or :fail: in an alias file or "fail" in a filter). If a configured + message was supplied, allow it to be included in an SMTP response after + verifying. Remove any SMTP code if it is not allowed. */ case FF_DEFER: - yield = DEFER; - goto SORT_MESSAGE; + yield = DEFER; + goto SORT_MESSAGE; case FF_FAIL: - if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK) - return xrc; - add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw); - yield = FAIL; + if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK) + return xrc; + add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw); + yield = FAIL; - SORT_MESSAGE: - if (addr->message == NULL) - addr->message = (yield == FAIL)? US"forced rejection" : US"forced defer"; - else - { - int ovector[3]; - if (ob->forbid_smtp_code && - pcre_exec(regex_smtp_code, NULL, CS addr->message, - Ustrlen(addr->message), 0, PCRE_EOPT, - ovector, sizeof(ovector)/sizeof(int)) >= 0) + SORT_MESSAGE: + if (!addr->message) + addr->message = yield == FAIL ? US"forced rejection" : US"forced defer"; + else { - DEBUG(D_route) debug_printf("SMTP code at start of error message " - "is ignored because forbid_smtp_code is set\n"); - addr->message += ovector[1]; + int ovector[3]; + if (ob->forbid_smtp_code && + pcre_exec(regex_smtp_code, NULL, CS addr->message, + Ustrlen(addr->message), 0, PCRE_EOPT, + ovector, sizeof(ovector)/sizeof(int)) >= 0) + { + DEBUG(D_route) debug_printf("SMTP code at start of error message " + "is ignored because forbid_smtp_code is set\n"); + addr->message += ovector[1]; + } + addr->user_message = addr->message; + setflag(addr, af_pass_message); } - addr->user_message = addr->message; - setflag(addr, af_pass_message); - } - return yield; + return yield; - /* As in the case of a system filter, a freeze does not happen after a manual - thaw. In case deliveries were set up by the filter, we set the child count - high so that their completion does not mark the original address done. */ + /* As in the case of a system filter, a freeze does not happen after a manual + thaw. In case deliveries were set up by the filter, we set the child count + high so that their completion does not mark the original address done. */ case FF_FREEZE: - if (!f.deliver_manual_thaw) - { - if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) - != OK) return xrc; - add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw); - if (addr->message == NULL) addr->message = US"frozen by filter"; - addr->special_action = SPECIAL_FREEZE; - addr->child_count = 9999; - return DEFER; - } - frc = FF_NOTDELIVERED; - break; + if (!f.deliver_manual_thaw) + { + if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) + != OK) return xrc; + add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw); + if (addr->message == NULL) addr->message = US"frozen by filter"; + addr->special_action = SPECIAL_FREEZE; + addr->child_count = 9999; + return DEFER; + } + frc = FF_NOTDELIVERED; + break; - /* Handle syntax errors and :include: failures and lookup defers */ + /* Handle syntax errors and :include: failures and lookup defers */ case FF_ERROR: case FF_INCLUDEFAIL: - /* If filtertype is still FILTER_UNSET, it means that the redirection data - was never inspected, so the error was an expansion failure or failure to open - the file, or whatever. In these cases, the existing error message is probably - sufficient. */ + /* If filtertype is still FILTER_UNSET, it means that the redirection data + was never inspected, so the error was an expansion failure or failure to open + the file, or whatever. In these cases, the existing error message is probably + sufficient. */ - if (filtertype == FILTER_UNSET) return DEFER; + if (filtertype == FILTER_UNSET) return DEFER; - /* If it was a filter and skip_syntax_errors is set, we want to set up - the error message so that it can be logged and mailed to somebody. */ + /* If it was a filter and skip_syntax_errors is set, we want to set up + the error message so that it can be logged and mailed to somebody. */ - if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors) - { - eblock = store_get(sizeof(error_block)); - eblock->next = NULL; - eblock->text1 = addr->message; - eblock->text2 = NULL; - addr->message = addr->user_message = NULL; - } + if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors) + { + eblock = store_get(sizeof(error_block), FALSE); + eblock->next = NULL; + eblock->text1 = addr->message; + eblock->text2 = NULL; + addr->message = addr->user_message = NULL; + } - /* Otherwise set up the error for the address and defer. */ + /* Otherwise set up the error for the address and defer. */ - else - { - addr->basic_errno = ERRNO_BADREDIRECT; - addr->message = string_sprintf("error in %s %s: %s", - (filtertype != FILTER_FORWARD)? "filter" : "redirect", - (ob->data == NULL)? "file" : "data", - addr->message); - return DEFER; - } + else + { + addr->basic_errno = ERRNO_BADREDIRECT; + addr->message = string_sprintf("error in %s %s: %s", + filtertype == FILTER_FORWARD ? "redirect" : "filter", + ob->data ? "data" : "file", + addr->message); + return DEFER; + } } diff --git a/src/src/routers/rf_change_domain.c b/src/src/routers/rf_change_domain.c index 71a6a106c..39c41fc9d 100644 --- a/src/src/routers/rf_change_domain.c +++ b/src/src/routers/rf_change_domain.c @@ -35,7 +35,7 @@ void rf_change_domain(address_item *addr, const uschar *domain, BOOL rewrite, address_item **addr_new) { -address_item *parent = store_get(sizeof(address_item)); +address_item *parent = store_get(sizeof(address_item), FALSE); uschar *at = Ustrrchr(addr->address, '@'); uschar *address = string_sprintf("%.*s@%s", (int)(at - addr->address), addr->address, domain); diff --git a/src/src/routers/rf_get_munge_headers.c b/src/src/routers/rf_get_munge_headers.c index f08b55adc..5c95d2d1c 100644 --- a/src/src/routers/rf_get_munge_headers.c +++ b/src/src/routers/rf_get_munge_headers.c @@ -59,7 +59,7 @@ if (rblock->extra_headers) shared with other addresses. The output function outputs them in reverse order. */ - header_line * h = store_get(sizeof(header_line)); + header_line * h = store_get(sizeof(header_line), FALSE); /* We used to use string_sprintf() to add the newline if needed, but that causes problems if the header line is exceedingly long (e.g. adding @@ -69,7 +69,7 @@ if (rblock->extra_headers) h->text = s; else { - h->text = store_get(slen+2); + h->text = store_get(slen+2, is_tainted(s)); memcpy(h->text, s, slen); h->text[slen++] = '\n'; h->text[slen] = 0; diff --git a/src/src/routers/rf_get_transport.c b/src/src/routers/rf_get_transport.c index 353c47041..4a43818ff 100644 --- a/src/src/routers/rf_get_transport.c +++ b/src/src/routers/rf_get_transport.c @@ -45,9 +45,9 @@ rf_get_transport(uschar *tpname, transport_instance **tpptr, address_item *addr, uschar *ss; BOOL expandable; -if (tpname == NULL) +if (!tpname) { - if (require_name == NULL) return TRUE; + if (!require_name) return TRUE; addr->basic_errno = ERRNO_BADTRANSPORT; addr->message = string_sprintf("%s unset in %s router", require_name, router_name); @@ -59,16 +59,25 @@ if (*tpptr != NULL && !expandable) return TRUE; if (expandable) { - ss = expand_string(tpname); - if (ss == NULL) + if (!(ss = expand_string(tpname))) { addr->basic_errno = ERRNO_BADTRANSPORT; addr->message = string_sprintf("failed to expand transport " "\"%s\" in %s router: %s", tpname, router_name, expand_string_message); return FALSE; } + if (is_tainted(ss)) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "attempt to use tainted value '%s' from '%s' for transport", ss, tpname); + addr->basic_errno = ERRNO_BADTRANSPORT; + /* Avoid leaking info to an attacker */ + addr->message = US"internal configuration error"; + return FALSE; + } } -else ss = tpname; +else + ss = tpname; for (transport_instance * tp = transports; tp; tp = tp->next) if (Ustrcmp(tp->name, ss) == 0) diff --git a/src/src/routers/rf_queue_add.c b/src/src/routers/rf_queue_add.c index 99de7b09c..938eee30a 100644 --- a/src/src/routers/rf_queue_add.c +++ b/src/src/routers/rf_queue_add.c @@ -96,7 +96,7 @@ DEBUG(D_route) { debug_printf("queued for %s transport: local_part = %s\ndomain = %s\n" " errors_to=%s\n", - (addr->transport == NULL)? US"" : addr->transport->name, + addr->transport ? addr->transport->name : US"", addr->local_part, addr->domain, addr->prop.errors_address); debug_printf(" domain_data=%s localpart_data=%s\n", addr->prop.domain_data, addr->prop.localpart_data); diff --git a/src/src/search.c b/src/src/search.c index 92b95a90b..df10f773a 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -41,7 +41,7 @@ static int open_filecount = 0; /* Allow us to reset store used for lookups and lookup caching */ -static void *search_reset_point = NULL; +static rmark search_reset_point = NULL; @@ -269,8 +269,7 @@ open_filecount = 0; for (int i = 0; i < lookup_list_count; i++) if (lookup_list[i]->tidy) (lookup_list[i]->tidy)(); -if (search_reset_point) store_reset(search_reset_point); -search_reset_point = NULL; +if (search_reset_point) search_reset_point = store_reset(search_reset_point); store_pool = old_pool; } @@ -339,7 +338,7 @@ int old_pool = store_pool; /* Change to the search store pool and remember our reset point */ store_pool = POOL_SEARCH; -if (search_reset_point == NULL) search_reset_point = store_get(0); +if (!search_reset_point) search_reset_point = store_mark(); DEBUG(D_lookup) debug_printf_indent("search_open: %s \"%s\"\n", lk->name, filename ? filename : US"NULL"); @@ -418,8 +417,8 @@ count alone. */ if (!t) { - t = store_get(sizeof(tree_node) + Ustrlen(keybuffer)); - t->data.ptr = c = store_get(sizeof(search_cache)); + t = store_get(sizeof(tree_node) + Ustrlen(keybuffer), FALSE); + t->data.ptr = c = store_get(sizeof(search_cache), FALSE); c->item_cache = NULL; Ustrcpy(t->name, keybuffer); tree_insertnode(&search_tree, t); @@ -537,7 +536,7 @@ else } else { - e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len); + e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring)); e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; e->ptr = data; t = (tree_node *)(e+1); @@ -685,7 +684,8 @@ else if (partial >= 0) if (affixlen == 0) keystring2 = keystring; else { - keystring2 = store_get(len + affixlen + 1); + keystring2 = store_get(len + affixlen + 1, + is_tainted(keystring) || is_tainted(affix)); Ustrncpy(keystring2, affix, affixlen); Ustrcpy(keystring2 + affixlen, keystring); DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2); diff --git a/src/src/setenv.c b/src/src/setenv.c index 9a05b64b9..027483019 100644 --- a/src/src/setenv.c +++ b/src/src/setenv.c @@ -17,7 +17,7 @@ setenv(const char * name, const char * val, int overwrite) uschar * s; if (Ustrchr(name, '=')) return -1; if (overwrite || !getenv(name)) - putenv(CS string_copy_malloc(string_sprintf("%s=%s", name, val))); + putenv(CS string_copy_perm(string_sprintf("%s=%s", name, val)), FALSE); return 0; } diff --git a/src/src/sieve.c b/src/src/sieve.c index 2317f2eeb..5e8d1e6f4 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -245,7 +245,7 @@ for (int pass = 0; pass <= 1; pass++) dst->length=0; else { - dst->character=store_get(dst->length+1); /* plus one for \0 */ + dst->character = store_get(dst->length+1, is_tainted(src->character)); /* plus one for \0 */ new=dst->character; } for (const uschar * start = src->character, * end = start + src->length; @@ -444,9 +444,9 @@ if (*uri && *uri!='?') filter->errmsg=US"Invalid URI encoding"; return -1; } - new=store_get(sizeof(string_item)); - new->text=store_get(to.length+1); - if (to.length) memcpy(new->text,to.character,to.length); + new=store_get(sizeof(string_item), FALSE); + new->text = store_get(to.length+1, is_tainted(to.character)); + if (to.length) memcpy(new->text, to.character, to.length); new->text[to.length]='\0'; new->next=*recipient; *recipient=new; @@ -502,9 +502,9 @@ if (*uri=='?') } if (hname.length==2 && strcmpic(hname.character, US"to")==0) { - new=store_get(sizeof(string_item)); - new->text=store_get(hvalue.length+1); - if (hvalue.length) memcpy(new->text,hvalue.character,hvalue.length); + new=store_get(sizeof(string_item), FALSE); + new->text = store_get(hvalue.length+1, is_tainted(hvalue.character)); + if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length); new->text[hvalue.length]='\0'; new->next=*recipient; *recipient=new; @@ -1087,7 +1087,8 @@ uschar *errmsg; value->length=0; value->character=(uschar*)0; -t=r=s=expand_string(string_sprintf("$rheader_%s",quote(header))); +t = r = s = expand_string(string_sprintf("$rheader_%s",quote(header))); +if (!t) return; while (*r==' ' || *r=='\t') ++r; while (*r) { @@ -1728,7 +1729,7 @@ if (*filter->pc=='[') /* string list */ struct String *new; dataCapacity = dataCapacity ? dataCapacity * 2 : 4; - new = store_get(sizeof(struct String) * dataCapacity); + new = store_get(sizeof(struct String) * dataCapacity, FALSE); if (d) memcpy(new,d,sizeof(struct String)*dataLength); d = new; @@ -1766,15 +1767,13 @@ if (*filter->pc=='[') /* string list */ } else /* single string */ { - if ((d=store_get(sizeof(struct String)*2))==(struct String*)0) - { + if (!(d=store_get(sizeof(struct String)*2, FALSE))) return -1; - } + m=parse_string(filter,&d[0]); if (m==-1) - { return -1; - } + else if (m==0) { filter->pc=orig; @@ -2119,8 +2118,7 @@ if (parse_identifier(filter,CUS "address")) if (exec) { /* We are only interested in addresses below, so no MIME decoding */ - header_value=expand_string(string_sprintf("$rheader_%s",quote(h))); - if (header_value == NULL) + if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h))))) { filter->errmsg=CUS "header string expansion failed"; return -1; @@ -2227,8 +2225,8 @@ else if (parse_identifier(filter,CUS "exists")) { uschar *header_def; - header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h))); - if (header_def == NULL) + header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h))); + if (!header_def) { filter->errmsg=CUS "header string expansion failed"; return -1; @@ -2311,8 +2309,8 @@ else if (parse_identifier(filter,CUS "header")) uschar *header_def; expand_header(&header_value,h); - header_def=expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h))); - if (header_value.character == NULL || header_def == NULL) + header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h))); + if (!header_value.character || !header_def) { filter->errmsg=CUS "header string expansion failed"; return -1; @@ -2495,7 +2493,7 @@ else if (parse_identifier(filter,CUS "envelope")) } if (exec && envelopeExpr) { - if ((envelope=expand_string(US envelopeExpr)) == NULL) + if (!(envelope=expand_string(US envelopeExpr))) { filter->errmsg=CUS "header string expansion failed"; return -1; @@ -2992,7 +2990,13 @@ while (*filter->pc) subject.character=(uschar*)0; body.length=-1; body.character=(uschar*)0; - envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US ""; + envelope_from = sender_address && sender_address[0] + ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US ""; + if (!envelope_from) + { + filter->errmsg=CUS "expansion failure for envelope from"; + return -1; + } for (;;) { if (parse_white(filter)==-1) return -1; @@ -3048,8 +3052,8 @@ while (*filter->pc) if (message.length==-1) message=subject; if (message.length==-1) expand_header(&message,&str_subject); expand_header(&auto_submitted_value,&str_auto_submitted); - auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}")); - if (auto_submitted_value.character == NULL || auto_submitted_def == NULL) + auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}"); + if (!auto_submitted_value.character || !auto_submitted_def) { filter->errmsg=CUS "header string expansion failed"; return -1; @@ -3069,8 +3073,7 @@ while (*filter->pc) if (!already) /* New notification, process it */ { - struct Notification *sent; - sent=store_get(sizeof(struct Notification)); + struct Notification * sent = store_get(sizeof(struct Notification), FALSE); sent->method=method; sent->importance=importance; sent->message=message; @@ -3088,7 +3091,8 @@ while (*filter->pc) int buffer_capacity; f = fdopen(fd, "wb"); - fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character); + fprintf(f,"From: %s\n", from.length == -1 + ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character); for (string_item * p = recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text); fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner); @@ -3100,7 +3104,7 @@ while (*filter->pc) } /* Allocation is larger than necessary, but enough even for split MIME words */ buffer_capacity=32+4*message.length; - buffer=store_get(buffer_capacity); + buffer=store_get(buffer_capacity, TRUE); if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE)); fprintf(f,"\n"); if (body.length>0) fprintf(f,"%s\n",body.character); @@ -3220,9 +3224,9 @@ while (*filter->pc) } for (struct String * a = addresses; a->length != -1; ++a) { - string_item * new = store_get(sizeof(string_item)); + string_item * new = store_get(sizeof(string_item), FALSE); - new->text=store_get(a->length+1); + new->text = store_get(a->length+1, is_tainted(a->character)); if (a->length) memcpy(new->text,a->character,a->length); new->text[a->length]='\0'; new->next=aliases; @@ -3313,8 +3317,8 @@ while (*filter->pc) { uschar *subject_def; - subject_def=expand_string(US"${if def:header_subject {true}{false}}"); - if (Ustrcmp(subject_def,"true")==0) + subject_def = expand_string(US"${if def:header_subject {true}{false}}"); + if (subject_def && Ustrcmp(subject_def,"true")==0) { gstring * g = string_catn(NULL, US"Auto: ", 6); @@ -3337,7 +3341,7 @@ while (*filter->pc) addr->prop.ignore_error = TRUE; addr->next = *generated; *generated = addr; - addr->reply = store_get(sizeof(reply_item)); + addr->reply = store_get(sizeof(reply_item), FALSE); memset(addr->reply,0,sizeof(reply_item)); /* XXX */ addr->reply->to = string_copy(sender_address); if (from.length==-1) @@ -3346,7 +3350,7 @@ while (*filter->pc) addr->reply->from = from.character; /* Allocation is larger than necessary, but enough even for split MIME words */ buffer_capacity=32+4*subject.length; - buffer=store_get(buffer_capacity); + buffer = store_get(buffer_capacity, is_tainted(subject.character)); /* deconst cast safe as we pass in a non-const item */ addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE); addr->reply->oncelog = string_from_gstring(once); @@ -3582,14 +3586,13 @@ options = options; /* Keep picky compilers happy */ error = error; DEBUG(D_route) debug_printf("Sieve: start of processing\n"); -sieve.filter=filter; +sieve.filter = filter; -if (vacation_directory == NULL) +if (!vacation_directory) sieve.vacation_directory = NULL; else { - sieve.vacation_directory=expand_string(vacation_directory); - if (sieve.vacation_directory == NULL) + if (!(sieve.vacation_directory = expand_string(vacation_directory))) { *error = string_sprintf("failed to expand \"%s\" " "(sieve_vacation_directory): %s", vacation_directory, @@ -3598,12 +3601,11 @@ else } } -if (enotify_mailto_owner == NULL) +if (!enotify_mailto_owner) sieve.enotify_mailto_owner = NULL; else { - sieve.enotify_mailto_owner=expand_string(enotify_mailto_owner); - if (sieve.enotify_mailto_owner == NULL) + if (!(sieve.enotify_mailto_owner = expand_string(enotify_mailto_owner))) { *error = string_sprintf("failed to expand \"%s\" " "(sieve_enotify_mailto_owner): %s", enotify_mailto_owner, @@ -3612,7 +3614,8 @@ else } } -sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress; +sieve.useraddress = useraddress + ? useraddress : CUS "$local_part_prefix$local_part$local_part_suffix"; sieve.subaddress = subaddress; #ifdef COMPILE_SYNTAX_CHECKER @@ -3624,12 +3627,12 @@ if (parse_start(&sieve,1,generated)==1) if (sieve.keep) { add_addr(generated,US"inbox",1,0,0,0); - msg = string_sprintf("Implicit keep"); + msg = US"Implicit keep"; r = FF_DELIVERED; } else { - msg = string_sprintf("No implicit keep"); + msg = US"No implicit keep"; r = FF_DELIVERED; } } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 1e478a6ac..24978c86c 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -433,7 +433,7 @@ if (!sender_address /* No transaction in progress */ if (recipients_count > 0) { - raw_recipients = store_get(recipients_count * sizeof(uschar *)); + raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE); for (int i = 0; i < recipients_count; i++) raw_recipients[i] = recipients_list[i].address; raw_recipients_count = recipients_count; @@ -527,8 +527,8 @@ if (rc <= 0) smtp_data_sigint_exit(); smtp_had_error = save_errno; - smtp_read_error = string_copy_malloc( - string_sprintf(" (error: %s)", strerror(save_errno))); + smtp_read_error = string_copy_perm( + string_sprintf(" (error: %s)", strerror(save_errno)), FALSE); } else smtp_had_eof = 1; @@ -898,6 +898,7 @@ va_end(ap); /* This is split off so that verify.c:respond_printf() can, in effect, call smtp_printf(), bearing in mind that in C a vararg function can't directly call another vararg function, only a function which accepts a va_list. */ +/*XXX consider passing caller-info in, for string_vformat-onward */ void smtp_vprintf(const char *format, BOOL more, va_list ap) @@ -905,19 +906,20 @@ smtp_vprintf(const char *format, BOOL more, va_list ap) gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }; BOOL yield; -yield = !! string_vformat(&gs, FALSE, format, ap); +/* Use taint-unchecked routines for writing into big_buffer, trusting +that we'll never expand it. */ + +yield = !! string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap); string_from_gstring(&gs); DEBUG(D_receive) { - void *reset_point = store_get(0); uschar *msg_copy, *cr, *end; msg_copy = string_copy(gs.s); end = msg_copy + gs.ptr; while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */ memmove(cr, cr + 1, (end--) - cr); debug_printf("SMTP>> %s", msg_copy); - store_reset(reset_point); } if (!yield) @@ -1906,11 +1908,7 @@ BOOL yield = fl.helo_accept_junk; /* Discard any previous helo name */ -if (sender_helo_name) - { - store_free(sender_helo_name); - sender_helo_name = NULL; - } +sender_helo_name = NULL; /* Skip tests if junk is permitted. */ @@ -1949,7 +1947,7 @@ if (!yield) /* Save argument if OK */ -if (yield) sender_helo_name = string_copy_malloc(start); +if (yield) sender_helo_name = string_copy_perm(start, TRUE); return yield; } @@ -2021,7 +2019,7 @@ Argument: the stacking pool storage reset point Returns: nothing */ -void +void * smtp_reset(void *reset_point) { recipients_list = NULL; @@ -2131,6 +2129,7 @@ while (acl_warn_logged) store_free(this); } store_reset(reset_point); +return store_mark(); } @@ -2158,7 +2157,7 @@ static int smtp_setup_batch_msg(void) { int done = 0; -void *reset_point = store_get(0); +rmark reset_point = store_mark(); /* Save the line count at the start of each transaction - single commands like HELO and RSET count as whole transactions. */ @@ -2168,7 +2167,7 @@ bsmtp_transaction_linecount = receive_linecount; if ((receive_feof)()) return 0; /* Treat EOF as QUIT */ cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg"); -smtp_reset(reset_point); /* Reset for start of message */ +reset_point = smtp_reset(reset_point); /* Reset for start of message */ /* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE value. The values are 2 larger than the required yield of the function. */ @@ -2193,7 +2192,7 @@ while (done <= 0) case RSET_CMD: cancel_cutthrough_connection(TRUE, US"RSET received"); - smtp_reset(reset_point); + reset_point = smtp_reset(reset_point); bsmtp_transaction_linecount = receive_linecount; break; @@ -2217,7 +2216,7 @@ while (done <= 0) /* Reset to start of message */ cancel_cutthrough_connection(TRUE, US"MAIL received"); - smtp_reset(reset_point); + reset_point = smtp_reset(reset_point); /* Apply SMTP rewrite */ @@ -2482,11 +2481,9 @@ fl.smtputf8_advertised = FALSE; acl_var_c = NULL; -/* Allow for trailing 0 in the command and data buffers. */ +/* Allow for trailing 0 in the command and data buffers. Tainted. */ -if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2))) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "malloc() failed for SMTP command buffer"); +smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE); smtp_cmd_buffer[0] = 0; smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1; @@ -2597,7 +2594,7 @@ if (!f.sender_host_unknown) { #if OPTSTYLE == 1 EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN; - struct ip_options *ipopt = store_get(optlen); + struct ip_options *ipopt = store_get(optlen, FALSE); #elif OPTSTYLE == 2 struct ip_opts ipoptblock; struct ip_opts *ipopt = &ipoptblock; @@ -3506,7 +3503,7 @@ if (code && defaultrespond) va_list ap; va_start(ap, defaultrespond); - g = string_vformat(NULL, TRUE, CS defaultrespond, ap); + g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap); va_end(ap); smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g)); } @@ -3719,7 +3716,7 @@ switch(rc) case OK: if (!au->set_id || set_id) /* Complete success */ { - if (set_id) authenticated_id = string_copy_malloc(set_id); + if (set_id) authenticated_id = string_copy_perm(set_id, TRUE); sender_host_authenticated = au->name; sender_host_auth_pubname = au->public_name; authentication_failed = FALSE; @@ -3740,7 +3737,7 @@ switch(rc) /* Fall through */ case DEFER: - if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); *s = string_sprintf("435 Unable to authenticate at present%s", auth_defer_user_msg); *ss = string_sprintf("435 Unable to authenticate at present%s: %s", @@ -3760,13 +3757,13 @@ switch(rc) break; case FAIL: - if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); *s = US"535 Incorrect authentication data"; *ss = string_sprintf("535 Incorrect authentication data%s", set_id); break; default: - if (set_id) authenticated_fail_id = string_copy_malloc(set_id); + if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); *s = US"435 Internal error"; *ss = string_sprintf("435 Internal error%s: return %d from authentication " "check", set_id, rc); @@ -3878,7 +3875,7 @@ BOOL toomany = FALSE; BOOL discarded = FALSE; BOOL last_was_rej_mail = FALSE; BOOL last_was_rcpt = FALSE; -void *reset_point = store_get(0); +rmark reset_point = store_mark(); DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n"); @@ -3888,7 +3885,7 @@ message. Ditto for EHLO/HELO and for STARTTLS, to allow for going in and out of TLS between messages (an Exim client may do this if it has messages queued up for the host). Note: we do NOT reset AUTH at this point. */ -smtp_reset(reset_point); +reset_point = smtp_reset(reset_point); message_ended = END_NOTSTARTED; chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED; @@ -4205,11 +4202,7 @@ while (done <= 0) &user_msg, &log_msg)) != OK) { done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg); - if (sender_helo_name) - { - store_free(sender_helo_name); - sender_helo_name = NULL; - } + sender_helo_name = NULL; host_build_sender_fullhost(); /* Rebuild */ break; } @@ -4239,7 +4232,9 @@ while (done <= 0) smtp_code = US"250 "; /* Default response code plus space*/ if (!user_msg) { - g = string_fmt_append(NULL, "%.3s %s Hello %s%s%s", + /* sender_host_name below will be tainted, so save on copy when we hit it */ + g = string_get_tainted(24, TRUE); + g = string_fmt_append(g, "%.3s %s Hello %s%s%s", smtp_code, smtp_active_hostname, sender_ident ? sender_ident : US"", @@ -4493,7 +4488,7 @@ while (done <= 0) + (tls_in.active.sock >= 0 ? pcrpted : 0) ]; cancel_cutthrough_connection(TRUE, US"sent EHLO response"); - smtp_reset(reset_point); + reset_point = smtp_reset(reset_point); toomany = FALSE; break; /* HELO/EHLO */ @@ -4548,7 +4543,7 @@ while (done <= 0) obviously need to throw away any previous data. */ cancel_cutthrough_connection(TRUE, US"MAIL received"); - smtp_reset(reset_point); + reset_point = smtp_reset(reset_point); toomany = FALSE; sender_data = recipient_data = NULL; @@ -5424,7 +5419,7 @@ while (done <= 0) incomplete_transaction_log(US"STARTTLS"); cancel_cutthrough_connection(TRUE, US"STARTTLS received"); - smtp_reset(reset_point); + reset_point = smtp_reset(reset_point); toomany = FALSE; cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE; @@ -5472,7 +5467,6 @@ while (done <= 0) cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; if (sender_helo_name) { - store_free(sender_helo_name); sender_helo_name = NULL; host_build_sender_fullhost(); /* Rebuild */ set_process_info("handling incoming TLS connection from %s", @@ -5563,7 +5557,7 @@ while (done <= 0) case RSET_CMD: smtp_rset_handler(); cancel_cutthrough_connection(TRUE, US"RSET received"); - smtp_reset(reset_point); + reset_point = smtp_reset(reset_point); toomany = FALSE; break; @@ -5585,17 +5579,17 @@ while (done <= 0) { uschar buffer[256]; buffer[0] = 0; - Ustrcat(buffer, " AUTH"); + Ustrcat(buffer, US" AUTH"); #ifndef DISABLE_TLS if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) - Ustrcat(buffer, " STARTTLS"); + Ustrcat(buffer, US" STARTTLS"); #endif - Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT"); - Ustrcat(buffer, " NOOP QUIT RSET HELP"); - if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN"); - if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN"); - if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY"); + Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT"); + Ustrcat(buffer, US" NOOP QUIT RSET HELP"); + if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN"); + if (acl_smtp_expn) Ustrcat(buffer, US" EXPN"); + if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY"); smtp_printf("214%s\r\n", FALSE, buffer); } break; @@ -5771,7 +5765,7 @@ while (done <= 0) } enq_end(etrn_serialize_key); - _exit(EXIT_SUCCESS); + exim_underbar_exit(EXIT_SUCCESS); } /* Back in the top level SMTP process. Check that we started a subprocess @@ -5785,10 +5779,10 @@ while (done <= 0) if (smtp_etrn_serialize) enq_end(etrn_serialize_key); } else - { - if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE); - else smtp_user_msg(US"250", user_msg); - } + if (!user_msg) + smtp_printf("250 OK\r\n", FALSE); + else + smtp_user_msg(US"250", user_msg); signal(SIGCHLD, oldsignal); break; diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 4f8a84e2b..ece79de10 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -52,6 +52,17 @@ if (!(expint = expand_string(istring))) return FALSE; } +if (is_tainted(expint)) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "attempt to use tainted value '%s' from '%s' for interface", + expint, istring); + addr->transport_return = PANIC; + addr->message = string_sprintf("failed to expand \"interface\" " + "option for %s: configuration error", msg); + return FALSE; + } + while (isspace(*expint)) expint++; if (*expint == 0) return TRUE; @@ -521,8 +532,14 @@ if (format) gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }; va_list ap; + /* Use taint-unchecked routines for writing into big_buffer, trusting that + we'll never expand the results. Actually, the error-message use - leaving + the results in big_buffer for potential later use - is uncomfortably distant. + XXX Would be better to assume all smtp commands are short, use normal pool + alloc rather than big_buffer, and another global for the data-for-error. */ + va_start(ap, format); - if (!string_vformat(&gs, FALSE, CS format, ap)) + if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing " "SMTP"); va_end(ap); @@ -538,7 +555,7 @@ if (format) if (!flush_buffer(outblock, SCMD_FLUSH)) return -1; } - Ustrncpy(CS outblock->ptr, gs.s, gs.ptr); + Ustrncpy(outblock->ptr, gs.s, gs.ptr); outblock->ptr += gs.ptr; outblock->cmd_count++; gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for error message */ diff --git a/src/src/spam.c b/src/src/spam.c index 7e334852e..4cc4a9ae0 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -293,7 +293,7 @@ start = time(NULL); uschar * s; DEBUG(D_acl) debug_printf_indent("spamd: addr entry '%s'\n", address); - sd = (spamd_address_container *)store_get(sizeof(spamd_address_container)); + sd = store_get(sizeof(spamd_address_container), FALSE); for (sublist = address, args = 0, spamd_param_init(sd); (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0)); @@ -565,7 +565,7 @@ else } Ustrcpy(spam_action_buffer, - spamd_score >= spamd_threshold ? "reject" : "no action"); + spamd_score >= spamd_threshold ? US"reject" : US"no action"); } /* Create report. Since this is a multiline string, diff --git a/src/src/spool_in.c b/src/src/spool_in.c index c5733f13e..101a6c40c 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -185,7 +185,7 @@ BOOL right = buffer[1] == 'Y'; if (n < 5) return FALSE; /* malformed line */ buffer[n-1] = 0; /* Remove \n */ -node = store_get(sizeof(tree_node) + n - 3); +node = store_get(sizeof(tree_node) + n - 3, is_tainted(buffer)); *connect = node; Ustrcpy(node->name, buffer + 3); node->data.ptr = NULL; @@ -411,7 +411,7 @@ n = Ustrlen(big_buffer); if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>') goto SPOOL_FORMAT_ERROR; -sender_address = store_get(n-2); +sender_address = store_get(n-2, TRUE); /* tainted */ Ustrncpy(sender_address, big_buffer+1, n-3); sender_address[n-3] = 0; @@ -440,12 +440,16 @@ by not re-scanning the first two characters. To allow new versions of Exim that add additional flags to interwork with older versions that do not understand them, just ignore any lines starting with "-" that we don't recognize. Otherwise it wouldn't be possible to back off a new -version that left new-style flags written on the spool. */ +version that left new-style flags written on the spool. + +If the line starts with "--" the content of the variable is tainted. */ -p = big_buffer + 2; for (;;) { int len; + BOOL tainted; + uschar * var; + if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; if (big_buffer[0] != '-') break; while ( (len = Ustrlen(big_buffer)) == big_buffer_size-1 @@ -454,7 +458,7 @@ for (;;) { /* buffer not big enough for line; certs make this possible */ uschar * buf; if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR; - buf = store_get_perm(big_buffer_size *= 2); + buf = store_get_perm(big_buffer_size *= 2, FALSE); memcpy(buf, big_buffer, --len); big_buffer = buf; if (Ufgets(big_buffer+len, big_buffer_size-len, fp) == NULL) @@ -462,7 +466,11 @@ for (;;) } big_buffer[len-1] = 0; - switch(big_buffer[1]) + tainted = big_buffer[1] == '-'; + var = big_buffer + (tainted ? 2 : 1); + p = var + 1; + + switch(*var) { case 'a': @@ -477,13 +485,13 @@ for (;;) uschar *name, *endptr; int count; tree_node *node; - endptr = Ustrchr(big_buffer + 6, ' '); + endptr = Ustrchr(var + 5, ' '); if (endptr == NULL) goto SPOOL_FORMAT_ERROR; - name = string_sprintf("%c%.*s", big_buffer[4], - (int)(endptr - big_buffer - 6), big_buffer + 6); + name = string_sprintf("%c%.*s", var[3], + (int)(endptr - var - 5), var + 5); if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR; node = acl_var_create(name); - node->data.ptr = store_get(count + 1); + node->data.ptr = store_get(count + 1, tainted); if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR; ((uschar*)node->data.ptr)[count] = 0; } @@ -494,11 +502,11 @@ for (;;) f.allow_unqualified_sender = TRUE; else if (Ustrncmp(p, "uth_id", 6) == 0) - authenticated_id = string_copy(big_buffer + 9); + authenticated_id = string_copy_taint(var + 8, tainted); else if (Ustrncmp(p, "uth_sender", 10) == 0) - authenticated_sender = string_copy(big_buffer + 13); + authenticated_sender = string_copy_taint(var + 12, tainted); else if (Ustrncmp(p, "ctive_hostname", 14) == 0) - smtp_active_hostname = string_copy(big_buffer + 17); + smtp_active_hostname = string_copy_taint(var + 16, tainted); /* For long-term backward compatibility, we recognize "-acl", which was used before the number of ACL variables changed from 10 to 20. This was @@ -512,7 +520,7 @@ for (;;) unsigned index, count; uschar name[20]; /* Need plenty of space for %u format */ tree_node * node; - if ( sscanf(CS big_buffer + 5, "%u %u", &index, &count) != 2 + if ( sscanf(CS var + 4, "%u %u", &index, &count) != 2 || index >= 20 || count > 16384 /* arbitrary limit on variable size */ ) @@ -522,7 +530,7 @@ for (;;) else (void) string_format(name, sizeof(name), "%c%u", 'm', index - 10); node = acl_var_create(name); - node->data.ptr = store_get(count + 1); + node->data.ptr = store_get(count + 1, tainted); /* We sanity-checked the count, so disable the Coverity error */ /* coverity[tainted_data] */ if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR; @@ -532,12 +540,12 @@ for (;;) case 'b': if (Ustrncmp(p, "ody_linecount", 13) == 0) - body_linecount = Uatoi(big_buffer + 15); + body_linecount = Uatoi(var + 14); else if (Ustrncmp(p, "ody_zerocount", 13) == 0) - body_zerocount = Uatoi(big_buffer + 15); + body_zerocount = Uatoi(var + 14); #ifdef EXPERIMENTAL_BRIGHTMAIL else if (Ustrncmp(p, "mi_verdicts ", 12) == 0) - bmi_verdicts = string_copy(big_buffer + 14); + bmi_verdicts = string_copy_taint(var + 13, tainted); #endif break; @@ -546,16 +554,16 @@ for (;;) f.deliver_firsttime = TRUE; /* Check if the dsn flags have been set in the header file */ else if (Ustrncmp(p, "sn_ret", 6) == 0) - dsn_ret= atoi(CS big_buffer + 8); + dsn_ret= atoi(CS var + 7); else if (Ustrncmp(p, "sn_envid", 8) == 0) - dsn_envid = string_copy(big_buffer + 11); + dsn_envid = string_copy_taint(var + 10, tainted); break; case 'f': if (Ustrncmp(p, "rozen", 5) == 0) { f.deliver_freeze = TRUE; - if (sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at) != 1) + if (sscanf(CS var+6, TIME_T_FMT, &deliver_frozen_at) != 1) goto SPOOL_READ_ERROR; } break; @@ -566,11 +574,11 @@ for (;;) else if (Ustrcmp(p, "ost_lookup_failed") == 0) host_lookup_failed = TRUE; else if (Ustrncmp(p, "ost_auth", 8) == 0) - sender_host_authenticated = string_copy(big_buffer + 11); + sender_host_authenticated = string_copy_taint(var + 10, tainted); else if (Ustrncmp(p, "ost_name", 8) == 0) - sender_host_name = string_copy(big_buffer + 11); + sender_host_name = string_copy_taint(var + 10, tainted); else if (Ustrncmp(p, "elo_name", 8) == 0) - sender_helo_name = string_copy(big_buffer + 11); + sender_helo_name = string_copy_taint(var + 10, tainted); /* We now record the port number after the address, separated by a dot. For compatibility during upgrading, do nothing if there @@ -578,36 +586,37 @@ for (;;) else if (Ustrncmp(p, "ost_address", 11) == 0) { - sender_host_port = host_address_extract_port(big_buffer + 14); - sender_host_address = string_copy(big_buffer + 14); + sender_host_port = host_address_extract_port(var + 13); + sender_host_address = string_copy_taint(var + 13, tainted); } break; case 'i': if (Ustrncmp(p, "nterface_address", 16) == 0) { - interface_port = host_address_extract_port(big_buffer + 19); - interface_address = string_copy(big_buffer + 19); + interface_port = host_address_extract_port(var + 18); + interface_address = string_copy_taint(var + 18, tainted); } else if (Ustrncmp(p, "dent", 4) == 0) - sender_ident = string_copy(big_buffer + 7); + sender_ident = string_copy_taint(var + 6, tainted); break; case 'l': if (Ustrcmp(p, "ocal") == 0) f.sender_local = TRUE; - else if (Ustrcmp(big_buffer, "-localerror") == 0) + else if (Ustrcmp(var, "localerror") == 0) f.local_error_message = TRUE; #ifdef HAVE_LOCAL_SCAN else if (Ustrncmp(p, "ocal_scan ", 10) == 0) - local_scan_data = string_copy(big_buffer + 12); + local_scan_data = string_copy_taint(var + 11, tainted); #endif break; case 'm': - if (Ustrcmp(p, "anual_thaw") == 0) f.deliver_manual_thaw = TRUE; + if (Ustrcmp(p, "anual_thaw") == 0) + f.deliver_manual_thaw = TRUE; else if (Ustrncmp(p, "ax_received_linelength", 22) == 0) - max_received_linelength = Uatoi(big_buffer + 24); + max_received_linelength = Uatoi(var + 23); break; case 'N': @@ -616,11 +625,11 @@ for (;;) case 'r': if (Ustrncmp(p, "eceived_protocol", 16) == 0) - received_protocol = string_copy(big_buffer + 19); + received_protocol = string_copy_taint(var + 18, tainted); else if (Ustrncmp(p, "eceived_time_usec", 17) == 0) { unsigned usec; - if (sscanf(CS big_buffer + 21, "%u", &usec) == 1) + if (sscanf(CS var + 20, "%u", &usec) == 1) received_time.tv_usec = usec; } break; @@ -630,11 +639,11 @@ for (;;) f.sender_set_untrusted = TRUE; #ifdef WITH_CONTENT_SCAN else if (Ustrncmp(p, "pam_bar ", 8) == 0) - spam_bar = string_copy(big_buffer + 10); + spam_bar = string_copy_taint(var + 9, tainted); else if (Ustrncmp(p, "pam_score ", 10) == 0) - spam_score = string_copy(big_buffer + 12); + spam_score = string_copy_taint(var + 11, tainted); else if (Ustrncmp(p, "pam_score_int ", 14) == 0) - spam_score_int = string_copy(big_buffer + 16); + spam_score_int = string_copy_taint(var + 15, tainted); #endif #ifndef COMPILE_UTILITY else if (Ustrncmp(p, "pool_file_wireformat", 20) == 0) @@ -654,22 +663,22 @@ for (;;) if (Ustrncmp(q, "certificate_verified", 20) == 0) tls_in.certificate_verified = TRUE; else if (Ustrncmp(q, "cipher", 6) == 0) - tls_in.cipher = string_copy(big_buffer + 12); + tls_in.cipher = string_copy_taint(var + 11, tainted); # ifndef COMPILE_UTILITY /* tls support fns not built in */ else if (Ustrncmp(q, "ourcert", 7) == 0) - (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert); + (void) tls_import_cert(var + 12, &tls_in.ourcert); else if (Ustrncmp(q, "peercert", 8) == 0) - (void) tls_import_cert(big_buffer + 14, &tls_in.peercert); + (void) tls_import_cert(var + 13, &tls_in.peercert); # endif else if (Ustrncmp(q, "peerdn", 6) == 0) - tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12)); + tls_in.peerdn = string_unprinting(string_copy_taint(var + 11, tainted)); else if (Ustrncmp(q, "sni", 3) == 0) - tls_in.sni = string_unprinting(string_copy(big_buffer + 9)); + tls_in.sni = string_unprinting(string_copy_taint(var + 8, tainted)); else if (Ustrncmp(q, "ocsp", 4) == 0) - tls_in.ocsp = big_buffer[10] - '0'; + tls_in.ocsp = var[9] - '0'; # ifdef EXPERIMENTAL_TLS_RESUME else if (Ustrncmp(q, "resumption", 10) == 0) - tls_in.resumption = big_buffer[16] - 'A'; + tls_in.resumption = var[15] - 'A'; # endif } @@ -730,7 +739,7 @@ DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount); #endif /* COMPILE_UTILITY */ recipients_list_max = rcount; -recipients_list = store_get(rcount * sizeof(recipient_item)); +recipients_list = store_get(rcount * sizeof(recipient_item), FALSE); /* We sanitised the count and know we have enough memory, so disable the Coverity error on recipients_count */ @@ -796,6 +805,9 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) if (*p == ',') { int dummy; +#if !defined (COMPILE_UTILITY) + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 3 spool file\n"); +#endif while (isdigit(*(--p)) || *p == ','); if (*p == ' ') { @@ -808,6 +820,9 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) else if (*p == ' ') { +#if !defined (COMPILE_UTILITY) + DEBUG(D_deliver) debug_printf("**** SPOOL_IN - early Exim 4 spool file\n"); +#endif *p++ = 0; (void)sscanf(CS p, "%d", &pno); } @@ -833,7 +848,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) if (len > 0) { p -= len; - errors_to = string_copy(p); + errors_to = string_copy_taint(p, TRUE); } } @@ -847,7 +862,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) if (len > 0) { p -= len; - orcpt = string_copy(p); + orcpt = string_copy_taint(p, TRUE); } } @@ -865,7 +880,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) big_buffer, errors_to); #endif - recipients_list[recipients_count].address = string_copy(big_buffer); + recipients_list[recipients_count].address = string_copy_taint(big_buffer, TRUE); recipients_list[recipients_count].pno = pno; recipients_list[recipients_count].errors_to = errors_to; recipients_list[recipients_count].orcpt = orcpt; @@ -897,11 +912,11 @@ while ((n = fgetc(fp)) != EOF) if (read_headers) { - h = store_get(sizeof(header_line)); + h = store_get(sizeof(header_line), FALSE); h->next = NULL; h->type = flag[0]; h->slen = n; - h->text = store_get(n+1); + h->text = store_get(n+1, TRUE); /* tainted */ if (h->type == htype_received) received_count++; diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index 188f405c0..caf1712cf 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -38,13 +38,13 @@ uschar *mbox_path; FILE *mbox_file = NULL, *l_data_file = NULL, *yield = NULL; struct stat statbuf; int j; -void *reset_point; +rmark reset_point; mbox_path = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id); if (mbox_fname) *mbox_fname = mbox_path; -reset_point = store_get(0); +reset_point = store_mark(); /* Skip creation if already spooled out as mbox file */ if (!spool_mbox_ok) @@ -210,12 +210,11 @@ malware_ok = 0; if (spool_mbox_ok && !f.no_mbox_unspool) { - uschar *mbox_path; uschar *file_path; struct dirent *entry; DIR *tempdir; - - mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id); + rmark reset_point = store_mark(); + uschar * mbox_path = string_sprintf("%s/scan/%s", spool_directory, spooled_message_id); if (!(tempdir = opendir(CS mbox_path))) { @@ -240,7 +239,7 @@ if (spool_mbox_ok && !f.no_mbox_unspool) /* remove directory */ rmdir(CS mbox_path); - store_reset(mbox_path); + store_reset(reset_point); } spool_mbox_ok = 0; } diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 6ceae38b2..0dfa4de52 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -104,6 +104,13 @@ return fd; +static void +spool_var_write(FILE * fp, const uschar * name, const uschar * val) +{ +if (is_tainted(val)) putc('-', fp); +fprintf(fp, "-%s %s\n", name, val); +} + /************************************************* * Write the header spool file * *************************************************/ @@ -158,36 +165,40 @@ fprintf(fp, "-received_time_usec .%06d\n", (int)received_time.tv_usec); /* If there is information about a sending host, remember it. The HELO data can be set for local SMTP as well as remote. */ -if (sender_helo_name) - fprintf(fp, "-helo_name %s\n", sender_helo_name); +if (sender_helo_name) spool_var_write(fp, US"helo_name", sender_helo_name); if (sender_host_address) { + if (is_tainted(sender_host_address)) putc('-', fp); fprintf(fp, "-host_address %s.%d\n", sender_host_address, sender_host_port); if (sender_host_name) - fprintf(fp, "-host_name %s\n", sender_host_name); + spool_var_write(fp, US"host_name", sender_host_name); if (sender_host_authenticated) - fprintf(fp, "-host_auth %s\n", sender_host_authenticated); + spool_var_write(fp, US"host_auth", sender_host_authenticated); } /* Also about the interface a message came in on */ if (interface_address) + { + if (is_tainted(interface_address)) putc('-', fp); fprintf(fp, "-interface_address %s.%d\n", interface_address, interface_port); + } if (smtp_active_hostname != primary_hostname) - fprintf(fp, "-active_hostname %s\n", smtp_active_hostname); + spool_var_write(fp, US"active_hostname", smtp_active_hostname); /* Likewise for any ident information; for local messages this is likely to be the same as originator_login, but will be different if the originator was root, forcing a different ident. */ -if (sender_ident) fprintf(fp, "-ident %s\n", sender_ident); +if (sender_ident) + spool_var_write(fp, US"ident", sender_ident); /* Ditto for the received protocol */ if (received_protocol) - fprintf(fp, "-received_protocol %s\n", received_protocol); + spool_var_write(fp, US"received_protocol", received_protocol); /* Preserve any ACL variables that are set. */ @@ -205,9 +216,9 @@ fprintf(fp, "-max_received_linelength %d\n", max_received_linelength); if (body_zerocount > 0) fprintf(fp, "-body_zerocount %d\n", body_zerocount); if (authenticated_id) - fprintf(fp, "-auth_id %s\n", authenticated_id); + spool_var_write(fp, US"auth_id", authenticated_id); if (authenticated_sender) - fprintf(fp, "-auth_sender %s\n", authenticated_sender); + spool_var_write(fp, US"auth_sender", authenticated_sender); if (f.allow_unqualified_recipient) fprintf(fp, "-allow_unqualified_recipient\n"); if (f.allow_unqualified_sender) fprintf(fp, "-allow_unqualified_sender\n"); @@ -219,30 +230,30 @@ if (host_lookup_failed) fprintf(fp, "-host_lookup_failed\n"); if (f.sender_local) fprintf(fp, "-local\n"); if (f.local_error_message) fprintf(fp, "-localerror\n"); #ifdef HAVE_LOCAL_SCAN -if (local_scan_data) fprintf(fp, "-local_scan %s\n", local_scan_data); +if (local_scan_data) spool_var_write(fp, US"local_scan", local_scan_data); #endif #ifdef WITH_CONTENT_SCAN -if (spam_bar) fprintf(fp,"-spam_bar %s\n", spam_bar); -if (spam_score) fprintf(fp,"-spam_score %s\n", spam_score); -if (spam_score_int) fprintf(fp,"-spam_score_int %s\n", spam_score_int); +if (spam_bar) spool_var_write(fp, US"spam_bar", spam_bar); +if (spam_score) spool_var_write(fp, US"spam_score", spam_score); +if (spam_score_int) spool_var_write(fp, US"spam_score_int", spam_score_int); #endif if (f.deliver_manual_thaw) fprintf(fp, "-manual_thaw\n"); if (f.sender_set_untrusted) fprintf(fp, "-sender_set_untrusted\n"); #ifdef EXPERIMENTAL_BRIGHTMAIL -if (bmi_verdicts) fprintf(fp, "-bmi_verdicts %s\n", bmi_verdicts); +if (bmi_verdicts) spool_var_write(fp, US"bmi_verdicts", bmi_verdicts); #endif #ifndef DISABLE_TLS if (tls_in.certificate_verified) fprintf(fp, "-tls_certificate_verified\n"); -if (tls_in.cipher) fprintf(fp, "-tls_cipher %s\n", tls_in.cipher); +if (tls_in.cipher) spool_var_write(fp, US"tls_cipher", tls_in.cipher); if (tls_in.peercert) { (void) tls_export_cert(big_buffer, big_buffer_size, tls_in.peercert); - fprintf(fp, "-tls_peercert %s\n", CS big_buffer); + fprintf(fp, "--tls_peercert %s\n", CS big_buffer); } -if (tls_in.peerdn) fprintf(fp, "-tls_peerdn %s\n", string_printing(tls_in.peerdn)); -if (tls_in.sni) fprintf(fp, "-tls_sni %s\n", string_printing(tls_in.sni)); +if (tls_in.peerdn) spool_var_write(fp, US"tls_peerdn", string_printing(tls_in.peerdn)); +if (tls_in.sni) spool_var_write(fp, US"tls_sni", string_printing(tls_in.sni)); if (tls_in.ourcert) { (void) tls_export_cert(big_buffer, big_buffer_size, tls_in.ourcert); diff --git a/src/src/store.c b/src/src/store.c index 94becbb90..7871d0ccc 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim maintainers 2019 */ /* See the file NOTICE for conditions of use and distribution. */ /* Exim gets and frees all its store through these functions. In the original @@ -35,6 +36,13 @@ The following different types of store are recognized: . There is a separate pool (POOL_SEARCH) that is used only for lookup storage. This means it can be freed when search_tidyup() is called to close down all the lookup caching. + +. Orthogonal to the three pool types, there are two classes of memory: untainted + and tainted. The latter is used for values derived from untrusted input, and + the string-expansion mechanism refuses to operate on such values (obviously, + it can expand an untainted value to return a tainted result). The classes + are implemented by duplicating the three pool types. Pool resets are requested + against the nontainted sibling and apply to both siblings. */ @@ -42,6 +50,7 @@ The following different types of store are recognized: /* keep config.h before memcheck.h, for NVALGRIND */ #include "config.h" +#include #include "memcheck.h" @@ -53,13 +62,7 @@ is a constant, the compiler should optimize it to a simple constant wherever it appears (I checked that gcc does do this). */ #define alignment \ - ((sizeof(void *) > sizeof(double))? sizeof(void *) : sizeof(double)) - -/* Size of block to get from malloc to carve up into smaller ones. This -must be a multiple of the alignment. We assume that 8192 is going to be -suitably aligned. */ - -#define STORE_BLOCK_SIZE 8192 + (sizeof(void *) > sizeof(double) ? sizeof(void *) : sizeof(double)) /* store_reset() will not free the following block if the last used block has less than this much left in it. */ @@ -80,32 +83,103 @@ length. */ #define ALIGNED_SIZEOF_STOREBLOCK \ (((sizeof(storeblock) + alignment - 1) / alignment) * alignment) +/* Size of block to get from malloc to carve up into smaller ones. This +must be a multiple of the alignment. We assume that 8192 is going to be +suitably aligned. */ + +#define STORE_BLOCK_SIZE (8192 - ALIGNED_SIZEOF_STOREBLOCK) + /* Variables holding data for the local pools of store. The current pool number is held in store_pool, which is global so that it can be changed from outside. Setting the initial length values to -1 forces a malloc for the first call, even if the length is zero (which is used for getting a point to reset to). */ -int store_pool = POOL_PERM; +int store_pool = POOL_MAIN; -static storeblock *chainbase[3] = { NULL, NULL, NULL }; -static storeblock *current_block[3] = { NULL, NULL, NULL }; -static void *next_yield[3] = { NULL, NULL, NULL }; -static int yield_length[3] = { -1, -1, -1 }; +#define NPOOLS 6 +static storeblock *chainbase[NPOOLS]; +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. */ + +static void * tainted_base = (void *)-1; +static 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 pointer. */ -static int pool_malloc = 0; -static int nonpool_malloc = 0; +static int pool_malloc; +static int nonpool_malloc; /* This variable is set by store_get() to its yield, and by store_reset() to NULL. This enables string_cat() to optimize its store handling for very long strings. That's why the variable is global. */ -void *store_last_get[3] = { NULL, NULL, NULL }; +void *store_last_get[NPOOLS]; + +/* These are purely for stats-gathering */ + +static int nbytes[NPOOLS]; /* current bytes allocated */ +static int maxbytes[NPOOLS]; /* max number reached */ +static int nblocks[NPOOLS]; /* current number of blocks allocated */ +static int maxblocks[NPOOLS]; +static int n_nonpool_blocks; /* current number of direct store_malloc() blocks */ +static int max_nonpool_blocks; +static int max_pool_malloc; /* max value for pool_malloc */ +static int max_nonpool_malloc; /* max value for nonpool_malloc */ + + +static const uschar * pooluse[NPOOLS] = { +[POOL_MAIN] = US"main", +[POOL_PERM] = US"perm", +[POOL_SEARCH] = US"search", +[POOL_TAINT_MAIN] = US"main", +[POOL_TAINT_PERM] = US"perm", +[POOL_TAINT_SEARCH] = US"search", +}; +static const uschar * poolclass[NPOOLS] = { +[POOL_MAIN] = US"untainted", +[POOL_PERM] = US"untainted", +[POOL_SEARCH] = US"untainted", +[POOL_TAINT_MAIN] = US"tainted", +[POOL_TAINT_PERM] = US"tainted", +[POOL_TAINT_SEARCH] = US"tainted", +}; + + +static void * store_mmap(int, const char *, int); +static void * internal_store_malloc(int, const char *, int); +static void internal_store_free(void *, const char *, int linenumber); + +/******************************************************************************/ + +/* Predicate: if an address is in a tainted pool. +By extension, a variable pointing to this address is tainted. +*/ + +BOOL +is_tainted(const void * p) +{ +BOOL rc = p >= tainted_base && p < tainted_top; +#ifndef COMPILE_UTILITY +DEBUG(D_memory) if (rc) debug_printf_indent("is_tainted: YES\n"); +#endif +return rc; +} + +void +die_tainted(const uschar * msg, const uschar * func, int line) +{ +log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n", + msg, func, line); +} /************************************************* @@ -119,15 +193,17 @@ store_last_was_get. Arguments: size amount wanted - filename source file from which called - linenumber line number in source file. + func function from which called + linenumber line number in source file Returns: pointer to store (panic on malloc failure) */ void * -store_get_3(int size, const char *filename, int linenumber) +store_get_3(int size, BOOL tainted, const char *func, int linenumber) { +int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; + /* Round up the size to a multiple of the alignment. Although this looks a messy statement, because "alignment" is a constant expression, the compiler can do a reasonable job of optimizing, especially if the value of "alignment" is a @@ -140,21 +216,31 @@ if (size % alignment != 0) size += alignment - (size % alignment); size is STORE_BLOCK_SIZE, and we would expect this to be the norm, since these functions are mostly called for small amounts of store. */ -if (size > yield_length[store_pool]) +if (size > yield_length[pool]) { - int length = (size <= STORE_BLOCK_SIZE)? STORE_BLOCK_SIZE : size; + int length = size <= STORE_BLOCK_SIZE ? STORE_BLOCK_SIZE : size; int mlength = length + ALIGNED_SIZEOF_STOREBLOCK; - storeblock * newblock = NULL; + storeblock * newblock; /* Sometimes store_reset() may leave a block for us; check if we can use it */ - if ( (newblock = current_block[store_pool]) + if ( (newblock = current_block[pool]) && (newblock = newblock->next) && newblock->length < length ) { /* Give up on this block, because it's too small */ - store_free(newblock); + nblocks[pool]--; + if (pool < POOL_TAINT_BASE) + internal_store_free(newblock, func, linenumber); + else + { +#ifndef COMPILE_UTILITY + DEBUG(D_memory) + debug_printf("---Unmap %6p %-20s %4d\n", newblock, func, linenumber); +#endif + munmap(newblock, newblock->length + ALIGNED_SIZEOF_STOREBLOCK); + } newblock = NULL; } @@ -162,53 +248,56 @@ if (size > yield_length[store_pool]) if (!newblock) { - pool_malloc += mlength; /* Used in pools */ - nonpool_malloc -= mlength; /* Exclude from overall total */ - newblock = store_malloc(mlength); + if ((nbytes[pool] += mlength) > maxbytes[pool]) + maxbytes[pool] = nbytes[pool]; + if ((pool_malloc += mlength) > max_pool_malloc) /* Used in pools */ + max_pool_malloc = pool_malloc; + nonpool_malloc -= mlength; /* Exclude from overall total */ + if (++nblocks[pool] > maxblocks[pool]) + maxblocks[pool] = nblocks[pool]; + + newblock = tainted + ? store_mmap(mlength, func, linenumber) + : internal_store_malloc(mlength, func, linenumber); newblock->next = NULL; newblock->length = length; - if (!chainbase[store_pool]) - chainbase[store_pool] = newblock; + + if (!chainbase[pool]) + chainbase[pool] = newblock; else - current_block[store_pool]->next = newblock; + current_block[pool]->next = newblock; } - current_block[store_pool] = newblock; - yield_length[store_pool] = newblock->length; - next_yield[store_pool] = - (void *)(CS current_block[store_pool] + ALIGNED_SIZEOF_STOREBLOCK); - (void) VALGRIND_MAKE_MEM_NOACCESS(next_yield[store_pool], yield_length[store_pool]); + current_block[pool] = newblock; + yield_length[pool] = newblock->length; + next_yield[pool] = + (void *)(CS current_block[pool] + ALIGNED_SIZEOF_STOREBLOCK); + (void) VALGRIND_MAKE_MEM_NOACCESS(next_yield[pool], yield_length[pool]); } /* There's (now) enough room in the current block; the yield is the next pointer. */ -store_last_get[store_pool] = next_yield[store_pool]; +store_last_get[pool] = next_yield[pool]; /* Cut out the debugging stuff for utilities, but stop picky compilers from giving warnings. */ #ifdef COMPILE_UTILITY -filename = filename; +func = func; linenumber = linenumber; #else DEBUG(D_memory) - { - if (f.running_in_test_harness) - debug_printf("---%d Get %5d\n", store_pool, size); - else - debug_printf("---%d Get %6p %5d %-14s %4d\n", store_pool, - store_last_get[store_pool], size, filename, linenumber); - } + debug_printf("---%d Get %6p %5d %-14s %4d\n", pool, + store_last_get[pool], size, func, linenumber); #endif /* COMPILE_UTILITY */ -(void) VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[store_pool], size); +(void) VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[pool], size); /* Update next pointer and number of bytes left in the current block. */ -next_yield[store_pool] = (void *)(CS next_yield[store_pool] + size); -yield_length[store_pool] -= size; - -return store_last_get[store_pool]; +next_yield[pool] = (void *)(CS next_yield[pool] + size); +yield_length[pool] -= size; +return store_last_get[pool]; } @@ -222,19 +311,19 @@ be obtained. Arguments: size amount wanted - filename source file from which called - linenumber line number in source file. + func function from which called + linenumber line number in source file Returns: pointer to store (panic on malloc failure) */ void * -store_get_perm_3(int size, const char *filename, int linenumber) +store_get_perm_3(int size, BOOL tainted, const char *func, int linenumber) { void *yield; int old_pool = store_pool; store_pool = POOL_PERM; -yield = store_get_3(size, filename, linenumber); +yield = store_get_3(size, tainted, func, linenumber); store_pool = old_pool; return yield; } @@ -247,15 +336,16 @@ return yield; /* While reading strings of unknown length, it is often the case that the string is being read into the block at the top of the stack. If it needs to be -extended, it is more efficient just to extend the top block rather than +extended, it is more efficient just to extend within the top block rather than allocate a new block and then have to copy the data. This function is provided for the use of string_cat(), but of course can be used elsewhere too. +The block itself is not expanded; only the top allocation from it. Arguments: ptr pointer to store block oldsize current size of the block, as requested by user newsize new size required - filename source file from which called + func function from which called linenumber line number in source file Returns: TRUE if the block is at the top of the stack and has been @@ -264,39 +354,41 @@ Returns: TRUE if the block is at the top of the stack and has been */ BOOL -store_extend_3(void *ptr, int oldsize, int newsize, const char *filename, - int linenumber) +store_extend_3(void *ptr, BOOL tainted, int oldsize, int newsize, + const char *func, int linenumber) { +int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; int inc = newsize - oldsize; int rounded_oldsize = oldsize; +/* Check that the block being extended was already of the required taint status; +refuse to extend if not. */ + +if (is_tainted(ptr) != tainted) + return FALSE; + if (rounded_oldsize % alignment != 0) rounded_oldsize += alignment - (rounded_oldsize % alignment); -if (CS ptr + rounded_oldsize != CS (next_yield[store_pool]) || - inc > yield_length[store_pool] + rounded_oldsize - oldsize) +if (CS ptr + rounded_oldsize != CS (next_yield[pool]) || + inc > yield_length[pool] + rounded_oldsize - oldsize) return FALSE; /* Cut out the debugging stuff for utilities, but stop picky compilers from giving warnings. */ #ifdef COMPILE_UTILITY -filename = filename; +func = func; linenumber = linenumber; #else DEBUG(D_memory) - { - if (f.running_in_test_harness) - debug_printf("---%d Ext %5d\n", store_pool, newsize); - else - debug_printf("---%d Ext %6p %5d %-14s %4d\n", store_pool, ptr, newsize, - filename, linenumber); - } + debug_printf("---%d Ext %6p %5d %-14s %4d\n", pool, ptr, newsize, + func, linenumber); #endif /* COMPILE_UTILITY */ if (newsize % alignment != 0) newsize += alignment - (newsize % alignment); -next_yield[store_pool] = CS ptr + newsize; -yield_length[store_pool] -= newsize - rounded_oldsize; +next_yield[pool] = CS ptr + newsize; +yield_length[pool] -= newsize - rounded_oldsize; (void) VALGRIND_MAKE_MEM_UNDEFINED(ptr + oldsize, inc); return TRUE; } @@ -309,44 +401,46 @@ return TRUE; *************************************************/ /* This function resets the next pointer, freeing any subsequent whole blocks -that are now unused. Normally it is given a pointer that was the yield of a -call to store_get, and is therefore aligned, but it may be given an offset -after such a pointer in order to release the end of a block and anything that -follows. +that are now unused. Call with a cookie obtained from store_mark() only; do +not call with a pointer returned by store_get(). Both the untainted and tainted +pools corresposding to store_pool are reset. Arguments: - ptr place to back up to - filename source file from which called + r place to back up to + func function from which called linenumber line number in source file Returns: nothing */ -void -store_reset_3(void *ptr, const char *filename, int linenumber) +static void +internal_store_reset(void * ptr, int pool, const char *func, int linenumber) { storeblock * bb; -storeblock * b = current_block[store_pool]; +storeblock * b = current_block[pool]; char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; -int newlength; +int newlength, count; +#ifndef COMPILE_UTILITY +int oldmalloc = pool_malloc; +#endif /* Last store operation was not a get */ -store_last_get[store_pool] = NULL; +store_last_get[pool] = NULL; /* See if the place is in the current block - as it often will be. Otherwise, search for the block in which it lies. */ if (CS ptr < bc || CS ptr > bc + b->length) { - for (b = chainbase[store_pool]; b; b = b->next) + for (b = chainbase[pool]; b; b = b->next) { bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; if (CS ptr >= bc && CS ptr <= bc + b->length) break; } if (!b) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal error: store_reset(%p) " - "failed: pool=%d %-14s %4d", ptr, store_pool, filename, linenumber); + "failed: pool=%d %-14s %4d", ptr, pool, func, linenumber); } /* Back up, rounding to the alignment if necessary. When testing, flatten @@ -356,7 +450,7 @@ newlength = bc + b->length - CS ptr; #ifndef COMPILE_UTILITY if (debug_store) { - assert_no_variables(ptr, newlength, filename, linenumber); + assert_no_variables(ptr, newlength, func, linenumber); if (f.running_in_test_harness) { (void) VALGRIND_MAKE_MEM_DEFINED(ptr, newlength); @@ -365,23 +459,25 @@ if (debug_store) } #endif (void) VALGRIND_MAKE_MEM_NOACCESS(ptr, newlength); -yield_length[store_pool] = newlength - (newlength % alignment); -next_yield[store_pool] = CS ptr + (newlength % alignment); -current_block[store_pool] = b; - -/* Free any subsequent block. Do NOT free the first successor, if our -current block has less than 256 bytes left. This should prevent us from -flapping memory. However, keep this block only when it has the default size. */ - -if (yield_length[store_pool] < STOREPOOL_MIN_SIZE && - b->next && - b->next->length == STORE_BLOCK_SIZE) +next_yield[pool] = CS ptr + (newlength % alignment); +count = yield_length[pool]; +count = (yield_length[pool] = newlength - (newlength % alignment)) - count; +current_block[pool] = b; + +/* Free any subsequent block. Do NOT free the first +successor, if our current block has less than 256 bytes left. This should +prevent us from flapping memory. However, keep this block only when it has +the default size. */ + +if ( yield_length[pool] < STOREPOOL_MIN_SIZE + && b->next + && b->next->length == STORE_BLOCK_SIZE) { b = b->next; #ifndef COMPILE_UTILITY if (debug_store) assert_no_variables(b, b->length + ALIGNED_SIZEOF_STOREBLOCK, - filename, linenumber); + func, linenumber); #endif (void) VALGRIND_MAKE_MEM_NOACCESS(CS b + ALIGNED_SIZEOF_STOREBLOCK, b->length - ALIGNED_SIZEOF_STOREBLOCK); @@ -392,36 +488,156 @@ b->next = NULL; while ((b = bb)) { + int siz = b->length + ALIGNED_SIZEOF_STOREBLOCK; #ifndef COMPILE_UTILITY if (debug_store) assert_no_variables(b, b->length + ALIGNED_SIZEOF_STOREBLOCK, - filename, linenumber); + func, linenumber); #endif bb = bb->next; - pool_malloc -= b->length + ALIGNED_SIZEOF_STOREBLOCK; - store_free_3(b, filename, linenumber); + nbytes[pool] -= siz; + pool_malloc -= siz; + nblocks[pool]--; + if (pool < POOL_TAINT_BASE) + internal_store_free(b, func, linenumber); + else + { +#ifndef COMPILE_UTILITY + DEBUG(D_memory) + debug_printf("---Unmap %6p %-20s %4d\n", b, func, linenumber); +#endif + munmap(b, b->length + ALIGNED_SIZEOF_STOREBLOCK); + } } /* Cut out the debugging stuff for utilities, but stop picky compilers from giving warnings. */ #ifdef COMPILE_UTILITY -filename = filename; +func = func; linenumber = linenumber; #else DEBUG(D_memory) + debug_printf("---%d Rst %6p %5d %-14s %4d %d\n", pool, ptr, + count + oldmalloc - pool_malloc, + func, linenumber, pool_malloc); +#endif /* COMPILE_UTILITY */ +} + + +rmark +store_reset_3(rmark r, int pool, const char *func, int linenumber) +{ +void ** ptr = r; + +if (pool >= POOL_TAINT_BASE) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "store_reset called for pool %d: %s %d\n", pool, func, linenumber); +if (!r) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "store_reset called with bad mark: %s %d\n", func, linenumber); + +internal_store_reset(*ptr, pool + POOL_TAINT_BASE, func, linenumber); +internal_store_reset(ptr, pool, func, linenumber); +return NULL; +} + + + +/* Free tail-end unused allocation. This lets us allocate a big chunk +early, for cases when we only discover later how much was really needed. + +Can be called with a value from store_get(), or an offset after such. Only +the tainted or untainted pool that serviced the store_get() will be affected. + +This is mostly a cut-down version of internal_store_reset(). +XXX needs rationalising +*/ + +void +store_release_above_3(void *ptr, const char *func, int linenumber) +{ +/* Search all pools' "current" blocks. If it isn't one of those, +ignore it (it usually will be). */ + +for (int pool = 0; pool < nelem(current_block); pool++) { - if (f.running_in_test_harness) - debug_printf("---%d Rst ** %d\n", store_pool, pool_malloc); - else - debug_printf("---%d Rst %6p ** %-14s %4d %d\n", store_pool, ptr, - filename, linenumber, pool_malloc); + storeblock * b = current_block[pool]; + char * bc; + int count, newlength; + + if (!b) + continue; + + bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; + if (CS ptr < bc || CS ptr > bc + b->length) + continue; + + /* Last store operation was not a get */ + + store_last_get[pool] = NULL; + + /* Back up, rounding to the alignment if necessary. When testing, flatten + the released memory. */ + + newlength = bc + b->length - CS ptr; +#ifndef COMPILE_UTILITY + if (debug_store) + { + assert_no_variables(ptr, newlength, func, linenumber); + if (f.running_in_test_harness) + { + (void) VALGRIND_MAKE_MEM_DEFINED(ptr, newlength); + memset(ptr, 0xF0, newlength); + } + } +#endif + (void) VALGRIND_MAKE_MEM_NOACCESS(ptr, newlength); + next_yield[pool] = CS ptr + (newlength % alignment); + count = yield_length[pool]; + count = (yield_length[pool] = newlength - (newlength % alignment)) - count; + + /* Cut out the debugging stuff for utilities, but stop picky compilers from + giving warnings. */ + +#ifdef COMPILE_UTILITY + func = func; + linenumber = linenumber; +#else + DEBUG(D_memory) + debug_printf("---%d Rel %6p %5d %-14s %4d %d\n", pool, ptr, count, + func, linenumber, pool_malloc); +#endif + return; } -#endif /* COMPILE_UTILITY */ +#ifndef COMPILE_UTILITY +DEBUG(D_memory) + debug_printf("non-last memory release try: %s %d\n", func, linenumber); +#endif } +rmark +store_mark_3(const char *func, int linenumber) +{ +void ** p; + +if (store_pool >= POOL_TAINT_BASE) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "store_mark called for pool %d: %s %d\n", store_pool, func, linenumber); + +/* Stash a mark for the tainted-twin release, in the untainted twin. Return +a cookie (actually the address in the untainted pool) to the caller. +Reset uses the cookie to recover the t-mark, winds back the tainted pool with it +and winds back the untainted pool with the cookie. */ + +p = store_get_3(sizeof(void *), FALSE, func, linenumber); +*p = store_get_3(0, TRUE, func, linenumber); +return p; +} + + /************************************************ @@ -433,38 +649,38 @@ block, and if so, releases that block. Arguments: block block of store to consider - filename source file from which called + func function from which called linenumber line number in source file Returns: nothing */ static void -store_release_3(void * block, const char * filename, int linenumber) +store_release_3(void * block, int pool, const char * func, int linenumber) { /* It will never be the first block, so no need to check that. */ -for (storeblock * b = chainbase[store_pool]; b; b = b->next) +for (storeblock * b = chainbase[pool]; b; b = b->next) { storeblock * bb = b->next; if (bb && CS block == CS bb + ALIGNED_SIZEOF_STOREBLOCK) { + int siz = bb->length + ALIGNED_SIZEOF_STOREBLOCK; b->next = bb->next; - pool_malloc -= bb->length + ALIGNED_SIZEOF_STOREBLOCK; + nbytes[pool] -= siz; + pool_malloc -= siz; + nblocks[pool]--; /* Cut out the debugging stuff for utilities, but stop picky compilers from giving warnings. */ #ifdef COMPILE_UTILITY - filename = filename; + func = func; linenumber = linenumber; #else DEBUG(D_memory) - if (f.running_in_test_harness) - debug_printf("-Release %d\n", pool_malloc); - else - debug_printf("-Release %6p %-20s %4d %d\n", (void *)bb, filename, - linenumber, pool_malloc); + debug_printf("-Release %6p %-20s %4d %d\n", (void *)bb, func, + linenumber, pool_malloc); if (f.running_in_test_harness) memset(bb, 0xF0, bb->length+ALIGNED_SIZEOF_STOREBLOCK); @@ -502,20 +718,74 @@ Returns: new location of data */ void * -store_newblock_3(void * block, int newsize, int len, - const char * filename, int linenumber) +store_newblock_3(void * block, BOOL tainted, int newsize, int len, + const char * func, int linenumber) { -BOOL release_ok = store_last_get[store_pool] == block; -uschar * newtext = store_get(newsize); +int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; +BOOL release_ok = !tainted && store_last_get[pool] == block; +uschar * newtext; + +if (is_tainted(block) != tainted) + die_tainted(US"store_newblock", CUS func, linenumber); +newtext = store_get(newsize, tainted); memcpy(newtext, block, len); -if (release_ok) store_release_3(block, filename, linenumber); +if (release_ok) store_release_3(block, pool, func, linenumber); return (void *)newtext; } +/******************************************************************************/ +static void * +store_alloc_tail(void * yield, int size, const char * func, int line, + const uschar * type) +{ +if ((nonpool_malloc += size) > max_nonpool_malloc) + max_nonpool_malloc = nonpool_malloc; + +/* Cut out the debugging stuff for utilities, but stop picky compilers from +giving warnings. */ + +#ifdef COMPILE_UTILITY +func = func; line = line; type = type; +#else + +/* If running in test harness, spend time making sure all the new store +is not filled with zeros so as to catch problems. */ + +if (f.running_in_test_harness) + memset(yield, 0xF0, (size_t)size); +DEBUG(D_memory) debug_printf("--%6s %6p %5d bytes\t%-14s %4d\tpool %5d nonpool %5d\n", + type, yield, size, func, line, pool_malloc, nonpool_malloc); +#endif /* COMPILE_UTILITY */ + +return yield; +} + +/************************************************* +* Mmap store * +*************************************************/ + +static void * +store_mmap(int size, const char * func, int line) +{ +void * yield, * top; + +if (size < 16) size = 16; + +if (!(yield = mmap(NULL, (size_t)size, + PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))) + 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 = yield + size) > tainted_top) tainted_top = top; + +return store_alloc_tail(yield, size, func, line, US"Mmap"); +} + /************************************************* * Malloc store * *************************************************/ @@ -526,50 +796,32 @@ function is called via the macro store_malloc(). Arguments: size amount of store wanted - filename source file from which called + func function from which called linenumber line number in source file Returns: pointer to gotten store (panic on failure) */ -void * -store_malloc_3(int size, const char *filename, int linenumber) +static void * +internal_store_malloc(int size, const char *func, int linenumber) { -void *yield; +void * yield; if (size < 16) size = 16; 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 of %s", size, linenumber, filename); - -nonpool_malloc += size; - -/* Cut out the debugging stuff for utilities, but stop picky compilers from -giving warnings. */ - -#ifdef COMPILE_UTILITY -filename = filename; -linenumber = linenumber; -#else - -/* If running in test harness, spend time making sure all the new store -is not filled with zeros so as to catch problems. */ + "called from line %d in %s", size, linenumber, func); -if (f.running_in_test_harness) - { - memset(yield, 0xF0, (size_t)size); - DEBUG(D_memory) debug_printf("--Malloc %5d %d %d\n", size, pool_malloc, - nonpool_malloc); - } -else - { - DEBUG(D_memory) debug_printf("--Malloc %6p %5d %-14s %4d %d %d\n", yield, - size, filename, linenumber, pool_malloc, nonpool_malloc); - } -#endif /* COMPILE_UTILITY */ +return store_alloc_tail(yield, size, func, linenumber, US"Malloc"); +} -return yield; +void * +store_malloc_3(int size, const char *func, int linenumber) +{ +if (n_nonpool_blocks++ > max_nonpool_blocks) + max_nonpool_blocks = n_nonpool_blocks; +return internal_store_malloc(size, func, linenumber); } @@ -581,28 +833,48 @@ return yield; Arguments: block block of store to free - filename source file from which called + func function from which called linenumber line number in source file Returns: nothing */ -void -store_free_3(void *block, const char *filename, int linenumber) +static void +internal_store_free(void *block, const char *func, int linenumber) { #ifdef COMPILE_UTILITY -filename = filename; +func = func; linenumber = linenumber; #else DEBUG(D_memory) - { - if (f.running_in_test_harness) - debug_printf("----Free\n"); - else - debug_printf("----Free %6p %-20s %4d\n", block, filename, linenumber); - } + debug_printf("----Free %6p %-20s %4d\n", block, func, linenumber); #endif /* COMPILE_UTILITY */ free(block); } +void +store_free_3(void *block, const char *func, int linenumber) +{ +n_nonpool_blocks--; +internal_store_free(block, func, linenumber); +} + +/******************************************************************************/ +/* Stats output on process exit */ +void +store_exit(void) +{ +#ifndef COMPILE_UTILITY +DEBUG(D_memory) + { + debug_printf("----Exit nonpool max: %3d kB in %d blocks\n", + (max_nonpool_malloc+1023)/1024, max_nonpool_blocks); + debug_printf("----Exit npools max: %3d kB\n", max_pool_malloc/1024); + for (int i = 0; i < NPOOLS; i++) + debug_printf("----Exit pool %d max: %3d kB in %d blocks\t%s %s\n", + i, maxbytes[i]/1024, maxblocks[i], poolclass[i], pooluse[i]); + } +#endif +} + /* End of store.c */ diff --git a/src/src/store.h b/src/src/store.h index 393f6c7b5..6464e32ed 100644 --- a/src/src/store.h +++ b/src/src/store.h @@ -12,13 +12,15 @@ /* Define symbols for identifying the store pools. */ -enum { POOL_MAIN, POOL_PERM, POOL_SEARCH }; +enum { POOL_MAIN, POOL_PERM, POOL_SEARCH, + POOL_TAINT_BASE, + POOL_TAINT_MAIN = POOL_TAINT_BASE, POOL_TAINT_PERM, POOL_TAINT_SEARCH }; /* This variable (the one for the current pool) is set by store_get() to its yield, and by store_reset() to NULL. This allows string_cat() to optimize its store handling. */ -extern void *store_last_get[3]; +extern void *store_last_get[6]; /* This variable contains the current store pool number. */ @@ -27,28 +29,40 @@ extern int store_pool; /* Macros for calling the memory allocation routines with tracing information for debugging. */ -#define store_extend(addr,old,new) \ - store_extend_3(addr, old, new, __FILE__, __LINE__) - -#define store_free(addr) store_free_3(addr, __FILE__, __LINE__) -#define store_get(size) store_get_3(size, __FILE__, __LINE__) -#define store_get_perm(size) store_get_perm_3(size, __FILE__, __LINE__) -#define store_malloc(size) store_malloc_3(size, __FILE__, __LINE__) -#define store_newblock(addr,newsize,datalen) \ - store_newblock_3(addr, newsize, datalen, __FILE__, __LINE__) -#define store_reset(addr) store_reset_3(addr, __FILE__, __LINE__) +#define store_extend(addr, tainted, old, new) \ + store_extend_3(addr, tainted, old, new, __FUNCTION__, __LINE__) + +#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__) +#define store_malloc(size) \ + store_malloc_3(size, __FUNCTION__, __LINE__) +#define store_mark(void) \ + store_mark_3(__FUNCTION__, __LINE__) +#define store_newblock(addr, tainted, newsize, datalen) \ + store_newblock_3(addr, tainted, newsize, datalen, __FUNCTION__, __LINE__) +#define store_release_above(addr) \ + store_release_above_3(addr, __FUNCTION__, __LINE__) +#define store_reset(mark) \ + store_reset_3(mark, store_pool, __FUNCTION__, __LINE__) /* The real functions */ +typedef void ** rmark; -/* The value of the 2nd arg is __FILE__ in every call, so give its correct type */ -extern BOOL store_extend_3(void *, int, int, const char *, int); +extern BOOL is_tainted(const void *); +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, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; -extern void *store_get_perm_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; -extern void *store_malloc_3(int, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; -extern void *store_newblock_3(void *, int, int, const char *, int); -extern void store_reset_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; +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); +extern void store_release_above_3(void *, const char *, int); +extern rmark store_reset_3(rmark, int, const char *, int); #endif /* STORE_H */ diff --git a/src/src/string.c b/src/src/string.c index fed210080..8cc34a077 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -12,6 +12,7 @@ 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 /************************************************* @@ -167,7 +168,7 @@ Returns: pointer to the buffer uschar * string_format_size(int size, uschar *buffer) { -if (size == 0) Ustrcpy(buffer, " "); +if (size == 0) Ustrcpy(buffer, US" "); else if (size < 1024) sprintf(CS buffer, "%5d", size); else if (size < 10*1024) sprintf(CS buffer, "%4.1fK", (double)size / 1024.0); @@ -306,7 +307,7 @@ if (nonprintcount == 0) return s; /* Get a new block of store guaranteed big enough to hold the expanded string. */ -ss = store_get(length + nonprintcount * 3 + 1); +ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s)); /* Copy everything, escaping non printers. */ @@ -362,7 +363,7 @@ p = Ustrchr(s, '\\'); if (!p) return s; len = Ustrlen(s) + 1; -ss = store_get(len); +ss = store_get(len, is_tainted(s)); q = ss; off = p - s; @@ -412,22 +413,32 @@ return ss; * Copy and save string * *************************************************/ -/* This function assumes that memcpy() is faster than strcpy(). - +/* Argument: string to copy -Returns: copy of string in new store +Returns: copy of string in new store with the same taint status */ uschar * string_copy_function(const uschar *s) { +return string_copy_taint(s, is_tainted(s)); +} + +/* This function assumes that memcpy() is faster than strcpy(). +As above, but explicitly specifying the result taint status +*/ + +uschar * +string_copy_taint(const uschar * s, BOOL tainted) +{ int len = Ustrlen(s) + 1; -uschar *ss = store_get(len); +uschar *ss = store_get(len, tainted); memcpy(ss, s, len); return ss; } + /************************************************* * Copy and save string, given length * *************************************************/ @@ -445,7 +456,7 @@ Returns: copy of string in new store uschar * string_copyn_function(const uschar *s, int n) { -uschar *ss = store_get(n + 1); +uschar *ss = store_get(n + 1, is_tainted(s)); Ustrncpy(ss, s, n); ss[n] = 0; return ss; @@ -555,7 +566,7 @@ uschar * string_copy_dnsdomain(uschar *s) { uschar *yield; -uschar *ss = yield = store_get(Ustrlen(s) + 1); +uschar *ss = yield = store_get(Ustrlen(s) + 1, is_tainted(s)); while (*s != 0) { @@ -617,25 +628,22 @@ else /* Get enough store to copy into */ -t = yield = store_get(s - *sptr + 1); +t = yield = store_get(s - *sptr + 1, is_tainted(*sptr)); s = *sptr; /* Do the copy */ if (*s != '\"') - { while (*s != 0 && !isspace(*s)) *t++ = *s++; - } else { s++; while (*s != 0 && *s != '\"') { - if (*s == '\\') *t++ = string_interpret_escape(&s); - else *t++ = *s; + *t++ = *s == '\\' ? string_interpret_escape(&s) : *s; s++; } - if (*s != 0) s++; + if (*s) s++; } /* Update the pointer and return the terminated copy */ @@ -664,35 +672,24 @@ Returns: pointer to fresh piece of store containing sprintf'ed string */ uschar * -string_sprintf(const char *format, ...) +string_sprintf_trc(const char *format, const uschar * func, unsigned line, ...) { -#ifdef COMPILE_UTILITY -uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; -gstring g = { .size = STRING_SPRINTF_BUFFER_SIZE, .ptr = 0, .s = buffer }; -gstring * gp = &g; -#else -gstring * gp = string_get(STRING_SPRINTF_BUFFER_SIZE); -#endif -gstring * gp2; +gstring * g; va_list ap; -va_start(ap, format); -gp2 = string_vformat(gp, FALSE, format, ap); -gp->s[gp->ptr] = '\0'; +va_start(ap, line); +g = string_vformat_trc(NULL, func, line, STRING_SPRINTF_BUFFER_SIZE, + SVFMT_REBUFFER|SVFMT_EXTEND, format, ap); va_end(ap); -if (!gp2) +if (!g) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "string_sprintf expansion was longer than %d; format string was (%s)\n" - "expansion started '%.32s'", - gp->size, format, gp->s); + " called from %s %d\n", + STRING_SPRINTF_BUFFER_SIZE, format, func, line); -#ifdef COMPILE_UTILITY -return string_copy(gp->s); -#else -gstring_release_unused(gp); -return gp->s; -#endif +gstring_release_unused(g); +return string_from_gstring(g); } @@ -801,7 +798,7 @@ return NULL; #ifdef COMPILE_UTILITY /* Dummy version for this function; it should never be called */ static void -gstring_grow(gstring * g, int p, int count) +gstring_grow(gstring * g, int count) { assert(FALSE); } @@ -1047,18 +1044,21 @@ return list; /************************************************/ -/* Add more space to a growable-string. +/* Add more space to a growable-string. The caller should check +first if growth is required. The gstring struct is modified on +return; specifically, the string-base-pointer may have been changed. Arguments: g the growable-string - p current end of data - count amount to grow by + count amount needed for g->ptr to increase by */ static void -gstring_grow(gstring * g, int p, int count) +gstring_grow(gstring * g, int count) { +int p = g->ptr; int oldsize = g->size; +BOOL tainted = is_tainted(g->s); /* Mostly, string_cat() is used to build small strings of a few hundred characters at most. There are times, however, when the strings are very much @@ -1067,7 +1067,9 @@ To try to keep things reasonable, we use increments whose size depends on the existing length of the string. */ unsigned inc = oldsize < 4096 ? 127 : 1023; -g->size = ((p + count + inc) & ~inc) + 1; + +if (count <= 0) return; +g->size = (p + count + inc + 1) & ~inc; /* one for a NUL */ /* Try to extend an existing allocation. If the result of calling store_extend() is false, either there isn't room in the current memory block, @@ -1079,8 +1081,8 @@ is at its start.) However, we can do this only if we know that the old string was the last item on the dynamic memory stack. This is the case if it matches store_last_get. */ -if (!store_extend(g->s, oldsize, g->size)) - g->s = store_newblock(g->s, g->size, p); +if (!store_extend(g->s, tainted, oldsize, g->size)) + g->s = store_newblock(g->s, tainted, g->size, p); } @@ -1113,17 +1115,20 @@ gstring * string_catn(gstring * g, const uschar *s, int count) { int p; +BOOL srctaint = is_tainted(s); if (!g) { unsigned inc = count < 4096 ? 127 : 1023; unsigned size = ((count + inc) & ~inc) + 1; - g = string_get(size); + g = string_get_tainted(size, srctaint); } +else if (srctaint && !is_tainted(g->s)) + gstring_rebuffer(g); p = g->ptr; if (p + count >= g->size) - gstring_grow(g, p, count); + gstring_grow(g, count); /* Because we always specify the exact number of characters to copy, we can use memcpy(), which is likely to be more efficient than strncopy() because the @@ -1207,12 +1212,14 @@ Returns: TRUE if the result fitted in the buffer */ BOOL -string_format(uschar * buffer, int buflen, const char * format, ...) +string_format_trc(uschar * buffer, int buflen, + const uschar * func, unsigned line, const char * format, ...) { gstring g = { .size = buflen, .ptr = 0, .s = buffer }, *gp; va_list ap; va_start(ap, format); -gp = string_vformat(&g, FALSE, format, ap); +gp = string_vformat_trc(&g, func, line, STRING_SPRINTF_BUFFER_SIZE, + 0, format, ap); va_end(ap); g.s[g.ptr] = '\0'; return !!gp; @@ -1220,14 +1227,24 @@ 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; +} + -/* Bulid or append to a growing-string, sprintf-style. +/* Build or append to a growing-string, sprintf-style. -If the "extend" argument is true, the string passed in can be NULL, -empty, or non-empty. +If the "extend" flag is true, the string passed in can be NULL, +empty, or non-empty. Growing is subject to an overall limit given +by the size_limit argument. -If the "extend" argument is false, the string passed in may not be NULL, +If the "extend" flag is false, the string passed in may not be NULL, will not be grown, and is usable in the original place after return. The return value can be NULL to signify overflow. @@ -1236,24 +1253,35 @@ not nul-terminated. */ gstring * -string_vformat(gstring * g, BOOL extend, const char *format, va_list ap) +string_vformat_trc(gstring * g, const uschar * func, unsigned line, + unsigned size_limit, unsigned flags, const char *format, va_list ap) { enum ltypes { L_NORMAL=1, L_SHORT=2, L_LONG=3, L_LONGLONG=4, L_LONGDOUBLE=5, L_SIZE=6 }; -int width, precision, off, lim; +int width, precision, off, lim, need; const char * fp = format; /* Deliberately not unsigned */ +BOOL dest_tainted = FALSE; string_datestamp_offset = -1; /* Datestamp not inserted */ string_datestamp_length = 0; /* Datestamp not inserted */ string_datestamp_type = 0; /* Datestamp not inserted */ #ifdef COMPILE_UTILITY -assert(!extend); +assert(!(flags & SVFMT_EXTEND)); assert(g); #else /* Ensure we have a string, to save on checking later */ if (!g) g = string_get(16); +else if (!(flags & SVFMT_TAINT_NOCHK)) dest_tainted = is_tainted(g->s); + +if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(format)) + { + if (!(flags & SVFMT_REBUFFER)) + die_tainted(US"string_vformat", func, line); + gstring_rebuffer(g); + dest_tainted = TRUE; + } #endif /*!COMPILE_UTILITY*/ lim = g->size - 1; /* leave one for a nul */ @@ -1276,10 +1304,10 @@ while (*fp) if (*fp != '%') { /* Avoid string_copyn() due to COMPILE_UTILITY */ - if (g->ptr >= lim - 1) + if ((need = g->ptr + 1) > lim) { - if (!extend) return NULL; - gstring_grow(g, g->ptr, 1); + if (!(flags & SVFMT_EXTEND) || need > size_limit) return NULL; + gstring_grow(g, 1); lim = g->size - 1; } g->s[g->ptr++] = (uschar) *fp++; @@ -1348,10 +1376,10 @@ while (*fp) case 'x': case 'X': width = length > L_LONG ? 24 : 12; - if (g->ptr >= lim - width) + if ((need = g->ptr + width) > lim) { - if (!extend) return NULL; - gstring_grow(g, g->ptr, width); + if (!(flags & SVFMT_EXTEND) || need >= size_limit) return NULL; + gstring_grow(g, width); lim = g->size - 1; gp = CS g->s + g->ptr; } @@ -1378,10 +1406,10 @@ while (*fp) case 'p': { void * ptr; - if (g->ptr >= lim - 24) + if ((need = g->ptr + 24) > lim) { - if (!extend) return NULL; - gstring_grow(g, g->ptr, 24); + if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL; + gstring_grow(g, 24); lim = g->size - 1; gp = CS g->s + g->ptr; } @@ -1411,10 +1439,10 @@ while (*fp) case 'g': case 'G': if (precision < 0) precision = 6; - if (g->ptr >= lim - precision - 8) + if ((need = g->ptr + precision + 8) > lim) { - if (!extend) return NULL; - gstring_grow(g, g->ptr, precision+8); + if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL; + gstring_grow(g, precision+8); lim = g->size - 1; gp = CS g->s + g->ptr; } @@ -1429,20 +1457,20 @@ while (*fp) /* String types */ case '%': - if (g->ptr >= lim - 1) + if ((need = g->ptr + 1) > lim) { - if (!extend) return NULL; - gstring_grow(g, g->ptr, 1); + if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL; + gstring_grow(g, 1); lim = g->size - 1; } g->s[g->ptr++] = (uschar) '%'; break; case 'c': - if (g->ptr >= lim - 1) + if ((need = g->ptr + 1) > lim) { - if (!extend) return NULL; - gstring_grow(g, g->ptr, 1); + if (!(flags & SVFMT_EXTEND || need >= size_limit)) return NULL; + gstring_grow(g, 1); lim = g->size - 1; } g->s[g->ptr++] = (uschar) va_arg(ap, int); @@ -1472,6 +1500,16 @@ while (*fp) if (!s) s = null; slen = Ustrlen(s); + if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(s)) + if (flags & SVFMT_REBUFFER) + { + gstring_rebuffer(g); + gp = CS g->s + g->ptr; + dest_tainted = TRUE; + } + else + die_tainted(US"string_vformat", func, line); + INSERT_STRING: /* Come to from %D or %M above */ { @@ -1497,10 +1535,10 @@ while (*fp) else width = precision = slen; - if (!extend) + if ((need = g->ptr + width) >= size_limit || !(flags & SVFMT_EXTEND)) { if (g->ptr == lim) return NULL; - if (g->ptr >= lim - width) + if (need > lim) { truncated = TRUE; width = precision = lim - g->ptr - 1; @@ -1508,9 +1546,9 @@ while (*fp) if (precision < 0) precision = 0; } } - else if (g->ptr >= lim - width) + else if (need > lim) { - gstring_grow(g, g->ptr, width - (lim - g->ptr)); + gstring_grow(g, width); lim = g->size - 1; gp = CS g->s + g->ptr; } @@ -1536,25 +1574,15 @@ while (*fp) } } +if (g->ptr > g->size) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "string_format internal error: caller %s %d", func, line); return g; } #ifndef COMPILE_UTILITY - -gstring * -string_fmt_append(gstring * g, const char *format, ...) -{ -va_list ap; -va_start(ap, format); -g = string_vformat(g, TRUE, format, ap); -va_end(ap); -return g; -} - - - /************************************************* * Generate an "open failed" message * *************************************************/ @@ -1572,7 +1600,8 @@ Returns: a message, in dynamic store */ uschar * -string_open_failed(int eno, const char *format, ...) +string_open_failed_trc(int eno, const uschar * func, unsigned line, + const char *format, ...) { va_list ap; gstring * g = string_get(1024); @@ -1585,7 +1614,8 @@ specified messages. If it does, the message just gets truncated, and there doesn't seem much we can do about that. */ va_start(ap, format); -(void) string_vformat(g, FALSE, format, ap); +(void) string_vformat_trc(g, func, line, STRING_SPRINTF_BUFFER_SIZE, + 0, format, ap); string_from_gstring(g); gstring_release_unused(g); va_end(ap); diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 423c3a23d..ca60ddb6a 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -648,7 +648,7 @@ if ((fd = Uopen(filename, O_RDONLY, 0)) >= 0) } m.size = statbuf.st_size; - if (!(m.data = malloc(m.size))) + if (!(m.data = store_malloc(m.size))) { fclose(fp); return tls_error_sys(US"malloc failed", errno, NULL, errstr); @@ -657,13 +657,13 @@ if ((fd = Uopen(filename, O_RDONLY, 0)) >= 0) { saved_errno = errno; fclose(fp); - free(m.data); + store_free(m.data); return tls_error_sys(US"fread failed", saved_errno, NULL, errstr); } fclose(fp); rc = gnutls_dh_params_import_pkcs3(dh_server_params, &m, GNUTLS_X509_FMT_PEM); - free(m.data); + store_free(m.data); if (rc) return tls_error_gnu(US"gnutls_dh_params_import_pkcs3", rc, host, errstr); DEBUG(D_tls) debug_printf("read D-H parameters from file \"%s\"\n", filename); @@ -736,25 +736,25 @@ if (rc < 0) return tls_error_gnu(US"gnutls_dh_params_export_pkcs3(NULL) sizing", rc, host, errstr); m.size = sz; - if (!(m.data = malloc(m.size))) + if (!(m.data = store_malloc(m.size))) return tls_error_sys(US"memory allocation failed", errno, NULL, errstr); /* this will return a size 1 less than the allocation size above */ if ((rc = gnutls_dh_params_export_pkcs3(dh_server_params, GNUTLS_X509_FMT_PEM, m.data, &sz))) { - free(m.data); + store_free(m.data); return tls_error_gnu(US"gnutls_dh_params_export_pkcs3() real", rc, host, errstr); } m.size = sz; /* shrink by 1, probably */ if ((sz = write_to_fd_buf(fd, m.data, (size_t) m.size)) != m.size) { - free(m.data); + store_free(m.data); return tls_error_sys(US"TLS cache write D-H params failed", errno, NULL, errstr); } - free(m.data); + store_free(m.data); if ((sz = write_to_fd_buf(fd, US"\n", 1)) != 1) return tls_error_sys(US"TLS cache write D-H params final newline failed", errno, NULL, errstr); @@ -1332,7 +1332,7 @@ if (host) several in parallel. */ int old_pool = store_pool; store_pool = POOL_PERM; - state = store_get(sizeof(exim_gnutls_state_st)); + state = store_get(sizeof(exim_gnutls_state_st), FALSE); store_pool = old_pool; memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); @@ -1629,7 +1629,7 @@ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) exim_gnutls_peer_err(US"getting size for cert DN failed"); return FAIL; /* should not happen */ } -dn_buf = store_get_perm(sz); +dn_buf = store_get_perm(sz, TRUE); /* tainted */ rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz); exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]"); @@ -1709,8 +1709,8 @@ else for(nrec = 0; state->dane_data_len[nrec]; ) nrec++; nrec++; - dd = store_get(nrec * sizeof(uschar *)); - ddl = store_get(nrec * sizeof(int)); + dd = store_get(nrec * sizeof(uschar *), FALSE); + ddl = store_get(nrec * sizeof(int), FALSE); nrec--; if ((rc = dane_state_init(&s, 0))) @@ -2392,8 +2392,8 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) ) if (rr->type == T_TLSA) i++; -dane_data = store_get(i * sizeof(uschar *)); -dane_data_len = store_get(i * sizeof(int)); +dane_data = store_get(i * sizeof(uschar *), FALSE); +dane_data_len = store_get(i * sizeof(int), FALSE); i = 0; for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; @@ -2401,6 +2401,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; ) if (rr->type == T_TLSA && rr->size > 3) { const uschar * p = rr->data; +/*XXX need somehow to mark rr and its data as tainted. Doues this mean copying it? */ uint8_t usage = p[0], sel = p[1], type = p[2]; DEBUG(D_tls) @@ -2505,7 +2506,7 @@ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET) { open_db dbblock, * dbm_file; int dlen = sizeof(dbdata_tls_session) + tkt.size; - dbdata_tls_session * dt = store_get(dlen); + dbdata_tls_session * dt = store_get(dlen, TRUE); DEBUG(D_tls) debug_printf("session data size %u\n", (unsigned)tkt.size); memcpy(dt->session, tkt.data, tkt.size); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index ea30ff7ca..9542a1ed4 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2790,7 +2790,7 @@ if (SSL_SESSION_is_resumable(ss)) /* 1.1.1 */ { int len = i2d_SSL_SESSION(ss, NULL); int dlen = sizeof(dbdata_tls_session) + len; - dbdata_tls_session * dt = store_get(dlen); + dbdata_tls_session * dt = store_get(dlen, TRUE); uschar * s = dt->session; open_db dbblock, * dbm_file; @@ -2908,7 +2908,7 @@ BOOL require_ocsp = FALSE; rc = store_pool; store_pool = POOL_PERM; -exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx)); +exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE); exim_client_ctx->corked = NULL; store_pool = rc; diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c index cebeae526..45135814c 100644 --- a/src/src/tlscert-gnu.c +++ b/src/src/tlscert-gnu.c @@ -25,7 +25,7 @@ int tls_export_cert(uschar * buf, size_t buflen, void * cert) { size_t sz = buflen; -void * reset_point = store_get(0); +rmark reset_point = store_mark(); int fail; const uschar * cp; @@ -49,7 +49,7 @@ return fail; int tls_import_cert(const uschar * buf, void ** cert) { -void * reset_point = store_get(0); +rmark reset_point = store_mark(); gnutls_datum_t datum; gnutls_x509_crt_t crt = *(gnutls_x509_crt_t *)cert; int fail = 0; @@ -112,7 +112,7 @@ size_t len = 32; if (mod && Ustrcmp(mod, "int") == 0) return string_sprintf("%u", (unsigned)t); -cp = store_get(len); +cp = store_get(len, FALSE); if (f.timestamps_utc) { uschar * tz = to_tz(US"GMT0"); @@ -146,7 +146,7 @@ if ((ret = gnutls_x509_crt_get_issuer_dn(cert, CS cp, &siz)) != GNUTLS_E_SHORT_MEMORY_BUFFER) return g_err("gi0", __FUNCTION__, ret); -cp = store_get(siz); +cp = store_get(siz, TRUE); if ((ret = gnutls_x509_crt_get_issuer_dn(cert, CS cp, &siz)) < 0) return g_err("gi1", __FUNCTION__, ret); @@ -200,7 +200,7 @@ if ((ret = gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, CS cp1, &len)) != GNUTLS_E_SHORT_MEMORY_BUFFER) return g_err("gs0", __FUNCTION__, ret); -cp1 = store_get(len*4+1); +cp1 = store_get(len*4+1, TRUE); if (gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, CS cp1, &len) != 0) return g_err("gs1", __FUNCTION__, ret); @@ -230,7 +230,7 @@ if ((ret = gnutls_x509_crt_get_dn(cert, CS cp, &siz)) != GNUTLS_E_SHORT_MEMORY_BUFFER) return g_err("gs0", __FUNCTION__, ret); -cp = store_get(siz); +cp = store_get(siz, TRUE); if ((ret = gnutls_x509_crt_get_dn(cert, CS cp, &siz)) < 0) return g_err("gs1", __FUNCTION__, ret); @@ -258,7 +258,7 @@ ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert, if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) return g_err("ge0", __FUNCTION__, ret); -cp1 = store_get(siz*4 + 1); +cp1 = store_get(siz*4 + 1, TRUE); ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert, CS oid, idx, CS cp1, &siz, &crit); @@ -314,7 +314,7 @@ for (int index = 0;; index++) return g_err("gs0", __FUNCTION__, ret); } - ele = store_get(siz+1); + ele = store_get(siz+1, TRUE); if ((ret = gnutls_x509_crt_get_subject_alt_name( (gnutls_x509_crt_t)cert, index, ele, &siz, NULL)) < 0) return g_err("gs1", __FUNCTION__, ret); @@ -397,7 +397,7 @@ for (int index = 0;; index++) return g_err("gc0", __FUNCTION__, ret); } - ele = store_get(siz); + ele = store_get(siz, TRUE); if ((ret = gnutls_x509_crt_get_crl_dist_points( (gnutls_x509_crt_t)cert, index, ele, &siz, NULL, NULL)) < 0) return g_err("gc1", __FUNCTION__, ret); @@ -420,7 +420,7 @@ int fail; if ( (fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert, GNUTLS_X509_FMT_DER, cp, &len)) != GNUTLS_E_SHORT_MEMORY_BUFFER - || !(cp = store_get((int)len)) + || !(cp = store_get((int)len, TRUE), TRUE) /* tainted */ || (fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert, GNUTLS_X509_FMT_DER, cp, &len)) ) @@ -445,7 +445,7 @@ if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, NULL, &siz)) != GNUTLS_E_SHORT_MEMORY_BUFFER) return g_err("gf0", __FUNCTION__, ret); -cp = store_get(siz*3+1); +cp = store_get(siz*3+1, TRUE); if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, cp, &siz)) < 0) return g_err("gf1", __FUNCTION__, ret); diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index f9808b354..0aa65c886 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -65,7 +65,7 @@ return fail; int tls_import_cert(const uschar * buf, void ** cert) { -void * reset_point = store_get(0); +rmark reset_point = store_mark(); const uschar * cp = string_unprinting(US buf); BIO * bp; X509 * x = *(X509 **)cert; @@ -172,7 +172,7 @@ else /* convert to string in our format */ len = 32; - s = store_get(len); + s = store_get(len, FALSE); strftime(CS s, (size_t)len, "%b %e %T %Y %z", tm_p); } } @@ -336,7 +336,7 @@ M_ASN1_OCTET_STRING_print(bp, adata); /* binary data, DER encoded */ /* just dump for now */ len = BIO_get_mem_data(bp, &cp1); -cp3 = cp2 = store_get(len*3+1); +cp3 = cp2 = store_get(len*3+1, TRUE); while(len) { @@ -503,7 +503,7 @@ if (!X509_digest(cert,fdig,md,&n)) expand_string_message = US"tls_cert_fprt: out of mem\n"; return NULL; } -cp = store_get(n*2+1); +cp = store_get(n*2+1, TRUE); for (int j = 0; j < (int)n; j++) sprintf(CS cp+2*j, "%02X", md[j]); return(cp); } diff --git a/src/src/transport.c b/src/src/transport.c index d745ef15f..ce02adbf2 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -375,8 +375,11 @@ transport_ctx tctx = {{0}}; gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }; va_list ap; +/* Use taint-unchecked routines for writing into big_buffer, trusting +that the result will never be expanded. */ + va_start(ap, format); -if (!string_vformat(&gs, FALSE, format, ap)) +if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport"); va_end(ap); tctx.u.fd = fd; @@ -638,7 +641,7 @@ so that we don't handle it again. */ for (ppp = *pdlist; ppp; ppp = ppp->next) if (p == ppp->ptr) return TRUE; -ppp = store_get(sizeof(struct aci)); +ppp = store_get(sizeof(struct aci), FALSE); ppp->next = *pdlist; *pdlist = ppp; ppp->ptr = p; @@ -662,7 +665,7 @@ if (ppp) return TRUE; /* Remember what we have output, and output it. */ -ppp = store_get(sizeof(struct aci)); +ppp = store_get(sizeof(struct aci), FALSE); ppp->next = *pplist; *pplist = ppp; ppp->ptr = pp; @@ -742,7 +745,7 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) { if (tblock && tblock->rewrite_rules) { - void *reset_point = store_get(0); + rmark reset_point = store_mark(); header_line *hh; if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules, @@ -949,7 +952,7 @@ if (!(tctx->options & topt_no_headers)) BOOL first = TRUE; struct aci *plist = NULL; struct aci *dlist = NULL; - void *reset_point = store_get(0); + rmark reset_point = store_mark(); if (!write_chunk(tctx, US"Envelope-to: ", 13)) goto bad; @@ -1252,7 +1255,7 @@ if ((write_pid = fork()) == 0) != sizeof(int) ) rc = FALSE; /* compiler quietening */ - _exit(0); + exim_underbar_exit(0); } save_errno = errno; @@ -1498,7 +1501,7 @@ for (host_item * host = hostlist; host; host = host->next) if (!(host_record = dbfn_read(dbm_file, host->name))) { - host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH); + host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, FALSE); host_record->count = host_record->sequence = 0; } @@ -1557,7 +1560,7 @@ for (host_item * host = hostlist; host; host = host->next) else { dbdata_wait *newr = - store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH); + store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, FALSE); memcpy(newr, host_record, sizeof(dbdata_wait) + host_length); host_record = newr; } @@ -1692,7 +1695,7 @@ while (1) /* create an array to read entire message queue into memory for processing */ - msgq = store_malloc(sizeof(msgq_t) * host_record->count); + msgq = store_get(sizeof(msgq_t) * host_record->count, FALSE); msgq_count = host_record->count; msgq_actual = msgq_count; @@ -1700,7 +1703,7 @@ while (1) { msgq[i].bKeep = TRUE; - Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), + Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), MESSAGE_ID_LENGTH); msgq[i].message_id[MESSAGE_ID_LENGTH] = 0; } @@ -1728,7 +1731,7 @@ while (1) msgq[i].bKeep = FALSE; else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data)) { - Ustrcpy(new_message_id, msgq[i].message_id); + Ustrcpy_nt(new_message_id, msgq[i].message_id); msgq[i].bKeep = FALSE; bFound = TRUE; break; @@ -1798,10 +1801,7 @@ while (1) } if (bFound) /* Usual exit from main loop */ - { - store_free (msgq); break; - } /* If host_length <= 0 we have emptied a record and not found a good message, and there are no continuation records. Otherwise there is a continuation @@ -1824,8 +1824,6 @@ while (1) dbfn_close(dbm_file); return FALSE; } - - store_free(msgq); } /* we need to process a continuation record */ /* Control gets here when an existing message has been encountered; its @@ -2018,7 +2016,7 @@ delivery batch option is set. */ for (address_item * ad = addr; ad; ad = ad->next) address_count++; max_args = address_count + 60; -*argvptr = argv = store_get((max_args+1)*sizeof(uschar *)); +*argvptr = argv = store_get((max_args+1)*sizeof(uschar *), FALSE); /* Split the command up into arguments terminated by white space. Lose trailing space at the start and end. Double-quoted arguments can contain \\ and @@ -2028,18 +2026,19 @@ arguments are verbatim. Copy each argument into a new string. */ s = cmd; while (isspace(*s)) s++; -while (*s != 0 && argcount < max_args) +for (; *s != 0 && argcount < max_args; argcount++) { if (*s == '\'') { ss = s + 1; while (*ss != 0 && *ss != '\'') ss++; - argv[argcount++] = ss = store_get(ss - s++); + argv[argcount] = ss = store_get(ss - s++, is_tainted(cmd)); while (*s != 0 && *s != '\'') *ss++ = *s++; if (*s != 0) s++; *ss++ = 0; } - else argv[argcount++] = string_copy(string_dequote(CUSS &s)); + else + argv[argcount] = string_dequote(CUSS &s); while (isspace(*s)) s++; } @@ -2079,8 +2078,8 @@ $recipients. */ DEBUG(D_transport) { debug_printf("direct command:\n"); - for (int i = 0; argv[i] != US 0; i++) - debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i])); + for (int i = 0; argv[i]; i++) + debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i])); } if (expand_arguments) @@ -2133,6 +2132,7 @@ if (expand_arguments) int address_pipe_argcount = 0; int address_pipe_max_args; uschar **address_pipe_argv; + BOOL tainted; /* We can never have more then the argv we will be loading into */ address_pipe_max_args = max_args - argcount + 1; @@ -2141,10 +2141,11 @@ if (expand_arguments) debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args); /* We allocate an additional for (uschar *)0 */ - address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *)); + address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), FALSE); /* +1 because addr->local_part[0] == '|' since af_force_command is set */ s = expand_string(addr->local_part + 1); + tainted = is_tainted(s); if (s == NULL || *s == '\0') { @@ -2163,7 +2164,7 @@ if (expand_arguments) { ss = s + 1; while (*ss != 0 && *ss != '\'') ss++; - address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++); + address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++, tainted); while (*s != 0 && *s != '\'') *ss++ = *s++; if (*s != 0) s++; *ss++ = 0; @@ -2242,12 +2243,12 @@ if (expand_arguments) expanded_arg = expand_cstring(argv[i]); f.enable_dollar_recipients = FALSE; - if (expanded_arg == NULL) + if (!expanded_arg) { uschar *msg = string_sprintf("Expansion of \"%s\" " "from command \"%s\" in %s failed: %s", argv[i], cmd, etext, expand_string_message); - if (addr != NULL) + if (addr) { addr->transport_return = expand_failed; addr->message = msg; diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index d9f8d4989..5d23008f8 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -613,11 +613,11 @@ notify_comsat(uschar *user, off_t offset) { struct servent *sp; host_item host; -uschar buffer[256]; +uschar * s; DEBUG(D_transport) debug_printf("notify_comsat called\n"); -sprintf(CS buffer, "%.200s@" OFF_T_FMT "\n", user, offset); +s = string_sprintf("%.200s@" OFF_T_FMT "\n", user, offset); if ((sp = getservbyname("biff", "udp")) == NULL) { @@ -658,7 +658,7 @@ for (host_item * h = &host; h; h = h->next) /* Connect never fails for a UDP socket, so don't set a timeout. */ (void)ip_connect(sock, host_af, h->address, ntohs(sp->s_port), 0, NULL); - rc = send(sock, buffer, Ustrlen(buffer) + 1, 0); + rc = send(sock, s, Ustrlen(s) + 1, 0); (void)close(sock); if (rc >= 0) break; @@ -765,7 +765,7 @@ Returns: the sum of the sizes of the stattable files */ off_t -check_dir_size(uschar *dirname, int *countptr, const pcre *regex) +check_dir_size(const uschar * dirname, int *countptr, const pcre *regex) { DIR *dir; off_t sum = 0; @@ -778,8 +778,7 @@ if (dir == NULL) return 0; while ((ent = readdir(dir)) != NULL) { - uschar *name = US ent->d_name; - uschar buffer[1024]; + uschar * path, * name = US ent->d_name; if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue; @@ -787,7 +786,7 @@ while ((ent = readdir(dir)) != NULL) /* If there's a regex, try to find the size using it */ - if (regex != NULL) + if (regex) { int ovector[6]; if (pcre_exec(regex, NULL, CS name, Ustrlen(name), 0, 0, ovector,6) >= 2) @@ -809,18 +808,12 @@ while ((ent = readdir(dir)) != NULL) /* No regex or no match for the regex, or captured non-digits */ - if (!string_format(buffer, sizeof(buffer), "%s/%s", dirname, name)) - { - DEBUG(D_transport) - debug_printf("check_dir_size: name too long: dir=%s name=%s\n", dirname, - name); - continue; - } + path = string_sprintf("%s/%s", dirname, name); - if (Ustat(buffer, &statbuf) < 0) + if (Ustat(path, &statbuf) < 0) { DEBUG(D_transport) - debug_printf("check_dir_size: stat error %d for %s: %s\n", errno, buffer, + debug_printf("check_dir_size: stat error %d for %s: %s\n", errno, path, strerror(errno)); continue; } @@ -828,7 +821,7 @@ while ((ent = readdir(dir)) != NULL) if ((statbuf.st_mode & S_IFMT) == S_IFREG) sum += statbuf.st_size; else if ((statbuf.st_mode & S_IFMT) == S_IFDIR) - sum += check_dir_size(buffer, &count, regex); + sum += check_dir_size(path, &count, regex); } closedir(dir); @@ -3091,7 +3084,7 @@ if (yield != OK) addr->message = string_sprintf("mailbox is full " "(quota exceeded while writing to file %s)", filename); #else - addr->message = string_sprintf("mailbox is full"); + addr->message = US"mailbox is full"; #endif /* EDQUOT */ addr->user_message = US"mailbox is full"; DEBUG(D_transport) debug_printf("System quota exceeded for %s%s%s\n", @@ -3291,7 +3284,7 @@ else uschar *iptr = expand_string(nametag); if (iptr != NULL) { - uschar *etag = store_get(Ustrlen(iptr) + 2); + uschar *etag = store_get(Ustrlen(iptr) + 2, is_tainted(iptr)); uschar *optr = etag; while (*iptr != 0) { diff --git a/src/src/transports/appendfile.h b/src/src/transports/appendfile.h index 4b14db180..4f0f126bc 100644 --- a/src/src/transports/appendfile.h +++ b/src/src/transports/appendfile.h @@ -94,6 +94,6 @@ extern void appendfile_transport_init(transport_instance *); /* Function that is shared with tf_maildir.c */ -extern off_t check_dir_size(uschar *, int *, const pcre *); +extern off_t check_dir_size(const uschar *, int *, const pcre *); /* End of transports/appendfile.h */ diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 9decfbaea..734e65833 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -446,7 +446,7 @@ if (oncelog && *oncelog != 0 && to) cache_size = statbuf.st_size; add_size = sizeof(time_t) + Ustrlen(to) + 1; - cache_buff = store_get(cache_size + add_size); + cache_buff = store_get(cache_size + add_size, is_tainted(oncelog)); if (read(cache_fd, cache_buff, cache_size) != cache_size) { @@ -821,48 +821,26 @@ if (logfile) int log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode); if (log_fd >= 0) { - uschar *ptr = log_buffer; + gstring gs = { .size = LOG_BUFFER_SIZE, .ptr = 0, .s = log_buffer }, *g = &gs; + + /* Use taint-unchecked routines for writing into log_buffer, trusting + that we'll never expand it. */ + DEBUG(D_transport) debug_printf("logging message details\n"); - sprintf(CS ptr, "%s\n", tod_stamp(tod_log)); - while(*ptr) ptr++; + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "%s\n", tod_stamp(tod_log)); if (from) - { - (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), - " From: %s\n", from); - while(*ptr) ptr++; - } + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " From: %s\n", from); if (to) - { - (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), - " To: %s\n", to); - while(*ptr) ptr++; - } + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " To: %s\n", to); if (cc) - { - (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), - " Cc: %s\n", cc); - while(*ptr) ptr++; - } + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Cc: %s\n", cc); if (bcc) - { - (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), - " Bcc: %s\n", bcc); - while(*ptr) ptr++; - } + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Bcc: %s\n", bcc); if (subject) - { - (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), - " Subject: %s\n", subject); - while(*ptr) ptr++; - } + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " Subject: %s\n", subject); if (headers) - { - (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), - " %s\n", headers); - while(*ptr) ptr++; - } - if(write(log_fd, log_buffer, ptr - log_buffer) != ptr-log_buffer - || close(log_fd)) + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s\n", headers); + if(write(log_fd, g->s, g->ptr) != g->ptr || close(log_fd)) DEBUG(D_transport) debug_printf("Problem writing log file %s for %s " "transport\n", logfile, tblock->name); } diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index b2bf5f0da..306ec450b 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -175,7 +175,7 @@ if (*errno_value == ERRNO_CHHEADER_FAIL) if (*errno_value == ERRNO_WRITEINCOMPLETE) { - *message = string_sprintf("failed to write a data block"); + *message = US"failed to write a data block"; return FALSE; } @@ -228,8 +228,11 @@ gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }; int rc; va_list ap; +/*XXX see comment in smtp_write_command() regarding leaving stuff in +big_buffer */ + va_start(ap, format); -if (!string_vformat(&gs, FALSE, CS format, ap)) +if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap)) { va_end(ap); errno = ERRNO_SMTPFORMAT; @@ -553,7 +556,7 @@ allows for message+recipient checks after the message has been received. */ /* First thing is to wait for an initial greeting. */ -Ustrcpy(big_buffer, "initial connection"); +Ustrcpy(big_buffer, US"initial connection"); if (!lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout)) goto RESPONSE_FAILED; @@ -641,7 +644,7 @@ if (send_data) sigalrm_seen = FALSE; transport_write_timeout = timeout; - Ustrcpy(big_buffer, "sending data block"); /* For error messages */ + Ustrcpy(big_buffer, US"sending data block"); /* For error messages */ DEBUG(D_transport|D_v) debug_printf(" LMTP>> writing message and terminating \".\"\n"); @@ -657,7 +660,7 @@ if (send_data) goto RESPONSE_FAILED; } - Ustrcpy(big_buffer, "end of data"); /* For error messages */ + Ustrcpy(big_buffer, US"end of data"); /* For error messages */ /* We now expect a response for every address that was accepted above, in the same order. For those that get a response, their status is fixed; @@ -763,9 +766,9 @@ if (errno == ERRNO_CHHEADER_FAIL) string_sprintf("Failed to expand headers_add or headers_remove: %s", expand_string_message); else if (errno == ERRNO_FILTER_FAIL) - addrlist->message = string_sprintf("Filter process failure"); + addrlist->message = US"Filter process failure"; else if (errno == ERRNO_WRITEINCOMPLETE) - addrlist->message = string_sprintf("Failed repeatedly to write data"); + addrlist->message = US"Failed repeatedly to write data"; else if (errno == ERRNO_SMTPFORMAT) addrlist->message = US"overlong LMTP command generated"; else diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 8e5c0c502..70df8729d 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -463,7 +463,7 @@ set_up_shell_command(const uschar ***argvptr, uschar *cmd, { const uschar **argv; -*argvptr = argv = store_get((4)*sizeof(uschar *)); +*argvptr = argv = store_get((4)*sizeof(uschar *), FALSE); argv[0] = US"/bin/sh"; argv[1] = US"-c"; @@ -938,7 +938,7 @@ if (!written_ok) addr->more_errno, (addr->more_errno == EX_EXECFAILED)? ": unable to execute command" : ""); else if (errno == ERRNO_WRITEINCOMPLETE) - addr->message = string_sprintf("Failed repeatedly to write data"); + addr->message = US"Failed repeatedly to write data"; else addr->message = string_sprintf("Error %d", errno); return FALSE; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ad0de52b6..617a55a16 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -620,7 +620,7 @@ switch(*errno_value) return FALSE; case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */ - *message = string_sprintf("failed to write a data block"); + *message = US"failed to write a data block"; return FALSE; #ifdef SUPPORT_I18N @@ -1184,8 +1184,14 @@ while (count-- > 0) else if (errno != 0 || sx->buffer[0] == 0) { - string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>", + gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs; + + /* Use taint-unchecked routines for writing into big_buffer, trusting + that we'll never expand it. */ + + g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>", transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes)); + string_from_gstring(g); return -2; } @@ -1555,20 +1561,20 @@ Globals f.smtp_authenticated Return True on error, otherwise buffer has (possibly empty) terminated string */ -BOOL +static BOOL smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist, smtp_transport_options_block *ob) { -uschar *local_authenticated_sender = authenticated_sender; +uschar * local_authenticated_sender = authenticated_sender; #ifdef notdef debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N"); #endif -if (ob->authenticated_sender != NULL) +if (ob->authenticated_sender) { uschar *new = expand_string(ob->authenticated_sender); - if (new == NULL) + if (!new) { if (!f.expand_string_forcedfail) { @@ -1578,17 +1584,17 @@ if (ob->authenticated_sender != NULL) return TRUE; } } - else if (new[0] != 0) local_authenticated_sender = new; + else if (*new) local_authenticated_sender = new; } /* Add the authenticated sender address if present */ -if ((f.smtp_authenticated || ob->authenticated_sender_force) && - local_authenticated_sender != NULL) +if ( (f.smtp_authenticated || ob->authenticated_sender_force) + && local_authenticated_sender) { - string_format(buffer, bufsize, " AUTH=%s", + string_format_nt(buffer, bufsize, " AUTH=%s", auth_xtextencode(local_authenticated_sender, - Ustrlen(local_authenticated_sender))); + Ustrlen(local_authenticated_sender))); client_authenticated_sender = string_copy(local_authenticated_sender); } else @@ -3040,7 +3046,7 @@ if ( sx->peer_offered & OPTION_UTF8 && addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt ) - Ustrcpy(p, " SMTPUTF8"), p += 9; + Ustrcpy(p, US" SMTPUTF8"), p += 9; #endif /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */ @@ -3061,9 +3067,9 @@ for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0; if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop) { if (dsn_ret == dsn_ret_hdrs) - { Ustrcpy(p, " RET=HDRS"); p += 9; } + { Ustrcpy(p, US" RET=HDRS"); p += 9; } else if (dsn_ret == dsn_ret_full) - { Ustrcpy(p, " RET=FULL"); p += 9; } + { Ustrcpy(p, US" RET=FULL"); p += 9; } if (dsn_envid) { @@ -3100,7 +3106,7 @@ if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop)) { BOOL first = TRUE; - Ustrcpy(p, " NOTIFY="); + Ustrcpy(p, US" NOTIFY="); while (*p) p++; for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i]) { @@ -4598,6 +4604,17 @@ if (!hostlist || (ob->hosts_override && ob->hosts)) else if (ob->hosts_randomize) s = expanded_hosts = string_copy(s); + if (is_tainted(s)) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "attempt to use tainted host list '%s' from '%s' in transport %s", + s, ob->hosts, tblock->name); + /* Avoid leaking info to an attacker */ + addrlist->message = US"internal configuration error"; + addrlist->transport_return = PANIC; + return FALSE; + } + host_build_hostlist(&hostlist, s, ob->hosts_randomize); /* Check that the expansion yielded something useful. */ @@ -5082,7 +5099,7 @@ retry_non_continued: if (expanded_hosts) { - thost = store_get(sizeof(host_item)); + thost = store_get(sizeof(host_item), FALSE); *thost = *host; thost->name = string_copy(host->name); thost->address = string_copy(host->address); diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 79d674fa1..ac5620971 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -198,9 +198,6 @@ extern void smtp_transport_closedown(transport_instance *); -extern BOOL smtp_mail_auth_str(uschar *, unsigned, - address_item *, smtp_transport_options_block *); - #ifdef SUPPORT_SOCKS extern int socks_sock_connect(host_item *, int, int, uschar *, transport_instance *, int); diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index 4caf0cdcf..611895e06 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -258,13 +258,12 @@ off_t sum = 0; struct dirent *ent; struct stat statbuf; -dir = opendir(CS path); -if (dir == NULL) return 0; +if (!(dir = opendir(CS path))) + return 0; -while ((ent = readdir(dir)) != NULL) +while ((ent = readdir(dir))) { - uschar *name = US ent->d_name; - uschar buffer[1024]; + uschar * s, * name = US ent->d_name; if (Ustrcmp(name, ".") == 0 || Ustrcmp(name, "..") == 0) continue; @@ -282,26 +281,19 @@ while ((ent = readdir(dir)) != NULL) /* The name is OK; stat it. */ - if (!string_format(buffer, sizeof(buffer), "%s/%s", path, name)) - { - DEBUG(D_transport) - debug_printf("maildir_compute_size: name too long: dir=%s name=%s\n", - path, name); - continue; - } - - if (Ustat(buffer, &statbuf) < 0) + s = string_sprintf("%s/%s", path, name); + if (Ustat(s, &statbuf) < 0) { DEBUG(D_transport) debug_printf("maildir_compute_size: stat error %d for %s: %s\n", errno, - buffer, strerror(errno)); + s, strerror(errno)); continue; } if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { DEBUG(D_transport) - debug_printf("skipping %s/%s: not a directory\n", path, name); + debug_printf("skipping %s/%s: not a directory\n", s, name); continue; } @@ -312,18 +304,14 @@ while ((ent = readdir(dir)) != NULL) /* If this is a maildir folder, call this function recursively. */ if (name[0] == '.') - { - sum += maildir_compute_size(buffer, filecount, latest, regex, dir_regex, + sum += maildir_compute_size(s, filecount, latest, regex, dir_regex, timestamp_only); - } /* Otherwise it must be a folder that contains messages (e.g. new or cur), so we need to get its size, unless all we are interested in is the timestamp. */ else if (!timestamp_only) - { - sum += check_dir_size(buffer, filecount, regex); - } + sum += check_dir_size(s, filecount, regex); } closedir(dir); @@ -392,8 +380,7 @@ the same thing. */ filename = string_sprintf("%s/maildirsize", path); DEBUG(D_transport) debug_printf("looking for maildirsize in %s\n", path); -fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600); -if (fd < 0) +if ((fd = Uopen(filename, O_RDWR|O_APPEND, ob->mode ? ob->mode : 0600)) < 0) { if (errno != ENOENT) return -1; DEBUG(D_transport) @@ -405,8 +392,7 @@ if (fd < 0) still correct, and that the size of the file is still small enough. If so, compute the maildir size from the file. */ -count = read(fd, buffer, sizeof(buffer)); -if (count >= sizeof(buffer)) +if ((count = read(fd, buffer, sizeof(buffer))) >= sizeof(buffer)) { DEBUG(D_transport) debug_printf("maildirsize file too big (%d): recalculating\n", count); diff --git a/src/src/tree.c b/src/src/tree.c index ff792bb6e..d5a409651 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -29,10 +29,11 @@ Returns: nothing void tree_add_nonrecipient(uschar *s) { -tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s)); +rmark rpoint = store_mark(); +tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s)); Ustrcpy(node->name, s); node->data.ptr = NULL; -if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(node); +if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(rpoint); } @@ -53,10 +54,11 @@ Returns: nothing void tree_add_duplicate(uschar *s, address_item *addr) { -tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s)); +rmark rpoint = store_mark(); +tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s)); Ustrcpy(node->name, s); node->data.ptr = addr; -if (!tree_insertnode(&tree_duplicates, node)) store_reset(node); +if (!tree_insertnode(&tree_duplicates, node)) store_reset(rpoint); } @@ -74,14 +76,16 @@ Returns: nothing void tree_add_unusable(host_item *h) { +rmark rpoint = store_mark(); tree_node *node; uschar s[256]; sprintf(CS s, "T:%.200s:%s", h->name, h->address); -node = store_get(sizeof(tree_node) + Ustrlen(s)); +node = store_get(sizeof(tree_node) + Ustrlen(s), + is_tainted(h->name) || is_tainted(h->address)); Ustrcpy(node->name, s); node->data.val = h->why; if (h->status == hstatus_unusable_expired) node->data.val += 256; -if (!tree_insertnode(&tree_unusable, node)) store_reset(node); +if (!tree_insertnode(&tree_unusable, node)) store_reset(rpoint); } @@ -369,7 +373,7 @@ static void tree_add_var(uschar * name, uschar * val, void * ctx) { tree_node ** root = ctx; -tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name)); +tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name)); Ustrcpy(node->name, name); node->data.ptr = val; (void) tree_insertnode(root, node); diff --git a/src/src/utf8.c b/src/src/utf8.c index dec72280f..529a9a660 100644 --- a/src/src/utf8.c +++ b/src/src/utf8.c @@ -143,7 +143,7 @@ if (!string_is_utf8(utf8)) return string_copy(utf8); p = (punycode_uint *) stringprep_utf8_to_ucs4(CCS utf8, -1, &ucs4_len); p_len = ucs4_len*4; /* this multiplier is pure guesswork */ -res = store_get(p_len+5); +res = store_get(p_len+5, is_tainted(utf8)); res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-'; @@ -172,7 +172,7 @@ uschar * s, * res; DEBUG(D_expand) debug_printf("l_a2u: '%s'\n", alabel); alabel += 4; p_len = Ustrlen(alabel); -p = (punycode_uint *) store_get((p_len+1) * sizeof(*p)); +p = store_get((p_len+1) * sizeof(*p), is_tainted(alabel)); if ((rc = punycode_decode(p_len, CCS alabel, &p_len, p, NULL)) != PUNYCODE_SUCCESS) { diff --git a/src/src/verify.c b/src/src/verify.c index 25a4a0ca7..e98dee669 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -98,7 +98,7 @@ if (type[0] == 'd' && cache_record->result != ccache_reject) { if (length == sizeof(dbdata_callout_cache_obs)) { - dbdata_callout_cache *new = store_get(sizeof(dbdata_callout_cache)); + dbdata_callout_cache *new = store_get(sizeof(dbdata_callout_cache), FALSE); memcpy(new, cache_record, length); new->postmaster_stamp = new->random_stamp = new->time_stamp; cache_record = new; @@ -420,7 +420,7 @@ if (addr->transport == cutthrough.addr.transport) if (done) { - address_item * na = store_get(sizeof(address_item)); + address_item * na = store_get(sizeof(address_item), FALSE); *na = cutthrough.addr; cutthrough.addr = *addr; cutthrough.addr.host_used = &cutthrough.host; @@ -976,8 +976,7 @@ no_conn: { extern int acl_where; /* src/acl.c */ errno = 0; - addr->message = string_sprintf( - "response to \"EHLO\" did not include SMTPUTF8"); + addr->message = US"response to \"EHLO\" did not include SMTPUTF8"; addr->user_message = acl_where == ACL_WHERE_RCPT ? US"533 no support for internationalised mailbox name" : US"550 mailbox unavailable"; @@ -1089,7 +1088,7 @@ no_conn: for (address_item * caddr = &cutthrough.addr, * parent = addr->parent; parent; caddr = caddr->parent, parent = parent->parent) - *(caddr->parent = store_get(sizeof(address_item))) = *parent; + *(caddr->parent = store_get(sizeof(address_item), FALSE)) = *parent; ctctx.outblock.buffer = ctbuffer; ctctx.outblock.buffersize = sizeof(ctbuffer); @@ -3396,9 +3395,9 @@ else else { /* Set up a tree entry to cache the lookup */ - t = store_get(sizeof(tree_node) + Ustrlen(query)); + t = store_get(sizeof(tree_node) + Ustrlen(query), is_tainted(query)); Ustrcpy(t->name, query); - t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block)); + t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), FALSE); (void)tree_insertnode(&dnsbl_cache, t); } diff --git a/src/src/version.c b/src/src/version.c index 91d812428..118ebbdf2 100644 --- a/src/src/version.c +++ b/src/src/version.c @@ -51,7 +51,7 @@ version_date[0] = 0; Ustrncat(version_date, EXIM_BUILD_DATE_OVERRIDE, sizeof(date_buffer)); #else -Ustrcpy(today, __DATE__); +Ustrcpy(today, US __DATE__); if (today[4] == ' ') today[4] = '0'; today[3] = today[6] = '-'; @@ -60,8 +60,8 @@ version_date[0] = 0; Ustrncat(version_date, today+4, 3); Ustrncat(version_date, today, 4); Ustrncat(version_date, today+7, 4); -Ustrcat(version_date, " "); -Ustrcat(version_date, __TIME__); +Ustrcat(version_date, US" "); +Ustrcat(version_date, US __TIME__); #endif } diff --git a/test/confs/0102 b/test/confs/0102 index dde11a5e9..2497fd032 100644 --- a/test/confs/0102 +++ b/test/confs/0102 @@ -25,7 +25,9 @@ localuser: local_part_prefix = bsmtp_ : mmdf_ local_part_prefix_optional retry_use_local_part - transport = ${local_part_prefix}local_delivery + transport = ${if !def:local_part_prefix {} \ + {${if eq {bsmtp_}{$local_part_prefix} {bsmtp_}\ + {${if eq {mmdf_}{$local_part_prefix} {mmdf_} {}}}}}}local_delivery # ----- Transports ----- diff --git a/test/confs/0137 b/test/confs/0137 index 1a2094ef3..7c48d6234 100644 --- a/test/confs/0137 +++ b/test/confs/0137 @@ -8,6 +8,7 @@ log_selector = +subject domainlist local_domains = test.ex qualify_domain = test.ex +untrusted_set_sender = * # ----- Routers ----- @@ -17,7 +18,10 @@ begin routers smart: driver = accept retry_use_local_part - transport = $h_transport: + transport = ${if eq {t1}{$sender_address_local_part} {local_delivery} \ + {${if eq {t2}{$sender_address_local_part} {local_delivery_fcntl} \ + {${if eq {t3}{$sender_address_local_part} {local_delivery_fcntl_blocking} \ + {} }}}}} # ----- Transports ----- diff --git a/test/confs/0140 b/test/confs/0140 index fbc64b43b..02567153c 100644 --- a/test/confs/0140 +++ b/test/confs/0140 @@ -22,7 +22,7 @@ reply: driver = accept retry_use_local_part senders = ! - transport = $local_part + transport = ${if eq {reply1}{$local_part} {reply1}{reply2}} # ----- Transports ----- diff --git a/test/confs/0284 b/test/confs/0284 index d38f38390..0103742f4 100644 --- a/test/confs/0284 +++ b/test/confs/0284 @@ -16,7 +16,9 @@ begin routers all: driver = accept address_data = ${if match{$local_part}{^(.)}{$1}} - transport = ${if match{$local_part}{^.*-(.*)\$}{$1}fail} + transport = ${if eq {${substr_-1_1:$local_part}}{1} {t1} \ + {${if eq {${substr_-1_1:$local_part}}{2} {t2} \ + {${if eq {${substr_-1_1:$local_part}}{3} {t3} {t4}}}}}} # ----- Transports ----- diff --git a/test/confs/0386 b/test/confs/0386 index 5eecfcc98..0af3202a3 100644 --- a/test/confs/0386 +++ b/test/confs/0386 @@ -6,11 +6,22 @@ primary_hostname = myhost.test.ex # ----- Main settings ----- -acl_smtp_rcpt = DIR/aux-fixed/TESTNUM.acl$local_part +acl_smtp_rcpt = chk_rcpt qualify_domain = test.ex trusted_users = CALLER +# ----- ACL ----- +begin acl + +chk_rcpt: + accept local_parts = 1 + endpass + acl = DIR/aux-fixed/TESTNUM.acl1 + accept local_parts = 2 + endpass + acl = DIR/aux-fixed/TESTNUM.acl2 + # ----- Routers ----- begin routers diff --git a/test/confs/0428 b/test/confs/0428 index 38601f30b..5ee5cdbb8 100644 --- a/test/confs/0428 +++ b/test/confs/0428 @@ -36,16 +36,112 @@ r1: user = CALLER file_transport = t1 -r2: +r2_8: + driver = redirect + local_parts = userx8 + allow_filter + data = #Sieve filter\n \ + require["fileinto","comparator-i;ascii-numeric"]; \ + if header :comparator "i;ascii-numeric" "X-Sieve" "99" { \ + fileinto "inbox.JUNK"; \ + stop; \ + } + user = CALLER + file_transport = t1 + reply_transport = t3 + +r2_9: + driver = redirect + local_parts = userx9 + allow_filter + data = #Sieve filter\n \ + require["fileinto","comparator-i;ascii-numeric"]; \ + if header :comparator "i;ascii-numeric" "X-Sieve" "98" { \ + fileinto "inbox.JUNK"; \ + stop; \ + } + user = CALLER + file_transport = t1 + reply_transport = t3 + +r2_10: + driver = redirect + local_parts = userx10 + allow_filter + data = #Sieve filter\n \ + require["fileinto","comparator-i;ascii-numeric"]; \ + if header :comparator "i;ascii-numeric" "X-Sieve" "99" { \ + fileinto "inbox.JUNK"; \ + stop; \ + } + user = CALLER + file_transport = t1 + reply_transport = t3 + +r2_11: driver = redirect + local_parts = userx11 + allow_filter + data = #Sieve filter\n \ + require["fileinto","comparator-i;ascii-numeric"]; \ + if header :comparator "i;ascii-numeric" "X-Sieve" "-99" { \ + fileinto "inbox.JUNK"; \ + stop; \ + } + user = CALLER + file_transport = t1 + reply_transport = t3 + +r2_12: + driver = redirect + local_parts = userx12 + allow_filter + data = #Sieve filter\n \ + require["fileinto","comparator-i;ascii-numeric"]; \ + if header :comparator "i;ascii-numeric" "X-Sieve" "-98" { \ + fileinto "inbox.JUNK"; \ + stop; \ + } + user = CALLER + file_transport = t1 + reply_transport = t3 + +r2_13: + driver = redirect + local_parts = userx13 : someone13 + allow_filter + data = #Sieve filter\n \ + require ["vacation"]; \ + vacation "I am gone. Not here."; + user = CALLER + file_transport = t1 + reply_transport = t3 + sieve_vacation_directory = DIR/test-vacation-directory + +r2_14: + driver = redirect + local_parts = userx14 local_part_suffix = -* local_part_suffix_optional allow_filter + data = #Sieve filter\n \ + require ["envelope","fileinto"]; \ + if envelope :matches :localpart "to" "*-suffix" { \ + fileinto "userx-sawsuffix"; \ + stop; \ + } + user = CALLER + file_transport = t1 + reply_transport = t3 + +r2: + driver = redirect + allow_filter + skip_syntax_errors data = "#Sieve filter\n$h_filter:" user = CALLER file_transport = t1 reply_transport = t3 - sieve_vacation_directory = DIR/test-vacation-directory # ----- Transports ----- diff --git a/test/confs/0478 b/test/confs/0478 index d9fd0a5ce..cfb9a17b1 100644 --- a/test/confs/0478 +++ b/test/confs/0478 @@ -27,7 +27,9 @@ begin transports t1: driver = smtp port = PORT_D - hosts = ${if eq {$sender_host_address}{}{$local_part}{V4NET.0.0.2}} + hosts = ${if !eq {$sender_host_address}{} {V4NET.0.0.2} \ + {${if eq {127.0.0.1}{$local_part} {127.0.0.1} \ + {${if eq {V4NET.0.0.1}{$local_part} {V4NET.0.0.1}}}}}} allow_localhost connect_timeout = 1s diff --git a/test/confs/0504 b/test/confs/0504 index b2dadfcb3..c7b0131c0 100644 --- a/test/confs/0504 +++ b/test/confs/0504 @@ -13,7 +13,7 @@ begin routers r1: driver = accept - transport = $h_transport: + transport = OPT # ----- Transports ----- diff --git a/test/confs/0610 b/test/confs/0610 index 0567d8aaf..49778352f 100644 --- a/test/confs/0610 +++ b/test/confs/0610 @@ -28,9 +28,15 @@ conn_chk: begin routers -client: +r1: driver = accept - transport = $sender_address_local_part + condition = ${if eq {t1}{$sender_address_local_part}} + transport = t1 + +r2: + driver = accept + condition = ${if eq {t2}{$sender_address_local_part}} + transport = t2 # ----- Transports ----- diff --git a/test/confs/1003 b/test/confs/1003 index bdbd2e6c3..d15a892fb 100644 --- a/test/confs/1003 +++ b/test/confs/1003 @@ -21,7 +21,11 @@ begin routers client: driver = accept condition = ${if eq {SERVER}{server}{no}{yes}} - transport = send_to_server + address_data = ${substr_1_1:$domain} + transport = send_to_server${if eq {1}{$address_data} {1} \ + {${if eq {2}{$address_data} {2} \ + {${if eq {3}{$address_data} {3} \ + {${if eq {4}{$address_data} {4}{5}}}}}}}} server: driver = accept @@ -37,10 +41,45 @@ local_delivery: file = DIR/test-mail/$local_part user = CALLER +send_to_server1: + driver = smtp + allow_localhost + hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}} + port = PORT_D + interface = <; ::1 ; HOSTIPV4 + +send_to_server2: + driver = smtp + allow_localhost + hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}} + port = PORT_D + interface = <; HOSTIPV6 ; HOSTIPV4 + +send_to_server3: + driver = smtp + allow_localhost + hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}} + port = PORT_D + interface = <; ${if eq{0}{1}{HOSTIPV6}fail} + +send_to_server4: + driver = smtp + allow_localhost + hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}} + port = PORT_D + interface = <; ${if eq{0}{1}{HOSTIPV6}{ }} + +send_to_server5: + driver = smtp + allow_localhost + hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}} + port = PORT_D + interface = <; ${if + send_to_server: driver = smtp allow_localhost - hosts = $h_hosts + hosts = ${if eq {$local_part}{user4} {127.0.0.1} {<; ::1}} port = PORT_D interface = ${expand:$h_interface:} diff --git a/test/confs/5000 b/test/confs/5000 index e3328c6a1..de9b4f8e8 100644 --- a/test/confs/5000 +++ b/test/confs/5000 @@ -2,6 +2,8 @@ .include DIR/aux-var/std_conf_prefix +OPT = + primary_hostname = myhost.test.ex # ----- Main settings ----- @@ -14,7 +16,7 @@ begin routers localuser: driver = accept local_parts = userx - transport = $h_maildir:appendfile + transport = VALUE # ----- Transports ----- @@ -33,7 +35,7 @@ maildir_tagged_appendfile: directory = DIR/test-mail envelope_to_add maildir_format - maildir_tag = ${expand:$h_tag:} + maildir_tag = OPT message_prefix = quota = 20K quota_size_regex = S=(\d+)$ @@ -45,7 +47,7 @@ maildir_taggedX_appendfile: directory = DIR/test-mail envelope_to_add maildir_format - maildir_tag = ${expand:$h_tag:} + maildir_tag = OPT message_prefix = quota = 20K quota_size_regex = ,S=(\d+): diff --git a/test/confs/5050 b/test/confs/5050 index 462d7dcf2..4258a727f 100644 --- a/test/confs/5050 +++ b/test/confs/5050 @@ -19,7 +19,9 @@ localuser: local_part_prefix = bsmtp_ : mmdf_ : mbx_ local_part_prefix_optional retry_use_local_part - transport = ${local_part_prefix}local_delivery + transport = ${if eq {bsmtp_} {${local_part_prefix}} {bsmtp_} \ + {${if eq {mmdf_} {${local_part_prefix}} {mmdf_} \ + {${if eq {mbx_} {${local_part_prefix}} {mbx_}{}}}}}}local_delivery # ----- Transports ----- diff --git a/test/confs/5103 b/test/confs/5103 index 7212fb30e..e9ac80c6a 100644 --- a/test/confs/5103 +++ b/test/confs/5103 @@ -13,7 +13,7 @@ begin routers r1: driver = accept - transport = $h_transport: + transport = t1 # ----- Transports ----- diff --git a/test/log/0137 b/test/log/0137 index 30ee4cdd5..022c9de93 100644 --- a/test/log/0137 +++ b/test/log/0137 @@ -1,19 +1,19 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="First" +1999-03-02 09:44:33 10HmaX-0005vi-00 <= t1@foo U=CALLER P=local S=sss T="First" 1999-03-02 09:44:33 10HmaX-0005vi-00 => userx R=smart T=local_delivery 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed -1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="Second" +1999-03-02 09:44:33 10HmaY-0005vi-00 <= t1@foo U=CALLER P=local S=sss T="Second" 1999-03-02 09:44:33 10HmaY-0005vi-00 == userx@test.ex R=smart T=local_delivery defer (-9): failed to lock mailbox TESTSUITE/test-mail/userx (lock file) 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaY-0005vi-00 => userx R=smart T=local_delivery 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed 1999-03-02 09:44:33 End queue run: pid=pppp -qf -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="Third" +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= t2@foo U=CALLER P=local S=sss T="Third" 1999-03-02 09:44:33 10HmaZ-0005vi-00 == userx@test.ex R=smart T=local_delivery_fcntl defer (-9): failed to lock mailbox TESTSUITE/test-mail/userx (fcntl/flock) 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx R=smart T=local_delivery_fcntl 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed 1999-03-02 09:44:33 End queue run: pid=pppp -qf -1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss T="Fourth" +1999-03-02 09:44:33 10HmbA-0005vi-00 <= t3@foo U=CALLER P=local S=sss T="Fourth" 1999-03-02 09:44:33 10HmbA-0005vi-00 == userx@test.ex R=smart T=local_delivery_fcntl_blocking defer (-9): failed to lock mailbox TESTSUITE/test-mail/userx (fcntl/flock) 1999-03-02 09:44:33 Start queue run: pid=pppp -qf 1999-03-02 09:44:33 10HmbA-0005vi-00 => userx R=smart T=local_delivery_fcntl_blocking diff --git a/test/log/0428 b/test/log/0428 index c33ef3266..a1dfd7810 100644 --- a/test/log/0428 +++ b/test/log/0428 @@ -26,28 +26,28 @@ 1999-03-02 09:44:33 10HmbF-0005vi-00 => TESTSUITE/test-mail/userx R=r2 T=t1 1999-03-02 09:44:33 10HmbF-0005vi-00 Completed 1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbG-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2 T=t1 +1999-03-02 09:44:33 10HmbG-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2_8 T=t1 1999-03-02 09:44:33 10HmbG-0005vi-00 Completed 1999-03-02 09:44:33 10HmbH-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbH-0005vi-00 => TESTSUITE/test-mail/userx R=r2 T=t1 +1999-03-02 09:44:33 10HmbH-0005vi-00 => TESTSUITE/test-mail/userx9 R=r2_9 T=t1 1999-03-02 09:44:33 10HmbH-0005vi-00 Completed 1999-03-02 09:44:33 10HmbI-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbI-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2 T=t1 +1999-03-02 09:44:33 10HmbI-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2_10 T=t1 1999-03-02 09:44:33 10HmbI-0005vi-00 Completed 1999-03-02 09:44:33 10HmbJ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbJ-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2 T=t1 +1999-03-02 09:44:33 10HmbJ-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2_11 T=t1 1999-03-02 09:44:33 10HmbJ-0005vi-00 Completed 1999-03-02 09:44:33 10HmbK-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbK-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2 T=t1 +1999-03-02 09:44:33 10HmbK-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK R=r2_12 T=t1 1999-03-02 09:44:33 10HmbK-0005vi-00 Completed 1999-03-02 09:44:33 10HmbL-0005vi-00 <= someone@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbL-0005vi-00 => TESTSUITE/test-mail/userx R=r2 T=t1 +1999-03-02 09:44:33 10HmbL-0005vi-00 => TESTSUITE/test-mail/userx13 R=r2_13 T=t1 1999-03-02 09:44:33 10HmbM-0005vi-00 <= <> R=10HmbL-0005vi-00 U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmbM-0005vi-00 => someone R=rb T=t2 1999-03-02 09:44:33 10HmbM-0005vi-00 Completed -1999-03-02 09:44:33 10HmbL-0005vi-00 => >someone@test.ex R=r2 T=t3 +1999-03-02 09:44:33 10HmbL-0005vi-00 => >someone@test.ex R=r2_13 T=t3 1999-03-02 09:44:33 10HmbL-0005vi-00 Completed 1999-03-02 09:44:33 10HmbN-0005vi-00 <= someone@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx R=r2 T=t1 -1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx-sawsuffix R=r2 T=t1 +1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx14 R=r2_14 T=t1 +1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx-sawsuffix R=r2_14 T=t1 1999-03-02 09:44:33 10HmbN-0005vi-00 Completed diff --git a/test/log/0610 b/test/log/0610 index 05025099e..d741a86a9 100644 --- a/test/log/0610 +++ b/test/log/0610 @@ -2,9 +2,9 @@ 1999-03-02 09:44:33 10HmaY-0005vi-00 <= t2@dustybelt.tld U=CALLER P=local-smtp S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qq 1999-03-02 09:44:33 10HmaX-0005vi-00 H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later -1999-03-02 09:44:33 10HmaX-0005vi-00 == fred@anotherone.tld R=client T=t1 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later +1999-03-02 09:44:33 10HmaX-0005vi-00 == fred@anotherone.tld R=r1 T=t1 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later 1999-03-02 09:44:33 10HmaY-0005vi-00 H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later -1999-03-02 09:44:33 10HmaY-0005vi-00 == fred@anotherone.tld R=client T=t2 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later +1999-03-02 09:44:33 10HmaY-0005vi-00 == fred@anotherone.tld R=r2 T=t2 defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after initial connection: 451 Temporary local problem - please try later 1999-03-02 09:44:33 End queue run: pid=pppp -qq ******** SERVER ******** diff --git a/test/log/1003 b/test/log/1003 index b70b6d182..2ecb76664 100644 --- a/test/log/1003 +++ b/test/log/1003 @@ -4,15 +4,15 @@ 1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 Start queue run: pid=pppp -qf -1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmbC-0005vi-00" +1999-03-02 09:44:33 10HmaY-0005vi-00 => user4@h1.test.ex R=client T=send_to_server1 H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmbC-0005vi-00" 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx@test.ex R=client T=send_to_server H=::1 [::1] C="250 OK id=10HmbD-0005vi-00" +1999-03-02 09:44:33 10HmaZ-0005vi-00 => user6@h2.test.ex R=client T=send_to_server2 H=::1 [::1] C="250 OK id=10HmbD-0005vi-00" 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed -1999-03-02 09:44:33 10HmbA-0005vi-00 => userx@test.ex R=client T=send_to_server H=::1 [::1] C="250 OK id=10HmbE-0005vi-00" +1999-03-02 09:44:33 10HmbA-0005vi-00 => user6@h3.test.ex R=client T=send_to_server3 H=::1 [::1] C="250 OK id=10HmbE-0005vi-00" 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed -1999-03-02 09:44:33 10HmbB-0005vi-00 => userx@test.ex R=client T=send_to_server H=::1 [::1] C="250 OK id=10HmbF-0005vi-00" +1999-03-02 09:44:33 10HmbB-0005vi-00 => user6@h4.test.ex R=client T=send_to_server4 H=::1 [::1] C="250 OK id=10HmbF-0005vi-00" 1999-03-02 09:44:33 10HmbB-0005vi-00 Completed -1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@test.ex R=client T=send_to_server defer (-1): failed to expand "interface" option for send_to_server transport: internal expansion of "<; ${if" failed: condition name expected, but found "" +1999-03-02 09:44:33 10HmaX-0005vi-00 == user6@h5.test.ex R=client T=send_to_server5 defer (-1): failed to expand "interface" option for send_to_server5 transport: condition name expected, but found "" 1999-03-02 09:44:33 End queue run: pid=pppp -qf ******** SERVER ******** diff --git a/test/log/5000 b/test/log/5000 index 89f91b81b..68bedae76 100644 --- a/test/log/5000 +++ b/test/log/5000 @@ -16,7 +16,7 @@ 1999-03-02 09:44:33 10HmbD-0005vi-00 => userx R=localuser T=maildir_tagged_appendfile 1999-03-02 09:44:33 10HmbD-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected +1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected 1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmbE-0005vi-00 => userx R=localuser T=maildir_tagged_appendfile 1999-03-02 09:44:33 10HmbE-0005vi-00 Completed diff --git a/test/mail/0137.userx b/test/mail/0137.userx index c44698254..cfcf6e2f7 100644 --- a/test/mail/0137.userx +++ b/test/mail/0137.userx @@ -1,52 +1,56 @@ -From CALLER@test.ex Tue Mar 02 09:44:33 1999 +From t1@foo Tue Mar 02 09:44:33 1999 Received: from CALLER by the.local.host.name with local (Exim x.yz) - (envelope-from ) + (envelope-from ) id 10HmaX-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Subject: First -Transport: local_delivery Message-Id: -From: CALLER_NAME +From: CALLER_NAME +Sender: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 +local_delivery First message. -From CALLER@test.ex Tue Mar 02 09:44:33 1999 +From t1@foo Tue Mar 02 09:44:33 1999 Received: from CALLER by the.local.host.name with local (Exim x.yz) - (envelope-from ) + (envelope-from ) id 10HmaY-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Subject: Second -Transport: local_delivery Message-Id: -From: CALLER_NAME +From: CALLER_NAME +Sender: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 +local_delivery Second message -From CALLER@test.ex Tue Mar 02 09:44:33 1999 +From t2@foo Tue Mar 02 09:44:33 1999 Received: from CALLER by the.local.host.name with local (Exim x.yz) - (envelope-from ) + (envelope-from ) id 10HmaZ-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Subject: Third -Transport: local_delivery_fcntl Message-Id: -From: CALLER_NAME +From: CALLER_NAME +Sender: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 +local_delivery_fcntl Third message -From CALLER@test.ex Tue Mar 02 09:44:33 1999 +From t3@foo Tue Mar 02 09:44:33 1999 Received: from CALLER by the.local.host.name with local (Exim x.yz) - (envelope-from ) + (envelope-from ) id 10HmbA-0005vi-00 for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 Subject: Fourth -Transport: local_delivery_fcntl_blocking Message-Id: -From: CALLER_NAME +From: CALLER_NAME +Sender: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 +local_delivery_fcntl_blocking Fourth message diff --git a/test/mail/0428.inbox.JUNK b/test/mail/0428.inbox.JUNK index 088f808c7..1ccfe989a 100644 --- a/test/mail/0428.inbox.JUNK +++ b/test/mail/0428.inbox.JUNK @@ -1,80 +1,80 @@ From CALLER@test.ex Tue Mar 02 09:44:33 1999 Return-path: -Envelope-to: userx@test.ex +Envelope-to: userx8@test.ex Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 Received: from CALLER by mail.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbG-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + for userx8@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 X-Sieve: 99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; - if header :comparator "i;ascii-numeric" "X-Sieve" "99" { - fileinto "inbox.JUNK"; - stop; - } Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 + require["fileinto","comparator-i;ascii-numeric"]; + if header :comparator "i;ascii-numeric" "X-Sieve" "99" { + fileinto "inbox.JUNK"; + stop; + } Test 8 From CALLER@test.ex Tue Mar 02 09:44:33 1999 Return-path: -Envelope-to: userx@test.ex +Envelope-to: userx10@test.ex Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 Received: from CALLER by mail.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbI-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + for userx10@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 X-Sieve: 99- -Filter: require["fileinto","comparator-i;ascii-numeric"]; - if header :comparator "i;ascii-numeric" "X-Sieve" "99" { - fileinto "inbox.JUNK"; - stop; - } Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 + require["fileinto","comparator-i;ascii-numeric"]; + if header :comparator "i;ascii-numeric" "X-Sieve" "99" { + fileinto "inbox.JUNK"; + stop; + } Test 10 From CALLER@test.ex Tue Mar 02 09:44:33 1999 Return-path: -Envelope-to: userx@test.ex +Envelope-to: userx11@test.ex Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 Received: from CALLER by mail.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbJ-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + for userx11@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 X-Sieve: -99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; - if header :comparator "i;ascii-numeric" "X-Sieve" "-99" { - fileinto "inbox.JUNK"; - stop; - } Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 + require["fileinto","comparator-i;ascii-numeric"]; + if header :comparator "i;ascii-numeric" "X-Sieve" "-99" { + fileinto "inbox.JUNK"; + stop; + } Test 11 From CALLER@test.ex Tue Mar 02 09:44:33 1999 Return-path: -Envelope-to: userx@test.ex +Envelope-to: userx12@test.ex Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 Received: from CALLER by mail.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbK-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 + for userx12@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 X-Sieve: -99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; - if header :comparator "i;ascii-numeric" "X-Sieve" "-98" { - fileinto "inbox.JUNK"; - stop; - } Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 + require["fileinto","comparator-i;ascii-numeric"]; + if header :comparator "i;ascii-numeric" "X-Sieve" "-98" { + fileinto "inbox.JUNK"; + stop; + } Test 12 diff --git a/test/mail/0428.someone b/test/mail/0428.someone index a3ba52b88..8939761ea 100644 --- a/test/mail/0428.someone +++ b/test/mail/0428.someone @@ -5,7 +5,7 @@ Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 Received: from CALLER by mail.test.ex with local (Exim x.yz) id 10HmbM-0005vi-00 for someone@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -From: userx@test.ex +From: userx13@test.ex To: someone@test.ex Subject: Automated reply In-Reply-To: diff --git a/test/mail/0428.userx b/test/mail/0428.userx index 8c3402bd7..b47f439e7 100644 --- a/test/mail/0428.userx +++ b/test/mail/0428.userx @@ -27,59 +27,3 @@ Date: Tue, 2 Mar 1999 09:44:33 +0000 Test 7 -From CALLER@test.ex Tue Mar 02 09:44:33 1999 -Return-path: -Envelope-to: userx@test.ex -Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 -Received: from CALLER by mail.test.ex with local (Exim x.yz) - (envelope-from ) - id 10HmbH-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -X-Sieve: 99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; - if header :comparator "i;ascii-numeric" "X-Sieve" "98" { - fileinto "inbox.JUNK"; - stop; - } -Message-Id: -From: CALLER_NAME -Date: Tue, 2 Mar 1999 09:44:33 +0000 - -Test 9 - -From someone@test.ex Tue Mar 02 09:44:33 1999 -Return-path: -Envelope-to: userx@test.ex -Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 -Received: from CALLER by mail.test.ex with local (Exim x.yz) - (envelope-from ) - id 10HmbL-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -To: userx@test.ex -Filter: require ["vacation"]; - vacation "I am gone. Not here."; -Message-Id: -From: someone@test.ex -Date: Tue, 2 Mar 1999 09:44:33 +0000 - -Test 13 - -From someone@test.ex Tue Mar 02 09:44:33 1999 -Return-path: -Envelope-to: userx-suffix2@test.ex -Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 -Received: from CALLER by mail.test.ex with local (Exim x.yz) - (envelope-from ) - id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000 -To: userx-suffix@test.ex -Filter: require ["envelope","fileinto"]; - if envelope :matches :localpart "to" "*-suffix" { - fileinto "userx-sawsuffix"; - stop; - } -Message-Id: -From: someone@test.ex -Date: Tue, 2 Mar 1999 09:44:33 +0000 - -Test 14 - diff --git a/test/mail/0428.userx-sawsuffix b/test/mail/0428.userx-sawsuffix index ef2e8b5e3..c2a457a7e 100644 --- a/test/mail/0428.userx-sawsuffix +++ b/test/mail/0428.userx-sawsuffix @@ -1,19 +1,18 @@ From someone@test.ex Tue Mar 02 09:44:33 1999 Return-path: -Envelope-to: userx-suffix@test.ex +Envelope-to: userx14-suffix@test.ex Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 Received: from CALLER by mail.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000 -To: userx-suffix@test.ex -Filter: require ["envelope","fileinto"]; - if envelope :matches :localpart "to" "*-suffix" { - fileinto "userx-sawsuffix"; - stop; - } Message-Id: From: someone@test.ex Date: Tue, 2 Mar 1999 09:44:33 +0000 + require ["envelope","fileinto"]; + if envelope :matches :localpart "to" "*-suffix" { + fileinto "userx-sawsuffix"; + stop; + } Test 14 diff --git a/test/mail/0428.userx13 b/test/mail/0428.userx13 new file mode 100644 index 000000000..7a70d93b1 --- /dev/null +++ b/test/mail/0428.userx13 @@ -0,0 +1,17 @@ +From someone@test.ex Tue Mar 02 09:44:33 1999 +Return-path: +Envelope-to: userx13@test.ex +Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 +Received: from CALLER by mail.test.ex with local (Exim x.yz) + (envelope-from ) + id 10HmbL-0005vi-00 + for userx13@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 +To: userx13@test.ex +Message-Id: +From: someone@test.ex +Date: Tue, 2 Mar 1999 09:44:33 +0000 + + require ["vacation"]; + vacation "I am gone. Not here."; +Test 13 + diff --git a/test/mail/0428.userx14 b/test/mail/0428.userx14 new file mode 100644 index 000000000..0c542a788 --- /dev/null +++ b/test/mail/0428.userx14 @@ -0,0 +1,18 @@ +From someone@test.ex Tue Mar 02 09:44:33 1999 +Return-path: +Envelope-to: userx14-suffix2@test.ex +Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 +Received: from CALLER by mail.test.ex with local (Exim x.yz) + (envelope-from ) + id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000 +Message-Id: +From: someone@test.ex +Date: Tue, 2 Mar 1999 09:44:33 +0000 + + require ["envelope","fileinto"]; + if envelope :matches :localpart "to" "*-suffix" { + fileinto "userx-sawsuffix"; + stop; + } +Test 14 + diff --git a/test/mail/0428.userx9 b/test/mail/0428.userx9 new file mode 100644 index 000000000..1cfb1560a --- /dev/null +++ b/test/mail/0428.userx9 @@ -0,0 +1,20 @@ +From CALLER@test.ex Tue Mar 02 09:44:33 1999 +Return-path: +Envelope-to: userx9@test.ex +Delivery-date: Tue, 2 Mar 1999 09:44:33 +0000 +Received: from CALLER by mail.test.ex with local (Exim x.yz) + (envelope-from ) + id 10HmbH-0005vi-00 + for userx9@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 +X-Sieve: 99 +Message-Id: +From: CALLER_NAME +Date: Tue, 2 Mar 1999 09:44:33 +0000 + + require["fileinto","comparator-i;ascii-numeric"]; + if header :comparator "i;ascii-numeric" "X-Sieve" "98" { + fileinto "inbox.JUNK"; + stop; + } +Test 9 + diff --git a/test/mail/5000.new/1.myhost.test.ex b/test/mail/5000.new/1.myhost.test.ex index 517121389..efa7079ca 100644 --- a/test/mail/5000.new/1.myhost.test.ex +++ b/test/mail/5000.new/1.myhost.test.ex @@ -2,7 +2,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmaY-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_ Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/mail/5000.new/2.myhost.test.ex b/test/mail/5000.new/2.myhost.test.ex index 1b5b06498..e2afa21cd 100644 --- a/test/mail/5000.new/2.myhost.test.ex +++ b/test/mail/5000.new/2.myhost.test.ex @@ -2,7 +2,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmaZ-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_ Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/mail/5000.new/3.myhost.test.ex:S370 b/test/mail/5000.new/3.myhost.test.ex:S370 index 7ea68ff5a..722505641 100644 --- a/test/mail/5000.new/3.myhost.test.ex:S370 +++ b/test/mail/5000.new/3.myhost.test.ex:S370 @@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbB-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_tagged_ -tag:Ssss Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/mail/5000.new/4.myhost.test.ex,S=370 b/test/mail/5000.new/4.myhost.test.ex,S=370 index ed3879152..d7b4768aa 100644 --- a/test/mail/5000.new/4.myhost.test.ex,S=370 +++ b/test/mail/5000.new/4.myhost.test.ex,S=370 @@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbC-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_tagged_ -tag:,S=sss Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/mail/5000.new/5.myhost.test.ex b/test/mail/5000.new/5.myhost.test.ex index 054bf9048..41aedbcd8 100644 --- a/test/mail/5000.new/5.myhost.test.ex +++ b/test/mail/5000.new/5.myhost.test.ex @@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbD-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_tagged_ -tag:${if eq{0}{1}{rhubarb}fail} Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/mail/5000.new/6.myhost.test.ex b/test/mail/5000.new/6.myhost.test.ex index df71e1788..a7210d10f 100644 --- a/test/mail/5000.new/6.myhost.test.ex +++ b/test/mail/5000.new/6.myhost.test.ex @@ -3,13 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbE-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_tagged_ -tag:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/mail/5000.new/7.myhost.test.ex,S=10694953:2,S b/test/mail/5000.new/7.myhost.test.ex,S=10694953:2,S index be53589ee..f33b5f27f 100644 --- a/test/mail/5000.new/7.myhost.test.ex,S=10694953:2,S +++ b/test/mail/5000.new/7.myhost.test.ex,S=10694953:2,S @@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from ) id 10HmbF-0005vi-00 for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -maildir:maildir_taggedX_ -tag:,S=sss:2,S Message-Id: From: CALLER_NAME Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/msglog/5000.10HmaX-0005vi-00 b/test/msglog/5000.10HmaX-0005vi-00 index ca265a69d..e50b21cf1 100644 --- a/test/msglog/5000.10HmaX-0005vi-00 +++ b/test/msglog/5000.10HmaX-0005vi-00 @@ -1,2 +1,2 @@ 1999-03-02 09:44:33 Received from CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected +1999-03-02 09:44:33 userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected diff --git a/test/paniclog/1003 b/test/paniclog/1003 index 89086b0bd..d85ecc495 100644 --- a/test/paniclog/1003 +++ b/test/paniclog/1003 @@ -1 +1 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@test.ex R=client T=send_to_server defer (-1): failed to expand "interface" option for send_to_server transport: internal expansion of "<; ${if" failed: condition name expected, but found "" +1999-03-02 09:44:33 10HmaX-0005vi-00 == user6@h5.test.ex R=client T=send_to_server5 defer (-1): failed to expand "interface" option for send_to_server5 transport: condition name expected, but found "" diff --git a/test/paniclog/5000 b/test/paniclog/5000 index 2427585ab..078251623 100644 --- a/test/paniclog/5000 +++ b/test/paniclog/5000 @@ -1 +1 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected +1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected diff --git a/test/runtest b/test/runtest index fe1f4efc9..a7b265b96 100755 --- a/test/runtest +++ b/test/runtest @@ -772,7 +772,7 @@ RESET_AFTER_EXTRA_LINE_READ: } # Port in host address in spool file output from -Mvh - s/^-host_address (.*)\.\d+/-host_address $1.9999/; + s/^(--?host_address) (.*)\.\d+/$1 $2.9999/; if ($dynamic_socket and $dynamic_socket->opened and my $port = $dynamic_socket->sockport) { s/^Connecting to 127\.0\.0\.1 port \K$port//; diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index 9334e0448..8e654ac95 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -932,8 +932,9 @@ exim -be -DPTBC=print_topbitchars escape: ${escape:B7·F2ò} **** # Checkout expansion debugging -exim -d-all+expand -be +exim -d-all+expand -f sndr@dom -be primary_hostname: $primary_hostname +sender_address: $sender_address match: ${if match{abcd}{\N^([ab]+)(\w+)$\N}{$2$1}fail} match: ${if match{wxyz}{\N^([ab]+)(\w+)$\N}{$2$1}fail} ${if eq {1}{1}{yes}{${lookup{xx}lsearch{/non/exist}}}} diff --git a/test/scripts/0000-Basic/0137 b/test/scripts/0000-Basic/0137 index 56941941f..78e04dd4b 100644 --- a/test/scripts/0000-Basic/0137 +++ b/test/scripts/0000-Basic/0137 @@ -1,29 +1,33 @@ # mailbox locking -exim -odi userx +exim -odi -f t1@foo userx Subject: First -Transport: local_delivery + +local_delivery First message. **** exim_lock -v test-mail/userx -exim -odi userx +exim -odi -f t1@foo userx Subject: Second -Transport: local_delivery + +local_delivery Second message **** exim -qf **** exim_lock -v -fcntl test-mail/userx -exim -odi userx +exim -odi -f t2@foo userx Subject: Third -Transport: local_delivery_fcntl + +local_delivery_fcntl Third message **** exim -qf **** exim_lock -v -fcntl test-mail/userx -exim -odi userx +exim -odi -f t3@foo userx Subject: Fourth -Transport: local_delivery_fcntl_blocking + +local_delivery_fcntl_blocking Fourth message **** exim -qf diff --git a/test/scripts/0000-Basic/0428 b/test/scripts/0000-Basic/0428 index d554c80d0..24c1b1265 100644 --- a/test/scripts/0000-Basic/0428 +++ b/test/scripts/0000-Basic/0428 @@ -29,9 +29,10 @@ Filter: if true { stop; fileinto "inbox.never"; } Test 7 **** # This should fileinto inbox.JUNK (99 equal 99): -exim -odi userx +exim -odi userx8 X-Sieve: 99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; + + require["fileinto","comparator-i;ascii-numeric"]; if header :comparator "i;ascii-numeric" "X-Sieve" "99" { fileinto "inbox.JUNK"; stop; @@ -39,9 +40,10 @@ Filter: require["fileinto","comparator-i;ascii-numeric"]; Test 8 **** # This should not fileinto inbox.JUNK (98 not equal 99): -exim -odi userx +exim -odi userx9 X-Sieve: 99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; + + require["fileinto","comparator-i;ascii-numeric"]; if header :comparator "i;ascii-numeric" "X-Sieve" "98" { fileinto "inbox.JUNK"; stop; @@ -49,9 +51,10 @@ Filter: require["fileinto","comparator-i;ascii-numeric"]; Test 9 **** # This should fileinto inbox.JUNK (99-suffix equal 99): -exim -odi userx +exim -odi userx10 X-Sieve: 99- -Filter: require["fileinto","comparator-i;ascii-numeric"]; + + require["fileinto","comparator-i;ascii-numeric"]; if header :comparator "i;ascii-numeric" "X-Sieve" "99" { fileinto "inbox.JUNK"; stop; @@ -59,9 +62,10 @@ Filter: require["fileinto","comparator-i;ascii-numeric"]; Test 10 **** # This should fileinto inbox.JUNK (non-numeric equal non-numeric): -exim -odi userx +exim -odi userx11 X-Sieve: -99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; + + require["fileinto","comparator-i;ascii-numeric"]; if header :comparator "i;ascii-numeric" "X-Sieve" "-99" { fileinto "inbox.JUNK"; stop; @@ -69,9 +73,10 @@ Filter: require["fileinto","comparator-i;ascii-numeric"]; Test 11 **** # This should fileinto inbox.JUNK (non-numeric equal non-numeric): -exim -odi userx +exim -odi userx12 X-Sieve: -99 -Filter: require["fileinto","comparator-i;ascii-numeric"]; + + require["fileinto","comparator-i;ascii-numeric"]; if header :comparator "i;ascii-numeric" "X-Sieve" "-98" { fileinto "inbox.JUNK"; stop; @@ -79,16 +84,17 @@ Filter: require["fileinto","comparator-i;ascii-numeric"]; Test 12 **** # This is a simple test of "vacation" -exim -odi -f someone@test.ex userx -To: userx@test.ex -Filter: require ["vacation"]; +exim -odi -f someone@test.ex userx13 +To: userx13@test.ex + + require ["vacation"]; vacation "I am gone. Not here."; Test 13 **** # Test use of suffix -exim -odi -f someone@test.ex userx-suffix userx-suffix2 -To: userx-suffix@test.ex -Filter: require ["envelope","fileinto"]; +exim -odi -f someone@test.ex userx14-suffix userx14-suffix2 + + require ["envelope","fileinto"]; if envelope :matches :localpart "to" "*-suffix" { fileinto "userx-sawsuffix"; stop; diff --git a/test/scripts/0000-Basic/0504 b/test/scripts/0000-Basic/0504 index a48e8b4fe..04f19593e 100644 --- a/test/scripts/0000-Basic/0504 +++ b/test/scripts/0000-Basic/0504 @@ -1,12 +1,10 @@ # transport filter command fails to execute need_ipv4 # -exim -odi userx -transport: t1 +exim -DOPT=t1 -odi userx Test 1 **** -exim -odi userx -transport: t2 +exim -DOPT=t2 -odi userx Test 2 **** server PORT_S @@ -21,8 +19,7 @@ RCPT TO DATA 354 Send it **** -exim -odi userx -transport: t3 +exim -DOPT=t3 -odi userx Test 3 **** no_msglog_check diff --git a/test/scripts/1000-Basic-ipv6/1003 b/test/scripts/1000-Basic-ipv6/1003 index 646b1b4a3..1be6923b0 100644 --- a/test/scripts/1000-Basic-ipv6/1003 +++ b/test/scripts/1000-Basic-ipv6/1003 @@ -4,24 +4,29 @@ need_ipv6 # exim -DSERVER=server -bd -oX PORT_D **** -exim userx@test.ex -hosts: 127.0.0.1 +exim user4@h1.test.ex + +1 interface: <; ::1 ; HOSTIPV4 **** -exim userx@test.ex -hosts: <; ::1 +exim user6@h2.test.ex + +2 interface: <; HOSTIPV6 ; HOSTIPV4 **** -exim userx@test.ex -hosts: <; ::1 +exim user6@h3.test.ex + +3 interface: <; ${if eq{0}{1}{HOSTIPV6}fail} **** -exim userx@test.ex -hosts: <; ::1 +exim user6@h4.test.ex + +4 interface: <; ${if eq{0}{1}{HOSTIPV6}{ }} **** -exim userx@test.ex -hosts: <; ::1 +exim user6@h5.test.ex + +5 interface: <; ${if **** exim -qf diff --git a/test/scripts/5000-maildir/5000 b/test/scripts/5000-maildir/5000 index 5b763fcd3..a1d1ce358 100644 --- a/test/scripts/5000-maildir/5000 +++ b/test/scripts/5000-maildir/5000 @@ -1,58 +1,37 @@ # exim quota + warn threshold with maildir -exim -odi userx -maildir:maildir_ +exim -DVALUE=maildir_appendfile -odi userx This is a test message **** write test-data 100x60 -maildir:maildir_ ++++ **** -exim -odi userx >>>>>>>>>>>>>>> Exim pid=pppp (main: expansion test) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -163,7 +167,7 @@ LOG: MAIN PANIC /considering: no}} |--expanding: no \_____result: no - |--expanding: a.b.c + |--expanding: match_address: ${if match_address{a.b.c}{a.b.c}{yes}{no}} \_____result: match_address: no >>>>>>>>>>>>>>>> Exim pid=pppp (main: expansion test) terminating with rc=0 >>>>>>>>>>>>>>>> Exim version x.yz .... @@ -182,9 +186,11 @@ dropping to exim gid; retaining priv uid ╭considering: -oMai authenticated_id = $authenticated_id ├──expanding: -oMai authenticated_id = $authenticated_id ╰─────result: -oMai authenticated_id = philip + ╰──(tainted) ╭considering: -oMas authenticated_sender = $authenticated_sender ├──expanding: -oMas authenticated_sender = $authenticated_sender ╰─────result: -oMas authenticated_sender = xx@yy.zz + ╰──(tainted) ╭considering: -oMi interface_address = $interface_address ├──expanding: -oMi interface_address = $interface_address ╰─────result: -oMi interface_address = 1.1.1.1 @@ -215,9 +221,11 @@ dropping to exim gid; retaining priv uid ╭considering: -oMai authenticated_id = $authenticated_id ├──expanding: -oMai authenticated_id = $authenticated_id ╰─────result: -oMai authenticated_id = philip + ╰──(tainted) ╭considering: -oMas authenticated_sender = $authenticated_sender ├──expanding: -oMas authenticated_sender = $authenticated_sender ╰─────result: -oMas authenticated_sender = xx@yy.zz + ╰──(tainted) ╭considering: -oMi interface_address = $interface_address ├──expanding: -oMi interface_address = $interface_address ╰─────result: -oMi interface_address = 1.1.1.1 @@ -255,6 +263,7 @@ sender_fullhost = ten-1.test.ex [V4NET.0.0.1] sender_rcvhost = ten-1.test.ex ([V4NET.0.0.1] ident=me) ├──expanding: -oMs sender_host_name = $sender_host_name ╰─────result: -oMs sender_host_name = ten-1.test.ex + ╰──(tainted) ╭considering: -oMt sender_ident = $sender_ident ├──expanding: -oMt sender_ident = $sender_ident ╰─────result: -oMt sender_ident = me diff --git a/test/stderr/0023 b/test/stderr/0023 index b7588756b..cb371d298 100644 --- a/test/stderr/0023 +++ b/test/stderr/0023 @@ -1247,7 +1247,7 @@ LOG: H=[30.30.30.30] F= rejected RCPT : domain=test.e >>> check dnslists = test.ex/$sender_address_domain+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+END >>> = test.ex/y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+END >>> DNS list check: test.ex/y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+END -LOG: dnslist query is too long (ignored): y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+... +LOG: dnslist query is too long (ignored): y+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+extra+e... >>> deny: condition test failed in ACL "acl_31_31_31" >>> processing "accept" (TESTSUITE/test-config 168) >>> accept: condition test succeeded in ACL "acl_31_31_31" diff --git a/test/stderr/0037 b/test/stderr/0037 index af8bf129d..5136a8f2b 100644 --- a/test/stderr/0037 +++ b/test/stderr/0037 @@ -9,8 +9,8 @@ configuration file is TESTSUITE/test-config trusted user admin user dropping to exim gid; retaining priv uid -rda_interpret (file): TESTSUITE/aux-var/0037.F -expanded: TESTSUITE/aux-var/0037.F +rda_interpret (file): 'TESTSUITE/aux-var/0037.F' +expanded: 'TESTSUITE/aux-var/0037.F' ssss bytes read from TESTSUITE/aux-var/0037.F data is an Exim filter program Filter: start of processing @@ -39,8 +39,8 @@ local_part=filter-userx domain=test.ex checking local_parts $home = >/usr< calling userfilter router -rda_interpret (file): TESTSUITE/aux-var/0037.f-user -expanded: TESTSUITE/aux-var/0037.f-user +rda_interpret (file): 'TESTSUITE/aux-var/0037.f-user' +expanded: 'TESTSUITE/aux-var/0037.f-user' ssss bytes read from TESTSUITE/aux-var/0037.f-user data is an Exim filter program Filter: start of processing diff --git a/test/stderr/0084 b/test/stderr/0084 index 4100ebe9e..45c7bd0f4 100644 --- a/test/stderr/0084 +++ b/test/stderr/0084 @@ -17,8 +17,8 @@ file check: TESTSUITE/aux-fixed/0084.$local_part expanded file: TESTSUITE/aux-fixed/0084.yes stat() yielded 0 calling forward router -rda_interpret (file): TESTSUITE/aux-fixed/0084.$local_part -expanded: TESTSUITE/aux-fixed/0084.yes +rda_interpret (file): 'TESTSUITE/aux-fixed/0084.$local_part' +expanded: 'TESTSUITE/aux-fixed/0084.yes' ssss bytes read from TESTSUITE/aux-fixed/0084.yes file is not a filter file parse_forward_list: userx@test.ex diff --git a/test/stderr/0085 b/test/stderr/0085 index 86ef5840d..56c65f6b4 100644 --- a/test/stderr/0085 +++ b/test/stderr/0085 @@ -27,8 +27,8 @@ checking domains y.z in "test.ex : myhost.test.ex"? no (end of list) y.z in "! +local_domains"? yes (end of list) calling fail_remote_domains router -rda_interpret (string): :fail: unrouteable mail domain "$domain" -expanded: :fail: unrouteable mail domain "y.z" +rda_interpret (string): ':fail: unrouteable mail domain "$domain"' +expanded: ':fail: unrouteable mail domain "y.z"' file is not a filter file parse_forward_list: :fail: unrouteable mail domain "y.z" extract item: :fail: unrouteable mail domain "y.z" @@ -226,8 +226,8 @@ checking domains y.z in "test.ex : myhost.test.ex"? no (end of list) y.z in "! +local_domains"? yes (end of list) calling fail_remote_domains router -rda_interpret (string): :fail: unrouteable mail domain "$domain" -expanded: :fail: unrouteable mail domain "y.z" +rda_interpret (string): ':fail: unrouteable mail domain "$domain"' +expanded: ':fail: unrouteable mail domain "y.z"' file is not a filter file parse_forward_list: :fail: unrouteable mail domain "y.z" extract item: :fail: unrouteable mail domain "y.z" @@ -281,8 +281,8 @@ checking domains smart.domain in "test.ex : myhost.test.ex"? no (end of list) smart.domain in "! +local_domains"? yes (end of list) calling fail_remote_domains router -rda_interpret (string): :fail: unrouteable mail domain "$domain" -expanded: :fail: unrouteable mail domain "smart.domain" +rda_interpret (string): ':fail: unrouteable mail domain "$domain"' +expanded: ':fail: unrouteable mail domain "smart.domain"' file is not a filter file parse_forward_list: :fail: unrouteable mail domain "smart.domain" extract item: :fail: unrouteable mail domain "smart.domain" diff --git a/test/stderr/0123 b/test/stderr/0123 index 34c2c1ee5..93894e498 100644 --- a/test/stderr/0123 +++ b/test/stderr/0123 @@ -16,7 +16,7 @@ routing x@test.ex --------> l1 router <-------- local_part=x domain=test.ex calling l1 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases1" search_find: file="TESTSUITE/aux-fixed/0123.aliases1" key="x" partial=-1 affix=NULL starflags=0 @@ -28,14 +28,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for x in TESTSUITE/aux-fixed/0123.aliases1 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l1 router declined for x@test.ex --------> l2 router <-------- local_part=x domain=test.ex calling l2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases2" search_find: file="TESTSUITE/aux-fixed/0123.aliases2" key="x" partial=-1 affix=NULL starflags=0 @@ -48,14 +48,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for x in TESTSUITE/aux-fixed/0123.aliases2 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l2 router declined for x@test.ex --------> l3 router <-------- local_part=x domain=test.ex calling l3 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases3" Too many lookup files open closing 0TESTSUITE/aux-fixed/0123.aliases1 @@ -70,14 +70,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for x in TESTSUITE/aux-fixed/0123.aliases3 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l3 router declined for x@test.ex --------> c1 router <-------- local_part=x domain=test.ex calling c1 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases4" Too many lookup files open closing 0TESTSUITE/aux-fixed/0123.aliases2 @@ -92,14 +92,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for x in TESTSUITE/aux-fixed/0123.aliases4 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c1 router declined for x@test.ex --------> c2 router <-------- local_part=x domain=test.ex calling c2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases5" Too many lookup files open closing 0TESTSUITE/aux-fixed/0123.aliases3 @@ -114,14 +114,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for x in TESTSUITE/aux-fixed/0123.aliases5 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c2 router declined for x@test.ex --------> c3 router <-------- local_part=x domain=test.ex calling c3 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases6" Too many lookup files open closing 0TESTSUITE/aux-fixed/0123.aliases4 @@ -136,7 +136,7 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for x in TESTSUITE/aux-fixed/0123.aliases6 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c3 router declined for x@test.ex @@ -150,7 +150,7 @@ routing y@test.ex --------> l1 router <-------- local_part=y domain=test.ex calling l1 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases1" cached closed Too many lookup files open @@ -166,14 +166,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for y in TESTSUITE/aux-fixed/0123.aliases1 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l1 router declined for y@test.ex --------> l2 router <-------- local_part=y domain=test.ex calling l2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases2" cached closed Too many lookup files open @@ -189,14 +189,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for y in TESTSUITE/aux-fixed/0123.aliases2 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l2 router declined for y@test.ex --------> l3 router <-------- local_part=y domain=test.ex calling l3 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases3" cached closed Too many lookup files open @@ -212,14 +212,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for y in TESTSUITE/aux-fixed/0123.aliases3 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l3 router declined for y@test.ex --------> c1 router <-------- local_part=y domain=test.ex calling c1 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases4" cached closed Too many lookup files open @@ -235,14 +235,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for y in TESTSUITE/aux-fixed/0123.aliases4 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c1 router declined for y@test.ex --------> c2 router <-------- local_part=y domain=test.ex calling c2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases5" cached closed Too many lookup files open @@ -258,14 +258,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for y in TESTSUITE/aux-fixed/0123.aliases5 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c2 router declined for y@test.ex --------> c3 router <-------- local_part=y domain=test.ex calling c3 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases6" cached closed Too many lookup files open @@ -281,7 +281,7 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for y in TESTSUITE/aux-fixed/0123.aliases6 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c3 router declined for y@test.ex @@ -295,7 +295,7 @@ routing z@test.ex --------> l1 router <-------- local_part=z domain=test.ex calling l1 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases1}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases1" cached closed Too many lookup files open @@ -311,14 +311,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for z in TESTSUITE/aux-fixed/0123.aliases1 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l1 router declined for z@test.ex --------> l2 router <-------- local_part=z domain=test.ex calling l2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases2}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases2" cached closed Too many lookup files open @@ -334,14 +334,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for z in TESTSUITE/aux-fixed/0123.aliases2 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l2 router declined for z@test.ex --------> l3 router <-------- local_part=z domain=test.ex calling l3 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases3}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases3" cached closed Too many lookup files open @@ -357,14 +357,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for z in TESTSUITE/aux-fixed/0123.aliases3 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: l3 router declined for z@test.ex --------> c1 router <-------- local_part=z domain=test.ex calling c1 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases4}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases4" cached closed Too many lookup files open @@ -380,14 +380,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for z in TESTSUITE/aux-fixed/0123.aliases4 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c1 router declined for z@test.ex --------> c2 router <-------- local_part=z domain=test.ex calling c2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases5}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases5" cached closed Too many lookup files open @@ -403,14 +403,14 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for z in TESTSUITE/aux-fixed/0123.aliases5 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c2 router declined for z@test.ex --------> c3 router <-------- local_part=z domain=test.ex calling c3 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}} +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.aliases6}}' search_open: lsearch "TESTSUITE/aux-fixed/0123.aliases6" cached closed Too many lookup files open @@ -426,7 +426,7 @@ rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0123.al file lookup required for z in TESTSUITE/aux-fixed/0123.aliases6 lookup failed -expanded: +expanded: '' file is not a filter file parse_forward_list: c3 router declined for z@test.ex diff --git a/test/stderr/0279 b/test/stderr/0279 index f69c13e7f..205224407 100644 --- a/test/stderr/0279 +++ b/test/stderr/0279 @@ -99,8 +99,8 @@ fun.1 in "fun.1 : fun.2"? yes (matched "fun.1") fun.1 in "+funny_domains"? yes (matched "+funny_domains") user1@fun.1 in "user1@+funny_domains"? yes (matched "user1@+funny_domains") calling rr1 router -rda_interpret (string): :fail: matched *@+funny_domains -expanded: :fail: matched *@+funny_domains +rda_interpret (string): ':fail: matched *@+funny_domains' +expanded: ':fail: matched *@+funny_domains' file is not a filter file parse_forward_list: :fail: matched *@+funny_domains extract item: :fail: matched *@+funny_domains diff --git a/test/stderr/0297 b/test/stderr/0297 index 4706f8780..36bcfb480 100644 --- a/test/stderr/0297 +++ b/test/stderr/0297 @@ -8,8 +8,8 @@ routing /a/b/c@myhost.test.ex --------> r1 router <-------- local_part=/a/b/c domain=myhost.test.ex calling r1 router -rda_interpret (string): $local_part -expanded: /a/b/c +rda_interpret (string): '$local_part' +expanded: '/a/b/c' file is not a filter file parse_forward_list: /a/b/c extract item: /a/b/c @@ -32,8 +32,8 @@ routing /x/y/z@myhost.test.ex --------> r1 router <-------- local_part=/x/y/z domain=myhost.test.ex calling r1 router -rda_interpret (string): $local_part -expanded: /x/y/z +rda_interpret (string): '$local_part' +expanded: '/x/y/z' file is not a filter file parse_forward_list: /x/y/z extract item: /x/y/z diff --git a/test/stderr/0360 b/test/stderr/0360 index e2ad7d479..de4b4f2b6 100644 --- a/test/stderr/0360 +++ b/test/stderr/0360 @@ -28,8 +28,8 @@ r1 router skipped: domains mismatch --------> r2 router <-------- local_part=cms domain=test.ex calling r2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}} -expanded: unknown@recurse.test.ex, defer +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}' +expanded: 'unknown@recurse.test.ex, defer' file is not a filter file parse_forward_list: unknown@recurse.test.ex, defer extract item: unknown@recurse.test.ex @@ -83,8 +83,8 @@ r1 router skipped: domains mismatch --------> r2 router <-------- local_part=defer domain=test.ex calling r2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}} -expanded: :defer: Forcibly deferred +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}' +expanded: ':defer: Forcibly deferred' file is not a filter file parse_forward_list: :defer: Forcibly deferred extract item: :defer: Forcibly deferred @@ -171,8 +171,8 @@ r1 router skipped: domains mismatch --------> r2 router <-------- local_part=cms domain=test.ex calling r2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}} -expanded: unknown@recurse.test.ex, defer +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}' +expanded: 'unknown@recurse.test.ex, defer' file is not a filter file parse_forward_list: unknown@recurse.test.ex, defer extract item: unknown@recurse.test.ex @@ -226,8 +226,8 @@ r1 router skipped: domains mismatch --------> r2 router <-------- local_part=defer domain=test.ex calling r2 router -rda_interpret (string): ${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}} -expanded: :defer: Forcibly deferred +rda_interpret (string): '${lookup{$local_part}lsearch{TESTSUITE/aux-fixed/0360.aliases}}' +expanded: ':defer: Forcibly deferred' file is not a filter file parse_forward_list: :defer: Forcibly deferred extract item: :defer: Forcibly deferred diff --git a/test/stderr/0361 b/test/stderr/0361 index 64e47dff7..9b74af458 100644 --- a/test/stderr/0361 +++ b/test/stderr/0361 @@ -141,8 +141,8 @@ local_part=kilos domain=recurse.test.ex.test.ex checking local_parts kilos in "kilos"? yes (matched "kilos") calling r3 router -rda_interpret (string): $local_part@$domain -expanded: kilos@recurse.test.ex.test.ex +rda_interpret (string): '$local_part@$domain' +expanded: 'kilos@recurse.test.ex.test.ex' file is not a filter file parse_forward_list: kilos@recurse.test.ex.test.ex extract item: kilos@recurse.test.ex.test.ex diff --git a/test/stderr/0364 b/test/stderr/0364 index 81fb83de1..1b08f726b 100644 --- a/test/stderr/0364 +++ b/test/stderr/0364 @@ -9,8 +9,8 @@ local_part=kilos domain=thishost checking domains processing address_data calling r1 router -rda_interpret (string): $local_part@$domain.test.ex -expanded: kilos@thishost.test.ex +rda_interpret (string): '$local_part@$domain.test.ex' +expanded: 'kilos@thishost.test.ex' file is not a filter file parse_forward_list: kilos@thishost.test.ex extract item: kilos@thishost.test.ex @@ -54,8 +54,8 @@ local_part=solik domain=otherhost checking domains processing address_data calling r1 router -rda_interpret (string): $local_part@$domain.test.ex -expanded: solik@otherhost.test.ex +rda_interpret (string): '$local_part@$domain.test.ex' +expanded: 'solik@otherhost.test.ex' file is not a filter file parse_forward_list: solik@otherhost.test.ex extract item: solik@otherhost.test.ex @@ -80,8 +80,8 @@ checking domains checking "condition" "${if eq{$address_data}{}{no}{yes}}"... processing address_data calling r3 router -rda_interpret (string): $local_part@$original_domain.sub.test.ex -expanded: solik@otherhost.sub.test.ex +rda_interpret (string): '$local_part@$original_domain.sub.test.ex' +expanded: 'solik@otherhost.sub.test.ex' file is not a filter file parse_forward_list: solik@otherhost.sub.test.ex extract item: solik@otherhost.sub.test.ex @@ -109,8 +109,8 @@ r3 router skipped: condition failure local_part=solik domain=otherhost.sub.test.ex checking domains calling r4 router -rda_interpret (string): :fail:Can't route to $domain -expanded: :fail:Can't route to otherhost.sub.test.ex +rda_interpret (string): ':fail:Can't route to $domain' +expanded: ':fail:Can't route to otherhost.sub.test.ex' file is not a filter file parse_forward_list: :fail:Can't route to otherhost.sub.test.ex extract item: :fail:Can't route to otherhost.sub.test.ex @@ -122,8 +122,8 @@ local_part=xxx domain=ten-1 checking domains processing address_data calling r1 router -rda_interpret (string): $local_part@$domain.test.ex -expanded: xxx@ten-1.test.ex +rda_interpret (string): '$local_part@$domain.test.ex' +expanded: 'xxx@ten-1.test.ex' file is not a filter file parse_forward_list: xxx@ten-1.test.ex extract item: xxx@ten-1.test.ex @@ -157,8 +157,8 @@ local_part=xxx domain=testsub checking domains processing address_data calling r1 router -rda_interpret (string): $local_part@$domain.test.ex -expanded: xxx@testsub.test.ex +rda_interpret (string): '$local_part@$domain.test.ex' +expanded: 'xxx@testsub.test.ex' file is not a filter file parse_forward_list: xxx@testsub.test.ex extract item: xxx@testsub.test.ex @@ -183,8 +183,8 @@ checking domains checking "condition" "${if eq{$address_data}{}{no}{yes}}"... processing address_data calling r3 router -rda_interpret (string): $local_part@$original_domain.sub.test.ex -expanded: xxx@testsub.sub.test.ex +rda_interpret (string): '$local_part@$original_domain.sub.test.ex' +expanded: 'xxx@testsub.sub.test.ex' file is not a filter file parse_forward_list: xxx@testsub.sub.test.ex extract item: xxx@testsub.sub.test.ex diff --git a/test/stderr/0370 b/test/stderr/0370 index 823ff0962..b2483cf21 100644 --- a/test/stderr/0370 +++ b/test/stderr/0370 @@ -21,7 +21,7 @@ changed uid/gid: local delivery to |TESTSUITE/bin/iefbr14 <|TESTSUITE/bin/iefbr1 uid=EXIM_UID gid=CALLER_GID pid=pppp t1 transport entered direct command: - argv[0] = TESTSUITE/bin/iefbr14 + argv[0] = 'TESTSUITE/bin/iefbr14' Writing message to pipe writing data block fd=dddd size=sss timeout=3600 writing error 32: Broken pipe diff --git a/test/stderr/0377 b/test/stderr/0377 index 007df5d54..98f92e0a3 100644 --- a/test/stderr/0377 +++ b/test/stderr/0377 @@ -54,8 +54,8 @@ cccc_2nd_time router skipped: condition failure local_part=cccc domain=myhost.test.ex checking local_parts calling cccc_redirect router -rda_interpret (string): cccc@$domain, defer_cccc@$domain -expanded: cccc@myhost.test.ex, defer_cccc@myhost.test.ex +rda_interpret (string): 'cccc@$domain, defer_cccc@$domain' +expanded: 'cccc@myhost.test.ex, defer_cccc@myhost.test.ex' file is not a filter file parse_forward_list: cccc@myhost.test.ex, defer_cccc@myhost.test.ex extract item: cccc@myhost.test.ex @@ -88,8 +88,8 @@ local_part=bbbb domain=myhost.test.ex checking local_parts checking "condition" "${if first_delivery{yes}{no}}"... calling bbbb router -rda_interpret (string): bbbb@$domain, defer_bbbb@$domain -expanded: bbbb@myhost.test.ex, defer_bbbb@myhost.test.ex +rda_interpret (string): 'bbbb@$domain, defer_bbbb@$domain' +expanded: 'bbbb@myhost.test.ex, defer_bbbb@myhost.test.ex' file is not a filter file parse_forward_list: bbbb@myhost.test.ex, defer_bbbb@myhost.test.ex extract item: bbbb@myhost.test.ex @@ -114,8 +114,8 @@ local_part=aaaa domain=myhost.test.ex checking local_parts checking "condition" "${if first_delivery{yes}{no}}"... calling unseen_aaaa router -rda_interpret (string): defer_aaaa@$domain -expanded: defer_aaaa@myhost.test.ex +rda_interpret (string): 'defer_aaaa@$domain' +expanded: 'defer_aaaa@myhost.test.ex' file is not a filter file parse_forward_list: defer_aaaa@myhost.test.ex extract item: defer_aaaa@myhost.test.ex @@ -158,8 +158,8 @@ routing defer_cccc@myhost.test.ex local_part=defer_cccc domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer @@ -216,8 +216,8 @@ routing defer_bbbb@myhost.test.ex local_part=defer_bbbb domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer @@ -261,8 +261,8 @@ routing defer_aaaa@myhost.test.ex local_part=defer_aaaa domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer @@ -356,8 +356,8 @@ local_part=cccc domain=myhost.test.ex checking local_parts checking "condition" "${if first_delivery{no}{yes}}"... calling cccc_2nd_time router -rda_interpret (string): $local_part@$domain -expanded: cccc@myhost.test.ex +rda_interpret (string): '$local_part@$domain' +expanded: 'cccc@myhost.test.ex' file is not a filter file parse_forward_list: cccc@myhost.test.ex extract item: cccc@myhost.test.ex @@ -458,8 +458,8 @@ cccc_2nd_time router skipped: previously routed cccc@myhost.test.ex local_part=cccc domain=myhost.test.ex checking local_parts calling cccc_redirect router -rda_interpret (string): cccc@$domain, defer_cccc@$domain -expanded: cccc@myhost.test.ex, defer_cccc@myhost.test.ex +rda_interpret (string): 'cccc@$domain, defer_cccc@$domain' +expanded: 'cccc@myhost.test.ex, defer_cccc@myhost.test.ex' file is not a filter file parse_forward_list: cccc@myhost.test.ex, defer_cccc@myhost.test.ex extract item: cccc@myhost.test.ex @@ -488,8 +488,8 @@ routing defer_cccc@myhost.test.ex local_part=defer_cccc domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer @@ -600,8 +600,8 @@ local_part=cccc domain=myhost.test.ex checking local_parts checking "condition" "${if first_delivery{no}{yes}}"... calling cccc_2nd_time router -rda_interpret (string): $local_part@$domain -expanded: cccc@myhost.test.ex +rda_interpret (string): '$local_part@$domain' +expanded: 'cccc@myhost.test.ex' file is not a filter file parse_forward_list: cccc@myhost.test.ex extract item: cccc@myhost.test.ex @@ -702,8 +702,8 @@ cccc_2nd_time router skipped: previously routed cccc@myhost.test.ex local_part=cccc domain=myhost.test.ex checking local_parts calling cccc_redirect router -rda_interpret (string): cccc@$domain, defer_cccc@$domain -expanded: cccc@myhost.test.ex, defer_cccc@myhost.test.ex +rda_interpret (string): 'cccc@$domain, defer_cccc@$domain' +expanded: 'cccc@myhost.test.ex, defer_cccc@myhost.test.ex' file is not a filter file parse_forward_list: cccc@myhost.test.ex, defer_cccc@myhost.test.ex extract item: cccc@myhost.test.ex @@ -732,8 +732,8 @@ routing defer_cccc@myhost.test.ex local_part=defer_cccc domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer diff --git a/test/stderr/0378 b/test/stderr/0378 index 74ab95932..e48fb7e76 100644 --- a/test/stderr/0378 +++ b/test/stderr/0378 @@ -34,18 +34,13 @@ aaaa_2nd_time router skipped: condition failure local_part=aaaa domain=myhost.test.ex checking local_parts calling aaaa router -rda_interpret (string): # Exim filter -deliver defer_aaaa@$domain -save TESTSUITE/test-mail/file -pipe "/bin/sh -c exit" -mail subject autoreply -text "This is an autoreply" -expanded: # Exim filter +rda_interpret (string): '# Exim filter\ndeliver defer_aaaa@$domain\nsave TESTSUITE/test-mail/file\npipe "/bin/sh -c exit"\nmail subject autoreply\ntext "This is an autoreply"' +expanded: '# Exim filter deliver defer_aaaa@myhost.test.ex save TESTSUITE/test-mail/file pipe "/bin/sh -c exit" mail subject autoreply -text "This is an autoreply" +text "This is an autoreply"' data is an Exim filter program Filter: start of processing Filter: end of processing @@ -99,8 +94,8 @@ bounce router skipped: senders mismatch local_part=defer_aaaa domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer @@ -141,8 +136,8 @@ routing CALLER@myhost.test.ex local_part=CALLER domain=myhost.test.ex checking senders calling bounce router -rda_interpret (string): :blackhole: -expanded: :blackhole: +rda_interpret (string): ':blackhole:' +expanded: ':blackhole:' file is not a filter file parse_forward_list: :blackhole: extract item: :blackhole: @@ -197,8 +192,8 @@ local_part=aaaa domain=myhost.test.ex checking local_parts checking "condition" "${if first_delivery{no}{yes}}"... calling aaaa_2nd_time router -rda_interpret (string): aaaa@$domain -expanded: aaaa@myhost.test.ex +rda_interpret (string): 'aaaa@$domain' +expanded: 'aaaa@myhost.test.ex' file is not a filter file parse_forward_list: aaaa@myhost.test.ex extract item: aaaa@myhost.test.ex @@ -229,18 +224,13 @@ aaaa_2nd_time router skipped: previously routed aaaa@myhost.test.ex local_part=aaaa domain=myhost.test.ex checking local_parts calling aaaa router -rda_interpret (string): # Exim filter -deliver defer_aaaa@$domain -save TESTSUITE/test-mail/file -pipe "/bin/sh -c exit" -mail subject autoreply -text "This is an autoreply" -expanded: # Exim filter +rda_interpret (string): '# Exim filter\ndeliver defer_aaaa@$domain\nsave TESTSUITE/test-mail/file\npipe "/bin/sh -c exit"\nmail subject autoreply\ntext "This is an autoreply"' +expanded: '# Exim filter deliver defer_aaaa@myhost.test.ex save TESTSUITE/test-mail/file pipe "/bin/sh -c exit" mail subject autoreply -text "This is an autoreply" +text "This is an autoreply"' data is an Exim filter program Filter: start of processing Filter: end of processing @@ -293,8 +283,8 @@ bounce router skipped: senders mismatch local_part=defer_aaaa domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer diff --git a/test/stderr/0379 b/test/stderr/0379 index c3777c222..26a225fe2 100644 --- a/test/stderr/0379 +++ b/test/stderr/0379 @@ -29,8 +29,8 @@ defer router skipped: local_parts mismatch local_part=aaaa domain=myhost.test.ex checking local_parts calling aaaa_redirect router -rda_interpret (string): bbbb@$domain, cccc@$domain -expanded: bbbb@myhost.test.ex, cccc@myhost.test.ex +rda_interpret (string): 'bbbb@$domain, cccc@$domain' +expanded: 'bbbb@myhost.test.ex, cccc@myhost.test.ex' file is not a filter file parse_forward_list: bbbb@myhost.test.ex, cccc@myhost.test.ex extract item: bbbb@myhost.test.ex @@ -50,8 +50,8 @@ routing defer@myhost.test.ex local_part=defer domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer @@ -151,8 +151,8 @@ routing defer@myhost.test.ex local_part=defer domain=myhost.test.ex checking local_parts calling defer router -rda_interpret (string): :defer: forced defer -expanded: :defer: forced defer +rda_interpret (string): ':defer: forced defer' +expanded: ':defer: forced defer' file is not a filter file parse_forward_list: :defer: forced defer extract item: :defer: forced defer diff --git a/test/stderr/0380 b/test/stderr/0380 index 7be8b5be7..c653fc309 100644 --- a/test/stderr/0380 +++ b/test/stderr/0380 @@ -33,8 +33,8 @@ r1 router skipped: local_parts mismatch local_part=bbbb domain=myhost.test.ex checking local_parts calling r2 router -rda_interpret (file): TESTSUITE/non-exist/$local_part -expanded: TESTSUITE/non-exist/bbbb +rda_interpret (file): 'TESTSUITE/non-exist/$local_part' +expanded: 'TESTSUITE/non-exist/bbbb' TESTSUITE/non-exist/bbbb does not exist ignore_enotdir set => skip checking parent directory r2 router declined for bbbb@myhost.test.ex @@ -61,8 +61,8 @@ forced failure in expansion of "${if eq {a}{b}{x}fail}" (address_data): decline local_part=aaaa domain=myhost.test.ex checking local_parts calling r1 router -rda_interpret (file): TESTSUITE/non-exist/$local_part -expanded: TESTSUITE/non-exist/aaaa +rda_interpret (file): 'TESTSUITE/non-exist/$local_part' +expanded: 'TESTSUITE/non-exist/aaaa' TESTSUITE/non-exist/aaaa does not exist checking parent directory stat(TESTSUITE/non-exist/.)=-1 diff --git a/test/stderr/0382 b/test/stderr/0382 index 792e5cee9..24fd7b7a0 100644 --- a/test/stderr/0382 +++ b/test/stderr/0382 @@ -10,8 +10,8 @@ trusted user admin user dropping to exim gid; retaining priv uid running system filter -Filtering did not set up a significant delivery. -Normal delivery will occur. + Filtering did not set up a significant delivery. + Normal delivery will occur. system filter returned 1 LOG: MAIN ** userx@test.ex R=r1: forced fail @@ -27,8 +27,8 @@ trusted user admin user dropping to exim gid; retaining priv uid running system filter -Filtering did not set up a significant delivery. -Normal delivery will occur. + Filtering did not set up a significant delivery. + Normal delivery will occur. system filter returned 1 LOG: MAIN ** CALLER@test.ex: Unrouteable address diff --git a/test/stderr/0386 b/test/stderr/0386 index b8f4b079b..d2444f5ad 100644 --- a/test/stderr/0386 +++ b/test/stderr/0386 @@ -32,19 +32,24 @@ spool directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to:<1@b> -read ACL from file TESTSUITE/aux-fixed/0386.acl1 -processing "accept" (TESTSUITE/test-config 32) -check hosts = : +using ACL "chk_rcpt" +processing "accept" (TESTSUITE/test-config 18) +check local_parts = 1 +1 in "1"? yes (matched "1") +check acl = TESTSUITE/aux-fixed/0386.acl1 + read ACL from file TESTSUITE/aux-fixed/0386.acl1 + processing "accept" (TESTSUITE/test-config 43) + check hosts = : host in ":"? no (end of list) -accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "deny" (TESTSUITE/test-config 32) -check local_parts = ^.*[@%!/|] + accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "deny" (TESTSUITE/test-config 43) + check local_parts = ^.*[@%!/|] 1 in "^.*[@%!/|]"? no (end of list) -deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "require" (TESTSUITE/test-config 32) -l_message: Invalid sender - message: Couldn't verify the sender -check verify = sender/defer_ok + deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "require" (TESTSUITE/test-config 43) + l_message: Invalid sender + message: Couldn't verify the sender + check verify = sender/defer_ok >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Verifying x@y >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -64,13 +69,15 @@ domain = y routed by r1 router envelope to: x@y transport: t1 ------------ end verify ------------ -sender x@y verified ok -require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "deny" (TESTSUITE/test-config 32) - message: No such user here -deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" -end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY + ----------- end verify ------------ + sender x@y verified ok + require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "deny" (TESTSUITE/test-config 43) + message: No such user here + deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" + end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY +accept: condition test failed in ACL "chk_rcpt" +accept: endpass encountered - denying access SMTP>> 550 No such user here LOG: MAIN REJECT H=[V4NET.9.8.7] F= rejected RCPT <1@b>: No such user here @@ -81,19 +88,24 @@ spool directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to:<1@b> -using ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "accept" (TESTSUITE/test-config 32) -check hosts = : +using ACL "chk_rcpt" +processing "accept" (TESTSUITE/test-config 18) +check local_parts = 1 +1 in "1"? yes (matched "1") +check acl = TESTSUITE/aux-fixed/0386.acl1 + using ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "accept" (TESTSUITE/test-config 43) + check hosts = : host in ":"? no (end of list) -accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "deny" (TESTSUITE/test-config 32) -check local_parts = ^.*[@%!/|] + accept: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "deny" (TESTSUITE/test-config 43) + check local_parts = ^.*[@%!/|] 1 in "^.*[@%!/|]"? no (end of list) -deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "require" (TESTSUITE/test-config 32) -l_message: Invalid sender - message: Couldn't verify the sender -check verify = sender/defer_ok + deny: condition test failed in ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "require" (TESTSUITE/test-config 43) + l_message: Invalid sender + message: Couldn't verify the sender + check verify = sender/defer_ok >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Verifying x@y >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -112,13 +124,15 @@ domain = y routed by r1 router envelope to: x@y transport: t1 ------------ end verify ------------ -sender x@y verified ok -require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" -processing "deny" (TESTSUITE/test-config 32) - message: No such user here -deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" -end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY + ----------- end verify ------------ + sender x@y verified ok + require: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" + processing "deny" (TESTSUITE/test-config 43) + message: No such user here + deny: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl1" + end of ACL "TESTSUITE/aux-fixed/0386.acl1": DENY +accept: condition test failed in ACL "chk_rcpt" +accept: endpass encountered - denying access SMTP>> 550 No such user here LOG: MAIN REJECT H=[V4NET.9.8.7] F= rejected RCPT <1@b>: No such user here @@ -164,11 +178,20 @@ spool directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to:<2@b> -read ACL from file TESTSUITE/aux-fixed/0386.acl2 -processing "warn" (TESTSUITE/test-config 32) - message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text -l_message: found in $dnslist_domain: $dnslist_text -check dnslists = rbl.test.ex +using ACL "chk_rcpt" +processing "accept" (TESTSUITE/test-config 18) +check local_parts = 1 +2 in "1"? no (end of list) +accept: condition test failed in ACL "chk_rcpt" +processing "accept" (TESTSUITE/test-config 21) +check local_parts = 2 +2 in "2"? yes (matched "2") +check acl = TESTSUITE/aux-fixed/0386.acl2 + read ACL from file TESTSUITE/aux-fixed/0386.acl2 + processing "warn" (TESTSUITE/test-config 43) + message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text + l_message: found in $dnslist_domain: $dnslist_text + check dnslists = rbl.test.ex DNS list check: rbl.test.ex new DNS lookup for 13.12.11.V4NET.rbl.test.ex DNS lookup of 13.12.11.V4NET.rbl.test.ex (A) using fakens @@ -177,13 +200,15 @@ DNS lookup for 13.12.11.V4NET.rbl.test.ex succeeded (yielding 127.0.0.2) DNS lookup of 13.12.11.V4NET.rbl.test.ex (TXT) using fakens DNS lookup of 13.12.11.V4NET.rbl.test.ex (TXT) succeeded => that means V4NET.11.12.13 is listed at rbl.test.ex -warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" + warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" LOG: MAIN H=[V4NET.11.12.13] U=CALLER Warning: found in rbl.test.ex: This is a test blacklisting message created log directory TESTSUITE/spool/log -processing "accept" (TESTSUITE/test-config 32) -accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" -end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT + processing "accept" (TESTSUITE/test-config 43) + accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" + end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT +accept: condition test succeeded in ACL "chk_rcpt" +end of ACL "chk_rcpt": ACCEPT SMTP>> 250 Accepted DSN: orcpt: NULL flags: 0 SMTP<< data @@ -351,21 +376,32 @@ spool directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to:<2@b> -using ACL "TESTSUITE/aux-fixed/0386.acl2" -processing "warn" (TESTSUITE/test-config 32) - message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text -l_message: found in $dnslist_domain: $dnslist_text -check dnslists = rbl.test.ex +using ACL "chk_rcpt" +processing "accept" (TESTSUITE/test-config 18) +check local_parts = 1 +2 in "1"? no (end of list) +accept: condition test failed in ACL "chk_rcpt" +processing "accept" (TESTSUITE/test-config 21) +check local_parts = 2 +2 in "2"? yes (matched "2") +check acl = TESTSUITE/aux-fixed/0386.acl2 + using ACL "TESTSUITE/aux-fixed/0386.acl2" + processing "warn" (TESTSUITE/test-config 43) + message: X-Warning: $sender_host_address is listed at $dnslist_domain\nX-Warning: $dnslist_text + l_message: found in $dnslist_domain: $dnslist_text + check dnslists = rbl.test.ex DNS list check: rbl.test.ex using result of previous DNS lookup DNS lookup for 13.12.11.V4NET.rbl.test.ex succeeded (yielding 127.0.0.2) => that means V4NET.11.12.13 is listed at rbl.test.ex -warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" + warn: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" LOG: MAIN H=[V4NET.11.12.13] U=CALLER Warning: found in rbl.test.ex: This is a test blacklisting message -processing "accept" (TESTSUITE/test-config 32) -accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" -end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT + processing "accept" (TESTSUITE/test-config 43) + accept: condition test succeeded in ACL "TESTSUITE/aux-fixed/0386.acl2" + end of ACL "TESTSUITE/aux-fixed/0386.acl2": ACCEPT +accept: condition test succeeded in ACL "chk_rcpt" +end of ACL "chk_rcpt": ACCEPT SMTP>> 250 Accepted DSN: orcpt: NULL flags: 0 SMTP<< data diff --git a/test/stderr/0388 b/test/stderr/0388 index a9be60fb4..e11ca71b3 100644 --- a/test/stderr/0388 +++ b/test/stderr/0388 @@ -258,8 +258,8 @@ checking senders address match test: subject= pattern= in ":"? yes (matched "") calling r0 router -rda_interpret (string): :blackhole: -expanded: :blackhole: +rda_interpret (string): ':blackhole:' +expanded: ':blackhole:' file is not a filter file parse_forward_list: :blackhole: extract item: :blackhole: diff --git a/test/stderr/0393 b/test/stderr/0393 index 34d20785f..81ffa5e85 100644 --- a/test/stderr/0393 +++ b/test/stderr/0393 @@ -12,7 +12,7 @@ dropping to exim gid; retaining priv uid >>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>> --------> userx@test.ex <-------- direct command: - argv[0] = /bin/cat + argv[0] = '/bin/cat' direct command after expansion: argv[0] = /bin/cat appendfile transport entered @@ -97,7 +97,7 @@ dropping to exim gid; retaining priv uid >>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>> --------> userx@test.ex <-------- direct command: - argv[0] = ${if={1}{1}{}{}} + argv[0] = '${if={1}{1}{}{}}' direct command after expansion: argv[0] = appendfile transport entered diff --git a/test/stderr/0399 b/test/stderr/0399 index 2dc717282..9a3b13297 100644 --- a/test/stderr/0399 +++ b/test/stderr/0399 @@ -16,8 +16,8 @@ routing x@y --------> r1 router <-------- local_part=x domain=y calling r1 router -rda_interpret (string): -expanded: +rda_interpret (string): '' +expanded: '' file is not a filter file parse_forward_list: r1 router declined for x@y @@ -25,8 +25,8 @@ expansion of "more" forced failure --------> r2 router <-------- local_part=x domain=y calling r2 router -rda_interpret (string): -expanded: +rda_interpret (string): '' +expanded: '' file is not a filter file parse_forward_list: r2 router declined for x@y @@ -34,8 +34,8 @@ expansion of "more" yields "yes" --------> r3 router <-------- local_part=x domain=y calling r3 router -rda_interpret (string): -expanded: +rda_interpret (string): '' +expanded: '' file is not a filter file parse_forward_list: r3 router declined for x@y diff --git a/test/stderr/0402 b/test/stderr/0402 index daa7b50f5..1d81d845c 100644 --- a/test/stderr/0402 +++ b/test/stderr/0402 @@ -284,9 +284,10 @@ usery in "usery"? yes (matched "usery") ╭considering: /non-exist/$domain ├──expanding: /non-exist/$domain ╰─────result: /non-exist/test.ex + ╰──(tainted) calling r5 router -rda_interpret (string): TESTSUITE/test-mail/junk -expanded: TESTSUITE/test-mail/junk +rda_interpret (string): 'TESTSUITE/test-mail/junk' +expanded: 'TESTSUITE/test-mail/junk' file is not a filter file parse_forward_list: TESTSUITE/test-mail/junk extract item: TESTSUITE/test-mail/junk @@ -323,9 +324,10 @@ CALLER in "CALLER"? yes (matched "CALLER") ╭considering: /non-exist/$local_part ├──expanding: /non-exist/$local_part ╰─────result: /non-exist/CALLER + ╰──(tainted) calling r4 router -rda_interpret (string): TESTSUITE/test-mail/junk -expanded: TESTSUITE/test-mail/junk +rda_interpret (string): 'TESTSUITE/test-mail/junk' +expanded: 'TESTSUITE/test-mail/junk' file is not a filter file parse_forward_list: TESTSUITE/test-mail/junk extract item: TESTSUITE/test-mail/junk @@ -356,6 +358,7 @@ userz in "userz"? yes (matched "userz") ╭considering: /non-exist/$domain ├──expanding: /non-exist/$domain ╰─────result: /non-exist/test.ex + ╰──(tainted) calling r3 router r3 router called for userz@test.ex domain = test.ex @@ -381,6 +384,7 @@ usery in "usery"? yes (matched "usery") ╭considering: /non-exist/$domain ├──expanding: /non-exist/$domain ╰─────result: /non-exist/test.ex + ╰──(tainted) calling r2 router r2 router called for usery@test.ex domain = test.ex @@ -401,6 +405,7 @@ CALLER in "CALLER"? yes (matched "CALLER") ╭considering: /non-exist/$local_part ├──expanding: /non-exist/$local_part ╰─────result: /non-exist/CALLER + ╰──(tainted) calling r1 router r1 router called for CALLER@test.ex domain = test.ex @@ -449,6 +454,7 @@ no retry data available ╭considering: /non-exist/$local_part ├──expanding: /non-exist/$local_part ╰─────result: /non-exist/usery + ╰──(tainted) search_tidyup called changed uid/gid: local delivery to TESTSUITE/test-mail/junk transport=ft1 uid=CALLER_UID gid=CALLER_GID pid=pppp @@ -477,6 +483,7 @@ writing to file TESTSUITE/test-mail/junk ├──expanding: $return_path ╰─────result: CALLER@test.ex + ╰──(tainted) ╭───scanning: MAILER-DAEMON}} ${tod_bsdinbox} ├──expanding: MAILER-DAEMON @@ -486,6 +493,7 @@ writing to file TESTSUITE/test-mail/junk ╰─────result: From CALLER@test.ex Tue Mar 02 09:44:33 1999 + ╰──(tainted) writing data block fd=dddd size=sss timeout=0 cannot use sendfile for body: spoolfile not wireformat writing data block fd=dddd size=sss timeout=0 @@ -534,6 +542,7 @@ writing to file TESTSUITE/test-mail/junk ├──expanding: $return_path ╰─────result: CALLER@test.ex + ╰──(tainted) ╭───scanning: MAILER-DAEMON}} ${tod_bsdinbox} ├──expanding: MAILER-DAEMON @@ -543,6 +552,7 @@ writing to file TESTSUITE/test-mail/junk ╰─────result: From CALLER@test.ex Tue Mar 02 09:44:33 1999 + ╰──(tainted) writing data block fd=dddd size=sss timeout=0 cannot use sendfile for body: spoolfile not wireformat writing data block fd=dddd size=sss timeout=0 @@ -592,6 +602,7 @@ no retry data available ╭considering: /non-exist/$local_part ├──expanding: /non-exist/$local_part ╰─────result: /non-exist/usery + ╰──(tainted) search_tidyup called changed uid/gid: local delivery to usery transport=t1 uid=CALLER_UID gid=CALLER_GID pid=pppp @@ -621,6 +632,7 @@ no retry data available ╭considering: /$local_part ├──expanding: /$local_part ╰─────result: /userz + ╰──(tainted) search_tidyup called changed uid/gid: local delivery to userz transport=t2 uid=CALLER_UID gid=CALLER_GID pid=pppp diff --git a/test/stderr/0403 b/test/stderr/0403 index e0fc74a94..29a555881 100644 --- a/test/stderr/0403 +++ b/test/stderr/0403 @@ -120,8 +120,8 @@ userx in "lsearch;TESTSUITE/aux-fixed/0403.data"? yes (matched "lsearch;TESTSUIT +++home=/usr processing address_data calling r1 router -rda_interpret (string): TESTSUITE/test-mail/junk -expanded: TESTSUITE/test-mail/junk +rda_interpret (string): 'TESTSUITE/test-mail/junk' +expanded: 'TESTSUITE/test-mail/junk' file is not a filter file parse_forward_list: TESTSUITE/test-mail/junk extract item: TESTSUITE/test-mail/junk diff --git a/test/stderr/0404 b/test/stderr/0404 index 652e65335..6ec697bc9 100644 --- a/test/stderr/0404 +++ b/test/stderr/0404 @@ -193,10 +193,10 @@ r1 router skipped: local_parts mismatch --------> r2 router <-------- local_part=userx domain=test.ex calling r2 router -rda_interpret (string): #Exim filter\nmail text rhubarb\nseen finish -expanded: #Exim filter +rda_interpret (string): '#Exim filter\nmail text rhubarb\nseen finish' +expanded: '#Exim filter mail text rhubarb -seen finish +seen finish' search_tidyup called changed uid/gid: r2 router (recipient is userx@test.ex) uid=CALLER_UID gid=CALLER_GID pid=pppp diff --git a/test/stderr/0426 b/test/stderr/0426 index 9918563ad..dda5191a2 100644 --- a/test/stderr/0426 +++ b/test/stderr/0426 @@ -73,8 +73,8 @@ routing CALLER@test.ex local_part=CALLER domain=test.ex checking senders calling r0 router -rda_interpret (string): :blackhole: -expanded: :blackhole: +rda_interpret (string): ':blackhole:' +expanded: ':blackhole:' file is not a filter file parse_forward_list: :blackhole: extract item: :blackhole: diff --git a/test/stderr/0464 b/test/stderr/0464 index 729f87244..512ed1b58 100644 --- a/test/stderr/0464 +++ b/test/stderr/0464 @@ -49,8 +49,8 @@ cached yes match for +special_domains cached lookup data = data for domain1 domain1 in "+special_domains"? yes (matched "+special_domains" - cached) calling r1 router -rda_interpret (string): $local_part@xxx.$domain -expanded: abc@xxx.domain1 +rda_interpret (string): '$local_part@xxx.$domain' +expanded: 'abc@xxx.domain1' file is not a filter file parse_forward_list: abc@xxx.domain1 extract item: abc@xxx.domain1 @@ -116,8 +116,8 @@ cached yes match for +special_domains cached lookup data = data for domain1 domain1 in "+special_domains"? yes (matched "+special_domains" - cached) calling r1 router -rda_interpret (string): $local_part@xxx.$domain -expanded: abc@xxx.domain1 +rda_interpret (string): '$local_part@xxx.$domain' +expanded: 'abc@xxx.domain1' file is not a filter file parse_forward_list: abc@xxx.domain1 extract item: abc@xxx.domain1 diff --git a/test/stderr/0544 b/test/stderr/0544 index c90d9b1b2..cce031101 100644 --- a/test/stderr/0544 +++ b/test/stderr/0544 @@ -128,6 +128,7 @@ dropping to exim gid; retaining priv uid ╭considering: $domain ├──expanding: $domain ╰─────result: domain1.ex + ╰──(tainted) LOG: MAIN == userx@domain1.ex R=smarthost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set LOG: MAIN @@ -176,12 +177,14 @@ LOG: smtp_connection MAIN ╭considering: $sender_helo_name}{+dlist}} ├──expanding: $sender_helo_name ╰─────result: ehlo.domain + ╰──(tainted) ╭considering: +dlist}} ├──expanding: +dlist ╰─────result: +dlist ╭considering: $domain ├──expanding: $domain ╰─────result: ehlo.domain + ╰──(tainted) ├──condition: match_domain {$sender_helo_name}{+dlist} ├─────result: true ├──expanding: ${if match_domain {$sender_helo_name}{+dlist}} @@ -189,12 +192,15 @@ LOG: smtp_connection MAIN ╭considering: domain=$domain/sender_domain=$sender_address_domain ├──expanding: domain=$domain/sender_domain=$sender_address_domain ╰─────result: domain=/sender_domain=sender.domain + ╰──(tainted) ╭considering: domain=$domain/sender_domain=$sender_address_domain ├──expanding: domain=$domain/sender_domain=$sender_address_domain ╰─────result: domain=recipient.domain/sender_domain=sender.domain + ╰──(tainted) ╭considering: domain=$domain/sender_domain=$sender_address_domain ├──expanding: domain=$domain/sender_domain=$sender_address_domain ╰─────result: domain=recipient.domain/sender_domain=sender.domain + ╰──(tainted) LOG: smtp_connection MAIN SMTP connection from CALLER closed by QUIT >>>>>>>>>>>>>>>> Exim pid=pppp (msg setup toplevel) terminating with rc=0 >>>>>>>>>>>>>>>> diff --git a/test/stderr/1003 b/test/stderr/1003 index c83c508f5..80599193e 100644 --- a/test/stderr/1003 +++ b/test/stderr/1003 @@ -1,3 +1,3 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@test.ex R=client T=send_to_server defer (-1): failed to expand "interface" option for send_to_server transport: internal expansion of "<; ${if" failed: condition name expected, but found "" +1999-03-02 09:44:33 10HmaX-0005vi-00 == user6@h5.test.ex R=client T=send_to_server5 defer (-1): failed to expand "interface" option for send_to_server5 transport: condition name expected, but found "" ******** SERVER ******** diff --git a/test/stderr/5000 b/test/stderr/5000 index 2bca5ea6a..2b96d8906 100644 --- a/test/stderr/5000 +++ b/test/stderr/5000 @@ -1,4 +1,4 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${expand:$h_tag:}" (maildir_tag for maildir_tagged_appendfile transport) failed: internal expansion of "${if eq{0}{1}{rhubarb}" failed: syntax error in "if" item - "fail" expected +1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@myhost.test.ex R=localuser T=maildir_tagged_appendfile defer (-1): Expansion of "${if eq{0}{1}{rhubarb}" (maildir_tag for maildir_tagged_appendfile transport) failed: syntax error in "if" item - "fail" expected Exim version x.yz .... configuration file is TESTSUITE/test-config admin user diff --git a/test/stderr/5004 b/test/stderr/5004 index 465f98acf..c3e489adb 100644 --- a/test/stderr/5004 +++ b/test/stderr/5004 @@ -91,10 +91,9 @@ routing userx@test.ex --------> r1 router <-------- local_part=userx domain=test.ex calling r1 router -rda_interpret (string): # Exim filter -save TESTSUITE/test-mail -expanded: # Exim filter -save TESTSUITE/test-mail +rda_interpret (string): '# Exim filter\nsave TESTSUITE/test-mail' +expanded: '# Exim filter +save TESTSUITE/test-mail' search_tidyup called changed uid/gid: r1 router (recipient is userx@test.ex) uid=CALLER_UID gid=CALLER_GID pid=pppp diff --git a/test/stderr/5204 b/test/stderr/5204 index 7c2109d09..1f927a485 100644 --- a/test/stderr/5204 +++ b/test/stderr/5204 @@ -187,8 +187,8 @@ calling q router q router called for "REDIRECT postmaster@test.ex"@some.host: domain = some.host requires uid=EXIM_UID gid=EXIM_GID current_directory=/ command wrote: REDIRECT postmaster@test.ex -rda_interpret (string): postmaster@test.ex -expanded: postmaster@test.ex +rda_interpret (string): 'postmaster@test.ex' +expanded: 'postmaster@test.ex' file is not a filter file parse_forward_list: postmaster@test.ex extract item: postmaster@test.ex diff --git a/test/stderr/5410 b/test/stderr/5410 index 7978a0266..a554fd953 100644 --- a/test/stderr/5410 +++ b/test/stderr/5410 @@ -38,6 +38,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: userx + ╰──(tainted) domain.com in "*"? yes (matched "*") ----------- end verify ------------ accept: condition test succeeded in ACL "cutthrough" @@ -48,6 +49,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: userx + ╰──(tainted) domain.com in "*"? yes (matched "*") Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected ╭considering: $primary_hostname @@ -67,6 +69,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{usery}{*}{:}} ├──expanding: $address_data ╰─────result: userx + ╰──(tainted) ╭considering: usery}{*}{:}} ├──expanding: usery ╰─────result: usery @@ -86,6 +89,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{userz}{*}{:}} ├──expanding: $address_data ╰─────result: userx + ╰──(tainted) ╭considering: userz}{*}{:}} ├──expanding: userz ╰─────result: userz @@ -191,10 +195,12 @@ end of inline ACL: ACCEPT ╰─────result: (helo=myhost.test.ex) + ╰──(tainted) ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }} ╰─────result: from CALLER (helo=myhost.test.ex) + ╰──(tainted) ├──condition: def:received_protocol ├─────result: true ╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -233,6 +239,7 @@ end of inline ACL: ACCEPT for $received_for ╰─────result: for userx@domain.com + ╰──(tainted) ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -245,6 +252,7 @@ end of inline ACL: ACCEPT (envelope-from ) id 10HmaX-0005vi-00 for userx@domain.com + ╰──(tainted) ----------- start cutthrough headers send ----------- ----------- done cutthrough headers send ------------ ╭considering: ${tod_full} @@ -305,6 +313,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") ----------- end verify ------------ accept: condition test succeeded in ACL "cutthrough" @@ -315,6 +324,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected ╭considering: $primary_hostname @@ -334,6 +344,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{usery}{*}{:}} ├──expanding: $address_data ╰─────result: usery + ╰──(tainted) ╭considering: usery}{*}{:}} ├──expanding: usery ╰─────result: usery @@ -427,10 +438,12 @@ end of inline ACL: ACCEPT ╰─────result: (helo=myhost.test.ex) + ╰──(tainted) ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }} ╰─────result: from CALLER (helo=myhost.test.ex) + ╰──(tainted) ├──condition: def:received_protocol ├─────result: true ╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -469,6 +482,7 @@ end of inline ACL: ACCEPT for $received_for ╰─────result: for usery@domain.com + ╰──(tainted) ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -481,6 +495,7 @@ end of inline ACL: ACCEPT (envelope-from ) id 10HmaZ-0005vi-00 for usery@domain.com + ╰──(tainted) ----------- start cutthrough headers send ----------- ----------- done cutthrough headers send ------------ ╭considering: ${tod_full} @@ -541,6 +556,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") ----------- end verify ------------ accept: condition test succeeded in ACL "cutthrough" @@ -551,6 +567,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected ╭considering: $primary_hostname @@ -570,6 +587,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{usery}{*}{:}} ├──expanding: $address_data ╰─────result: usery + ╰──(tainted) ╭considering: usery}{*}{:}} ├──expanding: usery ╰─────result: usery @@ -663,10 +681,12 @@ end of inline ACL: ACCEPT ╰─────result: (helo=myhost.test.ex) + ╰──(tainted) ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }} ╰─────result: from CALLER (helo=myhost.test.ex) + ╰──(tainted) ├──condition: def:received_protocol ├─────result: true ╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -705,6 +725,7 @@ end of inline ACL: ACCEPT for $received_for ╰─────result: for usery@domain.com + ╰──(tainted) ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -717,6 +738,7 @@ end of inline ACL: ACCEPT (envelope-from ) id 10HmbB-0005vi-00 for usery@domain.com + ╰──(tainted) ----------- start cutthrough headers send ----------- ----------- done cutthrough headers send ------------ ╭considering: ${tod_full} diff --git a/test/stderr/5420 b/test/stderr/5420 index 9b51bff2b..9aefc2431 100644 --- a/test/stderr/5420 +++ b/test/stderr/5420 @@ -38,6 +38,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: userx + ╰──(tainted) domain.com in "*"? yes (matched "*") ----------- end verify ------------ accept: condition test succeeded in ACL "cutthrough" @@ -48,6 +49,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: userx + ╰──(tainted) domain.com in "*"? yes (matched "*") Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected ╭considering: $primary_hostname @@ -67,6 +69,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{usery}{*}{:}} ├──expanding: $address_data ╰─────result: userx + ╰──(tainted) ╭considering: usery}{*}{:}} ├──expanding: usery ╰─────result: usery @@ -86,6 +89,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{userz}{*}{:}} ├──expanding: $address_data ╰─────result: userx + ╰──(tainted) ╭considering: userz}{*}{:}} ├──expanding: userz ╰─────result: userz @@ -192,10 +196,12 @@ end of inline ACL: ACCEPT ╰─────result: (helo=myhost.test.ex) + ╰──(tainted) ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }} ╰─────result: from CALLER (helo=myhost.test.ex) + ╰──(tainted) ├──condition: def:received_protocol ├─────result: true ╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -234,6 +240,7 @@ end of inline ACL: ACCEPT for $received_for ╰─────result: for userx@domain.com + ╰──(tainted) ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -246,6 +253,7 @@ end of inline ACL: ACCEPT (envelope-from ) id 10HmaX-0005vi-00 for userx@domain.com + ╰──(tainted) ----------- start cutthrough headers send ----------- ----------- done cutthrough headers send ------------ ╭considering: ${tod_full} @@ -306,6 +314,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") ----------- end verify ------------ accept: condition test succeeded in ACL "cutthrough" @@ -316,6 +325,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected ╭considering: $primary_hostname @@ -335,6 +345,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{usery}{*}{:}} ├──expanding: $address_data ╰─────result: usery + ╰──(tainted) ╭considering: usery}{*}{:}} ├──expanding: usery ╰─────result: usery @@ -428,10 +439,12 @@ end of inline ACL: ACCEPT ╰─────result: (helo=myhost.test.ex) + ╰──(tainted) ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }} ╰─────result: from CALLER (helo=myhost.test.ex) + ╰──(tainted) ├──condition: def:received_protocol ├─────result: true ╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -470,6 +483,7 @@ end of inline ACL: ACCEPT for $received_for ╰─────result: for usery@domain.com + ╰──(tainted) ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -482,6 +496,7 @@ end of inline ACL: ACCEPT (envelope-from ) id 10HmaZ-0005vi-00 for usery@domain.com + ╰──(tainted) ----------- start cutthrough headers send ----------- ----------- done cutthrough headers send ------------ ╭considering: ${tod_full} @@ -542,6 +557,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") ----------- end verify ------------ accept: condition test succeeded in ACL "cutthrough" @@ -552,6 +568,7 @@ domain.com in "! +local_domains"? yes (end of list) ╭considering: $local_part ├──expanding: $local_part ╰─────result: usery + ╰──(tainted) domain.com in "*"? yes (matched "*") Connecting to 127.0.0.1 [127.0.0.1]:1225 from ip4.ip4.ip4.ip4 ... connected ╭considering: $primary_hostname @@ -571,6 +588,7 @@ cmd buf flush ddd bytes ╭considering: $address_data}{usery}{*}{:}} ├──expanding: $address_data ╰─────result: usery + ╰──(tainted) ╭considering: usery}{*}{:}} ├──expanding: usery ╰─────result: usery @@ -664,10 +682,12 @@ end of inline ACL: ACCEPT ╰─────result: (helo=myhost.test.ex) + ╰──(tainted) ├──expanding: ${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }} ╰─────result: from CALLER (helo=myhost.test.ex) + ╰──(tainted) ├──condition: def:received_protocol ├─────result: true ╭considering: with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -706,6 +726,7 @@ end of inline ACL: ACCEPT for $received_for ╰─────result: for usery@domain.com + ╰──(tainted) ├──expanding: Received: ${if def:sender_rcvhost {from $sender_rcvhost }{${if def:sender_ident {from ${quote_local_part:$sender_ident} }}${if def:sender_helo_name {(helo=$sender_helo_name) }}}}by $primary_hostname ${if def:received_protocol {with $received_protocol }}${if def:tls_in_cipher_std { tls $tls_in_cipher_std @@ -718,6 +739,7 @@ end of inline ACL: ACCEPT (envelope-from ) id 10HmbB-0005vi-00 for usery@domain.com + ╰──(tainted) ----------- start cutthrough headers send ----------- ----------- done cutthrough headers send ------------ ╭considering: ${tod_full} diff --git a/test/stdout/0002 b/test/stdout/0002 index c1f9d3799..6319fdc65 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -873,6 +873,7 @@ xyz > escape: B7·F2ò > > primary_hostname: myhost.test.ex +> sender_address: sndr@dom > match: cdab > Failed: "if" failed and "fail" requested > yes diff --git a/test/stdout/0035 b/test/stdout/0035 index 60e2c62d2..09b5df4e9 100644 --- a/test/stdout/0035 +++ b/test/stdout/0035 @@ -145,7 +145,7 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp @@ -166,7 +166,7 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp @@ -190,7 +190,7 @@ EXIMUSER EXIM_UID EXIM_GID <> ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp @@ -212,7 +212,7 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp @@ -234,7 +234,7 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp @@ -259,7 +259,7 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp @@ -284,7 +284,7 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -interface_address 127.0.0.1.1225 -received_protocol esmtp diff --git a/test/stdout/3415 b/test/stdout/3415 index 8e20252e2..0889dd795 100644 --- a/test/stdout/3415 +++ b/test/stdout/3415 @@ -156,14 +156,14 @@ EXIMUSER EXIM_UID EXIM_GID ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -host_auth au1 -interface_address 127.0.0.1.1225 -received_protocol esmtpa -body_linecount 0 -max_received_linelength 0 --auth_id username +--auth_id username -deliver_firsttime XX 1 @@ -183,14 +183,14 @@ EXIMUSER EXIM_UID EXIM_GID <> ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -host_auth au1 -interface_address 127.0.0.1.1225 -received_protocol esmtpa -body_linecount 0 -max_received_linelength 0 --auth_id username +--auth_id username -deliver_firsttime XX 1 @@ -208,14 +208,14 @@ EXIMUSER EXIM_UID EXIM_GID <> ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -host_auth au1 -interface_address 127.0.0.1.1225 -received_protocol esmtpa -body_linecount 0 -max_received_linelength 0 --auth_id username +--auth_id username -deliver_firsttime XX 1 @@ -233,14 +233,14 @@ EXIMUSER EXIM_UID EXIM_GID <> ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -host_auth au1 -interface_address 127.0.0.1.1225 -received_protocol esmtpa -body_linecount 0 -max_received_linelength 0 --auth_id username@auth.id.domain +--auth_id username@auth.id.domain -deliver_firsttime XX 1 @@ -258,14 +258,14 @@ EXIMUSER EXIM_UID EXIM_GID <> ddddddddd 0 -received_time_usec .uuuuuu --helo_name rhu.barb +--helo_name rhu.barb -host_address 127.0.0.1.9999 -host_auth au1 -interface_address 127.0.0.1.1225 -received_protocol esmtpa -body_linecount 0 -max_received_linelength 15 --auth_id username@auth.id.domain +--auth_id username@auth.id.domain -deliver_firsttime XX 1 -- 2.30.2