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