X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/06fdb9f73dedfffc8b8613017ec1bb23f970b246..a8af957d93348cd8c74b852431fae7e65bc8297f:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index 9efc85844..fdd32b8e7 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2017 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -510,6 +510,8 @@ static control_def controls_list[] = { // ACL_BIT_PRDR| /* Not allow one user to freeze for all */ ACL_BIT_NOTSMTP | ACL_BIT_MIME) }, + + [CONTROL_SUBMISSION] = { US"submission", TRUE, (unsigned) @@ -523,7 +525,8 @@ static control_def controls_list[] = { }, #ifdef SUPPORT_I18N [CONTROL_UTF8_DOWNCONVERT] = - { US"utf8_downconvert", TRUE, 0 } + { US"utf8_downconvert", TRUE, (unsigned) ~(ACL_BIT_RCPT | ACL_BIT_VRFY) + } #endif }; @@ -627,8 +630,7 @@ Returns: index of a control entry, or -1 if not found static int find_control(const uschar * name, control_def * ol, int last) { -int first = 0; -while (last > first) +for (int first = 0; last > first; ) { int middle = (first + last)/2; uschar * s = ol[middle].name; @@ -659,8 +661,7 @@ Returns: offset in list, or -1 if not found static int acl_checkcondition(uschar * name, condition_def * list, int end) { -int start = 0; -while (start < end) +for (int start = 0; start < end; ) { int mid = (start + end)/2; int c = Ustrcmp(name, list[mid].name); @@ -689,9 +690,7 @@ Returns: offset in list, or -1 if not found static int acl_checkname(uschar *name, uschar **list, int end) { -int start = 0; - -while (start < end) +for (int start = 0; start < end; ) { int mid = (start + end)/2; int c = Ustrcmp(name, list[mid]); @@ -729,7 +728,7 @@ acl_block **lastp = &yield; acl_block *this = NULL; acl_condition_block *cond; acl_condition_block **condp = NULL; -uschar *s; +uschar * s; *error = NULL; @@ -761,7 +760,7 @@ while ((s = (*func)()) != NULL) if ((v = acl_checkname(name, verbs, nelem(verbs))) < 0) { - if (this == NULL) + if (!this) { *error = string_sprintf("unknown ACL verb \"%s\" in \"%s\"", name, saveline); @@ -782,8 +781,10 @@ while ((s = (*func)()) != NULL) *lastp = this; lastp = &(this->next); this->next = NULL; - this->verb = v; 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 */ if (*s == '!') @@ -957,7 +958,7 @@ else /* Loop for multiple header lines, taking care about continuations */ -for (p = q; *p != 0; ) +for (p = q; *p; p = q) { const uschar *s; uschar * hdr; @@ -969,7 +970,7 @@ for (p = q; *p != 0; ) for (;;) { q = Ustrchr(q, '\n'); /* we know there was a newline */ - if (*(++q) != ' ' && *q != '\t') break; + if (*++q != ' ' && *q != '\t') break; } /* If the line starts with a colon, interpret the instruction for where to @@ -1004,24 +1005,22 @@ for (p = q; *p != 0; ) to the front of it. */ for (s = p; s < q - 1; s++) - { if (*s == ':' || !isgraph(*s)) break; - } - hdr = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", (int) (q - p), p); + hdr = string_sprintf("%s%.*s", *s == ':' ? "" : "X-ACL-Warn: ", (int) (q - p), p); hlen = Ustrlen(hdr); /* See if this line has already been added */ - while (*hptr != NULL) + while (*hptr) { if (Ustrncmp((*hptr)->text, hdr, hlen) == 0) break; - hptr = &((*hptr)->next); + hptr = &(*hptr)->next; } /* Add if not previously present */ - if (*hptr == NULL) + if (!*hptr) { header_line *h = store_get(sizeof(header_line)); h->text = hdr; @@ -1029,12 +1028,8 @@ for (p = q; *p != 0; ) h->type = newtype; h->slen = hlen; *hptr = h; - hptr = &(h->next); + hptr = &h->next; } - - /* Advance for next header line within the string */ - - p = q; } } @@ -1047,33 +1042,15 @@ uschar * fn_hdrs_added(void) { gstring * g = NULL; -header_line * h = acl_added_headers; -uschar * s; -uschar * cp; - -if (!h) return NULL; -do +for (header_line * h = acl_added_headers; h; h = h->next) { - s = h->text; - while ((cp = Ustrchr(s, '\n')) != NULL) - { - if (cp[1] == '\0') break; - - /* contains embedded newline; needs doubling */ - g = string_catn(g, s, cp-s+1); - g = string_catn(g, US"\n", 1); - s = cp+1; - } - /* last bit of header */ - -/*XXX could we use add_listele? */ - g = string_catn(g, s, cp-s+1); /* newline-sep list */ + int i = h->slen; + if (h->text[i-1] == '\n') i--; + g = string_append_listele_n(g, '\n', h->text, i); } -while((h = h->next)); -g->s[g->ptr - 1] = '\0'; /* overwrite last newline */ -return g->s; +return g ? g->s : NULL; } @@ -1092,7 +1069,7 @@ Returns: nothing static void setup_remove_header(const uschar *hnames) { -if (*hnames != 0) +if (*hnames) acl_removed_headers = acl_removed_headers ? string_sprintf("%s : %s", acl_removed_headers, hnames) : string_copy(hnames); @@ -1141,10 +1118,10 @@ if (log_message != NULL && log_message != user_message) /* Search previously logged warnings. They are kept in malloc store so they can be freed at the start of a new message. */ - for (logged = acl_warn_logged; logged != NULL; logged = logged->next) + for (logged = acl_warn_logged; logged; logged = logged->next) if (Ustrcmp(logged->text, text) == 0) break; - if (logged == NULL) + if (!logged) { int length = Ustrlen(text) + 1; log_write(0, LOG_MAIN, "%s", text); @@ -1158,7 +1135,7 @@ if (log_message != NULL && log_message != user_message) /* If there's no user message, we are done. */ -if (user_message == NULL) return; +if (!user_message) return; /* If this isn't a message ACL, we can't do anything with a user message. Log an error. */ @@ -1223,11 +1200,10 @@ HDEBUG(D_acl) if ((rc = host_name_lookup()) != OK) { - *log_msgptr = (rc == DEFER)? - US"host lookup deferred for reverse lookup check" - : - string_sprintf("host lookup failed for reverse lookup check%s", - host_lookup_msg); + *log_msgptr = rc == DEFER + ? US"host lookup deferred for reverse lookup check" + : string_sprintf("host lookup failed for reverse lookup check%s", + host_lookup_msg); return rc; /* DEFER or FAIL */ } @@ -1265,13 +1241,10 @@ static int acl_verify_csa_address(dns_answer *dnsa, dns_scan *dnss, int reset, uschar *target) { -dns_record *rr; -dns_address *da; - -BOOL target_found = FALSE; +int rc = CSA_FAIL_NOADDR; -for (rr = dns_next_rr(dnsa, dnss, reset); - rr != NULL; +for (dns_record * rr = dns_next_rr(dnsa, dnss, reset); + rr; rr = dns_next_rr(dnsa, dnss, RESET_NEXT)) { /* Check this is an address RR for the target hostname. */ @@ -1284,12 +1257,12 @@ for (rr = dns_next_rr(dnsa, dnss, reset); if (strcmpic(target, rr->name) != 0) continue; - target_found = TRUE; + rc = CSA_FAIL_MISMATCH; /* Turn the target address RR into a list of textual IP addresses and scan the list. There may be more than one if it is an A6 RR. */ - for (da = dns_address_from_rr(dnsa, rr); da != NULL; da = da->next) + for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next) { /* If the client IP address matches the target IP address, it's good! */ @@ -1303,8 +1276,7 @@ for (rr = dns_next_rr(dnsa, dnss, reset); using an unauthorized IP address, otherwise the target has no authorized IP addresses. */ -if (target_found) return CSA_FAIL_MISMATCH; -else return CSA_FAIL_NOADDR; +return rc; } @@ -1463,7 +1435,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); /* If we didn't break the loop then no appropriate records were found. */ -if (rr == NULL) return t->data.val = CSA_UNKNOWN; +if (!rr) return t->data.val = CSA_UNKNOWN; /* Do not check addresses if the target is ".", in accordance with RFC 2782. A target of "." indicates there are no valid addresses, so the client cannot @@ -1540,7 +1512,7 @@ static verify_type_t verify_type_list[] = { { US"helo", VERIFY_HELO, ~0, TRUE, 0 }, { US"csa", VERIFY_CSA, ~0, FALSE, 0 }, { US"header_syntax", VERIFY_HDR_SYNTAX, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 }, - { US"not_blind", VERIFY_NOT_BLIND, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 }, + { US"not_blind", VERIFY_NOT_BLIND, ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 }, { US"header_sender", VERIFY_HDR_SNDR, ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 }, { US"sender", VERIFY_SNDR, ACL_BIT_MAIL | ACL_BIT_RCPT |ACL_BIT_PREDATA | ACL_BIT_DATA | ACL_BIT_NOTSMTP, @@ -1548,7 +1520,7 @@ static verify_type_t verify_type_list[] = { { US"recipient", VERIFY_RCPT, ACL_BIT_RCPT, FALSE, 0 }, { US"header_names_ascii", VERIFY_HDR_NAMES_ASCII, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 }, #ifdef EXPERIMENTAL_ARC - { US"arc", VERIFY_ARC, ACL_BIT_DATA, TRUE , 0 }, + { US"arc", VERIFY_ARC, ACL_BIT_DATA, FALSE , 0 }, #endif }; @@ -1636,7 +1608,7 @@ if (!ss) goto BAD_VERIFY; /* Handle name/address consistency verification in a separate function. */ -for (vp= verify_type_list; +for (vp = verify_type_list; CS vp < CS verify_type_list + sizeof(verify_type_list); vp++ ) @@ -1661,9 +1633,9 @@ if (!(vp->where_allowed & BIT(where))) switch(vp->value) { case VERIFY_REV_HOST_LKUP: - if (sender_host_address == NULL) return OK; + if (!sender_host_address) return OK; if ((rc = acl_verify_reverse(user_msgptr, log_msgptr)) == DEFER) - while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) + while ((ss = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(ss, US"defer_ok") == 0) return OK; return rc; @@ -1681,8 +1653,8 @@ switch(vp->value) /* We can test the result of optional HELO verification that might have occurred earlier. If not, we can attempt the verification now. */ - if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); - return helo_verified? OK : FAIL; + if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo(); + return f.helo_verified ? OK : FAIL; case VERIFY_CSA: /* Do Client SMTP Authorization checks in a separate function, and turn the @@ -1695,6 +1667,24 @@ switch(vp->value) DEBUG(D_acl) debug_printf_indent("CSA result %s\n", csa_status); return csa_return_code[rc]; +#ifdef EXPERIMENTAL_ARC + case VERIFY_ARC: + { /* Do Authenticated Received Chain checks in a separate function. */ + const uschar * condlist = CUS string_nextinlist(&list, &sep, NULL, 0); + int csep = 0; + uschar * cond; + + if (!(arc_state = acl_verify_arc())) return DEFER; + DEBUG(D_acl) debug_printf_indent("ARC verify result %s %s%s%s\n", arc_state, + arc_state_reason ? "(":"", arc_state_reason, arc_state_reason ? ")":""); + + if (!condlist) condlist = US"none:pass"; + while ((cond = string_nextinlist(&condlist, &csep, NULL, 0))) + if (Ustrcmp(arc_state, cond) == 0) return OK; + return FAIL; + } +#endif + case VERIFY_HDR_SYNTAX: /* Check that all relevant header lines have the correct 5322-syntax. If there is a syntax error, we return details of the error to the sender if configured to @@ -1714,22 +1704,34 @@ switch(vp->value) See RFC 5322, 2.2. and RFC 6532, 3. */ rc = verify_check_header_names_ascii(log_msgptr); - if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) + if (rc != OK && smtp_return_error_details && *log_msgptr) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); return rc; case VERIFY_NOT_BLIND: /* Check that no recipient of this message is "blind", that is, every envelope recipient must be mentioned in either To: or Cc:. */ + { + BOOL case_sensitive = TRUE; + + while ((ss = string_nextinlist(&list, &sep, NULL, 0))) + if (strcmpic(ss, US"case_insensitive") == 0) + case_sensitive = FALSE; + else + { + *log_msgptr = string_sprintf("unknown option \"%s\" in ACL " + "condition \"verify %s\"", ss, arg); + return ERROR; + } - rc = verify_check_notblind(); - if (rc != OK) + if ((rc = verify_check_notblind(case_sensitive)) != OK) { *log_msgptr = string_sprintf("bcc recipient detected"); if (smtp_return_error_details) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); } return rc; + } /* The remaining verification tests check recipient and sender addresses, either from the envelope or from the header. There are a number of @@ -1765,8 +1767,7 @@ switch(vp->value) /* Remaining items are optional; they apply to sender and recipient verification, including "header sender" verification. */ -while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) - != NULL) +while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) { if (strcmpic(ss, US"defer_ok") == 0) defer_ok = TRUE; else if (strcmpic(ss, US"no_details") == 0) no_details = TRUE; @@ -1799,10 +1800,10 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) { const uschar * sublist = ss; int optsep = ','; - uschar *opt; uschar buffer[256]; - while (isspace(*sublist)) sublist++; + uschar * opt; + while (isspace(*sublist)) sublist++; while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))) { callout_opt_t * op; @@ -1897,7 +1898,7 @@ if (verify_header_sender) { if (!*user_msgptr && *log_msgptr) *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); - if (rc == DEFER) acl_temp_details = TRUE; + if (rc == DEFER) f.acl_temp_details = TRUE; } } } @@ -2050,7 +2051,7 @@ else addr2.user_message : addr2.message; /* Allow details for temporary error if the address is so flagged. */ - if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE; + if (testflag((&addr2), af_pass_message)) f.acl_temp_details = TRUE; /* Make $address_data visible */ deliver_address_data = addr2.prop.address_data; @@ -2155,8 +2156,6 @@ Arguments: log_msgptr for error messages format format string ... supplementary arguments - ss ratelimit option name - where ACL_WHERE_xxxx indicating which ACL this is Returns: ERROR */ @@ -2165,14 +2164,15 @@ static int ratelimit_error(uschar **log_msgptr, const char *format, ...) { va_list ap; -uschar buffer[STRING_SPRINTF_BUFFER_SIZE]; +gstring * g = + string_cat(NULL, US"error in arguments to \"ratelimit\" condition: "); + va_start(ap, format); -if (!string_vformat(buffer, sizeof(buffer), format, ap)) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "string_sprintf expansion was longer than " SIZE_T_FMT, sizeof(buffer)); +g = string_vformat(g, TRUE, format, ap); va_end(ap); -*log_msgptr = string_sprintf( - "error in arguments to \"ratelimit\" condition: %s", buffer); + +gstring_reset_unused(g); +*log_msgptr = string_from_gstring(g); return ERROR; } @@ -2229,8 +2229,7 @@ error messages based on rate limits obtained from a table lookup. */ size, which must be greater than or equal to zero. Zero is useful for rate measurement as opposed to rate limiting. */ -sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0); -if (sender_rate_limit == NULL) +if (!(sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0))) return ratelimit_error(log_msgptr, "sender rate limit not set"); limit = Ustrtod(sender_rate_limit, &ss); @@ -2246,9 +2245,8 @@ if (limit < 0.0 || *ss != '\0') constant. This must be strictly greater than zero, because zero leads to run-time division errors. */ -sender_rate_period = string_nextinlist(&arg, &sep, NULL, 0); -if (sender_rate_period == NULL) period = -1.0; -else period = readconf_readtime(sender_rate_period, 0, FALSE); +period = !(sender_rate_period = string_nextinlist(&arg, &sep, NULL, 0)) + ? -1.0 : readconf_readtime(sender_rate_period, 0, FALSE); if (period <= 0.0) return ratelimit_error(log_msgptr, "\"%s\" is not a time value", sender_rate_period); @@ -2260,8 +2258,7 @@ count = 1.0; /* Parse the other options. */ -while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) - != NULL) +while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))) { if (strcmpic(ss, US"leaky") == 0) leaky = TRUE; else if (strcmpic(ss, US"strict") == 0) strict = TRUE; @@ -2298,25 +2295,24 @@ while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) zero and let the recorded rate decay as if nothing happened. */ RATE_SET(mode, PER_MAIL); if (where > ACL_WHERE_NOTSMTP) badacl = TRUE; - else count = message_size < 0 ? 0.0 : (double)message_size; + else count = message_size < 0 ? 0.0 : (double)message_size; } else if (strcmpic(ss, US"per_addr") == 0) { RATE_SET(mode, PER_RCPT); if (where != ACL_WHERE_RCPT) badacl = TRUE, unique = US"*"; - else unique = string_sprintf("%s@%s", deliver_localpart, deliver_domain); + else unique = string_sprintf("%s@%s", deliver_localpart, deliver_domain); } else if (strncmpic(ss, US"count=", 6) == 0) { uschar *e; count = Ustrtod(ss+6, &e); if (count < 0.0 || *e != '\0') - return ratelimit_error(log_msgptr, - "\"%s\" is not a positive number", ss); + return ratelimit_error(log_msgptr, "\"%s\" is not a positive number", ss); } else if (strncmpic(ss, US"unique=", 7) == 0) unique = string_copy(ss + 7); - else if (key == NULL) + else if (!key) key = string_copy(ss); else key = string_sprintf("%s/%s", key, ss); @@ -2332,7 +2328,7 @@ if (leaky + strict + readonly > 1) return ratelimit_error(log_msgptr, "conflicting update modes"); if (badacl && (leaky || strict) && !noupdate) return ratelimit_error(log_msgptr, - "\"%s\" must not have /leaky or /strict option in %s ACL", + "\"%s\" must not have /leaky or /strict option, or cannot be used in %s ACL", ratelimit_option_string[mode], acl_wherenames[where]); /* Set the default values of any unset options. In readonly mode we @@ -2350,8 +2346,8 @@ If there is no sender_host_address (e.g. -bs or acl_not_smtp) then we simply omit it. The smoothing constant (sender_rate_period) and the per_xxx options are added to the key because they alter the meaning of the stored data. */ -if (key == NULL) - key = (sender_host_address == NULL)? US"" : sender_host_address; +if (!key) + key = !sender_host_address ? US"" : sender_host_address; key = string_sprintf("%s/%s/%s%s", sender_rate_period, @@ -2372,30 +2368,30 @@ old_pool = store_pool; if (readonly) anchor = &ratelimiters_cmd; -else switch(mode) { -case RATE_PER_CONN: - anchor = &ratelimiters_conn; - store_pool = POOL_PERM; - break; -case RATE_PER_BYTE: -case RATE_PER_MAIL: -case RATE_PER_ALLRCPTS: - anchor = &ratelimiters_mail; - break; -case RATE_PER_ADDR: -case RATE_PER_CMD: -case RATE_PER_RCPT: - anchor = &ratelimiters_cmd; - break; -default: - anchor = NULL; /* silence an "unused" complaint */ - log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "internal ACL error: unknown ratelimit mode %d", mode); - break; -} +else switch(mode) + { + case RATE_PER_CONN: + anchor = &ratelimiters_conn; + store_pool = POOL_PERM; + break; + case RATE_PER_BYTE: + case RATE_PER_MAIL: + case RATE_PER_ALLRCPTS: + anchor = &ratelimiters_mail; + break; + case RATE_PER_ADDR: + case RATE_PER_CMD: + case RATE_PER_RCPT: + anchor = &ratelimiters_cmd; + break; + default: + anchor = NULL; /* silence an "unused" complaint */ + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "internal ACL error: unknown ratelimit mode %d", mode); + break; + } -t = tree_search(*anchor, key); -if (t != NULL) +if ((t = tree_search(*anchor, key))) { dbd = t->data.ptr; /* The following few lines duplicate some of the code below. */ @@ -2423,7 +2419,7 @@ dbd = NULL; gettimeofday(&tv, NULL); -if (dbdb != NULL) +if (dbdb) { /* Locate the basic ratelimit block inside the DB data. */ HDEBUG(D_acl) debug_printf_indent("ratelimit found key in database\n"); @@ -2434,7 +2430,7 @@ if (dbdb != NULL) filter because we want its size to change if the limit changes. Note that we keep the dbd pointer for copying the rate into the new data block. */ - if(unique != NULL && tv.tv_sec > dbdb->bloom_epoch + period) + if(unique && tv.tv_sec > dbdb->bloom_epoch + period) { HDEBUG(D_acl) debug_printf_indent("ratelimit discarding old Bloom filter\n"); dbdb = NULL; @@ -2442,7 +2438,7 @@ if (dbdb != NULL) /* Sanity check. */ - if(unique != NULL && dbdb_size < sizeof(*dbdb)) + if(unique && dbdb_size < sizeof(*dbdb)) { HDEBUG(D_acl) debug_printf_indent("ratelimit discarding undersize Bloom filter\n"); dbdb = NULL; @@ -2452,9 +2448,9 @@ if (dbdb != NULL) /* Allocate a new data block if the database lookup failed or the Bloom filter passed its age limit. */ -if (dbdb == NULL) +if (!dbdb) { - if (unique == NULL) + if (!unique) { /* No Bloom filter. This basic ratelimit block is initialized below. */ HDEBUG(D_acl) debug_printf_indent("ratelimit creating new rate data block\n"); @@ -2481,7 +2477,7 @@ if (dbdb == NULL) /* Preserve any basic ratelimit data (which is our longer-term memory) by copying it from the discarded block. */ - if (dbd != NULL) + if (dbd) { dbdb->dbd = *dbd; dbd = &dbdb->dbd; @@ -2495,7 +2491,7 @@ counted. We skip this code in readonly mode for efficiency, because any changes to the filter will be discarded and because count is already set to zero. */ -if (unique != NULL && !readonly) +if (unique && !readonly) { /* We identify unique events using a Bloom filter. (You can find my notes on Bloom filters at http://fanf.livejournal.com/81696.html) @@ -2577,7 +2573,7 @@ if (unique != NULL && !readonly) the new one, otherwise update the block from the database. The initial rate is what would be computed by the code below for an infinite interval. */ -if (dbd == NULL) +if (!dbd) { HDEBUG(D_acl) debug_printf_indent("ratelimit initializing new key's rate data\n"); dbd = &dbdb->dbd; @@ -2669,7 +2665,7 @@ else This matters for edge cases such as a limit of zero, when the client should be completely blocked. */ -rc = (dbd->rate < limit)? FAIL : OK; +rc = dbd->rate < limit ? FAIL : OK; /* Update the state if the rate is low or if we are being strict. If we are in leaky mode and the sender's rate is too high, we do not update @@ -2857,7 +2853,7 @@ int rc = OK; int sep = -'/'; #endif -for (; cb != NULL; cb = cb->next) +for (; cb; cb = cb->next) { const uschar *arg; int control_type; @@ -2896,10 +2892,10 @@ for (; cb != NULL; cb = cb->next) arg = cb->arg; else if (!(arg = expand_string(cb->arg))) { - if (expand_string_forcedfail) continue; + if (f.expand_string_forcedfail) continue; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", cb->arg, expand_string_message); - return search_find_defer ? DEFER : ERROR; + return f.search_find_defer ? DEFER : ERROR; } /* Show condition, and expanded condition if it's different */ @@ -2969,9 +2965,8 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_AUTHENTICATED: - rc = (sender_host_authenticated == NULL)? FAIL : - match_isinlist(sender_host_authenticated, &arg, 0, NULL, NULL, MCL_STRING, - TRUE, NULL); + rc = sender_host_authenticated ? match_isinlist(sender_host_authenticated, + &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL) : FAIL; break; #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -3022,7 +3017,7 @@ for (; cb != NULL; cb = cb->next) switch(control_type) { case CONTROL_AUTH_UNADVERTISED: - allow_auth_unadvertised = TRUE; + f.allow_auth_unadvertised = TRUE; break; #ifdef EXPERIMENTAL_BRIGHTMAIL @@ -3033,22 +3028,22 @@ for (; cb != NULL; cb = cb->next) #ifndef DISABLE_DKIM case CONTROL_DKIM_VERIFY: - dkim_disable_verify = TRUE; + f.dkim_disable_verify = TRUE; #ifdef EXPERIMENTAL_DMARC /* Since DKIM was blocked, skip DMARC too */ - dmarc_disable_verify = TRUE; - dmarc_enable_forensic = FALSE; + f.dmarc_disable_verify = TRUE; + f.dmarc_enable_forensic = FALSE; #endif break; #endif #ifdef EXPERIMENTAL_DMARC case CONTROL_DMARC_VERIFY: - dmarc_disable_verify = TRUE; + f.dmarc_disable_verify = TRUE; break; case CONTROL_DMARC_FORENSIC: - dmarc_enable_forensic = TRUE; + f.dmarc_enable_forensic = TRUE; break; #endif @@ -3113,24 +3108,24 @@ for (; cb != NULL; cb = cb->next) #ifdef WITH_CONTENT_SCAN case CONTROL_NO_MBOX_UNSPOOL: - no_mbox_unspool = TRUE; + f.no_mbox_unspool = TRUE; break; #endif case CONTROL_NO_MULTILINE: - no_multiline_responses = TRUE; + f.no_multiline_responses = TRUE; break; case CONTROL_NO_PIPELINING: - pipelining_enable = FALSE; + f.pipelining_enable = FALSE; break; case CONTROL_NO_DELAY_FLUSH: - disable_delay_flush = TRUE; + f.disable_delay_flush = TRUE; break; case CONTROL_NO_CALLOUT_FLUSH: - disable_callout_flush = TRUE; + f.disable_callout_flush = TRUE; break; case CONTROL_FAKEREJECT: @@ -3140,7 +3135,7 @@ for (; cb != NULL; cb = cb->next) if (*p == '/') { const uschar *pp = p + 1; - while (*pp != 0) pp++; + while (*pp) pp++; fake_response_text = expand_string(string_copyn(p+1, pp-p-1)); p = pp; } @@ -3152,7 +3147,7 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_FREEZE: - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); freeze_tell = freeze_tell_config; /* Reset to configured value */ if (Ustrncmp(p, "/no_tell", 8) == 0) @@ -3169,25 +3164,25 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_QUEUE_ONLY: - queue_only_policy = TRUE; + f.queue_only_policy = TRUE; cancel_cutthrough_connection(TRUE, US"queueing forced"); break; case CONTROL_SUBMISSION: originator_name = US""; - submission_mode = TRUE; + f.submission_mode = TRUE; while (*p == '/') { if (Ustrncmp(p, "/sender_retain", 14) == 0) { p += 14; - active_local_sender_retain = TRUE; - active_local_from_check = FALSE; + f.active_local_sender_retain = TRUE; + f.active_local_from_check = FALSE; } else if (Ustrncmp(p, "/domain=", 8) == 0) { const uschar *pp = p + 8; - while (*pp != 0 && *pp != '/') pp++; + while (*pp && *pp != '/') pp++; submission_domain = string_copyn(p+8, pp-p-8); p = pp; } @@ -3196,7 +3191,7 @@ for (; cb != NULL; cb = cb->next) else if (Ustrncmp(p, "/name=", 6) == 0) { const uschar *pp = p + 6; - while (*pp != 0) pp++; + while (*pp) pp++; submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, big_buffer, big_buffer_size)); p = pp; @@ -3247,10 +3242,12 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_SUPPRESS_LOCAL_FIXUPS: - suppress_local_fixups = TRUE; + f.suppress_local_fixups = TRUE; break; case CONTROL_CUTTHROUGH_DELIVERY: + { + uschar * ignored = NULL; #ifndef DISABLE_PRDR if (prdr_requested) #else @@ -3259,20 +3256,20 @@ for (; cb != NULL; cb = cb->next) /* Too hard to think about for now. We might in future cutthrough the case where both sides handle prdr and this-node prdr acl is "accept" */ - *log_msgptr = string_sprintf("PRDR on %s reception\n", arg); + ignored = US"PRDR active"; else { - if (deliver_freeze) - *log_msgptr = US"frozen"; - else if (queue_only_policy) - *log_msgptr = US"queue-only"; + if (f.deliver_freeze) + ignored = US"frozen"; + else if (f.queue_only_policy) + ignored = US"queue-only"; else if (fake_response == FAIL) - *log_msgptr = US"fakereject"; + ignored = US"fakereject"; else { if (rcpt_count == 1) { - cutthrough.delivery = TRUE; + cutthrough.delivery = TRUE; /* control accepted */ while (*p == '/') { const uschar * pp = p+1; @@ -3288,13 +3285,13 @@ for (; cb != NULL; cb = cb->next) } } else - DEBUG(D_acl) debug_printf(" cutthrough request ignored for nonfirst rcpt\n"); - break; + ignored = US"nonfirst rcpt"; } - *log_msgptr = string_sprintf("\"control=%s\" on %s item", - arg, *log_msgptr); } - return ERROR; + DEBUG(D_acl) if (ignored) + debug_printf(" cutthrough request ignored on %s item\n", ignored); + } + break; #ifdef SUPPORT_I18N case CONTROL_UTF8_DOWNCONVERT: @@ -3392,7 +3389,7 @@ for (; cb != NULL; cb = cb->next) else { - if (smtp_out != NULL && !disable_delay_flush) + if (smtp_out && !f.disable_delay_flush) mac_smtp_fflush(); #if !defined(NO_POLL_H) && defined (POLLRDHUP) @@ -3409,16 +3406,16 @@ for (; cb != NULL; cb = cb->next) HDEBUG(D_acl) debug_printf_indent("delay cancelled by peer close\n"); } #else - /* It appears to be impossible to detect that a TCP/IP connection has - gone away without reading from it. This means that we cannot shorten - the delay below if the client goes away, because we cannot discover - that the client has closed its end of the connection. (The connection - is actually in a half-closed state, waiting for the server to close its - end.) It would be nice to be able to detect this state, so that the - Exim process is not held up unnecessarily. However, it seems that we - can't. The poll() function does not do the right thing, and in any case - it is not always available. - */ + /* Lacking POLLRDHUP it appears to be impossible to detect that a + TCP/IP connection has gone away without reading from it. This means + that we cannot shorten the delay below if the client goes away, + because we cannot discover that the client has closed its end of the + connection. (The connection is actually in a half-closed state, + waiting for the server to close its end.) It would be nice to be able + to detect this state, so that the Exim process is not held up + unnecessarily. However, it seems that we can't. The poll() function + does not do the right thing, and in any case it is not always + available. */ while (delay > 0) delay = sleep(delay); #endif @@ -3444,9 +3441,9 @@ for (; cb != NULL; cb = cb->next) #ifdef EXPERIMENTAL_DMARC case ACLC_DMARC_STATUS: - if (!dmarc_has_been_checked) + if (!f.dmarc_has_been_checked) dmarc_process(); - dmarc_has_been_checked = TRUE; + f.dmarc_has_been_checked = TRUE; /* used long way of dmarc_exim_expand_query() in case we need more * view into the process in the future. */ rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS), @@ -3510,7 +3507,7 @@ for (; cb != NULL; cb = cb->next) int logbits = 0; int sep = 0; const uschar *s = arg; - uschar *ss; + uschar * ss; while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; @@ -3554,7 +3551,6 @@ for (; cb != NULL; cb = cb->next) } while (isspace(*s)) s++; - if (logbits == 0) logbits = LOG_MAIN; log_write(0, logbits, "%s", string_printing(s)); } @@ -3565,8 +3561,8 @@ for (; cb != NULL; cb = cb->next) { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); - uschar *opt; + uschar * ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); + uschar * opt; BOOL defer_ok = FALSE; int timeout = 0; @@ -3760,7 +3756,7 @@ if ((BIT(rc) & msgcond[verb]) != 0) expmessage = expand_string(user_message); if (!expmessage) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand ACL message \"%s\": %s", user_message, expand_string_message); } @@ -3773,7 +3769,7 @@ if ((BIT(rc) & msgcond[verb]) != 0) expmessage = expand_string(log_message); if (!expmessage) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand ACL message \"%s\": %s", log_message, expand_string_message); } @@ -3841,7 +3837,7 @@ for(;;) if (*acl_text == 0) return NULL; /* No more data */ yield = acl_text; /* Potential data line */ - while (*acl_text != 0 && *acl_text != '\n') acl_text++; + 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. */ @@ -3961,7 +3957,7 @@ if (acl_level == 0) { if (!(ss = expand_string(s))) { - if (expand_string_forcedfail) return OK; + if (f.expand_string_forcedfail) return OK; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", s, expand_string_message); return ERROR; @@ -3984,11 +3980,10 @@ read an ACL from a file, and save it so it can be re-used. */ if (Ustrchr(ss, ' ') == NULL) { - tree_node *t = tree_search(acl_anchor, ss); - if (t != NULL) + tree_node * t = tree_search(acl_anchor, ss); + if (t) { - acl = (acl_block *)(t->data.ptr); - if (acl == NULL) + if (!(acl = (acl_block *)(t->data.ptr))) { HDEBUG(D_acl) debug_printf_indent("ACL \"%s\" is empty: implicit DENY\n", ss); return FAIL; @@ -4000,8 +3995,7 @@ if (Ustrchr(ss, ' ') == NULL) else if (*ss == '/') { struct stat statbuf; - fd = Uopen(ss, O_RDONLY, 0); - if (fd < 0) + if ((fd = Uopen(ss, O_RDONLY, 0)) < 0) { *log_msgptr = string_sprintf("failed to open ACL file \"%s\": %s", ss, strerror(errno)); @@ -4036,13 +4030,13 @@ if (Ustrchr(ss, ' ') == NULL) in the ACL tree, having read it into the POOL_PERM store pool so that it persists between multiple messages. */ -if (acl == NULL) +if (!acl) { int old_pool = store_pool; if (fd >= 0) store_pool = POOL_PERM; acl = acl_read(acl_getline, log_msgptr); store_pool = old_pool; - if (acl == NULL && *log_msgptr != NULL) return ERROR; + if (!acl && *log_msgptr) return ERROR; if (fd >= 0) { tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss)); @@ -4054,7 +4048,7 @@ if (acl == NULL) /* Now we have an ACL to use. It's possible it may be NULL. */ -while (acl != NULL) +while (acl) { int cond; int basic_errno = 0; @@ -4063,9 +4057,10 @@ while (acl != NULL) && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT); *log_msgptr = *user_msgptr = NULL; - acl_temp_details = FALSE; + f.acl_temp_details = FALSE; - HDEBUG(D_acl) debug_printf_indent("processing \"%s\"\n", verbs[acl->verb]); + HDEBUG(D_acl) debug_printf_indent("processing \"%s\" (%s %d)\n", + verbs[acl->verb], acl->srcfile, acl->srcline); /* Clear out any search error message from a previous check before testing this condition. */ @@ -4080,46 +4075,47 @@ while (acl != NULL) switch (cond) { case DEFER: - HDEBUG(D_acl) debug_printf_indent("%s: condition test deferred in %s\n", verbs[acl->verb], acl_name); - if (basic_errno != ERRNO_CALLOUTDEFER) - { - if (search_error_message != NULL && *search_error_message != 0) - *log_msgptr = search_error_message; - if (smtp_return_error_details) acl_temp_details = TRUE; - } - else - { - acl_temp_details = TRUE; - } - if (acl->verb != ACL_WARN) return DEFER; - break; + HDEBUG(D_acl) debug_printf_indent("%s: condition test deferred in %s\n", + verbs[acl->verb], acl_name); + if (basic_errno != ERRNO_CALLOUTDEFER) + { + if (search_error_message != NULL && *search_error_message != 0) + *log_msgptr = search_error_message; + if (smtp_return_error_details) f.acl_temp_details = TRUE; + } + else + f.acl_temp_details = TRUE; + if (acl->verb != ACL_WARN) return DEFER; + break; default: /* Paranoia */ case ERROR: - HDEBUG(D_acl) debug_printf_indent("%s: condition test error in %s\n", verbs[acl->verb], acl_name); - return ERROR; + HDEBUG(D_acl) debug_printf_indent("%s: condition test error in %s\n", + verbs[acl->verb], acl_name); + return ERROR; case OK: - HDEBUG(D_acl) debug_printf_indent("%s: condition test succeeded in %s\n", - verbs[acl->verb], acl_name); - break; + HDEBUG(D_acl) debug_printf_indent("%s: condition test succeeded in %s\n", + verbs[acl->verb], acl_name); + break; case FAIL: - HDEBUG(D_acl) debug_printf_indent("%s: condition test failed in %s\n", verbs[acl->verb], acl_name); - break; + HDEBUG(D_acl) debug_printf_indent("%s: condition test failed in %s\n", + verbs[acl->verb], acl_name); + break; /* DISCARD and DROP can happen only from a nested ACL condition, and DISCARD can happen only for an "accept" or "discard" verb. */ case DISCARD: - HDEBUG(D_acl) debug_printf_indent("%s: condition test yielded \"discard\" in %s\n", - verbs[acl->verb], acl_name); - break; + HDEBUG(D_acl) debug_printf_indent("%s: condition test yielded \"discard\" in %s\n", + verbs[acl->verb], acl_name); + break; case FAIL_DROP: - HDEBUG(D_acl) debug_printf_indent("%s: condition test yielded \"drop\" in %s\n", - verbs[acl->verb], acl_name); - break; + HDEBUG(D_acl) debug_printf_indent("%s: condition test yielded \"drop\" in %s\n", + verbs[acl->verb], acl_name); + break; } /* At this point, cond for most verbs is either OK or FAIL or (as a result of @@ -4129,84 +4125,85 @@ while (acl != NULL) switch(acl->verb) { case ACL_ACCEPT: - if (cond == OK || cond == DISCARD) - { - HDEBUG(D_acl) debug_printf_indent("end of %s: ACCEPT\n", acl_name); - return cond; - } - if (endpass_seen) - { - HDEBUG(D_acl) debug_printf_indent("accept: endpass encountered - denying access\n"); - return cond; - } - break; + if (cond == OK || cond == DISCARD) + { + HDEBUG(D_acl) debug_printf_indent("end of %s: ACCEPT\n", acl_name); + return cond; + } + if (endpass_seen) + { + HDEBUG(D_acl) debug_printf_indent("accept: endpass encountered - denying access\n"); + return cond; + } + break; case ACL_DEFER: - if (cond == OK) - { - HDEBUG(D_acl) debug_printf_indent("end of %s: DEFER\n", acl_name); - if (acl_quit_check) goto badquit; - acl_temp_details = TRUE; - return DEFER; - } - break; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf_indent("end of %s: DEFER\n", acl_name); + if (acl_quit_check) goto badquit; + f.acl_temp_details = TRUE; + return DEFER; + } + break; case ACL_DENY: - if (cond == OK) - { - HDEBUG(D_acl) debug_printf_indent("end of %s: DENY\n", acl_name); - if (acl_quit_check) goto badquit; - return FAIL; - } - break; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf_indent("end of %s: DENY\n", acl_name); + if (acl_quit_check) goto badquit; + return FAIL; + } + break; case ACL_DISCARD: - if (cond == OK || cond == DISCARD) - { - HDEBUG(D_acl) debug_printf_indent("end of %s: DISCARD\n", acl_name); - if (acl_quit_check) goto badquit; - return DISCARD; - } - if (endpass_seen) - { - HDEBUG(D_acl) debug_printf_indent("discard: endpass encountered - denying access\n"); - return cond; - } - break; + if (cond == OK || cond == DISCARD) + { + HDEBUG(D_acl) debug_printf_indent("end of %s: DISCARD\n", acl_name); + if (acl_quit_check) goto badquit; + return DISCARD; + } + if (endpass_seen) + { + HDEBUG(D_acl) + debug_printf_indent("discard: endpass encountered - denying access\n"); + return cond; + } + break; case ACL_DROP: - if (cond == OK) - { - HDEBUG(D_acl) debug_printf_indent("end of %s: DROP\n", acl_name); - if (acl_quit_check) goto badquit; - return FAIL_DROP; - } - break; + if (cond == OK) + { + HDEBUG(D_acl) debug_printf_indent("end of %s: DROP\n", acl_name); + if (acl_quit_check) goto badquit; + return FAIL_DROP; + } + break; case ACL_REQUIRE: - if (cond != OK) - { - HDEBUG(D_acl) debug_printf_indent("end of %s: not OK\n", acl_name); - if (acl_quit_check) goto badquit; - return cond; - } - break; + if (cond != OK) + { + HDEBUG(D_acl) debug_printf_indent("end of %s: not OK\n", acl_name); + if (acl_quit_check) goto badquit; + return cond; + } + break; case ACL_WARN: - if (cond == OK) - acl_warn(where, *user_msgptr, *log_msgptr); - else if (cond == DEFER && LOGGING(acl_warn_skipped)) - log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: " - "condition test deferred%s%s", host_and_ident(TRUE), - (*log_msgptr == NULL)? US"" : US": ", - (*log_msgptr == NULL)? US"" : *log_msgptr); - *log_msgptr = *user_msgptr = NULL; /* In case implicit DENY follows */ - break; + if (cond == OK) + acl_warn(where, *user_msgptr, *log_msgptr); + else if (cond == DEFER && LOGGING(acl_warn_skipped)) + log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: " + "condition test deferred%s%s", host_and_ident(TRUE), + (*log_msgptr == NULL)? US"" : US": ", + (*log_msgptr == NULL)? US"" : *log_msgptr); + *log_msgptr = *user_msgptr = NULL; /* In case implicit DENY follows */ + break; default: - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal ACL error: unknown verb %d", - acl->verb); - break; + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal ACL error: unknown verb %d", + acl->verb); + break; } /* Pass to the next ACL item */ @@ -4279,10 +4276,10 @@ for (i = 0; i < 9; i++) acl_arg[i] = sav_arg[i]; return ret; bad: -if (expand_string_forcedfail) return ERROR; +if (f.expand_string_forcedfail) return ERROR; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", tmp, expand_string_message); -return search_find_defer?DEFER:ERROR; +return f.search_find_defer ? DEFER : ERROR; } @@ -4413,7 +4410,7 @@ switch (where) case ACL_WHERE_PRDR: #endif - if (host_checking_callout) /* -bhc mode */ + if (f.host_checking_callout) /* -bhc mode */ cancel_cutthrough_connection(TRUE, US"host-checking mode"); else if ( rc == OK @@ -4429,7 +4426,7 @@ switch (where) while (*s) s++; do --s; while (!isdigit(*s)); if (*--s && isdigit(*s) && *--s && isdigit(*s)) *user_msgptr = s; - acl_temp_details = TRUE; + f.acl_temp_details = TRUE; } else { @@ -4468,7 +4465,7 @@ switch (where) } deliver_domain = deliver_localpart = deliver_address_data = - sender_address_data = NULL; + deliver_domain_data = sender_address_data = NULL; /* A DISCARD response is permitted only for message ACLs, excluding the PREDATA ACL, which is really in the middle of an SMTP command. */