| 1 | $Cambridge: exim/doc/doc-txt/experimental-spec.txt,v 1.12 2009/06/11 14:07:57 tom Exp $ |
| 2 | |
| 3 | From time to time, experimental features may be added to Exim. |
| 4 | While a feature is experimental, there will be a build-time |
| 5 | option whose name starts "EXPERIMENTAL_" that must be set in |
| 6 | order to include the feature. This file contains information |
| 7 | about experimenatal features, all of which are unstable and |
| 8 | liable to incompatibile change. |
| 9 | |
| 10 | |
| 11 | Brightmail AntiSpam (BMI) suppport |
| 12 | -------------------------------------------------------------- |
| 13 | |
| 14 | Brightmail AntiSpam is a commercial package. Please see |
| 15 | http://www.brightmail.com for more information on |
| 16 | the product. For the sake of clarity, we'll refer to it as |
| 17 | "BMI" from now on. |
| 18 | |
| 19 | |
| 20 | 0) BMI concept and implementation overview |
| 21 | |
| 22 | In contrast to how spam-scanning with SpamAssassin is |
| 23 | implemented in exiscan-acl, BMI is more suited for per |
| 24 | -recipient scanning of messages. However, each messages is |
| 25 | scanned only once, but multiple "verdicts" for multiple |
| 26 | recipients can be returned from the BMI server. The exiscan |
| 27 | implementation passes the message to the BMI server just |
| 28 | before accepting it. It then adds the retrieved verdicts to |
| 29 | the messages header file in the spool. These verdicts can then |
| 30 | be queried in routers, where operation is per-recipient |
| 31 | instead of per-message. To use BMI, you need to take the |
| 32 | following steps: |
| 33 | |
| 34 | 1) Compile Exim with BMI support |
| 35 | 2) Set up main BMI options (top section of Exim config file) |
| 36 | 3) Set up ACL control statement (ACL section of the config |
| 37 | file) |
| 38 | 4) Set up your routers to use BMI verdicts (routers section |
| 39 | of the config file). |
| 40 | 5) (Optional) Set up per-recipient opt-in information. |
| 41 | |
| 42 | These four steps are explained in more details below. |
| 43 | |
| 44 | 1) Adding support for BMI at compile time |
| 45 | |
| 46 | To compile with BMI support, you need to link Exim against |
| 47 | the Brighmail client SDK, consisting of a library |
| 48 | (libbmiclient_single.so) and a header file (bmi_api.h). |
| 49 | You'll also need to explicitly set a flag in the Makefile to |
| 50 | include BMI support in the Exim binary. Both can be achieved |
| 51 | with these lines in Local/Makefile: |
| 52 | |
| 53 | EXPERIMENTAL_BRIGHTMAIL=yes |
| 54 | CFLAGS=-I/path/to/the/dir/with/the/includefile |
| 55 | EXTRALIBS_EXIM=-L/path/to/the/dir/with/the/library -lbmiclient_single |
| 56 | |
| 57 | If you use other CFLAGS or EXTRALIBS_EXIM settings then |
| 58 | merge the content of these lines with them. |
| 59 | |
| 60 | Note for BMI6.x users: You'll also have to add -lxml2_single |
| 61 | to the EXTRALIBS_EXIM line. Users of 5.5x do not need to do |
| 62 | this. |
| 63 | |
| 64 | You should also include the location of |
| 65 | libbmiclient_single.so in your dynamic linker configuration |
| 66 | file (usually /etc/ld.so.conf) and run "ldconfig" |
| 67 | afterwards, or else the produced Exim binary will not be |
| 68 | able to find the library file. |
| 69 | |
| 70 | |
| 71 | 2) Setting up BMI support in the Exim main configuration |
| 72 | |
| 73 | To enable BMI support in the main Exim configuration, you |
| 74 | should set the path to the main BMI configuration file with |
| 75 | the "bmi_config_file" option, like this: |
| 76 | |
| 77 | bmi_config_file = /opt/brightmail/etc/brightmail.cfg |
| 78 | |
| 79 | This must go into section 1 of Exim's configuration file (You |
| 80 | can put it right on top). If you omit this option, it |
| 81 | defaults to /opt/brightmail/etc/brightmail.cfg. |
| 82 | |
| 83 | Note for BMI6.x users: This file is in XML format in V6.xx |
| 84 | and its name is /opt/brightmail/etc/bmiconfig.xml. So BMI |
| 85 | 6.x users MUST set the bmi_config_file option. |
| 86 | |
| 87 | |
| 88 | 3) Set up ACL control statement |
| 89 | |
| 90 | To optimize performance, it makes sense only to process |
| 91 | messages coming from remote, untrusted sources with the BMI |
| 92 | server. To set up a messages for processing by the BMI |
| 93 | server, you MUST set the "bmi_run" control statement in any |
| 94 | ACL for an incoming message. You will typically do this in |
| 95 | an "accept" block in the "acl_check_rcpt" ACL. You should |
| 96 | use the "accept" block(s) that accept messages from remote |
| 97 | servers for your own domain(s). Here is an example that uses |
| 98 | the "accept" blocks from Exim's default configuration file: |
| 99 | |
| 100 | |
| 101 | accept domains = +local_domains |
| 102 | endpass |
| 103 | verify = recipient |
| 104 | control = bmi_run |
| 105 | |
| 106 | accept domains = +relay_to_domains |
| 107 | endpass |
| 108 | verify = recipient |
| 109 | control = bmi_run |
| 110 | |
| 111 | If bmi_run is not set in any ACL during reception of the |
| 112 | message, it will NOT be passed to the BMI server. |
| 113 | |
| 114 | |
| 115 | 4) Setting up routers to use BMI verdicts |
| 116 | |
| 117 | When a message has been run through the BMI server, one or |
| 118 | more "verdicts" are present. Different recipients can have |
| 119 | different verdicts. Each recipient is treated individually |
| 120 | during routing, so you can query the verdicts by recipient |
| 121 | at that stage. From Exim's view, a verdict can have the |
| 122 | following outcomes: |
| 123 | |
| 124 | o deliver the message normally |
| 125 | o deliver the message to an alternate location |
| 126 | o do not deliver the message |
| 127 | |
| 128 | To query the verdict for a recipient, the implementation |
| 129 | offers the following tools: |
| 130 | |
| 131 | |
| 132 | - Boolean router preconditions. These can be used in any |
| 133 | router. For a simple implementation of BMI, these may be |
| 134 | all that you need. The following preconditions are |
| 135 | available: |
| 136 | |
| 137 | o bmi_deliver_default |
| 138 | |
| 139 | This precondition is TRUE if the verdict for the |
| 140 | recipient is to deliver the message normally. If the |
| 141 | message has not been processed by the BMI server, this |
| 142 | variable defaults to TRUE. |
| 143 | |
| 144 | o bmi_deliver_alternate |
| 145 | |
| 146 | This precondition is TRUE if the verdict for the |
| 147 | recipient is to deliver the message to an alternate |
| 148 | location. You can get the location string from the |
| 149 | $bmi_alt_location expansion variable if you need it. See |
| 150 | further below. If the message has not been processed by |
| 151 | the BMI server, this variable defaults to FALSE. |
| 152 | |
| 153 | o bmi_dont_deliver |
| 154 | |
| 155 | This precondition is TRUE if the verdict for the |
| 156 | recipient is NOT to deliver the message to the |
| 157 | recipient. You will typically use this precondition in a |
| 158 | top-level blackhole router, like this: |
| 159 | |
| 160 | # don't deliver messages handled by the BMI server |
| 161 | bmi_blackhole: |
| 162 | driver = redirect |
| 163 | bmi_dont_deliver |
| 164 | data = :blackhole: |
| 165 | |
| 166 | This router should be on top of all others, so messages |
| 167 | that should not be delivered do not reach other routers |
| 168 | at all. If the message has not been processed by |
| 169 | the BMI server, this variable defaults to FALSE. |
| 170 | |
| 171 | |
| 172 | - A list router precondition to query if rules "fired" on |
| 173 | the message for the recipient. Its name is "bmi_rule". You |
| 174 | use it by passing it a colon-separated list of rule |
| 175 | numbers. You can use this condition to route messages that |
| 176 | matched specific rules. Here is an example: |
| 177 | |
| 178 | # special router for BMI rule #5, #8 and #11 |
| 179 | bmi_rule_redirect: |
| 180 | driver = redirect |
| 181 | bmi_rule = 5:8:11 |
| 182 | data = postmaster@mydomain.com |
| 183 | |
| 184 | |
| 185 | - Expansion variables. Several expansion variables are set |
| 186 | during routing. You can use them in custom router |
| 187 | conditions, for example. The following variables are |
| 188 | available: |
| 189 | |
| 190 | o $bmi_base64_verdict |
| 191 | |
| 192 | This variable will contain the BASE64 encoded verdict |
| 193 | for the recipient being routed. You can use it to add a |
| 194 | header to messages for tracking purposes, for example: |
| 195 | |
| 196 | localuser: |
| 197 | driver = accept |
| 198 | check_local_user |
| 199 | headers_add = X-Brightmail-Verdict: $bmi_base64_verdict |
| 200 | transport = local_delivery |
| 201 | |
| 202 | If there is no verdict available for the recipient being |
| 203 | routed, this variable contains the empty string. |
| 204 | |
| 205 | o $bmi_base64_tracker_verdict |
| 206 | |
| 207 | This variable will contain a BASE64 encoded subset of |
| 208 | the verdict information concerning the "rules" that |
| 209 | fired on the message. You can add this string to a |
| 210 | header, commonly named "X-Brightmail-Tracker". Example: |
| 211 | |
| 212 | localuser: |
| 213 | driver = accept |
| 214 | check_local_user |
| 215 | headers_add = X-Brightmail-Tracker: $bmi_base64_tracker_verdict |
| 216 | transport = local_delivery |
| 217 | |
| 218 | If there is no verdict available for the recipient being |
| 219 | routed, this variable contains the empty string. |
| 220 | |
| 221 | o $bmi_alt_location |
| 222 | |
| 223 | If the verdict is to redirect the message to an |
| 224 | alternate location, this variable will contain the |
| 225 | alternate location string returned by the BMI server. In |
| 226 | its default configuration, this is a header-like string |
| 227 | that can be added to the message with "headers_add". If |
| 228 | there is no verdict available for the recipient being |
| 229 | routed, or if the message is to be delivered normally, |
| 230 | this variable contains the empty string. |
| 231 | |
| 232 | o $bmi_deliver |
| 233 | |
| 234 | This is an additional integer variable that can be used |
| 235 | to query if the message should be delivered at all. You |
| 236 | should use router preconditions instead if possible. |
| 237 | |
| 238 | $bmi_deliver is '0': the message should NOT be delivered. |
| 239 | $bmi_deliver is '1': the message should be delivered. |
| 240 | |
| 241 | |
| 242 | IMPORTANT NOTE: Verdict inheritance. |
| 243 | The message is passed to the BMI server during message |
| 244 | reception, using the target addresses from the RCPT TO: |
| 245 | commands in the SMTP transaction. If recipients get expanded |
| 246 | or re-written (for example by aliasing), the new address(es) |
| 247 | inherit the verdict from the original address. This means |
| 248 | that verdicts also apply to all "child" addresses generated |
| 249 | from top-level addresses that were sent to the BMI server. |
| 250 | |
| 251 | |
| 252 | 5) Using per-recipient opt-in information (Optional) |
| 253 | |
| 254 | The BMI server features multiple scanning "profiles" for |
| 255 | individual recipients. These are usually stored in a LDAP |
| 256 | server and are queried by the BMI server itself. However, |
| 257 | you can also pass opt-in data for each recipient from the |
| 258 | MTA to the BMI server. This is particularly useful if you |
| 259 | already look up recipient data in Exim anyway (which can |
| 260 | also be stored in a SQL database or other source). This |
| 261 | implementation enables you to pass opt-in data to the BMI |
| 262 | server in the RCPT ACL. This works by setting the |
| 263 | 'bmi_optin' modifier in a block of that ACL. If should be |
| 264 | set to a list of comma-separated strings that identify the |
| 265 | features which the BMI server should use for that particular |
| 266 | recipient. Ideally, you would use the 'bmi_optin' modifier |
| 267 | in the same ACL block where you set the 'bmi_run' control |
| 268 | flag. Here is an example that will pull opt-in data for each |
| 269 | recipient from a flat file called |
| 270 | '/etc/exim/bmi_optin_data'. |
| 271 | |
| 272 | The file format: |
| 273 | |
| 274 | user1@mydomain.com: <OPTIN STRING1>:<OPTIN STRING2> |
| 275 | user2@thatdomain.com: <OPTIN STRING3> |
| 276 | |
| 277 | |
| 278 | The example: |
| 279 | |
| 280 | accept domains = +relay_to_domains |
| 281 | endpass |
| 282 | verify = recipient |
| 283 | bmi_optin = ${lookup{$local_part@$domain}lsearch{/etc/exim/bmi_optin_data}} |
| 284 | control = bmi_run |
| 285 | |
| 286 | Of course, you can also use any other lookup method that |
| 287 | Exim supports, including LDAP, Postgres, MySQL, Oracle etc., |
| 288 | as long as the result is a list of colon-separated opt-in |
| 289 | strings. |
| 290 | |
| 291 | For a list of available opt-in strings, please contact your |
| 292 | Brightmail representative. |
| 293 | |
| 294 | |
| 295 | |
| 296 | |
| 297 | Sender Policy Framework (SPF) support |
| 298 | -------------------------------------------------------------- |
| 299 | |
| 300 | To learn more about SPF, visit http://www.openspf.org. This |
| 301 | document does not explain the SPF fundamentals, you should |
| 302 | read and understand the implications of deploying SPF on your |
| 303 | system before doing so. |
| 304 | |
| 305 | SPF support is added via the libspf2 library. Visit |
| 306 | |
| 307 | http://www.libspf2.org/ |
| 308 | |
| 309 | to obtain a copy, then compile and install it. By default, |
| 310 | this will put headers in /usr/local/include and the static |
| 311 | library in /usr/local/lib. |
| 312 | |
| 313 | To compile Exim with SPF support, set these additional flags in |
| 314 | Local/Makefile: |
| 315 | |
| 316 | EXPERIMENTAL_SPF=yes |
| 317 | CFLAGS=-DSPF -I/usr/local/include |
| 318 | EXTRALIBS_EXIM=-L/usr/local/lib -lspf2 |
| 319 | |
| 320 | This assumes that the libspf2 files are installed in |
| 321 | their default locations. |
| 322 | |
| 323 | You can now run SPF checks in incoming SMTP by using the "spf" |
| 324 | ACL condition in either the MAIL, RCPT or DATA ACLs. When |
| 325 | using it in the RCPT ACL, you can make the checks dependend on |
| 326 | the RCPT address (or domain), so you can check SPF records |
| 327 | only for certain target domains. This gives you the |
| 328 | possibility to opt-out certain customers that do not want |
| 329 | their mail to be subject to SPF checking. |
| 330 | |
| 331 | The spf condition takes a list of strings on its right-hand |
| 332 | side. These strings describe the outcome of the SPF check for |
| 333 | which the spf condition should succeed. Valid strings are: |
| 334 | |
| 335 | o pass The SPF check passed, the sending host |
| 336 | is positively verified by SPF. |
| 337 | o fail The SPF check failed, the sending host |
| 338 | is NOT allowed to send mail for the domain |
| 339 | in the envelope-from address. |
| 340 | o softfail The SPF check failed, but the queried |
| 341 | domain can't absolutely confirm that this |
| 342 | is a forgery. |
| 343 | o none The queried domain does not publish SPF |
| 344 | records. |
| 345 | o neutral The SPF check returned a "neutral" state. |
| 346 | This means the queried domain has published |
| 347 | a SPF record, but wants to allow outside |
| 348 | servers to send mail under its domain as well. |
| 349 | o err_perm This indicates a syntax error in the SPF |
| 350 | record of the queried domain. This should be |
| 351 | treated like "none". |
| 352 | o err_temp This indicates a temporary error during all |
| 353 | processing, including Exim's SPF processing. |
| 354 | You may defer messages when this occurs. |
| 355 | |
| 356 | You can prefix each string with an exclamation mark to invert |
| 357 | is meaning, for example "!fail" will match all results but |
| 358 | "fail". The string list is evaluated left-to-right, in a |
| 359 | short-circuit fashion. When a string matches the outcome of |
| 360 | the SPF check, the condition succeeds. If none of the listed |
| 361 | strings matches the outcome of the SPF check, the condition |
| 362 | fails. |
| 363 | |
| 364 | Here is an example to fail forgery attempts from domains that |
| 365 | publish SPF records: |
| 366 | |
| 367 | /* ----------------- |
| 368 | deny message = $sender_host_address is not allowed to send mail from ${if def:sender_address_domain {$sender_address_domain}{$sender_helo_name}}. \ |
| 369 | Please see http://www.openspf.org/Why?scope=${if def:sender_address_domain {mfrom}{helo}};identity=${if def:sender_address_domain {$sender_address}{$sender_helo_name}};ip=$sender_host_address |
| 370 | spf = fail |
| 371 | --------------------- */ |
| 372 | |
| 373 | You can also give special treatment to specific domains: |
| 374 | |
| 375 | /* ----------------- |
| 376 | deny message = AOL sender, but not from AOL-approved relay. |
| 377 | sender_domains = aol.com |
| 378 | spf = fail:neutral |
| 379 | --------------------- */ |
| 380 | |
| 381 | Explanation: AOL publishes SPF records, but is liberal and |
| 382 | still allows non-approved relays to send mail from aol.com. |
| 383 | This will result in a "neutral" state, while mail from genuine |
| 384 | AOL servers will result in "pass". The example above takes |
| 385 | this into account and treats "neutral" like "fail", but only |
| 386 | for aol.com. Please note that this violates the SPF draft. |
| 387 | |
| 388 | When the spf condition has run, it sets up several expansion |
| 389 | variables. |
| 390 | |
| 391 | $spf_header_comment |
| 392 | This contains a human-readable string describing the outcome |
| 393 | of the SPF check. You can add it to a custom header or use |
| 394 | it for logging purposes. |
| 395 | |
| 396 | $spf_received |
| 397 | This contains a complete Received-SPF: header that can be |
| 398 | added to the message. Please note that according to the SPF |
| 399 | draft, this header must be added at the top of the header |
| 400 | list. Please see section 10 on how you can do this. |
| 401 | |
| 402 | Note: in case of "Best-guess" (see below), the convention is |
| 403 | to put this string in a header called X-SPF-Guess: instead. |
| 404 | |
| 405 | $spf_result |
| 406 | This contains the outcome of the SPF check in string form, |
| 407 | one of pass, fail, softfail, none, neutral, err_perm or |
| 408 | err_temp. |
| 409 | |
| 410 | $spf_smtp_comment |
| 411 | This contains a string that can be used in a SMTP response |
| 412 | to the calling party. Useful for "fail". |
| 413 | |
| 414 | In addition to SPF, you can also perform checks for so-called |
| 415 | "Best-guess". Strictly speaking, "Best-guess" is not standard |
| 416 | SPF, but it is supported by the same framework that enables SPF |
| 417 | capability. Refer to http://www.openspf.org/FAQ/Best_guess_record |
| 418 | for a description of what it means. |
| 419 | |
| 420 | To access this feature, simply use the spf_guess condition in place |
| 421 | of the spf one. For example: |
| 422 | |
| 423 | /* ----------------- |
| 424 | deny message = $sender_host_address doesn't look trustworthy to me |
| 425 | spf_guess = fail |
| 426 | --------------------- */ |
| 427 | |
| 428 | In case you decide to reject messages based on this check, you |
| 429 | should note that although it uses the same framework, "Best-guess" |
| 430 | is NOT SPF, and therefore you should not mention SPF at all in your |
| 431 | reject message. |
| 432 | |
| 433 | When the spf_guess condition has run, it sets up the same expansion |
| 434 | variables as when spf condition is run, described above. |
| 435 | |
| 436 | Additionally, since Best-guess is not standarized, you may redefine |
| 437 | what "Best-guess" means to you by redefining spf_guess variable in |
| 438 | global config. For example, the following: |
| 439 | |
| 440 | /* ----------------- |
| 441 | spf_guess = v=spf1 a/16 mx/16 ptr ?all |
| 442 | --------------------- */ |
| 443 | |
| 444 | would relax host matching rules to a broader network range. |
| 445 | |
| 446 | |
| 447 | SRS (Sender Rewriting Scheme) Support |
| 448 | -------------------------------------------------------------- |
| 449 | |
| 450 | Exiscan currently includes SRS support via Miles Wilton's |
| 451 | libsrs_alt library. The current version of the supported |
| 452 | library is 0.5. |
| 453 | |
| 454 | In order to use SRS, you must get a copy of libsrs_alt from |
| 455 | |
| 456 | http://srs.mirtol.com/ |
| 457 | |
| 458 | Unpack the tarball, then refer to MTAs/README.EXIM |
| 459 | to proceed. You need to set |
| 460 | |
| 461 | EXPERIMENTAL_SRS=yes |
| 462 | |
| 463 | in your Local/Makefile. |
| 464 | |
| 465 | |
| 466 | -------------------------------------------------------------- |
| 467 | End of file |
| 468 | -------------------------------------------------------------- |