define(`__CHECKING_LASTUPD',`2-Jun-1998')dnl
define(`__CHECKING_VERSION',`2.5')dnl
define(`__CHECKING_AUTHOR',`Andy Harper')dnl
dnl ## HACK(checking)
dnl ##
dnl ## Provides a suite of rulesets that check mail clients and addresses based
dnl ## on a client configuration database. Each client has an entry based on
dnl ## {client_addr} or {client_name} and these are used to locate a set of
dnl ## keyword options used to control what the client may (or may not) do.
dnl ## The database also contains configuration information about sender and
dnl ## recipient addresses and domains.
dnl ##
dnl ## Legal Stuff:
dnl ## This software is distributed as is at the user's own risk. No warranties
dnl ## are given and the author takes no responsibility for any problems caused by
dnl ## its use. The software may be freely distributed but no modifications
dnl ## may be made without permission of the author. It may not be sold for profit
dnl ## nor used in other applications for profit without the express permission
dnl ## of the author.
dnl ## (C) Andy Harper 1997, 1998
dnl ##
dnl ## Options:
dnl ## These options may be defined. There are defaults for all of them.
dnl ##
dnl ## SM89
dnl ## Build the rules to work with sendmail 8.9 or greater.
dnl ## If not defined, will work for sendmail 8.8 only.
dnl ##
dnl ## __CLIENT_DATABASE__
dnl ## Defines the name of the client database which holds information
dnl ## about clients of interest. The database is a keyed file where
dnl ## the data portion is a series of keywords describing capabilities
dnl ## and restrictions of the client.
dnl ## If not defined, then the default is:
dnl ## `dbm -a. /etc/mail/AddressDatabase'
dnl ##
dnl ## __CLIENT_DEFAULT__
dnl ## Defines the set of options used for a client that cannot be
dnl ## found in the client database. If not defined, a set of options
dnl ## is assumed that will permit general access with no restrictions.
dnl ##
dnl ## __RBL_SERVER__
dnl ## Defines the name of the Real-time Black-hole server. If not
dnl ## defined, defaults to `rbl.maps.vix.com'
dnl ##
dnl ## _S_
dnl ## The check_xxx rulesets use this as a separator between parts
dnl ## of a pattern in order to preserve stuff across rules (due to
dnl ## the limited storage available. If _S_ is likely to clash with
dnl ## anything, then it can be redefined to any other unique string.
dnl ##
dnl ## -----------------------------------------------------------------------
dnl ##
dnl ## The following define hooks into user supplied rulesets. By defining
dnl ## them to the name of a user ruleset, that ruleset will be called before
dnl ## or after the rulesets used by checking.
dnl ##
dnl ## __RELAY_PREPROCESS_HOOK__
dnl ## The check_relay ruleset check the validity of the client address and
dnl ## hostname. Defining this allows a specific ruleset to be called
dnl ## BEFORE the check_relay ruleset does anything. The input to it will
dnl ## be:
dnl ## client_addr $| client_name
dnl ##
dnl ## and this should remain unchanged if the ruleset returns.
dnl ## If not defined, no pre-processing ruleset is called by the
dnl ## check_relay ruleset.
dnl ##
dnl ## __RELAY_POSTPROCESS_HOOK__
dnl ## As for __RELAY_PREPROCESS_HOOK__ except that the ruleset is
dnl ## called AFTER the check_relay ruleset has completed all other checks.
dnl ## If not defined, no post-processing ruleset is called by the
dnl ## check_relay ruleset.
dnl ##
dnl ## __MAIL_PREPROCESS_HOOK__
dnl ## As for __RELAY_PREPROCESS_HOOK__ except that the check is made
dnl ## in the check_mail ruleset. Input to this ruleset is the address
dnl ## as specified on the MAIL FROM: protocol command.
dnl ##
dnl ## __MAIL_POSTPROCESS_HOOK__
dnl ## As for __RELAY_POSTPROCESS_HOOK__ except that the check is made
dnl ## in the check_mail ruleset.
dnl ##
dnl ## __RCPT_PREPROCESS_HOOK__
dnl ## As for __RELAY_PREPROCESS_HOOK__ except that the check is made
dnl ## in the check_rcpt ruleset. Input to this ruleset is the address
dnl ## as specified on the RCPT TO: protocol command.
dnl ##
dnl ## __RCPT_POSTPROCESS_HOOK__
dnl ## As for __RELAY_POSTPROCESS_HOOK__ except that the check is made
dnl ## in the check_rcpt ruleset.
dnl ##
dnl ## __COMPAT_PREPROCESS_HOOK__
dnl ## As for __RELAY_PREPROCESS_HOOK__ except that the check is made
dnl ## in the check_compat ruleset. Input to this ruleset are the
dnl ## sender and recipient addresses, separated by $|
dnl ##
dnl ## __COMPAT_POSTPROCESS_HOOK__
dnl ## As for __RELAY_POSTPROCESS_HOOK__ except that the check is made
dnl ## in the check_compat ruleset.
dnl ##
dnl ## ----------------------------------------------------------------------
dnl ## The following hooks allow interfacing to various optional add-on
dnl ## features that can be installed with sendmail (usually supplied by a
dnl ## third party). They MUST NOT be used if these add-ons are not
dnl ## installed!!!
dnl ##
dnl ## Hook for map-regex:
dnl ## __CHECK_FROM_REGEXn__ [ NOTE: n = 1 thru 9 ]
dnl ## Defines a regular expression against which the FROM address is
dnl ## matched to see if it is a SPAM address. If a match is found,
dnl ## the message may be rejected.
dnl ## Use ONLY IF:
dnl ## * Sendmail 8.9 or later is being used, OR
dnl ## * Jan Kruger's map-regex patch to sendmail is installed
dnl ##
dnl ## Hook for POPAUTH:
dnl ## __AUTOAUTH_FILE__ dbm -o -m -a@AUTH /etc/mail/AutoAuth
dnl ## Defines a database of IP addresses who are allowed to relay
dnl ## (LimitedRelayAuto) and use a local FROM address (FromAuto)
dnl ## This database is independent of the main client database and
dnl ## may be updated dynamically by any desired mechanism. Typically
dnl ## by a script that monitors POP/IMAP logins and adds the IP
dnl ## address to the list (which allows dynamic authorization of
dnl ## remote users).
dnl ##
dnl ## Author:
dnl ## 2.0 Andy Harper February 1998 Rewrite for client database
dnl ## 2.5 Andy Harper April 1998 Support for sendmail 8.9
dnl ##
dnl ## ----------------------------------------------------------------------
dnl
dnl
dnl
dnl
dnl ## ----------------------------------------------------------------------
dnl ## Useful macros
dnl ## __DO_FROM_PROG_TABLE Define a map to call an external program for a
dnl ## FROM address (still experimental and incomplete)
dnl ## __DO_FROM_PROG_MATCH Define rule to match address against above table
dnl ## __DO_FROM_REGEX_TABLE Define regular expression map
dnl ## __DO_FROM_REGEX_MATCH Define rule to match FROM against regular expression
dnl ## __DO_DEFINE_ALIAS Define the additional alias files
dnl ## _REPEAT Repeat a macro call, for arg1 = 1 -> N
dnl ## _USER_HOOK Generate user-ruleset hooks
dnl ## ----------------------------------------------------------------------
dnl
dnl ## Macros for generating interfaces to external programs
define(`__DO_FROM_PROG_TABLE',`ifdef(`__CHECK_FROM_PROG$1',`Kfromprog$1 program __CHECK_FROM_PROG$1__
')')dnl
define(`__DO_FROM_PROG_MATCH',`ifdef(`__CHECK_FROM_PROG$1',`R$`'* _S_ $`'* _S_ $`'* FromProg$1 $`'* $`'| $`'* $`'| $`'* $: $`'>ProgCheck fromprog$1 $| $`'1 _S_ $`'2 _S_ $`'3 FromProg$1 $`'4 $`'| $`'5 $`'| $`'6 Check program $1
')')dnl
dnl
dnl ## Macros for generating regular expression table definitions and checks
define(`__DO_FROM_REGEX_TABLE',`ifdef(`__CHECK_FROM_REGEX$1__',`Kfromregex$1 regex -a@MATCH __CHECK_FROM_REGEX$1__
')')dnl
define(`__DO_FROM_REGEX_MATCH',`ifdef(`__CHECK_FROM_REGEX$1__',`R$`'* _S_ $`'* _S_ $`'* FromRegex$1 $`'* $`'| $`'* $`'| $`'* NoSpamPlease $`'* $: $`'>RegexCheck fromregex$1 _S_ $`'2 _S_ $`'3 FromRegex$1 $`'4 $`'| $`'5 $`'| $`'6 NoSpamPlease $`'7 Check regular expression $1
')')dnl
dnl
dnl ## Macros for generating alias files
define(`__DO_DEFINE_ALIAS',`ifdef(`ALIAS_FILE$1',`define(`_AF_',_AF_ chkalias$1)'Kchkalias$1 implicit -m ALIAS_FILE$1
)')dnl
dnl
dnl ## Macro for repeating commands with a single numeric argument
dnl ## Usage: _REPEAT(n,macro)
dnl ## Effect: Calls macro(m), with m = 1->n
define(`_REPEAT',`ifelse($1,0,,`_REPEAT(decr($1),`$2')$2($1)')')dnl
dnl
dnl ## Macro for generating user ruleset hooks
define(`_USER_HOOK',`ifdef(`$1',R$`'* $: $>$1 ifelse(`$2',`',$`'1,detox($2))
)')dnl
dnl
dnl
dnl
dnl ## ----------------------------------------------------------------------
dnl ## Macros to distinguish between various sendmail versions
dnl ##
dnl ## Sendmail 8.8
dnl ## No special defines needed
dnl ##
dnl ## Sendmail 8.9 onwards:
dnl ## Define SM89 to modify the behaviour as follows:
dnl ## * Local Ruleset Hooks name change:
dnl ## check_relay --> Local_check_relay
dnl ## check_mail --> Local_check_mail
dnl ## check_rcpt --> Local_check_rcpt
dnl ## check_compat --> Local_check_compat
dnl ##
dnl ## * No need for dequote Tokenizing on RHS of rulesets
dnl ##
dnl ## ----------------------------------------------------------------------
define(`detox',ifdef(`SM89',`$1',`$(dequote "" $1 $)'))dnl
define(`__RLAY_CHECK__',ifdef(`SM89',`SLocal_check_relay',`Scheck_relay'))dnl
define(`__MAIL_CHECK__',ifdef(`SM89',`SLocal_check_mail',`Scheck_mail'))dnl
define(`__RCPT_CHECK__',ifdef(`SM89',`SLocal_check_rcpt',`Scheck_rcpt'))dnl
define(`__CMPT_CHECK__',ifdef(`SM89',`SLocal_check_compat',`Scheck_compat'))dnl
dnl
dnl
dnl ## ----------------------------------------------------------------------
dnl ## Set up default values for those macros not defined
dnl ## ----------------------------------------------------------------------
ifdef(`__AUTOAUTH_FILE__',`',`define(`__AUTOAUTH_FILE__',`dbm -o -m -a@AUTH /etc/mail/AutoAuth')')dnl
ifdef(`__CLIENT_DATABASE__',`',`define(`__CLIENT_DATABASE__',`dbm -a. /etc/mail/AddressDatabase')')dnl
ifdef(`__CLIENT_DEFAULT__',`',`define(`__CLIENT_DEFAULT__',`')')dnl
ifdef(`__RBL_SERVER__',`',`define(`__RBL_SERVER__',`rbl.maps.vix.com')')dnl
dnl
dnl
dnl
dnl
divert(0)dnl
VERSIONID(`@(#)checking.m4' __CHECKING_VERSION `('__CHECKING_AUTHOR`)' __CHECKING_LASTUPD)dnl
PUSHDIVERT(6)dnl
define(`_AF_',`alias')dnl
_REPEAT(9,`__DO_DEFINE_ALIAS')dnl ## Add any additional defined alias files
_REPEAT(9,`__DO_FROM_PROG_TABLE')dnl ## Add any external program calls
_REPEAT(9,`__DO_FROM_REGEX_TABLE')dnl ## Add any regex checks
dnl ##
dnl ## Define standard password and aliases maps
Kpasswd user -m
Kalias implicit -m ALIAS_FILE
Klocal sequence _AF_ passwd
KAddressDatabase __CLIENT_DATABASE__
Kautoauth __AUTOAUTH_FILE__
POPDIVERT
dnl
dnl
divert(2)dnl
LOCAL_RULESETS
dnl ## ----------------------------------------------------------------------
dnl ## generally useful rulesets, used as 'subroutines' by the main checking
dnl ## functions.
dnl ##
dnl ## Comments:
dnl ## The symbol _S_ is used as a separator. Can be redefined if needed.
dnl ##
dnl ## In general, the workspace usage is:
dnl ## thing to check _S_ original parameters _S_ current options
dnl ##
dnl ## ----------------------------------------------------------------------
dnl ## ----------------------------------------------------------------------
dnl ## AHdebug: Convert the special $| symbol when in test mode (-bt)
dnl ##
dnl ## Input:
dnl ## something $$| something [ typed in as something $| something ]
dnl ##
dnl ## Output:
dnl ## something $| something
dnl ##
dnl ## Comments:
dnl ## Used when run in test mode. Since $| is a special internal marker that
dnl ## cannot be entered, this ruleset simply converts it from the external
dnl ## to the internal form, wherever it appears in the pattern.
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper March 1998 Original Version
dnl ##
dnl ## ----------------------------------------------------------------------
SAHdebug
R$* $$| $* $1 $| $2 For sendmail -bt conversion
dnl ## ----------------------------------------------------------------------
dnl ## client_options: retrieve the options for the current client
dnl ##
dnl ## Input:
dnl ## client_addr $| client_name _S_ preserved stuff
dnl ##
dnl ## Output:
dnl ## garbage _S_ preserved_stuff _S_ client options list
dnl ##
dnl ## Comments:
dnl ## A client is identifed by {client_addr} and/or {client_name} (which
dnl ## are assumed to have been previously validated.
dnl ##
dnl ## {client_addr} has the form a.b.c.d and is looked up in the map
dnl ## in the order: a.b.c.d, then a.b.c, then a.b, then a; the first match
dnl ## found gives the options for this client.
dnl ##
dnl ## {client_name) has the form name.subdomain...subdomain.domain. If the
dnl ## {client_addr} does not match, then this is looked up in the map in the
dnl ## order: name.subdomain..subdomain.domain, then subdomain..subdomain.domain,,
dnl ## then subdomain.domain (each subdomain stripped one at a time), and finally
dnl ## just domain; the first match found gives the options for the client.
dnl ##
dnl ## If no match is given by any of the above, then a default set of options
dnl ## is returned.
dnl ##
dnl ## Options are simply a set of case insensitive keywords which indicate
dnl ## some requirement or ability of the client. Exact keywords or meanings
dnl ## depend on the calling rulesets.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.5 Andy Harper April 1998 Fix dequote for SM89
dnl ##
dnl ## ----------------------------------------------------------------------
Sclient_options
dnl ## Map 0 into local address
R0 $| $* _S_ $* $: 127.0.0.1 $| $1 _S_ $2 Map 0 into local address
dnl ## dequote and add 'dot'
R$* $| $* _S_ $* $: detox($1) . $| $2 _S_ $3 Suffix a dot to the client address
R$* $| $* _S_ $* $: $1 $| . detox($2) _S_ $3 Prefix a dot to the client name
R$* $: $1 _S_
dnl ## Search on address
R$*$-. $| $* _S_ $* _S_ $1 $| $3 _S_ $4 _S_ $(AddressDatabase $1$2 $: $) Lookup host address
dnl ## Search on name
R$* $| .$-$* _S_ $* _S_ $1 $| $3 _S_ $4 _S_ $(AddressDatabase $2$3 $: $) Lookup host domain
dnl ## Default
R$* $| $* _S_ $* _S_ $: $1 $| $2 _S_ $3 _S_ $(AddressDatabase DEFAULT $: $) Last ditch, lookup default entry
R$* $| $* _S_ $* _S_ $: $1 $| $2 _S_ $3 _S_ __CLIENT_DEFAULT__ Use in-built default
dnl ## ----------------------------------------------------------------------
dnl ## AddressOptions: retrieve the options for a given e-mail address/domain
dnl ##
dnl ## Input:
dnl ## address _S_ preserved stuff _S_ more preserved stuff
dnl ##
dnl ## Output:
dnl ## address options _S_ preserved stuff _S_ more preserved stuff
dnl ##
dnl ## Comments:
dnl ## An e-mail address has the general format:
dnl ## user@system.subdomain...subdomain.domain
dnl ##
dnl ## The e-mail address is looked up in the AddressDatabase map in the following
dnl ## order, the first one found defines the options for the address:
dnl ## 1. user@system.subdomain...subdomain.domain
dnl ## 2. system.subdomain...subdomain.domain
dnl ## 3. subdomain...subdomain.domain
dnl ## 4. : [ each subdomain removed one at a time ]
dnl ## 5. subdomain.domain
dnl ## 6. domain
dnl ##
dnl ## If none of the above result in a match, then the keyword DEFAULT is
dnl ## looked up in the map and used if found. If this is not found, then the
dnl ## set of built-in defaults is used (see __CLIENT_DEFAULT__).
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
SAddressOptions
R$* $: >$1
R>$*@$* _S_ $* _S_ $* $: $(AddressDatabase $1@$2 $: > .$2 $) _S_ $3 _S_ $4 Look up specific e-mail address
R>.$-$* _S_ $* _S_ $* $(AddressDatabase $1$2 $: > $2 $) _S_ $3 _S_ $4 Lookup domain
R>$* _S_ $* _S_ $* $: $(AddressDatabase DEFAULT $: > $1 $) _S_ $2 _S_ $3 Last ditch default entry
R>$* _S_ $* _S_ $* $: __CLIENT_DEFAULT__ _S_ $2 _S_ $3 Built-in default if not entry found
dnl ## ----------------------------------------------------------------------
dnl ## CheckAuto: Check if client address is in the autoauth map
dnl ##
dnl ## Input:
dnl ## junk _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## junk _S_ preserve _S_ preserve [ In autoauth ]
dnl ## >junk _S_ preserve _S_ preserve [ NOT in autoauth ]
dnl ##
dnl ## Comments:
dnl ## The autoauth map is dynamically updated by an external process
dnl ## with IP addresses of clients that have recently successfully logged in
dnl ## and read mail.
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper April 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
SCheckAuto
R $* _S_ $* _S_ $* $: >$(autoauth $&{client_addr} $) _S_ $2 _S_ $3 Client IP address may be auto-authorized
R>$*@AUTH _S_ $* _S_ $* $: $1 _S_ $2 _S_ $3 Client IP address IS allowed, remove marker
dnl ## ----------------------------------------------------------------------
dnl ## virtuser: Error if address not in virtual users table
dnl ##
dnl ## Input:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## Should be called only if hostname part is in =w
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Svirtuser
R$* _S_ $* _S_ $* $: $(virtuser $1 $: NOUSER@NOSITE $) $| <$1> _S_ $2 _S_ $3
RNOUSER@NOSITE $| $* _S_ $* _S_ $* $#error $@ NOUSER $: "550 unknown local user"
R$* $| $* _S_ $* _S_ $* $: $2 _S_ $3 _S_ $4
dnl ## ----------------------------------------------------------------------
dnl ## generics: error if address not in generics user table
dnl ##
dnl ## Input:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## Should be called only if hostname part is in =G
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Sgenerics
R$* _S_ $* _S_ $* $: $(generics $1 $: NOUSER@NOSITE $) $| $1 _S_ $2 _S_ $3
RNOUSER@NOSITE $| $* _S_ $* _S_ $* $#error $@ NOUSER $: "550 unknown local user"
R$* $| $* _S_ $* _S_ $* $: $2 _S_ $3 _S_ $4 Restore original address
dnl ## ----------------------------------------------------------------------
dnl ## localuser: check if username is in local user or alias tables
dnl ##
dnl ## Input:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The user is looked up in the standard password file and in the alias
dnl ## tables. Should only be called if hostname part is $j
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Slocaluser
R$*@$* _S_ $* _S_ $* $: $1 $| $1@$2 _S_ $3 _S_ $4 Save Original address, extract username
R$+ + $* $| $* _S_ $* _S_ $* $: $(local $1 $: NOUSER@NOSITE $) $| $3 _S_ $4 _S_ $5 Lookup 'plussed' user
RNOUSER@NOSITE $| $* _S_ $* _S_ $* $#error $@ NOUSER $: "550 unknown local user"
R$+ $| $* _S_ $* _S_ $* $: $(local $1 $: NOUSER@NOSITE $) $| $2 _S_ $3 _S_ $4 Lookup ordinary non-'plussed' user
RNOUSER@NOSITE $| $* _S_ $* _S_ $* $#error $@ NOUSER $: "550 unknown local user"
R$+ $| $* _S_ $* _S_ $* $: $2 _S_ $3 _S_ $4 Restore original address
dnl ## ----------------------------------------------------------------------
dnl ## testlocal: check if address is a local one. If not, prefix it
dnl ##
dnl ## Input:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ preserve _S_ preserve [ MATCH ]
dnl ## > address _S_ preserve _S_ preserve [ NO MATCH ]
dnl ##
dnl ## Comments:
dnl ## If address is local, it is checked for validity and rejected if not
dnl ## a known local one. If address, the address is returned with the
dnl ## error prefix.
dnl ##
dnl ## Revision History:
dnl ## 1.0 Andy Harper March 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
Stestlocal
R$* _S_ $* _S_ $* $: > $1 _S_ $2 _S_ $3 Add prefix
R>$*@$j _S_ $* _S_ $* $: $>localuser $1@$j _S_ $2 _S_ $3 Remove error marker if this is the local host
ifdef(`VIRTUSER_TABLE',`dnl
R>$*@$=w _S_ $* _S_ $* $: $>virtuser $1@$2 _S_ $3 _S_ $4 Ensure user in virtusertable
')dnl
ifdef(`GENERICS_TABLE',`dnl
R>$*@$=G _S_ $* _S_ $* $: $>generics $1@$2 _S_ $3 _S_ $4 Ensure user in genericstable
')dnl
dnl ## ----------------------------------------------------------------------
dnl ## HeloFqdn : Check if HELO string is not fully qualified
dnl ##
dnl ## Input:
dnl ## junk _S_ Preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## HELO string _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## RFC 1123, section 5.2.5 states that the name supplied on the HELO
dnl ## command in SMTP MUST be a fully qualified name. This routine checks
dnl ## that it is and generates a 501 error if not.
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
SHeloFqdn
R $* _S_ $* _S_ $* $: detox($&s) _S_ $2 _S_ $3 Get the HELO string
R $- _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 access denied, HELO string <"$&s"> from client ["$&{client_addr}"] not fully qualified (see RFC 1123, section 5.2.5)"
dnl ## ----------------------------------------------------------------------
dnl ## HeloCname : Check if HELO string is a CNAME; that is, an official host
dnl ## name rather than an alias.
dnl ##
dnl ## Input:
dnl ## junk _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## HELO string _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## RFC 1123, section 5.2.5 states that the hostname specified on the HELO
dnl ## command in SMTP must be canonical. That is, it must NOT be an alias but
dnl ## must be the official host name. This routine checks that it is and
dnl ## generates a 501 error if not. Note also that a dotted domain literal
dnl ## is also prohibited by this standard (E.G. [1.2.3.4] ).
dnl ##
dnl ## If the hostname lookup fails, further checking is suppressed since we
dnl ## dont want to reject a connection on this basis (RFC 1132 again!)
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
SHeloCname
R $* _S_ $* _S_ $* $: detox($&s) _S_ $2 _S_ $3
R [$*] _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 access denied, HELO name <"$&s"> from client ["$&{client_addr}"] not canonical (see RFC 1123, section 5.2.5). Dotted domain literal not permitted here."
R $* _S_ $* _S_ $* $: $[ $1 $] _S_ $2 _S_ $3
R $*. _S_ $* _S_ $* $: $1 _S_ $2 _S_ $3 Ignore this error, remove dot!!!
R $&s _S_ $* _S_ $* $@ detox($&s) _S_ $1 _S_ $2 Matches itself, all OK
R $* _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 access denied, HELO name <"$&s"> from client ["$&{client_addr}"] not canonical (see RFC 1123, section 5.2.5). Should be <"$1">"
dnl ## ----------------------------------------------------------------------
dnl ## HeloMatch: Check announced name matches that supplied in DNS
dnl ##
dnl ## Input:
dnl ## client_name _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## client_name _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The announced name, as specified on the HELO or EHLO command, is in
dnl ## macro $&s. We check this against the client name.
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper April 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
SHeloMatch
R $&s _S_ $* _S_ $* $@ $&s _S_ $1 _S_ $2
R $* _S_ $* _S_ $* $#error $@ 4.5.1 $: "451 access denied, client ["$&{client_addr}"] HELO name <"$&s"> does not match official DNS name <"$&{client_name}">. Check hostname configuration of client and DNS (may be temporary network error)"
dnl ## ----------------------------------------------------------------------
dnl ## AccessBarred: display message for a barred client
dnl ##
dnl ## Input:
dnl ## NONE
dnl ##
dnl ## Output:
dnl ## NONE
dnl ##
dnl ## Comments:
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
SAccessBarred
R$* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, client ["$&{client_addr}"] is barred from accessing this server"
dnl ## ----------------------------------------------------------------------
dnl ## RBLCheck: Check if client is known to the Real-Time Black Hole server
dnl ##
dnl ## Input:
dnl ## client_addr _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## junk _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The real-time black hole server is a central list of clients known to
dnl ## generate spam or behave in some other obnoxious way. If the client
dnl ## is found here, it will be rejected.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.5 Andy Harper April 1998 Fix dequote for SM89
dnl ##
dnl ## ----------------------------------------------------------------------
SRBLCheck
ifdef(`SM89',,dnl
R$* _S_ $* _S_ $* $: detox($1) _S_ $2 _S_ $3 Dequote the address
)dnl
R$-.$-.$-.$- _S_ $* _S_ $* $: $[ $4.$3.$2.$1.__RBL_SERVER__ $] _S_ $5 _S_ $6 Look up the address in the RTBL server
R$-.$-.$-.$-.__RBL_SERVER__. _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied from ["$4.$3.$2.$1"], see http://maps.vix.com/rbl/"
dnl ## ----------------------------------------------------------------------
dnl ## check_mailertable : check if hostname in mailertable
dnl ##
dnl ## Input:
dnl ## type Host/IP _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## > type Host/IP _S_ preserve _S_ preserve [NOT in mailertable]
dnl ## mailer : [ host ]_S_ preserve _S_ preserve [in mailertable]
dnl ##
dnl ## Comments:
dnl ## Mailertable provides static routing information for non-internet hosts.
dnl ## Such hosts have no DNS entry so DNS lookups will fail. Here we check
dnl ## if a host exists in the mailertable and return flags accordingly.
dnl ## The mailertable can also contain domain names.
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_mailertable
R $- $* _S_ $* _S_ $* $: > $1 <$2> $2 _S_ $3 _S_ $4 Add error marker, save type
ifdef(`MAILER_TABLE',`dnl
R> $* <$*> $* _S_ $* _S_ $* $: $(mailertable $2 $: > $1 <.$2> $3 $) _S_ $4 _S_ $5 Lookup, remove marker if resolves
R> $* <. $- $*> $* _S_ $* _S_ $* $(mailertable .$2$3 $: > $1 <$3> $4 $) _S_ $5 _S_ $6 Lookup domain, strip domains one at a time, remove marker if resolves
')
R> $* <$*> $* _S_ $* _S_ $* $: > $1 $3 _S_ $4 _S_ $5 Restore original format if no match
dnl ## ----------------------------------------------------------------------
dnl ## check_dns: validate address in the dns. If not present, reject.
dnl ##
dnl ## Input:
dnl ## ident host or [ip] _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## ident host or [ip] _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## For security, it is best to accept messages only from systems that
dnl ## have an entry in the DNS. This effectively rejects those messages
dnl ## which fake hostnames by randomly generating them as valid systems
dnl ## will have DNS entries.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.5 Andy Harper May 1998 Support for mailertable
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_dns
R$* _S_ $* _S_ $* $: $>check_mailertable $1 _S_ $2 _S_ $3
R>$- $* _S_ $* _S_ $* $: > $1 $[ $2 $] _S_ $3 _S_ $4 Lookup in DNS
R>$- $*$~. _S_ $* _S_ $* $#error $@ 4.5.1 $: "451 access denied, "$1" <"$2$3"> does not resolve in DNS (maybe a temporary network error). Contact your local network managers to arrange registration"
dnl ## ----------------------------------------------------------------------
dnl ## check_mtch: ensure that resolved client_addr matches client_name
dnl ##
dnl ## Input:
dnl ## client_addr _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## client_name _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## For security reasons, it is best if the client_addr resolves to the
dnl ## same name as client_name, otherwise the client may be attempting to
dnl ## forge who it is. So reject any non-match.
dnl ## This can also catch DNS configuration errors..
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.1 Andy Harper March 1998 Make this work at last!!
dnl ## 2.5 Andy Harper April 1998 Fix dequote for SM89
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_mtch
ifdef(`SM89',,dnl
R $* _S_ $* _S_ $* $: detox($1) _S_ $2 _S_ $3 Dequote the address
)dnl
R $* _S_ $* _S_ $* $: [$1] _S_ $2 _S_ $3 Add square brackets around address
R [0] _S_ $* _S_ $* $: [127.0.0.1] _S_ $1 _S_ $2 Replace 0 by local address
R $* _S_ $* _S_ $* $: $[ $1 $] _S_ $2 _S_ $3 Lookup address, then add error marker
R $*. _S_ $* _S_ $* $: $1 _S_ $2 _S_ $3 Remove the trailing dot added by successful lookup
R $&{client_name} _S_ $* _S_ $* $@ detox($&{client_name}) _S_ $1 _S_ $2 If names match then remove error marker
R $* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, relay hostname <"$&{client_name}"> does not match DNS resolution of address "$&{client_addr}" ("$1")"
dnl ## ----------------------------------------------------------------------
dnl ## check_syntax: check the syntax of the address on a protocol line
dnl ##
dnl ## Input:
dnl ##
_S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The syntax of the address must be , with no trailing dot
dnl ## in the hostname part. Any non-conformance to this and the message is
dnl ## rejected.
dnl ##
dnl ## We also check for multiple @ symbols in the address. However, this
dnl ## must be called after routing info has been stripped:
dnl ## <@xxx[,@yyy ..]:user@site> -->
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.5 Andy Harper May 1998 Add check for multiple '@'
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_syntax
R<<$*>> _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, extraneous level of angle brackets. Check configuration"
R$* @ $* @ $* _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, multiple @ in address (perhaps missing comma between two addresses?)"
R<$+@$*$~.> _S_ $* _S_ $* $@ <$1@$2$3> _S_ $4 _S_ $5
dnl ## miscellaneous specific errors, catch all at end
R<$+@$*.> _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, trailing dot at end of hostname is invalid (see RFC 1123 section 5.2.18)"
R<@$+> _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, missing username in address"
R<$+@> _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, missing hostname in address"
R<> _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, null address part"
R$* _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 invalid syntax, should be: "
dnl ## ----------------------------------------------------------------------
dnl ## check_fqdn: check that hostname part of address is fully qualified
dnl ##
dnl ## Input:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The hostname part must be fully qualified otherwise it is not valid at
dnl ## the receiving site. So reject the message if it is not.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fqdn
R$*@$- _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 hostname <"$2"> not fully qualified, check configuration"
dnl ## ----------------------------------------------------------------------
dnl ## FromCname: Check if FROM address is canonical
dnl ##
dnl ## Input:
dnl ## FROM address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## FROM address _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## RFC 1123 section 5.2.2 requires that a hostname in an address MUST
dnl ## be canonical and not an alias. It can also be a domain literal
dnl ## as in [a.b.c.d]. This routine checks it.
dnl ##
dnl ## The $f macro contains the original address and we use this to match.
dnl ## Due to restrictions on how we match, some problems can't be detected
dnl ## To avoid erroneous errors, we skip such formats. Known ones are:
dnl ## Multiple angle brackets
dnl ## Quoted username parts (dequote doesn't seem to work right here!)
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First Version
dnl ## ----------------------------------------------------------------------
SFromCname
dnl ## come in with @xyz:abc@def or abc@def
R $* _S_ $* _S_ $* $: detox($&f) _S_ $2 _S_ $3 Initialize with from address
dnl ## allow dotted domain literals
R $* @ [ $* ] _S_ $* _S_ $* $@ $1 @ [ $2 ] _S_ $3 _S_ $4 Always allow dotted domain literal
R $* _S_ $* _S_ $* $: > $1 _S_ $2 _S_ $3 Add error marker
R > $&f _S_ $* _S_ $* $: detox($&f) _S_ $1 _S_ $2 If $&f matches, no quotes so continue
R > $* _S_ $* _S_ $* $@ $1 _S_ $2 _S_ $3 Error marker still there, return now
dnl ## Remove extraneous <>, if any. invalid but can happen..
R<$*> _S_ $* _S_ $* $1 _S_ $2 _S_ $3 Remove extra <..>
dnl ## Initialize save area
R $* _S_ $* _S_ $* $: $1 <> _S_ $2 _S_ $3
dnl ## extract hostname if relay format
R @ $* : $* @ $* <> _S_ $* _S_ $* $: @ $1 : $2 <$3> $3 _S_ $4 _S_ $5 Extract hostname from relay format
dnl ## extract hostname if normal format
R $* @ $* <> _S_ $* _S_ $* $: $1 <$2> $2 _S_ $3 _S_ $4 Extract hostname for standard format
R $* <> _S_ $* _S_ $* $@ $1 _S_ $2 _S_ $3 No correct format was found, exit now
dnl ## canonicalize hostname, preserving original
R $* <$+> $* _S_ $* _S_ $* $: $1 <$2> $[ $3 $] _S_ $4 _S_ $5 Canonicalize hostname
R $* <$+> $*. _S_ $* _S_ $* $: $1 <$2> $3 _S_ $4 _S_ $5 Remove trailing dot if it matches
dnl ## put back into original form
R $* <$+> $* _S_ $* _S_ $* $: <$1@$3> <$2> $3 _S_ $4 _S_ $5 Restore format, so replace hostname by canonical version
dnl ## match against original, with and without one extra level of <..>
R <$&f> <$+> $* _S_ $* _S_ $* $@ detox($&f) _S_ $3 _S_ $4 Does it match original ?
R $&f <$+> $* _S_ $* _S_ $* $@ detox($&f) _S_ $3 _S_ $4 Does it match original ?
dnl ## no match to anything is an error
R <$*> <$+> $* _S_ $* _S_ $* $#error $@ 5.0.1 $: "501 access denied, FROM address <"$&f"> hostname <"$2"> is not canonical. See RFC 1123, section 5.2.2. Should be <"$3">"
dnl ## ----------------------------------------------------------------------
dnl ## MatchOne: Match $&f against a string
dnl ##
dnl ## Input:
dnl ## > string $| remainder $| original list _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## > string $| remainder $| original list _S_ preserve _S_ preserve
dnl ## string $| remainder $| original list _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## $&f is used as it is the only way to match against the workspace
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 Original Version
dnl ##
dnl ## ----------------------------------------------------------------------
SMatchOne
R>$&f $| $* $| $* _S_ $* _S_ $* $: detox($&f) $| $1 $| $2 _S_ $3 _S_ $4
dnl ## ----------------------------------------------------------------------
dnl ## check_fnone: Error out on all addresses
dnl ##
dnl ## Input:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## Should be called only if the database specifies that no from address is
dnl ## valid for the current client.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fnone
R$* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized FROM address <"$&f"> from client ["$&{client_addr}"]"
dnl ## ----------------------------------------------------------------------
dnl ## check_ffixed: Make sure address is the one fixed for the client
dnl ##
dnl ## Input:
dnl ## list of permitted addresses _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## matched address $| list of permitted addresses _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## Forces this client to use one of a list of specific fixed addresses,
dnl ## as given by the database entry: FromFixed=. Care
dnl ## should be taken that the address portion does not contain any words
dnl ## that could be mistaken for database keywords.
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper March 1998 First version
dnl ## 2.4 Andy Harper March 1998 Upgrade to allow a list of addresses
dnl ## 2.5 Andy Harper May 1998 Rewrite for common MatchOne routine
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_ffixed
R $* _S_ $* _S_ $* $: > $| $1, $| $1 _S_ $2 _S_ $3 Initialize format and add error marker
R> $* $| $*,$* $| $* _S_ $* _S_ $* $>MatchOne > $2 $| $3 $| $4 _S_ $5 _S_ $6 Match next address
R> $* $| $* $| $*,$* _S_ $* _S_ $* $# error $@ 5.7.1 $: "571 access denied, unauthorized FROM address <"$&f">. Client ["$&{client_addr}"] must use one of <"$3","$4">"
R> $* $| $* $| $* _S_ $* _S_ $* $# error $@ 5.7.1 $: "571 access denied, unauthorized FROM address <"$&f">. Client ["$&{client_addr}"] must use <"$3">"
dnl ## ----------------------------------------------------------------------
dnl ## check_fclient: check that the hostname part matches the current client_name
dnl ##
dnl ## Input:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fclient
R$*@$&{client_name} _S_ $* _S_ $* $@ $1@detox($&{client_name}) _S_ $2 _S_ $3 Strip error marker if its correct
R$* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized hostname in FROM address <"$&f">. Client ["$&{client_addr}"] must use <"$&{client_name}">"
dnl ## ----------------------------------------------------------------------
dnl ## check_fhost : check hostname part of FROM is in list of hostnames
dnl ##
dnl ## Input:
dnl ## domain list _S_ sender $| recipient _S_ options
dnl ##
dnl ## Output:
dnl ## > string $| remainder $| original list _S_ preserve _S_ preserve
dnl ## string $| remainder $| original list _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## Use $&f macro here to match with the from address.
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fhost
R $* _S_ $* _S_ $* $: > $| $1, $| $1 _S_ $2 _S_ $3 Initialize format and add error marker
R>$* $| $*,$* $| $* _S_ $*@$* $| $* _S_ $* $>MatchOne > $5@$2 $| $3 $| $4 _S_ $5@$6 $| $7 _S_ $8 Recursive scan thru list
R>$* $| $* $| $*,$* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized hostname in FROM address <"$&f">. Client ["$&{client_addr}"] must use one of <"$3","$4">"
R>$* $| $* $| $* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized hostname in FROM address <"$&f">. Client ["$&{client_addr}"] must use <"$3">"
dnl ## ----------------------------------------------------------------------
dnl ## check_flocal: check that the address is valid for the local system
dnl ##
dnl ## Input:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The address must be one which is accepted for delivery by the local
dnl ## system.
dnl ## So it must be either:
dnl ## * In the virtusertable, if this is defined, OR
dnl ## * In the genericstable, if this is defined, OR
dnl ## * A local alias or username
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.1 Andy Harper February 1998 Fix bug with virtual user check
dnl ## .. and use check_remote routine
dnl ## 2.2 Andy Harper March 1998 Use common testlocal routine
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_flocal
R$* _S_ $* _S_ $* $: $>testlocal $1 _S_ $2 _S_ $3 Check if a valid local user
R>$* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized non-local FROM address <"$&f">. Client ["$&{client_addr}"] must use a recognized local address"
dnl ## ----------------------------------------------------------------------
dnl ## check_fdomain: Check that the address specifies one within the local domain
dnl ##
dnl ## Input:
dnl ## address _S_ address$|preserve _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## address options _S_ address$|preserve _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## The database defines which addresses/domains are local, by means of the
dnl ## flag 'LocalDomain'.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fdomain
R$* _S_ $* _S_ $* $| $* LocalDomain $* $| $* $@ $1 _S_ $2 _S_ $3 $| $4 LocalDomain $5 $| $6 Test for being in local domain
R$* _S_ $* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized non-local FROM address <"$&f">. Client ["$&{client_addr}"] must use a local address"
dnl ## ----------------------------------------------------------------------
dnl ## check_fdomains: Check that current address lies within one of a set of domains
dnl ## MatchDomain: check that current address lies within specific domain
dnl ## Matchf: check that current address matches FROM
dnl ##
dnl ## Input:
dnl ## domainlist _S_ preserve _S_ preserve
dnl ##
dnl ## Internal:
dnl ## > address $| $f with focusing $| current domain $| remaining domains $| full domain list _S_ preserve _S_ preservre
dnl ##
dnl ## Output:
dnl ## junk _S_ preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## Current address is obtained from the $f macro.
dnl ##
dnl ## A VERY complicated set of routines! Check that the FROM address given
dnl ## by $&f lies within a set of specific domain names.
dnl ##
dnl ## Due to limitations in the way we can match things inside sendmail,
dnl ## a rather round-about algorithm is used to do this. Consider:
dnl ## address = and required domain = x.y.z
dnl ##
dnl ## What is required is for a.b.c.d to be within x.y.x, that is
dnl ## user@a.b.c.d should be equivalent to user@a.x.y.z
dnl ##
dnl ## So, we strip leading components of the FROM hostname and add them to the
dnl ## required domain name one at a time, then compare the result with the
dnl ## original address $f. If it matches, we have an address within the
dnl ## required domain. Using the above example:
dnl ## Match user@x.y.z with user@a.b.c.d THEN
dnl ## Match user@a.x.y.z with user@a.b.c.d THEN
dnl ## Match user@a.b.x.y.z with user@a.b.c.d ... etc.
dnl ##
dnl ## Real example:
dnl ## address=fred@green.computing.mich.edu, domain=mich.edu
dnl ##
dnl ## Compare:
dnl ## fred@mich.edu fred@green.computing.mich.edu [NO]
dnl ## fred@green.mich.edu fred@green.computing.mich.edu [NO]
dnl ## fred@green.computing.mich.edu fred@green.computing.mich.edu [YES!]
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
SMatchf
R> $&f $| $* $| $* $| $* $| $* _S_ $* _S_ $* $: detox($&f) $| $1 $| $2 $| $3 $| $4 _S_ $5 _S_ $6 If address matchs FROM, remove error marker
SMatchDomain
R> $* $| $*@$* $| $* $| $* $| $* _S_ $* _S_ $* $: > $1 $| $2@<$3.> $| $4 $| $5 $| $6 _S_ $7 _S_ $8 Add trailing dot to hostname and enclose it by <>
R> $* $| $*<$+.$*> $| $* $| $* $| $* _S_ $* _S_ $* $>Matchf > $2$5 $| $2$3.<$4> $| $5 $| $6 $| $7 _S_ $8 _S_ $9 Match until success or no more hostname components
R> $* $| $*<> $| $* $| $* $| $* _S_ $* _S_ $* $: $>Matchf > $2$3 $| $2 <> $| $3 $| $4 $| $5 _S_ $6 _S_ $7 As above, but final top level domain of hostname
Scheck_fdomains
R$* _S_ $* _S_ $* $: > $| $| $| $1,$| $1 _S_ $2 _S_ $2 Save domain list and initialize for scan
R> $* $| $* $| $* $| $*,$* $| $* _S_ $* _S_ $* $>MatchDomain > $| detox($&f) $| $4 $| $5 $| $6 _S_ $7 _S_ $8 Match until success, or no more domains
dnl ## output error if no match
R> $* $| $* $| $* $| $* $| $*,$* _S_ $* _S_ $* $# error $@ 5.7.1 $: "571 access denied, unauthorized top level domain name in FROM address <"$&f">. Client ["$&{client_addr}"] must use one of "$5","$6
R> $* $| $* $| $* $| $* $| $* _S_ $* _S_ $* $# error $@ 5.7.1 $: "571 access denied, unauthorized top level domain name in FROM address <"$&f">. Client ["$&{client_addr}"] must use "$5
dnl ## ----------------------------------------------------------------------
dnl ## check_fremote: check the address; if a local one it must be valid,
dnl ## otherwise any valid external address is allowed.
dnl ##
dnl ## Input:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The address may be any local or remote address but, if local, it must
dnl ## be one which is accepted for delivery by the local system.
dnl ## So it must be either:
dnl ## * In the virtusertable, if this is defined, OR
dnl ## * In the genericstable, if this is defined, OR
dnl ## * A local alias or username OR
dnl ## * An external address (not j, =w or =G)
dnl ##
dnl ## Revision History:
dnl ## 1.0 Andy Harper February 1998 First version
dnl ## 2.0 Andy Harper March 1998 Use common testlocal routine
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fremote
R$* _S_ $* _S_ $* $: $>testlocal $1 _S_ $2 _S_ $3 Test for a local address
R>$* $: $1 Remove error marker
dnl ## ----------------------------------------------------------------------
dnl ## check_fextern: check that the address is external I.E. not an address
dnl ## within the local domain.
dnl ##
dnl ## Input:
dnl ## address _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address options _S_ address$|preserve _S_ preserve
dnl ##
dnl ## Comments:
dnl ## The database defines which addresses/domains are local, by means of the
dnl ## flag 'LocalDomain'.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fextern
R$* _S_ $* _S_ $* $| $* LocalDomain $* $| $* $#error $@ 5.7.1 $: "571 access denied, unauthorized local FROM address <"$&f">. Client ["$&{client_addr}"] must use a non-local address"
dnl ## ----------------------------------------------------------------------
dnl ## check_fauth: Check for a dynamically authorized IP address
dnl ##
dnl ## Input:
dnl ## address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## address _S_ preserve _S_ preserve [ If AUTHORIZED ]
dnl ## address options _S_ preserve _S_ preserve [ If Not AUTHORIZED ]
dnl ##
dnl ## Comments:
dnl ## If the client IP address appears in the 'AutoAuth' database, it is
dnl ## deemed to be dynamically authorized and the FROM address is valid. If
dnl ## it is not in the 'AutoAuth' database, then the address MUST be an
dnl ## external one.
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper March 1998 First Version
dnl ## 2.5 Andy Harper April 1998 Fix dequote for SM89
dnl ##
dnl ## ----------------------------------------------------------------------
Scheck_fauth
R $* _S_ $* _S_ $* $: > detox($&{client_addr}) $| $1 _S_ $2 _S_ $3 Preserve address, Add error marker, dequote IP address
R> $* $| $* _S_ $* _S_ $* $: > $(autoauth $1 $) $| $2 _S_ $3 _S_ $4 Lookup IP address in auto-authorize table
R> $* @AUTH $| $* _S_ $* _S_ $* $: $2 _S_ $3 _S_ $4 Successful? remove error marker and check for local address
R> $* $| $* _S_ $* _S_ $* $: $>check_fextern $2 _S_ $3 _S_ $4 Not authorized, so must be external or else ...
dnl ## ----------------------------------------------------------------------
dnl ## PercentHack : Check local attempt to route using
dnl ##
dnl ## Input:
dnl ## a % _S_ preserve $| recipient _S_ clientopt $| senderopt $| recipientoptions
dnl ##
dnl ## Output:
dnl ## junk _S_ preserve $| a[%b]@c _S_ clientopt $| senderopt $| new recipientoptions
dnl ##
dnl ## Comments:
dnl ## To prevent users spoofing the relay checks by using an address of the
dnl ## form:
dnl ## a%b@c
dnl ##
dnl ## The address is parsed to strip out the local system and rewrite it
dnl ## as:
dnl ## a@b (only if c has the NoPercentHack attribute)
dnl ##
dnl ## The recipient options are then obtained for the new address
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper May 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
SPercentHack
R$* <$* % $* @ $*> _S_ $* $| $* _S_ $* $1 $2 % <$3 @ $4> _S_ $5 $| $6 _S_ $7 Find last percent location
R$* % <$* @ $*> _S_ $* $| $* _S_ $* $: $1 @ $2 _S_ $4 $| $1@$2 _S_ $6 Remove final routing portion
R$* _S_ $* $| $* _S_ $* $: $>AddressOptions $3 _S_ $2 $| $3 _S_ $4 Get new recipient options
R$* _S_ $* _S_ $*$|$*$|$* $: _S_ $2 _S_ $3$|$4$|$1 Insert recipient options as appropriate
dnl ## ----------------------------------------------------------------------
dnl ## ReDirect: Reject with indication of new address
dnl ##
dnl ## Input:
dnl ## New address _S_ preserve _S_ preserve
dnl ##
dnl ## Output:
dnl ## NONE
dnl ##
dnl ## Comments:
dnl ## Called when the 'ReDirect=<...>' option is specified for the recipient
dnl ## and causes the message to be rejected with an indication of the new
dnl ## address.
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper June 1998 First Version
dnl $$
dnl ## ----------------------------------------------------------------------
SReDirect
R$* _S_ $* _S_ $* $#error $@ 5.5.0 $: "550 address no longer valid, please redirect to "$1
dnl ## ----------------------------------------------------------------------
dnl ## LimitedRelayAuto: Block attempts to illegally relay through this system
dnl ## but allow autoauthorized clients
dnl ##
dnl ## Input:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## This routine is called if the client has limited relaying
dnl ## capability and autoauth capability (the 'LimitedRelayAuto' option).
dnl ##
dnl ## The client may relay only if one or more of the following are true:
dnl ## * The recipient has the 'RelayTo' option
dnl ##
dnl ## * The recipient has the 'RelayLocalFrom' option AND the sender
dnl ## address is within the 'LocalDomain'.
dnl ##
dnl ## * The client's IP address is currently in the autoauth database.
dnl ##
dnl ## NOTE: relaying based on the FROM address (RelayLocalFrom) is insecure
dnl ## because of the ease with which addresses can be faked.
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
SLimitedRelayAuto
R $* _S_ $* _S_ $* $| $* $| $* $: $>CheckAuto $1 _S_ $2 _S_ $3 $| $4 $| $5 Client IP address may be auto-authorized
R>$* _S_ $* _S_ $* $| $* $| $* RelayTo $* $: $1 _S_ $2 _S_ $3 $| $4 $| $5 RelayTo $6 Recipient has 'RelayTo', so remove marker
R>$* _S_ $* _S_ $* $| $* LocalDomain $* $| $* RelayLocalFrom $* $: $1 _S_ $2 _S_ $3 $| $4 LocalDomain $5 $| $6 RelayLocalFrom $7 FROM address is local, recipient has RelayLocalFrom
R>$* _S_ $*$|$* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized relay to <"$3"> from ["$&{client_addr}"]"
dnl ## ----------------------------------------------------------------------
dnl ## LimitedRelay: Block attempts to illegally relay through this system
dnl ##
dnl ## Input:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## This routine is called if the client has limited relaying
dnl ## capability (the 'LimitedRelay' option).
dnl ##
dnl ## The client may relay only if one or more of the following are true:
dnl ## * The recipient has the 'RelayTo' option
dnl ##
dnl ## * The recipient has the 'RelayLocalFrom' option AND the sender
dnl ## address is within the 'LocalDomain'.
dnl ##
dnl ## NOTE: Relaying based on the FROM address (RelayLocalFrom) is insecure
dnl ## because of the ease with which addresses can be faked.
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
SLimitedRelay
R $* _S_ $* _S_ $* $| $* $| $* $: > $1 _S_ $2 _S_ $3 $| $4 $| $5 Client IP address may be auto-authorized
R>$* _S_ $* _S_ $* $| $* $| $* RelayTo $* $: $1 _S_ $2 _S_ $3 $| $4 $| $5 RelayTo $6 Recipient has 'RelayTo', so remove marker
R>$* _S_ $* _S_ $* $| $* LocalDomain $* $| $* RelayLocalFrom $* $: $1 _S_ $2 _S_ $3 $| $4 LocalDomain $5 $| $6 RelayLocalFrom $7 FROM address is local, recipient has RelayLocalFrom
R>$* _S_ $*$|$* _S_ $* $#error $@ 5.7.1 $: "571 access denied, unauthorized relay to <"$3"> from ["$&{client_addr}"]"
dnl ## ----------------------------------------------------------------------
dnl ## RejectSpam: Block delivery of spam if recipient doesn't want it
dnl ##
dnl ## Input:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## Block delivery of this spam message. Should be called only if the
dnl ## sender has 'ImASpammer' or the client has 'SpamClient', and the
dnl ## recipient has 'NoSpamPlease'
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.4 Andy Harper March 1998 Remove 'NoSpamPlease' to caller
dnl ##
dnl ## ----------------------------------------------------------------------
SRejectSpam
R$* $#error $@ 5.7.1 $: "571 access denied, unsolicited mail from <"$&f"> via ["$&{client_addr}"] rejected"
dnl ## ----------------------------------------------------------------------
dnl ## RestrictMail: Block mail when sender is restricted to a subset of addresses
dnl ##
dnl ## Input:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## Should be called only when sender has 'LimitedMail' attribute and
dnl ## recipient has 'NoMailTo' attribute.
dnl ##
dnl ## Revision History:
dnl ## 1.0 Andy Harper February 1998 Rewrite to use client database
dnl ##
dnl ## ----------------------------------------------------------------------
SRestrictMail
R$* _S_ $*$|$* _S_ $* $#error $@ 5.7.1 $: "571 access denied, <"$&f"> not permitted to send to <"$3"> via ["$&{client_addr}"]"
dnl ## ----------------------------------------------------------------------
dnl ## BlackListFrom: Block mail when sender is flagged as blacklisted
dnl ##
dnl ## Input:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## Should be called only when sender has 'BlackListFrom' attribute
dnl ##
dnl ## Revision History:
dnl ## 2.3 Andy Harper March 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
SBlackListFrom
R$* $#error $@ 5.7.1 $: "571 access denied, sender address suspended due to previous misuse"
dnl ## ----------------------------------------------------------------------
dnl ## BlackListRcpt: Block mail when recipient is flagged as blacklisted
dnl ##
dnl ## Input:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## Should be called only when recipient has 'BlackListRcpt' attribute
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper March 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
SBlackListRcpt
R$* $#error $@ 5.7.1 $: "571 access denied, recipient address suspended due to previous misuse, cannot deliver"
dnl ## ----------------------------------------------------------------------
dnl ## ----------------------------------------------------------------------
SProgCheck
R$*$|$* _S_ $* _S_ $* $: $($1 $2 $) $| $2 _S_ $3 _S_ $4 Pass address to external program, store standard output
R$*@MATCH $| $* _S_ $* _S_ $* $# error $@ 5.7.1 $: "571 access denied, unsolicited mail from <"$&f"> via client ["$&{client_addr}"] (program match)"
dnl ## ----------------------------------------------------------------------
dnl ## RegexCheck: Check against regular expression
dnl ##
dnl ## Input:
dnl ## regex table name _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## regex table name _S_ sender$|recipient _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## Matches the sender address against the regular expression map. If this
dnl ## matches, it is rejected
dnl ##
dnl ## Must only be used if the map-regex feature is compiled into sendmail
dnl ##
dnl ## Revision History:
dnl ## 2.4 Andy Harper March 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
SRegexCheck
R$* _S_ $*$|$* _S_ $* $: $($1 $2 $) _S_ $2$|$3 _S_ $4 Match sender against regular expression
R$*@MATCH _S_ $*$|$* _S_ $* $# error $@ 5.7.1 $: "571 access denied, unsolicited mail from <"$&f"> via client ["$&{client_addr}"] (matches spam pattern)"
dnl ## ----------------------------------------------------------------------
dnl ## ClientCheck: Checks on SMTP client
dnl ##
dnl ## Input:
dnl ## junk _S_ preserve _S_ clientoptions
dnl ##
dnl ## Output:
dnl ## junk _S_ preserve _S_ clientoptions
dnl ##
dnl ## Purpose:
dnl ## * Check that the client is not barred explicitly
dnl ## * Check that client is not barred by the real-time black hole database
dnl ## * Check that client address and client name both resolve in the DNS
dnl ## * Check that resolved IP address matches host name
dnl ##
dnl ## Keyword Options:
dnl ## These keywords are recognized from the client database:
dnl ## AccessBarred Prevent the client from sending to us at all
dnl ## RBLCheck Lookup {client_addr} in the Real-time Black Hole database
dnl ## DNSClient Both {client_addr} and {client_name} must resolve in the DNS
dnl ## DNSmatch Resolution of {client_addr} must match with {client_name}
dnl ##
dnl ## Comments:
dnl ## Check various restrictions on the current client.
dnl ##
dnl ## NOTE 1: On Pre-SM89 systems, this cannot be called from check_relay
dnl ## because any error message is ignored. On SM89 and greater,
dnl ## any error message is correctly displayed.
dnl ## NOTE 2: On Pre-SM89 systems, the macros client_addr and client_name
dnl ## are not always defined in check_relay.
dnl ##
dnl ## To get around the above problems, we call this from check_mail if
dnl ## on a pre-SM89 system. Otherwise, it's called from check_relay
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper April 1998 First Version
dnl ##
dnl ## ----------------------------------------------------------------------
SClientCheck
R$* _S_ $* _S_ $* AccessBarred $* $: $>AccessBarred $&{client_addr} _S_ $2 _S_ $3 AccessBarred $4 Check if client barred
R$* _S_ $* _S_ $* DNSClient $* $: $>check_dns relay [$&{client_addr}] _S_ $2 _S_ $3 DNSClient $4 Check if client address resolves in the DNS
R$* _S_ $* _S_ $* DNSMatch $* $: $>check_mtch $&{client_addr} _S_ $2 _S_ $3 DNSMatch $4 Ensure resolved host name matches
R$* _S_ $* _S_ $* DNSClient $* $: $>check_dns relay $&{client_name} _S_ $2 _S_ $3 DNSClient $4 Check if client name resolves in the DNS
dnl ## ----------------------------------------------------------------------
dnl ## FromChecks: Make checks on the FROM address
dnl ##
dnl ## Input:
dnl ## junk _S_ From address $| preserve _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ From address $| preserve _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## Make comprehensive checks on the allowed form of the FROM address
dnl ## to ensure compatability with the client.
dnl ##
dnl ## The recipient options may be null at this point, so do not use them.
dnl ## But both client and sender options should be valid and may be used
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper April 1998 First version
dnl ## Andy Harper May 1998 Add 'FromHost=<..>' option
dnl ## Andy Harper May 1998 Add 'FromDomains='<..>' option
dnl ## Andy Harper May 1998 Re-order checks for speed
dnl ##
dnl ## ----------------------------------------------------------------------
SFromChecks
dnl ## Should use only ONE of the following FROM field checks!!!
R$* _S_ $*$|$* _S_ $* FromExternal $* $| $* $| $* $@ $>check_fextern $2 _S_ $2$|$3 _S_ $4 FromExternal $5 $| $6 $| $7 Must be an external address
R$* _S_ $*$|$* _S_ $* FromLocal $* $| $* $| $* $@ $>check_flocal $2 _S_ $2$|$3 _S_ $4 FromLocal $5 $| $6 $| $7 Must be address accepted by local mail host
R$* _S_ $*$|$* _S_ $* FromAuto $* $| $* $| $* $@ $>check_fauth $2 _S_ $2$|$3 _S_ $4 FromAuto $5 $| $6 $| $7 Dynamic authorization
R$* _S_ $*$|$* _S_ $* FromClient $* $| $* $| $* $@ $>check_fclient $2 _S_ $2$|$3 _S_ $4 FromClient $5 $| $6 $| $7 FROM address must match client name
R$* _S_ $*$|$* _S_ $* FromDomain $* $| $* $| $* $@ $>check_fdomain $2 _S_ $2$|$3 _S_ $4 FromDomain $5 $| $6 $| $7 Must be address within local domain
R$* _S_ $*$|$* _S_ $* FromDomains=<$*> $* $| $* $| $* $@ $>check_fdomains $5 _S_ $2$|$3 _S_ $4 FromDomains=<$5> $6 $| $7 $| $8
R$* _S_ $*$|$* _S_ $* FromHost=<$*> $* $| $* $| $* $@ $>check_fhost $5 _S_ $2$|$3 _S_ $4 FromHost=<$5> $6 $| $7 $| $8 Must be address within this list of domains
R$* _S_ $*$|$* _S_ $* FromFixed=<$*> $* $| $* $| $* $@ $>check_ffixed $5 _S_ $2$|$3 _S_ $4 FromFixed=<$5> $6 $| $7 $| $8 Extract the fixed address
R$* _S_ $*$|$* _S_ $* FromRemote $* $| $* $| $* $@ $>check_fremote $2 _S_ $2$|$3 _S_ $4 FromRemote $5 $| $6 $| $7 Must be address accepted by local mail host, or any external address
R$* _S_ $*$|$* _S_ $* FromNone $* $| $* $| $* $@ $>check_fnone $2 _S_ $2$|$3 _S_ $4 FromNone $5 $| $6 $| $7 No FROM Addres is permitted
dnl ## ----------------------------------------------------------------------
dnl ## FromChecksLocal: Do FromChecks, with automatic OK if recipient local
dnl ##
dnl ## Input:
dnl ## junk _S_ From address $| preserve _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Output:
dnl ## junk _S_ From address $| preserve _S_ clientopt $| senderopt $| recipopt
dnl ##
dnl ## Comments:
dnl ## If the 'FromOKIfLocalTo' option is specified, then we need to check
dnl ## the FROM address only if the recipient does NOT have the 'LocalDomain'
dnl ## option.
dnl ##
dnl ## A null FROM address (<>) is always allowed
dnl ##
dnl ## Revision History:
dnl ## 2.5 Andy Harper April 1998 First version
dnl ##
dnl ## ----------------------------------------------------------------------
SFromChecksLocal
R$* _S_ $* _S_ $* $| $* $| $* LocalDomain $* $@ $1 _S_ $2 _S_ $3 $| $4 $| $5 LocalDomain $6 Remove error marker to Skip check if recipient is local
R$* _S_ $|$* _S_ $* $@ $1 _S_ $|$2 _S_ $3 "" (=<>) is always allowed.
R$* $: $>FromChecks $1 Do the FROM checks if error marker still present
dnl ## ----------------------------------------------------------------------
dnl ## check_relay: Validate the client
dnl ##
dnl ## Input:
dnl ## client_addr $| client_name
dnl ##
dnl ## Purpose:
dnl ## * Check that the client is not barred explicitly
dnl ## * Check that client address and client name both resolve in the DNS
dnl ## * Check that resolved IP address matches host name
dnl ##
dnl ## Keyword Options:
dnl ## These keywords are recognized from the client database:
dnl ## AccessBarred Prevent the client from sending to us at all
dnl ## DNSClient Both {client_addr} and {client_name} must resolve in the DNS
dnl ## DNSmatch Resolution of {client_addr} must match with {client_name}
dnl ##
dnl ## Comments:
dnl ## The workspace is transformed to a fixed format used by all subroutines
dnl ## and looks like this:
dnl ## Thing to Check _S_ Original Input _S_ Options
dnl ##
dnl ## check_relay is called after the connection is established but before
dnl ## commands are processed.
dnl ##
dnl ## On Pre-SM89 systems:
dnl ## If check_relay returns a failure status then no message is
dnl ## issued but a flag is set indicating that the relay is bad. Subsequent
dnl ## commands, except for HELO, EHLO, RSET and QUIT are then rejected with
dnl ## '550 access denied' and it is not possible to change this message.
dnl ##
dnl ## Because of this inability to generate a more specific error message,
dnl ## checks on the relay have been moved into the check_mail ruleset so that
dnl ## a more useful message may be displayed.
dnl ##
dnl ## On SM89 systems and later:
dnl ## The configured error message is printed so most client checks are done here
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.1 Andy Harper February 1998 Move checks to check_mail
dnl ## 2.5 Andy Harper April 1998 External routine for relay checks
dnl ## Andy Harper April 1998 SM89: Move relay check
dnl ## Andy Harper April 1998 SM89: Ruleset Name change
dnl ## Andy Harper May 1998 Relocate RBLCheck to check_rcpt and make conditional on NoSpamPlease
dnl ##
dnl ## ----------------------------------------------------------------------
__RLAY_CHECK__
_USER_HOOK(`__RELAY_PREPROCESS_HOOK__')dnl
ifdef(`SM89',dnl
R$* $: $>client_options $1 _S_ $1 Retrieve options for this client
R$* $: $>ClientCheck $1 Check out the clients access
)dnl
_USER_HOOK(`__RELAY_POSTPROCESS_HOOK__',`$&{client_addr} $| $&{client_name}')dnl
dnl ## ----------------------------------------------------------------------
dnl ## check_mail: Make checks on the MAIL FROM: address
dnl ##
dnl ## Input:
dnl ## address as supplied on the MAIL FROM: protocol command
dnl ##
dnl ## Purpose:
dnl ## * Check HELO string is fully qualified (RFC 1123, section 5.2.5)
dnl ## * Check HELO string is canonical (RFC 1123, section 5.2.5)
dnl ## * Check HELO string matches client_name (RFC 1123, section 5.2.5) BUT NOT RFC COMPLIANT!!!!
dnl ## * Check that the client is not barred explicitly
dnl ## * Check that client address and client name both resolve in the DNS
dnl ## * Check that resolved IP address matches host name
dnl ## * Check syntax of address
dnl ## * Check hostname is fully qualified
dnl ## * Check hostname resolves in the DNS
dnl ## * Check validity of FROM address for the current client
dnl ##
dnl ## Keyword Options:
dnl ## These keywords are recognized from the client database:
dnl ## HeloFqdn Reject if HELO name not fully qualified
dnl ## HeloCname Reject if HELO name is not cacnonical
dnl ## HeloMatch Reject if HELO name doesn't match DNS name
dnl ## AccessBarred Prevent the client from sending to us at all
dnl ## DNSClient Both {client_addr} and {client_name} must resolve in the DNS
dnl ## DNSmatch Resolution of {client_addr} must match with {client_name}
dnl ## FromSyntax Check the syntax of the MAIL FROM: address
dnl ## FromFqdn Check the MAIL FROM: hostname is fully qualified
dnl ## FromCname Check that MAIL FROM: address is canonical
dnl ## FQDNFrom ... Alternate keyword for the above
dnl ## FromDns Check the MAIL FROM: hostname is in the DNS
dnl ## DNSFrom ... Alternate keyword for the above
dnl ## FromNone Disallow any address
dnl ## FromFixed= Allow only address a,b,...
dnl ## FromClient Allow only addresses with @{client_name}
dnl ## FromHost= Allow only hostnames a,b,...
dnl ## FromLocal Allow only valid local addresses
dnl ## FromRemote Allow valid local addresses or any external address
dnl ## FromDomain Allow only address flagged as 'LocalDomain'
dnl ## FromDomains= Allow only hostnames within domains a,b,...
dnl ## FromExternal Allow only address not flagged as 'LocalDomain'
dnl ## FromAuto Allow any external address; if auto-authorized, allow local too
dnl ## FromOkIfLocalTo If recipient is local, any FROM allowed
dnl ## BlackListFRom Prevent sending by this FROM address
dnl ##
dnl ## Comments:
dnl ## The workspace is transformed to a fixed format used by all subroutines
dnl ## and looks like this:
dnl ## Thing to Check _S_ Original Input _S_ Options
dnl ##
dnl ## The check_mail ruleset is called for each MAIL FROM: protocol line
dnl ##
dnl ## Pre-SM89:
dnl ## Some checks that would be better done in check_relay are actually done
dnl ## here so that a more specific error message may be given out.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.1 Andy Harper February 1998 Add checks from check_relay to here
dnl ## 2.2 Andy Harper February 1998 Add check for <>, remove routing info, canonicalize hostname
dnl ## 2.4 Andy Harper March 1998 Add FromFixed option
dnl ## Andy Harper March 1998 Add FromAuto option
dnl ## 2.5 Andy Harper April 1998 Add HeloMatch option
dnl ## Andy Harper April 1998 External routine for relay checks
dnl ## Andy Harper April 1998 Move FROM checks to external routine
dnl ## Andy Harper April 1998 SM89: Move relay check
dnl ## Andy Harper April 1998 SM89: Ruleset Name change
dnl ## Andy Harper April 1998 Add FromOkIfLocalTo option, defer check to check_rcpt in this case
dnl ## Andy Harper May 1998 Add FromHost=<..> option
dnl ## Andy Harper May 1998 Add FromDomains=<..> option
dnl ## Andy Harper May 1998 Add HeloFqdn option
dnl ## Andy Harper May 1998 Add HeloCname option
dnl ## Andy Harper May 1998 Add FromCname option
dnl ## Andy Harper May 1998 Relocate RBLCheck to check_rcpt
dnl ## Andy Harper Jun 1998 Add 'BlackListFrom' option
dnl ##
dnl ## ----------------------------------------------------------------------
__MAIL_CHECK__
_USER_HOOK(`__MAIL_PREPROCESS_HOOK__')dnl
R$* $: $>client_options $&{client_addr} $| $&{client_name} _S_ $1 Retrieve options for this client
R$* _S_ $* _S_ $* HeloFqdn $* $: $>HeloFqdn $1 _S_ $2 _S_ $3 HeloFqdn $4 Check HELO name; must be fully qualified
R$* _S_ $* _S_ $* HeloCname $* $: $>HeloCname $1 _S_ $2 _S_ $3 HeloCname $4 Check HELO name; must be canonical
R$* _S_ $* _S_ $* HeloMatch $* $: $>HeloMatch $&{client_name} _S_ $2 _S_ $3 HeloMatch $4 Check HELO name; must match DNS name (NOT RFC 1123 compliant!!!)
ifdef(`SM89',,dnl ## Insert client checks if pre-sm89
R$* $: $>ClientCheck $1 Make checks on the clients access
)dnl
R$* _S_ <@$*:$*> _S_ $* $1 _S_ <$3> _S_ $4 Remove routing info
R$* _S_ <> _S_ $* $@ OK Always allow <>
R$* _S_ $* _S_ $* FromSyntax $* $: $>check_syntax $2 _S_ $2 _S_ $3 FromSyntax $4 Check syntax of MAIL FROM: address
dnl ##
dnl ## remove <> from addresses
dnl ##
R$* _S_ <$*> _S_ $* $: $1 _S_ $2 _S_ $3 Remove <> around sender
R$* _S_ $* _S_ $* FQDNFrom $* $: $>check_fqdn $2 _S_ $2 _S_ $3 FQDNFrom $4 Check system name is fully qualified
R$* _S_ $* _S_ $* FromFqdn $* $: $>check_fqdn $2 _S_ $2 _S_ $3 FromFqdn $4 Check system name is fully qualified
R$* _S_ $* _S_ $* FromCname $* $: $>FromCname $2 _S_ $2 _S_ $3 FromCname $4 Check FROM hostname is canonical
R$* _S_ $*@$* _S_ $* DNSFrom $* $: $>check_dns host $3 _S_ $2@$3 _S_ $4 DNSFrom $5 Check hostname in DNS
R$* _S_ $*@$* _S_ $* FromDns $* $: $>check_dns host $3 _S_ $2@$3 _S_ $4 FromDNS $5 Check hostname in DNS
R$* _S_ $*@$* _S_ $* $: $1 _S_ $2@$[ $3 $] _S_ $4 Canonicalise hostname
R$* _S_ $*@$*. _S_ $* $: $1 _S_ $2@$3 _S_ $4 Remove trailing dot, if any
dnl ##
dnl ## Get the sender address options here
dnl ##
R$* _S_ $* _S_ $* $: $>AddressOptions $2 _S_ $2 _S_ $3 Get sender's options
R$* _S_ $* _S_ $* $: _S_ $2 _S_ $3 $| $1 $| Move options into place
R$* _S_ $* _S_ $* $| $* BlackListFrom $* $| $* $: $>BlackListFrom $1 _S_ $2 _S_ $3 $| $4 BlackListFrom $5 $| $6 Prevent mail if user is blacklisted from sending
dnl ##
dnl ## Defer the FromChecks if the FromOkIfLocalTo options is set (do them in check_rcpt instead)
dnl ##
R$* $: > $1 Add error marker
R>$* _S_ $* _S_ $* FromOkIfLocalTo$* $| $* $| $* $: $1 _S_ $2 _S_ $3 FromOkIfLocalTo $4 $| $5 $| $6 Remove error marker if 'FromOkIfLocalToPresent'.
R>$* _S_ $* _S_ $* $: $>FromChecks $1 _S_ $2$|$2 _S_ $3 Check the FROM address
R$* _S_ $*$|$* _S_ $* $: $1 _S_ $2 _S_ $4 Restore format
_REPEAT(9,`__DO_FROM_PROG_MATCH')dnl ## Add any defined program calls
_USER_HOOK(`__MAIL_POSTPROCESS_HOOK__',`$&f')dnl
dnl ## ----------------------------------------------------------------------
dnl ## check_rcpt: Make checks on the RCPT TO: address
dnl ##
dnl ## Input:
dnl ## address as supplied on the RCPT TO: protocol command
dnl ##
dnl ## Purpose:
dnl ## * Check client in real-time black hole database; if so, treat as spam
dnl ## * Check syntax of address
dnl ## * Check hostname part is fully qualified
dnl ## * Check hostname part is in the DNS
dnl ## * Check whether client may relay to given address
dnl ## * Check whether client/sender address is a known spam source; if so,
dnl ## check whether recipient wants spam.
dnl ## * Check whether sender is allowed to mail to recipient
dnl ## * Check whether recipient is a blacklisted account
dnl ## * Check if sender matches regular expression; if so, treat as spam
dnl ##
dnl ## Keyword Options:
dnl ## These keywords are recognized as options for the client:
dnl ## RBLCheck If client in black hole database, treat as spam
dnl ## RcptSyntax Client must use syntactically valid RCPT TO
dnl ## RcptFqdn RCPT TO must use fully qualified hostname part
dnl ## RcptDns RCPT TO hostname must resolve in DNS
dnl ## LimitedRelay Client can relay only to selected addresses
dnl ## LimitedRelayAuto As LimitedRelay, but with auto authorization
dnl ## SpamClient Client is a spammer
dnl ## FromRegexN [N=1-9] If matches against regex map N, sender is a known spammer
dnl ## LimitedMail Client can mail only to selected addresses
dnl ## FromOkIfLocalTo Any FROM is OK, if recipient is local
dnl ##
dnl ## These keywords are recognized as options for the sender address:
dnl ## LocalDomain Sender is within the local domain
dnl ## LimitedMail Sender has restricted mail capability
dnl ## ImASpammer Sender is a known spammer
dnl ##
dnl ## These keywords are recognized as options for the recipient address:
dnl ## LocalDomain Recipient is within the local domain
dnl ## NoMailTo This address not accessible to LimitedMail senders
dnl ## NoSpamPlease This address does not want to receive spam
dnl ## RelayTo Any client may relay to this address
dnl ## RelayLocalFrom Any client with local FROM may relay to this address
dnl ## BlackListRcpt Cannot deliver to this address
dnl ##
dnl ## Comments:
dnl ## The workspace is transformed to a fixed format used by all subroutines
dnl ## and looks like this:
dnl ## Thing to Check _S_ Sender $| _S_ Options
dnl ##
dnl ## The check_rcpt ruleset is called after each RCPT TO: protocol command.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.1 Andy Harper February 1998 Pass recipient not sender to RejectSpam
dnl ## 2.2 Andy Harper February 1998 Add syntax checking
dnl ## 2.2-1 Andy Harper February 1998 Add DNS validation
dnl ## 2.2-2 Andy Harper February 1998 Add limited mail capability
dnl ## 2.2-3 Andy Harper March 1998 Use NoMailTo rather than MailTo
dnl ## 2.2-4 Andy Harper March 1998 Remove <> for better checking
dnl ## 2.3 Andy Harper March 1998 Add 'BlackListRcpt' attribute
dnl ## 2.4 Andy Harper March 1998 Add hooks for map-regex feature
dnl ## Re-organize option storage
dnl ## Add extra relaying options
dnl ## 2.5 Andy Harper April 1998 SM89: Ruleset Name change
dnl ## Andy Harper April 1998 SM89: Fix dequote
dnl ## Andy Harper April 1998 Add 'SpamClient'
dnl ## Andy Harper April 1998 Add 'FromOkIfLocalTo'
dnl ## Andy Harper May 1998 Handle % routing
dnl ## Andy Harper May 1998 Make RBLCHeck condfitional on NoSpamPlease option too
dnl ##
dnl ## ----------------------------------------------------------------------
__RCPT_CHECK__
_USER_HOOK(`__RCPT_PREPROCESS_HOOK__')dnl
R$* $: $>client_options $&{client_addr} $| $&{client_name} _S_ detox($&f) $| $1 Retrieve options for this client
R$* _S_ $*$|<@$*:$*>_S_ $* $1 _S_ $2$|<$3> _S_ $4 Remove routing info
R$* _S_ $*$|$* _S_ $* RcptSyntax $* $: $>check_syntax $3 _S_ $2$|$3 _S_ $4 RcptSyntax $5 Check syntax
dnl ## remove <> around addresses
R$* _S_ <$*>$|$* _S_ $* $1 _S_ $2$|$3 _S_ $4 Remove brackets around sender
R$* _S_ $*$|<$*> _S_ $* $1 _S_ $2$|$3 _S_ $4 Remove brackets around recipient
R$* _S_ $*$|$* _S_ $* RcptFqdn $* $: $>check_fqdn $3 _S_ $2$|$3 _S_ $4 RcptFqdn $5 Check fully qualified
R$* _S_ $*$|$*@$* _S_ $* RcptDNS $* $: $>check_dns host $4 _S_ $2$|$3@$4 _S_ $5 RcptDNS $6 Check hostname is in DNS
dnl ##
dnl ## get sender and recipient options
dnl ##
R$* _S_ $*$|$* _S_ $* $: $>AddressOptions $2 _S_ $2$|$3 _S_ $4 Get sender options to 1
R$* _S_ $*$|$* _S_ $* $: $>AddressOptions $3 _S_ $2$|$3 _S_ $4 $| $1 Move sender to 3(b), get recipient in 1
R$* _S_ $* _S_ $* $: _S_ $2 _S_ $3 $| $1 Move recipient to 3(c)
dnl ## From here:
dnl ## wkspce _S_ preserve _S_ client opt $| sender opt $| recipient opt
R$* _S_ $*$|$*%$*@$*_S_ $* NoPercentHack $* $| $* $| $* $>PercentHack $3 % <$4@$5> _S_ $2$|$3%$4@$5 _S_ $6 NoPercentHack $7 $| $8 $| $9 Call percent hack format check if necessary
dnl ## Do the deferred FromChecks NOW, if the FromOkIfLocalTo option is set
R$* _S_ $* _S_ $* FromOkIfLocalTo $* $| $* $| $* $: $>FromChecksLocal $1 _S_ $2 _S_ $3 FromOkIfLocalTo $4 $| $5 $| $6 Check the FROM address, only if recipient NOT local
R$* _S_ $* _S_ $* LimitedMail $* $| $* $| $* NoMailTo $* $: $>RestrictMail $1 _S_ $2 _S_ $3 LimitedMail $4 $| $5 $| $6 NoMailTo $7 Client can mail only to a subset of addresses
R$* _S_ $* _S_ $* $| $* LimitedMail$* $| $* NoMailTo $* $: $>RestrictMail $1 _S_ $2 _S_ $3 $| $4 LimitedMail$5 $| $6 NoMailTo $7 Sender can mail only to a subset of addresses
R$* _S_ $* _S_ $* $| $* $| $* BlackListRcpt$* $: $>BlackListRcpt $1 _S_ $2 _S_ $3 $| $4 $| $5 BlackListRcpt$6 Recipient is blacklisted, block mail
R$* _S_ $* _S_ $* $| $* $| $* ReDirect=<$*>$* $: $>ReDirect $6 _S_ $2 _S_ $3 $| $4 $| $5 ReDirect=<$6>$7 Reject with indication of new address
R$* _S_ $* _S_ $* LimitedRelay $* $| $* $| $* $: $>LimitedRelay $1 _S_ $2 _S_ $3 LimitedRelay $4 $| $5 $| $6 Client has limited relaying capability
R$* _S_ $* _S_ $* LimitedRelayAuto$* $| $* $| $* $: $>LimitedRelayAuto $1 _S_ $2 _S_ $3 LimitedRelayAuto$4 $| $5 $| $6 Client has limited relaying capability, but auto authorization
R$* _S_ $* _S_ $* SpamClient $* $| $* $| $* NoSpamPlease $* $: $>RejectSpam $1 _S_ $2 _S_ $3 SpamClient $4 $| $5 $| $6 NoSpamPlease $7 If spam, possibly block it
R$* _S_ $* _S_ $* RBLCheck $* $| $* $| $* NoSpamPlease $* $: $>RBLCheck $&{client_addr} _S_ $2 _S_ $3 RBLCheck $4 $| $5 $| $6 NoSpamPlease $7 Check the real-time black list if required, possibly block it
R$* _S_ $* _S_ $* $| $* ImASpammer $* $| $* NoSpamPlease $* $: $>RejectSpam $1 _S_ $2 _S_ $3 $| $4 ImASpammer $5 $| $6 NoSpamPlease $7 If sender is a spammer, possibly block it
_REPEAT(9,`__DO_FROM_REGEX_MATCH')dnl ## Add 'FromRegexN' checks, if defined
R$* _S_ $*$|$* _S_ $* $: $3 Restore recipient
_USER_HOOK(`__RCPT_POSTPROCESS_HOOK__')dnl
dnl ## ----------------------------------------------------------------------
dnl ## check_compat: Make checks on sender/recipient to ensure compatibility
dnl ##
dnl ## Input:
dnl ## sender address $| recipient address
dnl ##
dnl ## Purpose:
dnl ##
dnl ## Comments:
dnl ## The workspace is transformed to a fixed format used by all subroutines
dnl ## and looks like this:
dnl ## Thing to Check _S_ Original Input _S_ Options
dnl ##
dnl ## The check_compat ruleset is called just before delivery of the message
dnl ## which is normally too late to be useful.
dnl ##
dnl ## Revision History:
dnl ## 2.0 Andy Harper February 1998 Rewrite to use client database
dnl ## 2.5 Andy Harper April 1998 SM89: Ruleset Name change
dnl ##
dnl ## ----------------------------------------------------------------------
__CMPT_CHECK__
_USER_HOOK(`__COMPAT_PREPROCESS_HOOK__')dnl
_USER_HOOK(`__COMPAT_POSTPROCESS_HOOK__')dnl