xref: /openbsd-src/usr.sbin/smtpd/smtpd-filters.7 (revision 605f2cbb7e7a7ce77926be634902ddb44690d93c)
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