1 /* $NetBSD: cleanup_message.c,v 1.1.1.3 2013/01/02 18:58:54 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(state, *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(state, *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 } else if (checks->error) { 504 msg_warn("%s: %s map lookup problem -- deferring delivery", 505 state->queue_id, checks->title); 506 state->errs |= CLEANUP_STAT_WRITE; 507 } 508 } 509 510 /* 511 * If this is an "unknown" header, just copy it to the output without 512 * even bothering to fold long lines. cleanup_out() will split long 513 * headers that do not fit a REC_TYPE_NORM record. 514 */ 515 if (hdr_opts == 0) { 516 cleanup_out_header(state, header_buf); 517 return; 518 } 519 520 /* 521 * Allow 8-bit type info to override 7-bit type info. XXX Should reuse 522 * the effort that went into MIME header parsing. 523 */ 524 hdrval = vstring_str(header_buf) + strlen(hdr_opts->name) + 1; 525 while (ISSPACE(*hdrval)) 526 hdrval++; 527 /* trimblanks(hdrval, 0)[0] = 0; */ 528 if (var_auto_8bit_enc_hdr 529 && hdr_opts->type == HDR_CONTENT_TRANSFER_ENCODING) { 530 for (cmp = code_map; cmp->name != 0; cmp++) { 531 if (strcasecmp(hdrval, cmp->name) == 0) { 532 if (strcasecmp(cmp->encoding, MAIL_ATTR_ENC_8BIT) == 0) 533 nvtable_update(state->attr, MAIL_ATTR_ENCODING, 534 cmp->encoding); 535 break; 536 } 537 } 538 } 539 540 /* 541 * Copy attachment etc. header blocks without further inspection. 542 */ 543 if (header_class != MIME_HDR_PRIMARY) { 544 cleanup_out_header(state, header_buf); 545 return; 546 } 547 548 /* 549 * Known header. Remember that we have seen at least one. Find out what 550 * we should do with this header: delete, count, rewrite. Note that we 551 * should examine headers even when they will be deleted from the output, 552 * because the addresses in those headers might be needed elsewhere. 553 * 554 * XXX 2821: Return-path breakage. 555 * 556 * RFC 821 specifies: When the receiver-SMTP makes the "final delivery" of a 557 * message it inserts at the beginning of the mail data a return path 558 * line. The return path line preserves the information in the 559 * <reverse-path> from the MAIL command. Here, final delivery means the 560 * message leaves the SMTP world. Normally, this would mean it has been 561 * delivered to the destination user, but in some cases it may be further 562 * processed and transmitted by another mail system. 563 * 564 * And that is what Postfix implements. Delivery agents prepend 565 * Return-Path:. In order to avoid cluttering up the message with 566 * possibly inconsistent Return-Path: information (the sender can change 567 * as the result of mail forwarding or mailing list delivery), Postfix 568 * removes any existing Return-Path: headers. 569 * 570 * RFC 2821 Section 4.4 specifies: A message-originating SMTP system 571 * SHOULD NOT send a message that already contains a Return-path header. 572 * SMTP servers performing a relay function MUST NOT inspect the message 573 * data, and especially not to the extent needed to determine if 574 * Return-path headers are present. SMTP servers making final delivery 575 * MAY remove Return-path headers before adding their own. 576 */ 577 else { 578 state->headers_seen |= (1 << hdr_opts->type); 579 if (hdr_opts->type == HDR_MESSAGE_ID) 580 msg_info("%s: message-id=%s", state->queue_id, hdrval); 581 if (hdr_opts->type == HDR_RESENT_MESSAGE_ID) 582 msg_info("%s: resent-message-id=%s", state->queue_id, hdrval); 583 if (hdr_opts->type == HDR_RECEIVED) 584 if (++state->hop_count >= var_hopcount_limit) 585 state->errs |= CLEANUP_STAT_HOPS; 586 if (CLEANUP_OUT_OK(state)) { 587 if (hdr_opts->flags & HDR_OPT_RR) 588 state->resent = "Resent-"; 589 if ((hdr_opts->flags & HDR_OPT_SENDER) 590 && state->hdr_rewrite_context) { 591 cleanup_rewrite_sender(state, hdr_opts, header_buf); 592 } else if ((hdr_opts->flags & HDR_OPT_RECIP) 593 && state->hdr_rewrite_context) { 594 cleanup_rewrite_recip(state, hdr_opts, header_buf); 595 } else if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { 596 cleanup_out_header(state, header_buf); 597 } 598 } 599 } 600 } 601 602 /* cleanup_header_done_callback - insert missing message headers */ 603 604 static void cleanup_header_done_callback(void *context) 605 { 606 const char *myname = "cleanup_header_done_callback"; 607 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 608 char time_stamp[1024]; /* XXX locale dependent? */ 609 struct tm *tp; 610 TOK822 *token; 611 time_t tv; 612 613 /* 614 * XXX Workaround: when we reach the end of headers, mime_state_update() 615 * may execute up to three call-backs before returning to the caller: 616 * head_out(), head_end(), and body_out() or body_end(). As long as 617 * call-backs don't return a result, each call-back has to check for 618 * itself if the previous call-back experienced a problem. 619 */ 620 if (CLEANUP_OUT_OK(state) == 0) 621 return; 622 623 /* 624 * Add a missing (Resent-)Message-Id: header. The message ID gives the 625 * time in GMT units, plus the local queue ID. 626 * 627 * XXX Message-Id is not a required message header (RFC 822 and RFC 2822). 628 * 629 * XXX It is the queue ID non-inode bits that prevent messages from getting 630 * the same Message-Id within the same second. 631 * 632 * XXX An arbitrary amount of time may pass between the start of the mail 633 * transaction and the creation of a queue file. Since we guarantee queue 634 * ID uniqueness only within a second, we must ensure that the time in 635 * the message ID matches the queue ID creation time, as long as we use 636 * the queue ID in the message ID. 637 * 638 * XXX We log a dummy name=value record so that we (hopefully) don't break 639 * compatibility with existing logfile analyzers, and so that we don't 640 * complicate future code that wants to log more name=value attributes. 641 */ 642 if ((state->hdr_rewrite_context || var_always_add_hdrs) 643 && (state->headers_seen & (1 << (state->resent[0] ? 644 HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) { 645 if (var_long_queue_ids) { 646 vstring_sprintf(state->temp1, "%s@%s", 647 state->queue_id, var_myhostname); 648 } else { 649 tv = state->handle->ctime.tv_sec; 650 tp = gmtime(&tv); 651 strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp); 652 vstring_sprintf(state->temp1, "%s.%s@%s", 653 time_stamp, state->queue_id, var_myhostname); 654 } 655 cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s>", 656 state->resent, vstring_str(state->temp1)); 657 msg_info("%s: %smessage-id=<%s>", 658 state->queue_id, *state->resent ? "resent-" : "", 659 vstring_str(state->temp1)); 660 state->headers_seen |= (1 << (state->resent[0] ? 661 HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID)); 662 } 663 if ((state->headers_seen & (1 << HDR_MESSAGE_ID)) == 0) 664 msg_info("%s: message-id=<>", state->queue_id); 665 666 /* 667 * Add a missing (Resent-)Date: header. The date is in local time units, 668 * with the GMT offset at the end. 669 */ 670 if ((state->hdr_rewrite_context || var_always_add_hdrs) 671 && (state->headers_seen & (1 << (state->resent[0] ? 672 HDR_RESENT_DATE : HDR_DATE))) == 0) { 673 cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s", 674 state->resent, mail_date(state->arrival_time.tv_sec)); 675 } 676 677 /* 678 * Add a missing (Resent-)From: header. 679 */ 680 if ((state->hdr_rewrite_context || var_always_add_hdrs) 681 && (state->headers_seen & (1 << (state->resent[0] ? 682 HDR_RESENT_FROM : HDR_FROM))) == 0) { 683 quote_822_local(state->temp1, *state->sender ? 684 state->sender : MAIL_ADDR_MAIL_DAEMON); 685 vstring_sprintf(state->temp2, "%sFrom: %s", 686 state->resent, vstring_str(state->temp1)); 687 if (*state->sender && state->fullname && *state->fullname) { 688 vstring_sprintf(state->temp1, "(%s)", state->fullname); 689 token = tok822_parse(vstring_str(state->temp1)); 690 vstring_strcat(state->temp2, " "); 691 tok822_externalize(state->temp2, token, TOK822_STR_NONE); 692 tok822_free_tree(token); 693 } 694 CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2); 695 } 696 697 /* 698 * XXX 2821: Appendix B: The return address in the MAIL command SHOULD, 699 * if possible, be derived from the system's identity for the submitting 700 * (local) user, and the "From:" header field otherwise. If there is a 701 * system identity available, it SHOULD also be copied to the Sender 702 * header field if it is different from the address in the From header 703 * field. (Any Sender field that was already there SHOULD be removed.) 704 * Similar wording appears in RFC 2822 section 3.6.2. 705 * 706 * Postfix presently does not insert a Sender: header if envelope and From: 707 * address differ. Older Postfix versions assumed that the envelope 708 * sender address specifies the system identity and inserted Sender: 709 * whenever envelope and From: differed. This was wrong with relayed 710 * mail, and was often not even desirable with original submissions. 711 * 712 * XXX 2822 Section 3.6.2, as well as RFC 822 Section 4.1: FROM headers can 713 * contain multiple addresses. If this is the case, then a Sender: header 714 * must be provided with a single address. 715 * 716 * Postfix does not count the number of addresses in a From: header 717 * (although doing so is trivial, once the address is parsed). 718 */ 719 720 /* 721 * Add a missing destination header. 722 */ 723 #define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \ 724 | (1 << HDR_CC) | (1 << HDR_RESENT_CC)) 725 726 if ((state->hdr_rewrite_context || var_always_add_hdrs) 727 && (state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) { 728 if (!is_header(var_rcpt_witheld)) { 729 msg_warn("bad %s header text \"%s\" -- " 730 "need \"headername: headervalue\"", 731 VAR_RCPT_WITHELD, var_rcpt_witheld); 732 } else { 733 cleanup_out_format(state, REC_TYPE_NORM, "%s", var_rcpt_witheld); 734 } 735 } 736 737 /* 738 * Place a dummy PTR record right after the last header so that we can 739 * append headers without having to worry about clobbering the 740 * end-of-content marker. 741 */ 742 if (state->milters || cleanup_milters) { 743 if ((state->append_hdr_pt_offset = vstream_ftell(state->dst)) < 0) 744 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 745 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 746 if ((state->append_hdr_pt_target = vstream_ftell(state->dst)) < 0) 747 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 748 state->body_offset = state->append_hdr_pt_target; 749 } 750 } 751 752 /* cleanup_body_callback - output one body record */ 753 754 static void cleanup_body_callback(void *context, int type, 755 const char *buf, ssize_t len, 756 off_t offset) 757 { 758 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 759 760 /* 761 * XXX Workaround: when we reach the end of headers, mime_state_update() 762 * may execute up to three call-backs before returning to the caller: 763 * head_out(), head_end(), and body_out() or body_end(). As long as 764 * call-backs don't return a result, each call-back has to check for 765 * itself if the previous call-back experienced a problem. 766 */ 767 if (CLEANUP_OUT_OK(state) == 0) 768 return; 769 770 /* 771 * Crude message body content filter for emergencies. This code has 772 * several problems: it sees one line at a time; it looks at long lines 773 * only in chunks of line_length_limit (2048) characters; it is easily 774 * bypassed with encodings and other tricks. 775 */ 776 if ((state->flags & CLEANUP_FLAG_FILTER) 777 && cleanup_body_checks 778 && (var_body_check_len == 0 || offset < var_body_check_len)) { 779 const char *value; 780 781 if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) { 782 const char *result; 783 784 if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_BODY, 785 buf, value, VAR_BODY_CHECKS)) 786 == CLEANUP_ACT_DROP) { 787 return; 788 } else if (result != buf) { 789 cleanup_out(state, type, result, strlen(result)); 790 myfree((char *) result); 791 return; 792 } 793 } else if (cleanup_body_checks->error) { 794 msg_warn("%s: %s map lookup problem -- deferring delivery", 795 state->queue_id, cleanup_body_checks->title); 796 state->errs |= CLEANUP_STAT_WRITE; 797 } 798 } 799 cleanup_out(state, type, buf, len); 800 } 801 802 /* cleanup_message_headerbody - process message content, header and body */ 803 804 static void cleanup_message_headerbody(CLEANUP_STATE *state, int type, 805 const char *buf, ssize_t len) 806 { 807 const char *myname = "cleanup_message_headerbody"; 808 const MIME_STATE_DETAIL *detail; 809 const char *cp; 810 char *dst; 811 812 /* 813 * Reject unwanted characters. 814 * 815 * XXX Possible optimization: simplify the loop when the "reject" set 816 * contains only one character. 817 */ 818 if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_reject_chars) { 819 for (cp = buf; cp < buf + len; cp++) { 820 if (memchr(vstring_str(cleanup_reject_chars), 821 *(const unsigned char *) cp, 822 VSTRING_LEN(cleanup_reject_chars))) { 823 cleanup_act(state, CLEANUP_ACT_CTXT_ANY, 824 buf, "REJECT disallowed character", 825 "character reject"); 826 return; 827 } 828 } 829 } 830 831 /* 832 * Strip unwanted characters. Don't overwrite the input. 833 * 834 * XXX Possible space+time optimization: use a bitset. 835 * 836 * XXX Possible optimization: simplify the loop when the "strip" set 837 * contains only one character. 838 * 839 * XXX Possible optimization: copy the input only if we really have to. 840 */ 841 if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_strip_chars) { 842 VSTRING_RESET(state->stripped_buf); 843 VSTRING_SPACE(state->stripped_buf, len + 1); 844 dst = vstring_str(state->stripped_buf); 845 for (cp = buf; cp < buf + len; cp++) 846 if (!memchr(vstring_str(cleanup_strip_chars), 847 *(const unsigned char *) cp, 848 VSTRING_LEN(cleanup_strip_chars))) 849 *dst++ = *cp; 850 *dst = 0; 851 buf = vstring_str(state->stripped_buf); 852 len = dst - buf; 853 } 854 855 /* 856 * Copy text record to the output. 857 */ 858 if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) { 859 state->mime_errs = mime_state_update(state->mime_state, type, buf, len); 860 } 861 862 /* 863 * If we have reached the end of the message content segment, record the 864 * current file position so we can compute the message size lateron. 865 */ 866 else if (type == REC_TYPE_XTRA) { 867 state->mime_errs = mime_state_update(state->mime_state, type, buf, len); 868 if (state->milters || cleanup_milters) 869 /* Make room for body modification. */ 870 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 871 /* Ignore header truncation after primary message headers. */ 872 state->mime_errs &= ~MIME_ERR_TRUNC_HEADER; 873 if (state->mime_errs && state->reason == 0) { 874 state->errs |= CLEANUP_STAT_CONT; 875 detail = mime_state_detail(state->mime_errs); 876 state->reason = dsn_prepend(detail->dsn, detail->text); 877 } 878 state->mime_state = mime_state_free(state->mime_state); 879 if ((state->xtra_offset = vstream_ftell(state->dst)) < 0) 880 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 881 state->cont_length = state->xtra_offset - state->data_offset; 882 state->action = cleanup_extracted; 883 } 884 885 /* 886 * This should never happen. 887 */ 888 else { 889 msg_warn("%s: message rejected: " 890 "unexpected record type %d in message content", myname, type); 891 state->errs |= CLEANUP_STAT_BAD; 892 } 893 } 894 895 /* cleanup_mime_error_callback - error report call-back routine */ 896 897 static void cleanup_mime_error_callback(void *context, int err_code, 898 const char *text, ssize_t len) 899 { 900 CLEANUP_STATE *state = (CLEANUP_STATE *) context; 901 const char *origin; 902 903 /* 904 * Message header too large errors are handled after the end of the 905 * primary message headers. 906 */ 907 if ((err_code & ~MIME_ERR_TRUNC_HEADER) != 0) { 908 if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0) 909 origin = MAIL_ATTR_ORG_NONE; 910 #define TEXT_LEN (len < 100 ? (int) len : 100) 911 msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>", 912 state->queue_id, mime_state_error(err_code), TEXT_LEN, text, 913 origin, state->sender, state->recip ? state->recip : "unknown"); 914 } 915 } 916 917 /* cleanup_message - initialize message content segment */ 918 919 void cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t len) 920 { 921 const char *myname = "cleanup_message"; 922 int mime_options; 923 924 /* 925 * Write the start-of-content segment marker. 926 */ 927 cleanup_out_string(state, REC_TYPE_MESG, ""); 928 if ((state->data_offset = vstream_ftell(state->dst)) < 0) 929 msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); 930 931 /* 932 * Set up MIME processing options, if any. MIME_OPT_DISABLE_MIME disables 933 * special processing of Content-Type: headers, and thus, causes all text 934 * after the primary headers to be treated as the message body. 935 */ 936 mime_options = 0; 937 if (var_disable_mime_input) { 938 mime_options |= MIME_OPT_DISABLE_MIME; 939 } else { 940 /* Turn off content checks if bouncing or forwarding mail. */ 941 if (state->flags & CLEANUP_FLAG_FILTER) { 942 if (var_strict_8bitmime || var_strict_7bit_hdrs) 943 mime_options |= MIME_OPT_REPORT_8BIT_IN_HEADER; 944 if (var_strict_8bitmime || var_strict_8bit_body) 945 mime_options |= MIME_OPT_REPORT_8BIT_IN_7BIT_BODY; 946 if (var_strict_encoding) 947 mime_options |= MIME_OPT_REPORT_ENCODING_DOMAIN; 948 if (var_strict_8bitmime || var_strict_7bit_hdrs 949 || var_strict_8bit_body || var_strict_encoding 950 || *var_header_checks || *var_mimehdr_checks 951 || *var_nesthdr_checks) 952 mime_options |= MIME_OPT_REPORT_NESTING; 953 } 954 } 955 state->mime_state = mime_state_alloc(mime_options, 956 cleanup_header_callback, 957 cleanup_header_done_callback, 958 cleanup_body_callback, 959 (MIME_STATE_ANY_END) 0, 960 cleanup_mime_error_callback, 961 (void *) state); 962 963 /* 964 * XXX Workaround: truncate a long message header so that we don't exceed 965 * the default Sendmail libmilter request size limit of 65535. 966 */ 967 #define KLUDGE_HEADER_LIMIT 60000 968 if ((cleanup_milters || state->milters) 969 && var_header_limit > KLUDGE_HEADER_LIMIT) 970 var_header_limit = KLUDGE_HEADER_LIMIT; 971 972 /* 973 * Pass control to the header processing routine. 974 */ 975 state->action = cleanup_message_headerbody; 976 cleanup_message_headerbody(state, type, buf, len); 977 } 978