X-Git-Url: https://git.distorted.org.uk/~mdw/exim-config/blobdiff_plain/fa5a92c6b7847f6bafbc97d63a3935db4f175e35..HEAD:/base.m4 diff --git a/base.m4 b/base.m4 index 6c2dc7b..d64b88b 100644 --- a/base.m4 +++ b/base.m4 @@ -26,6 +26,7 @@ SECTION(global, priv)m4_dnl admin_groups = CONF_admin_groups +trusted_groups = CONF_trusted_groups prod_requires_admin = false SECTION(global, logging)m4_dnl @@ -39,10 +40,11 @@ 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 +message_size_limit = 500M queue_only_load = 12 smtp_accept_max = 16 smtp_accept_queue = 32 @@ -58,6 +60,8 @@ gecos_name = $1 gecos_pattern = ([^,:]*) SECTION(global, incoming)m4_dnl +rfc1413_hosts = * +rfc1413_query_timeout = 10s received_header_text = Received: \ ${if def:sender_rcvhost \ {from $sender_rcvhost\n\t} \ @@ -72,24 +76,37 @@ received_header_text = Received: \ ${if def: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}} SECTION(global, smtp)m4_dnl smtp_return_error_details = true accept_8bitmime = true +chunking_advertise_hosts = + +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.cert +tls_certificate = CONF_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 @@ -109,6 +126,10 @@ SECTION(global, acl)m4_dnl acl_smtp_helo = helo SECTION(acl, misc)m4_dnl helo: + ## Don't worry if this is local submission. MUAs won't necessarily + ## have a clear idea of their hostnames. (For some reason.) + accept condition = ${if eq{$acl_c_mode}{submission}} + ## 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. @@ -128,7 +149,7 @@ helo: {CONF_sysconf_dir/helo.conf} \ {${if match_ip \ {$sender_host_address} \ - {$value}}}}}} + {<; $value}}}}}} !verify = helo set acl_c_helo_warning = true @@ -140,6 +161,7 @@ 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 @@ -150,26 +172,28 @@ SECTION(acl, mail)m4_dnl mail: ## If we stashed a warning header about HELO from earlier, we should - ## add it now. + ## 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 - add_header = :after_received:X-Distorted-Warning: \ - BADHELO \ - Client's HELO doesn't match its IP address.\n\t\ - helo-name=$sender_helo_name \ - address=$sender_host_address + !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 @@ -184,6 +208,22 @@ 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)})} + + ## 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 @@ -203,6 +243,7 @@ check_submission: ## Remember to apply submission controls. warn set acl_c_mode = submission + control = no_enforce_sync ## Done. accept @@ -214,16 +255,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 @@ -232,6 +292,10 @@ SECTION(global, acl)m4_dnl acl_smtp_data = data SECTION(acl, data)m4_dnl data: + ## Don't accept messages with overly-long lines. + deny message = line length exceeds SMTP permitted maximum: \ + $max_received_linelength > 998 + condition = ${if >{$max_received_linelength}{998}} SECTION(acl, data-tail)m4_dnl accept @@ -239,7 +303,7 @@ 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 @@ -260,31 +324,16 @@ mail_check_auth: 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. + ## User must be authenticated by now. deny message = Sender not authenticated - !hosts = +thishost - !authenticated = * + condition = ${if !def:acl_c_user} - ## Make sure that the local part is one that the authenticated sender - ## is allowed to claim. - deny message = Sender address forbidden to calling user - !condition = ${LOOKUP_DOMAIN($sender_address_domain, - {${if and {{match_local_part \ - {$acl_c_user} \ - {+dom_users}} \ - {match_local_part \ - {$sender_address_local_part} \ - {+dom_locals}}}}}, - {${if and {{match_local_part \ - {$sender_address_local_part} \ - {+user_extaddr}} \ - {or {{eq {$sender_address_domain} \ - {}} \ - {match_domain \ - {$sender_address_domain} \ - {+public}}}}}}})} + ## 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 @@ -337,6 +386,22 @@ $1: 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. m4_define(<:USER_DELIVERY:>, @@ -344,14 +409,106 @@ 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:>):>) + +m4_define(<:DKIM_SIGN_P:>, + <:and {{exists{CONF_sysconf_dir/dkim-sign.conf}} \ + {!def:h_DKIM-Signature:} \ + {!def:h_List-ID:} \ + {or {{def:authenticated_id} \ + {def:authenticated_sender}}} \ + {bool {DKIM_KEYS_INSTANCE(<:{true}:>, <:{false}:>)}}}:>) + +m4_define(<:DKIM_KEYS_INSTANCE:>, + <:${lookup {${domain:$h_From:}} partial0-lsearch \ + {CONF_sysconf_dir/dkim-sign.conf} \ + _LOOKUP_ARGS(<:$1:>, <:$2:>)}:>) +m4_define(<:DKIM_KEYS_STATE:>, <:${lookup {$1} lsearch \ + {DKIM_KEYS_INSTANCE(<:{CONF_dkim_keys_dir/$value/active/dkim-keys.state}:>)} \ + _LOOKUP_ARGS(<:$2:>, <:$3:>, <:fail:>)}:>) +m4_define(<:DKIM_KEYS_INFO:>, <:DKIM_KEYS_STATE(<:params:>, + <:{${if and {{>={$tod_epoch}{KV(t0)}} \ + {<{$tod_epoch}{${eval:KV(t0) + KV(n)*KV(step)}}}} \ + {DKIM_KEYS_STATE(<:info.${eval:($tod_epoch - KV(t0))/KV(step)}:>, + <:$1:>, <:$2:>)} \ + m4_ifelse(<:$2:>, <::>, <:fail:>, <:$2:>)}}:>, + m4_ifelse(<:$2:>, <::>, <:fail:>, <:$2:>)):>) + +m4_define(<:DKIM_SIGN:>, + <:dkim_domain = \ + ${if DKIM_SIGN_P \ + {DKIM_KEYS_INSTANCE({${domain:$h_From:}})}} + dkim_selector = DKIM_KEYS_INFO(<:{KV(k)}:>) + ##dkim_timestamps = m4_eval(<:7*24*60*60:>) + dkim_private_key = \ + DKIM_KEYS_INSTANCE(<:m4_dnl + {CONF_dkim_keys_dir/$value/active/$dkim_selector.priv}:>) + dkim_canon = relaxed + dkim_strict = true + ## The following ridiculous stunt does two important jobs. Firstly, + ## and more obviously, it arranges to include one more copy of each + ## header name than the message actually contains, thereby causing + ## the signature to fail if another header with the same name is + ## added. And secondly, and far more subtly, it also trims the + ## spaces from the header names so that they're in the format that + ## the signing machinery secretly wants. + dkim_sign_headers = \ + ${sg {${map {CONF_dkim_headers : \ + X-CONF_header_token-DKIM-Key-Publication} \ + {$item${sg {${expand:\$h_$item:}\n} \ + {((?:[^\n]+|\n\\s+)*)\n} \ + {:$item}}}}} \ + {::}{:}} + headers_add = \ + ${if DKIM_SIGN_P \ + {DKIM_KEYS_INFO(<:m4_dnl + {X-CONF_header_token-DKIM-Key-Publication: \ + DKIM signature not suitable \ + as evidence after delivery;\n\t\ + DKIM private key KV(k) will be \ + published\n\t\ + at KV(u)\n\t\ + on or before KV(tpub)}:>)}}:>) + + +m4_define(<:SMTP_DELIVERY:>, + <:## Prevent sending messages with overly long lines. The use of + ## `message_size_limit' here is somewhat misleading. + message_size_limit = ${if >{$max_received_linelength}{998}{1}{0}}:>) + SECTION(transports)m4_dnl ## 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. +## `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 + SMTP_DELIVERY + APPLY_HEADER_CHANGES + DKIM_SIGN + tls_require_ciphers = CONF_acceptable_ciphers + tls_dh_min_bits = 508 + tls_tempfail_tryclear = true + +m4_define(<:SMTP_TRANS_DHBITS:>, + <:driver = smtp + SMTP_DELIVERY + APPLY_HEADER_CHANGES + DKIM_SIGN + 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) @@ -363,29 +520,40 @@ smtp: {$value} \ {${expand:$value}}}} \ {CONF_acceptable_ciphers}) - ## Can't set this to an expansion. :-( - m4_dnl tls_dh_min_bits = DOMKV(dh-min-bits, {$value}{1020}) - tls_dh_min_bits = 1020 - tls_tempfail_tryclear = true + 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 + SMTP_DELIVERY + 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 = 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 @@ -395,17 +563,20 @@ deliver: ## 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 + 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 @@ -424,6 +595,13 @@ DIVERT(null) ### Retry configuration. SECTION(retry, default)m4_dnl +## Be persistent when sending to the site relay. It ought to work, but +## particularly satellites such as laptops often encounter annoying temporary +## failures due to network unavailability, and the usual gradual policy can +## leave mail building up for no good reason. +CONF_smarthost * \ + F,4d,15m + ## Default. * * \ F,2h,15m; G,16h,2h,1.5; F,4d,6h