X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Facl.c;h=e3efb7ed873c627b89474a8940ff3dab13824e5b;hb=27f9999e2828002705cabd10ef62ce86378287e3;hp=6c81d349dea6e1a5dd96878fc22e522b42d3605e;hpb=a453e37fa4f71167b49e96e7a33c843aea45d390;p=exim.git diff --git a/src/src/acl.c b/src/src/acl.c index 6c81d349d..e3efb7ed8 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -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<next); +while((h = h->next)); ret[ptr-1] = '\0'; /* overwrite last newline */ return ret; @@ -2726,6 +2822,106 @@ 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 host_af; +int len; +int r, s; + +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); + +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 = Ustrlen(arg); +r = send(s, arg, len, MSG_NOSIGNAL); +if (r < 0) goto defer; +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", strerror(errno)); +return DEFER; +} + + + /************************************************* * Handle conditions/modifiers on an ACL item * *************************************************/ @@ -2933,6 +3129,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 @@ -3226,6 +3437,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 +3652,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 @@ -4006,6 +4233,34 @@ return search_find_defer?DEFER: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. @@ -4024,6 +4279,7 @@ Returns: OK access is granted by an ACCEPT verb 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, @@ -4038,7 +4294,11 @@ sender_verified_failed = NULL; ratelimiters_cmd = NULL; log_reject_target = LOG_MAIN|LOG_REJECT; -if (where == ACL_WHERE_RCPT) +#ifdef EXPERIMENTAL_PRDR +if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR ) +#else +if (where == ACL_WHERE_RCPT ) +#endif { adb = address_defaults; addr = &adb; @@ -4052,7 +4312,9 @@ if (where == ACL_WHERE_RCPT) 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, @@ -4076,6 +4338,9 @@ If conn-failure, no action (and keep the spooled copy). switch (where) { case ACL_WHERE_RCPT: +#ifdef EXPERIMENTAL_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) @@ -4135,7 +4400,6 @@ return rc; } - /************************************************* * Create ACL variable * *************************************************/