* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for handling Access Control Lists (ACLs) */
enum {
CONTROL_AUTH_UNADVERTISED,
- #ifdef EXPERIMENTAL_BRIGHTMAIL
+#ifdef EXPERIMENTAL_BRIGHTMAIL
CONTROL_BMI_RUN,
- #endif
+#endif
CONTROL_DEBUG,
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
CONTROL_DKIM_VERIFY,
- #endif
- #ifdef EXPERIMENTAL_DMARC
+#endif
+#ifdef EXPERIMENTAL_DMARC
CONTROL_DMARC_VERIFY,
CONTROL_DMARC_FORENSIC,
- #endif
+#endif
CONTROL_DSCP,
CONTROL_ERROR,
CONTROL_CASEFUL_LOCAL_PART,
CONTROL_QUEUE_ONLY,
CONTROL_SUBMISSION,
CONTROL_SUPPRESS_LOCAL_FIXUPS,
- #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
CONTROL_NO_MBOX_UNSPOOL,
- #endif
+#endif
CONTROL_FAKEDEFER,
CONTROL_FAKEREJECT,
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ CONTROL_UTF8_DOWNCONVERT,
+#endif
CONTROL_NO_MULTILINE,
CONTROL_NO_PIPELINING,
CONTROL_NO_DELAY_FLUSH,
static uschar *controls[] = {
US"allow_auth_unadvertised",
- #ifdef EXPERIMENTAL_BRIGHTMAIL
+#ifdef EXPERIMENTAL_BRIGHTMAIL
US"bmi_run",
- #endif
+#endif
US"debug",
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
US"dkim_disable_verify",
- #endif
- #ifdef EXPERIMENTAL_DMARC
+#endif
+#ifdef EXPERIMENTAL_DMARC
US"dmarc_disable_verify",
US"dmarc_enable_forensic",
- #endif
+#endif
US"dscp",
US"error",
US"caseful_local_part",
US"queue_only",
US"submission",
US"suppress_local_fixups",
- #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
US"no_mbox_unspool",
- #endif
+#endif
US"fakedefer",
US"fakereject",
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ US"utf8_downconvert",
+#endif
US"no_multiline_responses",
US"no_pipelining",
US"no_delay_flush",
(unsigned int)
~((1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)), /* allow_auth_unadvertised */
- #ifdef EXPERIMENTAL_BRIGHTMAIL
+#ifdef EXPERIMENTAL_BRIGHTMAIL
0, /* bmi_run */
- #endif
+#endif
0, /* debug */
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */
- #ifndef DISABLE_PRDR
+# ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif
+# endif
(1<<ACL_WHERE_NOTSMTP_START),
- #endif
+#endif
- #ifdef EXPERIMENTAL_DMARC
+#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
+#endif
(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_NOTSMTP_START)|
(1<<ACL_WHERE_PREDATA)|
(1<<ACL_WHERE_NOTSMTP_START)),
- #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
(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
+#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif
+#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
+#ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif
+#endif
(1<<ACL_WHERE_MIME)),
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ 0, /* utf8_downconvert */
+#endif
+
(1<<ACL_WHERE_NOTSMTP)| /* no_multiline */
(1<<ACL_WHERE_NOTSMTP_START),
} control_def;
static control_def controls_list[] = {
- { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE },
+ { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE },
#ifdef EXPERIMENTAL_BRIGHTMAIL
- { US"bmi_run", CONTROL_BMI_RUN, FALSE },
+ { US"bmi_run", CONTROL_BMI_RUN, FALSE },
#endif
- { US"debug", CONTROL_DEBUG, TRUE },
+ { US"debug", CONTROL_DEBUG, TRUE },
#ifndef DISABLE_DKIM
- { US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE },
+ { 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 },
+ { 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 },
- { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE },
- { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE },
- { US"freeze", CONTROL_FREEZE, TRUE },
- { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE },
- { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE },
- { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE },
- { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE },
- { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE },
- { US"queue_only", CONTROL_QUEUE_ONLY, FALSE },
+ { US"dscp", CONTROL_DSCP, TRUE },
+ { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
+ { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE },
+ { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE },
+ { US"freeze", CONTROL_FREEZE, TRUE },
+ { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE },
+ { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE },
+ { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE },
+ { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE },
+ { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE },
+ { US"queue_only", CONTROL_QUEUE_ONLY, FALSE },
#ifdef WITH_CONTENT_SCAN
- { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE },
+ { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE },
#endif
- { US"fakedefer", CONTROL_FAKEDEFER, TRUE },
- { US"fakereject", CONTROL_FAKEREJECT, TRUE },
- { US"submission", CONTROL_SUBMISSION, TRUE },
+ { US"fakedefer", CONTROL_FAKEDEFER, TRUE },
+ { US"fakereject", CONTROL_FAKEREJECT, TRUE },
+ { US"submission", CONTROL_SUBMISSION, TRUE },
{ US"suppress_local_fixups", CONTROL_SUPPRESS_LOCAL_FIXUPS, FALSE },
- { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE }
+ { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE },
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ { US"utf8_downconvert", CONTROL_UTF8_DOWNCONVERT, TRUE }
+#endif
};
/* Support data structures for Client SMTP Authorization. acl_verify_csa()
sender_vaddr = deliver_make_addr(verify_sender_address, TRUE);
#ifdef EXPERIMENTAL_INTERNATIONAL
- sender_vaddr->prop.utf8 = message_smtputf8;
+ if ((sender_vaddr->prop.utf8_msg = message_smtputf8))
+ {
+ sender_vaddr->prop.utf8_downcvt = message_utf8_downconvert == 1;
+ sender_vaddr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1;
+ }
#endif
if (no_details) setflag(sender_vaddr, af_sverify_told);
if (verify_sender_address[0] != 0)
break;
case CONTROL_CUTTHROUGH_DELIVERY:
+#ifndef DISABLE_PRDR
if (prdr_requested)
+#else
+ if (0)
+#endif
/* 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" */
arg, *log_msgptr);
}
return ERROR;
+
+ #ifdef EXPERIMENTAL_INTERNATIONAL
+ case CONTROL_UTF8_DOWNCONVERT:
+ if (*p == '/')
+ {
+ if (p[1] == '1')
+ {
+ message_utf8_downconvert = 1;
+ addr->prop.utf8_downcvt = TRUE;
+ addr->prop.utf8_downcvt_maybe = FALSE;
+ p += 2;
+ break;
+ }
+ if (p[1] == '0')
+ {
+ message_utf8_downconvert = 0;
+ addr->prop.utf8_downcvt = FALSE;
+ addr->prop.utf8_downcvt_maybe = FALSE;
+ p += 2;
+ break;
+ }
+ if (p[1] == '-' && p[2] == '1')
+ {
+ message_utf8_downconvert = -1;
+ addr->prop.utf8_downcvt = FALSE;
+ addr->prop.utf8_downcvt_maybe = TRUE;
+ p += 3;
+ break;
+ }
+ *log_msgptr = US"bad option value for control=utf8_downconvert";
+ }
+ else
+ {
+ message_utf8_downconvert = 1;
+ addr->prop.utf8_downcvt = TRUE;
+ addr->prop.utf8_downcvt_maybe = FALSE;
+ break;
+ }
+ return ERROR;
+ #endif
+
}
break;
}
/* Run the dcc backend. */
rc = dcc_process(&ss);
/* Modify return code based upon the existance 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
debug_printf("delay skipped in -bh checking mode\n");
}
+ /* NOTE 1: Remember that we may be
+ dealing with stdin/stdout here, in addition to TCP/IP connections.
+ Also, delays may be specified for non-SMTP input, where smtp_out and
+ smtp_in will be NULL. Whatever is done must work in all cases.
+
+ NOTE 2: The added feature of flushing the output before a delay must
+ apply only to SMTP input. Hence the test for smtp_out being non-NULL.
+ */
+
+ else
+ {
+ if (smtp_out != NULL && !disable_delay_flush)
+ mac_smtp_fflush();
+
+#if !defined(NO_POLL_H) && defined (POLLRDHUP)
+ {
+ struct pollfd p;
+ nfds_t n = 0;
+ if (smtp_out)
+ {
+ p.fd = fileno(smtp_out);
+ p.events = POLLRDHUP;
+ n = 1;
+ }
+ if (poll(&p, n, delay*1000) > 0)
+ HDEBUG(D_acl) debug_printf("delay cancelled by peer close\n");
+ }
+#else
/* It appears to be impossible to detect that a TCP/IP connection has
gone away without reading from it. This means that we cannot shorten
the delay below if the client goes away, because we cannot discover
Exim process is not held up unnecessarily. However, it seems that we
can't. The poll() function does not do the right thing, and in any case
it is not always available.
-
- NOTE 1: If ever this state of affairs changes, remember that we may be
- dealing with stdin/stdout here, in addition to TCP/IP connections.
- Also, delays may be specified for non-SMTP input, where smtp_out and
- smtp_in will be NULL. Whatever is done must work in all cases.
-
- NOTE 2: The added feature of flushing the output before a delay must
- apply only to SMTP input. Hence the test for smtp_out being non-NULL.
*/
- else
- {
- if (smtp_out != NULL && !disable_delay_flush) mac_smtp_fflush();
while (delay > 0) delay = sleep(delay);
+#endif
}
}
}
rc = verify_check_this_host(&arg, sender_host_cache, NULL,
(sender_host_address == NULL)? US"" : sender_host_address,
CUSS &host_data);
- if (host_data != NULL) host_data = string_copy_malloc(host_data);
+ if (rc == DEFER) *log_msgptr = search_error_message;
+ if (host_data) host_data = string_copy_malloc(host_data);
break;
case ACLC_LOCAL_PARTS:
int sep = 0;
const uschar *s = arg;
uschar *ss;
- while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))
- != NULL)
+ while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)))
{
if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN;
else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC;
case ACL_WARN:
if (cond == OK)
acl_warn(where, *user_msgptr, *log_msgptr);
- else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0)
+ 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": ",
return DEFER;
}
#ifdef EXPERIMENTAL_INTERNATIONAL
- addr->prop.utf8 = message_smtputf8;
+ if ((addr->prop.utf8_msg = message_smtputf8))
+ {
+ addr->prop.utf8_downcvt = message_utf8_downconvert == 1;
+ addr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1;
+ }
#endif
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;