X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/560e71cc545182bb51a7d038ac40eebac8e045aa..6741531cf79cbd3b403b8a52ed07635fe543cd3a:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index 396e29965..fb8b75bc7 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 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -22,13 +22,14 @@ enum { ACL_ACCEPT, ACL_DEFER, ACL_DENY, ACL_DISCARD, ACL_DROP, ACL_REQUIRE, /* ACL verbs */ static uschar *verbs[] = { - US"accept", - US"defer", - US"deny", - US"discard", - US"drop", - US"require", - US"warn" }; + [ACL_ACCEPT] = US"accept", + [ACL_DEFER] = US"defer", + [ACL_DENY] = US"deny", + [ACL_DISCARD] = US"discard", + [ACL_DROP] = US"drop", + [ACL_REQUIRE] = US"require", + [ACL_WARN] = US"warn" +}; /* For each verb, the conditions for which "message" or "log_message" are used are held as a bitmap. This is to avoid expanding the strings unnecessarily. For @@ -36,13 +37,13 @@ are held as a bitmap. This is to avoid expanding the strings unnecessarily. For the code. */ static int msgcond[] = { - (1<u.varname = string_copyn(s, 18); + s = endptr; + while (isspace(*s)) s++; + } + else +#endif { uschar *endptr; @@ -993,9 +1047,7 @@ for (p = q; *p != 0; ) uschar * fn_hdrs_added(void) { -uschar * ret = NULL; -int size = 0; -int ptr = 0; +gstring * g = NULL; header_line * h = acl_added_headers; uschar * s; uschar * cp; @@ -1010,18 +1062,19 @@ do if (cp[1] == '\0') break; /* contains embedded newline; needs doubling */ - ret = string_catn(ret, &size, &ptr, s, cp-s+1); - ret = string_catn(ret, &size, &ptr, US"\n", 1); + g = string_catn(g, s, cp-s+1); + g = string_catn(g, US"\n", 1); s = cp+1; } /* last bit of header */ - ret = string_catn(ret, &size, &ptr, s, cp-s+1); /* newline-sep list */ +/*XXX could we use add_listele? */ + g = string_catn(g, s, cp-s+1); /* newline-sep list */ } while((h = h->next)); -ret[ptr-1] = '\0'; /* overwrite last newline */ -return ret; +g->s[g->ptr - 1] = '\0'; /* overwrite last newline */ +return g->s; } @@ -1097,7 +1150,7 @@ if (log_message != NULL && log_message != user_message) int length = Ustrlen(text) + 1; log_write(0, LOG_MAIN, "%s", text); logged = store_malloc(sizeof(string_item) + length); - logged->text = (uschar *)logged + sizeof(string_item); + logged->text = US logged + sizeof(string_item); memcpy(logged->text, text, length); logged->next = acl_warn_logged; acl_warn_logged = logged; @@ -1167,7 +1220,7 @@ if (host_lookup_failed) /* Need to do a lookup */ HDEBUG(D_acl) - debug_printf("looking up host name to force name/address consistency check\n"); + debug_printf_indent("looking up host name to force name/address consistency check\n"); if ((rc = host_name_lookup()) != OK) { @@ -1241,7 +1294,7 @@ for (rr = dns_next_rr(dnsa, dnss, reset); { /* If the client IP address matches the target IP address, it's good! */ - DEBUG(D_acl) debug_printf("CSA target address is %s\n", da->address); + DEBUG(D_acl) debug_printf_indent("CSA target address is %s\n", da->address); if (strcmpic(sender_host_address, da->address) == 0) return CSA_OK; } @@ -1372,7 +1425,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); GETSHORT(port, p); DEBUG(D_acl) - debug_printf("CSA priority=%d weight=%d port=%d\n", priority, weight, port); + debug_printf_indent("CSA priority=%d weight=%d port=%d\n", priority, weight, port); /* Check the CSA version number */ @@ -1404,7 +1457,7 @@ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); (void)dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, (DN_EXPAND_ARG4_TYPE)target, sizeof(target)); - DEBUG(D_acl) debug_printf("CSA target is %s\n", target); + DEBUG(D_acl) debug_printf_indent("CSA target is %s\n", target); break; } @@ -1472,7 +1525,7 @@ switch (dns_lookup(&dnsa, target, type, NULL)) enum { VERIFY_REV_HOST_LKUP, VERIFY_CERT, VERIFY_HELO, VERIFY_CSA, VERIFY_HDR_SYNTAX, VERIFY_NOT_BLIND, VERIFY_HDR_SNDR, VERIFY_SNDR, VERIFY_RCPT, - VERIFY_HDR_NAMES_ASCII + VERIFY_HDR_NAMES_ASCII, VERIFY_ARC }; typedef struct { uschar * name; @@ -1482,25 +1535,29 @@ typedef struct { unsigned alt_opt_sep; /* >0 Non-/ option separator (custom parser) */ } verify_type_t; static verify_type_t verify_type_list[] = { + /* name value where no-opt opt-sep */ { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, FALSE, 0 }, - { US"certificate", VERIFY_CERT, ~0, TRUE, 0 }, - { US"helo", VERIFY_HELO, ~0, TRUE, 0 }, + { US"certificate", VERIFY_CERT, ~0, TRUE, 0 }, + { US"helo", VERIFY_HELO, ~0, TRUE, 0 }, { US"csa", VERIFY_CSA, ~0, FALSE, 0 }, - { US"header_syntax", VERIFY_HDR_SYNTAX, (1<alt_opt_sep ? strncmpic(ss, vp->name, vp->alt_opt_sep) == 0 : strcmpic (ss, vp->name) == 0) break; -if ((char *)vp >= (char *)verify_type_list + sizeof(verify_type_list)) +if (CS vp >= CS verify_type_list + sizeof(verify_type_list)) goto BAD_VERIFY; -if (vp->no_options && slash != NULL) +if (vp->no_options && slash) { *log_msgptr = string_sprintf("unexpected '/' found in \"%s\" " "(this verify item has no options)", arg); return ERROR; } -if (!(vp->where_allowed & (1<where_allowed & BIT(where))) { - *log_msgptr = string_sprintf("cannot verify %s in ACL for %s", vp->name, acl_wherenames[where]); + *log_msgptr = string_sprintf("cannot verify %s in ACL for %s", + vp->name, acl_wherenames[where]); return ERROR; } switch(vp->value) @@ -1633,11 +1693,11 @@ switch(vp->value) *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", csa_reason_string[rc]); csa_status = csa_status_string[rc]; - DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); + DEBUG(D_acl) debug_printf_indent("CSA result %s\n", csa_status); return csa_return_code[rc]; case VERIFY_HDR_SYNTAX: - /* Check that all relevant header lines have the correct syntax. If there is + /* 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 send out full details. (But a "message" setting on the ACL can override, as always). */ @@ -1744,8 +1804,7 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) uschar buffer[256]; while (isspace(*sublist)) sublist++; - while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer))) - != NULL) + while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))) { callout_opt_t * op; double period = 1.0F; @@ -1767,15 +1826,11 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) } while (isspace(*opt)) opt++; } - if (op->timeval) + if (op->timeval && (period = readconf_readtime(opt, 0, FALSE)) < 0) { - period = readconf_readtime(opt, 0, FALSE); - if (period < 0) - { - *log_msgptr = string_sprintf("bad time value in ACL condition " - "\"verify %s\"", arg); - return ERROR; - } + *log_msgptr = string_sprintf("bad time value in ACL condition " + "\"verify %s\"", arg); + return ERROR; } switch(op->value) @@ -1888,7 +1943,7 @@ else if (verify_sender_address) rc = sender_vaddr->special_action; *basic_errno = sender_vaddr->basic_errno; } - HDEBUG(D_acl) debug_printf("using cached sender verify result\n"); + HDEBUG(D_acl) debug_printf_indent("using cached sender verify result\n"); } /* Do a new verification, and cache the result. The cache is used to avoid @@ -1935,7 +1990,7 @@ else if (verify_sender_address) rc = verify_address(sender_vaddr, NULL, verify_options, callout, callout_overall, callout_connect, se_mailfrom, pm_mailfrom, &routed); - HDEBUG(D_acl) debug_printf("----------- end verify ------------\n"); + HDEBUG(D_acl) debug_printf_indent("----------- end verify ------------\n"); if (rc != OK) *basic_errno = sender_vaddr->basic_errno; @@ -1943,10 +1998,10 @@ else if (verify_sender_address) DEBUG(D_acl) { if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0) - debug_printf("sender %s verified ok as %s\n", + debug_printf_indent("sender %s verified ok as %s\n", verify_sender_address, sender_vaddr->address); else - debug_printf("sender %s verified ok\n", + debug_printf_indent("sender %s verified ok\n", verify_sender_address); } } @@ -1988,7 +2043,7 @@ else addr2 = *addr; rc = verify_address(&addr2, NULL, verify_options|vopt_is_recipient, callout, callout_overall, callout_connect, se_mailfrom, pm_mailfrom, NULL); - HDEBUG(D_acl) debug_printf("----------- end verify ------------\n"); + HDEBUG(D_acl) debug_printf_indent("----------- end verify ------------\n"); *basic_errno = addr2.basic_errno; *log_msgptr = addr2.message; @@ -2007,7 +2062,7 @@ else if (rc == DEFER && (defer_ok || (callout_defer_ok && *basic_errno == ERRNO_CALLOUTDEFER))) { - HDEBUG(D_acl) debug_printf("verify defer overridden by %s\n", + HDEBUG(D_acl) debug_printf_indent("verify defer overridden by %s\n", defer_ok? "defer_ok" : "callout_defer_ok"); rc = OK; } @@ -2306,7 +2361,7 @@ key = string_sprintf("%s/%s/%s%s", key); HDEBUG(D_acl) - debug_printf("ratelimit condition count=%.0f %.1f/%s\n", count, limit, key); + debug_printf_indent("ratelimit condition count=%.0f %.1f/%s\n", count, limit, key); /* See if we have already computed the rate by looking in the relevant tree. For per-connection rate limiting, store tree nodes and dbdata in the permanent @@ -2349,19 +2404,18 @@ if (t != NULL) store_pool = old_pool; sender_rate = string_sprintf("%.1f", dbd->rate); HDEBUG(D_acl) - debug_printf("ratelimit found pre-computed rate %s\n", sender_rate); + debug_printf_indent("ratelimit found pre-computed rate %s\n", sender_rate); return rc; } /* We aren't using a pre-computed rate, so get a previously recorded rate from the database, which will be updated and written back if required. */ -dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE); -if (dbm == NULL) +if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE))) { store_pool = old_pool; sender_rate = NULL; - HDEBUG(D_acl) debug_printf("ratelimit database not available\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit database not available\n"); *log_msgptr = US"ratelimit database not available"; return DEFER; } @@ -2373,7 +2427,7 @@ gettimeofday(&tv, NULL); if (dbdb != NULL) { /* Locate the basic ratelimit block inside the DB data. */ - HDEBUG(D_acl) debug_printf("ratelimit found key in database\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit found key in database\n"); dbd = &dbdb->dbd; /* Forget the old Bloom filter if it is too old, so that we count each @@ -2383,7 +2437,7 @@ if (dbdb != NULL) if(unique != NULL && tv.tv_sec > dbdb->bloom_epoch + period) { - HDEBUG(D_acl) debug_printf("ratelimit discarding old Bloom filter\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit discarding old Bloom filter\n"); dbdb = NULL; } @@ -2391,7 +2445,7 @@ if (dbdb != NULL) if(unique != NULL && dbdb_size < sizeof(*dbdb)) { - HDEBUG(D_acl) debug_printf("ratelimit discarding undersize Bloom filter\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit discarding undersize Bloom filter\n"); dbdb = NULL; } } @@ -2404,14 +2458,14 @@ if (dbdb == NULL) if (unique == NULL) { /* No Bloom filter. This basic ratelimit block is initialized below. */ - HDEBUG(D_acl) debug_printf("ratelimit creating new rate data block\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit creating new rate data block\n"); dbdb_size = sizeof(*dbd); dbdb = store_get(dbdb_size); } else { int extra; - HDEBUG(D_acl) debug_printf("ratelimit creating new Bloom filter\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit creating new Bloom filter\n"); /* See the long comment below for an explanation of the magic number 2. The filter has a minimum size in case the rate limit is very small; @@ -2495,7 +2549,7 @@ if (unique != NULL && !readonly) /* Scan the bits corresponding to this event. A zero bit means we have not seen it before. Ensure all bits are set to record this event. */ - HDEBUG(D_acl) debug_printf("ratelimit checking uniqueness of %s\n", unique); + HDEBUG(D_acl) debug_printf_indent("ratelimit checking uniqueness of %s\n", unique); seen = TRUE; for (n = 0; n < 8; n++, hash += hinc) @@ -2513,11 +2567,11 @@ if (unique != NULL && !readonly) if (seen) { - HDEBUG(D_acl) debug_printf("ratelimit event found in Bloom filter\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit event found in Bloom filter\n"); count = 0.0; } else - HDEBUG(D_acl) debug_printf("ratelimit event added to Bloom filter\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit event added to Bloom filter\n"); } /* If there was no previous ratelimit data block for this key, initialize @@ -2526,7 +2580,7 @@ is what would be computed by the code below for an infinite interval. */ if (dbd == NULL) { - HDEBUG(D_acl) debug_printf("ratelimit initializing new key's rate data\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit initializing new key's rate data\n"); dbd = &dbdb->dbd; dbd->time_stamp = tv.tv_sec; dbd->time_usec = tv.tv_usec; @@ -2627,11 +2681,11 @@ neither leaky nor strict are set, so we do not do any updates. */ if ((rc == FAIL && leaky) || strict) { dbfn_write(dbm, key, dbdb, dbdb_size); - HDEBUG(D_acl) debug_printf("ratelimit db updated\n"); + HDEBUG(D_acl) debug_printf_indent("ratelimit db updated\n"); } else { - HDEBUG(D_acl) debug_printf("ratelimit db not updated: %s\n", + HDEBUG(D_acl) debug_printf_indent("ratelimit db not updated: %s\n", readonly? "readonly mode" : "over the limit, but leaky"); } @@ -2651,7 +2705,7 @@ store_pool = old_pool; sender_rate = string_sprintf("%.1f", dbd->rate); HDEBUG(D_acl) - debug_printf("ratelimit computed rate %s\n", sender_rate); + debug_printf_indent("ratelimit computed rate %s\n", sender_rate); return rc; } @@ -2729,10 +2783,11 @@ if (r == HOST_FIND_FAILED || r == HOST_FIND_AGAIN) } HDEBUG(D_acl) - debug_printf("udpsend [%s]:%d %s\n", h->address, portnum, arg); + debug_printf_indent("udpsend [%s]:%d %s\n", h->address, portnum, arg); +/*XXX this could better use sendto */ r = s = ip_connectedsocket(SOCK_DGRAM, h->address, portnum, portnum, - 1, NULL, &errstr); + 1, NULL, &errstr, NULL); if (r < 0) goto defer; len = Ustrlen(arg); r = send(s, arg, len, 0); @@ -2751,7 +2806,7 @@ if (r < len) } HDEBUG(D_acl) - debug_printf("udpsend %d bytes\n", r); + debug_printf_indent("udpsend %d bytes\n", r); return OK; @@ -2813,14 +2868,14 @@ for (; cb != NULL; cb = cb->next) if (cb->type == ACLC_MESSAGE) { - HDEBUG(D_acl) debug_printf(" message: %s\n", cb->arg); + HDEBUG(D_acl) debug_printf_indent(" message: %s\n", cb->arg); user_message = cb->arg; continue; } if (cb->type == ACLC_LOG_MESSAGE) { - HDEBUG(D_acl) debug_printf("l_message: %s\n", cb->arg); + HDEBUG(D_acl) debug_printf_indent("l_message: %s\n", cb->arg); log_message = cb->arg; continue; } @@ -2853,14 +2908,25 @@ for (; cb != NULL; cb = cb->next) HDEBUG(D_acl) { int lhswidth = 0; - debug_printf("check %s%s %n", + debug_printf_indent("check %s%s %n", (!conditions[cb->type].is_modifier && cb->u.negated)? "!":"", conditions[cb->type].name, &lhswidth); if (cb->type == ACLC_SET) { - debug_printf("acl_%s ", cb->u.varname); - lhswidth += 5 + Ustrlen(cb->u.varname); +#ifndef DISABLE_DKIM + if ( Ustrcmp(cb->u.varname, "dkim_verify_status") == 0 + || Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0) + { + debug_printf("%s ", cb->u.varname); + lhswidth += 19; + } + else +#endif + { + debug_printf("acl_%s ", cb->u.varname); + lhswidth += 5 + Ustrlen(cb->u.varname); + } } debug_printf("= %s\n", cb->arg); @@ -2893,7 +2959,7 @@ for (; cb != NULL; cb = cb->next) "discard" verb. */ case ACLC_ACL: - rc = acl_check_wargs(where, addr, arg, level+1, user_msgptr, log_msgptr); + rc = acl_check_wargs(where, addr, arg, user_msgptr, log_msgptr); if (rc == DISCARD && verb != ACL_ACCEPT && verb != ACL_DISCARD) { *log_msgptr = string_sprintf("nested ACL returned \"discard\" for " @@ -2998,7 +3064,7 @@ for (; cb != NULL; cb = cb->next) if (af < 0) { HDEBUG(D_acl) - debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n", + debug_printf_indent("smtp input is probably not a socket [%s], not setting DSCP\n", strerror(errno)); break; } @@ -3006,12 +3072,12 @@ for (; cb != NULL; cb = cb->next) { if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) { - HDEBUG(D_acl) debug_printf("failed to set input DSCP[%s]: %s\n", + HDEBUG(D_acl) debug_printf_indent("failed to set input DSCP[%s]: %s\n", p+1, strerror(errno)); } else { - HDEBUG(D_acl) debug_printf("set input DSCP to \"%s\"\n", p+1); + HDEBUG(D_acl) debug_printf_indent("set input DSCP to \"%s\"\n", p+1); } } else @@ -3069,7 +3135,7 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_FAKEREJECT: - cancel_cutthrough_connection("fakereject"); + cancel_cutthrough_connection(TRUE, US"fakereject"); case CONTROL_FAKEDEFER: fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; if (*p == '/') @@ -3100,12 +3166,12 @@ for (; cb != NULL; cb = cb->next) *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); return ERROR; } - cancel_cutthrough_connection("item frozen"); + cancel_cutthrough_connection(TRUE, US"item frozen"); break; case CONTROL_QUEUE_ONLY: queue_only_policy = TRUE; - cancel_cutthrough_connection("queueing forced"); + cancel_cutthrough_connection(TRUE, US"queueing forced"); break; case CONTROL_SUBMISSION: @@ -3186,6 +3252,8 @@ for (; cb != NULL; cb = cb->next) break; case CONTROL_CUTTHROUGH_DELIVERY: + { + uschar * ignored = NULL; #ifndef DISABLE_PRDR if (prdr_requested) #else @@ -3194,20 +3262,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"; + ignored = US"frozen"; else if (queue_only_policy) - *log_msgptr = US"queue-only"; + 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; @@ -3222,12 +3290,14 @@ for (; cb != NULL; cb = cb->next) p = pp; } } - break; + else + 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: @@ -3306,12 +3376,12 @@ for (; cb != NULL; cb = cb->next) } else { - HDEBUG(D_acl) debug_printf("delay modifier requests %d-second delay\n", + HDEBUG(D_acl) debug_printf_indent("delay modifier requests %d-second delay\n", delay); if (host_checking) { HDEBUG(D_acl) - debug_printf("delay skipped in -bh checking mode\n"); + debug_printf_indent("delay skipped in -bh checking mode\n"); } /* NOTE 1: Remember that we may be @@ -3339,7 +3409,7 @@ for (; cb != NULL; cb = cb->next) n = 1; } if (poll(&p, n, delay*1000) > 0) - HDEBUG(D_acl) debug_printf("delay cancelled by peer close\n"); + 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 @@ -3362,7 +3432,7 @@ for (; cb != NULL; cb = cb->next) #ifndef DISABLE_DKIM case ACLC_DKIM_SIGNER: - if (dkim_cur_signer != NULL) + if (dkim_cur_signer) rc = match_isinlist(dkim_cur_signer, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); else @@ -3370,7 +3440,7 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_DKIM_STATUS: - rc = match_isinlist(dkim_exim_expand_query(DKIM_VERIFY_STATUS), + rc = match_isinlist(dkim_verify_status, &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); break; #endif @@ -3526,6 +3596,12 @@ for (; cb != NULL; cb = cb->next) #endif case ACLC_QUEUE: + if (Ustrchr(arg, '/')) + { + *log_msgptr = string_sprintf( + "Directory separator not permitted in queue name: '%s'", arg); + return ERROR; + } queue_name = string_copy_malloc(arg); break; @@ -3534,7 +3610,7 @@ for (; cb != NULL; cb = cb->next) break; case ACLC_RECIPIENTS: - rc = match_address_list((const uschar *)addr->address, TRUE, TRUE, &arg, NULL, -1, 0, + rc = match_address_list(CUS addr->address, TRUE, TRUE, &arg, NULL, -1, 0, CUSS &recipient_data); break; @@ -3552,14 +3628,14 @@ for (; cb != NULL; cb = cb->next) { uschar *sdomain; sdomain = Ustrrchr(sender_address, '@'); - sdomain = (sdomain == NULL)? US"" : sdomain + 1; + sdomain = sdomain ? sdomain + 1 : US""; rc = match_isinlist(sdomain, &arg, 0, &domainlist_anchor, sender_domain_cache, MCL_DOMAIN, TRUE, NULL); } break; case ACLC_SENDERS: - rc = match_address_list((const uschar *)sender_address, TRUE, TRUE, &arg, + rc = match_address_list(CUS sender_address, TRUE, TRUE, &arg, sender_address_cache, -1, 0, CUSS &sender_data); break; @@ -3569,45 +3645,50 @@ for (; cb != NULL; cb = cb->next) { int old_pool = store_pool; if ( cb->u.varname[0] == 'c' +#ifndef DISABLE_DKIM + || cb->u.varname[0] == 'd' +#endif #ifndef DISABLE_EVENT || event_name /* An event is being delivered */ #endif ) store_pool = POOL_PERM; - acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); +#ifndef DISABLE_DKIM /* Overwriteable dkim result variables */ + if (Ustrcmp(cb->u.varname, "dkim_verify_status") == 0) + dkim_verify_status = string_copy(arg); + else if (Ustrcmp(cb->u.varname, "dkim_verify_reason") == 0) + dkim_verify_reason = string_copy(arg); + else +#endif + acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); store_pool = old_pool; } break; - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN case ACLC_SPAM: { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); - /* Run the spam backend. */ + rc = spam(CUSS &ss); /* Modify return code based upon the existence of options. */ - 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 && rc == DEFER) - { - /* FAIL so that the message is passed to the next ACL */ - rc = FAIL; - } - } + rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } break; - #endif +#endif - #ifdef EXPERIMENTAL_SPF +#ifdef SUPPORT_SPF case ACLC_SPF: rc = spf_process(&arg, sender_address, SPF_PROCESS_NORMAL); break; case ACLC_SPF_GUESS: rc = spf_process(&arg, sender_address, SPF_PROCESS_GUESS); break; - #endif +#endif case ACLC_UDPSEND: rc = acl_udpsend(arg, log_msgptr); @@ -3660,7 +3741,7 @@ present. */ if (*epp && rc == OK) user_message = NULL; -if (((1< DENY - level the nesting level user_msgptr where to put a user error (for SMTP response) log_msgptr where to put a logging message (not for SMTP response) @@ -3855,7 +3935,7 @@ Returns: OK access is granted */ static int -acl_check_internal(int where, address_item *addr, uschar *s, int level, +acl_check_internal(int where, address_item *addr, uschar *s, uschar **user_msgptr, uschar **log_msgptr) { int fd = -1; @@ -3865,25 +3945,24 @@ uschar *ss; /* Catch configuration loops */ -if (level > 20) +if (acl_level > 20) { *log_msgptr = US"ACL nested too deep: possible loop"; return ERROR; } -if (s == NULL) +if (!s) { - HDEBUG(D_acl) debug_printf("ACL is NULL: implicit DENY\n"); + HDEBUG(D_acl) debug_printf_indent("ACL is NULL: implicit DENY\n"); return FAIL; } /* At top level, we expand the incoming string. At lower levels, it has already been expanded as part of condition processing. */ -if (level == 0) +if (acl_level == 0) { - ss = expand_string(s); - if (ss == NULL) + if (!(ss = expand_string(s))) { if (expand_string_forcedfail) return OK; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", s, @@ -3893,7 +3972,7 @@ if (level == 0) } else ss = s; -while (isspace(*ss))ss++; +while (isspace(*ss)) ss++; /* If we can't find a named ACL, the default is to parse it as an inline one. (Unless it begins with a slash; non-existent files give rise to an error.) */ @@ -3914,11 +3993,11 @@ if (Ustrchr(ss, ' ') == NULL) acl = (acl_block *)(t->data.ptr); if (acl == NULL) { - HDEBUG(D_acl) debug_printf("ACL \"%s\" is empty: implicit DENY\n", ss); + HDEBUG(D_acl) debug_printf_indent("ACL \"%s\" is empty: implicit DENY\n", ss); return FAIL; } acl_name = string_sprintf("ACL \"%s\"", ss); - HDEBUG(D_acl) debug_printf("using ACL \"%s\"\n", ss); + HDEBUG(D_acl) debug_printf_indent("using ACL \"%s\"\n", ss); } else if (*ss == '/') @@ -3952,7 +4031,7 @@ if (Ustrchr(ss, ' ') == NULL) (void)close(fd); acl_name = string_sprintf("ACL \"%s\"", ss); - HDEBUG(D_acl) debug_printf("read ACL from file %s\n", ss); + HDEBUG(D_acl) debug_printf_indent("read ACL from file %s\n", ss); } } @@ -3983,19 +4062,19 @@ while (acl != NULL) int cond; int basic_errno = 0; BOOL endpass_seen = FALSE; - BOOL acl_quit_check = level == 0 + BOOL acl_quit_check = acl_level == 0 && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT); *log_msgptr = *user_msgptr = NULL; acl_temp_details = FALSE; - HDEBUG(D_acl) debug_printf("processing \"%s\"\n", verbs[acl->verb]); + HDEBUG(D_acl) debug_printf_indent("processing \"%s\"\n", verbs[acl->verb]); /* Clear out any search error message from a previous check before testing this condition. */ search_error_message = NULL; - cond = acl_check_condition(acl->verb, acl->condition, where, addr, level, + cond = acl_check_condition(acl->verb, acl->condition, where, addr, acl_level, &endpass_seen, user_msgptr, log_msgptr, &basic_errno); /* Handle special returns: DEFER causes a return except on a WARN verb; @@ -4004,7 +4083,7 @@ while (acl != NULL) switch (cond) { case DEFER: - HDEBUG(D_acl) debug_printf("%s: condition test deferred in %s\n", verbs[acl->verb], acl_name); + 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) @@ -4020,28 +4099,28 @@ while (acl != NULL) default: /* Paranoia */ case ERROR: - HDEBUG(D_acl) debug_printf("%s: condition test error in %s\n", verbs[acl->verb], acl_name); + 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("%s: condition test succeeded in %s\n", + 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("%s: condition test failed in %s\n", verbs[acl->verb], acl_name); + 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("%s: condition test yielded \"discard\" in %s\n", + 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("%s: condition test yielded \"drop\" in %s\n", + HDEBUG(D_acl) debug_printf_indent("%s: condition test yielded \"drop\" in %s\n", verbs[acl->verb], acl_name); break; } @@ -4055,12 +4134,12 @@ while (acl != NULL) case ACL_ACCEPT: if (cond == OK || cond == DISCARD) { - HDEBUG(D_acl) debug_printf("end of %s: ACCEPT\n", acl_name); + HDEBUG(D_acl) debug_printf_indent("end of %s: ACCEPT\n", acl_name); return cond; } if (endpass_seen) { - HDEBUG(D_acl) debug_printf("accept: endpass encountered - denying access\n"); + HDEBUG(D_acl) debug_printf_indent("accept: endpass encountered - denying access\n"); return cond; } break; @@ -4068,7 +4147,7 @@ while (acl != NULL) case ACL_DEFER: if (cond == OK) { - HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name); + 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; @@ -4078,7 +4157,7 @@ while (acl != NULL) case ACL_DENY: if (cond == OK) { - HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name); + HDEBUG(D_acl) debug_printf_indent("end of %s: DENY\n", acl_name); if (acl_quit_check) goto badquit; return FAIL; } @@ -4087,13 +4166,13 @@ while (acl != NULL) case ACL_DISCARD: if (cond == OK || cond == DISCARD) { - HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name); + 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("discard: endpass encountered - denying access\n"); + HDEBUG(D_acl) debug_printf_indent("discard: endpass encountered - denying access\n"); return cond; } break; @@ -4101,7 +4180,7 @@ while (acl != NULL) case ACL_DROP: if (cond == OK) { - HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name); + HDEBUG(D_acl) debug_printf_indent("end of %s: DROP\n", acl_name); if (acl_quit_check) goto badquit; return FAIL_DROP; } @@ -4110,7 +4189,7 @@ while (acl != NULL) case ACL_REQUIRE: if (cond != OK) { - HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name); + HDEBUG(D_acl) debug_printf_indent("end of %s: not OK\n", acl_name); if (acl_quit_check) goto badquit; return cond; } @@ -4140,7 +4219,7 @@ while (acl != NULL) /* We have reached the end of the ACL. This is an implicit DENY. */ -HDEBUG(D_acl) debug_printf("end of %s: implicit DENY\n", acl_name); +HDEBUG(D_acl) debug_printf_indent("end of %s: implicit DENY\n", acl_name); return FAIL; badquit: @@ -4156,7 +4235,7 @@ badquit: the name of an ACL followed optionally by up to 9 space-separated arguments. The name and args are separately expanded. Args go into $acl_arg globals. */ static int -acl_check_wargs(int where, address_item *addr, const uschar *s, int level, +acl_check_wargs(int where, address_item *addr, const uschar *s, uschar **user_msgptr, uschar **log_msgptr) { uschar * tmp; @@ -4194,7 +4273,9 @@ while (i < 9) acl_arg[i++] = NULL; } -ret = acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr); +acl_level++; +ret = acl_check_internal(where, addr, name, user_msgptr, log_msgptr); +acl_level--; acl_narg = sav_narg; for (i = 0; i < 9; i++) acl_arg[i] = sav_arg[i]; @@ -4219,6 +4300,7 @@ acl_eval(int where, uschar *s, uschar **user_msgptr, uschar **log_msgptr) { address_item adb; address_item *addr = NULL; +int rc; *user_msgptr = *log_msgptr = NULL; sender_verified_failed = NULL; @@ -4236,7 +4318,10 @@ if (where == ACL_WHERE_RCPT) addr->lc_local_part = deliver_localpart; } -return acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr); +acl_level++; +rc = acl_check_internal(where, addr, s, user_msgptr, log_msgptr); +acl_level--; +return rc; } @@ -4300,7 +4385,9 @@ if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY) } acl_where = where; -rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr); +acl_level = 0; +rc = acl_check_internal(where, addr, s, user_msgptr, log_msgptr); +acl_level = 0; acl_where = ACL_WHERE_UNKNOWN; /* Cutthrough - if requested, @@ -4328,41 +4415,56 @@ switch (where) #ifndef DISABLE_PRDR case ACL_WHERE_PRDR: #endif + if (host_checking_callout) /* -bhc mode */ - cancel_cutthrough_connection("host-checking mode"); + cancel_cutthrough_connection(TRUE, US"host-checking mode"); else if ( rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt - && (rc = open_cutthrough_connection(addr)) == DEFER ) - if (cutthrough.defer_pass) - { - uschar * s = addr->message; - /* Horrid kludge to recover target's SMTP message */ - while (*s) s++; - do --s; while (!isdigit(*s)); - if (*--s && isdigit(*s) && *--s && isdigit(*s)) *user_msgptr = s; - acl_temp_details = TRUE; - } + { + if ((rc = open_cutthrough_connection(addr)) == DEFER) + if (cutthrough.defer_pass) + { + uschar * s = addr->message; + /* Horrid kludge to recover target's SMTP message */ + while (*s) s++; + do --s; while (!isdigit(*s)); + if (*--s && isdigit(*s) && *--s && isdigit(*s)) *user_msgptr = s; + acl_temp_details = TRUE; + } else - { - HDEBUG(D_acl) debug_printf("cutthrough defer; will spool\n"); - rc = OK; - } + { + HDEBUG(D_acl) debug_printf_indent("cutthrough defer; will spool\n"); + rc = OK; + } + } + else HDEBUG(D_acl) if (cutthrough.delivery) + if (rcpt_count <= cutthrough.nrcpt) + debug_printf_indent("ignore cutthrough request; nonfirst message\n"); + else if (rc != OK) + debug_printf_indent("ignore cutthrough request; ACL did not accept\n"); break; case ACL_WHERE_PREDATA: if (rc == OK) cutthrough_predata(); else - cancel_cutthrough_connection("predata acl not ok"); + cancel_cutthrough_connection(TRUE, US"predata acl not ok"); break; case ACL_WHERE_QUIT: case ACL_WHERE_NOTQUIT: - cancel_cutthrough_connection("quit or notquit"); + /* Drop cutthrough conns, and drop heldopen verify conns if + the previous was not DATA */ + { + uschar prev = smtp_connection_had[smtp_ch_index-2]; + BOOL dropverify = !(prev == SCH_DATA || prev == SCH_BDAT); + + cancel_cutthrough_connection(dropverify, US"quit or conndrop"); break; + } default: break; @@ -4419,12 +4521,10 @@ Returns the pointer to variable's tree node */ tree_node * -acl_var_create(uschar *name) +acl_var_create(uschar * name) { -tree_node *node, **root; -root = (name[0] == 'c')? &acl_var_c : &acl_var_m; -node = tree_search(*root, name); -if (node == NULL) +tree_node * node, ** root = name[0] == 'c' ? &acl_var_c : &acl_var_m; +if (!(node = tree_search(*root, name))) { node = store_get(sizeof(tree_node) + Ustrlen(name)); Ustrcpy(node->name, name);