3 # Copyright (c) 1998-2010 Proofpoint, Inc. and its suppliers.
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.
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.
16 VERSIONID(`$Id: proto.m4,v 8.762 2013-11-22 20:51:13 ca Exp $')
18 # level CF_LEVEL config file format
19 V`'CF_LEVEL`'ifdef(`NO_VENDOR',`', `/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Berkeley')')
22 dnl if MAILER(`local') not defined: do it ourself; be nice
23 dnl maybe we should issue a warning?
24 ifdef(`_MAILER_local_',`', `MAILER(local)')
26 # do some sanity checking
28 `errprint(`*** ERROR: No system type defined (use OSTYPE macro)
31 # pick our default mailers
32 ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
33 ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
34 ifdef(`confRELAY_MAILER',,
35 `define(`confRELAY_MAILER',
36 `ifdef(`_MAILER_smtp_', `relay',
37 `ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
38 ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
39 define(`_SMTP_', `confSMTP_MAILER')dnl for readability only
40 define(`_LOCAL_', `confLOCAL_MAILER')dnl for readability only
41 define(`_RELAY_', `confRELAY_MAILER')dnl for readability only
42 define(`_UUCP_', `confUUCP_MAILER')dnl for readability only
44 # back compatibility with old config files
45 ifdef(`confDEF_GROUP_ID',
46 `errprint(`*** confDEF_GROUP_ID is obsolete.
47 Use confDEF_USER_ID with a colon in the value instead.
49 ifdef(`confREAD_TIMEOUT',
50 `errprint(`*** confREAD_TIMEOUT is obsolete.
51 Use individual confTO_<timeout> parameters instead.
53 ifdef(`confMESSAGE_TIMEOUT',
54 `define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
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)))')')
61 ifdef(`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.
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)
70 ifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
71 WARNING: Using default FEATURE(ldap_routing) map definition(s)
72 without setting confLDAP_DEFAULT_SPEC option.
75 # clean option definitions below....
76 define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
78 dnl required to "rename" the check_* rulesets...
79 define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
80 dnl default relaying denied message
81 ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG',
82 ifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))')
83 ifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')')
84 define(`_CODE553', `553')
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')
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')
101 # need to set this before any LDAP lookups are done (including classes)
102 ifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m')
106 `# file containing names of hosts for which we receive email
110 # my official domain name
111 # ... `define' this only if sendmail cannot automatically determine your domain
112 ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
114 # host/domain names ending with a token in class P are canonical
123 ifdef(`BITNET_RELAY',
129 ifdef(`DECNET_RELAY',
130 `define(`_USE_DECNET_SYNTAX_', 1)dnl
142 # "Smart" relay host (may be null)
143 DS`'ifdef(`SMART_HOST', `SMART_HOST')
145 ifdef(`LUSER_RELAY', `dnl
146 # place to which unknown users should be forwarded
151 # operators that cannot be in local usernames (i.e., network indicators)
152 CO @ ifdef(`_NO_PERCENTHACK_', `', `%') ifdef(`_NO_UUCP_', `', `!')
154 # a class with just dot (for identifying canonical names)
157 # a class with just a left bracket (for identifying domain literals)
160 ifdef(`_ACCESS_TABLE_', `dnl
161 # access_db acceptance class
163 ifdef(`_DELAY_COMPAT_8_10_',`dnl
164 ifdef(`_BLOCKLIST_RCPT_',`dnl
165 # possible access_db RHS for spam friends/haters
166 C{SpamTag}SPAMFRIEND SPAMHATER')')',
169 dnl mark for "domain is ok" (resolved or accepted anyway)
170 define(`_RES_OK_', `OKR')dnl
171 ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
172 # Resolve map (to check if a host exists in check_mail)
173 Kresolve host -a<_RES_OK_> -T<TEMP>')
176 ifdef(`_NEED_MACRO_MAP_', `dnl
177 ifdef(`_MACRO_MAP_', `', `# macro storage map
178 define(`_MACRO_MAP_', `1')dnl
179 Kmacro macro')', `dnl')
181 ifdef(`confCR_FILE', `dnl
182 # Hosts for which relaying is permitted ($=R)
186 ifdef(`_ACCESS_TABLE_', `dnl
187 define(`_FULL_TLS_CONNECTION_CHECK_', `1')', `dnl
188 ifdef(`_MTA_STS_', `define(`_FULL_TLS_CONNECTION_CHECK_', `1')')')
189 define(`TLS_SRV_TAG', `"TLS_Srv"')dnl
190 define(`TLS_CLT_TAG', `"TLS_Clt"')dnl
191 define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
192 define(`TLS_TRY_TAG', `"Try_TLS"')dnl
193 define(`SRV_FEAT_TAG', `"Srv_Features"')dnl
194 define(`CLT_FEAT_TAG', `"Clt_Features"')dnl
195 dnl this may be useful in other contexts too
196 ifdef(`_ARITH_MAP_', `', `# arithmetic map
197 define(`_ARITH_MAP_', `1')dnl
199 ifdef(`_FULL_TLS_CONNECTION_CHECK_', `dnl
200 ifdef(`_MACRO_MAP_', `', `# macro storage map
201 define(`_MACRO_MAP_', `1')dnl
203 # possible values for TLS_connection in access map
205 C{TlsVerified}OK TRUSTED
207 ifdef(`_CERT_REGEX_ISSUER_', `dnl
208 # extract relevant part from cert issuer
209 KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
210 ifdef(`_CERT_REGEX_SUBJECT_', `dnl
211 # extract relevant part from cert subject
212 KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
213 ifdef(`_MTA_STS_', `dnl
214 Kstsxsni regex -a: -s3 (.*)(servername=)(.*)
215 Kstsxsni2 regex -a: -s2 (.*)(servername=.*)
216 Kstsxmatch regex -a: -s2 (match=)(.*)
217 # flag d: turn off DANE
218 Kstsxnodaneflag regex -a@ -s3 (.*)(flags=)([^;]*d)(.*)
221 ifdef(`LOCAL_RELAY', `dnl
222 # who I send unqualified names to if `FEATURE(stickyhost)' is used
223 # (null means deliver locally)
226 ifdef(`MAIL_HUB', `dnl
227 # who gets all local email traffic
228 # ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
232 Kdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
234 divert(0)dnl # end of nullclient diversion
235 # class E: names that should be exposed as from this host, even if we masquerade
236 # class L: names that should be delivered locally, even if we have a relay
237 # class M: domains that should be converted to $M
238 # class N: domains that should not be converted to $M
241 ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
243 ifdef(`MASQUERADE_NAME', `dnl
244 # who I masquerade as (null for no masquerading) (see also $=M)
245 DM`'MASQUERADE_NAME')
247 # my name for error messages
248 ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
250 ifdef(`confOPENSSL_CNF',, `define(`confOPENSSL_CNF', `/etc/mail/sendmail.ossl')')
251 undivert(6)dnl LOCAL_CONFIG
252 ifelse(defn(`confOPENSSL_CNF'), `', `', `EOPENSSL_CONF=confOPENSSL_CNF')
253 include(_CF_DIR_`m4/version.m4')
258 ifdef(`confAUTO_REBUILD',
259 `errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
260 There was a potential for a denial of service attack if this is set.
263 # strip message body to 7 bits on input?
264 _OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
266 # 8-bit data handling
267 _OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
269 # wait for alias file rebuild (default units: minutes)
270 _OPTION(AliasWait, `confALIAS_WAIT', `5m')
272 # location of alias file
273 _OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
275 # minimum number of free blocks on filesystem
276 _OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
278 # maximum message size
279 _OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `0')
281 # substitution for space (blank) characters
282 _OPTION(BlankSub, `confBLANK_SUB', `_')
284 # avoid connecting to "expensive" mailers on initial submission?
285 _OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
287 # checkpoint queue runs after every N successful deliveries
288 _OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
290 # default delivery mode
291 _OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
293 # error message header/file
294 _OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
297 _OPTION(ErrorMode, `confERROR_MODE', `print')
299 # save Unix-style "From_" lines at top of header?
300 _OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
302 # queue file mode (qf files)
303 _OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
305 # temporary file mode
306 _OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
308 # match recipients against GECOS field?
309 _OPTION(MatchGECOS, `confMATCH_GECOS', `False')
312 _OPTION(MaxHopCount, `confMAX_HOP', `25')
314 # location of help file
315 O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
317 # ignore dots as terminators in incoming messages?
318 _OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
320 # name resolver options
321 _OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
323 # deliver MIME-encapsulated error messages?
324 _OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
326 # Forward file search path
327 _OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
329 # open connection cache size
330 _OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
332 # open connection cache timeout
333 _OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
335 # persistent host status directory
336 _OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
338 # single thread deliveries (requires HostStatusDirectory)?
339 _OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
341 # use Errors-To: header?
342 _OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
344 # use compressed IPv6 address format?
345 _OPTION(UseCompressedIPv6Addresses, `confUSE_COMPRESSED_IPV6_ADDRESSES', `')
348 _OPTION(LogLevel, `confLOG_LEVEL', `10')
350 # send to me too, even in an alias expansion?
351 _OPTION(MeToo, `confME_TOO', `True')
353 # verify RHS in newaliases?
354 _OPTION(CheckAliases, `confCHECK_ALIASES', `False')
356 # default messages to old style headers if no special punctuation?
357 _OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
359 # SMTP daemon options
360 ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
361 `errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
362 Use `DAEMON_OPTIONS()'; see cf/README.
364 `DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
365 ifelse(defn(`_DPO_'), `',
366 `ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
367 O DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
368 ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
370 # SMTP client options
371 ifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
372 `errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid. See cf/README for more information.
374 `CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
375 ifelse(defn(`_CPO_'), `',
376 `#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
378 # Modifiers to `define' {daemon_flags} for direct submissions
379 _OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
381 # Use as mail submission program? See sendmail/SECURITY
382 _OPTION(UseMSP, `confUSE_MSP', `')
385 _OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
387 # who (if anyone) should get extra copies of error messages
388 _OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
390 # slope of queue-only function
391 _OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
393 # limit on number of concurrent queue runners
394 _OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
396 # maximum number of queue-runners per queue-grouping with multiple queues
397 _OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
399 # priority of queue runners (nice(3))
400 _OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
402 # shall we sort the queue by hostname first?
403 _OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
405 # minimum time in queue before retry
406 _OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
408 # maximum time in queue before retry (if > 0; only for exponential delay)
409 _OPTION(MaxQueueAge, `confMAX_QUEUE_AGE', `')
411 # how many jobs can you process in the queue?
412 _OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `0')
414 # perform initial split of envelope without checking MX records
415 _OPTION(FastSplit, `confFAST_SPLIT', `1')
418 O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
420 # key for shared memory; 0 to turn off, -1 to auto-select
421 _OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
423 # file to store auto-selected key for shared memory (SharedMemoryKey = -1)
424 _OPTION(SharedMemoryKeyFile, `confSHARED_MEMORY_KEY_FILE', `')
426 # timeouts (many of these)
427 _OPTION(Timeout.initial, `confTO_INITIAL', `5m')
428 _OPTION(Timeout.connect, `confTO_CONNECT', `5m')
429 _OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
430 _OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
431 _OPTION(Timeout.helo, `confTO_HELO', `5m')
432 _OPTION(Timeout.mail, `confTO_MAIL', `10m')
433 _OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
434 _OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
435 _OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
436 _OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
437 _OPTION(Timeout.rset, `confTO_RSET', `5m')
438 _OPTION(Timeout.quit, `confTO_QUIT', `2m')
439 _OPTION(Timeout.misc, `confTO_MISC', `2m')
440 _OPTION(Timeout.command, `confTO_COMMAND', `1h')
441 _OPTION(Timeout.ident, `confTO_IDENT', `5s')
442 _OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
443 _OPTION(Timeout.control, `confTO_CONTROL', `2m')
444 _OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
445 _OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
446 _OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
447 _OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
448 _OPTION(Timeout.queuereturn.dsn, `confTO_QUEUERETURN_DSN', `5d')
449 _OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
450 _OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
451 _OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
452 _OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
453 _OPTION(Timeout.queuewarn.dsn, `confTO_QUEUEWARN_DSN', `4h')
454 _OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
455 _OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
456 _OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
457 _OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
458 _OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
459 _OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
460 _OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
461 _OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
462 _OPTION(Timeout.auth, `confTO_AUTH', `10m')
463 _OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
465 # time for DeliverBy; extension disabled if less than 0
466 _OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
468 # should we not prune routes in route-addr syntax addresses?
469 _OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
471 # queue up everything before forking?
472 _OPTION(SuperSafe, `confSAFE_QUEUE', `True')
475 _OPTION(StatusFile, `STATUS_FILE')
477 # time zone handling:
478 # if undefined, use system default
479 # if defined but null, use TZ envariable passed in
480 # if defined and non-null, use that info
481 ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
482 confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
483 `O TimeZoneSpec=confTIME_ZONE')
485 # default UID (can be username or userid:groupid)
486 _OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
488 # list of locations of user database file (null means no lookup)
489 _OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
492 _OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
494 # fallback smart host
495 _OPTION(FallbackSmartHost, `confFALLBACK_SMARTHOST', `fall.back.host.net')
497 # if we are the best MX host for a site, try it directly instead of config err
498 _OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
500 # load average at which we just queue messages
501 _OPTION(QueueLA, `confQUEUE_LA', `8')
503 # load average at which we refuse connections
504 _OPTION(RefuseLA, `confREFUSE_LA', `12')
506 # log interval when refusing connections for this long
507 _OPTION(RejectLogInterval, `confREJECT_LOG_INTERVAL', `3h')
509 # load average at which we delay connections; 0 means no limit
510 _OPTION(DelayLA, `confDELAY_LA', `0')
512 # maximum number of children we allow at one time
513 _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
515 # maximum number of new connections per second
516 _OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
518 # Width of the window
519 _OPTION(ConnectionRateWindowSize, `confCONNECTION_RATE_WINDOW_SIZE', `60s')
521 # work recipient factor
522 _OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
524 # deliver each queued job in a separate process?
525 _OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
528 _OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
531 _OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
533 # default character set
534 _OPTION(DefaultCharSet, `confDEF_CHAR_SET', `unknown-8bit')
536 # service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
537 _OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
539 # hosts file (normally /etc/hosts)
540 _OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
542 # dialup line delay on connection failure
543 _OPTION(DialDelay, `confDIAL_DELAY', `0s')
545 # action to take if there are no recipients in the message
546 _OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `none')
548 # chrooted environment for writing to files
549 _OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `')
551 # are colons OK in addresses?
552 _OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
554 # shall I avoid expanding CNAMEs (violates protocols)?
555 _OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
557 # SMTP initial login message (old $e macro)
558 _OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
560 # UNIX initial From header format (old $l macro)
561 _OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
563 # From: lines that have embedded newlines are unwrapped onto one line
564 _OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
566 # Allow HELO SMTP command that does not `include' a host name
567 _OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
569 # Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
570 _OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
572 # delimiter (operator) characters (old $o macro)
573 _OPTION(OperatorChars, `confOPERATORS', `.:@[]')
575 # shall I avoid calling initgroups(3) because of high NIS costs?
576 _OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
578 # are group-writable `:include:' and .forward files (un)trustworthy?
579 # True (the default) means they are not trustworthy.
580 _OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
581 ifdef(`confUNSAFE_GROUP_WRITES',
582 `errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
585 # where do errors that occur when sending errors get sent?
586 _OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
588 # issue temporary errors (4xy) instead of permanent errors (5xy)?
589 _OPTION(SoftBounce, `confSOFT_BOUNCE', `False')
591 # where to save bounces if all else fails
592 _OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
594 # what user id do we assume for the majority of the processing?
595 _OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
597 # maximum number of recipients per SMTP envelope
598 _OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `0')
600 # limit the rate recipients per SMTP envelope are accepted
601 # once the threshold number of recipients have been rejected
602 _OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `0')
605 # shall we get local names from our installed interfaces?
606 _OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
608 # Return-Receipt-To: header implies DSN request
609 _OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
611 # override connection address (for testing)
612 _OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
614 # Trusted user for file ownership and starting the daemon
615 _OPTION(TrustedUser, `confTRUSTED_USER', `root')
617 # Control socket for daemon management
618 _OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
620 # Maximum MIME header length to protect MUAs
621 _OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
623 # Maximum length of the sum of all headers
624 _OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
626 # Maximum depth of alias recursion
627 _OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
629 # location of pid file
630 _OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
632 # Prefix string for the process title shown on 'ps' listings
633 _OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
635 # Data file (df) memory-buffer file maximum size
636 _OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
638 # Transcript file (xf) memory-buffer file maximum size
639 _OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
641 # lookup type to find information about local mailboxes
642 _OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
644 # override compile time flag REQUIRES_DIR_FSYNC
645 _OPTION(RequiresDirfsync, `confREQUIRES_DIR_FSYNC', `true')
647 # list of authentication mechanisms
648 _OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
650 # Authentication realm
651 _OPTION(AuthRealm, `confAUTH_REALM', `')
653 # default authentication information for outgoing connections
654 _OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
657 _OPTION(AuthOptions, `confAUTH_OPTIONS', `')
659 # SMTP AUTH maximum encryption strength
660 _OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
662 # SMTP STARTTLS server options
663 _OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
666 _OPTION(CipherList, `confCIPHER_LIST', `')
667 # server side SSL options
668 _OPTION(ServerSSLOptions, `confSERVER_SSL_OPTIONS', `')
669 # client side SSL options
670 _OPTION(ClientSSLOptions, `confCLIENT_SSL_OPTIONS', `')
672 _OPTION(SSLEngine, `confSSL_ENGINE', `')
673 # Path to dynamic library for SSLEngine
674 _OPTION(SSLEnginePath, `confSSL_ENGINE_PATH', `')
675 # TLS: fall back to clear text after handshake failure?
676 _OPTION(TLSFallbacktoClear, `confTLS_FALLBACK_TO_CLEAR', `')
679 _OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
681 ifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
683 _OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
684 _OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
685 _OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
686 _OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
687 _OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')
688 _OPTION(Milter.macros.eom, `confMILTER_MACROS_EOM', `')
689 _OPTION(Milter.macros.eoh, `confMILTER_MACROS_EOH', `')
690 _OPTION(Milter.macros.data, `confMILTER_MACROS_DATA', `')')
693 _OPTION(CACertPath, `confCACERT_PATH', `')
695 _OPTION(CACertFile, `confCACERT', `')
697 _OPTION(ServerCertFile, `confSERVER_CERT', `')
699 _OPTION(ServerKeyFile, `confSERVER_KEY', `')
701 _OPTION(ClientCertFile, `confCLIENT_CERT', `')
703 _OPTION(ClientKeyFile, `confCLIENT_KEY', `')
704 # File containing certificate revocation lists
705 _OPTION(CRLFile, `confCRL', `')
706 # Directory containing hashes pointing to certificate revocation status files
707 _OPTION(CRLPath, `confCRL_PATH', `')
708 # DHParameters (only required if DSA/DH is used)
709 _OPTION(DHParameters, `confDH_PARAMETERS', `')
710 # Random data source (required for systems without /dev/urandom under OpenSSL)
711 _OPTION(RandFile, `confRAND_FILE', `')
712 # fingerprint algorithm (digest) to use for the presented cert
713 _OPTION(CertFingerprintAlgorithm, `confCERT_FINGERPRINT_ALGORITHM', `')
715 _OPTION(DANE, `confDANE', `false')
717 # Maximum number of "useless" commands before slowing down
718 _OPTION(MaxNOOPCommands, `confMAX_NOOP_COMMANDS', `20')
720 # Name to use for EHLO (defaults to $j)
721 _OPTION(HeloName, `confHELO_NAME')
723 ifdef(`_NEED_SMTPOPMODES_', `dnl
724 # SMTP operation modes
725 C{SMTPOpModes} s d D')
727 ############################
728 `# QUEUE GROUP DEFINITIONS #'
729 ############################
732 ###########################
733 # Message precedences #
734 ###########################
737 Pspecial-delivery=100
742 #####################
744 #####################
746 # this is equivalent to setting class "t"
747 ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
750 ifdef(`_NO_UUCP_', `dnl', `Tuucp')
751 ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
753 #########################
754 # Format of headers #
755 #########################
757 ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
758 ifdef(`confMESSAGEID_HEADER',, `define(`confMESSAGEID_HEADER', `<$t.$i@$j>')')dnl
759 H?P?Return-Path: <$g>
760 HReceived: confRECEIVED_HEADER
763 H?F?Resent-From: confFROM_HEADER
764 H?F?From: confFROM_HEADER
767 # H?l?Received-Date: $b
768 H?M?Resent-Message-Id: confMESSAGEID_HEADER
769 H?M?Message-Id: confMESSAGEID_HEADER
772 ######################################################################
773 ######################################################################
775 ##### REWRITING RULES
777 ######################################################################
778 ######################################################################
780 ############################################
781 ### Ruleset 3 -- Name Canonicalization ###
782 ############################################
785 # handle null input (translate to <@> special case)
788 # strip group: syntax (not inside angle brackets!) and trailing semicolon
789 R$* $: $1 <@> mark addresses
790 R$* < $* > $* <@> $: $1 < $2 > $3 unmark <addr>
791 R@ $* <@> $: @ $1 unmark @host:...
792 R$* [ IPv6 : $+ ] <@> $: $1 [ IPv6 : $2 ] unmark IPv6 addr
793 R$* :: $* <@> $: $1 :: $2 unmark node::addr
794 R:`include': $* <@> $: :`include': $1 unmark :`include':...
795 R$* : $* [ $* ] $: $1 : $2 [ $3 ] <@> remark if leading colon
796 R$* : $* <@> $: $2 strip colon if marked
798 R$* ; $1 strip trailing semi
799 R$* < $+ :; > $* $@ $2 :; <@> catch <list:;>
800 R$* < $* ; > $1 < $2 > bogus bracketed semi
802 # null input now results from list:; syntax
805 # strip angle brackets -- note RFC733 heuristic to get innermost item
806 R$* $: < $1 > housekeeping <>
807 R$+ < $* > < $2 > strip excess on left
808 R< $* > $+ < $1 > strip excess on right
809 R<> $@ < @ > MAIL FROM:<> case
810 R< $+ > $: $1 remove housekeeping <>
812 ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
813 # make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
814 R@ $+ , $+ @ $1 : $2 change all "," to ":"
816 # localize and dispose of route-based addresses
817 dnl XXX: IPv6 colon conflict
818 ifdef(`NO_NETINET6', `dnl',
819 `R@ [$+] : $+ $@ $>Canonify2 < @ [$1] > : $2 handle <route-addr>')
820 R@ $+ : $+ $@ $>Canonify2 < @$1 > : $2 handle <route-addr>
822 # strip route address <@a,@b,@c:user@d> -> <user@d>
824 ifdef(`NO_NETINET6', `dnl',
829 # find focus for list syntax
830 R $+ : $* ; @ $+ $@ $>Canonify2 $1 : $2 ; < @ $3 > list syntax
831 R $+ : $* ; $@ $1 : $2; list syntax
833 # find focus for @ syntax addresses
834 R$+ @ $+ $: $1 < @ $2 > focus on domain
835 R$+ < $+ @ $+ > $1 $2 < @ $3 > move gaze right
836 R$+ < @ $+ > $@ $>Canonify2 $1 < @ $2 > already canonical
838 dnl This is flagged as an error in S0; no need to silently fix it here.
839 dnl # do some sanity checking
840 dnl R$* < @ $~[ $* : $* > $* $1 < @ $2 $3 > $4 nix colons in addrs
842 ifdef(`_NO_UUCP_', `dnl',
843 `# convert old-style addresses to a domain-based address
844 R$- ! $+ $@ $>Canonify2 $2 < @ $1 .UUCP > resolve uucp names
845 R$+ . $- ! $+ $@ $>Canonify2 $3 < @ $1 . $2 > domain uucps
846 R$+ ! $+ $@ $>Canonify2 $2 < @ $1 .UUCP > uucp subdomains
848 ifdef(`_USE_DECNET_SYNTAX_',
849 `# convert node::user addresses into a domain-based address
850 R$- :: $+ $@ $>Canonify2 $2 < @ $1 .DECNET > resolve DECnet names
851 R$- . $- :: $+ $@ $>Canonify2 $3 < @ $1.$2 .DECNET > numeric DECnet addr
854 ifdef(`_NO_PERCENTHACK_', `dnl',
855 `# if we have % signs, take the rightmost one
856 R$* % $* $1 @ $2 First make them all @s.
857 R$* @ $* @ $* $1 % $2 @ $3 Undo all but the last.
859 R$* @ $* $@ $>Canonify2 $1 < @ $2 > Insert < > and finish
861 # else we must be a local name
862 R$* $@ $>Canonify2 $1
865 ################################################
866 ### Ruleset 96 -- bottom half of ruleset 3 ###
867 ################################################
871 # handle special cases for local names
872 R$* < @ localhost > $* $: $1 < @ $j . > $2 no domain at all
873 R$* < @ localhost . $m > $* $: $1 < @ $j . > $2 local domain
874 ifdef(`_NO_UUCP_', `dnl',
875 `R$* < @ localhost . UUCP > $* $: $1 < @ $j . > $2 .UUCP domain')
877 # check for IPv4/IPv6 domain literal
878 R$* < @ [ $+ ] > $* $: $1 < @@ [ $2 ] > $3 mark [addr]
879 R$* < @@ $=w > $* $: $1 < @ $j . > $3 self-literal
880 R$* < @@ $+ > $* $@ $1 < @ $2 > $3 canon IP addr
882 ifdef(`_DOMAIN_TABLE_', `dnl
883 # look up domains in the domain table
884 R$* < @ $+ > $* $: $1 < @ $(domaintable $2 $) > $3', `dnl')
886 undivert(2)dnl LOCAL_RULE_3
888 ifdef(`_BITDOMAIN_TABLE_', `dnl
889 # handle BITNET mapping
890 R$* < @ $+ .BITNET > $* $: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
892 ifdef(`_UUDOMAIN_TABLE_', `dnl
893 # handle UUCP mapping
894 R$* < @ $+ .UUCP > $* $: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
896 ifdef(`_NO_UUCP_', `dnl',
898 `# pass UUCP addresses straight through
899 R$* < @ $+ . UUCP > $* $@ $1 < @ $2 . UUCP . > $3',
900 `# if really UUCP, handle it immediately
902 `R$* < @ $=U . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
904 `R$* < @ $=V . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
906 `R$* < @ $=W . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
908 `R$* < @ $=X . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
910 `R$* < @ $=Y . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
912 ifdef(`_NO_CANONIFY_', `dnl', `dnl
913 # try UUCP traffic as a local address
914 R$* < @ $+ . UUCP > $* $: $1 < @ $[ $2 $] . UUCP . > $3
915 R$* < @ $+ . . UUCP . > $* $@ $1 < @ $2 . > $3')
917 # hostnames ending in class P are always canonical
918 R$* < @ $* $=P > $* $: $1 < @ $2 $3 . > $4
919 dnl apply the next rule only for hostnames not in class P
920 dnl this even works for phrases in class P since . is in class P
921 dnl which daemon flags are set?
922 R$* < @ $* $~P > $* $: $&{daemon_flags} $| $1 < @ $2 $3 > $4
923 dnl the other rules in this section only apply if the hostname
924 dnl does not end in class P hence no further checks are done here
925 dnl if this ever changes make sure the lookups are "protected" again!
926 ifdef(`_NO_CANONIFY_', `dnl
927 dnl do not canonify unless:
928 dnl domain ends in class {Canonify} (this does not work if the intersection
929 dnl with class P is non-empty)
930 dnl or {daemon_flags} has c set
931 # pass to name server to make hostname canonical if in class {Canonify}
932 R$* $| $* < @ $* $={Canonify} > $* $: $2 < @ $[ $3 $4 $] > $5
933 # pass to name server to make hostname canonical if requested
934 R$* c $* $| $* < @ $* > $* $: $3 < @ $[ $4 $] > $5
935 dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
936 R$* $| $* < @ $+ . > $* $: $2 < @ $3 . > $4
937 # add a trailing dot to qualified hostnames so other rules will work
938 R$* $| $* < @ $+.$+ > $* $: $2 < @ $3.$4 . > $5
939 ifdef(`_CANONIFY_HOSTS_', `dnl
940 dnl this should only apply to unqualified hostnames
941 dnl but if a valid character inside an unqualified hostname is an OperatorChar
942 dnl then $- does not work.
943 # look up unqualified hostnames
944 R$* $| $* < @ $* > $* $: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
945 dnl _NO_CANONIFY_ is not set: canonify unless:
946 dnl {daemon_flags} contains CC (do not canonify)
947 dnl but add a trailing dot to qualified hostnames so other rules will work
948 dnl should we do this for every hostname: even unqualified?
949 R$* CC $* $| $* < @ $+.$+ > $* $: $3 < @ $4.$5 . > $6
950 R$* CC $* $| $* $: $3
951 ifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
952 # do not canonify header addresses
953 R$* $| $* < @ $* $~P > $* $: $&{addr_type} $| $2 < @ $3 $4 > $5
954 R$* h $* $| $* < @ $+.$+ > $* $: $3 < @ $4.$5 . > $6
955 R$* h $* $| $* $: $3', `dnl')
956 # pass to name server to make hostname canonical
957 R$* $| $* < @ $* > $* $: $2 < @ $[ $3 $] > $4')
958 dnl remove {daemon_flags} for other cases
961 # local host aliases and pseudo-domains are always canonical
962 R$* < @ $=w > $* $: $1 < @ $2 . > $3
963 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
964 `R$* < @ $* $=M > $* $: $1 < @ $2 $3 . > $4',
965 `R$* < @ $=M > $* $: $1 < @ $2 . > $3')
966 ifdef(`_VIRTUSER_TABLE_', `dnl
967 dnl virtual hosts are also canonical
968 ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
969 `R$* < @ $* $={VirtHost} > $* $: $1 < @ $2 $3 . > $4',
970 `R$* < @ $={VirtHost} > $* $: $1 < @ $2 . > $3')',
972 ifdef(`_GENERICS_TABLE_', `dnl
973 dnl hosts for genericstable are also canonical
974 ifdef(`_GENERICS_ENTIRE_DOMAIN_',
975 `R$* < @ $* $=G > $* $: $1 < @ $2 $3 . > $4',
976 `R$* < @ $=G > $* $: $1 < @ $2 . > $3')',
978 dnl remove superfluous dots (maybe repeatedly) which may have been added
979 dnl by one of the rules before
980 R$* < @ $* . . > $* $1 < @ $2 . > $3
983 ##################################################
984 ### Ruleset 4 -- Final Output Post-rewriting ###
985 ##################################################
988 R$+ :; <@> $@ $1 : handle <list:;>
989 R$* <@> $@ handle <> and list:;
991 # strip trailing dot off possibly canonical name
992 R$* < @ $+ . > $* $1 < @ $2 > $3
994 # eliminate internal code
995 R$* < @ *LOCAL* > $* $1 < @ $j > $2
997 # externalize local domain info
998 R$* < $+ > $* $1 $2 $3 defocus
999 R@ $+ : @ $+ : $+ @ $1 , @ $2 : $3 <route-addr> canonical
1000 R@ $* $@ @ $1 ... and exit
1002 ifdef(`_NO_UUCP_', `dnl',
1003 `# UUCP must always be presented in old form
1004 R$+ @ $- . UUCP $2!$1 u@h.UUCP => h!u')
1006 ifdef(`_USE_DECNET_SYNTAX_',
1007 `# put DECnet back in :: form
1008 R$+ @ $+ . DECNET $2 :: $1 u@h.DECNET => h::u',
1010 # delete duplicate local names
1011 R$+ % $=w @ $=w $1 @ $2 u%host@host => u@host
1015 ##############################################################
1016 ### Ruleset 97 -- recanonicalize and call ruleset zero ###
1017 ### (used for recursive calls) ###
1018 ##############################################################
1021 R$* $: $>canonify $1
1025 ######################################
1026 ### Ruleset 0 -- Parse Address ###
1027 ######################################
1031 R$* $: $>Parse0 $1 initial parsing
1032 R<@> $#_LOCAL_ $: <@> special case error msgs
1033 R$* $: $>ParseLocal $1 handle local hacks
1034 R$* $: $>Parse1 $1 final parsing
1037 # Parse0 -- do initial syntax checking and eliminate local addresses.
1038 # This should either return with the (possibly modified) input
1039 # or return with a #error mailer. It should not return with a
1040 # #mailer other than the #error mailer.
1044 R<@> $@ <@> special case error msgs
1045 R$* : $* ; <@> $#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
1046 R@ <@ $* > < @ $1 > catch "@@host" bogosity
1047 R<@ $+> $#error $@ 5.1.3 $: "_CODE553 User address required"
1048 R$+ <@> $#error $@ 5.1.3 $: "_CODE553 Hostname required"
1050 dnl allow tricks like [host1]:[host2]
1051 R<> $* < @ [ $* ] : $+ > $* $1 < @ [ $2 ] : $3 > $4
1052 R<> $* < @ [ $* ] , $+ > $* $1 < @ [ $2 ] , $3 > $4
1054 R<> $* < @ [ $* ] $+ > $* $#error $@ 5.1.2 $: "_CODE553 Invalid address"
1055 R<> $* < @ [ $+ ] > $* $1 < @ [ $2 ] > $3
1056 R<> $* <$* : $* > $* $#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
1058 R$* < @ . $* > $* $#error $@ 5.1.2 $: "_CODE553 Invalid host name"
1059 R$* < @ $* .. $* > $* $#error $@ 5.1.2 $: "_CODE553 Invalid host name"
1061 R$* < @ $* @ > $* $#error $@ 5.1.2 $: "_CODE553 Invalid route address"
1063 R$* @ $* < @ $* > $* $#error $@ 5.1.3 $: "_CODE553 Invalid route address"
1064 dnl comma only allowed before @; this check is not complete
1065 R$* , $~O $* $#error $@ 5.1.3 $: "_CODE553 Invalid route address"
1067 ifdef(`_STRICT_RFC821_', `# more RFC 821 checks
1068 R$* . < @ $* > $* $#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
1069 R. $* < @ $* > $* $#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
1072 # now delete the local info -- note $=O to find characters that cause forwarding
1073 R$* < @ > $* $@ $>Parse0 $>canonify $1 user@ => user
1074 R< @ $=w . > : $* $@ $>Parse0 $>canonify $2 @here:... -> ...
1075 R$- < @ $=w . > $: $(dequote $1 $) < @ $2 . > dequote "foo"@here
1076 R< @ $+ > $#error $@ 5.1.3 $: "_CODE553 User address required"
1077 R$* $=O $* < @ $=w . > $@ $>Parse0 $>canonify $1 $2 $3 ...@here -> ...
1078 R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo"
1079 R< @ *LOCAL* > $#error $@ 5.1.3 $: "_CODE553 User address required"
1080 R$* $=O $* < @ *LOCAL* >
1081 $@ $>Parse0 $>canonify $1 $2 $3 ...@*LOCAL* -> ...
1082 R$* < @ *LOCAL* > $: $1
1084 ifdef(`_ADD_BCC_', `dnl
1085 R$+ $: $>ParseBcc $1', `dnl')
1086 ifdef(`_PREFIX_MOD_', `dnl
1087 dnl do this only for addr_type=e r?
1088 R _PREFIX_MOD_ $+ $: $1 $(macro {rcpt_flags} $@ _PREFIX_FLAGS_ $)
1092 # Parse1 -- the bottom half of ruleset 0.
1096 ifdef(`_LDAP_ROUTING_', `dnl
1097 # handle LDAP routing for hosts in $={LDAPRoute}
1098 R$+ < @ $={LDAPRoute} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
1099 R$+ < @ $={LDAPRouteEquiv} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
1102 ifdef(`_MAILER_smtp_',
1103 `# handle numeric address spec
1104 dnl there is no check whether this is really an IP number
1105 R$* < @ [ $+ ] > $* $: $>ParseLocal $1 < @ [ $2 ] > $3 numeric internet spec
1106 R$* < @ [ $+ ] > $* $: $1 < @ [ $2 ] : $S > $3 Add smart host to path
1107 R$* < @ [ $+ ] : > $* $#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3 no smarthost: send
1108 R$* < @ [ $+ ] : $- : $*> $* $#$3 $@ $4 $: $1 < @ [$2] > $5 smarthost with mailer
1109 R$* < @ [ $+ ] : $+ > $* $#_SMTP_ $@ $3 $: $1 < @ [$2] > $4 smarthost without mailer',
1112 ifdef(`_VIRTUSER_TABLE_', `dnl
1113 # handle virtual users
1114 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1115 dnl this is not a documented option
1116 dnl it stops looping in virtusertable mapping if input and output
1117 dnl are identical, i.e., if address A is mapped to A.
1118 dnl it does not deal with multi-level recursion
1119 # handle full domains in RHS of virtusertable
1120 R$+ < @ $+ > $: $(macro {RecipientAddress} $) $1 < @ $2 >
1121 R$+ < @ $+ > $: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
1122 R<?> $+ $| $+ $: $1 $(macro {RecipientAddress} $@ $2 $)
1123 R<?> $+ $| $* $: $1',
1125 R$+ $: <!> $1 Mark for lookup
1126 dnl input: <!> local<@domain>
1127 ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
1128 `R<!> $+ < @ $* $={VirtHost} . > $: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
1129 `R<!> $+ < @ $={VirtHost} . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
1130 dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
1131 R<!> $+ < @ $=w . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1132 dnl if <@> local<@domain>: no match but try lookup
1133 dnl user+detail: try user++@domain if detail not empty
1134 R<@> $+ + $+ < @ $* . >
1135 $: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1136 dnl user+detail: try user+*@domain
1137 R<@> $+ + $* < @ $* . >
1138 $: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1139 dnl user+detail: try user@domain
1140 R<@> $+ + $* < @ $* . >
1141 $: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1142 dnl try default entry: @domain
1144 R<@> $+ + $+ < @ $+ . > $: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1146 R<@> $+ + $* < @ $+ . > $: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1147 dnl @domain if +detail exists
1148 dnl if no match, change marker to prevent a second @domain lookup
1149 R<@> $+ + $* < @ $+ . > $: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
1151 R<@> $+ < @ $+ . > $: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1156 R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4
1157 R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2
1158 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1159 # check virtuser input address against output address, if same, skip recursion
1160 R< $+ > $+ < @ $+ > $: < $1 > $2 < @ $3 > $| $1
1161 # it is the same: stop now
1162 R< $+ > $+ < @ $+ > $| $&{RecipientAddress} $: $>ParseLocal $>Parse0 $>canonify $1
1163 R< $+ > $+ < @ $+ > $| $* $: < $1 > $2 < @ $3 >
1165 dnl this is not a documented option
1166 dnl it performs no looping at all for virtusertable
1167 ifdef(`_NO_VIRTUSER_RECURSION_',
1168 `R< $+ > $+ < @ $+ > $: $>ParseLocal $>Parse0 $>canonify $1',
1169 `R< $+ > $+ < @ $+ > $: $>Recurse $1')
1172 # short circuit local delivery so forwarded email works
1173 ifdef(`_MAILER_usenet_', `dnl
1174 R$+ . USENET < @ $=w . > $#usenet $@ usenet $: $1 handle usenet specially', `dnl')
1177 ifdef(`_STICKY_LOCAL_DOMAIN_',
1178 `R$+ < @ $=w . > $: < $H > $1 < @ $2 . > first try hub
1179 R< $+ > $+ < $+ > $>MailerToTriple < $1 > $2 < $3 > yep ....
1180 dnl $H empty (but @$=w.)
1181 R< > $+ + $* < $+ > $#_LOCAL_ $: $1 + $2 plussed name?
1182 R< > $+ < $+ > $#_LOCAL_ $: @ $1 nope, local address',
1183 `R$=L < @ $=w . > $#_LOCAL_ $: @ $1 special local names
1184 R$+ < @ $=w . > $#_LOCAL_ $: $1 regular local name')
1186 ifdef(`_MAILER_TABLE_', `dnl
1187 # not local -- try mailer table lookup
1188 R$* <@ $+ > $* $: < $2 > $1 < @ $2 > $3 extract host name
1189 R< $+ . > $* $: < $1 > $2 strip trailing dot
1190 R< $+ > $* $: < $(mailertable $1 $) > $2 lookup
1191 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1192 R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check -- resolved?
1193 R< $+ > $* $: $>Mailertable <$1> $2 try domain',
1195 undivert(4)dnl UUCP rules from `MAILER(uucp)'
1197 ifdef(`_NO_UUCP_', `dnl',
1198 `# resolve remotely connected UUCP links (if any)
1200 `R$* < @ $=V . UUCP . > $* $: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
1203 `R$* < @ $=W . UUCP . > $* $: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
1206 `R$* < @ $=X . UUCP . > $* $: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
1209 # resolve fake top level domains by forwarding to other hosts
1210 ifdef(`BITNET_RELAY',
1211 `R$*<@$+.BITNET.>$* $: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3 user@host.BITNET',
1213 ifdef(`DECNET_RELAY',
1214 `R$*<@$+.DECNET.>$* $: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3 user@host.DECNET',
1216 ifdef(`_MAILER_pop_',
1217 `R$+ < @ POP. > $#pop $: $1 user@POP',
1219 ifdef(`_MAILER_fax_',
1220 `R$+ < @ $+ .FAX. > $#fax $@ $2 $: $1 user@host.FAX',
1222 `R$*<@$+.FAX.>$* $: $>MailerToTriple < $F > $1 <@$2.FAX.> $3 user@host.FAX',
1226 `# forward non-local UUCP traffic to our UUCP relay
1227 R$*<@$*.UUCP.>$* $: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3 uucp mail',
1228 `ifdef(`_MAILER_uucp_',
1229 `# forward other UUCP traffic straight to UUCP
1230 R$* < @ $+ .UUCP. > $* $#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3 user@host.UUCP',
1232 ifdef(`_MAILER_usenet_', `
1233 # addresses sent to net.group.USENET will get forwarded to a newsgroup
1234 R$+ . USENET $#usenet $@ usenet $: $1',
1237 ifdef(`_LOCAL_RULES_',
1238 `# figure out what should stay in our local mail system
1239 undivert(1)dnl LOCAL_NET_CONFIG', `dnl')
1241 # pass names that still have a host to a smarthost (if defined)
1242 R$* < @ $* > $* $: $>MailerToTriple < $S > $1 < @ $2 > $3 glue on smarthost name
1244 # deal with other remote names
1245 ifdef(`_MAILER_smtp_',
1246 `R$* < @$* > $* $#_SMTP_ $@ $2 $: $1 < @ $2 > $3 user@host.domain',
1247 `R$* < @$* > $* $#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
1249 # handle locally delivered names
1250 R$=L $#_LOCAL_ $: @ $1 special local names
1251 R$+ $#_LOCAL_ $: $1 regular local names
1253 ifdef(`_ADD_BCC_', `dnl
1255 R$+ $: $&{addr_type} $| $&A $| $1
1256 Re b $| $+ $| $+ $>MailerToTriple < $1 > $2 copy?
1257 R$* $| $* $| $+ $@ $3 no copy
1260 ###########################################################################
1261 ### Ruleset 5 -- special rewriting after aliases have been expanded ###
1262 ###########################################################################
1266 R$+ $: $1 $| $>"Local_localaddr" $1
1267 R$+ $| $#ok $@ $1 no change
1271 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1272 # Preserve rcpt_host in {Host}
1273 R$+ $: $1 $| $&h $| $&{Host} check h and {Host}
1274 R$+ $| $| $: $(macro {Host} $@ $) $1 no h or {Host}
1275 R$+ $| $| $+ $: $1 h not set, {Host} set
1276 R$+ $| +$* $| $* $: $1 h is +detail, {Host} set
1277 R$+ $| $* @ $+ $| $* $: $(macro {Host} $@ @$3 $) $1 set {Host} to host in h
1278 R$+ $| $+ $| $* $: $(macro {Host} $@ @$2 $) $1 set {Host} to h
1281 ifdef(`_FFR_5_', `dnl
1282 # Preserve host in a macro
1283 R$+ $: $(macro {LocalAddrHost} $) $1
1284 R$+ @ $+ $: $(macro {LocalAddrHost} $@ @ $2 $) $1')
1286 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
1287 # deal with plussed users so aliases work nicely
1288 R$+ + * $#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1289 R$+ + $* $#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1291 # prepend an empty "forward host" on the front
1294 ifdef(`LUSER_RELAY', `dnl
1295 # send unrecognized local users to a relay host
1296 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1297 R< > $+ + $* $: < ? $L > <+ $2> $(user $1 $) look up user+
1298 R< > $+ $: < ? $L > < > $(user $1 $) look up user
1299 R< ? $* > < $* > $+ <> $: < > $3 $2 found; strip $L
1300 R< ? $* > < $* > $+ $: < $1 > $3 $2 not found', `
1301 R< > $+ $: < $L > $(user $1 $) look up user
1302 R< $* > $+ <> $: < > $2 found; strip $L')
1303 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1304 R< $+ > $+ $: < $1 > $2 $&{Host}')
1307 ifdef(`MAIL_HUB', `dnl
1308 R< > $+ $: < $H > $1 try hub', `dnl')
1309 ifdef(`LOCAL_RELAY', `dnl
1310 R< > $+ $: < $R > $1 try relay', `dnl')
1311 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1312 R< > $+ $@ $1', `dnl
1313 R< > $+ $: < > < $1 <> $&h > nope, restore +detail
1314 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1315 R< > < $+ @ $+ <> + $* > $: < > < $1 + $3 @ $2 > check whether +detail')
1316 R< > < $+ <> + $* > $: < > < $1 + $2 > check whether +detail
1317 R< > < $+ <> $* > $: < > < $1 > else discard
1318 R< > < $+ + $* > $* < > < $1 > + $2 $3 find the user part
1319 R< > < $+ > + $* $#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}') strip the extra +
1320 R< > < $+ > $@ $1 no +detail
1321 R$+ $: $1 <> $&h add +detail back in
1322 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1323 R$+ @ $+ <> + $* $: $1 + $3 @ $2 check whether +detail')
1324 R$+ <> + $* $: $1 + $2 check whether +detail
1325 R$+ <> $* $: $1 else discard')
1326 R< local : $* > $* $: $>MailerToTriple < local : $1 > $2 no host extension
1327 R< error : $* > $* $: $>MailerToTriple < error : $1 > $2 no host extension
1328 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1329 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1330 R< $~[ : $+ > $+ @ $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
1331 R< $~[ : $+ > $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1332 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1333 R< $+ > $+ @ $+ $@ $>MailerToTriple < $1 > $2 < @ $3 >')
1334 R< $+ > $+ $@ $>MailerToTriple < $1 > $2 < @ $1 >
1336 ifdef(`_MAILER_TABLE_', `dnl
1337 ifdef(`_LDAP_ROUTING_', `dnl
1338 ###################################################################
1339 ### Ruleset LDAPMailertable -- mailertable lookup for LDAP ###
1340 dnl input: <Domain> FullAddress
1341 ###################################################################
1344 R< $+ > $* $: < $(mailertable $1 $) > $2 lookup
1345 R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check resolved?
1346 R< $+ > $* $: < $1 > $>Mailertable <$1> $2 try domain
1347 R< $+ > $#$* $#$2 found
1348 R< $+ > $* $#_RELAY_ $@ $1 $: $2 not found, direct relay',
1351 ###################################################################
1352 ### Ruleset 90 -- try domain part of mailertable entry ###
1353 dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1354 ###################################################################
1358 dnl %2 is not documented in cf/README
1359 R$* <$- . $+ > $* $: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1360 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1361 R$* <$~[ : $* > $* $>MailerToTriple < $2 : $3 > $4 check -- resolved?
1362 R$* < . $+ > $* $@ $>Mailertable $1 . <$2> $3 no -- strip & try again
1363 dnl is $2 always empty?
1364 R$* < $* > $* $: < $(mailertable . $@ $1$2 $) > $3 try "."
1365 R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 "." found?
1366 dnl return full address
1367 R< $* > $* $@ $2 no mailertable match',
1370 ###################################################################
1371 ### Ruleset 95 -- canonify mailer:[user@]host syntax to triple ###
1372 dnl input: in general: <[mailer:]host> lp<@domain>rest
1373 dnl <> address -> address
1374 dnl <error:d.s.n:text> -> error
1375 dnl <error:keyword:text> -> error
1376 dnl <error:text> -> error
1377 dnl <mailer:user@host> lp<@domain>rest -> mailer host user
1378 dnl <mailer:host> address -> mailer host address
1379 dnl <localdomain> address -> address
1380 dnl <host> address -> relay host address
1381 ###################################################################
1384 R< > $* $@ $1 strip off null relay
1385 R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4
1386 R< error : $- : $+ > $* $#error $@ $(dequote $1 $) $: $2
1387 R< error : $+ > $* $#error $: $1
1388 R< local : $* > $* $>CanonLocal < $1 > $2
1389 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1390 R< $~[ : $+ @ $+ > $*<$*>$* $# $1 $@ $3 $: $2<@$3> use literal user
1391 R< $~[ : $+ > $* $# $1 $@ $2 $: $3 try qualified mailer
1392 R< $=w > $* $@ $2 delete local host
1393 R< $+ > $* $#_RELAY_ $@ $1 $: $2 use unqualified mailer
1395 ###################################################################
1396 ### Ruleset CanonLocal -- canonify local: syntax ###
1397 dnl input: <user> address
1398 dnl <x> <@host> : rest -> Recurse rest
1399 dnl <x> p1 $=O p2 <@host> -> Recurse p1 $=O p2
1400 dnl <> user <@host> rest -> local user@host user
1401 dnl <> user -> local user user
1402 dnl <user@host> lp <@domain> rest -> <user> lp <@host> [cont]
1403 dnl <user> lp <@host> rest -> local lp@host user
1404 dnl <user> lp -> local lp user
1405 ###################################################################
1408 # strip local host from routed addresses
1409 R< $* > < @ $+ > : $+ $@ $>Recurse $3
1410 R< $* > $+ $=O $+ < @ $+ > $@ $>Recurse $2 $3 $4
1412 # strip trailing dot from any host name that may appear
1413 R< $* > $* < @ $* . > $: < $1 > $2 < @ $3 >
1415 # handle local: syntax -- use old user, either with or without host
1416 R< > $* < @ $* > $* $#_LOCAL_ $@ $1@$2 $: $1
1417 R< > $+ $#_LOCAL_ $@ $1 $: $1
1419 # handle local:user@host syntax -- ignore host part
1420 R< $+ @ $+ > $* < @ $* > $: < $1 > $3 < @ $4 >
1422 # handle local:user syntax
1423 R< $+ > $* <@ $* > $* $#_LOCAL_ $@ $2@$3 $: $1
1424 R< $+ > $* $#_LOCAL_ $@ $2 $: $1
1426 ###################################################################
1427 ### Ruleset 93 -- convert header names to masqueraded form ###
1428 ###################################################################
1432 ifdef(`_GENERICS_TABLE_', `dnl
1433 # handle generics database
1434 ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1435 dnl if generics should be applied add a @ as mark
1436 `R$+ < @ $* $=G . > $: < $1@$2$3 > $1 < @ $2$3 . > @ mark',
1437 `R$+ < @ $=G . > $: < $1@$2 > $1 < @ $2 . > @ mark')
1438 R$+ < @ *LOCAL* > $: < $1@$j > $1 < @ *LOCAL* > @ mark
1439 dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1440 dnl ignore the first case for now
1441 dnl if it has the mark look up full address
1442 dnl broken: %1 is full address not just detail
1443 R< $+ > $+ < $* > @ $: < $(generics $1 $: @ $1 $) > $2 < $3 >
1444 dnl workspace: ... or <match|@user@domain> user <@domain>
1445 dnl no match, try user+detail@domain:
1446 dnl look up user+*@domain and user@domain
1447 R<@$+ + $* @ $+> $+ < @ $+ >
1448 $: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) > $4 < @ $5 >
1449 R<@$+ + $* @ $+> $+ < @ $+ >
1450 $: < $(generics $1@$3 $: $) > $4 < @ $5 >
1451 dnl no match, remove mark
1452 R<@$+ > $+ < @ $+ > $: < > $2 < @ $3 >
1453 dnl no match, try @domain for exceptions
1454 R< > $+ < @ $+ . > $: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1455 dnl workspace: ... or <match> user <@domain>
1456 dnl no match, try local part
1457 R< > $+ < @ $+ > $: < $(generics $1 $: $) > $1 < @ $2 >
1458 R< > $+ + $* < @ $+ > $: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1459 R< > $+ + $* < @ $+ > $: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1460 R< $* @ $* > $* < $* > $@ $>canonify $1 @ $2 found qualified
1461 R< $+ > $* < $* > $: $>canonify $1 @ *LOCAL* found unqualified
1462 R< > $* $: $1 not found',
1465 # do not masquerade anything in class N
1466 R$* < @ $* $=N . > $@ $1 < @ $2 $3 . >
1468 ifdef(`MASQUERADE_NAME', `dnl
1469 # special case the users that should be exposed
1470 R$=E < @ *LOCAL* > $@ $1 < @ $j . > leave exposed
1471 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1472 `R$=E < @ $* $=M . > $@ $1 < @ $2 $3 . >',
1473 `R$=E < @ $=M . > $@ $1 < @ $2 . >')
1474 ifdef(`_LIMITED_MASQUERADE_', `dnl',
1475 `R$=E < @ $=w . > $@ $1 < @ $2 . >')
1477 # handle domain-specific masquerading
1478 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1479 `R$* < @ $* $=M . > $* $: $1 < @ $2 $3 . @ $M > $4 convert masqueraded doms',
1480 `R$* < @ $=M . > $* $: $1 < @ $2 . @ $M > $3 convert masqueraded doms')
1481 ifdef(`_LIMITED_MASQUERADE_', `dnl',
1482 `R$* < @ $=w . > $* $: $1 < @ $2 . @ $M > $3')
1483 R$* < @ *LOCAL* > $* $: $1 < @ $j . @ $M > $2
1484 R$* < @ $+ @ > $* $: $1 < @ $2 > $3 $M is null
1485 R$* < @ $+ @ $+ > $* $: $1 < @ $3 . > $4 $M is not null
1486 dnl', `dnl no masquerading
1487 dnl just fix *LOCAL* leftovers
1488 R$* < @ *LOCAL* > $@ $1 < @ $j . >')
1490 ###################################################################
1491 ### Ruleset 94 -- convert envelope names to masqueraded form ###
1492 ###################################################################
1495 ifdef(`_MASQUERADE_ENVELOPE_',
1496 `R$+ $@ $>MasqHdr $1',
1497 `R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2')
1499 ###################################################################
1500 ### Ruleset 98 -- local part of ruleset zero (can be null) ###
1501 ###################################################################
1504 undivert(3)dnl LOCAL_RULE_0
1506 ifdef(`_LDAP_ROUTING_', `dnl
1507 ######################################################################
1508 ### LDAPExpand: Expand address using LDAP routing
1511 ### <$1> -- parsed address (user < @ domain . >) (pass through)
1512 ### <$2> -- RFC822 address (user @ domain) (used for lookup)
1513 ### <$3> -- +detail information
1516 ### Mailer triplet ($#mailer $@ host $: address)
1517 ### Parsed address (user < @ domain . >)
1518 ######################################################################
1521 # do the LDAP lookups
1522 R<$+><$+><$*> $: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
1524 # look for temporary failures and...
1525 R<$* <TMPF>> <$*> <$+> <$+> <$*> $: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1526 R<$*> <$* <TMPF>> <$+> <$+> <$*> $: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1527 ifelse(_LDAP_ROUTE_MAPTEMP_, `_TEMPFAIL_', `dnl
1528 # ... temp fail RCPT SMTP commands
1529 R$={SMTPOpModes} $| TMPF <e r> $| $+ $#error $@ 4.3.0 $: _TMPFMSG_(`OPM')')
1530 # ... return original address for MTA to queue up
1531 R$* $| TMPF <$*> $| $+ $@ $3
1533 # if mailRoutingAddress and local or non-existent mailHost,
1534 # return the new mailRoutingAddress
1535 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1536 R<$+@$+> <$=w> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 $6 @ $2
1537 R<$+@$+> <> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 $5 @ $2')
1538 R<$+> <$=w> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1
1539 R<$+> <> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1
1542 # if mailRoutingAddress and non-local mailHost,
1543 # relay to mailHost with new mailRoutingAddress
1544 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1545 ifdef(`_MAILER_TABLE_', `dnl
1546 # check mailertable for host, relay from there
1547 R<$+@$+> <$+> <$+> <$+> <$*> $>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
1548 `R<$+@$+> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
1549 ifdef(`_MAILER_TABLE_', `dnl
1550 # check mailertable for host, relay from there
1551 R<$+> <$+> <$+> <$+> <$*> $>LDAPMailertable <$2> $>canonify $1',
1552 `R<$+> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $2 $: $>canonify $1')
1554 # if no mailRoutingAddress and local mailHost,
1555 # return original address
1556 R<> <$=w> <$+> <$+> <$*> $@ $2
1559 # if no mailRoutingAddress and non-local mailHost,
1560 # relay to mailHost with original address
1561 ifdef(`_MAILER_TABLE_', `dnl
1562 # check mailertable for host, relay from there
1563 R<> <$+> <$+> <$+> <$*> $>LDAPMailertable <$1> $2',
1564 `R<> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $1 $: $2')
1566 ifdef(`_LDAP_ROUTE_DETAIL_',
1567 `# if no mailRoutingAddress and no mailHost,
1568 # try without +detail
1569 R<> <> <$+> <$+ + $* @ $+> <> $@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
1571 ifdef(`_LDAP_ROUTE_NODOMAIN_', `
1572 # pretend we did the @domain lookup
1573 R<> <> <$+> <$+ @ $+> <$*> $: <> <> <$1> <@ $3> <$4>', `
1574 # if still no mailRoutingAddress and no mailHost,
1576 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1577 R<> <> <$+> <$+ + $* @ $+> <> $@ $>LDAPExpand <$1> <@ $4> <+$3>')
1578 R<> <> <$+> <$+ @ $+> <$*> $@ $>LDAPExpand <$1> <@ $3> <$4>')
1580 # if no mailRoutingAddress and no mailHost and this was a domain attempt,
1581 ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1582 # user does not exist
1583 R<> <> <$+> <@ $+> <$*> $: <?> < $&{addr_type} > < $1 >
1584 # only give error for envelope recipient
1585 R<?> <e r> <$+> $#error $@ nouser $: "550 User unknown"
1586 ifdef(`_LDAP_SENDER_MUST_EXIST_', `dnl
1587 # and the sender too
1588 R<?> <e s> <$+> $#error $@ nouser $: "550 User unknown"')
1589 R<?> <$*> <$+> $@ $2',
1591 # return the original address
1592 R<> <> <$+> <@ $+> <$*> $@ $1')
1596 ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1598 ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
1599 ######################################################################
1600 ### D: LookUpDomain -- search for domain in access database
1603 ### <$1> -- key (domain name)
1604 ### <$2> -- default (what to return if not found in db)
1605 dnl must not be empty
1606 ### <$3> -- mark (must be <(!|+) single-token>)
1607 ### ! does lookup only with tag
1608 ### + does lookup with and without tag
1609 ### <$4> -- passthru (additional data passed unchanged through)
1610 dnl returns: <default> <passthru>
1611 dnl <result> <passthru>
1612 ######################################################################
1615 dnl workspace <key> <default> <passthru> <mark>
1616 dnl look up with tag (in front, no delimiter here)
1618 R<$*> <$+> <$- $-> <$*> $: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1619 dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1620 dnl look up without tag?
1622 R<?> <$+> <$+> <+ $-> <$*> $: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1623 ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: look up .rest
1624 dnl XXX apply this also to IP addresses?
1625 dnl currently it works the wrong way round for [1.2.3.4]
1627 R<?> <$+.$+> <$+> <$- $-> <$*> $: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
1629 R<?> <$+.$+> <$+> <+ $-> <$*> $: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
1630 ifdef(`_ACCESS_SKIP_', `dnl
1631 dnl found SKIP: return <default> and <passthru>
1633 R<SKIP> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>', `dnl')
1634 dnl not found: IPv4 net (no check is done whether it is an IP number!)
1636 R<?> <[$+.$-]> <$+> <$- $-> <$*> $@ $>D <[$1]> <$3> <$4 $5> <$6>
1637 ifdef(`NO_NETINET6', `dnl',
1638 `dnl not found: IPv6 net
1639 dnl (could be merged with previous rule if we have a class containing .:)
1641 R<?> <[$+::$-]> <$+> <$- $-> <$*> $: $>D <[$1]> <$3> <$4 $5> <$6>
1642 R<?> <[$+:$-]> <$+> <$- $-> <$*> $: $>D <[$1]> <$3> <$4 $5> <$6>')
1643 dnl not found, but subdomain: try again
1645 R<?> <$+.$+> <$+> <$- $-> <$*> $@ $>D <$2> <$3> <$4 $5> <$6>
1646 ifdef(`_FFR_LOOKUPTAG_', `dnl look up Tag:
1648 R<?> <$+> <$+> <! $-> <$*> $: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
1649 dnl not found, no subdomain: return <default> and <passthru>
1651 R<?> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>
1652 ifdef(`_ATMPF_', `dnl tempfail?
1654 R<$* _ATMPF_> <$+> <$+> <$- $-> <$*> $@ <_ATMPF_> <$6>', `dnl')
1655 dnl return <result of lookup> and <passthru>
1657 R<$*> <$+> <$+> <$- $-> <$*> $@ <$1> <$6>
1659 ######################################################################
1660 ### A: LookUpAddress -- search for host address in access database
1663 ### <$1> -- key (dot quadded host address)
1664 ### <$2> -- default (what to return if not found in db)
1665 dnl must not be empty
1666 ### <$3> -- mark (must be <(!|+) single-token>)
1667 ### ! does lookup only with tag
1668 ### + does lookup with and without tag
1669 ### <$4> -- passthru (additional data passed through)
1670 dnl returns: <default> <passthru>
1671 dnl <result> <passthru>
1672 ######################################################################
1675 dnl look up with tag
1677 R<$+> <$+> <$- $-> <$*> $: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1678 dnl look up without tag
1680 R<?> <$+> <$+> <+ $-> <$*> $: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1681 dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
1682 ifdef(`_ACCESS_SKIP_', `dnl
1683 dnl found SKIP: return <default> and <passthru>
1685 R<SKIP> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>', `dnl')
1686 ifdef(`NO_NETINET6', `dnl',
1687 `dnl no match; IPv6: remove last part
1689 R<?> <$+::$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>
1690 R<?> <$+:$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>')
1691 dnl no match; IPv4: remove last part
1693 R<?> <$+.$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>
1694 dnl no match: return default
1696 R<?> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>
1697 ifdef(`_ATMPF_', `dnl tempfail?
1699 R<$* _ATMPF_> <$+> <$+> <$- $-> <$*> $@ <_ATMPF_> <$6>', `dnl')
1700 dnl match: return result
1702 R<$*> <$+> <$+> <$- $-> <$*> $@ <$1> <$6>
1703 dnl endif _ACCESS_TABLE_
1705 ######################################################################
1706 ### CanonAddr -- Convert an address into a standard form for
1707 ### relay checking. Route address syntax is
1708 ### crudely converted into a %-hack address.
1711 ### $1 -- full recipient address
1714 ### parsed address, not in source route form
1715 dnl user%host%host<@domain>
1716 dnl host!user<@domain>
1717 ######################################################################
1720 R$* $: $>Parse0 $>canonify $1 make domain canonical
1721 ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1722 R< @ $+ > : $* @ $* < @ $1 > : $2 % $3 change @ to % in src route
1723 R$* < @ $+ > : $* : $* $3 $1 < @ $2 > : $4 change to % hack.
1724 R$* < @ $+ > : $* $3 $1 < @ $2 >
1727 ######################################################################
1728 ### ParseRecipient -- Strip off hosts in $=R as well as possibly
1729 ### $* $=m or the access database.
1730 ### Check user portion for host separators.
1733 ### $1 -- full recipient address
1736 ### parsed, non-local-relaying address
1737 ######################################################################
1740 dnl mark and canonify address
1741 R$* $: <?> $>CanonAddr $1
1742 dnl workspace: <?> localpart<@domain[.]>
1743 R<?> $* < @ $* . > <?> $1 < @ $2 > strip trailing dots
1744 dnl workspace: <?> localpart<@domain>
1745 R<?> $- < @ $* > $: <?> $(dequote $1 $) < @ $2 > dequote local part
1747 # if no $=O character, no host in the user portion, we are done
1748 R<?> $* $=O $* < @ $* > $: <NO> $1 $2 $3 < @ $4>
1749 dnl no $=O in localpart: return
1752 dnl workspace: <NO> localpart<@domain>, where localpart contains $=O
1753 dnl mark everything which has an "authorized" domain with <RELAY>
1754 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1755 # if we relay, check username portion for user%host so host can be checked also
1756 R<NO> $* < @ $* $=m > $: <RELAY> $1 < @ $2 $3 >', `dnl')
1757 dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1758 dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1760 dnl what if access map returns something else than RELAY?
1761 dnl we are only interested in RELAY entries...
1762 dnl other To: entries: blocklist recipient; generic entries?
1763 dnl if it is an error we probably do not want to relay anyway
1764 ifdef(`_RELAY_HOSTS_ONLY_',
1765 `R<NO> $* < @ $=R > $: <RELAY> $1 < @ $2 >
1766 ifdef(`_ACCESS_TABLE_', `dnl
1767 R<NO> $* < @ $+ > $: <$(access To:$2 $: NO $)> $1 < @ $2 >
1768 R<NO> $* < @ $+ > $: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1769 `R<NO> $* < @ $* $=R > $: <RELAY> $1 < @ $2 $3 >
1770 ifdef(`_ACCESS_TABLE_', `dnl
1771 R<NO> $* < @ $+ > $: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
1772 R<$+> <$+> $: <$1> $2',`dnl')')
1775 ifdef(`_RELAY_MX_SERVED_', `dnl
1776 dnl do "we" ($=w) act as backup MX server for the destination domain?
1777 R<NO> $* < @ $+ > $: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1778 R<MX> < : $* <TEMP> : > $* $#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
1779 dnl yes: mark it as <RELAY>
1780 R<MX> < $* : $=w. : $* > < $+ > $: <RELAY> $4
1781 dnl no: put old <NO> mark back
1782 R<MX> < : $* : > < $+ > $: <NO> $2', `dnl')
1784 dnl do we relay to this recipient domain?
1785 R<RELAY> $* < @ $* > $@ $>ParseRecipient $1
1790 ######################################################################
1791 ### check_relay -- check hostname/address on SMTP startup
1792 ######################################################################
1794 ifdef(`_CONTROL_IMMEDIATE_',`dnl
1796 ifdef(`_RATE_CONTROL_IMMEDIATE_',`dnl
1797 dnl workspace: ignored...
1798 R$* $: $>"RateControl" dummy', `dnl')
1799 ifdef(`_CONN_CONTROL_IMMEDIATE_',`dnl
1800 dnl workspace: ignored...
1801 R$* $: $>"ConnControl" dummy', `dnl')
1806 ifdef(`_USE_CLIENT_PTR_',`dnl
1807 R$* $| $* $: $&{client_ptr} $| $2', `dnl')
1808 R$* $: $1 $| $>"Local_check_relay" $1
1809 R$* $| $* $| $#$* $#$3
1810 R$* $| $* $| $* $@ $>"Basic_check_relay" $1 $| $2
1813 # check for deferred delivery mode
1814 R$* $: < $&{deliveryMode} > $1
1815 R< d > $* $@ deferred
1818 ifdef(`_ACCESS_TABLE_', `dnl
1819 dnl workspace: {client_name} $| {client_addr}
1820 R$+ $| $+ $: $>D < $1 > <?> <+ Connect> < $2 >
1821 dnl workspace: <result-of-lookup> <{client_addr}>
1822 dnl OR $| $+ if client_name is empty
1823 R $| $+ $: $>A < $1 > <?> <+ Connect> <> empty client_name
1824 dnl workspace: <result-of-lookup> <{client_addr}>
1825 R<?> <$+> $: $>A < $1 > <?> <+ Connect> <> no: another lookup
1826 dnl workspace: <result-of-lookup> (<>|<{client_addr}>)
1827 R<?> <$*> $: OK found nothing
1828 dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
1829 R<$={Accept}> <$*> $@ $1 return value of lookup
1830 R<REJECT> <$*> $#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
1831 R<DISCARD> <$*> $#discard $: discard
1832 R<QUARANTINE:$+> <$*> $#error $@ quarantine $: $1
1834 R<ERROR:$-.$-.$-:$+> <$*> $#error $@ $1.$2.$3 $: $4
1835 R<ERROR:$+> <$*> $#error $: $1
1836 ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*> $#error $@ 4.3.0 $: _TMPFMSG_(`CR')', `dnl')
1837 dnl generic error from access map
1838 R<$+> <$*> $#error $: $1', `dnl')
1841 # DNS based IP address spam list
1842 dnl workspace: ignored...
1843 R$* $: $&{client_addr}
1844 R$-.$-.$-.$- $: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1846 R<?>$+ $#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
1848 ifdef(`_RATE_CONTROL_',`dnl
1849 ifdef(`_RATE_CONTROL_IMMEDIATE_',`', `dnl
1850 dnl workspace: ignored...
1851 R$* $: $>"RateControl" dummy')', `dnl')
1852 ifdef(`_CONN_CONTROL_',`dnl
1853 ifdef(`_CONN_CONTROL_IMMEDIATE_',`',`dnl
1854 dnl workspace: ignored...
1855 R$* $: $>"ConnControl" dummy')', `dnl')
1856 undivert(8)dnl LOCAL_DNSBL
1857 ifdef(`_REQUIRE_RDNS_', `dnl
1858 R$* $: $&{client_addr} $| $&{client_resolve}
1859 R$=R $* $@ RELAY We relay for these
1860 R$* $| OK $@ OK Resolves.
1861 R$* $| FAIL $#error $@ 5.7.1 $: 550 Fix reverse DNS for $1
1862 R$* $| TEMP $#error $@ 4.1.8 $: 451 Client IP address $1 does not resolve
1863 R$* $| FORGED $#error $@ 4.1.8 $: 451 Possibly forged hostname for $1
1866 ######################################################################
1867 ### check_mail -- check SMTP ``MAIL FROM:'' command argument
1868 ######################################################################
1872 R$* $: $1 $| $>"Local_check_mail" $1
1874 R$* $| $* $@ $>"Basic_check_mail" $1
1877 # check for deferred delivery mode
1878 R$* $: < $&{deliveryMode} > $1
1879 R< d > $* $@ deferred
1883 dnl done first: we can require authentication for every mail transaction
1884 dnl workspace: address as given by MAIL FROM: (sender)
1885 R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL
1887 dnl undo damage: remove result of tls_client call
1890 dnl workspace: address as given by MAIL FROM:
1891 R<> $@ <OK> we MUST accept <> (RFC 1123)
1892 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1893 dnl do some additional checks
1895 dnl no user@localhost (if nonlocal sender)
1896 dnl this is a pretty simple canonification, it will not catch every case
1897 dnl just make sure the address has <> around it (which is required by
1898 dnl the RFC anyway, maybe we should complain if they are missing...)
1899 dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1900 dnl not be modified by host lookups.
1902 R<?><$+> $: <@> <$1>
1904 dnl workspace: <@> <address>
1905 dnl prepend daemon_flags
1906 R$* $: $&{daemon_flags} $| $1
1907 dnl workspace: ${daemon_flags} $| <@> <address>
1908 dnl do not allow these at all or only from local systems?
1909 R$* f $* $| <@> < $* @ $- > $: < ? $&{client_name} > < $3 @ $4 >
1910 dnl accept unqualified sender: change mark to avoid test
1911 R$* u $* $| <@> < $* > $: <?> < $3 >
1912 dnl workspace: ${daemon_flags} $| <@> <address>
1913 dnl or: <? ${client_name} > <address>
1914 dnl or: <?> <address>
1915 dnl remove daemon_flags
1917 # handle case of @localhost on address
1918 R<@> < $* @ localhost > $: < ? $&{client_name} > < $1 @ localhost >
1919 R<@> < $* @ [127.0.0.1] >
1920 $: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1921 R<@> < $* @ [IPv6:0:0:0:0:0:0:0:1] >
1922 $: < ? $&{client_name} > < $1 @ [IPv6:0:0:0:0:0:0:0:1] >
1923 R<@> < $* @ [IPv6:::1] >
1924 $: < ? $&{client_name} > < $1 @ [IPv6:::1] >
1925 R<@> < $* @ localhost.$m >
1926 $: < ? $&{client_name} > < $1 @ localhost.$m >
1927 ifdef(`_NO_UUCP_', `dnl',
1928 `R<@> < $* @ localhost.UUCP >
1929 $: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1930 dnl workspace: < ? $&{client_name} > <user@localhost|host>
1931 dnl or: <@> <address>
1932 dnl or: <?> <address> (thanks to u in ${daemon_flags})
1933 R<@> $* $: $1 no localhost as domain
1934 dnl workspace: < ? $&{client_name} > <user@localhost|host>
1936 dnl or: <?> <address> (thanks to u in ${daemon_flags})
1937 R<? $=w> $* $: $2 local client: ok
1938 R<? $+> <$+> $#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
1939 dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1941 dnl workspace: address (or <address>)
1942 R$* $: <?> $>CanonAddr $1 canonify sender address and mark it
1943 dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1944 dnl there is nothing behind the <@host> so no trailing $* needed
1945 R<?> $* < @ $+ . > <?> $1 < @ $2 > strip trailing dots
1946 # handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1947 R<?> $* < @ $* $=P > $: <_RES_OK_> $1 < @ $2 $3 >
1948 dnl workspace <mark> CanonicalAddress where mark is ? or OK
1949 dnl A sender address with my local host name ($j) is safe
1950 R<?> $* < @ $j > $: <_RES_OK_> $1 < @ $j >
1951 ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1952 `R<?> $* < @ $+ > $: <_RES_OK_> $1 < @ $2 > ... unresolvable OK',
1953 `R<?> $* < @ $+ > $: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1954 R<? $* <$->> $* < @ $+ >
1955 $: <$2> $3 < @ $4 >')
1956 dnl workspace <mark> CanonicalAddress where mark is ?, _RES_OK_, PERM, TEMP
1957 dnl mark is ? iff the address is user (wo @domain)
1959 ifdef(`_ACCESS_TABLE_', `dnl
1960 # check sender address: user@address, user@, address
1961 dnl should we remove +ext from user?
1962 dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
1963 R<$+> $+ < @ $* > $: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
1964 R<$+> $+ $: @<$1> <$2> $| <U:$2@>
1965 dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1966 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1967 dnl will only return user<@domain when "reversing" the args
1968 R@ <$+> <$*> $| <$+> $: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
1969 dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1970 R<@> <$+> <$*> $| <$*> $: <$3> <$1> <$2> reverse result
1971 dnl workspace: <result> <mark> <CanonicalAddress>
1972 # retransform for further use
1974 dnl <ResultOfLookup|mark> CanonicalAddress
1975 R<?> <$+> <$*> $: <$1> $2 no match
1976 R<$+> <$+> <$*> $: <$1> $3 relevant result, keep it', `dnl')
1977 dnl workspace <ResultOfLookup|mark> CanonicalAddress
1978 dnl mark is ? iff the address is user (wo @domain)
1980 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1981 # handle case of no @domain on address
1982 dnl prepend daemon_flags
1983 R<?> $* $: $&{daemon_flags} $| <?> $1
1984 dnl accept unqualified sender: change mark to avoid test
1985 R$* u $* $| <?> $* $: <_RES_OK_> $3
1986 dnl remove daemon_flags
1988 R<?> $* $: < ? $&{client_addr} > $1
1989 R<?> $* $@ <_RES_OK_> ...local unqualed ok
1990 R<? $+> $* $#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
1993 R<?> $* $: @ $1 mark address: nothing known about it
1994 R<$={ResOk}> $* $: @ $2 domain ok
1995 R<TEMP> $* $#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1996 R<PERM> $* $#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
1997 ifdef(`_ACCESS_TABLE_', `dnl
1998 R<$={Accept}> $* $# $1 accept from access map
1999 R<DISCARD> $* $#discard $: discard
2000 R<QUARANTINE:$+> $* $#error $@ quarantine $: $1
2001 R<REJECT> $* $#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
2003 R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4
2004 R<ERROR:$+> $* $#error $: $1
2005 ifdef(`_ATMPF_', `R<_ATMPF_> $* $#error $@ 4.3.0 $: _TMPFMSG_(`CM')', `dnl')
2006 dnl generic error from access map
2007 R<$+> $* $#error $: $1 error from access db',
2009 dnl workspace: @ CanonicalAddress (i.e. address in canonical form localpart<@host>)
2011 ifdef(`_BADMX_CHK_', `dnl
2012 R@ $*<@$+>$* $: $1<@$2>$3 $| $>BadMX $2
2016 # Look up MX records and ferret away a copy of the original address.
2017 # input: domain part of address to check
2018 R$+ $:<MX><$1><:$(mxlist $1$):><:>
2019 # workspace: <MX><domain><: mxlist-result $><:>
2020 R<MX><$+><:$*<TEMP>:><$*> $#error $@ 4.1.2 $: "450 MX lookup failure for "$1
2021 # workspace: <MX> <original destination> <unchecked mxlist> <checked mxlist>
2022 # Recursively run badmx check on each mx.
2023 R<MX><$*><:$+:$*><:$*> <MX><$1><:$3><: $4 $(badmx $2 $):>
2024 # See if any of them fail.
2025 R<MX><$*><$*><$*<BADMX>:$*> $#error $@ 5.1.2 $:"550 Illegal MX record for host "$1
2026 # Reverse the mxlists so we can use the same argument order again.
2027 R<MX><$*><$*><$*> $:<MX><$1><$3><$2>
2028 R<MX><$*><:$+:$*><:$*> <MX><$1><:$3><:$4 $(dnsA $2 $) :>
2030 # Reverse the lists so we can use the same argument order again.
2031 R<MX><$*><$*><$*> $:<MX><$1><$3><$2>
2032 R<MX><$*><:$+:$*><:$*> <MX><$1><:$3><:$4 $(BadMXIP $2 $) :>
2034 R<MX><$*><$*><$*<BADMXIP>:$*> $#error $@ 5.1.2 $:"550 Invalid MX record for host "$1',
2038 ######################################################################
2039 ### check_rcpt -- check SMTP ``RCPT TO:'' command argument
2040 ######################################################################
2044 R$* $: $1 $| $>"Local_check_rcpt" $1
2046 R$* $| $* $@ $>"Basic_check_rcpt" $1
2050 R<> $#error $@ nouser $: "553 User address required"
2051 R$@ $#error $@ nouser $: "553 User address required"
2052 # check for deferred delivery mode
2053 R$* $: < $&{deliveryMode} > $1
2054 R< d > $* $@ deferred
2057 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2058 dnl this code checks for user@host where host is not a FQHN.
2059 dnl it is not activated.
2060 dnl notice: code to check for a recipient without a domain name is
2061 dnl available down below; look for the same macro.
2062 dnl this check is done here because the name might be qualified by the
2063 dnl canonicalization.
2064 # require fully qualified domain part?
2065 dnl very simple canonification: make sure the address is in < >
2067 R<?> <$+> $: <@> <$1>
2069 R<@> < postmaster > $: postmaster
2070 R<@> < $* @ $+ . $+ > $: < $1 @ $2 . $3 >
2071 dnl prepend daemon_flags
2072 R<@> $* $: $&{daemon_flags} $| <@> $1
2073 dnl workspace: ${daemon_flags} $| <@> <address>
2074 dnl _r_equire qual.rcpt: ok
2075 R$* r $* $| <@> < $+ @ $+ > $: < $3 @ $4 >
2076 dnl do not allow these at all or only from local systems?
2077 R$* r $* $| <@> < $* > $: < ? $&{client_name} > < $3 >
2079 R<? $=w> < $* > $: <$1>
2080 R<? $+> <$+> $#error $@ 5.5.4 $: "553 Fully qualified domain name required"
2081 dnl remove daemon_flags for other cases
2082 R$* $| <@> $* $: $2', `dnl')
2084 dnl ##################################################################
2085 dnl call subroutines for recipient and relay
2086 dnl possible returns from subroutines:
2087 dnl $#TEMP temporary failure
2088 dnl $#error permanent failure (or temporary if from access map)
2089 dnl $#other stop processing
2090 dnl RELAY RELAYing allowed
2092 ######################################################################
2093 R$* $: $1 $| @ $>"Rcpt_ok" $1
2094 dnl temporary failure? remove mark @ and remember
2095 R$* $| @ $#TEMP $+ $: $1 $| T $2
2096 dnl error or ok (stop)
2098 ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2099 R$* $| @ RELAY $@ RELAY
2100 dnl something else: call check sender (relay)
2101 R$* $| @ $* $: O $| $>"Relay_ok" $1
2102 dnl temporary failure: call check sender (relay)
2103 R$* $| T $+ $: T $2 $| $>"Relay_ok" $1
2104 dnl temporary failure? return that
2105 R$* $| $#TEMP $+ $#error $2
2106 dnl error or ok (stop)
2108 R$* $| RELAY $@ RELAY
2109 dnl something else: return previous temp failure
2110 R T $+ $| $* $#error $1
2111 # anything else is bogus
2112 R$* $#error $@ 5.7.1 $: confRELAY_MSG
2115 ######################################################################
2116 ### Rcpt_ok: is the recipient ok?
2117 dnl input: recipient address (RCPT TO)
2118 dnl output: see explanation at call
2119 ######################################################################
2121 ifdef(`_LOOSE_RELAY_CHECK_',`dnl
2122 R$* $: $>CanonAddr $1
2123 R$* < @ $* . > $1 < @ $2 > strip trailing dots',
2124 `R$* $: $>ParseRecipient $1 strip relayable hosts')
2126 ifdef(`_BESTMX_IS_LOCAL_',`dnl
2127 ifelse(_BESTMX_IS_LOCAL_, `', `dnl
2129 R$* < @ $* > $* $: $1 < @ $2 @@ $(bestmx $2 $) > $3',
2131 # limit bestmx to $=B
2132 R$* < @ $* $=B > $* $: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
2133 R$* $=O $* < @ $* @@ $=w . > $* $@ $>"Rcpt_ok" $1 $2 $3
2134 R$* < @ $* @@ $=w . > $* $: $1 < @ $3 > $4
2135 R$* < @ $* @@ $* > $* $: $1 < @ $2 > $4')
2137 ifdef(`_BLOCKLIST_RCPT_',`dnl
2138 ifdef(`_ACCESS_TABLE_', `dnl
2139 # blocklist local users or any host from receiving mail
2141 dnl user is now tagged with @ to be consistent with check_mail
2142 dnl and to distinguish users from hosts (com would be host, com@ would be user)
2143 R<?> $+ < @ $=w > $: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
2144 R<?> $+ < @ $* > $: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
2145 R<?> $+ $: <> <$1> $| <U:$1@>
2146 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
2147 dnl will only return user<@domain when "reversing" the args
2148 R<> <$*> $| <$+> $: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
2149 R<@> <$*> $| <$*> $: <$2> <$1> reverse result
2150 R<?> <$*> $: @ $1 mark address as no match
2151 dnl we may have to filter here because otherwise some RHSs
2152 dnl would be interpreted as generic error messages...
2153 dnl error messages should be "tagged" by prefixing them with error: !
2154 dnl that would make a lot of things easier.
2155 R<$={Accept}> <$*> $: @ $2 mark address as no match
2156 ifdef(`_ACCESS_SKIP_', `dnl
2157 R<SKIP> <$*> $: @ $1 mark address as no match', `dnl')
2158 ifdef(`_DELAY_COMPAT_8_10_',`dnl
2159 dnl compatility with 8.11/8.10:
2160 dnl we have to filter these because otherwise they would be interpreted
2161 dnl as generic error message...
2162 dnl error messages should be "tagged" by prefixing them with error: !
2163 dnl that would make a lot of things easier.
2164 dnl maybe we should stop checks already here (if SPAM_xyx)?
2165 R<$={SpamTag}> <$*> $: @ $2 mark address as no match')
2166 R<REJECT> $* $#error $@ 5.2.1 $: confRCPTREJ_MSG
2167 R<DISCARD> $* $#discard $: discard
2168 R<QUARANTINE:$+> $* $#error $@ quarantine $: $1
2170 R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4
2171 R<ERROR:$+> $* $#error $: $1
2172 ifdef(`_ATMPF_', `R<_ATMPF_> $* $#error $@ 4.3.0 $: _TMPFMSG_(`ROK1')', `dnl')
2173 dnl generic error from access map
2174 R<$+> $* $#error $: $1 error from access db
2175 R@ $* $1 remove mark', `dnl')', `dnl')
2177 ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2178 # authenticated via TLS?
2179 R$* $: $1 $| $>RelayTLS client authenticated?
2180 R$* $| $# $+ $# $2 error/ok?
2183 R$* $: $1 $| $>"Local_Relay_Auth" $&{auth_type}
2184 dnl workspace: localpart<@domain> $| result of Local_Relay_Auth
2186 dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
2188 R$* $| $* $: $1 $| $&{auth_type}
2189 dnl workspace: localpart<@domain> [ $| ${auth_type} ]
2190 dnl empty ${auth_type}?
2192 dnl mechanism ${auth_type} accepted?
2193 dnl use $# to override further tests (delay_checks): see check_rcpt below
2194 R$* $| $={TrustAuthMech} $# RELAY
2195 dnl remove ${auth_type}
2197 dnl workspace: localpart<@domain> | localpart
2198 ifelse(defn(`_NO_UUCP_'), `r',
2199 `R$* ! $* < @ $* > $: <REMOTE> $2 < @ BANG_PATH >
2200 R$* ! $* $: <REMOTE> $2 < @ BANG_PATH >', `dnl')
2201 ifelse(defn(`_NO_PERCENTHACK_'), `r',
2202 `R$* % $* < @ $* > $: <REMOTE> $1 < @ PERCENT_HACK >
2203 R$* % $* $: <REMOTE> $1 < @ PERCENT_HACK >', `dnl')
2204 # anything terminating locally is ok
2205 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2206 R$+ < @ $* $=m > $@ RELAY', `dnl')
2207 R$+ < @ $=w > $@ RELAY
2208 ifdef(`_RELAY_HOSTS_ONLY_',
2209 `R$+ < @ $=R > $@ RELAY
2210 ifdef(`_ACCESS_TABLE_', `dnl
2211 ifdef(`_RELAY_FULL_ADDR_', `dnl
2212 R$+ < @ $+ > $: <$(access To:$1@$2 $: ? $)> <$1 < @ $2 >>
2213 R<?> <$+ < @ $+ >> $: <$(access To:$2 $: ? $)> <$1 < @ $2 >>',`
2214 R$+ < @ $+ > $: <$(access To:$2 $: ? $)> <$1 < @ $2 >>')
2215 dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2216 R<?> <$+ < @ $+ >> $: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
2217 `R$+ < @ $* $=R > $@ RELAY
2218 ifdef(`_ACCESS_TABLE_', `dnl
2219 ifdef(`_RELAY_FULL_ADDR_', `dnl
2220 R$+ < @ $+ > $: $1 < @ $2 > $| $>SearchList <+ To> $| <F:$1@$2> <D:$2> <F:$1@> <>
2221 R$+ < @ $+ > $| <$*> $: <$3> <$1 <@ $2>>
2222 R$+ < @ $+ > $| $* $: <$3> <$1 <@ $2>>',
2223 `R$+ < @ $+ > $: $>D <$2> <?> <+ To> <$1 < @ $2 >>')')')
2224 ifdef(`_ACCESS_TABLE_', `dnl
2225 dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2226 R<RELAY> $* $@ RELAY
2227 ifdef(`_ATMPF_', `R<$* _ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`ROK2')', `dnl')
2228 R<$*> <$*> $: $2',`dnl')
2231 ifdef(`_RELAY_MX_SERVED_', `dnl
2232 # allow relaying for hosts which we MX serve
2233 R$+ < @ $+ > $: < : $(mxserved $2 $) : > $1 < @ $2 >
2234 dnl this must not necessarily happen if the client is checked first...
2235 R< : $* <TEMP> : > $* $#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
2236 R<$* : $=w . : $*> $* $@ RELAY
2237 R< : $* : > $* $: $2',
2240 # check for local user (i.e. unqualified address)
2242 R<?> $* < @ $+ > $: <REMOTE> $1 < @ $2 >
2244 dnl is it really? the standard requires user@domain, not just user
2245 dnl but we should accept it anyway (maybe making it an option:
2247 dnl postmaster must be accepted without domain (DRUMS)
2248 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2249 R<?> postmaster $@ OK
2250 # require qualified recipient?
2251 dnl prepend daemon_flags
2252 R<?> $+ $: $&{daemon_flags} $| <?> $1
2253 dnl workspace: ${daemon_flags} $| <?> localpart
2254 dnl do not allow these at all or only from local systems?
2255 dnl r flag? add client_name
2256 R$* r $* $| <?> $+ $: < ? $&{client_name} > <?> $3
2257 dnl no r flag: relay to local user (only local part)
2258 # no qualified recipient required
2259 R$* $| <?> $+ $@ RELAY
2260 dnl client_name is empty
2261 R<?> <?> $+ $@ RELAY
2262 dnl client_name is local
2263 R<? $=w> <?> $+ $@ RELAY
2264 dnl client_name is not local
2265 R<? $+> $+ $#error $@ 5.5.4 $: "553 Domain name required"', `dnl
2266 dnl no qualified recipient required
2268 dnl it is a remote user: remove mark and then check client
2270 dnl currently the recipient address is not used below
2272 ######################################################################
2273 ### Relay_ok: is the relay/sender ok?
2275 dnl output: see explanation at call
2276 ######################################################################
2278 # anything originating locally is ok
2280 R$* $: $&{client_addr}
2281 R$@ $@ RELAY originated locally
2282 R0 $@ RELAY originated locally
2283 R127.0.0.1 $@ RELAY originated locally
2284 RIPv6:0:0:0:0:0:0:0:1 $@ RELAY originated locally
2285 dnl if compiled with IPV6_FULL=0
2286 RIPv6:::1 $@ RELAY originated locally
2287 R$=R $* $@ RELAY relayable IP address
2288 ifdef(`_ACCESS_TABLE_', `dnl
2289 R$* $: $>A <$1> <?> <+ Connect> <$1>
2290 R<RELAY> $* $@ RELAY relayable IP address
2291 ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
2292 dnl this will cause rejections in cases like:
2293 dnl Connect:My.Host.Domain RELAY
2294 dnl Connect:My.Net REJECT
2295 dnl since in check_relay client_name is checked before client_addr
2296 R<REJECT> $* $@ REJECT rejected IP address')
2297 ifdef(`_ATMPF_', `R<_ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK1')', `dnl')
2298 R<$*> <$*> $: $2', `dnl')
2299 R$* $: [ $1 ] put brackets around it...
2300 R$=w $@ RELAY ... and see if it is local
2302 ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2303 ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2304 ifdef(`_RELAY_MAIL_FROM_', `dnl
2305 dnl input: {client_addr} or something "broken"
2306 dnl just throw the input away; we do not need it.
2307 # check whether FROM is allowed to use system as relay
2308 R$* $: <?> $>CanonAddr $&f
2309 R<?> $+ < @ $+ . > <?> $1 < @ $2 > remove trailing dot
2310 ifdef(`_RELAY_LOCAL_FROM_', `dnl
2311 # check whether local FROM is ok
2312 R<?> $+ < @ $=w > $@ RELAY FROM local', `dnl')
2313 ifdef(`_RELAY_DB_FROM_', `dnl
2314 R<?> $+ < @ $+ > $: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
2315 R<@> <RELAY> $@ RELAY RELAY FROM sender ok
2316 ifdef(`_ATMPF_', `R<@> <_ATMPF_> $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK2')', `dnl')
2318 ifdef(`_RELAY_DB_FROM_DOMAIN_',
2319 `errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
2323 dnl notice: the rulesets above do not leave a unique workspace behind.
2324 dnl it does not matter in this case because the following rule ignores
2325 dnl the input. otherwise these rules must "clean up" the workspace.
2327 # check client name: first: did it resolve?
2329 R$* $: < $&{client_resolve} >
2330 R<TEMP> $#TEMP $@ 4.4.0 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
2331 R<FORGED> $#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
2332 R<FAIL> $#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
2333 dnl ${client_resolve} should be OK, so go ahead
2334 R$* $: <@> $&{client_name}
2335 dnl should not be necessary since it has been done for client_addr already
2336 dnl this rule actually may cause a problem if {client_name} resolves to ""
2337 dnl however, this should not happen since the forward lookup should fail
2338 dnl and {client_resolve} should be TEMP or FAIL.
2339 dnl nevertheless, removing the rule doesn't hurt.
2341 dnl workspace: <@> ${client_name} (not empty)
2342 # pass to name server to make hostname canonical
2343 R<@> $* $=P $:<?> $1 $2
2344 R<@> $+ $:<?> $[ $1 $]
2345 dnl workspace: <?> ${client_name} (canonified)
2346 R$* . $1 strip trailing dots
2347 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2348 R<?> $* $=m $@ RELAY', `dnl')
2350 ifdef(`_RELAY_HOSTS_ONLY_',
2352 ifdef(`_ACCESS_TABLE_', `dnl
2353 R<?> $* $: <$(access Connect:$1 $: ? $)> <$1>
2354 R<?> <$*> $: <$(access $1 $: ? $)> <$1>',`dnl')',
2355 `R<?> $* $=R $@ RELAY
2356 ifdef(`_ACCESS_TABLE_', `dnl
2357 R<?> $* $: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
2358 ifdef(`_ACCESS_TABLE_', `dnl
2359 R<RELAY> $* $@ RELAY
2360 ifdef(`_ATMPF_', `R<$* _ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK3')', `dnl')
2361 R<$*> <$*> $: $2',`dnl')
2362 dnl end of _PROMISCUOUS_RELAY_
2364 ifdef(`_DELAY_CHECKS_',`dnl
2365 # turn a canonical address in the form user<@domain>
2366 # qualify unqual. addresses with $j
2367 dnl it might have been only user (without <@domain>)
2369 R$* <@ $+ . > $1 <@ $2 >
2370 R$* <@ $* > $@ $1 <@ $2 >
2375 dnl code repeated here from Basic_check_mail
2376 dnl only called from check_rcpt in delay mode if checkrcpt returns $#
2377 R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL
2379 dnl return result from checkrcpt
2385 dnl code repeated here from Basic_check_mail
2386 dnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
2387 R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL
2389 dnl return result from friend/hater check
2393 # call all necessary rulesets
2395 dnl this test should be in the Basic_check_rcpt ruleset
2396 dnl which is the correct DSN code?
2397 # R$@ $#error $@ 5.1.3 $: "553 Recipient address required"
2399 R$+ $: $1 $| $>checkrcpt $1
2400 dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2401 dnl on error (or discard) stop now
2402 R$+ $| $#error $* $#error $2
2403 R$+ $| $#discard $* $#discard $2
2404 dnl otherwise call tls_client; see above
2405 R$+ $| $#$* $@ $>"Delay_TLS_Clt" $2
2406 R$+ $| $* $: <?> $>FullAddr $>CanonAddr $1
2408 `dnl look up user@ and user@address
2409 ifdef(`_ACCESS_TABLE_', `',
2410 `errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
2412 dnl one of the next two rules is supposed to match
2413 dnl this code has been copied from BLOCKLIST... etc
2414 dnl and simplified by omitting some < >.
2415 R<?> $+ < @ $=w > $: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
2416 R<?> $+ < @ $* > $: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
2417 dnl R<?> $@ something_is_very_wrong_here
2418 # look up the addresses only with Spam tag
2419 R<> $* $| <$+> $: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
2420 R<@> $* $| $* $: $2 $1 reverse result
2422 ifdef(`_SPAM_FRIEND_',
2423 `# is the recipient a spam friend?
2424 ifdef(`_SPAM_HATER_',
2425 `errprint(`*** ERROR: define either Hater or Friend -- not both.
2427 R<FRIEND> $+ $@ $>"Delay_TLS_Clt2" SPAMFRIEND
2430 ifdef(`_SPAM_HATER_',
2431 `# is the recipient no spam hater?
2432 R<HATER> $+ $: $1 spam hater: continue checks
2433 R<$*> $+ $@ $>"Delay_TLS_Clt2" NOSPAMHATER everyone else: stop
2436 dnl run further checks: check_mail
2437 dnl should we "clean up" $&f?
2438 ifdef(`_FFR_MAIL_MACRO',
2439 `R$* $: $1 $| $>checkmail $&{mail_from}',
2440 `R$* $: $1 $| $>checkmail <$&f>')
2441 dnl recipient (canonical format) $| result of checkmail
2443 dnl run further checks: check_relay
2444 R$* $| $* $: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
2449 ifdef(`_BLOCK_BAD_HELO_', `dnl
2450 R$* $: $1 $| <$&{auth_authen}> Get auth info
2451 dnl Bypass the test for users who have authenticated.
2452 R$* $| <$+> $: $1 skip if auth
2453 R$* $| <$*> $: $1 $| <$&{client_addr}> [$&s] Get connection info
2454 dnl Bypass for local clients -- IP address starts with $=R
2455 R$* $| <$=R $*> [$*] $: $1 skip if local client
2456 dnl Bypass a "sendmail -bs" session, which use 0 for client ip address
2457 R$* $| <0> [$*] $: $1 skip if sendmail -bs
2458 dnl Reject our IP - assumes "[ip]" is in class $=w
2459 R$* $| <$*> $=w $#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2460 dnl Reject our hostname
2461 R$* $| <$*> [$=w] $#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2462 dnl Pass anything else with a "." in the domain parameter
2463 R$* $| <$*> [$+.$+] $: $1 qualified domain ok
2464 dnl Pass IPv6: address literals
2465 R$* $| <$*> [IPv6:$+] $: $1 qualified domain ok
2466 dnl Reject if there was no "." or only an initial or final "."
2467 R$* $| <$*> [$*] $#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2468 dnl Clean up the workspace
2472 ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
2473 ######################################################################
2474 ### F: LookUpFull -- search for an entry in access database
2476 ### lookup of full key (which should be an address) and
2477 ### variations if +detail exists: +* and without +detail
2481 ### <$2> -- default (what to return if not found in db)
2482 dnl must not be empty
2483 ### <$3> -- mark (must be <(!|+) single-token>)
2484 ### ! does lookup only with tag
2485 ### + does lookup with and without tag
2486 ### <$4> -- passthru (additional data passed unchanged through)
2487 dnl returns: <default> <passthru>
2488 dnl <result> <passthru>
2489 ######################################################################
2492 dnl workspace: <key> <def> <o tag> <thru>
2495 R<$+> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2496 dnl no match, try without tag
2498 R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2499 dnl no match, +detail: try +*
2501 R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2502 $: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2503 dnl no match, +detail: try +* without tag
2505 R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2506 $: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2507 dnl no match, +detail: try without +detail
2509 R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2510 $: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2511 dnl no match, +detail: try without +detail and without tag
2513 R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2514 $: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2515 dnl no match, return <default> <passthru>
2517 R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5>
2518 ifdef(`_ATMPF_', `dnl tempfail?
2520 R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl')
2521 dnl match, return <match> <passthru>
2523 R<$+> <$*> <$- $-> <$*> $@ <$1> <$5>
2525 ######################################################################
2526 ### E: LookUpExact -- search for an entry in access database
2530 ### <$2> -- default (what to return if not found in db)
2531 dnl must not be empty
2532 ### <$3> -- mark (must be <(!|+) single-token>)
2533 ### ! does lookup only with tag
2534 ### + does lookup with and without tag
2535 ### <$4> -- passthru (additional data passed unchanged through)
2536 dnl returns: <default> <passthru>
2537 dnl <result> <passthru>
2538 ######################################################################
2542 R<$*> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2543 dnl no match, try without tag
2545 R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2546 dnl no match, return default passthru
2548 R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5>
2549 ifdef(`_ATMPF_', `dnl tempfail?
2551 R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl')
2552 dnl match, return <match> <passthru>
2554 R<$+> <$*> <$- $-> <$*> $@ <$1> <$5>
2556 ######################################################################
2557 ### U: LookUpUser -- search for an entry in access database
2559 ### lookup of key (which should be a local part) and
2560 ### variations if +detail exists: +* and without +detail
2563 ### <$1> -- key (user@)
2564 ### <$2> -- default (what to return if not found in db)
2565 dnl must not be empty
2566 ### <$3> -- mark (must be <(!|+) single-token>)
2567 ### ! does lookup only with tag
2568 ### + does lookup with and without tag
2569 ### <$4> -- passthru (additional data passed unchanged through)
2570 dnl returns: <default> <passthru>
2571 dnl <result> <passthru>
2572 ######################################################################
2575 dnl user lookups are always with trailing @
2577 R<$+> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2578 dnl no match, try without tag
2580 R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2581 dnl do not remove the @ from the lookup:
2582 dnl it is part of the +detail@ which is omitted for the lookup
2583 dnl no match, +detail: try +*
2585 R<?> <$+ + $* @> <$*> <$- $-> <$*>
2586 $: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2587 dnl no match, +detail: try +* without tag
2589 R<?> <$+ + $* @> <$*> <+ $-> <$*>
2590 $: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2591 dnl no match, +detail: try without +detail
2593 R<?> <$+ + $* @> <$*> <$- $-> <$*>
2594 $: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2595 dnl no match, +detail: try without +detail and without tag
2597 R<?> <$+ + $* @> <$*> <+ $-> <$*>
2598 $: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2599 dnl no match, return <default> <passthru>
2601 R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5>
2602 ifdef(`_ATMPF_', `dnl tempfail?
2604 R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl')
2605 dnl match, return <match> <passthru>
2607 R<$+> <$*> <$- $-> <$*> $@ <$1> <$5>
2609 ######################################################################
2610 ### SearchList: search a list of items in the access map
2612 ### <exact tag> $| <mark:address> <mark:address> ... <>
2613 dnl maybe we should have a @ (again) in front of the mark to
2614 dnl avoid erroneous matches (with error messages?)
2615 dnl if we can make sure that tag is always a single token
2616 dnl then we can omit the delimiter $|, otherwise we need it
2617 dnl to avoid erroneous matches (first rule: D: if there
2618 dnl is that mark somewhere in the list, it will be taken).
2619 dnl moreover, we can do some tricks to enforce lookup with
2620 dnl the tag only, e.g.:
2621 ### where "exact" is either "+" or "!":
2622 ### <+ TAG> look up with and w/o tag
2623 ### <! TAG> look up with tag
2624 dnl Warning: + and ! should be in OperatorChars (otherwise there must be
2625 dnl a blank between them and the tag.
2626 ### possible values for "mark" are:
2627 ### D: recursive host lookup (LookUpDomain)
2628 dnl A: recursive address lookup (LookUpAddress) [not yet required]
2629 ### E: exact lookup, no modifications
2630 ### F: full lookup, try user+ext@domain and user@domain
2631 ### U: user lookup, try user+ext and user (input must have trailing @)
2632 ### return: <RHS of lookup> or <?> (not found)
2633 ######################################################################
2635 # class with valid marks for SearchList
2636 dnl if A is activated: add it
2637 C{Src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
2639 # just call the ruleset with the name of the tag... nice trick...
2641 R<$+> $| <$={Src}:$*> <$*> $: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
2642 dnl workspace: <o tag> $| <rest> $| <result of lookup> <>
2643 dnl no match and nothing left: return
2644 R<$+> $| <> $| <?> <> $@ <?>
2645 dnl no match but something left: continue
2646 R<$+> $| <$+> $| <?> <> $@ $>SearchList <$1> $| <$2>
2648 R<$+> $| <$*> $| <$+> <> $@ <$3>
2649 dnl return result from recursive invocation
2650 R<$+> $| <$+> $@ <$2>
2651 dnl endif _ACCESS_TABLE_
2654 ######################################################################
2655 ### trust_auth: is user trusted to authenticate as someone else?
2658 ### $1: AUTH= parameter from MAIL command
2659 ######################################################################
2661 dnl empty ruleset definition so it can be called
2664 R$* $: $&{auth_type} $| $1
2665 # required by RFC 2554 section 4.
2666 R$@ $| $* $#error $@ 5.7.1 $: "550 not authenticated"
2667 dnl seems to be useful...
2668 R$* $| $&{auth_authen} $@ identical
2669 R$* $| <$&{auth_authen}> $@ identical
2670 dnl call user supplied code
2671 R$* $| $* $: $1 $| $>"Local_trust_auth" $2
2674 R$* $#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
2676 ######################################################################
2677 ### Relay_Auth: allow relaying based on authentication?
2680 ### $1: ${auth_type}
2681 ######################################################################
2684 ######################################################################
2685 ### srv_features: which features to offer to a client?
2686 ### (done in server)
2687 ######################################################################
2689 ifdef(`_LOCAL_SRV_FEATURES_', `dnl
2690 R$* $: $1 $| $>"Local_srv_features" $1
2692 R$* $| $* $: $1', `dnl')
2693 ifdef(`_ACCESS_TABLE_', `dnl
2694 R$* $: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
2695 R<?>$* $: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
2696 R<?>$* $: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
2698 ifdef(`_ATMPF_', `dnl tempfail?
2699 R<$* _ATMPF_>$* $#temp', `dnl')
2702 ######################################################################
2703 ### clt_features: which features to use with a server?
2704 ### (done in client)
2705 ######################################################################
2707 ifdef(`_LOCAL_CLT_FEATURES_', `dnl
2708 R$* $: $1 $| $>"Local_clt_features" $1
2710 R$* $| $* $: $1', `dnl')
2711 ifdef(`_ACCESS_TABLE_', `dnl
2712 dnl the servername can have a trailing dot from canonification
2714 R$+ $: $>D <$1> <?> <! CLT_FEAT_TAG> <>
2715 R<?>$* $: <$(access CLT_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
2717 ifdef(`_ATMPF_', `dnl tempfail?
2718 R<$* _ATMPF_>$* $#temp', `dnl')
2721 ######################################################################
2722 ### try_tls: try to use STARTTLS?
2723 ### (done in client)
2724 ######################################################################
2726 ifdef(`_LOCAL_TRY_TLS_', `dnl
2727 R$* $: $1 $| $>"Local_try_tls" $1
2729 R$* $| $* $: $1', `dnl')
2730 ifdef(`_ACCESS_TABLE_', `dnl
2731 R$* $: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
2732 R<?>$* $: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
2733 R<?>$* $: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
2735 ifdef(`_ATMPF_', `dnl tempfail?
2736 R<$* _ATMPF_>$* $#error $@ 4.3.0 $: _TMPFMSG_(`TT')', `dnl')
2737 R<NO>$* $#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"')
2739 ifdef(`_MTA_STS_', `dnl
2741 R$* :$&{TLS_Name}: $* $@ ok
2744 dnl check SAN for STS
2746 ifdef(`_STS_SAN', `dnl
2747 R$* $: $&{server_name}
2749 R$={cert_altnames} $@ ok
2750 # strip only one level (no recursion!)
2752 dnl wildcard: *. or just .?
2753 R *.$={cert_altnames} $@ ok
2754 dnl R .$={cert_altnames} $@ ok
2755 dnl always temporary error? make it an option (of the feature)?
2756 R$* $#error $@ 4.7.0 $: 450 $&{server_name} not listed in SANs', `dnl')
2758 dnl input: ${verify}
2759 dnl output: $# error ... (from TLS_connection)
2760 dnl everything else: ok
2762 R$* $: $&{rcpt_addr} $| $1
2763 # no {rcpt_addr}, no STS check
2765 dnl canonify to extract domain part?
2766 R$*@$+ $| $* $2 $| $3
2768 R$+ $| $* $: $(sts $1 $: none $) $| $2
2769 R$* <TMPF> $| $* $#error $@ 4.7.0 $: 450 STS lookup temp fail
2770 dnl check whether connection is "secure"
2771 dnl always temporary error? make it an option (of the feature)?
2772 R$* secure $* $| $* $@ $>"TLS_connection" $3 $| <TEMP+VERIFY:128>
2775 dnl check STS policy: secure and match? if so, check list
2777 R$* $: $&{rcpt_addr} $| $1
2778 # no {rcpt_addr}, no STS check
2780 # use the original argument for the test, not {rcpt_addr}
2781 R$* $| $* $: $2 $| $2
2782 dnl canonify to extract domain part?
2783 R$*@$+ $| $* $2 $| $3
2785 R$* $| $* $: $(sts $1 $: none $) $| mark
2786 R$* <TMPF> $| $* $#error $@ 4.7.0 $: 450 STS lookup temp fail
2787 dnl STS check only for "secure"
2788 dnl do this only if {sts_sni} is set?
2789 dnl workspace: result of sts lookup $| mark
2790 R$* secure $* $| mark $: $2 $| trmatch
2791 dnl not "secure": no check
2793 dnl remove servername=hostname, keep match=
2794 R$* servername=hostname $| trmatch $: $1 $| trmatch
2795 dnl extra list of matches, i.e., remove match=
2796 R$+ $| trmatch $: : $(stsxmatch $1 $: : $)
2798 R$* $| trmatch $@ $>STS_SAN
2799 dnl Remove trailing dots from each entry in the list;
2800 dnl those should not be there, but better safe than sorry.
2802 R:$+: $: $(macro {TLS_Name} $@ $&{server_name} $) $>TLS_NameInList :$1:
2804 R$* $: $1 $| $&{server_name}
2805 R:$* $| $-.$+ $: $(macro {TLS_Name} $@ .$3 $) $>TLS_NameInList :$1
2807 R:$*: $#error $@ 4.7.0 $: 450 $&{server_name} not found in " "$1', `dnl')
2809 ifdef(`TLS_PERM_ERR', `dnl
2810 define(`TLS_DSNCODE', `5.7.0')dnl
2811 define(`TLS_ERRCODE', `554')',`dnl
2812 define(`TLS_DSNCODE', `4.7.0')dnl
2813 define(`TLS_ERRCODE', `454')')dnl
2814 define(`SW_MSG', `TLS handshake failed.')dnl
2815 define(`DANE_MSG', `DANE check failed.')dnl
2816 define(`DANE_TEMP_MSG', `DANE check failed temporarily.')dnl
2817 define(`DANE_NOTLS_MSG', `DANE: missing STARTTLS.')dnl
2818 define(`PROT_MSG', `STARTTLS failed.')dnl
2819 define(`CNF_MSG', `STARTTLS temporarily not possible.')dnl
2821 ######################################################################
2822 ### tls_rcpt: is connection with server "good" enough?
2823 ### (done in client, per recipient)
2824 dnl called from deliver() before RCPT command
2828 ######################################################################
2830 ifdef(`_LOCAL_TLS_RCPT_', `dnl
2831 R$* $: $1 $| $>"Local_tls_rcpt" $1
2833 R$* $| $* $: $1', `dnl')
2834 ifdef(`_MTA_STS_', `dnl
2835 R$* $: $1 $| $>"STS_Check" $1
2837 R$* $| $* $: $1', `dnl')
2838 ifdef(`_ACCESS_TABLE_', `dnl
2839 dnl store name of other side
2840 R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1
2841 dnl canonify recipient address
2842 R$+ $: <?> $>CanonAddr $1
2843 dnl strip trailing dots
2844 R<?> $+ < @ $+ . > <?> $1 <@ $2 >
2846 R<?> $+ < @ $+ > $: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
2848 R<?> $+ $: $1 $| <U:$1@> <E:>
2850 dnl also look up a default value via E:
2851 R$* $| $+ $: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
2852 dnl no applicable requirements; trigger an error on DANE_FAIL
2853 dnl note: this allows to disable DANE per RCPT.
2854 R$* $| <?> $: $1 $| $&{verify} $| <?>
2855 R$* $| DANE_FAIL $| <?> $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_MSG"
2856 R$* $| DANE_NOTLS $| <?> $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_NOTLS_MSG"
2857 R$* $| DANE_TEMP $| <?> $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_TEMP_MSG"
2858 dnl found nothing: stop here
2860 ifdef(`_ATMPF_', `dnl tempfail?
2861 R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`TR')', `dnl')
2862 dnl use the generic routine (for now)
2863 R$* $| <$+> $@ $>"TLS_connection" $&{verify} $| <$2>', `dnl
2864 R$* $: $1 $| $&{verify}
2865 R$* $| DANE_NOTLS $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_NOTLS_MSG"
2866 R$* $| DANE_TEMP $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_TEMP_MSG"
2867 R$* $| DANE_FAIL $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_MSG"')
2869 ######################################################################
2870 ### tls_client: is connection with client "good" enough?
2871 ### (done in server)
2874 ### ${verify} $| (MAIL|STARTTLS)
2875 ######################################################################
2876 dnl MAIL: called from check_mail
2877 dnl STARTTLS: called from smtp() after STARTTLS has been accepted
2879 ifdef(`_LOCAL_TLS_CLIENT_', `dnl
2880 R$* $: $1 <?> $>"Local_tls_client" $1
2882 R$* <?> $* $: $1', `dnl')
2883 ifdef(`_ACCESS_TABLE_', `dnl
2884 dnl store name of other side
2885 R$* $: $(macro {TLS_Name} $@ $&{client_name} $) $1
2886 dnl ignore second arg for now
2887 dnl maybe use it to distinguish permanent/temporary error?
2888 dnl if MAIL: permanent (STARTTLS has not been offered)
2889 dnl if STARTTLS: temporary (offered but maybe failed)
2890 R$* $| $* $: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
2891 R$* $| <?>$* $: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
2892 dnl do a default lookup: just TLS_CLT_TAG
2893 R$* $| <?>$* $: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
2894 ifdef(`_ATMPF_', `dnl tempfail?
2895 R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`TC')', `dnl')
2896 R$* $@ $>"TLS_connection" $1', `dnl
2897 R$* $| $* $@ $>"TLS_connection" $1')
2899 ######################################################################
2900 ### tls_server: is connection with server "good" enough?
2901 ### (done in client)
2905 ######################################################################
2906 dnl i.e. has the server been authenticated and is encryption active?
2907 dnl called from deliver() after STARTTLS command
2909 ifdef(`_LOCAL_TLS_SERVER_', `dnl
2910 R$* $: $1 $| $>"Local_tls_server" $1
2912 R$* $| $* $: $1', `dnl')
2913 ifdef(`_TLS_FAILURES_',`dnl
2914 R$* $: $(macro {saved_verify} $@ $1 $) $1')
2915 ifdef(`_MTA_STS_', `dnl
2916 R$* $: $1 $| $>"STS_secure" $1
2918 R$* $| $* $: $1', `dnl')
2919 ifdef(`_ACCESS_TABLE_', `dnl
2920 dnl store name of other side
2921 R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1
2922 R$* $: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
2923 R$* $| <?>$* $: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
2924 dnl do a default lookup: just TLS_SRV_TAG
2925 R$* $| <?>$* $: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
2926 ifdef(`_ATMPF_', `dnl tempfail?
2927 R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`TS')', `dnl')
2928 R$* $@ $>"TLS_connection" $1', `dnl
2929 R$* $@ $>"TLS_connection" $1')
2931 ######################################################################
2932 ### TLS_connection: is TLS connection "good" enough?
2935 ifdef(`_ACCESS_TABLE_', `dnl
2936 ### ${verify} $| <Requirement> [<>]', `dnl
2938 ### Requirement: RHS from access map, may be ? for none.
2939 dnl syntax for Requirement:
2940 dnl [(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
2941 dnl extensions: could be a list of further requirements
2942 dnl for now: CN:string {cn_subject} == string
2943 ######################################################################
2945 ifdef(`_FULL_TLS_CONNECTION_CHECK_', `dnl', `dnl use default error
2946 dnl deal with TLS handshake failures: abort
2947 RSOFTWARE $#error $@ TLS_DSNCODE $: "TLS_ERRCODE SW_MSG"
2948 dnl RDANE_FAIL $#error $@ TLS_DSNCODE $: "TLS_ERRCODE DANE_MSG"
2949 RPROTOCOL $#error $@ TLS_DSNCODE $: "TLS_ERRCODE PROT_MSG"
2950 RCONFIG $#error $@ TLS_DSNCODE $: "TLS_ERRCODE CNF_MSG"
2951 dnl RDANE_TEMP $#error $@ 4.7.0 $: "454 DANE_TEMP_MSG"
2953 dnl common ruleset for tls_{client|server}
2954 dnl input: ${verify} $| <ResultOfLookup> [<>]
2955 dnl remove optional <>
2956 R$* $| <$*>$* $: $1 $| <$2>
2957 dnl workspace: ${verify} $| <ResultOfLookup>
2958 # create the appropriate error codes
2959 dnl permanent or temporary error?
2960 R$* $| <PERM + $={Tls} $*> $: $1 $| <503:5.7.0> <$2 $3>
2961 R$* $| <TEMP + $={Tls} $*> $: $1 $| <403:4.7.0> <$2 $3>
2962 dnl default case depends on TLS_PERM_ERR
2963 R$* $| <$={Tls} $*> $: $1 $| <TLS_ERRCODE:TLS_DSNCODE> <$2 $3>
2964 dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
2965 define(`TLS_ERRORS', `dnl
2966 R`'$1 $| <$-:$+> $`'* $`'#error $`'@ $`'2 $: $`'1 " $2"
2967 dnl no <reply:dns> i.e. no requirements in the access map
2968 dnl use default error
2969 R`'$1 $| $`'* $`'#error $`'@ TLS_DSNCODE $: "TLS_ERRCODE $2"')dnl
2970 # deal with TLS handshake failures: abort
2971 TLS_ERRORS(SOFTWARE,SW_MSG)
2972 # deal with TLS protocol errors: abort
2973 TLS_ERRORS(PROTOCOL,PROT_MSG)
2974 dnl # deal with DANE errors: abort
2975 dnl TLS_ERRORS(DANE_FAIL,DANE_MSG)
2976 # deal with CONFIG (tls_clt_features) errors: abort
2977 TLS_ERRORS(CONFIG,CNF_MSG)
2978 dnl # deal with DANE tempfail: abort
2979 dnl TLS_ERRORS(DANE_TEMP,DANE_TEMP_MSG)
2980 R$* $| <$*> <VERIFY> $: <$2> <VERIFY> <> $1
2981 dnl separate optional requirements
2982 R$* $| <$*> <VERIFY + $+> $: <$2> <VERIFY> <$3> $1
2983 R$* $| <$*> <$={Tls}:$->$* $: <$2> <$3:$4> <> $1
2984 dnl separate optional requirements
2985 R$* $| <$*> <$={Tls}:$- + $+>$* $: <$2> <$3:$4> <$5> $1
2986 dnl some other value in access map: accept
2987 dnl this also allows to override the default case (if used)
2989 # authentication required: give appropriate error
2990 # other side did authenticate (via STARTTLS)
2991 dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
2992 dnl only verification required and it succeeded
2993 R<$*><VERIFY> <> $={TlsVerified} $@ OK
2994 dnl verification required and it succeeded but extensions are given
2995 dnl change it to <SMTP:ESC> <REQ:0> <extensions>
2996 R<$*><VERIFY> <$+> $={TlsVerified} $: <$1> <REQ:0> <$2>
2997 dnl verification required + some level of encryption
2998 R<$*><VERIFY:$-> <$*> $={TlsVerified} $: <$1> <REQ:$2> <$3>
2999 dnl just some level of encryption required
3000 R<$*><ENCR:$-> <$*> $* $: <$1> <REQ:$2> <$3>
3002 dnl 1. <SMTP:ESC> <VERIFY [:bits]> <[extensions]> {verify} (!~ $={TlsVerified})
3003 dnl 2. <SMTP:ESC> <REQ:bits> <[extensions]>
3004 dnl verification required but ${verify} is not set (case 1.)
3005 R<$-:$+><VERIFY $*> <$*> $#error $@ $2 $: $1 " authentication required"
3006 R<$-:$+><VERIFY $*> <$*> FAIL $#error $@ $2 $: $1 " authentication failed"
3007 R<$-:$+><VERIFY $*> <$*> NO $#error $@ $2 $: $1 " not authenticated"
3008 R<$-:$+><VERIFY $*> <$*> NOT $#error $@ $2 $: $1 " no authentication requested"
3009 R<$-:$+><VERIFY $*> <$*> NONE $#error $@ $2 $: $1 " other side does not support STARTTLS"
3010 R<$-:$+><VERIFY $*> <$*> CLEAR $#error $@ $2 $: $1 " STARTTLS disabled locally"
3011 dnl some other value for ${verify}
3012 R<$-:$+><VERIFY $*> <$*> $+ $#error $@ $2 $: $1 " authentication failure " $4
3013 dnl some level of encryption required: get the maximum level (case 2.)
3014 R<$*><REQ:$-> <$*> $: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
3015 dnl compare required bits with actual bits
3016 R<$*><REQ:$-> <$*> $- $: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
3017 R<$-:$+><$-:$-> <$*> TRUE $#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
3018 dnl strength requirements fulfilled
3019 dnl TLS Additional Requirements Separator
3020 dnl this should be something which does not appear in the extensions itself
3021 dnl @ could be part of a CN, DN, etc...
3022 dnl use < > ? those are encoded in CN, DN, ...
3023 define(`_TLS_ARS_', `++')dnl
3025 dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
3026 R<$-:$+><$-:$-> <$*> $* $: <$1:$2 _TLS_ARS_ $5>
3027 dnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
3028 dnl continue: check extensions
3029 R<$-:$+ _TLS_ARS_ > $@ OK
3030 dnl split extensions into own list
3031 R<$-:$+ _TLS_ARS_ $+ > $: <$1:$2> <$3>
3032 R<$-:$+> < $+ _TLS_ARS_ $+ > <$1:$2> <$3> <$4>
3033 R<$-:$+> $+ $@ $>"TLS_req" $3 $| <$1:$2>
3035 ######################################################################
3036 ### TLS_req: check additional TLS requirements
3038 ### Parameters: [<list> <of> <req>] $| <$-:$+>
3039 ### $-: SMTP reply code
3040 ### $+: Enhanced Status Code
3041 dnl further requirements for this ruleset:
3042 dnl name of "other side" is stored is {TLS_name} (client/server_name)
3044 dnl right now this is only a logical AND
3045 dnl i.e. all requirements must be true
3046 dnl how about an OR? CN must be X or CN must be Y or ..
3047 dnl use a macro to compute this as a trivial sequential
3048 dnl operations (no precedences etc)?
3049 ######################################################################
3051 dnl no additional requirements: ok
3053 dnl require CN: but no CN specified: use name of other side
3054 R<CN> $* $| <$+> $: <CN:$&{TLS_Name}> $1 $| <$2>
3055 ifdef(`_FFR_TLS_ALTNAMES', `dnl
3056 R<CN:$={cert_altnames}> $* $| <$+> $@ $>"TLS_req" $2 $| <$3>
3057 R<CN:$-.$+> $* $| <$+> $: <CN:*.$2> $3 $| <$4>
3058 R<CN:$={cert_altnames}> $* $| <$+> $@ $>"TLS_req" $3 $| <$3>
3059 R<CN:$*> $* $| <$+> $: <CN:$&{TLS_Name}> $2 $| <$3>', `dnl')
3060 dnl match, check rest
3061 R<CN:$&{cn_subject}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
3062 dnl CN does not match
3064 R<CN:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
3066 R<CS:$&{cert_subject}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
3067 dnl CS does not match
3069 R<CS:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
3070 dnl match, check rest
3071 R<CI:$&{cert_issuer}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
3072 dnl CI does not match
3074 R<CI:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
3076 R<CITag:$-> $* $| <$+> $: <$(access $1:$&{cert_issuer} $: ? $)> $2 $| <$3>
3077 R<?> $* $| <$-:$+> $#error $@ $3 $: $2 " Cert Issuer " $&{cert_issuer} " not acceptable"
3078 R<OK> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
3079 dnl return from recursive call
3082 ######################################################################
3083 ### max: return the maximum of two values separated by :
3085 ### Parameters: [$-]:[$-]
3086 ######################################################################
3091 R$-:$- $: $(arith l $@ $1 $@ $2 $) : $1 : $2
3094 dnl endif _ACCESS_TABLE_
3097 dnl this must also be activated without _TLS_SESSION_FEATURES_
3098 ifdef(`_MTA_STS_', `dnl
3099 dnl caller preserves workspace
3101 R$* $: <$&{rcpt_addr}>
3102 # no {rcpt_addr}, no STS check
3104 dnl canonify to extract domain part?
3107 R$+ $: $(sts $1 $: none $)
3108 R$* <TMPF> $#error $@ 4.7.0 $: 450 STS lookup temp fail
3110 dnl get servername=sni and store it in {sts_sni}
3111 dnl stsxsni extracts the value of servername= (sni)
3112 dnl stsxsni2 extracts servername=sni so it can be returned to the caller
3113 R$* secure $* $: $(stsxsni $2 $: : $) $| sts=secure; $(stsxsni2 $2 $: : $)
3114 dnl store {server_addr} as sni if there was a match
3115 dnl Note: this implies that only servername=hostname (literally!)
3116 dnl is only ever returned.
3117 R$+: $| $+ : $: $(macro {sts_sni} $@ $&{server_name} $) set $| $2
3122 ifdef(`_TLS_SESSION_FEATURES_', `dnl
3123 define(`_NEED_TLS_CLT_FEATURES')
3125 ifdef(`_ACCESS_TABLE_', `dnl
3126 R$* $| $* $: $>D <$1> <?> <! TLS_Srv_Features> <$2>
3127 R<?> <$*> $: $>A <$1> <?> <! TLS_Srv_Features> <$1>
3133 ifdef(`_MTA_STS_',`define(`_NEED_TLS_CLT_FEATURES')')dnl
3136 ifdef(`_NEED_TLS_CLT_FEATURES', `dnl
3137 ifdef(`_ACCESS_TABLE_', `dnl
3139 R$* $| $* $: $>D <$1> <?> <! TLS_Clt_Features> <$2>
3140 R<?> <$*> $: $>A <$1> <?> <! TLS_Clt_Features> <$1>
3145 dnl Note: most of this is handled in the binary.
3146 dnl input: access map lookup for tls_clt_features
3150 R$+ $: < $(stsxnodaneflag $1 $: NOFLAGS $) >
3152 R$* $: < $&{sts_sni} >
3155 # R$* $: $&{client_flags}
3164 R$* $: $&{client_flags}
3167 R$* $: $&{rcpt_addr} $| $1
3168 # no {rcpt_addr}, no STS check
3175 ifdef(`_ACCESS_TABLE_', `dnl
3176 R$* $| <> $: $1 $| $>"tls_clt_feat_acc" $1
3177 R$* $| $* $| $* $: $1 $| $2 $| <$3>', `dnl')
3178 ifdef(`_MTA_STS_', `dnl
3179 dnl host $| ip $| <acc result - might be empty>
3180 R$* $| $* $| <$*> $: $1 $| $2 $| <$3> $| $>"STS_disabled" sts
3181 dnl host $| ip $| <acc result - might be empty> $| STS_disabled result
3182 dnl disable STS check? return access result
3183 R$* $| $* $| <$*> $| <> $@ $3
3184 dnl host $| ip $| <acc result - might be empty> $| STS_disabled result
3185 R$* $| $* $| <$*> $| $* $: $1 $| $2 $| <$3> $| $>"DANE_disabled" $3
3186 dnl DANE enabled: return access result; take care of empty return
3187 R$* $| $* $| <$+> $| <DANE> $@ $3
3188 R$* $| $* $| <> $| <DANE> $@ ""
3189 dnl host $| ip $| <acc result - might be empty> $| <DANE_disabled result>
3190 R$* $| $* $| <$*> $| <$*> $: $1 $| $2 $| <$3> $| $>"Set_SNI" $1
3191 dnl host $| ip $| <acc result - might be empty> $| sni result
3192 dnl return sni result if not empty but acc is
3193 R$* $| $* $| <""> $| $+ $@ $3
3194 dnl return acc + sni result if not empty
3195 R$* $| $* $| <$+;> $| $+ $@ $3 ; $4
3196 dnl return acc + sni result if not empty
3197 R$* $| $* $| <$+> $| $+ $@ $3 ; $4
3198 dnl return sni result if not empty
3199 R$* $| $* $| <> $| $+ $@ $3
3200 dnl remove sni result
3201 R$* $| $* $| $* $| $* $: $1 $| $2 $| $3
3203 dnl host $| ip $| <acc result - might be empty>
3204 R$* $| $* $| <""> $@ ""
3205 R$* $| $* $| <$+> $@ $3
3208 ######################################################################
3209 ### RelayTLS: allow relaying based on TLS authentication
3213 ######################################################################
3216 dnl we do not allow relaying for anyone who can present a cert
3217 dnl signed by a "trusted" CA. For example, even if we put verisigns
3218 dnl CA in CertPath so we can authenticate users, we do not allow
3219 dnl them to abuse our server (they might be easier to get hold of,
3221 dnl so here is the trick: if the verification succeeded
3222 dnl we look up the cert issuer in the access map
3223 dnl (maybe after extracting a part with a regular expression)
3224 dnl if this returns RELAY we relay without further questions
3225 dnl if it returns SUBJECT we perform a similar check on the
3227 ifdef(`_ACCESS_TABLE_', `dnl
3228 R$* $: <?> $&{verify}
3229 R<?> $={TlsVerified} $: OK authenticated: continue
3230 R<?> $* $@ NO not authenticated
3231 ifdef(`_CERT_REGEX_ISSUER_', `dnl
3232 R$* $: $(CERTIssuer $&{cert_issuer} $)',
3233 `R$* $: $&{cert_issuer}')
3234 R$+ $: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
3235 dnl use $# to stop further checks (delay_check)
3237 ifdef(`_CERT_REGEX_SUBJECT_', `dnl
3238 RSUBJECT $: <@> $(CERTSubject $&{cert_subject} $)',
3239 `RSUBJECT $: <@> $&{cert_subject}')
3240 R<@> $+ $: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
3244 ######################################################################
3245 ### authinfo: lookup authinfo in the access map
3248 ### $1: {server_name}
3249 ### $2: {server_addr}
3250 dnl both are currently ignored
3251 dnl if it should be done via another map, we either need to restrict
3252 dnl functionality (it calls D and A) or copy those rulesets (or add another
3253 dnl parameter which I want to avoid, it's quite complex already)
3254 ######################################################################
3255 dnl omit this ruleset if neither is defined?
3256 dnl it causes DefaultAuthInfo to be ignored
3257 dnl (which may be considered a good thing).
3259 ifdef(`_AUTHINFO_TABLE_', `dnl
3260 R$* $: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
3261 R<?> $: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
3262 R<?> $: <$(authinfo AuthInfo: $: ? $)>
3263 R<?> $@ no no authinfo available
3266 ifdef(`_ACCESS_TABLE_', `dnl
3267 R$* $: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
3268 R$* $| <?>$* $: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
3269 R$* $| <?>$* $: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
3270 R$* $| <?>$* $@ no no authinfo available
3271 R$* $| <$*> <> $# $2
3274 ifdef(`_RATE_CONTROL_',`dnl
3275 ######################################################################
3277 ### Parameters: ignored
3278 ### return: $#error or OK
3279 ######################################################################
3281 ifdef(`_ACCESS_TABLE_', `dnl
3282 R$* $: <A:$&{client_addr}> <E:>
3283 dnl also look up a default value via E:
3284 R$+ $: $>SearchList <! ClientRate> $| $1 <>
3285 dnl found nothing: stop here
3287 ifdef(`_ATMPF_', `dnl tempfail?
3288 R<$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`RC')', `dnl')
3289 dnl use the generic routine (for now)
3291 R<$+> $: <$1> $| $(arith l $@ $1 $@ $&{client_rate} $)
3292 dnl log this? Connection rate $&{client_rate} exceeds limit $1.
3293 R<$+> $| TRUE $#error $@ 4.3.2 $: _RATE_CONTROL_REPLY Connection rate limit exceeded.
3296 ifdef(`_CONN_CONTROL_',`dnl
3297 ######################################################################
3299 ### Parameters: ignored
3300 ### return: $#error or OK
3301 ######################################################################
3303 ifdef(`_ACCESS_TABLE_', `dnl
3304 R$* $: <A:$&{client_addr}> <E:>
3305 dnl also look up a default value via E:
3306 R$+ $: $>SearchList <! ClientConn> $| $1 <>
3307 dnl found nothing: stop here
3309 ifdef(`_ATMPF_', `dnl tempfail?
3310 R<$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`CC')', `dnl')
3311 dnl use the generic routine (for now)
3313 R<$+> $: <$1> $| $(arith l $@ $1 $@ $&{client_connections} $)
3314 dnl log this: Open connections $&{client_connections} exceeds limit $1.
3315 R<$+> $| TRUE $#error $@ 4.3.2 $: _CONN_CONTROL_REPLY Too many open connections.
3318 undivert(9)dnl LOCAL_RULESETS
3320 ######################################################################
3321 ######################################################################
3323 `##### MAIL FILTER DEFINITIONS'
3325 ######################################################################
3326 ######################################################################
3329 ######################################################################
3330 ######################################################################
3332 `##### MAILER DEFINITIONS'
3334 ######################################################################
3335 ######################################################################
3336 undivert(7)dnl MAILER_DEFINITIONS
3339 dnl Helper ruleset for -bt mode to invoke rulesets
3340 dnl which take two arguments separated by $|
3342 dnl Start,check_relay host.name $| I.P.V.4
3344 dnl R$* $$| $* $: $1 $| $2