1/*++ 2/* NAME 3/* cleanup_envelope 3 4/* SUMMARY 5/* process envelope segment 6/* SYNOPSIS 7/* #include <cleanup.h> 8/* 9/* void cleanup_envelope(state, type, buf, len) 10/* CLEANUP_STATE *state; 11/* int type; 12/* const char *buf; 13/* ssize_t len; 14/* DESCRIPTION 15/* This module processes envelope records and writes the result 16/* to the queue file. It validates the message structure, rewrites 17/* sender/recipient addresses to canonical form, and expands recipients 18/* according to entries in the virtual table. This routine absorbs but 19/* does not emit the envelope to content boundary record. 20/* 21/* Arguments: 22/* .IP state 23/* Queue file and message processing state. This state is updated 24/* as records are processed and as errors happen. 25/* .IP type 26/* Record type. 27/* .IP buf 28/* Record content. 29/* .IP len 30/* Record content length. 31/* LICENSE 32/* .ad 33/* .fi 34/* The Secure Mailer license must be distributed with this software. 35/* AUTHOR(S) 36/* Wietse Venema 37/* IBM T.J. Watson Research 38/* P.O. Box 704 39/* Yorktown Heights, NY 10598, USA 40/*--*/ 41 42/* System library. */ 43 44#include <sys_defs.h> 45#include <string.h> 46#include <stdlib.h> 47#include <ctype.h> 48 49/* Utility library. */ 50 51#include <msg.h> 52#include <vstring.h> 53#include <vstream.h> 54#include <mymalloc.h> 55#include <stringops.h> 56#include <nvtable.h> 57 58/* Global library. */ 59 60#include <record.h> 61#include <rec_type.h> 62#include <cleanup_user.h> 63#include <qmgr_user.h> 64#include <mail_params.h> 65#include <verp_sender.h> 66#include <mail_proto.h> 67#include <dsn_mask.h> 68#include <rec_attr_map.h> 69 70/* Application-specific. */ 71 72#include "cleanup.h" 73 74#define STR vstring_str 75#define STREQ(x,y) (strcmp((x), (y)) == 0) 76 77static void cleanup_envelope_process(CLEANUP_STATE *, int, const char *, ssize_t); 78 79/* cleanup_envelope - initialize message envelope */ 80 81void cleanup_envelope(CLEANUP_STATE *state, int type, 82 const char *str, ssize_t len) 83{ 84 85 /* 86 * The message size and count record goes first, so it can easily be 87 * updated in place. This information takes precedence over any size 88 * estimate provided by the client. It's all in one record, data size 89 * first, for backwards compatibility reasons. 90 */ 91 cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, 92 (REC_TYPE_SIZE_CAST1) 0, /* extra offs - content offs */ 93 (REC_TYPE_SIZE_CAST2) 0, /* content offset */ 94 (REC_TYPE_SIZE_CAST3) 0, /* recipient count */ 95 (REC_TYPE_SIZE_CAST4) 0, /* qmgr options */ 96 (REC_TYPE_SIZE_CAST5) 0); /* content length */ 97 98 /* 99 * Pass control to the actual envelope processing routine. 100 */ 101 state->action = cleanup_envelope_process; 102 cleanup_envelope_process(state, type, str, len); 103} 104 105/* cleanup_envelope_process - process one envelope record */ 106 107static void cleanup_envelope_process(CLEANUP_STATE *state, int type, 108 const char *buf, ssize_t len) 109{ 110 const char *myname = "cleanup_envelope_process"; 111 char *attr_name; 112 char *attr_value; 113 const char *error_text; 114 int extra_opts; 115 int junk; 116 int mapped_type = type; 117 const char *mapped_buf = buf; 118 int milter_count; 119 120#ifdef DELAY_ACTION 121 int defer_delay; 122 123#endif 124 125 if (msg_verbose) 126 msg_info("initial envelope %c %.*s", type, (int) len, buf); 127 128 if (type == REC_TYPE_FLGS) { 129 /* Not part of queue file format. */ 130 extra_opts = atoi(buf); 131 if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA) 132 msg_warn("%s: ignoring bad extra flags: 0x%x", 133 state->queue_id, extra_opts); 134 else 135 state->flags |= extra_opts; 136 return; 137 } 138#ifdef DELAY_ACTION 139 if (type == REC_TYPE_DELAY) { 140 /* Not part of queue file format. */ 141 defer_delay = atoi(buf); 142 if (defer_delay <= 0) 143 msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf); 144 else 145 state->defer_delay = defer_delay; 146 return; 147 } 148#endif 149 150 /* 151 * XXX We instantiate a MILTERS structure even when the filter count is 152 * zero (for example, all filters are in ACCEPT state, or the SMTP server 153 * sends a dummy MILTERS structure without any filters), otherwise the 154 * cleanup server would apply the non_smtpd_milters setting 155 * inappropriately. 156 */ 157 if (type == REC_TYPE_MILT_COUNT) { 158 /* Not part of queue file format. */ 159 if ((milter_count = atoi(buf)) >= 0) 160 cleanup_milter_receive(state, milter_count); 161 return; 162 } 163 164 /* 165 * Map DSN attribute name to pseudo record type so that we don't have to 166 * pollute the queue file with records that are incompatible with past 167 * Postfix versions. Preferably, people should be able to back out from 168 * an upgrade without losing mail. 169 */ 170 if (type == REC_TYPE_ATTR) { 171 vstring_strcpy(state->attr_buf, buf); 172 error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); 173 if (error_text != 0) { 174 msg_warn("%s: message rejected: malformed attribute: %s: %.100s", 175 state->queue_id, error_text, buf); 176 state->errs |= CLEANUP_STAT_BAD; 177 return; 178 } 179 /* Zero-length values are place holders for unavailable values. */ 180 if (*attr_value == 0) { 181 msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", 182 state->queue_id, attr_name); 183 return; 184 } 185 if ((junk = rec_attr_map(attr_name)) != 0) { 186 mapped_buf = attr_value; 187 mapped_type = junk; 188 } 189 } 190 191 /* 192 * Sanity check. 193 */ 194 if (strchr(REC_TYPE_ENVELOPE, type) == 0) { 195 msg_warn("%s: message rejected: unexpected record type %d in envelope", 196 state->queue_id, type); 197 state->errs |= CLEANUP_STAT_BAD; 198 return; 199 } 200 201 /* 202 * Although recipient records appear at the end of the initial or 203 * extracted envelope, the code for processing recipient records is first 204 * because there can be lots of them. 205 * 206 * Recipient records may be mixed with other information (such as FILTER or 207 * REDIRECT actions from SMTPD). In that case the queue manager needs to 208 * examine all queue file records before it can start delivery. This is 209 * not a problem when SMTPD recipient lists are small. 210 * 211 * However, if recipient records are not mixed with other records 212 * (typically, mailing list mail) then we can make an optimization: the 213 * queue manager does not need to examine every envelope record before it 214 * can start deliveries. This can help with very large mailing lists. 215 */ 216 217 /* 218 * On the transition from non-recipient records to recipient records, 219 * emit some records and do some sanity checks. 220 * 221 * XXX Moving the envelope sender (and the test for its presence) to the 222 * extracted segment can reduce qmqpd memory requirements because it no 223 * longer needs to read the entire message into main memory. 224 */ 225 if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 226 && strchr(REC_TYPE_ENV_RECIPIENT, type) != 0) { 227 if (state->sender == 0) { 228 msg_warn("%s: message rejected: missing sender envelope record", 229 state->queue_id); 230 state->errs |= CLEANUP_STAT_BAD; 231 return; 232 } 233 if (state->arrival_time.tv_sec == 0) { 234 msg_warn("%s: message rejected: missing time envelope record", 235 state->queue_id); 236 state->errs |= CLEANUP_STAT_BAD; 237 return; 238 } 239 240 /* 241 * XXX This works by accident, because the sender is recorded at the 242 * beginning of the envelope segment. 243 */ 244 if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0 245 && state->sender && *state->sender 246 && var_delay_warn_time > 0) { 247 cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, 248 REC_TYPE_WARN_ARG(state->arrival_time.tv_sec 249 + var_delay_warn_time)); 250 } 251 state->flags |= CLEANUP_FLAG_INRCPT; 252 } 253 254 /* 255 * Initial envelope recipient record processing. 256 */ 257 if (type == REC_TYPE_RCPT) { 258 if (state->sender == 0) { /* protect showq */ 259 msg_warn("%s: message rejected: envelope recipient precedes sender", 260 state->queue_id); 261 state->errs |= CLEANUP_STAT_BAD; 262 return; 263 } 264 if (state->orig_rcpt == 0) 265 state->orig_rcpt = mystrdup(buf); 266 cleanup_addr_recipient(state, buf); 267 if (cleanup_milters != 0 268 && state->milters == 0 269 && CLEANUP_MILTER_OK(state)) 270 cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); 271 myfree(state->orig_rcpt); 272 state->orig_rcpt = 0; 273 if (state->dsn_orcpt != 0) { 274 myfree(state->dsn_orcpt); 275 state->dsn_orcpt = 0; 276 } 277 state->dsn_notify = 0; 278 return; 279 } 280 if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { 281 if (state->orig_rcpt != 0) { 282 myfree(state->orig_rcpt); 283 state->orig_rcpt = 0; 284 } 285 if (state->dsn_orcpt != 0) { 286 myfree(state->dsn_orcpt); 287 state->dsn_orcpt = 0; 288 } 289 state->dsn_notify = 0; 290 return; 291 } 292 if (mapped_type == REC_TYPE_DSN_ORCPT) { 293 if (state->dsn_orcpt) { 294 msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", 295 state->queue_id, state->dsn_orcpt); 296 myfree(state->dsn_orcpt); 297 } 298 state->dsn_orcpt = mystrdup(mapped_buf); 299 return; 300 } 301 if (mapped_type == REC_TYPE_DSN_NOTIFY) { 302 if (state->dsn_notify) { 303 msg_warn("%s: ignoring out-of-order DSN notify record <%d>", 304 state->queue_id, state->dsn_notify); 305 state->dsn_notify = 0; 306 } 307 if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 308 || DSN_NOTIFY_OK(junk) == 0) 309 msg_warn("%s: ignoring malformed DSN notify record <%.200s>", 310 state->queue_id, buf); 311 else 312 state->qmgr_opts |= 313 QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); 314 return; 315 } 316 if (type == REC_TYPE_ORCP) { 317 if (state->orig_rcpt != 0) { 318 msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", 319 state->queue_id, state->orig_rcpt); 320 myfree(state->orig_rcpt); 321 } 322 state->orig_rcpt = mystrdup(buf); 323 return; 324 } 325 if (type == REC_TYPE_MESG) { 326 state->action = cleanup_message; 327 if (state->flags & CLEANUP_FLAG_INRCPT) { 328 if (state->milters || cleanup_milters) { 329 /* Make room to append recipient. */ 330 if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) 331 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 332 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 333 if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) 334 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 335 } 336 state->flags &= ~CLEANUP_FLAG_INRCPT; 337 } 338 return; 339 } 340 341 /* 342 * Initial envelope non-recipient record processing. 343 */ 344 if (state->flags & CLEANUP_FLAG_INRCPT) 345 /* Tell qmgr that recipient records are mixed with other information. */ 346 state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; 347 if (type == REC_TYPE_SIZE) 348 /* Use our own SIZE record instead. */ 349 return; 350 if (mapped_type == REC_TYPE_CTIME) 351 /* Use our own expiration time base record instead. */ 352 return; 353 if (type == REC_TYPE_TIME) { 354 /* First instance wins. */ 355 if (state->arrival_time.tv_sec == 0) { 356 REC_TYPE_TIME_SCAN(buf, state->arrival_time); 357 cleanup_out(state, type, buf, len); 358 } 359 /* Generate our own expiration time base record. */ 360 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%ld", 361 MAIL_ATTR_CREATE_TIME, (long) time((time_t *) 0)); 362 return; 363 } 364 if (type == REC_TYPE_FULL) { 365 /* First instance wins. */ 366 if (state->fullname == 0) { 367 state->fullname = mystrdup(buf); 368 cleanup_out(state, type, buf, len); 369 } 370 return; 371 } 372 if (type == REC_TYPE_FROM) { 373 /* Allow only one instance. */ 374 if (state->sender != 0) { 375 msg_warn("%s: message rejected: multiple envelope sender records", 376 state->queue_id); 377 state->errs |= CLEANUP_STAT_BAD; 378 return; 379 } 380 if (state->milters || cleanup_milters) { 381 /* Remember the sender record offset. */ 382 if ((state->sender_pt_offset = vstream_ftell(state->dst)) < 0) 383 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 384 } 385 cleanup_addr_sender(state, buf); 386 if (state->milters || cleanup_milters) { 387 /* Make room to replace sender. */ 388 if ((len = strlen(state->sender)) < REC_TYPE_PTR_PAYL_SIZE) 389 rec_pad(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_PAYL_SIZE - len); 390 /* Remember the after-sender record offset. */ 391 if ((state->sender_pt_target = vstream_ftell(state->dst)) < 0) 392 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 393 } 394 if (cleanup_milters != 0 395 && state->milters == 0 396 && CLEANUP_MILTER_OK(state)) 397 cleanup_milter_emul_mail(state, cleanup_milters, state->sender); 398 return; 399 } 400 if (mapped_type == REC_TYPE_DSN_ENVID) { 401 /* Allow only one instance. */ 402 if (state->dsn_envid != 0) { 403 msg_warn("%s: message rejected: multiple DSN envelope ID records", 404 state->queue_id); 405 state->errs |= CLEANUP_STAT_BAD; 406 return; 407 } 408 if (!allprint(mapped_buf)) { 409 msg_warn("%s: message rejected: bad DSN envelope ID record", 410 state->queue_id); 411 state->errs |= CLEANUP_STAT_BAD; 412 return; 413 } 414 state->dsn_envid = mystrdup(mapped_buf); 415 cleanup_out(state, type, buf, len); 416 return; 417 } 418 if (mapped_type == REC_TYPE_DSN_RET) { 419 /* Allow only one instance. */ 420 if (state->dsn_ret != 0) { 421 msg_warn("%s: message rejected: multiple DSN RET records", 422 state->queue_id); 423 state->errs |= CLEANUP_STAT_BAD; 424 return; 425 } 426 if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 427 || DSN_RET_OK(junk) == 0) { 428 msg_warn("%s: message rejected: bad DSN RET record <%.200s>", 429 state->queue_id, buf); 430 state->errs |= CLEANUP_STAT_BAD; 431 return; 432 } 433 state->dsn_ret = junk; 434 cleanup_out(state, type, buf, len); 435 return; 436 } 437 if (type == REC_TYPE_WARN) { 438 /* First instance wins. */ 439 if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) { 440 state->flags |= CLEANUP_FLAG_WARN_SEEN; 441 cleanup_out(state, type, buf, len); 442 } 443 return; 444 } 445 /* XXX Needed for cleanup_bounce(); sanity check usage. */ 446 if (type == REC_TYPE_VERP) { 447 if (state->verp_delims == 0) { 448 if (state->sender == 0 || state->sender[0] == 0) { 449 msg_warn("%s: ignoring VERP request for null sender", 450 state->queue_id); 451 } else if (verp_delims_verify(buf) != 0) { 452 msg_warn("%s: ignoring bad VERP request: \"%.100s\"", 453 state->queue_id, buf); 454 } else { 455 state->verp_delims = mystrdup(buf); 456 cleanup_out(state, type, buf, len); 457 } 458 } 459 return; 460 } 461 if (type == REC_TYPE_ATTR) { 462 if (state->attr->used >= var_qattr_count_limit) { 463 msg_warn("%s: message rejected: attribute count exceeds limit %d", 464 state->queue_id, var_qattr_count_limit); 465 state->errs |= CLEANUP_STAT_BAD; 466 return; 467 } 468 if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) { 469 /* Choose header rewriting context. See also cleanup_addr.c. */ 470 if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) { 471 state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL; 472 } else if (STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)) { 473 state->hdr_rewrite_context = 474 (*var_remote_rwr_domain ? MAIL_ATTR_RWR_REMOTE : 0); 475 } else { 476 msg_warn("%s: message rejected: bad rewriting context: %.100s", 477 state->queue_id, attr_value); 478 state->errs |= CLEANUP_STAT_BAD; 479 return; 480 } 481 } 482 nvtable_update(state->attr, attr_name, attr_value); 483 cleanup_out(state, type, buf, len); 484 return; 485 } else { 486 cleanup_out(state, type, buf, len); 487 return; 488 } 489} 490