* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2012 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for handling Access Control Lists (ACLs) */
/* ACL verbs */
-static uschar *verbs[] =
- { US"accept", US"defer", US"deny", US"discard", US"drop", US"require",
+static uschar *verbs[] = {
+ US"accept",
+ US"defer",
+ US"deny",
+ US"discard",
+ US"drop",
+ US"require",
US"warn" };
/* For each verb, the conditions for which "message" or "log_message" are used
#ifndef DISABLE_DKIM
ACLC_DKIM_SIGNER,
ACLC_DKIM_STATUS,
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ ACLC_DMARC_STATUS,
#endif
ACLC_DNSLISTS,
ACLC_DOMAINS,
ACLC_SPF,
ACLC_SPF_GUESS,
#endif
+ ACLC_UDPSEND,
ACLC_VERIFY };
/* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
#ifndef DISABLE_DKIM
US"dkim_signers",
US"dkim_status",
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ US"dmarc_status",
#endif
US"dnslists",
US"domains",
US"spf",
US"spf_guess",
#endif
+ US"udpsend",
US"verify" };
#ifndef DISABLE_DKIM
CONTROL_DKIM_VERIFY,
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ CONTROL_DMARC_VERIFY,
+ CONTROL_DMARC_FORENSIC,
+ #endif
CONTROL_DSCP,
CONTROL_ERROR,
CONTROL_CASEFUL_LOCAL_PART,
#ifndef DISABLE_DKIM
US"dkim_disable_verify",
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ US"dmarc_disable_verify",
+ US"dmarc_enable_forensic",
+ #endif
US"dscp",
US"error",
US"caseful_local_part",
#ifndef DISABLE_DKIM
TRUE, /* dkim_signers */
TRUE, /* dkim_status */
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ TRUE, /* dmarc_status */
#endif
TRUE, /* dnslists */
FALSE, /* domains */
TRUE, /* spf */
TRUE, /* spf_guess */
#endif
+ TRUE, /* udpsend */
TRUE /* verify */
};
#ifndef DISABLE_DKIM
FALSE, /* dkim_signers */
FALSE, /* dkim_status */
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ FALSE, /* dmarc_status */
#endif
FALSE, /* dnslists */
FALSE, /* domains */
FALSE, /* spf */
FALSE, /* spf_guess */
#endif
+ TRUE, /* udpsend */
FALSE /* verify */
};
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* add_header */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_DKIM)|
(1<<ACL_WHERE_NOTSMTP_START)),
(1<<ACL_WHERE_AUTH)| /* bmi_optin */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
#ifdef EXPERIMENTAL_DCC
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* dcc */
+ ~((1<<ACL_WHERE_DATA)| /* dcc */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
#ifdef WITH_CONTENT_SCAN
#ifdef WITH_OLD_DEMIME
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* demime */
+ ~((1<<ACL_WHERE_DATA)| /* demime */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
#ifndef DISABLE_DKIM
~(1<<ACL_WHERE_DKIM), /* dkim_status */
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ (unsigned int)
+ ~(1<<ACL_WHERE_DATA), /* dmarc_status */
+ #endif
+
(1<<ACL_WHERE_NOTSMTP)| /* dnslists */
(1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
- ~(1<<ACL_WHERE_RCPT), /* domains */
+ ~((1<<ACL_WHERE_RCPT) /* domains */
+ #ifndef DISABLE_PRDR
+ |(1<<ACL_WHERE_PRDR)
+ #endif
+ ),
(1<<ACL_WHERE_NOTSMTP)| /* encrypted */
(1<<ACL_WHERE_CONNECT)|
(1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
- ~(1<<ACL_WHERE_RCPT), /* local_parts */
+ ~((1<<ACL_WHERE_RCPT) /* local_parts */
+ #ifndef DISABLE_PRDR
+ |(1<<ACL_WHERE_PRDR)
+ #endif
+ ),
0, /* log_message */
#ifdef WITH_CONTENT_SCAN
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* malware */
+ ~((1<<ACL_WHERE_DATA)| /* malware */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
0, /* message */
#ifdef WITH_CONTENT_SCAN
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* regex */
+ ~((1<<ACL_WHERE_DATA)| /* regex */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_MIME)),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* remove_header */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_NOTSMTP_START)),
#ifdef WITH_CONTENT_SCAN
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* spam */
+ ~((1<<ACL_WHERE_DATA)| /* spam */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
#ifdef EXPERIMENTAL_SPF
(1<<ACL_WHERE_NOTSMTP_START),
#endif
+ 0, /* udpsend */
+
/* Certain types of verify are always allowed, so we let it through
always and check in the verify function itself */
#ifndef DISABLE_DKIM
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_NOTSMTP_START),
+ #endif
+
+ #ifdef EXPERIMENTAL_DMARC
+ (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_disable_verify */
+ (1<<ACL_WHERE_NOTSMTP_START),
+ (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_enable_forensic */
(1<<ACL_WHERE_NOTSMTP_START),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* freeze */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_MIME)),
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* queue_only */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_MIME)),
(unsigned int)
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* no_mbox_unspool */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_MIME)),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_MIME)),
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakereject */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_MIME)),
(1<<ACL_WHERE_NOTSMTP)| /* no_multiline */
{ US"debug", CONTROL_DEBUG, TRUE },
#ifndef DISABLE_DKIM
{ US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE },
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ { US"dmarc_disable_verify", CONTROL_DMARC_VERIFY, FALSE },
+ { US"dmarc_enable_forensic", CONTROL_DMARC_FORENSIC, FALSE },
#endif
{ US"dscp", CONTROL_DSCP, TRUE },
{ US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
+/*************************************************
+* List the added header lines *
+*************************************************/
+uschar *
+fn_hdrs_added(void)
+{
+uschar * ret = NULL;
+header_line * h = acl_added_headers;
+uschar * s;
+uschar * cp;
+int size = 0;
+int ptr = 0;
+
+if (!h) return NULL;
+
+do
+ {
+ s = h->text;
+ while ((cp = Ustrchr(s, '\n')) != NULL)
+ {
+ if (cp[1] == '\0') break;
+
+ /* contains embedded newline; needs doubling */
+ ret = string_cat(ret, &size, &ptr, s, cp-s+1);
+ ret = string_cat(ret, &size, &ptr, US"\n", 1);
+ s = cp+1;
+ }
+ /* last bit of header */
+
+ ret = string_cat(ret, &size, &ptr, s, cp-s+1); /* newline-sep list */
+ }
+while((h = h->next));
+
+ret[ptr-1] = '\0'; /* overwrite last newline */
+return ret;
+}
+
+
/*************************************************
* Set up removed header line(s) *
*************************************************/
assertion: legitimate SMTP clients are all explicitly authorized with CSA
SRV records of their own. */
- if (found != domain)
+ if (Ustrcmp(found, domain) != 0)
{
if (port & 1)
return t->data.val = CSA_FAIL_EXPLICIT;
DNS_LOOKUP_AGAIN:
#endif
+lookup_dnssec_authenticated = NULL;
switch (dns_lookup(&dnsa, target, type, NULL))
{
/* If something bad happened (most commonly DNS_AGAIN), defer. */
*************************************************/
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_NOT_BLIND, VERIFY_HDR_SNDR, VERIFY_SNDR, VERIFY_RCPT,
+ VERIFY_HDR_NAMES_ASCII
};
typedef struct {
uschar * name;
unsigned alt_opt_sep; /* >0 Non-/ option separator (custom parser) */
} verify_type_t;
static verify_type_t verify_type_list[] = {
- { US"reverse_host_lookup", VERIFY_REV_HOST_LKUP, ~0, TRUE, 0 },
+ { 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 },
{ US"sender", VERIFY_SNDR, (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)
|(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP),
FALSE, 6 },
- { US"recipient", VERIFY_RCPT, (1<<ACL_WHERE_RCPT), FALSE, 0 }
+ { US"recipient", VERIFY_RCPT, (1<<ACL_WHERE_RCPT), FALSE, 0 },
+ { US"header_names_ascii", VERIFY_HDR_NAMES_ASCII, (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP), TRUE, 0 }
};
{
case VERIFY_REV_HOST_LKUP:
if (sender_host_address == NULL) return OK;
- return acl_verify_reverse(user_msgptr, log_msgptr);
+ if ((rc = acl_verify_reverse(user_msgptr, log_msgptr)) == DEFER)
+ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+ if (strcmpic(ss, US"defer_ok") == 0)
+ return OK;
+ return rc;
case VERIFY_CERT:
/* TLS certificate verification is done at STARTTLS time; here we just
*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
return rc;
+ case VERIFY_HDR_NAMES_ASCII:
+ /* Check that all header names are true 7 bit strings
+ 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)
+ *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:. */
BAD_VERIFY:
*log_msgptr = string_sprintf("expected \"sender[=address]\", \"recipient\", "
- "\"helo\", \"header_syntax\", \"header_sender\" or "
- "\"reverse_host_lookup\" at start of ACL condition "
+ "\"helo\", \"header_syntax\", \"header_sender\", \"header_names_ascii\" "
+ "or \"reverse_host_lookup\" at start of ACL condition "
"\"verify %s\"", arg);
return ERROR;
}
sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0);
if (sender_rate_limit == NULL)
+ {
limit = -1.0;
+ ss = NULL; /* compiler quietening */
+ }
else
{
limit = Ustrtod(sender_rate_limit, &ss);
+/*************************************************
+* The udpsend ACL modifier *
+*************************************************/
+
+/* Called by acl_check_condition() below.
+
+Arguments:
+ arg the option string for udpsend=
+ log_msgptr for error messages
+
+Returns: OK - Completed.
+ DEFER - Problem with DNS lookup.
+ ERROR - Syntax error in options.
+*/
+
+static int
+acl_udpsend(uschar *arg, uschar **log_msgptr)
+{
+int sep = 0;
+uschar *hostname;
+uschar *portstr;
+uschar *portend;
+host_item *h;
+int portnum;
+int len;
+int r, s;
+uschar * errstr;
+
+hostname = string_nextinlist(&arg, &sep, NULL, 0);
+portstr = string_nextinlist(&arg, &sep, NULL, 0);
+
+if (hostname == NULL)
+ {
+ *log_msgptr = US"missing destination host in \"udpsend\" modifier";
+ return ERROR;
+ }
+if (portstr == NULL)
+ {
+ *log_msgptr = US"missing destination port in \"udpsend\" modifier";
+ return ERROR;
+ }
+if (arg == NULL)
+ {
+ *log_msgptr = US"missing datagram payload in \"udpsend\" modifier";
+ return ERROR;
+ }
+portnum = Ustrtol(portstr, &portend, 10);
+if (*portend != '\0')
+ {
+ *log_msgptr = US"bad destination port in \"udpsend\" modifier";
+ return ERROR;
+ }
+
+/* Make a single-item host list. */
+h = store_get(sizeof(host_item));
+memset(h, 0, sizeof(host_item));
+h->name = hostname;
+h->port = portnum;
+h->mx = MX_NONE;
+
+if (string_is_ip_address(hostname, NULL))
+ h->address = hostname, r = HOST_FOUND;
+else
+ r = host_find_byname(h, NULL, 0, NULL, FALSE);
+if (r == HOST_FIND_FAILED || r == HOST_FIND_AGAIN)
+ {
+ *log_msgptr = US"DNS lookup failed in \"udpsend\" modifier";
+ return DEFER;
+ }
+
+HDEBUG(D_acl)
+ debug_printf("udpsend [%s]:%d %s\n", h->address, portnum, arg);
+
+r = s = ip_connectedsocket(SOCK_DGRAM, h->address, portnum, portnum,
+ 1, NULL, &errstr);
+if (r < 0) goto defer;
+len = Ustrlen(arg);
+r = send(s, arg, len, 0);
+if (r < 0)
+ {
+ errstr = US strerror(errno);
+ close(s);
+ goto defer;
+ }
+close(s);
+if (r < len)
+ {
+ *log_msgptr =
+ string_sprintf("\"udpsend\" truncated from %d to %d octets", len, r);
+ return DEFER;
+ }
+
+HDEBUG(D_acl)
+ debug_printf("udpsend %d bytes\n", r);
+
+return OK;
+
+defer:
+*log_msgptr = string_sprintf("\"udpsend\" failed: %s", errstr);
+return DEFER;
+}
+
+
+
/*************************************************
* Handle conditions/modifiers on an ACL item *
*************************************************/
uschar *p = NULL;
int rc = OK;
#ifdef WITH_CONTENT_SCAN
-int sep = '/';
+int sep = -'/';
#endif
for (; cb != NULL; cb = cb->next)
if (cb->type == ACLC_MESSAGE)
{
+ HDEBUG(D_acl) debug_printf(" 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);
log_message = cb->arg;
continue;
}
/* The true/false parsing here should be kept in sync with that used in
expand.c when dealing with ECOND_BOOL so that we don't have too many
different definitions of what can be a boolean. */
- if (Ustrspn(arg, "0123456789") == Ustrlen(arg)) /* Digits, or empty */
+ if (*arg == '-'
+ ? Ustrspn(arg+1, "0123456789") == Ustrlen(arg+1) /* Negative number */
+ : Ustrspn(arg, "0123456789") == Ustrlen(arg)) /* Digits, or empty */
rc = (Uatoi(arg) == 0)? FAIL : OK;
else
rc = (strcmpic(arg, US"no") == 0 ||
#ifndef DISABLE_DKIM
case CONTROL_DKIM_VERIFY:
dkim_disable_verify = TRUE;
+ #ifdef EXPERIMENTAL_DMARC
+ /* Since DKIM was blocked, skip DMARC too */
+ dmarc_disable_verify = TRUE;
+ dmarc_enable_forensic = FALSE;
+ #endif
+ break;
+ #endif
+
+ #ifdef EXPERIMENTAL_DMARC
+ case CONTROL_DMARC_VERIFY:
+ dmarc_disable_verify = TRUE;
+ break;
+
+ case CONTROL_DMARC_FORENSIC:
+ dmarc_enable_forensic = TRUE;
break;
#endif
disable_callout_flush = TRUE;
break;
- case CONTROL_FAKEDEFER:
case CONTROL_FAKEREJECT:
+ cancel_cutthrough_connection("fakereject");
+ case CONTROL_FAKEDEFER:
fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL;
if (*p == '/')
{
*log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
return ERROR;
}
+ cancel_cutthrough_connection("item frozen");
break;
case CONTROL_QUEUE_ONLY:
queue_only_policy = TRUE;
+ cancel_cutthrough_connection("queueing forced");
break;
case CONTROL_SUBMISSION:
break;
case CONTROL_CUTTHROUGH_DELIVERY:
- if (deliver_freeze)
- {
- *log_msgptr = string_sprintf("\"control=%s\" on frozen item", arg);
- return ERROR;
- }
- if (queue_only_policy)
- {
- *log_msgptr = string_sprintf("\"control=%s\" on queue-only item", arg);
- return ERROR;
- }
- cutthrough_delivery = TRUE;
- break;
+ if (prdr_requested)
+ /* 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(US"PRDR on %s reception\n", arg);
+ else
+ {
+ if (deliver_freeze)
+ *log_msgptr = US"frozen";
+ else if (queue_only_policy)
+ *log_msgptr = US"queue-only";
+ else if (fake_response == FAIL)
+ *log_msgptr = US"fakereject";
+ else
+ {
+ if (rcpt_count == 1) cutthrough.delivery = TRUE;
+ break;
+ }
+ *log_msgptr = string_sprintf("\"control=%s\" on %s item",
+ arg, *log_msgptr);
+ }
+ return ERROR;
}
break;
break;
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ case ACLC_DMARC_STATUS:
+ if (!dmarc_has_been_checked)
+ dmarc_process();
+ 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),
+ &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
+ break;
+ #endif
+
case ACLC_DNSLISTS:
rc = verify_check_dnsbl(&arg);
break;
break;
#ifdef WITH_CONTENT_SCAN
- case ACLC_MALWARE:
+ case ACLC_MALWARE: /* Run the malware backend. */
{
/* Separate the regular expression and any optional parameters. */
uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
- /* Run the malware backend. */
- rc = malware(&ss);
- /* Modify return code based upon the existance of options. */
- while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
- != NULL) {
- if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
- {
- /* FAIL so that the message is passed to the next ACL */
- rc = FAIL;
- }
- }
+ uschar *opt;
+ BOOL defer_ok = FALSE;
+ int timeout = 0;
+
+ while ((opt = string_nextinlist(&arg, &sep, NULL, 0)))
+ if (strcmpic(opt, US"defer_ok") == 0)
+ defer_ok = TRUE;
+ else if ( strncmpic(opt, US"tmo=", 4) == 0
+ && (timeout = readconf_readtime(opt+4, '\0', FALSE)) < 0
+ )
+ {
+ *log_msgptr = string_sprintf("bad timeout value in '%s'", opt);
+ return ERROR;
+ }
+
+ rc = malware(ss, timeout);
+ if (rc == DEFER && defer_ok)
+ rc = FAIL; /* FAIL so that the message is passed to the next ACL */
}
break;
break;
#endif
+ case ACLC_UDPSEND:
+ rc = acl_udpsend(arg, log_msgptr);
+ break;
+
/* If the verb is WARN, discard any user message from verification, because
such messages are SMTP responses, not header additions. The latter come
only from explicit "message" modifiers. However, put the user message into
switch(acl->verb)
{
case ACL_ACCEPT:
- if (cond == OK || cond == DISCARD) return cond;
+ if (cond == OK || cond == DISCARD)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: ACCEPT\n", acl_name);
+ return cond;
+ }
if (endpass_seen)
{
HDEBUG(D_acl) debug_printf("accept: endpass encountered - denying access\n");
case ACL_DEFER:
if (cond == OK)
{
+ HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name);
acl_temp_details = TRUE;
return DEFER;
}
break;
case ACL_DENY:
- if (cond == OK) return FAIL;
+ if (cond == OK)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name);
+ return FAIL;
+ }
break;
case ACL_DISCARD:
- if (cond == OK || cond == DISCARD) return DISCARD;
+ if (cond == OK || cond == DISCARD)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name);
+ return DISCARD;
+ }
if (endpass_seen)
{
HDEBUG(D_acl) debug_printf("discard: endpass encountered - denying access\n");
break;
case ACL_DROP:
- if (cond == OK) return FAIL_DROP;
+ if (cond == OK)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name);
+ return FAIL_DROP;
+ }
break;
case ACL_REQUIRE:
- if (cond != OK) return cond;
+ if (cond != OK)
+ {
+ HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name);
+ return cond;
+ }
break;
case ACL_WARN:
{
uschar * tmp;
uschar * tmp_arg[9]; /* must match acl_arg[] */
+uschar * sav_arg[9]; /* must match acl_arg[] */
+int sav_narg;
uschar * name;
int i;
+int ret;
if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp)))
goto bad;
goto bad;
}
}
+
+sav_narg = acl_narg;
acl_narg = i;
-for (i = 0; i < acl_narg; i++) acl_arg[i] = tmp_arg[i];
-while (i < 9) acl_arg[i++] = NULL;
+for (i = 0; i < acl_narg; i++)
+ {
+ sav_arg[i] = acl_arg[i];
+ acl_arg[i] = tmp_arg[i];
+ }
+while (i < 9)
+ {
+ sav_arg[i] = acl_arg[i];
+ acl_arg[i++] = NULL;
+ }
+
+ret = acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr);
-return acl_check_internal(where, addr, name, level, user_msgptr, log_msgptr);
+acl_narg = sav_narg;
+for (i = 0; i < 9; i++) acl_arg[i] = sav_arg[i];
+return ret;
bad:
if (expand_string_forcedfail) return ERROR;
* Check access using an ACL *
*************************************************/
+/* Alternate interface for ACL, used by expansions */
+int
+acl_eval(int where, uschar *s, uschar **user_msgptr, uschar **log_msgptr)
+{
+address_item adb;
+address_item *addr = NULL;
+
+*user_msgptr = *log_msgptr = NULL;
+sender_verified_failed = NULL;
+ratelimiters_cmd = NULL;
+log_reject_target = LOG_MAIN|LOG_REJECT;
+
+if (where == ACL_WHERE_RCPT)
+ {
+ adb = address_defaults;
+ addr = &adb;
+ addr->address = expand_string(US"$local_part@$domain");
+ addr->domain = deliver_domain;
+ addr->local_part = deliver_localpart;
+ addr->cc_local_part = deliver_localpart;
+ addr->lc_local_part = deliver_localpart;
+ }
+
+return acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
+}
+
+
+
/* This is the external interface for ACL checks. It sets up an address and the
expansions for $domain and $local_part when called after RCPT, then calls
acl_check_internal() to do the actual work.
DEFER can't tell at the moment
ERROR disaster
*/
+int acl_where = ACL_WHERE_UNKNOWN;
int
acl_check(int where, uschar *recipient, uschar *s, uschar **user_msgptr,
ratelimiters_cmd = NULL;
log_reject_target = LOG_MAIN|LOG_REJECT;
+#ifndef DISABLE_PRDR
+if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR)
+#else
if (where == ACL_WHERE_RCPT)
+#endif
{
adb = address_defaults;
addr = &adb;
deliver_localpart = addr->local_part;
}
+acl_where = where;
rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
+acl_where = ACL_WHERE_UNKNOWN;
/* Cutthrough - if requested,
and WHERE_RCPT and not yet opened conn as result of recipient-verify,
switch (where)
{
case ACL_WHERE_RCPT:
- if( rcpt_count > 1 )
- cancel_cutthrough_connection("more than one recipient");
- else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0)
+#ifndef DISABLE_PRDR
+case ACL_WHERE_PRDR:
+#endif
+ if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt)
open_cutthrough_connection(addr);
break;
}
-
/*************************************************
* Create ACL variable *
*************************************************/
fprintf(f, "-acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value);
}
+/* vi: aw ai sw=2
+*/
/* End of acl.c */