From 4191cb150300d310ab5fa22ce2cfb02b6f6051b0 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 3 Mar 2022 22:23:42 +0000 Subject: Check query strings of query-style lookups for quoting. Bug 2850 --- doc/doc-docbook/spec.xfpt | 23 +- doc/doc-txt/NewStuff | 3 + src/OS/unsupported/os.c-IRIX | 2 +- src/OS/unsupported/os.c-IRIX6 | 2 +- src/OS/unsupported/os.c-IRIX632 | 2 +- src/OS/unsupported/os.c-IRIX65 | 2 +- src/exim_monitor/em_main.c | 2 +- src/exim_monitor/em_queue.c | 4 +- src/src/acl.c | 58 ++- src/src/arc.c | 16 +- src/src/auths/pwcheck.c | 2 +- src/src/auths/xtextdecode.c | 2 +- src/src/auths/xtextencode.c | 2 +- src/src/base64.c | 15 +- src/src/bmi_spam.c | 10 +- src/src/child.c | 2 +- src/src/daemon.c | 12 +- src/src/dbfn.c | 8 +- src/src/deliver.c | 44 ++- src/src/dkim.c | 2 +- src/src/dmarc.c | 2 +- src/src/dns.c | 10 +- src/src/dnsbl.c | 6 +- src/src/drtables.c | 2 +- src/src/exim.c | 142 +++++--- src/src/exim_dbmbuild.c | 2 +- src/src/exim_dbutil.c | 10 +- src/src/expand.c | 238 ++++++------ src/src/filter.c | 33 +- src/src/filtertest.c | 4 +- src/src/functions.h | 67 ++-- src/src/globals.c | 3 + src/src/globals.h | 3 + src/src/hash.c | 10 +- src/src/header.c | 4 +- src/src/host.c | 33 +- src/src/local_scan.h | 18 +- src/src/log.c | 22 +- src/src/lookupapi.h | 11 +- src/src/lookups/cdb.c | 10 +- src/src/lookups/dbmdb.c | 2 +- src/src/lookups/ibase.c | 89 ++--- src/src/lookups/json.c | 2 +- src/src/lookups/ldap.c | 27 +- src/src/lookups/lmdb.c | 2 +- src/src/lookups/mysql.c | 19 +- src/src/lookups/nisplus.c | 15 +- src/src/lookups/oracle.c | 30 +- src/src/lookups/pgsql.c | 8 +- src/src/lookups/readsock.c | 2 +- src/src/lookups/redis.c | 18 +- src/src/lookups/sqlite.c | 19 +- src/src/malware.c | 8 +- src/src/match.c | 14 +- src/src/mime.c | 2 +- src/src/moan.c | 2 +- src/src/os.c | 8 +- src/src/parse.c | 27 +- src/src/pdkim/pdkim.c | 24 +- src/src/pdkim/signing.c | 6 +- src/src/queue.c | 8 +- src/src/rda.c | 10 +- src/src/readconf.c | 32 +- src/src/receive.c | 27 +- src/src/regex.c | 6 +- src/src/retry.c | 6 +- src/src/rewrite.c | 4 +- src/src/rfc2047.c | 4 +- src/src/route.c | 27 +- src/src/routers/dnslookup.c | 2 +- src/src/routers/ipliteral.c | 2 +- src/src/routers/iplookup.c | 4 +- src/src/routers/manualroute.c | 2 +- src/src/routers/redirect.c | 2 +- src/src/routers/rf_change_domain.c | 6 +- src/src/routers/rf_get_munge_headers.c | 4 +- src/src/search.c | 53 ++- src/src/sieve.c | 30 +- src/src/smtp_in.c | 15 +- src/src/spam.c | 2 +- src/src/spool_in.c | 97 +++-- src/src/spool_out.c | 10 +- src/src/store.c | 643 ++++++++++++++++++++++++--------- src/src/store.h | 33 +- src/src/string.c | 49 +-- src/src/structs.h | 12 + src/src/tls-gnu.c | 14 +- src/src/tls-openssl.c | 18 +- src/src/tls.c | 2 +- src/src/tlscert-gnu.c | 18 +- src/src/tlscert-openssl.c | 6 +- src/src/transport.c | 46 ++- src/src/transports/appendfile.c | 2 +- src/src/transports/autoreply.c | 2 +- src/src/transports/pipe.c | 2 +- src/src/transports/smtp.c | 11 +- src/src/tree.c | 8 +- src/src/utf8.c | 4 +- src/src/verify.c | 29 +- test/confs/2610 | 35 +- test/confs/2620 | 21 +- test/log/2610 | 2 + test/paniclog/2610 | 1 + test/runtest | 2 +- test/scripts/2610-MySQL/2610 | 2 + test/stderr/2200 | 2 + test/stderr/2201 | 4 + test/stderr/2202 | 1 + test/stderr/2610 | 85 ++++- test/stderr/2620 | 103 ++++-- 110 files changed, 1701 insertions(+), 976 deletions(-) create mode 100644 test/paniclog/2610 diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 1bc63bff3..bfadeb10a 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -6839,6 +6839,12 @@ version of the lookup key. The &'query-style'& type accepts a generalized database query. No particular key value is assumed by Exim for query-style lookups. You can use whichever Exim variables you need to construct the database query. +.cindex "tainted data" "quoting for lookups" +.new +If tainted data is used in the query then it should be quuted by +using the &*${quote_*&<&'lookup-type'&>&*:*&<&'string'&>&*}*& expansion operator +appropriate for the lookup. +.wen .endlist The code for each lookup type is in a separate source file that is included in @@ -40994,8 +41000,18 @@ was received, in the conventional Unix form &-- the number of seconds since the start of the epoch. The second number is a count of the number of messages warning of delayed delivery that have been sent to the sender. -There follow a number of lines starting with a hyphen. These can appear in any -order, and are omitted when not relevant: +.new +There follow a number of lines starting with a hyphen. +These contain variables, can appear in any +order, and are omitted when not relevant. + +If there is a second hyphen after the first, +the corresponding data is tainted. +If there is a value in parentheses, the data is quoted for a lookup. + +The following word specifies a variable, +and the remainder of the item depends on the variable. +.wen .vlist .vitem "&%-acl%&&~<&'number'&>&~<&'length'&>" @@ -41151,9 +41167,6 @@ was received from the client, this records the Distinguished Name from that certificate. .endlist -Any of the above may have an extra hyphen prepended, to indicate the the -corresponding data is untrusted. - Following the options there is a list of those addresses to which the message is not to be delivered. This set of addresses is initialized from the command line when the &%-t%& option is used and &%extract_addresses_remove_arguments%& diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 394eb144d..730508adc 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -19,6 +19,9 @@ Version 4.96 5. The ACL "debug" control gains options "stop", "pretrigger" and "trigger". + 6. Query-style lookups are now checked for quoting, if the query string is + built using untrusted data ("tainted"). For now lack of quoting is merely + logged; a future release will upgrade this to an error. Version 4.95 ------------ diff --git a/src/OS/unsupported/os.c-IRIX b/src/OS/unsupported/os.c-IRIX index c1539cb50..d7a8b4f7a 100644 --- a/src/OS/unsupported/os.c-IRIX +++ b/src/OS/unsupported/os.c-IRIX @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed, FALSE); +buf = store_get(needed, GET_UNTAINTED); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-IRIX6 b/src/OS/unsupported/os.c-IRIX6 index c1539cb50..d7a8b4f7a 100644 --- a/src/OS/unsupported/os.c-IRIX6 +++ b/src/OS/unsupported/os.c-IRIX6 @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed, FALSE); +buf = store_get(needed, GET_UNTAINTED); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-IRIX632 b/src/OS/unsupported/os.c-IRIX632 index c1539cb50..d7a8b4f7a 100644 --- a/src/OS/unsupported/os.c-IRIX632 +++ b/src/OS/unsupported/os.c-IRIX632 @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed, FALSE); +buf = store_get(needed, GET_UNTAINTED); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/OS/unsupported/os.c-IRIX65 b/src/OS/unsupported/os.c-IRIX65 index c1539cb50..d7a8b4f7a 100644 --- a/src/OS/unsupported/os.c-IRIX65 +++ b/src/OS/unsupported/os.c-IRIX65 @@ -59,7 +59,7 @@ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "iflist-sysctl-estimate failed: %s", strerror(errno)); -buf = store_get(needed, FALSE); +buf = store_get(needed, GET_UNTAINTED); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) log_write(0, LOG_PANIC_DIE, "sysctl of ifnet list failed: %s", diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 38c72307a..af1323064 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -594,7 +594,7 @@ constructing file names and things. This call will initialize the store_get() function. */ store_init(); -big_buffer = store_get(big_buffer_size, FALSE); +big_buffer = store_get(big_buffer_size, GET_UNTAINTED); /* Set up the version string and date and output them */ diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index 2146fcb8c..276f3379b 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -138,8 +138,8 @@ tree_node * 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), FALSE); +root = name[0] == 'c' ? &acl_var_c : &acl_var_m; +node = store_get(sizeof(tree_node) + Ustrlen(name), GET_UNTAINTED); Ustrcpy(node->name, name); node->data.ptr = NULL; (void)tree_insertnode(root, node); diff --git a/src/src/acl.c b/src/src/acl.c index 46615ce24..aa8699a8a 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -642,6 +642,8 @@ static uschar *ratelimit_option_string[] = { static int acl_check_wargs(int, address_item *, const uschar *, uschar **, uschar **); +static acl_block * acl_current = NULL; + /************************************************* * Find control in list * @@ -806,7 +808,7 @@ while ((s = (*func)())) *error = string_sprintf("malformed ACL line \"%s\"", saveline); return NULL; } - this = store_get(sizeof(acl_block), FALSE); + this = store_get(sizeof(acl_block), GET_UNTAINTED); *lastp = this; lastp = &(this->next); this->next = NULL; @@ -853,7 +855,7 @@ while ((s = (*func)())) return NULL; } - cond = store_get(sizeof(acl_condition_block), FALSE); + cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED); cond->next = NULL; cond->type = c; cond->u.negated = negated; @@ -1052,7 +1054,7 @@ for (p = q; *p; p = q) { /* The header_line struct itself is not tainted, though it points to possibly tainted data. */ - header_line * h = store_get(sizeof(header_line), FALSE); + header_line * h = store_get(sizeof(header_line), GET_UNTAINTED); h->text = hdr; h->next = NULL; h->type = newtype; @@ -1341,7 +1343,7 @@ dns_scan dnss; dns_record *rr; int rc, type, yield; #define TARGET_SIZE 256 -uschar * target = store_get(TARGET_SIZE, TRUE); +uschar * target = store_get(TARGET_SIZE, GET_TAINTED); /* Work out the domain we are using for the CSA lookup. The default is the client's HELO domain. If the client has not said HELO, use its IP address @@ -1383,7 +1385,7 @@ we return from this function. */ if ((t = tree_search(csa_cache, domain))) return t->data.val; -t = store_get_perm(sizeof(tree_node) + Ustrlen(domain), is_tainted(domain)); +t = store_get_perm(sizeof(tree_node) + Ustrlen(domain), domain); Ustrcpy(t->name, domain); (void)tree_insertnode(&csa_cache, t); @@ -2585,7 +2587,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, FALSE); /* not tainted */ + dbdb = store_get(dbdb_size, GET_UNTAINTED); } else { @@ -2599,7 +2601,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, FALSE); /* not tainted */ + dbdb = store_get(dbdb_size, GET_UNTAINTED); dbdb->bloom_epoch = tv.tv_sec; dbdb->bloom_size = sizeof(dbdb->bloom) + extra; memset(dbdb->bloom, 0, dbdb->bloom_size); @@ -2819,7 +2821,7 @@ dbfn_close(dbm); /* 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), is_tainted(key)); +t = store_get(sizeof(tree_node) + Ustrlen(key), key); t->data.ptr = dbd; Ustrcpy(t->name, key); (void)tree_insertnode(anchor, t); @@ -3029,7 +3031,7 @@ if (*portend != '\0') } /* Make a single-item host list. */ -h = store_get(sizeof(host_item), FALSE); +h = store_get(sizeof(host_item), GET_UNTAINTED); memset(h, 0, sizeof(host_item)); h->name = hostname; h->port = portnum; @@ -4201,6 +4203,18 @@ for(;;) +/************************************************/ +/* For error messages, a string describing the config location +associated with current processing. NULL if not in an ACL. */ + +uschar * +acl_current_verb(void) +{ +if (acl_current) return string_sprintf(" (ACL %s, %s %d)", + verbs[acl_current->verb], acl_current->srcfile, acl_current->srcline); +return NULL; +} + /************************************************* * Check access using an ACL * *************************************************/ @@ -4321,7 +4335,7 @@ if (Ustrchr(ss, ' ') == NULL) } /* 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 = store_get(statbuf.st_size + 1, ss); acl_text_end = acl_text + statbuf.st_size + 1; if (read(fd, acl_text, statbuf.st_size) != statbuf.st_size) @@ -4351,7 +4365,7 @@ if (!acl) if (!acl && *log_msgptr) return ERROR; if (fd >= 0) { - tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss), is_tainted(ss)); + tree_node * t = store_get_perm(sizeof(tree_node) + Ustrlen(ss), ss); Ustrcpy(t->name, ss); t->data.ptr = acl; (void)tree_insertnode(&acl_anchor, t); @@ -4360,7 +4374,7 @@ if (!acl) /* Now we have an ACL to use. It's possible it may be NULL. */ -while (acl) +while ((acl_current = acl)) { int cond; int basic_errno = 0; @@ -4507,8 +4521,8 @@ while (acl) else if (cond == DEFER && LOGGING(acl_warn_skipped)) log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: " "condition test deferred%s%s", host_and_ident(TRUE), - (*log_msgptr == NULL)? US"" : US": ", - (*log_msgptr == NULL)? US"" : *log_msgptr); + *log_msgptr ? US": " : US"", + *log_msgptr ? *log_msgptr : US""); *log_msgptr = *user_msgptr = NULL; /* In case implicit DENY follows */ break; @@ -4833,7 +4847,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), is_tainted(name)); + node = store_get(sizeof(tree_node) + Ustrlen(name), name); Ustrcpy(node->name, name); (void)tree_insertnode(root, node); } @@ -4864,11 +4878,17 @@ Returns: nothing */ void -acl_var_write(uschar *name, uschar *value, void *ctx) +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); +FILE * f = (FILE *)ctx; +putc('-', f); +if (is_tainted(value)) + { + int q = quoter_for_address(value); + putc('-', f); + if (is_real_quoter(q)) fprintf(f, "(%s)", lookup_list[q]->name); + } +fprintf(f, "acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value); } #endif /* !MACRO_PREDEF */ diff --git a/src/src/arc.c b/src/src/arc.c index e0ee19950..a9523890d 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -142,7 +142,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), FALSE); +*pas = as = store_get(sizeof(arc_set), GET_UNTAINTED); memset(as, 0, sizeof(arc_set)); as->next = next; as->prev = prev; @@ -200,7 +200,7 @@ al->complete = h; if (!instance_only) { - al->rawsig_no_b_val.data = store_get(h->slen + 1, TRUE); /* tainted */ + al->rawsig_no_b_val.data = store_get(h->slen + 1, GET_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; @@ -386,7 +386,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), FALSE), ** alp; +arc_line * al = store_get(sizeof(arc_line), GET_UNTAINTED), ** alp; uschar * e; memset(al, 0, sizeof(arc_line)); @@ -497,7 +497,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), FALSE); + r = store_get(sizeof(hdr_rlist), GET_UNTAINTED); r->prev = rprev; r->used = FALSE; r->h = h; @@ -1103,7 +1103,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), FALSE); +hdr_rlist * r = store_get(sizeof(hdr_rlist) + sizeof(header_line), GET_UNTAINTED); header_line * h = r->h = (header_line *)(r+1); r->prev = list; @@ -1195,7 +1195,7 @@ arc_sign_append_aar(gstring * g, arc_ctx * ctx, { int aar_off = gstring_length(g); arc_set * as = - store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), FALSE); + store_get(sizeof(arc_set) + sizeof(arc_line) + sizeof(header_line), GET_UNTAINTED); arc_line * al = (arc_line *)(as+1); header_line * h = (header_line *)(al+1); @@ -1305,7 +1305,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), FALSE); +arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED); header_line * h = (header_line *)(al+1); /* debug_printf("%s\n", __FUNCTION__); */ @@ -1420,7 +1420,7 @@ arc_sign_prepend_as(gstring * arcset_interim, arc_ctx * ctx, { gstring * arcset; uschar * status = arc_ar_cv_status(ar); -arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), FALSE); +arc_line * al = store_get(sizeof(header_line) + sizeof(arc_line), GET_UNTAINTED); header_line * h = (header_line *)(al+1); uschar * badline_str; diff --git a/src/src/auths/pwcheck.c b/src/src/auths/pwcheck.c index 8e51f1757..f2188bb6a 100644 --- a/src/src/auths/pwcheck.c +++ b/src/src/auths/pwcheck.c @@ -291,7 +291,7 @@ static int read_string(int fd, uschar **retval) { return -1; } else { /* Assume the file is trusted, so no tainting */ - *retval = store_get(count + 1, FALSE); + *retval = store_get(count + 1, GET_UNTAINTED); rc = (retry_read(fd, *retval, count) < (int) count); (*retval)[count] = '\0'; return count; diff --git a/src/src/auths/xtextdecode.c b/src/src/auths/xtextdecode.c index 95cf5dab2..3940767fa 100644 --- a/src/src/auths/xtextdecode.c +++ b/src/src/auths/xtextdecode.c @@ -33,7 +33,7 @@ int auth_xtextdecode(uschar *code, uschar **ptr) { register int x; -uschar *result = store_get(Ustrlen(code) + 1, is_tainted(code)); +uschar * result = store_get(Ustrlen(code) + 1, code); *ptr = result; while ((x = (*code++)) != 0) diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index 30ff8f11d..46f9c021c 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -40,7 +40,7 @@ in order to get the right amount of store. */ while (c -- > 0) count += ((x = *p++) < 33 || x > 127 || x == '+' || x == '=')? 3 : 1; -pp = code = store_get(count, is_tainted(clear)); +pp = code = store_get(count, clear); p = US clear; c = len; diff --git a/src/src/base64.c b/src/src/base64.c index 29f9c77b0..f7ed0b5b5 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -157,10 +157,10 @@ b64decode(const uschar *code, uschar **ptr) int x, y; uschar *result; -{ + { int l = Ustrlen(code); - *ptr = result = store_get(1 + l/4 * 3 + l%4, is_tainted(code)); -} + *ptr = result = store_get(1 + l/4 * 3 + l%4, code); + } /* Each cycle of the loop handles a quantum of 4 input bytes. For the last quantum this may decode to 1, 2, or 3 output bytes. */ @@ -234,6 +234,7 @@ would probably run more slowly. Arguments: clear points to the clear text bytes len the number of bytes to encode + proto_mem taint indicator Returns: a pointer to the zero-terminated base 64 string, which is in working store @@ -243,10 +244,10 @@ static uschar *enc64table = US"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; uschar * -b64encode_taint(const uschar * clear, int len, BOOL tainted) +b64encode_taint(const uschar * clear, int len, const void * proto_mem) { -uschar *code = store_get(4*((len+2)/3) + 1, tainted); -uschar *p = code; +uschar * code = store_get(4*((len+2)/3) + 1, proto_mem); +uschar * p = code; while (len-- >0) { @@ -287,7 +288,7 @@ return code; uschar * b64encode(const uschar * clear, int len) { -return b64encode_taint(clear, len, is_tainted(clear)); +return b64encode_taint(clear, len, clear); } diff --git a/src/src/bmi_spam.c b/src/src/bmi_spam.c index 6972bc3a7..334022b00 100644 --- a/src/src/bmi_spam.c +++ b/src/src/bmi_spam.c @@ -193,16 +193,16 @@ uschar *bmi_process_message(header_line *header_list, int data_fd) { /* 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 = store_get(1, GET_TAINTED); *verdicts = '\0'; for ( err = bmiAccessFirstVerdict(message, &verdict); - verdict != NULL; + verdict; err = bmiAccessNextVerdict(message, verdict, &verdict) ) { char *verdict_str; err = bmiCreateStrFromVerdict(verdict,&verdict_str); - if (!store_extend(verdicts, TRUE, + if (!store_extend(verdicts, Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) { /* can't allocate more store */ return NULL; @@ -302,7 +302,7 @@ uschar *bmi_get_alt_location(uschar *base64_verdict) { } else { /* deliver to alternate location */ - rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, TRUE); + rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, GET_TAINTED); Ustrcpy(rc, bmiVerdictAccessDestination(verdict)); rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0'; }; @@ -327,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, TRUE); + verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1, GET_TAINTED); /* loop through verdicts */ verdict_ptr = bmi_verdicts; diff --git a/src/src/child.c b/src/src/child.c index 07115bee4..38b9d32fa 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -76,7 +76,7 @@ int n = 0; int extra = pcount ? *pcount : 0; uschar **argv; -argv = store_get((extra + acount + MAX_CLMACROS + 24) * sizeof(char *), FALSE); +argv = store_get((extra + acount + MAX_CLMACROS + 24) * sizeof(char *), GET_UNTAINTED); /* In all case, the list starts out with the path, any macros, and a changed config file. */ diff --git a/src/src/daemon.c b/src/src/daemon.c index 4a3cb6adb..daedf0080 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1346,7 +1346,7 @@ DEBUG(D_any|D_v) debug_selector |= D_pid; /* Allocate enough pollstructs for inetd mode plus the ancillary sockets; also used when there are no listen sockets. */ -fd_polls = store_get(sizeof(struct pollfd) * 3, FALSE); +fd_polls = store_get(sizeof(struct pollfd) * 3, GET_UNTAINTED); if (f.inetd_wait_mode) { @@ -1534,7 +1534,7 @@ if (f.daemon_listen && !f.inetd_wait_mode) sep = 0; while ((s = string_nextinlist(&list, &sep, NULL, 0))) pct++; - default_smtp_port = store_get((pct+1) * sizeof(int), FALSE); + default_smtp_port = store_get((pct+1) * sizeof(int), GET_UNTAINTED); list = daemon_smtp_port; sep = 0; for (pct = 0; @@ -1623,7 +1623,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), FALSE); + ip_address_item * new = store_get(sizeof(ip_address_item), GET_UNTAINTED); memcpy(new->address, ipa->address, Ustrlen(ipa->address) + 1); new->port = default_smtp_port[i]; @@ -1683,7 +1683,7 @@ if (f.daemon_listen && !f.inetd_wait_mode) for (ipa = addresses; ipa; ipa = ipa->next) listen_socket_count++; fd_polls = store_get(sizeof(struct pollfd) * (listen_socket_count + 2), - FALSE); + GET_UNTAINTED); for (struct pollfd * p = fd_polls; p < fd_polls + listen_socket_count + 2; p++) { p->fd = -1; p->events = POLLIN; } @@ -1709,7 +1709,7 @@ if (f.daemon_listen) if (smtp_accept_max > 0) { - smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot), FALSE); + smtp_slots = store_get(smtp_accept_max * sizeof(smtp_slot), GET_UNTAINTED); for (int i = 0; i < smtp_accept_max; i++) smtp_slots[i] = empty_smtp_slot; } } @@ -1991,7 +1991,7 @@ 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), FALSE); + queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t), GET_UNTAINTED); for (int i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0; } diff --git a/src/src/dbfn.c b/src/src/dbfn.c index 9c64d6a52..c318c2f58 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -268,7 +268,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, is_tainted(key)); +uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); @@ -284,7 +284,7 @@ if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; /* 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); +yield = store_get(EXIM_DATUM_SIZE(result_datum), GET_TAINTED); memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); if (length) *length = EXIM_DATUM_SIZE(result_datum); @@ -343,7 +343,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, is_tainted(key)); +uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); @@ -377,7 +377,7 @@ int dbfn_delete(open_db *dbblock, const uschar *key) { int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen, is_tainted(key)); +uschar * key_copy = store_get(klen, key); DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key); diff --git a/src/src/deliver.c b/src/src/deliver.c index 5c60e3ff5..8f85626a0 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -146,7 +146,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), FALSE); +address_item * addr = store_get(sizeof(address_item), GET_UNTAINTED); *addr = address_defaults; if (copy) address = string_copy(address); addr->address = address; @@ -1148,7 +1148,7 @@ pointer to a single host item in their host list, for use by the transport. */ #endif reset_point = store_mark(); -g = string_get_tainted(256, TRUE); /* addrs will be tainted, so avoid copy */ +g = string_get_tainted(256, GET_TAINTED); /* addrs will be tainted, so avoid copy */ if (msg) g = string_append(g, 2, host_and_ident(TRUE), US" "); @@ -2376,27 +2376,29 @@ if ((pid = exim_fork(US"delivery-local")) == 0) { BOOL ok = TRUE; set_process_info("delivering %s to %s using %s", message_id, - addr->local_part, addr->transport->name); + addr->local_part, tp->name); - /* Setting this global in the subprocess means we need never clear it */ + /* Setting these globals in the subprocess means we need never clear them */ transport_name = addr->transport->name; + driver_srcfile = tp->srcfile; + driver_srcline = tp->srcline; /* If a transport filter has been specified, set up its argument list. Any errors will get put into the address, and FALSE yielded. */ - if (addr->transport->filter_command) + if (tp->filter_command) { ok = transport_set_up_command(&transport_filter_argv, - addr->transport->filter_command, + tp->filter_command, TRUE, PANIC, addr, US"transport filter", NULL); - transport_filter_timeout = addr->transport->filter_timeout; + transport_filter_timeout = tp->filter_timeout; } else transport_filter_argv = NULL; if (ok) { - debug_print_string(addr->transport->debug_string); - replicate = !(addr->transport->info->code)(addr->transport, addr); + debug_print_string(tp->debug_string); + replicate = !(tp->info->code)(addr->transport, addr); } } @@ -3049,7 +3051,7 @@ while (addr_local) else for (addr2 = addr; addr2; addr2 = addr2->next) if (addr2->transport_return == OK) { - addr3 = store_get(sizeof(address_item), FALSE); + addr3 = store_get(sizeof(address_item), GET_UNTAINTED); *addr3 = *addr2; addr3->next = NULL; addr3->shadow_message = US &addr2->shadow_message; @@ -3444,7 +3446,7 @@ while (!done) if (!r || !(*ptr & rf_delete)) { - r = store_get(sizeof(retry_item), FALSE); + r = store_get(sizeof(retry_item), GET_UNTAINTED); r->next = addr->retries; addr->retries = r; r->flags = *ptr++; @@ -3635,7 +3637,7 @@ while (!done) if (*ptr) { - h = store_get(sizeof(host_item), FALSE); + h = store_get(sizeof(host_item), GET_UNTAINTED); h->name = string_copy(ptr); while (*ptr++); h->address = string_copy(ptr); @@ -4213,10 +4215,10 @@ set up, do so. */ if (!parlist) { - parlist = store_get(remote_max_parallel * sizeof(pardata), FALSE); + parlist = store_get(remote_max_parallel * sizeof(pardata), GET_UNTAINTED); for (poffset = 0; poffset < remote_max_parallel; poffset++) parlist[poffset].pid = 0; - parpoll = store_get(remote_max_parallel * sizeof(struct pollfd), FALSE); + parpoll = store_get(remote_max_parallel * sizeof(struct pollfd), GET_UNTAINTED); } /* Now loop for each remote delivery */ @@ -4664,8 +4666,10 @@ all pipes, so I do not see a reason to use non-blocking IO here int fd = pfd[pipe_write]; host_item *h; - /* Setting this global in the subprocess means we need never clear it */ - transport_name = tp->name; + /* Setting these globals in the subprocess means we need never clear them */ + transport_name = addr->transport->name; + driver_srcfile = tp->srcfile; + driver_srcline = tp->srcline; /* There are weird circumstances in which logging is disabled */ f.disable_logging = tp->disable_logging; @@ -5111,7 +5115,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, is_tainted(address)); +t = addr->cc_local_part = store_get(len+1, address); while(len-- > 0) { int c = *address++; @@ -5154,7 +5158,7 @@ if (percent_hack_domains) if (new_address) { - address_item *new_parent = store_get(sizeof(address_item), FALSE); + address_item * new_parent = store_get(sizeof(address_item), GET_UNTAINTED); *new_parent = *addr; addr->parent = new_parent; new_parent->child_count = 1; @@ -6552,7 +6556,7 @@ while (addr_new) /* Loop until all addresses dealt with */ if (Ustrcmp(addr->address, "/dev/null") == 0) { transport_instance * save_t = addr->transport; - transport_instance * t = store_get(sizeof(*t), is_tainted(save_t)); + transport_instance * t = store_get(sizeof(*t), save_t); *t = *save_t; t->name = US"**bypassed**"; addr->transport = t; @@ -7386,7 +7390,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), FALSE); + addr_senddsn = store_get(sizeof(address_item), GET_UNTAINTED); *addr_senddsn = *a; addr_senddsn->next = addr_next; } diff --git a/src/src/dkim.c b/src/src/dkim.c index 0c5e84788..0153024df 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -50,7 +50,7 @@ dkim_exim_query_dns_txt(const uschar * name) dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; rmark reset_point = store_mark(); -gstring * g = string_get_tainted(256, TRUE); +gstring * g = string_get_tainted(256, GET_TAINTED); lookup_dnssec_authenticated = NULL; if (dns_lookup(dnsa, name, T_TXT, NULL) != DNS_SUCCEED) diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 00aab2bee..f1b18bd59 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -530,7 +530,7 @@ 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 = store_get(DMARC_MAXHOSTNAMELEN, TRUE); + dmarc_domain = store_get(DMARC_MAXHOSTNAMELEN, GET_TAINTED); libdm_status = opendmarc_policy_fetch_utilized_domain(dmarc_pctx, dmarc_domain, DMARC_MAXHOSTNAMELEN-1); store_release_above(dmarc_domain + Ustrlen(dmarc_domain)+1); diff --git a/src/src/dns.c b/src/src/dns.c index 07c51e2dc..dd29d5c15 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -258,7 +258,7 @@ else { int v6[4]; - g = string_get_tainted(32, is_tainted(string)); + g = string_get_tainted(32, string); (void)host_aton(string, v6); /* The original specification for IPv6 reverse lookup was to invert each @@ -637,7 +637,7 @@ if ((previous = tree_search(tree_dns_fails, node_name))) e = previous->data.ptr; else { - e = store_get_perm(DNS_FAILNODE_SIZE, is_tainted(name)); + e = store_get_perm(DNS_FAILNODE_SIZE, name); new = (void *)(e+1); dns_fail_tag(new->name, name, type); new->data.ptr = e; @@ -1065,7 +1065,7 @@ for (int i = 0; i <= dns_cname_loops; i++) return DNS_FAIL; /* DNS data comes from the outside, hence tainted */ - data = store_get(256, TRUE); + data = store_get(256, GET_TAINTED); if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0) return DNS_FAIL; @@ -1293,7 +1293,7 @@ if (rr->type == T_A) if (p + 4 <= dnsa_lim) { /* the IP is not regarded as tainted */ - yield = store_get(sizeof(dns_address) + 20, FALSE); + yield = store_get(sizeof(dns_address) + 20, GET_UNTAINTED); (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); yield->next = NULL; } @@ -1307,7 +1307,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, FALSE); + yield = store_get(sizeof(dns_address) + 50, GET_UNTAINTED); inet_ntop(AF_INET6, &in6, CS yield->address, 50); yield->next = NULL; } diff --git a/src/src/dnsbl.c b/src/src/dnsbl.c index 5335b777b..e95da266b 100644 --- a/src/src/dnsbl.c +++ b/src/src/dnsbl.c @@ -115,9 +115,9 @@ else else { /* Set up a tree entry to cache the lookup */ - t = store_get(sizeof(tree_node) + qlen + 1 + 1, is_tainted(query)); + t = store_get(sizeof(tree_node) + qlen + 1 + 1, query); Ustrcpy(t->name, query); - t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), FALSE); + t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), GET_UNTAINTED); (void)tree_insertnode(&dnsbl_cache, t); } @@ -365,7 +365,7 @@ if (cb->rc == DNS_SUCCEED) int len = (rr->data)[0]; if (len > 511) len = 127; store_pool = POOL_PERM; - cb->text = string_copyn_taint(CUS (rr->data+1), len, TRUE); + cb->text = string_copyn_taint(CUS (rr->data+1), len, GET_TAINTED); store_pool = old_pool; break; } diff --git a/src/src/drtables.c b/src/src/drtables.c index a4958b179..39f58f803 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -518,7 +518,7 @@ static struct lookupmodulestr *lookupmodules = NULL; static void addlookupmodule(void *dl, struct lookup_module_info *info) { -struct lookupmodulestr *p = store_get(sizeof(struct lookupmodulestr), FALSE); +struct lookupmodulestr *p = store_get(sizeof(struct lookupmodulestr), GET_UNTAINTED); p->dl = dl; p->info = info; diff --git a/src/src/exim.c b/src/src/exim.c index bedac628b..eada6bbf8 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1902,7 +1902,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 */ +process_info = store_get(PROCESS_INFO_SIZE, GET_TAINTED); set_process_info("initializing"); os_restarting_signal(SIGUSR1, usr1_handler); /* exiwhat */ #ifdef SA_SIGINFO @@ -2273,7 +2273,9 @@ on the second character (the one after '-'), to save some effort. */ if (!*argrest || Ustrcmp(argrest, "c") == 0) { if (++i >= argc) { badarg = TRUE; break; } - sender_host_address = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-bh"), TRUE); + sender_host_address = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-bh"), + GET_TAINTED); host_checking = checking = f.log_testing_mode = TRUE; f.host_checking_callout = *argrest == 'c'; message_logs = FALSE; @@ -2746,7 +2748,9 @@ on the second character (the one after '-'), to save some effort. */ case 'F': if (!*argrest) if (++i < argc) argrest = argv[i]; else { badarg = TRUE; break; } - originator_name = string_copy_taint(exim_str_fail_toolong(argrest, EXIM_HUMANNAME_MAX, "-F"), TRUE); + originator_name = string_copy_taint( + exim_str_fail_toolong(argrest, EXIM_HUMANNAME_MAX, "-F"), + GET_TAINTED); f.sender_name_forced = TRUE; break; @@ -2774,7 +2778,7 @@ on the second character (the one after '-'), to save some effort. */ if (i+1 < argc) argrest = argv[++i]; else { badarg = TRUE; break; } (void) exim_str_fail_toolong(argrest, EXIM_DISPLAYMAIL_MAX, "-f"); if (!*argrest) - *(sender_address = store_get(1, FALSE)) = '\0'; /* Ensure writeable memory */ + *(sender_address = store_get(1, GET_UNTAINTED)) = '\0'; /* Ensure writeable memory */ else { uschar * temp = argrest + Ustrlen(argrest) - 1; @@ -2789,7 +2793,7 @@ on the second character (the one after '-'), to save some effort. */ &dummy_start, &dummy_end, &sender_address_domain, TRUE))) exim_fail("exim: bad -f address \"%s\": %s\n", argrest, errmess); - sender_address = string_copy_taint(sender_address, TRUE); + sender_address = string_copy_taint(sender_address, GET_TAINTED); #ifdef SUPPORT_I18N message_smtputf8 = string_is_utf8(sender_address); allow_utf8_domains = FALSE; @@ -2839,7 +2843,7 @@ on the second character (the one after '-'), to save some effort. */ exim_fail("exim: the -L syslog name is too long: \"%s\"\n", argrest); if (sz < 1) exim_fail("exim: the -L syslog name is too short\n"); - cmdline_syslog_name = string_copy_taint(argrest, TRUE); + cmdline_syslog_name = string_copy_taint(argrest, GET_TAINTED); break; case 'M': @@ -2869,9 +2873,15 @@ on the second character (the one after '-'), to save some effort. */ if (msg_action_arg >= 0) exim_fail("exim: incompatible arguments\n"); - continue_transport = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-C internal transport"), TRUE); - continue_hostname = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-C internal hostname"), TRUE); - continue_host_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-C internal hostaddr"), TRUE); + continue_transport = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-C internal transport"), + GET_TAINTED); + continue_hostname = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-C internal hostname"), + GET_TAINTED); + continue_host_address = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-C internal hostaddr"), + GET_TAINTED); continue_sequence = Uatoi(argv[++i]); msg_action = MSG_DELIVER; msg_action_arg = ++i; @@ -2916,7 +2926,9 @@ on the second character (the one after '-'), to save some effort. */ /* -MCd: for debug, set a process-purpose string */ case 'd': if (++i < argc) - process_purpose = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCd"), TRUE); + process_purpose = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCd"), + GET_TAINTED); else badarg = TRUE; break; @@ -2924,7 +2936,9 @@ on the second character (the one after '-'), to save some effort. */ from the commandline should be tainted - but we will need an untainted value for the spoolfile when doing a -odi delivery process. */ - case 'G': if (++i < argc) queue_name = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), FALSE); + case 'G': if (++i < argc) queue_name = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_DRIVERNAME_MAX, "-MCG"), + GET_UNTAINTED); else badarg = TRUE; break; @@ -2953,13 +2967,13 @@ on the second character (the one after '-'), to save some effort. */ case 'p': proxy_session = TRUE; if (++i < argc) { - proxy_local_address = string_copy_taint(argv[i], TRUE); + proxy_local_address = string_copy_taint(argv[i], GET_TAINTED); if (++i < argc) { proxy_local_port = Uatoi(argv[i]); if (++i < argc) { - proxy_external_address = string_copy_taint(argv[i], TRUE); + proxy_external_address = string_copy_taint(argv[i], GET_TAINTED); if (++i < argc) { proxy_external_port = Uatoi(argv[i]); @@ -2997,7 +3011,9 @@ on the second character (the one after '-'), to save some effort. */ case 'r': case 's': if (++i < argc) { - continue_proxy_sni = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-MCr/-MCs"), TRUE); + continue_proxy_sni = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_HOSTNAME_MAX, "-MCr/-MCs"), + GET_TAINTED); if (argrest[1] == 'r') continue_proxy_dane = TRUE; } else badarg = TRUE; @@ -3009,13 +3025,17 @@ on the second character (the one after '-'), to save some effort. */ and the TLS cipher. */ case 't': if (++i < argc) - sending_ip_address = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-MCt IP"), TRUE); + sending_ip_address = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_IPADDR_MAX, "-MCt IP"), + GET_TAINTED); else badarg = TRUE; if (++i < argc) sending_port = (int)(Uatol(argv[i])); else badarg = TRUE; if (++i < argc) - continue_proxy_cipher = string_copy_taint(exim_str_fail_toolong(argv[i], EXIM_CIPHERNAME_MAX, "-MCt cipher"), TRUE); + continue_proxy_cipher = string_copy_taint( + exim_str_fail_toolong(argv[i], EXIM_CIPHERNAME_MAX, "-MCt cipher"), + GET_TAINTED); else badarg = TRUE; /*FALLTHROUGH*/ @@ -3078,12 +3098,11 @@ on the second character (the one after '-'), to save some effort. */ else if (Ustrcmp(argrest, "G") == 0) { msg_action = MSG_SETQUEUE; - queue_name_dest = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"), TRUE); - } - else if (Ustrcmp(argrest, "mad") == 0) - { - msg_action = MSG_MARK_ALL_DELIVERED; + queue_name_dest = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-MG"), + GET_TAINTED); } + else if (Ustrcmp(argrest, "mad") == 0) msg_action = MSG_MARK_ALL_DELIVERED; else if (Ustrcmp(argrest, "md") == 0) { msg_action = MSG_MARK_DELIVERED; @@ -3291,27 +3310,37 @@ on the second character (the one after '-'), to save some effort. */ /* -oMa: Set sender host address */ if (Ustrcmp(argrest, "a") == 0) - sender_host_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"), TRUE); + sender_host_address = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMa"), + GET_TAINTED); /* -oMaa: Set authenticator name */ else if (Ustrcmp(argrest, "aa") == 0) - sender_host_authenticated = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"), TRUE); + sender_host_authenticated = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMaa"), + GET_TAINTED); /* -oMas: setting authenticated sender */ else if (Ustrcmp(argrest, "as") == 0) - authenticated_sender = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), TRUE); + authenticated_sender = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), + GET_TAINTED); /* -oMai: setting authenticated id */ else if (Ustrcmp(argrest, "ai") == 0) - authenticated_id = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), TRUE); + authenticated_id = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), + GET_TAINTED); /* -oMi: Set incoming interface address */ else if (Ustrcmp(argrest, "i") == 0) - interface_address = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"), TRUE); + interface_address = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IPADDR_MAX, "-oMi"), + GET_TAINTED); /* -oMm: Message reference */ @@ -3331,19 +3360,25 @@ on the second character (the one after '-'), to save some effort. */ if (received_protocol) exim_fail("received_protocol is set already\n"); else - received_protocol = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"), TRUE); + received_protocol = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_DRIVERNAME_MAX, "-oMr"), + GET_TAINTED); /* -oMs: Set sender host name */ else if (Ustrcmp(argrest, "s") == 0) - sender_host_name = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"), TRUE); + sender_host_name = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_HOSTNAME_MAX, "-oMs"), + GET_TAINTED); /* -oMt: Set sender ident */ else if (Ustrcmp(argrest, "t") == 0) { sender_ident_set = TRUE; - sender_ident = string_copy_taint(exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"), TRUE); + sender_ident = string_copy_taint( + exim_str_fail_toolong(argv[++i], EXIM_IDENTUSER_MAX, "-oMt"), + GET_TAINTED); } /* Else a bad argument */ @@ -3401,7 +3436,9 @@ on the second character (the one after '-'), to save some effort. */ case 'X': if (*argrest) badarg = TRUE; - else override_local_interfaces = string_copy_taint(exim_str_fail_toolong(argv[++i], 1024, "-oX"), TRUE); + else override_local_interfaces = string_copy_taint( + exim_str_fail_toolong(argv[++i], 1024, "-oX"), + GET_TAINTED); break; /* -oY: Override creation of daemon notifier socket */ @@ -3449,12 +3486,14 @@ on the second character (the one after '-'), to save some effort. */ exim_fail("received_protocol is set already\n"); if (!hn) - received_protocol = string_copy_taint(exim_str_fail_toolong(argrest, EXIM_DRIVERNAME_MAX, "-p"), TRUE); + received_protocol = string_copy_taint( + exim_str_fail_toolong(argrest, EXIM_DRIVERNAME_MAX, "-p"), + GET_TAINTED); else { (void) exim_str_fail_toolong(argrest, (EXIM_DRIVERNAME_MAX+1+EXIM_HOSTNAME_MAX), "-p:"); - received_protocol = string_copyn_taint(argrest, hn - argrest, TRUE); - sender_host_name = string_copy_taint(hn + 1, TRUE); + received_protocol = string_copyn_taint(argrest, hn - argrest, GET_TAINTED); + sender_host_name = string_copy_taint(hn + 1, GET_TAINTED); } } break; @@ -3523,9 +3562,9 @@ on the second character (the one after '-'), to save some effort. */ { queue_interval = 0; if (i+1 < argc && mac_ismsgid(argv[i+1])) - start_queue_run_id = string_copy_taint(argv[++i], TRUE); + start_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); if (i+1 < argc && mac_ismsgid(argv[i+1])) - stop_queue_run_id = string_copy_taint(argv[++i], TRUE); + stop_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); } /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally @@ -3573,7 +3612,9 @@ on the second character (the one after '-'), to save some effort. */ tainted_selectstr = argv[++i]; else exim_fail("exim: string expected after -R\n"); - deliver_selectstring = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), TRUE); + deliver_selectstring = string_copy_taint( + exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), + GET_TAINTED); } break; @@ -3616,7 +3657,9 @@ on the second character (the one after '-'), to save some effort. */ tainted_selectstr = argv[++i]; else exim_fail("exim: string expected after -S\n"); - deliver_selectstring_sender = string_copy_taint(exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), TRUE); + deliver_selectstring_sender = string_copy_taint( + exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), + GET_TAINTED); } break; @@ -3627,7 +3670,7 @@ on the second character (the one after '-'), to save some effort. */ case 'T': if (f.running_in_test_harness && Ustrcmp(argrest, "qt") == 0) - fudged_queue_times = string_copy_taint(argv[++i], TRUE); + fudged_queue_times = string_copy_taint(argv[++i], GET_TAINTED); else badarg = TRUE; break; @@ -3704,7 +3747,9 @@ on the second character (the one after '-'), to save some effort. */ case 'z': if (!*argrest) if (++i < argc) - log_oneline = string_copy_taint(exim_str_fail_toolong(argv[i], 2048, "-z logtext"), TRUE); + log_oneline = string_copy_taint( + exim_str_fail_toolong(argv[i], 2048, "-z logtext"), + GET_TAINTED); else exim_fail("exim: file name expected after %s\n", argv[i-1]); break; @@ -5172,7 +5217,9 @@ if (verify_address_mode || f.address_test_mode) while (recipients_arg < argc) { /* Supplied addresses are tainted since they come from a user */ - uschar * s = string_copy_taint(exim_str_fail_toolong(argv[recipients_arg++], EXIM_DISPLAYMAIL_MAX, "address verification"), TRUE); + uschar * s = string_copy_taint( + exim_str_fail_toolong(argv[recipients_arg++], EXIM_DISPLAYMAIL_MAX, "address verification"), + GET_TAINTED); while (*s) { BOOL finished = FALSE; @@ -5189,7 +5236,10 @@ if (verify_address_mode || f.address_test_mode) { uschar * s = get_stdinput(NULL, NULL); if (!s) break; - test_address(string_copy_taint(exim_str_fail_toolong(s, EXIM_DISPLAYMAIL_MAX, "address verification (stdin)"), TRUE), flags, &exit_value); + test_address(string_copy_taint( + exim_str_fail_toolong(s, EXIM_DISPLAYMAIL_MAX, "address verification (stdin)"), + GET_TAINTED), + flags, &exit_value); } route_tidyup(); @@ -5329,7 +5379,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, FALSE); /* large enough for full IPv6 */ + sender_host_address = store_get(48, GET_UNTAINTED); /* large enough for full IPv6 */ (void)host_nmtoa(size, x, -1, sender_host_address, ':'); /* Now set up for testing */ @@ -5701,11 +5751,13 @@ for (BOOL more = TRUE; more; ) uschar * errmess; /* There can be multiple addresses, so EXIM_DISPLAYMAIL_MAX (tuned for 1) is too short. * We'll still want to cap it to something, just in case. */ - uschar * s = string_copy_taint(exim_str_fail_toolong(list[i], BIG_BUFFER_SIZE, "address argument"), TRUE); + uschar * s = string_copy_taint( + exim_str_fail_toolong(list[i], BIG_BUFFER_SIZE, "address argument"), + GET_TAINTED); /* Loop for each comma-separated address */ - while (*s != 0) + while (*s) { BOOL finished = FALSE; uschar *recipient; @@ -5767,7 +5819,7 @@ for (BOOL more = TRUE; more; ) errors_sender_rc : EXIT_FAILURE; } - receive_add_recipient(string_copy_taint(recipient, TRUE), -1); + receive_add_recipient(string_copy_taint(recipient, GET_TAINTED), -1); s = ss; if (!finished) while (*(++s) != 0 && (*s == ',' || isspace(*s))); diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 45a3f1083..ff7205d3d 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -42,7 +42,7 @@ uschar * readconf_printtime(int t) { return NULL; } void * -store_get_3(int size, BOOL tainted, const char *filename, int linenumber) +store_get_3(int size, const void * proto_mem, const char *filename, int linenumber) { return NULL; } void ** store_reset_3(void **ptr, const char *filename, int linenumber) diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 5f2e66417..798f2fed6 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -430,7 +430,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, is_tainted(key)); +uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); @@ -444,7 +444,7 @@ if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL; /* 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); +yield = store_get(EXIM_DATUM_SIZE(result_datum), GET_TAINTED); memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); if (length) *length = EXIM_DATUM_SIZE(result_datum); @@ -477,7 +477,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, is_tainted(key)); +uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); gptr->time_stamp = time(NULL); @@ -509,7 +509,7 @@ int dbfn_delete(open_db *dbblock, const uschar *key) { int klen = Ustrlen(key) + 1; -uschar * key_copy = store_get(klen, is_tainted(key)); +uschar * key_copy = store_get(klen, key); memcpy(key_copy, key, klen); EXIM_DATUM key_datum; @@ -1249,7 +1249,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), is_tainted(key)); + key_item * k = store_get(sizeof(key_item) + Ustrlen(key), key); k->next = keychain; keychain = k; Ustrcpy(k->key, key); diff --git a/src/src/expand.c b/src/src/expand.c index 9b315b42b..0eb9fbff4 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1288,7 +1288,7 @@ expand_getlistele(int field, const uschar * list) const uschar * tlist = list; int sep = 0; /* Tainted mem for the throwaway element copies */ -uschar * dummy = store_get(2, TRUE); +uschar * dummy = store_get(2, GET_TAINTED); if (field < 0) { @@ -1975,7 +1975,7 @@ switch (vp->type) int len = message_body_visible; if (len > message_size) len = message_size; - *ss = body = store_get(len+1, TRUE); + *ss = body = store_get(len+1, GET_TAINTED); body[0] = 0; if (vp->type == vtype_msgbody_end) { @@ -3834,8 +3834,8 @@ Returns: pointer to string containing the last three static uschar * prvs_daystamp(int day_offset) { -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 */ +uschar * days = store_get(32, GET_UNTAINTED); /* 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"; } @@ -3906,7 +3906,7 @@ chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash); /* Hashing is deemed sufficient to de-taint any input data */ -p = finalhash_hex = store_get(40, FALSE); +p = finalhash_hex = store_get(40, GET_UNTAINTED); for (int i = 0; i < 3; i++) { *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; @@ -4347,7 +4347,7 @@ list = ((namedlist_block *)(t->data.ptr))->string; /* The list could be quite long so we (re)use a buffer for each element rather than getting each in new memory */ -if (is_tainted(list)) buffer = store_get(LISTNAMED_BUF_SIZE, TRUE); +if (is_tainted(list)) buffer = store_get(LISTNAMED_BUF_SIZE, GET_TAINTED); while ((item = string_nextinlist(&list, &sep, buffer, LISTNAMED_BUF_SIZE))) { uschar * buf = US" : "; @@ -4604,13 +4604,13 @@ while (*s) buffer. */ if (!yield) - g = store_get(sizeof(gstring), FALSE); + g = store_get(sizeof(gstring), GET_UNTAINTED); else if (yield->ptr == 0) { if (resetok) reset_point = store_reset(reset_point); yield = NULL; reset_point = store_mark(); - g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ + g = store_get(sizeof(gstring), GET_UNTAINTED); /* alloc _before_ calling find_variable() */ } /* Header */ @@ -6436,6 +6436,7 @@ while (*s) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { + /*{*/ expand_string_message = string_sprintf("missing '}' closing first arg of %s", name); goto EXPAND_FAILED_CURLY; @@ -6835,7 +6836,7 @@ while (*s) log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); goto EXPAND_FAILED; } - t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]), is_tainted(argv[0])); + t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]), argv[0]); Ustrcpy(t->name, argv[0]); t->data.ptr = handle; (void)tree_insertnode(&dlobj_anchor, t); @@ -7364,7 +7365,7 @@ NOT_ITEM: ; case EOP_LISTCOUNT: { int cnt = 0, sep = 0; - uschar * buf = store_get(2, is_tainted(sub)); + uschar * buf = store_get(2, sub); while (string_nextinlist(CUSS &sub, &sep, buf, 1)) cnt++; yield = string_fmt_append(yield, "%d", cnt); @@ -7624,109 +7625,108 @@ NOT_ITEM: ; goto EXPAND_FAILED; } - if (lookup_list[n]->quote) - sub = (lookup_list[n]->quote)(sub, opt); - else if (opt) - sub = NULL; + if (lookup_list[n]->quote) + sub = (lookup_list[n]->quote)(sub, opt, (unsigned)n); + else if (opt) + sub = NULL; - if (!sub) - { - expand_string_message = string_sprintf( - "\"%s\" unrecognized after \"${quote_%s\"", /*}*/ - opt, arg); - goto EXPAND_FAILED; - } + if (!sub) + { + expand_string_message = string_sprintf( + "\"%s\" unrecognized after \"${quote_%s\"", /*}*/ + opt, arg); + goto EXPAND_FAILED; + } - yield = string_cat(yield, sub); - break; - } + yield = string_cat(yield, sub); + break; + } - /* rx quote sticks in \ before any non-alphameric character so that - the insertion works in a regular expression. */ + /* rx quote sticks in \ before any non-alphameric character so that + the insertion works in a regular expression. */ - case EOP_RXQUOTE: - { - uschar *t = sub - 1; - while (*(++t) != 0) - { - if (!isalnum(*t)) - yield = string_catn(yield, US"\\", 1); - yield = string_catn(yield, t, 1); - } - break; - } + case EOP_RXQUOTE: + { + uschar *t = sub - 1; + while (*(++t) != 0) + { + if (!isalnum(*t)) + yield = string_catn(yield, US"\\", 1); + yield = string_catn(yield, t, 1); + } + break; + } - /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as - prescribed by the RFC, if there are characters that need to be encoded */ + /* RFC 2047 encodes, assuming headers_charset (default ISO 8859-1) as + prescribed by the RFC, if there are characters that need to be encoded */ - case EOP_RFC2047: - yield = string_cat(yield, - parse_quote_2047(sub, Ustrlen(sub), headers_charset, - FALSE)); - break; + case EOP_RFC2047: + yield = string_cat(yield, + parse_quote_2047(sub, Ustrlen(sub), headers_charset, + FALSE)); + break; - /* RFC 2047 decode */ + /* RFC 2047 decode */ - case EOP_RFC2047D: - { - int len; - uschar *error; - uschar *decoded = rfc2047_decode(sub, check_rfc2047_length, - headers_charset, '?', &len, &error); - if (error) - { - expand_string_message = error; - goto EXPAND_FAILED; - } - yield = string_catn(yield, decoded, len); - break; - } + case EOP_RFC2047D: + { + int len; + uschar *error; + uschar *decoded = rfc2047_decode(sub, check_rfc2047_length, + headers_charset, '?', &len, &error); + if (error) + { + expand_string_message = error; + goto EXPAND_FAILED; + } + yield = string_catn(yield, decoded, len); + break; + } - /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into - underscores */ + /* from_utf8 converts UTF-8 to 8859-1, turning non-existent chars into + underscores */ - case EOP_FROM_UTF8: - { - uschar * buff = store_get(4, is_tainted(sub)); - while (*sub) - { - int c; - GETUTF8INC(c, sub); - if (c > 255) c = '_'; - buff[0] = c; - yield = string_catn(yield, buff, 1); - } - break; - } + case EOP_FROM_UTF8: + { + uschar * buff = store_get(4, sub); + while (*sub) + { + int c; + GETUTF8INC(c, sub); + if (c > 255) c = '_'; + buff[0] = c; + yield = string_catn(yield, buff, 1); + } + break; + } - /* replace illegal UTF-8 sequences by replacement character */ + /* replace illegal UTF-8 sequences by replacement character */ - #define UTF8_REPLACEMENT_CHAR US"?" + #define UTF8_REPLACEMENT_CHAR US"?" - case EOP_UTF8CLEAN: - { - int seq_len = 0, index = 0; - int bytes_left = 0; - long codepoint = -1; - int complete; - uschar seq_buff[4]; /* accumulate utf-8 here */ + case EOP_UTF8CLEAN: + { + int seq_len = 0, index = 0; + int bytes_left = 0; + long codepoint = -1; + int complete; + uschar seq_buff[4]; /* accumulate utf-8 here */ - /* Manually track tainting, as we deal in individual chars below */ + /* Manually track tainting, as we deal in individual chars below */ - if (is_tainted(sub)) - if (yield->s && yield->ptr) - gstring_rebuffer(yield); - else - yield->s = store_get(yield->size = Ustrlen(sub), TRUE); + if (!yield->s || !yield->ptr) + yield->s = store_get(yield->size = Ustrlen(sub), sub); + else if (is_incompatible(yield->s, sub)) + gstring_rebuffer(yield, sub); - /* Check the UTF-8, byte-by-byte */ + /* Check the UTF-8, byte-by-byte */ - while (*sub) - { - complete = 0; - uschar c = *sub++; + while (*sub) + { + complete = 0; + uschar c = *sub++; - if (bytes_left) + if (bytes_left) { if ((c & 0xc0) != 0x80) /* wrong continuation byte; invalidate all bytes */ @@ -8149,9 +8149,34 @@ NOT_ITEM: ; } /* EOP_* switch */ DEBUG(D_expand) - if (start > 0 || *s) /* only if not the sole expansion of the line */ - debug_expansion_interim(US"op-res", - yield->s + start, yield->ptr - start, skipping); + { + const uschar * s = yield->s + start; + int i = yield->ptr - start; + BOOL tainted = is_tainted(s); + + DEBUG(D_noutf8) + { + debug_printf_indent("|-----op-res: %.*s\n", i, s); + if (tainted) + { + debug_printf_indent("%s \\__", skipping ? "| " : " "); + debug_print_taint(yield->s); + } + } + else + { + debug_printf_indent(UTF8_VERT_RIGHT + UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + "op-res: %.*s\n", i, s); + if (tainted) + { + debug_printf_indent("%s", + skipping + ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); + debug_print_taint(yield->s); + } + } + } continue; } } @@ -8172,13 +8197,13 @@ NOT_ITEM: ; gstring * g = NULL; if (!yield) - g = store_get(sizeof(gstring), FALSE); + g = store_get(sizeof(gstring), GET_UNTAINTED); else if (yield->ptr == 0) { if (resetok) reset_point = store_reset(reset_point); yield = NULL; reset_point = store_mark(); - g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ + g = store_get(sizeof(gstring), GET_UNTAINTED); /* alloc _before_ calling find_variable() */ } if (!(value = find_variable(name, FALSE, skipping, &newsize))) { @@ -8244,8 +8269,10 @@ DEBUG(D_expand) debug_printf_indent("%sresult: %s\n", skipping ? "|-----" : "\\_____", yield->s); if (tainted) - debug_printf_indent("%s \\__(tainted)\n", - skipping ? "| " : " "); + { + debug_printf_indent("%s \\__", skipping ? "| " : " "); + debug_print_taint(yield->s); + } if (skipping) debug_printf_indent("\\___skipping: result is not used\n"); } @@ -8259,9 +8286,12 @@ DEBUG(D_expand) skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, yield->s); if (tainted) - debug_printf_indent("%s(tainted)\n", + { + debug_printf_indent("%s", skipping ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); + debug_print_taint(yield->s); + } if (skipping) debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "skipping: result is not used\n"); @@ -8288,6 +8318,7 @@ that is a bad idea, because expand_string_message is in dynamic store. */ EXPAND_FAILED: if (left) *left = s; DEBUG(D_expand) + { DEBUG(D_noutf8) { debug_printf_indent("|failed to expand: %s\n", string); @@ -8307,6 +8338,7 @@ DEBUG(D_expand) if (f.expand_string_forcedfail) debug_printf_indent(UTF8_UP_RIGHT "failure was forced\n"); } + } if (resetok_p && !resetok) *resetok_p = FALSE; expand_level--; return NULL; diff --git a/src/src/filter.c b/src/src/filter.c index 887bc8bc1..629bbf536 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -498,7 +498,7 @@ for (;;) /* Build a condition block from the specific word. */ - c = store_get(sizeof(condition_block), FALSE); + c = store_get(sizeof(condition_block), GET_UNTAINTED); c->left.u = c->right.u = NULL; c->testfor = testfor; testfor = TRUE; @@ -528,7 +528,7 @@ for (;;) } ptr = nextitem(ptr, buffer, sizeof(buffer), TRUE); if (*error_pointer) break; - aa = store_get(sizeof(string_item), FALSE); + aa = store_get(sizeof(string_item), GET_UNTAINTED); aa->text = string_copy(buffer); aa->next = c->left.a; c->left.a = aa; @@ -684,7 +684,7 @@ for (;;) else if (Ustrcmp(buffer, "and") == 0) { - condition_block *andc = store_get(sizeof(condition_block), FALSE); + condition_block * andc = store_get(sizeof(condition_block), GET_UNTAINTED); andc->parent = current_parent; andc->type = cond_and; andc->testfor = TRUE; @@ -702,8 +702,8 @@ for (;;) else if (Ustrcmp(buffer, "or") == 0) { - condition_block *orc = store_get(sizeof(condition_block), FALSE); - condition_block *or_parent = NULL; + condition_block * orc = store_get(sizeof(condition_block), GET_UNTAINTED); + condition_block * or_parent = NULL; if (current_parent) { @@ -860,6 +860,8 @@ terminated by white space, but there are two exceptions, which are the "if" and as brackets are allowed in conditions and users will expect not to require white space here. */ +*buffer = '\0'; /* compiler quietening */ + if (Ustrncmp(ptr, "if(", 3) == 0) { Ustrcpy(buffer, US"if"); @@ -1019,9 +1021,10 @@ switch (command) FALSE for logging commands, and it doesn't matter for testprint, as that doesn't change the "delivered" status. */ - if (*error_pointer) yield = FALSE; else + if (*error_pointer) yield = FALSE; + else { - new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), FALSE); + new = store_get(sizeof(filter_cmd) + sizeof(union argtypes), GET_UNTAINTED); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); @@ -1105,12 +1108,12 @@ switch (command) /* Finish has no arguments; fmsg defaults to NULL */ case finish_command: - new = store_get(sizeof(filter_cmd), FALSE); + new = store_get(sizeof(filter_cmd), GET_UNTAINTED); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &(new->next); new->command = command; - new->seen = seen_force? seen_value : FALSE; + new->seen = seen_force ? seen_value : FALSE; new->args[0].u = fmsg; break; @@ -1129,7 +1132,7 @@ switch (command) /* Set up the command block for if */ - new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); + new = store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); new->next = NULL; **lastcmdptr = new; *lastcmdptr = &new->next; @@ -1157,7 +1160,7 @@ switch (command) while (had_else_endif == had_elif) { filter_cmd *newnew = - store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), FALSE); + store_get(sizeof(filter_cmd) + 4 * sizeof(union argtypes), GET_UNTAINTED); new->args[2].f = newnew; new = newnew; new->next = NULL; @@ -1210,10 +1213,10 @@ switch (command) case mail_command: case vacation_command: - new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), FALSE); + new = store_get(sizeof(filter_cmd) + mailargs_total * sizeof(union argtypes), GET_UNTAINTED); new->next = NULL; new->command = command; - new->seen = seen_force? seen_value : FALSE; + new->seen = seen_force ? seen_value : FALSE; new->noerror = noerror_force; for (i = 0; i < mailargs_total; i++) new->args[i].u = NULL; @@ -1892,7 +1895,7 @@ while (commands) if (expand_nmax >= 0 || filter_thisaddress != NULL) { int ecount = expand_nmax >= 0 ? expand_nmax : -1; - uschar **ss = store_get(sizeof(uschar *) * (ecount + 3), FALSE); + uschar ** ss = store_get(sizeof(uschar *) * (ecount + 3), GET_UNTAINTED); addr->pipe_expandn = ss; if (!filter_thisaddress) filter_thisaddress = US""; @@ -2304,7 +2307,7 @@ while (commands) addr->next = *generated; *generated = addr; - addr->reply = store_get(sizeof(reply_item), FALSE); + addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED); addr->reply->from = NULL; addr->reply->to = string_copy(to); addr->reply->file_expand = diff --git a/src/src/filtertest.c b/src/src/filtertest.c index 621ca76e9..125e9c879 100644 --- a/src/src/filtertest.c +++ b/src/src/filtertest.c @@ -114,7 +114,7 @@ if (body_len >= message_body_visible) int above = message_body_visible - below; if (above > 0) { - uschar *temp = store_get(below, TRUE); + uschar * temp = store_get(below, GET_UNTAINTED); memcpy(temp, message_body_end, below); memmove(message_body_end, s+1, above); memcpy(message_body_end + above, temp, below); @@ -180,7 +180,7 @@ if (fstat(fd, &statbuf) != 0) return FALSE; } -filebuf = store_get(statbuf.st_size + 1, is_tainted(filename)); +filebuf = store_get(statbuf.st_size + 1, filename); rc = read(fd, filebuf, statbuf.st_size); (void)close(fd); diff --git a/src/src/functions.h b/src/src/functions.h index e4d9b1a58..43840d4f6 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -98,6 +98,7 @@ extern int tlsa_lookup(const host_item *, dns_answer *, BOOL); extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); +extern uschar *acl_current_verb(void); extern int acl_eval(int, uschar *, uschar **, uschar **); extern tree_node *acl_var_create(uschar *); @@ -131,6 +132,7 @@ extern int auth_read_input(const uschar *); extern gstring * auth_show_supported(gstring *); extern uschar *auth_xtextencode(uschar *, int); extern int auth_xtextdecode(uschar *, uschar **); +extern uschar *authenticator_current_name(void); #ifdef EXPERIMENTAL_ARC extern gstring *authres_arc(gstring *); @@ -147,7 +149,7 @@ extern gstring *authres_spf(gstring *); #endif extern uschar *b64encode(const uschar *, int); -extern uschar *b64encode_taint(const uschar *, int, BOOL); +extern uschar *b64encode_taint(const uschar *, int, const void *); extern int b64decode(const uschar *, uschar **); extern int bdat_getc(unsigned); extern uschar *bdat_getbuf(unsigned *); @@ -465,6 +467,7 @@ extern BOOL route_find_expanded_user(uschar *, uschar *, uschar *, extern void route_init(void); extern gstring * route_show_supported(gstring *); extern void route_tidyup(void); +extern uschar *router_current_name(void); extern uschar *search_args(int, uschar *, uschar *, uschar **, const uschar *); extern uschar *search_find(void *, const uschar *, uschar *, int, @@ -602,9 +605,10 @@ extern uschar *tod_stamp(int); extern BOOL transport_check_waiting(const uschar *, const uschar *, int, uschar *, oicf, void*); -extern void transport_init(void); +extern uschar *transport_current_name(void); extern void transport_do_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int); +extern void transport_init(void); extern BOOL transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int #ifdef EXPERIMENTAL_ESMTP_LIMITS , unsigned, unsigned, unsigned @@ -678,6 +682,18 @@ return is_tainted_fn(p); #endif } +static inline BOOL +is_incompatible(const void * old, const void * new) +{ +#if defined(COMPILE_UTILITY) || defined(MACRO_PREDEF) || defined(EM_VERSION_C) +return FALSE; + +#else +extern BOOL is_incompatible_fn(const void *, const void *); +return is_incompatible_fn(old, new); +#endif +} + /******************************************************************************/ /* String functions */ static inline uschar * __Ustrcat(uschar * dst, const uschar * src, const char * func, int line) @@ -770,32 +786,32 @@ The result is explicitly nul-terminated. static inline uschar * string_copyn_taint_trc(const uschar * s, unsigned len, - BOOL tainted, const char * func, int line) + const void * proto_mem, const char * func, int line) { -uschar * ss = store_get_3(len + 1, tainted, func, line); +uschar * ss = store_get_3(len + 1, proto_mem, func, line); memcpy(ss, s, len); ss[len] = '\0'; return ss; } static inline uschar * -string_copy_taint_trc(const uschar * s, BOOL tainted, const char * func, int line) -{ return string_copyn_taint_trc(s, Ustrlen(s), tainted, func, line); } +string_copy_taint_trc(const uschar * s, const void * proto_mem, const char * func, int line) +{ return string_copyn_taint_trc(s, Ustrlen(s), proto_mem, func, line); } static inline uschar * string_copyn_trc(const uschar * s, unsigned len, const char * func, int line) -{ return string_copyn_taint_trc(s, len, is_tainted(s), func, line); } +{ return string_copyn_taint_trc(s, len, s, func, line); } static inline uschar * string_copy_trc(const uschar * s, const char * func, int line) -{ return string_copy_taint_trc(s, is_tainted(s), func, line); } +{ return string_copy_taint_trc(s, s, func, line); } /* String-copy functions explicitly setting the taint status */ -#define string_copyn_taint(s, len, tainted) \ - string_copyn_taint_trc((s), (len), (tainted), __FUNCTION__, __LINE__) -#define string_copy_taint(s, tainted) \ - string_copy_taint_trc((s), (tainted), __FUNCTION__, __LINE__) +#define string_copyn_taint(s, len, proto_mem) \ + string_copyn_taint_trc((s), (len), (proto_mem), __FUNCTION__, __LINE__) +#define string_copy_taint(s, proto_mem) \ + string_copy_taint_trc((s), (proto_mem), __FUNCTION__, __LINE__) /* Simple string-copy functions maintaining the taint */ @@ -817,7 +833,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, is_tainted(s)); +uschar * ss = store_get(Ustrlen(s) + 1, s); uschar * p = ss; while (*s) *p++ = tolower(*s++); *p = 0; @@ -843,8 +859,8 @@ 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, is_tainted(s)); -uschar *p = ss; +uschar * ss = store_get(n + 1, s); +uschar * p = ss; while (n-- > 0) *p++ = tolower(*s++); *p = 0; return ss; @@ -870,7 +886,7 @@ int len = Ustrlen(s) + 1; uschar *ss; store_pool = POOL_PERM; -ss = store_get(len, force_taint || is_tainted(s)); +ss = store_get(len, force_taint ? GET_TAINTED : s); memcpy(ss, s, len); store_pool = old_pool; return ss; @@ -898,13 +914,13 @@ va_end(ap); /* Create a growable-string with some preassigned space */ -#define string_get_tainted(size, tainted) \ - string_get_tainted_trc((size), (tainted), __FUNCTION__, __LINE__) +#define string_get_tainted(size, proto_mem) \ + string_get_tainted_trc((size), (proto_mem), __FUNCTION__, __LINE__) static inline gstring * -string_get_tainted_trc(unsigned size, BOOL tainted, const char * func, unsigned line) +string_get_tainted_trc(unsigned size, const void * proto_mem, const char * func, unsigned line) { -gstring * g = store_get_3(sizeof(gstring) + size, tainted, func, line); +gstring * g = store_get_3(sizeof(gstring) + size, proto_mem, func, line); g->size = size; /*XXX would be good if we could see the actual alloc size */ g->ptr = 0; g->s = US(g + 1); @@ -917,7 +933,7 @@ return g; static inline gstring * string_get_trc(unsigned size, const char * func, unsigned line) { -return string_get_tainted_trc(size, FALSE, func, line); +return string_get_tainted_trc(size, GET_UNTAINTED, func, line); } /* NUL-terminate the C string in the growable-string, and return it. */ @@ -970,12 +986,13 @@ return g; } -/* Copy the content of a string to tainted memory */ +/* Copy the content of a string to tainted memory. The proto_mem arg +will always be tainted, and suitable as a prototype. */ static inline void -gstring_rebuffer(gstring * g) +gstring_rebuffer(gstring * g, const void * proto_mem) { -uschar * s = store_get(g->size, TRUE); +uschar * s = store_get_3(g->size, proto_mem, __FUNCTION__, __LINE__); memcpy(s, g->s, g->ptr); g->s = s; } @@ -1052,7 +1069,7 @@ spool_fname(const uschar * purpose, const uschar * subdir, const uschar * fname, #ifdef COMPILE_UTILITY /* version avoiding string-extension */ int len = Ustrlen(spool_directory) + 1 + Ustrlen(queue_name) + 1 + Ustrlen(purpose) + 1 + Ustrlen(subdir) + 1 + Ustrlen(fname) + Ustrlen(suffix) + 1; -uschar * buf = store_get(len, FALSE); +uschar * buf = store_get(len, GET_UNTAINTED); string_format(buf, len, "%s/%s/%s/%s/%s%s", spool_directory, queue_name, purpose, subdir, fname, suffix); return buf; diff --git a/src/src/globals.c b/src/src/globals.c index 012c637dd..c3cccf1f2 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -650,6 +650,7 @@ auth_instance auth_defaults = { uschar *auth_defer_msg = US"reason not recorded"; uschar *auth_defer_user_msg = US""; const uschar *auth_vars[AUTH_VARS]; +uschar *authenticator_name = NULL; int auto_thaw = 0; #ifdef WITH_CONTENT_SCAN int av_failed = FALSE; /* boolean but accessed as vtype_int*/ @@ -894,6 +895,8 @@ uschar *dnslist_text = NULL; uschar *dnslist_value = NULL; tree_node *domainlist_anchor = NULL; int domainlist_count = 0; +const uschar *driver_srcfile = NULL; +int driver_srcline = 0; uschar *dsn_from = US DEFAULT_DSN_FROM; unsigned int dtrigger_selector = 0; diff --git a/src/src/globals.h b/src/src/globals.h index 02fcc9f33..f447b0096 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -364,6 +364,7 @@ extern uschar *authenticated_fail_id; /* ID that failed authentication */ extern uschar *authenticated_id; /* ID that was authenticated */ extern uschar *authenticated_sender; /* From AUTH on MAIL */ extern BOOL authentication_failed; /* TRUE if AUTH was tried and failed */ +extern uschar *authenticator_name; /* for debug and error messages */ extern uschar *auth_advertise_hosts; /* Only advertise to these */ extern auth_info auths_available[]; /* Vector of available auth mechanisms */ extern auth_instance *auths; /* Chain of instantiated auths */ @@ -573,6 +574,8 @@ extern int domainlist_count; /* Number defined */ /* This option is now a no-opt, retained for compatibility */ extern BOOL drop_cr; /* For broken local MUAs */ +extern const uschar *driver_srcfile; /* For debug & errors */ +extern int driver_srcline; /* For debug & errors */ extern unsigned int dtrigger_selector; /* when to start debug */ diff --git a/src/src/hash.c b/src/src/hash.c index c50c49aad..b5323b69c 100644 --- a/src/src/hash.c +++ b/src/src/hash.c @@ -116,7 +116,7 @@ void exim_sha_finish(hctx * h, blob * b) { /* Hashing is sufficient to purify any tainted input */ -b->data = store_get(b->len = h->hashlen, FALSE); +b->data = store_get(b->len = h->hashlen, GET_UNTAINTED); # if OPENSSL_VERSION_NUMBER < 0x30000000L switch (h->method) @@ -179,7 +179,7 @@ gnutls_hash(h->sha, data, len); void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen, FALSE); +b->data = store_get(b->len = h->hashlen, GET_UNTAINTED); gnutls_hash_output(h->sha, b->data); } @@ -216,7 +216,7 @@ gcry_md_write(h->sha, data, len); void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen, FALSE); +b->data = store_get(b->len = h->hashlen, GET_UNTAINTED); memcpy(b->data, gcry_md_read(h->sha, 0), h->hashlen); } @@ -254,7 +254,7 @@ switch (h->method) void exim_sha_finish(hctx * h, blob * b) { -b->data = store_get(b->len = h->hashlen, FALSE); +b->data = store_get(b->len = h->hashlen, GET_INTAINTED); switch (h->method) { case HASH_SHA1: sha1_finish(h->u.sha1, b->data); break; @@ -492,7 +492,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, FALSE); +b->data = store_get(b->len = h->hashlen, GET_UNTAINTED); native_sha1_end(&h->sha1, NULL, 0, b->data); } diff --git a/src/src/header.c b/src/src/header.c index 9bd376633..df84e2a53 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -102,7 +102,7 @@ gstring gs; if (!header_last) return NULL; -gs.s = buf = store_get(HEADER_ADD_BUFFER_SIZE, FALSE); +gs.s = buf = store_get(HEADER_ADD_BUFFER_SIZE, GET_UNTAINTED); gs.size = HEADER_ADD_BUFFER_SIZE; gs.ptr = 0; @@ -182,7 +182,7 @@ for (p = q = gs.s; *p; p = q) if (*(++q) != ' ' && *q != '\t') break; } - new = store_get(sizeof(header_line), FALSE); + new = store_get(sizeof(header_line), GET_UNTAINTED); new->text = string_copyn(p, q - p); new->slen = q - p; new->type = type; diff --git a/src/src/host.c b/src/src/host.c index 02d2da362..e99e6ceba 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -197,9 +197,9 @@ if ((ipa = string_is_ip_address(lname, NULL)) != 0) || ipa == 6 && af == AF_INET6) { int x[4]; - yield = store_get(sizeof(struct hostent), FALSE); - alist = store_get(2 * sizeof(char *), FALSE); - adds = store_get(alen, FALSE); + yield = store_get(sizeof(struct hostent), GET_UNTAINTED); + alist = store_get(2 * sizeof(char *), GET_UNTAINTED); + adds = store_get(alen, GET_UNTAINTED); 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), FALSE); - alist = store_get((count + 1) * sizeof(char *), FALSE); - adds = store_get(count *alen, FALSE); + yield = store_get(sizeof(struct hostent), GET_UNTAINTED); + alist = store_get((count + 1) * sizeof(char *), GET_UNTAINTED); + adds = store_get(count *alen, GET_UNTAINTED); yield->h_name = CS name; yield->h_aliases = NULL; @@ -328,12 +328,12 @@ while ((name = string_nextinlist(&list, &sep, NULL, 0))) continue; } - h = store_get(sizeof(host_item), FALSE); + h = store_get(sizeof(host_item), GET_UNTAINTED); h->name = name; h->address = NULL; h->port = PORT_NONE; h->mx = fake_mx; - h->sort_key = randomize? (-fake_mx)*1000 + random_number(1000) : 0; + h->sort_key = randomize ? (-fake_mx)*1000 + random_number(1000) : 0; h->status = hstatus_unknown; h->why = hwhy_unknown; h->last_try = 0; @@ -732,7 +732,6 @@ host_build_ifacelist(const uschar *list, uschar *name) int sep = 0; uschar *s; ip_address_item * yield = NULL, * last = NULL, * next; -BOOL taint = is_tainted(list); while ((s = string_nextinlist(&list, &sep, NULL, 0))) { @@ -751,7 +750,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), taint); + next = store_get(sizeof(ip_address_item), list); next->next = NULL; Ustrcpy(next->address, s); next->port = port; @@ -949,7 +948,7 @@ else /* If there is no buffer, put the string into some new store. */ -if (!buffer) buffer = store_get(46, FALSE); +if (!buffer) buffer = store_get(46, GET_UNTAINTED); /* Callers of this function with a non-NULL buffer must ensure that it is large enough to hold an IPv6 address, namely, at least 46 bytes. That's what @@ -1587,7 +1586,7 @@ Put it in permanent memory. */ for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) count++; store_pool = POOL_PERM; - ptr = sender_host_aliases = store_get(count * sizeof(uschar *), FALSE); + ptr = sender_host_aliases = store_get(count * sizeof(uschar *), GET_UNTAINTED); store_pool = POOL_TAINT_PERM; for (uschar ** aliases = USS hosts->h_aliases; *aliases; aliases++) @@ -1709,7 +1708,7 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) /* 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 *), FALSE); + aptr = sender_host_aliases = store_get(count * sizeof(uschar *), GET_UNTAINTED); /* Re-scan and extract the names */ @@ -1717,7 +1716,7 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR) { - uschar * s = store_get(ssize, TRUE); /* names are tainted */ + uschar * s = store_get(ssize, GET_TAINTED); /* names are tainted */ /* If an overlong response was received, the data will have been truncated and dn_expand may fail. */ @@ -2119,7 +2118,7 @@ for (int i = 1; i <= times; else { - host_item *next = store_get(sizeof(host_item), FALSE); + host_item *next = store_get(sizeof(host_item), GET_UNTAINTED); next->name = host->name; #ifndef DISABLE_TLS next->certname = host->certname; @@ -2456,7 +2455,7 @@ for (; i >= 0; i--) /* Not a duplicate */ new_sort_key = host->mx * 1000 + random_number(500) + randoffset; - next = store_get(sizeof(host_item), FALSE); + next = store_get(sizeof(host_item), GET_UNTAINTED); /* 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 @@ -2863,7 +2862,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), FALSE); + host_item * next = store_get(sizeof(host_item), GET_UNTAINTED); next->name = string_copy_dnsdomain(data); next->address = NULL; next->port = port; diff --git a/src/src/local_scan.h b/src/src/local_scan.h index 076fd6aa4..375a87642 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -40,8 +40,8 @@ ABI is changed in a non backward compatible way. The minor number is increased each time a new feature is added (in a way that doesn't break backward compatibility). */ -#define LOCAL_SCAN_ABI_VERSION_MAJOR 5 -#define LOCAL_SCAN_ABI_VERSION_MINOR 1 +#define LOCAL_SCAN_ABI_VERSION_MAJOR 6 +#define LOCAL_SCAN_ABI_VERSION_MINOR 0 #define LOCAL_SCAN_ABI_VERSION \ LOCAL_SCAN_ABI_VERSION_MAJOR.LOCAL_SCAN_ABI_VERSION_MINOR @@ -208,12 +208,12 @@ extern void smtp_vprintf(const char *, BOOL, va_list); string_sprintf_trc(fmt, US __FUNCTION__, __LINE__, __VA_ARGS__) extern uschar *string_sprintf_trc(const char *, const uschar *, unsigned, ...) ALMOST_PRINTF(1,4); -#define store_get(size, tainted) \ - store_get_3(size, tainted, __FUNCTION__, __LINE__) -extern void *store_get_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; -#define store_get_perm(size, tainted) \ - store_get_perm_3(size, tainted, __FUNCTION__, __LINE__) -extern void *store_get_perm_3(int, BOOL, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +#define store_get(size, proto_mem) \ + store_get_3((size), (proto_mem), __FUNCTION__, __LINE__) +extern void *store_get_3(int, const void *, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +#define store_get_perm(size, proto_mem) \ + store_get_perm_3((size), (proto_mem), __FUNCTION__, __LINE__) +extern void *store_get_perm_3(int, const void *, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; #if defined(LOCAL_SCAN) || defined(DLFUNC_IMPL) @@ -230,7 +230,7 @@ with the original name. */ extern uschar * string_copy_function(const uschar *); extern uschar * string_copyn_function(const uschar *, int n); -extern uschar * string_copy_taint_function(const uschar *, BOOL tainted); +extern uschar * string_copy_taint_function(const uschar *, const void * proto_mem); extern pid_t child_open_exim_function(int *, const uschar *); extern pid_t child_open_exim2_function(int *, uschar *, uschar *, const uschar *); extern pid_t child_open_function(uschar **, uschar **, int, int *, int *, BOOL, const uschar *); diff --git a/src/src/log.c b/src/src/log.c index 7a73b154a..6126b2058 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -288,8 +288,11 @@ if (fd < 0 && errno == ENOENT) uschar *lastslash = Ustrrchr(name, '/'); *lastslash = 0; created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE); - DEBUG(D_any) debug_printf("%s log directory %s\n", - created ? "created" : "failed to create", name); + DEBUG(D_any) + if (created) + debug_printf("created log directory %s\n", name); + else + debug_printf("failed to create log directory %s: %s\n", name, strerror(errno)); *lastslash = '/'; if (created) fd = Uopen(name, flags, LOG_MODE); } @@ -452,7 +455,7 @@ return fd; it does not exist. This may be called recursively on failure, in order to open the panic log. -The directory is in the static variable file_path. This is static so that it +The directory is in the static variable file_path. This is static so that the work of sorting out the path is done just once per Exim process. Exim is normally configured to avoid running as root wherever possible, the log @@ -512,6 +515,10 @@ switch (type) Ustrcpy(debuglog_name, buffer); if (tag) { + if (is_tainted(tag)) + die(US"exim: tainted tag for debug log filename", + US"Logging failure; please try later"); + /* this won't change the offset of the datestamp */ ok2 = string_format(buffer, sizeof(buffer), "%s%s", debuglog_name, tag); @@ -720,10 +727,17 @@ while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE))) } +/* Close mainlog, unless we do not see a chance to open the file mainlog later +again. This will happen if we log from a transport process (which has dropped +privs); something we traditionally avoid, but the introduction of taint-tracking +and resulting detection of errors is makinng harder. */ + void mainlog_close(void) { -if (mainlogfd < 0) return; +if (mainlogfd < 0 + || !(geteuid() == 0 || geteuid() == exim_uid)) + return; (void)close(mainlogfd); mainlogfd = -1; mainlog_inode = 0; diff --git a/src/src/lookupapi.h b/src/src/lookupapi.h index d8364dbb9..af7f59a06 100644 --- a/src/src/lookupapi.h +++ b/src/src/lookupapi.h @@ -3,7 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2015 */ -/* Copyright (c) The Exim Maintainers 2020 */ +/* Copyright (c) The Exim Maintainers 2022 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -42,16 +42,19 @@ typedef struct lookup_info { void (*tidy)(void); /* tidy function */ uschar *(*quote)( /* quoting function */ uschar *, /* string to quote */ - uschar *); /* additional data from quote name */ + uschar *, /* additional data from quote name */ + unsigned); /* lookup type index */ gstring * (*version_report)( /* diagnostic function */ - gstring *); /* true: stdout. false: debug */ + gstring *); /* string to appand to */ } lookup_info; /* This magic number is used by the following lookup_module_info structure for checking API compatibility. It used to be equivalent to the string"LMM3" */ -#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4933 +#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4935 /* Version 2 adds: version_report */ /* Version 3 change: non/cache becomes TTL in seconds */ +/* Version 4 add: index on quoting function */ +/* Version 5 change: version report now adds to a gstring */ typedef struct lookup_module_info { uint magic; diff --git a/src/src/lookups/cdb.c b/src/src/lookups/cdb.c index 7b9c2cdfd..5f8498809 100644 --- a/src/src/lookups/cdb.c +++ b/src/src/lookups/cdb.c @@ -178,7 +178,7 @@ if (statbuf.st_size < CDB_HASH_TABLE) } /* Having got a file open we need the structure to put things in */ -cdbp = store_get(sizeof(struct cdb_state), FALSE); +cdbp = store_get(sizeof(struct cdb_state), GET_UNTAINTED); /* store_get() does not return if memory was not available... */ /* preload the structure.... */ cdbp->fileno = fileno; @@ -213,7 +213,7 @@ DEBUG(D_lookup) debug_printf_indent("cdb mmap failed - %d\n", errno); /* 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, FALSE); +cdbp->cdb_offsets = store_get(CDB_HASH_TABLE, GET_UNTAINTED); /* now fill the buffer up... */ @@ -343,7 +343,7 @@ if (cdbp->cdb_map != NULL) /* ... and the returned result. Assume it is not tainted, lacking any way of telling. */ - *result = store_get(item_dat_len + 1, FALSE); + *result = store_get(item_dat_len + 1, GET_UNTAINTED); memcpy(*result, item_ptr, item_dat_len); (*result)[item_dat_len] = 0; return OK; @@ -387,7 +387,7 @@ for (int loop = 0; (loop < hash_offlen); ++loop) if (item_key_len == key_len) { /* finally check if key matches */ rmark reset_point = store_mark(); - uschar * item_key = store_get(key_len, TRUE); /* keys liable to be tainted */ + uschar * item_key = store_get(key_len, GET_TAINTED); /* 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) @@ -401,7 +401,7 @@ for (int loop = 0; (loop < hash_offlen); ++loop) /* 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); + *result = store_get(item_dat_len + 1, GET_UNTAINTED); /* coverity[tainted_data] */ if (cdb_bread(cdbp->fileno, *result, item_dat_len) == -1) return DEFER; diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index c53e55554..c607f7b9c 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -149,7 +149,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, is_tainted(keystring)); +key_buffer = store_get(buflen, keystring); key_buffer[0] = '\0'; diff --git a/src/src/lookups/ibase.c b/src/src/lookups/ibase.c index e1692992b..d72623037 100644 --- a/src/src/lookups/ibase.c +++ b/src/src/lookups/ibase.c @@ -177,7 +177,7 @@ if (cn) } else { - cn = store_get(sizeof(ibase_connection), FALSE); + cn = store_get(sizeof(ibase_connection), GET_UNTAINTED); cn->server = server_copy; cn->dbh = NULL; cn->transh = NULL; @@ -252,7 +252,7 @@ if (isc_dsql_allocate_statement(status, &cn->dbh, &stmth)) /* Lacking any information, assume that the data is untainted */ reset_point = store_mark(); -out_sqlda = store_get(XSQLDA_LENGTH(1), FALSE); +out_sqlda = store_get(XSQLDA_LENGTH(1), GET_UNTAINTED); out_sqlda->version = SQLDA_VERSION1; out_sqlda->sqln = 1; @@ -272,7 +272,7 @@ 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), FALSE); + XSQLDA *new_sqlda = store_get(XSQLDA_LENGTH(out_sqlda->sqld), GET_UNTAINTED); if (isc_dsql_describe (status, &stmth, out_sqlda->version, new_sqlda)) { @@ -294,46 +294,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, FALSE); + var->sqldata = CS store_get(sizeof(char) * var->sqllen + 2, GET_UNTAINTED); break; case SQL_TEXT: - var->sqldata = CS store_get(sizeof(char) * var->sqllen, FALSE); + var->sqldata = CS store_get(sizeof(char) * var->sqllen, GET_UNTAINTED); break; case SQL_SHORT: - var->sqldata = CS store_get(sizeof(short), FALSE); + var->sqldata = CS store_get(sizeof(short), GET_UNTAINTED); break; case SQL_LONG: - var->sqldata = CS store_get(sizeof(ISC_LONG), FALSE); + var->sqldata = CS store_get(sizeof(ISC_LONG), GET_UNTAINTED); break; #ifdef SQL_INT64 case SQL_INT64: - var->sqldata = CS store_get(sizeof(ISC_INT64), FALSE); + var->sqldata = CS store_get(sizeof(ISC_INT64), GET_UNTAINTED); break; #endif case SQL_FLOAT: - var->sqldata = CS store_get(sizeof(float), FALSE); + var->sqldata = CS store_get(sizeof(float), GET_UNTAINTED); break; case SQL_DOUBLE: - var->sqldata = CS store_get(sizeof(double), FALSE); + var->sqldata = CS store_get(sizeof(double), GET_UNTAINTED); break; #ifdef SQL_TIMESTAMP case SQL_DATE: - var->sqldata = CS store_get(sizeof(ISC_QUAD), FALSE); + var->sqldata = CS store_get(sizeof(ISC_QUAD), GET_UNTAINTED); break; #else case SQL_TIMESTAMP: - var->sqldata = CS store_get(sizeof(ISC_TIMESTAMP), FALSE); + var->sqldata = CS store_get(sizeof(ISC_TIMESTAMP), GET_UNTAINTED); break; case SQL_TYPE_DATE: - var->sqldata = CS store_get(sizeof(ISC_DATE), FALSE); + var->sqldata = CS store_get(sizeof(ISC_DATE), GET_UNTAINTED); break; case SQL_TYPE_TIME: - var->sqldata = CS store_get(sizeof(ISC_TIME), FALSE); + var->sqldata = CS store_get(sizeof(ISC_TIME), GET_UNTAINTED); break; #endif } if (var->sqltype & 1) - var->sqlind = (short *) store_get(sizeof(short), FALSE); + var->sqlind = (short *) store_get(sizeof(short), GET_UNTAINTED); } /* finally, we're ready to execute the statement */ @@ -492,51 +492,32 @@ can't quote "on spec". Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ -static uschar *ibase_quote(uschar * s, uschar * opt) +static uschar * +ibase_quote(uschar * s, uschar * opt, unsigned idx) { - register int c; - int count = 0; - uschar *t = s; - uschar *quoted; - - if (opt != NULL) - return NULL; /* No options recognized */ - - 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, FALSE); - - while ((c = *s++) != 0) { - if (Ustrchr("'", c) != NULL) { - *t++ = '\''; - *t++ = '\''; -/* switch(c) - { - case '\n': *t++ = 'n'; - break; - case '\t': *t++ = 't'; - break; - case '\r': *t++ = 'r'; - break; - case '\b': *t++ = 'b'; - break; - default: *t++ = c; - break; - }*/ - } else - *t++ = c; - } +int c; +int count = 0; +uschar * t = s, * quoted; + +if (opt) + return NULL; /* No options recognized */ + +while ((c = *t++)) + if (c == '\'') count++; + +t = quoted = store_get_quoted(Ustrlen(s) + count + 1, s, idx); + +while ((c = *s++)) + if (c == '\'') { *t++ = '\''; *t++ = '\''; } + else *t++ = c; - *t = 0; - return quoted; +*t = 0; +return quoted; } diff --git a/src/src/lookups/json.c b/src/src/lookups/json.c index 7f2478b63..e53ddc219 100644 --- a/src/src/lookups/json.c +++ b/src/src/lookups/json.c @@ -24,7 +24,7 @@ Assume that the file is trusted, so no tainting */ static void * json_malloc(size_t nbytes) { -void * p = store_get((int)nbytes, FALSE); +void * p = store_get((int)nbytes, GET_UNTAINTED); /* debug_printf("%s %d: %p\n", __FUNCTION__, (int)nbytes, p); */ return p; } diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 415266358..6065d2b8e 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -496,7 +496,7 @@ if (!lcp) /* Now add this connection to the chain of cached connections */ - lcp = store_get(sizeof(LDAP_CONNECTION), FALSE); + lcp = store_get(sizeof(LDAP_CONNECTION), GET_UNTAINTED); lcp->host = host ? string_copy(host) : NULL; lcp->bound = FALSE; lcp->user = NULL; @@ -1405,6 +1405,7 @@ Arguments: s the string to be quoted opt additional option text or NULL if none only "dn" is recognized + idx lookup type index Returns: the processed string or NULL for a bad option */ @@ -1430,18 +1431,15 @@ quote_ldap_dn, respectively. */ static uschar * -eldap_quote(uschar *s, uschar *opt) +eldap_quote(uschar * s, uschar * opt, unsigned idx) { -register int c; -int count = 0; -int len = 0; +int c, count = 0, len = 0; BOOL dn = FALSE; -uschar *t = s; -uschar *quoted; +uschar * t = s, * quoted; /* Test for a DN quotation. */ -if (opt != NULL) +if (opt) { if (Ustrcmp(opt, "dn") != 0) return NULL; /* No others recognized */ dn = TRUE; @@ -1454,24 +1452,25 @@ where, for example, < turns into %5C%3C. For simplicity, we just add 5 for each possibly escaped character. The really fast way would be just to test for non-alphanumerics, but it is probably better to spot a few others that are never escaped, because if there are no specials at all, we can avoid copying -the string. */ +the string. +XXX No longer true; we always copy, to support quoted-enforcement */ -while ((c = *t++) != 0) +while ((c = *t++)) { len++; if (!isalnum(c) && Ustrchr(ALWAYS_LITERAL, c) == NULL) count += 5; } -if (count == 0) return s; +/*if (count == 0) return s;*/ /* Get sufficient store to hold the quoted string */ -t = quoted = store_get(len + count + 1, is_tainted(s)); +t = quoted = store_get_quoted(len + count + 1, s, idx); /* Handle plain quote_ldap */ if (!dn) { - while ((c = *s++) != 0) + while ((c = *s++)) { if (!isalnum(c)) { @@ -1496,7 +1495,7 @@ if (!dn) else { - uschar *ss = s + len; + uschar * ss = s + len; /* Find the last char before any trailing spaces */ diff --git a/src/src/lookups/lmdb.c b/src/src/lookups/lmdb.c index 5fe86fea2..042229a55 100644 --- a/src/src/lookups/lmdb.c +++ b/src/src/lookups/lmdb.c @@ -31,7 +31,7 @@ Lmdbstrct * lmdb_p; int ret, save_errno; const uschar * errstr; -lmdb_p = store_get(sizeof(Lmdbstrct), FALSE); +lmdb_p = store_get(sizeof(Lmdbstrct), GET_UNTAINTED); lmdb_p->txn = NULL; if ((ret = mdb_env_create(&db_env))) diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index 782ad2a5f..d874d88cc 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -231,7 +231,7 @@ if (!cn) /* Get store for a new handle, initialize it, and connect to the server */ - mysql_handle = store_get(sizeof(MYSQL), FALSE); + mysql_handle = store_get(sizeof(MYSQL), GET_UNTAINTED); mysql_init(mysql_handle); mysql_options(mysql_handle, MYSQL_READ_DEFAULT_GROUP, CS group); if (mysql_real_connect(mysql_handle, @@ -247,7 +247,7 @@ if (!cn) /* Add the connection to the cache */ - cn = store_get(sizeof(mysql_connection), FALSE); + cn = store_get(sizeof(mysql_connection), GET_UNTAINTED); cn->server = server_copy; cn->handle = mysql_handle; cn->next = mysql_connections; @@ -413,25 +413,26 @@ can't quote "on spec". Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ static uschar * -mysql_quote(uschar * s, uschar * opt) +mysql_quote(uschar * s, uschar * opt, unsigned idx) { -register int c; -int count = 0; -uschar *t = s; -uschar *quoted; +int c, count = 0; +uschar * t = s, * quoted; if (opt) return NULL; /* No options recognized */ while ((c = *t++)) if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++; -if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); +/* Old code: if (count == 0) return s; +Now always allocate and copy, to track the quoted status. */ + +t = quoted = store_get_quoted(Ustrlen(s) + count + 1, s, idx); while ((c = *s++)) { diff --git a/src/src/lookups/nisplus.c b/src/src/lookups/nisplus.c index dc3735b6b..0892fc4c3 100644 --- a/src/src/lookups/nisplus.c +++ b/src/src/lookups/nisplus.c @@ -226,25 +226,24 @@ characters. No options are recognized. Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ static uschar * -nisplus_quote(uschar *s, uschar *opt) +nisplus_quote(uschar * s, uschar * opt, unsigned idx) { int count = 0; -uschar *quoted; -uschar *t = s; +uschar * quoted, * t = s; -if (opt != NULL) return NULL; /* No options recognized */ +if (opt) return NULL; /* No options recognized */ -while (*t != 0) if (*t++ == '\"') count++; -if (count == 0) return s; +while (*t) if (*t++ == '\"') count++; -t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); +t = quoted = store_get_quoted(Ustrlen(s) + count + 1, s, idx); -while (*s != 0) +while (*s) { *t++ = *s; if (*s++ == '\"') *t++ = '\"'; diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index 0429a8f84..8a6954035 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -306,8 +306,8 @@ if (!cn) /* Get store for a new connection, initialize it, and connect to the server */ - oracle_handle = store_get(sizeof(struct cda_def), FALSE); - hda = store_get(HDA_SIZE, FALSE); + oracle_handle = store_get(sizeof(struct cda_def), GET_UNTAINTED); + hda = store_get(HDA_SIZE, GET_UNTAINTED); memset(hda,'\0',HDA_SIZE); /* @@ -330,7 +330,7 @@ if (!cn) /* Add the connection to the cache */ - cn = store_get(sizeof(oracle_connection), FALSE); + cn = store_get(sizeof(oracle_connection), GET_UNTAINTED); cn->server = server_copy; cn->handle = oracle_handle; cn->next = oracle_connections; @@ -349,7 +349,7 @@ else /* We have a connection. Open a cursor and run the query */ -cda = store_get(sizeof(Cda_Def), FALSE); +cda = store_get(sizeof(Cda_Def), GET_UNTAINTED); if (oopen(cda, oracle_handle, (text *)0, -1, -1, (text *)0, -1) != 0) { @@ -370,8 +370,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, FALSE); -desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE, FALSE); +def = store_get(sizeof(Ora_Define)*MAX_SELECT_LIST_SIZE, GET_UNTAINTED); +desc = store_get(sizeof(Ora_Describe)*MAX_SELECT_LIST_SIZE, GET_UNTAINTED); if ((num_fields = describe_define(cda,def,desc)) == -1) { @@ -543,27 +543,25 @@ messages, since that isn't likely to be treated as a pattern of any kind. Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ static uschar * -oracle_quote(uschar *s, uschar *opt) +oracle_quote(uschar * s, uschar * opt, unsigned idx) { -register int c; -int count = 0; -uschar *t = s; -uschar *quoted; +int c, count = 0; +uschar * t = s, * quoted; -if (opt != NULL) return NULL; /* No options are recognized */ +if (opt) return NULL; /* No options are recognized */ -while ((c = *t++) != 0) +while ((c = *t++)) if (strchr("\n\t\r\b\'\"\\", c) != NULL) count++; -if (count == 0) return s; -t = quoted = store_get((int)strlen(s) + count + 1, is_tainted(s)); +t = quoted = store_get_quoted((int)Ustrlen(s) + count + 1, s, idx); -while ((c = *s++) != 0) +while ((c = *s++)) { if (strchr("\n\t\r\b\'\"\\", c) != NULL) { diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index c3053430e..dc5fd5750 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -262,7 +262,7 @@ if (!cn) /* Add the connection to the cache */ - cn = store_get(sizeof(pgsql_connection), FALSE); + cn = store_get(sizeof(pgsql_connection), GET_UNTAINTED); cn->server = server_copy; cn->handle = pg_conn; cn->next = pgsql_connections; @@ -414,12 +414,13 @@ Why, I don't know. Seems odd for just string escaping...] Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ static uschar * -pgsql_quote(uschar * s, uschar * opt) +pgsql_quote(uschar * s, uschar * opt, unsigned idx) { int count = 0, c; uschar * t = s, * quoted; @@ -429,8 +430,7 @@ if (opt) return NULL; /* No options recognized */ while ((c = *t++)) if (Ustrchr("\n\t\r\b\'\"\\", c) != NULL) count++; -if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); +t = quoted = store_get_quoted(Ustrlen(s) + count + 1, s, idx); while ((c = *s++)) { diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index 44159455d..dfde99945 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -150,7 +150,7 @@ that connection cacheing at the framework layer works. */ static void * readsock_open(const uschar * filename, uschar ** errmsg) { -client_conn_ctx * cctx = store_get(sizeof(*cctx), FALSE); +client_conn_ctx * cctx = store_get(sizeof(*cctx), GET_UNTAINTED); cctx->sock = -1; cctx->tls_ctx = NULL; DEBUG(D_lookup) debug_printf_indent("readsock: allocated context\n"); diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index 0bc506093..2ddc99abc 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -171,7 +171,7 @@ if (!cn) } /* Add the connection to the cache */ - cn = store_get(sizeof(redis_connection), FALSE); + cn = store_get(sizeof(redis_connection), GET_UNTAINTED); cn->server = server_copy; cn->handle = redis_handle; cn->next = redis_connections; @@ -399,27 +399,25 @@ whitespace into an argument. Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ static uschar * -redis_quote(uschar *s, uschar *opt) +redis_quote(uschar * s, uschar * opt, unsigned idx) { -register int c; -int count = 0; -uschar *t = s; -uschar *quoted; +int c, count = 0; +uschar * t = s, * quoted; if (opt) return NULL; /* No options recognized */ -while ((c = *t++) != 0) +while ((c = *t++)) if (isspace(c) || c == '\\') count++; -if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); +t = quoted = store_get_quoted(Ustrlen(s) + count + 1, s, idx); -while ((c = *s++) != 0) +while ((c = *s++)) { if (isspace(c) || c == '\\') *t++ = '\\'; *t++ = c; diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index 65e7cffc9..69c0ac7ba 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -126,26 +126,25 @@ for sqlite is the single quote, and it is quoted by doubling. Arguments: s the string to be quoted opt additional option text or NULL if none + idx lookup type index Returns: the processed string or NULL for a bad option */ static uschar * -sqlite_quote(uschar *s, uschar *opt) +sqlite_quote(uschar * s, uschar * opt, unsigned idx) { -register int c; -int count = 0; -uschar *t = s; -uschar *quoted; +int c, count = 0; +uschar * t = s, * quoted; -if (opt != NULL) return NULL; /* No options recognized */ +if (opt) return NULL; /* No options recognized */ -while ((c = *t++) != 0) if (c == '\'') count++; +while ((c = *t++)) if (c == '\'') count++; +count += t - s; -if (count == 0) return s; -t = quoted = store_get(Ustrlen(s) + count + 1, is_tainted(s)); +t = quoted = store_get_quoted(count + 1, s, idx); -while ((c = *s++) != 0) +while ((c = *s++)) { if (c == '\'') *t++ = '\''; *t++ = c; diff --git a/src/src/malware.c b/src/src/malware.c index d9ab3b9dd..289b64672 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -934,7 +934,7 @@ badseek: err = errno; drweb_slen = ntohl(drweb_slen); /* assume tainted, since it is external input */ - tmpbuf = store_get(drweb_slen, TRUE); + tmpbuf = store_get(drweb_slen, GET_TAINTED); /* read report body */ if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo)) @@ -1471,9 +1471,9 @@ badseek: err = errno; int subsep = ' '; /* Local file; so we def want to use_scan_command and don't want to try - * passing IP/port combinations */ + passing IP/port combinations */ use_scan_command = TRUE; - cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE); + cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED); /* extract socket-path part */ sublist = scanner_options; @@ -1507,7 +1507,7 @@ badseek: err = errno; continue; } - cd = (clamd_address *) store_get(sizeof(clamd_address), FALSE); + cd = (clamd_address *) store_get(sizeof(clamd_address), GET_UNTAINTED); /* extract host and port part */ sublist = scanner_options; diff --git a/src/src/match.c b/src/src/match.c index 069d0f672..a1ec5616b 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -432,11 +432,9 @@ match_check_list(const uschar **listptr, int sep, tree_node **anchorptr, void *arg, int type, const uschar *name, const uschar **valueptr) { int yield = OK; -unsigned int *original_cache_bits = *cache_ptr; -BOOL include_unknown = FALSE; -BOOL ignore_unknown = FALSE; -BOOL include_defer = FALSE; -BOOL ignore_defer = FALSE; +unsigned int * original_cache_bits = *cache_ptr; +BOOL include_unknown = FALSE, ignore_unknown = FALSE, + include_defer = FALSE, ignore_defer = FALSE; const uschar *list; uschar *sss; uschar *ot = NULL; @@ -445,8 +443,8 @@ uschar *ot = NULL; HDEBUG(D_any) { - uschar *listname = readconf_find_option(listptr); - if (listname[0] != 0) ot = string_sprintf("%s in %s?", name, listname); + uschar * listname = readconf_find_option(listptr); + if (*listname) ot = string_sprintf("%s in %s?", name, listname); } /* If the list is empty, the answer is no. Skip the debugging output for @@ -669,7 +667,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) so we use the permanent store pool */ store_pool = POOL_PERM; - p = store_get(sizeof(namedlist_cacheblock), FALSE); + p = store_get(sizeof(namedlist_cacheblock), GET_UNTAINTED); p->key = string_copy(get_check_key(arg, type)); diff --git a/src/src/mime.c b/src/src/mime.c index 8f30bcc8a..ddf923c25 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -501,7 +501,7 @@ uschar * header = NULL; struct mime_boundary_context nested_context; /* reserve a line buffer to work in. Assume tainted data. */ -header = store_get(MIME_MAX_HEADER_SIZE+1, TRUE); +header = store_get(MIME_MAX_HEADER_SIZE+1, GET_TAINTED); /* Not actually used at the moment, but will be vital to fixing * some RFC 2046 nonconformance later... */ diff --git a/src/src/moan.c b/src/src/moan.c index c8755911f..efa0f02e8 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -386,7 +386,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, TRUE); + uschar * buf = store_get(bounce_return_linesize_limit+2, GET_TAINTED); if (firstline) fprintf(fp, "%s", CS firstline); diff --git a/src/src/os.c b/src/src/os.c index 2bb5bbef1..1b826f3dc 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -511,7 +511,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), FALSE); + next = store_get(sizeof(ip_address_item), GET_UNTAINTED); next->next = NULL; next->port = 0; (void)host_ntoa(-1, ifa_addr, next->address, NULL); @@ -746,7 +746,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), FALSE); + next = store_get(sizeof(ip_address_item), GET_UNTAINTED); next->next = NULL; next->port = 0; (void)host_ntoa(-1, addrp, next->address, NULL); @@ -778,13 +778,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), FALSE); +ip_address_item *yield = store_get(sizeof(address_item), GET_UNTAINTED); yield->address = US"127.0.0.1"; yield->port = 0; yield->next = NULL; #if HAVE_IPV6 -yield->next = store_get(sizeof(address_item), FALSE); +yield->next = store_get(sizeof(address_item), GET_UNTAINTED); yield->next->address = US"::1"; yield->next->port = 0; yield->next->next = NULL; diff --git a/src/src/parse.c b/src/src/parse.c index cf673e716..3a7a4f18a 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -22,22 +22,25 @@ redundant apparatus. */ #ifdef STAND_ALONE -address_item *deliver_make_addr(uschar *address, BOOL copy) +address_item * +deliver_make_addr(uschar *address, BOOL copy) { -address_item *addr = store_get(sizeof(address_item), FALSE); +address_item *addr = store_get(sizeof(address_item), GET_UNTAINTED); addr->next = NULL; addr->parent = NULL; addr->address = address; return addr; } -uschar *rewrite_address(uschar *recipient, BOOL dummy1, BOOL dummy2, rewrite_rule +uschar * +rewrite_address(uschar *recipient, BOOL dummy1, BOOL dummy2, rewrite_rule *dummy3, int dummy4) { return recipient; } -uschar *rewrite_address_qualify(uschar *recipient, BOOL dummy1) +uschar * +rewrite_address_qualify(uschar *recipient, BOOL dummy1) { return recipient; } @@ -627,7 +630,7 @@ uschar * parse_extract_address(const uschar *mailbox, uschar **errorptr, int *start, int *end, int *domain, BOOL allow_null) { -uschar *yield = store_get(Ustrlen(mailbox) + 1, is_tainted(mailbox)); +uschar * yield = store_get(Ustrlen(mailbox) + 1, mailbox); const uschar *startptr, *endptr; const uschar *s = US mailbox; uschar *t = US yield; @@ -994,11 +997,9 @@ if (i < len) /* No non-printers; use the RFC 822 quoting rules */ if (len <= 0 || len >= INT_MAX/4) - { - return string_copy_taint(CUS"", is_tainted(phrase)); - } + return string_copy_taint(CUS"", phrase); -buffer = store_get((len+1)*4, is_tainted(phrase)); +buffer = store_get((len+1)*4, phrase); s = phrase; end = s + len; @@ -1545,7 +1546,7 @@ for (;;) return FF_ERROR; } - filebuf = store_get(statbuf.st_size + 1, is_tainted(filename)); + filebuf = store_get(statbuf.st_size + 1, filename); if (fread(filebuf, 1, statbuf.st_size, f) != statbuf.st_size) { *error = string_sprintf("error while reading included file %s: %s", @@ -1620,7 +1621,7 @@ for (;;) if ((*s_ltd == '|' || *s_ltd == '/') && (!recipient || domain == 0)) { - uschar * t = store_get(Ustrlen(s_ltd) + 1, is_tainted(s_ltd)); + uschar * t = store_get(Ustrlen(s_ltd) + 1, s_ltd); uschar * p = t, * q = s_ltd; while (*q) @@ -1658,7 +1659,7 @@ for (;;) if (syntax_errors) { - error_block * e = store_get(sizeof(error_block), FALSE); + error_block * e = store_get(sizeof(error_block), GET_UNTAINTED); error_block * last = *syntax_errors; if (last) { @@ -1738,7 +1739,7 @@ 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. */ reset_point = store_mark(); -id = *yield = store_get(Ustrlen(str) + 1, is_tainted(str)); +id = *yield = store_get(Ustrlen(str) + 1, str); *id++ = *str++; str = read_addr_spec(str, id, '>', error, &domain); diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 3aa4360da..13218df8c 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -248,7 +248,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), FALSE); +pdkim_stringlist * new_entry = store_get(sizeof(pdkim_stringlist), GET_UNTAINTED); memset(new_entry, 0, sizeof(pdkim_stringlist)); new_entry->value = string_copy(str); @@ -338,7 +338,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, TRUE); /* tainted */ +uschar * relaxed = store_get(len+3, GET_TAINTED); uschar * q = relaxed; for (const uschar * p = header; p - header < len; p++) @@ -418,7 +418,7 @@ pdkim_decode_qp(const uschar * str) int nchar = 0; uschar * q; const uschar * p = str; -uschar * n = store_get(Ustrlen(str)+1, TRUE); +uschar * n = store_get(Ustrlen(str)+1, GET_TAINTED); *n = '\0'; q = n; @@ -475,7 +475,7 @@ BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; -sig = store_get(sizeof(pdkim_signature), FALSE); +sig = store_get(sizeof(pdkim_signature), GET_UNTAINTED); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -484,7 +484,7 @@ sig->version = 0; sig->keytype = -1; sig->hashtype = -1; -q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1, TRUE); /* tainted */ +q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1, GET_TAINTED); for (uschar * p = raw_hdr; ; p++) { @@ -664,7 +664,7 @@ const uschar * ele; int sep = ';'; pdkim_pubkey * pub; -pub = store_get(sizeof(pdkim_pubkey), TRUE); /* tainted */ +pub = store_get(sizeof(pdkim_pubkey), GET_TAINTED); memset(pub, 0, sizeof(pdkim_pubkey)); while ((ele = string_nextinlist(&raw_record, &sep, NULL, 0))) @@ -1919,14 +1919,14 @@ pdkim_init_verify(uschar * (*dns_txt_callback)(const uschar *), BOOL dot_stuffin { pdkim_ctx * ctx; -ctx = store_get(sizeof(pdkim_ctx), FALSE); +ctx = store_get(sizeof(pdkim_ctx), GET_UNTAINTED); memset(ctx, 0, sizeof(pdkim_ctx)); if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM; /* The line-buffer is for message data, hence tainted */ -ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE); +ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, GET_TAINTED); ctx->dns_txt_callback = dns_txt_callback; -ctx->cur_header = string_get_tainted(36, TRUE); +ctx->cur_header = string_get_tainted(36, GET_TAINTED); return ctx; } @@ -1947,7 +1947,7 @@ if (!domain || !selector || !privkey) /* Allocate & init one signature struct */ -sig = store_get(sizeof(pdkim_signature), FALSE); +sig = store_get(sizeof(pdkim_signature), GET_UNTAINTED); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -2035,7 +2035,7 @@ for (b = ctx->bodyhash; b; b = b->next) DEBUG(D_receive) debug_printf("DKIM: new bodyhash %d/%d/%ld\n", hashtype, canon_method, bodylength); -b = store_get(sizeof(pdkim_bodyhash), FALSE); +b = store_get(sizeof(pdkim_bodyhash), GET_UNTAINTED); b->next = ctx->bodyhash; b->hashtype = hashtype; b->canon_method = canon_method; @@ -2080,7 +2080,7 @@ 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; /* The line buffer is for message data, hence tainted */ -ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, TRUE); +ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN, GET_TAINTED); DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback; } diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index 561eb6161..a71d63692 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -507,7 +507,7 @@ switch (hash) } #define SIGSPACE 128 -sig->data = store_get(SIGSPACE, FALSE); +sig->data = store_get(SIGSPACE, GET_UNTAINTED); if (gcry_mpi_cmp (sign_ctx->p, sign_ctx->q) > 0) { @@ -766,7 +766,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, FALSE)) + && (sig->data = store_get(siglen, GET_UNTAINTED)) /* Obtain the signature (slen could change here!) */ && EVP_DigestSign(ctx, sig->data, &siglen, data->data, data->len) > 0 @@ -782,7 +782,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, FALSE)) + && (sig->data = store_get(siglen, GET_UNTAINTED)) /* Obtain the signature (slen could change here!) */ && EVP_DigestSignFinal(ctx, sig->data, &siglen) > 0 diff --git a/src/src/queue.c b/src/src/queue.c index dff6168c0..2933c5439 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -214,8 +214,8 @@ for (; i <= *subcount; i++) (*pcount)++; else { - queue_filename *next = - store_get(sizeof(queue_filename) + Ustrlen(name), is_tainted(name)); + queue_filename * next = + store_get(sizeof(queue_filename) + Ustrlen(name), name); Ustrcpy(next->text, name); next->dir_uschar = subdirchar; @@ -901,8 +901,8 @@ if (count > 0) queue_filename *last = NULL; for (int i = 0; i < count; i++) { - queue_filename *next = - store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2, is_tainted(list[i])); + queue_filename * next = + store_get(sizeof(queue_filename) + Ustrlen(list[i]) + 2, list[i]); sprintf(CS next->text, "%s-H", list[i]); next->dir_uschar = '*'; next->next = NULL; diff --git a/src/src/rda.c b/src/src/rda.c index dba2e20fb..10a44c15c 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -284,7 +284,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, is_tainted(filename)); +filebuf = store_get(statbuf.st_size + 1, filename); if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size) { @@ -476,7 +476,7 @@ else /* We know we have enough memory so disable the error on "len" */ /* coverity[tainted_data] */ /* We trust the data source, so untainted */ - if (read(fd, *sp = store_get(len, FALSE), len) != len) return FALSE; + if (read(fd, *sp = store_get(len, GET_UNTAINTED), len) != len) return FALSE; return TRUE; } @@ -808,7 +808,7 @@ if (eblockp) uschar *s; if (!rda_read_string(fd, &s)) goto DISASTER; if (!s) break; - e = store_get(sizeof(error_block), FALSE); + e = store_get(sizeof(error_block), GET_UNTAINTED); e->next = NULL; e->text1 = s; if (!rda_read_string(fd, &s)) goto DISASTER; @@ -907,7 +907,7 @@ if (yield == FF_DELIVERED || yield == FF_NOTDELIVERED || if (i > 0) { - addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), FALSE); + addr->pipe_expandn = store_get((i+1) * sizeof(uschar *), GET_UNTAINTED); addr->pipe_expandn[i] = NULL; while (--i >= 0) addr->pipe_expandn[i] = expandn[i]; } @@ -917,7 +917,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), FALSE); + addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED); addr->reply->file_expand = (reply_options & REPLY_EXPAND) != 0; addr->reply->return_message = (reply_options & REPLY_RETURN) != 0; diff --git a/src/src/readconf.c b/src/src/readconf.c index 763a0c5f0..dc43d30f5 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -646,7 +646,7 @@ Args: macro_item * macro_create(const uschar * name, const uschar * val, BOOL command_line) { -macro_item * m = store_get(sizeof(macro_item), FALSE); +macro_item * m = store_get(sizeof(macro_item), GET_UNTAINTED); READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val); m->next = NULL; @@ -1076,7 +1076,7 @@ for (;;) if (config_lines) save_config_position(config_filename, config_lineno); - save = store_get(sizeof(config_file_item), FALSE); + save = store_get(sizeof(config_file_item), GET_UNTAINTED); save->next = config_file_stack; config_file_stack = save; save->file = config_file; @@ -1425,7 +1425,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), FALSE); +rewrite_rule * next = store_get(sizeof(rewrite_rule), GET_UNTAINTED); next->next = NULL; next->key = string_dequote(&p); @@ -3021,7 +3021,7 @@ if (*numberp >= max) Uskip_whitespace(&s); ss = s; while (isalnum(*s) || *s == '_') s++; -t = store_get(sizeof(tree_node) + s-ss, is_tainted(ss)); +t = store_get(sizeof(tree_node) + s-ss, ss); Ustrncpy(t->name, ss, s-ss); t->name[s-ss] = 0; Uskip_whitespace(&s); @@ -3279,7 +3279,7 @@ if (f.trusted_config && Ustrcmp(filename, US"/dev/null")) if (statbuf.st_size > 8192) { rmark r = store_mark(); - void * dummy = store_get((int)statbuf.st_size, FALSE); + void * dummy = store_get((int)statbuf.st_size, GET_UNTAINTED); store_reset(r); } } @@ -3769,6 +3769,8 @@ while ((buffer = get_config_line())) *p = d; p = &d->next; d->name = string_copy(name); + d->srcfile = config_filename; + d->srcline = config_lineno; /* Clear out the "set" bits in the generic options */ @@ -4056,7 +4058,7 @@ while ((p = get_config_line())) const uschar *pp; uschar *error; - next = store_get(sizeof(retry_config), FALSE); + next = store_get(sizeof(retry_config), GET_UNTAINTED); next->next = NULL; *chain = next; chain = &(next->next); @@ -4100,7 +4102,7 @@ while ((p = get_config_line())) while (*p) { - retry_rule *rule = store_get(sizeof(retry_rule), FALSE); + retry_rule * rule = store_get(sizeof(retry_rule), GET_UNTAINTED); *rchain = rule; rchain = &(rule->next); rule->next = NULL; @@ -4194,6 +4196,18 @@ f.smtp_in_early_pipe_no_auth = nauths > 16; } +/* For error messages, a string describing the config location associated +with current processing. NULL if we are not in an authenticator. */ + +uschar * +authenticator_current_name(void) +{ +if (!authenticator_name) return NULL; +return string_sprintf(" (authenticator %s, %s %d)", authenticator_name, driver_srcfile, driver_srcline); +} + + + /************************************************* @@ -4250,7 +4264,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_perm(sizeof(tree_node) + Ustrlen(name), is_tainted(name)); + node = store_get_perm(sizeof(tree_node) + Ustrlen(name), name); Ustrcpy(node->name, name); if (!tree_insertnode(&acl_anchor, node)) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, @@ -4395,7 +4409,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), FALSE); +next = (config_line_item*) store_get(sizeof(config_line_item), GET_UNTAINTED); next->line = string_copy(line); next->next = NULL; diff --git a/src/src/receive.c b/src/src/receive.c index 058a74cb2..ba84967c1 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -527,7 +527,7 @@ if (recipients_count >= recipients_list_max) } recipients_list_max = recipients_list_max ? 2*recipients_list_max : 50; - recipients_list = store_get(recipients_list_max * sizeof(recipient_item), FALSE); + recipients_list = store_get(recipients_list_max * sizeof(recipient_item), GET_UNTAINTED); if (oldlist) memcpy(recipients_list, oldlist, oldmax * sizeof(recipient_item)); } @@ -1760,17 +1760,18 @@ 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), FALSE); +received_header = header_list = header_last = store_get(sizeof(header_line), GET_UNTAINTED); header_list->next = NULL; header_list->type = htype_old; header_list->text = NULL; header_list->slen = 0; -/* Control block for the next header to be read. */ +/* Control block for the next header to be read. +The data comes from the message, so is tainted. */ reset_point = store_mark(); -next = store_get(sizeof(header_line), FALSE); /* not tainted */ -next->text = store_get(header_size, TRUE); /* tainted */ +next = store_get(sizeof(header_line), GET_UNTAINTED); +next->text = store_get(header_size, GET_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 @@ -1917,10 +1918,8 @@ for (;;) goto OVERSIZE; header_size *= 2; - /* 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); + if (!store_extend(next->text, oldsize, header_size)) + next->text = store_newblock(next->text, header_size, ptr); } /* Cope with receiving a binary zero. There is dispute about whether @@ -2309,8 +2308,8 @@ OVERSIZE: reset_point = store_mark(); header_size = 256; - next = store_get(sizeof(header_line), FALSE); - next->text = store_get(header_size, TRUE); + next = store_get(sizeof(header_line), GET_UNTAINTED); + next->text = store_get(header_size, GET_TAINTED); ptr = 0; had_zero = 0; prevlines_length = 0; @@ -2594,7 +2593,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, is_tainted(s)); + pp = recipient = store_get(ss - s + 1, s); for (uschar * p = s; p < ss; p++) if (*p != '\n') *pp++ = *p; *pp = 0; @@ -2626,7 +2625,7 @@ if (extract_recip) if (!recipient && Ustrcmp(errmess, "empty address") != 0) { int len = Ustrlen(s); - error_block *b = store_get(sizeof(error_block), FALSE); + error_block * b = store_get(sizeof(error_block), GET_UNTAINTED); while (len > 0 && isspace(s[len-1])) len--; b->next = NULL; b->text1 = string_printing(string_copyn(s, len)); @@ -2825,7 +2824,7 @@ function may mess with the real recipients. */ if (LOGGING(received_recipients)) { - raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE); + raw_recipients = store_get(recipients_count * sizeof(uschar *), GET_UNTAINTED); for (int i = 0; i < recipients_count; i++) raw_recipients[i] = string_copy(recipients_list[i].address); raw_recipients_count = recipients_count; diff --git a/src/src/regex.c b/src/src/regex.c index 2b3e308a2..c1acf4c6e 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -55,7 +55,7 @@ while ((regex_string = string_nextinlist(&list, &sep, NULL, 0))) continue; } - ri = store_get(sizeof(pcre_list), FALSE); + ri = store_get(sizeof(pcre_list), GET_UNTAINTED); ri->re = re; ri->pcre_text = regex_string; ri->next = re_list_head; @@ -133,7 +133,7 @@ if (!(re_list_head = compile(*listptr))) return FAIL; /* no regexes -> nothing to do */ /* match each line against all regexes */ -linebuffer = store_get(32767, TRUE); /* tainted */ +linebuffer = store_get(32767, GET_TAINTED); while (fgets(CS linebuffer, 32767, mbox_file)) { if ( mime_stream && mime_current_boundary /* check boundary */ @@ -204,7 +204,7 @@ if (!(f = fopen(CS mime_decoded_filename, "rb"))) } /* get 32k memory, tainted */ -mime_subject = store_get(32767, TRUE); +mime_subject = store_get(32767, GET_TAINTED); mime_subject_len = fread(mime_subject, 1, 32766, f); diff --git a/src/src/retry.c b/src/src/retry.c index 1993b7768..812738810 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -292,7 +292,7 @@ Returns: nothing void retry_add_item(address_item *addr, uschar *key, int flags) { -retry_item *rti = store_get(sizeof(retry_item), FALSE); +retry_item * rti = store_get(sizeof(retry_item), GET_UNTAINTED); host_item * host = addr->host_used; rti->next = addr->retries; @@ -669,7 +669,7 @@ for (int i = 0; i < 3; i++) if (!retry_record) { retry_record = store_get(sizeof(dbdata_retry) + message_length, - is_tainted(message)); + message); message_space = message_length; retry_record->first_failed = now; retry_record->last_try = now; @@ -815,7 +815,7 @@ for (int i = 0; i < 3; i++) if (message_length > message_space) { dbdata_retry * newr = - store_get(sizeof(dbdata_retry) + message_length, is_tainted(message)); + store_get(sizeof(dbdata_retry) + message_length, message); memcpy(newr, retry_record, sizeof(dbdata_retry)); retry_record = newr; } diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 42a63048f..9420e79b9 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -603,7 +603,7 @@ while (*s) int oldlen = end - start; header_line * prev = newh ? newh : h; - uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, TRUE); + uschar * newt = store_get_perm(prev->slen - oldlen + newlen + 4, GET_TAINTED); uschar * newtstart = newt; int type = prev->type; @@ -667,7 +667,7 @@ while (*s) store_reset(function_reset_point); function_reset_point = store_mark(); - newh = store_get(sizeof(header_line), FALSE); + newh = store_get(sizeof(header_line), GET_UNTAINTED); newh->type = type; newh->slen = slen; newh->text = string_copyn(newtstart, slen); diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index 4679d7677..1138d2dbe 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -47,7 +47,7 @@ rfc2047_qpdecode(uschar *string, uschar **ptrptr) int len = 0; uschar *ptr; -ptr = *ptrptr = store_get(Ustrlen(string) + 1, is_tainted(string)); /* No longer than this */ +ptr = *ptrptr = store_get(Ustrlen(string) + 1, string); /* No longer than this */ while (*string != 0) { @@ -209,7 +209,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, is_tainted(string)); +yield = store_get(sizeof(gstring) + ++size, string); yield->size = size; yield->ptr = 0; yield->s = US(yield + 1); diff --git a/src/src/route.c b/src/src/route.c index 225a19046..ae04c7917 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -1499,7 +1499,7 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) if (!(node = tree_search(*root, name))) { /* name should never be tainted */ - node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE); + node = store_get(sizeof(tree_node) + Ustrlen(name), GET_UNTAINTED); Ustrcpy(node->name, name); (void)tree_insertnode(root, node); } @@ -1693,7 +1693,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) int lplen = Ustrlen(addr->local_part) - slen; addr->suffix = vlen ? addr->local_part + lplen - : string_copy_taint(addr->local_part + lplen, slen); + : string_copy_taint(addr->local_part + lplen, GET_UNTAINTED); addr->suffix_v = addr->suffix + Ustrlen(addr->suffix) - vlen; addr->local_part = string_copyn(addr->local_part, lplen); DEBUG(D_route) debug_printf("stripped suffix %s\n", addr->suffix); @@ -1710,6 +1710,8 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) the local part sorted. */ router_name = r->name; + driver_srcfile = r->srcfile; + driver_srcline = r->srcline; deliver_set_expansions(addr); /* For convenience, the pre-router checks are in a separate function, which @@ -1717,7 +1719,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) if ((rc = check_router_conditions(r, addr, verify, &pw, &error)) != OK) { - router_name = NULL; + driver_srcfile = router_name = NULL; driver_srcline = 0; if (rc == SKIP) continue; addr->message = error; yield = rc; @@ -1768,7 +1770,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { DEBUG(D_route) debug_printf("\"more\"=false: skipping remaining routers\n"); - router_name = NULL; + driver_srcfile = router_name = NULL; driver_srcline = 0; r = NULL; break; } @@ -1820,7 +1822,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote, addr_new, addr_succeed); - router_name = NULL; + driver_srcfile = router_name = NULL; driver_srcline = 0; if (yield == FAIL) { @@ -2048,10 +2050,23 @@ if (yield == DEFER && addr->message) addr->message = expand_hide_passwords(addr->message); deliver_set_expansions(NULL); -router_name = NULL; +driver_srcfile = router_name = NULL; driver_srcline = 0; f.disable_logging = FALSE; return yield; } + + +/* For error messages, a string describing the config location associated +with current processing. NULL if we are not in a router. */ +/* Name only, for now */ + +uschar * +router_current_name(void) +{ +if (!router_name) return NULL; +return string_sprintf(" (router %s, %s %d)", router_name, driver_srcfile, driver_srcline); +} + #endif /*!MACRO_PREDEF*/ /* End of route.c */ diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index 977b58d45..807191b91 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -456,7 +456,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), FALSE); +addr->host_list = store_get(sizeof(host_item), GET_UNTAINTED); addr->host_list[0] = h; /* Fill in the transport and queue the address for delivery. */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index 80f377970..f2ec486fb 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -146,7 +146,7 @@ if (verify_check_this_host(CUSS&rblock->ignore_target_hosts, /* Set up a host item */ -h = store_get(sizeof(host_item), FALSE); +h = store_get(sizeof(host_item), GET_UNTAINTED); h->next = NULL; h->address = string_copy(ip); diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index 2237d0386..bbef1c063 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -161,7 +161,7 @@ uschar *reply; uschar *hostname, *reroute, *domain; const uschar *listptr; uschar host_buffer[256]; -host_item *host = store_get(sizeof(host_item), FALSE); +host_item *host = store_get(sizeof(host_item), GET_UNTAINTED); address_item *new_addr; iplookup_router_options_block *ob = (iplookup_router_options_block *)(rblock->options_block); @@ -172,7 +172,7 @@ int sep = 0; DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n", rblock->name, addr->address, addr->domain); -reply = store_get(256, TRUE); /* tainted data */ +reply = store_get(256, GET_TAINTED); /* Build the query string to send. If not explicitly given, a default of "user@domain user@domain" is used. */ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index e3ef1eadb..12d87a689 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -399,7 +399,7 @@ if (transport && transport->info->local) if (hostlist[0]) { host_item *h; - addr->host_list = h = store_get(sizeof(host_item), FALSE); + addr->host_list = h = store_get(sizeof(host_item), GET_UNTAINTED); h->name = string_copy(hostlist); h->address = NULL; h->port = PORT_NONE; diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 7d2473758..bcad39402 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -676,7 +676,7 @@ switch (frc) if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors) { - eblock = store_get(sizeof(error_block), FALSE); + eblock = store_get(sizeof(error_block), GET_UNTAINTED); eblock->next = NULL; eblock->text1 = addr->message; eblock->text2 = NULL; diff --git a/src/src/routers/rf_change_domain.c b/src/src/routers/rf_change_domain.c index 39c41fc9d..e3a623aec 100644 --- a/src/src/routers/rf_change_domain.c +++ b/src/src/routers/rf_change_domain.c @@ -35,9 +35,9 @@ void rf_change_domain(address_item *addr, const uschar *domain, BOOL rewrite, address_item **addr_new) { -address_item *parent = store_get(sizeof(address_item), FALSE); -uschar *at = Ustrrchr(addr->address, '@'); -uschar *address = string_sprintf("%.*s@%s", +address_item * parent = store_get(sizeof(address_item), GET_UNTAINTED); +uschar * at = Ustrrchr(addr->address, '@'); +uschar * address = string_sprintf("%.*s@%s", (int)(at - addr->address), addr->address, domain); DEBUG(D_route) debug_printf("domain changed to %s\n", domain); diff --git a/src/src/routers/rf_get_munge_headers.c b/src/src/routers/rf_get_munge_headers.c index 5f0bbc993..4cd1752d4 100644 --- a/src/src/routers/rf_get_munge_headers.c +++ b/src/src/routers/rf_get_munge_headers.c @@ -59,7 +59,7 @@ if (rblock->extra_headers) shared with other addresses. The output function outputs them in reverse order. */ - header_line * h = store_get(sizeof(header_line), FALSE); + header_line * h = store_get(sizeof(header_line), GET_UNTAINTED); /* 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, is_tainted(s)); + h->text = store_get(slen+2, s); memcpy(h->text, s, slen); h->text[slen++] = '\n'; h->text[slen] = 0; diff --git a/src/src/search.c b/src/src/search.c index d93397f09..979dae739 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -476,8 +476,8 @@ count alone. */ if (!t) { - t = store_get(sizeof(tree_node) + Ustrlen(keybuffer), FALSE); - t->data.ptr = c = store_get(sizeof(search_cache), FALSE); + t = store_get(sizeof(tree_node) + Ustrlen(keybuffer), GET_UNTAINTED); + t->data.ptr = c = store_get(sizeof(search_cache), GET_UNTAINTED); c->item_cache = NULL; Ustrcpy(t->name, keybuffer); tree_insertnode(&search_tree, t); @@ -578,6 +578,47 @@ else filename ? US"file" : US"database", keystring, filename ? US"\n in " : US"", filename ? filename : US""); + if (!filename && is_tainted(keystring)) + { + debug_printf_indent(" "); + debug_print_taint(keystring); + } + } + + /* Check that the query, for query-style lookups, + is either untainted or properly quoted for the lookup type. + + XXX Should we this move into lf_sqlperform() ? The server-taint check is there. + */ + + if ( !filename && lookup_list[search_type]->quote + && is_tainted(keystring) && !is_quoted_like(keystring, search_type)) + { + uschar * s = acl_current_verb(); + if (!s) s = authenticator_current_name(); /* must be before transport */ + if (!s) s = transport_current_name(); /* must be before router */ + if (!s) s = router_current_name(); /* GCC ?: would be good, but not in clang */ + if (!s) s = US""; +#ifdef enforce_quote_protection_notyet + search_error_message = string_sprintf( + "tainted search query is not properly quoted%s: %s%s", + s, keystring); + f.search_find_defer = TRUE; +#else + { + int q = quoter_for_address(keystring); + /* If we're called from a transport, no privs to open the paniclog; + the logging punts to using stderr - and that seems to stop the debug + stream. */ + log_write(0, + transport_name ? LOG_MAIN : LOG_MAIN|LOG_PANIC, + "tainted search query is not properly quoted%s: %s", s, keystring); + + DEBUG(D_lookup) debug_printf_indent("search_type %d (%s) quoting %d (%s)\n", + search_type, lookup_list[search_type]->name, + q, is_real_quoter(q) ? lookup_list[q]->name : US"none"); + } +#endif } /* Call the code for the different kinds of search. DEFER is handled @@ -585,7 +626,7 @@ else distinguish if necessary. */ if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength, - &data, &search_error_message, &do_cache, opts) == DEFER) + &data, &search_error_message, &do_cache, opts) == DEFER) f.search_find_defer = TRUE; /* A record that has been found is now in data, which is either NULL @@ -603,8 +644,8 @@ else if (!t) /* No existing entry. Create new one. */ { int len = keylength + 1; - e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, - is_tainted(keystring)); + /* The cache node value should never be expanded so use tainted mem */ + e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, GET_TAINTED); t = (tree_node *)(e+1); memcpy(t->name, keystring, len); t->data.ptr = e; @@ -777,7 +818,7 @@ else if (partial >= 0) if (affixlen == 0) keystring2 = keystring; else { keystring2 = store_get(len + affixlen + 1, - is_tainted(keystring) || is_tainted(affix)); + is_tainted(keystring) || is_tainted(affix) ? GET_TAINTED : GET_UNTAINTED); Ustrncpy(keystring2, affix, affixlen); Ustrcpy(keystring2 + affixlen, keystring); DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2); diff --git a/src/src/sieve.c b/src/src/sieve.c index f693ebf4e..d1f5d93c8 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -245,7 +245,7 @@ for (int pass = 0; pass <= 1; pass++) dst->length=0; else { - dst->character = store_get(dst->length+1, is_tainted(src->character)); /* plus one for \0 */ + dst->character = store_get(dst->length+1, src->character); /* plus one for \0 */ new=dst->character; } for (const uschar * start = src->character, * end = start + src->length; @@ -444,16 +444,16 @@ if (*uri && *uri!='?') filter->errmsg=US"Invalid URI encoding"; return -1; } - new=store_get(sizeof(string_item), FALSE); - new->text = store_get(to.length+1, is_tainted(to.character)); + new = store_get(sizeof(string_item), GET_UNTAINTED); + new->text = store_get(to.length+1, to.character); if (to.length) memcpy(new->text, to.character, to.length); - new->text[to.length]='\0'; - new->next=*recipient; - *recipient=new; + new->text[to.length] = '\0'; + new->next = *recipient; + *recipient = new; } else { - filter->errmsg=US"Missing addr-spec in URI"; + filter->errmsg = US"Missing addr-spec in URI"; return -1; } if (*uri=='%') uri+=3; @@ -502,8 +502,8 @@ if (*uri=='?') } if (hname.length==2 && strcmpic(hname.character, US"to")==0) { - new=store_get(sizeof(string_item), FALSE); - new->text = store_get(hvalue.length+1, is_tainted(hvalue.character)); + new=store_get(sizeof(string_item), GET_UNTAINTED); + new->text = store_get(hvalue.length+1, hvalue.character); if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length); new->text[hvalue.length]='\0'; new->next=*recipient; @@ -1729,7 +1729,7 @@ if (*filter->pc=='[') /* string list */ struct String *new; dataCapacity = dataCapacity ? dataCapacity * 2 : 4; - new = store_get(sizeof(struct String) * dataCapacity, FALSE); + new = store_get(sizeof(struct String) * dataCapacity, GET_UNTAINTED); if (d) memcpy(new,d,sizeof(struct String)*dataLength); d = new; @@ -1767,7 +1767,7 @@ if (*filter->pc=='[') /* string list */ } else /* single string */ { - if (!(d=store_get(sizeof(struct String)*2, FALSE))) + if (!(d=store_get(sizeof(struct String)*2, GET_UNTAINTED))) return -1; m=parse_string(filter,&d[0]); @@ -3073,7 +3073,7 @@ while (*filter->pc) if (!already) /* New notification, process it */ { - struct Notification * sent = store_get(sizeof(struct Notification), FALSE); + struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED); sent->method=method; sent->importance=importance; sent->message=message; @@ -3212,9 +3212,9 @@ while (*filter->pc) } for (struct String * a = addresses; a->length != -1; ++a) { - string_item * new = store_get(sizeof(string_item), FALSE); + string_item * new = store_get(sizeof(string_item), GET_UNTAINTED); - new->text = store_get(a->length+1, is_tainted(a->character)); + new->text = store_get(a->length+1, a->character); if (a->length) memcpy(new->text,a->character,a->length); new->text[a->length]='\0'; new->next=aliases; @@ -3327,7 +3327,7 @@ while (*filter->pc) addr->prop.ignore_error = TRUE; addr->next = *generated; *generated = addr; - addr->reply = store_get(sizeof(reply_item), FALSE); + addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED); memset(addr->reply,0,sizeof(reply_item)); /* XXX */ addr->reply->to = string_copy(sender_address); if (from.length==-1) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 9efc816fa..852148fcf 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -342,7 +342,7 @@ if (!sender_address /* No transaction in progress */ if (recipients_count > 0) { - raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE); + raw_recipients = store_get(recipients_count * sizeof(uschar *), GET_UNTAINTED); for (int i = 0; i < recipients_count; i++) raw_recipients[i] = recipients_list[i].address; raw_recipients_count = recipients_count; @@ -2572,7 +2572,7 @@ acl_var_c = NULL; /* Allow for trailing 0 in the command and data buffers. Tainted. */ -smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE); +smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, GET_TAINTED); smtp_cmd_buffer[0] = 0; smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1; @@ -2682,7 +2682,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, FALSE); + struct ip_options *ipopt = store_get(optlen, GET_UNTAINTED); #elif OPTSTYLE == 2 struct ip_opts ipoptblock; struct ip_opts *ipopt = &ipoptblock; @@ -3777,6 +3777,12 @@ smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss) const uschar *set_id = NULL; int rc; +/* Set up globals for error messages */ + +authenticator_name = au->name; +driver_srcfile = au->srcfile; +driver_srcline = au->srcline; + /* Run the checking code, passing the remainder of the command line as data. Initials the $auth variables as empty. Initialize $0 empty and set it as the only set numerical variable. The authenticator may set $auth @@ -3797,6 +3803,7 @@ rc = (au->info->servercode)(au, smtp_cmd_data); if (au->set_id) set_id = expand_string(au->set_id); expand_nmax = -1; /* Reset numeric variables */ for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth */ +driver_srcfile = authenticator_name = NULL; driver_srcline = 0; /* The value of authenticated_id is stored in the spool file and printed in log lines. It must not contain binary zeros or newline characters. In @@ -4369,7 +4376,7 @@ while (done <= 0) if (!user_msg) { /* sender_host_name below will be tainted, so save on copy when we hit it */ - g = string_get_tainted(24, TRUE); + g = string_get_tainted(24, GET_TAINTED); g = string_fmt_append(g, "%.3s %s Hello %s%s%s", smtp_code, smtp_active_hostname, diff --git a/src/src/spam.c b/src/src/spam.c index e3316ed96..614c9ecc7 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -269,7 +269,7 @@ start = time(NULL); uschar * s; DEBUG(D_acl) debug_printf_indent("spamd: addr entry '%s'\n", address); - sd = store_get(sizeof(spamd_address_container), FALSE); + sd = store_get(sizeof(spamd_address_container), GET_UNTAINTED); for (sublist = address, args = 0, spamd_param_init(sd); (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0)); diff --git a/src/src/spool_in.c b/src/src/spool_in.c index de6ee7e51..940baa419 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -186,7 +186,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, TRUE); /* rcpt names tainted */ +node = store_get(sizeof(tree_node) + n - 3, GET_TAINTED); /* rcpt names tainted */ *connect = node; Ustrcpy(node->name, buffer + 3); node->data.ptr = NULL; @@ -442,7 +442,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, TRUE); /* tainted */ +sender_address = store_get(n-2, GET_TAINTED); Ustrncpy(sender_address, big_buffer+1, n-3); sender_address[n-3] = 0; @@ -479,11 +479,13 @@ 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. -If the line starts with "--" the content of the variable is tainted. */ +If the line starts with "--" the content of the variable is tainted. +If the line start "--()" it is also quoted for the given . +*/ for (;;) { - BOOL tainted; + const void * proto_mem; uschar * var; const uschar * p; @@ -491,8 +493,23 @@ for (;;) if (big_buffer[0] != '-') break; big_buffer[Ustrlen(big_buffer)-1] = 0; - tainted = big_buffer[1] == '-'; - var = big_buffer + (tainted ? 2 : 1); + proto_mem = big_buffer[1] == '-' ? GET_TAINTED : GET_UNTAINTED; + var = big_buffer + (proto_mem == GET_UNTAINTED ? 1 : 2); + if (*var == '(') /* marker for quoted value */ + { + uschar * s; + int idx; + for (s = ++var; *s != ')'; ) s++; +#ifndef COMPILE_UTILITY + if ((idx = search_findtype(var, s - var)) < 0) + { + DEBUG(D_any) debug_printf("Unrecognised quoter %.*s\n", (int)(s - var), var+1); + goto SPOOL_FORMAT_ERROR; + } + proto_mem = store_get_quoted(1, GET_TAINTED, idx); +#endif /* COMPILE_UTILITY */ + var = s + 1; + } p = var + 1; switch(*var) @@ -516,7 +533,7 @@ for (;;) (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, tainted); + node->data.ptr = store_get(count + 1, proto_mem); if (fread(node->data.ptr, 1, count+1, fp) < count) goto SPOOL_READ_ERROR; ((uschar*)node->data.ptr)[count] = 0; } @@ -527,11 +544,11 @@ for (;;) f.allow_unqualified_sender = TRUE; else if (Ustrncmp(p, "uth_id", 6) == 0) - authenticated_id = string_copy_taint(var + 8, tainted); + authenticated_id = string_copy_taint(var + 8, proto_mem); else if (Ustrncmp(p, "uth_sender", 10) == 0) - authenticated_sender = string_copy_taint(var + 12, tainted); + authenticated_sender = string_copy_taint(var + 12, proto_mem); else if (Ustrncmp(p, "ctive_hostname", 14) == 0) - smtp_active_hostname = string_copy_taint(var + 16, tainted); + smtp_active_hostname = string_copy_taint(var + 16, proto_mem); /* For long-term backward compatibility, we recognize "-acl", which was used before the number of ACL variables changed from 10 to 20. This was @@ -555,7 +572,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, tainted); + node->data.ptr = store_get(count + 1, proto_mem); /* 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; @@ -570,7 +587,7 @@ for (;;) body_zerocount = Uatoi(var + 14); #ifdef EXPERIMENTAL_BRIGHTMAIL else if (Ustrncmp(p, "mi_verdicts ", 12) == 0) - bmi_verdicts = string_copy_taint(var + 13, tainted); + bmi_verdicts = string_copy_taint(var + 13, proto_mem); #endif break; @@ -581,7 +598,7 @@ for (;;) else if (Ustrncmp(p, "sn_ret", 6) == 0) dsn_ret= atoi(CS var + 7); else if (Ustrncmp(p, "sn_envid", 8) == 0) - dsn_envid = string_copy_taint(var + 10, tainted); + dsn_envid = string_copy_taint(var + 10, proto_mem); break; case 'f': @@ -599,13 +616,13 @@ for (;;) else if (Ustrcmp(p, "ost_lookup_failed") == 0) host_lookup_failed = TRUE; else if (Ustrncmp(p, "ost_auth_pubname", 16) == 0) - sender_host_auth_pubname = string_copy_taint(var + 18, tainted); + sender_host_auth_pubname = string_copy_taint(var + 18, proto_mem); else if (Ustrncmp(p, "ost_auth", 8) == 0) - sender_host_authenticated = string_copy_taint(var + 10, tainted); + sender_host_authenticated = string_copy_taint(var + 10, proto_mem); else if (Ustrncmp(p, "ost_name", 8) == 0) - sender_host_name = string_copy_taint(var + 10, tainted); + sender_host_name = string_copy_taint(var + 10, proto_mem); else if (Ustrncmp(p, "elo_name", 8) == 0) - sender_helo_name = string_copy_taint(var + 10, tainted); + sender_helo_name = string_copy_taint(var + 10, proto_mem); /* We now record the port number after the address, separated by a dot. For compatibility during upgrading, do nothing if there @@ -614,7 +631,7 @@ for (;;) else if (Ustrncmp(p, "ost_address", 11) == 0) { sender_host_port = host_address_extract_port(var + 13); - sender_host_address = string_copy_taint(var + 13, tainted); + sender_host_address = string_copy_taint(var + 13, proto_mem); } break; @@ -622,10 +639,10 @@ for (;;) if (Ustrncmp(p, "nterface_address", 16) == 0) { interface_port = host_address_extract_port(var + 18); - interface_address = string_copy_taint(var + 18, tainted); + interface_address = string_copy_taint(var + 18, proto_mem); } else if (Ustrncmp(p, "dent", 4) == 0) - sender_ident = string_copy_taint(var + 6, tainted); + sender_ident = string_copy_taint(var + 6, proto_mem); break; case 'l': @@ -635,7 +652,7 @@ for (;;) f.local_error_message = TRUE; #ifdef HAVE_LOCAL_SCAN else if (Ustrncmp(p, "ocal_scan ", 10) == 0) - local_scan_data = string_copy_taint(var + 11, tainted); + local_scan_data = string_copy_taint(var + 11, proto_mem); #endif break; @@ -652,7 +669,7 @@ for (;;) case 'r': if (Ustrncmp(p, "eceived_protocol", 16) == 0) - received_protocol = string_copy_taint(var + 18, tainted); + received_protocol = string_copy_taint(var + 18, proto_mem); else if (Ustrncmp(p, "eceived_time_usec", 17) == 0) { unsigned usec; @@ -678,11 +695,11 @@ for (;;) f.sender_set_untrusted = TRUE; #ifdef WITH_CONTENT_SCAN else if (Ustrncmp(p, "pam_bar ", 8) == 0) - spam_bar = string_copy_taint(var + 9, tainted); + spam_bar = string_copy_taint(var + 9, proto_mem); else if (Ustrncmp(p, "pam_score ", 10) == 0) - spam_score = string_copy_taint(var + 11, tainted); + spam_score = string_copy_taint(var + 11, proto_mem); else if (Ustrncmp(p, "pam_score_int ", 14) == 0) - spam_score_int = string_copy_taint(var + 15, tainted); + spam_score_int = string_copy_taint(var + 15, proto_mem); #endif #ifndef COMPILE_UTILITY else if (Ustrncmp(p, "pool_file_wireformat", 20) == 0) @@ -702,7 +719,7 @@ 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_taint(q+7, tainted); + tls_in.cipher = string_copy_taint(q+7, proto_mem); # ifndef COMPILE_UTILITY /* tls support fns not built in */ else if (Ustrncmp(q, "ourcert", 7) == 0) (void) tls_import_cert(q+8, &tls_in.ourcert); @@ -710,9 +727,9 @@ for (;;) (void) tls_import_cert(q+9, &tls_in.peercert); # endif else if (Ustrncmp(q, "peerdn", 6) == 0) - tls_in.peerdn = string_unprinting(string_copy_taint(q+7, tainted)); + tls_in.peerdn = string_unprinting(string_copy_taint(q+7, proto_mem)); else if (Ustrncmp(q, "sni", 3) == 0) - tls_in.sni = string_unprinting(string_copy_taint(q+4, tainted)); + tls_in.sni = string_unprinting(string_copy_taint(q+4, proto_mem)); else if (Ustrncmp(q, "ocsp", 4) == 0) tls_in.ocsp = q[5] - '0'; # ifndef DISABLE_TLS_RESUME @@ -720,7 +737,7 @@ for (;;) tls_in.resumption = q[11] - 'A'; # endif else if (Ustrncmp(q, "ver", 3) == 0) - tls_in.ver = string_copy_taint(q+4, tainted); + tls_in.ver = string_copy_taint(q+4, proto_mem); } break; #endif @@ -775,7 +792,7 @@ DEBUG(D_deliver) debug_printf_indent("recipients_count=%d\n", rcount); #endif /* COMPILE_UTILITY */ recipients_list_max = rcount; -recipients_list = store_get(rcount * sizeof(recipient_item), FALSE); +recipients_list = store_get(rcount * sizeof(recipient_item), GET_UNTAINTED); /* We sanitised the count and know we have enough memory, so disable the Coverity error on recipients_count */ @@ -875,7 +892,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) (void)sscanf(CS p+1, "%d", &flags); - if ((flags & 0x01) != 0) /* one_time data exists */ + if (flags & 0x01) /* one_time data exists */ { int len; while (isdigit(*(--p)) || *p == ',' || *p == '-'); @@ -884,12 +901,12 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) if (len > 0) { p -= len; - errors_to = string_copy_taint(p, TRUE); + errors_to = string_copy_taint(p, GET_TAINTED); } } - *(--p) = 0; /* Terminate address */ - if ((flags & 0x02) != 0) /* one_time data exists */ + *--p = 0; /* Terminate address */ + if (flags & 0x02) /* one_time data exists */ { int len; while (isdigit(*(--p)) || *p == ',' || *p == '-'); @@ -898,11 +915,11 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) if (len > 0) { p -= len; - orcpt = string_copy_taint(p, TRUE); + orcpt = string_copy_taint(p, GET_TAINTED); } } - *(--p) = 0; /* Terminate address */ + *--p = 0; /* Terminate address */ } #if !defined(COMPILE_UTILITY) else @@ -916,7 +933,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) big_buffer, errors_to); #endif - recipients_list[recipients_count].address = string_copy_taint(big_buffer, TRUE); + recipients_list[recipients_count].address = string_copy_taint(big_buffer, GET_TAINTED); recipients_list[recipients_count].pno = pno; recipients_list[recipients_count].errors_to = errors_to; recipients_list[recipients_count].orcpt = orcpt; @@ -948,11 +965,11 @@ while ((n = fgetc(fp)) != EOF) if (read_headers) { - h = store_get(sizeof(header_line), FALSE); + h = store_get(sizeof(header_line), GET_UNTAINTED); h->next = NULL; h->type = flag[0]; h->slen = n; - h->text = store_get(n+1, TRUE); /* tainted */ + h->text = store_get(n+1, GET_TAINTED); if (h->type == htype_received) received_count++; @@ -1055,7 +1072,7 @@ if ( Ufgets(big_buffer, big_buffer_size, fp) != NULL && big_buffer[0] == '<' && big_buffer[n-2] == '>' ) { - yield = store_get(n-2, TRUE); /* tainted */ + yield = store_get(n-2, GET_TAINTED); Ustrncpy(yield, big_buffer+1, n-3); yield[n-3] = 0; } diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 13e8dd664..d5cad86d4 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -120,8 +120,14 @@ return z; 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); +putc('-', fp); +if (is_tainted(val)) + { + int q = quoter_for_address(val); + putc('-', fp); + if (is_real_quoter(q)) fprintf(fp, "(%s)", lookup_list[q]->name); + } +fprintf(fp, "%s %s\n", name, val); } /************************************************* diff --git a/src/src/store.c b/src/src/store.c index 8603a8fb1..1e555cc18 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -44,7 +44,9 @@ The following different types of store are recognized: - There is a dedicated pool for configuration data read from the config file(s). Once complete, it is made readonly. -. Orthogonal to the three pool types, there are two classes of memory: untainted +- There are pools for each active combination of lookup-quoting, dynamically created. + +. Orthogonal to the four main 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 @@ -100,6 +102,38 @@ typedef struct storeblock { size_t length; } storeblock; +/* Pool descriptor struct */ + +typedef struct pooldesc { + storeblock * chainbase; /* list of blocks in pool */ + storeblock * current_block; /* top block, still with free space */ + void * next_yield; /* next allocation point */ + int yield_length; /* remaining space in current block */ + unsigned store_block_order; /* log2(size) block allocation size */ + + /* 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; + + /* These are purely for stats-gathering */ + + int nbytes; + int maxbytes; + int nblocks; + int maxblocks; + unsigned maxorder; +} pooldesc; + +/* Enhanced pool descriptor for quoted pools */ + +typedef struct quoted_pooldesc { + pooldesc pool; + unsigned quoter; + struct quoted_pooldesc * next; +} quoted_pooldesc; + /* Just in case we find ourselves on a system where the structure above has a length that is not a multiple of the alignment, set up a macro for the padded length. */ @@ -131,11 +165,13 @@ even if the length is zero (which is used for getting a point to reset to). */ int store_pool = POOL_MAIN; -static storeblock *chainbase[NPOOLS]; -static storeblock *current_block[NPOOLS]; -static void *next_yield[NPOOLS]; -static int yield_length[NPOOLS]; -static unsigned store_block_order[NPOOLS]; +pooldesc paired_pools[N_PAIRED_POOLS]; +quoted_pooldesc * quoted_pools = NULL; + +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 */ /* 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 @@ -145,27 +181,9 @@ pointer. */ 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[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 unsigned maxorder[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 */ - #ifndef COMPILE_UTILITY -static const uschar * pooluse[NPOOLS] = { +static const uschar * pooluse[N_PAIRED_POOLS] = { [POOL_MAIN] = US"main", [POOL_PERM] = US"perm", [POOL_CONFIG] = US"config", @@ -177,7 +195,7 @@ static const uschar * pooluse[NPOOLS] = { [POOL_TAINT_SEARCH] = US"search", [POOL_TAINT_MESSAGE] = US"message", }; -static const uschar * poolclass[NPOOLS] = { +static const uschar * poolclass[N_PAIRED_POOLS] = { [POOL_MAIN] = US"untainted", [POOL_PERM] = US"untainted", [POOL_CONFIG] = US"untainted", @@ -196,21 +214,71 @@ static void * internal_store_malloc(size_t, const char *, int); static void internal_store_free(void *, const char *, int linenumber); /******************************************************************************/ + +static void +pool_init(pooldesc * pp) +{ +memset(pp, 0, sizeof(*pp)); +pp->yield_length = -1; +pp->store_block_order = 12; /* log2(allocation_size) ie. 4kB */ +} + /* Initialisation, for things fragile with parameter channges when using static initialisers. */ void store_init(void) { -for (int i = 0; i < NPOOLS; i++) - { - yield_length[i] = -1; - store_block_order[i] = 12; /* log2(allocation_size) ie. 4kB */ - } +for (pooldesc * pp = paired_pools; pp < paired_pools + N_PAIRED_POOLS; pp++) + pool_init(pp); } /******************************************************************************/ +/* Locating elements given memory pointer */ + +static BOOL +is_pointer_in_block(const storeblock * b, const void * p) +{ +uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; +return US p >= bc && US p < bc + b->length; +} + +static pooldesc * +pool_current_for_pointer(const void * p) +{ +storeblock * b; + +for (quoted_pooldesc * qp = quoted_pools; qp; qp = qp->next) + if ((b = qp->pool.current_block) && is_pointer_in_block(b, p)) + return &qp->pool; + +for (pooldesc * pp = paired_pools; pp < paired_pools + N_PAIRED_POOLS; pp++) + if ((b = pp->current_block) && is_pointer_in_block(b, p)) + return pp; +return NULL; +} +static pooldesc * +pool_for_pointer(const void * p) +{ +pooldesc * pp; +storeblock * b; + +if ((pp = pool_current_for_pointer(p))) return pp; + +for (quoted_pooldesc * qp = quoted_pools; qp; qp = qp->next) + for (b = qp->pool.chainbase; b; b = b->next) + if (is_pointer_in_block(b, p)) return &qp->pool; + +for (pp = paired_pools; pp < paired_pools + N_PAIRED_POOLS; pp++) + for (b = pp->chainbase; b; b = b->next) + if (is_pointer_in_block(b, p)) return pp; + +log_write(0, LOG_MAIN|LOG_PANIC_DIE, "bad memory reference; pool not found"); +return NULL; +} + +/******************************************************************************/ /* Test if a pointer refers to tainted memory. Slower version check, for use when platform intermixes malloc and mmap area @@ -225,19 +293,27 @@ is_tainted_fn(const void * p) { storeblock * b; -for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) - if ((b = current_block[pool])) - { - uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; - if (US p >= bc && US p < bc + b->length) return TRUE; - } +if (p == GET_UNTAINTED) return FALSE; +if (p == GET_TAINTED) return TRUE; + +for (pooldesc * pp = paired_pools + POOL_TAINT_BASE; + pp < paired_pools + N_PAIRED_POOLS; pp++) + if ((b = pp->current_block)) + if (is_pointer_in_block(b, p)) return TRUE; + +for (quoted_pooldesc * qp = quoted_pools; qp; qp = qp->next) + if (b = qp->pool.current_block) + if (is_pointer_in_block(b, p)) return TRUE; + +for (pooldesc * pp = paired_pools + POOL_TAINT_BASE; + pp < paired_pools + N_PAIRED_POOLS; pp++) + for (b = pp->chainbase; b; b = b->next) + if (is_pointer_in_block(b, p)) return TRUE; + +for (quoted_pooldesc * qp = quoted_pools; qp; qp = qp->next) + for (b = qp->pool.chainbase; b; b = b->next) + if (is_pointer_in_block(b, p)) return TRUE; -for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) - for (b = chainbase[pool]; b; b = b->next) - { - uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; - if (US p >= bc && US p < bc + b->length) return TRUE; - } return FALSE; } @@ -250,13 +326,41 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Taint mismatch, %s: %s %d\n", } +#ifndef COMPILE_UTILITY +/* Return the pool for the given quoter, or null */ + +static pooldesc * +pool_for_quoter(unsigned quoter) +{ +for (quoted_pooldesc * qp = quoted_pools; qp; qp = qp->next) + if (qp->quoter == quoter) + return &qp->pool; +return NULL; +} + +/* Allocate/init a new quoted-pool and return the pool */ + +static pooldesc * +quoted_pool_new(unsigned quoter) +{ +// debug_printf("allocating quoted-pool\n"); +quoted_pooldesc * qp = store_get_perm(sizeof(quoted_pooldesc), GET_UNTAINTED); + +pool_init(&qp->pool); +qp->quoter = quoter; +qp->next = quoted_pools; +quoted_pools = qp; +return &qp->pool; +} +#endif + /******************************************************************************/ void store_writeprotect(int pool) { #if !defined(COMPILE_UTILITY) && !defined(MISSING_POSIX_MEMALIGN) -for (storeblock * b = chainbase[pool]; b; b = b->next) +for (storeblock * b = paired_pools[pool].chainbase; b; b = b->next) if (mprotect(b, ALIGNED_SIZEOF_STOREBLOCK + b->length, PROT_READ) != 0) DEBUG(D_any) debug_printf("config block mprotect: (%d) %s\n", errno, strerror(errno)); #endif @@ -264,29 +368,9 @@ for (storeblock * b = chainbase[pool]; b; b = b->next) /******************************************************************************/ -/************************************************* -* Get a block from the current pool * -*************************************************/ - -/* Running out of store is a total disaster. This function is called via the -macro store_get(). It passes back a block of store within the current big -block, getting a new one if necessary. The address is saved in -store_last_was_get. - -Arguments: - size amount wanted, bytes - tainted class: set to true for untrusted data (eg. from smtp input) - 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, BOOL tainted, const char * func, int linenumber) +static void * +pool_get(pooldesc * pp, int size, BOOL align_mem, const char * func, int linenumber) { -int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; - /* Ensure we've been asked to allocate memory. A negative size is a sign of a security problem. A zero size might be also suspect, but our internal usage deliberately @@ -310,23 +394,23 @@ 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[pool]) +if (size > pp->yield_length) { int length = MAX( - STORE_BLOCK_SIZE(store_block_order[pool]) - ALIGNED_SIZEOF_STOREBLOCK, + STORE_BLOCK_SIZE(pp->store_block_order) - ALIGNED_SIZEOF_STOREBLOCK, size); int mlength = length + ALIGNED_SIZEOF_STOREBLOCK; storeblock * newblock; /* Sometimes store_reset() may leave a block for us; check if we can use it */ - if ( (newblock = current_block[pool]) + if ( (newblock = pp->current_block) && (newblock = newblock->next) && newblock->length < length ) { /* Give up on this block, because it's too small */ - nblocks[pool]--; + pp->nblocks--; internal_store_free(newblock, func, linenumber); newblock = NULL; } @@ -335,16 +419,16 @@ if (size > yield_length[pool]) if (!newblock) { - if ((nbytes[pool] += mlength) > maxbytes[pool]) - maxbytes[pool] = nbytes[pool]; + if ((pp->nbytes += mlength) > pp->maxbytes) + pp->maxbytes = pp->nbytes; 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]; + if (++pp->nblocks > pp->maxblocks) + pp->maxblocks = pp->nblocks; #ifndef MISSING_POSIX_MEMALIGN - if (pool == POOL_CONFIG) + if (align_mem) { long pgsize = sysconf(_SC_PAGESIZE); int err = posix_memalign((void **)&newblock, @@ -361,43 +445,97 @@ if (size > yield_length[pool]) newblock->next = NULL; newblock->length = length; #ifndef RESTRICTED_MEMORY - if (store_block_order[pool]++ > maxorder[pool]) - maxorder[pool] = store_block_order[pool]; + if (pp->store_block_order++ > pp->maxorder) + pp->maxorder = pp->store_block_order; #endif - if (!chainbase[pool]) - chainbase[pool] = newblock; + if (! pp->chainbase) + pp->chainbase = newblock; else - current_block[pool]->next = newblock; + pp->current_block->next = newblock; } - 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]); + pp->current_block = newblock; + pp->yield_length = newblock->length; + pp->next_yield = + (void *)(CS pp->current_block + ALIGNED_SIZEOF_STOREBLOCK); + (void) VALGRIND_MAKE_MEM_NOACCESS(pp->next_yield, pp->yield_length); } /* There's (now) enough room in the current block; the yield is the next pointer. */ -store_last_get[pool] = next_yield[pool]; +pp->store_last_get = pp->next_yield; -/* Cut out the debugging stuff for utilities, but stop picky compilers from -giving warnings. */ +(void) VALGRIND_MAKE_MEM_UNDEFINED(pp->store_last_get, size); +/* Update next pointer and number of bytes left in the current block. */ + +pp->next_yield = (void *)(CS pp->next_yield + size); +pp->yield_length -= size; +return pp->store_last_get; +} + +/************************************************* +* Get a block from the current pool * +*************************************************/ + +/* Running out of store is a total disaster. This function is called via the +macro store_get(). The current store_pool is used, adjusting for taint. +If the protoype is quoted, use a quoted-pool. +Return a block of store within the current big block of the pool, getting a new +one if necessary. The address is saved in store_last_get for the pool. + +Arguments: + size amount wanted, bytes + proto_mem class: get store conformant to this + Special values: 0 forces untainted, 1 forces tainted + 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 void * proto_mem, const char * func, int linenumber) +{ #ifndef COMPILE_UTILITY -DEBUG(D_memory) - debug_printf("---%d Get %6p %5d %-14s %4d\n", pool, - store_last_get[pool], size, func, linenumber); -#endif /* COMPILE_UTILITY */ +int quoter = quoter_for_address(proto_mem); +#endif +pooldesc * pp; +void * yield; -(void) VALGRIND_MAKE_MEM_UNDEFINED(store_last_get[pool], size); -/* Update next pointer and number of bytes left in the current block. */ +#ifndef COMPILE_UTILITY +if (!is_real_quoter(quoter)) +#endif + { + BOOL tainted = is_tainted(proto_mem); + int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; + pp = paired_pools + pool; + yield = pool_get(pp, size, (pool == POOL_CONFIG), func, linenumber); -next_yield[pool] = (void *)(CS next_yield[pool] + size); -yield_length[pool] -= size; -return store_last_get[pool]; + /* Cut out the debugging stuff for utilities, but stop picky compilers from + giving warnings. */ + +#ifndef COMPILE_UTILITY + DEBUG(D_memory) + debug_printf("---%d Get %6p %5d %-14s %4d\n", pool, + pp->store_last_get, size, func, linenumber); +#endif + } +#ifndef COMPILE_UTILITY +else + { + DEBUG(D_memory) + debug_printf("allocating quoted-block for quoter %u (from %s %d)\n", + quoter, func, linenumber); + if (!(pp = pool_for_quoter(quoter))) pp = quoted_pool_new(quoter); + yield = pool_get(pp, size, FALSE, func, linenumber); + DEBUG(D_memory) + debug_printf("---QQ Get %6p %5d %-14s %4d\n", + pp->store_last_get, size, func, linenumber); + } +#endif +return yield; } @@ -411,6 +549,7 @@ be obtained. Arguments: size amount wanted + proto_mem class: get store conformant to this func function from which called linenumber line number in source file @@ -418,17 +557,131 @@ Returns: pointer to store (panic on malloc failure) */ void * -store_get_perm_3(int size, BOOL tainted, const char * func, int linenumber) +store_get_perm_3(int size, const void * proto_mem, const char * func, int linenumber) { void * yield; int old_pool = store_pool; store_pool = POOL_PERM; -yield = store_get_3(size, tainted, func, linenumber); +yield = store_get_3(size, proto_mem, func, linenumber); store_pool = old_pool; return yield; } +#ifndef COMPILE_UTILITY +/************************************************* +* Get a block annotated as being lookup-quoted * +*************************************************/ + +/* Allocate from pool a pool consistent with the proto_mem augmented by the +requested quoter type. + +XXX currently not handling mark/release + +Args: size number of bytes to allocate + quoter id for the quoting type + func caller, for debug + linenumber caller, for debug + +Return: allocated memory block +*/ + +static void * +store_force_get_quoted(int size, unsigned quoter, + const char * func, int linenumber) +{ +pooldesc * pp = pool_for_quoter(quoter); +void * yield; + +DEBUG(D_memory) + debug_printf("allocating quoted-block for quoter %u (from %s %d)\n", quoter, func, linenumber); + +if (!pp) pp = quoted_pool_new(quoter); +yield = pool_get(pp, size, FALSE, func, linenumber); + +DEBUG(D_memory) + debug_printf("---QQ Get %6p %5d %-14s %4d\n", + pp->store_last_get, size, func, linenumber); + +return yield; +} + +/* Maybe get memory for the specified quoter, but only if the +prototype memory is tainted. Otherwise, get plain memory. +*/ +void * +store_get_quoted_3(int size, const void * proto_mem, unsigned quoter, + const char * func, int linenumber) +{ +// debug_printf("store_get_quoted_3: quoter %u\n", quoter); +return is_tainted(proto_mem) + ? store_force_get_quoted(size, quoter, func, linenumber) + : store_get_3(size, proto_mem, func, linenumber); +} + +/* Return quoter for given address, or -1 if not in a quoted-pool. */ +int +quoter_for_address(const void * p) +{ +for (quoted_pooldesc * qp = quoted_pools; qp; qp = qp->next) + { + pooldesc * pp = &qp->pool; + storeblock * b; + + if (b = pp->current_block) + if (is_pointer_in_block(b, p)) + return qp->quoter; + + for (b = pp->chainbase; b; b = b->next) + if (is_pointer_in_block(b, p)) + return qp->quoter; + } +return -1; +} + +/* Return TRUE iff the given address is quoted for the given type. +There is extra complexity to handle lookup providers with multiple +find variants but shared quote functions. */ +BOOL +is_quoted_like(const void * p, unsigned quoter) +{ +int pq = quoter_for_address(p); +BOOL y = + is_real_quoter(pq) && lookup_list[pq]->quote == lookup_list[quoter]->quote; +/* debug_printf("is_quoted(%p, %u): %c\n", p, quoter, y?'T':'F'); */ +return y; +} + +/* Return TRUE if the quoter value indicates an actual quoter */ +BOOL +is_real_quoter(int quoter) +{ +return quoter >= 0; +} + +/* Return TRUE if the "new" data requires that the "old" data +be recopied to new-class memory. We order the classes as + + 2: tainted, not quoted + 1: quoted (which is also tainted) + 0: untainted + +If the "new" is higher-order than the "old", they are not compatible +and a copy is needed. If both are quoted, but the quoters differ, +not compatible. Otherwise they are compatible. +*/ +BOOL +is_incompatible_fn(const void * old, const void * new) +{ +int oq, nq; +unsigned oi, ni; + +ni = is_real_quoter(nq = quoter_for_address(new)) ? 1 : is_tainted(new) ? 2 : 0; +oi = is_real_quoter(oq = quoter_for_address(old)) ? 1 : is_tainted(old) ? 2 : 0; +return ni > oi || ni == oi && nq != oq; +} + +#endif /*!COMPILE_UTILITY*/ /************************************************* * Extend a block if it is at the top * @@ -451,13 +704,16 @@ Arguments: Returns: TRUE if the block is at the top of the stack and has been extended; FALSE if it isn't at the top of the stack, or cannot be extended + +XXX needs extension for quoted-tracking. This assumes that the global store_pool +is the one to alloc from, which breaks with separated pools. */ BOOL -store_extend_3(void *ptr, BOOL tainted, int oldsize, int newsize, - const char *func, int linenumber) +store_extend_3(void * ptr, int oldsize, int newsize, + const char * func, int linenumber) { -int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; +pooldesc * pp = pool_for_pointer(ptr); int inc = newsize - oldsize; int rounded_oldsize = oldsize; @@ -466,17 +722,11 @@ if (oldsize < 0 || newsize < oldsize || newsize >= INT_MAX/2) "bad memory extension requested (%d -> %d bytes) at %s %d", oldsize, newsize, func, linenumber); -/* 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[pool]) || - inc > yield_length[pool] + rounded_oldsize - oldsize) +if (CS ptr + rounded_oldsize != CS (pp->next_yield) || + inc > pp->yield_length + rounded_oldsize - oldsize) return FALSE; /* Cut out the debugging stuff for utilities, but stop picky compilers from @@ -484,13 +734,26 @@ giving warnings. */ #ifndef COMPILE_UTILITY DEBUG(D_memory) - debug_printf("---%d Ext %6p %5d %-14s %4d\n", pool, ptr, newsize, - func, linenumber); + { + quoted_pooldesc * qp; + for (qp = quoted_pools; qp; qp = qp->next) + if (pp == &qp->pool) + { + debug_printf("---Q%d Ext %6p %5d %-14s %4d\n", + (int)(qp - quoted_pools), + ptr, newsize, func, linenumber); + break; + } + if (!qp) + debug_printf("---%d Ext %6p %5d %-14s %4d\n", + (int)(pp - paired_pools), + ptr, newsize, func, linenumber); + } #endif /* COMPILE_UTILITY */ if (newsize % alignment != 0) newsize += alignment - (newsize % alignment); -next_yield[pool] = CS ptr + newsize; -yield_length[pool] -= newsize - rounded_oldsize; +pp->next_yield = CS ptr + newsize; +pp->yield_length -= newsize - rounded_oldsize; (void) VALGRIND_MAKE_MEM_UNDEFINED(ptr + oldsize, inc); return TRUE; } @@ -515,6 +778,8 @@ 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. +Quoted pools are not handled. + Arguments: ptr place to back up to pool pool holding the pointer @@ -528,23 +793,26 @@ static void internal_store_reset(void * ptr, int pool, const char *func, int linenumber) { storeblock * bb; -storeblock * b = current_block[pool]; +pooldesc * pp = paired_pools + pool; +storeblock * b = pp->current_block; char * bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; int newlength, count; #ifndef COMPILE_UTILITY int oldmalloc = pool_malloc; #endif +if (!b) return; /* exim_dumpdb gets this, becuse it has never used tainted mem */ + /* Last store operation was not a get */ -store_last_get[pool] = NULL; +pp->store_last_get = 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[pool]; b; b = b->next) + for (b = pp->chainbase; b; b = b->next) { bc = CS b + ALIGNED_SIZEOF_STOREBLOCK; if (CS ptr >= bc && CS ptr <= bc + b->length) break; @@ -570,17 +838,17 @@ if (debug_store) } #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; -current_block[pool] = b; +pp->next_yield = CS ptr + (newlength % alignment); +count = pp->yield_length; +count = (pp->yield_length = newlength - (newlength % alignment)) - count; +pp->current_block = 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 a power-of-two size so probably is not a custom inflated one. */ -if ( yield_length[pool] < STOREPOOL_MIN_SIZE +if ( pp->yield_length < STOREPOOL_MIN_SIZE && b->next && is_pwr2_size(b->next->length + ALIGNED_SIZEOF_STOREBLOCK)) { @@ -608,14 +876,14 @@ while ((b = bb)) func, linenumber); #endif bb = bb->next; - nbytes[pool] -= siz; + pp->nbytes -= siz; pool_malloc -= siz; - nblocks[pool]--; + pp->nblocks--; if (pool != POOL_CONFIG) internal_store_free(b, func, linenumber); #ifndef RESTRICTED_MEMORY - if (store_block_order[pool] > 13) store_block_order[pool]--; + if (pp->store_block_order > 13) pp->store_block_order--; #endif } @@ -631,8 +899,12 @@ DEBUG(D_memory) } +/* Back up the pool pair, untainted and tainted, of the store_pool setting. +Quoted pools are not handled. +*/ + rmark -store_reset_3(rmark r, const char *func, int linenumber) +store_reset_3(rmark r, const char * func, int linenumber) { void ** ptr = r; @@ -649,6 +921,7 @@ 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. @@ -661,32 +934,26 @@ XXX needs rationalising */ void -store_release_above_3(void *ptr, const char *func, int linenumber) +store_release_above_3(void * ptr, const char * func, int linenumber) { +pooldesc * pp; + /* 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 ((pp = pool_current_for_pointer(ptr))) { - storeblock * b = current_block[pool]; - char * bc; + storeblock * b = pp->current_block; 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; + pp->store_last_get = NULL; /* Back up, rounding to the alignment if necessary. When testing, flatten the released memory. */ - newlength = bc + b->length - CS ptr; + newlength = (CS b + ALIGNED_SIZEOF_STOREBLOCK) + b->length - CS ptr; #ifndef COMPILE_UTILITY if (debug_store) { @@ -699,17 +966,27 @@ for (int pool = 0; pool < nelem(current_block); pool++) } #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; + pp->next_yield = CS ptr + (newlength % alignment); + count = pp->yield_length; + count = (pp->yield_length = newlength - (newlength % alignment)) - count; /* Cut out the debugging stuff for utilities, but stop picky compilers from giving warnings. */ #ifndef COMPILE_UTILITY DEBUG(D_memory) - debug_printf("---%d Rel %6p %5d %-14s %4d\tpool %d\n", pool, ptr, count, - func, linenumber, pool_malloc); + { + quoted_pooldesc * qp; + for (qp = quoted_pools; qp; qp = qp->next) + if (pp == &qp->pool) + debug_printf("---Q%d Rel %6p %5d %-14s %4d\tpool %d\n", + (int)(qp - quoted_pools), + ptr, count, func, linenumber, pool_malloc); + if (!qp) + debug_printf("---%d Rel %6p %5d %-14s %4d\tpool %d\n", + (int)(pp - paired_pools), ptr, count, + func, linenumber, pool_malloc); + } #endif return; } @@ -722,7 +999,7 @@ DEBUG(D_memory) rmark -store_mark_3(const char *func, int linenumber) +store_mark_3(const char * func, int linenumber) { void ** p; @@ -741,8 +1018,8 @@ 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); +p = store_get_3(sizeof(void *), GET_UNTAINTED, func, linenumber); +*p = store_get_3(0, GET_TAINTED, func, linenumber); return p; } @@ -758,6 +1035,7 @@ block, and if so, releases that block. Arguments: block block of store to consider + pp pool containing the block func function from which called linenumber line number in source file @@ -765,20 +1043,20 @@ Returns: nothing */ static void -store_release_3(void * block, int pool, const char * func, int linenumber) +store_release_3(void * block, pooldesc * pp, const char * func, int linenumber) { /* It will never be the first block, so no need to check that. */ -for (storeblock * b = chainbase[pool]; b; b = b->next) +for (storeblock * b = pp->chainbase; 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; - nbytes[pool] -= siz; + pp->nbytes -= siz; pool_malloc -= siz; - nblocks[pool]--; + pp->nblocks--; /* Cut out the debugging stuff for utilities, but stop picky compilers from giving warnings. */ @@ -816,35 +1094,30 @@ the pointer it is given is the first thing in a block, and that nothing has been allocated since. If so, releases that block. Arguments: - block - newsize - len + oldblock + newsize requested size + len current size Returns: new location of data */ void * -store_newblock_3(void * block, BOOL tainted, int newsize, int len, +store_newblock_3(void * oldblock, int newsize, int len, const char * func, int linenumber) { -int pool = tainted ? store_pool + POOL_TAINT_BASE : store_pool; -BOOL release_ok = !tainted && store_last_get[pool] == block; -uschar * newtext; - -#if !defined(MACRO_PREDEF) && !defined(COMPILE_UTILITY) -if (is_tainted(block) != tainted) - die_tainted(US"store_newblock", CUS func, linenumber); -#endif +pooldesc * pp = pool_for_pointer(oldblock); +BOOL release_ok = !is_tainted(oldblock) && pp->store_last_get == oldblock; /*XXX why tainted not handled? */ +uschar * newblock; if (len < 0 || len > newsize) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "bad memory extension requested (%d -> %d bytes) at %s %d", len, newsize, func, linenumber); -newtext = store_get(newsize, tainted); -memcpy(newtext, block, len); -if (release_ok) store_release_3(block, pool, func, linenumber); -return (void *)newtext; +newblock = store_get(newsize, oldblock); +memcpy(newblock, oldblock, len); +if (release_ok) store_release_3(oldblock, pp, func, linenumber); +return (void *)newblock; } @@ -960,13 +1233,25 @@ store_exit(void) #ifndef COMPILE_UTILITY DEBUG(D_memory) { + int i; 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 at order %u\t%s %s\n", - i, (maxbytes[i]+1023)/1024, maxblocks[i], maxorder[i], + + for (i = 0; i < N_PAIRED_POOLS; i++) + { + pooldesc * pp = paired_pools + i; + debug_printf("----Exit pool %2d max: %3d kB in %d blocks at order %u\t%s %s\n", + i, (pp->maxbytes+1023)/1024, pp->maxblocks, pp->maxorder, poolclass[i], pooluse[i]); + } + i = 0; + for (quoted_pooldesc * qp = quoted_pools; qp; i++, qp = qp->next) + { + pooldesc * pp = &qp->pool; + debug_printf("----Exit pool Q%d max: %3d kB in %d blocks at order %u\ttainted quoted:%s\n", + i, (pp->maxbytes+1023)/1024, pp->maxblocks, pp->maxorder, lookup_list[qp->quoter]->name); + } } #endif } @@ -986,7 +1271,8 @@ if (!message_reset_point) message_reset_point = store_mark(); store_pool = oldpool; } -void message_tidyup(void) +void +message_tidyup(void) { int oldpool; if (!message_reset_point) return; @@ -996,4 +1282,19 @@ message_reset_point = store_reset(message_reset_point); store_pool = oldpool; } +/******************************************************************************/ +/* Debug analysis of address */ + +#ifndef COMPILE_UTILITY +void +debug_print_taint(const void * p) +{ +int q = quoter_for_address(p); +if (!is_tainted(p)) return; +debug_printf("(tainted"); +if (is_real_quoter(q)) debug_printf(", quoted:%s", lookup_list[q]->name); +debug_printf(")\n"); +} +#endif + /* End of store.c */ diff --git a/src/src/store.h b/src/src/store.h index 3e4240842..1917e654d 100644 --- a/src/src/store.h +++ b/src/src/store.h @@ -13,7 +13,6 @@ /* Define symbols for identifying the store pools. */ -#define NPOOLS 10 enum { POOL_MAIN, POOL_PERM, POOL_CONFIG, @@ -26,13 +25,16 @@ enum { POOL_MAIN, POOL_TAINT_PERM, POOL_TAINT_CONFIG, POOL_TAINT_SEARCH, - POOL_TAINT_MESSAGE }; + POOL_TAINT_MESSAGE, + + N_PAIRED_POOLS +}; /* 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[NPOOLS]; +extern void * store_last_get[]; /* This variable contains the current store pool number. */ @@ -41,18 +43,20 @@ extern int store_pool; /* Macros for calling the memory allocation routines with tracing information for debugging. */ -#define store_extend(addr, tainted, old, new) \ - store_extend_3(addr, tainted, old, new, __FUNCTION__, __LINE__) +#define store_extend(addr, old, new) \ + store_extend_3(addr, old, new, __FUNCTION__, __LINE__) #define store_free(addr) \ store_free_3(addr, __FUNCTION__, __LINE__) /* store_get & store_get_perm are in local_scan.h */ +#define store_get_quoted(size, proto_mem, quoter) \ + store_get_quoted_3((size), (proto_mem), (quoter), __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_newblock(oldblock, newsize, datalen) \ + store_newblock_3(oldblock, newsize, datalen, __FUNCTION__, __LINE__) #define store_release_above(addr) \ store_release_above_3(addr, __FUNCTION__, __LINE__) #define store_reset(mark) \ @@ -62,15 +66,24 @@ tracing information for debugging. */ /* The real functions */ typedef void ** rmark; -extern BOOL store_extend_3(void *, BOOL, int, int, const char *, int); +extern BOOL store_extend_3(void *, int, int, const char *, int); extern void store_free_3(void *, const char *, int); /* store_get_3 & store_get_perm_3 are in local_scan.h */ -extern void *store_malloc_3(size_t, const char *, int) ALLOC ALLOC_SIZE(1) WARN_UNUSED_RESULT; +extern void * store_get_quoted_3(int, const void *, unsigned, const char *, int); +extern void * store_malloc_3(size_t, 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_newblock_3(void *, int, int, const char *, int); extern void store_release_above_3(void *, const char *, int); extern rmark store_reset_3(rmark, const char *, int); +#define GET_UNTAINTED (const void *)0 +#define GET_TAINTED (const void *)1 + +extern int quoter_for_address(const void *); +extern BOOL is_quoted_like(const void *, unsigned); +extern BOOL is_real_quoter(int); +extern void debug_print_taint(const void * p); + #endif /* STORE_H */ /* End of store.h */ diff --git a/src/src/string.c b/src/src/string.c index 1e7922457..4d870ec9a 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -313,7 +313,7 @@ if (nonprintcount == 0) return s; /* Get a new block of store guaranteed big enough to hold the expanded string. */ -tt = ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s)); +tt = ss = store_get(length + nonprintcount * 3 + 1, s); /* Copy everything, escaping non printers. */ @@ -371,7 +371,7 @@ p = Ustrchr(s, '\\'); if (!p) return s; len = Ustrlen(s) + 1; -ss = store_get(len, is_tainted(s)); +ss = store_get(len, s); q = ss; off = p - s; @@ -428,18 +428,18 @@ Returns: copy of string in new store with the same taint status */ uschar * -string_copy_function(const uschar *s) +string_copy_function(const uschar * s) { -return string_copy_taint(s, is_tainted(s)); +return string_copy_taint(s, s); } /* As above, but explicitly specifying the result taint status */ uschar * -string_copy_taint_function(const uschar * s, BOOL tainted) +string_copy_taint_function(const uschar * s, const void * proto_mem) { -return string_copy_taint(s, tainted); +return string_copy_taint(s, proto_mem); } @@ -568,7 +568,7 @@ uschar * string_copy_dnsdomain(uschar * s) { uschar * yield; -uschar * ss = yield = store_get(Ustrlen(s) + 1, TRUE); /* always treat as tainted */ +uschar * ss = yield = store_get(Ustrlen(s) + 1, GET_TAINTED); /* always treat as tainted */ while (*s) { @@ -626,7 +626,7 @@ else /* Get enough store to copy into */ -t = yield = store_get(s - *sptr + 1, is_tainted(*sptr)); +t = yield = store_get(s - *sptr + 1, *sptr); s = *sptr; /* Do the copy */ @@ -911,6 +911,7 @@ if (!*s) return NULL; sep_is_special = iscntrl(sep); /* Handle the case when a buffer is provided. */ +/*XXX need to also deal with qouted-requirements mismatch */ if (buffer) { @@ -1087,7 +1088,6 @@ 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 @@ -1119,8 +1119,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, tainted, oldsize, g->size)) - g->s = store_newblock(g->s, tainted, g->size, p); +if (!store_extend(g->s, oldsize, g->size)) + g->s = store_newblock(g->s, g->size, p); } @@ -1150,24 +1150,27 @@ Returns: growable string, changed if copied for expansion. /* coverity[+alloc] */ gstring * -string_catn(gstring * g, const uschar *s, int count) +string_catn(gstring * g, const uschar * s, int count) { int p; -BOOL srctaint = is_tainted(s); if (count < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal error in string_catn (count %d)", count); if (count == 0) return g; +/*debug_printf("string_catn '%.*s'\n", count, s);*/ if (!g) { unsigned inc = count < 4096 ? 127 : 1023; unsigned size = ((count + inc) & ~inc) + 1; /* round up requested count */ - g = string_get_tainted(size, srctaint); + g = string_get_tainted(size, s); + } +else if (is_incompatible(g->s, s)) + { +/* debug_printf("rebuf A\n"); */ + gstring_rebuffer(g, s); } -else if (srctaint && !is_tainted(g->s)) - gstring_rebuffer(g); if (g->ptr < 0 || g->ptr > g->size) log_write(0, LOG_MAIN|LOG_PANIC_DIE, @@ -1310,7 +1313,6 @@ enum ltypes { L_NORMAL=1, L_SHORT=2, L_LONG=3, L_LONGLONG=4, L_LONGDOUBLE=5, L_S 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 */ @@ -1323,16 +1325,15 @@ assert(g); /* 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_TAINT_NOCHK) && is_incompatible(g->s, format)) { #ifndef MACRO_PREDEF if (!(flags & SVFMT_REBUFFER)) die_tainted(US"string_vformat", func, line); #endif - gstring_rebuffer(g); - dest_tainted = TRUE; +/* debug_printf("rebuf B\n"); */ + gstring_rebuffer(g, format); } #endif /*!COMPILE_UTILITY*/ @@ -1552,12 +1553,12 @@ while (*fp) if (!s) s = null; slen = Ustrlen(s); - if (!(flags & SVFMT_TAINT_NOCHK) && !dest_tainted && is_tainted(s)) + if (!(flags & SVFMT_TAINT_NOCHK) && is_incompatible(g->s, s)) if (flags & SVFMT_REBUFFER) { - gstring_rebuffer(g); +/* debug_printf("%s %d: untainted workarea, tainted %%s :- rebuffer\n", __FUNCTION__, __LINE__); */ + gstring_rebuffer(g, s); gp = CS g->s + g->ptr; - dest_tainted = TRUE; } #ifndef MACRO_PREDEF else diff --git a/src/src/structs.h b/src/src/structs.h index a42c26d39..8c103caa8 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -133,11 +133,15 @@ typedef struct driver_instance { uschar *name; /* Instance name */ struct driver_info *info; /* Points to info for this driver */ void *options_block; /* Pointer to private options */ + uschar *driver_name; /* All start with this generic option */ + const uschar *srcfile; /* and config source info for errors */ + int srcline; } driver_instance; typedef struct driver_info { uschar *driver_name; /* Name of driver */ + optionlist *options; /* Table of private options names */ int *options_count; /* -> Number of entries in table */ void *options_block; /* Points to default private block */ @@ -159,6 +163,9 @@ typedef struct transport_instance { struct transport_info *info; /* Info for this driver */ void *options_block; /* Pointer to private options */ uschar *driver_name; /* Must be first */ + const uschar *srcfile; + int srcline; + int (*setup)( /* Setup entry point */ struct transport_instance *, struct address_item *, @@ -283,6 +290,8 @@ typedef struct router_instance { struct router_info *info; void *options_block; /* Pointer to private options */ uschar *driver_name; /* Must be first */ + const uschar *srcfile; + int srcline; uschar *address_data; /* Arbitrary data */ #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -400,6 +409,9 @@ typedef struct auth_instance { struct auth_info *info; /* Pointer to driver info block */ void *options_block; /* Pointer to private options */ uschar *driver_name; /* Must be first */ + const uschar *srcfile; + int srcline; + uschar *advertise_condition; /* Are we going to advertise this?*/ uschar *client_condition; /* Should the client try this? */ uschar *public_name; /* Advertised name */ diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 10f038446..1a2cd915c 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2031,7 +2031,7 @@ if (host) int old_pool = store_pool; store_pool = POOL_PERM; - state = store_get(sizeof(exim_gnutls_state_st), FALSE); + state = store_get(sizeof(exim_gnutls_state_st), GET_UNTAINTED); store_pool = old_pool; memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); @@ -2431,8 +2431,8 @@ else for (nrec = 0; state->dane_data_len[nrec]; ) nrec++; nrec++; - dd = store_get(nrec * sizeof(uschar *), FALSE); - ddl = store_get(nrec * sizeof(int), FALSE); + dd = store_get(nrec * sizeof(uschar *), GET_UNTAINTED); + ddl = store_get(nrec * sizeof(int), GET_UNTAINTED); nrec--; if ((rc = dane_state_init(&s, 0))) @@ -2889,7 +2889,7 @@ else while (string_nextinlist(&list, &sep, NULL, 0)) cnt++; - p = store_get(sizeof(gnutls_datum_t) * cnt, is_tainted(exp_alpn)); + p = store_get(sizeof(gnutls_datum_t) * cnt, exp_alpn); list = exp_alpn; for (int i = 0; s = string_nextinlist(&list, &sep, NULL, 0); i++) { p[i].data = s; p[i].size = Ustrlen(s); } @@ -3208,8 +3208,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 *), FALSE); -dane_data_len = store_get(i * sizeof(int), FALSE); +dane_data = store_get(i * sizeof(uschar *), GET_UNTAINTED); +dane_data_len = store_get(i * sizeof(int), GET_UNTAINTED); i = 0; for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; @@ -3322,7 +3322,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, TRUE); + dbdata_tls_session * dt = store_get(dlen, GET_TAINTED); DEBUG(D_tls) debug_printf("session data size %u\n", (unsigned)tkt.size); memcpy(dt->session, tkt.data, tkt.size); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 262798486..14ba2fc0c 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1446,7 +1446,7 @@ supply_response: ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry; while (oentry = *op) op = &oentry->next; - *op = oentry = store_get(sizeof(ocsp_resplist), FALSE); + *op = oentry = store_get(sizeof(ocsp_resplist), GET_UNTAINTED); oentry->next = NULL; oentry->resp = resp; } @@ -2174,7 +2174,7 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername, /* Make the extension value available for expansion */ store_pool = POOL_PERM; -tls_in.sni = string_copy_taint(US servername, TRUE); +tls_in.sni = string_copy_taint(US servername, GET_TAINTED); store_pool = old_pool; if (!reexpand_tls_files_for_sni) @@ -3464,9 +3464,9 @@ See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */ size_t len = SSL_get_peer_finished(ssl, &c, 0); int old_pool = store_pool; - SSL_get_peer_finished(ssl, s = store_get((int)len, FALSE), len); + SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len); store_pool = POOL_PERM; - tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE); + tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED); store_pool = old_pool; DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding); } @@ -3702,7 +3702,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, TRUE); + dbdata_tls_session * dt = store_get(dlen, GET_TAINTED); uschar * s = dt->session; open_db dbblock, * dbm_file; @@ -3807,7 +3807,7 @@ else but it's little extra code complexity in the client. */ const uschar * list = exp_alpn; - uschar * p = store_get(Ustrlen(exp_alpn), is_tainted(exp_alpn)), * s, * t; + uschar * p = store_get(Ustrlen(exp_alpn), exp_alpn), * s, * t; int sep = 0; uschar len; @@ -3861,7 +3861,7 @@ BOOL require_ocsp = FALSE; rc = store_pool; store_pool = POOL_PERM; -exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE); +exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), GET_UNTAINTED); exim_client_ctx->corked = NULL; store_pool = rc; @@ -4147,9 +4147,9 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0); int old_pool = store_pool; - SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len); + SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len); store_pool = POOL_PERM; - tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE); + tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED); store_pool = old_pool; DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp); } diff --git a/src/src/tls.c b/src/src/tls.c index e6b1bf7a7..bc3261ad2 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -209,7 +209,7 @@ for (;;) if (!(S_ISLNK(sb.st_mode))) break; - t = store_get(1024, FALSE); + t = store_get(1024, GET_UNTAINTED); Ustrncpy(t, s, 1022); j = Ustrlen(s); t[j++] = '/'; diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c index cbc7accf0..99acf5ec0 100644 --- a/src/src/tlscert-gnu.c +++ b/src/src/tlscert-gnu.c @@ -115,7 +115,7 @@ size_t len = 32; if (mod && Ustrcmp(mod, "int") == 0) return string_sprintf("%u", (unsigned)t); -cp = store_get(len, FALSE); +cp = store_get(len, GET_UNTAINTED); if (f.timestamps_utc) { uschar * tz = to_tz(US"GMT0"); @@ -149,7 +149,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, TRUE); +cp = store_get(siz, GET_TAINTED); if ((ret = gnutls_x509_crt_get_issuer_dn(cert, CS cp, &siz)) < 0) return g_err("gi1", __FUNCTION__, ret); @@ -203,7 +203,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, TRUE); +cp1 = store_get(len*4+1, GET_TAINTED); if (gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, CS cp1, &len) != 0) return g_err("gs1", __FUNCTION__, ret); @@ -233,7 +233,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, TRUE); +cp = store_get(siz, GET_TAINTED); if ((ret = gnutls_x509_crt_get_dn(cert, CS cp, &siz)) < 0) return g_err("gs1", __FUNCTION__, ret); @@ -261,7 +261,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, TRUE); +cp1 = store_get(siz*4 + 1, GET_TAINTED); ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert, CS oid, idx, CS cp1, &siz, &crit); @@ -317,7 +317,7 @@ for (int index = 0;; index++) return g_err("gs0", __FUNCTION__, ret); } - ele = store_get(siz+1, TRUE); + ele = store_get(siz+1, GET_TAINTED); 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); @@ -400,7 +400,7 @@ for (int index = 0;; index++) return g_err("gc0", __FUNCTION__, ret); } - ele = store_get(siz, TRUE); + ele = store_get(siz, GET_TAINTED); 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); @@ -423,7 +423,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, TRUE), TRUE) /* tainted */ + || !(cp = store_get((int)len, GET_TAINTED), TRUE) /* tainted */ || (fail = gnutls_x509_crt_export((gnutls_x509_crt_t)cert, GNUTLS_X509_FMT_DER, cp, &len)) ) @@ -448,7 +448,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, TRUE); +cp = store_get(siz*3+1, GET_TAINTED); if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, cp, &siz)) < 0) return g_err("gf1", __FUNCTION__, ret); diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index 403dc4236..abe34966b 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -171,7 +171,7 @@ else /* convert to string in our format */ len = 32; - s = store_get(len, FALSE); + s = store_get(len, GET_UNTAINTED); strftime(CS s, (size_t)len, "%b %e %T %Y %z", tm_p); } } @@ -335,7 +335,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, TRUE); +cp3 = cp2 = store_get(len*3+1, GET_TAINTED); while(len) { @@ -502,7 +502,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, TRUE); +cp = store_get(n*2+1, GET_TAINTED); for (int j = 0; j < (int)n; j++) sprintf(CS cp+2*j, "%02X", md[j]); return(cp); } diff --git a/src/src/transport.c b/src/src/transport.c index ef523657e..96a936503 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -651,7 +651,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), FALSE); +ppp = store_get(sizeof(struct aci), GET_UNTAINTED); ppp->next = *pdlist; *pdlist = ppp; ppp->ptr = p; @@ -675,7 +675,7 @@ if (ppp) return TRUE; /* Remember what we have output, and output it. */ -ppp = store_get(sizeof(struct aci), FALSE); +ppp = store_get(sizeof(struct aci), GET_UNTAINTED); ppp->next = *pplist; *pplist = ppp; ppp->ptr = pp; @@ -1521,7 +1521,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, FALSE); + host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, GET_UNTAINTED); host_record->count = host_record->sequence = 0; } @@ -1585,7 +1585,7 @@ for (host_item * host = hostlist; host; host = host->next) else { dbdata_wait *newr = - store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, FALSE); + store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, GET_UNTAINTED); memcpy(newr, host_record, sizeof(dbdata_wait) + host_length); host_record = newr; } @@ -1719,7 +1719,7 @@ while (1) /* create an array to read entire message queue into memory for processing */ - msgq = store_get(sizeof(msgq_t) * host_record->count, FALSE); + msgq = store_get(sizeof(msgq_t) * host_record->count, GET_UNTAINTED); msgq_count = host_record->count; msgq_actual = msgq_count; @@ -2091,7 +2091,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 *), FALSE); +*argvptr = argv = store_get((max_args+1)*sizeof(uschar *), GET_UNTAINTED); /* Split the command up into arguments terminated by white space. Lose trailing space at the start and end. Double-quoted arguments can contain \\ and @@ -2107,7 +2107,7 @@ for (; *s != 0 && argcount < max_args; argcount++) { ss = s + 1; while (*ss != 0 && *ss != '\'') ss++; - argv[argcount] = ss = store_get(ss - s++, is_tainted(cmd)); + argv[argcount] = ss = store_get(ss - s++, cmd); while (*s != 0 && *s != '\'') *ss++ = *s++; if (*s != 0) s++; *ss++ = 0; @@ -2207,7 +2207,6 @@ 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; @@ -2216,13 +2215,12 @@ 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 *), FALSE); + address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), GET_UNTAINTED); /* +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') + if (!s || *s == '\0') { addr->transport_return = FAIL; addr->message = string_sprintf("Expansion of \"%s\" " @@ -2233,15 +2231,16 @@ if (expand_arguments) while (isspace(*s)) s++; /* strip leading space */ - while (*s != 0 && address_pipe_argcount < address_pipe_max_args) + while (*s && address_pipe_argcount < address_pipe_max_args) { if (*s == '\'') { - ss = s + 1; - while (*ss != 0 && *ss != '\'') ss++; - address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++, tainted); - while (*s != 0 && *s != '\'') *ss++ = *s++; - if (*s != 0) s++; + int n; + for (ss = s + 1; *ss && *ss != '\''; ) ss++; + n = ss - s++; + address_pipe_argv[address_pipe_argcount++] = ss = store_get(n, s); + while (*s && *s != '\'') *ss++ = *s++; + if (*s) s++; *ss++ = 0; } else address_pipe_argv[address_pipe_argcount++] = @@ -2343,6 +2342,19 @@ if (expand_arguments) return TRUE; } + + +/* For error messages, a string describing the config location associated +with current processing. NULL if we are not in a transport. */ +/* Name only, for now */ + +uschar * +transport_current_name(void) +{ +if (!transport_name) return NULL; +return string_sprintf(" (transport %s, %s %d)", transport_name, driver_srcfile, driver_srcline); +} + #endif /*!MACRO_PREDEF*/ /* vi: aw ai sw=2 */ diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 440f0de20..43fe883f6 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -3191,7 +3191,7 @@ else uschar *iptr = expand_string(nametag); if (iptr) { - uschar *etag = store_get(Ustrlen(iptr) + 2, is_tainted(iptr)); + uschar *etag = store_get(Ustrlen(iptr) + 2, iptr); uschar *optr = etag; for ( ; *iptr; iptr++) if (mac_isgraph(*iptr) && *iptr != '/') diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 13e2eaf19..217c54ccd 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -437,7 +437,7 @@ if (oncelog && *oncelog && to) cache_size = statbuf.st_size; add_size = sizeof(time_t) + Ustrlen(to) + 1; - cache_buff = store_get(cache_size + add_size, is_tainted(oncelog)); + cache_buff = store_get(cache_size + add_size, oncelog); if (read(cache_fd, cache_buff, cache_size) != cache_size) { diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 2ec4a72fb..df3693aba 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -435,7 +435,7 @@ set_up_shell_command(const uschar ***argvptr, uschar *cmd, { const uschar **argv; -*argvptr = argv = store_get((4)*sizeof(uschar *), FALSE); +*argvptr = argv = store_get((4)*sizeof(uschar *), GET_UNTAINTED); argv[0] = US"/bin/sh"; argv[1] = US"-c"; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index d36e95ce5..6a979a243 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1433,10 +1433,17 @@ smtp_transport_options_block * ob = sx->conn_args.ob; /* transport options */ host_item * host = sx->conn_args.host; /* host to deliver to */ int rc; +/* Set up globals for error messages */ + +authenticator_name = au->name; +driver_srcfile = au->srcfile; +driver_srcline = au->srcline; + sx->outblock.authenticating = TRUE; rc = (au->info->clientcode)(au, sx, ob->command_timeout, sx->buffer, sizeof(sx->buffer)); sx->outblock.authenticating = FALSE; +driver_srcfile = authenticator_name = NULL; driver_srcline = 0; DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", au->name, rc); /* A temporary authentication failure must hold up delivery to @@ -3685,7 +3692,7 @@ int rc; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; -smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ +smtp_context * sx = store_get(sizeof(*sx), GET_TAINTED); /* tainted, for the data buffers */ BOOL pass_message = FALSE; #ifdef EXPERIMENTAL_ESMTP_LIMITS BOOL mail_limit = FALSE; @@ -5591,7 +5598,7 @@ retry_non_continued: if (expanded_hosts) { - thost = store_get(sizeof(host_item), FALSE); + thost = store_get(sizeof(host_item), GET_UNTAINTED); *thost = *host; thost->name = string_copy(host->name); thost->address = string_copy(host->address); diff --git a/src/src/tree.c b/src/src/tree.c index f2c97db1e..a42103738 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -31,7 +31,7 @@ void tree_add_nonrecipient(const uschar *s) { rmark rpoint = store_mark(); -tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s)); +tree_node * node = store_get(sizeof(tree_node) + Ustrlen(s), s); Ustrcpy(node->name, s); node->data.ptr = NULL; if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(rpoint); @@ -56,7 +56,7 @@ void tree_add_duplicate(const uschar *s, address_item *addr) { rmark rpoint = store_mark(); -tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s)); +tree_node * node = store_get(sizeof(tree_node) + Ustrlen(s), s); Ustrcpy(node->name, s); node->data.ptr = addr; if (!tree_insertnode(&tree_duplicates, node)) store_reset(rpoint); @@ -82,7 +82,7 @@ tree_node *node; uschar s[256]; sprintf(CS s, "T:%.200s:%s", h->name, h->address); node = store_get(sizeof(tree_node) + Ustrlen(s), - is_tainted(h->name) || is_tainted(h->address)); + is_tainted(h->name) || is_tainted(h->address) ? GET_TAINTED : GET_UNTAINTED); Ustrcpy(node->name, s); node->data.val = h->why; if (h->status == hstatus_unusable_expired) node->data.val += 256; @@ -374,7 +374,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), is_tainted(name)); +tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name), name); Ustrcpy(node->name, name); node->data.ptr = val; (void) tree_insertnode(root, node); diff --git a/src/src/utf8.c b/src/src/utf8.c index f035dafd0..168e7fc7f 100644 --- a/src/src/utf8.c +++ b/src/src/utf8.c @@ -148,7 +148,7 @@ if (!p || !ucs4_len) return NULL; } p_len = ucs4_len*4; /* this multiplier is pure guesswork */ -res = store_get(p_len+5, is_tainted(utf8)); +res = store_get(p_len+5, utf8); res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-'; @@ -177,7 +177,7 @@ uschar * s, * res; DEBUG(D_expand) debug_printf("l_a2u: '%s'\n", alabel); alabel += 4; p_len = Ustrlen(alabel); -p = store_get((p_len+1) * sizeof(*p), is_tainted(alabel)); +p = store_get((p_len+1) * sizeof(*p), alabel); if ((rc = punycode_decode(p_len, CCS alabel, &p_len, p, NULL)) != PUNYCODE_SUCCESS) { diff --git a/src/src/verify.c b/src/src/verify.c index a5c6de576..d78b8bf24 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -78,7 +78,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), FALSE); + dbdata_callout_cache * new = store_get(sizeof(dbdata_callout_cache), GET_UNTAINTED); memcpy(new, cache_record, length); new->postmaster_stamp = new->random_stamp = new->time_stamp; cache_record = new; @@ -400,7 +400,7 @@ if (addr->transport == cutthrough.addr.transport) if (done) { - address_item * na = store_get(sizeof(address_item), FALSE); + address_item * na = store_get(sizeof(address_item), GET_UNTAINTED); *na = cutthrough.addr; cutthrough.addr = *addr; cutthrough.addr.host_used = &cutthrough.host; @@ -651,7 +651,7 @@ coding means skipping this whole loop and doing the append separately. */ log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address, addr->message); - if (!sx) sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */ + if (!sx) sx = store_get(sizeof(*sx), GET_TAINTED); /* tainted buffers */ memset(sx, 0, sizeof(*sx)); sx->addrlist = sx->first_addr = addr; @@ -1096,7 +1096,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), FALSE)) = *parent; + *(caddr->parent = store_get(sizeof(address_item), GET_UNTAINTED)) = *parent; ctctx.outblock.buffer = ctbuffer; ctctx.outblock.buffersize = sizeof(ctbuffer); @@ -2872,28 +2872,27 @@ Returns: OK matched */ int -check_host(void *arg, const uschar *ss, const uschar **valueptr, uschar **error) +check_host(void * arg, const uschar * ss, const uschar ** valueptr, uschar ** error) { -check_host_block *cb = (check_host_block *)arg; +check_host_block * cb = (check_host_block *)arg; int mlen = -1; int maskoffset; -BOOL iplookup = FALSE; -BOOL isquery = FALSE; -BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0; -const uschar *t; +BOOL iplookup = FALSE, isquery = FALSE; +BOOL isiponly = cb->host_name && !cb->host_name[0]; +const uschar * t; uschar * semicolon, * endname, * opts; -uschar **aliases; +uschar ** aliases; /* Optimize for the special case when the pattern is "*". */ -if (*ss == '*' && ss[1] == 0) return OK; +if (*ss == '*' && !ss[1]) return OK; /* If the pattern is empty, it matches only in the case when there is no host - this can occur in ACL checking for SMTP input using the -bs option. In this situation, the host address is the empty string. */ -if (cb->host_address[0] == 0) return (*ss == 0)? OK : FAIL; -if (*ss == 0) return FAIL; +if (!cb->host_address[0]) return *ss ? FAIL : OK; +if (!*ss) return FAIL; /* If the pattern is precisely "@" then match against the primary host name, provided that host name matching is permitted; if it's "@[]" match against the @@ -2930,7 +2929,7 @@ course slashes may be present in lookups, but not preceded only by digits and dots). */ for (t = ss; isdigit(*t) || *t == '.'; ) t++; -if (*t == 0 || (*t == '/' && t != ss)) +if (!*t || (*t == '/' && t != ss)) { *error = US"malformed IPv4 address or address mask"; return ERROR; diff --git a/test/confs/2610 b/test/confs/2610 index 98a93b63b..9b139d2b6 100644 --- a/test/confs/2610 +++ b/test/confs/2610 @@ -10,6 +10,7 @@ domainlist local_domains = @ hostlist relay_hosts = net-mysql;select * from them where id='$sender_host_address' acl_smtp_rcpt = check_recipient +acl_not_smtp = check_notsmtp PARTIAL = 127.0.0.1::PORT_N SSPEC = PARTIAL/test/root/pass @@ -23,31 +24,40 @@ begin acl check_recipient: # Tainted-data checks warn - # taint only in lookup string - set acl_m0 = ok: ${lookup mysql {select name from them where id = '$local_part'}} + # taint only in lookup string, properly quoted + set acl_m0 = ok: ${lookup mysql {select name from them where id = '${quote_mysql:$local_part}'}} + # taint only in lookup string, but not quoted + set acl_m0 = FAIL: ${lookup mysql,no_rd {select name from them where id = '$local_part'}} + warn # option on lookup type unaffected - set acl_m0 = ok: ${lookup mysql,servers=SSPEC {select name from them where id = '$local_part'}} + set acl_m0 = ok: ${lookup mysql,servers=SSPEC {select name from them where id = '${quote_mysql:$local_part}'}} # partial server-spec, indexing main-option, works - set acl_m0 = ok: ${lookup mysql,servers=PARTIAL {select name from them where id = '$local_part'}} + set acl_m0 = ok: ${lookup mysql,servers=PARTIAL {select name from them where id = '${quote_mysql:$local_part}'}} # oldstyle server spec, prepended to lookup string, fails with taint - set acl_m0 = FAIL: ${lookup mysql {servers=SSPEC; select name from them where id = '$local_part'}} + set acl_m0 = FAIL: ${lookup mysql {servers=SSPEC; select name from them where id = '${quote_mysql:$local_part}'}} - # In list-stle lookup, tainted lookup string is ok if server spec comes from main-option + # In list-style lookup, tainted lookup string is ok if server spec comes from main-option warn set acl_m0 = ok: hostlist - hosts = net-mysql;select * from them where id='$local_part' + hosts = net-mysql;select * from them where id='${quote_mysql:$local_part}' # ... but setting a per-query servers spec fails due to the taint warn set acl_m0 = FAIL: hostlist - hosts = <& net-mysql;servers=SSPEC; select * from them where id='$local_part' + hosts = <& net-mysql;servers=SSPEC; select * from them where id='${quote_mysql:$local_part}' # The newer server-list-as-option-to-lookup-type is not a solution to tainted data in the lookup, because # string-expansion is done before list-expansion so the taint contaminates the entire list. warn set acl_m0 = FAIL: hostlist - hosts = <& net-mysql,servers=SSPEC; select * from them where id='$local_part' + hosts = <& net-mysql,servers=SSPEC; select * from them where id='${quote_mysql:$local_part}' accept domains = +local_domains + # the quoted status of this var should survive being passed via spoolfile + set acl_m_qtest = ${quote_mysql:$local_part} accept hosts = +relay_hosts deny message = relay not permitted +check_notsmtp: + accept + # the quoted status of this var should survive being passed via spoolfile + set acl_m_qtest = ${quote_mysql:$recipients} # ----- Routers ----- @@ -55,7 +65,10 @@ begin routers r1: driver = accept - address_data = ${lookup mysql{select name from them where id='ph10'}} + debug_print = acl_m_qtest: <$acl_m_qtest> lkup: <${lookup mysql{select name from them where id='$acl_m_qtest'}}> + + # this tests the unquoted case, but will need enhancement when we enforce (vs. just logging), else no transport call + address_data = ${lookup mysql{select name from them where id='$local_part'}} transport = t1 @@ -66,7 +79,7 @@ begin transports t1: driver = appendfile file = DIR/test-mail/\ - ${lookup mysql{select id from them where id='ph10'}{$value}fail} + ${lookup mysql{select id from them where id='$local_part'}{$value}fail} user = CALLER diff --git a/test/confs/2620 b/test/confs/2620 index 85d25035f..70a460e24 100644 --- a/test/confs/2620 +++ b/test/confs/2620 @@ -25,26 +25,29 @@ begin acl check_recipient: # Tainted-data checks warn - # taint only in lookup string - set acl_m0 = ok: ${lookup pgsql {select name from them where id = '$local_part'}} + # taint only in lookup string, properly quoted + set acl_m0 = ok: ${lookup pgsql {select name from them where id = '${quote_pgsql:$local_part}'}} + # taint only in lookup string, but not quoted + set acl_m0 = FAIL: ${lookup pgsql,cache=no_rd {select name from them where id = '$local_part'}} + warn # option on lookup type unaffected - set acl_m0 = ok: ${lookup pgsql,servers=SERVERS {select name from them where id = '$local_part'}} + set acl_m0 = ok: ${lookup pgsql,servers=SERVERS {select name from them where id = '${quote_pgsql:$local_part}'}} # partial server-spec, indexing main-option, works - set acl_m0 = ok: ${lookup pgsql,servers=PARTIAL {select name from them where id = '$local_part'}} + set acl_m0 = ok: ${lookup pgsql,servers=PARTIAL {select name from them where id = '${quote_pgsql:$local_part}'}} # oldstyle server spec, prepended to lookup string, fails with taint - set acl_m0 = FAIL: ${lookup pgsql {servers=SERVERS; select name from them where id = '$local_part'}} + set acl_m0 = FAIL: ${lookup pgsql {servers=SERVERS; select name from them where id = '${quote_pgsql:$local_part}'}} - # In list-stle lookup, tainted lookup string is ok if server spec comes from main-option + # In list-style lookup, tainted lookup string is ok if server spec comes from main-option warn set acl_m0 = ok: hostlist - hosts = net-pgsql;select * from them where id='$local_part' + hosts = net-pgsql;select * from them where id='${quote_pgsql:$local_part}' # ... but setting a per-query servers spec fails due to the taint warn set acl_m0 = FAIL: hostlist - hosts = <& net-pgsql;servers=SERVERS; select * from them where id='$local_part' + hosts = <& net-pgsql;servers=SERVERS; select * from them where id='${quote_pgsql:$local_part}' # The newer server-list-as-option-to-lookup-type is not a solution to tainted data in the lookup, because # string-expansion is done before list-expansion so the taint contaminates the entire list. warn set acl_m0 = FAIL: hostlist - hosts = <& net-pgsql,servers=SERVERS; select * from them where id='$local_part' + hosts = <& net-pgsql,servers=SERVERS; select * from them where id='${quote_pgsql:$local_part}' accept domains = +local_domains accept hosts = +relay_hosts diff --git a/test/log/2610 b/test/log/2610 index 38ea30eb6..380c7a32d 100644 --- a/test/log/2610 +++ b/test/log/2610 @@ -1,3 +1,5 @@ 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 tainted search query is not properly quoted (router r1, TESTSUITE/test-config 66): select name from them where id='ph10' +1999-03-02 09:44:33 10HmaX-0005vi-00 tainted search query is not properly quoted (transport t1, TESTSUITE/test-config 79): select id from them where id='ph10' 1999-03-02 09:44:33 10HmaX-0005vi-00 => ph10 R=r1 T=t1 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed diff --git a/test/paniclog/2610 b/test/paniclog/2610 new file mode 100644 index 000000000..920e2768a --- /dev/null +++ b/test/paniclog/2610 @@ -0,0 +1 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 tainted search query is not properly quoted (router r1, TESTSUITE/test-config 66): select name from them where id='ph10' diff --git a/test/runtest b/test/runtest index 6f1bd0add..2ac6198eb 100755 --- a/test/runtest +++ b/test/runtest @@ -1297,7 +1297,7 @@ RESET_AFTER_EXTRA_LINE_READ: } # Different builds will have different lookup types included - s/^search_type \K\d+ \((\w+)\) quoting -1 \(none\)$/NN ($1) quoting -1 (none)/; + s/^\s*search_type \K\d+ \((\w+)\) quoting -1 \(none\)$/NN ($1) quoting -1 (none)/; # DISABLE_OCSP next if /in hosts_requ(est|ire)_ocsp\? (no|yes)/; diff --git a/test/scripts/2610-MySQL/2610 b/test/scripts/2610-MySQL/2610 index 3065eac44..ba4a67bb0 100644 --- a/test/scripts/2610-MySQL/2610 +++ b/test/scripts/2610-MySQL/2610 @@ -87,6 +87,8 @@ mail from: rcpt to: quit **** +# Check the quote-tracking of tainted data. +# Currently this will log but continue. exim -odi -d ph10 Test message . diff --git a/test/stderr/2200 b/test/stderr/2200 index 9631e9b82..b37f75f93 100644 --- a/test/stderr/2200 +++ b/test/stderr/2200 @@ -35,6 +35,7 @@ search_tidyup called internal_search_find: file="NULL" type=dnsdb key="a=shorthost.test.ex" opts=NULL database lookup required for a=shorthost.test.ex + (tainted) dnsdb key: shorthost.test.ex creating new cache entry lookup yielded: 127.0.0.1 @@ -46,6 +47,7 @@ search_tidyup called internal_search_find: file="NULL" type=dnsdb key="a=shorthost.test.ex" opts=NULL cached data found but out-of-date; database lookup required for a=shorthost.test.ex + (tainted) dnsdb key: shorthost.test.ex replacing old cache entry lookup yielded: 127.0.0.1 diff --git a/test/stderr/2201 b/test/stderr/2201 index 229de2ce0..c1c39ef28 100644 --- a/test/stderr/2201 +++ b/test/stderr/2201 @@ -34,6 +34,7 @@ LRU list: internal_search_find: file="NULL" type=dnsdb key="test.ex" opts=NULL database lookup required for test.ex + (tainted) dnsdb key: test.ex DNS lookup of test.ex (TXT) using fakens DNS lookup of test.ex (TXT) succeeded @@ -96,6 +97,7 @@ LRU list: internal_search_find: file="NULL" type=dnsdb key="unknown" opts=NULL database lookup required for unknown + (tainted) dnsdb key: unknown DNS lookup of unknown (TXT) using fakens DNS lookup of unknown (TXT) gave HOST_NOT_FOUND @@ -161,6 +163,7 @@ LRU list: internal_search_find: file="NULL" type=dnsdb key="a=shorthost.test.ex" opts=NULL database lookup required for a=shorthost.test.ex + (tainted) dnsdb key: shorthost.test.ex creating new cache entry lookup yielded: 127.0.0.1 @@ -181,6 +184,7 @@ LRU list: internal_search_find: file="NULL" type=dnsdb key="a=shorthost.test.ex" opts=NULL cached data found but out-of-date; database lookup required for a=shorthost.test.ex + (tainted) dnsdb key: shorthost.test.ex replacing old cache entry lookup yielded: 127.0.0.1 diff --git a/test/stderr/2202 b/test/stderr/2202 index ad234c6af..dd9f2ff14 100644 --- a/test/stderr/2202 +++ b/test/stderr/2202 @@ -43,6 +43,7 @@ check hosts = +ignore_unknown : *.$sender_address_domain : $sender_address_domai internal_search_find: file="NULL" type=dnsdb key=">:defer_never,mxh=cioce.test.again.dns" opts=NULL database lookup required for >:defer_never,mxh=cioce.test.again.dns + (tainted) dnsdb key: cioce.test.again.dns DNS lookup of cioce.test.again.dns (MX) using fakens DNS lookup of cioce.test.again.dns (MX) gave TRY_AGAIN diff --git a/test/stderr/2610 b/test/stderr/2610 index 731952ed9..56ae41f8e 100644 --- a/test/stderr/2610 +++ b/test/stderr/2610 @@ -266,7 +266,7 @@ log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100 SMTP>> 250 OK SMTP<< rcpt to: using ACL "check_recipient" -processing "warn" (TESTSUITE/test-config 25) +processing "warn" (TESTSUITE/test-config 26) search_open: mysql "NULL" search_find: file="NULL" key="select name from them where id = 'c'" partial=-1 affix=NULL starflags=0 opts=NULL @@ -274,13 +274,35 @@ processing "warn" (TESTSUITE/test-config 25) internal_search_find: file="NULL" type=mysql key="select name from them where id = 'c'" opts=NULL database lookup required for select name from them where id = 'c' + (tainted, quoted:mysql) MySQL query: "select name from them where id = 'c'" opts 'NULL' MYSQL new connection: host=127.0.0.1 port=1223 socket=NULL database=test user=root MYSQL: no data found creating new cache entry lookup failed -check set acl_m0 = ok: ${lookup mysql {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup mysql {select name from them where id = '${quote_mysql:$local_part}'}} = ok: + search_open: mysql "NULL" + cached open + search_find: file="NULL" + key="select name from them where id = 'c'" partial=-1 affix=NULL starflags=0 opts="no_rd" + LRU list: + internal_search_find: file="NULL" + type=mysql key="select name from them where id = 'c'" opts="no_rd" + cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted) +LOG: MAIN PANIC + tainted search query is not properly quoted (ACL warn, TESTSUITE/test-config 26): select name from them where id = 'c' + search_type NN (mysql) quoting -1 (none) + MySQL query: "select name from them where id = 'c'" opts 'no_rd' + MYSQL using cached connection for 127.0.0.1:1223/test/root + MYSQL: no data found + replacing old cache entry + lookup failed +check set acl_m0 = FAIL: ${lookup mysql,no_rd {select name from them where id = '$local_part'}} + = FAIL: +warn: condition test succeeded in ACL "check_recipient" +processing "warn" (TESTSUITE/test-config 31) search_open: mysql "NULL" cached open search_find: file="NULL" @@ -289,12 +311,13 @@ check set acl_m0 = ok: ${lookup mysql {select name from the internal_search_find: file="NULL" type=mysql key="select name from them where id = 'c'" opts="servers=127.0.0.1::1223/test/root/pass" cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:mysql) MySQL query: "select name from them where id = 'c'" opts 'servers=127.0.0.1::1223/test/root/pass' MYSQL using cached connection for 127.0.0.1:1223/test/root MYSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup mysql,servers=127.0.0.1::1223/test/root/pass {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup mysql,servers=127.0.0.1::1223/test/root/pass {select name from them where id = '${quote_mysql:$local_part}'}} = ok: search_open: mysql "NULL" cached open @@ -304,12 +327,13 @@ check set acl_m0 = ok: ${lookup mysql,servers=127.0.0.1::1223/test/root/pass internal_search_find: file="NULL" type=mysql key="select name from them where id = 'c'" opts="servers=127.0.0.1::1223" cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:mysql) MySQL query: "select name from them where id = 'c'" opts 'servers=127.0.0.1::1223' MYSQL using cached connection for 127.0.0.1:1223/test/root MYSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup mysql,servers=127.0.0.1::1223 {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup mysql,servers=127.0.0.1::1223 {select name from them where id = '${quote_mysql:$local_part}'}} = ok: search_open: mysql "NULL" cached open @@ -319,14 +343,15 @@ check set acl_m0 = ok: ${lookup mysql,servers=127.0.0.1::1223 {select name internal_search_find: file="NULL" type=mysql key="servers=127.0.0.1::1223/test/root/pass; select name from them where id = 'c'" opts=NULL database lookup required for servers=127.0.0.1::1223/test/root/pass; select name from them where id = 'c' + (tainted, quoted:mysql) MySQL query: "servers=127.0.0.1::1223/test/root/pass; select name from them where id = 'c'" opts 'NULL' lookup deferred: MySQL server "127.0.0.1:1223/test/root/pass" is tainted warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: MySQL server "127.0.0.1:1223/test/root/pass" is tainted -processing "warn" (TESTSUITE/test-config 36) +processing "warn" (TESTSUITE/test-config 40) check set acl_m0 = ok: hostlist -check hosts = net-mysql;select * from them where id='$local_part' +check hosts = net-mysql;select * from them where id='${quote_mysql:$local_part}' search_open: mysql "NULL" cached open search_find: file="NULL" @@ -335,6 +360,7 @@ LRU list: internal_search_find: file="NULL" type=mysql key="select * from them where id='c'" opts=NULL database lookup required for select * from them where id='c' + (tainted, quoted:mysql) MySQL query: "select * from them where id='c'" opts 'NULL' MYSQL using cached connection for 127.0.0.1:1223/test/root MYSQL: no data found @@ -342,9 +368,9 @@ creating new cache entry lookup failed host in "net-mysql;select * from them where id='c'"? no (end of list) warn: condition test failed in ACL "check_recipient" -processing "warn" (TESTSUITE/test-config 39) +processing "warn" (TESTSUITE/test-config 43) check set acl_m0 = FAIL: hostlist -check hosts = <& net-mysql;servers=127.0.0.1::1223/test/root/pass; select * from them where id='$local_part' +check hosts = <& net-mysql;servers=127.0.0.1::1223/test/root/pass; select * from them where id='${quote_mysql:$local_part}' search_open: mysql "NULL" cached open search_find: file="NULL" @@ -353,15 +379,16 @@ LRU list: internal_search_find: file="NULL" type=mysql key="servers=127.0.0.1::1223/test/root/pass; select * from them where id='c'" opts=NULL database lookup required for servers=127.0.0.1::1223/test/root/pass; select * from them where id='c' + (tainted, quoted:mysql) MySQL query: "servers=127.0.0.1::1223/test/root/pass; select * from them where id='c'" opts 'NULL' lookup deferred: MySQL server "127.0.0.1:1223/test/root/pass" is tainted host in "<& net-mysql;servers=127.0.0.1::1223/test/root/pass; select * from them where id='c'"? list match deferred for net-mysql;servers=127.0.0.1::1223/test/root/pass; select * from them where id='c' warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: MySQL server "127.0.0.1:1223/test/root/pass" is tainted -processing "warn" (TESTSUITE/test-config 44) +processing "warn" (TESTSUITE/test-config 48) check set acl_m0 = FAIL: hostlist -check hosts = <& net-mysql,servers=127.0.0.1::1223/test/root/pass; select * from them where id='$local_part' +check hosts = <& net-mysql,servers=127.0.0.1::1223/test/root/pass; select * from them where id='${quote_mysql:$local_part}' search_open: mysql "NULL" cached open search_find: file="NULL" @@ -370,18 +397,19 @@ LRU list: internal_search_find: file="NULL" type=mysql key=" select * from them where id='c'" opts="servers=127.0.0.1::1223/test/root/pass" database lookup required for select * from them where id='c' + (tainted, quoted:mysql) MySQL query: " select * from them where id='c'" opts 'servers=127.0.0.1::1223/test/root/pass' lookup deferred: MySQL server "127.0.0.1:1223/test/root/pass" is tainted host in "<& net-mysql,servers=127.0.0.1::1223/test/root/pass; select * from them where id='c'"? list match deferred for net-mysql,servers=127.0.0.1::1223/test/root/pass; select * from them where id='c' warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: MySQL server "127.0.0.1:1223/test/root/pass" is tainted -processing "accept" (TESTSUITE/test-config 47) +processing "accept" (TESTSUITE/test-config 51) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 48) +processing "accept" (TESTSUITE/test-config 54) check hosts = +relay_hosts search_open: mysql "NULL" cached open @@ -399,7 +427,7 @@ lookup failed host in "net-mysql;select * from them where id='10.0.0.0'"? no (end of list) host in "+relay_hosts"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "deny" (TESTSUITE/test-config 49) +processing "deny" (TESTSUITE/test-config 55) message: relay not permitted deny: condition test succeeded in ACL "check_recipient" end of ACL "check_recipient": DENY @@ -452,6 +480,12 @@ P Received: from CALLER by myhost.test.ex with local (Exim x.yz) id 10HmaX-0005vi-00 for ph10@myhost.test.ex; Tue, 2 Mar 1999 09:44:33 +0000 +using ACL "check_notsmtp" +processing "accept" (TESTSUITE/test-config 58) +check set acl_m_qtest = ${quote_mysql:$recipients} + = ph10@myhost.test.ex +accept: condition test succeeded in ACL "check_notsmtp" +end of ACL "check_notsmtp": ACCEPT Writing spool header file: TESTSUITE/spool//input//hdr.10HmaX-0005vi-00 DSN: **** SPOOL_OUT - address: errorsto: orcpt: dsn_flags: 0x0 Renaming spool header file: TESTSUITE/spool//input//10HmaX-0005vi-00-H @@ -501,16 +535,35 @@ ph10@myhost.test.ex: queued for routing routing ph10@myhost.test.ex --------> r1 router <-------- local_part=ph10 domain=myhost.test.ex + search_open: mysql "NULL" + search_find: file="NULL" + key="select name from them where id='ph10@myhost.test.ex'" partial=-1 affix=NULL starflags=0 opts=NULL + LRU list: + internal_search_find: file="NULL" + type=mysql key="select name from them where id='ph10@myhost.test.ex'" opts=NULL + database lookup required for select name from them where id='ph10@myhost.test.ex' + (tainted, quoted:mysql) + MySQL query: "select name from them where id='ph10@myhost.test.ex'" opts 'NULL' + MYSQL new connection: host=127.0.0.1 port=1223 socket=NULL database=test user=root + MYSQL: no data found + creating new cache entry + lookup failed +acl_m_qtest: lkup: <> processing address_data search_open: mysql "NULL" + cached open search_find: file="NULL" key="select name from them where id='ph10'" partial=-1 affix=NULL starflags=0 opts=NULL LRU list: internal_search_find: file="NULL" type=mysql key="select name from them where id='ph10'" opts=NULL database lookup required for select name from them where id='ph10' + (tainted) +LOG: MAIN PANIC + tainted search query is not properly quoted (router r1, TESTSUITE/test-config 66): select name from them where id='ph10' + search_type NN (mysql) quoting -1 (none) MySQL query: "select name from them where id='ph10'" opts 'NULL' - MYSQL new connection: host=127.0.0.1 port=1223 socket=NULL database=test user=root + MYSQL using cached connection for 127.0.0.1:1223/test/root creating new cache entry lookup yielded: Philip Hazel calling r1 router @@ -554,6 +607,10 @@ appendfile transport entered internal_search_find: file="NULL" type=mysql key="select id from them where id='ph10'" opts=NULL database lookup required for select id from them where id='ph10' + (tainted) +LOG: MAIN + tainted search query is not properly quoted (transport t1, TESTSUITE/test-config 79): select id from them where id='ph10' + search_type NN (mysql) quoting -1 (none) MySQL query: "select id from them where id='ph10'" opts 'NULL' MYSQL new connection: host=127.0.0.1 port=1223 socket=NULL database=test user=root creating new cache entry diff --git a/test/stderr/2620 b/test/stderr/2620 index 991e61efa..dd3fa8844 100644 --- a/test/stderr/2620 +++ b/test/stderr/2620 @@ -260,13 +260,35 @@ processing "warn" (TESTSUITE/test-config 27) internal_search_find: file="NULL" type=pgsql key="select name from them where id = 'c'" opts=NULL database lookup required for select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "select name from them where id = 'c'" opts 'NULL' PGSQL new connection: host=localhost port=1223 database=test user=CALLER PGSQL: no data found creating new cache entry lookup failed -check set acl_m0 = ok: ${lookup pgsql {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup pgsql {select name from them where id = '${quote_pgsql:$local_part}'}} = ok: + search_open: pgsql "NULL" + cached open + search_find: file="NULL" + key="select name from them where id = 'c'" partial=-1 affix=NULL starflags=0 opts="cache=no_rd" + LRU list: + internal_search_find: file="NULL" + type=pgsql key="select name from them where id = 'c'" opts=NULL + cached data found but no_rd option set; database lookup required for select name from them where id = 'c' + (tainted) +LOG: MAIN PANIC + tainted search query is not properly quoted (ACL warn, TESTSUITE/test-config 27): select name from them where id = 'c' + search_type NN (pgsql) quoting -1 (none) + PostgreSQL query: "select name from them where id = 'c'" opts 'NULL' + PGSQL using cached connection for localhost:1223/test/CALLER + PGSQL: no data found + replacing old cache entry + lookup failed +check set acl_m0 = FAIL: ${lookup pgsql,cache=no_rd {select name from them where id = '$local_part'}} + = FAIL: +warn: condition test succeeded in ACL "check_recipient" +processing "warn" (TESTSUITE/test-config 32) search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -275,12 +297,13 @@ check set acl_m0 = ok: ${lookup pgsql {select name from the internal_search_find: file="NULL" type=pgsql key="select name from them where id = 'c'" opts="servers=localhost::1223/test/CALLER/" cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "select name from them where id = 'c'" opts 'servers=localhost::1223/test/CALLER/' PGSQL using cached connection for localhost:1223/test/CALLER PGSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223/test/CALLER/ {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223/test/CALLER/ {select name from them where id = '${quote_pgsql:$local_part}'}} = ok: search_open: pgsql "NULL" cached open @@ -290,12 +313,13 @@ check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223/test/CALLER/ internal_search_find: file="NULL" type=pgsql key="select name from them where id = 'c'" opts="servers=localhost::1223" cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "select name from them where id = 'c'" opts 'servers=localhost::1223' PGSQL using cached connection for localhost:1223/test/CALLER PGSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223 {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223 {select name from them where id = '${quote_pgsql:$local_part}'}} = ok: search_open: pgsql "NULL" cached open @@ -305,14 +329,15 @@ check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223 {select name internal_search_find: file="NULL" type=pgsql key="servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts=NULL database lookup required for servers=localhost::1223/test/CALLER/; select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts 'NULL' lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted -processing "warn" (TESTSUITE/test-config 38) +processing "warn" (TESTSUITE/test-config 41) check set acl_m0 = ok: hostlist -check hosts = net-pgsql;select * from them where id='$local_part' +check hosts = net-pgsql;select * from them where id='${quote_pgsql:$local_part}' search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -321,6 +346,7 @@ LRU list: internal_search_find: file="NULL" type=pgsql key="select * from them where id='c'" opts=NULL database lookup required for select * from them where id='c' + (tainted, quoted:pgsql) PostgreSQL query: "select * from them where id='c'" opts 'NULL' PGSQL using cached connection for localhost:1223/test/CALLER PGSQL: no data found @@ -328,9 +354,9 @@ creating new cache entry lookup failed host in "net-pgsql;select * from them where id='c'"? no (end of list) warn: condition test failed in ACL "check_recipient" -processing "warn" (TESTSUITE/test-config 41) +processing "warn" (TESTSUITE/test-config 44) check set acl_m0 = FAIL: hostlist -check hosts = <& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='$local_part' +check hosts = <& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='${quote_pgsql:$local_part}' search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -339,15 +365,16 @@ LRU list: internal_search_find: file="NULL" type=pgsql key="servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts=NULL database lookup required for servers=localhost::1223/test/CALLER/; select * from them where id='c' + (tainted, quoted:pgsql) PostgreSQL query: "servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts 'NULL' lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted host in "<& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c' warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted -processing "warn" (TESTSUITE/test-config 46) +processing "warn" (TESTSUITE/test-config 49) check set acl_m0 = FAIL: hostlist -check hosts = <& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='$local_part' +check hosts = <& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='${quote_pgsql:$local_part}' search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -356,18 +383,19 @@ LRU list: internal_search_find: file="NULL" type=pgsql key=" select * from them where id='c'" opts="servers=localhost::1223/test/CALLER/" database lookup required for select * from them where id='c' + (tainted, quoted:pgsql) PostgreSQL query: " select * from them where id='c'" opts 'servers=localhost::1223/test/CALLER/' lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted host in "<& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c' warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted -processing "accept" (TESTSUITE/test-config 49) +processing "accept" (TESTSUITE/test-config 52) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 50) +processing "accept" (TESTSUITE/test-config 53) check hosts = +relay_hosts search_open: pgsql "NULL" cached open @@ -385,7 +413,7 @@ lookup failed host in "net-pgsql;select * from them where id='10.0.0.0'"? no (end of list) host in "+relay_hosts"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "deny" (TESTSUITE/test-config 51) +processing "deny" (TESTSUITE/test-config 54) message: relay not permitted deny: condition test succeeded in ACL "check_recipient" end of ACL "check_recipient": DENY @@ -403,13 +431,35 @@ processing "warn" (TESTSUITE/test-config 27) internal_search_find: file="NULL" type=pgsql key="select name from them where id = 'c'" opts=NULL cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "select name from them where id = 'c'" opts 'NULL' PGSQL using cached connection for localhost:1223/test/CALLER PGSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup pgsql {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup pgsql {select name from them where id = '${quote_pgsql:$local_part}'}} = ok: + search_open: pgsql "NULL" + cached open + search_find: file="NULL" + key="select name from them where id = 'c'" partial=-1 affix=NULL starflags=0 opts="cache=no_rd" + LRU list: + internal_search_find: file="NULL" + type=pgsql key="select name from them where id = 'c'" opts=NULL + cached data found but no_rd option set; database lookup required for select name from them where id = 'c' + (tainted) +LOG: MAIN PANIC + tainted search query is not properly quoted (ACL warn, TESTSUITE/test-config 27): select name from them where id = 'c' + search_type NN (pgsql) quoting -1 (none) + PostgreSQL query: "select name from them where id = 'c'" opts 'NULL' + PGSQL using cached connection for localhost:1223/test/CALLER + PGSQL: no data found + replacing old cache entry + lookup failed +check set acl_m0 = FAIL: ${lookup pgsql,cache=no_rd {select name from them where id = '$local_part'}} + = FAIL: +warn: condition test succeeded in ACL "check_recipient" +processing "warn" (TESTSUITE/test-config 32) search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -418,12 +468,13 @@ check set acl_m0 = ok: ${lookup pgsql {select name from the internal_search_find: file="NULL" type=pgsql key="select name from them where id = 'c'" opts="servers=localhost::1223/test/CALLER/" cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "select name from them where id = 'c'" opts 'servers=localhost::1223/test/CALLER/' PGSQL using cached connection for localhost:1223/test/CALLER PGSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223/test/CALLER/ {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223/test/CALLER/ {select name from them where id = '${quote_pgsql:$local_part}'}} = ok: search_open: pgsql "NULL" cached open @@ -433,12 +484,13 @@ check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223/test/CALLER/ internal_search_find: file="NULL" type=pgsql key="select name from them where id = 'c'" opts="servers=localhost::1223" cached data found but wrong opts; database lookup required for select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "select name from them where id = 'c'" opts 'servers=localhost::1223' PGSQL using cached connection for localhost:1223/test/CALLER PGSQL: no data found replacing old cache entry lookup failed -check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223 {select name from them where id = '$local_part'}} +check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223 {select name from them where id = '${quote_pgsql:$local_part}'}} = ok: search_open: pgsql "NULL" cached open @@ -448,14 +500,15 @@ check set acl_m0 = ok: ${lookup pgsql,servers=localhost::1223 {select name internal_search_find: file="NULL" type=pgsql key="servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts=NULL database lookup required for servers=localhost::1223/test/CALLER/; select name from them where id = 'c' + (tainted, quoted:pgsql) PostgreSQL query: "servers=localhost::1223/test/CALLER/; select name from them where id = 'c'" opts 'NULL' lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted -processing "warn" (TESTSUITE/test-config 38) +processing "warn" (TESTSUITE/test-config 41) check set acl_m0 = ok: hostlist -check hosts = net-pgsql;select * from them where id='$local_part' +check hosts = net-pgsql;select * from them where id='${quote_pgsql:$local_part}' search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -467,9 +520,9 @@ cached data used for lookup of select * from them where id='c' lookup failed host in "net-pgsql;select * from them where id='c'"? no (end of list) warn: condition test failed in ACL "check_recipient" -processing "warn" (TESTSUITE/test-config 41) +processing "warn" (TESTSUITE/test-config 44) check set acl_m0 = FAIL: hostlist -check hosts = <& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='$local_part' +check hosts = <& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='${quote_pgsql:$local_part}' search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -478,15 +531,16 @@ LRU list: internal_search_find: file="NULL" type=pgsql key="servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts=NULL database lookup required for servers=localhost::1223/test/CALLER/; select * from them where id='c' + (tainted, quoted:pgsql) PostgreSQL query: "servers=localhost::1223/test/CALLER/; select * from them where id='c'" opts 'NULL' lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted host in "<& net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql;servers=localhost::1223/test/CALLER/; select * from them where id='c' warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted -processing "warn" (TESTSUITE/test-config 46) +processing "warn" (TESTSUITE/test-config 49) check set acl_m0 = FAIL: hostlist -check hosts = <& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='$local_part' +check hosts = <& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='${quote_pgsql:$local_part}' search_open: pgsql "NULL" cached open search_find: file="NULL" @@ -495,18 +549,19 @@ LRU list: internal_search_find: file="NULL" type=pgsql key=" select * from them where id='c'" opts="servers=localhost::1223/test/CALLER/" database lookup required for select * from them where id='c' + (tainted, quoted:pgsql) PostgreSQL query: " select * from them where id='c'" opts 'servers=localhost::1223/test/CALLER/' lookup deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted host in "<& net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c'"? list match deferred for net-pgsql,servers=localhost::1223/test/CALLER/; select * from them where id='c' warn: condition test deferred in ACL "check_recipient" LOG: MAIN H=(test) [10.0.0.0] Warning: ACL "warn" statement skipped: condition test deferred: PostgreSQL server "localhost:1223/test/CALLER/" is tainted -processing "accept" (TESTSUITE/test-config 49) +processing "accept" (TESTSUITE/test-config 52) check domains = +local_domains d in "@"? no (end of list) d in "+local_domains"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "accept" (TESTSUITE/test-config 50) +processing "accept" (TESTSUITE/test-config 53) check hosts = +relay_hosts search_open: pgsql "NULL" cached open @@ -520,7 +575,7 @@ lookup failed host in "net-pgsql;select * from them where id='10.0.0.0'"? no (end of list) host in "+relay_hosts"? no (end of list) accept: condition test failed in ACL "check_recipient" -processing "deny" (TESTSUITE/test-config 51) +processing "deny" (TESTSUITE/test-config 54) message: relay not permitted deny: condition test succeeded in ACL "check_recipient" end of ACL "check_recipient": DENY -- cgit v1.2.3