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