From a7cbbf501402231457e8167b6d446f4df454ba17 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Tue, 14 Dec 2010 03:42:36 -0500 Subject: [PATCH] Implement -D filtering, first pass. --- src/src/EDITME | 25 +++++++++ src/src/buildconfig.c | 6 ++- src/src/config.h.defaults | 2 + src/src/exim.c | 108 +++++++++++++++++++++++++++++++++++++- src/src/globals.c | 3 ++ src/src/globals.h | 3 ++ 6 files changed, 144 insertions(+), 3 deletions(-) diff --git a/src/src/EDITME b/src/src/EDITME index 1ae139944..d093eb690 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -493,6 +493,31 @@ FIXED_NEVER_USERS=root # DISABLE_D_OPTION=yes +#------------------------------------------------------------------------------ +# By contrast, you might be maintaining a system which relies upon the ability +# to override values with -D and assumes that these will be passed through to +# the delivery processes. As of Exim 4.73, this is no longer the case by +# default. Going forward, we strongly recommend that you use a shim Exim +# configuration file owned by root stored under TRUSTED_CONFIG_PREFIX_LIST. +# That shim can set macros before .include'ing your main configuration file. +# +# As a strictly transient measure to ease migration to 4.73, the +# WHITELIST_D_MACROS value definies a colon-separated list of macro-names +# which are permitted to be overriden from the command-line which will be +# honoured by the Exim user. So these are macros that can persist to delivery +# time. +# Examples might be -DTLS or -DSPOOL=/some/dir. The values on the +# command-line are filtered to only permit: [A-Za-z0-9_/.-]* +# +# This option is highly likely to be removed in a future release. It exists +# only to make 4.73 as easy as possible to migrate to. If you use it, we +# encourage you to schedule time to rework your configuration to not depend +# upon it. Most people should not need to use this. +# +# By default, no macros are whitelisted for -D usage. + +# WHITELIST_D_MACROS=TLS:SPOOL + #------------------------------------------------------------------------------ # Exim has support for the AUTH (authentication) extension of the SMTP # protocol, as defined by RFC 2554. If you don't know what SMTP authentication diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index 7e2790bb8..49d8c411a 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -800,11 +800,13 @@ while (fgets(buffer, sizeof(buffer), base) != NULL) fprintf(new, "\"%s\"\n", value); } - /* Timezone values HEADERS_CHARSET, and TCP_WRAPPERS_DAEMON_NAME get quoted */ + /* Timezone values HEADERS_CHARSET, TCP_WRAPPERS_DAEMON_NAME and + WHITELIST_D_MACROS get quoted */ else if (strcmp(name, "TIMEZONE_DEFAULT") == 0|| strcmp(name, "TCP_WRAPPERS_DAEMON_NAME") == 0|| - strcmp(name, "HEADERS_CHARSET") == 0) + strcmp(name, "HEADERS_CHARSET") == 0|| + strcmp(name, "WHITELIST_D_MACROS") == 0) fprintf(new, "\"%s\"\n", value); /* For others, quote any paths and don't quote anything else */ diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index d3d7d65bb..b4e2c6d9c 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -145,6 +145,8 @@ it's a default value. */ #define USE_TCP_WRAPPERS #define USE_TDB +#define WHITELIST_D_MACROS + #define WITH_CONTENT_SCAN #define WITH_OLD_DEMIME #define WITH_OLD_CLAMAV_STREAM diff --git a/src/src/exim.c b/src/src/exim.c index 729114c1c..f50a62b94 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1131,6 +1131,102 @@ exit(EXIT_FAILURE); +/************************************************* +* Validate that the macros given are okay * +*************************************************/ + +/* Typically, Exim will drop privileges if macros are supplied. In some +cases, we want to not do so. + +Arguments: none (macros is a global) +Returns: true if trusted, false otherwise +*/ + +static BOOL +macros_trusted(void) +{ +#ifdef WHITELIST_D_MACROS +macro_item *m; +uschar *whitelisted, *end, *p, **whites, **w; +int white_count, i, n; +size_t len; +BOOL prev_char_item, found; +#endif + +if (macros == NULL) + return TRUE; +#ifndef WHITELIST_D_MACROS +return FALSE; +#else + +/* Get a list of macros which are whitelisted */ +whitelisted = string_copy_malloc(US WHITELIST_D_MACROS); +prev_char_item = FALSE; +white_count = 0; +for (p = whitelisted; *p != '\0'; ++p) + { + if (*p == ':' || isspace(*p)) + { + *p = '\0'; + if (prev_char_item) + ++white_count; + prev_char_item = FALSE; + continue; + } + if (!prev_char_item) + prev_char_item = TRUE; + } +end = p; +if (prev_char_item) + ++white_count; +if (!white_count) + return FALSE; +whites = store_malloc(sizeof(uschar *) * (white_count+1)); +for (p = whitelisted, i = 0; (p != end) && (i < white_count); ++p) + { + if (*p != '\0') + { + whites[i++] = p; + if (i == white_count) + break; + while (*p != '\0' && p < end) + ++p; + } + } +whites[i] = NULL; + +/* The list of macros should be very short. Accept the N*M complexity. */ +for (m = macros; m != NULL; m = m->next) + { + found = FALSE; + for (w = whites; *w; ++w) + if (Ustrcmp(*w, m->name) == 0) + { + found = TRUE; + break; + } + if (!found) + return FALSE; + if (m->replacement == NULL) + continue; + len = Ustrlen(m->replacement); + if (len == 0) + continue; + n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len, + 0, PCRE_EOPT, NULL, 0); + if (n < 0) + { + if (n != PCRE_ERROR_NOMATCH) + debug_printf("macros_trusted checking %s returned %d\n", m->name, n); + return FALSE; + } + } +debug_printf("macros_trusted overriden to true by whitelisting\n"); +return TRUE; +#endif +} + + /************************************************* * Entry point and high-level code * *************************************************/ @@ -1417,6 +1513,15 @@ regex_smtp_code = regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?", FALSE, TRUE); +#ifdef WHITELIST_D_MACROS +/* Precompile the regular expression used to filter the content of macros +given to -D for permissibility. */ + +regex_whitelisted_macro = + regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); +#endif + + /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic links called "mailq" in standard OS configurations. */ @@ -3145,7 +3250,8 @@ values (such as the path name). If running in the test harness, pretend that configuration file changes and macro definitions haven't happened. */ if (( /* EITHER */ - (!trusted_config || macros != NULL) && /* Config changed, and */ + (!trusted_config || /* Config changed, or */ + !macros_trusted()) && /* impermissible macros and */ real_uid != root_uid && /* Not root, and */ !running_in_test_harness /* Not fudged */ ) || /* OR */ diff --git a/src/src/globals.c b/src/src/globals.c index 500691cb0..71de52aac 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -930,6 +930,9 @@ const pcre *regex_PIPELINING = NULL; const pcre *regex_SIZE = NULL; const pcre *regex_smtp_code = NULL; const pcre *regex_ismsgid = NULL; +#ifdef WHITELIST_D_MACROS +const pcre *regex_whitelisted_macro = NULL; +#endif #ifdef WITH_CONTENT_SCAN uschar *regex_match_string = NULL; #endif diff --git a/src/src/globals.h b/src/src/globals.h index 62dd6b04a..b4e3f2a6c 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -594,6 +594,9 @@ extern const pcre *regex_PIPELINING; /* For recognizing PIPELINING */ extern const pcre *regex_SIZE; /* For recognizing SIZE settings */ extern const pcre *regex_smtp_code; /* For recognizing SMTP codes */ extern const pcre *regex_ismsgid; /* Compiled r.e. for message it */ +#ifdef WHITELIST_D_MACROS +extern const pcre *regex_whitelisted_macro; /* For -D macro values */ +#endif #ifdef WITH_CONTENT_SCAN extern uschar *regex_match_string; /* regex that matched a line (regex ACL condition) */ #endif -- 2.25.1