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