* 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) */
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* add_header */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
#endif
(1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_AUTH)| /* bmi_optin */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
#endif
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
#ifdef EXPERIMENTAL_DCC
(unsigned int)
~((1<<ACL_WHERE_DATA)| /* dcc */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_PRDR */
+ #endif
(1<<ACL_WHERE_NOTSMTP)),
#endif
#ifdef WITH_OLD_DEMIME
(unsigned int)
~((1<<ACL_WHERE_DATA)| /* demime */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_PRDR */
+ #endif
(1<<ACL_WHERE_NOTSMTP)),
#endif
(unsigned int)
~((1<<ACL_WHERE_RCPT) /* domains */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
|(1<<ACL_WHERE_PRDR)
#endif
),
(unsigned int)
~((1<<ACL_WHERE_RCPT) /* local_parts */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
|(1<<ACL_WHERE_PRDR)
#endif
),
#ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_DATA)| /* malware */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_PRDR */
+ #endif
(1<<ACL_WHERE_NOTSMTP)),
#endif
#ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_DATA)| /* regex */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_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)|
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
#endif
(1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
#ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_DATA)| /* spam */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_PRDR */
+ #endif
(1<<ACL_WHERE_NOTSMTP)),
#endif
#ifndef DISABLE_DKIM
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_PRDR */
+ #endif
(1<<ACL_WHERE_NOTSMTP_START),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_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)|
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif /* EXPERIMENTAL_PRDR */
+ #endif
(1<<ACL_WHERE_MIME)),
(1<<ACL_WHERE_NOTSMTP)| /* no_multiline */
/* Enable recursion between acl_check_internal() and acl_check_condition() */
-static int acl_check_wargs(int, address_item *, uschar *, int, uschar **,
+static int acl_check_wargs(int, address_item *, const uschar *, int, uschar **,
uschar **);
*/
static void
-setup_header(uschar *hstring)
+setup_header(const uschar *hstring)
{
-uschar *p, *q;
+const uschar *p, *q;
int hlen = Ustrlen(hstring);
/* Ignore any leading newlines */
/* An empty string does nothing; ensure exactly one final newline. */
if (hlen <= 0) return;
-if (hstring[--hlen] != '\n') hstring = string_sprintf("%s\n", hstring);
-else while(hstring[--hlen] == '\n') hstring[hlen+1] = '\0';
+if (hstring[--hlen] != '\n')
+ q = string_sprintf("%s\n", hstring);
+else if (hstring[hlen-1] == '\n')
+ {
+ uschar * s = string_copy(hstring);
+ while(s[--hlen] == '\n')
+ s[hlen+1] = '\0';
+ q = s;
+ }
+else
+ q = hstring;
/* Loop for multiple header lines, taking care about continuations */
-for (p = q = hstring; *p != 0; )
+for (p = q; *p != 0; )
{
- uschar *s;
+ const uschar *s;
+ uschar * hdr;
int newtype = htype_add_bot;
header_line **hptr = &acl_added_headers;
if (*s == ':' || !isgraph(*s)) break;
}
- s = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", (int) (q - p), p);
- hlen = Ustrlen(s);
+ 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)
{
- if (Ustrncmp((*hptr)->text, s, hlen) == 0) break;
+ if (Ustrncmp((*hptr)->text, hdr, hlen) == 0) break;
hptr = &((*hptr)->next);
}
if (*hptr == NULL)
{
header_line *h = store_get(sizeof(header_line));
- h->text = s;
+ h->text = hdr;
h->next = NULL;
h->type = newtype;
h->slen = hlen;
*/
static void
-setup_remove_header(uschar *hnames)
+setup_remove_header(const uschar *hnames)
{
if (*hnames != 0)
- {
- if (acl_removed_headers == NULL)
- acl_removed_headers = hnames;
- else
- acl_removed_headers = string_sprintf("%s : %s", acl_removed_headers, hnames);
- }
+ acl_removed_headers = acl_removed_headers
+ ? string_sprintf("%s : %s", acl_removed_headers, hnames)
+ : string_copy(hnames);
}
if (rr->type != T_A
#if HAVE_IPV6
&& rr->type != T_AAAA
- #ifdef SUPPORT_A6
- && rr->type != T_A6
- #endif
#endif
) continue;
*/
static int
-acl_verify_csa(uschar *domain)
+acl_verify_csa(const uschar *domain)
{
tree_node *t;
-uschar *found, *p;
+const uschar *found;
+uschar *p;
int priority, weight, port;
dns_answer dnsa;
dns_scan dnss;
if (domain[0] == '[')
{
- uschar *start = Ustrchr(domain, ':');
+ const uschar *start = Ustrchr(domain, ':');
if (start == NULL) start = domain;
domain = string_copyn(start + 1, Ustrlen(start) - 2);
}
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;
type = T_A;
-#if HAVE_IPV6 && defined(SUPPORT_A6)
-DNS_LOOKUP_AGAIN:
-#endif
-
+lookup_dnssec_authenticated = NULL;
switch (dns_lookup(&dnsa, target, type, NULL))
{
/* If something bad happened (most commonly DNS_AGAIN), defer. */
default:
- return t->data.val = CSA_DEFER_ADDR;
+ return t->data.val = CSA_DEFER_ADDR;
/* If the query succeeded, scan the addresses and return the result. */
case DNS_SUCCEED:
- rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target);
- if (rc != CSA_FAIL_NOADDR) return t->data.val = rc;
- /* else fall through */
+ rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ANSWERS, target);
+ if (rc != CSA_FAIL_NOADDR) return t->data.val = rc;
+ /* else fall through */
/* If the target has no IP addresses, the client cannot have an authorized
IP address. However, if the target site uses A6 records (not AAAA records)
case DNS_NOMATCH:
case DNS_NODATA:
-
- #if HAVE_IPV6 && defined(SUPPORT_A6)
- if (type == T_AAAA) { type = T_A6; goto DNS_LOOKUP_AGAIN; }
- #endif
-
- return t->data.val = CSA_FAIL_NOADDR;
+ return t->data.val = CSA_FAIL_NOADDR;
}
}
*************************************************/
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 }
};
*/
static int
-acl_verify(int where, address_item *addr, uschar *arg,
+acl_verify(int where, address_item *addr, const uschar *arg,
uschar **user_msgptr, uschar **log_msgptr, int *basic_errno)
{
int sep = '/';
*/
uschar *slash = Ustrchr(arg, '/');
-uschar *list = arg;
+const uschar *list = arg;
uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
verify_type_t * vp;
{
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:. */
while (isspace(*ss)) ss++;
if (*ss++ == '=')
{
+ const uschar * sublist = ss;
int optsep = ',';
uschar *opt;
uschar buffer[256];
- while (isspace(*ss)) ss++;
+ while (isspace(*sublist)) sublist++;
- while ((opt = string_nextinlist(&ss, &optsep, buffer, sizeof(buffer)))
+ while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))
!= NULL)
{
callout_opt_t * op;
uschar *save_address_data = deliver_address_data;
sender_vaddr = deliver_make_addr(verify_sender_address, TRUE);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ sender_vaddr->prop.utf8 = message_smtputf8;
+#endif
if (no_details) setflag(sender_vaddr, af_sverify_told);
if (verify_sender_address[0] != 0)
{
/* Put the sender address_data value into $sender_address_data */
- sender_address_data = sender_vaddr->p.address_data;
+ sender_address_data = sender_vaddr->prop.address_data;
}
/* A recipient address just gets a straightforward verify; again we must handle
if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE;
/* Make $address_data visible */
- deliver_address_data = addr2.p.address_data;
+ deliver_address_data = addr2.prop.address_data;
}
/* We have a result from the relevant test. Handle defer overrides first. */
if (rc != OK && verify_sender_address != NULL)
{
if (rc != DEFER)
- {
*log_msgptr = *user_msgptr = US"Sender verify failed";
- }
else if (*basic_errno != ERRNO_CALLOUTDEFER)
- {
*log_msgptr = *user_msgptr = US"Could not complete sender verify";
- }
else
{
*log_msgptr = US"Could not complete sender verify callout";
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;
}
*/
static int
-decode_control(uschar *arg, uschar **pptr, int where, uschar **log_msgptr)
+decode_control(const uschar *arg, const uschar **pptr, int where, uschar **log_msgptr)
{
int len;
control_def *d;
*/
static int
-acl_ratelimit(uschar *arg, int where, uschar **log_msgptr)
+acl_ratelimit(const uschar *arg, int where, uschar **log_msgptr)
{
double limit, period, count;
uschar *ss;
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);
*/
static int
-acl_udpsend(uschar *arg, uschar **log_msgptr)
+acl_udpsend(const uschar *arg, uschar **log_msgptr)
{
int sep = 0;
uschar *hostname;
uschar *portend;
host_item *h;
int portnum;
-int host_af;
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 = "missing destination host in \"udpsend\" modifier";
+ *log_msgptr = US"missing destination host in \"udpsend\" modifier";
return ERROR;
}
if (portstr == NULL)
{
- *log_msgptr = "missing destination port in \"udpsend\" modifier";
+ *log_msgptr = US"missing destination port in \"udpsend\" modifier";
return ERROR;
}
if (arg == NULL)
{
- *log_msgptr = "missing datagram payload in \"udpsend\" modifier";
+ *log_msgptr = US"missing datagram payload in \"udpsend\" modifier";
return ERROR;
}
portnum = Ustrtol(portstr, &portend, 10);
if (*portend != '\0')
{
- *log_msgptr = "bad destination port in \"udpsend\" modifier";
+ *log_msgptr = US"bad destination port in \"udpsend\" modifier";
return ERROR;
}
r = host_find_byname(h, NULL, 0, NULL, FALSE);
if (r == HOST_FIND_FAILED || r == HOST_FIND_AGAIN)
{
- *log_msgptr = "DNS lookup failed in \"udpsend\" modifier";
+ *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);
-host_af = (Ustrchr(h->address, ':') == NULL)? AF_INET:AF_INET6;
-r = s = ip_socket(SOCK_DGRAM, host_af);
-if (r < 0) goto defer;
-r = ip_connect(s, host_af, h->address, portnum, 1);
-if (r < 0) goto defer;
-len = strlen(arg);
-r = send(s, arg, len, MSG_NOSIGNAL);
+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 =
return OK;
defer:
-*log_msgptr = string_sprintf("\"udpsend\" failed: %s", strerror(errno));
+*log_msgptr = string_sprintf("\"udpsend\" failed: %s", errstr);
return DEFER;
}
uschar *log_message = NULL;
uschar *debug_tag = NULL;
uschar *debug_opts = NULL;
-uschar *p = NULL;
int rc = OK;
#ifdef WITH_CONTENT_SCAN
-int sep = '/';
+int sep = -'/';
#endif
for (; cb != NULL; cb = cb->next)
{
- uschar *arg;
+ const uschar *arg;
int control_type;
/* The message and log_message items set up messages to be used in
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 ||
break;
case ACLC_CONTROL:
- control_type = decode_control(arg, &p, where, log_msgptr);
-
- /* Check if this control makes sense at this time */
-
- if ((control_forbids[control_type] & (1 << where)) != 0)
{
- *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL",
- controls[control_type], acl_wherenames[where]);
- return ERROR;
- }
-
- switch(control_type)
- {
- case CONTROL_AUTH_UNADVERTISED:
- allow_auth_unadvertised = TRUE;
- break;
-
- #ifdef EXPERIMENTAL_BRIGHTMAIL
- case CONTROL_BMI_RUN:
- bmi_run = 1;
- break;
- #endif
-
- #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
-
- case CONTROL_DSCP:
- if (*p == '/')
- {
- int fd, af, level, optname, value;
- /* If we are acting on stdin, the setsockopt may fail if stdin is not
- a socket; we can accept that, we'll just debug-log failures anyway. */
- fd = fileno(smtp_in);
- af = ip_get_address_family(fd);
- if (af < 0)
- {
- HDEBUG(D_acl)
- debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n",
- strerror(errno));
- break;
- }
- if (dscp_lookup(p+1, af, &level, &optname, &value))
- {
- if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0)
- {
- HDEBUG(D_acl) debug_printf("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);
- }
- }
- else
- {
- *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg);
- return ERROR;
- }
- }
- else
- {
- *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
- return ERROR;
- }
- break;
-
- case CONTROL_ERROR:
- return ERROR;
-
- case CONTROL_CASEFUL_LOCAL_PART:
- deliver_localpart = addr->cc_local_part;
- break;
-
- case CONTROL_CASELOWER_LOCAL_PART:
- deliver_localpart = addr->lc_local_part;
- break;
-
- case CONTROL_ENFORCE_SYNC:
- smtp_enforce_sync = TRUE;
- break;
-
- case CONTROL_NO_ENFORCE_SYNC:
- smtp_enforce_sync = FALSE;
- break;
-
- #ifdef WITH_CONTENT_SCAN
- case CONTROL_NO_MBOX_UNSPOOL:
- no_mbox_unspool = TRUE;
- break;
- #endif
-
- case CONTROL_NO_MULTILINE:
- no_multiline_responses = TRUE;
- break;
-
- case CONTROL_NO_PIPELINING:
- pipelining_enable = FALSE;
- break;
-
- case CONTROL_NO_DELAY_FLUSH:
- disable_delay_flush = TRUE;
- break;
-
- case CONTROL_NO_CALLOUT_FLUSH:
- disable_callout_flush = TRUE;
- break;
-
- case CONTROL_FAKEDEFER:
- case CONTROL_FAKEREJECT:
- fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL;
- if (*p == '/')
- {
- uschar *pp = p + 1;
- while (*pp != 0) pp++;
- fake_response_text = expand_string(string_copyn(p+1, pp-p-1));
- p = pp;
- }
- else
- {
- /* Explicitly reset to default string */
- fake_response_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s).";
- }
- break;
-
- case CONTROL_FREEZE:
- deliver_freeze = TRUE;
- deliver_frozen_at = time(NULL);
- freeze_tell = freeze_tell_config; /* Reset to configured value */
- if (Ustrncmp(p, "/no_tell", 8) == 0)
- {
- p += 8;
- freeze_tell = NULL;
- }
- if (*p != 0)
- {
- *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
- return ERROR;
- }
- break;
-
- case CONTROL_QUEUE_ONLY:
- queue_only_policy = TRUE;
- break;
-
- case CONTROL_SUBMISSION:
- originator_name = US"";
- submission_mode = TRUE;
- while (*p == '/')
- {
- if (Ustrncmp(p, "/sender_retain", 14) == 0)
- {
- p += 14;
- active_local_sender_retain = TRUE;
- active_local_from_check = FALSE;
- }
- else if (Ustrncmp(p, "/domain=", 8) == 0)
- {
- uschar *pp = p + 8;
- while (*pp != 0 && *pp != '/') pp++;
- submission_domain = string_copyn(p+8, pp-p-8);
- p = pp;
- }
- /* The name= option must be last, because it swallows the rest of
- the string. */
- else if (Ustrncmp(p, "/name=", 6) == 0)
- {
- uschar *pp = p + 6;
- while (*pp != 0) pp++;
- submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6,
- big_buffer, big_buffer_size));
- p = pp;
- }
- else break;
- }
- if (*p != 0)
- {
- *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
- return ERROR;
- }
- break;
-
- case CONTROL_DEBUG:
- while (*p == '/')
- {
- if (Ustrncmp(p, "/tag=", 5) == 0)
- {
- uschar *pp = p + 5;
- while (*pp != '\0' && *pp != '/') pp++;
- debug_tag = string_copyn(p+5, pp-p-5);
- p = pp;
- }
- else if (Ustrncmp(p, "/opts=", 6) == 0)
- {
- uschar *pp = p + 6;
- while (*pp != '\0' && *pp != '/') pp++;
- debug_opts = string_copyn(p+6, pp-p-6);
- p = pp;
- }
- }
- debug_logging_activate(debug_tag, debug_opts);
- break;
-
- case CONTROL_SUPPRESS_LOCAL_FIXUPS:
- suppress_local_fixups = TRUE;
- 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;
+ const uschar *p = NULL;
+ control_type = decode_control(arg, &p, where, log_msgptr);
+
+ /* Check if this control makes sense at this time */
+
+ if ((control_forbids[control_type] & (1 << where)) != 0)
+ {
+ *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL",
+ controls[control_type], acl_wherenames[where]);
+ return ERROR;
+ }
+
+ switch(control_type)
+ {
+ case CONTROL_AUTH_UNADVERTISED:
+ allow_auth_unadvertised = TRUE;
+ break;
+
+ #ifdef EXPERIMENTAL_BRIGHTMAIL
+ case CONTROL_BMI_RUN:
+ bmi_run = 1;
+ break;
+ #endif
+
+ #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
+
+ case CONTROL_DSCP:
+ if (*p == '/')
+ {
+ int fd, af, level, optname, value;
+ /* If we are acting on stdin, the setsockopt may fail if stdin is not
+ a socket; we can accept that, we'll just debug-log failures anyway. */
+ fd = fileno(smtp_in);
+ af = ip_get_address_family(fd);
+ if (af < 0)
+ {
+ HDEBUG(D_acl)
+ debug_printf("smtp input is probably not a socket [%s], not setting DSCP\n",
+ strerror(errno));
+ break;
+ }
+ if (dscp_lookup(p+1, af, &level, &optname, &value))
+ {
+ if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0)
+ {
+ HDEBUG(D_acl) debug_printf("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);
+ }
+ }
+ else
+ {
+ *log_msgptr = string_sprintf("unrecognised DSCP value in \"control=%s\"", arg);
+ return ERROR;
+ }
+ }
+ else
+ {
+ *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
+ return ERROR;
+ }
+ break;
+
+ case CONTROL_ERROR:
+ return ERROR;
+
+ case CONTROL_CASEFUL_LOCAL_PART:
+ deliver_localpart = addr->cc_local_part;
+ break;
+
+ case CONTROL_CASELOWER_LOCAL_PART:
+ deliver_localpart = addr->lc_local_part;
+ break;
+
+ case CONTROL_ENFORCE_SYNC:
+ smtp_enforce_sync = TRUE;
+ break;
+
+ case CONTROL_NO_ENFORCE_SYNC:
+ smtp_enforce_sync = FALSE;
+ break;
+
+ #ifdef WITH_CONTENT_SCAN
+ case CONTROL_NO_MBOX_UNSPOOL:
+ no_mbox_unspool = TRUE;
+ break;
+ #endif
+
+ case CONTROL_NO_MULTILINE:
+ no_multiline_responses = TRUE;
+ break;
+
+ case CONTROL_NO_PIPELINING:
+ pipelining_enable = FALSE;
+ break;
+
+ case CONTROL_NO_DELAY_FLUSH:
+ disable_delay_flush = TRUE;
+ break;
+
+ case CONTROL_NO_CALLOUT_FLUSH:
+ disable_callout_flush = TRUE;
+ break;
+
+ case CONTROL_FAKEREJECT:
+ cancel_cutthrough_connection("fakereject");
+ case CONTROL_FAKEDEFER:
+ fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL;
+ if (*p == '/')
+ {
+ const uschar *pp = p + 1;
+ while (*pp != 0) pp++;
+ fake_response_text = expand_string(string_copyn(p+1, pp-p-1));
+ p = pp;
+ }
+ else
+ {
+ /* Explicitly reset to default string */
+ fake_response_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s).";
+ }
+ break;
+
+ case CONTROL_FREEZE:
+ deliver_freeze = TRUE;
+ deliver_frozen_at = time(NULL);
+ freeze_tell = freeze_tell_config; /* Reset to configured value */
+ if (Ustrncmp(p, "/no_tell", 8) == 0)
+ {
+ p += 8;
+ freeze_tell = NULL;
+ }
+ if (*p != 0)
+ {
+ *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:
+ originator_name = US"";
+ submission_mode = TRUE;
+ while (*p == '/')
+ {
+ if (Ustrncmp(p, "/sender_retain", 14) == 0)
+ {
+ p += 14;
+ active_local_sender_retain = TRUE;
+ active_local_from_check = FALSE;
+ }
+ else if (Ustrncmp(p, "/domain=", 8) == 0)
+ {
+ const uschar *pp = p + 8;
+ while (*pp != 0 && *pp != '/') pp++;
+ submission_domain = string_copyn(p+8, pp-p-8);
+ p = pp;
+ }
+ /* The name= option must be last, because it swallows the rest of
+ the string. */
+ else if (Ustrncmp(p, "/name=", 6) == 0)
+ {
+ const uschar *pp = p + 6;
+ while (*pp != 0) pp++;
+ submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6,
+ big_buffer, big_buffer_size));
+ p = pp;
+ }
+ else break;
+ }
+ if (*p != 0)
+ {
+ *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
+ return ERROR;
+ }
+ break;
+
+ case CONTROL_DEBUG:
+ while (*p == '/')
+ {
+ if (Ustrncmp(p, "/tag=", 5) == 0)
+ {
+ const uschar *pp = p + 5;
+ while (*pp != '\0' && *pp != '/') pp++;
+ debug_tag = string_copyn(p+5, pp-p-5);
+ p = pp;
+ }
+ else if (Ustrncmp(p, "/opts=", 6) == 0)
+ {
+ const uschar *pp = p + 6;
+ while (*pp != '\0' && *pp != '/') pp++;
+ debug_opts = string_copyn(p+6, pp-p-6);
+ p = pp;
+ }
+ }
+ debug_logging_activate(debug_tag, debug_opts);
+ break;
+
+ case CONTROL_SUPPRESS_LOCAL_FIXUPS:
+ suppress_local_fixups = TRUE;
+ break;
+
+ case CONTROL_CUTTHROUGH_DELIVERY:
+ 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("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;
#ifdef EXPERIMENTAL_DCC
case ACLC_DCC:
{
/* Seperate the regular expression and any optional parameters. */
- uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
+ const uschar * list = arg;
+ uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
/* Run the dcc backend. */
rc = dcc_process(&ss);
/* Modify return code based upon the existance of options. */
- while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
!= NULL) {
if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
{
case ACLC_DOMAINS:
rc = match_isinlist(addr->domain, &arg, 0, &domainlist_anchor,
- addr->domain_cache, MCL_DOMAIN, TRUE, &deliver_domain_data);
+ addr->domain_cache, MCL_DOMAIN, TRUE, CUSS &deliver_domain_data);
break;
/* The value in tls_cipher is the full cipher name, for example,
case ACLC_HOSTS:
rc = verify_check_this_host(&arg, sender_host_cache, NULL,
- (sender_host_address == NULL)? US"" : sender_host_address, &host_data);
+ (sender_host_address == NULL)? US"" : sender_host_address,
+ CUSS &host_data);
if (host_data != NULL) host_data = string_copy_malloc(host_data);
break;
case ACLC_LOCAL_PARTS:
rc = match_isinlist(addr->cc_local_part, &arg, 0,
&localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART, TRUE,
- &deliver_localpart_data);
+ CUSS &deliver_localpart_data);
break;
case ACLC_LOG_REJECT_TARGET:
{
int logbits = 0;
int sep = 0;
- uschar *s = arg;
+ const uschar *s = arg;
uschar *ss;
while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))
!= NULL)
case ACLC_LOGWRITE:
{
int logbits = 0;
- uschar *s = arg;
+ const uschar *s = arg;
if (*s == ':')
{
s++;
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;
- }
- }
+ const uschar * list = arg;
+ uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
+ uschar *opt;
+ BOOL defer_ok = FALSE;
+ int timeout = 0;
+
+ while ((opt = string_nextinlist(&list, &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;
case ACLC_RECIPIENTS:
- rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0,
- &recipient_data);
+ rc = match_address_list((const uschar *)addr->address, TRUE, TRUE, &arg, NULL, -1, 0,
+ CUSS &recipient_data);
break;
#ifdef WITH_CONTENT_SCAN
break;
case ACLC_SENDERS:
- rc = match_address_list(sender_address, TRUE, TRUE, &arg,
- sender_address_cache, -1, 0, &sender_data);
+ rc = match_address_list((const uschar *)sender_address, TRUE, TRUE, &arg,
+ sender_address_cache, -1, 0, CUSS &sender_data);
break;
/* Connection variables must persist forever */
case ACLC_SPAM:
{
/* Seperate the regular expression and any optional parameters. */
- uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
+ const uschar * list = arg;
+ uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
/* Run the spam backend. */
- rc = spam(&ss);
+ rc = spam(CUSS &ss);
/* Modify return code based upon the existance of options. */
- while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
!= NULL) {
if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
{
int cond;
int basic_errno = 0;
BOOL endpass_seen = FALSE;
+ BOOL acl_quit_check = level == 0
+ && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT);
*log_msgptr = *user_msgptr = NULL;
acl_temp_details = FALSE;
- if ((where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT) &&
- acl->verb != ACL_ACCEPT &&
- acl->verb != ACL_WARN)
- {
- *log_msgptr = string_sprintf("\"%s\" is not allowed in a QUIT or not-QUIT ACL",
- verbs[acl->verb]);
- return ERROR;
- }
-
HDEBUG(D_acl) debug_printf("processing \"%s\"\n", verbs[acl->verb]);
/* Clear out any search error message from a previous check before testing
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);
+ if (acl_quit_check) goto badquit;
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);
+ if (acl_quit_check) goto badquit;
+ 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);
+ if (acl_quit_check) goto badquit;
+ 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);
+ if (acl_quit_check) goto badquit;
+ 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);
+ if (acl_quit_check) goto badquit;
+ return cond;
+ }
break;
case ACL_WARN:
HDEBUG(D_acl) debug_printf("end of %s: implicit DENY\n", acl_name);
return FAIL;
+
+badquit:
+ *log_msgptr = string_sprintf("QUIT or not-QUIT teplevel ACL may not fail "
+ "('%s' verb used incorrectly)", verbs[acl->verb]);
+ return ERROR;
}
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, uschar *s, int level,
+acl_check_wargs(int where, address_item *addr, const uschar *s, int level,
uschar **user_msgptr, uschar **log_msgptr)
{
uschar * tmp;
ratelimiters_cmd = NULL;
log_reject_target = LOG_MAIN|LOG_REJECT;
-#ifdef EXPERIMENTAL_PRDR
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR )
+#ifndef DISABLE_PRDR
+if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR)
#else
-if (where == ACL_WHERE_RCPT )
+if (where == ACL_WHERE_RCPT)
#endif
{
adb = address_defaults;
*log_msgptr = US"defer in percent_hack_domains check";
return DEFER;
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ addr->prop.utf8 = message_smtputf8;
+#endif
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;
}
switch (where)
{
case ACL_WHERE_RCPT:
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
case ACL_WHERE_PRDR:
#endif
- if( rcpt_count > 1 )
- cancel_cutthrough_connection("more than one recipient");
- else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0)
+ if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt)
open_cutthrough_connection(addr);
break;
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 */