Exim – Restrict User Email Sending Addresses

exim

By default, exim allows any user to send out an email with any "From:" address they wish, which can lead to users spoofing each other. How do I restrict which addresses each system user can send from? For example the user james should be able to send from james@localhost, but also me@my-domain.com and james@other-domain.com.

I want all system users to be able to send email from their SYSTEM_USER@HOSTNAME, but if they attempt to send from another address I want to look up that address in a text file to see if it is allowed.

I currently have a list, but it is for what addresses a system user will receive from. But it could still work. Given the example me@my-domain.com the data is stored in the file /etc/exim4/virtual/my-domain.com where the content looks like:

me : james@localhost

If we could reuse the existing files that would be great. But any suggestions would be helpful. Thank you in advance.

Best Answer

Exim4 already does some standard checks on the envelope sender, From: and Sender: of the locally submitted messages (cf. trusted users). Since you want to extend this validation you should:

  • For messages submitted through sendmail (i.e. exim4 -bm), add to the main configuration section:

    # allow untrusted users to set an envelope sender
    untrusted_set_sender = *
    # don't delete the Sender: header
    local_sender_retain = true
    # don't add Sender: header
    local_from_check = false
    
  • For messages submitted through SMTP you need to add the control = submission/sender_retain modifier. E.g. Debian has in the MAIL acl a rule:

    accept
      authenticated = *
      control = submission/sender_retain
      control = dkim_disable_verify
    

To configure your check you need to add a couple of ACL rules at different stages of mail processing. Since these rules need to be applied to different ways of message submission you can create a new named ACL (I assume that all your /etc/exim4/virtual/<domain> files return username@localhost as value):

# Requires system user id in $acl_arg1
acl_check_sender:

  # Users with default domain
  accept
    condition = ${if eq{$sender_address}{$acl_arg1@$qualify_domain}}
    condition = ${if eq{${address:$h_from:}}{$acl_arg1@$qualify_domain}}
    condition = ${if match_address{${address:$h_sender:}}{:$acl_arg1@$qualify_domain}}

  deny
    ! condition = ${if and{\
        {exists{/etc/exim4/virtual/${domain:${address:$h_from:}}}}\
        {eq {${acl_arg1}@localhost}\
          {${lookup {${local_part:${address:$h_from:}}}\
            lsearch{/etc/exim4/virtual/${domain:${address:$h_from:}}}\
          }}\
        }\
      }}
    message = Spoofed From: header.

  # Most messages don't have a Sender: header, but if it is not empty, check it.
  deny
    ! condition = ${if or{\
        {eq {$h_sender:}{}}\
        {and{\
          {exists{/etc/exim4/virtual/${domain:${address:$h_sender:}}}}\
          {eq {${acl_arg1}@localhost}\
              {${lookup {${local_part:${address:$h_sender:}}}\
                lsearch{/etc/exim4/virtual/${domain:${address:$h_sender:}}}\
              }}\ 
          }\
        }}\
      }}
    message = Spoofed Sender: header.

  deny
    ! condition = ${if and{\
        {exists{/etc/exim4/virtual/$sender_address_domain}}\
        {eq {${acl_arg1}@localhost}\
          {${lookup {$sender_address_local_part}\
            lsearch{/etc/exim4/virtual/$sender_address_domain}\
          }}\
        }\
      }}
    message = Spoofed envelope sender.

  accept

For the syntax of string expansion (quite full of braces), check chapter 11 of Exim's documentation. You can use exim4 -bem <message> <expansion_string> to test them (the envelope sender is passed in the message like the mbox format).

You can apply it to different situations:

  • For messages submitted through exim4 -bm, add to the acl_not_smtp ACL the following rule:

    deny
      ! acl = acl_check_sender ${sender_ident}
    
  • For messages submitted by authenticated users through SMTP add to the acl_smtp_data ACL something like:

    deny
      authenticated = *
      ! acl = acl_check_sender ${authenticated_id}
    
  • Some programs submit messages using a non-authenticated SMTP connection to localhost (or exim -bs). For them you can add to the acl_smtp_data ACL:

    deny
      hosts = : localhost
      ! acl = acl_check_sender ${sender_ident}
    

    and install some identd on the server.

Related Topic