X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Facl.c;h=ef8f06da644d4e30475bd158af59abc0249b43d2;hb=b2f5a03200c914f601bc9d28c6e069316a3b20eb;hp=08c69c679c78d62c48babb957ea1c0685bf753de;hpb=2a4be8f93bd41c49707fe5e6ce2d782b709b551c;p=exim.git diff --git a/src/src/acl.c b/src/src/acl.c index 08c69c679..ef8f06da6 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.36 2005/05/31 10:58:18 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.50 2005/10/03 13:25:33 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -81,7 +81,9 @@ ACLC_CONDITION, ACLC_CONTROL, "log_message", "logwrite", and "set" are modifiers that look like conditions but always return TRUE. They are used for their side effects. */ -static uschar *conditions[] = { US"acl", US"authenticated", +static uschar *conditions[] = { + US"acl", + US"authenticated", #ifdef EXPERIMENTAL_BRIGHTMAIL US"bmi_optin", #endif @@ -125,11 +127,45 @@ static uschar *conditions[] = { US"acl", US"authenticated", #endif US"verify" }; -/* ACL control names */ -static uschar *controls[] = { US"error", US"caseful_local_part", +/* Return values from decode_control(); keep in step with the table of names +that follows! */ + +enum { +#ifdef EXPERIMENTAL_BRIGHTMAIL + CONTROL_BMI_RUN, +#endif +#ifdef EXPERIMENTAL_DOMAINKEYS + CONTROL_DK_VERIFY, +#endif + CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART, + CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE, + CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_SUPPRESS_LOCAL_FIXUPS, +#ifdef WITH_CONTENT_SCAN + CONTROL_NO_MBOX_UNSPOOL, +#endif + CONTROL_FAKEDEFER, CONTROL_FAKEREJECT, CONTROL_NO_MULTILINE }; + +/* ACL control names; keep in step with the table above! This list is used for +turning ids into names. The actual list of recognized names is in the variable +control_def controls_list[] below. The fact that there are two lists is a mess +and should be tidied up. */ + +static uschar *controls[] = { + #ifdef EXPERIMENTAL_BRIGHTMAIL + US"bmi_run", + #endif + #ifdef EXPERIMENTAL_DOMAINKEYS + US"dk_verify", + #endif + 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"suppress_local_fixups", + #ifdef WITH_CONTENT_SCAN + US"no_mbox_unspool", + #endif + + 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 @@ -412,23 +448,6 @@ static unsigned int cond_forbids[] = { }; -/* Return values from decode_control() */ - -enum { -#ifdef EXPERIMENTAL_BRIGHTMAIL - CONTROL_BMI_RUN, -#endif -#ifdef EXPERIMENTAL_DOMAINKEYS - CONTROL_DK_VERIFY, -#endif - CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART, - CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE, - CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, -#ifdef WITH_CONTENT_SCAN - CONTROL_NO_MBOX_UNSPOOL, -#endif - CONTROL_FAKEDEFER, CONTROL_FAKEREJECT, CONTROL_NO_MULTILINE }; - /* Bit map vector of which controls are not allowed at certain times. For each control, there's a bitmap of dis-allowed times. For some, it is easier to specify the negation of a small number of allowed times. */ @@ -467,6 +486,10 @@ static unsigned int control_forbids[] = { ~((1<name, domain); /* Now we are ready to do the actual DNS lookup(s). */ +found = domain; switch (dns_special_lookup(&dnsa, domain, T_CSA, &found)) { /* If something bad happened (most commonly DNS_AGAIN), defer. */ @@ -1322,6 +1347,7 @@ BOOL verify_header_sender = FALSE; BOOL defer_ok = FALSE; BOOL callout_defer_ok = FALSE; BOOL no_details = FALSE; +BOOL success_on_redirect = FALSE; address_item *sender_vaddr = NULL; uschar *verify_sender_address = NULL; uschar *pm_mailfrom = NULL; @@ -1359,11 +1385,13 @@ if (strcmpic(ss, US"certificate") == 0) return FAIL; } -/* We can test the result of optional HELO verification */ +/* We can test the result of optional HELO verification that might have +occurred earlier. If not, we can attempt the verification now. */ if (strcmpic(ss, US"helo") == 0) { if (slash != NULL) goto NO_OPTIONS; + if (!helo_verified && !helo_verify_failed) smtp_verify_helo(); return helo_verified? OK : FAIL; } @@ -1388,18 +1416,29 @@ 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; - } + if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL; 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 no recipient of this message is "blind", that is, every envelope +recipient must be mentioned in either To: or Cc:. */ + +if (strcmpic(ss, US"not_blind") == 0) + { + if (slash != NULL) goto NO_OPTIONS; + if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL; + rc = verify_check_notblind(); + if (rc != OK) + { + *log_msgptr = string_sprintf("bcc recipient detected"); + if (smtp_return_error_details) + *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr); + } + return rc; + } /* The remaining verification tests check recipient and sender addresses, either from the envelope or from the header. There are a number of @@ -1412,12 +1451,7 @@ sender and recipient. */ 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; - } + if (where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP) goto WRONG_ACL; verify_header_sender = TRUE; } @@ -1464,6 +1498,7 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)) { if (strcmpic(ss, US"defer_ok") == 0) defer_ok = TRUE; else if (strcmpic(ss, US"no_details") == 0) no_details = TRUE; + else if (strcmpic(ss, US"success_on_redirect") == 0) success_on_redirect = TRUE; /* These two old options are left for backwards compatibility */ @@ -1721,6 +1756,9 @@ else if (verify_sender_address != NULL) else verify_options |= vopt_fake_sender; + if (success_on_redirect) + verify_options |= vopt_success_on_redirect; + /* The recipient, qualify, and expn options are never set in verify_options. */ @@ -1772,6 +1810,9 @@ else { address_item addr2; + if (success_on_redirect) + verify_options |= vopt_success_on_redirect; + /* We must use a copy of the address for verification, because it might get rewritten. */ @@ -1847,6 +1888,13 @@ NO_OPTIONS: *log_msgptr = string_sprintf("unexpected '/' found in \"%s\" " "(this verify item has no options)", arg); return ERROR; + +/* Calls in the wrong ACL come here */ + +WRONG_ACL: +*log_msgptr = string_sprintf("cannot check header contents in ACL for %s " + "(only possible in ACL for DATA)", acl_wherenames[where]); +return ERROR; } @@ -1920,7 +1968,7 @@ static int acl_ratelimit(uschar *arg, uschar **log_msgptr) { double limit, period; -uschar *ss, *key = arg; +uschar *ss, *key; int sep = '/'; BOOL have_key = FALSE, leaky = FALSE, strict = FALSE; BOOL per_byte = FALSE, per_cmd = FALSE, per_conn = FALSE, per_mail = FALSE; @@ -1956,6 +2004,12 @@ if (limit < 0.0 || *ss != 0) return ERROR; } +/* We use the rest of the argument list following the limit as the +lookup key, because it doesn't make sense to use the same stored data +if the period or options are different. */ + +key = arg; + /* Second is the rate measurement period and exponential smoothing time constant. This must be strictly greater than zero, because zero leads to run-time division errors. */ @@ -1996,9 +2050,7 @@ if (leaky + strict > 1 || per_byte + per_cmd + per_conn + per_mail > 1) if (!strict) leaky = TRUE; if (!per_byte && !per_cmd && !per_conn) per_mail = TRUE; -/* We use the whole of the argument list as the lookup key, because it doesn't -make sense to use the same stored data if any of the arguments are different. -If there is no explicit key, use the sender_host_address. If there is no +/* If there is no explicit key, use the sender_host_address. If there is no sender_host_address (e.g. -bs or acl_not_smtp) then we simply omit it. */ if (!have_key && sender_host_address != NULL) @@ -2007,8 +2059,7 @@ if (!have_key && sender_host_address != NULL) HDEBUG(D_acl) debug_printf("ratelimit condition limit=%.0f period=%.0f key=%s\n", limit, period, key); -/* If we are dealing with rate limits per connection, per message, or per byte, -see if we have already computed the rate by looking in the relevant tree. For +/* See if we have already computed the rate by looking in the relevant tree. For per-connection rate limiting, store tree nodes and dbdata in the permanent pool so that they survive across resets. */ @@ -2020,8 +2071,10 @@ if (per_conn) anchor = &ratelimiters_conn; store_pool = POOL_PERM; } -if (per_mail || per_byte) +else if (per_mail || per_byte) anchor = &ratelimiters_mail; +else if (per_cmd) + anchor = &ratelimiters_cmd; if (anchor != NULL && (t = tree_search(*anchor, key)) != NULL) { @@ -2109,16 +2162,16 @@ else + (double)tv.tv_usec / 1000000.0; double prev_time = (double)dbd->time_stamp + (double)dbd->time_usec / 1000000.0; - double interval = this_time - prev_time; - - double i_over_p = interval / period; - double a = exp(-i_over_p); /* We must avoid division by zero, and deal gracefully with the clock going backwards. If we blunder ahead when time is in reverse then the computed - rate will become bogusly huge. Clamp i/p to a very small number instead. */ + rate will be bogus. To be safe we clamp interval to a very small number. */ - if (i_over_p <= 0.0) i_over_p = 1e-9; + double interval = this_time - prev_time <= 0.0 ? 1e-9 + : this_time - prev_time; + + double i_over_p = interval / period; + double a = exp(-i_over_p); dbd->time_stamp = tv.tv_sec; dbd->time_usec = tv.tv_usec; @@ -2444,11 +2497,13 @@ for (; cb != NULL; cb = cb->next) 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 != '/') pp++; - originator_name = string_copy(parse_fix_phrase(p+6, 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; } @@ -2460,6 +2515,10 @@ for (; cb != NULL; cb = cb->next) return ERROR; } break; + + case CONTROL_SUPPRESS_LOCAL_FIXUPS: + suppress_local_fixups = TRUE; + break; } break; @@ -3111,7 +3170,7 @@ if (Ustrchr(ss, ' ') == NULL) return ERROR; } acl_text[statbuf.st_size] = 0; - close(fd); + (void)close(fd); acl_name = string_sprintf("ACL \"%s\"", ss); HDEBUG(D_acl) debug_printf("read ACL from file %s\n", ss); @@ -3298,7 +3357,7 @@ acl_check_internal() to do the actual work. Arguments: where ACL_WHERE_xxxx indicating where called from - data_string RCPT address, or SMTP command argument, or NULL + recipient RCPT address for RCPT check, else NULL s the input string; NULL is the same as an empty ACL => DENY user_msgptr where to put a user error (for SMTP response) log_msgptr where to put a logging message (not for SMTP response) @@ -3312,21 +3371,22 @@ Returns: OK access is granted by an ACCEPT verb */ int -acl_check(int where, uschar *data_string, uschar *s, uschar **user_msgptr, +acl_check(int where, uschar *recipient, uschar *s, uschar **user_msgptr, uschar **log_msgptr) { int rc; address_item adb; -address_item *addr; +address_item *addr = NULL; *user_msgptr = *log_msgptr = NULL; sender_verified_failed = NULL; +ratelimiters_cmd = NULL; if (where == ACL_WHERE_RCPT) { adb = address_defaults; addr = &adb; - addr->address = data_string; + addr->address = recipient; if (deliver_split_address(addr) == DEFER) { *log_msgptr = US"defer in percent_hack_domains check"; @@ -3335,16 +3395,11 @@ if (where == ACL_WHERE_RCPT) deliver_domain = addr->domain; deliver_localpart = addr->local_part; } -else - { - addr = NULL; - smtp_command_argument = data_string; - } rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr); -smtp_command_argument = deliver_domain = - deliver_localpart = deliver_address_data = sender_address_data = NULL; +deliver_domain = deliver_localpart = deliver_address_data = + sender_address_data = NULL; /* A DISCARD response is permitted only for message ACLs, excluding the PREDATA ACL, which is really in the middle of an SMTP command. */