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