xref: /netbsd-src/external/ibm-public/postfix/dist/src/cleanup/cleanup_api.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: cleanup_api.c,v 1.2 2017/02/14 01:16:44 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	cleanup_api 3
6 /* SUMMARY
7 /*	cleanup callable interface, message processing
8 /* SYNOPSIS
9 /*	#include "cleanup.h"
10 /*
11 /*	CLEANUP_STATE *cleanup_open(src)
12 /*	VSTREAM	*src;
13 /*
14 /*	void	cleanup_control(state, flags)
15 /*	CLEANUP_STATE *state;
16 /*	int	flags;
17 /*
18 /*	void	CLEANUP_RECORD(state, type, buf, len)
19 /*	CLEANUP_STATE *state;
20 /*	int	type;
21 /*	char	*buf;
22 /*	int	len;
23 /*
24 /*	int	cleanup_flush(state)
25 /*	CLEANUP_STATE *state;
26 /*
27 /*	int	cleanup_free(state)
28 /*	CLEANUP_STATE *state;
29 /* DESCRIPTION
30 /*	This module implements a callable interface to the cleanup service
31 /*	for processing one message and for writing it to queue file.
32 /*	For a description of the cleanup service, see cleanup(8).
33 /*
34 /*	cleanup_open() creates a new queue file and performs other
35 /*	per-message initialization. The result is a handle that should be
36 /*	given to the cleanup_control(), cleanup_record(), cleanup_flush()
37 /*	and cleanup_free() routines. The name of the queue file is in the
38 /*	queue_id result structure member.
39 /*
40 /*	cleanup_control() processes per-message flags specified by the caller.
41 /*	These flags control the handling of data errors, and must be set
42 /*	before processing the first message record.
43 /* .IP CLEANUP_FLAG_BOUNCE
44 /*	The cleanup server is responsible for returning undeliverable
45 /*	mail (too many hops, message too large) to the sender.
46 /* .IP CLEANUP_FLAG_BCC_OK
47 /*	It is OK to add automatic BCC recipient addresses.
48 /* .IP CLEANUP_FLAG_FILTER
49 /*	Enable header/body filtering. This should be enabled only with mail
50 /*	that enters Postfix, not with locally forwarded mail or with bounce
51 /*	messages.
52 /* .IP CLEANUP_FLAG_MILTER
53 /*	Enable Milter applications. This should be enabled only with mail
54 /*	that enters Postfix, not with locally forwarded mail or with bounce
55 /*	messages.
56 /* .IP CLEANUP_FLAG_MAP_OK
57 /*	Enable canonical and virtual mapping, and address masquerading.
58 /* .PP
59 /*	For convenience the CLEANUP_FLAG_MASK_EXTERNAL macro specifies
60 /*	the options that are normally needed for mail that enters
61 /*	Postfix from outside, and CLEANUP_FLAG_MASK_INTERNAL specifies
62 /*	the options that are normally needed for internally generated or
63 /*	forwarded mail.
64 /*
65 /*	CLEANUP_RECORD() is a macro that processes one message record,
66 /*	that copies the result to the queue file, and that maintains a
67 /*	little state machine. The last record in a valid message has type
68 /*	REC_TYPE_END.  In order to find out if a message is corrupted,
69 /*	the caller is encouraged to test the CLEANUP_OUT_OK(state) macro.
70 /*	The result is false when further message processing is futile.
71 /*	In that case, it is safe to call cleanup_flush() immediately.
72 /*
73 /*	cleanup_flush() closes a queue file. In case of any errors,
74 /*	the file is removed. The result value is non-zero in case of
75 /*	problems. In some cases a human-readable text can be found in
76 /*	the state->reason member. In all other cases, use cleanup_strerror()
77 /*	to translate the result into human-readable text.
78 /*
79 /*	cleanup_free() destroys its argument.
80 /* .IP CLEANUP_FLAG_SMTPUTF8
81 /*	Request SMTPUTF8 support when delivering mail.
82 /* .IP CLEANUP_FLAG_AUTOUTF8
83 /*	Autodetection: request SMTPUTF8 support if the message
84 /*	contains an UTF8 message header, sender, or recipient.
85 /* DIAGNOSTICS
86 /*	Problems and transactions are logged to \fBsyslogd\fR(8).
87 /* SEE ALSO
88 /*	cleanup(8) cleanup service description.
89 /*	cleanup_init(8) cleanup callable interface, initialization
90 /* LICENSE
91 /* .ad
92 /* .fi
93 /*	The Secure Mailer license must be distributed with this software.
94 /* AUTHOR(S)
95 /*	Wietse Venema
96 /*	IBM T.J. Watson Research
97 /*	P.O. Box 704
98 /*	Yorktown Heights, NY 10598, USA
99 /*--*/
100 
101 /* System library. */
102 
103 #include <sys_defs.h>
104 #include <errno.h>
105 
106 /* Utility library. */
107 
108 #include <msg.h>
109 #include <vstring.h>
110 #include <mymalloc.h>
111 
112 /* Global library. */
113 
114 #include <cleanup_user.h>
115 #include <mail_queue.h>
116 #include <mail_proto.h>
117 #include <bounce.h>
118 #include <mail_params.h>
119 #include <mail_stream.h>
120 #include <mail_flow.h>
121 #include <rec_type.h>
122 #include <smtputf8.h>
123 
124 /* Milter library. */
125 
126 #include <milter.h>
127 
128 /* Application-specific. */
129 
130 #include "cleanup.h"
131 
132 /* cleanup_open - open queue file and initialize */
133 
134 CLEANUP_STATE *cleanup_open(VSTREAM *src)
135 {
136     CLEANUP_STATE *state;
137     static const char *log_queues[] = {
138 	MAIL_QUEUE_DEFER,
139 	MAIL_QUEUE_BOUNCE,
140 	MAIL_QUEUE_TRACE,
141 	0,
142     };
143     const char **cpp;
144 
145     /*
146      * Initialize private state.
147      */
148     state = cleanup_state_alloc(src);
149 
150     /*
151      * Open the queue file. Save the queue file name in a global variable, so
152      * that the runtime error handler can clean up in case of problems.
153      *
154      * XXX For now, a lot of detail is frozen that could be more useful if it
155      * were made configurable.
156      */
157     state->queue_name = mystrdup(MAIL_QUEUE_INCOMING);
158     state->handle = mail_stream_file(state->queue_name,
159 				   MAIL_CLASS_PUBLIC, var_queue_service, 0);
160     state->dst = state->handle->stream;
161     cleanup_path = mystrdup(VSTREAM_PATH(state->dst));
162     state->queue_id = mystrdup(state->handle->id);
163     if (msg_verbose)
164 	msg_info("cleanup_open: open %s", cleanup_path);
165 
166     /*
167      * If there is a time to get rid of spurious log files, this is it. The
168      * down side is that this costs performance for every message, while the
169      * probability of spurious log files is quite low.
170      *
171      * XXX The defer logfile is deleted when the message is moved into the
172      * active queue. We must also remove it now, otherwise mailq produces
173      * nonsense.
174      */
175     for (cpp = log_queues; *cpp; cpp++) {
176 	if (mail_queue_remove(*cpp, state->queue_id) == 0)
177 	    msg_warn("%s: removed spurious %s log", *cpp, state->queue_id);
178 	else if (errno != ENOENT)
179 	    msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id);
180     }
181     return (state);
182 }
183 
184 /* cleanup_control - process client options */
185 
186 void    cleanup_control(CLEANUP_STATE *state, int flags)
187 {
188 
189     /*
190      * If the client requests us to do the bouncing in case of problems,
191      * throw away the input only in case of real show-stopper errors, such as
192      * unrecognizable data (which should never happen) or insufficient space
193      * for the queue file (which will happen occasionally). Otherwise,
194      * discard input after any lethal error. See the CLEANUP_OUT_OK() macro
195      * definition.
196      */
197     if (msg_verbose)
198 	msg_info("cleanup flags = %s", cleanup_strflags(flags));
199     if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
200 	state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE;
201     } else {
202 	state->err_mask = ~0;
203     }
204     if (state->flags & CLEANUP_FLAG_SMTPUTF8)
205 	state->smtputf8 = SMTPUTF8_FLAG_REQUESTED;
206 }
207 
208 /* cleanup_flush - finish queue file */
209 
210 int     cleanup_flush(CLEANUP_STATE *state)
211 {
212     int     status;
213     char   *junk;
214     VSTRING *trace_junk;
215 
216     /*
217      * Raise these errors only if we examined all queue file records.
218      */
219     if (CLEANUP_OUT_OK(state)) {
220 	if (state->recip == 0)
221 	    state->errs |= CLEANUP_STAT_RCPT;
222 	if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0)
223 	    state->errs |= CLEANUP_STAT_BAD;
224     }
225 
226     /*
227      * Status sanitization. Always report success when the discard flag was
228      * raised by some user-specified access rule.
229      */
230     if (state->flags & CLEANUP_FLAG_DISCARD)
231 	state->errs = 0;
232 
233     /*
234      * Apply external mail filter.
235      *
236      * XXX Include test for a built-in action to tempfail this message.
237      */
238     if (CLEANUP_MILTER_OK(state)) {
239 	if (state->milters)
240 	    cleanup_milter_inspect(state, state->milters);
241 	else if (cleanup_milters) {
242 	    cleanup_milter_emul_data(state, cleanup_milters);
243 	    if (CLEANUP_MILTER_OK(state))
244 		cleanup_milter_inspect(state, cleanup_milters);
245 	}
246     }
247 
248     /*
249      * Update the preliminary message size and count fields with the actual
250      * values.
251      */
252     if (CLEANUP_OUT_OK(state))
253 	cleanup_final(state);
254 
255     /*
256      * If there was an error that requires us to generate a bounce message
257      * (mail submitted with the Postfix sendmail command, mail forwarded by
258      * the local(8) delivery agent, or mail re-queued with "postsuper -r"),
259      * send a bounce notification, reset the error flags in case of success,
260      * and request deletion of the the incoming queue file and of the
261      * optional DSN SUCCESS records from virtual alias expansion.
262      *
263      * XXX It would make no sense to knowingly report success after we already
264      * have bounced all recipients, especially because the information in the
265      * DSN SUCCESS notice is completely redundant compared to the information
266      * in the bounce notice (however, both may be incomplete when the queue
267      * file size would exceed the safety limit).
268      *
269      * An alternative is to keep the DSN SUCCESS records and to delegate bounce
270      * notification to the queue manager, just like we already delegate
271      * success notification. This requires that we leave the undeliverable
272      * message in the incoming queue; versions up to 20050726 did exactly
273      * that. Unfortunately, this broke with over-size queue files, because
274      * the queue manager cannot handle incomplete queue files (and it should
275      * not try to do so).
276      */
277 #define CAN_BOUNCE() \
278 	((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
279 	    && state->sender != 0 \
280 	    && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
281 
282     if (state->errs != 0 && CAN_BOUNCE())
283 	cleanup_bounce(state);
284 
285     /*
286      * Optionally, place the message on hold, but only if the message was
287      * received successfully and only if it's not being discarded for other
288      * reasons. This involves renaming the queue file before "finishing" it
289      * (or else the queue manager would grab it too early) and updating our
290      * own idea of the queue file name for error recovery and for error
291      * reporting purposes.
292      *
293      * XXX Include test for a built-in action to tempfail this message.
294      */
295     if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) {
296 	if ((state->flags & CLEANUP_FLAG_HOLD) != 0
297 #ifdef DELAY_ACTION
298 	    || state->defer_delay > 0
299 #endif
300 	    ) {
301 	    myfree(state->queue_name);
302 #ifdef DELAY_ACTION
303 	    state->queue_name = mystrdup((state->flags & CLEANUP_FLAG_HOLD) ?
304 				     MAIL_QUEUE_HOLD : MAIL_QUEUE_DEFERRED);
305 #else
306 	    state->queue_name = mystrdup(MAIL_QUEUE_HOLD);
307 #endif
308 	    mail_stream_ctl(state->handle,
309 			    CA_MAIL_STREAM_CTL_QUEUE(state->queue_name),
310 			    CA_MAIL_STREAM_CTL_CLASS((char *) 0),
311 			    CA_MAIL_STREAM_CTL_SERVICE((char *) 0),
312 #ifdef DELAY_ACTION
313 			    CA_MAIL_STREAM_CTL_DELAY(state->defer_delay),
314 #endif
315 			    CA_MAIL_STREAM_CTL_END);
316 	    junk = cleanup_path;
317 	    cleanup_path = mystrdup(VSTREAM_PATH(state->handle->stream));
318 	    myfree(junk);
319 
320 	    /*
321 	     * XXX: When delivering to a non-incoming queue, do not consume
322 	     * in_flow tokens. Unfortunately we can't move the code that
323 	     * consumes tokens until after the mail is received, because that
324 	     * would increase the risk of duplicate deliveries (RFC 1047).
325 	     */
326 	    (void) mail_flow_put(1);
327 	}
328 	state->errs = mail_stream_finish(state->handle, (VSTRING *) 0);
329     } else {
330 
331 	/*
332 	 * XXX: When discarding mail, should we consume in_flow tokens? See
333 	 * also the comments above for mail that is placed on hold.
334 	 */
335 #if 0
336 	(void) mail_flow_put(1);
337 #endif
338 	mail_stream_cleanup(state->handle);
339     }
340     state->handle = 0;
341     state->dst = 0;
342 
343     /*
344      * If there was an error, or if the message must be discarded for other
345      * reasons, remove the queue file and the optional trace file with DSN
346      * SUCCESS records from virtual alias expansion.
347      */
348     if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) {
349 	if (cleanup_trace_path)
350 	    (void) REMOVE(vstring_str(cleanup_trace_path));
351 	if (REMOVE(cleanup_path))
352 	    msg_warn("remove %s: %m", cleanup_path);
353     }
354 
355     /*
356      * Make sure that our queue file will not be deleted by the error handler
357      * AFTER we have taken responsibility for delivery. Better to deliver
358      * twice than to lose mail.
359      */
360     trace_junk = cleanup_trace_path;
361     cleanup_trace_path = 0;			/* don't delete upon error */
362     junk = cleanup_path;
363     cleanup_path = 0;				/* don't delete upon error */
364 
365     if (trace_junk)
366 	vstring_free(trace_junk);
367     myfree(junk);
368 
369     /*
370      * Cleanup internal state. This is simply complementary to the
371      * initializations at the beginning of cleanup_open().
372      */
373     if (msg_verbose)
374 	msg_info("cleanup_flush: status %d", state->errs);
375     status = state->errs;
376     return (status);
377 }
378 
379 /* cleanup_free - pay the last respects */
380 
381 void    cleanup_free(CLEANUP_STATE *state)
382 {
383 
384     /*
385      * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after
386      * we have started.
387      */
388     if (cleanup_milters != 0 && state->milters == 0)
389 	milter_disc_event(cleanup_milters);
390     cleanup_state_free(state);
391 }
392