X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=1bfc75d2b6cf1e31cdd36864fe39679e09af92b1;hp=2fc3a81f1bb337ed033d87329ab28f4fb056ca9d;hb=f3ebb786e451da973560f1c9d8cdb151d25108b5;hpb=9883af7240d3c25b7a8a859c9e8482caacd5f1aa diff --git a/src/src/expand.c b/src/src/expand.c index 2fc3a81f1..1bfc75d2b 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -235,6 +235,7 @@ static uschar *op_table_main[] = { US"rxquote", US"s", US"sha1", + US"sha2", US"sha256", US"sha3", US"stat", @@ -281,6 +282,7 @@ enum { EOP_RXQUOTE, EOP_S, EOP_SHA1, + EOP_SHA2, EOP_SHA256, EOP_SHA3, EOP_STAT, @@ -751,7 +753,10 @@ static var_entry var_table[] = { { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, -#if defined(SUPPORT_TLS) +#ifdef EXPERIMENTAL_TLS_RESUME + { "tls_in_resumption", vtype_int, &tls_in.resumption }, +#endif +#ifndef DISABLE_TLS { "tls_in_sni", vtype_stringptr, &tls_in.sni }, #endif { "tls_out_bits", vtype_int, &tls_out.bits }, @@ -765,7 +770,10 @@ static var_entry var_table[] = { { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, { "tls_out_peercert", vtype_cert, &tls_out.peercert }, { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn }, -#if defined(SUPPORT_TLS) +#ifdef EXPERIMENTAL_TLS_RESUME + { "tls_out_resumption", vtype_int, &tls_out.resumption }, +#endif +#ifndef DISABLE_TLS { "tls_out_sni", vtype_stringptr, &tls_out.sni }, #endif #ifdef SUPPORT_DANE @@ -773,7 +781,7 @@ static var_entry var_table[] = { #endif { "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */ -#if defined(SUPPORT_TLS) +#ifndef DISABLE_TLS { "tls_sni", vtype_stringptr, &tls_in.sni }, /* mind the alphabetical order! */ #endif @@ -959,7 +967,7 @@ weirdness they'll twist this into. The result should ideally handle fork(). However, if we're stuck unable to provide this, then we'll fall back to appallingly bad randomness. -If SUPPORT_TLS is defined then this will not be used except as an emergency +If DISABLE_TLS is not defined then this will not be used except as an emergency fallback. Arguments: @@ -967,56 +975,55 @@ Arguments: Returns a random number in range [0, max-1] */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS # define vaguely_random_number vaguely_random_number_fallback #endif int vaguely_random_number(int max) { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS # undef vaguely_random_number #endif - static pid_t pid = 0; - pid_t p2; -#if defined(HAVE_SRANDOM) && !defined(HAVE_SRANDOMDEV) - struct timeval tv; -#endif +static pid_t pid = 0; +pid_t p2; - p2 = getpid(); - if (p2 != pid) +if ((p2 = getpid()) != pid) + { + if (pid != 0) { - if (pid != 0) - { #ifdef HAVE_ARC4RANDOM - /* cryptographically strong randomness, common on *BSD platforms, not - so much elsewhere. Alas. */ -#ifndef NOT_HAVE_ARC4RANDOM_STIR - arc4random_stir(); -#endif + /* cryptographically strong randomness, common on *BSD platforms, not + so much elsewhere. Alas. */ +# ifndef NOT_HAVE_ARC4RANDOM_STIR + arc4random_stir(); +# endif #elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) -#ifdef HAVE_SRANDOMDEV - /* uses random(4) for seeding */ - srandomdev(); -#else - gettimeofday(&tv, NULL); - srandom(tv.tv_sec | tv.tv_usec | getpid()); -#endif +# ifdef HAVE_SRANDOMDEV + /* uses random(4) for seeding */ + srandomdev(); +# else + { + struct timeval tv; + gettimeofday(&tv, NULL); + srandom(tv.tv_sec | tv.tv_usec | getpid()); + } +# endif #else - /* Poor randomness and no seeding here */ + /* Poor randomness and no seeding here */ #endif - } - pid = p2; } + pid = p2; + } #ifdef HAVE_ARC4RANDOM - return arc4random() % max; +return arc4random() % max; #elif defined(HAVE_SRANDOM) || defined(HAVE_SRANDOMDEV) - return random() % max; +return random() % max; #else - /* This one returns a 16-bit number, definitely not crypto-strong */ - return random_number(max); +/* This one returns a 16-bit number, definitely not crypto-strong */ +return random_number(max); #endif } @@ -1282,7 +1289,7 @@ return string_nextinlist(&list, &sep, NULL, 0); /* Certificate fields, by name. Worry about by-OID later */ /* Names are chosen to not have common prefixes */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS typedef struct { uschar * name; @@ -1343,7 +1350,7 @@ expand_string_message = string_sprintf("bad field selector \"%s\" for certextract", field); return NULL; } -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ /************************************************* * Extract a substring from a string * @@ -1766,8 +1773,13 @@ set, in which case give an error. */ if ((Ustrncmp(name, "acl_c", 5) == 0 || Ustrncmp(name, "acl_m", 5) == 0) && !isalpha(name[5])) { - tree_node *node = - tree_search((name[4] == 'c')? acl_var_c : acl_var_m, name + 4); + tree_node * node = + tree_search(name[4] == 'c' ? acl_var_c : acl_var_m, name + 4); + return node ? node->data.ptr : strict_acl_vars ? NULL : US""; + } +else if (Ustrncmp(name, "r_", 2) == 0) + { + tree_node * node = tree_search(router_var, name + 2); return node ? node->data.ptr : strict_acl_vars ? NULL : US""; } @@ -1844,22 +1856,17 @@ switch (vp->type) return sender_host_name ? sender_host_name : US""; case vtype_localpart: /* Get local part from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; - domain = Ustrrchr(s, '@'); - if (domain == NULL) return s; + if (!(s = *((uschar **)(val)))) return US""; + if (!(domain = Ustrrchr(s, '@'))) return s; if (domain - s > sizeof(var_buffer) - 1) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT " in string expansion", sizeof(var_buffer)); - Ustrncpy(var_buffer, s, domain - s); - var_buffer[domain - s] = 0; - return var_buffer; + return string_copyn(s, domain - s); case vtype_domain: /* Get domain from address */ - s = *((uschar **)(val)); - if (s == NULL) return US""; + if (!(s = *((uschar **)(val)))) return US""; domain = Ustrrchr(s, '@'); - return (domain == NULL)? US"" : domain + 1; + return domain ? domain + 1 : US""; case vtype_msgheaders: return find_header(NULL, newsize, exists_only ? FH_EXISTS_ONLY : 0, NULL); @@ -2234,6 +2241,56 @@ return US item; +/************************************************/ +/* Return offset in ops table, or -1 if not found. +Repoint to just after the operator in the string. + +Argument: + ss string representation of operator + opname split-out operator name +*/ + +static int +identify_operator(const uschar ** ss, uschar ** opname) +{ +const uschar * s = *ss; +uschar name[256]; + +/* Numeric comparisons are symbolic */ + +if (*s == '=' || *s == '>' || *s == '<') + { + int p = 0; + name[p++] = *s++; + if (*s == '=') + { + name[p++] = '='; + s++; + } + name[p] = 0; + } + +/* All other conditions are named */ + +else + s = read_name(name, sizeof(name), s, US"_"); +*ss = s; + +/* If we haven't read a name, it means some non-alpha character is first. */ + +if (!name[0]) + { + expand_string_message = string_sprintf("condition name expected, " + "but found \"%.16s\"", s); + return -1; + } +if (opname) + *opname = string_copy(name); + +return chop_match(name, cond_table, nelem(cond_table)); +} + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -2264,6 +2321,7 @@ BOOL is_forany, is_json, is_jsons; int rc, cond_type, roffset; int_eximarith_t num[2]; struct stat statbuf; +uschar * opname; uschar name[256]; const uschar *sub[10]; @@ -2276,37 +2334,7 @@ for (;;) if (*s == '!') { testfor = !testfor; s++; } else break; } -/* Numeric comparisons are symbolic */ - -if (*s == '=' || *s == '>' || *s == '<') - { - int p = 0; - name[p++] = *s++; - if (*s == '=') - { - name[p++] = '='; - s++; - } - name[p] = 0; - } - -/* All other conditions are named */ - -else s = read_name(name, 256, s, US"_"); - -/* If we haven't read a name, it means some non-alpha character is first. */ - -if (name[0] == 0) - { - expand_string_message = string_sprintf("condition name expected, " - "but found \"%.16s\"", s); - return NULL; - } - -/* Find which condition we are dealing with, and switch on it */ - -cond_type = chop_match(name, cond_table, nelem(cond_table)); -switch(cond_type) +switch(cond_type = identify_operator(&s, &opname)) { /* def: tests for a non-empty variable, or for the existence of a header. If yield == NULL we are in a skipping state, and don't care about the answer. */ @@ -2321,7 +2349,7 @@ switch(cond_type) return NULL; } - s = read_name(name, 256, s+1, US"_"); + s = read_name(name, sizeof(name), s+1, US"_"); /* Test for a header's existence. If the name contains a closing brace character, this may be a user error where the terminating colon has been @@ -2333,7 +2361,7 @@ switch(cond_type) && (*++t == '_' || Ustrncmp(t, "eader_", 6) == 0) ) { - s = read_header_name(name, 256, s); + s = read_header_name(name, sizeof(name), s); /* {-for-text-editors */ if (Ustrchr(name, '}') != NULL) malformed_header = TRUE; if (yield) *yield = @@ -2347,9 +2375,9 @@ switch(cond_type) { if (!(t = find_variable(name, TRUE, yield == NULL, NULL))) { - expand_string_message = (name[0] == 0)? - string_sprintf("variable name omitted after \"def:\"") : - string_sprintf("unknown variable \"%s\" after \"def:\"", name); + expand_string_message = name[0] + ? string_sprintf("unknown variable \"%s\" after \"def:\"", name) + : US"variable name omitted after \"def:\""; check_variable_error_message(name); return NULL; } @@ -2505,8 +2533,9 @@ switch(cond_type) if (yield != NULL) { + int rc; *resetok = FALSE; /* eval_acl() might allocate; do not reclaim */ - switch(eval_acl(sub, nelem(sub), &user_msg)) + switch(rc = eval_acl(sub, nelem(sub), &user_msg)) { case OK: cond = TRUE; @@ -2521,7 +2550,8 @@ switch(cond_type) f.expand_string_forcedfail = TRUE; /*FALLTHROUGH*/ default: - expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + expand_string_message = string_sprintf("%s from acl \"%s\"", + rc_names[rc], sub[0]); return NULL; } } @@ -2625,7 +2655,7 @@ switch(cond_type) { if (i == 0) goto COND_FAILED_CURLY_START; expand_string_message = string_sprintf("missing 2nd string in {} " - "after \"%s\"", name); + "after \"%s\"", opname); return NULL; } if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL, @@ -2640,7 +2670,7 @@ switch(cond_type) conditions that compare numbers do not start with a letter. This just saves checking for them individually. */ - if (!isalpha(name[0]) && yield != NULL) + if (!isalpha(opname[0]) && yield != NULL) if (sub[i][0] == 0) { num[i] = 0; @@ -2952,7 +2982,7 @@ switch(cond_type) uschar *save_iterate_item = iterate_item; int (*compare)(const uschar *, const uschar *); - DEBUG(D_expand) debug_printf_indent("condition: %s item: %s\n", name, sub[0]); + DEBUG(D_expand) debug_printf_indent("condition: %s item: %s\n", opname, sub[0]); tempcond = FALSE; compare = cond_type == ECOND_INLISTI @@ -2994,14 +3024,14 @@ switch(cond_type) if (*s != '{') /* }-for-text-editors */ { expand_string_message = string_sprintf("each subcondition " - "inside an \"%s{...}\" condition must be in its own {}", name); + "inside an \"%s{...}\" condition must be in its own {}", opname); return NULL; } if (!(s = eval_condition(s+1, resetok, subcondptr))) { expand_string_message = string_sprintf("%s inside \"%s{...}\" condition", - expand_string_message, name); + expand_string_message, opname); return NULL; } while (isspace(*s)) s++; @@ -3011,7 +3041,7 @@ switch(cond_type) { /* {-for-text-editors */ expand_string_message = string_sprintf("missing } at end of condition " - "inside \"%s\" group", name); + "inside \"%s\" group", opname); return NULL; } @@ -3049,7 +3079,7 @@ switch(cond_type) int sep = 0; uschar *save_iterate_item = iterate_item; - DEBUG(D_expand) debug_printf_indent("condition: %s\n", name); + DEBUG(D_expand) debug_printf_indent("condition: %s\n", opname); while (isspace(*s)) s++; if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ @@ -3070,7 +3100,7 @@ switch(cond_type) if (!(s = eval_condition(sub[1], resetok, NULL))) { expand_string_message = string_sprintf("%s inside \"%s\" condition", - expand_string_message, name); + expand_string_message, opname); return NULL; } while (isspace(*s)) s++; @@ -3080,7 +3110,7 @@ switch(cond_type) { /* {-for-text-editors */ expand_string_message = string_sprintf("missing } at end of condition " - "inside \"%s\"", name); + "inside \"%s\"", opname); return NULL; } @@ -3100,15 +3130,15 @@ switch(cond_type) return NULL; } - DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, iterate_item); + DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", opname, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) { expand_string_message = string_sprintf("%s inside \"%s\" condition", - expand_string_message, name); + expand_string_message, opname); iterate_item = save_iterate_item; return NULL; } - DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", name, + DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", opname, tempcond? "true":"false"); if (yield) *yield = (tempcond == testfor); @@ -3201,19 +3231,20 @@ switch(cond_type) /* Unknown condition */ default: - expand_string_message = string_sprintf("unknown condition \"%s\"", name); - return NULL; + if (!expand_string_message || !*expand_string_message) + expand_string_message = string_sprintf("unknown condition \"%s\"", opname); + return NULL; } /* End switch on condition type */ /* Missing braces at start and end of data */ COND_FAILED_CURLY_START: -expand_string_message = string_sprintf("missing { after \"%s\"", name); +expand_string_message = string_sprintf("missing { after \"%s\"", opname); return NULL; COND_FAILED_CURLY_END: expand_string_message = string_sprintf("missing } at end of \"%s\" condition", - name); + opname); return NULL; /* A condition requires code that is not compiled */ @@ -3223,7 +3254,7 @@ return NULL; !defined(SUPPORT_CRYPTEQ) || !defined(CYRUS_SASLAUTHD_SOCKET) COND_FAILED_NOT_COMPILED: expand_string_message = string_sprintf("support for \"%s\" not compiled", - name); + opname); return NULL; #endif } @@ -3534,7 +3565,7 @@ Returns: pointer to string containing the last three static uschar * prvs_daystamp(int day_offset) { -uschar *days = store_get(32); /* Need at least 24 for cases */ +uschar *days = store_get(32, FALSE); /* Need at least 24 for cases */ (void)string_format(days, 32, TIME_T_FMT, /* where TIME_T_FMT is %lld */ (time(NULL) + day_offset*86400)/86400); return (Ustrlen(days) >= 3) ? &days[Ustrlen(days)-3] : US"100"; @@ -3571,7 +3602,7 @@ uschar innerhash[20]; uschar finalhash[20]; uschar innerkey[64]; uschar outerkey[64]; -uschar *finalhash_hex = store_get(40); +uschar *finalhash_hex; if (key_num == NULL) key_num = US"0"; @@ -3604,7 +3635,9 @@ chash_start(HMAC_SHA1, &h); chash_mid(HMAC_SHA1, &h, outerkey); chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash); -p = finalhash_hex; +/* Hashing is deemed sufficient to de-taint any input data */ + +p = finalhash_hex = store_get(40, FALSE); for (int i = 0; i < 3; i++) { *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; @@ -3653,7 +3686,7 @@ return yield; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS static gstring * cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol) { @@ -3702,17 +3735,15 @@ eval_expr(uschar **sptr, BOOL decimal, uschar **error, BOOL endket) { uschar *s = *sptr; int_eximarith_t x = eval_op_or(&s, decimal, error); -if (*error == NULL) - { + +if (!*error) if (endket) - { if (*s != ')') *error = US"expecting closing parenthesis"; else while (isspace(*(++s))); - } - else if (*s != 0) *error = US"expecting operator"; - } + else if (*s) + *error = US"expecting operator"; *sptr = s; return x; } @@ -3721,12 +3752,12 @@ return x; static int_eximarith_t eval_number(uschar **sptr, BOOL decimal, uschar **error) { -register int c; +int c; int_eximarith_t n; uschar *s = *sptr; + while (isspace(*s)) s++; -c = *s; -if (isdigit(c)) +if (isdigit((c = *s))) { int count; (void)sscanf(CS s, (decimal? SC_EXIM_DEC "%n" : SC_EXIM_ARITH "%n"), &n, &count); @@ -3769,9 +3800,8 @@ if (*s == '+' || *s == '-' || *s == '~') else if (op == '~') x = ~x; } else - { x = eval_number(&s, decimal, error); - } + *sptr = s; return x; } @@ -3950,6 +3980,56 @@ return x; +/************************************************/ +/* Comparison operation for sort expansion. We need to avoid +re-expanding the fields being compared, so need a custom routine. + +Arguments: + cond_type Comparison operator code + leftarg, rightarg Arguments for comparison + +Return true iff (leftarg compare rightarg) +*/ + +static BOOL +sortsbefore(int cond_type, BOOL alpha_cond, + const uschar * leftarg, const uschar * rightarg) +{ +int_eximarith_t l_num, r_num; + +if (!alpha_cond) + { + l_num = expanded_string_integer(leftarg, FALSE); + if (expand_string_message) return FALSE; + r_num = expanded_string_integer(rightarg, FALSE); + if (expand_string_message) return FALSE; + + switch (cond_type) + { + case ECOND_NUM_G: return l_num > r_num; + case ECOND_NUM_GE: return l_num >= r_num; + case ECOND_NUM_L: return l_num < r_num; + case ECOND_NUM_LE: return l_num <= r_num; + default: break; + } + } +else + switch (cond_type) + { + case ECOND_STR_LT: return Ustrcmp (leftarg, rightarg) < 0; + case ECOND_STR_LTI: return strcmpic(leftarg, rightarg) < 0; + case ECOND_STR_LE: return Ustrcmp (leftarg, rightarg) <= 0; + case ECOND_STR_LEI: return strcmpic(leftarg, rightarg) <= 0; + case ECOND_STR_GT: return Ustrcmp (leftarg, rightarg) > 0; + case ECOND_STR_GTI: return strcmpic(leftarg, rightarg) > 0; + case ECOND_STR_GE: return Ustrcmp (leftarg, rightarg) >= 0; + case ECOND_STR_GEI: return strcmpic(leftarg, rightarg) >= 0; + default: break; + } +return FALSE; /* should not happen */ +} + + /************************************************* * Expand string * *************************************************/ @@ -4017,6 +4097,7 @@ static uschar * expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, BOOL skipping, BOOL honour_dollar, BOOL *resetok_p) { +rmark reset_point = store_mark(); gstring * yield = string_get(Ustrlen(string) + 64); int item_type; const uschar *s = string; @@ -4039,6 +4120,14 @@ DEBUG(D_expand) f.expand_string_forcedfail = FALSE; expand_string_message = US""; +if (is_tainted(string)) + { + expand_string_message = + string_sprintf("attempt to expand tainted string '%s'", s); + log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); + goto EXPAND_FAILED; + } + while (*s != 0) { uschar *value; @@ -4110,12 +4199,13 @@ while (*s != 0) buffer. */ if (!yield) - g = store_get(sizeof(gstring)); + g = store_get(sizeof(gstring), FALSE); else if (yield->ptr == 0) { - if (resetok) store_reset(yield); + if (resetok) reset_point = store_reset(reset_point); yield = NULL; - g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */ + reset_point = store_mark(); + g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ } /* Header */ @@ -4241,6 +4331,7 @@ while (*s != 0) { uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; + int rc; switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, US"acl", &resetok)) @@ -4252,7 +4343,7 @@ while (*s != 0) if (skipping) continue; resetok = FALSE; - switch(eval_acl(sub, nelem(sub), &user_msg)) + switch(rc = eval_acl(sub, nelem(sub), &user_msg)) { case OK: case FAIL: @@ -4266,7 +4357,8 @@ while (*s != 0) f.expand_string_forcedfail = TRUE; /*FALLTHROUGH*/ default: - expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + expand_string_message = string_sprintf("%s from acl \"%s\"", + rc_names[rc], sub[0]); goto EXPAND_FAILED; } } @@ -4934,13 +5026,13 @@ while (*s != 0) client_conn_ctx cctx; int timeout = 5; int save_ptr = yield->ptr; - FILE * fp; + FILE * fp = NULL; uschar * arg; uschar * sub_arg[4]; uschar * server_name = NULL; host_item host; BOOL do_shutdown = TRUE; - BOOL do_tls = FALSE; /* Only set under SUPPORT_TLS */ + BOOL do_tls = FALSE; /* Only set under ! DISABLE_TLS */ blob reqstr; if (expand_forbid & RDO_READSOCK) @@ -4984,7 +5076,7 @@ while (*s != 0) while ((item = string_nextinlist(&list, &sep, NULL, 0))) if (Ustrncmp(item, US"shutdown=", 9) == 0) { if (Ustrcmp(item + 9, US"no") == 0) do_shutdown = FALSE; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS else if (Ustrncmp(item, US"tls=", 4) == 0) { if (Ustrcmp(item + 9, US"no") != 0) do_tls = TRUE; } #endif @@ -5040,7 +5132,7 @@ while (*s != 0) port = ntohs(service_info->s_port); } - /*XXX we trust that the request is idempotent. Hmm. */ + /*XXX we trust that the request is idempotent for TFO. Hmm. */ cctx.sock = ip_connectedsocket(SOCK_STREAM, server_name, port, port, timeout, &host, &expand_string_message, do_tls ? NULL : &reqstr); @@ -5091,7 +5183,7 @@ while (*s != 0) DEBUG(D_expand) debug_printf_indent("connected to socket %s\n", sub_arg[0]); -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (do_tls) { smtp_connect_args conn_args = {.host = &host }; @@ -5116,7 +5208,7 @@ while (*s != 0) DEBUG(D_expand) debug_printf_indent("writing \"%s\" to socket\n", reqstr.data); if ( ( -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS do_tls ? tls_write(cctx.tls_ctx, reqstr.data, reqstr.len, FALSE) : #endif write(cctx.sock, reqstr.data, reqstr.len)) != reqstr.len) @@ -5145,13 +5237,13 @@ while (*s != 0) sigalrm_seen = FALSE; ALARM(timeout); yield = -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS do_tls ? cat_file_tls(cctx.tls_ctx, yield, sub_arg[3]) : #endif cat_file(fp, yield, sub_arg[3]); ALARM_CLR(0); -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (do_tls) { tls_close(cctx.tls_ctx, TRUE); @@ -5295,7 +5387,7 @@ while (*s != 0) { if (sigalrm_seen || runrc == -256) { - expand_string_message = string_sprintf("command timed out"); + expand_string_message = US"command timed out"; killpg(pid, SIGKILL); /* Kill the whole process group */ } @@ -5837,8 +5929,8 @@ while (*s != 0) while (isspace(*s)) s++; if (*s != ':') { - expand_string_message = string_sprintf( - "missing object value-separator for extract json"); + expand_string_message = + US"missing object value-separator for extract json"; goto EXPAND_FAILED_CURLY; } s++; @@ -5985,7 +6077,7 @@ while (*s != 0) continue; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS case EITEM_CERTEXTRACT: { uschar *save_lookup_value = lookup_value; @@ -6065,7 +6157,7 @@ while (*s != 0) save_expand_nlength); continue; } -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ /* Handle list operations */ @@ -6271,9 +6363,10 @@ while (*s != 0) case EITEM_SORT: { + int cond_type; int sep = 0; const uschar *srclist, *cmp, *xtract; - uschar *srcitem; + uschar * opname, * srcitem; const uschar *dstlist = NULL, *dstkeylist = NULL; uschar * tmp; uschar *save_iterate_item = iterate_item; @@ -6308,6 +6401,25 @@ while (*s != 0) goto EXPAND_FAILED_CURLY; } + if ((cond_type = identify_operator(&cmp, &opname)) == -1) + { + if (!expand_string_message) + expand_string_message = string_sprintf("unknown condition \"%s\"", s); + goto EXPAND_FAILED; + } + switch(cond_type) + { + case ECOND_NUM_L: case ECOND_NUM_LE: + case ECOND_NUM_G: case ECOND_NUM_GE: + case ECOND_STR_GE: case ECOND_STR_GEI: case ECOND_STR_GT: case ECOND_STR_GTI: + case ECOND_STR_LE: case ECOND_STR_LEI: case ECOND_STR_LT: case ECOND_STR_LTI: + break; + + default: + expand_string_message = US"comparator not handled for sort"; + goto EXPAND_FAILED; + } + while (isspace(*s)) s++; if (*s++ != '{') { @@ -6316,8 +6428,8 @@ while (*s != 0) } xtract = s; - tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); - if (!tmp) goto EXPAND_FAILED; + if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok))) + goto EXPAND_FAILED; xtract = string_copyn(xtract, s - xtract); if (*s++ != '}') @@ -6335,11 +6447,10 @@ while (*s != 0) if (skipping) continue; while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0))) - { - uschar * dstitem; + { + uschar * srcfield, * dstitem; gstring * newlist = NULL; gstring * newkeylist = NULL; - uschar * srcfield; DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, srcitem); @@ -6360,25 +6471,15 @@ while (*s != 0) while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) { uschar * dstfield; - uschar * expr; - BOOL before; /* field for comparison */ if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) goto sort_mismatch; - /* build and run condition string */ - expr = string_sprintf("%s{%s}{%s}", cmp, srcfield, dstfield); + /* String-comparator names start with a letter; numeric names do not */ - DEBUG(D_expand) debug_printf_indent("%s: cond = \"%s\"\n", name, expr); - if (!eval_condition(expr, &resetok, &before)) - { - expand_string_message = string_sprintf("comparison in sort: %s", - expr); - goto EXPAND_FAILED; - } - - if (before) + if (sortsbefore(cond_type, isalpha(opname[0]), + srcfield, dstfield)) { /* New-item sorts before this dst-item. Append new-item, then dst-item, then remainder of dst list. */ @@ -6390,6 +6491,7 @@ while (*s != 0) newlist = string_append_listele(newlist, sep, dstitem); newkeylist = string_append_listele(newkeylist, sep, dstfield); +/*XXX why field-at-a-time copy? Why not just dup the rest of the list? */ while ((dstitem = string_nextinlist(&dstlist, &sep, NULL, 0))) { if (!(dstfield = string_nextinlist(&dstkeylist, &sep, NULL, 0))) @@ -6485,7 +6587,7 @@ while (*s != 0) log_write(0, LOG_MAIN|LOG_PANIC, "%s", expand_string_message); goto EXPAND_FAILED; } - t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0])); + t = store_get_perm(sizeof(tree_node) + Ustrlen(argv[0]), is_tainted(argv[0])); Ustrcpy(t->name, argv[0]); t->data.ptr = handle; (void)tree_insertnode(&dlobj_anchor, t); @@ -6577,7 +6679,7 @@ while (*s != 0) int c; uschar *arg = NULL; uschar *sub; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS var_entry *vp = NULL; #endif @@ -6600,7 +6702,7 @@ while (*s != 0) as we do not want to do the usual expansion. For most, expand the string.*/ switch(c) { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS case EOP_MD5: case EOP_SHA1: case EOP_SHA256: @@ -6755,7 +6857,7 @@ while (*s != 0) } case EOP_MD5: -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (vp && *(void **)vp->value) { uschar * cp = tls_cert_fprt_md5(*(void **)vp->value); @@ -6774,7 +6876,7 @@ while (*s != 0) continue; case EOP_SHA1: -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (vp && *(void **)vp->value) { uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value); @@ -6792,23 +6894,30 @@ while (*s != 0) } continue; + case EOP_SHA2: case EOP_SHA256: #ifdef EXIM_HAVE_SHA2 if (vp && *(void **)vp->value) - { - uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value); - yield = string_cat(yield, cp); - } + if (c == EOP_SHA256) + yield = string_cat(yield, tls_cert_fprt_sha256(*(void **)vp->value)); + else + expand_string_message = US"sha2_N not supported with certificates"; else { hctx h; blob b; + hashmethod m = !arg ? HASH_SHA2_256 + : Ustrcmp(arg, "256") == 0 ? HASH_SHA2_256 + : Ustrcmp(arg, "384") == 0 ? HASH_SHA2_384 + : Ustrcmp(arg, "512") == 0 ? HASH_SHA2_512 + : HASH_BADTYPE; - if (!exim_sha_init(&h, HASH_SHA2_256)) + if (m == HASH_BADTYPE || !exim_sha_init(&h, m)) { - expand_string_message = US"unrecognised sha256 variant"; + expand_string_message = US"unrecognised sha2 variant"; goto EXPAND_FAILED; } + exim_sha_update(&h, sub, Ustrlen(sub)); exim_sha_finish(&h, &b); while (b.len-- > 0) @@ -6950,7 +7059,7 @@ while (*s != 0) case 'h': t = tree_search(hostlist_anchor, sub); suffix = US"_h"; break; case 'l': t = tree_search(localpartlist_anchor, sub); suffix = US"_l"; break; default: - expand_string_message = string_sprintf("bad suffix on \"list\" operator"); + expand_string_message = US"bad suffix on \"list\" operator"; goto EXPAND_FAILED; } @@ -7545,7 +7654,7 @@ while (*s != 0) case EOP_STR2B64: case EOP_BASE64: { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS uschar * s = vp && *(void **)vp->value ? tls_cert_der_b64(*(void **)vp->value) : b64encode(CUS sub, Ustrlen(sub)); @@ -7774,12 +7883,13 @@ while (*s != 0) gstring * g = NULL; if (!yield) - g = store_get(sizeof(gstring)); + g = store_get(sizeof(gstring), FALSE); else if (yield->ptr == 0) { - if (resetok) store_reset(yield); + if (resetok) reset_point = store_reset(reset_point); yield = NULL; - g = store_get(sizeof(gstring)); /* alloc _before_ calling find_variable() */ + reset_point = store_mark(); + g = store_get(sizeof(gstring), FALSE); /* alloc _before_ calling find_variable() */ } if (!(value = find_variable(name, FALSE, skipping, &newsize))) { @@ -7833,15 +7943,20 @@ if (left) *left = s; In many cases the final string will be the first one that was got and so there will be optimal store usage. */ -if (resetok) store_reset(yield->s + (yield->size = yield->ptr + 1)); +if (resetok) gstring_release_unused(yield); else if (resetok_p) *resetok_p = FALSE; DEBUG(D_expand) + { + BOOL tainted = is_tainted(yield->s); DEBUG(D_noutf8) { debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); debug_printf_indent("%sresult: %s\n", skipping ? "|-----" : "\\_____", yield->s); + if (tainted) + debug_printf_indent("%s \\__(tainted)\n", + skipping ? "| " : " "); if (skipping) debug_printf_indent("\\___skipping: result is not used\n"); } @@ -7850,15 +7965,19 @@ DEBUG(D_expand) debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ "expanding: %.*s\n", (int)(s - string), string); - debug_printf_indent("%s" - UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "result: %s\n", skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, yield->s); + if (tainted) + debug_printf_indent("%s(tainted)\n", + skipping + ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); if (skipping) debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "skipping: result is not used\n"); } + } expand_level--; return yield->s; @@ -8356,15 +8475,15 @@ if (opt_perl_startup != NULL) } #endif /* EXIM_PERL */ +/* Thie deliberately regards the input as untainted, so that it can be +expanded; only reasonable since this is a test for string-expansions. */ + while (fgets(buffer, sizeof(buffer), stdin) != NULL) { - void *reset_point = store_get(0); + rmark reset_point = store_mark(); uschar *yield = expand_string(buffer); - if (yield != NULL) - { + if (yield) printf("%s\n", yield); - store_reset(reset_point); - } else { if (f.search_find_defer) printf("search_find deferred\n"); @@ -8372,6 +8491,7 @@ while (fgets(buffer, sizeof(buffer), stdin) != NULL) if (f.expand_string_forcedfail) printf("Forced failure\n"); printf("\n"); } + store_reset(reset_point); } search_tidyup();