1 /* $NetBSD: cleanup_message.c,v 1.1.1.2 2011/03/02 19:32:09 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* cleanup_message 3 6 /* SUMMARY 7 /* process message segment 8 /* SYNOPSIS 9 /* #include "cleanup.h" 10 /* 11 /* void cleanup_message(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 message content records and copies the 18 /* result to the queue file. It validates the input, rewrites 19 /* sender/recipient addresses to canonical form, inserts missing 20 /* message headers, and extracts information from message headers 21 /* to be used later when generating the extracted output segment. 22 /* This routine absorbs but does not emit the content to extracted 23 /* boundary record. 24 /* 25 /* Arguments: 26 /* .IP state 27 /* Queue file and message processing state. This state is updated 28 /* as records are processed and as errors happen. 29 /* .IP type 30 /* Record type. 31 /* .IP buf 32 /* Record content. 33 /* .IP len 34 /* Record content length. 35 /* LICENSE 36 /* .ad 37 /* .fi 38 /* The Secure Mailer license must be distributed with this software. 39 /* AUTHOR(S) 40 /* Wietse Venema 41 /* IBM T.J. Watson Research 42 /* P.O. Box 704 43 /* Yorktown Heights, NY 10598, USA 44 /*--*/ 45 46 /* System library. */ 47 48 #include <sys_defs.h> 49 #include <ctype.h> 50 #include <string.h> 51 #include <time.h> 52 #include <unistd.h> 53 54 #ifdef STRCASECMP_IN_STRINGS_H 55 #include <strings.h> 56 #endif 57 58 /* Utility library. */ 59 60 #include <msg.h> 61 #include <vstring.h> 62 #include <vstream.h> 63 #include <argv.h> 64 #include <split_at.h> 65 #include <mymalloc.h> 66 #include <stringops.h> 67 #include <nvtable.h> 68 69 /* Global library. */ 70 71 #include <record.h> 72 #include <rec_type.h> 73 #include <cleanup_user.h> 74 #include <tok822.h> 75 #include <header_opts.h> 76 #include <quote_822_local.h> 77 #include <mail_params.h> 78 #include <mail_date.h> 79 #include <mail_addr.h> 80 #include <is_header.h> 81 #include <ext_prop.h> 82 #include <mail_proto.h> 83 #include <mime_state.h> 84 #include <lex_822.h> 85 #include <dsn_util.h> 86 #include <conv_time.h> 87 88 /* Application-specific. */ 89 90 #include "cleanup.h" 91 92 /* cleanup_fold_header - wrap address list header */ 93 94 static void cleanup_fold_header(CLEANUP_STATE *state, VSTRING *header_buf) 95 { 96 char *start_line = vstring_str(header_buf); 97 char *end_line; 98 char *next_line; 99 char *line; 100 101 /* 102 * A rewritten address list contains one address per line. The code below 103 * replaces newlines by spaces, to fit as many addresses on a line as 104 * possible (without rearranging the order of addresses). Prepending 105 * white space to the beginning of lines is delegated to the output 106 * routine. 107 */ 108 for (line = start_line; line != 0; line = next_line) { 109 end_line = line + strcspn(line, "\n"); 110 if (line > start_line) { 111 if (end_line - start_line < 70) { /* TAB counts as one */ 112 line[-1] = ' '; 113 } else { 114 start_line = line; 115 } 116 } 117 next_line = *end_line ? end_line + 1 : 0; 118 } 119 cleanup_out_header(state, header_buf); 120 } 121 122 /* cleanup_extract_internal - save unquoted copy of extracted address */ 123 124 static char *cleanup_extract_internal(VSTRING *buffer, TOK822 *addr) 125 { 126 127 /* 128 * A little routine to stash away a copy of an address that we extracted 129 * from a message header line. 130 */ 131 tok822_internalize(buffer, addr->head, TOK822_STR_DEFL); 132 return (mystrdup(vstring_str(buffer))); 133 } 134 135 /* cleanup_rewrite_sender - sender address rewriting */ 136 137 static void cleanup_rewrite_sender(CLEANUP_STATE *state, 138 const HEADER_OPTS *hdr_opts, 139 VSTRING *header_buf) 140 { 141 TOK822 *tree; 142 TOK822 **addr_list; 143 TOK822 **tpp; 144 int did_rewrite = 0; 145 146 if (msg_verbose) 147 msg_info("rewrite_sender: %s", hdr_opts->name); 148 149 /* 150 * Parse the header line, rewrite each address found, and regenerate the 151 * header line. Finally, pipe the result through the header line folding 152 * routine. 153 */ 154 tree = tok822_parse_limit(vstring_str(header_buf) 155 + strlen(hdr_opts->name) + 1, 156 var_token_limit); 157 addr_list = tok822_grep(tree, TOK822_ADDR); 158 for (tpp = addr_list; *tpp; tpp++) { 159 did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp); 160 if (state->flags & CLEANUP_FLAG_MAP_OK) { 161 if (cleanup_send_canon_maps 162 && (cleanup_send_canon_flags & CLEANUP_CANON_FLAG_HDR_FROM)) 163 did_rewrite |= 164 cleanup_map11_tree(state, *tpp, cleanup_send_canon_maps, 165 cleanup_ext_prop_mask & EXT_PROP_CANONICAL); 166 if (cleanup_comm_canon_maps 167 && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_FROM)) 168 did_rewrite |= 169 cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps, 170 cleanup_ext_prop_mask & EXT_PROP_CANONICAL); 171 if (cleanup_masq_domains 172 && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_FROM)) 173 did_rewrite |= 174 cleanup_masquerade_tree(*tpp, cleanup_masq_domains); 175 } 176 } 177 if (did_rewrite) { 178 vstring_truncate(header_buf, strlen(hdr_opts->name)); 179 vstring_strcat(header_buf, ": "); 180 tok822_externalize(header_buf, tree, TOK822_STR_HEAD); 181 } 182 myfree((char *) addr_list); 183 tok822_free_tree(tree); 184 if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { 185 if (did_rewrite) 186 cleanup_fold_header(state, header_buf); 187 else 188 cleanup_out_header(state, header_buf); 189 } 190 } 191 192 /* cleanup_rewrite_recip - recipient address rewriting */ 193 194 static void cleanup_rewrite_recip(CLEANUP_STATE *state, 195 const HEADER_OPTS *hdr_opts, 196 VSTRING *header_buf) 197 { 198 TOK822 *tree; 199 TOK822 **addr_list; 200 TOK822 **tpp; 201 int did_rewrite = 0; 202 203 if (msg_verbose) 204 msg_info("rewrite_recip: %s", hdr_opts->name); 205 206 /* 207 * Parse the header line, rewrite each address found, and regenerate the 208 * header line. Finally, pipe the result through the header line folding 209 * routine. 210 */ 211 tree = tok822_parse_limit(vstring_str(header_buf) 212 + strlen(hdr_opts->name) + 1, 213 var_token_limit); 214 addr_list = tok822_grep(tree, TOK822_ADDR); 215 for (tpp = addr_list; *tpp; tpp++) { 216 did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp); 217 if (state->flags & CLEANUP_FLAG_MAP_OK) { 218 if (cleanup_rcpt_canon_maps 219 && (cleanup_rcpt_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT)) 220 did_rewrite |= 221 cleanup_map11_tree(state, *tpp, cleanup_rcpt_canon_maps, 222 cleanup_ext_prop_mask & EXT_PROP_CANONICAL); 223 if (cleanup_comm_canon_maps 224 && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT)) 225 did_rewrite |= 226 cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps, 227 cleanup_ext_prop_mask & EXT_PROP_CANONICAL); 228 if (cleanup_masq_domains 229 && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_RCPT)) 230 did_rewrite |= 231 cleanup_masquerade_tree(*tpp, cleanup_masq_domains); 232 } 233 } 234 if (did_rewrite) { 235 vstring_truncate(header_buf, strlen(hdr_opts->name)); 236 vstring_strcat(header_buf, ": "); 237 tok822_externalize(header_buf, tree, TOK822_STR_HEAD); 238 } 239 myfree((char *) addr_list); 240 tok822_free_tree(tree); 241 if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { 242 if (did_rewrite) 243 cleanup_fold_header(state, header_buf); 244 else 245 cleanup_out_header(state, header_buf); 246 } 247 } 248 249 /* cleanup_act_log - log action with context */ 250 251 static void cleanup_act_log(CLEANUP_STATE *state, 252 const char *action, const char *class, 253 const char *content, const char *text) 254 { 255 const char *attr; 256 257 if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0) 258 attr = "unknown"; 259 vstring_sprintf(state->temp1, "%s: %s: %s %.200s from %s;", 260 state->queue_id, action, class, content, attr); 261 if (state->sender) 262 vstring_sprintf_append(state->temp1, " from=<%s>", state->sender); 263 if (state->recip) 264 vstring_sprintf_append(state->temp1, " to=<%s>", state->recip); 265 if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0) 266 vstring_sprintf_append(state->temp1, " proto=%s", attr); 267 if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0) 268 vstring_sprintf_append(state->temp1, " helo=<%s>", attr); 269 if (text && *text) 270 vstring_sprintf_append(state->temp1, ": %s", text); 271 msg_info("%s", vstring_str(state->temp1)); 272 } 273 274 #define CLEANUP_ACT_CTXT_HEADER "header" 275 #define CLEANUP_ACT_CTXT_BODY "body" 276 #define CLEANUP_ACT_CTXT_ANY "content" 277 278 /* cleanup_act - act upon a header/body match */ 279 280 static const char *cleanup_act(CLEANUP_STATE *state, char *context, 281 const char *buf, const char *value, 282 const char *map_class) 283 { 284 const char *optional_text = value + strcspn(value, " \t"); 285 int command_len = optional_text - value; 286 287 #ifdef DELAY_ACTION 288 int defer_delay; 289 290 #endif 291 292 while (*optional_text && ISSPACE(*optional_text)) 293 optional_text++; 294 295 #define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0) 296 #define CLEANUP_ACT_DROP 0 297 298 /* 299 * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason 300 * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates 301 * queue record processing, and prevents bounces from being sent. 302 */ 303 if (STREQUAL(value, "REJECT", command_len)) { 304 const CLEANUP_STAT_DETAIL *detail; 305 306 if (state->reason) 307 myfree(state->reason); 308 if (*optional_text) { 309 state->reason = dsn_prepend("5.7.1", optional_text); 310 if (*state->reason != '4' && *state->reason != '5') { 311 msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x", 312 optional_text); 313 *state->reason = '4'; 314 } 315 } else { 316 detail = cleanup_stat_detail(CLEANUP_STAT_CONT); 317 state->reason = dsn_prepend(detail->dsn, detail->text); 318 } 319 if (*state->reason == '4') 320 state->errs |= CLEANUP_STAT_DEFER; 321 else 322 state->errs |= CLEANUP_STAT_CONT; 323 state->flags &= ~CLEANUP_FLAG_FILTER_ALL; 324 cleanup_act_log(state, "reject", context, buf, state->reason); 325 return (buf); 326 } 327 if (STREQUAL(value, "WARN", command_len)) { 328 cleanup_act_log(state, "warning", context, buf, optional_text); 329 return (buf); 330 } 331 if (STREQUAL(value, "INFO", command_len)) { 332 cleanup_act_log(state, "info", context, buf, optional_text); 333 return (buf); 334 } 335 if (STREQUAL(value, "FILTER", command_len)) { 336 if (*optional_text == 0) { 337 msg_warn("missing FILTER command argument in %s map", map_class); 338 } else if (strchr(optional_text, ':') == 0) { 339 msg_warn("bad FILTER command %s in %s -- " 340 "need transport:destination", 341 optional_text, map_class); 342 } else { 343 if (state->filter) 344 myfree(state->filter); 345 state->filter = mystrdup(optional_text); 346 cleanup_act_log(state, "filter", context, buf, optional_text); 347 } 348 return (buf); 349 } 350 if (STREQUAL(value, "DISCARD", command_len)) { 351 cleanup_act_log(state, "discard", context, buf, optional_text); 352 state->flags |= CLEANUP_FLAG_DISCARD; 353 state->flags &= ~CLEANUP_FLAG_FILTER_ALL; 354 return (buf); 355 } 356 if (STREQUAL(value, "HOLD", command_len)) { 357 if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) { 358 cleanup_act_log(state, "hold", context, buf, optional_text); 359 state->flags |= CLEANUP_FLAG_HOLD; 360 } 361 return (buf); 362 } 363 364 /* 365 * The DELAY feature is disabled because it has too many problems. 1) It 366 * does not work on some remote file systems; 2) mail will be delivered 367 * anyway with "sendmail -q" etc.; 3) while the mail is queued it bogs 368 * down the deferred queue scan with huge amounts of useless disk I/O 369 * operations. 370 */ 371 #ifdef DELAY_ACTION 372 if (STREQUAL(value, "DELAY", command_len)) { 373 if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) { 374 if (*optional_text == 0) { 375 msg_warn("missing DELAY argument in %s map", map_class); 376 } else if (conv_time(optional_text, &defer_delay, 's') == 0) { 377 msg_warn("ignoring bad DELAY argument %s in %s map", 378 optional_text, map_class); 379 } else { 380 cleanup_act_log(state, "delay", context, buf, optional_text); 381 state->defer_delay = defer_delay; 382 } 383 } 384 return (buf); 385 } 386 #endif 387 if (STREQUAL(value, "PREPEND", command_len)) { 388 if (*optional_text == 0) { 389 msg_warn("PREPEND action without text in %s map", map_class); 390 } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0 391 && !is_header(optional_text)) { 392 msg_warn("bad PREPEND header text \"%s\" in %s map -- " 393 "need \"headername: headervalue\"", 394 optional_text, map_class); 395 } else { 396 cleanup_act_log(state, "prepend", context, buf, optional_text); 397 cleanup_out_string(state, REC_TYPE_NORM, optional_text); 398 } 399 return (buf); 400 } 401 if (STREQUAL(value, "REPLACE", command_len)) { 402 if (*optional_text == 0) { 403 msg_warn("REPLACE action without text in %s map", map_class); 404 return (buf); 405 } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0 406 && !is_header(optional_text)) { 407 msg_warn("bad REPLACE header text \"%s\" in %s map -- " 408 "need \"headername: headervalue\"", 409 optional_text, map_class); 410 return (buf); 411 } else { 412 cleanup_act_log(state, "replace", context, buf, optional_text); 413 return (mystrdup(optional_text)); 414 } 415 } 416 if (STREQUAL(value, "REDIRECT", command_len)) { 417 if (strchr(optional_text, '@') == 0) { 418 msg_warn("bad REDIRECT target \"%s\" in %s map -- " 419 "need user@domain", 420 optional_text, map_class); 421 } else { 422 if (state->redirect) 423 myfree(state->redirect); 424 state->redirect = mystrdup(optional_text); 425 cleanup_act_log(state, "redirect", context, buf, optional_text); 426 state->flags &= ~CLEANUP_FLAG_FILTER_ALL; 427 } 428 return (buf); 429 } 430 /* Allow and ignore optional text after the action. */ 431 432 if (STREQUAL(value, "IGNORE", command_len)) 433 return (CLEANUP_ACT_DROP); 434 435 if (STREQUAL(value, "DUNNO", command_len)) /* preferred */ 436 return (buf); 437 438 if (STREQUAL(value, "OK", command_len)) /* compat */ 439 return (buf); 440 441 msg_warn("unknown command in %s map: %s", map_class, value); 442 return (buf); 443 } 444 445 /* cleanup_header_callback - process one complete header line */ 446 447 static void cleanup_header_callback(void *context, int header_class, 448 const HEADER_OPTS *hdr_opts, 449 VSTRING *header_buf, 450 off_t unused_offset) 451 { 452 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 453 const char *myname = "cleanup_header_callback"; 454 char *hdrval; 455 struct code_map { 456 const char *name; 457 const char *encoding; 458 }; 459 static struct code_map code_map[] = { /* RFC 2045 */ 460 "7bit", MAIL_ATTR_ENC_7BIT, 461 "8bit", MAIL_ATTR_ENC_8BIT, 462 "binary", MAIL_ATTR_ENC_8BIT, /* XXX Violation */ 463 "quoted-printable", MAIL_ATTR_ENC_7BIT, 464 "base64", MAIL_ATTR_ENC_7BIT, 465 0, 466 }; 467 struct code_map *cmp; 468 MAPS *checks; 469 const char *map_class; 470 471 if (msg_verbose) 472 msg_info("%s: '%.200s'", myname, vstring_str(header_buf)); 473 474 /* 475 * Crude header filtering. This stops malware that isn't sophisticated 476 * enough to use fancy header encodings. 477 */ 478 #define CHECK(class, maps, var_name) \ 479 (header_class == class && (map_class = var_name, checks = maps) != 0) 480 481 if (hdr_opts && (hdr_opts->flags & HDR_OPT_MIME)) 482 header_class = MIME_HDR_MULTIPART; 483 484 if ((state->flags & CLEANUP_FLAG_FILTER) 485 && (CHECK(MIME_HDR_PRIMARY, cleanup_header_checks, VAR_HEADER_CHECKS) 486 || CHECK(MIME_HDR_MULTIPART, cleanup_mimehdr_checks, VAR_MIMEHDR_CHECKS) 487 || CHECK(MIME_HDR_NESTED, cleanup_nesthdr_checks, VAR_NESTHDR_CHECKS))) { 488 char *header = vstring_str(header_buf); 489 const char *value; 490 491 if ((value = maps_find(checks, header, 0)) != 0) { 492 const char *result; 493 494 if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_HEADER, 495 header, value, map_class)) 496 == CLEANUP_ACT_DROP) { 497 return; 498 } else if (result != header) { 499 vstring_strcpy(header_buf, result); 500 hdr_opts = header_opts_find(result); 501 myfree((char *) result); 502 } 503 } 504 } 505 506 /* 507 * If this is an "unknown" header, just copy it to the output without 508 * even bothering to fold long lines. cleanup_out() will split long 509 * headers that do not fit a REC_TYPE_NORM record. 510 */ 511 if (hdr_opts == 0) { 512 cleanup_out_header(state, header_buf); 513 return; 514 } 515 516 /* 517 * Allow 8-bit type info to override 7-bit type info. XXX Should reuse 518 * the effort that went into MIME header parsing. 519 */ 520 hdrval = vstring_str(header_buf) + strlen(hdr_opts->name) + 1; 521 while (ISSPACE(*hdrval)) 522 hdrval++; 523 /* trimblanks(hdrval, 0)[0] = 0; */ 524 if (var_auto_8bit_enc_hdr 525 && hdr_opts->type == HDR_CONTENT_TRANSFER_ENCODING) { 526 for (cmp = code_map; cmp->name != 0; cmp++) { 527 if (strcasecmp(hdrval, cmp->name) == 0) { 528 if (strcasecmp(cmp->encoding, MAIL_ATTR_ENC_8BIT) == 0) 529 nvtable_update(state->attr, MAIL_ATTR_ENCODING, 530 cmp->encoding); 531 break; 532 } 533 } 534 } 535 536 /* 537 * Copy attachment etc. header blocks without further inspection. 538 */ 539 if (header_class != MIME_HDR_PRIMARY) { 540 cleanup_out_header(state, header_buf); 541 return; 542 } 543 544 /* 545 * Known header. Remember that we have seen at least one. Find out what 546 * we should do with this header: delete, count, rewrite. Note that we 547 * should examine headers even when they will be deleted from the output, 548 * because the addresses in those headers might be needed elsewhere. 549 * 550 * XXX 2821: Return-path breakage. 551 * 552 * RFC 821 specifies: When the receiver-SMTP makes the "final delivery" of a 553 * message it inserts at the beginning of the mail data a return path 554 * line. The return path line preserves the information in the 555 * <reverse-path> from the MAIL command. Here, final delivery means the 556 * message leaves the SMTP world. Normally, this would mean it has been 557 * delivered to the destination user, but in some cases it may be further 558 * processed and transmitted by another mail system. 559 * 560 * And that is what Postfix implements. Delivery agents prepend 561 * Return-Path:. In order to avoid cluttering up the message with 562 * possibly inconsistent Return-Path: information (the sender can change 563 * as the result of mail forwarding or mailing list delivery), Postfix 564 * removes any existing Return-Path: headers. 565 * 566 * RFC 2821 Section 4.4 specifies: A message-originating SMTP system 567 * SHOULD NOT send a message that already contains a Return-path header. 568 * SMTP servers performing a relay function MUST NOT inspect the message 569 * data, and especially not to the extent needed to determine if 570 * Return-path headers are present. SMTP servers making final delivery 571 * MAY remove Return-path headers before adding their own. 572 */ 573 else { 574 state->headers_seen |= (1 << hdr_opts->type); 575 if (hdr_opts->type == HDR_MESSAGE_ID) 576 msg_info("%s: message-id=%s", state->queue_id, hdrval); 577 if (hdr_opts->type == HDR_RESENT_MESSAGE_ID) 578 msg_info("%s: resent-message-id=%s", state->queue_id, hdrval); 579 if (hdr_opts->type == HDR_RECEIVED) 580 if (++state->hop_count >= var_hopcount_limit) 581 state->errs |= CLEANUP_STAT_HOPS; 582 if (CLEANUP_OUT_OK(state)) { 583 if (hdr_opts->flags & HDR_OPT_RR) 584 state->resent = "Resent-"; 585 if ((hdr_opts->flags & HDR_OPT_SENDER) 586 && state->hdr_rewrite_context) { 587 cleanup_rewrite_sender(state, hdr_opts, header_buf); 588 } else if ((hdr_opts->flags & HDR_OPT_RECIP) 589 && state->hdr_rewrite_context) { 590 cleanup_rewrite_recip(state, hdr_opts, header_buf); 591 } else if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { 592 cleanup_out_header(state, header_buf); 593 } 594 } 595 } 596 } 597 598 /* cleanup_header_done_callback - insert missing message headers */ 599 600 static void cleanup_header_done_callback(void *context) 601 { 602 const char *myname = "cleanup_header_done_callback"; 603 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 604 char time_stamp[1024]; /* XXX locale dependent? */ 605 struct tm *tp; 606 TOK822 *token; 607 time_t tv; 608 609 /* 610 * XXX Workaround: when we reach the end of headers, mime_state_update() 611 * may execute up to three call-backs before returning to the caller: 612 * head_out(), head_end(), and body_out() or body_end(). As long as 613 * call-backs don't return a result, each call-back has to check for 614 * itself if the previous call-back experienced a problem. 615 */ 616 if (CLEANUP_OUT_OK(state) == 0) 617 return; 618 619 /* 620 * Add a missing (Resent-)Message-Id: header. The message ID gives the 621 * time in GMT units, plus the local queue ID. 622 * 623 * XXX Message-Id is not a required message header (RFC 822 and RFC 2822). 624 * 625 * XXX It is the queue ID non-inode bits that prevent messages from getting 626 * the same Message-Id within the same second. 627 * 628 * XXX An arbitrary amount of time may pass between the start of the mail 629 * transaction and the creation of a queue file. Since we guarantee queue 630 * ID uniqueness only within a second, we must ensure that the time in 631 * the message ID matches the queue ID creation time, as long as we use 632 * the queue ID in the message ID. 633 * 634 * XXX We log a dummy name=value record so that we (hopefully) don't break 635 * compatibility with existing logfile analyzers, and so that we don't 636 * complicate future code that wants to log more name=value attributes. 637 */ 638 if ((state->hdr_rewrite_context || var_always_add_hdrs) 639 && (state->headers_seen & (1 << (state->resent[0] ? 640 HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) { 641 tv = state->handle->ctime.tv_sec; 642 tp = gmtime(&tv); 643 strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp); 644 cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s.%s@%s>", 645 state->resent, time_stamp, state->queue_id, var_myhostname); 646 msg_info("%s: %smessage-id=<%s.%s@%s>", 647 state->queue_id, *state->resent ? "resent-" : "", 648 time_stamp, state->queue_id, var_myhostname); 649 state->headers_seen |= (1 << (state->resent[0] ? 650 HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID)); 651 } 652 if ((state->headers_seen & (1 << HDR_MESSAGE_ID)) == 0) 653 msg_info("%s: message-id=<>", state->queue_id); 654 655 /* 656 * Add a missing (Resent-)Date: header. The date is in local time units, 657 * with the GMT offset at the end. 658 */ 659 if ((state->hdr_rewrite_context || var_always_add_hdrs) 660 && (state->headers_seen & (1 << (state->resent[0] ? 661 HDR_RESENT_DATE : HDR_DATE))) == 0) { 662 cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s", 663 state->resent, mail_date(state->arrival_time.tv_sec)); 664 } 665 666 /* 667 * Add a missing (Resent-)From: header. 668 */ 669 if ((state->hdr_rewrite_context || var_always_add_hdrs) 670 && (state->headers_seen & (1 << (state->resent[0] ? 671 HDR_RESENT_FROM : HDR_FROM))) == 0) { 672 quote_822_local(state->temp1, *state->sender ? 673 state->sender : MAIL_ADDR_MAIL_DAEMON); 674 vstring_sprintf(state->temp2, "%sFrom: %s", 675 state->resent, vstring_str(state->temp1)); 676 if (*state->sender && state->fullname && *state->fullname) { 677 vstring_sprintf(state->temp1, "(%s)", state->fullname); 678 token = tok822_parse(vstring_str(state->temp1)); 679 vstring_strcat(state->temp2, " "); 680 tok822_externalize(state->temp2, token, TOK822_STR_NONE); 681 tok822_free_tree(token); 682 } 683 CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2); 684 } 685 686 /* 687 * XXX 2821: Appendix B: The return address in the MAIL command SHOULD, 688 * if possible, be derived from the system's identity for the submitting 689 * (local) user, and the "From:" header field otherwise. If there is a 690 * system identity available, it SHOULD also be copied to the Sender 691 * header field if it is different from the address in the From header 692 * field. (Any Sender field that was already there SHOULD be removed.) 693 * Similar wording appears in RFC 2822 section 3.6.2. 694 * 695 * Postfix presently does not insert a Sender: header if envelope and From: 696 * address differ. Older Postfix versions assumed that the envelope 697 * sender address specifies the system identity and inserted Sender: 698 * whenever envelope and From: differed. This was wrong with relayed 699 * mail, and was often not even desirable with original submissions. 700 * 701 * XXX 2822 Section 3.6.2, as well as RFC 822 Section 4.1: FROM headers can 702 * contain multiple addresses. If this is the case, then a Sender: header 703 * must be provided with a single address. 704 * 705 * Postfix does not count the number of addresses in a From: header 706 * (although doing so is trivial, once the address is parsed). 707 */ 708 709 /* 710 * Add a missing destination header. 711 */ 712 #define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \ 713 | (1 << HDR_CC) | (1 << HDR_RESENT_CC)) 714 715 if ((state->hdr_rewrite_context || var_always_add_hdrs) 716 && (state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) { 717 if (!is_header(var_rcpt_witheld)) { 718 msg_warn("bad %s header text \"%s\" -- " 719 "need \"headername: headervalue\"", 720 VAR_RCPT_WITHELD, var_rcpt_witheld); 721 } else { 722 cleanup_out_format(state, REC_TYPE_NORM, "%s", var_rcpt_witheld); 723 } 724 } 725 726 /* 727 * Place a dummy PTR record right after the last header so that we can 728 * append headers without having to worry about clobbering the 729 * end-of-content marker. 730 */ 731 if (state->milters || cleanup_milters) { 732 if ((state->append_hdr_pt_offset = vstream_ftell(state->dst)) < 0) 733 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 734 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 735 if ((state->append_hdr_pt_target = vstream_ftell(state->dst)) < 0) 736 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 737 state->body_offset = state->append_hdr_pt_target; 738 } 739 } 740 741 /* cleanup_body_callback - output one body record */ 742 743 static void cleanup_body_callback(void *context, int type, 744 const char *buf, ssize_t len, 745 off_t offset) 746 { 747 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 748 749 /* 750 * XXX Workaround: when we reach the end of headers, mime_state_update() 751 * may execute up to three call-backs before returning to the caller: 752 * head_out(), head_end(), and body_out() or body_end(). As long as 753 * call-backs don't return a result, each call-back has to check for 754 * itself if the previous call-back experienced a problem. 755 */ 756 if (CLEANUP_OUT_OK(state) == 0) 757 return; 758 759 /* 760 * Crude message body content filter for emergencies. This code has 761 * several problems: it sees one line at a time; it looks at long lines 762 * only in chunks of line_length_limit (2048) characters; it is easily 763 * bypassed with encodings and other tricks. 764 */ 765 if ((state->flags & CLEANUP_FLAG_FILTER) 766 && cleanup_body_checks 767 && (var_body_check_len == 0 || offset < var_body_check_len)) { 768 const char *value; 769 770 if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) { 771 const char *result; 772 773 if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_BODY, 774 buf, value, VAR_BODY_CHECKS)) 775 == CLEANUP_ACT_DROP) { 776 return; 777 } else if (result != buf) { 778 cleanup_out(state, type, result, strlen(result)); 779 myfree((char *) result); 780 return; 781 } 782 } 783 } 784 cleanup_out(state, type, buf, len); 785 } 786 787 /* cleanup_message_headerbody - process message content, header and body */ 788 789 static void cleanup_message_headerbody(CLEANUP_STATE *state, int type, 790 const char *buf, ssize_t len) 791 { 792 const char *myname = "cleanup_message_headerbody"; 793 const MIME_STATE_DETAIL *detail; 794 const char *cp; 795 char *dst; 796 797 /* 798 * Reject unwanted characters. 799 * 800 * XXX Possible optimization: simplify the loop when the "reject" set 801 * contains only one character. 802 */ 803 if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_reject_chars) { 804 for (cp = buf; cp < buf + len; cp++) { 805 if (memchr(vstring_str(cleanup_reject_chars), 806 *(const unsigned char *) cp, 807 VSTRING_LEN(cleanup_reject_chars))) { 808 cleanup_act(state, CLEANUP_ACT_CTXT_ANY, 809 buf, "REJECT disallowed character", 810 "character reject"); 811 return; 812 } 813 } 814 } 815 816 /* 817 * Strip unwanted characters. Don't overwrite the input. 818 * 819 * XXX Possible optimization: simplify the loop when the "strip" set 820 * contains only one character. 821 * 822 * XXX Possible optimization: copy the input only if we really have to. 823 */ 824 if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_strip_chars) { 825 VSTRING_RESET(state->stripped_buf); 826 VSTRING_SPACE(state->stripped_buf, len + 1); 827 dst = vstring_str(state->stripped_buf); 828 for (cp = buf; cp < buf + len; cp++) 829 if (!memchr(vstring_str(cleanup_strip_chars), 830 *(const unsigned char *) cp, 831 VSTRING_LEN(cleanup_strip_chars))) 832 *dst++ = *cp; 833 *dst = 0; 834 buf = vstring_str(state->stripped_buf); 835 len = dst - buf; 836 } 837 838 /* 839 * Copy text record to the output. 840 */ 841 if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) { 842 state->mime_errs = mime_state_update(state->mime_state, type, buf, len); 843 } 844 845 /* 846 * If we have reached the end of the message content segment, record the 847 * current file position so we can compute the message size lateron. 848 */ 849 else if (type == REC_TYPE_XTRA) { 850 state->mime_errs = mime_state_update(state->mime_state, type, buf, len); 851 if (state->milters || cleanup_milters) 852 /* Make room for body modification. */ 853 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 854 /* Ignore header truncation after primary message headers. */ 855 state->mime_errs &= ~MIME_ERR_TRUNC_HEADER; 856 if (state->mime_errs && state->reason == 0) { 857 state->errs |= CLEANUP_STAT_CONT; 858 detail = mime_state_detail(state->mime_errs); 859 state->reason = dsn_prepend(detail->dsn, detail->text); 860 } 861 state->mime_state = mime_state_free(state->mime_state); 862 if ((state->xtra_offset = vstream_ftell(state->dst)) < 0) 863 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 864 state->cont_length = state->xtra_offset - state->data_offset; 865 state->action = cleanup_extracted; 866 } 867 868 /* 869 * This should never happen. 870 */ 871 else { 872 msg_warn("%s: message rejected: " 873 "unexpected record type %d in message content", myname, type); 874 state->errs |= CLEANUP_STAT_BAD; 875 } 876 } 877 878 /* cleanup_mime_error_callback - error report call-back routine */ 879 880 static void cleanup_mime_error_callback(void *context, int err_code, 881 const char *text, ssize_t len) 882 { 883 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 884 const char *origin; 885 886 /* 887 * Message header too large errors are handled after the end of the 888 * primary message headers. 889 */ 890 if ((err_code & ~MIME_ERR_TRUNC_HEADER) != 0) { 891 if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0) 892 origin = MAIL_ATTR_ORG_NONE; 893 #define TEXT_LEN (len < 100 ? (int) len : 100) 894 msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>", 895 state->queue_id, mime_state_error(err_code), TEXT_LEN, text, 896 origin, state->sender, state->recip ? state->recip : "unknown"); 897 } 898 } 899 900 /* cleanup_message - initialize message content segment */ 901 902 void cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t len) 903 { 904 const char *myname = "cleanup_message"; 905 int mime_options; 906 907 /* 908 * Write the start-of-content segment marker. 909 */ 910 cleanup_out_string(state, REC_TYPE_MESG, ""); 911 if ((state->data_offset = vstream_ftell(state->dst)) < 0) 912 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 913 914 /* 915 * Set up MIME processing options, if any. MIME_OPT_DISABLE_MIME disables 916 * special processing of Content-Type: headers, and thus, causes all text 917 * after the primary headers to be treated as the message body. 918 */ 919 mime_options = 0; 920 if (var_disable_mime_input) { 921 mime_options |= MIME_OPT_DISABLE_MIME; 922 } else { 923 /* Turn off content checks if bouncing or forwarding mail. */ 924 if (state->flags & CLEANUP_FLAG_FILTER) { 925 if (var_strict_8bitmime || var_strict_7bit_hdrs) 926 mime_options |= MIME_OPT_REPORT_8BIT_IN_HEADER; 927 if (var_strict_8bitmime || var_strict_8bit_body) 928 mime_options |= MIME_OPT_REPORT_8BIT_IN_7BIT_BODY; 929 if (var_strict_encoding) 930 mime_options |= MIME_OPT_REPORT_ENCODING_DOMAIN; 931 if (var_strict_8bitmime || var_strict_7bit_hdrs 932 || var_strict_8bit_body || var_strict_encoding 933 || *var_header_checks || *var_mimehdr_checks 934 || *var_nesthdr_checks) 935 mime_options |= MIME_OPT_REPORT_NESTING; 936 } 937 } 938 state->mime_state = mime_state_alloc(mime_options, 939 cleanup_header_callback, 940 cleanup_header_done_callback, 941 cleanup_body_callback, 942 (MIME_STATE_ANY_END) 0, 943 cleanup_mime_error_callback, 944 (void *) state); 945 946 /* 947 * XXX Workaround: truncate a long message header so that we don't exceed 948 * the default Sendmail libmilter request size limit of 65535. 949 */ 950 #define KLUDGE_HEADER_LIMIT 60000 951 if ((cleanup_milters || state->milters) 952 && var_header_limit > KLUDGE_HEADER_LIMIT) 953 var_header_limit = KLUDGE_HEADER_LIMIT; 954 955 /* 956 * Pass control to the header processing routine. 957 */ 958 state->action = cleanup_message_headerbody; 959 cleanup_message_headerbody(state, type, buf, len); 960 } 961