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