Track tainted data and refuse to expand it
authorJeremy Harris <jgh146exb@wizmail.org>
Thu, 25 Jul 2019 11:06:07 +0000 (12:06 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Thu, 25 Jul 2019 11:06:07 +0000 (12:06 +0100)
205 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/OS/unsupported/os.c-IRIX
src/OS/unsupported/os.c-IRIX6
src/OS/unsupported/os.c-IRIX632
src/OS/unsupported/os.c-IRIX65
src/OS/unsupported/os.c-cygwin
src/exim_monitor/em_log.c
src/exim_monitor/em_main.c
src/exim_monitor/em_menu.c
src/exim_monitor/em_queue.c
src/exim_monitor/em_strip.c
src/exim_monitor/em_version.c
src/exim_monitor/em_xs.c
src/src/acl.c
src/src/arc.c
src/src/auths/call_pam.c
src/src/auths/cram_md5.c
src/src/auths/cyrus_sasl.c
src/src/auths/heimdal_gssapi.c
src/src/auths/pwcheck.c
src/src/auths/xtextdecode.c
src/src/auths/xtextencode.c
src/src/base64.c
src/src/bmi_spam.c
src/src/child.c
src/src/daemon.c
src/src/dane-openssl.c
src/src/dbfn.c
src/src/dcc.c
src/src/debug.c
src/src/deliver.c
src/src/dkim.c
src/src/dmarc.c
src/src/dns.c
src/src/drtables.c
src/src/dummies.c
src/src/environment.c
src/src/exim.c
src/src/exim_dbmbuild.c
src/src/exim_dbutil.c
src/src/expand.c
src/src/filter.c
src/src/filtertest.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/hash.c
src/src/header.c
src/src/host.c
src/src/ip.c
src/src/local_scan.h
src/src/log.c
src/src/lookups/cdb.c
src/src/lookups/dbmdb.c
src/src/lookups/dnsdb.c
src/src/lookups/ibase.c
src/src/lookups/json.c
src/src/lookups/ldap.c
src/src/lookups/lmdb.c
src/src/lookups/lsearch.c
src/src/lookups/mysql.c
src/src/lookups/nis.c
src/src/lookups/nisplus.c
src/src/lookups/oracle.c
src/src/lookups/pgsql.c
src/src/lookups/redis.c
src/src/lookups/sqlite.c
src/src/macros.h
src/src/malware.c
src/src/match.c
src/src/mime.c
src/src/moan.c
src/src/mytypes.h
src/src/os.c
src/src/parse.c
src/src/pdkim/pdkim.c
src/src/pdkim/signing.c
src/src/queue.c
src/src/rda.c
src/src/readconf.c
src/src/receive.c
src/src/regex.c
src/src/retry.c
src/src/rewrite.c
src/src/rfc2047.c
src/src/route.c
src/src/routers/dnslookup.c
src/src/routers/ipliteral.c
src/src/routers/iplookup.c
src/src/routers/manualroute.c
src/src/routers/redirect.c
src/src/routers/rf_change_domain.c
src/src/routers/rf_get_munge_headers.c
src/src/routers/rf_get_transport.c
src/src/routers/rf_queue_add.c
src/src/search.c
src/src/setenv.c
src/src/sieve.c
src/src/smtp_in.c
src/src/smtp_out.c
src/src/spam.c
src/src/spool_in.c
src/src/spool_mbox.c
src/src/spool_out.c
src/src/store.c
src/src/store.h
src/src/string.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/tlscert-gnu.c
src/src/tlscert-openssl.c
src/src/transport.c
src/src/transports/appendfile.c
src/src/transports/appendfile.h
src/src/transports/autoreply.c
src/src/transports/lmtp.c
src/src/transports/pipe.c
src/src/transports/smtp.c
src/src/transports/smtp.h
src/src/transports/tf_maildir.c
src/src/tree.c
src/src/utf8.c
src/src/verify.c
src/src/version.c
test/confs/0102
test/confs/0137
test/confs/0140
test/confs/0284
test/confs/0386
test/confs/0428
test/confs/0478
test/confs/0504
test/confs/0610
test/confs/1003
test/confs/5000
test/confs/5050
test/confs/5103
test/log/0137
test/log/0428
test/log/0610
test/log/1003
test/log/5000
test/mail/0137.userx
test/mail/0428.inbox.JUNK
test/mail/0428.someone
test/mail/0428.userx
test/mail/0428.userx-sawsuffix
test/mail/0428.userx13 [new file with mode: 0644]
test/mail/0428.userx14 [new file with mode: 0644]
test/mail/0428.userx9 [new file with mode: 0644]
test/mail/5000.new/1.myhost.test.ex
test/mail/5000.new/2.myhost.test.ex
test/mail/5000.new/3.myhost.test.ex:S370
test/mail/5000.new/4.myhost.test.ex,S=370
test/mail/5000.new/5.myhost.test.ex
test/mail/5000.new/6.myhost.test.ex
test/mail/5000.new/7.myhost.test.ex,S=10694953:2,S
test/msglog/5000.10HmaX-0005vi-00
test/paniclog/1003
test/paniclog/5000
test/runtest
test/scripts/0000-Basic/0002
test/scripts/0000-Basic/0137
test/scripts/0000-Basic/0428
test/scripts/0000-Basic/0504
test/scripts/1000-Basic-ipv6/1003
test/scripts/5000-maildir/5000
test/scripts/5100-lmtp-transport/5103
test/stderr/0002
test/stderr/0023
test/stderr/0037
test/stderr/0084
test/stderr/0085
test/stderr/0123
test/stderr/0279
test/stderr/0297
test/stderr/0360
test/stderr/0361
test/stderr/0364
test/stderr/0370
test/stderr/0377
test/stderr/0378
test/stderr/0379
test/stderr/0380
test/stderr/0382
test/stderr/0386
test/stderr/0388
test/stderr/0393
test/stderr/0399
test/stderr/0402
test/stderr/0403
test/stderr/0404
test/stderr/0426
test/stderr/0464
test/stderr/0544
test/stderr/1003
test/stderr/5000
test/stderr/5004
test/stderr/5204
test/stderr/5410
test/stderr/5420
test/stdout/0002
test/stdout/0035
test/stdout/3415

index c4d6112ad61bb7a641f50ed01e8f5d97375a61ae..32d57d027ec1a7c2d2d42a6c5a0e807b69508bab 100644 (file)
@@ -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 &dagger; 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%&
index 2e839039c6bb0c4729790f17b1beb677cf940f86..78cb1272056c16a76db546bd0450ec4d06453e4d 100644 (file)
@@ -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
 -----------------
index 1f6b0e1b10c4f7239542239629b27bdcbfd6aa7c..c1539cb507846273acf5c7096b92c87f735a5738 100644 (file)
@@ -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",
index 1f6b0e1b10c4f7239542239629b27bdcbfd6aa7c..c1539cb507846273acf5c7096b92c87f735a5738 100644 (file)
@@ -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",
index 1f6b0e1b10c4f7239542239629b27bdcbfd6aa7c..c1539cb507846273acf5c7096b92c87f735a5738 100644 (file)
@@ -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",
index 1f6b0e1b10c4f7239542239629b27bdcbfd6aa7c..c1539cb507846273acf5c7096b92c87f735a5738 100644 (file)
@@ -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",
index c9464aae26013a6af0ba9c05a2a4976fff417a57..5ca05a8db0aea59d3c3acbbc479b19f86a694181 100644 (file)
@@ -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') {
index 52eef6b2096ff8b994c31ed8ed424fcbc2f90c3e..1e1dc7c04c424e76a5d437548ba1a22b920f7fa6 100644 (file)
@@ -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)
index 7aa760eb3e2d5e7f19b3eac7671ce01638f77c0e..9c7f442e5b4e974d51f49cd2ba64b94a5be1ec56 100644 (file)
@@ -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");
index 31ce1a3718794c8b0c3f255daae733047a75b3d5..92e0b351ba2cc9061fb7988c9f74eedeee47813a 100644 (file)
@@ -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)
index c8d9a40fc02b1bc40098b62f21d578220d5ddece..f121527cce3b0a33db4884065e5b536bc6df340f 100644 (file)
@@ -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)
   {
index 2a5f0b84e72969bab41d9dd0b4453e2c8034ac55..03864d2908b2dcc84b1bf8fbe392d65d915f252b 100644 (file)
@@ -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;
index e5a4ebbb07e1f587042675c805ce7dcc03167603..52c55a4a3593b4b2438f768d2e6d9957c4f19013 100644 (file)
@@ -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
 }
 
index b145fb993f62e4990ceeb67a66912ce25e84d332..ee91f7c159c06ff85e4754c6f6670fbc98e92590 100644 (file)
@@ -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 */
index 3788612f8501ab8d23e85ccac03575268ef77676..dac2ba570cdeba68efac738be2acb632837ae936 100644 (file)
@@ -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);
 }
 
index 7bdf4bfbbc7bc46eb0dec7c291f7f81625526ecc..c266849ca5d6514333735070f5119d545ed4a898 100644 (file)
@@ -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;
index 204f449d76c1122b6afa70a8dc9d5624321ee608..9a46b6b63c45c2156cb5c6b2df0ba5738d652a4e 100644 (file)
@@ -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;
     }
index b1c5610e709988d08394e7bf4f50bc40a94fcca8..a60db56e4dee71363cbf7581e921bbb57315c4ff 100644 (file)
@@ -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);
   }
index bce42024988d554682ef46a8886a7079e651137d..480010bab8135ea3a0b8a4392adab3834e8122a1 100644 (file)
@@ -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
index a70bc8aa3b64b605342f56f73a60d37cf86876ad..273d4f47b5a2d7c77b61cc9d8aab582f8cb422ee 100644 (file)
@@ -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);
   }
 
index 54ba80f92ab9881e92c1ffde53e1ed7a2786b56b..f733b9898046d94523a59ee7523ff82c901a11ed 100644 (file)
@@ -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;
index 5312acf351ffb07a5c825183383113c1355ac8b2..95cf5dab2c4c2f84ec7e3e6974cf066be5694351 100644 (file)
@@ -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)
index 2c00c4a3d907c3ae8f2bcf791f39cd45f53a2a7b..30ff8f11ddb039401f34d74c1e2ccfea0e1210ff 100644 (file)
@@ -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;
index 289383b66ddd02a9fd80e0a7bff109a9d3c3c97b..6c8191462a9c8cd63c9c88c744b8590c6b638315 100644 (file)
@@ -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)
index 546ac1e36837963c173d8e1618ec09973352ba38..6651de5ad7d195a9d5ee0212fa976750bf4a3ff5 100644 (file)
@@ -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;
index e53e448ed09539085631aad439bf878c997fa42d..d3cd88201d46c5422588e11e894a10d4996af718 100644 (file)
@@ -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. */
index 0b4d347b94a88687e4314b7c5af1a463d776618a..21ce2f03e89102cd0628fe56e9710c89de2ca80c 100644 (file)
@@ -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)
index c0bbf3d0d67472ba79cfd78442bd9b9a335f1f47..9b86a488b5cc3c20d47cd673dc4a43af2aa3f663 100644 (file)
@@ -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;
index a607756819313cd69c5f1db34cce06a056cc7a37..63a1aefe353818ea385feee3b6700828fc5e05e5 100644 (file)
@@ -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);
 
index 5aa2b17aafacc08b9282f956679e3c7850bc535a..4c9c0964fd708f7571e05b5ad105925b92533af7 100644 (file)
@@ -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);
index eb62157def1dfc6a90d4830225bf2279cf0d288d..de979623239886863042ddf84a3b966adff63949 100644 (file)
@@ -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;
index a597c9a886c088be371dc7a337a8ef81275c36e5..ba9572e48be0423fd267fd2b15c4e0e0e1ca3b32 100644 (file)
@@ -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;
     }
index 715774c7e8228cd32c3cbb69ee0ad2a72660698e..a410ed55bbcbfe2734b12493c784bc0c67429f8f 100644 (file)
@@ -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? */
 }
 
index 5bf33032a59f6b45d311077c6182b1466560047c..be9c4b384057c6c94c9ffc23038243680e8a3f14 100644 (file)
@@ -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,
index 6ef6b7784b03343bff3a2b3fae635577d8300d37..e3845978c36de35610681faa3ea8bc58a63f68f4 100644 (file)
@@ -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;
     }
index b7024297d962e38bcccfe02c9ef8cbe7b9eadb25..05975628445debcd6cf430ded748b15d34c5b9df 100644 (file)
@@ -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;
 }
index 2e1ad11f5864fb1c1e24a9ba3bba3a9248719bbb..197415f0ccd6a4c0fa6e113071709401d9897ac5 100644 (file)
@@ -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;
index 9d3d126a693629ad7358ac9963dcf39ff4d96fe2..c29cc6c8dd9e6b945854fd5d80f82229117131fb 100644 (file)
@@ -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;
 }
index 7571705d7092a9f8310937c26ee769d3eb705560..f163b1249f1f9e9a45513813a79690afe088fd08 100644 (file)
@@ -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)
index 39207be46d9264231be2ed0c7fbe25cbe304e8af..311d961e81e6e31413937021b8d77c0c032c6e65 100644 (file)
@@ -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);
index 2c7aad63b40ac42260b3b099674f49ca526a3c7a..8b71a414018d9182c9ea573a005eeb831d5f6bc0 100644 (file)
@@ -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);
index 65c585d1c0cd6641eedcc52429ee6f7c33f76fb3..1bfc75d2b6cf1e31cdd36864fe39679e09af92b1 100644 (file)
@@ -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();
index a16416c6818309920b0bedf07fbec7cfbd0b8960..3da616700a2259c640b9ea83500f844575f1339a 100644 (file)
@@ -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"<default>",
-         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"<default>",
            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;
 }
index f3d3acc522a35c8dc931e36f5c501f4db91dd403..f54cbefb5582e80d950c2f3052ed0958a4a59cd8 100644 (file)
@@ -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);
 
index 806ba755dc73e439be79bcd76bc09fd7aebc6cbe..bcf04c2d21461b62d449858966efb3fe1b8f35ce 100644 (file)
@@ -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;
 }
 
 /******************************************************************************/
index 742584ed1d65b62c19a1af92bf46cd1adde94142..15fb0898c72714ed2c67959083df1b853fba6d04 100644 (file)
@@ -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;
 
index 80e1764a36188e59dd5422fd816cc1bf1dd8c0d8..c22600425ea90ea75238b27d0a90adba62bb5655 100644 (file)
@@ -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 */
index 1bdeaef5fea85d41dda27cd30030c4e208130d5f..f1a6c4096f81ed1a4e6c77f248f9fdbff3e25fdc 100644 (file)
@@ -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);
 }
index 76ea10f132eee0aafa985dfb0767c8e7b952147d..a6c44fac82d9b383b3881a36a3ada5b79f2a361a 100644 (file)
@@ -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;
index 9d94a2fde97e6c47a565e8dab9b1dedacefa107f..a00d048f675b6d3a8e62a0ea4104d110df668b86 100644 (file)
@@ -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;
index 0f25df1dacc1a598615e8ec89ed1f3beb0f6bfd3..19be51a030d89b4cbefffc6e95fb1d1103f720a4 100644 (file)
@@ -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)
   {
index dced8bd529072997326c04b7170c92e675dc3433..b250a163b511adf559815fe9bd50a728f09f7c8e 100644 (file)
@@ -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
index 0aaf94a31fb12dd89fcc684590c63c9c45686ef8..e2543a74dda99130022ccc8cc2025c59f7ab30fc 100644 (file)
@@ -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--;
index 13fe8b7d27d20870dc613a69a44046654a8304c8..5cae1535f5efb26975dbef8ca4936d7b0d33841a 100644 (file)
@@ -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;
index bdc002d4e943e1e6d37934e906681eab40d31bad..5e97009ccae4c3d6a42aa7e32bb07d4a8b3da586 100644 (file)
@@ -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';
 
index 70203fa6d89b13d5414b4b147019ef7c1a52b536..5654b5615f418502b85eec5f12f93619e366b958 100644 (file)
@@ -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. */
index eab3c6ad8126dbfba715f59fb26ef2e7fd5b15b8..f08f503f0ea3432c43316537fbfe6d1e7695383b 100644 (file)
@@ -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) {
index a02b7b9bdd0ea760d188f8568361840d3990d799..15b8617512f07d666d8ac918345089b3f66af0a5 100644 (file)
@@ -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;
 }
index 97ee188d273999da1585d99c7ff7fb9b96ababee..5b0cffaf8c88245960db21b0966361adb7946043 100644 (file)
@@ -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;
     }
   }
index 931ea5bbe047d83852620396bf1d1ba5447e8298..2976cfac5be591283face64fab8980d9a3884d79 100644 (file)
@@ -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)))
index 8b4459ad131a4f87b0f3c7b8a0b0515ce5a2d81d..76b76b8f9ad6115735486e6c06c8b7e37808913b 100644 (file)
@@ -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;
   }
index 1984e30b2f460f98e144e8e628028b687766ee8e..460ee2973e5e5e857d5a85f94246ed766394fc9f 100644 (file)
@@ -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)
   {
index d3f0480acf2b9176ffe3e0e5a562432c069d51b8..6f5307f4362fab679ed15bc5a754086f1c811e1d 100644 (file)
@@ -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;
index 6a3351eccab09168b88117ffeae17903a754cbf7..98f3df303e4c2e06c80c5add7883cc6f79d6bba5 100644 (file)
@@ -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)
   {
index d106c519fb52d29fd965ecd45694c6d7aa8d343d..4e8cba5caa03e1011c115f47febbf5d8737bfa49 100644 (file)
@@ -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)
   {
index cf1e1743e5329787f4da632c0d61ef8d08cfaf82..b5f60938e7946dba99b20504f80c01c216d32155 100644 (file)
@@ -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)
   {
index 6de675787141cc73ad45f48274fe39b40774461b..1b53eed69cec940067c413f87d231e4a3bc9d2ad 100644 (file)
@@ -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)
   {
index 5da2de814dd5eb7bf190342a62ec50b8c1d6bd1a..6200d6c820503f184b68f76841d5bd2fd1d1315a 100644 (file)
@@ -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)
   {
index 2c7900b8318252476ff70e416c2f9f4b6c3e39bb..a94a71f7eec52eb55991cf17d869bef874739477 100644 (file)
@@ -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 <Mailer-Daemon@$qualify_domain>"
@@ -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 */
index 91649cf2022d0d51089e652fceb4a993be35431b..481b46acc2ee4d5971b22b175124003b61d97bb2 100644 (file)
@@ -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 */
index 43f5912fd4cc8a50ba3c26bb55dfe04b367cee45..4cd9259bbec6e355b7cadc56707f7c403cde1543 100644 (file)
@@ -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;
 
index cf537d7c187eed0d1879c1d4fdd828f900112224..d47b569827795cbfaeed88d6a47319c895bb4d77 100644 (file)
@@ -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... */
index cdec74586b530e353730dc76649c9efaf402f5d3..fea3683ba07631214eb7157e1b306040138f8cb4 100644 (file)
@@ -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);
 
index be11240bf711f0fe4fbf34f52b9c91aff1388dbb..84baa9eea310e111c3e00135a6916d05ec7944b4 100644 (file)
@@ -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 <string.h>
+
 #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 */
index 9c1281a78ce31626b9a032f45e2b219028e942ba..c44bd3e99a7c7f0a9cc1d2a64593b576b21caf04 100644 (file)
@@ -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;
index 4b0efa0e1367ff6b0de3783bc9de84500e7d786e..e64cb94a12f8d689a900a43af5bdcb5af608e73e 100644 (file)
@@ -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;
index 9ebcfc1b6676d8f724522d49e56e0950b302eb29..239532bb699998ac3e96ff125a10163a94a9904c 100644 (file)
@@ -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;
 }
 
index 53a8a7b45143b1a2f6c7f49c33ba27a38d1f418e..de64ee4280ec104840b5f8e0a7b7ec834288f32b 100644 (file)
@@ -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
index f54124c5e901c46d6c9f88549728946fbe8b7e37..a124782e91e1684b21fbcc7d4e89318ed508ea30 100644 (file)
@@ -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
     )
   {
index 228f574097b0877daf0677080e1c6b3cf7f1e4f9..42b7b14a5557ee972fa6b20dece577c05583f6dc 100644 (file)
@@ -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;
index a7cf03dd353171409166f4778136282d2cb4b50b..52c64830e5eea5a7d6883b34b081e784dab3abf0 100644 (file)
@@ -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;
 
index ed2afb31748f148d537c6531895a68aba01ab755..c7553f80265475f7fb2d1d7e66dc20c48fce5fe9 100644 (file)
@@ -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. */
 
index 87d03f363a2919bfe57cb645dc7e2e29379364bc..f9c06b9e3dde9e26ba87741db92662d3310e584c 100644 (file)
@@ -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);
 
index 509de123c6a799bd31990a2ea6561b53acc244a5..d068f547d386f18beb1fee762ffc8a7a10cfcbde 100644 (file)
@@ -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;
           }
index 221b438b8e683d6c9dab64466614f7e92f2a51b1..f942bec051a841cca46973b1a49415ab9fd05507 100644 (file)
@@ -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 */
 
index 5d527d44bc2412e77e6e05d13f17cca29d0fb38c..f7085007a8df5ecf10b7fde81b4c8b86bc9124fd 100644 (file)
@@ -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,
index b6930493f6899a7d85fdc087129a620590735e53..41716bc0e4ad2d9bcb5c6049501d3a52137f36b9 100644 (file)
@@ -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"<none>" : 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"<none>");
   goto ROUTE_EXIT;
   }
 
index 33939be4c26cc0c30d337a15c9376e61a6286e00..2471f2fb7884cce90b19df95d3902c1642115240 100644 (file)
@@ -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. */
index ecc60427891495725ad3fb2e2e8317d91a487938..fb25e695b0b85c705dad3a33f19aab9e597407f6 100644 (file)
@@ -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);
index 13849f9f1e48370a7b3a548f0ad5595f809a86f2..4ceb1f59a2fa08158dd02bfcce4a479a42dc1d61 100644 (file)
@@ -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. */
index f7cca3a429bed7dc8814d834cb3ef3107112c3dc..c9ddd27862336b910e3ac4836f739d91447970d3 100644 (file)
@@ -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;
index 09f15d035bcb124d0a155e125c7b5066c07d6a8e..6bbbf37e26625593f2e619f5b5b528a366a98592 100644 (file)
@@ -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;
+      }
   }
 
 
index 71a6a106cd633ff41544b9854d32f83657db253b..39c41fc9da0c37884b11591e9220f0d08b0b4550 100644 (file)
@@ -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);
index f08b55adcdf7f0608b278ef0475076705f81cf39..5c95d2d1c957026f9f8ba93b501bb919062f909b 100644 (file)
@@ -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;
index 353c47041f54ee5f10fe9e019790099c91b4fdac..4a43818ff45a745b42c000976888e2d3fc289828 100644 (file)
@@ -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)
index 99de7b09cfb7bf1f92abce918c34cb7c42ef4ad9..938eee30a22a18cbf7b04ee5fa83cb60ab8bf98e 100644 (file)
@@ -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"<unset>" : addr->transport->name,
+    addr->transport ? addr->transport->name : US"<unset>",
     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);
index 92b95a90beee3308b448f82697692c61579a2fdd..df10f773a903b287ef3710fb03cbe18d29e99d91 100644 (file)
@@ -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);
index 9a05b64b90cc42ef23787695cd07063d40d7e7c1..0274830195c72ff6bf03ab1c1947e7486a25101e 100644 (file)
@@ -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;
 }
 
index 2317f2eebca22fadd6601dc228ffef7f2b042d0f..5e8d1e6f47760c6a92c748e574208ad7eb48df65 100644 (file)
@@ -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;
     }
   }
index 1e478a6ac7a99b77ea75730911997a05a7bd719a..24978c86cfc117873ba86a8abde980da7f679f13 100644 (file)
@@ -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;
index 4f8a84e2b794b074642249883a59794f9d461f07..ece79de10a52e22b9ee692cdaaef96eb897b923b 100644 (file)
@@ -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 */
index 7e334852e53ba4454a50d0bee545112423deb12c..4cc4a9ae0967c9f069baba3feeae58b14120c186 100644 (file)
@@ -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,
index c5733f13e14120e678ad89d57067a0372f9cf6fb..101a6c40cfb94245f3869a806e1383d0b996c25a 100644 (file)
@@ -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++;
 
index 188f405c028ac8a584be6690a1db5e4e19bc42b2..caf1712cf7dde5619870e963826ed769e7b6dff5 100644 (file)
@@ -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;
 }
index 6ceae38b2285b2fabab6c673ad5b0336b3c03a0a..0dfa4de525769829074484aabc18070262b0cb02 100644 (file)
@@ -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);
index 94becbb9004e5adce4344e7ecd07752dabcb519b..7871d0ccce96d9bc9683951d88925bb36ea1a6ee 100644 (file)
@@ -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 <sys/mman.h>
 #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 */
index 393f6c7b5cb1b82fcd843e849fe3337c384eba9b..6464e32ed556ab907e48fb157113e85506ad8ce0 100644 (file)
 
 /* 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 */
 
index fed210080971806fdc0b4641d5cb7de9ad3cab05..8cc34a0773cbb70cad5ff5b388be03c62e6a9636 100644 (file)
@@ -12,6 +12,7 @@ utilities and tests, and are cut out by the COMPILE_UTILITY macro. */
 #include "exim.h"
 #include <assert.h>
 
+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);
index 423c3a23db4659a2874723e168f48f1c57347eda..ca60ddb6abdd509d3e82d3be4bc1d32b18bdaf7d 100644 (file)
@@ -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);
index ea30ff7cad2ce8ac6e701a902b4194a470db9f67..9542a1ed477681d9f2cf004b5e283b07b080132e 100644 (file)
@@ -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;
 
index cebeae526315da2bd876e54f46f04ce6b06e5771..45135814ca912542af69e3bd39c67858f58ce290 100644 (file)
@@ -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);
 
index f9808b3542435f68d50326aba26f9792022b51a9..0aa65c8861cf3dbee82a676d50c7443f7febffb7 100644 (file)
@@ -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);
 }
index d745ef15fe7ab13bdfb46224c6e3e9246565b904..ce02adbf2b60e77915bc03f422af5444ccec6d42 100644 (file)
@@ -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;
index d9f8d4989a7161763afb2090aaa92a2ae75a5e6b..5d23008f85e6b536c1b22f75fe800a2e59140e5f 100644 (file)
@@ -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)
               {
index 4b14db180cd4931d424c64c9c4c798c1f097408c..4f0f126bc56c16adcd91622a4a56d2a103f39631 100644 (file)
@@ -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 */
index 9decfbaeab6d9378b9b920c5c40dc7639d47d176..734e65833f6152eb0476b94413f1eb84c30a965c 100644 (file)
@@ -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);
     }
index b2bf5f0dae0801e7b989b5ef147765d841cae262..306ec450b8a3a3724273fc44f4258ed423a92e94 100644 (file)
@@ -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
index 8e5c0c502c0b4d8e30b49dc1b8953369250cc3b6..70df8729d47a94bec136f2aa358681ed0dd212dd 100644 (file)
@@ -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;
index ad0de52b6bce7b26988d76020c0919a7d5f252fd..617a55a1691d9ada34cacecf925c72d29d1db487 100644 (file)
@@ -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);
index 79d674fa1ed556fd7664b1a1b0e2983bcbc27cd5..ac5620971f8223f4239c32af1c0fc948ba6bce12 100644 (file)
@@ -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);
index 4caf0cdcfd916359f99a2baed1d4bd2e7a35988e..611895e064a72aac70be4e4f491fe36e98bb527f 100644 (file)
@@ -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);
index ff792bb6e293ff303412c25a776d37b54c556369..d5a4096510d8853bea0377172fad95a58c6ada67 100644 (file)
@@ -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);
index dec72280fb17ea9c0253fcefaace12a405a47f55..529a9a6604c26b86d213b1f6c524a7f593c3b22d 100644 (file)
@@ -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)
   {
index 25a4a0ca745d4611f0204c2c0dfdc348f6f72720..e98dee669de84f7a15dbaeb303ffb7fa9e031b42 100644 (file)
@@ -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);
     }
 
index 91d8124280206787a1c21bbd7e8bad86d514292d..118ebbdf2166710fc673c010e234256200a18e65 100644 (file)
@@ -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
 }
 
index dde11a5e9e84aa600f23778723c7fd2dee0b2315..2497fd032b422b611441b8b614df8ff0a8f3031d 100644 (file)
@@ -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 -----
index 1a2094ef372227b7de9108bbb32399032cfc66d2..7c48d62349121cf588817ed9460cdbbf7cfb9ded 100644 (file)
@@ -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 -----
index fbc64b43bccb531d7ab2c6a836c161af7ec1cfb1..02567153ceff659d983b0500e8fa8dcd210842f6 100644 (file)
@@ -22,7 +22,7 @@ reply:
   driver = accept
   retry_use_local_part
   senders = !
-  transport = $local_part
+  transport = ${if eq {reply1}{$local_part} {reply1}{reply2}}
 
 
 # ----- Transports -----
index d38f3839003840d93f248ddb7309edd2a21d83fd..0103742f4016937e699e436b7db97cdc1dc97a99 100644 (file)
@@ -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 -----
index 5eecfcc98212b0a19a0714e0c601494424496069..0af3202a38a8f5df7c12b0cfb07374775f5e293b 100644 (file)
@@ -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
index 38601f30bf488888031afddda75b30e57ccf692d..5ee5cdbb840370242b85d09ab661a39b64f212cd 100644 (file)
@@ -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 -----
index d9fd0a5ce162173357eedae8e832a6ee7b938f08..cfb9a17b1af20023eb263121231fc0f5eb642c75 100644 (file)
@@ -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
 
index b2dadfcb3c1d9f23597cdb9f0a32623ff6e1ee6c..c7b0131c06f8a6aa4fc465ccf78dcd95dd27aa83 100644 (file)
@@ -13,7 +13,7 @@ begin routers
 
 r1:
   driver = accept
-  transport = $h_transport:
+  transport = OPT
 
 
 # ----- Transports -----
index 0567d8aaf6d5fc5f2062df1148f82ee7f558e9ea..49778352f87ababc5bbb625d9208f19f9a89d213 100644 (file)
@@ -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 -----
 
index bdbd2e6c3f85ece3bb1b3e5a07a8c0bad542955e..d15a892fbe82eb440c24dbaf2731ad749ae32e4b 100644 (file)
@@ -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:}
 
index e3328c6a166c1268d26d8c214a8ab1e104d8f75c..de9b4f8e83bef7d9266941d899bbfd77a235bc58 100644 (file)
@@ -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+):
index 462d7dcf23a8680b36e531fe22649d76f2275e94..4258a727f7bcfeebcd2081de0b5ab4da2e9d77b3 100644 (file)
@@ -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 -----
index 7212fb30e50cad6e02aee0dd58c39d036e8dc642..e9ac80c6aa8063cb84f2c93e52cbd8ddc98647ac 100644 (file)
@@ -13,7 +13,7 @@ begin routers
 
 r1:
   driver = accept
-  transport = $h_transport:
+  transport = t1
 
 
 # ----- Transports -----
index 30ee4cdd5e1ec41e9b7f0203d7dc5e63813b5a74..022c9de93699d1396548aa814363f0d54103fd3f 100644 (file)
@@ -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 <userx@test.ex> 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 <userx@test.ex> 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 <userx@test.ex> 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 <userx@test.ex> R=smart T=local_delivery_fcntl_blocking
index c33ef32664112612dae95f5361b0124f6ba7ea14..a1dfd78109783122ae9263e3e1eab23ab3214f69 100644 (file)
 1999-03-02 09:44:33 10HmbF-0005vi-00 => TESTSUITE/test-mail/userx <userx@test.ex> 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 <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbG-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx8@test.ex> 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 <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbH-0005vi-00 => TESTSUITE/test-mail/userx9 <userx9@test.ex> 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 <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbI-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx10@test.ex> 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 <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbJ-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx11@test.ex> 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 <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbK-0005vi-00 => TESTSUITE/test-mail/inbox.JUNK <userx12@test.ex> 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 <userx@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbL-0005vi-00 => TESTSUITE/test-mail/userx13 <userx13@test.ex> 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 <someone@test.ex> 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 <userx@test.ex> R=r2 T=t3
+1999-03-02 09:44:33 10HmbL-0005vi-00 => >someone@test.ex <userx13@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 <userx-suffix2@test.ex> R=r2 T=t1
-1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx-sawsuffix <userx-suffix@test.ex> R=r2 T=t1
+1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx14 <userx14-suffix2@test.ex> R=r2_14 T=t1
+1999-03-02 09:44:33 10HmbN-0005vi-00 => TESTSUITE/test-mail/userx-sawsuffix <userx14-suffix@test.ex> R=r2_14 T=t1
 1999-03-02 09:44:33 10HmbN-0005vi-00 Completed
index 05025099e0067e8035c351b45582775b3daa4c57..d741a86a99b771061f2d6399241047b5c26c3b9f 100644 (file)
@@ -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 ********
index b70b6d18265261ca1bbbb5ceb60e2eebca88fc8a..2ecb7666444fe1be8c9ace227d80e46e0cfa3d93 100644 (file)
@@ -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 ********
index 89f91b81b46c5973f6d7669df147d9ba1cd41e5a..68bedae763fb2dfbe4d3d0962c47f5f8740f0a24 100644 (file)
@@ -16,7 +16,7 @@
 1999-03-02 09:44:33 10HmbD-0005vi-00 => userx <userx@myhost.test.ex> 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 <userx@myhost.test.ex> R=localuser T=maildir_tagged_appendfile
 1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
index c4469825411d68c693957bb83ef74e3e19add8c2..cfcf6e2f788b0d520a99a19b7afdc0e80db3f11e 100644 (file)
@@ -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 <CALLER@test.ex>)
+       (envelope-from <t1@foo>)
        id 10HmaX-0005vi-00
        for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 Subject: First
-Transport: local_delivery
 Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t1@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
 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 <CALLER@test.ex>)
+       (envelope-from <t1@foo>)
        id 10HmaY-0005vi-00
        for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 Subject: Second
-Transport: local_delivery
 Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t1@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
 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 <CALLER@test.ex>)
+       (envelope-from <t2@foo>)
        id 10HmaZ-0005vi-00
        for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 Subject: Third
-Transport: local_delivery_fcntl
 Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t2@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
 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 <CALLER@test.ex>)
+       (envelope-from <t3@foo>)
        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: <E10HmbA-0005vi-00@the.local.host.name>
-From: CALLER_NAME <CALLER@test.ex>
+From: CALLER_NAME <t3@foo>
+Sender: CALLER_NAME <CALLER@test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
 
+local_delivery_fcntl_blocking
 Fourth message
 
index 088f808c73d6ccab0f040a7231701242283954bc..1ccfe989a1e9b0f7509cfcbe52f26a53a6f5ae99 100644 (file)
@@ -1,80 +1,80 @@
 From CALLER@test.ex Tue Mar 02 09:44:33 1999
 Return-path: <CALLER@test.ex>
-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 <CALLER@test.ex>)
        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: <E10HmbG-0005vi-00@mail.test.ex>
 From: CALLER_NAME <CALLER@test.ex>
 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: <CALLER@test.ex>
-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 <CALLER@test.ex>)
        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: <E10HmbI-0005vi-00@mail.test.ex>
 From: CALLER_NAME <CALLER@test.ex>
 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: <CALLER@test.ex>
-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 <CALLER@test.ex>)
        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: <E10HmbJ-0005vi-00@mail.test.ex>
 From: CALLER_NAME <CALLER@test.ex>
 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: <CALLER@test.ex>
-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 <CALLER@test.ex>)
        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: <E10HmbK-0005vi-00@mail.test.ex>
 From: CALLER_NAME <CALLER@test.ex>
 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
 
index a3ba52b88efbc41b9d03fcb7c0bd1ad818174334..8939761ea780e7b58abf988fffb254babe457b86 100644 (file)
@@ -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: <E10HmbL-0005vi-00@mail.test.ex>
index 8c3402bd77c122324e7a106ad28ba58cdf40d6da..b47f439e71e764ed5ffafad998174528f015d8a4 100644 (file)
@@ -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: <CALLER@test.ex>
-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 <CALLER@test.ex>)
-       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: <E10HmbH-0005vi-00@mail.test.ex>
-From: CALLER_NAME <CALLER@test.ex>
-Date: Tue, 2 Mar 1999 09:44:33 +0000
-
-Test 9
-
-From someone@test.ex Tue Mar 02 09:44:33 1999
-Return-path: <someone@test.ex>
-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 <someone@test.ex>)
-       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: <E10HmbL-0005vi-00@mail.test.ex>
-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: <someone@test.ex>
-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 <someone@test.ex>)
-       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: <E10HmbN-0005vi-00@mail.test.ex>
-From: someone@test.ex
-Date: Tue, 2 Mar 1999 09:44:33 +0000
-
-Test 14
-
index ef2e8b5e35afef158b65a26e876cd4c263e5325e..c2a457a7e336f087d62dec165bd8d20e4cddd8b6 100644 (file)
@@ -1,19 +1,18 @@
 From someone@test.ex Tue Mar 02 09:44:33 1999
 Return-path: <someone@test.ex>
-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 <someone@test.ex>)
        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: <E10HmbN-0005vi-00@mail.test.ex>
 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 (file)
index 0000000..7a70d93
--- /dev/null
@@ -0,0 +1,17 @@
+From someone@test.ex Tue Mar 02 09:44:33 1999
+Return-path: <someone@test.ex>
+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 <someone@test.ex>)
+       id 10HmbL-0005vi-00
+       for userx13@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+To: userx13@test.ex
+Message-Id: <E10HmbL-0005vi-00@mail.test.ex>
+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 (file)
index 0000000..0c542a7
--- /dev/null
@@ -0,0 +1,18 @@
+From someone@test.ex Tue Mar 02 09:44:33 1999
+Return-path: <someone@test.ex>
+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 <someone@test.ex>)
+       id 10HmbN-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbN-0005vi-00@mail.test.ex>
+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 (file)
index 0000000..1cfb156
--- /dev/null
@@ -0,0 +1,20 @@
+From CALLER@test.ex Tue Mar 02 09:44:33 1999
+Return-path: <CALLER@test.ex>
+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 <CALLER@test.ex>)
+       id 10HmbH-0005vi-00
+       for userx9@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+X-Sieve: 99
+Message-Id: <E10HmbH-0005vi-00@mail.test.ex>
+From: CALLER_NAME <CALLER@test.ex>
+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
+
index 51712138907c50323746c4a96adae6199113463f..efa7079cabc94815ecc2d6ee70ce8fe7285265d9 100644 (file)
@@ -2,7 +2,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        id 10HmaY-0005vi-00
        for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_
 Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index 1b5b064983009a5cfd56427bd7a47d88a6dd1070..e2afa21cd032dd5a584f539d39c957081f318900 100644 (file)
@@ -2,7 +2,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        id 10HmaZ-0005vi-00
        for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_
 Message-Id: <E10HmaZ-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index 7ea68ff5a6282b7c5962a9be24ab812705906d38..72250564130ce14210f54edeac4471c30ca9f1ca 100644 (file)
@@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        id 10HmbB-0005vi-00
        for userx@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-maildir:maildir_tagged_
-tag:Ssss
 Message-Id: <E10HmbB-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index ed38791527e583c3fd239c20a20aac91a710ce18..d7b4768aa683b06ca4fa0fbf73250e67a5d52f11 100644 (file)
@@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        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: <E10HmbC-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index 054bf9048a235ebcfe8e34ade0d15b36f5819189..41aedbcd8513bae7977b03fef60f2b0b7faa24a7 100644 (file)
@@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        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: <E10HmbD-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index df71e1788c8fa4995deb8e0cab90ab86650c0e25..a7210d10f65feae04f4528d509765ab59af62abe 100644 (file)
@@ -3,13 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        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: <E10HmbE-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index be53589ee747c95ffb393760d896c9696ebab36a..f33b5f27fe27ef283a707c28c12437fbdcc31e05 100644 (file)
@@ -3,8 +3,6 @@ Received: from CALLER by myhost.test.ex with local (Exim x.yz)
        (envelope-from <CALLER@myhost.test.ex>)
        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: <E10HmbF-0005vi-00@myhost.test.ex>
 From: CALLER_NAME <CALLER@myhost.test.ex>
 Date: Tue, 2 Mar 1999 09:44:33 +0000
index ca265a69df5c6ea505aa2e6310f75509bde99390..e50b21cf12c6de5b27bf12946e4f80471c7f9f74 100644 (file)
@@ -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
index 89086b0bdfd561f50a0d294a9637d9565beedd85..d85ecc4954bb7f4a686c1b1391dfdd74646eac26 100644 (file)
@@ -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 ""
index 2427585abe78a4406fa6ac2591eb66b834c408f7..078251623b6e57fc50d4e3e2cc674e91f5cc2919 100644 (file)
@@ -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
index fe1f4efc992d497a8a2a81079f5d174b8c1aae82..a7b265b96c5d25b076be106484ffa3d4b60cae70 100755 (executable)
@@ -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/<dynamic port>/;
index 9334e04487da74d8083f6a01663e31edc8ff2aa4..8e654ac95fa4fb2b003e405e7fe6e4ef7ad516db 100644 (file)
@@ -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}}}}
index 56941941f4e86b62f7a13fd95bbf54f68a6837e6..78e04dd4b5e35843e3f4cca9ce851f8db745d29f 100644 (file)
@@ -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
index d554c80d0f71a57b26b3d253d960992acb9348a3..24c1b12653d39a546539acc32c40cd26d04b2619 100644 (file)
@@ -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; 
index a48e8b4fec7172f0757f1fbe5001d7dbeaf73722..04f19593ed500ed5f2d39829d450cbd812e7bad3 100644 (file)
@@ -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
index 646b1b4a3b320653032bbc493bca32c809e623ec..1be6923b010756d85c4beeeb50098e958140c566 100644 (file)
@@ -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
index 5b763fcd3e8b70006624229f1bf65fc9a4a9df6c..a1d1ce3586842c397479203152d2795d0e8e0080 100644 (file)
@@ -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 <test-data
+exim -DVALUE=maildir_appendfile -odi userx <test-data
 ****
-exim -odi userx <test-data
+exim -DVALUE=maildir_appendfile -odi userx <test-data
 ****
-exim -odi userx
-maildir:maildir_tagged_
-tag:S370
+exim -DVALUE=maildir_tagged_appendfile -DOPT=S370 -odi userx
 This is a test message
 ****
 sleep 1
-exim -odi userx
-maildir:maildir_tagged_
-tag:,S=370
+exim -DVALUE=maildir_tagged_appendfile -DOPT=,S=370 -odi userx
 This is a test message
 ****
 sleep 1
-exim -odi userx
-maildir:maildir_tagged_
-tag:${if eq{0}{1}{rhubarb}fail}
+exim -DVALUE=maildir_tagged_appendfile -DOPT='${if eq{0}{1}{rhubarb}fail}' -odi userx
 This is a test message
 ****
 sleep 1
 # deliberate syntax fail
-exim -odi userx
-maildir:maildir_tagged_
-tag:${if eq{0}{1}{rhubarb}
+exim -DVALUE=maildir_tagged_appendfile -DOPT='${if eq{0}{1}{rhubarb}' -odi userx
 This is a test message
 ****
 sleep 1
 # overlongname
-exim -odi userx
-maildir:maildir_tagged_
-tag:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+exim -DVALUE=maildir_tagged_appendfile -DOPT=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -odi userx
 This is a test message
 ****
 sleep 1
-exim -odi userx
-maildir:maildir_taggedX_
-tag:,S=10694953:2,S
+exim -DVALUE=maildir_taggedX_appendfile -DOPT=,S=10694953:2,S -odi userx
 ****
-exim -d-all+transport -odi userx
-maildir:maildir_taggedX_
-tag:,S=412:2,S
+exim -d-all+transport -DVALUE=maildir_taggedX_appendfile -DOPT=,S=412:2,S -odi userx
 ****
index 3d7ace50fa8d09dd6418c0554be5160b1a202730..ce894401b98963239f5fb888031e660e3ac21972 100644 (file)
@@ -2,7 +2,6 @@
 need_ipv4
 #
 exim -odi userx
-transport: t1
 Test 1
 ****
 no_msglog_check
index 41bf8840bb154be610e651bb42ca9d6972ad83a9..8f85e71460704dbc1d655aaedf4a13e25ef07b48 100644 (file)
@@ -5,6 +5,10 @@ dropping to exim gid; retaining priv uid
  ╭considering: primary_hostname: $primary_hostname
  ├──expanding: primary_hostname: $primary_hostname
  ╰─────result: primary_hostname: myhost.test.ex
+ ╭considering: sender_address: $sender_address
+ ├──expanding: sender_address: $sender_address
+ ╰─────result: sender_address: sndr@dom
+            ╰──(tainted)
  ╭considering: match:  ${if match{abcd}{\N^([ab]+)(\w+)$\N}{$2$1}fail}
   ╭considering: abcd}{\N^([ab]+)(\w+)$\N}{$2$1}fail}
   ├──expanding: abcd
@@ -79,7 +83,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 ....
@@ -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
index b7588756b89cc4e1a23bf51ce78e3dcc31b77056..cb371d2987db50d07ca3a972c4937473aafbf461 100644 (file)
@@ -1247,7 +1247,7 @@ LOG: H=[30.30.30.30] F=<a@13.12.11.V4NET.rbl> rejected RCPT <x@y>: 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"
index af8bf129deb91ef14f1c14046c4616f658f21d5b..5136a8f2bb402a40fb6a7c1e0194f6ba620d43f1 100644 (file)
@@ -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
index 4100ebe9ede8ef37120e341a4132a8e23e3ac87d..45c7bd0f488723746ce48f5d61cd94d6a57fee12 100644 (file)
@@ -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
index 86ef5840d94c5e07a5355d10694c760acf536fb7..56c65f6b454f91b2c71e7f58512db5ce2ab67616 100644 (file)
@@ -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"
index 34c2c1ee54dcc0ebf6db48aab4fcbe99889bb5e8..93894e49897aeae645fdbe02fcfc6f983284c8b3 100644 (file)
@@ -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
index f69c13e7ff91915baac8f0369073f523ce497a06..20522440756213ee03279201dce24f226ec45c9f 100644 (file)
@@ -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
index 4706f8780fe6958b71d941478b4e552f19b55c9e..36bcfb48061f109a65a6d10223f102c549eef4a8 100644 (file)
@@ -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
index e2ad7d4798eb0eeb0f703e43f66dd90340838df5..de4b4f2b61a4599fe06f37e0cfd4305b93d82ed5 100644 (file)
@@ -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
index 64e47dff7b053225790f88334bc8d9083e3e5fbb..9b74af458badabdd43f840d4632af0912d2917a9 100644 (file)
@@ -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
index 81fb83de1f1abac3f413c689e71d47b34b93500f..1b08f726b8a389cd26e94abcbe6c40d62bc42462 100644 (file)
@@ -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
index 823ff0962e49294e72f8ce44413ef89b8f9c6bc6..b2483cf21378ebb7b65151eb7a760d185f4c5aca 100644 (file)
@@ -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
index 007df5d542536a6c3ae74297fa1cfbbd421c50cc..98f92e0a360f884b5ea76ac67647c75fdd98185e 100644 (file)
@@ -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
index 74ab95932ea4d748b37545e2dab92a8f38fe897b..e48fb7e76d8f2e214398ebd757640aab39865e6f 100644 (file)
@@ -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
index c3777c222c04e97f8acdbd7a635acd3b77cd9329..26a225fe2d27dbec6196585bfa9d698c8224fd85 100644 (file)
@@ -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
index 7be8b5be7dc37ba311562f47bf052b03cf62c2cc..c653fc309344736e4cf70a72bf1d4dd0afdfbd17 100644 (file)
@@ -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
index 792e5cee9ce963ce027fa3552038b9a4957a5e4c..24fd7b7a055a01ab9a182750ce7706c91a00e92d 100644 (file)
@@ -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
index b8f4b079bb4da1e84698230d1645c19e1173d4fe..d2444f5aded262ee971e3e64b24f66ff06739567 100644 (file)
@@ -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=<x@y> 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=<x@y> 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
index a9be60fb4e96fad50b6c6fef230386efbc1e47e2..e11ca71b3c0f5177026f7dcfbe78e0722d7e0069 100644 (file)
@@ -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:
index 34d20785f91b87ec11a914e1300b6c2afcbfd758..81ffa5e85310623f55ea1db0f8650a22deba3b56 100644 (file)
@@ -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
index 2dc717282e73a9fc2fcf98917f60d4392d7fca4b..9a3b1329722bcc53656af169b264e5d58c39842d 100644 (file)
@@ -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
index daa7b50f53e68d5406639f050ad96816c03fedb0..1d81d845ca63bade52b4c52e371cb14a51e541e2 100644 (file)
@@ -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 <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 <usery@test.ex> 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 <userz@test.ex> transport=t2
   uid=CALLER_UID gid=CALLER_GID pid=pppp
index e0fc74a9469dbbc0b99aa0679832b0842825b025..29a55588187d748b2e7fdc9deb55dbacef703ed3 100644 (file)
@@ -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
index 652e65335dbfa584bd8bf12f1a5712de6aee4135..6ec697bc9a639f7cd5058db850802844e518a23a 100644 (file)
@@ -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
index 9918563ad10856f6f5f01a958d81664f0cc68ae9..dda5191a2b0e733635a93079ca00117ea3217a8a 100644 (file)
@@ -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:
index 729f872443541ec5f655bad18ed8207904ffe74e..512ed1b5843f5c4f59d5a047cb3a782353c99395 100644 (file)
@@ -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
index c90d9b1b22418a5b4251513488e6c96506e02d05..cce0311012460e32b186a8da6707fde4936bc880 100644 (file)
@@ -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 >>>>>>>>>>>>>>>>
index c83c508f5bb00e9f4849c10fe062a16e3731dda5..80599193e32bd001bb2b8a85198d92f6d8ceddf1 100644 (file)
@@ -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 ********
index 2bca5ea6acd861358f0612eb1485904682cb1e5f..2b96d890638ee766b79559ca6dca55cace4ccffc 100644 (file)
@@ -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
index 465f98acffc3b433eaf28d735eca9589a2f99501..c3e489adb5555f8b9db8c8f52ff86ee04eea28cb 100644 (file)
@@ -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
index 7c2109d096506ba12b99044ecbdad908ccddce1e..1f927a4855c6d3801a71d13a0f5b3be6601a6f50 100644 (file)
@@ -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
index 7978a0266d400539a054422ca418ad8a8c79ae4b..a554fd953e6a010a39fff0df54d0a6414f30c55a 100644 (file)
@@ -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 <CALLER@myhost.test.ex>)
        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 <CALLER@myhost.test.ex>)
        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 <CALLER@myhost.test.ex>)
        id 10HmbB-0005vi-00
        for usery@domain.com
+            ╰──(tainted)
 ----------- start cutthrough headers send -----------
 ----------- done cutthrough headers send ------------
  ╭considering: ${tod_full}
index 9b51bff2b48bf125c8fa877aa95f8130a62df12d..9aefc2431b68eb7376694bd2b6eb110fe793f3ee 100644 (file)
@@ -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 <CALLER@myhost.test.ex>)
        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 <CALLER@myhost.test.ex>)
        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 <CALLER@myhost.test.ex>)
        id 10HmbB-0005vi-00
        for usery@domain.com
+            ╰──(tainted)
 ----------- start cutthrough headers send -----------
 ----------- done cutthrough headers send ------------
  ╭considering: ${tod_full}
index c1f9d379969a502490229819185db1d39d1f0f2e..6319fdc655e055cd69d2d0d333504780d8e09303 100644 (file)
@@ -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
index 60e2c62d2a9f1a232c99adf53d3a19c92bde3474..09b5df4e90ab91394bd7dac0b155546e0154007d 100644 (file)
@@ -145,7 +145,7 @@ EXIMUSER EXIM_UID EXIM_GID
 <notsubmit@y>
 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
 <a@y>
 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
 <notsubmit@y>
 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
 <a@y>
 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
 <a@y>
 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
 <a@y>
 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
index 8e20252e24bfbb4d9cfb4a28db466c2429ffc199..0889dd7957dc7fe14dfc2431c6b2afa9830b450b 100644 (file)
@@ -156,14 +156,14 @@ EXIMUSER EXIM_UID EXIM_GID
 <username@myhost.test.ex>
 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