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