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