X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/44644c2e404a3ea0191db0b0458e86924fb240bb..ee3c2fea18d0c940c2256c6bf041f546c703c375:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index b01512425..8a571b2d9 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1298,15 +1298,16 @@ expand_getlistele(int field, const uschar * list) { const uschar * tlist = list; int sep = 0; -uschar dummy; +/* Tainted mem for the throwaway element copies */ +uschar * dummy = store_get(2, TRUE); if (field < 0) { - for (field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++; + for (field++; string_nextinlist(&tlist, &sep, dummy, 1); ) field++; sep = 0; } if (field == 0) return NULL; -while (--field > 0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; +while (--field > 0 && (string_nextinlist(&list, &sep, dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); } @@ -1708,9 +1709,9 @@ authres_iprev(gstring * g) if (sender_host_name) g = string_append(g, 3, US";\n\tiprev=pass (", sender_host_name, US")"); else if (host_lookup_deferred) - g = string_catn(g, US";\n\tiprev=temperror", 19); + g = string_cat(g, US";\n\tiprev=temperror"); else if (host_lookup_failed) - g = string_catn(g, US";\n\tiprev=fail", 13); + g = string_cat(g, US";\n\tiprev=fail"); else return g; @@ -1984,11 +1985,12 @@ switch (vp->type) ss = (uschar **)(val); if (!*ss && deliver_datafile >= 0) /* Read body when needed */ { - uschar *body; + uschar * body; off_t start_offset = SPOOL_DATA_START_OFFSET; int len = message_body_visible; + if (len > message_size) len = message_size; - *ss = body = store_malloc(len+1); + *ss = body = store_get(len+1, TRUE); body[0] = 0; if (vp->type == vtype_msgbody_end) { @@ -2003,8 +2005,7 @@ switch (vp->type) if (lseek(deliver_datafile, start_offset, SEEK_SET) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "deliver_datafile lseek: %s", strerror(errno)); - len = read(deliver_datafile, body, len); - if (len > 0) + if ((len = read(deliver_datafile, body, len)) > 0) { body[len] = 0; if (message_body_newlines) /* Separate loops for efficiency */ @@ -3451,7 +3452,7 @@ switch(cond_type = identify_operator(&s, &opname)) uschar * sub[2]; const pcre * re; int ovec[3*(4+1)]; - int n; + int n, quoting = 0; uschar cksum[4]; BOOL boolvalue = FALSE; @@ -3474,10 +3475,20 @@ switch(cond_type = identify_operator(&s, &opname)) goto srs_result; } - /* Side-effect: record the decoded recipient */ + if (sub[0][0] == '"') + quoting = 1; + else for (uschar * s = sub[0]; *s; s++) + if (!isalnum(*s) && Ustrchr(".!#$%&'*+-/=?^_`{|}~", *s) == NULL) + { quoting = 1; break; } + if (quoting) + DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n"); + + /* Record the (quoted, if needed) decoded recipient as $srs_recipient */ - srs_recipient = string_sprintf("%.*S@%.*S", /* lowercased */ + srs_recipient = string_sprintf("%.*s%.*S%.*s@%.*S", /* lowercased */ + quoting, "\"", ovec[9]-ovec[8], sub[0] + ovec[8], /* substring 4 */ + quoting, "\"", ovec[7]-ovec[6], sub[0] + ovec[6]); /* substring 3 */ /* If a zero-length secret was given, we're done. Otherwise carry on @@ -4292,6 +4303,98 @@ return FALSE; /* should not happen */ } +/* Expand a named list. Return false on failure. */ +static gstring * +expand_listnamed(gstring * yield, const uschar * name, const uschar * listtype) +{ +tree_node *t = NULL; +const uschar * list; +int sep = 0; +uschar * item; +uschar * suffix = US""; +BOOL needsep = FALSE; +#define LISTNAMED_BUF_SIZE 256 +uschar b[LISTNAMED_BUF_SIZE]; +uschar * buffer = b; + +if (*name == '+') name++; +if (!listtype) /* no-argument version */ + { + if ( !(t = tree_search(addresslist_anchor, name)) + && !(t = tree_search(domainlist_anchor, name)) + && !(t = tree_search(hostlist_anchor, name))) + t = tree_search(localpartlist_anchor, name); + } +else switch(*listtype) /* specific list-type version */ + { + case 'a': t = tree_search(addresslist_anchor, name); suffix = US"_a"; break; + case 'd': t = tree_search(domainlist_anchor, name); suffix = US"_d"; break; + case 'h': t = tree_search(hostlist_anchor, name); suffix = US"_h"; break; + case 'l': t = tree_search(localpartlist_anchor, name); suffix = US"_l"; break; + default: + expand_string_message = US"bad suffix on \"list\" operator"; + return yield; + } + +if(!t) + { + expand_string_message = string_sprintf("\"%s\" is not a %snamed list", + name, !listtype?"" + : *listtype=='a'?"address " + : *listtype=='d'?"domain " + : *listtype=='h'?"host " + : *listtype=='l'?"localpart " + : 0); + return yield; + } + +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); +while ((item = string_nextinlist(&list, &sep, buffer, LISTNAMED_BUF_SIZE))) + { + uschar * buf = US" : "; + if (needsep) + yield = string_catn(yield, buf, 3); + else + needsep = TRUE; + + if (*item == '+') /* list item is itself a named list */ + { + yield = expand_listnamed(yield, item, listtype); + if (expand_string_message) + return yield; + } + + else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */ + { + char tok[3]; + tok[0] = sep; tok[1] = ':'; tok[2] = 0; + + for(char * cp; cp = strpbrk(CCS item, tok); item = US cp) + { + yield = string_catn(yield, item, cp - CS item); + if (*cp++ == ':') /* colon in a non-colon-sep list item, needs doubling */ + yield = string_catn(yield, US"::", 2); + else /* sep in item; should already be doubled; emit once */ + { + yield = string_catn(yield, US tok, 1); + if (*cp == sep) cp++; + } + } + yield = string_cat(yield, item); + } + else + yield = string_cat(yield, item); + } +return yield; +} + + + /************************************************* * Expand string * *************************************************/ @@ -4390,7 +4493,7 @@ if (is_tainted(string)) goto EXPAND_FAILED; } -while (*s != 0) +while (*s) { uschar *value; uschar name[256]; @@ -4776,7 +4879,7 @@ while (*s != 0) int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); - if ((expand_forbid & RDO_LOOKUP) != 0) + if (expand_forbid & RDO_LOOKUP) { expand_string_message = US"lookup expansions are not permitted"; goto EXPAND_FAILED; @@ -4875,21 +4978,7 @@ while (*s != 0) file types, the query (i.e. "key") starts with a file name. */ if (!key) - { - Uskip_whitespace(&filename); - key = filename; - - if (mac_islookup(stype, lookup_querystyle)) - filename = NULL; - else - if (*filename == '/') - { - while (*key && !isspace(*key)) key++; - if (*key) *key++ = '\0'; - } - else - filename = NULL; - } + key = search_args(stype, name, filename, &filename, opts); /* If skipping, don't do the next bit - just lookup_value == NULL, as if the entry was not found. Note that there is no search_close() function. @@ -4920,7 +5009,7 @@ while (*s != 0) { expand_string_message = string_sprintf("lookup of \"%s\" gave DEFER: %s", - string_printing2(key, FALSE), search_error_message); + string_printing2(key, SP_TAB), search_error_message); goto EXPAND_FAILED; } if (expand_setup > 0) expand_nmax = expand_setup; @@ -5265,7 +5354,7 @@ while (*s != 0) if (!(f = Ufopen(sub_arg[0], "rb"))) { - expand_string_message = string_open_failed(errno, "%s", sub_arg[0]); + expand_string_message = string_open_failed("%s", sub_arg[0]); goto EXPAND_FAILED; } @@ -5334,11 +5423,14 @@ while (*s != 0) while ((item = string_nextinlist(&list, &sep, NULL, 0))) g = string_append_listele(g, ',', item); - /* possibly plus an EOL string */ + /* possibly plus an EOL string. Process with escapes, to protect + from list-processing. The only current user of eol= in search + options is the readsock expansion. */ + if (sub_arg[3] && *sub_arg[3]) g = string_append_listele(g, ',', - string_sprintf("eol=%s", sub_arg[3])); - + string_sprintf("eol=%s", + string_printing2(sub_arg[3], SP_TAB|SP_SPACE))); } /* Gat a (possibly cached) handle for the connection */ @@ -6791,6 +6883,8 @@ while (*s != 0) { uschar * sub[3]; uschar cksum[4]; + gstring * g = NULL; + BOOL quoted = FALSE; switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok)) { @@ -6799,41 +6893,65 @@ while (*s != 0) case 3: goto EXPAND_FAILED; } - yield = string_catn(yield, US"SRS0=", 5); + g = string_catn(g, US"SRS0=", 5); /* ${l_4:${hmac{md5}{SRS_SECRET}{${lc:$return_path}}}}= */ hmac_md5(sub[0], string_copylc(sub[1]), cksum, sizeof(cksum)); - yield = string_catn(yield, cksum, sizeof(cksum)); - yield = string_catn(yield, US"=", 1); + g = string_catn(g, cksum, sizeof(cksum)); + g = string_catn(g, US"=", 1); /* ${base32:${eval:$tod_epoch/86400&0x3ff}}= */ { struct timeval now; unsigned long i; - gstring * g = NULL; + gstring * h = NULL; gettimeofday(&now, NULL); for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5) - g = string_catn(g, &base32_chars[i & 0x1f], 1); - if (g) while (g->ptr > 0) - yield = string_catn(yield, &g->s[--g->ptr], 1); + h = string_catn(h, &base32_chars[i & 0x1f], 1); + if (h) while (h->ptr > 0) + g = string_catn(g, &h->s[--h->ptr], 1); } - yield = string_catn(yield, US"=", 1); + g = string_catn(g, US"=", 1); /* ${domain:$return_path}=${local_part:$return_path} */ { int start, end, domain; uschar * t = parse_extract_address(sub[1], &expand_string_message, &start, &end, &domain, FALSE); + uschar * s; + if (!t) goto EXPAND_FAILED; - if (domain > 0) yield = string_cat(yield, t + domain); - yield = string_catn(yield, US"=", 1); - yield = domain > 0 - ? string_catn(yield, t, domain - 1) : string_cat(yield, t); + if (domain > 0) g = string_cat(g, t + domain); + g = string_catn(g, US"=", 1); + + s = domain > 0 ? string_copyn(t, domain - 1) : t; + if ((quoted = Ustrchr(s, '"') != NULL)) + { + gstring * h = NULL; + DEBUG(D_expand) debug_printf_indent("auto-quoting local part\n"); + while (*s) /* de-quote */ + { + while (*s && *s != '"') h = string_catn(h, s++, 1); + if (*s) s++; + while (*s && *s != '"') h = string_catn(h, s++, 1); + if (*s) s++; + } + gstring_release_unused(h); + s = string_from_gstring(h); + } + g = string_cat(g, s); } + /* Assume that if the original local_part had quotes + it was for good reason */ + + if (quoted) yield = string_catn(yield, US"\"", 1); + yield = string_catn(yield, g->s, g->ptr); + if (quoted) yield = string_catn(yield, US"\"", 1); + /* @$original_domain */ yield = string_catn(yield, US"@", 1); yield = string_cat(yield, sub[2]); @@ -7206,10 +7324,10 @@ while (*s != 0) case EOP_LISTCOUNT: { - int cnt = 0; - int sep = 0; + int cnt = 0, sep = 0; + uschar * buf = store_get(2, is_tainted(sub)); - while (string_nextinlist(CUSS &sub, &sep, NULL, 0)) cnt++; + while (string_nextinlist(CUSS &sub, &sep, buf, 1)) cnt++; yield = string_fmt_append(yield, "%d", cnt); continue; } @@ -7218,86 +7336,11 @@ while (*s != 0) /* handles nested named lists; requotes as colon-sep list */ case EOP_LISTNAMED: - { - tree_node *t = NULL; - const uschar * list; - int sep = 0; - uschar * item; - uschar * suffix = US""; - BOOL needsep = FALSE; - uschar buffer[256]; - - if (*sub == '+') sub++; - if (!arg) /* no-argument version */ - { - if (!(t = tree_search(addresslist_anchor, sub)) && - !(t = tree_search(domainlist_anchor, sub)) && - !(t = tree_search(hostlist_anchor, sub))) - t = tree_search(localpartlist_anchor, sub); - } - else switch(*arg) /* specific list-type version */ - { - case 'a': t = tree_search(addresslist_anchor, sub); suffix = US"_a"; break; - case 'd': t = tree_search(domainlist_anchor, sub); suffix = US"_d"; break; - case 'h': t = tree_search(hostlist_anchor, sub); suffix = US"_h"; break; - case 'l': t = tree_search(localpartlist_anchor, sub); suffix = US"_l"; break; - default: - expand_string_message = US"bad suffix on \"list\" operator"; - goto EXPAND_FAILED; - } - - if(!t) - { - expand_string_message = string_sprintf("\"%s\" is not a %snamed list", - sub, !arg?"" - : *arg=='a'?"address " - : *arg=='d'?"domain " - : *arg=='h'?"host " - : *arg=='l'?"localpart " - : 0); + expand_string_message = NULL; + yield = expand_listnamed(yield, sub, arg); + if (expand_string_message) goto EXPAND_FAILED; - } - - list = ((namedlist_block *)(t->data.ptr))->string; - - while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) - { - uschar * buf = US" : "; - if (needsep) - yield = string_catn(yield, buf, 3); - else - needsep = TRUE; - - if (*item == '+') /* list item is itself a named list */ - { - uschar * sub = string_sprintf("${listnamed%s:%s}", suffix, item); - item = expand_string_internal(sub, FALSE, NULL, FALSE, TRUE, &resetok); - } - else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */ - { - char * cp; - char tok[3]; - tok[0] = sep; tok[1] = ':'; tok[2] = 0; - while ((cp= strpbrk(CCS item, tok))) - { - yield = string_catn(yield, item, cp - CS item); - if (*cp++ == ':') /* colon in a non-colon-sep list item, needs doubling */ - { - yield = string_catn(yield, US"::", 2); - item = US cp; - } - else /* sep in item; should already be doubled; emit once */ - { - yield = string_catn(yield, US tok, 1); - if (*cp == sep) cp++; - item = US cp; - } - } - } - yield = string_cat(yield, item); - } continue; - } /* quote a list-item for the given list-separator */ @@ -7485,24 +7528,20 @@ while (*s != 0) uschar *t = sub - 1; if (c == EOP_QUOTE) - { - while (!needs_quote && *(++t) != 0) + while (!needs_quote && *++t) needs_quote = !isalnum(*t) && !strchr("_-.", *t); - } + else /* EOP_QUOTE_LOCAL_PART */ - { - while (!needs_quote && *(++t) != 0) - needs_quote = !isalnum(*t) && - strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL && - (*t != '.' || t == sub || t[1] == 0); - } + while (!needs_quote && *++t) + needs_quote = !isalnum(*t) + && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL + && (*t != '.' || t == sub || !t[1]); if (needs_quote) { yield = string_catn(yield, US"\"", 1); t = sub - 1; - while (*(++t) != 0) - { + while (*++t) if (*t == '\n') yield = string_catn(yield, US"\\n", 2); else if (*t == '\r') @@ -7513,10 +7552,10 @@ while (*s != 0) yield = string_catn(yield, US"\\", 1); yield = string_catn(yield, t, 1); } - } yield = string_catn(yield, US"\"", 1); } - else yield = string_cat(yield, sub); + else + yield = string_cat(yield, sub); continue; } @@ -7571,13 +7610,10 @@ while (*s != 0) prescribed by the RFC, if there are characters that need to be encoded */ case EOP_RFC2047: - { - uschar buffer[2048]; yield = string_cat(yield, parse_quote_2047(sub, Ustrlen(sub), headers_charset, - buffer, sizeof(buffer), FALSE)); + FALSE)); continue; - } /* RFC 2047 decode */ @@ -8605,6 +8641,7 @@ debug_selector = D_v; debug_file = stderr; debug_fd = fileno(debug_file); big_buffer = malloc(big_buffer_size); +store_init(); for (int i = 1; i < argc; i++) {