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