ACLC_DKIM_SIGNER,
ACLC_DKIM_STATUS,
#endif
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
ACLC_DMARC_STATUS,
#endif
ACLC_DNSLISTS,
[ACLC_DKIM_SIGNER] = { US"dkim_signers", TRUE, FALSE, (unsigned int) ~ACL_BIT_DKIM },
[ACLC_DKIM_STATUS] = { US"dkim_status", TRUE, FALSE, (unsigned int) ~ACL_BIT_DKIM },
#endif
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
[ACLC_DMARC_STATUS] = { US"dmarc_status", TRUE, FALSE, (unsigned int) ~ACL_BIT_DATA },
#endif
#ifndef DISABLE_DKIM
CONTROL_DKIM_VERIFY,
#endif
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
CONTROL_DMARC_VERIFY,
CONTROL_DMARC_FORENSIC,
#endif
CONTROL_NO_PIPELINING,
CONTROL_QUEUE_ONLY,
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- CONTROL_REQUIRETLS,
-#endif
CONTROL_SUBMISSION,
CONTROL_SUPPRESS_LOCAL_FIXUPS,
#ifdef SUPPORT_I18N
},
#endif
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
[CONTROL_DMARC_VERIFY] =
{ US"dmarc_disable_verify", FALSE,
ACL_BIT_DATA | ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START
},
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-[CONTROL_REQUIRETLS] =
- { US"requiretls", FALSE,
- (unsigned)
- ~(ACL_BIT_MAIL | ACL_BIT_RCPT | ACL_BIT_PREDATA |
- ACL_BIT_DATA | ACL_BIT_MIME |
- ACL_BIT_NOTSMTP)
- },
-#endif
-
[CONTROL_SUBMISSION] =
{ US"submission", TRUE,
(unsigned)
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;
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);
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]);
acl_block *this = NULL;
acl_condition_block *cond;
acl_condition_block **condp = NULL;
-uschar *s;
+uschar * s;
*error = 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);
*error = string_sprintf("malformed ACL line \"%s\"", saveline);
return NULL;
}
- this = store_get(sizeof(acl_block));
+ this = store_get(sizeof(acl_block), FALSE);
*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 == '!')
return NULL;
}
- cond = store_get(sizeof(acl_condition_block));
+ cond = store_get(sizeof(acl_condition_block), FALSE);
cond->next = NULL;
cond->type = c;
cond->u.negated = negated;
if (!*hptr)
{
- header_line *h = store_get(sizeof(header_line));
+ /* The header_line struct itself is not tainted, though it points to
+ tainted data. */
+ header_line *h = store_get(sizeof(header_line), FALSE);
h->text = hdr;
h->next = NULL;
h->type = newtype;
fn_hdrs_added(void)
{
gstring * g = NULL;
-header_line * h;
-for (h = acl_added_headers; h; h = h->next)
+for (header_line * h = acl_added_headers; h; h = h->next)
{
int i = h->slen;
if (h->text[i-1] == '\n') i--;
/* 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);
/* 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. */
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 */
}
acl_verify_csa_address(dns_answer *dnsa, dns_scan *dnss, int reset,
uschar *target)
{
-dns_record *rr;
-dns_address *da;
+int rc = CSA_FAIL_NOADDR;
-BOOL target_found = FALSE;
-
-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. */
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! */
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;
}
tree_node *t;
const uschar *found;
int priority, weight, port;
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
dns_scan dnss;
dns_record *rr;
int rc, type;
if (string_is_ip_address(domain, NULL) != 0)
{
if (!dns_csa_use_reverse) return CSA_UNKNOWN;
- dns_build_reverse(domain, target);
- domain = target;
+ domain = dns_build_reverse(domain);
}
/* Find out if we've already done the CSA check for this domain. If we have,
t = tree_search(csa_cache, domain);
if (t != NULL) return t->data.val;
-t = store_get_perm(sizeof(tree_node) + Ustrlen(domain));
+t = store_get_perm(sizeof(tree_node) + Ustrlen(domain), is_tainted(domain));
Ustrcpy(t->name, domain);
(void)tree_insertnode(&csa_cache, t);
/* Now we are ready to do the actual DNS lookup(s). */
found = domain;
-switch (dns_special_lookup(&dnsa, domain, T_CSA, &found))
+switch (dns_special_lookup(dnsa, domain, T_CSA, &found))
{
/* If something bad happened (most commonly DNS_AGAIN), defer. */
/* Scan the reply for well-formed CSA SRV records. */
-for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
+for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
rr;
- rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) if (rr->type == T_SRV)
+ rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_SRV)
{
const uschar * p = rr->data;
client's IP address is listed as one of the SRV target addresses. Save the
target hostname then break to scan the additional data for its addresses. */
- (void)dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
+ (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, p,
(DN_EXPAND_ARG4_TYPE)target, sizeof(target));
DEBUG(D_acl) debug_printf_indent("CSA target is %s\n", target);
/* 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
because it does not fully support SRV records), we need to do another lookup
to obtain the target addresses; otherwise we have a definitive result. */
-rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ADDITIONAL, target);
+rc = acl_verify_csa_address(dnsa, &dnss, RESET_ADDITIONAL, target);
if (rc != CSA_FAIL_NOADDR) return t->data.val = rc;
/* The DNS lookup type corresponds to the IP version used by the client. */
lookup_dnssec_authenticated = NULL;
-switch (dns_lookup(&dnsa, target, type, NULL))
+switch (dns_lookup(dnsa, target, type, NULL))
{
/* If something bad happened (most commonly DNS_AGAIN), defer. */
/* If the query succeeded, scan the addresses and return the result. */
case DNS_SUCCEED:
- rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target);
+ rc = acl_verify_csa_address(dnsa, &dnss, RESET_ANSWERS, target);
if (rc != CSA_FAIL_NOADDR) return t->data.val = rc;
/* else fall through */
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"csa", VERIFY_CSA, ~0, FALSE, 0 },
+ /* name value where no-opt opt-sep */
+ { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, (unsigned)~0, FALSE, 0 },
+ { US"certificate", VERIFY_CERT, (unsigned)~0, TRUE, 0 },
+ { US"helo", VERIFY_HELO, (unsigned)~0, TRUE, 0 },
+ { US"csa", VERIFY_CSA, (unsigned)~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,
/* 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++
)
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;
- if ((rc = verify_check_notblind()) != OK)
+ 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;
+ }
+
+ if ((rc = verify_check_notblind(case_sensitive)) != OK)
{
- *log_msgptr = string_sprintf("bcc recipient detected");
+ *log_msgptr = US"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
/* 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;
{
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;
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, SVFMT_EXTEND|SVFMT_REBUFFER, format, ap);
va_end(ap);
-*log_msgptr = string_sprintf(
- "error in arguments to \"ratelimit\" condition: %s", buffer);
+
+gstring_release_unused(g);
+*log_msgptr = string_from_gstring(g);
return ERROR;
}
/* 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. */
-if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE)))
+if (!(dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE, TRUE)))
{
store_pool = old_pool;
sender_rate = NULL;
/* No Bloom filter. This basic ratelimit block is initialized below. */
HDEBUG(D_acl) debug_printf_indent("ratelimit creating new rate data block\n");
dbdb_size = sizeof(*dbd);
- dbdb = store_get(dbdb_size);
+ dbdb = store_get(dbdb_size, FALSE); /* not tainted */
}
else
{
extra = (int)limit * 2 - sizeof(dbdb->bloom);
if (extra < 0) extra = 0;
dbdb_size = sizeof(*dbdb) + extra;
- dbdb = store_get(dbdb_size);
+ dbdb = store_get(dbdb_size, FALSE); /* not tainted */
dbdb->bloom_epoch = tv.tv_sec;
dbdb->bloom_size = sizeof(dbdb->bloom) + extra;
memset(dbdb->bloom, 0, dbdb->bloom_size);
dbfn_close(dbm);
-/* Store the result in the tree for future reference. */
+/* Store the result in the tree for future reference. Take the taint status
+from the key for consistency even though it's unlikely we'll ever expand this. */
-t = store_get(sizeof(tree_node) + Ustrlen(key));
+t = store_get(sizeof(tree_node) + Ustrlen(key), is_tainted(key));
t->data.ptr = dbd;
Ustrcpy(t->name, key);
(void)tree_insertnode(anchor, t);
}
/* Make a single-item host list. */
-h = store_get(sizeof(host_item));
+h = store_get(sizeof(host_item), FALSE);
memset(h, 0, sizeof(host_item));
h->name = hostname;
h->port = portnum;
break;
#endif
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
case CONTROL_DKIM_VERIFY:
f.dkim_disable_verify = TRUE;
- #ifdef EXPERIMENTAL_DMARC
+# ifdef SUPPORT_DMARC
/* Since DKIM was blocked, skip DMARC too */
f.dmarc_disable_verify = TRUE;
f.dmarc_enable_forensic = FALSE;
- #endif
+# endif
break;
- #endif
+#endif
- #ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
case CONTROL_DMARC_VERIFY:
f.dmarc_disable_verify = TRUE;
break;
case CONTROL_DMARC_FORENSIC:
f.dmarc_enable_forensic = TRUE;
break;
- #endif
+#endif
case CONTROL_DSCP:
if (*p == '/')
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;
}
cancel_cutthrough_connection(TRUE, US"queueing forced");
break;
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- case CONTROL_REQUIRETLS:
- tls_requiretls |= REQUIRETLS_MSG;
- break;
-#endif
case CONTROL_SUBMISSION:
originator_name = US"";
f.submission_mode = TRUE;
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;
}
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;
break;
#endif
- #ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
case ACLC_DMARC_STATUS:
if (!f.dmarc_has_been_checked)
dmarc_process();
rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS),
&arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
break;
- #endif
+#endif
case ACLC_DNSLISTS:
rc = verify_check_dnsbl(where, &arg, log_msgptr);
(sender_host_address == NULL)? US"" : sender_host_address,
CUSS &host_data);
if (rc == DEFER) *log_msgptr = search_error_message;
- if (host_data) host_data = string_copy_malloc(host_data);
+ if (host_data) host_data = string_copy_perm(host_data, TRUE);
break;
case ACLC_LOCAL_PARTS:
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;
{
/* 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;
"Directory separator not permitted in queue name: '%s'", arg);
return ERROR;
}
- queue_name = string_copy_malloc(arg);
+ queue_name = string_copy_perm(arg, FALSE);
break;
case ACLC_RATELIMIT:
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. */
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;
else if (*ss == '/')
{
struct stat statbuf;
- fd = Uopen(ss, O_RDONLY, 0);
- if (fd < 0)
+ if (is_tainted(ss))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to open tainted ACL file name \"%s\"", ss);
+ /* Avoid leaking info to an attacker */
+ *log_msgptr = US"internal configuration error";
+ return ERROR;
+ }
+ if ((fd = Uopen(ss, O_RDONLY, 0)) < 0)
{
*log_msgptr = string_sprintf("failed to open ACL file \"%s\": %s", ss,
strerror(errno));
return ERROR;
}
-
if (fstat(fd, &statbuf) != 0)
{
*log_msgptr = string_sprintf("failed to fstat ACL file \"%s\": %s", ss,
return ERROR;
}
- acl_text = store_get(statbuf.st_size + 1);
+ /* If the string being used as a filename is tainted, so is the file content */
+ acl_text = store_get(statbuf.st_size + 1, is_tainted(ss));
acl_text_end = acl_text + statbuf.st_size + 1;
if (read(fd, acl_text, statbuf.st_size) != statbuf.st_size)
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));
+ tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss), is_tainted(ss));
Ustrcpy(t->name, ss);
t->data.ptr = acl;
(void)tree_insertnode(&acl_anchor, t);
/* 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;
*log_msgptr = *user_msgptr = NULL;
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. */
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) f.acl_temp_details = TRUE;
- }
- else
- f.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
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;
- f.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 */
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));
+ node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
Ustrcpy(node->name, name);
(void)tree_insertnode(root, node);
}
acl_var_write(uschar *name, uschar *value, void *ctx)
{
FILE *f = (FILE *)ctx;
+if (is_tainted(value)) putc('-', f);
fprintf(f, "-acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
}