1 /* $NetBSD: milter.c,v 1.5 2022/10/08 16:12:46 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* milter 3 6 /* SUMMARY 7 /* generic MTA-side mail filter interface 8 /* SYNOPSIS 9 /* #include <milter.h> 10 /* 11 /* MILTERS *milter_create(milter_names, conn_timeout, cmd_timeout, 12 /* msg_timeout, protocol, def_action, 13 /* conn_macros, helo_macros, 14 /* mail_macros, rcpt_macros, 15 /* data_macros, eoh_macros, 16 /* eod_macros, unk_macros, 17 /* macro_deflts) 18 /* const char *milter_names; 19 /* int conn_timeout; 20 /* int cmd_timeout; 21 /* int msg_timeout; 22 /* const char *protocol; 23 /* const char *def_action; 24 /* const char *conn_macros; 25 /* const char *helo_macros; 26 /* const char *mail_macros; 27 /* const char *rcpt_macrps; 28 /* const char *data_macros; 29 /* const char *eoh_macros; 30 /* const char *eod_macros; 31 /* const char *unk_macros; 32 /* const char *macro_deflts; 33 /* 34 /* void milter_free(milters) 35 /* MILTERS *milters; 36 /* 37 /* void milter_macro_callback(milters, mac_lookup, mac_context) 38 /* const char *(*mac_lookup)(const char *name, void *context); 39 /* void *mac_context; 40 /* 41 /* void milter_edit_callback(milters, add_header, upd_header, 42 /* ins_header, del_header, chg_from, 43 /* add_rcpt, add_rcpt_par, del_rcpt, 44 /* repl_body, context) 45 /* MILTERS *milters; 46 /* MILTER_ADD_HEADER_FN add_header; 47 /* MILTER_EDIT_HEADER_FN upd_header; 48 /* MILTER_EDIT_HEADER_FN ins_header; 49 /* MILTER_DEL_HEADER_FN del_header; 50 /* MILTER_EDIT_FROM_FN chg_from; 51 /* MILTER_EDIT_RCPT_FN add_rcpt; 52 /* MILTER_EDIT_RCPT_PAR_FN add_rcpt_par; 53 /* MILTER_EDIT_RCPT_FN del_rcpt; 54 /* MILTER_EDIT_BODY_FN repl_body; 55 /* void *context; 56 /* 57 /* const char *milter_conn_event(milters, client_name, client_addr, 58 /* client_port, addr_family) 59 /* MILTERS *milters; 60 /* const char *client_name; 61 /* const char *client_addr; 62 /* const char *client_port; 63 /* int addr_family; 64 /* 65 /* const char *milter_disc_event(milters) 66 /* MILTERS *milters; 67 /* 68 /* const char *milter_helo_event(milters, helo_name, esmtp_flag) 69 /* MILTERS *milters; 70 /* const char *helo_name; 71 /* int esmtp_flag; 72 /* 73 /* const char *milter_mail_event(milters, argv) 74 /* MILTERS *milters; 75 /* const char **argv; 76 /* 77 /* const char *milter_rcpt_event(milters, flags, argv) 78 /* MILTERS *milters; 79 /* int flags; 80 /* const char **argv; 81 /* 82 /* const char *milter_data_event(milters) 83 /* MILTERS *milters; 84 /* 85 /* const char *milter_unknown_event(milters, command) 86 /* MILTERS *milters; 87 /* const char *command; 88 /* 89 /* const char *milter_other_event(milters) 90 /* MILTERS *milters; 91 /* 92 /* const char *milter_message(milters, qfile, data_offset, auto_hdrs) 93 /* MILTERS *milters; 94 /* VSTREAM *qfile; 95 /* off_t data_offset; 96 /* ARGV *auto_hdrs; 97 /* 98 /* const char *milter_abort(milters) 99 /* MILTERS *milters; 100 /* 101 /* int milter_send(milters, fp) 102 /* MILTERS *milters; 103 /* VSTREAM *fp; 104 /* 105 /* MILTERS *milter_receive(fp, count) 106 /* VSTREAM *fp; 107 /* int count; 108 /* 109 /* int milter_dummy(milters, fp) 110 /* MILTERS *milters; 111 /* VSTREAM *fp; 112 /* DESCRIPTION 113 /* The functions in this module manage one or more milter (mail 114 /* filter) clients. Currently, only the Sendmail 8 filter 115 /* protocol is supported. 116 /* 117 /* The functions that inspect content or envelope commands 118 /* return either an SMTP reply ([45]XX followed by enhanced 119 /* status code and text), "D" (discard), "H" (quarantine), 120 /* "S" (shutdown connection), or a null pointer, which means 121 /* "no news is good news". 122 /* 123 /* milter_create() instantiates the milter clients specified 124 /* with the milter_names argument. The conn_macros etc. 125 /* arguments specify the names of macros that are sent to the 126 /* mail filter applications upon a connect etc. event, and the 127 /* macro_deflts argument specifies macro defaults that will be used 128 /* only if the application's lookup call-back returns null. This 129 /* function should be called during process initialization, 130 /* before entering a chroot jail. The timeout parameters specify 131 /* time limits for the completion of the specified request 132 /* classes. The protocol parameter specifies a protocol version 133 /* and optional extensions. When the milter application is 134 /* unavailable, the milter client will go into a suitable error 135 /* state as specified with the def_action parameter (i.e. 136 /* reject, tempfail or accept all subsequent events). 137 /* 138 /* milter_free() disconnects from the milter instances that 139 /* are still opened, and destroys the data structures created 140 /* by milter_create(). This function is safe to call at any 141 /* point after milter_create(). 142 /* 143 /* milter_macro_callback() specifies a call-back function and 144 /* context for macro lookup. This function must be called 145 /* before milter_conn_event(). 146 /* 147 /* milter_edit_callback() specifies call-back functions and 148 /* context for editing the queue file after the end-of-data 149 /* is received. This function must be called before milter_message(); 150 /* 151 /* milter_conn_event() reports an SMTP client connection event 152 /* to the specified milter instances, after sending the macros 153 /* specified with the milter_create() conn_macros argument. 154 /* This function must be called before reporting any other 155 /* events. 156 /* 157 /* milter_disc_event() reports an SMTP client disconnection 158 /* event to the specified milter instances. No events can 159 /* reported after this call. To simplify usage, redundant calls 160 /* of this function are NO-OPs and don't raise a run-time 161 /* error. 162 /* 163 /* milter_helo_event() reports a HELO or EHLO event to the 164 /* specified milter instances, after sending the macros that 165 /* were specified with the milter_create() helo_macros argument. 166 /* 167 /* milter_mail_event() reports a MAIL FROM event to the specified 168 /* milter instances, after sending the macros that were specified 169 /* with the milter_create() mail_macros argument. 170 /* 171 /* milter_rcpt_event() reports an RCPT TO event to the specified 172 /* milter instances, after sending the macros that were specified 173 /* with the milter_create() rcpt_macros argument. The flags 174 /* argument supports the following: 175 /* .IP MILTER_FLAG_WANT_RCPT_REJ 176 /* When this flag is cleared, invoke all milters. When this 177 /* flag is set, invoke only milters that want to receive 178 /* rejected recipients; with Sendmail V8 Milters, {rcpt_mailer} 179 /* is set to "error", {rcpt_host} is set to an enhanced status 180 /* code, and {rcpt_addr} is set to descriptive text. 181 /* .PP 182 /* milter_data_event() reports a DATA event to the specified 183 /* milter instances, after sending the macros that were specified 184 /* with the milter_create() data_macros argument. 185 /* 186 /* milter_unknown_event() reports an unknown command event to 187 /* the specified milter instances, after sending the macros 188 /* that were specified with the milter_create() unk_macros 189 /* argument. 190 /* 191 /* milter_other_event() returns the current default mail filter 192 /* reply for the current SMTP connection state; it does not 193 /* change milter states. A null pointer result means that all 194 /* is well. This function can be used for SMTP commands such 195 /* as AUTH, STARTTLS that don't have their own milter event 196 /* routine. 197 /* 198 /* milter_message() sends the message header and body to the 199 /* to the specified milter instances, and sends the macros 200 /* specified with the milter_create() eoh_macros after the 201 /* message header, and with the eod_macros argument at 202 /* the end. Each milter sees the result of any changes made 203 /* by a preceding milter. This function must be called with 204 /* as argument an open Postfix queue file. 205 /* 206 /* milter_abort() cancels a mail transaction in progress. To 207 /* simplify usage, redundant calls of this function are NO-OPs 208 /* and don't raise a run-time error. 209 /* 210 /* milter_send() sends a list of mail filters over the specified 211 /* stream. When given a null list pointer, a "no filter" 212 /* indication is sent. The result is non-zero in case of 213 /* error. 214 /* 215 /* milter_receive() receives the specified number of mail 216 /* filters over the specified stream. The result is a null 217 /* pointer when no milters were sent, or when an error happened. 218 /* 219 /* milter_dummy() is like milter_send(), except that it sends 220 /* a dummy, but entirely valid, mail filter list. 221 /* SEE ALSO 222 /* milter8(3) Sendmail 8 Milter protocol 223 /* DIAGNOSTICS 224 /* Panic: interface violation. 225 /* Fatal errors: memory allocation problem. 226 /* LICENSE 227 /* .ad 228 /* .fi 229 /* The Secure Mailer license must be distributed with this software. 230 /* AUTHOR(S) 231 /* Wietse Venema 232 /* IBM T.J. Watson Research 233 /* P.O. Box 704 234 /* Yorktown Heights, NY 10598, USA 235 /* 236 /* Wietse Venema 237 /* Google, Inc. 238 /* 111 8th Avenue 239 /* New York, NY 10011, USA 240 /*--*/ 241 242 /* System library. */ 243 244 #include <sys_defs.h> 245 246 /* Utility library. */ 247 248 #include <msg.h> 249 #include <mymalloc.h> 250 #include <stringops.h> 251 #include <argv.h> 252 #include <attr.h> 253 #include <htable.h> 254 255 /* Global library. */ 256 257 #include <mail_proto.h> 258 #include <record.h> 259 #include <rec_type.h> 260 #include <mail_params.h> 261 #include <attr_override.h> 262 263 /* Postfix Milter library. */ 264 265 #include <milter.h> 266 267 /* Application-specific. */ 268 269 /* 270 * SLMs. 271 */ 272 #define STR(x) vstring_str(x) 273 274 /* milter_macro_defaults_create - parse default macro entries */ 275 276 HTABLE *milter_macro_defaults_create(const char *macro_defaults) 277 { 278 const char myname[] = "milter_macro_defaults_create"; 279 char *saved_defaults = mystrdup(macro_defaults); 280 char *cp = saved_defaults; 281 HTABLE *table = 0; 282 VSTRING *canon_buf = 0; 283 char *nameval; 284 285 while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) { 286 const char *err; 287 char *name; 288 char *value; 289 290 /* 291 * Split the input into (name, value) pairs. Allow the forms 292 * name=value and { name = value }, where the last form ignores 293 * whitespace after the opening "{", around the "=", and before the 294 * closing "}". A name may also be specified as {name}. 295 * 296 * Use the form {name} for table lookups, because that is the form of 297 * the S8_MAC_* macro names. 298 */ 299 if (*nameval == CHARS_BRACE[0] 300 && nameval[balpar(nameval, CHARS_BRACE)] != '=' 301 && (err = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) 302 msg_fatal("malformed default macro entry: %s in \"%s\"", 303 err, macro_defaults); 304 if ((err = split_nameval(nameval, &name, &value)) != 0) 305 msg_fatal("malformed default macro entry: %s in \"%s\"", 306 err, macro_defaults); 307 if (*name != '{') /* } */ 308 name = STR(vstring_sprintf(canon_buf ? canon_buf : 309 (canon_buf = vstring_alloc(20)), "{%s}", name)); 310 if (table == 0) 311 table = htable_create(1); 312 if (htable_find(table, name) != 0) { 313 msg_warn("ignoring multiple default macro entries for %s in \"%s\"", 314 name, macro_defaults); 315 } else { 316 (void) htable_enter(table, name, mystrdup(value)); 317 if (msg_verbose) 318 msg_info("%s: add name=%s default=%s", myname, name, value); 319 } 320 } 321 myfree(saved_defaults); 322 if (canon_buf) 323 vstring_free(canon_buf); 324 return (table); 325 } 326 327 /* milter_macro_lookup - look up macros */ 328 329 static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names) 330 { 331 const char *myname = "milter_macro_lookup"; 332 char *saved_names = mystrdup(macro_names); 333 char *cp = saved_names; 334 ARGV *argv = argv_alloc(10); 335 VSTRING *canon_buf = vstring_alloc(20); 336 const char *value; 337 const char *name; 338 const char *cname; 339 340 while ((name = mystrtok(&cp, CHARS_COMMA_SP)) != 0) { 341 if (msg_verbose) 342 msg_info("%s: \"%s\"", myname, name); 343 if (*name != '{') /* } */ 344 cname = STR(vstring_sprintf(canon_buf, "{%s}", name)); 345 else 346 cname = name; 347 if ((value = milters->mac_lookup(cname, milters->mac_context)) != 0) { 348 if (msg_verbose) 349 msg_info("%s: result \"%s\"", myname, value); 350 argv_add(argv, name, value, (char *) 0); 351 } else if (milters->macro_defaults != 0 352 && (value = htable_find(milters->macro_defaults, cname)) != 0) { 353 if (msg_verbose) 354 msg_info("%s: using default \"%s\"", myname, value); 355 argv_add(argv, name, value, (char *) 0); 356 } 357 } 358 myfree(saved_names); 359 vstring_free(canon_buf); 360 return (argv); 361 } 362 363 /* milter_macro_callback - specify macro lookup */ 364 365 void milter_macro_callback(MILTERS *milters, 366 const char *(*mac_lookup) (const char *, void *), 367 void *mac_context) 368 { 369 milters->mac_lookup = mac_lookup; 370 milters->mac_context = mac_context; 371 } 372 373 /* milter_edit_callback - specify queue file edit call-back information */ 374 375 void milter_edit_callback(MILTERS *milters, 376 MILTER_ADD_HEADER_FN add_header, 377 MILTER_EDIT_HEADER_FN upd_header, 378 MILTER_EDIT_HEADER_FN ins_header, 379 MILTER_DEL_HEADER_FN del_header, 380 MILTER_EDIT_FROM_FN chg_from, 381 MILTER_EDIT_RCPT_FN add_rcpt, 382 MILTER_EDIT_RCPT_PAR_FN add_rcpt_par, 383 MILTER_EDIT_RCPT_FN del_rcpt, 384 MILTER_EDIT_BODY_FN repl_body, 385 void *chg_context) 386 { 387 milters->add_header = add_header; 388 milters->upd_header = upd_header; 389 milters->ins_header = ins_header; 390 milters->del_header = del_header; 391 milters->chg_from = chg_from; 392 milters->add_rcpt = add_rcpt; 393 milters->add_rcpt_par = add_rcpt_par; 394 milters->del_rcpt = del_rcpt; 395 milters->repl_body = repl_body; 396 milters->chg_context = chg_context; 397 } 398 399 /* milter_conn_event - report connect event */ 400 401 const char *milter_conn_event(MILTERS *milters, 402 const char *client_name, 403 const char *client_addr, 404 const char *client_port, 405 unsigned addr_family) 406 { 407 const char *resp; 408 MILTER *m; 409 ARGV *global_macros = 0; 410 ARGV *any_macros; 411 412 #define MILTER_MACRO_EVAL(global_macros, m, milters, member) \ 413 ((m->macros && m->macros->member[0]) ? \ 414 milter_macro_lookup(milters, m->macros->member) : \ 415 global_macros ? global_macros : \ 416 (global_macros = \ 417 milter_macro_lookup(milters, milters->macros->member))) 418 419 if (msg_verbose) 420 msg_info("report connect to all milters"); 421 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 422 if (m->connect_on_demand != 0) 423 m->connect_on_demand(m); 424 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, conn_macros); 425 resp = m->conn_event(m, client_name, client_addr, client_port, 426 addr_family, any_macros); 427 if (any_macros != global_macros) 428 argv_free(any_macros); 429 } 430 if (global_macros) 431 argv_free(global_macros); 432 return (resp); 433 } 434 435 /* milter_helo_event - report helo event */ 436 437 const char *milter_helo_event(MILTERS *milters, const char *helo_name, 438 int esmtp_flag) 439 { 440 const char *resp; 441 MILTER *m; 442 ARGV *global_macros = 0; 443 ARGV *any_macros; 444 445 if (msg_verbose) 446 msg_info("report helo to all milters"); 447 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 448 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, helo_macros); 449 resp = m->helo_event(m, helo_name, esmtp_flag, any_macros); 450 if (any_macros != global_macros) 451 argv_free(any_macros); 452 } 453 if (global_macros) 454 argv_free(global_macros); 455 return (resp); 456 } 457 458 /* milter_mail_event - report mail from event */ 459 460 const char *milter_mail_event(MILTERS *milters, const char **argv) 461 { 462 const char *resp; 463 MILTER *m; 464 ARGV *global_macros = 0; 465 ARGV *any_macros; 466 467 if (msg_verbose) 468 msg_info("report sender to all milters"); 469 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 470 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, mail_macros); 471 resp = m->mail_event(m, argv, any_macros); 472 if (any_macros != global_macros) 473 argv_free(any_macros); 474 } 475 if (global_macros) 476 argv_free(global_macros); 477 return (resp); 478 } 479 480 /* milter_rcpt_event - report rcpt to event */ 481 482 const char *milter_rcpt_event(MILTERS *milters, int flags, const char **argv) 483 { 484 const char *resp; 485 MILTER *m; 486 ARGV *global_macros = 0; 487 ARGV *any_macros; 488 489 if (msg_verbose) 490 msg_info("report recipient to all milters (flags=0x%x)", flags); 491 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 492 if ((flags & MILTER_FLAG_WANT_RCPT_REJ) == 0 493 || (m->flags & MILTER_FLAG_WANT_RCPT_REJ) != 0) { 494 any_macros = 495 MILTER_MACRO_EVAL(global_macros, m, milters, rcpt_macros); 496 resp = m->rcpt_event(m, argv, any_macros); 497 if (any_macros != global_macros) 498 argv_free(any_macros); 499 } 500 } 501 if (global_macros) 502 argv_free(global_macros); 503 return (resp); 504 } 505 506 /* milter_data_event - report data event */ 507 508 const char *milter_data_event(MILTERS *milters) 509 { 510 const char *resp; 511 MILTER *m; 512 ARGV *global_macros = 0; 513 ARGV *any_macros; 514 515 if (msg_verbose) 516 msg_info("report data to all milters"); 517 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 518 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, data_macros); 519 resp = m->data_event(m, any_macros); 520 if (any_macros != global_macros) 521 argv_free(any_macros); 522 } 523 if (global_macros) 524 argv_free(global_macros); 525 return (resp); 526 } 527 528 /* milter_unknown_event - report unknown command */ 529 530 const char *milter_unknown_event(MILTERS *milters, const char *command) 531 { 532 const char *resp; 533 MILTER *m; 534 ARGV *global_macros = 0; 535 ARGV *any_macros; 536 537 if (msg_verbose) 538 msg_info("report unknown command to all milters"); 539 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 540 any_macros = MILTER_MACRO_EVAL(global_macros, m, milters, unk_macros); 541 resp = m->unknown_event(m, command, any_macros); 542 if (any_macros != global_macros) 543 argv_free(any_macros); 544 } 545 if (global_macros) 546 argv_free(global_macros); 547 return (resp); 548 } 549 550 /* milter_other_event - other SMTP event */ 551 552 const char *milter_other_event(MILTERS *milters) 553 { 554 const char *resp; 555 MILTER *m; 556 557 if (msg_verbose) 558 msg_info("query milter states for other event"); 559 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) 560 resp = m->other_event(m); 561 return (resp); 562 } 563 564 /* milter_message - inspect message content */ 565 566 const char *milter_message(MILTERS *milters, VSTREAM *fp, off_t data_offset, 567 ARGV *auto_hdrs) 568 { 569 const char *resp; 570 MILTER *m; 571 ARGV *global_eoh_macros = 0; 572 ARGV *global_eod_macros = 0; 573 ARGV *any_eoh_macros; 574 ARGV *any_eod_macros; 575 576 if (msg_verbose) 577 msg_info("inspect content by all milters"); 578 for (resp = 0, m = milters->milter_list; resp == 0 && m != 0; m = m->next) { 579 any_eoh_macros = MILTER_MACRO_EVAL(global_eoh_macros, m, milters, eoh_macros); 580 any_eod_macros = MILTER_MACRO_EVAL(global_eod_macros, m, milters, eod_macros); 581 resp = m->message(m, fp, data_offset, any_eoh_macros, any_eod_macros, 582 auto_hdrs); 583 if (any_eoh_macros != global_eoh_macros) 584 argv_free(any_eoh_macros); 585 if (any_eod_macros != global_eod_macros) 586 argv_free(any_eod_macros); 587 } 588 if (global_eoh_macros) 589 argv_free(global_eoh_macros); 590 if (global_eod_macros) 591 argv_free(global_eod_macros); 592 return (resp); 593 } 594 595 /* milter_abort - cancel message receiving state, all milters */ 596 597 void milter_abort(MILTERS *milters) 598 { 599 MILTER *m; 600 601 if (msg_verbose) 602 msg_info("abort all milters"); 603 for (m = milters->milter_list; m != 0; m = m->next) 604 m->abort(m); 605 } 606 607 /* milter_disc_event - report client disconnect event to all milters */ 608 609 void milter_disc_event(MILTERS *milters) 610 { 611 MILTER *m; 612 613 if (msg_verbose) 614 msg_info("disconnect event to all milters"); 615 for (m = milters->milter_list; m != 0; m = m->next) 616 m->disc_event(m); 617 } 618 619 /* 620 * Table-driven parsing of main.cf parameter overrides for specific Milters. 621 * We derive the override names from the corresponding main.cf parameter 622 * names by skipping the redundant "milter_" prefix. 623 */ 624 static ATTR_OVER_TIME time_table[] = { 625 7 + (const char *) VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, 0, 1, 0, 626 7 + (const char *) VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, 0, 1, 0, 627 7 + (const char *) VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, 0, 1, 0, 628 0, 629 }; 630 static ATTR_OVER_STR str_table[] = { 631 7 + (const char *) VAR_MILT_PROTOCOL, 0, 1, 0, 632 7 + (const char *) VAR_MILT_DEF_ACTION, 0, 1, 0, 633 0, 634 }; 635 636 #define link_override_table_to_variable(table, var) \ 637 do { table[var##_offset].target = &var; } while (0) 638 639 #define my_conn_timeout_offset 0 640 #define my_cmd_timeout_offset 1 641 #define my_msg_timeout_offset 2 642 643 #define my_protocol_offset 0 644 #define my_def_action_offset 1 645 646 /* milter_new - create milter list */ 647 648 MILTERS *milter_new(const char *names, 649 int conn_timeout, 650 int cmd_timeout, 651 int msg_timeout, 652 const char *protocol, 653 const char *def_action, 654 MILTER_MACROS *macros, 655 HTABLE *macro_defaults) 656 { 657 MILTERS *milters; 658 MILTER *head = 0; 659 MILTER *tail = 0; 660 char *name; 661 MILTER *milter; 662 const char *sep = CHARS_COMMA_SP; 663 const char *parens = CHARS_BRACE; 664 int my_conn_timeout; 665 int my_cmd_timeout; 666 int my_msg_timeout; 667 const char *my_protocol; 668 const char *my_def_action; 669 670 /* 671 * Initialize. 672 */ 673 link_override_table_to_variable(time_table, my_conn_timeout); 674 link_override_table_to_variable(time_table, my_cmd_timeout); 675 link_override_table_to_variable(time_table, my_msg_timeout); 676 link_override_table_to_variable(str_table, my_protocol); 677 link_override_table_to_variable(str_table, my_def_action); 678 679 /* 680 * Parse the milter list. 681 */ 682 milters = (MILTERS *) mymalloc(sizeof(*milters)); 683 if (names != 0 && *names != 0) { 684 char *saved_names = mystrdup(names); 685 char *cp = saved_names; 686 char *op; 687 char *err; 688 689 /* 690 * Instantiate Milters, allowing for per-Milter overrides. 691 */ 692 while ((name = mystrtokq(&cp, sep, parens)) != 0) { 693 my_conn_timeout = conn_timeout; 694 my_cmd_timeout = cmd_timeout; 695 my_msg_timeout = msg_timeout; 696 my_protocol = protocol; 697 my_def_action = def_action; 698 if (name[0] == parens[0]) { 699 op = name; 700 if ((err = extpar(&op, parens, EXTPAR_FLAG_NONE)) != 0) 701 msg_fatal("milter service syntax error: %s", err); 702 if ((name = mystrtok(&op, sep)) == 0) 703 msg_fatal("empty milter definition: \"%s\"", names); 704 attr_override(op, sep, parens, 705 CA_ATTR_OVER_STR_TABLE(str_table), 706 CA_ATTR_OVER_TIME_TABLE(time_table), 707 CA_ATTR_OVER_END); 708 } 709 milter = milter8_create(name, my_conn_timeout, my_cmd_timeout, 710 my_msg_timeout, my_protocol, 711 my_def_action, milters); 712 if (head == 0) { 713 head = milter; 714 } else { 715 tail->next = milter; 716 } 717 tail = milter; 718 } 719 myfree(saved_names); 720 } 721 milters->milter_list = head; 722 milters->mac_lookup = 0; 723 milters->mac_context = 0; 724 milters->macros = macros; 725 milters->macro_defaults = macro_defaults; 726 milters->add_header = 0; 727 milters->upd_header = milters->ins_header = 0; 728 milters->del_header = 0; 729 milters->add_rcpt = milters->del_rcpt = 0; 730 milters->repl_body = 0; 731 milters->chg_context = 0; 732 return (milters); 733 } 734 735 /* milter_free - destroy all milters */ 736 737 void milter_free(MILTERS *milters) 738 { 739 MILTER *m; 740 MILTER *next; 741 742 if (msg_verbose) 743 msg_info("free all milters"); 744 for (m = milters->milter_list; m != 0; m = next) 745 next = m->next, m->free(m); 746 if (milters->macros) 747 milter_macros_free(milters->macros); 748 if (milters->macro_defaults) 749 htable_free(milters->macro_defaults, myfree); 750 myfree((void *) milters); 751 } 752 753 /* milter_dummy - send empty milter list */ 754 755 int milter_dummy(MILTERS *milters, VSTREAM *stream) 756 { 757 MILTERS dummy = *milters; 758 759 dummy.milter_list = 0; 760 return (milter_send(&dummy, stream)); 761 } 762 763 /* milter_send - send Milter instances over stream */ 764 765 int milter_send(MILTERS *milters, VSTREAM *stream) 766 { 767 MILTER *m; 768 int status = 0; 769 int count = 0; 770 771 /* 772 * XXX Optimization: send only the filters that are actually used in the 773 * remote process. No point sending a filter that looks at HELO commands 774 * to a cleanup server. For now we skip only the filters that are known 775 * to be disabled (either in global error state or in global accept 776 * state). 777 * 778 * XXX We must send *some* information, even when there are no active 779 * filters, otherwise the cleanup server would try to apply its own 780 * non_smtpd_milters settings. 781 */ 782 if (milters != 0) 783 for (m = milters->milter_list; m != 0; m = m->next) 784 if (m->active(m)) 785 count++; 786 (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count); 787 788 if (msg_verbose) 789 msg_info("send %d milters", count); 790 791 /* 792 * XXX Optimization: don't send or receive further information when there 793 * aren't any active filters. 794 */ 795 if (count <= 0) 796 return (0); 797 798 /* 799 * Send the filter macro name lists. 800 */ 801 (void) attr_print(stream, ATTR_FLAG_MORE, 802 SEND_ATTR_FUNC(milter_macros_print, 803 (const void *) milters->macros), 804 ATTR_TYPE_END); 805 806 /* 807 * Send the filter macro defaults. 808 */ 809 count = milters->macro_defaults ? milters->macro_defaults->used : 0; 810 (void) attr_print(stream, ATTR_FLAG_MORE, 811 SEND_ATTR_INT(MAIL_ATTR_SIZE, count), 812 ATTR_TYPE_END); 813 if (count > 0) 814 (void) attr_print(stream, ATTR_FLAG_MORE, 815 SEND_ATTR_HASH(milters->macro_defaults), 816 ATTR_TYPE_END); 817 818 /* 819 * Send the filter instances. 820 */ 821 for (m = milters->milter_list; m != 0; m = m->next) 822 if (m->active(m) && (status = m->send(m, stream)) != 0) 823 break; 824 825 /* 826 * Over to you. 827 */ 828 if (status != 0 829 || attr_scan(stream, ATTR_FLAG_STRICT, 830 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), 831 ATTR_TYPE_END) != 1 832 || status != 0) { 833 msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream)); 834 return (-1); 835 } 836 return (0); 837 } 838 839 /* milter_receive - receive milters from stream */ 840 841 MILTERS *milter_receive(VSTREAM *stream, int count) 842 { 843 MILTERS *milters; 844 MILTER *head = 0; 845 MILTER *tail = 0; 846 MILTER *milter = 0; 847 int macro_default_count; 848 849 if (msg_verbose) 850 msg_info("receive %d milters", count); 851 852 /* 853 * XXX We must instantiate a MILTERS structure even when the sender has 854 * no active filters, otherwise the cleanup server would try to use its 855 * own non_smtpd_milters settings. 856 */ 857 #define NO_MILTERS ((char *) 0) 858 #define NO_TIMEOUTS 0, 0, 0 859 #define NO_PROTOCOL ((char *) 0) 860 #define NO_ACTION ((char *) 0) 861 #define NO_MACROS ((MILTER_MACROS *) 0) 862 #define NO_MACRO_DEFLTS ((HTABLE *) 0) 863 864 milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION, 865 NO_MACROS, NO_MACRO_DEFLTS); 866 867 /* 868 * XXX Optimization: don't send or receive further information when there 869 * aren't any active filters. 870 */ 871 if (count <= 0) 872 return (milters); 873 874 /* 875 * Receive the global macro name lists. 876 */ 877 milters->macros = milter_macros_alloc(MILTER_MACROS_ALLOC_ZERO); 878 if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, 879 RECV_ATTR_FUNC(milter_macros_scan, 880 (void *) milters->macros), 881 ATTR_TYPE_END) != 1) { 882 milter_free(milters); 883 return (0); 884 } 885 886 /* 887 * Receive the filter macro defaults. 888 */ 889 if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, 890 RECV_ATTR_INT(MAIL_ATTR_SIZE, ¯o_default_count), 891 ATTR_TYPE_END) != 1 892 || (macro_default_count > 0 893 && attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, 894 RECV_ATTR_HASH(milters->macro_defaults 895 = htable_create(1)), 896 ATTR_TYPE_END) != macro_default_count)) { 897 milter_free(milters); 898 return (0); 899 } 900 901 /* 902 * Receive the filters. 903 */ 904 for (; count > 0; count--) { 905 if ((milter = milter8_receive(stream, milters)) == 0) { 906 msg_warn("cannot receive milters via service %s socket", 907 VSTREAM_PATH(stream)); 908 milter_free(milters); 909 return (0); 910 } 911 if (head == 0) { 912 /* Coverity: milter_free() depends on milters->milter_list. */ 913 milters->milter_list = head = milter; 914 } else { 915 tail->next = milter; 916 } 917 tail = milter; 918 } 919 920 /* 921 * Over to you. 922 */ 923 (void) attr_print(stream, ATTR_FLAG_NONE, 924 SEND_ATTR_INT(MAIL_ATTR_STATUS, 0), 925 ATTR_TYPE_END); 926 return (milters); 927 } 928 929 #ifdef TEST 930 931 /* 932 * Proof-of-concept test program. This can be used interactively, but is 933 * typically used for automated regression tests from a script. 934 */ 935 936 /* System library. */ 937 938 #include <sys/socket.h> 939 #include <stdlib.h> 940 #include <string.h> 941 942 /* Utility library. */ 943 944 #include "msg_vstream.h" 945 #include "vstring_vstream.h" 946 947 /* Global library. */ 948 949 #include <mail_params.h> 950 951 int var_milt_conn_time = 10; 952 int var_milt_cmd_time = 10; 953 int var_milt_msg_time = 100; 954 char *var_milt_protocol = DEF_MILT_PROTOCOL; 955 char *var_milt_def_action = DEF_MILT_DEF_ACTION; 956 957 static void usage(void) 958 { 959 vstream_fprintf(VSTREAM_ERR, "usage: \n" 960 " create names... create and connect\n" 961 #if 0 962 " conn_macros names... define connect macros\n" 963 " helo_macros names... define helo command macros\n" 964 " mail_macros names... define mail command macros\n" 965 " rcpt_macros names... define rcpt command macros\n" 966 " data_macros names... define data command macros\n" 967 " unk_macros names... unknown command macros\n" 968 " message_macros names... define message macros\n" 969 #endif 970 " free disconnect and destroy\n" 971 " connect name addr port family\n" 972 " helo hostname\n" 973 " ehlo hostname\n" 974 " mail from sender...\n" 975 " rcpt to recipient...\n" 976 " data\n" 977 " disconnect\n" 978 " unknown command\n"); 979 vstream_fflush(VSTREAM_ERR); 980 } 981 982 int main(int argc, char **argv) 983 { 984 MILTERS *milters = 0; 985 char *conn_macros, *helo_macros, *mail_macros, *rcpt_macros; 986 char *data_macros, *eoh_macros, *eod_macros, *unk_macros; 987 char *macro_deflts; 988 VSTRING *inbuf = vstring_alloc(100); 989 char *bufp; 990 char *cmd; 991 int ch; 992 int istty = isatty(vstream_fileno(VSTREAM_IN)); 993 994 conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros 995 = eoh_macros = eod_macros = unk_macros = macro_deflts = ""; 996 997 msg_vstream_init(argv[0], VSTREAM_ERR); 998 while ((ch = GETOPT(argc, argv, "V:v")) > 0) { 999 switch (ch) { 1000 default: 1001 msg_fatal("usage: %s [-a action] [-p protocol] [-v]", argv[0]); 1002 case 'a': 1003 var_milt_def_action = optarg; 1004 break; 1005 case 'p': 1006 var_milt_protocol = optarg; 1007 break; 1008 case 'v': 1009 msg_verbose++; 1010 break; 1011 } 1012 } 1013 optind = OPTIND; 1014 1015 for (;;) { 1016 const char *resp = 0; 1017 ARGV *argv; 1018 char **args; 1019 1020 if (istty) { 1021 vstream_printf("- "); 1022 vstream_fflush(VSTREAM_OUT); 1023 } 1024 if (vstring_fgets_nonl(inbuf, VSTREAM_IN) <= 0) 1025 break; 1026 bufp = vstring_str(inbuf); 1027 if (!istty) { 1028 vstream_printf("> %s\n", bufp); 1029 vstream_fflush(VSTREAM_OUT); 1030 } 1031 if (*bufp == '#') 1032 continue; 1033 cmd = mystrtok(&bufp, " "); 1034 if (cmd == 0) { 1035 usage(); 1036 continue; 1037 } 1038 argv = argv_split(bufp, " "); 1039 args = argv->argv; 1040 if (strcmp(cmd, "create") == 0 && argv->argc == 1) { 1041 if (milters != 0) { 1042 msg_warn("deleting existing milters"); 1043 milter_free(milters); 1044 } 1045 milters = milter_create(args[0], var_milt_conn_time, 1046 var_milt_cmd_time, var_milt_msg_time, 1047 var_milt_protocol, var_milt_def_action, 1048 conn_macros, helo_macros, mail_macros, 1049 rcpt_macros, data_macros, eoh_macros, 1050 eod_macros, unk_macros, macro_deflts); 1051 } else if (strcmp(cmd, "free") == 0 && argv->argc == 0) { 1052 if (milters == 0) { 1053 msg_warn("no milters"); 1054 continue; 1055 } 1056 milter_free(milters); 1057 milters = 0; 1058 } else if (strcmp(cmd, "connect") == 0 && argv->argc == 4) { 1059 if (milters == 0) { 1060 msg_warn("no milters"); 1061 continue; 1062 } 1063 resp = milter_conn_event(milters, args[0], args[1], args[2], 1064 strcmp(args[3], "AF_INET") == 0 ? AF_INET : 1065 strcmp(args[3], "AF_INET6") == 0 ? AF_INET6 : 1066 strcmp(args[3], "AF_UNIX") == 0 ? AF_UNIX : 1067 AF_UNSPEC); 1068 } else if (strcmp(cmd, "helo") == 0 && argv->argc == 1) { 1069 if (milters == 0) { 1070 msg_warn("no milters"); 1071 continue; 1072 } 1073 resp = milter_helo_event(milters, args[0], 0); 1074 } else if (strcmp(cmd, "ehlo") == 0 && argv->argc == 1) { 1075 if (milters == 0) { 1076 msg_warn("no milters"); 1077 continue; 1078 } 1079 resp = milter_helo_event(milters, args[0], 1); 1080 } else if (strcmp(cmd, "mail") == 0 && argv->argc > 0) { 1081 if (milters == 0) { 1082 msg_warn("no milters"); 1083 continue; 1084 } 1085 resp = milter_mail_event(milters, (const char **) args); 1086 } else if (strcmp(cmd, "rcpt") == 0 && argv->argc > 0) { 1087 if (milters == 0) { 1088 msg_warn("no milters"); 1089 continue; 1090 } 1091 resp = milter_rcpt_event(milters, 0, (const char **) args); 1092 } else if (strcmp(cmd, "unknown") == 0 && argv->argc > 0) { 1093 if (milters == 0) { 1094 msg_warn("no milters"); 1095 continue; 1096 } 1097 resp = milter_unknown_event(milters, args[0]); 1098 } else if (strcmp(cmd, "data") == 0 && argv->argc == 0) { 1099 if (milters == 0) { 1100 msg_warn("no milters"); 1101 continue; 1102 } 1103 resp = milter_data_event(milters); 1104 } else if (strcmp(cmd, "disconnect") == 0 && argv->argc == 0) { 1105 if (milters == 0) { 1106 msg_warn("no milters"); 1107 continue; 1108 } 1109 milter_disc_event(milters); 1110 } else { 1111 usage(); 1112 } 1113 if (resp != 0) 1114 msg_info("%s", resp); 1115 argv_free(argv); 1116 } 1117 if (milters != 0) 1118 milter_free(milters); 1119 vstring_free(inbuf); 1120 return (0); 1121 } 1122 1123 #endif 1124