From: Jeremy Harris Date: Sun, 26 Jun 2022 14:27:32 +0000 (+0100) Subject: Variable setting in -be X-Git-Tag: exim-4.97-RC0~280 X-Git-Url: https://git.exim.org/exim.git/commitdiff_plain/c6887a05b9c56d373086e9a79e20c26bebd300b2 Variable setting in -be --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 3b9082031..deee95a9b 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -2913,6 +2913,12 @@ defined and macros will be expanded. Because macros in the config file are often used for secrets, those are only available to admin users. +.new +The word &"set"& at the start of a line, followed by a single space, +is recognised specially as defining a value for a variable. +The syntax is otherwise the same as the ACL modifier &"set ="&. +.wen + .vitem &%-bem%&&~<&'filename'&> .oindex "&%-bem%&" .cindex "testing" "string expansion" diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 2986b2cdd..807d9e434 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -6,6 +6,11 @@ Before a formal release, there may be quite a lot of detail so that people can test from the snapshots or the Git before the documentation is updated. Once the documentation is updated, this file is reduced to a short list. +Version 4.97 +------------ + + 1. The expansion-test faciility (exim -be) can set variables. + Version 4.96 ------------ diff --git a/src/src/acl.c b/src/src/acl.c index 0078aca7d..3af3a4eee 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -734,6 +734,78 @@ return -1; } +static BOOL +acl_varname_to_cond(const uschar ** sp, acl_condition_block * cond, uschar ** error) +{ +const uschar * s = *sp, * endptr; + +#ifndef DISABLE_DKIM +if ( Ustrncmp(s, "dkim_verify_status", 18) == 0 + || Ustrncmp(s, "dkim_verify_reason", 18) == 0) + { + endptr = s+18; + if (isalnum(*endptr)) + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" " + "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)", + s); + return FALSE; + } + cond->u.varname = string_copyn(s, 18); + } +else +#endif + { + if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0) + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s); + return FALSE; + } + + endptr = s + 5; + if (!isdigit(*endptr) && *endptr != '_') + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)", + s); + return FALSE; + } + + for ( ; *endptr && *endptr != '=' && !isspace(*endptr); endptr++) + if (!isalnum(*endptr) && *endptr != '_') + { + *error = string_sprintf("invalid character \"%c\" in variable name " + "in ACL modifier \"set %s\"", *endptr, s); + return FALSE; + } + + cond->u.varname = string_copyn(s + 4, endptr - s - 4); + } +s = endptr; +Uskip_whitespace(&s); +*sp = s; +return TRUE; +} + + +static BOOL +acl_data_to_cond(const uschar * s, acl_condition_block * cond, + const uschar * name, uschar ** error) +{ +if (*s++ != '=') + { + *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name, + conditions[cond->type].is_modifier ? US"modifier" : US"condition"); + return FALSE;; + } +Uskip_whitespace(&s); +cond->arg = string_copy(s); +return TRUE; +} + + /************************************************* * Read and parse one ACL * *************************************************/ @@ -760,7 +832,7 @@ acl_block **lastp = &yield; acl_block *this = NULL; acl_condition_block *cond; acl_condition_block **condp = NULL; -uschar * s; +const uschar * s; *error = NULL; @@ -768,7 +840,7 @@ while ((s = (*func)())) { int v, c; BOOL negated = FALSE; - uschar *saveline = s; + const uschar * saveline = s; uschar name[EXIM_DRIVERNAME_MAX]; /* Conditions (but not verbs) are allowed to be negated by an initial @@ -808,16 +880,15 @@ while ((s = (*func)())) *error = string_sprintf("malformed ACL line \"%s\"", saveline); return NULL; } - this = store_get(sizeof(acl_block), GET_UNTAINTED); - *lastp = this; - lastp = &(this->next); + *lastp = this = store_get(sizeof(acl_block), GET_UNTAINTED); + lastp = &this->next; this->next = NULL; this->condition = NULL; this->verb = v; this->srcline = config_lineno; /* for debug output */ this->srcfile = config_filename; /**/ - condp = &(this->condition); - if (*s == 0) continue; /* No condition on this line */ + condp = &this->condition; + if (!*s) continue; /* No condition on this line */ if (*s == '!') { negated = TRUE; @@ -861,7 +932,7 @@ while ((s = (*func)())) cond->u.negated = negated; *condp = cond; - condp = &(cond->next); + condp = &cond->next; /* The "set" modifier is different in that its argument is "name=value" rather than just a value, and we can check the validity of the name, which @@ -874,75 +945,13 @@ while ((s = (*func)())) compatibility. */ if (c == ACLC_SET) -#ifndef DISABLE_DKIM - if ( Ustrncmp(s, "dkim_verify_status", 18) == 0 - || Ustrncmp(s, "dkim_verify_reason", 18) == 0) - { - uschar * endptr = s+18; - - if (isalnum(*endptr)) - { - *error = string_sprintf("invalid variable name after \"set\" in ACL " - "modifier \"set %s\" " - "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)", - s); - return NULL; - } - cond->u.varname = string_copyn(s, 18); - s = endptr; - Uskip_whitespace(&s); - } - else -#endif - { - uschar *endptr; - - if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0) - { - *error = string_sprintf("invalid variable name after \"set\" in ACL " - "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s); - return NULL; - } - - endptr = s + 5; - if (!isdigit(*endptr) && *endptr != '_') - { - *error = string_sprintf("invalid variable name after \"set\" in ACL " - "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)", - s); - return NULL; - } - - while (*endptr && *endptr != '=' && !isspace(*endptr)) - { - if (!isalnum(*endptr) && *endptr != '_') - { - *error = string_sprintf("invalid character \"%c\" in variable name " - "in ACL modifier \"set %s\"", *endptr, s); - return NULL; - } - endptr++; - } - - cond->u.varname = string_copyn(s + 4, endptr - s - 4); - s = endptr; - Uskip_whitespace(&s); - } + if (!acl_varname_to_cond(&s, cond, error)) return NULL; /* For "set", we are now positioned for the data. For the others, only "endpass" has no data */ if (c != ACLC_ENDPASS) - { - if (*s++ != '=') - { - *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name, - conditions[c].is_modifier ? US"modifier" : US"condition"); - return NULL; - } - Uskip_whitespace(&s); - cond->arg = string_copy(s); - } + if (!acl_data_to_cond(s, cond, name, error)) return NULL; } return yield; @@ -4894,6 +4903,29 @@ if (is_tainted(value)) fprintf(f, "acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value); } + + + +uschar * +acl_standalone_setvar(const uschar * s) +{ +acl_condition_block * cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED); +uschar * errstr = NULL, * log_msg = NULL; +BOOL endpass_seen; +int e; + +cond->next = NULL; +cond->type = ACLC_SET; +if (!acl_varname_to_cond(&s, cond, &errstr)) return errstr; +if (!acl_data_to_cond(s, cond, US"'-be'", &errstr)) return errstr; + +if (acl_check_condition(ACL_WARN, cond, ACL_WHERE_UNKNOWN, + NULL, 0, &endpass_seen, &errstr, &log_msg, &e) != OK) + return string_sprintf("oops: %s", errstr); +return string_sprintf("variable %s set", cond->u.varname); +} + + #endif /* !MACRO_PREDEF */ /* vi: aw ai sw=2 */ diff --git a/src/src/exim.c b/src/src/exim.c index dec8de4b4..23e206d2a 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1670,6 +1670,8 @@ if (isupper(big_buffer[0])) if (macro_read_assignment(big_buffer)) printf("Defined macro '%s'\n", mlast->name); } +else if (Ustrncmp(big_buffer, "set ", 4) == 0) + printf("%s\n", acl_standalone_setvar(big_buffer+4)); else if ((s = expand_string(big_buffer))) printf("%s\n", CS s); else printf("Failed: %s\n", expand_string_message); diff --git a/src/src/functions.h b/src/src/functions.h index 4caae346d..e71823410 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -100,6 +100,7 @@ extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); extern uschar *acl_current_verb(void); extern int acl_eval(int, uschar *, uschar **, uschar **); +extern uschar *acl_standalone_setvar(const uschar *); extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); @@ -425,7 +426,7 @@ extern void readconf_main(BOOL); extern void readconf_options_from_list(optionlist *, unsigned, const uschar *, uschar *); extern BOOL readconf_print(const uschar *, uschar *, BOOL); extern uschar *readconf_printtime(int); -extern uschar *readconf_readname(uschar *, int, uschar *); +extern const uschar *readconf_readname(uschar *, int, const uschar *); extern int readconf_readtime(const uschar *, int, BOOL); extern void readconf_rest(void); extern uschar *readconf_retry_error(const uschar *, const uschar *, int *, int *); diff --git a/src/src/readconf.c b/src/src/readconf.c index 5068dc60e..83ee51b65 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1172,8 +1172,8 @@ Arguments: Returns: new input pointer */ -uschar * -readconf_readname(uschar *name, int len, uschar *s) +const uschar * +readconf_readname(uschar * name, int len, const uschar * s) { int p = 0; BOOL broken = FALSE; @@ -1632,7 +1632,7 @@ rmark reset_point; int intbase = 0; uschar *inttype = US""; uschar *sptr; -uschar *s = buffer; +const uschar * s = buffer; uschar **str_target; uschar name[EXIM_DRIVERNAME_MAX]; uschar name2[EXIM_DRIVERNAME_MAX]; @@ -1752,337 +1752,337 @@ switch (type) case opt_gidlist: case opt_rewrite: - reset_point = store_mark(); - sptr = read_string(s, name); + reset_point = store_mark(); + sptr = read_string(s, name); - /* Having read a string, we now have several different ways of using it, - depending on the data type, so do another switch. If keeping the actual - string is not required (because it is interpreted), freesptr is set TRUE, - and at the end we reset the pool. */ + /* Having read a string, we now have several different ways of using it, + depending on the data type, so do another switch. If keeping the actual + string is not required (because it is interpreted), freesptr is set TRUE, + and at the end we reset the pool. */ - switch (type) - { - /* If this was a string, set the variable to point to the new string, - and set the flag so its store isn't reclaimed. If it was a list of rewrite - rules, we still keep the string (for printing), and parse the rules into a - control block and flags word. */ - - case opt_stringptr: - str_target = data_block ? USS (US data_block + ol->v.offset) - : USS ol->v.value; - if (ol->type & opt_rep_con) - { - uschar * saved_condition; - /* We already have a condition, we're conducting a crude hack to let - multiple condition rules be chained together, despite storing them in - text form. */ - *str_target = string_copy_perm( (saved_condition = *str_target) - ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}", - saved_condition, sptr) - : sptr, - FALSE); - /* TODO(pdp): there is a memory leak here and just below - when we set 3 or more conditions; I still don't - understand the store mechanism enough to know - what's the safe way to free content from an earlier store. - AFAICT, stores stack, so freeing an early stored item also stores - all data alloc'd after it. If we knew conditions were adjacent, - we could survive that, but we don't. So I *think* we need to take - another bit from opt_type to indicate "malloced"; this seems like - quite a hack, especially for this one case. It also means that - we can't ever reclaim the store from the *first* condition. - - Because we only do this once, near process start-up, I'm prepared to - let this slide for the time being, even though it rankles. */ - } - else if (ol->type & opt_rep_str) + switch (type) { - uschar sep_o = - Ustrncmp(name, "headers_add", 11) == 0 ? '\n' - : Ustrncmp(name, "set", 3) == 0 ? ';' - : ':'; - int sep_i = -(int)sep_o; - const uschar * list = sptr; - uschar * s; - gstring * list_o = NULL; - - if (*str_target) - { - list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr)); - list_o = string_cat(list_o, *str_target); - } - - while ((s = string_nextinlist(&list, &sep_i, NULL, 0))) - list_o = string_append_listele(list_o, sep_o, s); + /* If this was a string, set the variable to point to the new string, + and set the flag so its store isn't reclaimed. If it was a list of rewrite + rules, we still keep the string (for printing), and parse the rules into a + control block and flags word. */ + + case opt_stringptr: + str_target = data_block ? USS (US data_block + ol->v.offset) + : USS ol->v.value; + if (ol->type & opt_rep_con) + { + uschar * saved_condition; + /* We already have a condition, we're conducting a crude hack to let + multiple condition rules be chained together, despite storing them in + text form. */ + *str_target = string_copy_perm( (saved_condition = *str_target) + ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}", + saved_condition, sptr) + : sptr, + FALSE); + /* TODO(pdp): there is a memory leak here and just below + when we set 3 or more conditions; I still don't + understand the store mechanism enough to know + what's the safe way to free content from an earlier store. + AFAICT, stores stack, so freeing an early stored item also stores + all data alloc'd after it. If we knew conditions were adjacent, + we could survive that, but we don't. So I *think* we need to take + another bit from opt_type to indicate "malloced"; this seems like + quite a hack, especially for this one case. It also means that + we can't ever reclaim the store from the *first* condition. + + Because we only do this once, near process start-up, I'm prepared to + let this slide for the time being, even though it rankles. */ + } + else if (ol->type & opt_rep_str) + { + uschar sep_o = + Ustrncmp(name, "headers_add", 11) == 0 ? '\n' + : Ustrncmp(name, "set", 3) == 0 ? ';' + : ':'; + int sep_i = -(int)sep_o; + const uschar * list = sptr; + uschar * s; + gstring * list_o = NULL; + + if (*str_target) + { + list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr)); + list_o = string_cat(list_o, *str_target); + } - if (list_o) - *str_target = string_copy_perm(string_from_gstring(list_o), FALSE); - } - else - { - *str_target = sptr; - freesptr = FALSE; - } - break; + while ((s = string_nextinlist(&list, &sep_i, NULL, 0))) + list_o = string_append_listele(list_o, sep_o, s); - case opt_rewrite: - if (data_block) - *USS (US data_block + ol->v.offset) = sptr; - else - *USS ol->v.value = sptr; - freesptr = FALSE; - if (type == opt_rewrite) - { - int sep = 0; - int *flagptr; - uschar *p = sptr; - rewrite_rule **chain; - optionlist *ol3; - - sprintf(CS name2, "*%.50s_rules", name); - ol2 = find_option(name2, oltop, last); - sprintf(CS name2, "*%.50s_flags", name); - ol3 = find_option(name2, oltop, last); - - if (!ol2 || !ol3) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "rewrite rules not available for driver"); + if (list_o) + *str_target = string_copy_perm(string_from_gstring(list_o), FALSE); + } + else + { + *str_target = sptr; + freesptr = FALSE; + } + break; - if (data_block) - { - chain = (rewrite_rule **)(US data_block + ol2->v.offset); - flagptr = (int *)(US data_block + ol3->v.offset); - } - else - { - chain = (rewrite_rule **)ol2->v.value; - flagptr = (int *)ol3->v.value; - } + case opt_rewrite: + if (data_block) + *USS (US data_block + ol->v.offset) = sptr; + else + *USS ol->v.value = sptr; + freesptr = FALSE; + if (type == opt_rewrite) + { + int sep = 0; + int *flagptr; + uschar *p = sptr; + rewrite_rule **chain; + optionlist *ol3; + + sprintf(CS name2, "*%.50s_rules", name); + ol2 = find_option(name2, oltop, last); + sprintf(CS name2, "*%.50s_flags", name); + ol3 = find_option(name2, oltop, last); + + if (!ol2 || !ol3) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "rewrite rules not available for driver"); + + if (data_block) + { + chain = (rewrite_rule **)(US data_block + ol2->v.offset); + flagptr = (int *)(US data_block + ol3->v.offset); + } + else + { + chain = (rewrite_rule **)ol2->v.value; + flagptr = (int *)ol3->v.value; + } - /* This will trap if sptr is tainted. Not sure if that can happen */ - while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE))) - { - rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE); - *chain = next; - chain = &(next->next); - } + /* This will trap if sptr is tainted. Not sure if that can happen */ + while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE))) + { + rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE); + *chain = next; + chain = &(next->next); + } - if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a " - "non-header rewrite - not allowed at transport time -"); - } - break; + if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a " + "non-header rewrite - not allowed at transport time -"); + } + break; - /* If it was an expanded uid, see if there is any expansion to be - done by checking for the presence of a $ character. If there is, save it - in the corresponding *expand_user option field. Otherwise, fall through - to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds - of data. */ + /* If it was an expanded uid, see if there is any expansion to be + done by checking for the presence of a $ character. If there is, save it + in the corresponding *expand_user option field. Otherwise, fall through + to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds + of data. */ - case opt_expand_uid: - sprintf(CS name2, "*expand_%.50s", name); - if ((ol2 = find_option(name2, oltop, last))) - { - uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; + case opt_expand_uid: + sprintf(CS name2, "*expand_%.50s", name); + if ((ol2 = find_option(name2, oltop, last))) + { + uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; - if (data_block) - *(USS(US data_block + ol2->v.offset)) = ss; - else - *(USS ol2->v.value) = ss; + if (data_block) + *(USS(US data_block + ol2->v.offset)) = ss; + else + *(USS ol2->v.value) = ss; - if (ss) - { - *(get_set_flag(name, oltop, last, data_block)) = FALSE; - freesptr = FALSE; - break; - } - } + if (ss) + { + *(get_set_flag(name, oltop, last, data_block)) = FALSE; + freesptr = FALSE; + break; + } + } - /* Look up a fixed uid, and also make use of the corresponding gid - if a passwd entry is returned and the gid has not been set. */ + /* Look up a fixed uid, and also make use of the corresponding gid + if a passwd entry is returned and the gid has not been set. */ - case opt_uid: - if (!route_finduser(sptr, &pw, &uid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr); - if (data_block) - *(uid_t *)(US data_block + ol->v.offset) = uid; - else - *(uid_t *)ol->v.value = uid; + case opt_uid: + if (!route_finduser(sptr, &pw, &uid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr); + if (data_block) + *(uid_t *)(US data_block + ol->v.offset) = uid; + else + *(uid_t *)ol->v.value = uid; - /* Set the flag indicating a fixed value is set */ + /* Set the flag indicating a fixed value is set */ - *(get_set_flag(name, oltop, last, data_block)) = TRUE; + *(get_set_flag(name, oltop, last, data_block)) = TRUE; - /* Handle matching gid if we have a passwd entry: done by finding the - same name with terminating "user" changed to "group"; if not found, - ignore. Also ignore if the value is already set. */ + /* Handle matching gid if we have a passwd entry: done by finding the + same name with terminating "user" changed to "group"; if not found, + ignore. Also ignore if the value is already set. */ - if (pw == NULL) break; - Ustrcpy(name+Ustrlen(name)-4, US"group"); - ol2 = find_option(name, oltop, last); - if (ol2 && ((ol2->type & opt_mask) == opt_gid || - (ol2->type & opt_mask) == opt_expand_gid)) - { - BOOL *set_flag = get_set_flag(name, oltop, last, data_block); - if (!*set_flag) - { - if (data_block) - *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid; - else - *((gid_t *)ol2->v.value) = pw->pw_gid; - *set_flag = TRUE; - } - } - break; + if (pw == NULL) break; + Ustrcpy(name+Ustrlen(name)-4, US"group"); + ol2 = find_option(name, oltop, last); + if (ol2 && ((ol2->type & opt_mask) == opt_gid || + (ol2->type & opt_mask) == opt_expand_gid)) + { + BOOL *set_flag = get_set_flag(name, oltop, last, data_block); + if (!*set_flag) + { + if (data_block) + *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid; + else + *((gid_t *)ol2->v.value) = pw->pw_gid; + *set_flag = TRUE; + } + } + break; - /* If it was an expanded gid, see if there is any expansion to be - done by checking for the presence of a $ character. If there is, save it - in the corresponding *expand_user option field. Otherwise, fall through - to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds - of data. */ + /* If it was an expanded gid, see if there is any expansion to be + done by checking for the presence of a $ character. If there is, save it + in the corresponding *expand_user option field. Otherwise, fall through + to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds + of data. */ - case opt_expand_gid: - sprintf(CS name2, "*expand_%.50s", name); - if ((ol2 = find_option(name2, oltop, last))) - { - uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; + case opt_expand_gid: + sprintf(CS name2, "*expand_%.50s", name); + if ((ol2 = find_option(name2, oltop, last))) + { + uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; - if (data_block) - *(USS(US data_block + ol2->v.offset)) = ss; - else - *(USS ol2->v.value) = ss; + if (data_block) + *(USS(US data_block + ol2->v.offset)) = ss; + else + *(USS ol2->v.value) = ss; - if (ss) - { - *(get_set_flag(name, oltop, last, data_block)) = FALSE; - freesptr = FALSE; - break; - } - } + if (ss) + { + *(get_set_flag(name, oltop, last, data_block)) = FALSE; + freesptr = FALSE; + break; + } + } - /* Handle freestanding gid */ + /* Handle freestanding gid */ - case opt_gid: - if (!route_findgroup(sptr, &gid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr); - if (data_block) - *((gid_t *)(US data_block + ol->v.offset)) = gid; - else - *((gid_t *)ol->v.value) = gid; - *(get_set_flag(name, oltop, last, data_block)) = TRUE; - break; + case opt_gid: + if (!route_findgroup(sptr, &gid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr); + if (data_block) + *((gid_t *)(US data_block + ol->v.offset)) = gid; + else + *((gid_t *)ol->v.value) = gid; + *(get_set_flag(name, oltop, last, data_block)) = TRUE; + break; - /* If it was a uid list, look up each individual entry, and build - a vector of uids, with a count in the first element. Put the vector - in malloc store so we can free the string. (We are reading into - permanent store already.) */ + /* If it was a uid list, look up each individual entry, and build + a vector of uids, with a count in the first element. Put the vector + in malloc store so we can free the string. (We are reading into + permanent store already.) */ - case opt_uidlist: - { - int count = 1; - uid_t *list; - int ptr = 0; - const uschar *p; - const uschar *op = expand_string (sptr); - - if (op == NULL) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", - name, expand_string_message); - - p = op; - if (*p != 0) count++; - while (*p != 0) if (*p++ == ':' && *p != 0) count++; - list = store_malloc(count*sizeof(uid_t)); - list[ptr++] = (uid_t)(count - 1); - - if (data_block) - *((uid_t **)(US data_block + ol->v.offset)) = list; - else - *((uid_t **)ol->v.value) = list; + case opt_uidlist: + { + int count = 1; + uid_t *list; + int ptr = 0; + const uschar *p; + const uschar *op = expand_string (sptr); + + if (op == NULL) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", + name, expand_string_message); + + p = op; + if (*p != 0) count++; + while (*p != 0) if (*p++ == ':' && *p != 0) count++; + list = store_malloc(count*sizeof(uid_t)); + list[ptr++] = (uid_t)(count - 1); + + if (data_block) + *((uid_t **)(US data_block + ol->v.offset)) = list; + else + *((uid_t **)ol->v.value) = list; - p = op; - while (count-- > 1) - { - int sep = 0; - /* If p is tainted we trap. Not sure that can happen */ - (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); - if (!route_finduser(big_buffer, NULL, &uid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", - big_buffer); - list[ptr++] = uid; - } - } - break; + p = op; + while (count-- > 1) + { + int sep = 0; + /* If p is tainted we trap. Not sure that can happen */ + (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); + if (!route_finduser(big_buffer, NULL, &uid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", + big_buffer); + list[ptr++] = uid; + } + break; + } - /* If it was a gid list, look up each individual entry, and build - a vector of gids, with a count in the first element. Put the vector - in malloc store so we can free the string. (We are reading into permanent - store already.) */ + /* If it was a gid list, look up each individual entry, and build + a vector of gids, with a count in the first element. Put the vector + in malloc store so we can free the string. (We are reading into permanent + store already.) */ - case opt_gidlist: - { - int count = 1; - gid_t *list; - int ptr = 0; - const uschar *p; - const uschar *op = expand_string (sptr); - - if (!op) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", - name, expand_string_message); - - p = op; - if (*p != 0) count++; - while (*p != 0) if (*p++ == ':' && *p != 0) count++; - list = store_malloc(count*sizeof(gid_t)); - list[ptr++] = (gid_t)(count - 1); - - if (data_block) - *((gid_t **)(US data_block + ol->v.offset)) = list; - else - *((gid_t **)ol->v.value) = list; + case opt_gidlist: + { + int count = 1; + gid_t *list; + int ptr = 0; + const uschar *p; + const uschar *op = expand_string (sptr); + + if (!op) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", + name, expand_string_message); + + p = op; + if (*p != 0) count++; + while (*p != 0) if (*p++ == ':' && *p != 0) count++; + list = store_malloc(count*sizeof(gid_t)); + list[ptr++] = (gid_t)(count - 1); + + if (data_block) + *((gid_t **)(US data_block + ol->v.offset)) = list; + else + *((gid_t **)ol->v.value) = list; - p = op; - while (count-- > 1) - { - int sep = 0; - /* If p is tainted we trap. Not sure that can happen */ - (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); - if (!route_findgroup(big_buffer, &gid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", - big_buffer); - list[ptr++] = gid; - } + p = op; + while (count-- > 1) + { + int sep = 0; + /* If p is tainted we trap. Not sure that can happen */ + (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); + if (!route_findgroup(big_buffer, &gid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", + big_buffer); + list[ptr++] = gid; + } + break; + } } - break; - } - /* Release store if the value of the string doesn't need to be kept. */ + /* Release store if the value of the string doesn't need to be kept. */ - if (freesptr) reset_point = store_reset(reset_point); - break; + if (freesptr) reset_point = store_reset(reset_point); + break; /* Expanded boolean: if no characters follow, or if there are no dollar characters, this is a fixed-valued boolean, and we fall through. Otherwise, save the string for later expansion in the alternate place. */ case opt_expand_bool: - if (*s && Ustrchr(s, '$') != 0) - { - sprintf(CS name2, "*expand_%.50s", name); - if ((ol2 = find_option(name2, oltop, last))) + if (*s && Ustrchr(s, '$') != 0) { - reset_point = store_mark(); - sptr = read_string(s, name); - if (data_block) - *(USS(US data_block + ol2->v.offset)) = sptr; - else - *(USS ol2->v.value) = sptr; - freesptr = FALSE; - break; + sprintf(CS name2, "*expand_%.50s", name); + if ((ol2 = find_option(name2, oltop, last))) + { + reset_point = store_mark(); + sptr = read_string(s, name); + if (data_block) + *(USS(US data_block + ol2->v.offset)) = sptr; + else + *(USS ol2->v.value) = sptr; + freesptr = FALSE; + break; + } } - } - /* Fall through */ + /* Fall through */ /* Boolean: if no characters follow, the value is boolvalue. Otherwise look for yes/not/true/false. Some booleans are stored in a single bit in @@ -2095,121 +2095,121 @@ switch (type) case opt_bit: case opt_bool_verify: case opt_bool_set: - if (*s != 0) - { - s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s); - if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0) - boolvalue = TRUE; - else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0) - boolvalue = FALSE; - else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "\"%s\" is not a valid value for the \"%s\" option", name2, name); - if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2), - US"for boolean option ", name); - } + if (*s) + { + s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s); + if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0) + boolvalue = TRUE; + else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0) + boolvalue = FALSE; + else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "\"%s\" is not a valid value for the \"%s\" option", name2, name); + if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2), + US"for boolean option ", name); + } - /* Handle single-bit type. */ + /* Handle single-bit type. */ - if (type == opt_bit) - { - int bit = 1 << ((ol->type >> 16) & 31); - int * ptr = data_block - ? (int *)(US data_block + ol->v.offset) - : (int *)ol->v.value; - if (boolvalue) *ptr |= bit; else *ptr &= ~bit; - break; - } + if (type == opt_bit) + { + int bit = 1 << ((ol->type >> 16) & 31); + int * ptr = data_block + ? (int *)(US data_block + ol->v.offset) + : (int *)ol->v.value; + if (boolvalue) *ptr |= bit; else *ptr &= ~bit; + break; + } - /* Handle full BOOL types */ + /* Handle full BOOL types */ - if (data_block) - *((BOOL *)(US data_block + ol->v.offset)) = boolvalue; - else - *((BOOL *)ol->v.value) = boolvalue; + if (data_block) + *((BOOL *)(US data_block + ol->v.offset)) = boolvalue; + else + *((BOOL *)ol->v.value) = boolvalue; - /* Verify fudge */ + /* Verify fudge */ - if (type == opt_bool_verify) - { - sprintf(CS name2, "%.50s_recipient", name + offset); - if ((ol2 = find_option(name2, oltop, last))) - if (data_block) - *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue; - else - *((BOOL *)ol2->v.value) = boolvalue; - } + if (type == opt_bool_verify) + { + sprintf(CS name2, "%.50s_recipient", name + offset); + if ((ol2 = find_option(name2, oltop, last))) + if (data_block) + *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue; + else + *((BOOL *)ol2->v.value) = boolvalue; + } - /* Note that opt_bool_set type is set, if there is somewhere to do so */ + /* Note that opt_bool_set type is set, if there is somewhere to do so */ - else if (type == opt_bool_set) - { - sprintf(CS name2, "*set_%.50s", name + offset); - if ((ol2 = find_option(name2, oltop, last))) - if (data_block) - *((BOOL *)(US data_block + ol2->v.offset)) = TRUE; - else - *((BOOL *)ol2->v.value) = TRUE; - } - break; + else if (type == opt_bool_set) + { + sprintf(CS name2, "*set_%.50s", name + offset); + if ((ol2 = find_option(name2, oltop, last))) + if (data_block) + *((BOOL *)(US data_block + ol2->v.offset)) = TRUE; + else + *((BOOL *)ol2->v.value) = TRUE; + } + break; /* Octal integer */ case opt_octint: - intbase = 8; - inttype = US"octal "; + intbase = 8; + inttype = US"octal "; /* Integer: a simple(ish) case; allow octal and hex formats, and suffixes K, M, G, and T. The different types affect output, not input. */ case opt_mkint: case opt_int: - { - uschar *endptr; - long int lvalue; + { + uschar *endptr; + long int lvalue; - errno = 0; - lvalue = strtol(CS s, CSS &endptr, intbase); + errno = 0; + lvalue = strtol(CS s, CSS &endptr, intbase); - if (endptr == s) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s", - inttype, name); - - if (errno != ERANGE && *endptr) - { - uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */ + if (endptr == s) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s", + inttype, name); - if ((mp = Ustrchr(mp, *endptr))) + if (errno != ERANGE && *endptr) { - endptr++; - do + uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */ + + if ((mp = Ustrchr(mp, *endptr))) { - if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) + endptr++; + do { - errno = ERANGE; - break; + if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) + { + errno = ERANGE; + break; + } + lvalue *= 1024; } - lvalue *= 1024; + while (*(mp += 2)); } - while (*(mp += 2)); } - } - if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "absolute value of integer \"%s\" is too large (overflow)", s); + if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "absolute value of integer \"%s\" is too large (overflow)", s); - while (isspace(*endptr)) endptr++; - if (*endptr) - extra_chars_error(endptr, inttype, US"integer value for ", name); + while (isspace(*endptr)) endptr++; + if (*endptr) + extra_chars_error(endptr, inttype, US"integer value for ", name); - value = (int)lvalue; - } + value = (int)lvalue; + } - if (data_block) - *(int *)(US data_block + ol->v.offset) = value; - else - *(int *)ol->v.value = value; - break; + if (data_block) + *(int *)(US data_block + ol->v.offset) = value; + else + *(int *)ol->v.value = value; + break; /* Integer held in K: again, allow formats and suffixes as above. */ @@ -2261,56 +2261,56 @@ switch (type) /* Fixed-point number: held to 3 decimal places. */ case opt_fixed: - if (sscanf(CS s, "%d%n", &value, &count) != 1) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "fixed-point number expected for %s", name); + if (sscanf(CS s, "%d%n", &value, &count) != 1) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "fixed-point number expected for %s", name); - if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "integer \"%s\" is too large (overflow)", s); + if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "integer \"%s\" is too large (overflow)", s); - value *= 1000; + value *= 1000; - if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "integer \"%s\" is too large (overflow)", s); + if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "integer \"%s\" is too large (overflow)", s); - /* We get a coverity error here for using count, as it derived - from the tainted buffer pointed to by s, as parsed by sscanf(). - By the definition of sscanf we must be accessing between start - and end of s (assuming it is nul-terminated...) so ignore the error. */ - /* coverity[tainted_data] */ - if (s[count] == '.') - { - int d = 100; - while (isdigit(s[++count])) + /* We get a coverity error here for using count, as it derived + from the tainted buffer pointed to by s, as parsed by sscanf(). + By the definition of sscanf we must be accessing between start + and end of s (assuming it is nul-terminated...) so ignore the error. */ + /* coverity[tainted_data] */ + if (s[count] == '.') { - value += (s[count] - '0') * d; - d /= 10; + int d = 100; + while (isdigit(s[++count])) + { + value += (s[count] - '0') * d; + d /= 10; + } } - } - while (isspace(s[count])) count++; + while (isspace(s[count])) count++; - if (s[count] != 0) - extra_chars_error(s+count, US"fixed-point value for ", name, US""); + if (s[count] != 0) + extra_chars_error(s+count, US"fixed-point value for ", name, US""); - if (data_block) - *((int *)(US data_block + ol->v.offset)) = value; - else - *((int *)ol->v.value) = value; - break; + if (data_block) + *((int *)(US data_block + ol->v.offset)) = value; + else + *((int *)ol->v.value) = value; + break; /* There's a special routine to read time values. */ case opt_time: - value = readconf_readtime(s, 0, FALSE); - if (value < 0) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s", - name); - if (data_block) - *((int *)(US data_block + ol->v.offset)) = value; - else - *((int *)ol->v.value) = value; - break; + value = readconf_readtime(s, 0, FALSE); + if (value < 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s", + name); + if (data_block) + *((int *)(US data_block + ol->v.offset)) = value; + else + *((int *)ol->v.value) = value; + break; /* A time list is a list of colon-separated times, with the first element holding the size of the list and the second the number of @@ -3714,14 +3714,14 @@ readconf_driver_init( optionlist *driver_optionlist, int driver_optionlist_count) { -driver_instance **p = anchor; -driver_instance *d = NULL; -uschar *buffer; +driver_instance ** p = anchor; +driver_instance * d = NULL; +uschar * buffer; while ((buffer = get_config_line())) { uschar name[EXIM_DRIVERNAME_MAX]; - uschar *s; + const uschar * s; /* Read the first name on the line and test for the start of a new driver. A macro definition indicates the end of the previous driver. If this isn't the @@ -4241,8 +4241,6 @@ Returns: nothing static void readconf_acl(void) { -uschar *p; - /* Read each ACL and add it into the tree. Macro (re)definitions are allowed between ACLs. */ @@ -4251,10 +4249,10 @@ acl_line = get_config_line(); while(acl_line) { uschar name[EXIM_DRIVERNAME_MAX]; - tree_node *node; - uschar *error; + tree_node * node; + uschar * error; + const uschar * p = readconf_readname(name, sizeof(name), acl_line); - p = readconf_readname(name, sizeof(name), acl_line); if (isupper(*name) && *p == '=') { if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE);