X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/e851856fc72bf126f7b649a007fb7040140d5d3c..2983e1a616058c03b57f1ab32a691f8b8ff9764e:/src/src/acl.c?ds=sidebyside diff --git a/src/src/acl.c b/src/src/acl.c index 938bea7d9..c1d60bbd9 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -112,7 +113,8 @@ enum { ACLC_ACL, /* ACL conditions/modifiers: "delay", "control", "continue", "endpass", "message", "log_message", "log_reject_target", "logwrite", "queue" and "set" are modifiers that look like conditions but always return TRUE. They are used for -their side effects. */ +their side effects. Do not invent new modifier names that result in one name +being the prefix of another; the binary-search in the list will go wrong. */ typedef struct condition_def { uschar *name; @@ -366,7 +368,7 @@ enum { CONTROL_NO_MULTILINE, CONTROL_NO_PIPELINING, - CONTROL_QUEUE_ONLY, + CONTROL_QUEUE, CONTROL_SUBMISSION, CONTROL_SUPPRESS_LOCAL_FIXUPS, #ifdef SUPPORT_I18N @@ -502,8 +504,8 @@ static control_def controls_list[] = { ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START }, -[CONTROL_QUEUE_ONLY] = - { US"queue_only", FALSE, +[CONTROL_QUEUE] = + { US"queue", TRUE, (unsigned) ~(ACL_BIT_MAIL | ACL_BIT_RCPT | ACL_BIT_PREDATA | ACL_BIT_DATA | @@ -511,7 +513,6 @@ static control_def controls_list[] = { ACL_BIT_NOTSMTP | ACL_BIT_MIME) }, - [CONTROL_SUBMISSION] = { US"submission", TRUE, (unsigned) @@ -732,7 +733,7 @@ uschar * s; *error = NULL; -while ((s = (*func)()) != NULL) +while ((s = (*func)())) { int v, c; BOOL negated = FALSE; @@ -742,8 +743,7 @@ while ((s = (*func)()) != NULL) /* Conditions (but not verbs) are allowed to be negated by an initial exclamation mark. */ - while (isspace(*s)) s++; - if (*s == '!') + if (Uskip_whitespace(&s) == '!') { negated = TRUE; s++; @@ -859,7 +859,7 @@ while ((s = (*func)()) != NULL) } cond->u.varname = string_copyn(s, 18); s = endptr; - while (isspace(*s)) s++; + Uskip_whitespace(&s); } else #endif @@ -895,7 +895,7 @@ while ((s = (*func)()) != NULL) cond->u.varname = string_copyn(s + 4, endptr - s - 4); s = endptr; - while (isspace(*s)) s++; + Uskip_whitespace(&s); } /* For "set", we are now positioned for the data. For the others, only @@ -909,7 +909,7 @@ while ((s = (*func)()) != NULL) conditions[c].is_modifier ? US"modifier" : US"condition"); return NULL; } - while (isspace(*s)) s++; + Uskip_whitespace(&s); cond->arg = string_copy(s); } } @@ -1601,7 +1601,7 @@ an error if options are given for items that don't expect them. uschar *slash = Ustrchr(arg, '/'); const uschar *list = arg; -uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); +uschar *ss = string_nextinlist(&list, &sep, NULL, 0); verify_type_t * vp; if (!ss) goto BAD_VERIFY; @@ -2113,7 +2113,9 @@ return ERROR; * Check argument for control= modifier * *************************************************/ -/* Called from acl_check_condition() below +/* Called from acl_check_condition() below. +To handle the case "queue_only" we accept an _ in the +initial / option-switch position. Arguments: arg the argument string for control= @@ -2129,10 +2131,11 @@ decode_control(const uschar *arg, const uschar **pptr, int where, uschar **log_m { int idx, len; control_def * d; +uschar c; if ( (idx = find_control(arg, controls_list, nelem(controls_list))) < 0 - || ( arg[len = Ustrlen((d = controls_list+idx)->name)] != 0 - && (!d->has_option || arg[len] != '/') + || ( (c = arg[len = Ustrlen((d = controls_list+idx)->name)]) != 0 + && (!d->has_option || c != '/' && c != '_') ) ) { *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); @@ -2258,7 +2261,7 @@ count = 1.0; /* Parse the other options. */ -while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))) +while ((ss = string_nextinlist(&arg, &sep, NULL, 0))) { if (strcmpic(ss, US"leaky") == 0) leaky = TRUE; else if (strcmpic(ss, US"strict") == 0) strict = TRUE; @@ -3158,8 +3161,17 @@ for (; cb; cb = cb->next) cancel_cutthrough_connection(TRUE, US"item frozen"); break; - case CONTROL_QUEUE_ONLY: + case CONTROL_QUEUE: f.queue_only_policy = TRUE; + if (Ustrcmp(p, "_only") == 0) + p += 5; + else while (*p == '/') + if (Ustrncmp(p, "/only", 5) == 0) + { p += 5; f.queue_smtp = FALSE; } + else if (Ustrncmp(p, "/first_pass_route", 17) == 0) + { p += 17; f.queue_smtp = TRUE; } + else + break; cancel_cutthrough_connection(TRUE, US"queueing forced"); break; @@ -3466,13 +3478,13 @@ for (; cb; cb = cb->next) { uschar *endcipher = NULL; uschar *cipher = Ustrchr(tls_in.cipher, ':'); - if (cipher == NULL) cipher = tls_in.cipher; else + if (!cipher) cipher = tls_in.cipher; else { endcipher = Ustrchr(++cipher, ':'); - if (endcipher != NULL) *endcipher = 0; + if (endcipher) *endcipher = 0; } rc = match_isinlist(cipher, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); - if (endcipher != NULL) *endcipher = ':'; + if (endcipher) *endcipher = ':'; } break; @@ -3485,8 +3497,7 @@ for (; cb; cb = cb->next) case ACLC_HOSTS: rc = verify_check_this_host(&arg, sender_host_cache, NULL, - (sender_host_address == NULL)? US"" : sender_host_address, - CUSS &host_data); + sender_host_address ? sender_host_address : US"", CUSS &host_data); if (rc == DEFER) *log_msgptr = search_error_message; if (host_data) host_data = string_copy_perm(host_data, TRUE); break; @@ -3831,16 +3842,16 @@ uschar *yield; for(;;) { - while (isspace(*acl_text)) acl_text++; /* Leading spaces/empty lines */ - if (*acl_text == 0) return NULL; /* No more data */ - yield = acl_text; /* Potential data line */ + Uskip_whitespace(&acl_text); /* Leading spaces/empty lines */ + if (!*acl_text) return NULL; /* No more data */ + yield = acl_text; /* Potential data line */ while (*acl_text && *acl_text != '\n') acl_text++; /* If we hit the end before a newline, we have the whole logical line. If it's a comment, there's no more data to be given. Otherwise, yield it. */ - if (*acl_text == 0) return (*yield == '#')? NULL : yield; + if (!*acl_text) return *yield == '#' ? NULL : yield; /* After reaching a newline, end this loop if the physical line does not start with '#'. If it does, it's a comment, and the loop continues. */