proto.m4 revision 110647
1divert(-1)
2#
3# Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
4#	All rights reserved.
5# Copyright (c) 1983, 1995 Eric P. Allman.  All rights reserved.
6# Copyright (c) 1988, 1993
7#	The Regents of the University of California.  All rights reserved.
8#
9# By using this file, you agree to the terms and conditions set
10# forth in the LICENSE file which can be found at the top level of
11# the sendmail distribution.
12#
13#
14divert(0)
15
16VERSIONID(`$Id: proto.m4,v 8.649.2.14 2002/12/30 15:46:02 ca Exp $')
17
18# level CF_LEVEL config file format
19V`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')
20divert(-1)
21
22dnl if MAILER(`local') not defined: do it ourself; be nice
23dnl maybe we should issue a warning?
24ifdef(`_MAILER_local_',`', `MAILER(local)')
25
26# do some sanity checking
27ifdef(`__OSTYPE__',,
28	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
29')')
30
31# pick our default mailers
32ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
33ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
34ifdef(`confRELAY_MAILER',,
35	`define(`confRELAY_MAILER',
36		`ifdef(`_MAILER_smtp_', `relay',
37			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
38ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
39define(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
40define(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
41define(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
42define(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
43
44# back compatibility with old config files
45ifdef(`confDEF_GROUP_ID',
46`errprint(`*** confDEF_GROUP_ID is obsolete.
47    Use confDEF_USER_ID with a colon in the value instead.
48')')
49ifdef(`confREAD_TIMEOUT',
50`errprint(`*** confREAD_TIMEOUT is obsolete.
51    Use individual confTO_<timeout> parameters instead.
52')')
53ifdef(`confMESSAGE_TIMEOUT',
54	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
55	 ifelse(_ARG_, -1,
56		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
57		`define(`confTO_QUEUERETURN',
58			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
59		 define(`confTO_QUEUEWARN',
60			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
61ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
62`errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
63    Use confMAX_MESSAGE_SIZE for the second part of the value.
64')')')
65
66
67# Sanity check on ldap_routing feature
68# If the user doesn't specify a new map, they better have given as a
69# default LDAP specification which has the LDAP base (and most likely the host)
70ifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
71WARNING: Using default FEATURE(ldap_routing) map definition(s)
72without setting confLDAP_DEFAULT_SPEC option.
73')')')dnl
74
75# clean option definitions below....
76define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
77
78dnl required to "rename" the check_* rulesets...
79define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
80dnl default relaying denied message
81ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG',
82ifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))')
83ifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')')
84define(`_CODE553', `553')
85divert(0)dnl
86
87# override file safeties - setting this option compromises system security,
88# addressing the actual file configuration problem is preferred
89# need to set this before any file actions are encountered in the cf file
90_OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
91
92# default LDAP map specification
93# need to set this now before any LDAP maps are defined
94_OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
95
96##################
97#   local info   #
98##################
99
100# my LDAP cluster
101# need to set this before any LDAP lookups are done (including classes)
102ifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m')
103
104Cwlocalhost
105ifdef(`USE_CW_FILE',
106`# file containing names of hosts for which we receive email
107Fw`'confCW_FILE',
108	`dnl')
109
110# my official domain name
111# ... `define' this only if sendmail cannot automatically determine your domain
112ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
113
114CP.
115
116ifdef(`UUCP_RELAY',
117`# UUCP relay host
118DY`'UUCP_RELAY
119CPUUCP
120
121')dnl
122ifdef(`BITNET_RELAY',
123`#  BITNET relay host
124DB`'BITNET_RELAY
125CPBITNET
126
127')dnl
128ifdef(`DECNET_RELAY',
129`define(`_USE_DECNET_SYNTAX_', 1)dnl
130# DECnet relay host
131DC`'DECNET_RELAY
132CPDECNET
133
134')dnl
135ifdef(`FAX_RELAY',
136`# FAX relay host
137DF`'FAX_RELAY
138CPFAX
139
140')dnl
141# "Smart" relay host (may be null)
142DS`'ifdef(`SMART_HOST', `SMART_HOST')
143
144ifdef(`LUSER_RELAY', `dnl
145# place to which unknown users should be forwarded
146Kuser user -m -a<>
147DL`'LUSER_RELAY',
148`dnl')
149
150# operators that cannot be in local usernames (i.e., network indicators)
151CO @ % ifdef(`_NO_UUCP_', `', `!')
152
153# a class with just dot (for identifying canonical names)
154C..
155
156# a class with just a left bracket (for identifying domain literals)
157C[[
158
159ifdef(`_ACCESS_TABLE_', `dnl
160# access_db acceptance class
161C{Accept}OK RELAY
162ifdef(`_DELAY_COMPAT_8_10_',`dnl
163ifdef(`_BLACKLIST_RCPT_',`dnl
164# possible access_db RHS for spam friends/haters
165C{SpamTag}SPAMFRIEND SPAMHATER')')',
166`dnl')
167
168dnl mark for "domain is ok" (resolved or accepted anyway)
169define(`_RES_OK_', `OKR')dnl
170ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
171# Resolve map (to check if a host exists in check_mail)
172Kresolve host -a<_RES_OK_> -T<TEMP>')
173C{ResOk}_RES_OK_
174
175ifdef(`_NEED_MACRO_MAP_', `dnl
176ifdef(`_MACRO_MAP_', `', `# macro storage map
177define(`_MACRO_MAP_', `1')dnl
178Kmacro macro')', `dnl')
179
180ifdef(`confCR_FILE', `dnl
181# Hosts for which relaying is permitted ($=R)
182FR`'confCR_FILE',
183`dnl')
184
185define(`TLS_SRV_TAG', `"TLS_Srv"')dnl
186define(`TLS_CLT_TAG', `"TLS_Clt"')dnl
187define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
188define(`TLS_TRY_TAG', `"Try_TLS"')dnl
189define(`SRV_FEAT_TAG', `"Srv_Features"')dnl
190dnl this may be useful in other contexts too
191ifdef(`_ARITH_MAP_', `', `# arithmetic map
192define(`_ARITH_MAP_', `1')dnl
193Karith arith')
194ifdef(`_ACCESS_TABLE_', `dnl
195ifdef(`_MACRO_MAP_', `', `# macro storage map
196define(`_MACRO_MAP_', `1')dnl
197Kmacro macro')
198# possible values for TLS_connection in access map
199C{tls}VERIFY ENCR', `dnl')
200ifdef(`_CERT_REGEX_ISSUER_', `dnl
201# extract relevant part from cert issuer
202KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
203ifdef(`_CERT_REGEX_SUBJECT_', `dnl
204# extract relevant part from cert subject
205KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
206
207ifdef(`LOCAL_RELAY', `dnl
208# who I send unqualified names to if `FEATURE(stickyhost)' is used
209# (null means deliver locally)
210DR`'LOCAL_RELAY')
211
212ifdef(`MAIL_HUB', `dnl
213# who gets all local email traffic
214# ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
215DH`'MAIL_HUB')
216
217# dequoting map
218Kdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
219
220divert(0)dnl	# end of nullclient diversion
221# class E: names that should be exposed as from this host, even if we masquerade
222# class L: names that should be delivered locally, even if we have a relay
223# class M: domains that should be converted to $M
224# class N: domains that should not be converted to $M
225#CL root
226undivert(5)dnl
227ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
228
229ifdef(`MASQUERADE_NAME', `dnl
230# who I masquerade as (null for no masquerading) (see also $=M)
231DM`'MASQUERADE_NAME')
232
233# my name for error messages
234ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
235
236undivert(6)dnl LOCAL_CONFIG
237include(_CF_DIR_`m4/version.m4')
238
239###############
240#   Options   #
241###############
242ifdef(`confAUTO_REBUILD',
243`errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
244	There was a potential for a denial of service attack if this is set.
245)')dnl
246
247# strip message body to 7 bits on input?
248_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
249
250# 8-bit data handling
251_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
252
253# wait for alias file rebuild (default units: minutes)
254_OPTION(AliasWait, `confALIAS_WAIT', `5m')
255
256# location of alias file
257_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
258
259# minimum number of free blocks on filesystem
260_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
261
262# maximum message size
263_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `1000000')
264
265# substitution for space (blank) characters
266_OPTION(BlankSub, `confBLANK_SUB', `_')
267
268# avoid connecting to "expensive" mailers on initial submission?
269_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
270
271# checkpoint queue runs after every N successful deliveries
272_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
273
274# default delivery mode
275_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
276
277# error message header/file
278_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
279
280# error mode
281_OPTION(ErrorMode, `confERROR_MODE', `print')
282
283# save Unix-style "From_" lines at top of header?
284_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
285
286# queue file mode (qf files)
287_OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
288
289# temporary file mode
290_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
291
292# match recipients against GECOS field?
293_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
294
295# maximum hop count
296_OPTION(MaxHopCount, `confMAX_HOP', `25')
297
298# location of help file
299O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
300
301# ignore dots as terminators in incoming messages?
302_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
303
304# name resolver options
305_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
306
307# deliver MIME-encapsulated error messages?
308_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
309
310# Forward file search path
311_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
312
313# open connection cache size
314_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
315
316# open connection cache timeout
317_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
318
319# persistent host status directory
320_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
321
322# single thread deliveries (requires HostStatusDirectory)?
323_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
324
325# use Errors-To: header?
326_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
327
328# log level
329_OPTION(LogLevel, `confLOG_LEVEL', `10')
330
331# send to me too, even in an alias expansion?
332_OPTION(MeToo, `confME_TOO', `True')
333
334# verify RHS in newaliases?
335_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
336
337# default messages to old style headers if no special punctuation?
338_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
339
340# SMTP daemon options
341ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
342`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
343	Use `DAEMON_OPTIONS()'; see cf/README.
344)'dnl
345`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
346ifelse(defn(`_DPO_'), `',
347`ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
348O DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
349ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
350
351# SMTP client options
352ifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
353`errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid.  See cf/README for more information.
354)'dnl
355`CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
356ifelse(defn(`_CPO_'), `',
357`#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
358
359# Modifiers to `define' {daemon_flags} for direct submissions
360_OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
361
362# Use as mail submission program? See sendmail/SECURITY
363_OPTION(UseMSP, `confUSE_MSP', `')
364
365# privacy flags
366_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
367
368# who (if anyone) should get extra copies of error messages
369_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
370
371# slope of queue-only function
372_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
373
374# limit on number of concurrent queue runners
375_OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
376
377# maximum number of queue-runners per queue-grouping with multiple queues
378_OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
379
380# priority of queue runners (nice(3))
381_OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
382
383# shall we sort the queue by hostname first?
384_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
385
386# minimum time in queue before retry
387_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
388
389# how many jobs can you process in the queue?
390_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000')
391
392# perform initial split of envelope without checking MX records
393_OPTION(FastSplit, `confFAST_SPLIT', `1')
394
395# queue directory
396O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
397
398# key for shared memory; 0 to turn off
399_OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
400
401ifdef(`confSHARED_MEMORY_KEY_FILE', `dnl
402# file to store key for shared memory (if SharedMemoryKey = -1)
403O SharedMemoryKeyFile=confSHARED_MEMORY_KEY_FILE')
404
405# timeouts (many of these)
406_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
407_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
408_OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
409_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
410_OPTION(Timeout.helo, `confTO_HELO', `5m')
411_OPTION(Timeout.mail, `confTO_MAIL', `10m')
412_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
413_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
414_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
415_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
416_OPTION(Timeout.rset, `confTO_RSET', `5m')
417_OPTION(Timeout.quit, `confTO_QUIT', `2m')
418_OPTION(Timeout.misc, `confTO_MISC', `2m')
419_OPTION(Timeout.command, `confTO_COMMAND', `1h')
420_OPTION(Timeout.ident, `confTO_IDENT', `5s')
421_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
422_OPTION(Timeout.control, `confTO_CONTROL', `2m')
423_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
424_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
425_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
426_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
427_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
428_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
429_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
430_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
431_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
432_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
433_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
434_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
435_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
436_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
437_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
438_OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
439_OPTION(Timeout.auth, `confTO_AUTH', `10m')
440_OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
441
442# time for DeliverBy; extension disabled if less than 0
443_OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
444
445# should we not prune routes in route-addr syntax addresses?
446_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
447
448# queue up everything before forking?
449_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
450
451# status file
452O StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', `MAIL_SETTINGS_DIR`'statistics')
453
454# time zone handling:
455#  if undefined, use system default
456#  if defined but null, use TZ envariable passed in
457#  if defined and non-null, use that info
458ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
459	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
460	`O TimeZoneSpec=confTIME_ZONE')
461
462# default UID (can be username or userid:groupid)
463_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
464
465# list of locations of user database file (null means no lookup)
466_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
467
468# fallback MX host
469_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
470
471# if we are the best MX host for a site, try it directly instead of config err
472_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
473
474# load average at which we just queue messages
475_OPTION(QueueLA, `confQUEUE_LA', `8')
476
477# load average at which we refuse connections
478_OPTION(RefuseLA, `confREFUSE_LA', `12')
479
480# load average at which we delay connections; 0 means no limit
481_OPTION(DelayLA, `confDELAY_LA', `0')
482
483# maximum number of children we allow at one time
484_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
485
486# maximum number of new connections per second
487_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
488
489# work recipient factor
490_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
491
492# deliver each queued job in a separate process?
493_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
494
495# work class factor
496_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
497
498# work time factor
499_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
500
501# default character set
502_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `iso-8859-1')
503
504# service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
505_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
506
507# hosts file (normally /etc/hosts)
508_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
509
510# dialup line delay on connection failure
511_OPTION(DialDelay, `confDIAL_DELAY', `10s')
512
513# action to take if there are no recipients in the message
514_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `add-to-undisclosed')
515
516# chrooted environment for writing to files
517_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `/arch')
518
519# are colons OK in addresses?
520_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
521
522# shall I avoid expanding CNAMEs (violates protocols)?
523_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
524
525# SMTP initial login message (old $e macro)
526_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
527
528# UNIX initial From header format (old $l macro)
529_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
530
531# From: lines that have embedded newlines are unwrapped onto one line
532_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
533
534# Allow HELO SMTP command that does not `include' a host name
535_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
536
537# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
538_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
539
540# delimiter (operator) characters (old $o macro)
541_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
542
543# shall I avoid calling initgroups(3) because of high NIS costs?
544_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
545
546# are group-writable `:include:' and .forward files (un)trustworthy?
547# True (the default) means they are not trustworthy.
548_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
549ifdef(`confUNSAFE_GROUP_WRITES',
550`errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
551')')
552
553# where do errors that occur when sending errors get sent?
554_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
555
556# where to save bounces if all else fails
557_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
558
559# what user id do we assume for the majority of the processing?
560_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
561
562# maximum number of recipients per SMTP envelope
563_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `100')
564
565# limit the rate recipients per SMTP envelope are accepted
566# once the threshold number of recipients have been rejected
567_OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `20')
568
569# shall we get local names from our installed interfaces?
570_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
571
572# Return-Receipt-To: header implies DSN request
573_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
574
575# override connection address (for testing)
576_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
577
578# Trusted user for file ownership and starting the daemon
579_OPTION(TrustedUser, `confTRUSTED_USER', `root')
580
581# Control socket for daemon management
582_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
583
584# Maximum MIME header length to protect MUAs
585_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
586
587# Maximum length of the sum of all headers
588_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
589
590# Maximum depth of alias recursion
591_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
592
593# location of pid file
594_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
595
596# Prefix string for the process title shown on 'ps' listings
597_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
598
599# Data file (df) memory-buffer file maximum size
600_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
601
602# Transcript file (xf) memory-buffer file maximum size
603_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
604
605# lookup type to find information about local mailboxes
606_OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
607
608# list of authentication mechanisms
609_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
610
611# default authentication information for outgoing connections
612_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
613
614# SMTP AUTH flags
615_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
616
617# SMTP AUTH maximum encryption strength
618_OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
619
620# SMTP STARTTLS server options
621_OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
622
623# Input mail filters
624_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
625
626ifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
627# Milter options
628_OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
629_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
630_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
631_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
632_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')')
633
634# CA directory
635_OPTION(CACertPath, `confCACERT_PATH', `')
636# CA file
637_OPTION(CACertFile, `confCACERT', `')
638# Server Cert
639_OPTION(ServerCertFile, `confSERVER_CERT', `')
640# Server private key
641_OPTION(ServerKeyFile, `confSERVER_KEY', `')
642# Client Cert
643_OPTION(ClientCertFile, `confCLIENT_CERT', `')
644# Client private key
645_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
646# DHParameters (only required if DSA/DH is used)
647_OPTION(DHParameters, `confDH_PARAMETERS', `')
648# Random data source (required for systems without /dev/urandom under OpenSSL)
649_OPTION(RandFile, `confRAND_FILE', `')
650
651############################
652`# QUEUE GROUP DEFINITIONS  #'
653############################
654_QUEUE_GROUP_
655
656###########################
657#   Message precedences   #
658###########################
659
660Pfirst-class=0
661Pspecial-delivery=100
662Plist=-30
663Pbulk=-60
664Pjunk=-100
665
666#####################
667#   Trusted users   #
668#####################
669
670# this is equivalent to setting class "t"
671ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
672Troot
673Tdaemon
674ifdef(`_NO_UUCP_', `dnl', `Tuucp')
675ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
676
677#########################
678#   Format of headers   #
679#########################
680
681ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
682H?P?Return-Path: <$g>
683HReceived: confRECEIVED_HEADER
684H?D?Resent-Date: $a
685H?D?Date: $a
686H?F?Resent-From: confFROM_HEADER
687H?F?From: confFROM_HEADER
688H?x?Full-Name: $x
689# HPosted-Date: $a
690# H?l?Received-Date: $b
691H?M?Resent-Message-Id: <$t.$i@$j>
692H?M?Message-Id: <$t.$i@$j>
693
694#
695######################################################################
696######################################################################
697#####
698#####			REWRITING RULES
699#####
700######################################################################
701######################################################################
702
703############################################
704###  Ruleset 3 -- Name Canonicalization  ###
705############################################
706Scanonify=3
707
708# handle null input (translate to <@> special case)
709R$@			$@ <@>
710
711# strip group: syntax (not inside angle brackets!) and trailing semicolon
712R$*			$: $1 <@>			mark addresses
713R$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
714R@ $* <@>		$: @ $1				unmark @host:...
715R$* [ IPv6 : $+ ] <@>	$: $1 [ IPv6 : $2 ]		unmark IPv6 addr
716R$* :: $* <@>		$: $1 :: $2			unmark node::addr
717R:`include': $* <@>	$: :`include': $1			unmark :`include':...
718R$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
719R$* : $* <@>		$: $2				strip colon if marked
720R$* <@>			$: $1				unmark
721R$* ;			   $1				strip trailing semi
722R$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
723R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
724
725# null input now results from list:; syntax
726R$@			$@ :; <@>
727
728# strip angle brackets -- note RFC733 heuristic to get innermost item
729R$*			$: < $1 >			housekeeping <>
730R$+ < $* >		   < $2 >			strip excess on left
731R< $* > $+		   < $1 >			strip excess on right
732R<>			$@ < @ >			MAIL FROM:<> case
733R< $+ >			$: $1				remove housekeeping <>
734
735ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
736# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
737R@ $+ , $+		@ $1 : $2			change all "," to ":"
738
739# localize and dispose of route-based addresses
740dnl XXX: IPv6 colon conflict
741ifdef(`NO_NETINET6', `dnl',
742`R@ [$+] : $+		$@ $>Canonify2 < @ [$1] > : $2	handle <route-addr>')
743R@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
744dnl',`dnl
745# strip route address <@a,@b,@c:user@d> -> <user@d>
746R@ $+ , $+		$2
747ifdef(`NO_NETINET6', `dnl',
748`R@ [ $* ] : $+		$2')
749R@ $+ : $+		$2
750dnl')
751
752# find focus for list syntax
753R $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
754R $+ : $* ;		$@ $1 : $2;			list syntax
755
756# find focus for @ syntax addresses
757R$+ @ $+		$: $1 < @ $2 >			focus on domain
758R$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
759R$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
760
761dnl This is flagged as an error in S0; no need to silently fix it here.
762dnl # do some sanity checking
763dnl R$* < @ $~[ $* : $* > $*	$1 < @ $2 $3 > $4	nix colons in addrs
764
765ifdef(`_NO_UUCP_', `dnl',
766`# convert old-style addresses to a domain-based address
767R$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
768R$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
769R$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
770')
771ifdef(`_USE_DECNET_SYNTAX_',
772`# convert node::user addresses into a domain-based address
773R$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
774R$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
775',
776	`dnl')
777# if we have % signs, take the rightmost one
778R$* % $*		$1 @ $2				First make them all @s.
779R$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
780R$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
781
782# else we must be a local name
783R$*			$@ $>Canonify2 $1
784
785
786################################################
787###  Ruleset 96 -- bottom half of ruleset 3  ###
788################################################
789
790SCanonify2=96
791
792# handle special cases for local names
793R$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
794R$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
795ifdef(`_NO_UUCP_', `dnl',
796`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
797
798# check for IPv4/IPv6 domain literal
799R$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [addr]
800R$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
801R$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
802
803ifdef(`_DOMAIN_TABLE_', `dnl
804# look up domains in the domain table
805R$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
806
807undivert(2)dnl LOCAL_RULE_3
808
809ifdef(`_BITDOMAIN_TABLE_', `dnl
810# handle BITNET mapping
811R$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
812
813ifdef(`_UUDOMAIN_TABLE_', `dnl
814# handle UUCP mapping
815R$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
816
817ifdef(`_NO_UUCP_', `dnl',
818`ifdef(`UUCP_RELAY',
819`# pass UUCP addresses straight through
820R$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
821`# if really UUCP, handle it immediately
822ifdef(`_CLASS_U_',
823`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
824ifdef(`_CLASS_V_',
825`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
826ifdef(`_CLASS_W_',
827`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
828ifdef(`_CLASS_X_',
829`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
830ifdef(`_CLASS_Y_',
831`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
832
833ifdef(`_NO_CANONIFY_', `dnl', `dnl
834# try UUCP traffic as a local address
835R$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
836R$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
837')')
838# hostnames ending in class P are always canonical
839R$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
840dnl apply the next rule only for hostnames not in class P
841dnl this even works for phrases in class P since . is in class P
842dnl which daemon flags are set?
843R$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
844dnl the other rules in this section only apply if the hostname
845dnl does not end in class P hence no further checks are done here
846dnl if this ever changes make sure the lookups are "protected" again!
847ifdef(`_NO_CANONIFY_', `dnl
848dnl do not canonify unless:
849dnl domain ends in class {Canonify} (this does not work if the intersection
850dnl	with class P is non-empty)
851dnl or {daemon_flags} has c set
852# pass to name server to make hostname canonical if in class {Canonify}
853R$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
854# pass to name server to make hostname canonical if requested
855R$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
856dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
857R$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
858# add a trailing dot to qualified hostnames so other rules will work
859R$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
860ifdef(`_CANONIFY_HOSTS_', `dnl
861dnl this should only apply to unqualified hostnames
862dnl but if a valid character inside an unqualified hostname is an OperatorChar
863dnl then $- does not work.
864# lookup unqualified hostnames
865R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
866dnl _NO_CANONIFY_ is not set: canonify unless:
867dnl {daemon_flags} contains CC (do not canonify)
868dnl but add a trailing dot to qualified hostnames so other rules will work
869dnl should we do this for every hostname: even unqualified?
870R$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
871R$* CC $* $| $*			$: $3
872ifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
873# do not canonify header addresses
874R$* $| $* < @ $* $~P > $*	$: $&{addr_type} $| $2 < @ $3 $4 > $5
875R$* h $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
876R$* h $* $| $*			$: $3', `dnl')
877# pass to name server to make hostname canonical
878R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
879dnl remove {daemon_flags} for other cases
880R$* $| $*			$: $2
881
882# local host aliases and pseudo-domains are always canonical
883R$* < @ $=w > $*		$: $1 < @ $2 . > $3
884ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
885`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
886`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
887ifdef(`_VIRTUSER_TABLE_', `dnl
888dnl virtual hosts are also canonical
889ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
890`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
891`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
892`dnl')
893ifdef(`_GENERICS_TABLE_', `dnl
894dnl hosts for genericstable are also canonical
895ifdef(`_GENERICS_ENTIRE_DOMAIN_',
896`R$* < @ $* $=G > $* 	$: $1 < @ $2 $3 . > $4',
897`R$* < @ $=G > $* 	$: $1 < @ $2 . > $3')',
898`dnl')
899dnl remove superfluous dots (maybe repeatedly) which may have been added
900dnl by one of the rules before
901R$* < @ $* . . > $*		$1 < @ $2 . > $3
902
903
904##################################################
905###  Ruleset 4 -- Final Output Post-rewriting  ###
906##################################################
907Sfinal=4
908
909R$+ :; <@>		$@ $1 :				handle <list:;>
910R$* <@>			$@				handle <> and list:;
911
912# strip trailing dot off possibly canonical name
913R$* < @ $+ . > $*	$1 < @ $2 > $3
914
915# eliminate internal code
916R$* < @ *LOCAL* > $*	$1 < @ $j > $2
917
918# externalize local domain info
919R$* < $+ > $*		$1 $2 $3			defocus
920R@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
921R@ $*			$@ @ $1				... and exit
922
923ifdef(`_NO_UUCP_', `dnl',
924`# UUCP must always be presented in old form
925R$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
926
927ifdef(`_USE_DECNET_SYNTAX_',
928`# put DECnet back in :: form
929R$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
930	`dnl')
931# delete duplicate local names
932R$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
933
934
935
936##############################################################
937###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
938###		   (used for recursive calls)		   ###
939##############################################################
940
941SRecurse=97
942R$*			$: $>canonify $1
943R$*			$@ $>parse $1
944
945
946######################################
947###   Ruleset 0 -- Parse Address   ###
948######################################
949
950Sparse=0
951
952R$*			$: $>Parse0 $1		initial parsing
953R<@>			$#_LOCAL_ $: <@>		special case error msgs
954R$*			$: $>ParseLocal $1	handle local hacks
955R$*			$: $>Parse1 $1		final parsing
956
957#
958#  Parse0 -- do initial syntax checking and eliminate local addresses.
959#	This should either return with the (possibly modified) input
960#	or return with a #error mailer.  It should not return with a
961#	#mailer other than the #error mailer.
962#
963
964SParse0
965R<@>			$@ <@>			special case error msgs
966R$* : $* ; <@>		$#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
967R@ <@ $* >		< @ $1 >		catch "@@host" bogosity
968R<@ $+>			$#error $@ 5.1.3 $: "_CODE553 User address required"
969R$+ <@>			$#error $@ 5.1.3 $: "_CODE553 Hostname required"
970R$*			$: <> $1
971dnl allow tricks like [host1]:[host2]
972R<> $* < @ [ $* ] : $+ > $*	$1 < @ [ $2 ] : $3 > $4
973R<> $* < @ [ $* ] , $+ > $*	$1 < @ [ $2 ] , $3 > $4
974dnl but no a@[b]c
975R<> $* < @ [ $* ] $+ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid address"
976R<> $* < @ [ $+ ] > $*		$1 < @ [ $2 ] > $3
977R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
978R<> $*			$1
979R$* < @ . $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
980R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
981dnl no a@b@
982R$* < @ $* @ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid route address"
983dnl no a@b@c
984R$* @ $* < @ $* > $*	$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
985dnl comma only allowed before @; this check is not complete
986R$* , $~O $*		$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
987
988ifdef(`_STRICT_RFC821_', `# more RFC 821 checks
989R$* . < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
990R. $* < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
991dnl', `dnl')
992
993# now delete the local info -- note $=O to find characters that cause forwarding
994R$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
995R< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
996R$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
997R< @ $+ >		$#error $@ 5.1.3 $: "_CODE553 User address required"
998R$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
999R$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
1000R< @ *LOCAL* >		$#error $@ 5.1.3 $: "_CODE553 User address required"
1001R$* $=O $* < @ *LOCAL* >
1002			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
1003R$* < @ *LOCAL* >	$: $1
1004
1005#
1006#  Parse1 -- the bottom half of ruleset 0.
1007#
1008
1009SParse1
1010ifdef(`_LDAP_ROUTING_', `dnl
1011# handle LDAP routing for hosts in $={LDAPRoute}
1012R$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
1013R$+ < @ $={LDAPRouteEquiv} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
1014`dnl')
1015
1016ifdef(`_MAILER_smtp_',
1017`# handle numeric address spec
1018dnl there is no check whether this is really an IP number
1019R$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
1020R$* < @ [ $+ ] > $*	$1 < @ [ $2 ] : $S > $3		Add smart host to path
1021R$* < @ [ $+ ] : > $*		$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
1022R$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
1023R$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
1024	`dnl')
1025
1026ifdef(`_VIRTUSER_TABLE_', `dnl
1027# handle virtual users
1028ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1029dnl this is not a documented option
1030dnl it stops looping in virtusertable mapping if input and output
1031dnl are identical, i.e., if address A is mapped to A.
1032dnl it does not deal with multi-level recursion
1033# handle full domains in RHS of virtusertable
1034R$+ < @ $+ >			$: $(macro {RecipientAddress} $) $1 < @ $2 >
1035R$+ < @ $+ > 			$: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
1036R<?> $+ $| $+			$: $1 $(macro {RecipientAddress} $@ $2 $)
1037R<?> $+ $| $*			$: $1',
1038`dnl')
1039R$+			$: <!> $1		Mark for lookup
1040dnl input: <!> local<@domain>
1041ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
1042`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
1043`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
1044dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
1045R<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1046dnl if <@> local<@domain>: no match but try lookup
1047dnl user+detail: try user++@domain if detail not empty
1048R<@> $+ + $+ < @ $* . >
1049			$: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1050dnl user+detail: try user+*@domain
1051R<@> $+ + $* < @ $* . >
1052			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1053dnl user+detail: try user@domain
1054R<@> $+ + $* < @ $* . >
1055			$: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1056dnl try default entry: @domain
1057dnl ++@domain
1058R<@> $+ + $+ < @ $+ . >	$: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1059dnl +*@domain
1060R<@> $+ + $* < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1061dnl @domain if +detail exists
1062dnl if no match, change marker to prevent a second @domain lookup
1063R<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
1064dnl without +detail
1065R<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1066dnl no match
1067R<@> $+			$: $1
1068dnl remove mark
1069R<!> $+			$: $1
1070R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1071R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
1072ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1073# check virtuser input address against output address, if same, skip recursion
1074R< $+ > $+ < @ $+ >				$: < $1 > $2 < @ $3 > $| $1
1075# it is the same: stop now
1076R< $+ > $+ < @ $+ > $| $&{RecipientAddress}	$: $>ParseLocal $>Parse0 $>canonify $1
1077R< $+ > $+ < @ $+ > $| $* 			$: < $1 > $2 < @ $3 >
1078dnl', `dnl')
1079dnl this is not a documented option
1080dnl it performs no looping at all for virtusertable
1081ifdef(`_NO_VIRTUSER_RECURSION_',
1082`R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
1083`R< $+ > $+ < @ $+ >	$: $>Recurse $1')
1084dnl', `dnl')
1085
1086# short circuit local delivery so forwarded email works
1087ifdef(`_MAILER_usenet_', `dnl
1088R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
1089
1090
1091ifdef(`_STICKY_LOCAL_DOMAIN_',
1092`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
1093R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
1094dnl $H empty (but @$=w.)
1095R< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
1096R< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
1097`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
1098R$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
1099
1100ifdef(`_MAILER_TABLE_', `dnl
1101# not local -- try mailer table lookup
1102R$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
1103R< $+ . > $*		$: < $1 > $2			strip trailing dot
1104R< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
1105dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1106R< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
1107R< $+ > $*		$: $>Mailertable <$1> $2		try domain',
1108`dnl')
1109undivert(4)dnl UUCP rules from `MAILER(uucp)'
1110
1111ifdef(`_NO_UUCP_', `dnl',
1112`# resolve remotely connected UUCP links (if any)
1113ifdef(`_CLASS_V_',
1114`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
1115	`dnl')
1116ifdef(`_CLASS_W_',
1117`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
1118	`dnl')
1119ifdef(`_CLASS_X_',
1120`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
1121	`dnl')')
1122
1123# resolve fake top level domains by forwarding to other hosts
1124ifdef(`BITNET_RELAY',
1125`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
1126	`dnl')
1127ifdef(`DECNET_RELAY',
1128`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
1129	`dnl')
1130ifdef(`_MAILER_pop_',
1131`R$+ < @ POP. >		$#pop $: $1			user@POP',
1132	`dnl')
1133ifdef(`_MAILER_fax_',
1134`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
1135`ifdef(`FAX_RELAY',
1136`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
1137	`dnl')')
1138
1139ifdef(`UUCP_RELAY',
1140`# forward non-local UUCP traffic to our UUCP relay
1141R$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
1142`ifdef(`_MAILER_uucp_',
1143`# forward other UUCP traffic straight to UUCP
1144R$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
1145	`dnl')')
1146ifdef(`_MAILER_usenet_', `
1147# addresses sent to net.group.USENET will get forwarded to a newsgroup
1148R$+ . USENET		$#usenet $@ usenet $: $1',
1149	`dnl')
1150
1151ifdef(`_LOCAL_RULES_',
1152`# figure out what should stay in our local mail system
1153undivert(1)', `dnl')
1154
1155# pass names that still have a host to a smarthost (if defined)
1156R$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
1157
1158# deal with other remote names
1159ifdef(`_MAILER_smtp_',
1160`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
1161`R$* < @$* > $*		$#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
1162
1163# handle locally delivered names
1164R$=L			$#_LOCAL_ $: @ $1		special local names
1165R$+			$#_LOCAL_ $: $1			regular local names
1166
1167###########################################################################
1168###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
1169###########################################################################
1170
1171SLocal_localaddr
1172Slocaladdr=5
1173R$+			$: $1 $| $>"Local_localaddr" $1
1174R$+ $| $#ok		$@ $1			no change
1175R$+ $| $#$*		$#$2
1176R$+ $| $*		$: $1
1177
1178ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1179# Preserve rcpt_host in {Host}
1180R$+			$: $1 $| $&h $| $&{Host}	check h and {Host}
1181R$+ $| $|		$: $(macro {Host} $@ $) $1	no h or {Host}
1182R$+ $| $| $+		$: $1			h not set, {Host} set
1183R$+ $| +$* $| $*	$: $1			h is +detail, {Host} set
1184R$+ $| $* @ $+ $| $*	$: $(macro {Host} $@ @$3 $) $1	set {Host} to host in h
1185R$+ $| $+ $| $*		$: $(macro {Host} $@ @$2 $) $1	set {Host} to h
1186')dnl
1187
1188ifdef(`_FFR_5_', `dnl
1189# Preserve host in a macro
1190R$+			$: $(macro {LocalAddrHost} $) $1
1191R$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
1192
1193ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
1194# deal with plussed users so aliases work nicely
1195R$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1196R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1197')
1198# prepend an empty "forward host" on the front
1199R$+			$: <> $1
1200
1201ifdef(`LUSER_RELAY', `dnl
1202# send unrecognized local users to a relay host
1203ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1204R< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
1205R< > $+			$: < ? $L > < > $(user $1 $)	look up user
1206R< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
1207R< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
1208R< > $+ 		$: < $L > $(user $1 $)		look up user
1209R< $* > $+ <>		$: < > $2			found; strip $L')
1210ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1211R< $+ > $+		$: < $1 > $2 $&{Host}')
1212dnl')
1213
1214ifdef(`MAIL_HUB', `dnl
1215R< > $+			$: < $H > $1			try hub', `dnl')
1216ifdef(`LOCAL_RELAY', `dnl
1217R< > $+			$: < $R > $1			try relay', `dnl')
1218ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1219R< > $+			$@ $1', `dnl
1220R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
1221ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1222R< > < $+ @ $+ <> + $* >	$: < > < $1 + $3 @ $2 >	check whether +detail')
1223R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
1224R< > < $+ <> $* >	$: < > < $1 >			else discard
1225R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
1226R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
1227R< > < $+ >		$@ $1				no +detail
1228R$+			$: $1 <> $&h			add +detail back in
1229ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1230R$+ @ $+ <> + $*	$: $1 + $3 @ $2			check whether +detail')
1231R$+ <> + $*		$: $1 + $2			check whether +detail
1232R$+ <> $*		$: $1				else discard')
1233R< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
1234R< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
1235ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1236dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1237R< $~[ : $+ > $+ @ $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
1238R< $~[ : $+ > $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1239ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1240R< $+ > $+ @ $+		$@ $>MailerToTriple < $1 > $2 < @ $3 >')
1241R< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
1242
1243ifdef(`_MAILER_TABLE_', `dnl
1244ifdef(`_LDAP_ROUTING_', `dnl
1245###################################################################
1246###  Ruleset LDAPMailertable -- mailertable lookup for LDAP     ###
1247dnl input: <Domain> FullAddress
1248###################################################################
1249
1250SLDAPMailertable
1251R< $+ > $*		$: < $(mailertable $1 $) > $2		lookup
1252R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		check resolved?
1253R< $+ > $*		$: < $1 > $>Mailertable <$1> $2		try domain
1254R< $+ > $#$*		$#$2					found
1255R< $+ > $*		$#_RELAY_ $@ $1 $: $2			not found, direct relay',
1256`dnl')
1257
1258###################################################################
1259###  Ruleset 90 -- try domain part of mailertable entry 	###
1260dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1261###################################################################
1262
1263SMailertable=90
1264dnl shift and check
1265dnl %2 is not documented in cf/README
1266R$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1267dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1268R$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
1269R$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
1270dnl is $2 always empty?
1271R$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
1272R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
1273dnl return full address
1274R< $* > $*		$@ $2				no mailertable match',
1275`dnl')
1276
1277###################################################################
1278###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
1279dnl input: in general: <[mailer:]host> lp<@domain>rest
1280dnl	<> address				-> address
1281dnl	<error:d.s.n:text>			-> error
1282dnl	<error:text>				-> error
1283dnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
1284dnl	<mailer:host> address			-> mailer host address
1285dnl	<localdomain> address			-> address
1286dnl	<host> address				-> relay host address
1287###################################################################
1288
1289SMailerToTriple=95
1290R< > $*				$@ $1			strip off null relay
1291R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1292R< error : $- $+ > $*		$#error $@ $(dequote $1 $) $: $2
1293R< local : $* > $*		$>CanonLocal < $1 > $2
1294dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1295R< $~[ : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
1296R< $~[ : $+ > $*		$# $1 $@ $2 $: $3	try qualified mailer
1297R< $=w > $*			$@ $2			delete local host
1298R< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
1299
1300###################################################################
1301###  Ruleset CanonLocal -- canonify local: syntax		###
1302dnl input: <user> address
1303dnl <x> <@host> : rest			-> Recurse rest
1304dnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
1305dnl <> user <@host> rest		-> local user@host user
1306dnl <> user				-> local user user
1307dnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
1308dnl <user> lp <@host> rest		-> local lp@host user
1309dnl <user> lp				-> local lp user
1310###################################################################
1311
1312SCanonLocal
1313# strip local host from routed addresses
1314R< $* > < @ $+ > : $+		$@ $>Recurse $3
1315R< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
1316
1317# strip trailing dot from any host name that may appear
1318R< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
1319
1320# handle local: syntax -- use old user, either with or without host
1321R< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
1322R< > $+				$#_LOCAL_ $@ $1    $: $1
1323
1324# handle local:user@host syntax -- ignore host part
1325R< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
1326
1327# handle local:user syntax
1328R< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
1329R< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
1330
1331###################################################################
1332###  Ruleset 93 -- convert header names to masqueraded form	###
1333###################################################################
1334
1335SMasqHdr=93
1336
1337ifdef(`_GENERICS_TABLE_', `dnl
1338# handle generics database
1339ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1340dnl if generics should be applied add a @ as mark
1341`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
1342`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
1343R$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
1344dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1345dnl ignore the first case for now
1346dnl if it has the mark lookup full address
1347dnl broken: %1 is full address not just detail
1348R< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
1349dnl workspace: ... or <match|@user@domain> user <@domain>
1350dnl no match, try user+detail@domain
1351R<@$+ + $* @ $+> $+ < @ $+ >
1352		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
1353R<@$+ + $* @ $+> $+ < @ $+ >
1354		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
1355dnl no match, remove mark
1356R<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
1357dnl no match, try @domain for exceptions
1358R< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1359dnl workspace: ... or <match> user <@domain>
1360dnl no match, try local part
1361R< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
1362R< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1363R< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1364R< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
1365R< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
1366R< > $*			$: $1				not found',
1367`dnl')
1368
1369# do not masquerade anything in class N
1370R$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
1371
1372ifdef(`MASQUERADE_NAME', `dnl
1373# special case the users that should be exposed
1374R$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
1375ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1376`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
1377`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
1378ifdef(`_LIMITED_MASQUERADE_', `dnl',
1379`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
1380
1381# handle domain-specific masquerading
1382ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1383`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
1384`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
1385ifdef(`_LIMITED_MASQUERADE_', `dnl',
1386`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
1387R$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
1388R$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
1389R$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
1390dnl', `dnl no masquerading
1391dnl just fix *LOCAL* leftovers
1392R$* < @ *LOCAL* >	$@ $1 < @ $j . >')
1393
1394###################################################################
1395###  Ruleset 94 -- convert envelope names to masqueraded form	###
1396###################################################################
1397
1398SMasqEnv=94
1399ifdef(`_MASQUERADE_ENVELOPE_',
1400`R$+			$@ $>MasqHdr $1',
1401`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
1402
1403###################################################################
1404###  Ruleset 98 -- local part of ruleset zero (can be null)	###
1405###################################################################
1406
1407SParseLocal=98
1408undivert(3)dnl LOCAL_RULE_0
1409
1410ifdef(`_LDAP_ROUTING_', `dnl
1411######################################################################
1412###  LDAPExpand: Expand address using LDAP routing
1413###
1414###	Parameters:
1415###		<$1> -- parsed address (user < @ domain . >) (pass through)
1416###		<$2> -- RFC822 address (user @ domain) (used for lookup)
1417###		<$3> -- +detail information
1418###
1419###	Returns:
1420###		Mailer triplet ($#mailer $@ host $: address)
1421###		Parsed address (user < @ domain . >)
1422######################################################################
1423
1424SLDAPExpand
1425# do the LDAP lookups
1426R<$+><$+><$*>	$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
1427
1428# look for temporary failures (return original address, MTA will queue up)
1429R<$* <TMPF>> <$*> <$+> <$+> <$*>	$@ $3
1430R<$*> <$* <TMPF>> <$+> <$+> <$*>	$@ $3
1431
1432# if mailRoutingAddress and local or non-existant mailHost,
1433# return the new mailRoutingAddress
1434ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1435R<$+@$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $6 @ $2
1436R<$+@$+> <> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $5 @ $2')
1437R<$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1
1438R<$+> <> <$+> <$+> <$*>		$@ $>Parse0 $>canonify $1
1439
1440
1441# if mailRoutingAddress and non-local mailHost,
1442# relay to mailHost with new mailRoutingAddress
1443ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1444ifdef(`_MAILER_TABLE_', `dnl
1445# check mailertable for host, relay from there
1446R<$+@$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
1447`R<$+@$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
1448ifdef(`_MAILER_TABLE_', `dnl
1449# check mailertable for host, relay from there
1450R<$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$2> $>canonify $1',
1451`R<$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $2 $: $>canonify $1')
1452
1453# if no mailRoutingAddress and local mailHost,
1454# return original address
1455R<> <$=w> <$+> <$+> <$*>	$@ $2
1456
1457
1458# if no mailRoutingAddress and non-local mailHost,
1459# relay to mailHost with original address
1460ifdef(`_MAILER_TABLE_', `dnl
1461# check mailertable for host, relay from there
1462R<> <$+> <$+> <$+> <$*>		$>LDAPMailertable <$1> $2',
1463`R<> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $1 $: $2')
1464
1465ifdef(`_LDAP_ROUTE_DETAIL_',
1466`# if no mailRoutingAddress and no mailHost,
1467# try without +detail
1468R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
1469
1470# if still no mailRoutingAddress and no mailHost,
1471# try @domain
1472ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1473R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <@ $4> <+$3>')
1474R<> <> <$+> <$+ @ $+> <$*>	$@ $>LDAPExpand <$1> <@ $3> <$4>
1475
1476# if no mailRoutingAddress and no mailHost and this was a domain attempt,
1477ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1478# user does not exist
1479R<> <> <$+> <@ $+> <$*>		$: <?> < $&{addr_type} > < $1 >
1480# only give error for envelope recipient
1481R<?> <e r> <$+>			$#error $@ nouser $: "550 User unknown"
1482R<?> <$*> <$+>			$@ $2',
1483`dnl
1484# return the original address
1485R<> <> <$+> <@ $+> <$*>		$@ $1')',
1486`dnl')
1487
1488ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1489')')
1490ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
1491######################################################################
1492###  D: LookUpDomain -- search for domain in access database
1493###
1494###	Parameters:
1495###		<$1> -- key (domain name)
1496###		<$2> -- default (what to return if not found in db)
1497dnl			must not be empty
1498###		<$3> -- mark (must be <(!|+) single-token>)
1499###			! does lookup only with tag
1500###			+ does lookup with and without tag
1501###		<$4> -- passthru (additional data passed unchanged through)
1502dnl returns:		<default> <passthru>
1503dnl 			<result> <passthru>
1504######################################################################
1505
1506SD
1507dnl workspace <key> <default> <passthru> <mark>
1508dnl lookup with tag (in front, no delimiter here)
1509dnl    2    3  4    5
1510R<$*> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1511dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1512dnl lookup without tag?
1513dnl   1    2      3    4
1514R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1515ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
1516dnl XXX apply this also to IP addresses?
1517dnl currently it works the wrong way round for [1.2.3.4]
1518dnl   1  2    3    4  5    6
1519R<?> <$+.$+> <$+> <$- $-> <$*>	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
1520dnl   1  2    3      4    5
1521R<?> <$+.$+> <$+> <+ $-> <$*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
1522ifdef(`_ACCESS_SKIP_', `dnl
1523dnl found SKIP: return <default> and <passthru>
1524dnl      1    2    3  4    5
1525R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
1526dnl not found: IPv4 net (no check is done whether it is an IP number!)
1527dnl    1  2     3    4  5    6
1528R<?> <[$+.$-]> <$+> <$- $-> <$*>	$@ $>D <[$1]> <$3> <$4 $5> <$6>
1529ifdef(`NO_NETINET6', `dnl',
1530`dnl not found: IPv6 net
1531dnl (could be merged with previous rule if we have a class containing .:)
1532dnl    1   2     3    4  5    6
1533R<?> <[$+::$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>
1534R<?> <[$+:$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>')
1535dnl not found, but subdomain: try again
1536dnl   1  2    3    4  5    6
1537R<?> <$+.$+> <$+> <$- $-> <$*>	$@ $>D <$2> <$3> <$4 $5> <$6>
1538ifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
1539dnl   1    2      3    4
1540R<?> <$+> <$+> <! $-> <$*>	$: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
1541dnl not found, no subdomain: return <default> and <passthru>
1542dnl   1    2    3  4    5
1543R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
1544ifdef(`_ATMPF_', `dnl tempfail?
1545dnl            2    3    4  5    6
1546R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
1547dnl return <result of lookup> and <passthru>
1548dnl    2    3    4  5    6
1549R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
1550
1551######################################################################
1552###  A: LookUpAddress -- search for host address in access database
1553###
1554###	Parameters:
1555###		<$1> -- key (dot quadded host address)
1556###		<$2> -- default (what to return if not found in db)
1557dnl			must not be empty
1558###		<$3> -- mark (must be <(!|+) single-token>)
1559###			! does lookup only with tag
1560###			+ does lookup with and without tag
1561###		<$4> -- passthru (additional data passed through)
1562dnl	returns:	<default> <passthru>
1563dnl			<result> <passthru>
1564######################################################################
1565
1566SA
1567dnl lookup with tag
1568dnl    2    3  4    5
1569R<$+> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1570dnl lookup without tag
1571dnl   1    2      3    4
1572R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1573dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
1574ifdef(`_ACCESS_SKIP_', `dnl
1575dnl found SKIP: return <default> and <passthru>
1576dnl      1    2    3  4    5
1577R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
1578ifdef(`NO_NETINET6', `dnl',
1579`dnl no match; IPv6: remove last part
1580dnl   1   2    3    4  5    6
1581R<?> <$+::$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
1582R<?> <$+:$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>')
1583dnl no match; IPv4: remove last part
1584dnl   1  2    3    4  5    6
1585R<?> <$+.$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
1586dnl no match: return default
1587dnl   1    2    3  4    5
1588R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
1589ifdef(`_ATMPF_', `dnl tempfail?
1590dnl            2    3    4  5    6
1591R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
1592dnl match: return result
1593dnl    2    3    4  5    6
1594R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
1595dnl endif _ACCESS_TABLE_
1596divert(0)
1597######################################################################
1598###  CanonAddr --	Convert an address into a standard form for
1599###			relay checking.  Route address syntax is
1600###			crudely converted into a %-hack address.
1601###
1602###	Parameters:
1603###		$1 -- full recipient address
1604###
1605###	Returns:
1606###		parsed address, not in source route form
1607dnl		user%host%host<@domain>
1608dnl		host!user<@domain>
1609######################################################################
1610
1611SCanonAddr
1612R$*			$: $>Parse0 $>canonify $1	make domain canonical
1613ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1614R< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
1615R$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
1616R$* < @ $+ > : $*	$3 $1 < @ $2 >
1617dnl')
1618
1619######################################################################
1620###  ParseRecipient --	Strip off hosts in $=R as well as possibly
1621###			$* $=m or the access database.
1622###			Check user portion for host separators.
1623###
1624###	Parameters:
1625###		$1 -- full recipient address
1626###
1627###	Returns:
1628###		parsed, non-local-relaying address
1629######################################################################
1630
1631SParseRecipient
1632dnl mark and canonify address
1633R$*				$: <?> $>CanonAddr $1
1634dnl workspace: <?> localpart<@domain[.]>
1635R<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
1636dnl workspace: <?> localpart<@domain>
1637R<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
1638
1639# if no $=O character, no host in the user portion, we are done
1640R<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
1641dnl no $=O in localpart: return
1642R<?> $*				$@ $1
1643
1644dnl workspace: <NO> localpart<@domain>, where localpart contains $=O
1645dnl mark everything which has an "authorized" domain with <RELAY>
1646ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1647# if we relay, check username portion for user%host so host can be checked also
1648R<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
1649dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1650dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1651
1652dnl what if access map returns something else than RELAY?
1653dnl we are only interested in RELAY entries...
1654dnl other To: entries: blacklist recipient; generic entries?
1655dnl if it is an error we probably do not want to relay anyway
1656ifdef(`_RELAY_HOSTS_ONLY_',
1657`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
1658ifdef(`_ACCESS_TABLE_', `dnl
1659R<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
1660R<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1661`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
1662ifdef(`_ACCESS_TABLE_', `dnl
1663R<NO> $* < @ $+ >		$: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
1664R<$+> <$+>			$: <$1> $2',`dnl')')
1665
1666
1667ifdef(`_RELAY_MX_SERVED_', `dnl
1668dnl do "we" ($=w) act as backup MX server for the destination domain?
1669R<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1670R<MX> < : $* <TEMP> : > $*	$#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
1671dnl yes: mark it as <RELAY>
1672R<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
1673dnl no: put old <NO> mark back
1674R<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
1675
1676dnl do we relay to this recipient domain?
1677R<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
1678dnl something else
1679R<$+> $*			$@ $2
1680
1681
1682######################################################################
1683###  check_relay -- check hostname/address on SMTP startup
1684######################################################################
1685
1686SLocal_check_relay
1687Scheck`'_U_`'relay
1688R$*			$: $1 $| $>"Local_check_relay" $1
1689R$* $| $* $| $#$*	$#$3
1690R$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
1691
1692SBasic_check_relay
1693# check for deferred delivery mode
1694R$*			$: < $&{deliveryMode} > $1
1695R< d > $*		$@ deferred
1696R< $* > $*		$: $2
1697
1698ifdef(`_ACCESS_TABLE_', `dnl
1699dnl workspace: {client_name} $| {client_addr}
1700R$+ $| $+		$: $>D < $1 > <?> <+ Connect> < $2 >
1701dnl workspace: <result-of-lookup> <{client_addr}>
1702dnl OR $| $+ if client_name is empty
1703R   $| $+		$: $>A < $1 > <?> <+ Connect> <>	empty client_name
1704dnl workspace: <result-of-lookup> <{client_addr}>
1705R<?> <$+>		$: $>A < $1 > <?> <+ Connect> <>	no: another lookup
1706dnl workspace: <result-of-lookup> (<>|<{client_addr}>)
1707R<?> <$*>		$: OK				found nothing
1708dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
1709R<$={Accept}> <$*>	$@ $1				return value of lookup
1710R<REJECT> <$*>		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1711R<DISCARD> <$*>		$#discard $: discard
1712ifdef(`_FFR_QUARANTINE',
1713`R<QUARANTINE:$+> <$*>	$#error $@ quarantine $: $1', `dnl')
1714dnl error tag
1715R<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
1716R<ERROR:$+> <$*>		$#error $: $1
1717ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*>		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
1718dnl generic error from access map
1719R<$+> <$*>		$#error $: $1', `dnl')
1720
1721ifdef(`_RBL_',`dnl
1722# DNS based IP address spam list
1723dnl workspace: ignored...
1724R$*			$: $&{client_addr}
1725R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1726R<?>OK			$: OKSOFAR
1727R<?>$+			$#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
1728`dnl')
1729undivert(8)
1730
1731######################################################################
1732###  check_mail -- check SMTP ``MAIL FROM:'' command argument
1733######################################################################
1734
1735SLocal_check_mail
1736Scheck`'_U_`'mail
1737R$*			$: $1 $| $>"Local_check_mail" $1
1738R$* $| $#$*		$#$2
1739R$* $| $*		$@ $>"Basic_check_mail" $1
1740
1741SBasic_check_mail
1742# check for deferred delivery mode
1743R$*			$: < $&{deliveryMode} > $1
1744R< d > $*		$@ deferred
1745R< $* > $*		$: $2
1746
1747# authenticated?
1748dnl done first: we can require authentication for every mail transaction
1749dnl workspace: address as given by MAIL FROM: (sender)
1750R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
1751R$* $| $#$+		$#$2
1752dnl undo damage: remove result of tls_client call
1753R$* $| $*		$: $1
1754
1755dnl workspace: address as given by MAIL FROM:
1756R<>			$@ <OK>			we MUST accept <> (RFC 1123)
1757ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1758dnl do some additional checks
1759dnl no user@host
1760dnl no user@localhost (if nonlocal sender)
1761dnl this is a pretty simple canonification, it will not catch every case
1762dnl just make sure the address has <> around it (which is required by
1763dnl the RFC anyway, maybe we should complain if they are missing...)
1764dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1765dnl not be modified by host lookups.
1766R$+			$: <?> $1
1767R<?><$+>		$: <@> <$1>
1768R<?>$+			$: <@> <$1>
1769dnl workspace: <@> <address>
1770dnl prepend daemon_flags
1771R$*			$: $&{daemon_flags} $| $1
1772dnl workspace: ${daemon_flags} $| <@> <address>
1773dnl do not allow these at all or only from local systems?
1774R$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1775dnl accept unqualified sender: change mark to avoid test
1776R$* u $* $| <@> < $* >	$: <?> < $3 >
1777dnl workspace: ${daemon_flags} $| <@> <address>
1778dnl        or:                    <? ${client_name} > <address>
1779dnl        or:                    <?> <address>
1780dnl remove daemon_flags
1781R$* $| $*		$: $2
1782# handle case of @localhost on address
1783R<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
1784R<@> < $* @ [127.0.0.1] >
1785			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1786R<@> < $* @ localhost.$m >
1787			$: < ? $&{client_name} > < $1 @ localhost.$m >
1788ifdef(`_NO_UUCP_', `dnl',
1789`R<@> < $* @ localhost.UUCP >
1790			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1791dnl workspace: < ? $&{client_name} > <user@localhost|host>
1792dnl	or:    <@> <address>
1793dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1794R<@> $*			$: $1			no localhost as domain
1795dnl workspace: < ? $&{client_name} > <user@localhost|host>
1796dnl	or:    <address>
1797dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1798R<? $=w> $*		$: $2			local client: ok
1799R<? $+> <$+>		$#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
1800dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1801R<?> $*			$: $1')
1802dnl workspace: address (or <address>)
1803R$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
1804dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1805dnl there is nothing behind the <@host> so no trailing $* needed
1806R<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
1807# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1808R<?> $* < @ $* $=P >	$: <_RES_OK_> $1 < @ $2 $3 >
1809dnl workspace <mark> CanonicalAddress	where mark is ? or OK
1810dnl A sender address with my local host name ($j) is safe
1811R<?> $* < @ $j >	$: <_RES_OK_> $1 < @ $j >
1812ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1813`R<?> $* < @ $+ >	$: <_RES_OK_> $1 < @ $2 >		... unresolvable OK',
1814`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1815R<? $* <$->> $* < @ $+ >
1816			$: <$2> $3 < @ $4 >')
1817dnl workspace <mark> CanonicalAddress	where mark is ?, _RES_OK_, PERM, TEMP
1818dnl mark is ? iff the address is user (wo @domain)
1819
1820ifdef(`_ACCESS_TABLE_', `dnl
1821# check sender address: user@address, user@, address
1822dnl should we remove +ext from user?
1823dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
1824R<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
1825R<$+> $+		$: @<$1> <$2> $| <U:$2@>
1826dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1827dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1828dnl will only return user<@domain when "reversing" the args
1829R@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
1830dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1831R<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
1832dnl workspace: <result> <mark> <CanonicalAddress>
1833# retransform for further use
1834dnl required form:
1835dnl <ResultOfLookup|mark> CanonicalAddress
1836R<?> <$+> <$*>		$: <$1> $2	no match
1837R<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
1838dnl workspace <ResultOfLookup|mark> CanonicalAddress
1839dnl mark is ? iff the address is user (wo @domain)
1840
1841ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1842# handle case of no @domain on address
1843dnl prepend daemon_flags
1844R<?> $*			$: $&{daemon_flags} $| <?> $1
1845dnl accept unqualified sender: change mark to avoid test
1846R$* u $* $| <?> $*	$: <_RES_OK_> $3
1847dnl remove daemon_flags
1848R$* $| $*		$: $2
1849R<?> $*			$: < ? $&{client_addr} > $1
1850R<?> $*			$@ <_RES_OK_>			...local unqualed ok
1851R<? $+> $*		$#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
1852							...remote is not')
1853# check results
1854R<?> $*			$: @ $1		mark address: nothing known about it
1855R<$={ResOk}> $*		$@ <_RES_OK_>	domain ok: stop
1856R<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1857R<PERM> $*		$#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
1858ifdef(`_ACCESS_TABLE_', `dnl
1859R<$={Accept}> $*	$# $1		accept from access map
1860R<DISCARD> $*		$#discard $: discard
1861ifdef(`_FFR_QUARANTINE',
1862`R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1', `dnl')
1863R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1864dnl error tag
1865R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1866R<ERROR:$+> $*		$#error $: $1
1867ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
1868dnl generic error from access map
1869R<$+> $*		$#error $: $1		error from access db',
1870`dnl')
1871
1872######################################################################
1873###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
1874######################################################################
1875
1876SLocal_check_rcpt
1877Scheck`'_U_`'rcpt
1878R$*			$: $1 $| $>"Local_check_rcpt" $1
1879R$* $| $#$*		$#$2
1880R$* $| $*		$@ $>"Basic_check_rcpt" $1
1881
1882SBasic_check_rcpt
1883# empty address?
1884R<>			$#error $@ nouser $: "553 User address required"
1885R$@			$#error $@ nouser $: "553 User address required"
1886# check for deferred delivery mode
1887R$*			$: < $&{deliveryMode} > $1
1888R< d > $*		$@ deferred
1889R< $* > $*		$: $2
1890
1891ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1892dnl this code checks for user@host where host is not a FQHN.
1893dnl it is not activated.
1894dnl notice: code to check for a recipient without a domain name is
1895dnl available down below; look for the same macro.
1896dnl this check is done here because the name might be qualified by the
1897dnl canonicalization.
1898# require fully qualified domain part?
1899dnl very simple canonification: make sure the address is in < >
1900R$+			$: <?> $1
1901R<?> <$+>		$: <@> <$1>
1902R<?> $+			$: <@> <$1>
1903R<@> < postmaster >	$: postmaster
1904R<@> < $* @ $+ . $+ >	$: < $1 @ $2 . $3 >
1905dnl prepend daemon_flags
1906R<@> $*			$: $&{daemon_flags} $| <@> $1
1907dnl workspace: ${daemon_flags} $| <@> <address>
1908dnl do not allow these at all or only from local systems?
1909R$* r $* $| <@> < $* @ $* >	$: < ? $&{client_name} > < $3 @ $4 >
1910R<?> < $* >		$: <$1>
1911R<? $=w> < $* >		$: <$1>
1912R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Fully qualified domain name required"
1913dnl remove daemon_flags for other cases
1914R$* $| <@> $*		$: $2', `dnl')
1915
1916dnl ##################################################################
1917dnl call subroutines for recipient and relay
1918dnl possible returns from subroutines:
1919dnl $#TEMP	temporary failure
1920dnl $#error	permanent failure (or temporary if from access map)
1921dnl $#other	stop processing
1922dnl RELAY	RELAYing allowed
1923dnl other	otherwise
1924######################################################################
1925R$*			$: $1 $| @ $>"Rcpt_ok" $1
1926dnl temporary failure? remove mark @ and remember
1927R$* $| @ $#TEMP $+	$: $1 $| T $2
1928dnl error or ok (stop)
1929R$* $| @ $#$*		$#$2
1930ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
1931R$* $| @ RELAY		$@ RELAY
1932dnl something else: call check sender (relay)
1933R$* $| @ $*		$: O $| $>"Relay_ok" $1
1934dnl temporary failure: call check sender (relay)
1935R$* $| T $+		$: T $2 $| $>"Relay_ok" $1
1936dnl temporary failure? return that
1937R$* $| $#TEMP $+	$#error $2
1938dnl error or ok (stop)
1939R$* $| $#$*		$#$2
1940R$* $| RELAY		$@ RELAY
1941dnl something else: return previous temp failure
1942R T $+ $| $*		$#error $1
1943# anything else is bogus
1944R$*			$#error $@ 5.7.1 $: confRELAY_MSG
1945divert(0)
1946
1947######################################################################
1948### Rcpt_ok: is the recipient ok?
1949dnl input: recipient address (RCPT TO)
1950dnl output: see explanation at call
1951######################################################################
1952SRcpt_ok
1953ifdef(`_LOOSE_RELAY_CHECK_',`dnl
1954R$*			$: $>CanonAddr $1
1955R$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
1956`R$*			$: $>ParseRecipient $1		strip relayable hosts')
1957
1958ifdef(`_BESTMX_IS_LOCAL_',`dnl
1959ifelse(_BESTMX_IS_LOCAL_, `', `dnl
1960# unlimited bestmx
1961R$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
1962`dnl
1963# limit bestmx to $=B
1964R$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
1965R$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Rcpt_ok" $1 $2 $3
1966R$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
1967R$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
1968
1969ifdef(`_BLACKLIST_RCPT_',`dnl
1970ifdef(`_ACCESS_TABLE_', `dnl
1971# blacklist local users or any host from receiving mail
1972R$*			$: <?> $1
1973dnl user is now tagged with @ to be consistent with check_mail
1974dnl and to distinguish users from hosts (com would be host, com@ would be user)
1975R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
1976R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
1977R<?> $+			$: <> <$1> $| <U:$1@>
1978dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1979dnl will only return user<@domain when "reversing" the args
1980R<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
1981R<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
1982R<?> <$*>		$: @ $1		mark address as no match
1983dnl we may have to filter here because otherwise some RHSs
1984dnl would be interpreted as generic error messages...
1985dnl error messages should be "tagged" by prefixing them with error: !
1986dnl that would make a lot of things easier.
1987R<$={Accept}> <$*>	$: @ $2		mark address as no match
1988ifdef(`_ACCESS_SKIP_', `dnl
1989R<SKIP> <$*>		$: @ $1		mark address as no match', `dnl')
1990ifdef(`_DELAY_COMPAT_8_10_',`dnl
1991dnl compatility with 8.11/8.10:
1992dnl we have to filter these because otherwise they would be interpreted
1993dnl as generic error message...
1994dnl error messages should be "tagged" by prefixing them with error: !
1995dnl that would make a lot of things easier.
1996dnl maybe we should stop checks already here (if SPAM_xyx)?
1997R<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
1998R<REJECT> $*		$#error $@ 5.2.1 $: confRCPTREJ_MSG
1999R<DISCARD> $*		$#discard $: discard
2000ifdef(`_FFR_QUARANTINE',
2001`R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1', `dnl')
2002dnl error tag
2003R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
2004R<ERROR:$+> $*		$#error $: $1
2005ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2006dnl generic error from access map
2007R<$+> $*		$#error $: $1		error from access db
2008R@ $*			$1		remove mark', `dnl')', `dnl')
2009
2010ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2011# authenticated via TLS?
2012R$*			$: $1 $| $>RelayTLS	client authenticated?
2013R$* $| $# $+		$# $2			error/ok?
2014R$* $| $*		$: $1			no
2015
2016R$*			$: $1 $| $>"Local_Relay_Auth" $&{auth_type}
2017dnl workspace: localpart<@domain> $| result of Local_Relay_Auth
2018R$* $| $# $*		$# $2
2019dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
2020R$* $| NO		$: $1
2021R$* $| $*		$: $1 $| $&{auth_type}
2022dnl workspace: localpart<@domain> [ $| ${auth_type} ]
2023dnl empty ${auth_type}?
2024R$* $|			$: $1
2025dnl mechanism ${auth_type} accepted?
2026dnl use $# to override further tests (delay_checks): see check_rcpt below
2027R$* $| $={TrustAuthMech}	$# RELAY
2028dnl remove ${auth_type}
2029R$* $| $*		$: $1
2030dnl workspace: localpart<@domain> | localpart
2031ifelse(defn(`_NO_UUCP_'), `r',
2032`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
2033R$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
2034# anything terminating locally is ok
2035ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2036R$+ < @ $* $=m >	$@ RELAY', `dnl')
2037R$+ < @ $=w >		$@ RELAY
2038ifdef(`_RELAY_HOSTS_ONLY_',
2039`R$+ < @ $=R >		$@ RELAY
2040ifdef(`_ACCESS_TABLE_', `dnl
2041R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>
2042dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2043R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
2044`R$+ < @ $* $=R >	$@ RELAY
2045ifdef(`_ACCESS_TABLE_', `dnl
2046R$+ < @ $+ >		$: $>D <$2> <?> <+ To> <$1 < @ $2 >>',`dnl')')
2047ifdef(`_ACCESS_TABLE_', `dnl
2048dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2049R<RELAY> $*		$@ RELAY
2050ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2051R<$*> <$*>		$: $2',`dnl')
2052
2053
2054ifdef(`_RELAY_MX_SERVED_', `dnl
2055# allow relaying for hosts which we MX serve
2056R$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
2057dnl this must not necessarily happen if the client is checked first...
2058R< : $* <TEMP> : > $*	$#TEMP $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
2059R<$* : $=w . : $*> $*	$@ RELAY
2060R< : $* : > $*		$: $2',
2061`dnl')
2062
2063# check for local user (i.e. unqualified address)
2064R$*			$: <?> $1
2065R<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
2066# local user is ok
2067dnl is it really? the standard requires user@domain, not just user
2068dnl but we should accept it anyway (maybe making it an option:
2069dnl RequireFQDN ?)
2070dnl postmaster must be accepted without domain (DRUMS)
2071ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2072R<?> postmaster		$@ OK
2073# require qualified recipient?
2074dnl prepend daemon_flags
2075R<?> $+			$: $&{daemon_flags} $| <?> $1
2076dnl workspace: ${daemon_flags} $| <?> localpart
2077dnl do not allow these at all or only from local systems?
2078dnl r flag? add client_name
2079R$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
2080dnl no r flag: relay to local user (only local part)
2081# no qualified recipient required
2082R$* $| <?> $+		$@ RELAY
2083dnl client_name is empty
2084R<?> <?> $+		$@ RELAY
2085dnl client_name is local
2086R<? $=w> <?> $+		$@ RELAY
2087dnl client_name is not local
2088R<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
2089dnl no qualified recipient required
2090R<?> $+			$@ RELAY')
2091dnl it is a remote user: remove mark and then check client
2092R<$+> $*		$: $2
2093dnl currently the recipient address is not used below
2094
2095######################################################################
2096### Relay_ok: is the relay/sender ok?
2097dnl input: ignored
2098dnl output: see explanation at call
2099######################################################################
2100SRelay_ok
2101# anything originating locally is ok
2102# check IP address
2103R$*			$: $&{client_addr}
2104R$@			$@ RELAY		originated locally
2105R0			$@ RELAY		originated locally
2106R127.0.0.1		$@ RELAY		originated locally
2107RIPv6:::1		$@ RELAY		originated locally
2108R$=R $*			$@ RELAY		relayable IP address
2109ifdef(`_ACCESS_TABLE_', `dnl
2110R$*			$: $>A <$1> <?> <+ Connect> <$1>
2111R<RELAY> $* 		$@ RELAY		relayable IP address
2112ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
2113dnl this will cause rejections in cases like:
2114dnl Connect:My.Host.Domain	RELAY
2115dnl Connect:My.Net		REJECT
2116dnl since in check_relay client_name is checked before client_addr
2117R<REJECT> $* 		$@ REJECT		rejected IP address')
2118ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2119R<$*> <$*>		$: $2', `dnl')
2120R$*			$: [ $1 ]		put brackets around it...
2121R$=w			$@ RELAY		... and see if it is local
2122
2123ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2124ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2125ifdef(`_RELAY_MAIL_FROM_', `dnl
2126dnl input: {client_addr} or something "broken"
2127dnl just throw the input away; we do not need it.
2128# check whether FROM is allowed to use system as relay
2129R$*			$: <?> $>CanonAddr $&f
2130R<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
2131ifdef(`_RELAY_LOCAL_FROM_', `dnl
2132# check whether local FROM is ok
2133R<?> $+ < @ $=w >	$@ RELAY		FROM local', `dnl')
2134ifdef(`_RELAY_DB_FROM_', `dnl
2135R<?> $+ < @ $+ >	$: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
2136R<@> <RELAY>		$@ RELAY		RELAY FROM sender ok
2137ifdef(`_ATMPF_', `R<@> <_ATMPF_>		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2138', `dnl
2139ifdef(`_RELAY_DB_FROM_DOMAIN_',
2140`errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
2141')',
2142`dnl')
2143dnl')', `dnl')
2144dnl notice: the rulesets above do not leave a unique workspace behind.
2145dnl it does not matter in this case because the following rule ignores
2146dnl the input. otherwise these rules must "clean up" the workspace.
2147
2148# check client name: first: did it resolve?
2149dnl input: ignored
2150R$*			$: < $&{client_resolve} >
2151R<TEMP>			$#TEMP $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
2152R<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
2153R<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
2154dnl ${client_resolve} should be OK, so go ahead
2155R$*			$: <@> $&{client_name}
2156dnl should not be necessary since it has been done for client_addr already
2157dnl this rule actually may cause a problem if {client_name} resolves to ""
2158dnl however, this should not happen since the forward lookup should fail
2159dnl and {client_resolve} should be TEMP or FAIL.
2160dnl nevertheless, removing the rule doesn't hurt.
2161dnl R<@>			$@ RELAY
2162dnl workspace: <@> ${client_name} (not empty)
2163# pass to name server to make hostname canonical
2164R<@> $* $=P 		$:<?>  $1 $2
2165R<@> $+			$:<?>  $[ $1 $]
2166dnl workspace: <?> ${client_name} (canonified)
2167R$* .			$1			strip trailing dots
2168ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2169R<?> $* $=m		$@ RELAY', `dnl')
2170R<?> $=w		$@ RELAY
2171ifdef(`_RELAY_HOSTS_ONLY_',
2172`R<?> $=R		$@ RELAY
2173ifdef(`_ACCESS_TABLE_', `dnl
2174R<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
2175R<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
2176`R<?> $* $=R			$@ RELAY
2177ifdef(`_ACCESS_TABLE_', `dnl
2178R<?> $*			$: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
2179ifdef(`_ACCESS_TABLE_', `dnl
2180R<RELAY> $*		$@ RELAY
2181ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2182R<$*> <$*>		$: $2',`dnl')
2183dnl end of _PROMISCUOUS_RELAY_
2184divert(0)
2185ifdef(`_DELAY_CHECKS_',`dnl
2186# turn a canonical address in the form user<@domain>
2187# qualify unqual. addresses with $j
2188dnl it might have been only user (without <@domain>)
2189SFullAddr
2190R$* <@ $+ . >		$1 <@ $2 >
2191R$* <@ $* >		$@ $1 <@ $2 >
2192R$+			$@ $1 <@ $j >
2193
2194SDelay_TLS_Client
2195# authenticated?
2196dnl code repeated here from Basic_check_mail
2197dnl only called from check_rcpt in delay mode if checkrcpt returns $#
2198R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2199R$* $| $#$+		$#$2
2200dnl return result from checkrcpt
2201R$*			$# $1
2202
2203SDelay_TLS_Client2
2204# authenticated?
2205dnl code repeated here from Basic_check_mail
2206dnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
2207R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
2208R$* $| $#$+		$#$2
2209dnl return result from friend/hater check
2210R$*			$@ $1
2211
2212# call all necessary rulesets
2213Scheck_rcpt
2214dnl this test should be in the Basic_check_rcpt ruleset
2215dnl which is the correct DSN code?
2216# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
2217
2218R$+			$: $1 $| $>checkrcpt $1
2219dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2220dnl on error (or discard) stop now
2221R$+ $| $#error $*	$#error $2
2222R$+ $| $#discard $*	$#discard $2
2223dnl otherwise call tls_client; see above
2224R$+ $| $#$*		$@ $>"Delay_TLS_Client" $2
2225R$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
2226ifdef(`_SPAM_FH_',
2227`dnl lookup user@ and user@address
2228ifdef(`_ACCESS_TABLE_', `',
2229`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
2230')')dnl
2231dnl one of the next two rules is supposed to match
2232dnl this code has been copied from BLACKLIST... etc
2233dnl and simplified by omitting some < >.
2234R<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
2235R<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
2236dnl R<?>		$@ something_is_very_wrong_here
2237# lookup the addresses only with Spam tag
2238R<> $* $| <$+>		$: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
2239R<@> $* $| $*		$: $2 $1		reverse result
2240dnl', `dnl')
2241ifdef(`_SPAM_FRIEND_',
2242`# is the recipient a spam friend?
2243ifdef(`_SPAM_HATER_',
2244	`errprint(`*** ERROR: define either Hater or Friend -- not both.
2245')', `dnl')
2246R<FRIEND> $+		$@ $>"Delay_TLS_Client2" SPAMFRIEND
2247R<$*> $+		$: $2',
2248`dnl')
2249ifdef(`_SPAM_HATER_',
2250`# is the recipient no spam hater?
2251R<HATER> $+		$: $1			spam hater: continue checks
2252R<$*> $+		$@ $>"Delay_TLS_Client2" NOSPAMHATER	everyone else: stop
2253dnl',`dnl')
2254dnl run further checks: check_mail
2255dnl should we "clean up" $&f?
2256ifdef(`_FFR_MAIL_MACRO',
2257`R$*			$: $1 $| $>checkmail $&{mail_from}',
2258`R$*			$: $1 $| $>checkmail <$&f>')
2259dnl recipient (canonical format) $| result of checkmail
2260R$* $| $#$*		$#$2
2261dnl run further checks: check_relay
2262R$* $| $*		$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
2263R$* $| $#$*		$#$2
2264R$* $| $*		$: $1
2265', `dnl')
2266
2267ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
2268######################################################################
2269###  F: LookUpFull -- search for an entry in access database
2270###
2271###	lookup of full key (which should be an address) and
2272###	variations if +detail exists: +* and without +detail
2273###
2274###	Parameters:
2275###		<$1> -- key
2276###		<$2> -- default (what to return if not found in db)
2277dnl			must not be empty
2278###		<$3> -- mark (must be <(!|+) single-token>)
2279###			! does lookup only with tag
2280###			+ does lookup with and without tag
2281###		<$4> -- passthru (additional data passed unchanged through)
2282dnl returns:		<default> <passthru>
2283dnl 			<result> <passthru>
2284######################################################################
2285
2286SF
2287dnl workspace: <key> <def> <o tag> <thru>
2288dnl full lookup
2289dnl    2    3  4    5
2290R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2291dnl no match, try without tag
2292dnl   1    2      3    4
2293R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2294dnl no match, +detail: try +*
2295dnl   1    2    3    4    5  6    7
2296R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2297			$: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2298dnl no match, +detail: try +* without tag
2299dnl   1    2    3    4      5    6
2300R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2301			$: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2302dnl no match, +detail: try without +detail
2303dnl   1    2    3    4    5  6    7
2304R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2305			$: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2306dnl no match, +detail: try without +detail and without tag
2307dnl   1    2    3    4      5    6
2308R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2309			$: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2310dnl no match, return <default> <passthru>
2311dnl   1    2    3  4    5
2312R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2313ifdef(`_ATMPF_', `dnl tempfail?
2314dnl            2    3  4    5
2315R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2316dnl match, return <match> <passthru>
2317dnl    2    3  4    5
2318R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2319
2320######################################################################
2321###  E: LookUpExact -- search for an entry in access database
2322###
2323###	Parameters:
2324###		<$1> -- key
2325###		<$2> -- default (what to return if not found in db)
2326dnl			must not be empty
2327###		<$3> -- mark (must be <(!|+) single-token>)
2328###			! does lookup only with tag
2329###			+ does lookup with and without tag
2330###		<$4> -- passthru (additional data passed unchanged through)
2331dnl returns:		<default> <passthru>
2332dnl 			<result> <passthru>
2333######################################################################
2334
2335SE
2336dnl    2    3  4    5
2337R<$*> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2338dnl no match, try without tag
2339dnl   1    2      3    4
2340R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2341dnl no match, return default passthru
2342dnl   1    2    3  4    5
2343R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2344ifdef(`_ATMPF_', `dnl tempfail?
2345dnl            2    3  4    5
2346R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2347dnl match, return <match> <passthru>
2348dnl    2    3  4    5
2349R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2350
2351######################################################################
2352###  U: LookUpUser -- search for an entry in access database
2353###
2354###	lookup of key (which should be a local part) and
2355###	variations if +detail exists: +* and without +detail
2356###
2357###	Parameters:
2358###		<$1> -- key (user@)
2359###		<$2> -- default (what to return if not found in db)
2360dnl			must not be empty
2361###		<$3> -- mark (must be <(!|+) single-token>)
2362###			! does lookup only with tag
2363###			+ does lookup with and without tag
2364###		<$4> -- passthru (additional data passed unchanged through)
2365dnl returns:		<default> <passthru>
2366dnl 			<result> <passthru>
2367######################################################################
2368
2369SU
2370dnl user lookups are always with trailing @
2371dnl    2    3  4    5
2372R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2373dnl no match, try without tag
2374dnl   1    2      3    4
2375R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2376dnl do not remove the @ from the lookup:
2377dnl it is part of the +detail@ which is omitted for the lookup
2378dnl no match, +detail: try +*
2379dnl   1    2      3    4  5    6
2380R<?> <$+ + $* @> <$*> <$- $-> <$*>
2381			$: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2382dnl no match, +detail: try +* without tag
2383dnl   1    2      3      4    5
2384R<?> <$+ + $* @> <$*> <+ $-> <$*>
2385			$: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2386dnl no match, +detail: try without +detail
2387dnl   1    2      3    4  5    6
2388R<?> <$+ + $* @> <$*> <$- $-> <$*>
2389			$: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2390dnl no match, +detail: try without +detail and without tag
2391dnl   1    2      3      4    5
2392R<?> <$+ + $* @> <$*> <+ $-> <$*>
2393			$: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2394dnl no match, return <default> <passthru>
2395dnl   1    2    3  4    5
2396R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
2397ifdef(`_ATMPF_', `dnl tempfail?
2398dnl            2    3  4    5
2399R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
2400dnl match, return <match> <passthru>
2401dnl    2    3  4    5
2402R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
2403
2404######################################################################
2405###  SearchList: search a list of items in the access map
2406###	Parameters:
2407###		<exact tag> $| <mark:address> <mark:address> ... <>
2408dnl	maybe we should have a @ (again) in front of the mark to
2409dnl	avoid errorneous matches (with error messages?)
2410dnl	if we can make sure that tag is always a single token
2411dnl	then we can omit the delimiter $|, otherwise we need it
2412dnl	to avoid errorneous matchs (first rule: D: if there
2413dnl	is that mark somewhere in the list, it will be taken).
2414dnl	moreover, we can do some tricks to enforce lookup with
2415dnl	the tag only, e.g.:
2416###	where "exact" is either "+" or "!":
2417###	<+ TAG>	lookup with and w/o tag
2418###	<! TAG>	lookup with tag
2419dnl	Warning: + and ! should be in OperatorChars (otherwise there must be
2420dnl		a blank between them and the tag.
2421###	possible values for "mark" are:
2422###		D: recursive host lookup (LookUpDomain)
2423dnl		A: recursive address lookup (LookUpAddress) [not yet required]
2424###		E: exact lookup, no modifications
2425###		F: full lookup, try user+ext@domain and user@domain
2426###		U: user lookup, try user+ext and user (input must have trailing @)
2427###	return: <RHS of lookup> or <?> (not found)
2428######################################################################
2429
2430# class with valid marks for SearchList
2431dnl if A is activated: add it
2432C{src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
2433SSearchList
2434# just call the ruleset with the name of the tag... nice trick...
2435dnl       2       3    4
2436R<$+> $| <$={src}:$*> <$*>	$: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
2437dnl workspace: <o tag> $| <rest> $| <result of lookup> <>
2438dnl no match and nothing left: return
2439R<$+> $| <> $| <?> <>		$@ <?>
2440dnl no match but something left: continue
2441R<$+> $| <$+> $| <?> <>		$@ $>SearchList <$1> $| <$2>
2442dnl match: return
2443R<$+> $| <$*> $| <$+> <>	$@ <$3>
2444dnl return result from recursive invocation
2445R<$+> $| <$+>			$@ <$2>
2446dnl endif _ACCESS_TABLE_
2447divert(0)
2448
2449######################################################################
2450###  trust_auth: is user trusted to authenticate as someone else?
2451###
2452###	Parameters:
2453###		$1: AUTH= parameter from MAIL command
2454######################################################################
2455
2456dnl empty ruleset definition so it can be called
2457SLocal_trust_auth
2458Strust_auth
2459R$*			$: $&{auth_type} $| $1
2460# required by RFC 2554 section 4.
2461R$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
2462dnl seems to be useful...
2463R$* $| $&{auth_authen}		$@ identical
2464R$* $| <$&{auth_authen}>	$@ identical
2465dnl call user supplied code
2466R$* $| $*		$: $1 $| $>"Local_trust_auth" $1
2467R$* $| $#$*		$#$2
2468dnl default: error
2469R$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
2470
2471######################################################################
2472###  Relay_Auth: allow relaying based on authentication?
2473###
2474###	Parameters:
2475###		$1: ${auth_type}
2476######################################################################
2477SLocal_Relay_Auth
2478
2479ifdef(`_ACCESS_TABLE_', `dnl
2480######################################################################
2481###  srv_features: which features to offer to a client?
2482###	(done in server)
2483######################################################################
2484Ssrv_features
2485ifdef(`_LOCAL_SRV_FEATURES_', `dnl
2486R$*			$: $1 $| $>"Local_srv_features" $1
2487R$* $| $#$*		$#$2
2488R$* $| $*		$: $1', `dnl')
2489R$*		$: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
2490R<?>$*		$: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
2491R<?>$*		$: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
2492R<?>$*		$@ OK
2493ifdef(`_ATMPF_', `dnl tempfail?
2494R<$* _ATMPF_>$*	$#temp', `dnl')
2495R<$+>$*		$# $1
2496
2497######################################################################
2498###  try_tls: try to use STARTTLS?
2499###	(done in client)
2500######################################################################
2501Stry_tls
2502ifdef(`_LOCAL_TRY_TLS_', `dnl
2503R$*			$: $1 $| $>"Local_try_tls" $1
2504R$* $| $#$*		$#$2
2505R$* $| $*		$: $1', `dnl')
2506R$*		$: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
2507R<?>$*		$: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
2508R<?>$*		$: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
2509R<?>$*		$@ OK
2510ifdef(`_ATMPF_', `dnl tempfail?
2511R<$* _ATMPF_>$*	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2512R<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"
2513 
2514######################################################################
2515###  tls_rcpt: is connection with server "good" enough?
2516###	(done in client, per recipient)
2517dnl called from deliver() before RCPT command
2518###
2519###	Parameters:
2520###		$1: recipient
2521######################################################################
2522Stls_rcpt
2523ifdef(`_LOCAL_TLS_RCPT_', `dnl
2524R$*			$: $1 $| $>"Local_tls_rcpt" $1
2525R$* $| $#$*		$#$2
2526R$* $| $*		$: $1', `dnl')
2527dnl store name of other side
2528R$*			$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2529dnl canonify recipient address
2530R$+			$: <?> $>CanonAddr $1
2531dnl strip trailing dots
2532R<?> $+ < @ $+ . >	<?> $1 <@ $2 >
2533dnl full address?
2534R<?> $+ < @ $+ >	$: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
2535dnl only localpart?
2536R<?> $+			$: $1 $| <U:$1@> <E:>
2537dnl look it up
2538dnl also look up a default value via E:
2539R$* $| $+	$: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
2540dnl found nothing: stop here
2541R$* $| <?>	$@ OK
2542ifdef(`_ATMPF_', `dnl tempfail?
2543R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2544dnl use the generic routine (for now)
2545R$* $| <$+>	$@ $>"TLS_connection" $&{verify} $| <$2>')
2546
2547######################################################################
2548###  tls_client: is connection with client "good" enough?
2549###	(done in server)
2550###
2551###	Parameters:
2552###		${verify} $| (MAIL|STARTTLS)
2553######################################################################
2554dnl MAIL: called from check_mail
2555dnl STARTTLS: called from smtp() after STARTTLS has been accepted
2556Stls_client
2557ifdef(`_LOCAL_TLS_CLIENT_', `dnl
2558R$*			$: $1 $| $>"Local_tls_client" $1
2559R$* $| $#$*		$#$2
2560R$* $| $*		$: $1', `dnl')
2561ifdef(`_ACCESS_TABLE_', `dnl
2562dnl store name of other side
2563R$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2564dnl ignore second arg for now
2565dnl maybe use it to distinguish permanent/temporary error?
2566dnl if MAIL: permanent (STARTTLS has not been offered)
2567dnl if STARTTLS: temporary (offered but maybe failed)
2568R$* $| $*	$: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
2569R$* $| <?>$*	$: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
2570dnl do a default lookup: just TLS_CLT_TAG
2571R$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
2572ifdef(`_ATMPF_', `dnl tempfail?
2573R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2574R$*		$@ $>"TLS_connection" $1', `dnl
2575R$* $| $*	$@ $>"TLS_connection" $1')
2576
2577######################################################################
2578###  tls_server: is connection with server "good" enough?
2579###	(done in client)
2580###
2581###	Parameter:
2582###		${verify}
2583######################################################################
2584dnl i.e. has the server been authenticated and is encryption active?
2585dnl called from deliver() after STARTTLS command
2586Stls_server
2587ifdef(`_LOCAL_TLS_SERVER_', `dnl
2588R$*			$: $1 $| $>"Local_tls_server" $1
2589R$* $| $#$*		$#$2
2590R$* $| $*		$: $1', `dnl')
2591ifdef(`_ACCESS_TABLE_', `dnl
2592dnl store name of other side
2593R$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
2594R$*		$: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
2595R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
2596dnl do a default lookup: just TLS_SRV_TAG
2597R$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
2598ifdef(`_ATMPF_', `dnl tempfail?
2599R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
2600R$*		$@ $>"TLS_connection" $1', `dnl
2601R$*		$@ $>"TLS_connection" $1')
2602
2603######################################################################
2604###  TLS_connection: is TLS connection "good" enough?
2605###
2606###	Parameters:
2607ifdef(`_ACCESS_TABLE_', `dnl
2608###		${verify} $| <Requirement> [<>]', `dnl
2609###		${verify}')
2610###		Requirement: RHS from access map, may be ? for none.
2611dnl	syntax for Requirement:
2612dnl	[(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
2613dnl	extensions: could be a list of further requirements
2614dnl		for now: CN:string	{cn_subject} == string
2615######################################################################
2616STLS_connection
2617ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
2618dnl deal with TLS handshake failures: abort
2619RSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
2620divert(-1)')
2621dnl common ruleset for tls_{client|server}
2622dnl input: ${verify} $| <ResultOfLookup> [<>]
2623dnl remove optional <>
2624R$* $| <$*>$*			$: $1 $| <$2>
2625dnl workspace: ${verify} $| <ResultOfLookup>
2626# create the appropriate error codes
2627dnl permanent or temporary error?
2628R$* $| <PERM + $={tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
2629R$* $| <TEMP + $={tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
2630dnl default case depends on TLS_PERM_ERR
2631R$* $| <$={tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
2632dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
2633# deal with TLS handshake failures: abort
2634RSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
2635dnl no <reply:dns> i.e. not requirements in the access map
2636dnl use default error
2637RSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
2638R$* $| <$*> <VERIFY>		$: <$2> <VERIFY> <> $1
2639dnl separate optional requirements
2640R$* $| <$*> <VERIFY + $+>	$: <$2> <VERIFY> <$3> $1
2641R$* $| <$*> <$={tls}:$->$*	$: <$2> <$3:$4> <> $1
2642dnl separate optional requirements
2643R$* $| <$*> <$={tls}:$- + $+>$*	$: <$2> <$3:$4> <$5> $1
2644dnl some other value in access map: accept
2645dnl this also allows to override the default case (if used)
2646R$* $| $*			$@ OK
2647# authentication required: give appropriate error
2648# other side did authenticate (via STARTTLS)
2649dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
2650dnl only verification required and it succeeded
2651R<$*><VERIFY> <> OK		$@ OK
2652dnl verification required and it succeeded but extensions are given
2653dnl change it to <SMTP:ESC> <REQ:0>  <extensions>
2654R<$*><VERIFY> <$+> OK		$: <$1> <REQ:0> <$2>
2655dnl verification required + some level of encryption
2656R<$*><VERIFY:$-> <$*> OK	$: <$1> <REQ:$2> <$3>
2657dnl just some level of encryption required
2658R<$*><ENCR:$-> <$*> $*		$: <$1> <REQ:$2> <$3>
2659dnl workspace:
2660dnl 1. <SMTP:ESC> <VERIFY [:bits]>  <[extensions]> {verify} (!= OK)
2661dnl 2. <SMTP:ESC> <REQ:bits>  <[extensions]>
2662dnl verification required but ${verify} is not set (case 1.)
2663R<$-:$+><VERIFY $*> <$*>	$#error $@ $2 $: $1 " authentication required"
2664R<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
2665R<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
2666R<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
2667R<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
2668dnl some other value for ${verify}
2669R<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
2670dnl some level of encryption required: get the maximum level (case 2.)
2671R<$*><REQ:$-> <$*>		$: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
2672dnl compare required bits with actual bits
2673R<$*><REQ:$-> <$*> $-		$: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
2674R<$-:$+><$-:$-> <$*> TRUE	$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
2675dnl strength requirements fulfilled
2676dnl TLS Additional Requirements Separator
2677dnl this should be something which does not appear in the extensions itself
2678dnl @ could be part of a CN, DN, etc...
2679dnl use < > ? those are encoded in CN, DN, ...
2680define(`_TLS_ARS_', `++')dnl
2681dnl workspace:
2682dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
2683R<$-:$+><$-:$-> <$*> $*		$: <$1:$2 _TLS_ARS_ $5>
2684dnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
2685dnl continue: check  extensions
2686R<$-:$+ _TLS_ARS_ >			$@ OK
2687dnl split extensions into own list
2688R<$-:$+ _TLS_ARS_ $+ >			$: <$1:$2> <$3>
2689R<$-:$+> < $+ _TLS_ARS_ $+ >		<$1:$2> <$3> <$4>
2690R<$-:$+> $+			$@ $>"TLS_req" $3 $| <$1:$2>
2691
2692######################################################################
2693###  TLS_req: check additional TLS requirements
2694###
2695###	Parameters: [<list> <of> <req>] $| <$-:$+>
2696###		$-: SMTP reply code
2697###		$+: Enhanced Status Code
2698dnl  further requirements for this ruleset:
2699dnl	name of "other side" is stored is {TLS_name} (client/server_name)
2700dnl
2701dnl	currently only CN[:common_name] is implemented
2702dnl	right now this is only a logical AND
2703dnl	i.e. all requirements must be true
2704dnl	how about an OR? CN must be X or CN must be Y or ..
2705dnl	use a macro to compute this as a trivial sequential
2706dnl	operations (no precedences etc)?
2707######################################################################
2708STLS_req
2709dnl no additional requirements: ok
2710R $| $+		$@ OK
2711dnl require CN: but no CN specified: use name of other side
2712R<CN> $* $| <$+>		$: <CN:$&{TLS_Name}> $1 $| <$2>
2713dnl match, check rest
2714R<CN:$&{cn_subject}> $* $| <$+>		$@ $>"TLS_req" $1 $| <$2>
2715dnl CN does not match
2716dnl  1   2      3  4
2717R<CN:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
2718dnl cert subject
2719R<CS:$&{cert_subject}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
2720dnl CS does not match
2721dnl  1   2      3  4
2722R<CS:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
2723dnl match, check rest
2724R<CI:$&{cert_issuer}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
2725dnl CI does not match
2726dnl  1   2      3  4
2727R<CI:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
2728dnl return from recursive call
2729ROK			$@ OK
2730
2731######################################################################
2732###  max: return the maximum of two values separated by :
2733###
2734###	Parameters: [$-]:[$-]
2735######################################################################
2736Smax
2737R:		$: 0
2738R:$-		$: $1
2739R$-:		$: $1
2740R$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
2741RTRUE:$-:$-	$: $2
2742R$-:$-:$-	$: $2
2743dnl endif _ACCESS_TABLE_
2744divert(0)
2745
2746######################################################################
2747###  RelayTLS: allow relaying based on TLS authentication
2748###
2749###	Parameters:
2750###		none
2751######################################################################
2752SRelayTLS
2753# authenticated?
2754dnl we do not allow relaying for anyone who can present a cert
2755dnl signed by a "trusted" CA. For example, even if we put verisigns
2756dnl CA in CertPath so we can authenticate users, we do not allow
2757dnl them to abuse our server (they might be easier to get hold of,
2758dnl but anyway).
2759dnl so here is the trick: if the verification succeeded
2760dnl we look up the cert issuer in the access map
2761dnl (maybe after extracting a part with a regular expression)
2762dnl if this returns RELAY we relay without further questions
2763dnl if it returns SUBJECT we perform a similar check on the
2764dnl cert subject.
2765ifdef(`_ACCESS_TABLE_', `dnl
2766R$*			$: <?> $&{verify}
2767R<?> OK			$: OK		authenticated: continue
2768R<?> $*			$@ NO		not authenticated
2769ifdef(`_CERT_REGEX_ISSUER_', `dnl
2770R$*			$: $(CERTIssuer $&{cert_issuer} $)',
2771`R$*			$: $&{cert_issuer}')
2772R$+			$: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
2773dnl use $# to stop further checks (delay_check)
2774RRELAY			$# RELAY
2775ifdef(`_CERT_REGEX_SUBJECT_', `dnl
2776RSUBJECT		$: <@> $(CERTSubject $&{cert_subject} $)',
2777`RSUBJECT		$: <@> $&{cert_subject}')
2778R<@> $+			$: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
2779R<@> RELAY		$# RELAY
2780R$*			$: NO', `dnl')
2781
2782######################################################################
2783###  authinfo: lookup authinfo in the access map
2784###
2785###	Parameters:
2786###		$1: {server_name}
2787###		$2: {server_addr}
2788dnl	both are currently ignored
2789dnl if it should be done via another map, we either need to restrict
2790dnl functionality (it calls D and A) or copy those rulesets (or add another
2791dnl parameter which I want to avoid, it's quite complex already)
2792######################################################################
2793dnl omit this ruleset if neither is defined?
2794dnl it causes DefaultAuthInfo to be ignored
2795dnl (which may be considered a good thing).
2796Sauthinfo
2797ifdef(`_AUTHINFO_TABLE_', `dnl
2798R$*		$: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
2799R<?>		$: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
2800R<?>		$: <$(authinfo AuthInfo: $: ? $)>
2801R<?>		$@ no				no authinfo available
2802R<$*>		$# $1
2803dnl', `dnl
2804ifdef(`_ACCESS_TABLE_', `dnl
2805R$*		$: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
2806R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
2807R$* $| <?>$*	$: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
2808R$* $| <?>$*	$@ no				no authinfo available
2809R$* $| <$*> <>	$# $2
2810dnl', `dnl')')
2811
2812undivert(9)dnl LOCAL_RULESETS
2813#
2814######################################################################
2815######################################################################
2816#####
2817`#####			MAIL FILTER DEFINITIONS'
2818#####
2819######################################################################
2820######################################################################
2821_MAIL_FILTERS_
2822#
2823######################################################################
2824######################################################################
2825#####
2826`#####			MAILER DEFINITIONS'
2827#####
2828######################################################################
2829######################################################################
2830undivert(7)dnl MAILER_DEFINITIONS
2831
2832