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