X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/dc08244915d761836957db9d4fa491e48d212d81..e5a9dba621b4301bfbe2bc05576ddc5ec752b1b5:/src/src/acl.c diff --git a/src/src/acl.c b/src/src/acl.c index fb8590132..b91d9d041 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.16 2005/01/12 14:41:12 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.29 2005/05/10 10:19:11 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -45,7 +45,15 @@ ACLC_CONDITION, ACLC_CONTROL, ACLC_DELAY, #ifdef WITH_OLD_DEMIME ACLC_DEMIME, -#endif +#endif +#ifdef EXPERIMENTAL_DOMAINKEYS + ACLC_DK_DOMAIN_SOURCE, + ACLC_DK_POLICY, + ACLC_DK_SENDER_DOMAINS, + ACLC_DK_SENDER_LOCAL_PARTS, + ACLC_DK_SENDERS, + ACLC_DK_STATUS, +#endif ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS, ACLC_HOSTS, ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE, #ifdef WITH_CONTENT_SCAN @@ -61,7 +69,7 @@ ACLC_CONDITION, ACLC_CONTROL, #endif ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET, #ifdef WITH_CONTENT_SCAN - ACLC_SPAM, + ACLC_SPAM, #endif #ifdef EXPERIMENTAL_SPF ACLC_SPF, @@ -77,13 +85,21 @@ static uschar *conditions[] = { US"acl", US"authenticated", US"bmi_optin", #endif US"condition", - US"control", + US"control", #ifdef WITH_CONTENT_SCAN US"decode", #endif US"delay", #ifdef WITH_OLD_DEMIME US"demime", +#endif +#ifdef EXPERIMENTAL_DOMAINKEYS + US"dk_domain_source", + US"dk_policy", + US"dk_sender_domains", + US"dk_sender_local_parts", + US"dk_senders", + US"dk_status", #endif US"dnslists", US"domains", US"encrypted", US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite", @@ -106,12 +122,12 @@ static uschar *conditions[] = { US"acl", US"authenticated", US"spf", #endif US"verify" }; - + /* ACL control names */ static uschar *controls[] = { US"error", US"caseful_local_part", US"caselower_local_part", US"enforce_sync", US"no_enforce_sync", US"freeze", - US"queue_only", US"submission", US"no_multiline"}; + US"queue_only", US"submission", US"no_multiline"}; /* Flags to indicate for which conditions /modifiers a string expansion is done at the outer level. In the other cases, expansion already occurs in the @@ -122,7 +138,7 @@ static uschar cond_expand_at_top[] = { FALSE, /* authenticated */ #ifdef EXPERIMENTAL_BRIGHTMAIL TRUE, /* bmi_optin */ -#endif +#endif TRUE, /* condition */ TRUE, /* control */ #ifdef WITH_CONTENT_SCAN @@ -131,6 +147,14 @@ static uschar cond_expand_at_top[] = { TRUE, /* delay */ #ifdef WITH_OLD_DEMIME TRUE, /* demime */ +#endif +#ifdef EXPERIMENTAL_DOMAINKEYS + TRUE, /* dk_domain_source */ + TRUE, /* dk_policy */ + TRUE, /* dk_sender_domains */ + TRUE, /* dk_sender_local_parts */ + TRUE, /* dk_senders */ + TRUE, /* dk_status */ #endif TRUE, /* dnslists */ FALSE, /* domains */ @@ -170,7 +194,7 @@ static uschar cond_modifiers[] = { FALSE, /* authenticated */ #ifdef EXPERIMENTAL_BRIGHTMAIL TRUE, /* bmi_optin */ -#endif +#endif FALSE, /* condition */ TRUE, /* control */ #ifdef WITH_CONTENT_SCAN @@ -179,6 +203,14 @@ static uschar cond_modifiers[] = { TRUE, /* delay */ #ifdef WITH_OLD_DEMIME FALSE, /* demime */ +#endif +#ifdef EXPERIMENTAL_DOMAINKEYS + FALSE, /* dk_domain_source */ + FALSE, /* dk_policy */ + FALSE, /* dk_sender_domains */ + FALSE, /* dk_sender_local_parts */ + FALSE, /* dk_senders */ + FALSE, /* dk_status */ #endif FALSE, /* dnslists */ FALSE, /* domains */ @@ -212,120 +244,132 @@ static uschar cond_modifiers[] = { }; /* Bit map vector of which conditions are not allowed at certain times. For -each condition, there's a bitmap of dis-allowed times. */ +each condition, there's a bitmap of dis-allowed times. For some, it is easier +to specify the negation of a small number of allowed times. */ static unsigned int cond_forbids[] = { 0, /* acl */ - + (1<type != T_A + #if HAVE_IPV6 + && rr->type != T_AAAA + #ifdef SUPPORT_A6 + && rr->type != T_A6 + #endif + #endif + ) continue; + + if (strcmpic(target, rr->name) != 0) continue; + + target_found = TRUE; + + /* Turn the target address RR into a list of textual IP addresses and scan + the list. There may be more than one if it is an A6 RR. */ + + for (da = dns_address_from_rr(dnsa, rr); da != NULL; da = da->next) + { + /* If the client IP address matches the target IP address, it's good! */ + + DEBUG(D_acl) debug_printf("CSA target address is %s\n", da->address); + + if (strcmpic(sender_host_address, da->address) == 0) return CSA_OK; + } + } + +/* If we found some target addresses but none of them matched, the client is +using an unauthorized IP address, otherwise the target has no authorized IP +addresses. */ + +if (target_found) return CSA_FAIL_MISMATCH; +else return CSA_FAIL_NOADDR; +} + + + +/************************************************* +* Verify Client SMTP Authorization * +*************************************************/ + +/* Called from acl_verify() below. This routine calls dns_lookup_special() +to find the CSA SRV record corresponding to the domain argument, or +$sender_helo_name if no argument is provided. It then checks that the +client is authorized, and that its IP address corresponds to the SRV +target's address by calling acl_verify_csa_address() above. The address +should have been returned in the DNS response's ADDITIONAL section, but if +not we perform another DNS lookup to get it. + +Arguments: + domain pointer to optional parameter following verify = csa + +Returns: CSA_UNKNOWN no valid CSA record found + CSA_OK successfully authorized + CSA_FAIL_* client is definitely not authorized + CSA_DEFER_* there was a DNS problem +*/ + +static int +acl_verify_csa(uschar *domain) +{ +tree_node *t; +uschar *found, *p; +int priority, weight, port; +dns_answer dnsa; +dns_scan dnss; +dns_record *rr; +int rc, type; +uschar target[256]; + +/* Work out the domain we are using for the CSA lookup. The default is the +client's HELO domain. If the client has not said HELO, use its IP address +instead. If it's a local client (exim -bs), CSA isn't applicable. */ + +while (isspace(*domain) && *domain != '\0') ++domain; +if (*domain == '\0') domain = sender_helo_name; +if (domain == NULL) domain = sender_host_address; +if (sender_host_address == NULL) return CSA_UNKNOWN; + +/* If we have an address literal, strip off the framing ready for turning it +into a domain. The framing consists of matched square brackets possibly +containing a keyword and a colon before the actual IP address. */ + +if (domain[0] == '[') + { + uschar *start = Ustrchr(domain, ':'); + if (start == NULL) start = domain; + domain = string_copyn(start + 1, Ustrlen(start) - 2); + } + +/* Turn domains that look like bare IP addresses into domains in the reverse +DNS. This code also deals with address literals and $sender_host_address. It's +not quite kosher to treat bare domains such as EHLO 192.0.2.57 the same as +address literals, but it's probably the most friendly thing to do. This is an +extension to CSA, so we allow it to be turned off for proper conformance. */ + +if (string_is_ip_address(domain, NULL)) + { + if (!dns_csa_use_reverse) return CSA_UNKNOWN; + dns_build_reverse(domain, target); + domain = target; + } + +/* Find out if we've already done the CSA check for this domain. If we have, +return the same result again. Otherwise build a new cached result structure +for this domain. The name is filled in now, and the value is filled in when +we return from this function. */ + +t = tree_search(csa_cache, domain); +if (t != NULL) return t->data.val; + +t = store_get_perm(sizeof(tree_node) + Ustrlen(domain)); +Ustrcpy(t->name, domain); +(void)tree_insertnode(&csa_cache, t); + +/* Now we are ready to do the actual DNS lookup(s). */ + +switch (dns_special_lookup(&dnsa, domain, T_CSA, &found)) + { + /* If something bad happened (most commonly DNS_AGAIN), defer. */ + + default: + return t->data.val = CSA_DEFER_SRV; + + /* If we found nothing, the client's authorization is unknown. */ + + case DNS_NOMATCH: + case DNS_NODATA: + return t->data.val = CSA_UNKNOWN; + + /* We got something! Go on to look at the reply in more detail. */ + + case DNS_SUCCEED: + break; + } + +/* Scan the reply for well-formed CSA SRV records. */ + +for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); + rr != NULL; + rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) + { + if (rr->type != T_SRV) continue; + + /* Extract the numerical SRV fields (p is incremented) */ + + p = rr->data; + GETSHORT(priority, p); + GETSHORT(weight, p); + GETSHORT(port, p); + + DEBUG(D_acl) + debug_printf("CSA priority=%d weight=%d port=%d\n", priority, weight, port); + + /* Check the CSA version number */ + + if (priority != 1) continue; + + /* If the domain does not have a CSA SRV record of its own (i.e. the domain + found by dns_special_lookup() is a parent of the one we asked for), we check + the subdomain assertions in the port field. At the moment there's only one + assertion: legitimate SMTP clients are all explicitly authorized with CSA + SRV records of their own. */ + + if (found != domain) + { + if (port & 1) + return t->data.val = CSA_FAIL_EXPLICIT; + else + return t->data.val = CSA_UNKNOWN; + } + + /* This CSA SRV record refers directly to our domain, so we check the value + in the weight field to work out the domain's authorization. 0 and 1 are + unauthorized; 3 means the client is authorized but we can't check the IP + address in order to authenticate it, so we treat it as unknown; values + greater than 3 are undefined. */ + + if (weight < 2) return t->data.val = CSA_FAIL_DOMAIN; + + if (weight > 2) continue; + + /* Weight == 2, which means the domain is authorized. We must check that the + client's IP address is listed as one of the SRV target addresses. Save the + target hostname then break to scan the additional data for its addresses. */ + + (void)dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p, + (DN_EXPAND_ARG4_TYPE)target, sizeof(target)); + + DEBUG(D_acl) debug_printf("CSA target is %s\n", target); + + break; + } + +/* If we didn't break the loop then no appropriate records were found. */ + +if (rr == NULL) return t->data.val = CSA_UNKNOWN; + +/* Do not check addresses if the target is ".", in accordance with RFC 2782. +A target of "." indicates there are no valid addresses, so the client cannot +be authorized. (This is an odd configuration because weight=2 target=. is +equivalent to weight=1, but we check for it in order to keep load off the +root name servers.) Note that dn_expand() turns "." into "". */ + +if (Ustrcmp(target, "") == 0) return t->data.val = CSA_FAIL_NOADDR; + +/* Scan the additional section of the CSA SRV reply for addresses belonging +to the target. If the name server didn't return any additional data (e.g. +because it does not fully support SRV records), we need to do another lookup +to obtain the target addresses; otherwise we have a definitive result. */ + +rc = acl_verify_csa_address(&dnsa, &dnss, RESET_ADDITIONAL, target); +if (rc != CSA_FAIL_NOADDR) return t->data.val = rc; + +/* The DNS lookup type corresponds to the IP version used by the client. */ + +#if HAVE_IPV6 +if (Ustrchr(sender_host_address, ':') != NULL) + type = T_AAAA; +else +#endif /* HAVE_IPV6 */ + type = T_A; + + +#if HAVE_IPV6 && defined(SUPPORT_A6) +DNS_LOOKUP_AGAIN: +#endif + +switch (dns_lookup(&dnsa, target, type, NULL)) + { + /* If something bad happened (most commonly DNS_AGAIN), defer. */ + + default: + 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 */ + + /* 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) + we have to do yet another lookup in order to check them. */ + + 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; + } +} + + + /************************************************* * Handle verification (address & other) * *************************************************/ @@ -935,6 +1314,13 @@ address_item *sender_vaddr = NULL; uschar *verify_sender_address = NULL; uschar *pm_mailfrom = NULL; uschar *se_mailfrom = NULL; + +/* Some of the verify items have slash-separated options; some do not. Diagnose +an error if options are given for items that don't expect them. This code has +now got very message. Refactoring to use a table would be a good idea one day. +*/ + +uschar *slash = Ustrchr(arg, '/'); uschar *list = arg; uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); @@ -944,6 +1330,7 @@ if (ss == NULL) goto BAD_VERIFY; if (strcmpic(ss, US"reverse_host_lookup") == 0) { + if (slash != NULL) goto NO_OPTIONS; if (sender_host_address == NULL) return OK; return acl_verify_reverse(user_msgptr, log_msgptr); } @@ -954,6 +1341,7 @@ mandatory verification, the connection doesn't last this long.) */ if (strcmpic(ss, US"certificate") == 0) { + if (slash != NULL) goto NO_OPTIONS; if (tls_certificate_verified) return OK; *user_msgptr = US"no verified certificate"; return FAIL; @@ -961,42 +1349,64 @@ if (strcmpic(ss, US"certificate") == 0) /* We can test the result of optional HELO verification */ -if (strcmpic(ss, US"helo") == 0) return helo_verified? OK : FAIL; +if (strcmpic(ss, US"helo") == 0) + { + if (slash != NULL) goto NO_OPTIONS; + return helo_verified? OK : FAIL; + } + +/* Do Client SMTP Authorization checks in a separate function, and turn the +result code into user-friendly strings. */ -/* Handle header verification options - permitted only after DATA or a non-SMTP -message. */ +if (strcmpic(ss, US"csa") == 0) + { + rc = acl_verify_csa(list); + *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s", + csa_reason_string[rc]); + csa_status = csa_status_string[rc]; + DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status); + return csa_return_code[rc]; + } -if (strncmpic(ss, US"header_", 7) == 0) +/* Check that all relevant header lines have the correct syntax. If there is +a syntax error, we return details of the error to the sender if configured to +send out full details. (But a "message" setting on the ACL can override, as +always). */ + +if (strcmpic(ss, US"header_syntax") == 0) { + if (slash != NULL) goto NO_OPTIONS; if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) { *log_msgptr = string_sprintf("cannot check header contents in ACL for %s " "(only possible in ACL for DATA)", acl_wherenames[where]); return ERROR; } + rc = verify_check_headers(log_msgptr); + if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + return rc; + } - /* Check that all relevant header lines have the correct syntax. If there is - a syntax error, we return details of the error to the sender if configured to - send out full details. (But a "message" setting on the ACL can override, as - always). */ - - if (strcmpic(ss+7, US"syntax") == 0) - { - int rc = verify_check_headers(log_msgptr); - if (rc != OK && smtp_return_error_details && *log_msgptr != NULL) - *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); - return rc; - } - /* Check that there is at least one verifiable sender address in the relevant - header lines. This can be followed by callout and defer options, just like - sender and recipient. */ +/* The remaining verification tests check recipient and sender addresses, +either from the envelope or from the header. There are a number of +slash-separated options that are common to all of them. */ - else if (strcmpic(ss+7, US"sender") == 0) verify_header_sender = TRUE; - /* Unknown verify argument starting with "header_" */ +/* Check that there is at least one verifiable sender address in the relevant +header lines. This can be followed by callout and defer options, just like +sender and recipient. */ - else goto BAD_VERIFY; +if (strcmpic(ss, US"header_sender") == 0) + { + if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) + { + *log_msgptr = string_sprintf("cannot check header contents in ACL for %s " + "(only possible in ACL for DATA)", acl_wherenames[where]); + return ERROR; + } + verify_header_sender = TRUE; } /* Otherwise, first item in verify argument must be "sender" or "recipient". @@ -1034,7 +1444,8 @@ else } } -/* Remaining items are optional */ +/* Remaining items are optional; they apply to sender and recipient +verification, including "header sender" verification. */ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) != NULL) @@ -1071,11 +1482,11 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) uschar *opt; uschar buffer[256]; while (isspace(*ss)) ss++; - - /* This callout option handling code has become a mess as new options - have been added in an ad hoc manner. It should be tidied up into some + + /* This callout option handling code has become a mess as new options + have been added in an ad hoc manner. It should be tidied up into some kind of table-driven thing. */ - + while ((opt = string_nextinlist(&ss, &optsep, buffer, sizeof(buffer))) != NULL) { @@ -1207,13 +1618,19 @@ message if giving out verification details. */ if (verify_header_sender) { + int verrno; rc = verify_check_header_address(user_msgptr, log_msgptr, callout, - callout_overall, callout_connect, se_mailfrom, pm_mailfrom, verify_options); - if (smtp_return_error_details) + callout_overall, callout_connect, se_mailfrom, pm_mailfrom, verify_options, + &verrno); + if (rc != OK) { - if (*user_msgptr == NULL && *log_msgptr != NULL) - *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); - if (rc == DEFER) acl_temp_details = TRUE; + *basic_errno = verrno; + if (smtp_return_error_details) + { + if (*user_msgptr == NULL && *log_msgptr != NULL) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + if (rc == DEFER) acl_temp_details = TRUE; + } } } @@ -1273,7 +1690,7 @@ else if (verify_sender_address != NULL) { BOOL routed = TRUE; uschar *save_address_data = deliver_address_data; - + sender_vaddr = deliver_make_addr(verify_sender_address, TRUE); if (no_details) setflag(sender_vaddr, af_sverify_told); if (verify_sender_address[0] != 0) @@ -1319,16 +1736,16 @@ else if (verify_sender_address != NULL) sender_vaddr->special_action = rc; sender_vaddr->next = sender_verified_list; sender_verified_list = sender_vaddr; - - /* Restore the recipient address data, which might have been clobbered by + + /* Restore the recipient address data, which might have been clobbered by the sender verification. */ - + deliver_address_data = save_address_data; } - + /* Put the sender address_data value into $sender_address_data */ - sender_address_data = sender_vaddr->p.address_data; + sender_address_data = sender_vaddr->p.address_data; } /* A recipient address just gets a straightforward verify; again we must handle @@ -1345,8 +1762,10 @@ else rc = verify_address(&addr2, NULL, verify_options|vopt_is_recipient, callout, callout_overall, callout_connect, se_mailfrom, pm_mailfrom, NULL); HDEBUG(D_acl) debug_printf("----------- end verify ------------\n"); + *log_msgptr = addr2.message; - *user_msgptr = addr2.user_message; + *user_msgptr = (addr2.user_message != NULL)? + addr2.user_message : addr2.message; *basic_errno = addr2.basic_errno; /* Make $address_data visible */ @@ -1400,9 +1819,17 @@ return rc; BAD_VERIFY: *log_msgptr = string_sprintf("expected \"sender[=address]\", \"recipient\", " - "\"header_syntax\" or \"header_sender\" at start of ACL condition " + "\"helo\", \"header_syntax\", \"header_sender\" or " + "\"reverse_host_lookup\" at start of ACL condition " "\"verify %s\"", arg); return ERROR; + +/* Options supplied when not allowed come here */ + +NO_OPTIONS: +*log_msgptr = string_sprintf("unexpected '/' found in \"%s\" " + "(this verify item has no options)", arg); +return ERROR; } @@ -1495,7 +1922,7 @@ int sep = '/'; for (; cb != NULL; cb = cb->next) { uschar *arg; - int control_type; + int control_type; /* The message and log_message items set up messages to be used in case of rejection. They are expanded later. */ @@ -1631,7 +2058,7 @@ for (; cb != NULL; cb = cb->next) *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL", controls[control_type], acl_wherenames[where]); return ERROR; - } + } switch(control_type) { @@ -1640,7 +2067,11 @@ for (; cb != NULL; cb = cb->next) bmi_run = 1; break; #endif - +#ifdef EXPERIMENTAL_DOMAINKEYS + case CONTROL_DK_VERIFY: + dk_do_verify = 1; + break; +#endif case CONTROL_ERROR: return ERROR; @@ -1673,9 +2104,9 @@ for (; cb != NULL; cb = cb->next) case CONTROL_FAKEREJECT: fake_reject = TRUE; if (*p == '/') - { + { uschar *pp = p + 1; - while (*pp != 0) pp++; + while (*pp != 0) pp++; fake_reject_text = expand_string(string_copyn(p+1, pp-p)); p = pp; } @@ -1698,22 +2129,22 @@ for (; cb != NULL; cb = cb->next) case CONTROL_SUBMISSION: submission_mode = TRUE; while (*p == '/') - { + { if (Ustrncmp(p, "/sender_retain", 14) == 0) { p += 14; active_local_sender_retain = TRUE; - active_local_from_check = FALSE; - } + active_local_from_check = FALSE; + } else if (Ustrncmp(p, "/domain=", 8) == 0) { uschar *pp = p + 8; - while (*pp != 0 && *pp != '/') pp++; + while (*pp != 0 && *pp != '/') pp++; submission_domain = string_copyn(p+8, pp-p); - p = pp; + p = pp; } - else break; - } + else break; + } if (*p != 0) { *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg); @@ -1747,10 +2178,10 @@ for (; cb != NULL; cb = cb->next) HDEBUG(D_acl) debug_printf("delay skipped in -bh checking mode\n"); } - else + else { while (delay > 0) delay = sleep(delay); - } + } } } break; @@ -1761,6 +2192,93 @@ for (; cb != NULL; cb = cb->next) break; #endif +#ifdef EXPERIMENTAL_DOMAINKEYS + case ACLC_DK_DOMAIN_SOURCE: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + /* check header source of domain against given string */ + switch (dk_verify_block->address_source) { + case DK_EXIM_ADDRESS_FROM_FROM: + rc = match_isinlist(US"from", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_ADDRESS_FROM_SENDER: + rc = match_isinlist(US"sender", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_ADDRESS_NONE: + rc = match_isinlist(US"none", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + } + break; + case ACLC_DK_POLICY: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + /* check policy against given string, default FAIL */ + rc = FAIL; + if (dk_verify_block->signsall) + rc = match_isinlist(US"signsall", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + if (dk_verify_block->testing) + rc = match_isinlist(US"testing", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case ACLC_DK_SENDER_DOMAINS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->domain != NULL) + rc = match_isinlist(dk_verify_block->domain, &arg, 0, &domainlist_anchor, + NULL, MCL_DOMAIN, TRUE, NULL); + else rc = FAIL; + break; + case ACLC_DK_SENDER_LOCAL_PARTS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->local_part != NULL) + rc = match_isinlist(dk_verify_block->local_part, &arg, 0, &localpartlist_anchor, + NULL, MCL_LOCALPART, TRUE, NULL); + else rc = FAIL; + break; + case ACLC_DK_SENDERS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->address != NULL) + rc = match_address_list(dk_verify_block->address, TRUE, TRUE, &arg, NULL, -1, 0, NULL); + else rc = FAIL; + break; + case ACLC_DK_STATUS: + if (dk_verify_block == NULL) { rc = FAIL; break; }; + if (dk_verify_block->result > 0) { + switch(dk_verify_block->result) { + case DK_EXIM_RESULT_BAD_FORMAT: + rc = match_isinlist(US"bad format", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_NO_KEY: + rc = match_isinlist(US"no key", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_NO_SIGNATURE: + rc = match_isinlist(US"no signature", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_REVOKED: + rc = match_isinlist(US"revoked", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_NON_PARTICIPANT: + rc = match_isinlist(US"non-participant", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_GOOD: + rc = match_isinlist(US"good", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + case DK_EXIM_RESULT_BAD: + rc = match_isinlist(US"bad", &arg, 0, NULL, + NULL, MCL_STRING, TRUE, NULL); + break; + } + } + break; +#endif + case ACLC_DNSLISTS: rc = verify_check_dnsbl(&arg); break; @@ -1840,7 +2358,7 @@ for (; cb != NULL; cb = cb->next) log_write(0, logbits, "%s", string_printing(s)); } break; - + #ifdef WITH_CONTENT_SCAN case ACLC_MALWARE: { @@ -1930,10 +2448,13 @@ for (; cb != NULL; cb = cb->next) /* 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. */ + only from explicit "message" modifiers. However, put the user message into + $acl_verify_message so it can be used in subsequent conditions or modifiers + (until something changes it). */ case ACLC_VERIFY: rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno); + acl_verify_message = *user_msgptr; if (verb == ACL_WARN) *user_msgptr = NULL; break;