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 define(`TLS_SRV_TAG', `"TLS_Srv"')dnl
187 define(`TLS_CLT_TAG', `"TLS_Clt"')dnl
188 define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
189 define(`TLS_TRY_TAG', `"Try_TLS"')dnl
190 define(`SRV_FEAT_TAG', `"Srv_Features"')dnl
191 dnl this may be useful in other contexts too
192 ifdef(`_ARITH_MAP_', `', `# arithmetic map
193 define(`_ARITH_MAP_', `1')dnl
195 ifdef(`_ACCESS_TABLE_', `dnl
196 ifdef(`_MACRO_MAP_', `', `# macro storage map
197 define(`_MACRO_MAP_', `1')dnl
199 # possible values for TLS_connection in access map
201 C{TlsVerified}OK TRUSTED
203 ifdef(`_CERT_REGEX_ISSUER_', `dnl
204 # extract relevant part from cert issuer
205 KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
206 ifdef(`_CERT_REGEX_SUBJECT_', `dnl
207 # extract relevant part from cert subject
208 KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
210 ifdef(`LOCAL_RELAY', `dnl
211 # who I send unqualified names to if `FEATURE(stickyhost)' is used
212 # (null means deliver locally)
215 ifdef(`MAIL_HUB', `dnl
216 # who gets all local email traffic
217 # ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
221 Kdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
223 divert(0)dnl # end of nullclient diversion
224 # class E: names that should be exposed as from this host, even if we masquerade
225 # class L: names that should be delivered locally, even if we have a relay
226 # class M: domains that should be converted to $M
227 # class N: domains that should not be converted to $M
230 ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
232 ifdef(`MASQUERADE_NAME', `dnl
233 # who I masquerade as (null for no masquerading) (see also $=M)
234 DM`'MASQUERADE_NAME')
236 # my name for error messages
237 ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
239 undivert(6)dnl LOCAL_CONFIG
240 include(_CF_DIR_`m4/version.m4')
245 ifdef(`confAUTO_REBUILD',
246 `errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
247 There was a potential for a denial of service attack if this is set.
250 # strip message body to 7 bits on input?
251 _OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
253 # 8-bit data handling
254 _OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
256 # wait for alias file rebuild (default units: minutes)
257 _OPTION(AliasWait, `confALIAS_WAIT', `5m')
259 # location of alias file
260 _OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
262 # minimum number of free blocks on filesystem
263 _OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
265 # maximum message size
266 _OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `0')
268 # substitution for space (blank) characters
269 _OPTION(BlankSub, `confBLANK_SUB', `_')
271 # avoid connecting to "expensive" mailers on initial submission?
272 _OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
274 # checkpoint queue runs after every N successful deliveries
275 _OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
277 # default delivery mode
278 _OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
280 # error message header/file
281 _OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
284 _OPTION(ErrorMode, `confERROR_MODE', `print')
286 # save Unix-style "From_" lines at top of header?
287 _OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
289 # queue file mode (qf files)
290 _OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
292 # temporary file mode
293 _OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
295 # match recipients against GECOS field?
296 _OPTION(MatchGECOS, `confMATCH_GECOS', `False')
299 _OPTION(MaxHopCount, `confMAX_HOP', `25')
301 # location of help file
302 O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
304 # ignore dots as terminators in incoming messages?
305 _OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
307 # name resolver options
308 _OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
310 # deliver MIME-encapsulated error messages?
311 _OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
313 # Forward file search path
314 _OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
316 # open connection cache size
317 _OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
319 # open connection cache timeout
320 _OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
322 # persistent host status directory
323 _OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
325 # single thread deliveries (requires HostStatusDirectory)?
326 _OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
328 # use Errors-To: header?
329 _OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
331 # use compressed IPv6 address format?
332 _OPTION(UseCompressedIPv6Addresses, `confUSE_COMPRESSED_IPV6_ADDRESSES', `')
335 _OPTION(LogLevel, `confLOG_LEVEL', `10')
337 # send to me too, even in an alias expansion?
338 _OPTION(MeToo, `confME_TOO', `True')
340 # verify RHS in newaliases?
341 _OPTION(CheckAliases, `confCHECK_ALIASES', `False')
343 # default messages to old style headers if no special punctuation?
344 _OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
346 # SMTP daemon options
347 ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
348 `errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
349 Use `DAEMON_OPTIONS()'; see cf/README.
351 `DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
352 ifelse(defn(`_DPO_'), `',
353 `ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
354 O DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
355 ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
357 # SMTP client options
358 ifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
359 `errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid. See cf/README for more information.
361 `CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
362 ifelse(defn(`_CPO_'), `',
363 `#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
365 # Modifiers to `define' {daemon_flags} for direct submissions
366 _OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
368 # Use as mail submission program? See sendmail/SECURITY
369 _OPTION(UseMSP, `confUSE_MSP', `')
372 _OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
374 # who (if anyone) should get extra copies of error messages
375 _OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
377 # slope of queue-only function
378 _OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
380 # limit on number of concurrent queue runners
381 _OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
383 # maximum number of queue-runners per queue-grouping with multiple queues
384 _OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
386 # priority of queue runners (nice(3))
387 _OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
389 # shall we sort the queue by hostname first?
390 _OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
392 # minimum time in queue before retry
393 _OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
395 # maximum time in queue before retry (if > 0; only for exponential delay)
396 _OPTION(MaxQueueAge, `confMAX_QUEUE_AGE', `')
398 # how many jobs can you process in the queue?
399 _OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `0')
401 # perform initial split of envelope without checking MX records
402 _OPTION(FastSplit, `confFAST_SPLIT', `1')
405 O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
407 # key for shared memory; 0 to turn off, -1 to auto-select
408 _OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
410 # file to store auto-selected key for shared memory (SharedMemoryKey = -1)
411 _OPTION(SharedMemoryKeyFile, `confSHARED_MEMORY_KEY_FILE', `')
413 # timeouts (many of these)
414 _OPTION(Timeout.initial, `confTO_INITIAL', `5m')
415 _OPTION(Timeout.connect, `confTO_CONNECT', `5m')
416 _OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
417 _OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
418 _OPTION(Timeout.helo, `confTO_HELO', `5m')
419 _OPTION(Timeout.mail, `confTO_MAIL', `10m')
420 _OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
421 _OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
422 _OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
423 _OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
424 _OPTION(Timeout.rset, `confTO_RSET', `5m')
425 _OPTION(Timeout.quit, `confTO_QUIT', `2m')
426 _OPTION(Timeout.misc, `confTO_MISC', `2m')
427 _OPTION(Timeout.command, `confTO_COMMAND', `1h')
428 _OPTION(Timeout.ident, `confTO_IDENT', `5s')
429 _OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
430 _OPTION(Timeout.control, `confTO_CONTROL', `2m')
431 _OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
432 _OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
433 _OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
434 _OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
435 _OPTION(Timeout.queuereturn.dsn, `confTO_QUEUERETURN_DSN', `5d')
436 _OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
437 _OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
438 _OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
439 _OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
440 _OPTION(Timeout.queuewarn.dsn, `confTO_QUEUEWARN_DSN', `4h')
441 _OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
442 _OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
443 _OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
444 _OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
445 _OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
446 _OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
447 _OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
448 _OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
449 _OPTION(Timeout.auth, `confTO_AUTH', `10m')
450 _OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
452 # time for DeliverBy; extension disabled if less than 0
453 _OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
455 # should we not prune routes in route-addr syntax addresses?
456 _OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
458 # queue up everything before forking?
459 _OPTION(SuperSafe, `confSAFE_QUEUE', `True')
462 _OPTION(StatusFile, `STATUS_FILE')
464 # time zone handling:
465 # if undefined, use system default
466 # if defined but null, use TZ envariable passed in
467 # if defined and non-null, use that info
468 ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
469 confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
470 `O TimeZoneSpec=confTIME_ZONE')
472 # default UID (can be username or userid:groupid)
473 _OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
475 # list of locations of user database file (null means no lookup)
476 _OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
479 _OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
481 # fallback smart host
482 _OPTION(FallbackSmartHost, `confFALLBACK_SMARTHOST', `fall.back.host.net')
484 # if we are the best MX host for a site, try it directly instead of config err
485 _OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
487 # load average at which we just queue messages
488 _OPTION(QueueLA, `confQUEUE_LA', `8')
490 # load average at which we refuse connections
491 _OPTION(RefuseLA, `confREFUSE_LA', `12')
493 # log interval when refusing connections for this long
494 _OPTION(RejectLogInterval, `confREJECT_LOG_INTERVAL', `3h')
496 # load average at which we delay connections; 0 means no limit
497 _OPTION(DelayLA, `confDELAY_LA', `0')
499 # maximum number of children we allow at one time
500 _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
502 # maximum number of new connections per second
503 _OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
505 # Width of the window
506 _OPTION(ConnectionRateWindowSize, `confCONNECTION_RATE_WINDOW_SIZE', `60s')
508 # work recipient factor
509 _OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
511 # deliver each queued job in a separate process?
512 _OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
515 _OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
518 _OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
520 # default character set
521 _OPTION(DefaultCharSet, `confDEF_CHAR_SET', `unknown-8bit')
523 # service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
524 _OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
526 # hosts file (normally /etc/hosts)
527 _OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
529 # dialup line delay on connection failure
530 _OPTION(DialDelay, `confDIAL_DELAY', `0s')
532 # action to take if there are no recipients in the message
533 _OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `none')
535 # chrooted environment for writing to files
536 _OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `')
538 # are colons OK in addresses?
539 _OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
541 # shall I avoid expanding CNAMEs (violates protocols)?
542 _OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
544 # SMTP initial login message (old $e macro)
545 _OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
547 # UNIX initial From header format (old $l macro)
548 _OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
550 # From: lines that have embedded newlines are unwrapped onto one line
551 _OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
553 # Allow HELO SMTP command that does not `include' a host name
554 _OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
556 # Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
557 _OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
559 # delimiter (operator) characters (old $o macro)
560 _OPTION(OperatorChars, `confOPERATORS', `.:@[]')
562 # shall I avoid calling initgroups(3) because of high NIS costs?
563 _OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
565 # are group-writable `:include:' and .forward files (un)trustworthy?
566 # True (the default) means they are not trustworthy.
567 _OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
568 ifdef(`confUNSAFE_GROUP_WRITES',
569 `errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
572 # where do errors that occur when sending errors get sent?
573 _OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
575 # issue temporary errors (4xy) instead of permanent errors (5xy)?
576 _OPTION(SoftBounce, `confSOFT_BOUNCE', `False')
578 # where to save bounces if all else fails
579 _OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
581 # what user id do we assume for the majority of the processing?
582 _OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
584 # maximum number of recipients per SMTP envelope
585 _OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `0')
587 # limit the rate recipients per SMTP envelope are accepted
588 # once the threshold number of recipients have been rejected
589 _OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `0')
592 # shall we get local names from our installed interfaces?
593 _OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
595 # Return-Receipt-To: header implies DSN request
596 _OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
598 # override connection address (for testing)
599 _OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
601 # Trusted user for file ownership and starting the daemon
602 _OPTION(TrustedUser, `confTRUSTED_USER', `root')
604 # Control socket for daemon management
605 _OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
607 # Maximum MIME header length to protect MUAs
608 _OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `0/0')
610 # Maximum length of the sum of all headers
611 _OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
613 # Maximum depth of alias recursion
614 _OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
616 # location of pid file
617 _OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
619 # Prefix string for the process title shown on 'ps' listings
620 _OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
622 # Data file (df) memory-buffer file maximum size
623 _OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
625 # Transcript file (xf) memory-buffer file maximum size
626 _OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
628 # lookup type to find information about local mailboxes
629 _OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
631 # override compile time flag REQUIRES_DIR_FSYNC
632 _OPTION(RequiresDirfsync, `confREQUIRES_DIR_FSYNC', `true')
634 # list of authentication mechanisms
635 _OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
637 # Authentication realm
638 _OPTION(AuthRealm, `confAUTH_REALM', `')
640 # default authentication information for outgoing connections
641 _OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
644 _OPTION(AuthOptions, `confAUTH_OPTIONS', `')
646 # SMTP AUTH maximum encryption strength
647 _OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
649 # SMTP STARTTLS server options
650 _OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
653 _OPTION(CipherList, `confCIPHER_LIST', `')
654 # server side SSL options
655 _OPTION(ServerSSLOptions, `confSERVER_SSL_OPTIONS', `')
656 # client side SSL options
657 _OPTION(ClientSSLOptions, `confCLIENT_SSL_OPTIONS', `')
659 _OPTION(SSLEngine, `confSSL_ENGINE', `')
660 # Path to dynamic library for SSLEngine
661 _OPTION(SSLEnginePath, `confSSL_ENGINE_PATH', `')
662 # TLS: fall back to clear text after handshake failure?
663 _OPTION(TLSFallbacktoClear, `confTLS_FALLBACK_TO_CLEAR', `')
666 _OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
668 ifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
670 _OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
671 _OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
672 _OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
673 _OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
674 _OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')
675 _OPTION(Milter.macros.eom, `confMILTER_MACROS_EOM', `')
676 _OPTION(Milter.macros.eoh, `confMILTER_MACROS_EOH', `')
677 _OPTION(Milter.macros.data, `confMILTER_MACROS_DATA', `')')
680 _OPTION(CACertPath, `confCACERT_PATH', `')
682 _OPTION(CACertFile, `confCACERT', `')
684 _OPTION(ServerCertFile, `confSERVER_CERT', `')
686 _OPTION(ServerKeyFile, `confSERVER_KEY', `')
688 _OPTION(ClientCertFile, `confCLIENT_CERT', `')
690 _OPTION(ClientKeyFile, `confCLIENT_KEY', `')
691 # File containing certificate revocation lists
692 _OPTION(CRLFile, `confCRL', `')
693 # Directory containing hashes pointing to certificate revocation status files
694 _OPTION(CRLPath, `confCRL_PATH', `')
695 # DHParameters (only required if DSA/DH is used)
696 _OPTION(DHParameters, `confDH_PARAMETERS', `')
697 # Random data source (required for systems without /dev/urandom under OpenSSL)
698 _OPTION(RandFile, `confRAND_FILE', `')
699 # fingerprint algorithm (digest) to use for the presented cert
700 _OPTION(CertFingerprintAlgorithm, `confCERT_FINGERPRINT_ALGORITHM', `')
702 _OPTION(DANE, `confDANE', `false')
704 # Maximum number of "useless" commands before slowing down
705 _OPTION(MaxNOOPCommands, `confMAX_NOOP_COMMANDS', `20')
707 # Name to use for EHLO (defaults to $j)
708 _OPTION(HeloName, `confHELO_NAME')
710 ifdef(`_NEED_SMTPOPMODES_', `dnl
711 # SMTP operation modes
712 C{SMTPOpModes} s d D')
714 ############################
715 `# QUEUE GROUP DEFINITIONS #'
716 ############################
719 ###########################
720 # Message precedences #
721 ###########################
724 Pspecial-delivery=100
729 #####################
731 #####################
733 # this is equivalent to setting class "t"
734 ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
737 ifdef(`_NO_UUCP_', `dnl', `Tuucp')
738 ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
740 #########################
741 # Format of headers #
742 #########################
744 ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
745 ifdef(`confMESSAGEID_HEADER',, `define(`confMESSAGEID_HEADER', `<$t.$i@$j>')')dnl
746 H?P?Return-Path: <$g>
747 HReceived: confRECEIVED_HEADER
750 H?F?Resent-From: confFROM_HEADER
751 H?F?From: confFROM_HEADER
754 # H?l?Received-Date: $b
755 H?M?Resent-Message-Id: confMESSAGEID_HEADER
756 H?M?Message-Id: confMESSAGEID_HEADER
759 ######################################################################
760 ######################################################################
762 ##### REWRITING RULES
764 ######################################################################
765 ######################################################################
767 ############################################
768 ### Ruleset 3 -- Name Canonicalization ###
769 ############################################
772 # handle null input (translate to <@> special case)
775 # strip group: syntax (not inside angle brackets!) and trailing semicolon
776 R$* $: $1 <@> mark addresses
777 R$* < $* > $* <@> $: $1 < $2 > $3 unmark <addr>
778 R@ $* <@> $: @ $1 unmark @host:...
779 R$* [ IPv6 : $+ ] <@> $: $1 [ IPv6 : $2 ] unmark IPv6 addr
780 R$* :: $* <@> $: $1 :: $2 unmark node::addr
781 R:`include': $* <@> $: :`include': $1 unmark :`include':...
782 R$* : $* [ $* ] $: $1 : $2 [ $3 ] <@> remark if leading colon
783 R$* : $* <@> $: $2 strip colon if marked
785 R$* ; $1 strip trailing semi
786 R$* < $+ :; > $* $@ $2 :; <@> catch <list:;>
787 R$* < $* ; > $1 < $2 > bogus bracketed semi
789 # null input now results from list:; syntax
792 # strip angle brackets -- note RFC733 heuristic to get innermost item
793 R$* $: < $1 > housekeeping <>
794 R$+ < $* > < $2 > strip excess on left
795 R< $* > $+ < $1 > strip excess on right
796 R<> $@ < @ > MAIL FROM:<> case
797 R< $+ > $: $1 remove housekeeping <>
799 ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
800 # make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
801 R@ $+ , $+ @ $1 : $2 change all "," to ":"
803 # localize and dispose of route-based addresses
804 dnl XXX: IPv6 colon conflict
805 ifdef(`NO_NETINET6', `dnl',
806 `R@ [$+] : $+ $@ $>Canonify2 < @ [$1] > : $2 handle <route-addr>')
807 R@ $+ : $+ $@ $>Canonify2 < @$1 > : $2 handle <route-addr>
809 # strip route address <@a,@b,@c:user@d> -> <user@d>
811 ifdef(`NO_NETINET6', `dnl',
816 # find focus for list syntax
817 R $+ : $* ; @ $+ $@ $>Canonify2 $1 : $2 ; < @ $3 > list syntax
818 R $+ : $* ; $@ $1 : $2; list syntax
820 # find focus for @ syntax addresses
821 R$+ @ $+ $: $1 < @ $2 > focus on domain
822 R$+ < $+ @ $+ > $1 $2 < @ $3 > move gaze right
823 R$+ < @ $+ > $@ $>Canonify2 $1 < @ $2 > already canonical
825 dnl This is flagged as an error in S0; no need to silently fix it here.
826 dnl # do some sanity checking
827 dnl R$* < @ $~[ $* : $* > $* $1 < @ $2 $3 > $4 nix colons in addrs
829 ifdef(`_NO_UUCP_', `dnl',
830 `# convert old-style addresses to a domain-based address
831 R$- ! $+ $@ $>Canonify2 $2 < @ $1 .UUCP > resolve uucp names
832 R$+ . $- ! $+ $@ $>Canonify2 $3 < @ $1 . $2 > domain uucps
833 R$+ ! $+ $@ $>Canonify2 $2 < @ $1 .UUCP > uucp subdomains
835 ifdef(`_USE_DECNET_SYNTAX_',
836 `# convert node::user addresses into a domain-based address
837 R$- :: $+ $@ $>Canonify2 $2 < @ $1 .DECNET > resolve DECnet names
838 R$- . $- :: $+ $@ $>Canonify2 $3 < @ $1.$2 .DECNET > numeric DECnet addr
841 ifdef(`_NO_PERCENTHACK_', `dnl',
842 `# if we have % signs, take the rightmost one
843 R$* % $* $1 @ $2 First make them all @s.
844 R$* @ $* @ $* $1 % $2 @ $3 Undo all but the last.
846 R$* @ $* $@ $>Canonify2 $1 < @ $2 > Insert < > and finish
848 # else we must be a local name
849 R$* $@ $>Canonify2 $1
852 ################################################
853 ### Ruleset 96 -- bottom half of ruleset 3 ###
854 ################################################
858 # handle special cases for local names
859 R$* < @ localhost > $* $: $1 < @ $j . > $2 no domain at all
860 R$* < @ localhost . $m > $* $: $1 < @ $j . > $2 local domain
861 ifdef(`_NO_UUCP_', `dnl',
862 `R$* < @ localhost . UUCP > $* $: $1 < @ $j . > $2 .UUCP domain')
864 # check for IPv4/IPv6 domain literal
865 R$* < @ [ $+ ] > $* $: $1 < @@ [ $2 ] > $3 mark [addr]
866 R$* < @@ $=w > $* $: $1 < @ $j . > $3 self-literal
867 R$* < @@ $+ > $* $@ $1 < @ $2 > $3 canon IP addr
869 ifdef(`_DOMAIN_TABLE_', `dnl
870 # look up domains in the domain table
871 R$* < @ $+ > $* $: $1 < @ $(domaintable $2 $) > $3', `dnl')
873 undivert(2)dnl LOCAL_RULE_3
875 ifdef(`_BITDOMAIN_TABLE_', `dnl
876 # handle BITNET mapping
877 R$* < @ $+ .BITNET > $* $: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
879 ifdef(`_UUDOMAIN_TABLE_', `dnl
880 # handle UUCP mapping
881 R$* < @ $+ .UUCP > $* $: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
883 ifdef(`_NO_UUCP_', `dnl',
885 `# pass UUCP addresses straight through
886 R$* < @ $+ . UUCP > $* $@ $1 < @ $2 . UUCP . > $3',
887 `# if really UUCP, handle it immediately
889 `R$* < @ $=U . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
891 `R$* < @ $=V . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
893 `R$* < @ $=W . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
895 `R$* < @ $=X . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
897 `R$* < @ $=Y . UUCP > $* $@ $1 < @ $2 . UUCP . > $3', `dnl')
899 ifdef(`_NO_CANONIFY_', `dnl', `dnl
900 # try UUCP traffic as a local address
901 R$* < @ $+ . UUCP > $* $: $1 < @ $[ $2 $] . UUCP . > $3
902 R$* < @ $+ . . UUCP . > $* $@ $1 < @ $2 . > $3')
904 # hostnames ending in class P are always canonical
905 R$* < @ $* $=P > $* $: $1 < @ $2 $3 . > $4
906 dnl apply the next rule only for hostnames not in class P
907 dnl this even works for phrases in class P since . is in class P
908 dnl which daemon flags are set?
909 R$* < @ $* $~P > $* $: $&{daemon_flags} $| $1 < @ $2 $3 > $4
910 dnl the other rules in this section only apply if the hostname
911 dnl does not end in class P hence no further checks are done here
912 dnl if this ever changes make sure the lookups are "protected" again!
913 ifdef(`_NO_CANONIFY_', `dnl
914 dnl do not canonify unless:
915 dnl domain ends in class {Canonify} (this does not work if the intersection
916 dnl with class P is non-empty)
917 dnl or {daemon_flags} has c set
918 # pass to name server to make hostname canonical if in class {Canonify}
919 R$* $| $* < @ $* $={Canonify} > $* $: $2 < @ $[ $3 $4 $] > $5
920 # pass to name server to make hostname canonical if requested
921 R$* c $* $| $* < @ $* > $* $: $3 < @ $[ $4 $] > $5
922 dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
923 R$* $| $* < @ $+ . > $* $: $2 < @ $3 . > $4
924 # add a trailing dot to qualified hostnames so other rules will work
925 R$* $| $* < @ $+.$+ > $* $: $2 < @ $3.$4 . > $5
926 ifdef(`_CANONIFY_HOSTS_', `dnl
927 dnl this should only apply to unqualified hostnames
928 dnl but if a valid character inside an unqualified hostname is an OperatorChar
929 dnl then $- does not work.
930 # lookup unqualified hostnames
931 R$* $| $* < @ $* > $* $: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
932 dnl _NO_CANONIFY_ is not set: canonify unless:
933 dnl {daemon_flags} contains CC (do not canonify)
934 dnl but add a trailing dot to qualified hostnames so other rules will work
935 dnl should we do this for every hostname: even unqualified?
936 R$* CC $* $| $* < @ $+.$+ > $* $: $3 < @ $4.$5 . > $6
937 R$* CC $* $| $* $: $3
938 ifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
939 # do not canonify header addresses
940 R$* $| $* < @ $* $~P > $* $: $&{addr_type} $| $2 < @ $3 $4 > $5
941 R$* h $* $| $* < @ $+.$+ > $* $: $3 < @ $4.$5 . > $6
942 R$* h $* $| $* $: $3', `dnl')
943 # pass to name server to make hostname canonical
944 R$* $| $* < @ $* > $* $: $2 < @ $[ $3 $] > $4')
945 dnl remove {daemon_flags} for other cases
948 # local host aliases and pseudo-domains are always canonical
949 R$* < @ $=w > $* $: $1 < @ $2 . > $3
950 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
951 `R$* < @ $* $=M > $* $: $1 < @ $2 $3 . > $4',
952 `R$* < @ $=M > $* $: $1 < @ $2 . > $3')
953 ifdef(`_VIRTUSER_TABLE_', `dnl
954 dnl virtual hosts are also canonical
955 ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
956 `R$* < @ $* $={VirtHost} > $* $: $1 < @ $2 $3 . > $4',
957 `R$* < @ $={VirtHost} > $* $: $1 < @ $2 . > $3')',
959 ifdef(`_GENERICS_TABLE_', `dnl
960 dnl hosts for genericstable are also canonical
961 ifdef(`_GENERICS_ENTIRE_DOMAIN_',
962 `R$* < @ $* $=G > $* $: $1 < @ $2 $3 . > $4',
963 `R$* < @ $=G > $* $: $1 < @ $2 . > $3')',
965 dnl remove superfluous dots (maybe repeatedly) which may have been added
966 dnl by one of the rules before
967 R$* < @ $* . . > $* $1 < @ $2 . > $3
970 ##################################################
971 ### Ruleset 4 -- Final Output Post-rewriting ###
972 ##################################################
975 R$+ :; <@> $@ $1 : handle <list:;>
976 R$* <@> $@ handle <> and list:;
978 # strip trailing dot off possibly canonical name
979 R$* < @ $+ . > $* $1 < @ $2 > $3
981 # eliminate internal code
982 R$* < @ *LOCAL* > $* $1 < @ $j > $2
984 # externalize local domain info
985 R$* < $+ > $* $1 $2 $3 defocus
986 R@ $+ : @ $+ : $+ @ $1 , @ $2 : $3 <route-addr> canonical
987 R@ $* $@ @ $1 ... and exit
989 ifdef(`_NO_UUCP_', `dnl',
990 `# UUCP must always be presented in old form
991 R$+ @ $- . UUCP $2!$1 u@h.UUCP => h!u')
993 ifdef(`_USE_DECNET_SYNTAX_',
994 `# put DECnet back in :: form
995 R$+ @ $+ . DECNET $2 :: $1 u@h.DECNET => h::u',
997 # delete duplicate local names
998 R$+ % $=w @ $=w $1 @ $2 u%host@host => u@host
1002 ##############################################################
1003 ### Ruleset 97 -- recanonicalize and call ruleset zero ###
1004 ### (used for recursive calls) ###
1005 ##############################################################
1008 R$* $: $>canonify $1
1012 ######################################
1013 ### Ruleset 0 -- Parse Address ###
1014 ######################################
1018 R$* $: $>Parse0 $1 initial parsing
1019 R<@> $#_LOCAL_ $: <@> special case error msgs
1020 R$* $: $>ParseLocal $1 handle local hacks
1021 R$* $: $>Parse1 $1 final parsing
1024 # Parse0 -- do initial syntax checking and eliminate local addresses.
1025 # This should either return with the (possibly modified) input
1026 # or return with a #error mailer. It should not return with a
1027 # #mailer other than the #error mailer.
1031 R<@> $@ <@> special case error msgs
1032 R$* : $* ; <@> $#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
1033 R@ <@ $* > < @ $1 > catch "@@host" bogosity
1034 R<@ $+> $#error $@ 5.1.3 $: "_CODE553 User address required"
1035 R$+ <@> $#error $@ 5.1.3 $: "_CODE553 Hostname required"
1037 dnl allow tricks like [host1]:[host2]
1038 R<> $* < @ [ $* ] : $+ > $* $1 < @ [ $2 ] : $3 > $4
1039 R<> $* < @ [ $* ] , $+ > $* $1 < @ [ $2 ] , $3 > $4
1041 R<> $* < @ [ $* ] $+ > $* $#error $@ 5.1.2 $: "_CODE553 Invalid address"
1042 R<> $* < @ [ $+ ] > $* $1 < @ [ $2 ] > $3
1043 R<> $* <$* : $* > $* $#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
1045 R$* < @ . $* > $* $#error $@ 5.1.2 $: "_CODE553 Invalid host name"
1046 R$* < @ $* .. $* > $* $#error $@ 5.1.2 $: "_CODE553 Invalid host name"
1048 R$* < @ $* @ > $* $#error $@ 5.1.2 $: "_CODE553 Invalid route address"
1050 R$* @ $* < @ $* > $* $#error $@ 5.1.3 $: "_CODE553 Invalid route address"
1051 dnl comma only allowed before @; this check is not complete
1052 R$* , $~O $* $#error $@ 5.1.3 $: "_CODE553 Invalid route address"
1054 ifdef(`_STRICT_RFC821_', `# more RFC 821 checks
1055 R$* . < @ $* > $* $#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
1056 R. $* < @ $* > $* $#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
1059 # now delete the local info -- note $=O to find characters that cause forwarding
1060 R$* < @ > $* $@ $>Parse0 $>canonify $1 user@ => user
1061 R< @ $=w . > : $* $@ $>Parse0 $>canonify $2 @here:... -> ...
1062 R$- < @ $=w . > $: $(dequote $1 $) < @ $2 . > dequote "foo"@here
1063 R< @ $+ > $#error $@ 5.1.3 $: "_CODE553 User address required"
1064 R$* $=O $* < @ $=w . > $@ $>Parse0 $>canonify $1 $2 $3 ...@here -> ...
1065 R$- $: $(dequote $1 $) < @ *LOCAL* > dequote "foo"
1066 R< @ *LOCAL* > $#error $@ 5.1.3 $: "_CODE553 User address required"
1067 R$* $=O $* < @ *LOCAL* >
1068 $@ $>Parse0 $>canonify $1 $2 $3 ...@*LOCAL* -> ...
1069 R$* < @ *LOCAL* > $: $1
1071 ifdef(`_ADD_BCC_', `dnl
1072 R$+ $: $>ParseBcc $1', `dnl')
1073 ifdef(`_PREFIX_MOD_', `dnl
1074 dnl do this only for addr_type=e r?
1075 R _PREFIX_MOD_ $+ $: $1 $(macro {rcpt_flags} $@ _PREFIX_FLAGS_ $)
1079 # Parse1 -- the bottom half of ruleset 0.
1083 ifdef(`_LDAP_ROUTING_', `dnl
1084 # handle LDAP routing for hosts in $={LDAPRoute}
1085 R$+ < @ $={LDAPRoute} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
1086 R$+ < @ $={LDAPRouteEquiv} . > $: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
1089 ifdef(`_MAILER_smtp_',
1090 `# handle numeric address spec
1091 dnl there is no check whether this is really an IP number
1092 R$* < @ [ $+ ] > $* $: $>ParseLocal $1 < @ [ $2 ] > $3 numeric internet spec
1093 R$* < @ [ $+ ] > $* $: $1 < @ [ $2 ] : $S > $3 Add smart host to path
1094 R$* < @ [ $+ ] : > $* $#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3 no smarthost: send
1095 R$* < @ [ $+ ] : $- : $*> $* $#$3 $@ $4 $: $1 < @ [$2] > $5 smarthost with mailer
1096 R$* < @ [ $+ ] : $+ > $* $#_SMTP_ $@ $3 $: $1 < @ [$2] > $4 smarthost without mailer',
1099 ifdef(`_VIRTUSER_TABLE_', `dnl
1100 # handle virtual users
1101 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1102 dnl this is not a documented option
1103 dnl it stops looping in virtusertable mapping if input and output
1104 dnl are identical, i.e., if address A is mapped to A.
1105 dnl it does not deal with multi-level recursion
1106 # handle full domains in RHS of virtusertable
1107 R$+ < @ $+ > $: $(macro {RecipientAddress} $) $1 < @ $2 >
1108 R$+ < @ $+ > $: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
1109 R<?> $+ $| $+ $: $1 $(macro {RecipientAddress} $@ $2 $)
1110 R<?> $+ $| $* $: $1',
1112 R$+ $: <!> $1 Mark for lookup
1113 dnl input: <!> local<@domain>
1114 ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
1115 `R<!> $+ < @ $* $={VirtHost} . > $: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
1116 `R<!> $+ < @ $={VirtHost} . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
1117 dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
1118 R<!> $+ < @ $=w . > $: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1119 dnl if <@> local<@domain>: no match but try lookup
1120 dnl user+detail: try user++@domain if detail not empty
1121 R<@> $+ + $+ < @ $* . >
1122 $: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1123 dnl user+detail: try user+*@domain
1124 R<@> $+ + $* < @ $* . >
1125 $: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1126 dnl user+detail: try user@domain
1127 R<@> $+ + $* < @ $* . >
1128 $: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1129 dnl try default entry: @domain
1131 R<@> $+ + $+ < @ $+ . > $: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1133 R<@> $+ + $* < @ $+ . > $: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
1134 dnl @domain if +detail exists
1135 dnl if no match, change marker to prevent a second @domain lookup
1136 R<@> $+ + $* < @ $+ . > $: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
1138 R<@> $+ < @ $+ . > $: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
1143 R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4
1144 R< error : $- $+ > $* $#error $@ $(dequote $1 $) $: $2
1145 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
1146 # check virtuser input address against output address, if same, skip recursion
1147 R< $+ > $+ < @ $+ > $: < $1 > $2 < @ $3 > $| $1
1148 # it is the same: stop now
1149 R< $+ > $+ < @ $+ > $| $&{RecipientAddress} $: $>ParseLocal $>Parse0 $>canonify $1
1150 R< $+ > $+ < @ $+ > $| $* $: < $1 > $2 < @ $3 >
1152 dnl this is not a documented option
1153 dnl it performs no looping at all for virtusertable
1154 ifdef(`_NO_VIRTUSER_RECURSION_',
1155 `R< $+ > $+ < @ $+ > $: $>ParseLocal $>Parse0 $>canonify $1',
1156 `R< $+ > $+ < @ $+ > $: $>Recurse $1')
1159 # short circuit local delivery so forwarded email works
1160 ifdef(`_MAILER_usenet_', `dnl
1161 R$+ . USENET < @ $=w . > $#usenet $@ usenet $: $1 handle usenet specially', `dnl')
1164 ifdef(`_STICKY_LOCAL_DOMAIN_',
1165 `R$+ < @ $=w . > $: < $H > $1 < @ $2 . > first try hub
1166 R< $+ > $+ < $+ > $>MailerToTriple < $1 > $2 < $3 > yep ....
1167 dnl $H empty (but @$=w.)
1168 R< > $+ + $* < $+ > $#_LOCAL_ $: $1 + $2 plussed name?
1169 R< > $+ < $+ > $#_LOCAL_ $: @ $1 nope, local address',
1170 `R$=L < @ $=w . > $#_LOCAL_ $: @ $1 special local names
1171 R$+ < @ $=w . > $#_LOCAL_ $: $1 regular local name')
1173 ifdef(`_MAILER_TABLE_', `dnl
1174 # not local -- try mailer table lookup
1175 R$* <@ $+ > $* $: < $2 > $1 < @ $2 > $3 extract host name
1176 R< $+ . > $* $: < $1 > $2 strip trailing dot
1177 R< $+ > $* $: < $(mailertable $1 $) > $2 lookup
1178 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1179 R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check -- resolved?
1180 R< $+ > $* $: $>Mailertable <$1> $2 try domain',
1182 undivert(4)dnl UUCP rules from `MAILER(uucp)'
1184 ifdef(`_NO_UUCP_', `dnl',
1185 `# resolve remotely connected UUCP links (if any)
1187 `R$* < @ $=V . UUCP . > $* $: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
1190 `R$* < @ $=W . UUCP . > $* $: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
1193 `R$* < @ $=X . UUCP . > $* $: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
1196 # resolve fake top level domains by forwarding to other hosts
1197 ifdef(`BITNET_RELAY',
1198 `R$*<@$+.BITNET.>$* $: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3 user@host.BITNET',
1200 ifdef(`DECNET_RELAY',
1201 `R$*<@$+.DECNET.>$* $: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3 user@host.DECNET',
1203 ifdef(`_MAILER_pop_',
1204 `R$+ < @ POP. > $#pop $: $1 user@POP',
1206 ifdef(`_MAILER_fax_',
1207 `R$+ < @ $+ .FAX. > $#fax $@ $2 $: $1 user@host.FAX',
1209 `R$*<@$+.FAX.>$* $: $>MailerToTriple < $F > $1 <@$2.FAX.> $3 user@host.FAX',
1213 `# forward non-local UUCP traffic to our UUCP relay
1214 R$*<@$*.UUCP.>$* $: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3 uucp mail',
1215 `ifdef(`_MAILER_uucp_',
1216 `# forward other UUCP traffic straight to UUCP
1217 R$* < @ $+ .UUCP. > $* $#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3 user@host.UUCP',
1219 ifdef(`_MAILER_usenet_', `
1220 # addresses sent to net.group.USENET will get forwarded to a newsgroup
1221 R$+ . USENET $#usenet $@ usenet $: $1',
1224 ifdef(`_LOCAL_RULES_',
1225 `# figure out what should stay in our local mail system
1226 undivert(1)', `dnl')
1228 # pass names that still have a host to a smarthost (if defined)
1229 R$* < @ $* > $* $: $>MailerToTriple < $S > $1 < @ $2 > $3 glue on smarthost name
1231 # deal with other remote names
1232 ifdef(`_MAILER_smtp_',
1233 `R$* < @$* > $* $#_SMTP_ $@ $2 $: $1 < @ $2 > $3 user@host.domain',
1234 `R$* < @$* > $* $#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
1236 # handle locally delivered names
1237 R$=L $#_LOCAL_ $: @ $1 special local names
1238 R$+ $#_LOCAL_ $: $1 regular local names
1240 ifdef(`_ADD_BCC_', `dnl
1242 R$+ $: $&{addr_type} $| $&A $| $1
1243 Re b $| $+ $| $+ $>MailerToTriple < $1 > $2 copy?
1244 R$* $| $* $| $+ $@ $3 no copy
1247 ###########################################################################
1248 ### Ruleset 5 -- special rewriting after aliases have been expanded ###
1249 ###########################################################################
1253 R$+ $: $1 $| $>"Local_localaddr" $1
1254 R$+ $| $#ok $@ $1 no change
1258 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1259 # Preserve rcpt_host in {Host}
1260 R$+ $: $1 $| $&h $| $&{Host} check h and {Host}
1261 R$+ $| $| $: $(macro {Host} $@ $) $1 no h or {Host}
1262 R$+ $| $| $+ $: $1 h not set, {Host} set
1263 R$+ $| +$* $| $* $: $1 h is +detail, {Host} set
1264 R$+ $| $* @ $+ $| $* $: $(macro {Host} $@ @$3 $) $1 set {Host} to host in h
1265 R$+ $| $+ $| $* $: $(macro {Host} $@ @$2 $) $1 set {Host} to h
1268 ifdef(`_FFR_5_', `dnl
1269 # Preserve host in a macro
1270 R$+ $: $(macro {LocalAddrHost} $) $1
1271 R$+ @ $+ $: $(macro {LocalAddrHost} $@ @ $2 $) $1')
1273 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
1274 # deal with plussed users so aliases work nicely
1275 R$+ + * $#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1276 R$+ + $* $#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
1278 # prepend an empty "forward host" on the front
1281 ifdef(`LUSER_RELAY', `dnl
1282 # send unrecognized local users to a relay host
1283 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1284 R< > $+ + $* $: < ? $L > <+ $2> $(user $1 $) look up user+
1285 R< > $+ $: < ? $L > < > $(user $1 $) look up user
1286 R< ? $* > < $* > $+ <> $: < > $3 $2 found; strip $L
1287 R< ? $* > < $* > $+ $: < $1 > $3 $2 not found', `
1288 R< > $+ $: < $L > $(user $1 $) look up user
1289 R< $* > $+ <> $: < > $2 found; strip $L')
1290 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1291 R< $+ > $+ $: < $1 > $2 $&{Host}')
1294 ifdef(`MAIL_HUB', `dnl
1295 R< > $+ $: < $H > $1 try hub', `dnl')
1296 ifdef(`LOCAL_RELAY', `dnl
1297 R< > $+ $: < $R > $1 try relay', `dnl')
1298 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
1299 R< > $+ $@ $1', `dnl
1300 R< > $+ $: < > < $1 <> $&h > nope, restore +detail
1301 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1302 R< > < $+ @ $+ <> + $* > $: < > < $1 + $3 @ $2 > check whether +detail')
1303 R< > < $+ <> + $* > $: < > < $1 + $2 > check whether +detail
1304 R< > < $+ <> $* > $: < > < $1 > else discard
1305 R< > < $+ + $* > $* < > < $1 > + $2 $3 find the user part
1306 R< > < $+ > + $* $#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}') strip the extra +
1307 R< > < $+ > $@ $1 no +detail
1308 R$+ $: $1 <> $&h add +detail back in
1309 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1310 R$+ @ $+ <> + $* $: $1 + $3 @ $2 check whether +detail')
1311 R$+ <> + $* $: $1 + $2 check whether +detail
1312 R$+ <> $* $: $1 else discard')
1313 R< local : $* > $* $: $>MailerToTriple < local : $1 > $2 no host extension
1314 R< error : $* > $* $: $>MailerToTriple < error : $1 > $2 no host extension
1315 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1316 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1317 R< $~[ : $+ > $+ @ $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
1318 R< $~[ : $+ > $+ $: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
1319 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
1320 R< $+ > $+ @ $+ $@ $>MailerToTriple < $1 > $2 < @ $3 >')
1321 R< $+ > $+ $@ $>MailerToTriple < $1 > $2 < @ $1 >
1323 ifdef(`_MAILER_TABLE_', `dnl
1324 ifdef(`_LDAP_ROUTING_', `dnl
1325 ###################################################################
1326 ### Ruleset LDAPMailertable -- mailertable lookup for LDAP ###
1327 dnl input: <Domain> FullAddress
1328 ###################################################################
1331 R< $+ > $* $: < $(mailertable $1 $) > $2 lookup
1332 R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 check resolved?
1333 R< $+ > $* $: < $1 > $>Mailertable <$1> $2 try domain
1334 R< $+ > $#$* $#$2 found
1335 R< $+ > $* $#_RELAY_ $@ $1 $: $2 not found, direct relay',
1338 ###################################################################
1339 ### Ruleset 90 -- try domain part of mailertable entry ###
1340 dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
1341 ###################################################################
1345 dnl %2 is not documented in cf/README
1346 R$* <$- . $+ > $* $: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
1347 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1348 R$* <$~[ : $* > $* $>MailerToTriple < $2 : $3 > $4 check -- resolved?
1349 R$* < . $+ > $* $@ $>Mailertable $1 . <$2> $3 no -- strip & try again
1350 dnl is $2 always empty?
1351 R$* < $* > $* $: < $(mailertable . $@ $1$2 $) > $3 try "."
1352 R< $~[ : $* > $* $>MailerToTriple < $1 : $2 > $3 "." found?
1353 dnl return full address
1354 R< $* > $* $@ $2 no mailertable match',
1357 ###################################################################
1358 ### Ruleset 95 -- canonify mailer:[user@]host syntax to triple ###
1359 dnl input: in general: <[mailer:]host> lp<@domain>rest
1360 dnl <> address -> address
1361 dnl <error:d.s.n:text> -> error
1362 dnl <error:keyword:text> -> error
1363 dnl <error:text> -> error
1364 dnl <mailer:user@host> lp<@domain>rest -> mailer host user
1365 dnl <mailer:host> address -> mailer host address
1366 dnl <localdomain> address -> address
1367 dnl <host> address -> relay host address
1368 ###################################################################
1371 R< > $* $@ $1 strip off null relay
1372 R< error : $-.$-.$- : $+ > $* $#error $@ $1.$2.$3 $: $4
1373 R< error : $- : $+ > $* $#error $@ $(dequote $1 $) $: $2
1374 R< error : $+ > $* $#error $: $1
1375 R< local : $* > $* $>CanonLocal < $1 > $2
1376 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
1377 R< $~[ : $+ @ $+ > $*<$*>$* $# $1 $@ $3 $: $2<@$3> use literal user
1378 R< $~[ : $+ > $* $# $1 $@ $2 $: $3 try qualified mailer
1379 R< $=w > $* $@ $2 delete local host
1380 R< $+ > $* $#_RELAY_ $@ $1 $: $2 use unqualified mailer
1382 ###################################################################
1383 ### Ruleset CanonLocal -- canonify local: syntax ###
1384 dnl input: <user> address
1385 dnl <x> <@host> : rest -> Recurse rest
1386 dnl <x> p1 $=O p2 <@host> -> Recurse p1 $=O p2
1387 dnl <> user <@host> rest -> local user@host user
1388 dnl <> user -> local user user
1389 dnl <user@host> lp <@domain> rest -> <user> lp <@host> [cont]
1390 dnl <user> lp <@host> rest -> local lp@host user
1391 dnl <user> lp -> local lp user
1392 ###################################################################
1395 # strip local host from routed addresses
1396 R< $* > < @ $+ > : $+ $@ $>Recurse $3
1397 R< $* > $+ $=O $+ < @ $+ > $@ $>Recurse $2 $3 $4
1399 # strip trailing dot from any host name that may appear
1400 R< $* > $* < @ $* . > $: < $1 > $2 < @ $3 >
1402 # handle local: syntax -- use old user, either with or without host
1403 R< > $* < @ $* > $* $#_LOCAL_ $@ $1@$2 $: $1
1404 R< > $+ $#_LOCAL_ $@ $1 $: $1
1406 # handle local:user@host syntax -- ignore host part
1407 R< $+ @ $+ > $* < @ $* > $: < $1 > $3 < @ $4 >
1409 # handle local:user syntax
1410 R< $+ > $* <@ $* > $* $#_LOCAL_ $@ $2@$3 $: $1
1411 R< $+ > $* $#_LOCAL_ $@ $2 $: $1
1413 ###################################################################
1414 ### Ruleset 93 -- convert header names to masqueraded form ###
1415 ###################################################################
1419 ifdef(`_GENERICS_TABLE_', `dnl
1420 # handle generics database
1421 ifdef(`_GENERICS_ENTIRE_DOMAIN_',
1422 dnl if generics should be applied add a @ as mark
1423 `R$+ < @ $* $=G . > $: < $1@$2$3 > $1 < @ $2$3 . > @ mark',
1424 `R$+ < @ $=G . > $: < $1@$2 > $1 < @ $2 . > @ mark')
1425 R$+ < @ *LOCAL* > $: < $1@$j > $1 < @ *LOCAL* > @ mark
1426 dnl workspace: either user<@domain> or <user@domain> user <@domain> @
1427 dnl ignore the first case for now
1428 dnl if it has the mark lookup full address
1429 dnl broken: %1 is full address not just detail
1430 R< $+ > $+ < $* > @ $: < $(generics $1 $: @ $1 $) > $2 < $3 >
1431 dnl workspace: ... or <match|@user@domain> user <@domain>
1432 dnl no match, try user+detail@domain
1433 R<@$+ + $* @ $+> $+ < @ $+ >
1434 $: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) > $4 < @ $5 >
1435 R<@$+ + $* @ $+> $+ < @ $+ >
1436 $: < $(generics $1@$3 $: $) > $4 < @ $5 >
1437 dnl no match, remove mark
1438 R<@$+ > $+ < @ $+ > $: < > $2 < @ $3 >
1439 dnl no match, try @domain for exceptions
1440 R< > $+ < @ $+ . > $: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
1441 dnl workspace: ... or <match> user <@domain>
1442 dnl no match, try local part
1443 R< > $+ < @ $+ > $: < $(generics $1 $: $) > $1 < @ $2 >
1444 R< > $+ + $* < @ $+ > $: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
1445 R< > $+ + $* < @ $+ > $: < $(generics $1 $: $) > $1 + $2 < @ $3 >
1446 R< $* @ $* > $* < $* > $@ $>canonify $1 @ $2 found qualified
1447 R< $+ > $* < $* > $: $>canonify $1 @ *LOCAL* found unqualified
1448 R< > $* $: $1 not found',
1451 # do not masquerade anything in class N
1452 R$* < @ $* $=N . > $@ $1 < @ $2 $3 . >
1454 ifdef(`MASQUERADE_NAME', `dnl
1455 # special case the users that should be exposed
1456 R$=E < @ *LOCAL* > $@ $1 < @ $j . > leave exposed
1457 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1458 `R$=E < @ $* $=M . > $@ $1 < @ $2 $3 . >',
1459 `R$=E < @ $=M . > $@ $1 < @ $2 . >')
1460 ifdef(`_LIMITED_MASQUERADE_', `dnl',
1461 `R$=E < @ $=w . > $@ $1 < @ $2 . >')
1463 # handle domain-specific masquerading
1464 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
1465 `R$* < @ $* $=M . > $* $: $1 < @ $2 $3 . @ $M > $4 convert masqueraded doms',
1466 `R$* < @ $=M . > $* $: $1 < @ $2 . @ $M > $3 convert masqueraded doms')
1467 ifdef(`_LIMITED_MASQUERADE_', `dnl',
1468 `R$* < @ $=w . > $* $: $1 < @ $2 . @ $M > $3')
1469 R$* < @ *LOCAL* > $* $: $1 < @ $j . @ $M > $2
1470 R$* < @ $+ @ > $* $: $1 < @ $2 > $3 $M is null
1471 R$* < @ $+ @ $+ > $* $: $1 < @ $3 . > $4 $M is not null
1472 dnl', `dnl no masquerading
1473 dnl just fix *LOCAL* leftovers
1474 R$* < @ *LOCAL* > $@ $1 < @ $j . >')
1476 ###################################################################
1477 ### Ruleset 94 -- convert envelope names to masqueraded form ###
1478 ###################################################################
1481 ifdef(`_MASQUERADE_ENVELOPE_',
1482 `R$+ $@ $>MasqHdr $1',
1483 `R$* < @ *LOCAL* > $* $: $1 < @ $j . > $2')
1485 ###################################################################
1486 ### Ruleset 98 -- local part of ruleset zero (can be null) ###
1487 ###################################################################
1490 undivert(3)dnl LOCAL_RULE_0
1492 ifdef(`_LDAP_ROUTING_', `dnl
1493 ######################################################################
1494 ### LDAPExpand: Expand address using LDAP routing
1497 ### <$1> -- parsed address (user < @ domain . >) (pass through)
1498 ### <$2> -- RFC822 address (user @ domain) (used for lookup)
1499 ### <$3> -- +detail information
1502 ### Mailer triplet ($#mailer $@ host $: address)
1503 ### Parsed address (user < @ domain . >)
1504 ######################################################################
1507 # do the LDAP lookups
1508 R<$+><$+><$*> $: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
1510 # look for temporary failures and...
1511 R<$* <TMPF>> <$*> <$+> <$+> <$*> $: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1512 R<$*> <$* <TMPF>> <$+> <$+> <$*> $: $&{opMode} $| TMPF <$&{addr_type}> $| $3
1513 ifelse(_LDAP_ROUTE_MAPTEMP_, `_TEMPFAIL_', `dnl
1514 # ... temp fail RCPT SMTP commands
1515 R$={SMTPOpModes} $| TMPF <e r> $| $+ $#error $@ 4.3.0 $: _TMPFMSG_(`OPM')')
1516 # ... return original address for MTA to queue up
1517 R$* $| TMPF <$*> $| $+ $@ $3
1519 # if mailRoutingAddress and local or non-existant mailHost,
1520 # return the new mailRoutingAddress
1521 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1522 R<$+@$+> <$=w> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 $6 @ $2
1523 R<$+@$+> <> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1 $5 @ $2')
1524 R<$+> <$=w> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1
1525 R<$+> <> <$+> <$+> <$*> $@ $>Parse0 $>canonify $1
1528 # if mailRoutingAddress and non-local mailHost,
1529 # relay to mailHost with new mailRoutingAddress
1530 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1531 ifdef(`_MAILER_TABLE_', `dnl
1532 # check mailertable for host, relay from there
1533 R<$+@$+> <$+> <$+> <$+> <$*> $>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
1534 `R<$+@$+> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
1535 ifdef(`_MAILER_TABLE_', `dnl
1536 # check mailertable for host, relay from there
1537 R<$+> <$+> <$+> <$+> <$*> $>LDAPMailertable <$2> $>canonify $1',
1538 `R<$+> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $2 $: $>canonify $1')
1540 # if no mailRoutingAddress and local mailHost,
1541 # return original address
1542 R<> <$=w> <$+> <$+> <$*> $@ $2
1545 # if no mailRoutingAddress and non-local mailHost,
1546 # relay to mailHost with original address
1547 ifdef(`_MAILER_TABLE_', `dnl
1548 # check mailertable for host, relay from there
1549 R<> <$+> <$+> <$+> <$*> $>LDAPMailertable <$1> $2',
1550 `R<> <$+> <$+> <$+> <$*> $#_RELAY_ $@ $1 $: $2')
1552 ifdef(`_LDAP_ROUTE_DETAIL_',
1553 `# if no mailRoutingAddress and no mailHost,
1554 # try without +detail
1555 R<> <> <$+> <$+ + $* @ $+> <> $@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
1557 ifdef(`_LDAP_ROUTE_NODOMAIN_', `
1558 # pretend we did the @domain lookup
1559 R<> <> <$+> <$+ @ $+> <$*> $: <> <> <$1> <@ $3> <$4>', `
1560 # if still no mailRoutingAddress and no mailHost,
1562 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
1563 R<> <> <$+> <$+ + $* @ $+> <> $@ $>LDAPExpand <$1> <@ $4> <+$3>')
1564 R<> <> <$+> <$+ @ $+> <$*> $@ $>LDAPExpand <$1> <@ $3> <$4>')
1566 # if no mailRoutingAddress and no mailHost and this was a domain attempt,
1567 ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
1568 # user does not exist
1569 R<> <> <$+> <@ $+> <$*> $: <?> < $&{addr_type} > < $1 >
1570 # only give error for envelope recipient
1571 R<?> <e r> <$+> $#error $@ nouser $: "550 User unknown"
1572 ifdef(`_LDAP_SENDER_MUST_EXIST_', `dnl
1573 # and the sender too
1574 R<?> <e s> <$+> $#error $@ nouser $: "550 User unknown"')
1575 R<?> <$*> <$+> $@ $2',
1577 # return the original address
1578 R<> <> <$+> <@ $+> <$*> $@ $1')
1582 ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
1584 ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
1585 ######################################################################
1586 ### D: LookUpDomain -- search for domain in access database
1589 ### <$1> -- key (domain name)
1590 ### <$2> -- default (what to return if not found in db)
1591 dnl must not be empty
1592 ### <$3> -- mark (must be <(!|+) single-token>)
1593 ### ! does lookup only with tag
1594 ### + does lookup with and without tag
1595 ### <$4> -- passthru (additional data passed unchanged through)
1596 dnl returns: <default> <passthru>
1597 dnl <result> <passthru>
1598 ######################################################################
1601 dnl workspace <key> <default> <passthru> <mark>
1602 dnl lookup with tag (in front, no delimiter here)
1604 R<$*> <$+> <$- $-> <$*> $: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1605 dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
1606 dnl lookup without tag?
1608 R<?> <$+> <$+> <+ $-> <$*> $: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1609 ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
1610 dnl XXX apply this also to IP addresses?
1611 dnl currently it works the wrong way round for [1.2.3.4]
1613 R<?> <$+.$+> <$+> <$- $-> <$*> $: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
1615 R<?> <$+.$+> <$+> <+ $-> <$*> $: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
1616 ifdef(`_ACCESS_SKIP_', `dnl
1617 dnl found SKIP: return <default> and <passthru>
1619 R<SKIP> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>', `dnl')
1620 dnl not found: IPv4 net (no check is done whether it is an IP number!)
1622 R<?> <[$+.$-]> <$+> <$- $-> <$*> $@ $>D <[$1]> <$3> <$4 $5> <$6>
1623 ifdef(`NO_NETINET6', `dnl',
1624 `dnl not found: IPv6 net
1625 dnl (could be merged with previous rule if we have a class containing .:)
1627 R<?> <[$+::$-]> <$+> <$- $-> <$*> $: $>D <[$1]> <$3> <$4 $5> <$6>
1628 R<?> <[$+:$-]> <$+> <$- $-> <$*> $: $>D <[$1]> <$3> <$4 $5> <$6>')
1629 dnl not found, but subdomain: try again
1631 R<?> <$+.$+> <$+> <$- $-> <$*> $@ $>D <$2> <$3> <$4 $5> <$6>
1632 ifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
1634 R<?> <$+> <$+> <! $-> <$*> $: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
1635 dnl not found, no subdomain: return <default> and <passthru>
1637 R<?> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>
1638 ifdef(`_ATMPF_', `dnl tempfail?
1640 R<$* _ATMPF_> <$+> <$+> <$- $-> <$*> $@ <_ATMPF_> <$6>', `dnl')
1641 dnl return <result of lookup> and <passthru>
1643 R<$*> <$+> <$+> <$- $-> <$*> $@ <$1> <$6>
1645 ######################################################################
1646 ### A: LookUpAddress -- search for host address in access database
1649 ### <$1> -- key (dot quadded host address)
1650 ### <$2> -- default (what to return if not found in db)
1651 dnl must not be empty
1652 ### <$3> -- mark (must be <(!|+) single-token>)
1653 ### ! does lookup only with tag
1654 ### + does lookup with and without tag
1655 ### <$4> -- passthru (additional data passed through)
1656 dnl returns: <default> <passthru>
1657 dnl <result> <passthru>
1658 ######################################################################
1663 R<$+> <$+> <$- $-> <$*> $: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
1664 dnl lookup without tag
1666 R<?> <$+> <$+> <+ $-> <$*> $: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
1667 dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
1668 ifdef(`_ACCESS_SKIP_', `dnl
1669 dnl found SKIP: return <default> and <passthru>
1671 R<SKIP> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>', `dnl')
1672 ifdef(`NO_NETINET6', `dnl',
1673 `dnl no match; IPv6: remove last part
1675 R<?> <$+::$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>
1676 R<?> <$+:$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>')
1677 dnl no match; IPv4: remove last part
1679 R<?> <$+.$-> <$+> <$- $-> <$*> $@ $>A <$1> <$3> <$4 $5> <$6>
1680 dnl no match: return default
1682 R<?> <$+> <$+> <$- $-> <$*> $@ <$2> <$5>
1683 ifdef(`_ATMPF_', `dnl tempfail?
1685 R<$* _ATMPF_> <$+> <$+> <$- $-> <$*> $@ <_ATMPF_> <$6>', `dnl')
1686 dnl match: return result
1688 R<$*> <$+> <$+> <$- $-> <$*> $@ <$1> <$6>
1689 dnl endif _ACCESS_TABLE_
1691 ######################################################################
1692 ### CanonAddr -- Convert an address into a standard form for
1693 ### relay checking. Route address syntax is
1694 ### crudely converted into a %-hack address.
1697 ### $1 -- full recipient address
1700 ### parsed address, not in source route form
1701 dnl user%host%host<@domain>
1702 dnl host!user<@domain>
1703 ######################################################################
1706 R$* $: $>Parse0 $>canonify $1 make domain canonical
1707 ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
1708 R< @ $+ > : $* @ $* < @ $1 > : $2 % $3 change @ to % in src route
1709 R$* < @ $+ > : $* : $* $3 $1 < @ $2 > : $4 change to % hack.
1710 R$* < @ $+ > : $* $3 $1 < @ $2 >
1713 ######################################################################
1714 ### ParseRecipient -- Strip off hosts in $=R as well as possibly
1715 ### $* $=m or the access database.
1716 ### Check user portion for host separators.
1719 ### $1 -- full recipient address
1722 ### parsed, non-local-relaying address
1723 ######################################################################
1726 dnl mark and canonify address
1727 R$* $: <?> $>CanonAddr $1
1728 dnl workspace: <?> localpart<@domain[.]>
1729 R<?> $* < @ $* . > <?> $1 < @ $2 > strip trailing dots
1730 dnl workspace: <?> localpart<@domain>
1731 R<?> $- < @ $* > $: <?> $(dequote $1 $) < @ $2 > dequote local part
1733 # if no $=O character, no host in the user portion, we are done
1734 R<?> $* $=O $* < @ $* > $: <NO> $1 $2 $3 < @ $4>
1735 dnl no $=O in localpart: return
1738 dnl workspace: <NO> localpart<@domain>, where localpart contains $=O
1739 dnl mark everything which has an "authorized" domain with <RELAY>
1740 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
1741 # if we relay, check username portion for user%host so host can be checked also
1742 R<NO> $* < @ $* $=m > $: <RELAY> $1 < @ $2 $3 >', `dnl')
1743 dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
1744 dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
1746 dnl what if access map returns something else than RELAY?
1747 dnl we are only interested in RELAY entries...
1748 dnl other To: entries: blocklist recipient; generic entries?
1749 dnl if it is an error we probably do not want to relay anyway
1750 ifdef(`_RELAY_HOSTS_ONLY_',
1751 `R<NO> $* < @ $=R > $: <RELAY> $1 < @ $2 >
1752 ifdef(`_ACCESS_TABLE_', `dnl
1753 R<NO> $* < @ $+ > $: <$(access To:$2 $: NO $)> $1 < @ $2 >
1754 R<NO> $* < @ $+ > $: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
1755 `R<NO> $* < @ $* $=R > $: <RELAY> $1 < @ $2 $3 >
1756 ifdef(`_ACCESS_TABLE_', `dnl
1757 R<NO> $* < @ $+ > $: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
1758 R<$+> <$+> $: <$1> $2',`dnl')')
1761 ifdef(`_RELAY_MX_SERVED_', `dnl
1762 dnl do "we" ($=w) act as backup MX server for the destination domain?
1763 R<NO> $* < @ $+ > $: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
1764 R<MX> < : $* <TEMP> : > $* $#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
1765 dnl yes: mark it as <RELAY>
1766 R<MX> < $* : $=w. : $* > < $+ > $: <RELAY> $4
1767 dnl no: put old <NO> mark back
1768 R<MX> < : $* : > < $+ > $: <NO> $2', `dnl')
1770 dnl do we relay to this recipient domain?
1771 R<RELAY> $* < @ $* > $@ $>ParseRecipient $1
1776 ######################################################################
1777 ### check_relay -- check hostname/address on SMTP startup
1778 ######################################################################
1780 ifdef(`_CONTROL_IMMEDIATE_',`dnl
1782 ifdef(`_RATE_CONTROL_IMMEDIATE_',`dnl
1783 dnl workspace: ignored...
1784 R$* $: $>"RateControl" dummy', `dnl')
1785 ifdef(`_CONN_CONTROL_IMMEDIATE_',`dnl
1786 dnl workspace: ignored...
1787 R$* $: $>"ConnControl" dummy', `dnl')
1792 ifdef(`_USE_CLIENT_PTR_',`dnl
1793 R$* $| $* $: $&{client_ptr} $| $2', `dnl')
1794 R$* $: $1 $| $>"Local_check_relay" $1
1795 R$* $| $* $| $#$* $#$3
1796 R$* $| $* $| $* $@ $>"Basic_check_relay" $1 $| $2
1799 # check for deferred delivery mode
1800 R$* $: < $&{deliveryMode} > $1
1801 R< d > $* $@ deferred
1804 ifdef(`_ACCESS_TABLE_', `dnl
1805 dnl workspace: {client_name} $| {client_addr}
1806 R$+ $| $+ $: $>D < $1 > <?> <+ Connect> < $2 >
1807 dnl workspace: <result-of-lookup> <{client_addr}>
1808 dnl OR $| $+ if client_name is empty
1809 R $| $+ $: $>A < $1 > <?> <+ Connect> <> empty client_name
1810 dnl workspace: <result-of-lookup> <{client_addr}>
1811 R<?> <$+> $: $>A < $1 > <?> <+ Connect> <> no: another lookup
1812 dnl workspace: <result-of-lookup> (<>|<{client_addr}>)
1813 R<?> <$*> $: OK found nothing
1814 dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
1815 R<$={Accept}> <$*> $@ $1 return value of lookup
1816 R<REJECT> <$*> $#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
1817 R<DISCARD> <$*> $#discard $: discard
1818 R<QUARANTINE:$+> <$*> $#error $@ quarantine $: $1
1820 R<ERROR:$-.$-.$-:$+> <$*> $#error $@ $1.$2.$3 $: $4
1821 R<ERROR:$+> <$*> $#error $: $1
1822 ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*> $#error $@ 4.3.0 $: _TMPFMSG_(`CR')', `dnl')
1823 dnl generic error from access map
1824 R<$+> <$*> $#error $: $1', `dnl')
1827 # DNS based IP address spam list
1828 dnl workspace: ignored...
1829 R$* $: $&{client_addr}
1830 R$-.$-.$-.$- $: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
1832 R<?>$+ $#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
1834 ifdef(`_RATE_CONTROL_',`dnl
1835 ifdef(`_RATE_CONTROL_IMMEDIATE_',`', `dnl
1836 dnl workspace: ignored...
1837 R$* $: $>"RateControl" dummy')', `dnl')
1838 ifdef(`_CONN_CONTROL_',`dnl
1839 ifdef(`_CONN_CONTROL_IMMEDIATE_',`',`dnl
1840 dnl workspace: ignored...
1841 R$* $: $>"ConnControl" dummy')', `dnl')
1842 undivert(8)dnl LOCAL_DNSBL
1843 ifdef(`_REQUIRE_RDNS_', `dnl
1844 R$* $: $&{client_addr} $| $&{client_resolve}
1845 R$=R $* $@ RELAY We relay for these
1846 R$* $| OK $@ OK Resolves.
1847 R$* $| FAIL $#error $@ 5.7.1 $: 550 Fix reverse DNS for $1
1848 R$* $| TEMP $#error $@ 4.1.8 $: 451 Client IP address $1 does not resolve
1849 R$* $| FORGED $#error $@ 4.1.8 $: 451 Possibly forged hostname for $1
1852 ######################################################################
1853 ### check_mail -- check SMTP ``MAIL FROM:'' command argument
1854 ######################################################################
1858 R$* $: $1 $| $>"Local_check_mail" $1
1860 R$* $| $* $@ $>"Basic_check_mail" $1
1863 # check for deferred delivery mode
1864 R$* $: < $&{deliveryMode} > $1
1865 R< d > $* $@ deferred
1869 dnl done first: we can require authentication for every mail transaction
1870 dnl workspace: address as given by MAIL FROM: (sender)
1871 R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL
1873 dnl undo damage: remove result of tls_client call
1876 dnl workspace: address as given by MAIL FROM:
1877 R<> $@ <OK> we MUST accept <> (RFC 1123)
1878 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1879 dnl do some additional checks
1881 dnl no user@localhost (if nonlocal sender)
1882 dnl this is a pretty simple canonification, it will not catch every case
1883 dnl just make sure the address has <> around it (which is required by
1884 dnl the RFC anyway, maybe we should complain if they are missing...)
1885 dnl dirty trick: if it is user@host, just add a dot: user@host. this will
1886 dnl not be modified by host lookups.
1888 R<?><$+> $: <@> <$1>
1890 dnl workspace: <@> <address>
1891 dnl prepend daemon_flags
1892 R$* $: $&{daemon_flags} $| $1
1893 dnl workspace: ${daemon_flags} $| <@> <address>
1894 dnl do not allow these at all or only from local systems?
1895 R$* f $* $| <@> < $* @ $- > $: < ? $&{client_name} > < $3 @ $4 >
1896 dnl accept unqualified sender: change mark to avoid test
1897 R$* u $* $| <@> < $* > $: <?> < $3 >
1898 dnl workspace: ${daemon_flags} $| <@> <address>
1899 dnl or: <? ${client_name} > <address>
1900 dnl or: <?> <address>
1901 dnl remove daemon_flags
1903 # handle case of @localhost on address
1904 R<@> < $* @ localhost > $: < ? $&{client_name} > < $1 @ localhost >
1905 R<@> < $* @ [127.0.0.1] >
1906 $: < ? $&{client_name} > < $1 @ [127.0.0.1] >
1907 R<@> < $* @ [IPv6:0:0:0:0:0:0:0:1] >
1908 $: < ? $&{client_name} > < $1 @ [IPv6:0:0:0:0:0:0:0:1] >
1909 R<@> < $* @ [IPv6:::1] >
1910 $: < ? $&{client_name} > < $1 @ [IPv6:::1] >
1911 R<@> < $* @ localhost.$m >
1912 $: < ? $&{client_name} > < $1 @ localhost.$m >
1913 ifdef(`_NO_UUCP_', `dnl',
1914 `R<@> < $* @ localhost.UUCP >
1915 $: < ? $&{client_name} > < $1 @ localhost.UUCP >')
1916 dnl workspace: < ? $&{client_name} > <user@localhost|host>
1917 dnl or: <@> <address>
1918 dnl or: <?> <address> (thanks to u in ${daemon_flags})
1919 R<@> $* $: $1 no localhost as domain
1920 dnl workspace: < ? $&{client_name} > <user@localhost|host>
1922 dnl or: <?> <address> (thanks to u in ${daemon_flags})
1923 R<? $=w> $* $: $2 local client: ok
1924 R<? $+> <$+> $#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
1925 dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
1927 dnl workspace: address (or <address>)
1928 R$* $: <?> $>CanonAddr $1 canonify sender address and mark it
1929 dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
1930 dnl there is nothing behind the <@host> so no trailing $* needed
1931 R<?> $* < @ $+ . > <?> $1 < @ $2 > strip trailing dots
1932 # handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
1933 R<?> $* < @ $* $=P > $: <_RES_OK_> $1 < @ $2 $3 >
1934 dnl workspace <mark> CanonicalAddress where mark is ? or OK
1935 dnl A sender address with my local host name ($j) is safe
1936 R<?> $* < @ $j > $: <_RES_OK_> $1 < @ $j >
1937 ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
1938 `R<?> $* < @ $+ > $: <_RES_OK_> $1 < @ $2 > ... unresolvable OK',
1939 `R<?> $* < @ $+ > $: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
1940 R<? $* <$->> $* < @ $+ >
1941 $: <$2> $3 < @ $4 >')
1942 dnl workspace <mark> CanonicalAddress where mark is ?, _RES_OK_, PERM, TEMP
1943 dnl mark is ? iff the address is user (wo @domain)
1945 ifdef(`_ACCESS_TABLE_', `dnl
1946 # check sender address: user@address, user@, address
1947 dnl should we remove +ext from user?
1948 dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
1949 R<$+> $+ < @ $* > $: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
1950 R<$+> $+ $: @<$1> <$2> $| <U:$2@>
1951 dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
1952 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
1953 dnl will only return user<@domain when "reversing" the args
1954 R@ <$+> <$*> $| <$+> $: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
1955 dnl workspace: <@><mark> <CanonicalAddress> $| <result>
1956 R<@> <$+> <$*> $| <$*> $: <$3> <$1> <$2> reverse result
1957 dnl workspace: <result> <mark> <CanonicalAddress>
1958 # retransform for further use
1960 dnl <ResultOfLookup|mark> CanonicalAddress
1961 R<?> <$+> <$*> $: <$1> $2 no match
1962 R<$+> <$+> <$*> $: <$1> $3 relevant result, keep it', `dnl')
1963 dnl workspace <ResultOfLookup|mark> CanonicalAddress
1964 dnl mark is ? iff the address is user (wo @domain)
1966 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
1967 # handle case of no @domain on address
1968 dnl prepend daemon_flags
1969 R<?> $* $: $&{daemon_flags} $| <?> $1
1970 dnl accept unqualified sender: change mark to avoid test
1971 R$* u $* $| <?> $* $: <_RES_OK_> $3
1972 dnl remove daemon_flags
1974 R<?> $* $: < ? $&{client_addr} > $1
1975 R<?> $* $@ <_RES_OK_> ...local unqualed ok
1976 R<? $+> $* $#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
1979 R<?> $* $: @ $1 mark address: nothing known about it
1980 R<$={ResOk}> $* $: @ $2 domain ok
1981 R<TEMP> $* $#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
1982 R<PERM> $* $#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
1983 ifdef(`_ACCESS_TABLE_', `dnl
1984 R<$={Accept}> $* $# $1 accept from access map
1985 R<DISCARD> $* $#discard $: discard
1986 R<QUARANTINE:$+> $* $#error $@ quarantine $: $1
1987 R<REJECT> $* $#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
1989 R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4
1990 R<ERROR:$+> $* $#error $: $1
1991 ifdef(`_ATMPF_', `R<_ATMPF_> $* $#error $@ 4.3.0 $: _TMPFMSG_(`CM')', `dnl')
1992 dnl generic error from access map
1993 R<$+> $* $#error $: $1 error from access db',
1995 dnl workspace: @ CanonicalAddress (i.e. address in canonical form localpart<@host>)
1997 ifdef(`_BADMX_CHK_', `dnl
1998 R@ $*<@$+>$* $: $1<@$2>$3 $| $>BadMX $2
2002 # Look up MX records and ferret away a copy of the original address.
2003 # input: domain part of address to check
2004 R$+ $:<MX><$1><:$(mxlist $1$):><:>
2005 # workspace: <MX><domain><: mxlist-result $><:>
2006 R<MX><$+><:$*<TEMP>:><$*> $#error $@ 4.1.2 $: "450 MX lookup failure for "$1
2007 # workspace: <MX> <original destination> <unchecked mxlist> <checked mxlist>
2008 # Recursively run badmx check on each mx.
2009 R<MX><$*><:$+:$*><:$*> <MX><$1><:$3><: $4 $(badmx $2 $):>
2010 # See if any of them fail.
2011 R<MX><$*><$*><$*<BADMX>:$*> $#error $@ 5.1.2 $:"550 Illegal MX record for host "$1
2012 # Reverse the mxlists so we can use the same argument order again.
2013 R<MX><$*><$*><$*> $:<MX><$1><$3><$2>
2014 R<MX><$*><:$+:$*><:$*> <MX><$1><:$3><:$4 $(dnsA $2 $) :>
2016 # Reverse the lists so we can use the same argument order again.
2017 R<MX><$*><$*><$*> $:<MX><$1><$3><$2>
2018 R<MX><$*><:$+:$*><:$*> <MX><$1><:$3><:$4 $(BadMXIP $2 $) :>
2020 R<MX><$*><$*><$*<BADMXIP>:$*> $#error $@ 5.1.2 $:"550 Invalid MX record for host "$1',
2024 ######################################################################
2025 ### check_rcpt -- check SMTP ``RCPT TO:'' command argument
2026 ######################################################################
2030 R$* $: $1 $| $>"Local_check_rcpt" $1
2032 R$* $| $* $@ $>"Basic_check_rcpt" $1
2036 R<> $#error $@ nouser $: "553 User address required"
2037 R$@ $#error $@ nouser $: "553 User address required"
2038 # check for deferred delivery mode
2039 R$* $: < $&{deliveryMode} > $1
2040 R< d > $* $@ deferred
2043 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2044 dnl this code checks for user@host where host is not a FQHN.
2045 dnl it is not activated.
2046 dnl notice: code to check for a recipient without a domain name is
2047 dnl available down below; look for the same macro.
2048 dnl this check is done here because the name might be qualified by the
2049 dnl canonicalization.
2050 # require fully qualified domain part?
2051 dnl very simple canonification: make sure the address is in < >
2053 R<?> <$+> $: <@> <$1>
2055 R<@> < postmaster > $: postmaster
2056 R<@> < $* @ $+ . $+ > $: < $1 @ $2 . $3 >
2057 dnl prepend daemon_flags
2058 R<@> $* $: $&{daemon_flags} $| <@> $1
2059 dnl workspace: ${daemon_flags} $| <@> <address>
2060 dnl _r_equire qual.rcpt: ok
2061 R$* r $* $| <@> < $+ @ $+ > $: < $3 @ $4 >
2062 dnl do not allow these at all or only from local systems?
2063 R$* r $* $| <@> < $* > $: < ? $&{client_name} > < $3 >
2065 R<? $=w> < $* > $: <$1>
2066 R<? $+> <$+> $#error $@ 5.5.4 $: "553 Fully qualified domain name required"
2067 dnl remove daemon_flags for other cases
2068 R$* $| <@> $* $: $2', `dnl')
2070 dnl ##################################################################
2071 dnl call subroutines for recipient and relay
2072 dnl possible returns from subroutines:
2073 dnl $#TEMP temporary failure
2074 dnl $#error permanent failure (or temporary if from access map)
2075 dnl $#other stop processing
2076 dnl RELAY RELAYing allowed
2078 ######################################################################
2079 R$* $: $1 $| @ $>"Rcpt_ok" $1
2080 dnl temporary failure? remove mark @ and remember
2081 R$* $| @ $#TEMP $+ $: $1 $| T $2
2082 dnl error or ok (stop)
2084 ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2085 R$* $| @ RELAY $@ RELAY
2086 dnl something else: call check sender (relay)
2087 R$* $| @ $* $: O $| $>"Relay_ok" $1
2088 dnl temporary failure: call check sender (relay)
2089 R$* $| T $+ $: T $2 $| $>"Relay_ok" $1
2090 dnl temporary failure? return that
2091 R$* $| $#TEMP $+ $#error $2
2092 dnl error or ok (stop)
2094 R$* $| RELAY $@ RELAY
2095 dnl something else: return previous temp failure
2096 R T $+ $| $* $#error $1
2097 # anything else is bogus
2098 R$* $#error $@ 5.7.1 $: confRELAY_MSG
2101 ######################################################################
2102 ### Rcpt_ok: is the recipient ok?
2103 dnl input: recipient address (RCPT TO)
2104 dnl output: see explanation at call
2105 ######################################################################
2107 ifdef(`_LOOSE_RELAY_CHECK_',`dnl
2108 R$* $: $>CanonAddr $1
2109 R$* < @ $* . > $1 < @ $2 > strip trailing dots',
2110 `R$* $: $>ParseRecipient $1 strip relayable hosts')
2112 ifdef(`_BESTMX_IS_LOCAL_',`dnl
2113 ifelse(_BESTMX_IS_LOCAL_, `', `dnl
2115 R$* < @ $* > $* $: $1 < @ $2 @@ $(bestmx $2 $) > $3',
2117 # limit bestmx to $=B
2118 R$* < @ $* $=B > $* $: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
2119 R$* $=O $* < @ $* @@ $=w . > $* $@ $>"Rcpt_ok" $1 $2 $3
2120 R$* < @ $* @@ $=w . > $* $: $1 < @ $3 > $4
2121 R$* < @ $* @@ $* > $* $: $1 < @ $2 > $4')
2123 ifdef(`_BLOCKLIST_RCPT_',`dnl
2124 ifdef(`_ACCESS_TABLE_', `dnl
2125 # blocklist local users or any host from receiving mail
2127 dnl user is now tagged with @ to be consistent with check_mail
2128 dnl and to distinguish users from hosts (com would be host, com@ would be user)
2129 R<?> $+ < @ $=w > $: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
2130 R<?> $+ < @ $* > $: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
2131 R<?> $+ $: <> <$1> $| <U:$1@>
2132 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
2133 dnl will only return user<@domain when "reversing" the args
2134 R<> <$*> $| <$+> $: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
2135 R<@> <$*> $| <$*> $: <$2> <$1> reverse result
2136 R<?> <$*> $: @ $1 mark address as no match
2137 dnl we may have to filter here because otherwise some RHSs
2138 dnl would be interpreted as generic error messages...
2139 dnl error messages should be "tagged" by prefixing them with error: !
2140 dnl that would make a lot of things easier.
2141 R<$={Accept}> <$*> $: @ $2 mark address as no match
2142 ifdef(`_ACCESS_SKIP_', `dnl
2143 R<SKIP> <$*> $: @ $1 mark address as no match', `dnl')
2144 ifdef(`_DELAY_COMPAT_8_10_',`dnl
2145 dnl compatility with 8.11/8.10:
2146 dnl we have to filter these because otherwise they would be interpreted
2147 dnl as generic error message...
2148 dnl error messages should be "tagged" by prefixing them with error: !
2149 dnl that would make a lot of things easier.
2150 dnl maybe we should stop checks already here (if SPAM_xyx)?
2151 R<$={SpamTag}> <$*> $: @ $2 mark address as no match')
2152 R<REJECT> $* $#error $@ 5.2.1 $: confRCPTREJ_MSG
2153 R<DISCARD> $* $#discard $: discard
2154 R<QUARANTINE:$+> $* $#error $@ quarantine $: $1
2156 R<ERROR:$-.$-.$-:$+> $* $#error $@ $1.$2.$3 $: $4
2157 R<ERROR:$+> $* $#error $: $1
2158 ifdef(`_ATMPF_', `R<_ATMPF_> $* $#error $@ 4.3.0 $: _TMPFMSG_(`ROK1')', `dnl')
2159 dnl generic error from access map
2160 R<$+> $* $#error $: $1 error from access db
2161 R@ $* $1 remove mark', `dnl')', `dnl')
2163 ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
2164 # authenticated via TLS?
2165 R$* $: $1 $| $>RelayTLS client authenticated?
2166 R$* $| $# $+ $# $2 error/ok?
2169 R$* $: $1 $| $>"Local_Relay_Auth" $&{auth_type}
2170 dnl workspace: localpart<@domain> $| result of Local_Relay_Auth
2172 dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
2174 R$* $| $* $: $1 $| $&{auth_type}
2175 dnl workspace: localpart<@domain> [ $| ${auth_type} ]
2176 dnl empty ${auth_type}?
2178 dnl mechanism ${auth_type} accepted?
2179 dnl use $# to override further tests (delay_checks): see check_rcpt below
2180 R$* $| $={TrustAuthMech} $# RELAY
2181 dnl remove ${auth_type}
2183 dnl workspace: localpart<@domain> | localpart
2184 ifelse(defn(`_NO_UUCP_'), `r',
2185 `R$* ! $* < @ $* > $: <REMOTE> $2 < @ BANG_PATH >
2186 R$* ! $* $: <REMOTE> $2 < @ BANG_PATH >', `dnl')
2187 ifelse(defn(`_NO_PERCENTHACK_'), `r',
2188 `R$* % $* < @ $* > $: <REMOTE> $1 < @ PERCENT_HACK >
2189 R$* % $* $: <REMOTE> $1 < @ PERCENT_HACK >', `dnl')
2190 # anything terminating locally is ok
2191 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2192 R$+ < @ $* $=m > $@ RELAY', `dnl')
2193 R$+ < @ $=w > $@ RELAY
2194 ifdef(`_RELAY_HOSTS_ONLY_',
2195 `R$+ < @ $=R > $@ RELAY
2196 ifdef(`_ACCESS_TABLE_', `dnl
2197 ifdef(`_RELAY_FULL_ADDR_', `dnl
2198 R$+ < @ $+ > $: <$(access To:$1@$2 $: ? $)> <$1 < @ $2 >>
2199 R<?> <$+ < @ $+ >> $: <$(access To:$2 $: ? $)> <$1 < @ $2 >>',`
2200 R$+ < @ $+ > $: <$(access To:$2 $: ? $)> <$1 < @ $2 >>')
2201 dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2202 R<?> <$+ < @ $+ >> $: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
2203 `R$+ < @ $* $=R > $@ RELAY
2204 ifdef(`_ACCESS_TABLE_', `dnl
2205 ifdef(`_RELAY_FULL_ADDR_', `dnl
2206 R$+ < @ $+ > $: $1 < @ $2 > $| $>SearchList <+ To> $| <F:$1@$2> <D:$2> <F:$1@> <>
2207 R$+ < @ $+ > $| <$*> $: <$3> <$1 <@ $2>>
2208 R$+ < @ $+ > $| $* $: <$3> <$1 <@ $2>>',
2209 `R$+ < @ $+ > $: $>D <$2> <?> <+ To> <$1 < @ $2 >>')')')
2210 ifdef(`_ACCESS_TABLE_', `dnl
2211 dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
2212 R<RELAY> $* $@ RELAY
2213 ifdef(`_ATMPF_', `R<$* _ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`ROK2')', `dnl')
2214 R<$*> <$*> $: $2',`dnl')
2217 ifdef(`_RELAY_MX_SERVED_', `dnl
2218 # allow relaying for hosts which we MX serve
2219 R$+ < @ $+ > $: < : $(mxserved $2 $) : > $1 < @ $2 >
2220 dnl this must not necessarily happen if the client is checked first...
2221 R< : $* <TEMP> : > $* $#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
2222 R<$* : $=w . : $*> $* $@ RELAY
2223 R< : $* : > $* $: $2',
2226 # check for local user (i.e. unqualified address)
2228 R<?> $* < @ $+ > $: <REMOTE> $1 < @ $2 >
2230 dnl is it really? the standard requires user@domain, not just user
2231 dnl but we should accept it anyway (maybe making it an option:
2233 dnl postmaster must be accepted without domain (DRUMS)
2234 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
2235 R<?> postmaster $@ OK
2236 # require qualified recipient?
2237 dnl prepend daemon_flags
2238 R<?> $+ $: $&{daemon_flags} $| <?> $1
2239 dnl workspace: ${daemon_flags} $| <?> localpart
2240 dnl do not allow these at all or only from local systems?
2241 dnl r flag? add client_name
2242 R$* r $* $| <?> $+ $: < ? $&{client_name} > <?> $3
2243 dnl no r flag: relay to local user (only local part)
2244 # no qualified recipient required
2245 R$* $| <?> $+ $@ RELAY
2246 dnl client_name is empty
2247 R<?> <?> $+ $@ RELAY
2248 dnl client_name is local
2249 R<? $=w> <?> $+ $@ RELAY
2250 dnl client_name is not local
2251 R<? $+> $+ $#error $@ 5.5.4 $: "553 Domain name required"', `dnl
2252 dnl no qualified recipient required
2254 dnl it is a remote user: remove mark and then check client
2256 dnl currently the recipient address is not used below
2258 ######################################################################
2259 ### Relay_ok: is the relay/sender ok?
2261 dnl output: see explanation at call
2262 ######################################################################
2264 # anything originating locally is ok
2266 R$* $: $&{client_addr}
2267 R$@ $@ RELAY originated locally
2268 R0 $@ RELAY originated locally
2269 R127.0.0.1 $@ RELAY originated locally
2270 RIPv6:0:0:0:0:0:0:0:1 $@ RELAY originated locally
2271 dnl if compiled with IPV6_FULL=0
2272 RIPv6:::1 $@ RELAY originated locally
2273 R$=R $* $@ RELAY relayable IP address
2274 ifdef(`_ACCESS_TABLE_', `dnl
2275 R$* $: $>A <$1> <?> <+ Connect> <$1>
2276 R<RELAY> $* $@ RELAY relayable IP address
2277 ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
2278 dnl this will cause rejections in cases like:
2279 dnl Connect:My.Host.Domain RELAY
2280 dnl Connect:My.Net REJECT
2281 dnl since in check_relay client_name is checked before client_addr
2282 R<REJECT> $* $@ REJECT rejected IP address')
2283 ifdef(`_ATMPF_', `R<_ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK1')', `dnl')
2284 R<$*> <$*> $: $2', `dnl')
2285 R$* $: [ $1 ] put brackets around it...
2286 R$=w $@ RELAY ... and see if it is local
2288 ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2289 ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
2290 ifdef(`_RELAY_MAIL_FROM_', `dnl
2291 dnl input: {client_addr} or something "broken"
2292 dnl just throw the input away; we do not need it.
2293 # check whether FROM is allowed to use system as relay
2294 R$* $: <?> $>CanonAddr $&f
2295 R<?> $+ < @ $+ . > <?> $1 < @ $2 > remove trailing dot
2296 ifdef(`_RELAY_LOCAL_FROM_', `dnl
2297 # check whether local FROM is ok
2298 R<?> $+ < @ $=w > $@ RELAY FROM local', `dnl')
2299 ifdef(`_RELAY_DB_FROM_', `dnl
2300 R<?> $+ < @ $+ > $: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
2301 R<@> <RELAY> $@ RELAY RELAY FROM sender ok
2302 ifdef(`_ATMPF_', `R<@> <_ATMPF_> $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK2')', `dnl')
2304 ifdef(`_RELAY_DB_FROM_DOMAIN_',
2305 `errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
2309 dnl notice: the rulesets above do not leave a unique workspace behind.
2310 dnl it does not matter in this case because the following rule ignores
2311 dnl the input. otherwise these rules must "clean up" the workspace.
2313 # check client name: first: did it resolve?
2315 R$* $: < $&{client_resolve} >
2316 R<TEMP> $#TEMP $@ 4.4.0 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
2317 R<FORGED> $#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
2318 R<FAIL> $#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
2319 dnl ${client_resolve} should be OK, so go ahead
2320 R$* $: <@> $&{client_name}
2321 dnl should not be necessary since it has been done for client_addr already
2322 dnl this rule actually may cause a problem if {client_name} resolves to ""
2323 dnl however, this should not happen since the forward lookup should fail
2324 dnl and {client_resolve} should be TEMP or FAIL.
2325 dnl nevertheless, removing the rule doesn't hurt.
2327 dnl workspace: <@> ${client_name} (not empty)
2328 # pass to name server to make hostname canonical
2329 R<@> $* $=P $:<?> $1 $2
2330 R<@> $+ $:<?> $[ $1 $]
2331 dnl workspace: <?> ${client_name} (canonified)
2332 R$* . $1 strip trailing dots
2333 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
2334 R<?> $* $=m $@ RELAY', `dnl')
2336 ifdef(`_RELAY_HOSTS_ONLY_',
2338 ifdef(`_ACCESS_TABLE_', `dnl
2339 R<?> $* $: <$(access Connect:$1 $: ? $)> <$1>
2340 R<?> <$*> $: <$(access $1 $: ? $)> <$1>',`dnl')',
2341 `R<?> $* $=R $@ RELAY
2342 ifdef(`_ACCESS_TABLE_', `dnl
2343 R<?> $* $: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
2344 ifdef(`_ACCESS_TABLE_', `dnl
2345 R<RELAY> $* $@ RELAY
2346 ifdef(`_ATMPF_', `R<$* _ATMPF_> $* $#TEMP $@ 4.3.0 $: _TMPFMSG_(`YOK3')', `dnl')
2347 R<$*> <$*> $: $2',`dnl')
2348 dnl end of _PROMISCUOUS_RELAY_
2350 ifdef(`_DELAY_CHECKS_',`dnl
2351 # turn a canonical address in the form user<@domain>
2352 # qualify unqual. addresses with $j
2353 dnl it might have been only user (without <@domain>)
2355 R$* <@ $+ . > $1 <@ $2 >
2356 R$* <@ $* > $@ $1 <@ $2 >
2361 dnl code repeated here from Basic_check_mail
2362 dnl only called from check_rcpt in delay mode if checkrcpt returns $#
2363 R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL
2365 dnl return result from checkrcpt
2371 dnl code repeated here from Basic_check_mail
2372 dnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
2373 R$* $: $1 $| $>"tls_client" $&{verify} $| MAIL
2375 dnl return result from friend/hater check
2379 # call all necessary rulesets
2381 dnl this test should be in the Basic_check_rcpt ruleset
2382 dnl which is the correct DSN code?
2383 # R$@ $#error $@ 5.1.3 $: "553 Recipient address required"
2385 R$+ $: $1 $| $>checkrcpt $1
2386 dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
2387 dnl on error (or discard) stop now
2388 R$+ $| $#error $* $#error $2
2389 R$+ $| $#discard $* $#discard $2
2390 dnl otherwise call tls_client; see above
2391 R$+ $| $#$* $@ $>"Delay_TLS_Clt" $2
2392 R$+ $| $* $: <?> $>FullAddr $>CanonAddr $1
2394 `dnl lookup user@ and user@address
2395 ifdef(`_ACCESS_TABLE_', `',
2396 `errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
2398 dnl one of the next two rules is supposed to match
2399 dnl this code has been copied from BLOCKLIST... etc
2400 dnl and simplified by omitting some < >.
2401 R<?> $+ < @ $=w > $: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
2402 R<?> $+ < @ $* > $: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
2403 dnl R<?> $@ something_is_very_wrong_here
2404 # lookup the addresses only with Spam tag
2405 R<> $* $| <$+> $: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
2406 R<@> $* $| $* $: $2 $1 reverse result
2408 ifdef(`_SPAM_FRIEND_',
2409 `# is the recipient a spam friend?
2410 ifdef(`_SPAM_HATER_',
2411 `errprint(`*** ERROR: define either Hater or Friend -- not both.
2413 R<FRIEND> $+ $@ $>"Delay_TLS_Clt2" SPAMFRIEND
2416 ifdef(`_SPAM_HATER_',
2417 `# is the recipient no spam hater?
2418 R<HATER> $+ $: $1 spam hater: continue checks
2419 R<$*> $+ $@ $>"Delay_TLS_Clt2" NOSPAMHATER everyone else: stop
2422 dnl run further checks: check_mail
2423 dnl should we "clean up" $&f?
2424 ifdef(`_FFR_MAIL_MACRO',
2425 `R$* $: $1 $| $>checkmail $&{mail_from}',
2426 `R$* $: $1 $| $>checkmail <$&f>')
2427 dnl recipient (canonical format) $| result of checkmail
2429 dnl run further checks: check_relay
2430 R$* $| $* $: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
2435 ifdef(`_BLOCK_BAD_HELO_', `dnl
2436 R$* $: $1 $| <$&{auth_authen}> Get auth info
2437 dnl Bypass the test for users who have authenticated.
2438 R$* $| <$+> $: $1 skip if auth
2439 R$* $| <$*> $: $1 $| <$&{client_addr}> [$&s] Get connection info
2440 dnl Bypass for local clients -- IP address starts with $=R
2441 R$* $| <$=R $*> [$*] $: $1 skip if local client
2442 dnl Bypass a "sendmail -bs" session, which use 0 for client ip address
2443 R$* $| <0> [$*] $: $1 skip if sendmail -bs
2444 dnl Reject our IP - assumes "[ip]" is in class $=w
2445 R$* $| <$*> $=w $#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2446 dnl Reject our hostname
2447 R$* $| <$*> [$=w] $#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2448 dnl Pass anything else with a "." in the domain parameter
2449 R$* $| <$*> [$+.$+] $: $1 qualified domain ok
2450 dnl Pass IPv6: address literals
2451 R$* $| <$*> [IPv6:$+] $: $1 qualified domain ok
2452 dnl Reject if there was no "." or only an initial or final "."
2453 R$* $| <$*> [$*] $#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
2454 dnl Clean up the workspace
2458 ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
2459 ######################################################################
2460 ### F: LookUpFull -- search for an entry in access database
2462 ### lookup of full key (which should be an address) and
2463 ### variations if +detail exists: +* and without +detail
2467 ### <$2> -- default (what to return if not found in db)
2468 dnl must not be empty
2469 ### <$3> -- mark (must be <(!|+) single-token>)
2470 ### ! does lookup only with tag
2471 ### + does lookup with and without tag
2472 ### <$4> -- passthru (additional data passed unchanged through)
2473 dnl returns: <default> <passthru>
2474 dnl <result> <passthru>
2475 ######################################################################
2478 dnl workspace: <key> <def> <o tag> <thru>
2481 R<$+> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2482 dnl no match, try without tag
2484 R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2485 dnl no match, +detail: try +*
2487 R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2488 $: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2489 dnl no match, +detail: try +* without tag
2491 R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2492 $: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2493 dnl no match, +detail: try without +detail
2495 R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
2496 $: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
2497 dnl no match, +detail: try without +detail and without tag
2499 R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
2500 $: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
2501 dnl no match, return <default> <passthru>
2503 R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5>
2504 ifdef(`_ATMPF_', `dnl tempfail?
2506 R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl')
2507 dnl match, return <match> <passthru>
2509 R<$+> <$*> <$- $-> <$*> $@ <$1> <$5>
2511 ######################################################################
2512 ### E: LookUpExact -- search for an entry in access database
2516 ### <$2> -- default (what to return if not found in db)
2517 dnl must not be empty
2518 ### <$3> -- mark (must be <(!|+) single-token>)
2519 ### ! does lookup only with tag
2520 ### + does lookup with and without tag
2521 ### <$4> -- passthru (additional data passed unchanged through)
2522 dnl returns: <default> <passthru>
2523 dnl <result> <passthru>
2524 ######################################################################
2528 R<$*> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2529 dnl no match, try without tag
2531 R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2532 dnl no match, return default passthru
2534 R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5>
2535 ifdef(`_ATMPF_', `dnl tempfail?
2537 R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl')
2538 dnl match, return <match> <passthru>
2540 R<$+> <$*> <$- $-> <$*> $@ <$1> <$5>
2542 ######################################################################
2543 ### U: LookUpUser -- search for an entry in access database
2545 ### lookup of key (which should be a local part) and
2546 ### variations if +detail exists: +* and without +detail
2549 ### <$1> -- key (user@)
2550 ### <$2> -- default (what to return if not found in db)
2551 dnl must not be empty
2552 ### <$3> -- mark (must be <(!|+) single-token>)
2553 ### ! does lookup only with tag
2554 ### + does lookup with and without tag
2555 ### <$4> -- passthru (additional data passed unchanged through)
2556 dnl returns: <default> <passthru>
2557 dnl <result> <passthru>
2558 ######################################################################
2561 dnl user lookups are always with trailing @
2563 R<$+> <$*> <$- $-> <$*> $: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
2564 dnl no match, try without tag
2566 R<?> <$+> <$*> <+ $-> <$*> $: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
2567 dnl do not remove the @ from the lookup:
2568 dnl it is part of the +detail@ which is omitted for the lookup
2569 dnl no match, +detail: try +*
2571 R<?> <$+ + $* @> <$*> <$- $-> <$*>
2572 $: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2573 dnl no match, +detail: try +* without tag
2575 R<?> <$+ + $* @> <$*> <+ $-> <$*>
2576 $: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2577 dnl no match, +detail: try without +detail
2579 R<?> <$+ + $* @> <$*> <$- $-> <$*>
2580 $: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
2581 dnl no match, +detail: try without +detail and without tag
2583 R<?> <$+ + $* @> <$*> <+ $-> <$*>
2584 $: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
2585 dnl no match, return <default> <passthru>
2587 R<?> <$+> <$*> <$- $-> <$*> $@ <$2> <$5>
2588 ifdef(`_ATMPF_', `dnl tempfail?
2590 R<$+ _ATMPF_> <$*> <$- $-> <$*> $@ <_ATMPF_> <$5>', `dnl')
2591 dnl match, return <match> <passthru>
2593 R<$+> <$*> <$- $-> <$*> $@ <$1> <$5>
2595 ######################################################################
2596 ### SearchList: search a list of items in the access map
2598 ### <exact tag> $| <mark:address> <mark:address> ... <>
2599 dnl maybe we should have a @ (again) in front of the mark to
2600 dnl avoid errorneous matches (with error messages?)
2601 dnl if we can make sure that tag is always a single token
2602 dnl then we can omit the delimiter $|, otherwise we need it
2603 dnl to avoid errorneous matchs (first rule: D: if there
2604 dnl is that mark somewhere in the list, it will be taken).
2605 dnl moreover, we can do some tricks to enforce lookup with
2606 dnl the tag only, e.g.:
2607 ### where "exact" is either "+" or "!":
2608 ### <+ TAG> lookup with and w/o tag
2609 ### <! TAG> lookup with tag
2610 dnl Warning: + and ! should be in OperatorChars (otherwise there must be
2611 dnl a blank between them and the tag.
2612 ### possible values for "mark" are:
2613 ### D: recursive host lookup (LookUpDomain)
2614 dnl A: recursive address lookup (LookUpAddress) [not yet required]
2615 ### E: exact lookup, no modifications
2616 ### F: full lookup, try user+ext@domain and user@domain
2617 ### U: user lookup, try user+ext and user (input must have trailing @)
2618 ### return: <RHS of lookup> or <?> (not found)
2619 ######################################################################
2621 # class with valid marks for SearchList
2622 dnl if A is activated: add it
2623 C{Src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
2625 # just call the ruleset with the name of the tag... nice trick...
2627 R<$+> $| <$={Src}:$*> <$*> $: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
2628 dnl workspace: <o tag> $| <rest> $| <result of lookup> <>
2629 dnl no match and nothing left: return
2630 R<$+> $| <> $| <?> <> $@ <?>
2631 dnl no match but something left: continue
2632 R<$+> $| <$+> $| <?> <> $@ $>SearchList <$1> $| <$2>
2634 R<$+> $| <$*> $| <$+> <> $@ <$3>
2635 dnl return result from recursive invocation
2636 R<$+> $| <$+> $@ <$2>
2637 dnl endif _ACCESS_TABLE_
2640 ######################################################################
2641 ### trust_auth: is user trusted to authenticate as someone else?
2644 ### $1: AUTH= parameter from MAIL command
2645 ######################################################################
2647 dnl empty ruleset definition so it can be called
2650 R$* $: $&{auth_type} $| $1
2651 # required by RFC 2554 section 4.
2652 R$@ $| $* $#error $@ 5.7.1 $: "550 not authenticated"
2653 dnl seems to be useful...
2654 R$* $| $&{auth_authen} $@ identical
2655 R$* $| <$&{auth_authen}> $@ identical
2656 dnl call user supplied code
2657 R$* $| $* $: $1 $| $>"Local_trust_auth" $2
2660 R$* $#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
2662 ######################################################################
2663 ### Relay_Auth: allow relaying based on authentication?
2666 ### $1: ${auth_type}
2667 ######################################################################
2670 ######################################################################
2671 ### srv_features: which features to offer to a client?
2672 ### (done in server)
2673 ######################################################################
2675 ifdef(`_LOCAL_SRV_FEATURES_', `dnl
2676 R$* $: $1 $| $>"Local_srv_features" $1
2678 R$* $| $* $: $1', `dnl')
2679 ifdef(`_ACCESS_TABLE_', `dnl
2680 R$* $: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
2681 R<?>$* $: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
2682 R<?>$* $: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
2684 ifdef(`_ATMPF_', `dnl tempfail?
2685 R<$* _ATMPF_>$* $#temp', `dnl')
2688 ######################################################################
2689 ### try_tls: try to use STARTTLS?
2690 ### (done in client)
2691 ######################################################################
2693 ifdef(`_LOCAL_TRY_TLS_', `dnl
2694 R$* $: $1 $| $>"Local_try_tls" $1
2696 R$* $| $* $: $1', `dnl')
2697 ifdef(`_ACCESS_TABLE_', `dnl
2698 R$* $: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
2699 R<?>$* $: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
2700 R<?>$* $: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
2702 ifdef(`_ATMPF_', `dnl tempfail?
2703 R<$* _ATMPF_>$* $#error $@ 4.3.0 $: _TMPFMSG_(`TT')', `dnl')
2704 R<NO>$* $#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"')
2706 ######################################################################
2707 ### tls_rcpt: is connection with server "good" enough?
2708 ### (done in client, per recipient)
2709 dnl called from deliver() before RCPT command
2713 ######################################################################
2715 ifdef(`_LOCAL_TLS_RCPT_', `dnl
2716 R$* $: $1 $| $>"Local_tls_rcpt" $1
2718 R$* $| $* $: $1', `dnl')
2719 ifdef(`_ACCESS_TABLE_', `dnl
2720 dnl store name of other side
2721 R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1
2722 dnl canonify recipient address
2723 R$+ $: <?> $>CanonAddr $1
2724 dnl strip trailing dots
2725 R<?> $+ < @ $+ . > <?> $1 <@ $2 >
2727 R<?> $+ < @ $+ > $: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
2729 R<?> $+ $: $1 $| <U:$1@> <E:>
2731 dnl also look up a default value via E:
2732 R$* $| $+ $: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
2733 dnl found nothing: stop here
2735 ifdef(`_ATMPF_', `dnl tempfail?
2736 R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`TR')', `dnl')
2737 dnl use the generic routine (for now)
2738 R$* $| <$+> $@ $>"TLS_connection" $&{verify} $| <$2>')
2740 ######################################################################
2741 ### tls_client: is connection with client "good" enough?
2742 ### (done in server)
2745 ### ${verify} $| (MAIL|STARTTLS)
2746 ######################################################################
2747 dnl MAIL: called from check_mail
2748 dnl STARTTLS: called from smtp() after STARTTLS has been accepted
2750 ifdef(`_LOCAL_TLS_CLIENT_', `dnl
2751 R$* $: $1 <?> $>"Local_tls_client" $1
2753 R$* <?> $* $: $1', `dnl')
2754 ifdef(`_ACCESS_TABLE_', `dnl
2755 dnl store name of other side
2756 R$* $: $(macro {TLS_Name} $@ $&{client_name} $) $1
2757 dnl ignore second arg for now
2758 dnl maybe use it to distinguish permanent/temporary error?
2759 dnl if MAIL: permanent (STARTTLS has not been offered)
2760 dnl if STARTTLS: temporary (offered but maybe failed)
2761 R$* $| $* $: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
2762 R$* $| <?>$* $: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
2763 dnl do a default lookup: just TLS_CLT_TAG
2764 R$* $| <?>$* $: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
2765 ifdef(`_ATMPF_', `dnl tempfail?
2766 R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`TC')', `dnl')
2767 R$* $@ $>"TLS_connection" $1', `dnl
2768 R$* $| $* $@ $>"TLS_connection" $1')
2770 ######################################################################
2771 ### tls_server: is connection with server "good" enough?
2772 ### (done in client)
2776 ######################################################################
2777 dnl i.e. has the server been authenticated and is encryption active?
2778 dnl called from deliver() after STARTTLS command
2780 ifdef(`_LOCAL_TLS_SERVER_', `dnl
2781 R$* $: $1 $| $>"Local_tls_server" $1
2783 R$* $| $* $: $1', `dnl')
2784 ifdef(`_TLS_FAILURES_',`dnl
2785 R$* $: $(macro {saved_verify} $@ $1 $) $1')
2786 ifdef(`_ACCESS_TABLE_', `dnl
2787 dnl store name of other side
2788 R$* $: $(macro {TLS_Name} $@ $&{server_name} $) $1
2789 R$* $: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
2790 R$* $| <?>$* $: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
2791 dnl do a default lookup: just TLS_SRV_TAG
2792 R$* $| <?>$* $: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
2793 ifdef(`_ATMPF_', `dnl tempfail?
2794 R$* $| <$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`TS')', `dnl')
2795 R$* $@ $>"TLS_connection" $1', `dnl
2796 R$* $@ $>"TLS_connection" $1')
2798 ######################################################################
2799 ### TLS_connection: is TLS connection "good" enough?
2802 ifdef(`_ACCESS_TABLE_', `dnl
2803 ### ${verify} $| <Requirement> [<>]', `dnl
2805 ### Requirement: RHS from access map, may be ? for none.
2806 dnl syntax for Requirement:
2807 dnl [(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
2808 dnl extensions: could be a list of further requirements
2809 dnl for now: CN:string {cn_subject} == string
2810 ######################################################################
2812 ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
2813 dnl deal with TLS handshake failures: abort
2814 RSOFTWARE $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
2815 RDANE_FAIL $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') DANE check failed."
2817 dnl common ruleset for tls_{client|server}
2818 dnl input: ${verify} $| <ResultOfLookup> [<>]
2819 dnl remove optional <>
2820 R$* $| <$*>$* $: $1 $| <$2>
2821 dnl workspace: ${verify} $| <ResultOfLookup>
2822 # create the appropriate error codes
2823 dnl permanent or temporary error?
2824 R$* $| <PERM + $={Tls} $*> $: $1 $| <503:5.7.0> <$2 $3>
2825 R$* $| <TEMP + $={Tls} $*> $: $1 $| <403:4.7.0> <$2 $3>
2826 dnl default case depends on TLS_PERM_ERR
2827 R$* $| <$={Tls} $*> $: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
2828 dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
2829 # deal with TLS handshake failures: abort
2830 RSOFTWARE $| <$-:$+> $* $#error $@ $2 $: $1 " TLS handshake failed."
2831 dnl no <reply:dns> i.e. no requirements in the access map
2832 dnl use default error
2833 RSOFTWARE $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
2834 # deal with TLS protocol errors: abort
2835 RPROTOCOL $| <$-:$+> $* $#error $@ $2 $: $1 " STARTTLS failed."
2836 dnl no <reply:dns> i.e. no requirements in the access map
2837 dnl use default error
2838 RPROTOCOL $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') STARTTLS failed."
2839 # deal with DANE errors: abort
2840 RDANE_FAIL $| <$-:$+> $* $#error $@ $2 $: $1 " DANE check failed."
2841 dnl no <reply:dns> i.e. no requirements in the access map
2842 dnl use default error
2843 RDANE_FAIL $| $* $#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') DANE check failed."
2844 R$* $| <$*> <VERIFY> $: <$2> <VERIFY> <> $1
2845 dnl separate optional requirements
2846 R$* $| <$*> <VERIFY + $+> $: <$2> <VERIFY> <$3> $1
2847 R$* $| <$*> <$={Tls}:$->$* $: <$2> <$3:$4> <> $1
2848 dnl separate optional requirements
2849 R$* $| <$*> <$={Tls}:$- + $+>$* $: <$2> <$3:$4> <$5> $1
2850 dnl some other value in access map: accept
2851 dnl this also allows to override the default case (if used)
2853 # authentication required: give appropriate error
2854 # other side did authenticate (via STARTTLS)
2855 dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
2856 dnl only verification required and it succeeded
2857 R<$*><VERIFY> <> $={TlsVerified} $@ OK
2858 dnl verification required and it succeeded but extensions are given
2859 dnl change it to <SMTP:ESC> <REQ:0> <extensions>
2860 R<$*><VERIFY> <$+> $={TlsVerified} $: <$1> <REQ:0> <$2>
2861 dnl verification required + some level of encryption
2862 R<$*><VERIFY:$-> <$*> $={TlsVerified} $: <$1> <REQ:$2> <$3>
2863 dnl just some level of encryption required
2864 R<$*><ENCR:$-> <$*> $* $: <$1> <REQ:$2> <$3>
2866 dnl 1. <SMTP:ESC> <VERIFY [:bits]> <[extensions]> {verify} (!~ $={TlsVerified})
2867 dnl 2. <SMTP:ESC> <REQ:bits> <[extensions]>
2868 dnl verification required but ${verify} is not set (case 1.)
2869 R<$-:$+><VERIFY $*> <$*> $#error $@ $2 $: $1 " authentication required"
2870 R<$-:$+><VERIFY $*> <$*> FAIL $#error $@ $2 $: $1 " authentication failed"
2871 R<$-:$+><VERIFY $*> <$*> NO $#error $@ $2 $: $1 " not authenticated"
2872 R<$-:$+><VERIFY $*> <$*> NOT $#error $@ $2 $: $1 " no authentication requested"
2873 R<$-:$+><VERIFY $*> <$*> NONE $#error $@ $2 $: $1 " other side does not support STARTTLS"
2874 R<$-:$+><VERIFY $*> <$*> CLEAR $#error $@ $2 $: $1 " STARTTLS disabled locally"
2875 dnl some other value for ${verify}
2876 R<$-:$+><VERIFY $*> <$*> $+ $#error $@ $2 $: $1 " authentication failure " $4
2877 dnl some level of encryption required: get the maximum level (case 2.)
2878 R<$*><REQ:$-> <$*> $: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
2879 dnl compare required bits with actual bits
2880 R<$*><REQ:$-> <$*> $- $: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
2881 R<$-:$+><$-:$-> <$*> TRUE $#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
2882 dnl strength requirements fulfilled
2883 dnl TLS Additional Requirements Separator
2884 dnl this should be something which does not appear in the extensions itself
2885 dnl @ could be part of a CN, DN, etc...
2886 dnl use < > ? those are encoded in CN, DN, ...
2887 define(`_TLS_ARS_', `++')dnl
2889 dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
2890 R<$-:$+><$-:$-> <$*> $* $: <$1:$2 _TLS_ARS_ $5>
2891 dnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
2892 dnl continue: check extensions
2893 R<$-:$+ _TLS_ARS_ > $@ OK
2894 dnl split extensions into own list
2895 R<$-:$+ _TLS_ARS_ $+ > $: <$1:$2> <$3>
2896 R<$-:$+> < $+ _TLS_ARS_ $+ > <$1:$2> <$3> <$4>
2897 R<$-:$+> $+ $@ $>"TLS_req" $3 $| <$1:$2>
2899 ######################################################################
2900 ### TLS_req: check additional TLS requirements
2902 ### Parameters: [<list> <of> <req>] $| <$-:$+>
2903 ### $-: SMTP reply code
2904 ### $+: Enhanced Status Code
2905 dnl further requirements for this ruleset:
2906 dnl name of "other side" is stored is {TLS_name} (client/server_name)
2908 dnl right now this is only a logical AND
2909 dnl i.e. all requirements must be true
2910 dnl how about an OR? CN must be X or CN must be Y or ..
2911 dnl use a macro to compute this as a trivial sequential
2912 dnl operations (no precedences etc)?
2913 ######################################################################
2915 dnl no additional requirements: ok
2917 dnl require CN: but no CN specified: use name of other side
2918 R<CN> $* $| <$+> $: <CN:$&{TLS_Name}> $1 $| <$2>
2919 ifdef(`_FFR_TLS_ALTNAMES', `dnl
2920 R<CN:$={cert_altnames}> $* $| <$+> $@ $>"TLS_req" $2 $| <$3>
2921 R<CN:$-.$+> $* $| <$+> $: <CN:*.$2> $3 $| <$4>
2922 R<CN:$={cert_altnames}> $* $| <$+> $@ $>"TLS_req" $3 $| <$3>
2923 R<CN:$*> $* $| <$+> $: <CN:$&{TLS_Name}> $2 $| <$3>', `dnl')
2924 dnl match, check rest
2925 R<CN:$&{cn_subject}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
2926 dnl CN does not match
2928 R<CN:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
2930 R<CS:$&{cert_subject}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
2931 dnl CS does not match
2933 R<CS:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
2934 dnl match, check rest
2935 R<CI:$&{cert_issuer}> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
2936 dnl CI does not match
2938 R<CI:$+> $* $| <$-:$+> $#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
2940 R<CITag:$-> $* $| <$+> $: <$(access $1:$&{cert_issuer} $: ? $)> $2 $| <$3>
2941 R<?> $* $| <$-:$+> $#error $@ $3 $: $2 " Cert Issuer " $&{cert_issuer} " not acceptable"
2942 R<OK> $* $| <$+> $@ $>"TLS_req" $1 $| <$2>
2943 dnl return from recursive call
2946 ######################################################################
2947 ### max: return the maximum of two values separated by :
2949 ### Parameters: [$-]:[$-]
2950 ######################################################################
2955 R$-:$- $: $(arith l $@ $1 $@ $2 $) : $1 : $2
2958 dnl endif _ACCESS_TABLE_
2961 ifdef(`_TLS_SESSION_FEATURES_', `dnl
2963 ifdef(`_ACCESS_TABLE_', `dnl
2964 R$* $| $* $: $>D <$1> <?> <! TLS_Srv_Features> <$2>
2965 R<?> <$*> $: $>A <$1> <?> <! TLS_Srv_Features> <$1>
2972 ifdef(`_ACCESS_TABLE_', `dnl
2973 R$* $| $* $: $>D <$1> <?> <! TLS_Clt_Features> <$2>
2974 R<?> <$*> $: $>A <$1> <?> <! TLS_Clt_Features> <$1>
2981 ######################################################################
2982 ### RelayTLS: allow relaying based on TLS authentication
2986 ######################################################################
2989 dnl we do not allow relaying for anyone who can present a cert
2990 dnl signed by a "trusted" CA. For example, even if we put verisigns
2991 dnl CA in CertPath so we can authenticate users, we do not allow
2992 dnl them to abuse our server (they might be easier to get hold of,
2994 dnl so here is the trick: if the verification succeeded
2995 dnl we look up the cert issuer in the access map
2996 dnl (maybe after extracting a part with a regular expression)
2997 dnl if this returns RELAY we relay without further questions
2998 dnl if it returns SUBJECT we perform a similar check on the
3000 ifdef(`_ACCESS_TABLE_', `dnl
3001 R$* $: <?> $&{verify}
3002 R<?> $={TlsVerified} $: OK authenticated: continue
3003 R<?> $* $@ NO not authenticated
3004 ifdef(`_CERT_REGEX_ISSUER_', `dnl
3005 R$* $: $(CERTIssuer $&{cert_issuer} $)',
3006 `R$* $: $&{cert_issuer}')
3007 R$+ $: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
3008 dnl use $# to stop further checks (delay_check)
3010 ifdef(`_CERT_REGEX_SUBJECT_', `dnl
3011 RSUBJECT $: <@> $(CERTSubject $&{cert_subject} $)',
3012 `RSUBJECT $: <@> $&{cert_subject}')
3013 R<@> $+ $: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
3017 ######################################################################
3018 ### authinfo: lookup authinfo in the access map
3021 ### $1: {server_name}
3022 ### $2: {server_addr}
3023 dnl both are currently ignored
3024 dnl if it should be done via another map, we either need to restrict
3025 dnl functionality (it calls D and A) or copy those rulesets (or add another
3026 dnl parameter which I want to avoid, it's quite complex already)
3027 ######################################################################
3028 dnl omit this ruleset if neither is defined?
3029 dnl it causes DefaultAuthInfo to be ignored
3030 dnl (which may be considered a good thing).
3032 ifdef(`_AUTHINFO_TABLE_', `dnl
3033 R$* $: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
3034 R<?> $: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
3035 R<?> $: <$(authinfo AuthInfo: $: ? $)>
3036 R<?> $@ no no authinfo available
3039 ifdef(`_ACCESS_TABLE_', `dnl
3040 R$* $: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
3041 R$* $| <?>$* $: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
3042 R$* $| <?>$* $: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
3043 R$* $| <?>$* $@ no no authinfo available
3044 R$* $| <$*> <> $# $2
3047 ifdef(`_RATE_CONTROL_',`dnl
3048 ######################################################################
3050 ### Parameters: ignored
3051 ### return: $#error or OK
3052 ######################################################################
3054 ifdef(`_ACCESS_TABLE_', `dnl
3055 R$* $: <A:$&{client_addr}> <E:>
3056 dnl also look up a default value via E:
3057 R$+ $: $>SearchList <! ClientRate> $| $1 <>
3058 dnl found nothing: stop here
3060 ifdef(`_ATMPF_', `dnl tempfail?
3061 R<$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`RC')', `dnl')
3062 dnl use the generic routine (for now)
3064 R<$+> $: <$1> $| $(arith l $@ $1 $@ $&{client_rate} $)
3065 dnl log this? Connection rate $&{client_rate} exceeds limit $1.
3066 R<$+> $| TRUE $#error $@ 4.3.2 $: _RATE_CONTROL_REPLY Connection rate limit exceeded.
3069 ifdef(`_CONN_CONTROL_',`dnl
3070 ######################################################################
3072 ### Parameters: ignored
3073 ### return: $#error or OK
3074 ######################################################################
3076 ifdef(`_ACCESS_TABLE_', `dnl
3077 R$* $: <A:$&{client_addr}> <E:>
3078 dnl also look up a default value via E:
3079 R$+ $: $>SearchList <! ClientConn> $| $1 <>
3080 dnl found nothing: stop here
3082 ifdef(`_ATMPF_', `dnl tempfail?
3083 R<$* _ATMPF_> $#error $@ 4.3.0 $: _TMPFMSG_(`CC')', `dnl')
3084 dnl use the generic routine (for now)
3086 R<$+> $: <$1> $| $(arith l $@ $1 $@ $&{client_connections} $)
3087 dnl log this: Open connections $&{client_connections} exceeds limit $1.
3088 R<$+> $| TRUE $#error $@ 4.3.2 $: _CONN_CONTROL_REPLY Too many open connections.
3091 undivert(9)dnl LOCAL_RULESETS
3093 ######################################################################
3094 ######################################################################
3096 `##### MAIL FILTER DEFINITIONS'
3098 ######################################################################
3099 ######################################################################
3102 ######################################################################
3103 ######################################################################
3105 `##### MAILER DEFINITIONS'
3107 ######################################################################
3108 ######################################################################
3109 undivert(7)dnl MAILER_DEFINITIONS