X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/e661a29c6c38215e205f595a8ed1aedaf3a963ed..01603eec64d42431f182b33008206facfc7f800e:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index 79304c8b1..31059c432 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, @@ -312,7 +314,11 @@ static uschar *cond_table[] = { US"exists", US"first_delivery", US"forall", + US"forall_json", + US"forall_jsons", US"forany", + US"forany_json", + US"forany_jsons", US"ge", US"gei", US"gt", @@ -358,7 +364,11 @@ enum { ECOND_EXISTS, ECOND_FIRST_DELIVERY, ECOND_FORALL, + ECOND_FORALL_JSON, + ECOND_FORALL_JSONS, ECOND_FORANY, + ECOND_FORANY_JSON, + ECOND_FORANY_JSONS, ECOND_STR_GE, ECOND_STR_GEI, ECOND_STR_GT, @@ -660,9 +670,6 @@ static var_entry var_table[] = { { "regex_match_string", vtype_stringptr, ®ex_match_string }, #endif { "reply_address", vtype_reply, NULL }, -#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) - { "requiretls", vtype_bool, &tls_requiretls }, -#endif { "return_path", vtype_stringptr, &return_path }, { "return_size_limit", vtype_int, &bounce_return_size_limit }, { "router_name", vtype_stringptr, &router_name }, @@ -741,16 +748,21 @@ static var_entry var_table[] = { { "tls_in_bits", vtype_int, &tls_in.bits }, { "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified }, { "tls_in_cipher", vtype_stringptr, &tls_in.cipher }, + { "tls_in_cipher_std", vtype_stringptr, &tls_in.cipher_stdname }, { "tls_in_ocsp", vtype_int, &tls_in.ocsp }, { "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 }, { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified }, { "tls_out_cipher", vtype_stringptr, &tls_out.cipher }, + { "tls_out_cipher_std", vtype_stringptr, &tls_out.cipher_stdname }, #ifdef SUPPORT_DANE { "tls_out_dane", vtype_bool, &tls_out.dane_verified }, #endif @@ -758,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 @@ -766,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 @@ -952,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: @@ -960,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 } @@ -1275,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; @@ -1336,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 * @@ -2144,6 +2158,89 @@ return ret; +/* Return pointer to dewrapped string, with enclosing specified chars removed. +The given string is modified on return. Leading whitespace is skipped while +looking for the opening wrap character, then the rest is scanned for the trailing +(non-escaped) wrap character. A backslash in the string will act as an escape. + +A nul is written over the trailing wrap, and a pointer to the char after the +leading wrap is returned. + +Arguments: + s String for de-wrapping + wrap Two-char string, the first being the opener, second the closer wrapping + character +Return: + Pointer to de-wrapped string, or NULL on error (with expand_string_message set). +*/ + +static uschar * +dewrap(uschar * s, const uschar * wrap) +{ +uschar * p = s; +unsigned depth = 0; +BOOL quotesmode = wrap[0] == wrap[1]; + +while (isspace(*p)) p++; + +if (*p == *wrap) + { + s = ++p; + wrap++; + while (*p) + { + if (*p == '\\') p++; + else if (!quotesmode && *p == wrap[-1]) depth++; + else if (*p == *wrap) + if (depth == 0) + { + *p = '\0'; + return s; + } + else + depth--; + p++; + } + } +expand_string_message = string_sprintf("missing '%c'", *wrap); +return NULL; +} + + +/* Pull off the leading array or object element, returning +a copy in an allocated string. Update the list pointer. + +The element may itself be an abject or array. +Return NULL when the list is empty. +*/ + +static uschar * +json_nextinlist(const uschar ** list) +{ +unsigned array_depth = 0, object_depth = 0; +const uschar * s = *list, * item; + +while (isspace(*s)) s++; + +for (item = s; + *s && (*s != ',' || array_depth != 0 || object_depth != 0); + s++) + switch (*s) + { + case '[': array_depth++; break; + case ']': array_depth--; break; + case '{': object_depth++; break; + case '}': object_depth--; break; + } +*list = *s ? s+1 : s; +if (item == s) return NULL; +item = string_copyn(item, s - item); +DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item); +return US item; +} + + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -2170,6 +2267,7 @@ BOOL testfor = TRUE; BOOL tempcond, combined_cond; BOOL *subcondptr; BOOL sub2_honour_dollar = TRUE; +BOOL is_forany, is_json, is_jsons; int rc, cond_type, roffset; int_eximarith_t num[2]; struct stat statbuf; @@ -2945,8 +3043,14 @@ switch(cond_type) /* forall/forany: iterates a condition with different values */ - case ECOND_FORALL: - case ECOND_FORANY: + case ECOND_FORALL: is_forany = FALSE; is_json = FALSE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORANY: is_forany = TRUE; is_json = FALSE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORALL_JSON: is_forany = FALSE; is_json = TRUE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORANY_JSON: is_forany = TRUE; is_json = TRUE; is_jsons = FALSE; goto FORMANY; + case ECOND_FORALL_JSONS: is_forany = FALSE; is_json = TRUE; is_jsons = TRUE; goto FORMANY; + case ECOND_FORANY_JSONS: is_forany = TRUE; is_json = TRUE; is_jsons = TRUE; goto FORMANY; + + FORMANY: { const uschar * list; int sep = 0; @@ -2987,10 +3091,22 @@ switch(cond_type) return NULL; } - if (yield != NULL) *yield = !testfor; + if (yield) *yield = !testfor; list = sub[0]; - while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0)) != NULL) + if (is_json) list = dewrap(string_copy(list), US"[]"); + while ((iterate_item = is_json + ? json_nextinlist(&list) : string_nextinlist(&list, &sep, NULL, 0))) { + if (is_jsons) + if (!(iterate_item = dewrap(iterate_item, US"\"\""))) + { + expand_string_message = + string_sprintf("%s wrapping string result for extract jsons", + expand_string_message); + iterate_item = save_iterate_item; + return NULL; + } + DEBUG(D_expand) debug_printf_indent("%s: $item = \"%s\"\n", name, iterate_item); if (!eval_condition(sub[1], resetok, &tempcond)) { @@ -3002,8 +3118,8 @@ switch(cond_type) DEBUG(D_expand) debug_printf_indent("%s: condition evaluated to %s\n", name, tempcond? "true":"false"); - if (yield != NULL) *yield = (tempcond == testfor); - if (tempcond == (cond_type == ECOND_FORANY)) break; + if (yield) *yield = (tempcond == testfor); + if (tempcond == is_forany) break; } iterate_item = save_iterate_item; @@ -3544,7 +3660,7 @@ return yield; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS static gstring * cat_file_tls(void * tls_ctx, gstring * yield, uschar * eol) { @@ -3841,89 +3957,6 @@ return x; -/* Return pointer to dewrapped string, with enclosing specified chars removed. -The given string is modified on return. Leading whitespace is skipped while -looking for the opening wrap character, then the rest is scanned for the trailing -(non-escaped) wrap character. A backslash in the string will act as an escape. - -A nul is written over the trailing wrap, and a pointer to the char after the -leading wrap is returned. - -Arguments: - s String for de-wrapping - wrap Two-char string, the first being the opener, second the closer wrapping - character -Return: - Pointer to de-wrapped string, or NULL on error (with expand_string_message set). -*/ - -static uschar * -dewrap(uschar * s, const uschar * wrap) -{ -uschar * p = s; -unsigned depth = 0; -BOOL quotesmode = wrap[0] == wrap[1]; - -while (isspace(*p)) p++; - -if (*p == *wrap) - { - s = ++p; - wrap++; - while (*p) - { - if (*p == '\\') p++; - else if (!quotesmode && *p == wrap[-1]) depth++; - else if (*p == *wrap) - if (depth == 0) - { - *p = '\0'; - return s; - } - else - depth--; - p++; - } - } -expand_string_message = string_sprintf("missing '%c'", *wrap); -return NULL; -} - - -/* Pull off the leading array or object element, returning -a copy in an allocated string. Update the list pointer. - -The element may itself be an object or array. -Return NULL when the list is empty. -*/ - -uschar * -json_nextinlist(const uschar ** list) -{ -unsigned array_depth = 0, object_depth = 0; -const uschar * s = *list, * item; - -while (isspace(*s)) s++; - -for (item = s; - *s && (*s != ',' || array_depth != 0 || object_depth != 0); - s++) - switch (*s) - { - case '[': array_depth++; break; - case ']': array_depth--; break; - case '{': object_depth++; break; - case '}': object_depth--; break; - } -*list = *s ? s+1 : s; -if (item == s) return NULL; -item = string_copyn(item, s - item); -DEBUG(D_expand) debug_printf_indent(" json ele: '%s'\n", item); -return US item; -} - - - /************************************************* * Expand string * *************************************************/ @@ -4905,17 +4938,16 @@ while (*s != 0) case EITEM_READSOCK: { - int fd; + 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 */ - void * tls_ctx = NULL; /* ditto */ + BOOL do_tls = FALSE; /* Only set under ! DISABLE_TLS */ blob reqstr; if (expand_forbid & RDO_READSOCK) @@ -4959,7 +4991,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 @@ -5015,12 +5047,12 @@ while (*s != 0) port = ntohs(service_info->s_port); } - /*XXX we trust that the request is idempotent. Hmm. */ - fd = ip_connectedsocket(SOCK_STREAM, server_name, port, port, + /*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); callout_address = NULL; - if (fd < 0) + if (cctx.sock < 0) goto SOCK_FAIL; if (!do_tls) reqstr.len = 0; @@ -5033,7 +5065,7 @@ while (*s != 0) struct sockaddr_un sockun; /* don't call this "sun" ! */ int rc; - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + if ((cctx.sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { expand_string_message = string_sprintf("failed to create socket: %s", strerror(errno)); @@ -5047,7 +5079,7 @@ while (*s != 0) sigalrm_seen = FALSE; ALARM(timeout); - rc = connect(fd, (struct sockaddr *)(&sockun), sizeof(sockun)); + rc = connect(cctx.sock, (struct sockaddr *)(&sockun), sizeof(sockun)); ALARM_CLR(0); if (sigalrm_seen) { @@ -5066,17 +5098,14 @@ 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 }; tls_support tls_dummy = {.sni=NULL}; uschar * errstr; - if (!(tls_ctx = tls_client_start(fd, &host, NULL, NULL, -# ifdef SUPPORT_DANE - NULL, -# endif - &tls_dummy, &errstr))) + if (!tls_client_start(&cctx, &conn_args, NULL, &tls_dummy, &errstr)) { expand_string_message = string_sprintf("TLS connect failed: %s", errstr); goto SOCK_FAIL; @@ -5094,10 +5123,10 @@ while (*s != 0) DEBUG(D_expand) debug_printf_indent("writing \"%s\" to socket\n", reqstr.data); if ( ( -#ifdef SUPPORT_TLS - tls_ctx ? tls_write(tls_ctx, reqstr.data, reqstr.len, FALSE) : +#ifndef DISABLE_TLS + do_tls ? tls_write(cctx.tls_ctx, reqstr.data, reqstr.len, FALSE) : #endif - write(fd, reqstr.data, reqstr.len)) != reqstr.len) + write(cctx.sock, reqstr.data, reqstr.len)) != reqstr.len) { expand_string_message = string_sprintf("request write to socket " "failed: %s", strerror(errno)); @@ -5110,7 +5139,7 @@ while (*s != 0) system doesn't have this function, make it conditional. */ #ifdef SHUT_WR - if (!tls_ctx && do_shutdown) shutdown(fd, SHUT_WR); + if (!do_tls && do_shutdown) shutdown(cctx.sock, SHUT_WR); #endif if (f.running_in_test_harness) millisleep(100); @@ -5118,22 +5147,22 @@ while (*s != 0) /* Now we need to read from the socket, under a timeout. The function that reads a file can be used. */ - if (!tls_ctx) - fp = fdopen(fd, "rb"); + if (!do_tls) + fp = fdopen(cctx.sock, "rb"); sigalrm_seen = FALSE; ALARM(timeout); yield = -#ifdef SUPPORT_TLS - tls_ctx ? cat_file_tls(tls_ctx, yield, sub_arg[3]) : +#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 - if (tls_ctx) +#ifndef DISABLE_TLS + if (do_tls) { - tls_close(tls_ctx, TRUE); - close(fd); + tls_close(cctx.tls_ctx, TRUE); + close(cctx.sock); } else #endif @@ -5963,7 +5992,7 @@ while (*s != 0) continue; } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS case EITEM_CERTEXTRACT: { uschar *save_lookup_value = lookup_value; @@ -6043,7 +6072,7 @@ while (*s != 0) save_expand_nlength); continue; } -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ /* Handle list operations */ @@ -6555,7 +6584,7 @@ while (*s != 0) int c; uschar *arg = NULL; uschar *sub; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS var_entry *vp = NULL; #endif @@ -6578,7 +6607,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: @@ -6733,7 +6762,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); @@ -6752,7 +6781,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); @@ -6770,23 +6799,35 @@ 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) + { + uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value); + yield = string_cat(yield, cp); + } + 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) @@ -7074,16 +7115,11 @@ while (*s != 0) uschar * t = parse_extract_address(sub, &error, &start, &end, &domain, FALSE); if (t) - if (c != EOP_DOMAIN) - { - if (c == EOP_LOCAL_PART && domain != 0) end = start + domain - 1; - yield = string_catn(yield, sub+start, end-start); - } - else if (domain != 0) - { - domain += start; - yield = string_catn(yield, sub+domain, end-domain); - } + yield = c == EOP_DOMAIN + ? string_cat(yield, t + domain) + : c == EOP_LOCAL_PART && domain > 0 + ? string_catn(yield, t, domain - 1 ) + : string_cat(yield, t); continue; } @@ -7107,7 +7143,7 @@ while (*s != 0) for (;;) { - uschar *p = parse_find_address_end(sub, FALSE); + uschar * p = parse_find_address_end(sub, FALSE); uschar saveend = *p; *p = '\0'; address = parse_extract_address(sub, &error, &start, &end, &domain, @@ -7120,7 +7156,7 @@ while (*s != 0) list, add in a space if the new address begins with the separator character, or is an empty string. */ - if (address != NULL) + if (address) { if (yield->ptr != save_ptr && address[0] == *outsep) yield = string_catn(yield, US" ", 1); @@ -7528,7 +7564,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));