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