X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/faa05a9388f4efb82db4e7ea20ae746ab62f578a..67791ce4a0ae39a79aab22157d587d774190ac61:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index c1eebf655..6e635fbf1 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 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) */ @@ -21,8 +21,13 @@ enum { ACL_ACCEPT, ACL_DEFER, ACL_DENY, ACL_DISCARD, ACL_DROP, ACL_REQUIRE, /* ACL verbs */ -static uschar *verbs[] = - { US"accept", US"defer", US"deny", US"discard", US"drop", US"require", +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 @@ -66,6 +71,9 @@ enum { ACLC_ACL, #ifndef DISABLE_DKIM ACLC_DKIM_SIGNER, ACLC_DKIM_STATUS, +#endif +#ifdef EXPERIMENTAL_DMARC + ACLC_DMARC_STATUS, #endif ACLC_DNSLISTS, ACLC_DOMAINS, @@ -99,6 +107,7 @@ enum { ACLC_ACL, ACLC_SPF, ACLC_SPF_GUESS, #endif + ACLC_UDPSEND, ACLC_VERIFY }; /* ACL conditions/modifiers: "delay", "control", "continue", "endpass", @@ -129,6 +138,9 @@ static uschar *conditions[] = { #ifndef DISABLE_DKIM US"dkim_signers", US"dkim_status", +#endif +#ifdef EXPERIMENTAL_DMARC + US"dmarc_status", #endif US"dnslists", US"domains", @@ -160,6 +172,7 @@ static uschar *conditions[] = { US"spf", US"spf_guess", #endif + US"udpsend", US"verify" }; @@ -175,6 +188,10 @@ enum { #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, @@ -211,6 +228,10 @@ static uschar *controls[] = { #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", @@ -260,6 +281,9 @@ static uschar cond_expand_at_top[] = { #ifndef DISABLE_DKIM TRUE, /* dkim_signers */ TRUE, /* dkim_status */ +#endif +#ifdef EXPERIMENTAL_DMARC + TRUE, /* dmarc_status */ #endif TRUE, /* dnslists */ FALSE, /* domains */ @@ -293,6 +317,7 @@ static uschar cond_expand_at_top[] = { TRUE, /* spf */ TRUE, /* spf_guess */ #endif + TRUE, /* udpsend */ TRUE /* verify */ }; @@ -321,6 +346,9 @@ static uschar cond_modifiers[] = { #ifndef DISABLE_DKIM FALSE, /* dkim_signers */ FALSE, /* dkim_status */ +#endif +#ifdef EXPERIMENTAL_DMARC + FALSE, /* dmarc_status */ #endif FALSE, /* dnslists */ FALSE, /* domains */ @@ -354,6 +382,7 @@ static uschar cond_modifiers[] = { FALSE, /* spf */ FALSE, /* spf_guess */ #endif + TRUE, /* udpsend */ FALSE /* verify */ }; @@ -368,6 +397,9 @@ static unsigned int cond_forbids[] = { (unsigned int) ~((1<value) *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:. */ @@ -2106,8 +2214,8 @@ return rc; 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; } @@ -2726,6 +2834,110 @@ return rc; +/************************************************* +* 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 * *************************************************/ @@ -2780,12 +2992,14 @@ 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; } @@ -2892,7 +3106,9 @@ for (; cb != NULL; cb = cb->next) /* 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 || @@ -2933,6 +3149,21 @@ for (; cb != NULL; cb = cb->next) #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 @@ -3017,8 +3248,9 @@ for (; cb != NULL; cb = cb->next) 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 == '/') { @@ -3048,10 +3280,12 @@ for (; cb != NULL; cb = cb->next) *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: @@ -3118,17 +3352,19 @@ for (; cb != NULL; cb = cb->next) 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; + *log_msgptr = US"frozen"; + else if (queue_only_policy) + *log_msgptr = US"queue-only"; + else if (fake_response == FAIL) + *log_msgptr = US"fakereject"; + else + { + cutthrough_delivery = TRUE; + break; + } + *log_msgptr = string_sprintf("\"control=%s\" on %s item", + arg, *log_msgptr); + return ERROR; } break; @@ -3226,6 +3462,18 @@ for (; cb != NULL; cb = cb->next) 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; @@ -3429,6 +3677,10 @@ for (; cb != NULL; cb = cb->next) 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 @@ -4008,10 +4260,8 @@ return search_find_defer?DEFER:ERROR; /* Alternate interface for ACL, used by expansions */ int -acl_eval(int where, uschar *recipient, uschar *s, uschar **user_msgptr, - uschar **log_msgptr) +acl_eval(int where, uschar *s, uschar **user_msgptr, uschar **log_msgptr) { -int rc; address_item adb; address_item *addr = NULL; @@ -4024,14 +4274,11 @@ if (where == ACL_WHERE_RCPT) { adb = address_defaults; addr = &adb; - addr->address = recipient; - if (deliver_split_address(addr) == DEFER) - { - *log_msgptr = US"defer in percent_hack_domains check"; - return DEFER; - } - deliver_domain = addr->domain; - deliver_localpart = addr->local_part; + 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); @@ -4072,7 +4319,11 @@ sender_verified_failed = NULL; ratelimiters_cmd = NULL; log_reject_target = LOG_MAIN|LOG_REJECT; -if (where == ACL_WHERE_RCPT) +#ifndef DISABLE_PRDR +if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR ) +#else +if (where == ACL_WHERE_RCPT ) +#endif { adb = address_defaults; addr = &adb; @@ -4112,6 +4363,9 @@ If conn-failure, no action (and keep the spooled copy). switch (where) { case ACL_WHERE_RCPT: +#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) @@ -4229,4 +4483,6 @@ FILE *f = (FILE *)ctx; 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 */