xref: /netbsd-src/external/ibm-public/postfix/dist/src/cleanup/cleanup_envelope.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
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 
87 static void cleanup_envelope_process(CLEANUP_STATE *, int, const char *, ssize_t);
88 
89 /* cleanup_envelope - initialize message envelope */
90 
cleanup_envelope(CLEANUP_STATE * state,int type,const char * str,ssize_t len)91 void    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 
cleanup_envelope_process(CLEANUP_STATE * state,int type,const char * buf,ssize_t len)118 static 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