1/*	$NetBSD: cleanup_envelope.c,v 1.5 2020/03/18 19:05:15 christos Exp $	*/
2
3/*++
4/* NAME
5/*	cleanup_envelope 3
6/* SUMMARY
7/*	process envelope segment
8/* SYNOPSIS
9/*	#include <cleanup.h>
10/*
11/*	void	cleanup_envelope(state, type, buf, len)
12/*	CLEANUP_STATE *state;
13/*	int	type;
14/*	const char *buf;
15/*	ssize_t	len;
16/* DESCRIPTION
17/*	This module processes envelope records and writes the result
18/*	to the queue file.  It validates the message structure, rewrites
19/*	sender/recipient addresses to canonical form, and expands recipients
20/*	according to entries in the virtual table. This routine absorbs but
21/*	does not emit the envelope to content boundary record.
22/*
23/*	Arguments:
24/* .IP state
25/*	Queue file and message processing state. This state is updated
26/*	as records are processed and as errors happen.
27/* .IP type
28/*	Record type.
29/* .IP buf
30/*	Record content.
31/* .IP len
32/*	Record content length.
33/* LICENSE
34/* .ad
35/* .fi
36/*	The Secure Mailer license must be distributed with this software.
37/* AUTHOR(S)
38/*	Wietse Venema
39/*	IBM T.J. Watson Research
40/*	P.O. Box 704
41/*	Yorktown Heights, NY 10598, USA
42/*
43/*	Wietse Venema
44/*	Google, Inc.
45/*	111 8th Avenue
46/*	New York, NY 10011, USA
47/*--*/
48
49/* System library. */
50
51#include <sys_defs.h>
52#include <string.h>
53#include <stdlib.h>
54#include <stdio.h>			/* ssscanf() */
55#include <ctype.h>
56
57/* Utility library. */
58
59#include <msg.h>
60#include <vstring.h>
61#include <vstream.h>
62#include <mymalloc.h>
63#include <stringops.h>
64#include <nvtable.h>
65
66/* Global library. */
67
68#include <record.h>
69#include <rec_type.h>
70#include <cleanup_user.h>
71#include <qmgr_user.h>
72#include <mail_params.h>
73#include <verp_sender.h>
74#include <mail_proto.h>
75#include <dsn_mask.h>
76#include <rec_attr_map.h>
77#include <smtputf8.h>
78#include <deliver_request.h>
79
80/* Application-specific. */
81
82#include "cleanup.h"
83
84#define STR	vstring_str
85#define STREQ(x,y) (strcmp((x), (y)) == 0)
86
87static void cleanup_envelope_process(CLEANUP_STATE *, int, const char *, ssize_t);
88
89/* cleanup_envelope - initialize message envelope */
90
91void    cleanup_envelope(CLEANUP_STATE *state, int type,
92			         const char *str, ssize_t len)
93{
94
95    /*
96     * The message size and count record goes first, so it can easily be
97     * updated in place. This information takes precedence over any size
98     * estimate provided by the client. It's all in one record, data size
99     * first, for backwards compatibility reasons.
100     */
101    cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
102		       (REC_TYPE_SIZE_CAST1) 0,	/* extra offs - content offs */
103		       (REC_TYPE_SIZE_CAST2) 0,	/* content offset */
104		       (REC_TYPE_SIZE_CAST3) 0,	/* recipient count */
105		       (REC_TYPE_SIZE_CAST4) 0,	/* qmgr options */
106		       (REC_TYPE_SIZE_CAST5) 0,	/* content length */
107		       (REC_TYPE_SIZE_CAST6) 0);	/* smtputf8 */
108
109    /*
110     * Pass control to the actual envelope processing routine.
111     */
112    state->action = cleanup_envelope_process;
113    cleanup_envelope_process(state, type, str, len);
114}
115
116/* cleanup_envelope_process - process one envelope record */
117
118static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
119				             const char *buf, ssize_t len)
120{
121    const char *myname = "cleanup_envelope_process";
122    char   *attr_name;
123    char   *attr_value;
124    const char *error_text;
125    int     extra_opts;
126    int     junk;
127    int     mapped_type = type;
128    const char *mapped_buf = buf;
129    int     milter_count;
130
131#ifdef DELAY_ACTION
132    int     defer_delay;
133
134#endif
135
136    if (msg_verbose)
137	msg_info("initial envelope %c %.*s", type, (int) len, buf);
138
139    if (type == REC_TYPE_FLGS) {
140	/* Not part of queue file format. */
141	extra_opts = atoi(buf);
142	if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA)
143	    msg_warn("%s: ignoring bad extra flags: 0x%x",
144		     state->queue_id, extra_opts);
145	else
146	    state->flags |= extra_opts;
147	return;
148    }
149#ifdef DELAY_ACTION
150    if (type == REC_TYPE_DELAY) {
151	/* Not part of queue file format. */
152	defer_delay = atoi(buf);
153	if (defer_delay <= 0)
154	    msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf);
155	else
156	    state->defer_delay = defer_delay;
157	return;
158    }
159#endif
160
161    /*
162     * XXX We instantiate a MILTERS structure even when the filter count is
163     * zero (for example, all filters are in ACCEPT state, or the SMTP server
164     * sends a dummy MILTERS structure without any filters), otherwise the
165     * cleanup server would apply the non_smtpd_milters setting
166     * inappropriately.
167     */
168    if (type == REC_TYPE_MILT_COUNT) {
169	/* Not part of queue file format. */
170	if ((milter_count = atoi(buf)) >= 0)
171	    cleanup_milter_receive(state, milter_count);
172	return;
173    }
174
175    /*
176     * Map DSN attribute name to pseudo record type so that we don't have to
177     * pollute the queue file with records that are incompatible with past
178     * Postfix versions. Preferably, people should be able to back out from
179     * an upgrade without losing mail.
180     */
181    if (type == REC_TYPE_ATTR) {
182	vstring_strcpy(state->attr_buf, buf);
183	error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value);
184	if (error_text != 0) {
185	    msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
186		     state->queue_id, error_text, buf);
187	    state->errs |= CLEANUP_STAT_BAD;
188	    return;
189	}
190	/* Zero-length values are place holders for unavailable values. */
191	if (*attr_value == 0) {
192	    msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
193		     state->queue_id, attr_name);
194	    return;
195	}
196	if ((junk = rec_attr_map(attr_name)) != 0) {
197	    mapped_buf = attr_value;
198	    mapped_type = junk;
199	}
200    }
201
202    /*
203     * Sanity check.
204     */
205    if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
206	msg_warn("%s: message rejected: unexpected record type %d in envelope",
207		 state->queue_id, type);
208	state->errs |= CLEANUP_STAT_BAD;
209	return;
210    }
211
212    /*
213     * Although recipient records appear at the end of the initial or
214     * extracted envelope, the code for processing recipient records is first
215     * because there can be lots of them.
216     *
217     * Recipient records may be mixed with other information (such as FILTER or
218     * REDIRECT actions from SMTPD). In that case the queue manager needs to
219     * examine all queue file records before it can start delivery. This is
220     * not a problem when SMTPD recipient lists are small.
221     *
222     * However, if recipient records are not mixed with other records
223     * (typically, mailing list mail) then we can make an optimization: the
224     * queue manager does not need to examine every envelope record before it
225     * can start deliveries. This can help with very large mailing lists.
226     */
227
228    /*
229     * On the transition from non-recipient records to recipient records,
230     * emit some records and do some sanity checks.
231     *
232     * XXX Moving the envelope sender (and the test for its presence) to the
233     * extracted segment can reduce qmqpd memory requirements because it no
234     * longer needs to read the entire message into main memory.
235     */
236    if ((state->flags & CLEANUP_FLAG_INRCPT) == 0
237	&& strchr(REC_TYPE_ENV_RECIPIENT, type) != 0) {
238	if (state->sender == 0) {
239	    msg_warn("%s: message rejected: missing sender envelope record",
240		     state->queue_id);
241	    state->errs |= CLEANUP_STAT_BAD;
242	    return;
243	}
244	if (state->arrival_time.tv_sec == 0) {
245	    msg_warn("%s: message rejected: missing time envelope record",
246		     state->queue_id);
247	    state->errs |= CLEANUP_STAT_BAD;
248	    return;
249	}
250
251	/*
252	 * XXX This works by accident, because the sender is recorded at the
253	 * beginning of the envelope segment.
254	 */
255	if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0
256	    && state->sender && *state->sender
257	    && var_delay_warn_time > 0) {
258	    cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
259			       REC_TYPE_WARN_ARG(state->arrival_time.tv_sec
260						 + var_delay_warn_time));
261	}
262	state->flags |= CLEANUP_FLAG_INRCPT;
263    }
264
265    /*
266     * Initial envelope recipient record processing.
267     */
268    if (type == REC_TYPE_RCPT) {
269	if (state->sender == 0) {		/* protect showq */
270	    msg_warn("%s: message rejected: envelope recipient precedes sender",
271		     state->queue_id);
272	    state->errs |= CLEANUP_STAT_BAD;
273	    return;
274	}
275	if (state->orig_rcpt == 0)
276	    state->orig_rcpt = mystrdup(buf);
277	cleanup_addr_recipient(state, buf);
278	if (cleanup_milters != 0
279	    && state->milters == 0
280	    && CLEANUP_MILTER_OK(state))
281	    cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip);
282	myfree(state->orig_rcpt);
283	state->orig_rcpt = 0;
284	if (state->dsn_orcpt != 0) {
285	    myfree(state->dsn_orcpt);
286	    state->dsn_orcpt = 0;
287	}
288	state->dsn_notify = 0;
289	return;
290    }
291    if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) {
292	if (state->orig_rcpt != 0) {
293	    myfree(state->orig_rcpt);
294	    state->orig_rcpt = 0;
295	}
296	if (state->dsn_orcpt != 0) {
297	    myfree(state->dsn_orcpt);
298	    state->dsn_orcpt = 0;
299	}
300	state->dsn_notify = 0;
301	return;
302    }
303    if (mapped_type == REC_TYPE_DSN_ORCPT) {
304	if (state->dsn_orcpt) {
305	    msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>",
306		     state->queue_id, state->dsn_orcpt);
307	    myfree(state->dsn_orcpt);
308	}
309	state->dsn_orcpt = mystrdup(mapped_buf);
310	return;
311    }
312    if (mapped_type == REC_TYPE_DSN_NOTIFY) {
313	if (state->dsn_notify) {
314	    msg_warn("%s: ignoring out-of-order DSN notify record <%d>",
315		     state->queue_id, state->dsn_notify);
316	    state->dsn_notify = 0;
317	}
318	if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0
319	    || DSN_NOTIFY_OK(junk) == 0)
320	    msg_warn("%s: ignoring malformed DSN notify record <%.200s>",
321		     state->queue_id, buf);
322	else
323	    state->qmgr_opts |=
324		QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk);
325	return;
326    }
327    if (type == REC_TYPE_ORCP) {
328	if (state->orig_rcpt != 0) {
329	    msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
330		     state->queue_id, state->orig_rcpt);
331	    myfree(state->orig_rcpt);
332	}
333	state->orig_rcpt = mystrdup(buf);
334	return;
335    }
336    if (type == REC_TYPE_MESG) {
337	state->action = cleanup_message;
338	if (state->flags & CLEANUP_FLAG_INRCPT) {
339	    if (state->milters || cleanup_milters) {
340		/* Make room to append recipient. */
341		if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0)
342		    msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
343		cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
344		if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0)
345		    msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
346	    }
347	    state->flags &= ~CLEANUP_FLAG_INRCPT;
348	}
349	return;
350    }
351
352    /*
353     * Initial envelope non-recipient record processing.
354     *
355     * If the message was requeued with "postsuper -r" use their
356     * SMTPUTF8_REQUESTED flag.
357     */
358    if (state->flags & CLEANUP_FLAG_INRCPT)
359	/* Tell qmgr that recipient records are mixed with other information. */
360	state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
361    if (type == REC_TYPE_SIZE) {
362	/* Use our own SIZE record, except for the SMTPUTF8_REQUESTED flag. */
363	(void) sscanf(buf, "%*s $*s %*s %*s %*s %d", &state->smtputf8);
364	state->smtputf8 &= SMTPUTF8_FLAG_REQUESTED;
365	return;
366    }
367    if (mapped_type == REC_TYPE_CTIME)
368	/* Use our own expiration time base record instead. */
369	return;
370    if (type == REC_TYPE_TIME) {
371	/* First instance wins. */
372	if (state->arrival_time.tv_sec == 0) {
373	    REC_TYPE_TIME_SCAN(buf, state->arrival_time);
374	    cleanup_out(state, type, buf, len);
375	}
376	/* Generate our own expiration time base record. */
377	cleanup_out_format(state, REC_TYPE_ATTR, "%s=%ld",
378			   MAIL_ATTR_CREATE_TIME, (long) time((time_t *) 0));
379	return;
380    }
381    if (type == REC_TYPE_FULL) {
382	/* First instance wins. */
383	if (state->fullname == 0) {
384	    state->fullname = mystrdup(buf);
385	    cleanup_out(state, type, buf, len);
386	}
387	return;
388    }
389    if (type == REC_TYPE_FROM) {
390	off_t after_sender_offs;
391
392	/* Allow only one instance. */
393	if (state->sender != 0) {
394	    msg_warn("%s: message rejected: multiple envelope sender records",
395		     state->queue_id);
396	    state->errs |= CLEANUP_STAT_BAD;
397	    return;
398	}
399	if (state->milters || cleanup_milters) {
400	    /* Remember the sender record offset. */
401	    if ((state->sender_pt_offset = vstream_ftell(state->dst)) < 0)
402		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
403	}
404	after_sender_offs = cleanup_addr_sender(state, buf);
405	if (state->milters || cleanup_milters) {
406	    /* Remember the after-sender record offset. */
407	    state->sender_pt_target = after_sender_offs;
408	}
409	if (cleanup_milters != 0
410	    && state->milters == 0
411	    && CLEANUP_MILTER_OK(state))
412	    cleanup_milter_emul_mail(state, cleanup_milters, state->sender);
413	return;
414    }
415    if (mapped_type == REC_TYPE_DSN_ENVID) {
416	/* Don't break "postsuper -r" after Milter overrides ENVID. */
417	if (!allprint(mapped_buf)) {
418	    msg_warn("%s: message rejected: bad DSN envelope ID record",
419		     state->queue_id);
420	    state->errs |= CLEANUP_STAT_BAD;
421	    return;
422	}
423	if (state->dsn_envid != 0)
424	    myfree(state->dsn_envid);
425	state->dsn_envid = mystrdup(mapped_buf);
426	cleanup_out(state, type, buf, len);
427	return;
428    }
429    if (mapped_type == REC_TYPE_DSN_RET) {
430	/* Don't break "postsuper -r" after Milter overrides RET. */
431	if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0
432	    || DSN_RET_OK(junk) == 0) {
433	    msg_warn("%s: message rejected: bad DSN RET record <%.200s>",
434		     state->queue_id, buf);
435	    state->errs |= CLEANUP_STAT_BAD;
436	    return;
437	}
438	state->dsn_ret = junk;
439	cleanup_out(state, type, buf, len);
440	return;
441    }
442    if (type == REC_TYPE_WARN) {
443	/* First instance wins. */
444	if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) {
445	    state->flags |= CLEANUP_FLAG_WARN_SEEN;
446	    cleanup_out(state, type, buf, len);
447	}
448	return;
449    }
450    /* XXX Needed for cleanup_bounce(); sanity check usage. */
451    if (type == REC_TYPE_VERP) {
452	if (state->verp_delims == 0) {
453	    if (state->sender == 0 || state->sender[0] == 0) {
454		msg_warn("%s: ignoring VERP request for null sender",
455			 state->queue_id);
456	    } else if (verp_delims_verify(buf) != 0) {
457		msg_warn("%s: ignoring bad VERP request: \"%.100s\"",
458			 state->queue_id, buf);
459	    } else {
460		state->verp_delims = mystrdup(buf);
461		cleanup_out(state, type, buf, len);
462	    }
463	}
464	return;
465    }
466    if (type == REC_TYPE_ATTR) {
467	if (state->attr->used >= var_qattr_count_limit) {
468	    msg_warn("%s: message rejected: attribute count exceeds limit %d",
469		     state->queue_id, var_qattr_count_limit);
470	    state->errs |= CLEANUP_STAT_BAD;
471	    return;
472	}
473	if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) {
474	    /* Choose header rewriting context. See also cleanup_addr.c. */
475	    if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) {
476		state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL;
477	    } else if (STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)) {
478		state->hdr_rewrite_context =
479		    (*var_remote_rwr_domain ? MAIL_ATTR_RWR_REMOTE : 0);
480	    } else {
481		msg_warn("%s: message rejected: bad rewriting context: %.100s",
482			 state->queue_id, attr_value);
483		state->errs |= CLEANUP_STAT_BAD;
484		return;
485	    }
486	}
487	if (strcmp(attr_name, MAIL_ATTR_TRACE_FLAGS) == 0) {
488	    if (!alldig(attr_value)) {
489		msg_warn("%s: message rejected: bad TFLAG record <%.200s>",
490			 state->queue_id, buf);
491		state->errs |= CLEANUP_STAT_BAD;
492		return;
493	    }
494	    if (state->tflags == 0)
495		state->tflags = DEL_REQ_TRACE_FLAGS(atoi(attr_value));
496	}
497	nvtable_update(state->attr, attr_name, attr_value);
498	cleanup_out(state, type, buf, len);
499	return;
500    } else {
501	cleanup_out(state, type, buf, len);
502	return;
503    }
504}
505