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