From: Mark Wooding Date: Wed, 23 Mar 2016 22:33:05 +0000 (+0000) Subject: spam.m4, user-spam.m4: Log details about spam rejections for users. X-Git-Url: https://git.distorted.org.uk/~mdw/exim-config/commitdiff_plain/33dbcec9dca3a8bb70405fb2f13fbc032c517b02 spam.m4, user-spam.m4: Log details about spam rejections for users. * When we notice a delivery to a user during recipient verification, take a note of the user's name in the `user' field of the address_data. * In the `rcpt_spam' ACL, pick the user name out of the address_data and remember it and the corresponding recipient address (in a rather unpleasantly escaped form) along with the others in the variable `$acl_m_spam_users'. * Finally, in `data_spam', if we end up rejecting the message, log a message with the condensed SpamAssassin report, and the user names and matching recipient addresses. This leaves, in the rejectlog, enough information for a service to tell which rejection reports apply to a calling user, and tell them about the message. We should be able to pick the sender address and the headers from the usual rejection report, but we don't want to leak the other envelope recipient addresses. (The user would have seen the /header/ recipients had we not rejected the message as being spam; but the envelope may contain Bcc recipients or other interesting secrets.) --- diff --git a/spam.m4 b/spam.m4 index 897e6a9..7ed0f04 100644 --- a/spam.m4 +++ b/spam.m4 @@ -49,12 +49,13 @@ m4_define(<:SPAMLIMIT_ROUTER:>, m4_define(<:SPAMLIMIT_SET:>, <:address_data = \ ${if def:address_data {$address_data}{}} \ - $1:>) + m4_ifelse(<:$2:>, <::>, <::>, <:$2 \ + :>)$1:>) m4_define(<:SPAMLIMIT_LOOKUP:>, <:condition = ${if exists{$1}} SPAMLIMIT_SET(<:${lookup {$2@$3/$4} nwildlsearch {$1} \ - {SPAMLIMIT_CHECK($value)}}:>):>) + {SPAMLIMIT_CHECK(<:$value:>)}}:>, <:$5:>):>) m4_define(<:SPAMLIMIT_USERV:>, <:SPAMLIMIT_SET(<:${run {/usr/bin/timeout 5s \ @@ -62,7 +63,7 @@ m4_define(<:SPAMLIMIT_USERV:>, SHQUOTE($1) exim-spam-limit \ SHQUOTE($4) \ SHQUOTE($2) SHQUOTE(@$3)} \ - {SPAMLIMIT_CHECK($value)}}:>):>) + {SPAMLIMIT_CHECK(<:$value:>)}}:>, <:$5:>):>) m4_define(<:GET_ADDRDATA:>, <:extract{<:$1:>}{${if def:address_data{$address_data}{}}}:>) @@ -119,6 +120,14 @@ rcpt_spam: ${sg {${GET_ADDRDATA(spam_limit){$value}{nil}}} \ {^(|.*\\D.*)\$}{CONF_spam_max}} + warn condition = ${GET_ADDRDATA(user){true}{false}} + set acl_m_spam_users = \ + ${if def:acl_m_spam_users {$acl_m_spam_users::}{}}\ + ${GET_ADDRDATA(user) \ + {$value=${sg{$local_part@$domain}\ + {([!:])}{!\$1}}} \ + fail} + ## If there's a spam limit already established, and it's different ## from this user's limit, then the sender will have to try this user ## again later. @@ -200,8 +209,15 @@ data_spam: ## Undo the escaping. set acl_m_spam_tests = ${sg{$acl_m_spam_tests}{!(.)}{\$1}} - ## If we've decided to reject, then tell the sender to get knotted. + ## If we've decided to reject, then leave a dropping in the log file + ## so that users can analyse rejections for incoming messages, and + ## tell the sender to get knotted. deny message = Tinned meat product detected ($spam_score) + log_message = Spam rejection \ + score=$spam_score \ + limit=$acl_m_spam_limit_presentation \ + tests=$acl_m_spam_tests \ + users=$acl_m_spam_users condition = ${if >{$spam_score_int}{$acl_m_spam_limit} \ {true}{false}} diff --git a/user-spam.m4 b/user-spam.m4 index 1b47f15..42b6f15 100644 --- a/user-spam.m4 +++ b/user-spam.m4 @@ -35,10 +35,10 @@ m4_define(<:USER_SPAMLIMIT_ROUTER:>, m4_define(<:USER_SPAMLIMIT_ROUTERS:>, <:USER_SPAMLIMIT_ROUTER(<:lookup:>, <:$1:>, <:$5SPAMLIMIT_LOOKUP(<:CONF_userconf_dir/spam-limit:>, - <:$2:>, <:$3:>, <:$4:>):>) + <:$2:>, <:$3:>, <:$4:>, <:user=$local_part:>):>) USER_SPAMLIMIT_ROUTER(<:userv:>, <:$1:>, <:$5SPAMLIMIT_USERV(<:SHQUOTE($local_part):>, - <:$2:>, <:$3:>, <:$4:>):>):>) + <:$2:>, <:$3:>, <:$4:>, <:user=$local_part:>):>):>) m4_define(<:CURRENT_LOCAL_PART:>, <:$local_part_prefix$local_part$local_part_suffix:>)