1.\" $OpenBSD: smtpd-filters.7,v 1.13 2024/11/05 19:36:53 jmc Exp $ 2.\" 3.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> 4.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 5.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 6.\" 7.\" Permission to use, copy, modify, and distribute this software for any 8.\" purpose with or without fee is hereby granted, provided that the above 9.\" copyright notice and this permission notice appear in all copies. 10.\" 11.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18.\" 19.\" 20.Dd $Mdocdate: November 5 2024 $ 21.Dt SMTPD-FILTERS 7 22.Os 23.Sh NAME 24.Nm smtpd-filters 25.Nd filtering API for the smtpd daemon 26.Sh DESCRIPTION 27The 28.Xr smtpd 8 29daemon provides a Simple Mail Transfer Protocol (SMTP) implementation, 30which allows ordinary machines to become Mail eXchangers (MX). 31Some features that are commonly used by MX, 32such as delivery reporting or spam filtering, 33are outside the scope of SMTP and too complex to fit in 34.Xr smtpd 8 . 35.Pp 36Because an MX may need to provide these features, 37.Xr smtpd 8 38provides an API to extend its behavior through 39.Nm . 40.Pp 41At runtime, 42.Xr smtpd 8 43can report events to 44.Nm , 45querying what it should answer to these events. 46This allows the decision logic to rely on third-party programs. 47.Sh DESIGN 48.Nm 49are programs that run as unique standalone processes, 50they do not share 51.Xr smtpd 8 52memory space. 53They are executed by 54.Xr smtpd 8 55at startup and expected to run in an infinite loop, 56reading events and filtering requests from 57.Xr stdin 4 , 58writing responses to 59.Xr stdout 4 60and logging to 61.Xr stderr 4 . 62They are not allowed to terminate. 63.Pp 64Because 65.Nm 66are standalone programs that communicate with 67.Xr smtpd 8 68through 69.Xr fd 4 , 70they may run as different users than 71.Xr smtpd 8 72and may be written in any language. 73.Nm 74must not use blocking I/O, 75they must support answering asynchronously to 76.Xr smtpd 8 . 77.Sh REPORT AND FILTER 78The API relies on two streams, 79report and filter. 80.Pp 81The report stream is a one-way stream which allows 82.Xr smtpd 8 83to inform 84.Nm 85in real-time about events. 86Report events do not expect an answer from 87.Nm ; 88they are just meant to provide information. 89A filter should be able to replicate the 90.Xr smtpd 8 91state for a session by gathering information coming from report events. 92No decision is ever taken by the report stream. 93.Pp 94The filter stream is a two-way stream which allows 95.Xr smtpd 8 96to query 97.Nm 98about what it should do with a session at a given phase. 99Filter requests expect an answer from 100.Nm ; 101.Xr smtpd 8 102will not let the session move forward until then. 103A decision must always be taken by the filter stream. 104.Pp 105It is sometimes possible to rely on filter requests to gather information, 106but because a response is expected by 107.Xr smtpd 8 , 108this is more costly than using report events. 109The correct pattern for writing filters is to use report events to 110create a local state for a session, 111then use filter requests to take decisions based on this state. 112The only case when using filter requests instead of report events is correct 113is when a decision is required for the filter request and there is no need 114for more information than that of the event itself. 115.Sh PROTOCOL 116The protocol consists of human-readable lines exchanged between 117.Nm 118and 119.Xr smtpd 8 , 120through 121.Xr fd 4 . 122.Pp 123The protocol begins with a handshake. 124First, 125.Xr smtpd 8 126provides 127.Nm 128with general configuration information in the form of key-value lines: 129.Bd -literal -offset indent 130config|smtpd-version|7.5.0 131config|protocol|0.7 132config|smtp-session-timeout|300 133config|subsystem|smtp-in 134config|ready 135.Ed 136.Pp 137Then, 138.Nm 139register the stream, 140subsystem and event they want to handle: 141.Bd -literal -offset indent 142register|report|smtp-in|link-connect 143register|ready 144.Ed 145.Pp 146Finally, 147.Xr smtpd 8 148emits report events and filter requests, 149expecting 150.Nm 151to respond or not depending on the stream: 152.Bd -literal -offset indent 153report|0.7|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 154report|0.7|1576147242.200225|smtp-in|link-connect|7641dfb3798eb5bf|mail.openbsd.org|pass|199.185.178.25:31205|45.77.67.80:25 155report|0.7|1576148447.982572|smtp-in|link-connect|7641dfc063102cbd|mail.openbsd.org|pass|199.185.178.25:24786|45.77.67.80:25 156.Ed 157.Pp 158The character 159.Dq | 160may only appear in the last field of a payload, 161in which case it should be considered a regular character and not a separator. 162No other field may contain a 163.Dq | . 164.Pp 165The list of subsystems and events, 166as well as the format of requests and responses, 167are documented in the sections below. 168.Sh CONFIGURATION 169During the initial handshake, 170.Xr smtpd 8 171emits a series of configuration keys and values. 172The list is meant to be ignored by 173.Nm 174that do not require it and consumed gracefully by filters that do. 175.Pp 176There are currently three keys: 177.Bd -literal -offset indent 178config|smtpd-version|7.5.0 179config|protocol|0.7 180config|smtp-session-timeout|300 181config|subsystem|smtp-in 182.Ed 183.Pp 184When 185.Xr smtpd 8 186has sent all configuration keys, it emits the following line: 187.Bd -literal -offset indent 188config|ready 189.Ed 190.Sh REPORT EVENTS 191There is currently only one subsystem supported in the API: 192smtp-in. 193.Pp 194Each report event is generated by 195.Xr smtpd 8 196as a single line similar to the one below: 197.Bd -literal -offset indent 198report|0.7|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 199.Ed 200.Pp 201The format consists of a protocol prefix containing the stream, 202the protocol version, 203the timestamp, 204the subsystem, 205the event and the unique session identifier, 206separated by 207.Dq | : 208.Bd -literal -offset indent 209report|0.7|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00 210.Ed 211.Pp 212It is followed by a suffix containing the event-specific parameters, 213also separated by 214.Dq | : 215.Bd -literal -offset indent 216mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 217.Ed 218.Pp 219The list of events and event-specific parameters for smtp-in are as follows: 220.Bl -tag -width Ds 221.It Ic link-connect : Ar rdns fcrdns src dest 222This event is generated upon connection. 223.Pp 224.Ar rdns 225contains the reverse DNS hostname for the remote end or an empty string if none. 226.Pp 227.Ar fcrdns 228contains the string 229.Dq pass 230or 231.Dq fail 232depending on if the remote end validates FCrDNS. 233.Pp 234.Ar src 235contains either the IP address and port of the source address, 236in the format 237.Dq address:port , 238or the path to a UNIX socket in the format 239.Dq unix:/path . 240.Pp 241.Ar dest 242holds either the IP address and port of the destination address, 243in the format 244.Dq address:port , 245or the path to a UNIX socket in the format 246.Dq unix:/path . 247.It Ic link-greeting : Ar hostname 248This event is generated upon display of the server banner. 249.Pp 250.Ar hostname 251contains the hostname displayed in the banner. 252.It Ic link-identify : Ar method identity 253This event is generated upon 254.Dq HELO 255or 256.Dq EHLO 257command from the client. 258.Pp 259.Ar method 260contains the string 261.Dq HELO 262or 263.Dq EHLO 264indicating the method used by the client. 265.Pp 266.Ar identity 267contains the identity provided by the client. 268.It Ic link-tls : Ar tls-string 269This event is generated upon successful negotiation of TLS. 270.Pp 271.Ar tls-string 272contains a colon-separated list of TLS properties including the TLS version, 273the cipher suite used by the session and the cipher strength in bits. 274.It Ic link-disconnect 275This event is generated upon disconnection of the client. 276.It Ic link-auth : Ar result username 277This event is generated upon an authentication attempt by the client. 278.Pp 279.Ar result 280contains the string 281.Dq pass , 282.Dq fail 283or 284.Dq error 285depending on the result of the authentication attempt. 286.Pp 287.Ar username 288contains the username used for the authentication attempt. 289.It Ic tx-reset : Op message-id 290This event is generated when a transaction is reset. 291.Pp 292If reset took place during a transaction, 293.Ar message-id 294contains the identifier of the transaction being reset. 295.It Ic tx-begin : Ar message-id 296This event is generated when a transaction is initiated. 297.Pp 298.Ar message-id 299contains the identifier for the transaction. 300.It Ic tx-mail : Ar message-id Ar result address 301This event is generated when client emits 302.Dq MAIL FROM . 303.Pp 304.Ar message-id 305contains the identifier for the transaction. 306.Pp 307.Ar result 308contains 309.Dq ok 310if the sender was accepted, 311.Dq permfail 312if it was rejected 313or 314.Dq tempfail 315if it was rejected for a transient error. 316.Pp 317.Ar address 318contains the e-mail address of the sender. 319The address is normalized and sanitized, 320the characters 321.Dq < 322and 323.Dq > 324are removed, 325along with any parameters to 326.Dq MAIL FROM . 327.It Ic tx-rcpt : Ar message-id Ar result address 328This event is generated when client emits 329.Dq RCPT TO . 330.Pp 331.Ar message-id 332contains the identifier for the transaction. 333.Pp 334.Ar result 335contains 336.Dq ok 337if the recipient was accepted, 338.Dq permfail 339if it was rejected 340or 341.Dq tempfail 342if it was rejected for a transient error. 343.Pp 344.Ar address 345contains the e-mail address of the recipient. 346The address is normalized and sanitized, 347the characters 348.Dq < 349and 350.Dq > 351are removed, 352along with any parameters to 353.Dq RCPT TO . 354.It Ic tx-envelope : Ar message-id Ar envelope-id 355This event is generated when an envelope is accepted. 356.Pp 357.Ar envelope-id 358contains the unique identifier for the envelope. 359.It Ic tx-data : Ar message-id Ar result 360This event is generated when client has emitted 361.Dq DATA . 362.Pp 363.Ar message-id 364contains the unique identifier for the transaction. 365.Pp 366.Ar result 367contains 368.Dq ok 369if server accepted the message for processing, 370.Dq permfail 371if it has not been accepted and 372.Dq tempfail 373if a transient error prevented message processing. 374.It Ic tx-commit : Ar message-id Ar message-size 375This event is generated when a transaction has been accepted by the server. 376.Pp 377.Ar message-id 378contains the unique identifier for the SMTP transaction. 379.Pp 380.Ar message-size 381contains the size of the message submitted in the 382.Dq DATA 383phase of the SMTP transaction. 384.It Ic tx-rollback : Ar message-id 385This event is generated when a transaction has been rejected by the server. 386.Pp 387.Ar message-id 388contains the unique identifier for the SMTP transaction. 389.It Ic protocol-client : Ar command 390This event is generated for every command submitted by the client. 391It contains the raw command as received by the server. 392.Pp 393.Ar command 394contains the command emitted by the client to the server. 395.It Ic protocol-server : Ar response 396This event is generated for every response emitted by the server. 397It contains the raw response as emitted by the server. 398.Pp 399.Ar response 400contains the response emitted by the server to the client. 401.It Ic filter-report : Ar filter-kind Ar name message 402This event is generated when a filter emits a report. 403.Pp 404.Ar filter-kind may be either 405.Dq builtin 406or 407.Dq proc 408depending on if the filter is an 409.Xr smtpd 8 410builtin filter or a proc filter implementing the API. 411.Pp 412.Ar name 413is the name of the filter that generated the report. 414.Pp 415.Ar message 416is a filter-specific message. 417.It Ic filter-response : Ar phase response Op param 418This event is generated when a filter responds to a filtering request. 419.Pp 420.Ar phase 421contains the phase name for the request. 422The phases are documented in the next section. 423.Pp 424.Ar response 425contains the response of the filter to the request, 426it is either one of 427.Dq proceed , 428.Dq report , 429.Dq reject , 430.Dq disconnect , 431.Dq junk 432or 433.Dq rewrite . 434.Pp 435If specified, 436.Ar param 437is the parameter to the response. 438.It Ic timeout 439This event is generated when a timeout happens for a session. 440.El 441.Sh FILTER REQUESTS 442There is currently only one subsystem supported in the API: 443smtp-in. 444.Pp 445Filter requests allow 446.Xr smtpd 8 447to query 448.Nm 449about what to do with a session at a particular phase. 450In addition, 451they allow 452.Nm 453to alter the content of a message by adding, 454modifying, 455or suppressing lines of input in a way that is similar to what program like 456.Xr sed 1 457or 458.Xr grep 1 459would do. 460.Pp 461Each filter request is generated by 462.Xr smtpd 8 463as a single line similar to the one below. 464Fields are separated by the 465.Dq | 466character. 467.Bd -literal -offset indent 468filter|0.7|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 469.Ed 470.Pp 471The format consists of a protocol prefix containing the stream, 472the protocol version, 473the timestamp, 474the subsystem, 475the filtering phase, 476the unique session identifier and an opaque token that the filter 477should provide in its response: 478.Bd -literal -offset indent 479filter|0.7|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d 480.Ed 481.Pp 482It is followed by a suffix containing the phase-specific parameters of the 483filter request, 484also separated by 485.Dq | : 486.Bd -literal -offset indent 487mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 488.Ed 489.Pp 490Unlike with report events, 491.Xr smtpd 8 492expects answers from filter requests and will not allow a session to move 493forward until the filter has instructed 494.Xr smtpd 8 495how to treat it. 496.Pp 497For all phases except 498.Dq data-line , 499responses must follow the same construct: 500a message of type 501.Dq filter-result , 502followed by the unique session id, 503the opaque token, 504a decision and optional decision-specific parameters: 505.Bd -literal -offset indent 506filter-result|7641df9771b4ed00|1ef1c203cc576e5d|proceed 507filter-result|7641df9771b4ed00|1ef1c203cc576e5d|reject|550 nope 508.Ed 509.Pp 510The possible decisions for a 511.Dq filter-result 512message are documented below. 513.Pp 514For the 515.Dq data-line 516phase, 517.Nm 518are fed a stream of lines corresponding to the message to filter, 519terminated by a single dot: 520.Bd -literal -offset indent 521filter|0.7|1576146008.006099|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 1 522filter|0.7|1576146008.006103|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 2 523filter|0.7|1576146008.006105|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|. 524.Ed 525.Pp 526They are expected to return an output stream 527similarly terminated by a single dot. 528A filter may add to, 529suppress, 530modify or echo back the lines it receives. 531Ultimately, 532.Xr smtpd 8 533assumes that the message consists of the output from 534.Nm . 535.Pp 536Note that filters may be chained, 537and the lines that are input into a subsequent filter 538are the lines that are output from a previous filter. 539.Pp 540The response to 541.Dq data-line 542requests use their own construct. 543A 544.Dq filter-dataline 545prefix, 546followed by the unique session identifier, 547the opaque token and the output line as follows: 548.Bd -literal -offset indent 549filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 1 550filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 2 551filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|. 552.Ed 553.Pp 554The list of events and event-specific parameters for smtp-in are as follows: 555.Bl -tag -width Ds 556.It Ic connect : Ar rdns fcrdns src dest 557This request is emitted after connection, 558before the banner is displayed. 559.It Ic helo : Ar identity 560This request is emitted after the client has emitted 561.Dq HELO . 562.It Ic ehlo : Ar identity 563This request is emitted after the client has emitted 564.Dq EHLO . 565.It Ic starttls : Ar tls-string 566This request is emitted after the client has requested 567.Dq STARTTLS . 568.It Ic auth : Ar auth 569This request is emitted after the client has requested 570.Dq AUTH . 571.It Ic mail-from : Ar address 572This request is emitted after the client has requested 573.Dq MAIL FROM . 574.It Ic rcpt-to : Ar address 575This request is emitted after the client has requested 576.Dq RCPT TO . 577.It Ic data 578This request is emitted after the client has requested 579.Dq DATA . 580.It Ic data-line : Ar line 581This request is emitted for each line of input in the 582.Dq DATA 583phase. 584The lines are raw dot-escaped SMTP DATA input, 585terminated with a single dot. 586.It Ic commit 587This request is emitted after the final single dot is received. 588.El 589.Pp 590For every filtering phase, 591excepted 592.Dq data-line , 593the following decisions may be taken by a filter: 594.Bl -tag -width Ds 595.It Ic proceed 596No action is taken, 597session or transaction may be passed to the next filter. 598.It Ic junk 599The session or transaction is marked as spam. 600.Xr smtpd 8 601will prepend an 602.Dq X-Spam 603header to the message. 604.It Ic reject Ar error 605The command is rejected with the message 606.Ar error . 607The message must be a valid SMTP message including status code, 6085xx or 4xx. 609.Pp 610Messages starting with a 5xx status result in a permanent failure, 611those starting with a 4xx status result in a temporary failure. 612.Pp 613Messages starting with a 421 status will result in a client disconnect. 614.It Ic disconnect Ar error 615The client is disconnected with the message 616.Ar error . 617The message must be a valid SMTP message including status code, 6185xx or 4xx. 619.Pp 620Messages starting with a 5xx status result in a permanent failure, 621those starting with a 4xx status result in a temporary failure. 622.It Ic rewrite Ar parameter 623The command parameter is rewritten. 624.Pp 625This decision allows a filter to perform a rewrite of client-submitted 626commands before they are processed by the SMTP engine. 627.Ar parameter 628is expected to be a valid SMTP parameter for the command. 629.It Ic report Ar parameter 630Generates a report with 631.Ar parameter 632for this filter. 633.El 634.\".Sh EXAMPLES 635.\"This example filter written in 636.\".Xr sh 1 637.\"will echo back... 638.\".Bd -literal -offset indent 639.\"XXX 640.\".Ed 641.\".Pp 642.\"This example filter will filter... 643.\".Bd -literal -offset indent 644.\"XXX 645.\".Ed 646.\".Pp 647.\"Note that libraries may provide a simpler interface to 648.\".Nm 649.\"that does not require implementing the protocol itself. 650.\".Ed 651.Sh SEE ALSO 652.Xr smtpd 8 653.Sh HISTORY 654.Nm 655first appeared in 656.Ox 6.6 . 657