proto.m4 revision 64562
1divert(-1)
2#
3# Copyright (c) 1998-2000 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.446.2.5.2.12 2000/07/19 21:41:19 gshapiro Exp $')
17
18MAILER(local)dnl
19
20# level CF_LEVEL config file format
21V`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')
22divert(-1)
23
24# do some sanity checking
25ifdef(`__OSTYPE__',,
26	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
27')')
28
29# pick our default mailers
30ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
31ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
32ifdef(`confRELAY_MAILER',,
33	`define(`confRELAY_MAILER',
34		`ifdef(`_MAILER_smtp_', `relay',
35			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
36ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
37define(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
38define(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
39define(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
40define(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
41
42# back compatibility with old config files
43ifdef(`confDEF_GROUP_ID',
44`errprint(`*** confDEF_GROUP_ID is obsolete.
45    Use confDEF_USER_ID with a colon in the value instead.
46')')
47ifdef(`confREAD_TIMEOUT',
48`errprint(`*** confREAD_TIMEOUT is obsolete.
49    Use individual confTO_<timeout> parameters instead.
50')')
51ifdef(`confMESSAGE_TIMEOUT',
52	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
53	 ifelse(_ARG_, -1,
54		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
55		`define(`confTO_QUEUERETURN',
56			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
57		 define(`confTO_QUEUEWARN',
58			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
59ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
60`errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
61    Use confMAX_MESSAGE_SIZE for the second part of the value.
62')')')
63
64
65# Sanity check on ldap_routing feature
66# If the user doesn't specify a new map, they better have given as a
67# default LDAP specification which has the LDAP base (and most likely the host)
68ifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
69WARNING: Using default FEATURE(ldap_routing) map definition(s)
70without setting confLDAP_DEFAULT_SPEC option.
71')')')dnl
72
73# clean option definitions below....
74define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
75
76dnl required to "rename" the check_* rulesets...
77define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
78dnl default relaying denied message
79ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG', `"550 Relaying denied"')')
80divert(0)dnl
81
82# override file safeties - setting this option compromises system security,
83# addressing the actual file configuration problem is preferred
84# need to set this before any file actions are encountered in the cf file
85_OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
86
87# default LDAP map specification
88# need to set this now before any LDAP maps are defined
89_OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
90
91##################
92#   local info   #
93##################
94
95Cwlocalhost
96ifdef(`USE_CW_FILE',
97`# file containing names of hosts for which we receive email
98Fw`'confCW_FILE',
99	`dnl')
100
101# my official domain name
102# ... `define' this only if sendmail cannot automatically determine your domain
103ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
104
105CP.
106
107ifdef(`UUCP_RELAY',
108`# UUCP relay host
109DY`'UUCP_RELAY
110CPUUCP
111
112')dnl
113ifdef(`BITNET_RELAY',
114`#  BITNET relay host
115DB`'BITNET_RELAY
116CPBITNET
117
118')dnl
119ifdef(`DECNET_RELAY',
120`define(`_USE_DECNET_SYNTAX_', 1)dnl
121# DECnet relay host
122DC`'DECNET_RELAY
123CPDECNET
124
125')dnl
126ifdef(`FAX_RELAY',
127`# FAX relay host
128DF`'FAX_RELAY
129CPFAX
130
131')dnl
132# "Smart" relay host (may be null)
133DS`'ifdef(`SMART_HOST', SMART_HOST)
134
135ifdef(`LUSER_RELAY', `dnl
136# place to which unknown users should be forwarded
137Kuser user -m -a<>
138DL`'LUSER_RELAY',
139`dnl')
140
141# operators that cannot be in local usernames (i.e., network indicators)
142CO @ % ifdef(`_NO_UUCP_', `', `!')
143
144# a class with just dot (for identifying canonical names)
145C..
146
147# a class with just a left bracket (for identifying domain literals)
148C[[
149
150ifdef(`_ACCESS_TABLE_', `dnl
151# access_db acceptance class
152C{Accept}OK RELAY
153ifdef(`_DELAY_CHECKS_',`dnl
154ifdef(`_BLACKLIST_RCPT_',`dnl
155# possible access_db RHS for spam friends/haters
156C{SpamTag}SPAMFRIEND SPAMHATER')')',
157`dnl')
158
159ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
160# Resolve map (to check if a host exists in check_mail)
161Kresolve host -a<OK> -T<TEMP>')
162
163ifdef(`confCR_FILE', `dnl
164# Hosts that will permit relaying ($=R)
165FR`'confCR_FILE',
166`dnl')
167
168define(`TLS_SRV_TAG', `TLS_Srv')dnl
169define(`TLS_CLT_TAG', `TLS_Clt')dnl
170define(`TLS_TRY_TAG', `Try_TLS')dnl
171define(`TLS_OFF_TAG', `Offer_TLS')dnl
172dnl this may be useful in other contexts too
173ifdef(`_ARITH_MAP_', `', `# arithmetic map
174define(`_ARITH_MAP_', `1')dnl
175Karith arith')
176ifdef(`_ACCESS_TABLE_', `dnl
177# possible values for tls_connect in access map
178C{tls}VERIFY ENCR', `dnl')
179ifdef(`_CERT_REGEX_ISSUER_', `dnl
180# extract relevant part from cert issuer
181KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
182ifdef(`_CERT_REGEX_SUBJECT_', `dnl
183# extract relevant part from cert subject
184KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
185
186# who I send unqualified names to (null means deliver locally)
187DR`'ifdef(`LOCAL_RELAY', LOCAL_RELAY)
188
189# who gets all local email traffic ($R has precedence for unqualified names)
190DH`'ifdef(`MAIL_HUB', MAIL_HUB)
191
192# dequoting map
193Kdequote dequote
194
195divert(0)dnl	# end of nullclient diversion
196# class E: names that should be exposed as from this host, even if we masquerade
197# class L: names that should be delivered locally, even if we have a relay
198# class M: domains that should be converted to $M
199# class N: domains that should not be converted to $M
200#CL root
201undivert(5)dnl
202ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
203
204# who I masquerade as (null for no masquerading) (see also $=M)
205DM`'ifdef(`MASQUERADE_NAME', MASQUERADE_NAME)
206
207# my name for error messages
208ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
209
210undivert(6)dnl LOCAL_CONFIG
211include(_CF_DIR_`m4/version.m4')
212
213###############
214#   Options   #
215###############
216
217# strip message body to 7 bits on input?
218_OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
219
220# 8-bit data handling
221_OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `adaptive')
222
223# wait for alias file rebuild (default units: minutes)
224_OPTION(AliasWait, `confALIAS_WAIT', `5m')
225
226# location of alias file
227_OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
228
229# minimum number of free blocks on filesystem
230_OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
231
232# maximum message size
233_OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `1000000')
234
235# substitution for space (blank) characters
236_OPTION(BlankSub, `confBLANK_SUB', `_')
237
238# avoid connecting to "expensive" mailers on initial submission?
239_OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
240
241# checkpoint queue runs after every N successful deliveries
242_OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
243
244# default delivery mode
245_OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
246
247# automatically rebuild the alias database?
248# NOTE: There is a potential for a denial of service attack if this is set.
249#       This option is deprecated and will be removed from a future version.
250_OPTION(AutoRebuildAliases, `confAUTO_REBUILD', `False')
251
252# error message header/file
253_OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
254
255# error mode
256_OPTION(ErrorMode, `confERROR_MODE', `print')
257
258# save Unix-style "From_" lines at top of header?
259_OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
260
261# temporary file mode
262_OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
263
264# match recipients against GECOS field?
265_OPTION(MatchGECOS, `confMATCH_GECOS', `False')
266
267# maximum hop count
268_OPTION(MaxHopCount, `confMAX_HOP', `17')
269
270# location of help file
271O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
272
273# ignore dots as terminators in incoming messages?
274_OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
275
276# name resolver options
277_OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
278
279# deliver MIME-encapsulated error messages?
280_OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
281
282# Forward file search path
283_OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
284
285# open connection cache size
286_OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
287
288# open connection cache timeout
289_OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
290
291# persistent host status directory
292_OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
293
294# single thread deliveries (requires HostStatusDirectory)?
295_OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
296
297# use Errors-To: header?
298_OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
299
300# log level
301_OPTION(LogLevel, `confLOG_LEVEL', `10')
302
303# send to me too, even in an alias expansion?
304_OPTION(MeToo, `confME_TOO', `True')
305
306# verify RHS in newaliases?
307_OPTION(CheckAliases, `confCHECK_ALIASES', `False')
308
309# default messages to old style headers if no special punctuation?
310_OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
311
312# SMTP daemon options
313ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
314`errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.  See cf/README for more information.
315)'dnl
316`DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
317ifelse(defn(`_DPO_'), `', `O DaemonPortOptions=Name=MTA', `_DPO_')
318ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
319
320# SMTP client options
321_OPTION(ClientPortOptions, `confCLIENT_OPTIONS', `Address=0.0.0.0')
322
323# privacy flags
324_OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
325
326# who (if anyone) should get extra copies of error messages
327_OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
328
329# slope of queue-only function
330_OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
331
332# queue directory
333O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
334
335# timeouts (many of these)
336_OPTION(Timeout.initial, `confTO_INITIAL', `5m')
337_OPTION(Timeout.connect, `confTO_CONNECT', `5m')
338_OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
339_OPTION(Timeout.helo, `confTO_HELO', `5m')
340_OPTION(Timeout.mail, `confTO_MAIL', `10m')
341_OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
342_OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
343_OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
344_OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
345_OPTION(Timeout.rset, `confTO_RSET', `5m')
346_OPTION(Timeout.quit, `confTO_QUIT', `2m')
347_OPTION(Timeout.misc, `confTO_MISC', `2m')
348_OPTION(Timeout.command, `confTO_COMMAND', `1h')
349_OPTION(Timeout.ident, `confTO_IDENT', `5s')
350_OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
351_OPTION(Timeout.control, `confTO_CONTROL', `2m')
352_OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
353_OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
354_OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
355_OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
356_OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
357_OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
358_OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
359_OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
360_OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
361_OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
362_OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
363_OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
364_OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
365_OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
366_OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
367
368# should we not prune routes in route-addr syntax addresses?
369_OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
370
371# queue up everything before forking?
372_OPTION(SuperSafe, `confSAFE_QUEUE', `True')
373
374# status file
375O StatusFile=ifdef(`STATUS_FILE', `STATUS_FILE', `MAIL_SETTINGS_DIR`'statistics')
376
377# time zone handling:
378#  if undefined, use system default
379#  if defined but null, use TZ envariable passed in
380#  if defined and non-null, use that info
381ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
382	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
383	`O TimeZoneSpec=confTIME_ZONE')
384
385# default UID (can be username or userid:groupid)
386_OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
387
388# list of locations of user database file (null means no lookup)
389_OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
390
391# fallback MX host
392_OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
393
394# if we are the best MX host for a site, try it directly instead of config err
395_OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
396
397# load average at which we just queue messages
398_OPTION(QueueLA, `confQUEUE_LA', `8')
399
400# load average at which we refuse connections
401_OPTION(RefuseLA, `confREFUSE_LA', `12')
402
403# maximum number of children we allow at one time
404_OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `12')
405
406# maximum number of new connections per second
407_OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `3')
408
409# work recipient factor
410_OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
411
412# deliver each queued job in a separate process?
413_OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
414
415# work class factor
416_OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
417
418# work time factor
419_OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
420
421# shall we sort the queue by hostname first?
422_OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
423
424# minimum time in queue before retry
425_OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
426
427# default character set
428_OPTION(DefaultCharSet, `confDEF_CHAR_SET', `iso-8859-1')
429
430# service switch file (ignored on Solaris, Ultrix, OSF/1, others)
431_OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
432
433# hosts file (normally /etc/hosts)
434_OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
435
436# dialup line delay on connection failure
437_OPTION(DialDelay, `confDIAL_DELAY', `10s')
438
439# action to take if there are no recipients in the message
440_OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `add-to-undisclosed')
441
442# chrooted environment for writing to files
443_OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `/arch')
444
445# are colons OK in addresses?
446_OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
447
448# how many jobs can you process in the queue?
449_OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `10000')
450
451# shall I avoid expanding CNAMEs (violates protocols)?
452_OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
453
454# SMTP initial login message (old $e macro)
455_OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
456
457# UNIX initial From header format (old $l macro)
458_OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
459
460# From: lines that have embedded newlines are unwrapped onto one line
461_OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
462
463# Allow HELO SMTP command that does not `include' a host name
464_OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
465
466# Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
467_OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
468
469# delimiter (operator) characters (old $o macro)
470_OPTION(OperatorChars, `confOPERATORS', `.:@[]')
471
472# shall I avoid calling initgroups(3) because of high NIS costs?
473_OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
474
475# are group-writable `:include:' and .forward files (un)trustworthy?
476_OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
477
478# where do errors that occur when sending errors get sent?
479_OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
480
481# where to save bounces if all else fails
482_OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
483
484# what user id do we assume for the majority of the processing?
485_OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
486
487# maximum number of recipients per SMTP envelope
488_OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `100')
489
490# shall we get local names from our installed interfaces?
491_OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
492
493# Return-Receipt-To: header implies DSN request
494_OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
495
496# override connection address (for testing)
497_OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
498
499# Trusted user for file ownership and starting the daemon
500_OPTION(TrustedUser, `confTRUSTED_USER', `root')
501
502# Control socket for daemon management
503_OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
504
505# Maximum MIME header length to protect MUAs
506_OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
507
508# Maximum length of the sum of all headers
509_OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
510
511# Maximum depth of alias recursion
512_OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
513
514# location of pid file
515_OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
516
517# Prefix string for the process title shown on 'ps' listings
518_OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
519
520# Data file (df) memory-buffer file maximum size
521_OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
522
523# Transcript file (xf) memory-buffer file maximum size
524_OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
525
526# list of authentication mechanisms
527_OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
528
529# default authentication information for outgoing connections
530_OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
531
532# SMTP AUTH flags
533_OPTION(AuthOptions, `confAUTH_OPTIONS', `')
534
535ifdef(`_FFR_MILTER', `
536# Input mail filters
537_OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
538
539# Milter options
540_OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
541_OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
542_OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
543_OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')')
544
545# CA directory
546_OPTION(CACERTPath, `confCACERT_PATH', `')
547# CA file
548_OPTION(CACERTFile, `confCACERT', `')
549# Server Cert
550_OPTION(ServerCertFile, `confSERVER_CERT', `')
551# Server private key
552_OPTION(ServerKeyFile, `confSERVER_KEY', `')
553# Client Cert
554_OPTION(ClientCertFile, `confCLIENT_CERT', `')
555# Client private key
556_OPTION(ClientKeyFile, `confCLIENT_KEY', `')
557# DHParameters (only required if DSA/DH is used)
558_OPTION(DHParameters, `confDH_PARAMETERS', `')
559# Random data source (required for systems without /dev/urandom under OpenSSL)
560_OPTION(RandFile, `confRAND_FILE', `')
561
562ifdef(`confQUEUE_FILE_MODE',
563`# queue file mode (qf files)
564O QueueFileMode=confQUEUE_FILE_MODE
565')
566
567###########################
568#   Message precedences   #
569###########################
570
571Pfirst-class=0
572Pspecial-delivery=100
573Plist=-30
574Pbulk=-60
575Pjunk=-100
576
577#####################
578#   Trusted users   #
579#####################
580
581# this is equivalent to setting class "t"
582ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
583Troot
584Tdaemon
585ifdef(`_NO_UUCP_', `dnl', `Tuucp')
586ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
587
588#########################
589#   Format of headers   #
590#########################
591
592ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
593H?P?Return-Path: <$g>
594HReceived: confRECEIVED_HEADER
595H?D?Resent-Date: $a
596H?D?Date: $a
597H?F?Resent-From: confFROM_HEADER
598H?F?From: confFROM_HEADER
599H?x?Full-Name: $x
600# HPosted-Date: $a
601# H?l?Received-Date: $b
602H?M?Resent-Message-Id: <$t.$i@$j>
603H?M?Message-Id: <$t.$i@$j>
604
605#
606######################################################################
607######################################################################
608#####
609#####			REWRITING RULES
610#####
611######################################################################
612######################################################################
613
614############################################
615###  Ruleset 3 -- Name Canonicalization  ###
616############################################
617Scanonify=3
618
619# handle null input (translate to <@> special case)
620R$@			$@ <@>
621
622# strip group: syntax (not inside angle brackets!) and trailing semicolon
623R$*			$: $1 <@>			mark addresses
624R$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
625R@ $* <@>		$: @ $1				unmark @host:...
626R$* :: $* <@>		$: $1 :: $2			unmark node::addr
627R:`include': $* <@>	$: :`include': $1			unmark :`include':...
628R$* [ IPv6 $- ] <@>	$: $1 [ IPv6 $2 ]		unmark IPv6 addr
629R$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
630R$* : $* <@>		$: $2				strip colon if marked
631R$* <@>			$: $1				unmark
632R$* ;			   $1				strip trailing semi
633R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
634
635# null input now results from list:; syntax
636R$@			$@ :; <@>
637
638# strip angle brackets -- note RFC733 heuristic to get innermost item
639R$*			$: < $1 >			housekeeping <>
640R$+ < $* >		   < $2 >			strip excess on left
641R< $* > $+		   < $1 >			strip excess on right
642R<>			$@ < @ >			MAIL FROM:<> case
643R< $+ >			$: $1				remove housekeeping <>
644
645ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
646# make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
647R@ $+ , $+		@ $1 : $2			change all "," to ":"
648
649# localize and dispose of route-based addresses
650R@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
651dnl',`dnl
652# strip route address <@a,@b,@c:user@d> -> <user@d>
653R@ $+ , $+		$2
654R@ $+ : $+		$2
655dnl')
656
657# find focus for list syntax
658R $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
659R $+ : $* ;		$@ $1 : $2;			list syntax
660
661# find focus for @ syntax addresses
662R$+ @ $+		$: $1 < @ $2 >			focus on domain
663R$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
664R$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
665
666# do some sanity checking
667R$* < @ $* : $* > $*	$1 < @ $2 $3 > $4		nix colons in addrs
668
669ifdef(`_NO_UUCP_', `dnl',
670`# convert old-style addresses to a domain-based address
671R$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
672R$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
673R$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
674')
675ifdef(`_USE_DECNET_SYNTAX_',
676`# convert node::user addresses into a domain-based address
677R$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
678R$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
679',
680	`dnl')
681# if we have % signs, take the rightmost one
682R$* % $*		$1 @ $2				First make them all @s.
683R$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
684R$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
685
686# else we must be a local name
687R$*			$@ $>Canonify2 $1
688
689
690################################################
691###  Ruleset 96 -- bottom half of ruleset 3  ###
692################################################
693
694SCanonify2=96
695
696# handle special cases for local names
697R$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
698R$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
699ifdef(`_NO_UUCP_', `dnl',
700`R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
701
702# check for IPv6 domain literal (save quoted form)
703R$* < @ [ IPv6 $- ] > $*	$: $2 $| $1 < @@ [ $(dequote $2 $) ] > $3	mark IPv6 addr
704R$- $| $* < @@ $=w > $*		$: $2 < @ $j . > $4		self-literal
705R$- $| $* < @@ [ $+ ] > $*	$@ $2 < @ [ IPv6 $1 ] > $4	canon IP addr
706
707# check for IPv4 domain literal
708R$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [a.b.c.d]
709R$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
710R$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
711
712ifdef(`_DOMAIN_TABLE_', `dnl
713# look up domains in the domain table
714R$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
715
716undivert(2)dnl LOCAL_RULE_3
717
718ifdef(`_BITDOMAIN_TABLE_', `dnl
719# handle BITNET mapping
720R$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
721
722ifdef(`_UUDOMAIN_TABLE_', `dnl
723# handle UUCP mapping
724R$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
725
726ifdef(`_NO_UUCP_', `dnl',
727`ifdef(`UUCP_RELAY',
728`# pass UUCP addresses straight through
729R$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
730`# if really UUCP, handle it immediately
731ifdef(`_CLASS_U_',
732`R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
733ifdef(`_CLASS_V_',
734`R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
735ifdef(`_CLASS_W_',
736`R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
737ifdef(`_CLASS_X_',
738`R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
739ifdef(`_CLASS_Y_',
740`R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
741
742ifdef(`_NO_CANONIFY_', `dnl', `dnl
743# try UUCP traffic as a local address
744R$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
745R$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
746')')
747# hostnames ending in class P are always canonical
748R$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
749dnl apply the next rule only for hostnames not in class P
750dnl this even works for phrases in class P since . is in class P
751dnl which daemon flags are set?
752R$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
753dnl the other rules in this section only apply if the hostname
754dnl does not end in class P hence no further checks are done here
755dnl if this ever changes make sure the lookups are "protected" again!
756ifdef(`_NO_CANONIFY_', `dnl
757dnl do not canonify unless:
758dnl domain ends in class {Canonify} (this does not work if the intersection
759dnl	with class P is non-empty)
760dnl or {daemon_flags} has c set
761# pass to name server to make hostname canonical if in class {Canonify}
762R$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
763# pass to name server to make hostname canonical if requested
764R$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
765dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
766R$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
767# add a trailing dot to qualified hostnames so other rules will work
768R$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
769ifdef(`_CANONIFY_HOSTS_', `dnl
770dnl this should only apply to unqualified hostnames
771dnl but if a valid character inside an unqualified hostname is an OperatorChar
772dnl then $- does not work.
773# lookup unqualified hostnames
774R$* $| $* < @ $* > $*	$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
775dnl _NO_CANONIFY_ is not set: canonify unless:
776dnl {daemon_flags} contains CC (do not canonify)
777R$* CC $* $| $*			$: $3
778# pass to name server to make hostname canonical
779R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
780dnl remove {daemon_flags} for other cases
781R$* $| $*			$: $2
782
783# local host aliases and pseudo-domains are always canonical
784R$* < @ $=w > $*		$: $1 < @ $2 . > $3
785ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
786`R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
787`R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
788ifdef(`_VIRTUSER_TABLE_', `dnl
789dnl virtual hosts are also canonical
790ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
791`R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
792`R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
793`dnl')
794dnl remove superfluous dots (maybe repeatedly) which may have been added
795dnl by one of the rules before
796R$* < @ $* . . > $*		$1 < @ $2 . > $3
797
798
799##################################################
800###  Ruleset 4 -- Final Output Post-rewriting  ###
801##################################################
802Sfinal=4
803
804R$* <@>			$@				handle <> and list:;
805
806# strip trailing dot off possibly canonical name
807R$* < @ $+ . > $*	$1 < @ $2 > $3
808
809# eliminate internal code
810R$* < @ *LOCAL* > $*	$1 < @ $j > $2
811
812# externalize local domain info
813R$* < $+ > $*		$1 $2 $3			defocus
814R@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
815R@ $*			$@ @ $1				... and exit
816
817ifdef(`_NO_UUCP_', `dnl',
818`# UUCP must always be presented in old form
819R$+ @ $- . UUCP		$2!$1				u@h.UUCP => h!u')
820
821ifdef(`_USE_DECNET_SYNTAX_',
822`# put DECnet back in :: form
823R$+ @ $+ . DECNET	$2 :: $1			u@h.DECNET => h::u',
824	`dnl')
825# delete duplicate local names
826R$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
827
828
829
830##############################################################
831###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
832###		   (used for recursive calls)		   ###
833##############################################################
834
835SRecurse=97
836R$*			$: $>canonify $1
837R$*			$@ $>parse $1
838
839
840######################################
841###   Ruleset 0 -- Parse Address   ###
842######################################
843
844Sparse=0
845
846R$*			$: $>Parse0 $1		initial parsing
847R<@>			$#_LOCAL_ $: <@>		special case error msgs
848R$*			$: $>ParseLocal $1	handle local hacks
849R$*			$: $>Parse1 $1		final parsing
850
851#
852#  Parse0 -- do initial syntax checking and eliminate local addresses.
853#	This should either return with the (possibly modified) input
854#	or return with a #error mailer.  It should not return with a
855#	#mailer other than the #error mailer.
856#
857
858SParse0
859R<@>			$@ <@>			special case error msgs
860R$* : $* ; <@>		$#error $@ 5.1.3 $: "553 List:; syntax illegal for recipient addresses"
861R@ <@ $* >		< @ $1 >		catch "@@host" bogosity
862R<@ $+>			$#error $@ 5.1.3 $: "553 User address required"
863R$*			$: <> $1
864R<> $* < @ [ $+ ] > $*	$1 < @ [ $2 ] > $3
865R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "553 Colon illegal in host name part"
866R<> $*			$1
867R$* < @ . $* > $*	$#error $@ 5.1.2 $: "553 Invalid host name"
868R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "553 Invalid host name"
869dnl comma only allowed before @; this check is not complete
870R$* , $~O $*		$#error $@ 5.1.2 $: "553 Invalid route address"
871
872# now delete the local info -- note $=O to find characters that cause forwarding
873R$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
874R< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
875R$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
876R< @ $+ >		$#error $@ 5.1.3 $: "553 User address required"
877R$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
878R$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
879R< @ *LOCAL* >		$#error $@ 5.1.3 $: "553 User address required"
880R$* $=O $* < @ *LOCAL* >
881			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
882R$* < @ *LOCAL* >	$: $1
883
884#
885#  Parse1 -- the bottom half of ruleset 0.
886#
887
888SParse1
889ifdef(`_LDAP_ROUTING_', `dnl
890# handle LDAP routing for hosts in $={LDAPRoute}
891R$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2>',
892`dnl')
893
894
895ifdef(`_MAILER_smtp_',
896`# handle numeric address spec
897dnl there is no check whether this is really an IP number
898R$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
899R$* < @ [ $+ ] > $*	$1 < @ [ $2 ] : $S > $3		Add smart host to path
900R$* < @ [ IPv6 $- ] : > $*
901		$#_SMTP_ $@ [ $(dequote $2 $) ] $: $1 < @ [IPv6 $2 ] > $3	no smarthost: send
902R$* < @ [ $+ ] : > $*	$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
903R$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
904R$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
905	`dnl')
906
907ifdef(`_VIRTUSER_TABLE_', `dnl
908# handle virtual users
909R$+			$: <!> $1		Mark for lookup
910ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
911`R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
912`R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
913R<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
914R<@> $+ + $* < @ $* . >
915			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . >
916R<@> $+ + $* < @ $* . >
917			$: < $(virtuser $1 @ $3 $@ $1 $: @ $) > $1 + $2 < @ $3 . >
918dnl try default entry: @domain
919dnl +*@domain
920R<@> $+ + $+ < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . >
921dnl @domain if +detail exists
922R<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $: @ $) > $1 + $2 < @ $3 . >
923dnl without +detail (or no match)
924R<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
925R<@> $+			$: $1
926R<!> $+			$: $1
927R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
928R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
929R< $+ > $+ < @ $+ >	$: $>Recurse $1',
930`dnl')
931
932# short circuit local delivery so forwarded email works
933ifdef(`_MAILER_usenet_', `dnl
934R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
935ifdef(`_STICKY_LOCAL_DOMAIN_',
936`R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
937R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
938dnl $H empty (but @$=w.)
939R< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
940R< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
941`R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
942R$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
943
944ifdef(`_MAILER_TABLE_', `dnl
945# not local -- try mailer table lookup
946R$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
947R< $+ . > $*		$: < $1 > $2			strip trailing dot
948R< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
949dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
950R< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
951R< $+ > $*		$: $>Mailertable <$1> $2		try domain',
952`dnl')
953undivert(4)dnl UUCP rules from `MAILER(uucp)'
954
955ifdef(`_NO_UUCP_', `dnl',
956`# resolve remotely connected UUCP links (if any)
957ifdef(`_CLASS_V_',
958`R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
959	`dnl')
960ifdef(`_CLASS_W_',
961`R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
962	`dnl')
963ifdef(`_CLASS_X_',
964`R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
965	`dnl')')
966
967# resolve fake top level domains by forwarding to other hosts
968ifdef(`BITNET_RELAY',
969`R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user@host.BITNET',
970	`dnl')
971ifdef(`DECNET_RELAY',
972`R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user@host.DECNET',
973	`dnl')
974ifdef(`_MAILER_pop_',
975`R$+ < @ POP. >		$#pop $: $1			user@POP',
976	`dnl')
977ifdef(`_MAILER_fax_',
978`R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user@host.FAX',
979`ifdef(`FAX_RELAY',
980`R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user@host.FAX',
981	`dnl')')
982
983ifdef(`UUCP_RELAY',
984`# forward non-local UUCP traffic to our UUCP relay
985R$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
986`ifdef(`_MAILER_uucp_',
987`# forward other UUCP traffic straight to UUCP
988R$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user@host.UUCP',
989	`dnl')')
990ifdef(`_MAILER_usenet_', `
991# addresses sent to net.group.USENET will get forwarded to a newsgroup
992R$+ . USENET		$#usenet $@ usenet $: $1',
993	`dnl')
994
995ifdef(`_LOCAL_RULES_',
996`# figure out what should stay in our local mail system
997undivert(1)', `dnl')
998
999# pass names that still have a host to a smarthost (if defined)
1000R$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
1001
1002# deal with other remote names
1003ifdef(`_MAILER_smtp_',
1004`R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user@host.domain',
1005`R$* < @$* > $*		$#error $@ 5.1.2 $: "553 Unrecognized host name " $2')
1006
1007# handle locally delivered names
1008R$=L			$#_LOCAL_ $: @ $1		special local names
1009R$+			$#_LOCAL_ $: $1			regular local names
1010
1011###########################################################################
1012###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
1013###########################################################################
1014
1015SLocal_localaddr
1016Slocaladdr=5
1017R$+			$: $1 $| $>"Local_localaddr" $1
1018R$+ $| $#$*		$#$2
1019R$+ $| $*		$: $1
1020
1021# deal with plussed users so aliases work nicely
1022R$+ + *			$#_LOCAL_ $@ $&h $: $1
1023R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *
1024
1025# prepend an empty "forward host" on the front
1026R$+			$: <> $1
1027
1028ifdef(`LUSER_RELAY', `dnl
1029# send unrecognized local users to a relay host
1030R< > $+ 		$: < $L > $(user $1 $)		look up user
1031R< $* > $+ <>		$: < > $2			found; strip $L',
1032`dnl')
1033
1034# see if we have a relay or a hub
1035R< > $+			$: < $H > $1			try hub
1036R< > $+			$: < $R > $1			try relay
1037R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
1038R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
1039R< > < $+ <> $* >	$: < > < $1 >			else discard
1040R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
1041R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1		strip the extra +
1042R< > < $+ >		$@ $1				no +detail
1043R$+			$: $1 <> $&h			add +detail back in
1044R$+ <> + $*		$: $1 + $2			check whether +detail
1045R$+ <> $*		$: $1				else discard
1046R< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
1047R< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
1048R< $- : $+ > $+		$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1049R< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
1050
1051ifdef(`_MAILER_TABLE_', `dnl
1052###################################################################
1053###  Ruleset 90 -- try domain part of mailertable entry 	###
1054dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1055###################################################################
1056
1057SMailertable=90
1058dnl shift and check
1059dnl %2 is not documented in cf/README
1060R$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1061dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1062R$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
1063R$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
1064dnl is $2 always empty?
1065R$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
1066R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
1067dnl return full address
1068R< $* > $*		$@ $2				no mailertable match',
1069`dnl')
1070
1071###################################################################
1072###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
1073dnl input: in general: <[mailer:]host> lp<@domain>rest
1074dnl	<> address				-> address
1075dnl	<error:d.s.n:text>			-> error
1076dnl	<error:text>				-> error
1077dnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
1078dnl	<mailer:host> address			-> mailer host address
1079dnl	<localdomain> address			-> address
1080dnl	<[IPv6 number]> address			-> relay number address
1081dnl	<host> address				-> relay host address
1082###################################################################
1083
1084SMailerToTriple=95
1085R< > $*				$@ $1			strip off null relay
1086R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
1087R< error : $- $+ > $*		$#error $@ $(dequote $1 $) $: $2
1088R< local : $* > $*		$>CanonLocal < $1 > $2
1089R< $- : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
1090R< $- : $+ > $*			$# $1 $@ $2 $: $3	try qualified mailer
1091R< $=w > $*			$@ $2			delete local host
1092R< [ IPv6 $+ ] > $*		$#_RELAY_ $@ $(dequote $1 $) $: $2	use unqualified mailer
1093R< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
1094
1095###################################################################
1096###  Ruleset CanonLocal -- canonify local: syntax		###
1097dnl input: <user> address
1098dnl <x> <@host> : rest			-> Recurse rest
1099dnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
1100dnl <> user <@host> rest		-> local user@host user
1101dnl <> user				-> local user user
1102dnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
1103dnl <user> lp <@host> rest		-> local lp@host user
1104dnl <user> lp				-> local lp user
1105###################################################################
1106
1107SCanonLocal
1108# strip local host from routed addresses
1109R< $* > < @ $+ > : $+		$@ $>Recurse $3
1110R< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
1111
1112# strip trailing dot from any host name that may appear
1113R< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
1114
1115# handle local: syntax -- use old user, either with or without host
1116R< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
1117R< > $+				$#_LOCAL_ $@ $1    $: $1
1118
1119# handle local:user@host syntax -- ignore host part
1120R< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
1121
1122# handle local:user syntax
1123R< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
1124R< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
1125
1126###################################################################
1127###  Ruleset 93 -- convert header names to masqueraded form	###
1128###################################################################
1129
1130SMasqHdr=93
1131
1132ifdef(`_GENERICS_TABLE_', `dnl
1133# handle generics database
1134ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1135dnl if generics should be applied add a @ as mark
1136`R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
1137`R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
1138R$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
1139dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1140dnl ignore the first case for now
1141dnl if it has the mark lookup full address
1142R< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
1143dnl workspace: ... or <match|@user@domain> user <@domain>
1144dnl no match, try user+detail@domain
1145R<@$+ + $* @ $+> $+ < @ $+ >
1146		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
1147R<@$+ + $* @ $+> $+ < @ $+ >
1148		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
1149dnl no match, remove mark
1150R<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
1151dnl no match, try @domain for exceptions
1152R< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1153dnl workspace: ... or <match> user <@domain>
1154dnl no match, try local part
1155R< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
1156R< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1157R< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1158R< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
1159R< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
1160R< > $*			$: $1				not found',
1161`dnl')
1162
1163# do not masquerade anything in class N
1164R$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
1165
1166# special case the users that should be exposed
1167R$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
1168ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1169`R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
1170`R$=E < @ $=M . >	$@ $1 < @ $2 . >')
1171ifdef(`_LIMITED_MASQUERADE_', `dnl',
1172`R$=E < @ $=w . >	$@ $1 < @ $2 . >')
1173
1174# handle domain-specific masquerading
1175ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1176`R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
1177`R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
1178ifdef(`_LIMITED_MASQUERADE_', `dnl',
1179`R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
1180R$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
1181R$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
1182R$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
1183
1184###################################################################
1185###  Ruleset 94 -- convert envelope names to masqueraded form	###
1186###################################################################
1187
1188SMasqEnv=94
1189ifdef(`_MASQUERADE_ENVELOPE_',
1190`R$+			$@ $>MasqHdr $1',
1191`R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
1192
1193###################################################################
1194###  Ruleset 98 -- local part of ruleset zero (can be null)	###
1195###################################################################
1196
1197SParseLocal=98
1198undivert(3)dnl LOCAL_RULE_0
1199
1200ifdef(`_LDAP_ROUTING_', `dnl
1201SLDAPExpand
1202# do the LDAP lookups
1203R<$+><$+>		$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2>
1204
1205# if mailRoutingAddress and local or non-existant mailHost,
1206# return the new mailRoutingAddress
1207R< $+ > < $=w > < $+ > < $+ >	$@ $>Parse0 $>canonify $1
1208R< $+ > <  > < $+ > < $+ >	$@ $>Parse0 $>canonify $1
1209
1210# if mailRoutingAddress and non-local mailHost,
1211# relay to mailHost with new mailRoutingAddress
1212R< $+ > < $+ > < $+ > < $+ >	$#_RELAY_ $@ $2 $: $>canonify $1
1213
1214# if no mailRoutingAddress and local mailHost,
1215# return original address
1216R< > < $=w > <$+> <$+>		$@ $2
1217
1218# if no mailRoutingAddress and non-local mailHost,
1219# relay to mailHost with original address
1220R< > < $+ > <$+> <$+>		$#_RELAY_ $@ $1 $: $2
1221
1222# if no mailRoutingAddress and no mailHost,
1223# try @domain
1224R< > < > <$+> <$+ @ $+>		$@ $>LDAPExpand <$1> <@ $3>
1225
1226# if no mailRoutingAddress and no mailHost and this was a domain attempt,
1227ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1228# user does not exist
1229R< > < > <$+> <@ $+>		$#error $@ nouser $: "550 User unknown"',
1230`dnl
1231# return the original address
1232R< > < > <$+> <@ $+>		$@ $1')',
1233`dnl')
1234
1235ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1236')')
1237ifdef(`_ACCESS_TABLE_', `dnl
1238######################################################################
1239###  LookUpDomain -- search for domain in access database
1240###
1241###	Parameters:
1242###		<$1> -- key (domain name)
1243###		<$2> -- default (what to return if not found in db)
1244dnl			must not be empty
1245###		<$3> -- passthru (additional data passed unchanged through)
1246###		<$4> -- mark (must be <(!|+) single-token>)
1247###			! does lookup only with tag
1248###			+ does lookup with and without tag
1249dnl returns:		<default> <passthru>
1250dnl 			<result> <passthru>
1251######################################################################
1252
1253SLookUpDomain
1254dnl remove IPv6 mark and dequote address
1255dnl it is a bit ugly because it is checked on each "iteration"
1256R<[IPv6 $-]> <$+> <$*> <$*>	$: <[$(dequote $1 $)]> <$2> <$3> <$4>
1257dnl workspace <key> <default> <passthru> <mark>
1258dnl lookup with tag (in front, no delimiter here)
1259R<$*> <$+> <$*> <$- $->		$: < $(access $5`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3> <$4 $5>
1260dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1261ifdef(`_FFR_LOOKUPDOTDOMAIN', `dnl omit first component: lookup .rest
1262R<?> <$+.$+> <$+> <$*> <$- $->	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4> <$5 $6>', `dnl')
1263dnl lookup without tag?
1264R<?> <$+> <$+> <$*> <+ $*>	$: < $(access $1 $: ? $) > <$1> <$2> <$3> <+ $4>
1265ifdef(`_FFR_LOOKUPDOTDOMAIN', `dnl omit first component: lookup .rest
1266R<?> <$+.$+> <$+> <$*> <+ $*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <$4> <+ $5>', `dnl')
1267dnl lookup IP address (no check is done whether it is an IP number!)
1268R<?> <[$+.$-]> <$+> <$*> <$*>	$@ $>LookUpDomain <[$1]> <$3> <$4> <$5>
1269dnl lookup IPv6 address
1270R<?> <[$+:$-]> <$+> <$*> <$*>	$: $>LookUpDomain <[$1]> <$3> <$4> <$5>
1271dnl not found, but subdomain: try again
1272R<?> <$+.$+> <$+> <$*> <$*>	$@ $>LookUpDomain <$2> <$3> <$4> <$5>
1273dnl not found, no subdomain: return default
1274R<?> <$+> <$+> <$*> <$*>	$@ <$2> <$3>
1275dnl return result of lookup
1276R<$*> <$+> <$+> <$*> <$*>	$@ <$1> <$4>
1277
1278######################################################################
1279###  LookUpAddress -- search for host address in access database
1280###
1281###	Parameters:
1282###		<$1> -- key (dot quadded host address)
1283###		<$2> -- default (what to return if not found in db)
1284dnl			must not be empty
1285###		<$3> -- passthru (additional data passed through)
1286###		<$4> -- mark (must be <(!|+) single-token>)
1287###			! does lookup only with tag
1288###			+ does lookup with and without tag
1289dnl	returns:	<default> <passthru>
1290dnl			<result> <passthru>
1291######################################################################
1292
1293SLookUpAddress
1294dnl lookup with tag
1295R<$+> <$+> <$*> <$- $+>		$: < $(access $5`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3> <$4 $5>
1296dnl lookup without tag
1297R<?> <$+> <$+> <$*> <+ $+>	$: < $(access $1 $: ? $) > <$1> <$2> <$3> <+ $4>
1298dnl no match; IPv6: remove last part
1299R<?> <$+:$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
1300dnl no match; IPv4: remove last part
1301R<?> <$+.$-> <$+> <$*> <$*>	$@ $>LookUpAddress <$1> <$3> <$4> <$5>
1302dnl no match: return default
1303R<?> <$+> <$+> <$*> <$*>	$@ <$2> <$3>
1304dnl match: return result
1305R<$*> <$+> <$+> <$*> <$*>	$@ <$1> <$4>',
1306`dnl')
1307
1308######################################################################
1309###  CanonAddr --	Convert an address into a standard form for
1310###			relay checking.  Route address syntax is
1311###			crudely converted into a %-hack address.
1312###
1313###	Parameters:
1314###		$1 -- full recipient address
1315###
1316###	Returns:
1317###		parsed address, not in source route form
1318dnl		user%host%host<@domain>
1319dnl		host!user<@domain>
1320######################################################################
1321
1322SCanonAddr
1323R$*			$: $>Parse0 $>canonify $1	make domain canonical
1324ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1325R< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
1326R$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
1327R$* < @ $+ > : $*	$3 $1 < @ $2 >
1328dnl')
1329
1330######################################################################
1331###  ParseRecipient --	Strip off hosts in $=R as well as possibly
1332###			$* $=m or the access database.
1333###			Check user portion for host separators.
1334###
1335###	Parameters:
1336###		$1 -- full recipient address
1337###
1338###	Returns:
1339###		parsed, non-local-relaying address
1340######################################################################
1341
1342SParseRecipient
1343dnl mark and canonify address
1344R$*				$: <?> $>CanonAddr $1
1345dnl workspace: <?> localpart<@domain[.]>
1346R<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
1347dnl workspace: <?> localpart<@domain>
1348R<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
1349
1350# if no $=O character, no host in the user portion, we are done
1351R<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
1352dnl no $=O in localpart: return
1353R<?> $*				$@ $1
1354
1355dnl workspace: <?> localpart<@domain>, where localpart contains $=O
1356dnl mark everything which has an "authorized" domain with <RELAY>
1357ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1358# if we relay, check username portion for user%host so host can be checked also
1359R<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
1360
1361ifdef(`_RELAY_MX_SERVED_', `dnl
1362dnl do "we" ($=w) act as backup MX server for the destination domain?
1363R<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1364R<MX> < : $* <TEMP> : > $*	$#error $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
1365dnl yes: mark it as <RELAY>
1366R<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
1367dnl no: put old <NO> mark back
1368R<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
1369
1370dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1371dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1372ifdef(`_RELAY_HOSTS_ONLY_',
1373`R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
1374ifdef(`_ACCESS_TABLE_', `dnl
1375R<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
1376R<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1377`R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
1378ifdef(`_ACCESS_TABLE_', `dnl
1379R<NO> $* < @ $+ >		$: $>LookUpDomain <$2> <NO> <$1 < @ $2 >> <+To>
1380R<$+> <$+>			$: <$1> $2',`dnl')')
1381
1382
1383R<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
1384R<$-> $*			$@ $2
1385
1386
1387######################################################################
1388###  check_relay -- check hostname/address on SMTP startup
1389######################################################################
1390
1391SLocal_check_relay
1392Scheck`'_U_`'relay
1393R$*			$: $1 $| $>"Local_check_relay" $1
1394R$* $| $* $| $#$*	$#$3
1395R$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
1396
1397SBasic_check_relay
1398# check for deferred delivery mode
1399R$*			$: < ${deliveryMode} > $1
1400R< d > $*		$@ deferred
1401R< $* > $*		$: $2
1402
1403ifdef(`_ACCESS_TABLE_', `dnl
1404R$+ $| $+		$: $>LookUpDomain < $1 > <?> < $2 > <+Connect>
1405R<?> <$+>		$: $>LookUpAddress < $1 > <?> < $1 > <+Connect>	no: another lookup
1406R<?> < $+ >		$: $1					found nothing
1407R<$={Accept}> < $* >	$@ $1
1408R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1409R<DISCARD> $*		$#discard $: discard
1410dnl error tag
1411R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1412R<ERROR:$+> $*		$#error $: $1
1413dnl generic error from access map
1414R<$+> $*		$#error $: $1', `dnl')
1415
1416ifdef(`_RBL_',`dnl
1417# DNS based IP address spam list
1418R$*			$: $&{client_addr}
1419R::ffff:$-.$-.$-.$-	$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1420R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1421R<?>OK			$: OKSOFAR
1422R<?>$+			$#error $@ 5.7.1 $: "550 Mail from " $&{client_addr} " refused by blackhole site _RBL_"',
1423`dnl')
1424undivert(8)
1425
1426######################################################################
1427###  check_mail -- check SMTP ``MAIL FROM:'' command argument
1428######################################################################
1429
1430SLocal_check_mail
1431Scheck`'_U_`'mail
1432R$*			$: $1 $| $>"Local_check_mail" $1
1433R$* $| $#$*		$#$2
1434R$* $| $*		$@ $>"Basic_check_mail" $1
1435
1436SBasic_check_mail
1437# check for deferred delivery mode
1438R$*			$: < ${deliveryMode} > $1
1439R< d > $*		$@ deferred
1440R< $* > $*		$: $2
1441
1442# authenticated?
1443dnl done first: we can require authentication for every mail transaction
1444dnl workspace: address as given by MAIL FROM: (sender)
1445R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
1446R$* $| $#$+		$#$2
1447dnl undo damage: remove result of tls_client call
1448R$* $| $*		$: $1
1449
1450dnl workspace: address as given by MAIL FROM:
1451R<>			$@ <OK>			we MUST accept <> (RFC 1123)
1452ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1453dnl do some additional checks
1454dnl no user@host
1455dnl no user@localhost (if nonlocal sender)
1456dnl this is a pretty simple canonification, it will not catch every case
1457dnl just make sure the address has <> around it (which is required by
1458dnl the RFC anyway, maybe we should complain if they are missing...)
1459dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1460dnl not be modified by host lookups.
1461R$+			$: <?> $1
1462R<?><$+>		$: <@> <$1>
1463R<?>$+			$: <@> <$1>
1464dnl workspace: <@> <address>
1465dnl prepend daemon_flags
1466R$*			$: $&{daemon_flags} $| $1
1467dnl workspace: ${daemon_flags} $| <@> <address>
1468dnl do not allow these at all or only from local systems?
1469R$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1470dnl accept unqualified sender: change mark to avoid test
1471R$* u $* $| <@> < $* >	$: <?> < $3 >
1472dnl workspace: ${daemon_flags} $| <@> <address>
1473dnl        or:                    <? ${client_name} > <address>
1474dnl        or:                    <?> <address>
1475dnl remove daemon_flags
1476R$* $| $*		$: $2
1477# handle case of @localhost on address
1478R<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
1479R<@> < $* @ [127.0.0.1] >
1480			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1481R<@> < $* @ localhost.$m >
1482			$: < ? $&{client_name} > < $1 @ localhost.$m >
1483ifdef(`_NO_UUCP_', `dnl',
1484`R<@> < $* @ localhost.UUCP >
1485			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1486dnl workspace: < ? $&{client_name} > <user@localhost|host>
1487dnl	or:    <@> <address>
1488dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1489R<@> $*			$: $1			no localhost as domain
1490dnl workspace: < ? $&{client_name} > <user@localhost|host>
1491dnl	or:    <address>
1492dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
1493R<? $=w> $*		$: $2			local client: ok
1494R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Real domain name required"
1495dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1496R<?> $*			$: $1')
1497dnl workspace: address (or <address>)
1498R$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
1499dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1500dnl there is nothing behind the <@host> so no trailing $* needed
1501R<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
1502# handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1503R<?> $* < @ $* $=P >	$: <OK> $1 < @ $2 $3 >
1504dnl workspace <mark> CanonicalAddress	where mark is ? or OK
1505ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1506`R<?> $* < @ $+ >	$: <OK> $1 < @ $2 >		... unresolvable OK',
1507`R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1508R<? $* <$->> $* < @ $+ >
1509			$: <$2> $3 < @ $4 >')
1510dnl workspace <mark> CanonicalAddress	where mark is ?, OK, PERM, TEMP
1511dnl mark is ? iff the address is user (wo @domain)
1512
1513ifdef(`_ACCESS_TABLE_', `dnl
1514# check sender address: user@address, user@, address
1515dnl should we remove +ext from user?
1516dnl workspace: <mark> CanonicalAddress where mark is: ?, OK, PERM, TEMP
1517R<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <H:$3>
1518R<$+> $+		$: @<$1> <$2> $| <U:$2@>
1519dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1520dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1521dnl will only return user<@domain when "reversing" the args
1522R@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+From> $| <$3> <>
1523dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1524R<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
1525dnl workspace: <result> <mark> <CanonicalAddress>
1526# retransform for further use
1527dnl required form:
1528dnl <ResultOfLookup|mark> CanonicalAddress
1529R<?> <$+> <$*>		$: <$1> $2	no match
1530R<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
1531dnl workspace <ResultOfLookup|mark> CanonicalAddress
1532dnl mark is ? iff the address is user (wo @domain)
1533
1534ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1535# handle case of no @domain on address
1536dnl prepend daemon_flags
1537R<?> $*			$: $&{daemon_flags} $| <?> $1
1538dnl accept unqualified sender: change mark to avoid test
1539R$* u $* $| <?> $*	$: <OK> $3
1540dnl remove daemon_flags
1541R$* $| $*		$: $2
1542R<?> $*			$: < ? $&{client_name} > $1
1543R<?> $*			$@ <OK>				...local unqualed ok
1544R<? $+> $*		$#error $@ 5.5.4 $: "553 Domain name required"
1545							...remote is not')
1546# check results
1547R<?> $*			$: @ $1		mark address: nothing known about it
1548R<OK> $*		$@ <OK>
1549R<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1550R<PERM> $*		$#error $@ 5.1.8 $: "501 Domain of sender address " $&f " does not exist"
1551ifdef(`_ACCESS_TABLE_', `dnl
1552R<$={Accept}> $*	$# $1
1553R<DISCARD> $*		$#discard $: discard
1554R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: "confREJECT_MSG"', `$@ 5.7.1 $: "550 Access denied"')
1555dnl error tag
1556R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1557R<ERROR:$+> $*		$#error $: $1
1558dnl generic error from access map
1559R<$+> $*		$#error $: $1		error from access db',
1560`dnl')
1561
1562######################################################################
1563###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
1564######################################################################
1565
1566SLocal_check_rcpt
1567Scheck`'_U_`'rcpt
1568R$*			$: $1 $| $>"Local_check_rcpt" $1
1569R$* $| $#$*		$#$2
1570R$* $| $*		$@ $>"Basic_check_rcpt" $1
1571
1572SBasic_check_rcpt
1573# check for deferred delivery mode
1574R$*			$: < ${deliveryMode} > $1
1575R< d > $*		$@ deferred
1576R< $* > $*		$: $2
1577
1578ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1579# require qualified recipient?
1580R$+			$: <?> $1
1581R<?><$+>		$: <@> <$1>
1582R<?>$+			$: <@> <$1>
1583dnl prepend daemon_flags
1584R$*			$: $&{daemon_flags} $| $1
1585dnl workspace: ${daemon_flags} $| <@> <address>
1586dnl do not allow these at all or only from local systems?
1587R$* r $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
1588R<?> < $* >		$: <$1>
1589R<? $=w> < $* >		$: <$1>
1590R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Domain name required"
1591dnl remove daemon_flags for other cases
1592R$* $| <@> $*		$: $2', `dnl')
1593
1594ifdef(`_LOOSE_RELAY_CHECK_',`dnl
1595R$*			$: $>CanonAddr $1
1596R$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
1597`R$*			$: $>ParseRecipient $1		strip relayable hosts')
1598
1599ifdef(`_BESTMX_IS_LOCAL_',`dnl
1600ifelse(_BESTMX_IS_LOCAL_, `', `dnl
1601# unlimited bestmx
1602R$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
1603`dnl
1604# limit bestmx to $=B
1605R$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
1606R$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Basic_check_rcpt" $1 $2 $3
1607R$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
1608R$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
1609
1610ifdef(`_BLACKLIST_RCPT_',`dnl
1611ifdef(`_ACCESS_TABLE_', `dnl
1612# blacklist local users or any host from receiving mail
1613R$*			$: <?> $1
1614dnl user is now tagged with @ to be consistent with check_mail
1615dnl and to distinguish users from hosts (com would be host, com@ would be user)
1616R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <H:$2>
1617R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <H:$2>
1618R<?> $+			$: <> <$1> $| <U:$1@>
1619dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1620dnl will only return user<@domain when "reversing" the args
1621R<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+To> $| <$2> <>
1622R<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
1623R<?> <$*>		$: @ $1		mark address as no match
1624R<$={Accept}> <$*>	$: @ $2		mark address as no match
1625ifdef(`_DELAY_CHECKS_',`dnl
1626dnl we have to filter these because otherwise they would be interpreted
1627dnl as generic error message...
1628dnl error messages should be "tagged" by prefixing them with error: !
1629dnl that would make a lot of things easier.
1630dnl maybe we should stop checks already here (if SPAM_xyx)?
1631R<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
1632R<REJECT> $*		$#error $@ 5.2.1 $: "550 Mailbox disabled for this recipient"
1633R<DISCARD> $*		$#discard $: discard
1634dnl error tag
1635R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
1636R<ERROR:$+> $*		$#error $: $1
1637dnl generic error from access map
1638R<$+> $*		$#error $: $1		error from access db
1639R@ $*			$1		remove mark', `dnl')', `dnl')
1640
1641ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)')
1642# authenticated?
1643dnl do this unconditionally? this requires to manage CAs carefully
1644dnl just because someone has a CERT signed by a "trusted" CA
1645dnl does not mean we want to allow relaying for her,
1646dnl either use a subroutine or provide something more sophisticated
1647dnl this could for example check the DN (maybe an access map lookup)
1648R$*		$: $1 $| $>RelayAuth $1 $| $&{verify}	client authenticated?
1649R$* $| $# $+		$# $2				error/ok?
1650R$* $| $*		$: $1				no
1651
1652# authenticated by a trusted mechanism?
1653R$*			$: $1 $| $&{auth_type}
1654dnl empty ${auth_type}?
1655R$* $|			$: $1
1656dnl mechanism ${auth_type} accepted?
1657dnl use $# to override further tests (delay_checks): see check_rcpt below
1658R$* $| $={TrustAuthMech}	$# RELAYAUTH
1659dnl undo addition of ${auth_type}
1660R$* $| $*		$: $1
1661dnl workspace: localpart<@domain>
1662ifelse(defn(`_NO_UUCP_'), `r',
1663`R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
1664# anything terminating locally is ok
1665ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1666R$+ < @ $* $=m >	$@ RELAYTO', `dnl')
1667R$+ < @ $=w >		$@ RELAYTO
1668ifdef(`_RELAY_HOSTS_ONLY_',
1669`R$+ < @ $=R >		$@ RELAYTO
1670ifdef(`_ACCESS_TABLE_', `dnl
1671R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>
1672dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
1673R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
1674`R$+ < @ $* $=R >	$@ RELAYTO
1675ifdef(`_ACCESS_TABLE_', `dnl
1676R$+ < @ $+ >		$: $>LookUpDomain <$2> <?> <$1 < @ $2 >> <+To>',`dnl')')
1677ifdef(`_ACCESS_TABLE_', `dnl
1678dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
1679R<RELAY> $*		$@ RELAYTO
1680R<$*> <$*>		$: $2',`dnl')
1681
1682
1683ifdef(`_RELAY_MX_SERVED_', `dnl
1684# allow relaying for hosts which we MX serve
1685R$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
1686dnl this must not necessarily happen if the client is checked first...
1687R< : $* <TEMP> : > $*	$#error $@ 4.7.1 $: "450 Can not check MX records for recipient host " $1
1688R<$* : $=w . : $*> $*	$@ RELAYTO
1689R< : $* : > $*		$: $2',
1690`dnl')
1691
1692# check for local user (i.e. unqualified address)
1693R$*			$: <?> $1
1694R<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
1695# local user is ok
1696dnl is it really? the standard requires user@domain, not just user
1697dnl but we should accept it anyway (maybe making it an option:
1698dnl RequireFQDN ?)
1699dnl postmaster must be accepted without domain (DRUMS)
1700ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
1701R<?> postmaster		$@ TOPOSTMASTER
1702# require qualified recipient?
1703dnl prepend daemon_flags
1704R<?> $+			$: $&{daemon_flags} $| <?> $1
1705dnl workspace: ${daemon_flags} $| <?> localpart
1706dnl do not allow these at all or only from local systems?
1707dnl r flag? add client_name
1708R$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
1709dnl no r flag: relay to local user (only local part)
1710# no qualified recipient required
1711R$* $| <?> $+		$@ RELAYTOLOCAL
1712dnl client_name is empty
1713R<?> <?> $+		$@ RELAYTOLOCAL
1714dnl client_name is local
1715R<? $=w> <?> $+		$@ RELAYTOLOCAL
1716dnl client_name is not local
1717R<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
1718dnl no qualified recipient required
1719R<?> $+			$@ RELAYTOLOCAL')
1720dnl it is a remote user: remove mark and then check client
1721R<$+> $*		$: $2
1722dnl currently the recipient address is not used below
1723
1724# anything originating locally is ok
1725# check IP address
1726R$*			$: $&{client_addr}
1727R$@			$@ RELAYFROM		originated locally
1728R0			$@ RELAYFROM		originated locally
1729R$=R $*			$@ RELAYFROM		relayable IP address
1730ifdef(`_ACCESS_TABLE_', `dnl
1731R$*			$: $>LookUpAddress <$1> <?> <$1> <+Connect>
1732R<RELAY> $* 		$@ RELAYFROM		relayable IP address
1733R<$*> <$*>		$: $2', `dnl')
1734R$*			$: [ $1 ]		put brackets around it...
1735R$=w			$@ RELAYFROM		... and see if it is local
1736
1737ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
1738ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
1739ifdef(`_RELAY_MAIL_FROM_', `dnl
1740dnl input: {client_addr} or something "broken"
1741dnl just throw the input away; we do not need it.
1742# check whether FROM is allowed to use system as relay
1743R$*			$: <?> $>CanonAddr $&f
1744ifdef(`_RELAY_LOCAL_FROM_', `dnl
1745# check whether local FROM is ok
1746R<?> $+ < @ $=w . >	$@ RELAYFROMMAIL	FROM local', `dnl')
1747ifdef(`_RELAY_DB_FROM_', `dnl
1748R<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
1749R<?> $+ < @ $+ >	$: $1 < @ $2 > $| $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', `<H:$2>') <>
1750R$* <RELAY>		$@ RELAYFROMMAIL	RELAY FROM sender ok', `dnl
1751ifdef(`_RELAY_DB_FROM_DOMAIN_', `errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
1752')',
1753`dnl')
1754dnl')', `dnl')
1755
1756# check client name: first: did it resolve?
1757dnl input: ignored
1758R$*			$: < $&{client_resolve} >
1759R<TEMP>			$#error $@ 4.7.1 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
1760R<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
1761R<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
1762dnl ${client_resolve} should be OK, so go ahead
1763R$*			$: <?> $&{client_name}
1764# pass to name server to make hostname canonical
1765R<?> $* $~P 		$:<?>  $[ $1 $2 $]
1766R$* .			$1			strip trailing dots
1767dnl should not be necessary since it has been done for client_addr already
1768R<?>			$@ RELAYFROM
1769ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1770R<?> $* $=m		$@ RELAYFROM', `dnl')
1771R<?> $=w		$@ RELAYFROM
1772ifdef(`_RELAY_HOSTS_ONLY_',
1773`R<?> $=R		$@ RELAYFROM
1774ifdef(`_ACCESS_TABLE_', `dnl
1775R<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
1776R<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
1777`R<?> $* $=R			$@ RELAYFROM
1778ifdef(`_ACCESS_TABLE_', `dnl
1779R<?> $*			$: $>LookUpDomain <$1> <?> <$1> <+Connect>',`dnl')')
1780ifdef(`_ACCESS_TABLE_', `dnl
1781R<RELAY> $*		$@ RELAYFROM
1782R<$*> <$*>		$: $2',`dnl')
1783
1784# anything else is bogus
1785R$*			$#error $@ 5.7.1 $: confRELAY_MSG
1786divert(0)
1787ifdef(`_DELAY_CHECKS_',`dnl
1788# turn a canonical address in the form user<@domain>
1789# qualify unqual. addresses with $j
1790dnl it might have been only user (without <@domain>)
1791SFullAddr
1792R$* <@ $+ . >		$1 <@ $2 >
1793R$* <@ $* >		$@ $1 <@ $2 >
1794R$+			$@ $1 <@ $j >
1795
1796# call all necessary rulesets
1797Scheck_rcpt
1798dnl this test should be in the Basic_check_rcpt ruleset
1799dnl which is the correct DSN code?
1800# R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
1801R$+			$: $1 $| $>checkrcpt $1
1802dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
1803R$+ $| $#$*		$#$2
1804R$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
1805ifdef(`_SPAM_FH_',
1806`dnl lookup user@ and user@address
1807ifdef(`_ACCESS_TABLE_', `',
1808`errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
1809')')dnl
1810dnl one of the next two rules is supposed to match
1811dnl this code has been copied from BLACKLIST... etc
1812dnl and simplified by omitting some < >.
1813R<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <U: $1@>
1814R<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 >
1815dnl R<?>		$@ something_is_very_wrong_here
1816# lookup the addresses only with To tag
1817R<> $* $| <$+>		$: <@> $1 $| $>SearchList <!To> $| <$2> <>
1818R<@> $* $| $*		$: $2 $1		reverse result
1819dnl', `dnl')
1820ifdef(`_SPAM_FRIEND_',
1821`# is the recipient a spam friend?
1822ifdef(`_SPAM_HATER_',
1823	`errprint(`*** ERROR: define either SpamHater or SpamFriend
1824')', `dnl')
1825R<SPAMFRIEND> $+	$@ SPAMFRIEND
1826R<$*> $+		$: $2',
1827`dnl')
1828ifdef(`_SPAM_HATER_',
1829`# is the recipient no spam hater?
1830R<SPAMHATER> $+		$: $1			spam hater: continue checks
1831R<$*> $+		$@ NOSPAMHATER		everyone else: stop
1832dnl',`dnl')
1833dnl run further checks: check_mail
1834dnl should we "clean up" $&f?
1835R$*			$: $1 $| $>checkmail <$&f>
1836R$* $| $#$*		$#$2
1837dnl run further checks: check_relay
1838R$*			$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
1839R$* $| $#$*		$#$2
1840R$* $| $*		$: $1
1841', `dnl')
1842ifdef(`_ACCESS_TABLE_', `dnl
1843######################################################################
1844###  SearchList: search a list of items in the access map
1845###	Parameters:
1846###		<exact tag> $| <mark:address> <mark:address> ... <>
1847dnl	maybe we should have a @ (again) in front of the mark to
1848dnl	avoid errorneous matches (with error messages?)
1849dnl	if we can make sure that tag is always a single token
1850dnl	then we can omit the delimiter $|, otherwise we need it
1851dnl	to avoid errorneous matchs (first rule: H: if there
1852dnl	is that mark somewhere in the list, it will be taken).
1853dnl	moreover, we can do some tricks to enforce lookup with
1854dnl	the tag only, e.g.:
1855###	where "exact" is either "+" or "!":
1856###	<+ TAG>	lookup with and w/o tag
1857###	<! TAG>	lookup with tag
1858dnl	Warning: + and ! should be in OperatorChars (otherwise there must be
1859dnl		a blank between them and the tag.
1860###	possible values for "mark" are:
1861###		H: recursive host lookup (LookUpDomain)
1862dnl		A: recursive address lookup (LookUpAddress) [not yet required]
1863###		E: exact lookup, no modifications
1864###		F: full lookup, try user+ext@domain and user@domain
1865###		U: user lookup, try user+ext and user (input must have trailing @)
1866###	return: <RHS of lookup> or <?> (not found)
1867######################################################################
1868
1869# class with valid marks for SearchList
1870dnl if A is activated: add it
1871C{src}E F H U
1872SSearchList
1873# mark H: lookup domain
1874R<$+> $| <H:$+> <$*>		$: <$1> $| <@> $>LookUpDomain <$2> <?> <$3> <$1>
1875R<$+> $| <@> <$+> <$*>		$: <$1> $| <$2> <$3>
1876dnl A: NOT YET REQUIRED
1877dnl R<$+> $| <A:$+> <$*>	$: <$1> $| <@> $>LookUpAddress <$2> <?> <$3> <$1>
1878dnl R<$+> $| <@> <$+> <$*>	$: <$1> $| <$2> <$3>
1879dnl lookup of the item with tag
1880dnl this applies to F: U: E:
1881R<$- $-> $| <$={src}:$+> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$4 $: $3:$4 $)> <$5>
1882dnl no match, try without tag
1883R<+ $-> $| <$={src}:$+> <$*>	$: <+ $1> $| <$(access $3 $: $2:$3 $)> <$4>
1884dnl do we really have to distinguish these cases?
1885dnl probably yes, there might be a + in the domain part (is that allowed?)
1886dnl user+detail lookups: should it be:
1887dnl user+detail, user+*, user; just like aliases?
1888R<$- $-> $| <F:$* + $*@$+> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$3@$5 $: F:$3 + $4@$5$)> <$6>
1889R<+ $-> $| <F:$* + $*@$+> <$*>	$: <+ $1> $| <$(access $2@$4 $: F:$2 + $3@$4$)> <$5>
1890dnl user lookups are always with trailing @
1891dnl do not remove the @ from the lookup:
1892dnl it is part of the +detail@ which is omitted for the lookup
1893R<$- $-> $| <U:$* + $*> <$*>	$: <$1 $2> $| <$(access $2`'_TAG_DELIM_`'$3@ $: U:$3 + $4$)> <$5>
1894dnl no match, try without tag
1895R<+ $-> $| <U:$* + $*> <$*>	$: <+ $1> $| <$(access $2@ $: U:$2 + $3$)> <$4>
1896dnl no match, try rest of list
1897R<$+> $| <$={src}:$+> <$+>	$@ $>SearchList <$1> $| <$4>
1898dnl no match, list empty: return failure
1899R<$+> $| <$={src}:$+> <>	$@ <?>
1900dnl got result, return it
1901R<$+> $| <$+> <$*>		$@ <$2>
1902dnl return result from recursive invocation
1903R<$+> $| <$+>			$@ <$2>', `dnl')
1904
1905# is user trusted to authenticate as someone else?
1906dnl AUTH= parameter from MAIL command
1907Strust_auth
1908R$*			$: $&{auth_type} $| $1
1909# required by RFC 2554 section 4.
1910R$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
1911dnl seems to be useful...
1912R$* $| $&{auth_authen}		$@ identical
1913R$* $| <$&{auth_authen}>	$@ identical
1914dnl call user supplied code
1915R$* $| $*		$: $1 $| $>"Local_trust_auth" $1
1916R$* $| $#$*		$#$2
1917dnl default: error
1918R$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
1919
1920dnl empty ruleset definition so it can be called
1921SLocal_trust_auth
1922
1923ifdef(`_FFR_TLS_O_T', `dnl
1924Soffer_tls
1925R$*		$: $>LookUpDomain <$&{client_name}> <?> <> <! TLS_OFF_TAG>
1926R<?>$*		$: $>LookUpAddress <$&{client_addr}> <?> <> <! TLS_OFF_TAG>
1927R<?>$*		$: <$(access TLS_OFF_TAG: $: ? $)>
1928R<?>$*		$@ OK
1929R<NO> <>	$#error $@ 5.7.1 $: "550 do not offer TLS for " $&{client_name} " ["$&{client_addr}"]"
1930
1931Stry_tls
1932R$*		$: $>LookUpDomain <$&{server_name}> <?> <> <! TLS_TRY_TAG>
1933R<?>$*		$: $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_TRY_TAG>
1934R<?>$*		$: <$(access TLS_TRY_TAG: $: ? $)>
1935R<?>$*		$@ OK
1936R<NO> <>	$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"
1937')dnl
1938
1939# is connection with client "good" enough? (done in server)
1940# input: ${verify} $| (MAIL|STARTTLS)
1941dnl MAIL: called from check_mail
1942dnl STARTTLS: called from smtp() after STARTTLS has been accepted
1943Stls_client
1944ifdef(`_ACCESS_TABLE_', `dnl
1945dnl ignore second arg for now
1946dnl maybe use it to distinguish permanent/temporary error?
1947dnl if MAIL: permanent (STARTTLS has not been offered)
1948dnl if STARTTLS: temporary (offered but maybe failed)
1949R$* $| $*	$: $1 $| $>LookUpDomain <$&{client_name}> <?> <> <! TLS_CLT_TAG>
1950R$* $| <?>$*	$: $1 $| $>LookUpAddress <$&{client_addr}> <?> <> <! TLS_CLT_TAG>
1951dnl do a default lookup: just TLS_CLT_TAG
1952R$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
1953R$*		$@ $>"tls_connection" $1', `dnl
1954R$* $| $*	$@ $>"tls_connection" $1')
1955
1956# is connection with server "good" enough? (done in client)
1957dnl i.e. has the server been authenticated and is encryption active?
1958dnl called from deliver() after STARTTLS command
1959# input: ${verify}
1960Stls_server
1961ifdef(`_ACCESS_TABLE_', `dnl
1962R$*		$: $1 $| $>LookUpDomain <$&{server_name}> <?> <> <! TLS_SRV_TAG>
1963R$* $| <?>$*	$: $1 $| $>LookUpAddress <$&{server_addr}> <?> <> <! TLS_SRV_TAG>
1964dnl do a default lookup: just TLS_SRV_TAG
1965R$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
1966R$*		$@ $>"tls_connection" $1', `dnl
1967R$*		$@ $>"tls_connection" $1')
1968
1969Stls_connection
1970ifdef(`_ACCESS_TABLE_', `dnl
1971dnl common ruleset for tls_{client|server}
1972dnl input: $&{verify} $| <ResultOfLookup> [<>]
1973dnl remove optional <>
1974R$* $| <$*>$*			$: $1 $| <$2>
1975dnl permanent or temporary error?
1976R$* $| <PERM + $={tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
1977R$* $| <TEMP + $={tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
1978dnl default case depends on TLS_PERM_ERR
1979R$* $| <$={tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
1980dnl deal with TLS handshake failures: abort
1981RSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
1982dnl no <reply:dns> i.e. not requirements in the access map
1983dnl use default error
1984RSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
1985R$* $| <$*> <VERIFY>		$: <$2> <VERIFY> $1
1986R$* $| <$*> <$={tls}:$->$*	$: <$2> <$3:$4> $1
1987dnl some other value in access map: accept
1988dnl this also allows to override the default case (if used)
1989R$* $| $*			$@ OK
1990# authentication required: give appropriate error
1991# other side did authenticate (via STARTTLS)
1992dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> ${verify}
1993dnl only verification required and it succeeded
1994R<$*><VERIFY> OK		$@ OK
1995dnl verification required + some level of encryption
1996R<$*><VERIFY:$-> OK		$: <$1> <REQ:$2>
1997dnl just some level of encryption required
1998R<$*><ENCR:$-> $*		$: <$1> <REQ:$2>
1999dnl verification required but ${verify} is not set
2000R<$-:$+><VERIFY $*>		$#error $@ $2 $: $1 " authentication required"
2001R<$-:$+><VERIFY $*> FAIL	$#error $@ $2 $: $1 " authentication failed"
2002R<$-:$+><VERIFY $*> NO		$#error $@ $2 $: $1 " not authenticated"
2003R<$-:$+><VERIFY $*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
2004dnl some other value for ${verify}
2005R<$-:$+><VERIFY $*> $+		$#error $@ $2 $: $1 " authentication failure " $4
2006dnl some level of encryption required: get the maximum level
2007R<$*><REQ:$->			$: <$1> <REQ:$2> $>max $&{cipher_bits} : $&{auth_ssf}
2008dnl compare required bits with actual bits
2009R<$*><REQ:$-> $-		$: <$1> <$2:$3> $(arith l $@ $3 $@ $2 $)
2010R<$-:$+><$-:$-> TRUE		$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
2011
2012Smax
2013dnl compute the max of two values separated by :
2014R:		$: 0
2015R:$-		$: $1
2016R$-:		$: $1
2017R$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
2018RTRUE:$-:$-	$: $2
2019R$-:$-:$-	$: $2',
2020`dnl use default error
2021dnl deal with TLS handshake failures: abort
2022RSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."')
2023
2024SRelayAuth
2025# authenticated?
2026dnl we do not allow relaying for anyone who can present a cert
2027dnl signed by a "trusted" CA. For example, even if we put verisigns
2028dnl CA in CERTPath so we can authenticate users, we do not allow
2029dnl them to abuse our server (they might be easier to get hold of,
2030dnl but anyway).
2031dnl so here is the trick: if the verification succeeded
2032dnl we look up the cert issuer in the access map
2033dnl (maybe after extracting a part with a regular expression)
2034dnl if this returns RELAY we relay without further questions
2035dnl if it returns SUBJECT we perform a similar check on the
2036dnl cert subject.
2037R$* $| OK		$: $1
2038R$* $| $*		$@ NO		not authenticated
2039ifdef(`_ACCESS_TABLE_', `dnl
2040ifdef(`_CERT_REGEX_ISSUER_', `dnl
2041R$*			$: $1 $| $(CERTIssuer $&{cert_issuer} $)',
2042`R$*			$: $1 $| $&{cert_issuer}')
2043R$* $| $+		$: $1 $| $(access CERTISSUER:$2 $)
2044dnl use $# to stop further checks (delay_check)
2045R$* $| RELAY		$# RELAYCERTISSUER
2046ifdef(`_CERT_REGEX_SUBJECT_', `dnl
2047R$* $| SUBJECT		$: $1 $| <@> $(CERTSubject $&{cert_subject} $)',
2048`R$* $| SUBJECT		$: $1 $| <@> $&{cert_subject}')
2049R$* $| <@> $+		$: $1 $| <@> $(access CERTSUBJECT:$&{cert_subject} $)
2050R$* $| <@> RELAY	$# RELAYCERTSUBJECT
2051R$* $| $*		$: $1', `dnl')
2052
2053undivert(9)dnl LOCAL_RULESETS
2054ifdef(`_FFR_MILTER', `
2055#
2056######################################################################
2057######################################################################
2058#####
2059`#####			MAIL FILTER DEFINITIONS'
2060#####
2061######################################################################
2062######################################################################
2063_MAIL_FILTERS_')
2064#
2065######################################################################
2066######################################################################
2067#####
2068`#####			MAILER DEFINITIONS'
2069#####
2070######################################################################
2071######################################################################
2072undivert(7)dnl MAILER_DEFINITIONS
2073