Email: blocking messages with too many public recipients


#1

Hi All

Some users are prone to dump address books in To and Cc fields which results in (unintended) address leakage. In one instance, a small non-profit is using roundcube and wanted to block leaky messages so I’ve been looking at options. A couple of years ago roundcube introduced a very neat feature warning users about high public address counts and offering to switch addresses to Bcc. Debian roundcube is one version behind that functionality and it’s only a warning, requiring user opt-in to take place. Rather than tweak roundcube I thought I’d have a go at exim <gulp>, because, with GPDR, it’s only a matter of time before other mail clients need covering.

Note that the roundcube leaky-user-experience isn’t great: a notice will pop up saying there’s been an error sending but not why. They’ll be sitting on an unsent message, not wanting to lose their Draft, and probably, shouting about computers. :slight_smile: When they make their back to the Inbox, they’ll see a bounce message with a brief explanation.

Usually when I ask about exim tweaks there’s a stony silence so I didn’t try asking this time – it’s down to trial and syntax error. I’m posting for two reasons i) it might and should be useful to others ii) to encourage people to make suggestions, criticisms, etc – Eximites might easily see better ways of going about the task. Also, handling webmail (non-smtp) is slightly more invasive than normal and there may be “unforeseen consequences”. :slight_smile: However, it’s been running fine here, so far.

So, here’s the set-up:

1) Create an acl definition for non-smtp traffic

/etc/exim4/symbiosis.d/00-main/31-x-acl-definitions-extra

# Defines the access control list that is run on non-smtp transactions
# - non-stock (experimental) acl aimed at webmail traffic (primarily, roundcube)
# - 'x' naming is used to lessen chances of config overwrites & clashes on symbiosis updates

acl_not_smtp = acl_x_check_not_smtp

2) Create acl directory

sudo mkdir /etc/exim4/symbiosis.d/10-acl/70-x-acl-check-not-smtp

3) Add three files

/etc/exim4/symbiosis.d/10-acl/70-x-acl-check-not-smtp/00-header

# This ACL is for non-smtp traffic. It's a non-standard addition to symbiosis
# and is primarily intended to act on outbound webmail messages (roundcube).

acl_x_check_not_smtp:

/etc/exim4/symbiosis.d/10-acl/70-x-acl-check-not-smtp/10-x-public-recipient-limit

# reject message if there are too many recipients listed in the To or Cc headers
# - intended to stop address leakage when local users (unintentionally) dump address books in To & Cc fields
# - /srv/my-brilliant-site.com/config/email-public-recipients-limit file required
# - defaults to 10 if a 'max' field isn't found (e.g., empty file)

    deny    sender_domains          = +local_domains
            condition               = ${if exists{VHOST_DIR/$sender_address_domain/VHOST_CONFIG_DIR/email-public-recipients-limit}}
            set acl_m_107010_max    = ${lookup{max}lsearch{VHOST_DIR/$sender_address_domain/VHOST_CONFIG_DIR/email-public-recipients-limit}{$value}{10}}
                                      # require extracted value to be vaguely sane
            condition               = ${if and{                                     \
                                            {match{$acl_m_107010_max}{\N^\d+$\N}}   \
                                            {>{$acl_m_107010_max}{0}}               \
                                            {<{$acl_m_107010_max}{500}}             \
                                        }                                           \
                                      }
                                      # don't read the headers if the envelope recipient count is low enough
            condition               = ${if >{$recipients_count}{$acl_m_107010_max}}
#           condition               = ${if match{${lc:$h_User-Agent:}}{\N^roundcube\N}}
            set acl_m_107010_n      = ${listcount:${addresses:$h_To:,$h_Cc:}}
            condition               = ${if >{$acl_m_107010_n}{$acl_m_107010_max}}
            logwrite                = ACL:10-70-10 public recipient address limit $acl_m_107010_max exceeded: $sender_address hit $acl_m_107010_n
            message                 = Too many public recipients (i.e. addresses shown in the To and/or Cc header fields). \
                                      Use Bcc instead - addresses will not be exposed and the https://ico.org.uk will purr ;)
/etc/exim4/symbiosis.d/10-acl/70-x-acl-check-not-smtp/99-default
# Accept the message.
accept

4) Invoke the new exim4 config

cd /etc/exim4/
sudo make

Barring syntax errors the service should restart in a flash of glory (jessie+).

5) Add config files

/srv/my-brilliant-site.com/config/email-public-recipients-limit

# reject outbound mail with too many addresses shown in the To and Cc fields 
# - only operates on webmail (may also include smtp outbound by the time you read this)
# - defaults to 10 if no 'max' entry is found

max     12 

Untested here, as yet, but to cover, non-webmail traffic, you’d only need one file in the standard directory structure. I suspect it would benefit from more checks about the source, e.g., auth users only.

/etc/exim4/symbiosis.d/10-acl/60-acl-check-data/50-x-public-recipients-limit

# reject message if there are too many recipients listed in the To or Cc headers
# - intended to stop address leakage when local users (unintentionally) dump address books in To & Cc fields
# - /srv/my-brilliant-site.com/config/email-public-recipients-limit file required
# - defaults to 10 if a 'max' field isn't found (e.g., empty file)

    deny    sender_domains          = +local_domains
            condition               = ${if exists{VHOST_DIR/$sender_address_domain/VHOST_CONFIG_DIR/email-public-recipients-limit}}
            set acl_m_106050_max    = ${lookup{max}lsearch{VHOST_DIR/$sender_address_domain/VHOST_CONFIG_DIR/email-public-recipients-limit}{$value}{10}}
                                      # require extracted value to be vaguely sane
            condition               = ${if and{                                     \
                                            {match{$acl_m_106050_max}{\N^\d+$\N}}   \
                                            {>{$acl_m_106050_max}{0}}               \
                                            {<{$acl_m_106050_max}{500}}             \
                                        }                                           \
                                      }
                                      # don't read the headers if the envelope recipient count is low enough
            condition               = ${if >{$recipients_count}{$acl_m_106050_max}}
            set acl_m_106050_n      = ${listcount:${addresses:$h_To:,$h_Cc:}}
            condition               = ${if >{$acl_m_106050_n}{$acl_m_106050_max}}
            logwrite                = ACL:10-60-50 H=$sender_fullhost public recipient address limit $acl_m_106050_max exceeded: $sender_address hit $acl_m_106050_n
            message                 = Too many public recipients (i.e. addresses shown in the To and/or Cc header fields). \
                                      Use Bcc instead - addresses will not be exposed and the https://ico.org.uk will purr ;)

Good luck!