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