X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/exim-config/blobdiff_plain/e12c95862063d5cba833a8db2852916eb5e4e87f..3e7e168cf0a57fea364ad9a913574b383565d9be:/base.m4 diff --git a/base.m4 b/base.m4 index fee3626..e78194a 100644 --- a/base.m4 +++ b/base.m4 @@ -25,6 +25,8 @@ ### Global settings. SECTION(global, priv)m4_dnl +admin_groups = CONF_admin_groups +trusted_groups = CONF_trusted_groups prod_requires_admin = false SECTION(global, logging)m4_dnl @@ -38,7 +40,7 @@ syslog_timestamp = false SECTION(global, daemon)m4_dnl local_interfaces = <; CONF_interfaces -extra_local_interfaces = <; 0.0.0.0 ; :: +extra_local_interfaces = <; 0.0.0.0 ; ::0 SECTION(global, resource)m4_dnl deliver_queue_load_max = 8 @@ -58,20 +60,26 @@ gecos_pattern = ([^,:]*) SECTION(global, incoming)m4_dnl received_header_text = Received: \ - ${if def:sender_rcvhost {from $sender_rcvhost\n\t} \ + ${if def:sender_rcvhost \ + {from $sender_rcvhost\n\t} \ {${if def:sender_ident \ - {from ${quote_local_part:$sender_ident} }}\ - ${if def:sender_helo_name \ - {(helo=$sender_helo_name)\n\t}}}}\ + {from ${quote_local_part:$sender_ident} }}}}\ by $primary_hostname \ + (Exim $version_number)\ + ${if def:tls_cipher {\n\t} { }}\ ${if def:received_protocol \ {with $received_protocol \ - ${if def:tls_cipher {(cipher=$tls_cipher)\n\t}}}}\ - (Exim $version_number)\n\t\ + ${if def:tls_cipher {(cipher=$tls_cipher)}}}}\n\t\ ${if def:sender_address \ - {(envelope-from <$sender_address>\ + {(envelope-from $sender_address\ ${if def:authenticated_id \ - {; auth=$authenticated_id}})\n\t}}\ + {; auth=${quote_local_part:$authenticated_id}} \ + {${if and {{def:authenticated_sender} \ + {match_address{$authenticated_sender} \ + {*@CONF_master_domain}}} \ + {; auth=${quote_local_part:\ + ${local_part:\ + $authenticated_sender}}}}}})\n\t}}\ id $message_exim_id\ ${if def:received_for {\n\tfor $received_for}} @@ -79,14 +87,32 @@ SECTION(global, smtp)m4_dnl smtp_return_error_details = true accept_8bitmime = true +SECTION(global, env)m4_dnl +keep_environment = + SECTION(global, process)m4_dnl extract_addresses_remove_arguments = false headers_charset = utf-8 qualify_domain = CONF_master_domain +untrusted_set_sender = * +local_from_check = false +local_sender_retain = true SECTION(global, bounce)m4_dnl delay_warning = 1h : 24h : 2d +SECTION(global, tls)m4_dnl +tls_certificate = CONF_sysconf_dir/server.certlist +tls_privatekey = CONF_sysconf_dir/server.key +tls_advertise_hosts = ${if exists {CONF_sysconf_dir/server.key} {*}{}} +tls_dhparam = CONF_ca_dir/dh-param-2048.pem +tls_require_ciphers = ${if or {{={$received_port}{CONF_submission_port}} \ + {match_ip {$sender_host_address}{+trusted}}} \ + {CONF_good_ciphers} \ + {CONF_acceptable_ciphers}} +tls_verify_certificates = CONF_ca_dir/ca.cert +tls_verify_hosts = ${if eq{$acl_c_mode}{submission} {} {+allnets}} + DIVERT(null) ###-------------------------------------------------------------------------- ### Access control lists. @@ -96,9 +122,40 @@ SECTION(global, acl)m4_dnl acl_smtp_helo = helo SECTION(acl, misc)m4_dnl helo: - require message = The other one has bells on - verify = helo + ## Check that the caller's claimed identity is actually plausible. + ## This seems like it's a fairly effective filter on spamminess, but + ## it's too blunt a tool. Rather than reject, add a warning header. + ## Only we can't do this the easy way, so save it up for use in MAIL. + ## Also, we're liable to get a subsequent HELO (e.g., after STARTTLS) + ## and we should only care about the most recent one. + warn set acl_c_helo_warning = false + !condition = \ + ${if and {{match_ip {$sender_host_address} \ + {<; 127.0.0.0/8 ; ::1}} \ + {match_domain {$sender_helo_name} \ + {localhost : +thishost}}}} + !condition = \ + ${if exists {CONF_sysconf_dir/helo.conf} \ + {${lookup {$sender_helo_name} \ + partial0-lsearch \ + {CONF_sysconf_dir/helo.conf} \ + {${if match_ip \ + {$sender_host_address} \ + {<; $value}}}}}} + !verify = helo + set acl_c_helo_warning = true + + accept +SECTION(global, acl)m4_dnl +acl_not_smtp_start = not_smtp_start +SECTION(acl, misc)m4_dnl +not_smtp_start: + ## Record the user's name. + warn set acl_c_user = $sender_ident + set acl_m_user = $sender_ident + + ## Done. accept SECTION(global, acl)m4_dnl @@ -106,23 +163,60 @@ acl_smtp_mail = mail SECTION(acl, mail)m4_dnl mail: + ## If we stashed a warning header about HELO from earlier, we should + ## add it now. Only don't bother if the client has authenticated + ## successfully for submission (because we can't expect mobile + ## clients to be properly set up knowing their names), or it's one of + ## our own satellites (because they're either properly set up anyway, + ## or satellites using us as a smarthost). + warn condition = $acl_c_helo_warning + !condition = ${if eq{$acl_c_mode}{submission}} + !hosts = +allnets + WARNING_HEADER(BADHELO, + <:Client's HELO doesn't match its IP address.\n\t\ + helo-name=$sender_helo_name \ + address=$sender_host_address:>) + ## Always allow the empty sender, so that we can receive bounces. accept senders = : - ## Ensure that the sender is routable. This is important to prevent - ## undeliverable bounces. - require message = Invalid sender; \ - ($sender_verify_failure; $acl_verify_message) - verify = sender + ## Ensure that the sender looks valid. + require acl = mail_check_sender ## If this is directly from a client then hack on it for a while. warn condition = ${if eq{$acl_c_mode}{submission}} - control = submission + control = submission/sender_retain + + ## Insist that a local client connect through TLS. + deny message = Hosts within CONF_master_domain must use TLS + !condition = ${if eq{$acl_c_mode}{submission}} + hosts = +allnets + !encrypted = * + + ## Check that a submitted message's sender address is allowable. + require acl = mail_check_auth SECTION(acl, mail-tail)m4_dnl ## And we're done. accept +SECTION(acl, misc)m4_dnl +mail_check_sender: + + ## See whether there's a special exception for this sender domain. + accept senders = ${LOOKUP_DOMAIN($sender_address_domain, + {KV(senders, {$value}{})}, + {})} + + ## Ensure that the sender is routable. This is important to prevent + ## undeliverable bounces. + require message = Invalid sender; \ + ($sender_verify_failure; $acl_verify_message) + verify = sender + + ## We're good, then. + accept + SECTION(global, acl)m4_dnl acl_smtp_connect = connect SECTION(acl, connect)m4_dnl @@ -136,7 +230,7 @@ SECTION(acl, connect-tail)m4_dnl check_submission: ## See whether this message needs hacking on. - accept !hosts = +localnet + accept !hosts = +thishost !condition = ${if ={$received_port}{CONF_submission_port}} set acl_c_mode = relay @@ -153,16 +247,35 @@ rcpt: ## Reject if the client isn't allowed to relay and the recipient ## isn't in one of our known domains. - deny message = Relaying not permitted - !hosts = CONF_relay_clients - !authenticated = * - !domains = +known + require message = Relaying not permitted + acl = check_relay ## Ensure that the recipient is routable. require message = Invalid recipient \ ($recipient_verify_failure; $acl_verify_message) verify = recipient +SECTION(acl, misc)m4_dnl +check_relay: + ## Accept either if the client is allowed to relay through us, or if + ## we're the correct place to send this mail. + + ## Known clients and authenticated users are OK. + accept hosts = CONF_relay_clients + accept authenticated = * + + ## Known domains are OK. + accept domains = +public + + ## Finally, domains in our table are OK, unless they say they aren't. + accept domains = \ + ${if exists{CONF_sysconf_dir/domains.conf} \ + {partial0-lsearch; CONF_sysconf_dir/domains.conf}} + condition = DOMKV(service, {$value}{true}) + + ## Nope, that's not allowed. + deny + SECTION(acl, rcpt-tail)m4_dnl ## Everything checks out OK: let this one go through. accept @@ -178,11 +291,41 @@ SECTION(acl, data-tail)m4_dnl SECTION(global, acl)m4_dnl acl_smtp_expn = expn_vrfy acl_smtp_vrfy = expn_vrfy -SECTION(acl)m4_dnl +SECTION(acl, misc)m4_dnl expn_vrfy: accept hosts = +trusted deny message = Suck it and see +DIVERT(null) +###-------------------------------------------------------------------------- +### Verification of sender address. + +SECTION(acl, misc)m4_dnl +mail_check_auth: + + ## If this isn't a submission then it doesn't need checking. + accept condition = ${if !eq{$acl_c_mode}{submission}} + + ## If the caller hasn't formally authenticated, but this is a + ## loopback connection, then we can trust identd to tell us the right + ## answer. So we should stash the right name somewhere consistent. + warn set acl_c_user = $authenticated_id + hosts = +thishost + !authenticated = * + condition = ${if def:sender_ident} + set acl_c_user = $sender_ident + + ## User must be authenticated by now. + deny message = Sender not authenticated + condition = ${if !def:acl_c_user} + + ## Set the per-message authentication flag, since we now know that + ## there's a sensible value. + warn set acl_m_user = $acl_c_user + + ## All done. + accept + DIVERT(null) ###-------------------------------------------------------------------------- ### Common options for forwarding routers. @@ -220,6 +363,31 @@ m4_define(<:FILTER_TRANSPORTS:>, pipe_transport = pipe reply_transport = reply:>) +m4_define(<:FILTER_ROUTER:>, +<:$1_vrf: + $2 + FILTER_VERIFY<::>$3 +$1: + $2 + verify = no + FILTER_TRANSPORTS<::>$4:>) + +DIVERT(null) +###-------------------------------------------------------------------------- +### Common routers. + +SECTION(routers, alias)m4_dnl +## Look up the local part in the address map. +alias: + driver = redirect + allow_fail = true + allow_defer = true + user = CONF_filter_user + FILTER_TRANSPORTS + local_parts = nwildlsearch; CONF_alias_file + data = ${expand:$local_part_data} +SECTION(routers, alias-opts)m4_dnl + DIVERT(null) ###-------------------------------------------------------------------------- ### Some standard transports. @@ -229,50 +397,111 @@ m4_define(<:USER_DELIVERY:>, envelope_to_add = true return_path_add = true:>) +m4_define(<:APPLY_HEADER_CHANGES:>, + <:headers_add = m4_ifelse(<:$1:>, <::>, + <:$acl_m_hdradd:>, + <:${if def:acl_m_hdradd{$acl_m_hdradd\n}}\ + $1:>) + headers_remove = m4_ifelse(<:$2:>, <::>, + <:$acl_m_hdrrm:>, + <:${if def:acl_m_hdrrm{$acl_m_hdrrm:}}\ + $2:>):>) + SECTION(transports)m4_dnl -## A standard transport for remote delivery. Try to do TLS, and don't worry -## too much if it's not very secure: the alternative is sending in plaintext -## anyway. +## A standard transport for remote delivery. By default, try to do TLS, and +## don't worry too much if it's not very secure: the alternative is sending +## in plaintext anyway. But all of this can be overridden from the +## `domains.conf' file. Annoyingly, the `tls_dh_min_bits' setting isn't +## expanded before use, so we can't set it the obvious way. Instead, encode +## it into the transport name. This is very unpleasant, of course. smtp: driver = smtp + APPLY_HEADER_CHANGES tls_require_ciphers = CONF_acceptable_ciphers - tls_dh_min_bits = 1020 + tls_dh_min_bits = 508 tls_tempfail_tryclear = true +m4_define(<:SMTP_TRANS_DHBITS:>, + <:driver = smtp + APPLY_HEADER_CHANGES + hosts_try_auth = * + hosts_require_tls = DOMKV(tls-peer-ca, {*}{}) + hosts_require_auth = \ + ${if bool {DOMKV(require-auth, {$value}{false})} {*}{}} + tls_certificate = DOMKV(tls-certificate, {${expand:$value}}fail) + tls_privatekey = DOMKV(tls-private-key, {${expand:$value}}fail) + tls_verify_certificates = DOMKV(tls-peer-ca, {${expand:$value}}fail) + tls_require_ciphers = \ + DOMKV(tls-ciphers, + {${extract {${expand:$value}} \ + { good = CONF_good_ciphers \ + any = CONF_acceptable_ciphers } \ + {$value} \ + {${expand:$value}}}} \ + {CONF_acceptable_ciphers}) + tls_dh_min_bits = $1 + tls_tempfail_tryclear = true:>)m4_dnl +smtp_dhbits_512: + SMTP_TRANS_DHBITS(508) +smtp_dhbits_768: + SMTP_TRANS_DHBITS(764) +smtp_dhbits_1024: + SMTP_TRANS_DHBITS(1020) +smtp_dhbits_2048: + SMTP_TRANS_DHBITS(2044) + ## Transport to a local SMTP server; use TLS and perform client ## authentication. smtp_local: driver = smtp + APPLY_HEADER_CHANGES hosts_require_tls = * - tls_certificate = CONF_sysconf_dir/client.cert + tls_certificate = CONF_sysconf_dir/client.certlist tls_privatekey = CONF_sysconf_dir/client.key tls_verify_certificates = CONF_ca_dir/ca.cert tls_require_ciphers = CONF_good_ciphers - tls_dh_min_bits = 3070 + tls_dh_min_bits = 2046 tls_tempfail_tryclear = false - authenticated_sender = ${if def:authenticated_id \ - ${authenticated_id@CONF_master_domain} \ - fail} + authenticated_sender_force = true + authenticated_sender = \ + ${if def:acl_m_user {$acl_m_user@CONF_master_domain} \ + {${if def:authenticated_sender {$authenticated_sender} \ + fail}}} ## A standard transport for local delivery. deliver: driver = appendfile + APPLY_HEADER_CHANGES file = /var/mail/$local_part + group = mail + mode = 0600 + mode_fail_narrower = false USER_DELIVERY ## Transports for user filters. mailbox: driver = appendfile + APPLY_HEADER_CHANGES + initgroups = true USER_DELIVERY maildir: driver = appendfile + APPLY_HEADER_CHANGES maildir_format = true + initgroups = true USER_DELIVERY pipe: driver = pipe - return_output = true + APPLY_HEADER_CHANGES + path = ${if and {{def:home} {exists{$home/bin}}} {$home/bin:} {}}\ + /usr/local/bin:/usr/local/sbin:\ + /usr/bin:/usr/sbin:/bin:/sbin + initgroups = true + umask = 002 + return_fail_output = true + log_output = true ## A special dummy transport for use during address verification. dummy: