X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/e47376befc6e4a415319e3c29fad9b0ba2bbd8ee..4dc2379ac1ab6c21f265abed06dd9aaa214976af:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index 5189cdcfe..a7edaae47 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -199,12 +199,15 @@ enum { static uschar *op_table_main[] = { US"address", US"addresses", + US"base32", + US"base32d", US"base62", US"base62d", US"base64", US"base64d", US"domain", US"escape", + US"escape8bit", US"eval", US"eval10", US"expand", @@ -231,6 +234,7 @@ static uschar *op_table_main[] = { US"s", US"sha1", US"sha256", + US"sha3", US"stat", US"str2b64", US"strlen", @@ -241,12 +245,15 @@ static uschar *op_table_main[] = { enum { EOP_ADDRESS = nelem(op_table_underscore), EOP_ADDRESSES, + EOP_BASE32, + EOP_BASE32D, EOP_BASE62, EOP_BASE62D, EOP_BASE64, EOP_BASE64D, EOP_DOMAIN, EOP_ESCAPE, + EOP_ESCAPE8BIT, EOP_EVAL, EOP_EVAL10, EOP_EXPAND, @@ -273,6 +280,7 @@ enum { EOP_S, EOP_SHA1, EOP_SHA256, + EOP_SHA3, EOP_STAT, EOP_STR2B64, EOP_STRLEN, @@ -625,6 +633,7 @@ static var_entry var_table[] = { { "prvscheck_result", vtype_stringptr, &prvscheck_result }, { "qualify_domain", vtype_stringptr, &qualify_domain_sender }, { "qualify_recipient", vtype_stringptr, &qualify_domain_recipient }, + { "queue_name", vtype_stringptr, &queue_name }, { "rcpt_count", vtype_int, &rcpt_count }, { "rcpt_defer_count", vtype_int, &rcpt_defer_count }, { "rcpt_fail_count", vtype_int, &rcpt_fail_count }, @@ -835,6 +844,9 @@ static int utf8_table2[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; } + +static uschar * base32_chars = US"abcdefghijklmnopqrstuvwxyz234567"; + /************************************************* * Binary chop search on a table * *************************************************/ @@ -2506,7 +2518,6 @@ switch(cond_type) checking for them individually. */ if (!isalpha(name[0]) && yield != NULL) - { if (sub[i][0] == 0) { num[i] = 0; @@ -2518,7 +2529,6 @@ switch(cond_type) num[i] = expanded_string_integer(sub[i], FALSE); if (expand_string_message != NULL) return NULL; } - } } /* Result not required */ @@ -2686,7 +2696,7 @@ switch(cond_type) uschar digest[16]; md5_start(&base); - md5_end(&base, (uschar *)sub[0], Ustrlen(sub[0]), digest); + md5_end(&base, sub[0], Ustrlen(sub[0]), digest); /* If the length that we are comparing against is 24, the MD5 digest is expressed as a base64 string. This is the way LDAP does it. However, @@ -2695,7 +2705,7 @@ switch(cond_type) if (sublen == 24) { - uschar *coded = b64encode((uschar *)digest, 16); + uschar *coded = b64encode(digest, 16); DEBUG(D_auth) debug_printf("crypteq: using MD5+B64 hashing\n" " subject=%s\n crypted=%s\n", coded, sub[1]+5); tempcond = (Ustrcmp(coded, sub[1]+5) == 0); @@ -2721,11 +2731,11 @@ switch(cond_type) else if (strncmpic(sub[1], US"{sha1}", 6) == 0) { int sublen = Ustrlen(sub[1]+6); - sha1 base; + hctx h; uschar digest[20]; - sha1_start(&base); - sha1_end(&base, (uschar *)sub[0], Ustrlen(sub[0]), digest); + sha1_start(&h); + sha1_end(&h, sub[0], Ustrlen(sub[0]), digest); /* If the length that we are comparing against is 28, assume the SHA1 digest is expressed as a base64 string. If the length is 40, assume a @@ -2733,7 +2743,7 @@ switch(cond_type) if (sublen == 28) { - uschar *coded = b64encode((uschar *)digest, 20); + uschar *coded = b64encode(digest, 20); DEBUG(D_auth) debug_printf("crypteq: using SHA1+B64 hashing\n" " subject=%s\n crypted=%s\n", coded, sub[1]+6); tempcond = (Ustrcmp(coded, sub[1]+6) == 0); @@ -3040,6 +3050,8 @@ switch(cond_type) "value \"%s\"", t); return NULL; } + DEBUG(D_expand) debug_printf("%s: condition evaluated to %s\n", ourname, + boolvalue? "true":"false"); if (yield != NULL) *yield = (boolvalue == testfor); return s; } @@ -3181,16 +3193,17 @@ items. */ while (isspace(*s)) s++; if (*s == '}') { - if (type[0] == 'i') - { - if (yes) *yieldptr = string_catn(*yieldptr, sizeptr, ptrptr, US"true", 4); - } - else - { - if (yes && lookup_value) - *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, lookup_value); - lookup_value = save_lookup; - } + if (!skipping) + if (type[0] == 'i') + { + if (yes) *yieldptr = string_catn(*yieldptr, sizeptr, ptrptr, US"true", 4); + } + else + { + if (yes && lookup_value) + *yieldptr = string_cat(*yieldptr, sizeptr, ptrptr, lookup_value); + lookup_value = save_lookup; + } s++; goto RETURN; } @@ -3338,7 +3351,7 @@ chash_start(int type, void *base) if (type == HMAC_MD5) md5_start((md5 *)base); else - sha1_start((sha1 *)base); + sha1_start((hctx *)base); } static void @@ -3347,7 +3360,7 @@ chash_mid(int type, void *base, uschar *string) if (type == HMAC_MD5) md5_mid((md5 *)base, string); else - sha1_mid((sha1 *)base, string); + sha1_mid((hctx *)base, string); } static void @@ -3356,7 +3369,7 @@ chash_end(int type, void *base, uschar *string, int length, uschar *digest) if (type == HMAC_MD5) md5_end((md5 *)base, string, length, digest); else - sha1_end((sha1 *)base, string, length, digest); + sha1_end((hctx *)base, string, length, digest); } @@ -3415,8 +3428,7 @@ prvs_hmac_sha1(uschar *address, uschar *key, uschar *key_num, uschar *daystamp) { uschar *hash_source, *p; int size = 0,offset = 0,i; -sha1 sha1_base; -void *use_base = &sha1_base; +hctx h; uschar innerhash[20]; uschar finalhash[20]; uschar innerkey[64]; @@ -3445,13 +3457,13 @@ for (i = 0; i < Ustrlen(key); i++) outerkey[i] ^= key[i]; } -chash_start(HMAC_SHA1, use_base); -chash_mid(HMAC_SHA1, use_base, innerkey); -chash_end(HMAC_SHA1, use_base, hash_source, offset, innerhash); +chash_start(HMAC_SHA1, &h); +chash_mid(HMAC_SHA1, &h, innerkey); +chash_end(HMAC_SHA1, &h, hash_source, offset, innerhash); -chash_start(HMAC_SHA1, use_base); -chash_mid(HMAC_SHA1, use_base, outerkey); -chash_end(HMAC_SHA1, use_base, innerhash, 20, finalhash); +chash_start(HMAC_SHA1, &h); +chash_mid(HMAC_SHA1, &h, outerkey); +chash_end(HMAC_SHA1, &h, innerhash, 20, finalhash); p = finalhash_hex; for (i = 0; i < 3; i++) @@ -3488,7 +3500,6 @@ Returns: new value of string pointer static uschar * cat_file(FILE *f, uschar *yield, int *sizep, int *ptrp, uschar *eol) { -int eollen = eol ? Ustrlen(eol) : 0; uschar buffer[1024]; while (Ufgets(buffer, sizeof(buffer), f)) @@ -3496,8 +3507,8 @@ while (Ufgets(buffer, sizeof(buffer), f)) int len = Ustrlen(buffer); if (eol && buffer[len-1] == '\n') len--; yield = string_catn(yield, sizep, ptrp, buffer, len); - if (buffer[len] != 0) - yield = string_catn(yield, sizep, ptrp, eol, eollen); + if (eol && buffer[len]) + yield = string_cat(yield, sizep, ptrp, eol); } if (yield) yield[*ptrp] = 0; @@ -3851,8 +3862,8 @@ expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, { int ptr = 0; int size = Ustrlen(string)+ 64; -int item_type; uschar *yield = store_get(size); +int item_type; const uschar *s = string; uschar *save_expand_nstring[EXPAND_MAXN+1]; int save_expand_nlength[EXPAND_MAXN+1]; @@ -4105,8 +4116,9 @@ while (*s != 0) if (next_s == NULL) goto EXPAND_FAILED; /* message already set */ DEBUG(D_expand) - debug_printf("condition: %.*s\n result: %s\n", (int)(next_s - s), s, - cond? "true" : "false"); + debug_printf(" condition: %.*s\n result: %s\n", + (int)(next_s - s), s, + cond ? "true" : "false"); s = next_s; @@ -4952,7 +4964,10 @@ while (*s != 0) } if (skipping) /* Just pretend it worked when we're skipping */ + { runrc = 0; + lookup_value = NULL; + } else { if (!transport_set_up_command(&argv, /* anchor for arg list */ @@ -4994,9 +5009,9 @@ while (*s != 0) return code for serious disasters. Simple non-zero returns are passed on. */ - if (sigalrm_seen == TRUE || (runrc = child_close(pid, 30)) < 0) + if (sigalrm_seen || (runrc = child_close(pid, 30)) < 0) { - if (sigalrm_seen == TRUE || runrc == -256) + if (sigalrm_seen || runrc == -256) { expand_string_message = string_sprintf("command timed out"); killpg(pid, SIGKILL); /* Kill the whole process group */ @@ -5144,7 +5159,7 @@ while (*s != 0) { uschar *sub[3]; md5 md5_base; - sha1 sha1_base; + hctx sha1_ctx; void *use_base; int type, i; int hashlen; /* Number of octets for the hash algorithm's output */ @@ -5166,79 +5181,81 @@ while (*s != 0) case 3: goto EXPAND_FAILED; } - if (Ustrcmp(sub[0], "md5") == 0) - { - type = HMAC_MD5; - use_base = &md5_base; - hashlen = 16; - hashblocklen = 64; - } - else if (Ustrcmp(sub[0], "sha1") == 0) - { - type = HMAC_SHA1; - use_base = &sha1_base; - hashlen = 20; - hashblocklen = 64; - } - else - { - expand_string_message = - string_sprintf("hmac algorithm \"%s\" is not recognised", sub[0]); - goto EXPAND_FAILED; - } + if (!skipping) + { + if (Ustrcmp(sub[0], "md5") == 0) + { + type = HMAC_MD5; + use_base = &md5_base; + hashlen = 16; + hashblocklen = 64; + } + else if (Ustrcmp(sub[0], "sha1") == 0) + { + type = HMAC_SHA1; + use_base = &sha1_ctx; + hashlen = 20; + hashblocklen = 64; + } + else + { + expand_string_message = + string_sprintf("hmac algorithm \"%s\" is not recognised", sub[0]); + goto EXPAND_FAILED; + } - keyptr = sub[1]; - keylen = Ustrlen(keyptr); + keyptr = sub[1]; + keylen = Ustrlen(keyptr); - /* If the key is longer than the hash block length, then hash the key - first */ + /* If the key is longer than the hash block length, then hash the key + first */ - if (keylen > hashblocklen) - { - chash_start(type, use_base); - chash_end(type, use_base, keyptr, keylen, keyhash); - keyptr = keyhash; - keylen = hashlen; - } + if (keylen > hashblocklen) + { + chash_start(type, use_base); + chash_end(type, use_base, keyptr, keylen, keyhash); + keyptr = keyhash; + keylen = hashlen; + } - /* Now make the inner and outer key values */ + /* Now make the inner and outer key values */ - memset(innerkey, 0x36, hashblocklen); - memset(outerkey, 0x5c, hashblocklen); + memset(innerkey, 0x36, hashblocklen); + memset(outerkey, 0x5c, hashblocklen); - for (i = 0; i < keylen; i++) - { - innerkey[i] ^= keyptr[i]; - outerkey[i] ^= keyptr[i]; - } + for (i = 0; i < keylen; i++) + { + innerkey[i] ^= keyptr[i]; + outerkey[i] ^= keyptr[i]; + } - /* Now do the hashes */ + /* Now do the hashes */ - chash_start(type, use_base); - chash_mid(type, use_base, innerkey); - chash_end(type, use_base, sub[2], Ustrlen(sub[2]), innerhash); + chash_start(type, use_base); + chash_mid(type, use_base, innerkey); + chash_end(type, use_base, sub[2], Ustrlen(sub[2]), innerhash); - chash_start(type, use_base); - chash_mid(type, use_base, outerkey); - chash_end(type, use_base, innerhash, hashlen, finalhash); + chash_start(type, use_base); + chash_mid(type, use_base, outerkey); + chash_end(type, use_base, innerhash, hashlen, finalhash); - /* Encode the final hash as a hex string */ + /* Encode the final hash as a hex string */ - p = finalhash_hex; - for (i = 0; i < hashlen; i++) - { - *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; - *p++ = hex_digits[finalhash[i] & 0x0f]; - } + p = finalhash_hex; + for (i = 0; i < hashlen; i++) + { + *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; + *p++ = hex_digits[finalhash[i] & 0x0f]; + } - DEBUG(D_any) debug_printf("HMAC[%s](%.*s,%.*s)=%.*s\n", sub[0], - (int)keylen, keyptr, Ustrlen(sub[2]), sub[2], hashlen*2, finalhash_hex); + DEBUG(D_any) debug_printf("HMAC[%s](%.*s,%s)=%.*s\n", + sub[0], (int)keylen, keyptr, sub[2], hashlen*2, finalhash_hex); - yield = string_catn(yield, &size, &ptr, finalhash_hex, hashlen*2); + yield = string_catn(yield, &size, &ptr, finalhash_hex, hashlen*2); + } + continue; } - continue; - /* Handle global substitution for "sg" - like Perl's s/xxx/yyy/g operator. We have to save the numerical variables and restore them afterwards. */ @@ -5359,7 +5376,7 @@ while (*s != 0) case EITEM_EXTRACT: { int i; - int j = 2; + int j; int field_number = 1; BOOL field_number_set = FALSE; uschar *save_lookup_value = lookup_value; @@ -5369,12 +5386,12 @@ while (*s != 0) /* While skipping we cannot rely on the data for expansions being available (eg. $item) hence cannot decide on numeric vs. keyed. - Just read as many arguments as there are. */ + Read a maximum of 5 arguments (inclding the yes/no) */ if (skipping) { while (isspace(*s)) s++; - while (*s == '{') + for (j = 5; j > 0 && *s == '{'; j--) { if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)) goto EXPAND_FAILED; /*{*/ @@ -5385,6 +5402,13 @@ while (*s != 0) } while (isspace(*s)) s++; } + if ( Ustrncmp(s, "fail", 4) == 0 + && (s[4] == '}' || s[4] == ' ' || s[4] == '\t' || !s[4]) + ) + { + s += 4; + while (isspace(*s)) s++; + } if (*s != '}') { expand_string_message = US"missing '}' closing extract"; @@ -5392,7 +5416,7 @@ while (*s != 0) } } - else for (i = 0; i < j; i++) /* Read the proper number of arguments */ + else for (i = 0, j = 2; i < j; i++) /* Read the proper number of arguments */ { while (isspace(*s)) s++; if (*s == '{') /*}*/ @@ -5402,7 +5426,7 @@ while (*s != 0) if (*s++ != '}') { expand_string_message = string_sprintf( - US"missing '}' closing arg %d of extract", i+1); + "missing '}' closing arg %d of extract", i+1); goto EXPAND_FAILED_CURLY; } @@ -5448,7 +5472,7 @@ while (*s != 0) else { expand_string_message = string_sprintf( - US"missing '{' for arg %d of extract", i+1); + "missing '{' for arg %d of extract", i+1); goto EXPAND_FAILED_CURLY; } } @@ -5505,7 +5529,7 @@ while (*s != 0) if (*s != '{') /*}*/ { expand_string_message = string_sprintf( - US"missing '{' for arg %d of listextract", i+1); + "missing '{' for arg %d of listextract", i+1); goto EXPAND_FAILED_CURLY; } @@ -5514,7 +5538,7 @@ while (*s != 0) if (*s++ != '}') { expand_string_message = string_sprintf( - US"missing '}' closing arg %d of listextract", i+1); + "missing '}' closing arg %d of listextract", i+1); goto EXPAND_FAILED_CURLY; } @@ -6249,6 +6273,47 @@ while (*s != 0) switch(c) { + case EOP_BASE32: + { + uschar *t; + unsigned long int n = Ustrtoul(sub, &t, 10); + uschar * s = NULL; + int sz = 0, i = 0; + + if (*t != 0) + { + expand_string_message = string_sprintf("argument for base32 " + "operator is \"%s\", which is not a decimal number", sub); + goto EXPAND_FAILED; + } + for ( ; n; n >>= 5) + s = string_catn(s, &sz, &i, &base32_chars[n & 0x1f], 1); + + while (i > 0) yield = string_catn(yield, &size, &ptr, &s[--i], 1); + continue; + } + + case EOP_BASE32D: + { + uschar *tt = sub; + unsigned long int n = 0; + uschar * s; + while (*tt) + { + uschar * t = Ustrchr(base32_chars, *tt++); + if (t == NULL) + { + expand_string_message = string_sprintf("argument for base32d " + "operator is \"%s\", which is not a base 32 number", sub); + goto EXPAND_FAILED; + } + n = n * 32 + (t - base32_chars); + } + s = string_sprintf("%ld", n); + yield = string_cat(yield, &size, &ptr, s); + continue; + } + case EOP_BASE62: { uschar *t; @@ -6351,29 +6416,78 @@ while (*s != 0) else #endif { - sha1 base; + hctx h; uschar digest[20]; int j; char st[41]; - sha1_start(&base); - sha1_end(&base, sub, Ustrlen(sub), digest); + sha1_start(&h); + sha1_end(&h, sub, Ustrlen(sub), digest); for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]); - yield = string_cat(yield, &size, &ptr, US st); + yield = string_catn(yield, &size, &ptr, US st, 40); } continue; case EOP_SHA256: -#ifdef SUPPORT_TLS +#ifdef EXIM_HAVE_SHA2 if (vp && *(void **)vp->value) { uschar * cp = tls_cert_fprt_sha256(*(void **)vp->value); yield = string_cat(yield, &size, &ptr, cp); } else + { + hctx h; + blob b; + char st[3]; + + exim_sha_init(&h, HASH_SHA256); + exim_sha_update(&h, sub, Ustrlen(sub)); + exim_sha_finish(&h, &b); + while (b.len-- > 0) + { + sprintf(st, "%02X", *b.data++); + yield = string_catn(yield, &size, &ptr, US st, 2); + } + } +#else + expand_string_message = US"sha256 only supported with TLS"; #endif - expand_string_message = US"sha256 only supported for certificates"; continue; + case EOP_SHA3: +#ifdef EXIM_HAVE_SHA3 + { + hctx h; + blob b; + char st[3]; + hashmethod m = !arg ? HASH_SHA3_256 + : Ustrcmp(arg, "224") == 0 ? HASH_SHA3_224 + : Ustrcmp(arg, "256") == 0 ? HASH_SHA3_256 + : Ustrcmp(arg, "384") == 0 ? HASH_SHA3_384 + : Ustrcmp(arg, "512") == 0 ? HASH_SHA3_512 + : HASH_BADTYPE; + + if (m == HASH_BADTYPE) + { + expand_string_message = US"unrecognised sha3 variant"; + goto EXPAND_FAILED; + } + + exim_sha_init(&h, m); + exim_sha_update(&h, sub, Ustrlen(sub)); + exim_sha_finish(&h, &b); + while (b.len-- > 0) + { + sprintf(st, "%02X", *b.data++); + yield = string_catn(yield, &size, &ptr, US st, 2); + } + } + continue; +#else + expand_string_message = US"sha3 only supported with GnuTLS 3.5.0 +"; + goto EXPAND_FAILED; +#endif + /* Convert hex encoding to base64 encoding */ case EOP_HEX2B64: @@ -6999,11 +7113,23 @@ while (*s != 0) case EOP_ESCAPE: { - const uschar *t = string_printing(sub); + const uschar * t = string_printing(sub); yield = string_cat(yield, &size, &ptr, t); continue; } + case EOP_ESCAPE8BIT: + { + const uschar * s = sub; + uschar c; + + for (s = sub; (c = *s); s++) + yield = c < 127 && c != '\\' + ? string_catn(yield, &size, &ptr, s, 1) + : string_catn(yield, &size, &ptr, string_sprintf("\\%03o", c), 4); + continue; + } + /* Handle numeric expression evaluation */ case EOP_EVAL: @@ -7301,8 +7427,7 @@ while (*s != 0) yield = NULL; size = 0; } - value = find_variable(name, FALSE, skipping, &newsize); - if (value == NULL) + if (!(value = find_variable(name, FALSE, skipping, &newsize))) { expand_string_message = string_sprintf("unknown variable in \"${%s}\"", name); @@ -7310,13 +7435,14 @@ while (*s != 0) goto EXPAND_FAILED; } len = Ustrlen(value); - if (yield == NULL && newsize != 0) + if (!yield && newsize) { yield = value; size = newsize; ptr = len; } - else yield = string_catn(yield, &size, &ptr, value, len); + else + yield = string_catn(yield, &size, &ptr, value, len); continue; } @@ -7624,6 +7750,29 @@ return OK; +/* Avoid potentially exposing a password in a string about to be logged */ + +uschar * +expand_hide_passwords(uschar * s) +{ +return ( ( Ustrstr(s, "failed to expand") != NULL + || Ustrstr(s, "expansion of ") != NULL + ) + && ( Ustrstr(s, "mysql") != NULL + || Ustrstr(s, "pgsql") != NULL + || Ustrstr(s, "redis") != NULL + || Ustrstr(s, "sqlite") != NULL + || Ustrstr(s, "ldap:") != NULL + || Ustrstr(s, "ldaps:") != NULL + || Ustrstr(s, "ldapi:") != NULL + || Ustrstr(s, "ldapdn:") != NULL + || Ustrstr(s, "ldapm:") != NULL + ) ) + ? US"Temporary internal error" : s; +} + + + /************************************************* **************************************************