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