X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Facl.c;h=4ad2b01b9a1cc7f04110b7b45592df706d2f376e;hb=3346ab0144239462a8690e011bff1df8fd504076;hp=d0ed0a51c1089a3aee55d3c120fb57840dc47685;hpb=4c590bd11647b7440bd982a8c72ebcf5c66564b0;p=exim.git diff --git a/src/src/acl.c b/src/src/acl.c index d0ed0a51c..4ad2b01b9 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.72 2007/02/06 12:19:27 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.88 2010/06/06 00:27:52 pdp Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for handling Access Control Lists (ACLs) */ @@ -53,7 +53,11 @@ enum { ACLC_ACL, ACLC_BMI_OPTIN, #endif ACLC_CONDITION, + ACLC_CONTINUE, ACLC_CONTROL, +#ifdef EXPERIMENTAL_DCC + ACLC_DCC, +#endif #ifdef WITH_CONTENT_SCAN ACLC_DECODE, #endif @@ -61,13 +65,9 @@ enum { ACLC_ACL, #ifdef WITH_OLD_DEMIME ACLC_DEMIME, #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, +#ifndef DISABLE_DKIM + ACLC_DKIM_SIGNER, + ACLC_DKIM_STATUS, #endif ACLC_DNSLISTS, ACLC_DOMAINS, @@ -98,13 +98,14 @@ enum { ACLC_ACL, #endif #ifdef EXPERIMENTAL_SPF ACLC_SPF, + ACLC_SPF_GUESS, #endif ACLC_VERIFY }; -/* ACL conditions/modifiers: "delay", "control", "endpass", "message", -"log_message", "log_reject_target", "logwrite", and "set" are modifiers that -look like conditions but always return TRUE. They are used for their side -effects. */ +/* ACL conditions/modifiers: "delay", "control", "continue", "endpass", +"message", "log_message", "log_reject_target", "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", @@ -114,7 +115,11 @@ static uschar *conditions[] = { US"bmi_optin", #endif US"condition", + US"continue", US"control", +#ifdef EXPERIMENTAL_DCC + US"dcc", +#endif #ifdef WITH_CONTENT_SCAN US"decode", #endif @@ -122,13 +127,9 @@ static uschar *conditions[] = { #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", +#ifndef DISABLE_DKIM + US"dkim_signers", + US"dkim_status", #endif US"dnslists", US"domains", @@ -157,6 +158,7 @@ static uschar *conditions[] = { #endif #ifdef EXPERIMENTAL_SPF US"spf", + US"spf_guess", #endif US"verify" }; @@ -169,8 +171,9 @@ enum { #ifdef EXPERIMENTAL_BRIGHTMAIL CONTROL_BMI_RUN, #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - CONTROL_DK_VERIFY, + CONTROL_DEBUG, + #ifndef DISABLE_DKIM + CONTROL_DKIM_VERIFY, #endif CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, @@ -202,8 +205,9 @@ static uschar *controls[] = { #ifdef EXPERIMENTAL_BRIGHTMAIL US"bmi_run", #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - US"dk_verify", + US"debug", + #ifndef DISABLE_DKIM + US"dkim_disable_verify", #endif US"error", US"caseful_local_part", @@ -219,7 +223,7 @@ static uschar *controls[] = { #endif US"fakedefer", US"fakereject", - US"no_multiline", + US"no_multiline_responses", US"no_pipelining", US"no_delay_flush", US"no_callout_flush" @@ -237,7 +241,11 @@ static uschar cond_expand_at_top[] = { TRUE, /* bmi_optin */ #endif TRUE, /* condition */ + TRUE, /* continue */ TRUE, /* control */ +#ifdef EXPERIMENTAL_DCC + TRUE, /* dcc */ +#endif #ifdef WITH_CONTENT_SCAN TRUE, /* decode */ #endif @@ -245,13 +253,9 @@ static uschar cond_expand_at_top[] = { #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 */ +#ifndef DISABLE_DKIM + TRUE, /* dkim_signers */ + TRUE, /* dkim_status */ #endif TRUE, /* dnslists */ FALSE, /* domains */ @@ -282,6 +286,7 @@ static uschar cond_expand_at_top[] = { #endif #ifdef EXPERIMENTAL_SPF TRUE, /* spf */ + TRUE, /* spf_guess */ #endif TRUE /* verify */ }; @@ -296,7 +301,11 @@ static uschar cond_modifiers[] = { TRUE, /* bmi_optin */ #endif FALSE, /* condition */ + TRUE, /* continue */ TRUE, /* control */ +#ifdef EXPERIMENTAL_DCC + FALSE, /* dcc */ +#endif #ifdef WITH_CONTENT_SCAN FALSE, /* decode */ #endif @@ -304,13 +313,9 @@ static uschar cond_modifiers[] = { #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 */ +#ifndef DISABLE_DKIM + FALSE, /* dkim_signers */ + FALSE, /* dkim_status */ #endif FALSE, /* dnslists */ FALSE, /* domains */ @@ -341,13 +346,15 @@ static uschar cond_modifiers[] = { #endif #ifdef EXPERIMENTAL_SPF FALSE, /* spf */ + FALSE, /* spf_guess */ #endif FALSE /* verify */ }; -/* Bit map vector of which conditions are not allowed at certain times. For -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. */ +/* Bit map vector of which conditions and modifiers are not allowed at certain +times. For each condition and modifier, 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 */ @@ -356,6 +363,7 @@ static unsigned int cond_forbids[] = { ~((1< 1 || per_byte + per_cmd + per_conn + per_mail > 1) { *log_msgptr = US"conflicting options for \"ratelimit\" condition"; @@ -2197,21 +2181,32 @@ if (leaky + strict > 1 || per_byte + per_cmd + per_conn + per_mail > 1) } /* Default option values */ + if (!strict) leaky = TRUE; if (!per_byte && !per_cmd && !per_conn) per_mail = TRUE; -/* 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. */ +/* Create the lookup key. If there is no explicit key, use sender_host_address. +If there is no sender_host_address (e.g. -bs or acl_not_smtp) then we simply +omit it. The smoothing constant (sender_rate_period) and the per_xxx options +are added to the key because they alter the meaning of the stored data. */ -if (!have_key && sender_host_address != NULL) - key = string_sprintf("%s / %s", key, sender_host_address); +if (key == NULL) + key = (sender_host_address == NULL)? US"" : sender_host_address; + +key = string_sprintf("%s/%s/%s/%s", + sender_rate_period, + per_byte? US"per_byte" : + per_cmd? US"per_cmd" : + per_mail? US"per_mail" : US"per_conn", + strict? US"strict" : US"leaky", + key); HDEBUG(D_acl) debug_printf("ratelimit condition limit=%.0f period=%.0f key=%s\n", limit, period, key); -/* 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. */ +/* 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. */ anchor = NULL; old_pool = store_pool; @@ -2230,8 +2225,7 @@ if (anchor != NULL && (t = tree_search(*anchor, key)) != NULL) { dbd = t->data.ptr; /* The following few lines duplicate some of the code below. */ - if (dbd->rate < limit) rc = FAIL; - else rc = OK; + rc = (dbd->rate < limit)? FAIL : OK; store_pool = old_pool; sender_rate = string_sprintf("%.1f", dbd->rate); HDEBUG(D_acl) @@ -2240,8 +2234,8 @@ if (anchor != NULL && (t = tree_search(*anchor, key)) != NULL) } /* We aren't using a pre-computed rate, so get a previously recorded -rate from the database, update it, and write it back. If there's no -previous rate for this key, create one. */ +rate from the database, update it, and write it back when required. If there's +no previous rate for this key, create one. */ dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE); if (dbm == NULL) @@ -2346,21 +2340,30 @@ matters for edge cases such the first message sent by a client (which gets the initial rate of 0.0) when the rate limit is zero (i.e. the client should be completely blocked). */ -if (dbd->rate < limit) rc = FAIL; - else rc = OK; +rc = (dbd->rate < limit)? FAIL : OK; /* Update the state if the rate is low or if we are being strict. If we are in leaky mode and the sender's rate is too high, we do not update the recorded rate in order to avoid an over-aggressive sender's retry -rate preventing them from getting any email through. */ +rate preventing them from getting any email through. If noupdate is set, +do not do any updates. */ -if (rc == FAIL || !leaky) +if ((rc == FAIL || !leaky) && !noupdate) + { dbfn_write(dbm, key, dbd, sizeof(dbdata_ratelimit)); + HDEBUG(D_acl) debug_printf("ratelimit db updated\n"); + } +else + { + HDEBUG(D_acl) debug_printf("ratelimit db not updated: %s\n", + noupdate? "noupdate set" : "over the limit, but leaky"); + } + dbfn_close(dbm); /* Store the result in the tree for future reference, if necessary. */ -if (anchor != NULL) +if (anchor != NULL && !noupdate) { t = store_get(sizeof(tree_node) + Ustrlen(key)); t->data.ptr = dbd; @@ -2418,6 +2421,8 @@ acl_check_condition(int verb, acl_condition_block *cb, int where, { uschar *user_message = NULL; uschar *log_message = NULL; +uschar *debug_tag = NULL; +uschar *debug_opts = NULL; uschar *p = NULL; int rc = OK; #ifdef WITH_CONTENT_SCAN @@ -2543,6 +2548,9 @@ for (; cb != NULL; cb = cb->next) #endif case ACLC_CONDITION: + /* 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 */ rc = (Uatoi(arg) == 0)? FAIL : OK; else @@ -2554,6 +2562,9 @@ for (; cb != NULL; cb = cb->next) *log_msgptr = string_sprintf("invalid \"condition\" value \"%s\"", arg); break; + case ACLC_CONTINUE: /* Always succeeds */ + break; + case ACLC_CONTROL: control_type = decode_control(arg, &p, where, log_msgptr); @@ -2578,9 +2589,9 @@ for (; cb != NULL; cb = cb->next) break; #endif - #ifdef EXPERIMENTAL_DOMAINKEYS - case CONTROL_DK_VERIFY: - dk_do_verify = 1; + #ifndef DISABLE_DKIM + case CONTROL_DKIM_VERIFY: + dkim_disable_verify = TRUE; break; #endif @@ -2699,12 +2710,53 @@ for (; cb != NULL; cb = cb->next) } break; + case CONTROL_DEBUG: + while (*p == '/') + { + if (Ustrncmp(p, "/tag=", 5) == 0) + { + uschar *pp = p + 5; + while (*pp != '\0' && *pp != '/') pp++; + debug_tag = string_copyn(p+5, pp-p-5); + p = pp; + } + else if (Ustrncmp(p, "/opts=", 6) == 0) + { + uschar *pp = p + 6; + while (*pp != '\0' && *pp != '/') pp++; + debug_opts = string_copyn(p+6, pp-p-6); + p = pp; + } + } + debug_logging_activate(debug_tag, debug_opts); + break; + case CONTROL_SUPPRESS_LOCAL_FIXUPS: suppress_local_fixups = TRUE; break; } break; + #ifdef EXPERIMENTAL_DCC + case ACLC_DCC: + { + /* Seperate the regular expression and any optional parameters. */ + uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size); + /* Run the dcc backend. */ + rc = dcc_process(&ss); + /* Modify return code based upon the existance of options. */ + while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) + != NULL) { + if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) + { + /* FAIL so that the message is passed to the next ACL */ + rc = FAIL; + } + } + } + break; + #endif + #ifdef WITH_CONTENT_SCAN case ACLC_DECODE: rc = mime_decode(&arg); @@ -2764,95 +2816,18 @@ 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; + #ifndef DISABLE_DKIM + case ACLC_DKIM_SIGNER: + if (dkim_cur_signer != NULL) + rc = match_isinlist(dkim_cur_signer, + &arg,0,NULL,NULL,MCL_STRING,TRUE,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; - } - } + case ACLC_DKIM_STATUS: + rc = match_isinlist(dkim_exim_expand_query(DKIM_VERIFY_STATUS), + &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); break; #endif @@ -3048,7 +3023,10 @@ for (; cb != NULL; cb = cb->next) #ifdef EXPERIMENTAL_SPF case ACLC_SPF: - rc = spf_process(&arg, sender_address); + rc = spf_process(&arg, sender_address, SPF_PROCESS_NORMAL); + break; + case ACLC_SPF_GUESS: + rc = spf_process(&arg, sender_address, SPF_PROCESS_GUESS); break; #endif @@ -3427,11 +3405,11 @@ while (acl != NULL) *log_msgptr = *user_msgptr = NULL; acl_temp_details = FALSE; - if (where == ACL_WHERE_QUIT && + if ((where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT) && acl->verb != ACL_ACCEPT && acl->verb != ACL_WARN) { - *log_msgptr = string_sprintf("\"%s\" is not allowed in a QUIT ACL", + *log_msgptr = string_sprintf("\"%s\" is not allowed in a QUIT or not-QUIT ACL", verbs[acl->verb]); return ERROR; } @@ -3647,48 +3625,9 @@ if (rc == FAIL_DROP && where == ACL_WHERE_MAILAUTH) /* Before giving a response, take a look at the length of any user message, and split it up into multiple lines if possible. */ -if (*user_msgptr != NULL && Ustrlen(*user_msgptr) > 75) - { - uschar *s = *user_msgptr = string_copy(*user_msgptr); - uschar *ss = s; - - for (;;) - { - int i = 0; - while (i < 75 && *ss != 0 && *ss != '\n') ss++, i++; - if (*ss == 0) break; - if (*ss == '\n') - s = ++ss; - else - { - uschar *t = ss + 1; - uschar *tt = NULL; - while (--t > s + 35) - { - if (*t == ' ') - { - if (t[-1] == ':') { tt = t; break; } - if (tt == NULL) tt = t; - } - } - - if (tt == NULL) /* Can't split behind - try ahead */ - { - t = ss + 1; - while (*t != 0) - { - if (*t == ' ' || *t == '\n') - { tt = t; break; } - t++; - } - } - - if (tt == NULL) break; /* Can't find anywhere to split */ - *tt = '\n'; - s = ss = tt+1; - } - } - } +*user_msgptr = string_split_message(*user_msgptr); +if (fake_response != OK) + fake_response_text = string_split_message(fake_response_text); return rc; }