1 /* $OpenBSD: lka_filter.c,v 1.78 2024/08/12 09:32:44 op Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <errno.h> 20 #include <inttypes.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "smtpd.h" 25 #include "log.h" 26 27 #define PROTOCOL_VERSION "0.7" 28 29 struct filter; 30 struct filter_session; 31 static void filter_protocol_internal(struct filter_session *, uint64_t *, uint64_t, enum filter_phase, const char *); 32 static void filter_protocol(uint64_t, enum filter_phase, const char *); 33 static void filter_protocol_next(uint64_t, uint64_t, enum filter_phase); 34 static void filter_protocol_query(struct filter *, uint64_t, uint64_t, const char *, const char *); 35 36 static void filter_data_internal(struct filter_session *, uint64_t, uint64_t, const char *); 37 static void filter_data(uint64_t, const char *); 38 static void filter_data_next(uint64_t, uint64_t, const char *); 39 static void filter_data_query(struct filter *, uint64_t, uint64_t, const char *); 40 41 static int filter_builtins_notimpl(struct filter_session *, struct filter *, uint64_t, const char *); 42 static int filter_builtins_connect(struct filter_session *, struct filter *, uint64_t, const char *); 43 static int filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *); 44 static int filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *); 45 static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *); 46 static int filter_builtins_data(struct filter_session *, struct filter *, uint64_t, const char *); 47 static int filter_builtins_commit(struct filter_session *, struct filter *, uint64_t, const char *); 48 49 static void filter_result_proceed(uint64_t); 50 static void filter_result_report(uint64_t, const char *); 51 static void filter_result_junk(uint64_t); 52 static void filter_result_rewrite(uint64_t, const char *); 53 static void filter_result_reject(uint64_t, const char *); 54 static void filter_result_disconnect(uint64_t, const char *); 55 56 static void filter_session_io(struct io *, int, void *); 57 void lka_filter_process_response(const char *, const char *); 58 59 60 struct filter_session { 61 uint64_t id; 62 struct io *io; 63 64 char *lastparam; 65 66 char *filter_name; 67 struct sockaddr_storage ss_src; 68 struct sockaddr_storage ss_dest; 69 char *rdns; 70 int fcrdns; 71 72 char *helo; 73 char *username; 74 char *mail_from; 75 76 enum filter_phase phase; 77 }; 78 79 static struct filter_exec { 80 enum filter_phase phase; 81 const char *phase_name; 82 int (*func)(struct filter_session *, struct filter *, uint64_t, const char *); 83 } filter_execs[FILTER_PHASES_COUNT] = { 84 { FILTER_CONNECT, "connect", filter_builtins_connect }, 85 { FILTER_HELO, "helo", filter_builtins_helo }, 86 { FILTER_EHLO, "ehlo", filter_builtins_helo }, 87 { FILTER_STARTTLS, "starttls", filter_builtins_notimpl }, 88 { FILTER_AUTH, "auth", filter_builtins_notimpl }, 89 { FILTER_MAIL_FROM, "mail-from", filter_builtins_mail_from }, 90 { FILTER_RCPT_TO, "rcpt-to", filter_builtins_rcpt_to }, 91 { FILTER_DATA, "data", filter_builtins_data }, 92 { FILTER_DATA_LINE, "data-line", filter_builtins_notimpl }, 93 { FILTER_RSET, "rset", filter_builtins_notimpl }, 94 { FILTER_QUIT, "quit", filter_builtins_notimpl }, 95 { FILTER_NOOP, "noop", filter_builtins_notimpl }, 96 { FILTER_HELP, "help", filter_builtins_notimpl }, 97 { FILTER_WIZ, "wiz", filter_builtins_notimpl }, 98 { FILTER_COMMIT, "commit", filter_builtins_commit }, 99 }; 100 101 struct filter { 102 uint64_t id; 103 uint32_t phases; 104 const char *name; 105 const char *proc; 106 struct filter **chain; 107 size_t chain_size; 108 struct filter_config *config; 109 }; 110 static struct dict filters; 111 112 struct filter_entry { 113 TAILQ_ENTRY(filter_entry) entries; 114 uint64_t id; 115 const char *name; 116 }; 117 118 struct filter_chain { 119 TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)]; 120 }; 121 122 static struct tree sessions; 123 static int filters_inited; 124 125 static struct dict filter_chains; 126 127 struct reporter_proc { 128 TAILQ_ENTRY(reporter_proc) entries; 129 const char *name; 130 }; 131 TAILQ_HEAD(reporters, reporter_proc); 132 133 static struct dict report_smtp_in; 134 static struct dict report_smtp_out; 135 136 static struct smtp_events { 137 const char *event; 138 } smtp_events[] = { 139 { "link-connect" }, 140 { "link-disconnect" }, 141 { "link-greeting" }, 142 { "link-identify" }, 143 { "link-tls" }, 144 { "link-auth" }, 145 146 { "tx-reset" }, 147 { "tx-begin" }, 148 { "tx-mail" }, 149 { "tx-rcpt" }, 150 { "tx-envelope" }, 151 { "tx-data" }, 152 { "tx-commit" }, 153 { "tx-rollback" }, 154 155 { "protocol-client" }, 156 { "protocol-server" }, 157 158 { "filter-report" }, 159 { "filter-response" }, 160 161 { "timeout" }, 162 }; 163 164 static int processors_inited = 0; 165 static struct dict processors; 166 167 struct processor_instance { 168 char *name; 169 struct io *io; 170 struct io *errfd; 171 int ready; 172 uint32_t subsystems; 173 }; 174 175 static void processor_io(struct io *, int, void *); 176 static void processor_errfd(struct io *, int, void *); 177 void lka_filter_process_response(const char *, const char *); 178 179 int 180 lka_proc_ready(void) 181 { 182 void *iter; 183 struct processor_instance *pi; 184 185 iter = NULL; 186 while (dict_iter(&processors, &iter, NULL, (void **)&pi)) 187 if (!pi->ready) 188 return 0; 189 return 1; 190 } 191 192 static void 193 lka_proc_config(struct processor_instance *pi) 194 { 195 io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION); 196 io_printf(pi->io, "config|protocol|%s\n", PROTOCOL_VERSION); 197 io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT); 198 if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN) 199 io_printf(pi->io, "config|subsystem|smtp-in\n"); 200 if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT) 201 io_printf(pi->io, "config|subsystem|smtp-out\n"); 202 io_printf(pi->io, "config|admd|%s\n", 203 env->sc_admd != NULL ? env->sc_admd : env->sc_hostname); 204 io_printf(pi->io, "config|ready\n"); 205 } 206 207 void 208 lka_proc_forked(const char *name, uint32_t subsystems, int fd) 209 { 210 struct processor_instance *processor; 211 212 if (!processors_inited) { 213 dict_init(&processors); 214 processors_inited = 1; 215 } 216 217 processor = xcalloc(1, sizeof *processor); 218 processor->name = xstrdup(name); 219 processor->io = io_new(); 220 processor->subsystems = subsystems; 221 222 io_set_nonblocking(fd); 223 224 io_set_fd(processor->io, fd); 225 io_set_callback(processor->io, processor_io, processor->name); 226 dict_xset(&processors, name, processor); 227 } 228 229 void 230 lka_proc_errfd(const char *name, int fd) 231 { 232 struct processor_instance *processor; 233 234 processor = dict_xget(&processors, name); 235 236 io_set_nonblocking(fd); 237 238 processor->errfd = io_new(); 239 io_set_fd(processor->errfd, fd); 240 io_set_callback(processor->errfd, processor_errfd, processor->name); 241 242 lka_proc_config(processor); 243 } 244 245 struct io * 246 lka_proc_get_io(const char *name) 247 { 248 struct processor_instance *processor; 249 250 processor = dict_xget(&processors, name); 251 252 return processor->io; 253 } 254 255 static void 256 processor_register(const char *name, const char *line) 257 { 258 struct processor_instance *processor; 259 260 processor = dict_xget(&processors, name); 261 262 if (strcmp(line, "register|ready") == 0) { 263 processor->ready = 1; 264 return; 265 } 266 267 if (strncmp(line, "register|report|", 16) == 0) { 268 lka_report_register_hook(name, line+16); 269 return; 270 } 271 272 if (strncmp(line, "register|filter|", 16) == 0) { 273 lka_filter_register_hook(name, line+16); 274 return; 275 } 276 277 fatalx("Invalid register line received: %s", line); 278 } 279 280 static void 281 processor_io(struct io *io, int evt, void *arg) 282 { 283 struct processor_instance *processor; 284 const char *name = arg; 285 char *line = NULL; 286 ssize_t len; 287 288 switch (evt) { 289 case IO_DATAIN: 290 while ((line = io_getline(io, &len)) != NULL) { 291 if (strncmp("register|", line, 9) == 0) { 292 processor_register(name, line); 293 continue; 294 } 295 296 processor = dict_xget(&processors, name); 297 if (!processor->ready) 298 fatalx("Non-register message before register|" 299 "ready: %s", line); 300 else if (strncmp(line, "filter-result|", 14) == 0 || 301 strncmp(line, "filter-dataline|", 16) == 0) 302 lka_filter_process_response(name, line); 303 else if (strncmp(line, "report|", 7) == 0) 304 lka_report_proc(name, line); 305 else 306 fatalx("Invalid filter message type: %s", line); 307 } 308 } 309 } 310 311 static void 312 processor_errfd(struct io *io, int evt, void *arg) 313 { 314 const char *name = arg; 315 char *line = NULL; 316 ssize_t len; 317 318 switch (evt) { 319 case IO_DATAIN: 320 while ((line = io_getline(io, &len)) != NULL) 321 log_warnx("%s: %s", name, line); 322 } 323 } 324 325 void 326 lka_filter_init(void) 327 { 328 void *iter; 329 const char *name; 330 struct filter *filter; 331 struct filter_config *filter_config; 332 size_t i; 333 char buffer[LINE_MAX]; /* for traces */ 334 335 dict_init(&filters); 336 dict_init(&filter_chains); 337 338 /* first pass, allocate and init individual filters */ 339 iter = NULL; 340 while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) { 341 switch (filter_config->filter_type) { 342 case FILTER_TYPE_BUILTIN: 343 filter = xcalloc(1, sizeof(*filter)); 344 filter->name = name; 345 filter->phases |= (1<<filter_config->phase); 346 filter->config = filter_config; 347 dict_set(&filters, name, filter); 348 log_trace(TRACE_FILTERS, "filters init type=builtin, name=%s, hooks=%08x", 349 name, filter->phases); 350 break; 351 352 case FILTER_TYPE_PROC: 353 filter = xcalloc(1, sizeof(*filter)); 354 filter->name = name; 355 filter->proc = filter_config->proc; 356 filter->config = filter_config; 357 dict_set(&filters, name, filter); 358 log_trace(TRACE_FILTERS, "filters init type=proc, name=%s, proc=%s", 359 name, filter_config->proc); 360 break; 361 362 case FILTER_TYPE_CHAIN: 363 break; 364 } 365 } 366 367 /* second pass, allocate and init filter chains but don't build yet */ 368 iter = NULL; 369 while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) { 370 switch (filter_config->filter_type) { 371 case FILTER_TYPE_CHAIN: 372 filter = xcalloc(1, sizeof(*filter)); 373 filter->name = name; 374 filter->chain = xcalloc(filter_config->chain_size, sizeof(void **)); 375 filter->chain_size = filter_config->chain_size; 376 filter->config = filter_config; 377 378 buffer[0] = '\0'; 379 for (i = 0; i < filter->chain_size; ++i) { 380 filter->chain[i] = dict_xget(&filters, filter_config->chain[i]); 381 if (i) 382 (void)strlcat(buffer, ", ", sizeof buffer); 383 (void)strlcat(buffer, filter->chain[i]->name, sizeof buffer); 384 } 385 log_trace(TRACE_FILTERS, "filters init type=chain, name=%s { %s }", name, buffer); 386 387 dict_set(&filters, name, filter); 388 break; 389 390 case FILTER_TYPE_BUILTIN: 391 case FILTER_TYPE_PROC: 392 break; 393 } 394 } 395 } 396 397 void 398 lka_filter_register_hook(const char *name, const char *hook) 399 { 400 struct filter *filter; 401 const char *filter_name; 402 void *iter; 403 size_t i; 404 405 if (strncasecmp(hook, "smtp-in|", 8) == 0) { 406 hook += 8; 407 } 408 else 409 fatalx("Invalid message direction: %s", hook); 410 411 for (i = 0; i < nitems(filter_execs); i++) 412 if (strcmp(hook, filter_execs[i].phase_name) == 0) 413 break; 414 if (i == nitems(filter_execs)) 415 fatalx("Unrecognized report name: %s", hook); 416 417 iter = NULL; 418 while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) 419 if (filter->proc && strcmp(name, filter->proc) == 0) 420 filter->phases |= (1<<filter_execs[i].phase); 421 } 422 423 void 424 lka_filter_ready(void) 425 { 426 struct filter *filter; 427 struct filter *subfilter; 428 const char *filter_name; 429 struct filter_entry *filter_entry; 430 struct filter_chain *filter_chain; 431 void *iter; 432 size_t i; 433 size_t j; 434 435 /* all filters are ready, actually build the filter chains */ 436 iter = NULL; 437 while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) { 438 filter_chain = xcalloc(1, sizeof *filter_chain); 439 for (i = 0; i < nitems(filter_execs); i++) 440 TAILQ_INIT(&filter_chain->chain[i]); 441 dict_set(&filter_chains, filter_name, filter_chain); 442 443 if (filter->chain) { 444 for (i = 0; i < filter->chain_size; i++) { 445 subfilter = filter->chain[i]; 446 for (j = 0; j < nitems(filter_execs); ++j) { 447 if (subfilter->phases & (1<<j)) { 448 filter_entry = xcalloc(1, sizeof *filter_entry); 449 filter_entry->id = generate_uid(); 450 filter_entry->name = subfilter->name; 451 TAILQ_INSERT_TAIL(&filter_chain->chain[j], 452 filter_entry, entries); 453 } 454 } 455 } 456 continue; 457 } 458 459 for (i = 0; i < nitems(filter_execs); ++i) { 460 if (filter->phases & (1<<i)) { 461 filter_entry = xcalloc(1, sizeof *filter_entry); 462 filter_entry->id = generate_uid(); 463 filter_entry->name = filter_name; 464 TAILQ_INSERT_TAIL(&filter_chain->chain[i], 465 filter_entry, entries); 466 } 467 } 468 } 469 } 470 471 int 472 lka_filter_proc_in_session(uint64_t reqid, const char *proc) 473 { 474 struct filter_session *fs; 475 struct filter *filter; 476 size_t i; 477 478 if ((fs = tree_get(&sessions, reqid)) == NULL) 479 return 0; 480 481 filter = dict_get(&filters, fs->filter_name); 482 if (filter == NULL || (filter->proc == NULL && filter->chain == NULL)) 483 return 0; 484 485 if (filter->proc) 486 return strcmp(filter->proc, proc) == 0 ? 1 : 0; 487 488 for (i = 0; i < filter->chain_size; i++) 489 if (filter->chain[i]->proc && 490 strcmp(filter->chain[i]->proc, proc) == 0) 491 return 1; 492 493 return 0; 494 } 495 496 void 497 lka_filter_begin(uint64_t reqid, const char *filter_name) 498 { 499 struct filter_session *fs; 500 501 if (!filters_inited) { 502 tree_init(&sessions); 503 filters_inited = 1; 504 } 505 506 fs = xcalloc(1, sizeof (struct filter_session)); 507 fs->id = reqid; 508 fs->filter_name = xstrdup(filter_name); 509 tree_xset(&sessions, fs->id, fs); 510 511 log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid); 512 } 513 514 void 515 lka_filter_end(uint64_t reqid) 516 { 517 struct filter_session *fs; 518 519 fs = tree_xpop(&sessions, reqid); 520 free(fs->rdns); 521 free(fs->helo); 522 free(fs->mail_from); 523 free(fs->username); 524 free(fs->lastparam); 525 free(fs->filter_name); 526 free(fs); 527 log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-end", reqid); 528 } 529 530 void 531 lka_filter_data_begin(uint64_t reqid) 532 { 533 struct filter_session *fs; 534 int sp[2]; 535 int fd = -1; 536 537 fs = tree_xget(&sessions, reqid); 538 539 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) 540 goto end; 541 io_set_nonblocking(sp[0]); 542 io_set_nonblocking(sp[1]); 543 fd = sp[0]; 544 fs->io = io_new(); 545 io_set_fd(fs->io, sp[1]); 546 io_set_callback(fs->io, filter_session_io, fs); 547 548 end: 549 m_create(p_dispatcher, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, fd); 550 m_add_id(p_dispatcher, reqid); 551 m_add_int(p_dispatcher, fd != -1 ? 1 : 0); 552 m_close(p_dispatcher); 553 log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-begin fd=%d", reqid, fd); 554 } 555 556 void 557 lka_filter_data_end(uint64_t reqid) 558 { 559 struct filter_session *fs; 560 561 fs = tree_xget(&sessions, reqid); 562 if (fs->io) { 563 io_free(fs->io); 564 fs->io = NULL; 565 } 566 log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-end", reqid); 567 } 568 569 static void 570 filter_session_io(struct io *io, int evt, void *arg) 571 { 572 struct filter_session *fs = arg; 573 char *line = NULL; 574 ssize_t len; 575 576 log_trace(TRACE_IO, "filter session: %p: %s %s", fs, io_strevent(evt), 577 io_strio(io)); 578 579 switch (evt) { 580 case IO_DATAIN: 581 nextline: 582 line = io_getline(fs->io, &len); 583 /* No complete line received */ 584 if (line == NULL) 585 return; 586 587 filter_data(fs->id, line); 588 589 goto nextline; 590 } 591 } 592 593 void 594 lka_filter_process_response(const char *name, const char *line) 595 { 596 uint64_t reqid; 597 uint64_t token; 598 char *ep = NULL; 599 const char *kind = NULL; 600 const char *qid = NULL; 601 const char *response = NULL; 602 const char *parameter = NULL; 603 struct filter_session *fs; 604 605 kind = line; 606 607 if ((ep = strchr(kind, '|')) == NULL) 608 fatalx("Missing token: %s", line); 609 qid = ep+1; 610 611 errno = 0; 612 reqid = strtoull(qid, &ep, 16); 613 if (qid[0] == '\0' || *ep != '|') 614 fatalx("Invalid reqid: %s", line); 615 if (errno == ERANGE && reqid == ULLONG_MAX) 616 fatal("Invalid reqid: %s", line); 617 618 qid = ep + 1; 619 token = strtoull(qid, &ep, 16); 620 if (qid[0] == '\0' || *ep != '|') 621 fatalx("Invalid token: %s", line); 622 if (errno == ERANGE && token == ULLONG_MAX) 623 fatal("Invalid token: %s", line); 624 625 response = ep+1; 626 627 /* session can legitimately disappear on a resume */ 628 if ((fs = tree_get(&sessions, reqid)) == NULL) 629 return; 630 631 if (strncmp(kind, "filter-dataline|", 16) == 0) { 632 if (fs->phase != FILTER_DATA_LINE) 633 fatalx("filter-dataline out of dataline phase"); 634 filter_data_next(token, reqid, response); 635 return; 636 } 637 if (fs->phase == FILTER_DATA_LINE) 638 fatalx("filter-result in dataline phase"); 639 640 if ((ep = strchr(response, '|')) != NULL) 641 parameter = ep + 1; 642 643 if (strcmp(response, "proceed") == 0) { 644 filter_protocol_next(token, reqid, 0); 645 return; 646 } else if (strcmp(response, "junk") == 0) { 647 if (fs->phase == FILTER_COMMIT) 648 fatalx("filter-reponse junk after DATA"); 649 filter_result_junk(reqid); 650 return; 651 } else { 652 if (parameter == NULL) 653 fatalx("Missing parameter: %s", line); 654 655 if (strncmp(response, "rewrite|", 8) == 0) 656 filter_result_rewrite(reqid, parameter); 657 else if (strncmp(response, "reject|", 7) == 0) 658 filter_result_reject(reqid, parameter); 659 else if (strncmp(response, "disconnect|", 11) == 0) 660 filter_result_disconnect(reqid, parameter); 661 else if (strncmp(response, "report|", 7) == 0) 662 filter_result_report(reqid, parameter); 663 else 664 fatalx("Invalid directive: %s", line); 665 } 666 } 667 668 void 669 lka_filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param) 670 { 671 filter_protocol(reqid, phase, param); 672 } 673 674 static void 675 filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t reqid, enum filter_phase phase, const char *param) 676 { 677 struct filter_chain *filter_chain; 678 struct filter_entry *filter_entry; 679 struct filter *filter; 680 struct timeval tv; 681 const char *phase_name = filter_execs[phase].phase_name; 682 int resume = 1; 683 684 if (!*token) { 685 fs->phase = phase; 686 resume = 0; 687 } 688 689 /* XXX - this sanity check requires a protocol change, stub for now */ 690 phase = fs->phase; 691 if (fs->phase != phase) 692 fatalx("misbehaving filter"); 693 694 /* based on token, identify the filter_entry we should apply */ 695 filter_chain = dict_get(&filter_chains, fs->filter_name); 696 filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]); 697 if (*token) { 698 TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries) 699 if (filter_entry->id == *token) 700 break; 701 if (filter_entry == NULL) 702 fatalx("misbehaving filter"); 703 filter_entry = TAILQ_NEXT(filter_entry, entries); 704 } 705 706 /* no filter_entry, we either had none or reached end of chain */ 707 if (filter_entry == NULL) { 708 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, resume=%s, " 709 "action=proceed", 710 fs->id, phase_name, resume ? "y" : "n"); 711 filter_result_proceed(reqid); 712 return; 713 } 714 715 /* process param with current filter_entry */ 716 *token = filter_entry->id; 717 filter = dict_get(&filters, filter_entry->name); 718 if (filter->proc) { 719 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 720 "resume=%s, action=deferred, filter=%s", 721 fs->id, phase_name, resume ? "y" : "n", 722 filter->name); 723 filter_protocol_query(filter, filter_entry->id, reqid, 724 filter_execs[fs->phase].phase_name, param); 725 return; /* deferred response */ 726 } 727 728 if (filter_execs[fs->phase].func(fs, filter, reqid, param)) { 729 if (filter->config->rewrite) { 730 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 731 "resume=%s, action=rewrite, filter=%s, query=%s, response=%s", 732 fs->id, phase_name, resume ? "y" : "n", 733 filter->name, 734 param, 735 filter->config->rewrite); 736 filter_result_rewrite(reqid, filter->config->rewrite); 737 return; 738 } 739 else if (filter->config->disconnect) { 740 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 741 "resume=%s, action=disconnect, filter=%s, query=%s, response=%s", 742 fs->id, phase_name, resume ? "y" : "n", 743 filter->name, 744 param, 745 filter->config->disconnect); 746 filter_result_disconnect(reqid, filter->config->disconnect); 747 return; 748 } 749 else if (filter->config->junk) { 750 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 751 "resume=%s, action=junk, filter=%s, query=%s", 752 fs->id, phase_name, resume ? "y" : "n", 753 filter->name, 754 param); 755 filter_result_junk(reqid); 756 return; 757 } else if (filter->config->report) { 758 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 759 "resume=%s, action=report, filter=%s, query=%s response=%s", 760 fs->id, phase_name, resume ? "y" : "n", 761 filter->name, 762 param, filter->config->report); 763 764 gettimeofday(&tv, NULL); 765 lka_report_filter_report(fs->id, filter->name, 1, 766 "smtp-in", &tv, filter->config->report); 767 } else if (filter->config->bypass) { 768 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 769 "resume=%s, action=bypass, filter=%s, query=%s", 770 fs->id, phase_name, resume ? "y" : "n", 771 filter->name, 772 param); 773 filter_result_proceed(reqid); 774 return; 775 } else { 776 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 777 "resume=%s, action=reject, filter=%s, query=%s, response=%s", 778 fs->id, phase_name, resume ? "y" : "n", 779 filter->name, 780 param, 781 filter->config->reject); 782 filter_result_reject(reqid, filter->config->reject); 783 return; 784 } 785 } 786 787 log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " 788 "resume=%s, action=proceed, filter=%s, query=%s", 789 fs->id, phase_name, resume ? "y" : "n", 790 filter->name, 791 param); 792 793 /* filter_entry resulted in proceed, try next filter */ 794 filter_protocol_internal(fs, token, reqid, phase, param); 795 return; 796 } 797 798 static void 799 filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid, const char *line) 800 { 801 struct filter_chain *filter_chain; 802 struct filter_entry *filter_entry; 803 struct filter *filter; 804 805 if (!token) 806 fs->phase = FILTER_DATA_LINE; 807 if (fs->phase != FILTER_DATA_LINE) 808 fatalx("misbehaving filter"); 809 810 /* based on token, identify the filter_entry we should apply */ 811 filter_chain = dict_get(&filter_chains, fs->filter_name); 812 filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]); 813 if (token) { 814 TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries) 815 if (filter_entry->id == token) 816 break; 817 if (filter_entry == NULL) 818 fatalx("misbehaving filter"); 819 filter_entry = TAILQ_NEXT(filter_entry, entries); 820 } 821 822 /* no filter_entry, we either had none or reached end of chain */ 823 if (filter_entry == NULL) { 824 io_printf(fs->io, "%s\n", line); 825 return; 826 } 827 828 /* pass data to the filter */ 829 filter = dict_get(&filters, filter_entry->name); 830 filter_data_query(filter, filter_entry->id, reqid, line); 831 } 832 833 static void 834 filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param) 835 { 836 struct filter_session *fs; 837 uint64_t token = 0; 838 char *nparam = NULL; 839 840 fs = tree_xget(&sessions, reqid); 841 842 switch (phase) { 843 case FILTER_HELO: 844 case FILTER_EHLO: 845 free(fs->helo); 846 fs->helo = xstrdup(param); 847 break; 848 case FILTER_MAIL_FROM: 849 free(fs->mail_from); 850 fs->mail_from = xstrdup(param + 1); 851 *strchr(fs->mail_from, '>') = '\0'; 852 param = fs->mail_from; 853 854 break; 855 case FILTER_RCPT_TO: 856 nparam = xstrdup(param + 1); 857 *strchr(nparam, '>') = '\0'; 858 param = nparam; 859 break; 860 case FILTER_STARTTLS: 861 /* TBD */ 862 break; 863 default: 864 break; 865 } 866 867 free(fs->lastparam); 868 fs->lastparam = xstrdup(param); 869 870 filter_protocol_internal(fs, &token, reqid, phase, param); 871 if (nparam) 872 free(nparam); 873 } 874 875 static void 876 filter_protocol_next(uint64_t token, uint64_t reqid, enum filter_phase phase) 877 { 878 struct filter_session *fs; 879 880 /* session can legitimately disappear on a resume */ 881 if ((fs = tree_get(&sessions, reqid)) == NULL) 882 return; 883 884 filter_protocol_internal(fs, &token, reqid, phase, fs->lastparam); 885 } 886 887 static void 888 filter_data(uint64_t reqid, const char *line) 889 { 890 struct filter_session *fs; 891 892 fs = tree_xget(&sessions, reqid); 893 894 filter_data_internal(fs, 0, reqid, line); 895 } 896 897 static void 898 filter_data_next(uint64_t token, uint64_t reqid, const char *line) 899 { 900 struct filter_session *fs; 901 902 /* session can legitimately disappear on a resume */ 903 if ((fs = tree_get(&sessions, reqid)) == NULL) 904 return; 905 906 filter_data_internal(fs, token, reqid, line); 907 } 908 909 static void 910 filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *phase, const char *param) 911 { 912 int n; 913 struct filter_session *fs; 914 struct timeval tv; 915 916 gettimeofday(&tv, NULL); 917 918 fs = tree_xget(&sessions, reqid); 919 if (strcmp(phase, "connect") == 0) 920 n = io_printf(lka_proc_get_io(filter->proc), 921 "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n", 922 PROTOCOL_VERSION, 923 (long long)tv.tv_sec, (long)tv.tv_usec, 924 phase, reqid, token, fs->rdns, param); 925 else 926 n = io_printf(lka_proc_get_io(filter->proc), 927 "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n", 928 PROTOCOL_VERSION, 929 (long long)tv.tv_sec, (long)tv.tv_usec, 930 phase, reqid, token, param); 931 if (n == -1) 932 fatalx("failed to write to processor"); 933 } 934 935 static void 936 filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *line) 937 { 938 int n; 939 struct timeval tv; 940 941 gettimeofday(&tv, NULL); 942 943 n = io_printf(lka_proc_get_io(filter->proc), 944 "filter|%s|%lld.%06ld|smtp-in|data-line|" 945 "%016"PRIx64"|%016"PRIx64"|%s\n", 946 PROTOCOL_VERSION, 947 (long long)tv.tv_sec, (long)tv.tv_usec, 948 reqid, token, line); 949 if (n == -1) 950 fatalx("failed to write to processor"); 951 } 952 953 static void 954 filter_result_proceed(uint64_t reqid) 955 { 956 m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 957 m_add_id(p_dispatcher, reqid); 958 m_add_int(p_dispatcher, FILTER_PROCEED); 959 m_close(p_dispatcher); 960 } 961 962 static void 963 filter_result_report(uint64_t reqid, const char *param) 964 { 965 m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 966 m_add_id(p_dispatcher, reqid); 967 m_add_int(p_dispatcher, FILTER_REPORT); 968 m_add_string(p_dispatcher, param); 969 m_close(p_dispatcher); 970 } 971 972 static void 973 filter_result_junk(uint64_t reqid) 974 { 975 m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 976 m_add_id(p_dispatcher, reqid); 977 m_add_int(p_dispatcher, FILTER_JUNK); 978 m_close(p_dispatcher); 979 } 980 981 static void 982 filter_result_rewrite(uint64_t reqid, const char *param) 983 { 984 m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 985 m_add_id(p_dispatcher, reqid); 986 m_add_int(p_dispatcher, FILTER_REWRITE); 987 m_add_string(p_dispatcher, param); 988 m_close(p_dispatcher); 989 } 990 991 static void 992 filter_result_reject(uint64_t reqid, const char *message) 993 { 994 m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 995 m_add_id(p_dispatcher, reqid); 996 m_add_int(p_dispatcher, FILTER_REJECT); 997 m_add_string(p_dispatcher, message); 998 m_close(p_dispatcher); 999 } 1000 1001 static void 1002 filter_result_disconnect(uint64_t reqid, const char *message) 1003 { 1004 m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1); 1005 m_add_id(p_dispatcher, reqid); 1006 m_add_int(p_dispatcher, FILTER_DISCONNECT); 1007 m_add_string(p_dispatcher, message); 1008 m_close(p_dispatcher); 1009 } 1010 1011 1012 /* below is code for builtin filters */ 1013 1014 static int 1015 filter_check_rdns_table(struct filter *filter, enum table_service kind, const char *key) 1016 { 1017 int ret = 0; 1018 1019 if (filter->config->rdns_table == NULL) 1020 return 0; 1021 1022 if (table_match(filter->config->rdns_table, kind, key) > 0) 1023 ret = 1; 1024 1025 return filter->config->not_rdns_table < 0 ? !ret : ret; 1026 } 1027 1028 static int 1029 filter_check_rdns_regex(struct filter *filter, const char *key) 1030 { 1031 int ret = 0; 1032 1033 if (filter->config->rdns_regex == NULL) 1034 return 0; 1035 1036 if (table_match(filter->config->rdns_regex, K_REGEX, key) > 0) 1037 ret = 1; 1038 return filter->config->not_rdns_regex < 0 ? !ret : ret; 1039 } 1040 1041 static int 1042 filter_check_src_table(struct filter *filter, enum table_service kind, const char *key) 1043 { 1044 int ret = 0; 1045 1046 if (filter->config->src_table == NULL) 1047 return 0; 1048 1049 if (table_match(filter->config->src_table, kind, key) > 0) 1050 ret = 1; 1051 return filter->config->not_src_table < 0 ? !ret : ret; 1052 } 1053 1054 static int 1055 filter_check_src_regex(struct filter *filter, const char *key) 1056 { 1057 int ret = 0; 1058 1059 if (filter->config->src_regex == NULL) 1060 return 0; 1061 1062 if (table_match(filter->config->src_regex, K_REGEX, key) > 0) 1063 ret = 1; 1064 return filter->config->not_src_regex < 0 ? !ret : ret; 1065 } 1066 1067 static int 1068 filter_check_helo_table(struct filter *filter, enum table_service kind, const char *key) 1069 { 1070 int ret = 0; 1071 1072 if (filter->config->helo_table == NULL) 1073 return 0; 1074 1075 if (table_match(filter->config->helo_table, kind, key) > 0) 1076 ret = 1; 1077 return filter->config->not_helo_table < 0 ? !ret : ret; 1078 } 1079 1080 static int 1081 filter_check_helo_regex(struct filter *filter, const char *key) 1082 { 1083 int ret = 0; 1084 1085 if (filter->config->helo_regex == NULL) 1086 return 0; 1087 1088 if (table_match(filter->config->helo_regex, K_REGEX, key) > 0) 1089 ret = 1; 1090 return filter->config->not_helo_regex < 0 ? !ret : ret; 1091 } 1092 1093 static int 1094 filter_check_auth(struct filter *filter, const char *username) 1095 { 1096 int ret = 0; 1097 1098 if (!filter->config->auth) 1099 return 0; 1100 1101 ret = username ? 1 : 0; 1102 1103 return filter->config->not_auth < 0 ? !ret : ret; 1104 } 1105 1106 static int 1107 filter_check_auth_table(struct filter *filter, enum table_service kind, const char *key) 1108 { 1109 int ret = 0; 1110 1111 if (filter->config->auth_table == NULL) 1112 return 0; 1113 1114 if (key && table_match(filter->config->auth_table, kind, key) > 0) 1115 ret = 1; 1116 1117 return filter->config->not_auth_table < 0 ? !ret : ret; 1118 } 1119 1120 static int 1121 filter_check_auth_regex(struct filter *filter, const char *key) 1122 { 1123 int ret = 0; 1124 1125 if (filter->config->auth_regex == NULL) 1126 return 0; 1127 1128 if (key && table_match(filter->config->auth_regex, K_REGEX, key) > 0) 1129 ret = 1; 1130 return filter->config->not_auth_regex < 0 ? !ret : ret; 1131 } 1132 1133 1134 static int 1135 filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key) 1136 { 1137 int ret = 0; 1138 1139 if (filter->config->mail_from_table == NULL) 1140 return 0; 1141 1142 if (table_match(filter->config->mail_from_table, kind, key) > 0) 1143 ret = 1; 1144 return filter->config->not_mail_from_table < 0 ? !ret : ret; 1145 } 1146 1147 static int 1148 filter_check_mail_from_regex(struct filter *filter, const char *key) 1149 { 1150 int ret = 0; 1151 1152 if (filter->config->mail_from_regex == NULL) 1153 return 0; 1154 1155 if (table_match(filter->config->mail_from_regex, K_REGEX, key) > 0) 1156 ret = 1; 1157 return filter->config->not_mail_from_regex < 0 ? !ret : ret; 1158 } 1159 1160 static int 1161 filter_check_rcpt_to_table(struct filter *filter, enum table_service kind, const char *key) 1162 { 1163 int ret = 0; 1164 1165 if (filter->config->rcpt_to_table == NULL) 1166 return 0; 1167 1168 if (table_match(filter->config->rcpt_to_table, kind, key) > 0) 1169 ret = 1; 1170 return filter->config->not_rcpt_to_table < 0 ? !ret : ret; 1171 } 1172 1173 static int 1174 filter_check_rcpt_to_regex(struct filter *filter, const char *key) 1175 { 1176 int ret = 0; 1177 1178 if (filter->config->rcpt_to_regex == NULL) 1179 return 0; 1180 1181 if (table_match(filter->config->rcpt_to_regex, K_REGEX, key) > 0) 1182 ret = 1; 1183 return filter->config->not_rcpt_to_regex < 0 ? !ret : ret; 1184 } 1185 1186 static int 1187 filter_check_fcrdns(struct filter *filter, int fcrdns) 1188 { 1189 int ret = 0; 1190 1191 if (!filter->config->fcrdns) 1192 return 0; 1193 1194 ret = fcrdns == 1; 1195 return filter->config->not_fcrdns < 0 ? !ret : ret; 1196 } 1197 1198 static int 1199 filter_check_rdns(struct filter *filter, const char *hostname) 1200 { 1201 int ret = 0; 1202 struct netaddr netaddr; 1203 1204 if (!filter->config->rdns) 1205 return 0; 1206 1207 /* this is a hack until smtp session properly deals with lack of rdns */ 1208 ret = strcmp("<unknown>", hostname); 1209 if (ret == 0) 1210 return filter->config->not_rdns < 0 ? !ret : ret; 1211 1212 /* if text_to_netaddress succeeds, 1213 * we don't have an rDNS so the filter should match 1214 */ 1215 ret = !text_to_netaddr(&netaddr, hostname); 1216 return filter->config->not_rdns < 0 ? !ret : ret; 1217 } 1218 1219 static int 1220 filter_builtins_notimpl(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1221 { 1222 return 0; 1223 } 1224 1225 static int 1226 filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_t reqid) 1227 { 1228 return filter_check_fcrdns(filter, fs->fcrdns) || 1229 filter_check_rdns(filter, fs->rdns) || 1230 filter_check_rdns_table(filter, K_DOMAIN, fs->rdns) || 1231 filter_check_rdns_regex(filter, fs->rdns) || 1232 filter_check_src_table(filter, K_NETADDR, ss_to_text(&fs->ss_src)) || 1233 filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) || 1234 filter_check_helo_table(filter, K_DOMAIN, fs->helo) || 1235 filter_check_helo_regex(filter, fs->helo) || 1236 filter_check_auth(filter, fs->username) || 1237 filter_check_auth_table(filter, K_STRING, fs->username) || 1238 filter_check_auth_table(filter, K_CREDENTIALS, fs->username) || 1239 filter_check_auth_regex(filter, fs->username) || 1240 filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) || 1241 filter_check_mail_from_regex(filter, fs->mail_from); 1242 } 1243 1244 static int 1245 filter_builtins_connect(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1246 { 1247 return filter_builtins_global(fs, filter, reqid); 1248 } 1249 1250 static int 1251 filter_builtins_helo(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1252 { 1253 return filter_builtins_global(fs, filter, reqid); 1254 } 1255 1256 static int 1257 filter_builtins_mail_from(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1258 { 1259 return filter_builtins_global(fs, filter, reqid); 1260 } 1261 1262 static int 1263 filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1264 { 1265 return filter_builtins_global(fs, filter, reqid) || 1266 filter_check_rcpt_to_table(filter, K_MAILADDR, param) || 1267 filter_check_rcpt_to_regex(filter, param); 1268 } 1269 1270 static int 1271 filter_builtins_data(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1272 { 1273 return filter_builtins_global(fs, filter, reqid); 1274 } 1275 1276 static int 1277 filter_builtins_commit(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) 1278 { 1279 return filter_builtins_global(fs, filter, reqid); 1280 } 1281 1282 static void 1283 report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *, 1284 const char *, ...) __attribute__((__format__ (printf, 5, 6))); 1285 1286 void 1287 lka_report_init(void) 1288 { 1289 struct reporters *tailq; 1290 size_t i; 1291 1292 dict_init(&report_smtp_in); 1293 dict_init(&report_smtp_out); 1294 1295 for (i = 0; i < nitems(smtp_events); ++i) { 1296 tailq = xcalloc(1, sizeof (struct reporters)); 1297 TAILQ_INIT(tailq); 1298 dict_xset(&report_smtp_in, smtp_events[i].event, tailq); 1299 1300 tailq = xcalloc(1, sizeof (struct reporters)); 1301 TAILQ_INIT(tailq); 1302 dict_xset(&report_smtp_out, smtp_events[i].event, tailq); 1303 } 1304 } 1305 1306 void 1307 lka_report_register_hook(const char *name, const char *hook) 1308 { 1309 struct dict *subsystem; 1310 struct reporter_proc *rp; 1311 struct reporters *tailq; 1312 void *iter; 1313 size_t i; 1314 1315 if (strncmp(hook, "smtp-in|", 8) == 0) { 1316 subsystem = &report_smtp_in; 1317 hook += 8; 1318 } 1319 else if (strncmp(hook, "smtp-out|", 9) == 0) { 1320 subsystem = &report_smtp_out; 1321 hook += 9; 1322 } 1323 else 1324 fatalx("Invalid message direction: %s", hook); 1325 1326 if (strcmp(hook, "*") == 0) { 1327 iter = NULL; 1328 while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) { 1329 rp = xcalloc(1, sizeof *rp); 1330 rp->name = xstrdup(name); 1331 TAILQ_INSERT_TAIL(tailq, rp, entries); 1332 } 1333 return; 1334 } 1335 1336 for (i = 0; i < nitems(smtp_events); i++) 1337 if (strcmp(hook, smtp_events[i].event) == 0) 1338 break; 1339 if (i == nitems(smtp_events)) 1340 fatalx("Unrecognized report name: %s", hook); 1341 1342 tailq = dict_get(subsystem, hook); 1343 rp = xcalloc(1, sizeof *rp); 1344 rp->name = xstrdup(name); 1345 TAILQ_INSERT_TAIL(tailq, rp, entries); 1346 } 1347 1348 static void 1349 report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event, 1350 const char *format, ...) 1351 { 1352 va_list ap; 1353 struct dict *d; 1354 struct reporters *tailq; 1355 struct reporter_proc *rp; 1356 1357 if (strcmp("smtp-in", direction) == 0) 1358 d = &report_smtp_in; 1359 1360 else if (strcmp("smtp-out", direction) == 0) 1361 d = &report_smtp_out; 1362 1363 else 1364 fatalx("unexpected direction: %s", direction); 1365 1366 tailq = dict_xget(d, event); 1367 TAILQ_FOREACH(rp, tailq, entries) { 1368 if (!lka_filter_proc_in_session(reqid, rp->name)) 1369 continue; 1370 1371 va_start(ap, format); 1372 if (io_printf(lka_proc_get_io(rp->name), 1373 "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s", 1374 PROTOCOL_VERSION, (long long)tv->tv_sec, (long)tv->tv_usec, 1375 direction, event, reqid, 1376 format[0] != '\n' ? "|" : "") == -1 || 1377 io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1) 1378 fatalx("failed to write to processor"); 1379 va_end(ap); 1380 } 1381 } 1382 1383 void 1384 lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns, 1385 int fcrdns, 1386 const struct sockaddr_storage *ss_src, 1387 const struct sockaddr_storage *ss_dest) 1388 { 1389 struct filter_session *fs; 1390 char src[NI_MAXHOST + 5]; 1391 char dest[NI_MAXHOST + 5]; 1392 uint16_t src_port = 0; 1393 uint16_t dest_port = 0; 1394 const char *fcrdns_str; 1395 1396 if (ss_src->ss_family == AF_INET) 1397 src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port); 1398 else if (ss_src->ss_family == AF_INET6) 1399 src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port); 1400 1401 if (ss_dest->ss_family == AF_INET) 1402 dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port); 1403 else if (ss_dest->ss_family == AF_INET6) 1404 dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port); 1405 1406 if (strcmp(ss_to_text(ss_src), "local") == 0) { 1407 (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET); 1408 (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET); 1409 } else { 1410 (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port); 1411 (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port); 1412 } 1413 1414 switch (fcrdns) { 1415 case 1: 1416 fcrdns_str = "pass"; 1417 break; 1418 case 0: 1419 fcrdns_str = "fail"; 1420 break; 1421 default: 1422 fcrdns_str = "error"; 1423 break; 1424 } 1425 1426 fs = tree_xget(&sessions, reqid); 1427 fs->rdns = xstrdup(rdns); 1428 fs->fcrdns = fcrdns; 1429 fs->ss_src = *ss_src; 1430 fs->ss_dest = *ss_dest; 1431 1432 report_smtp_broadcast(reqid, direction, tv, "link-connect", 1433 "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest); 1434 } 1435 1436 void 1437 lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid) 1438 { 1439 report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n"); 1440 } 1441 1442 void 1443 lka_report_smtp_link_greeting(const char *direction, uint64_t reqid, 1444 struct timeval *tv, const char *domain) 1445 { 1446 report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n", 1447 domain); 1448 } 1449 1450 void 1451 lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid, 1452 const char *username, const char *result) 1453 { 1454 struct filter_session *fs; 1455 1456 if (strcmp(result, "pass") == 0) { 1457 fs = tree_xget(&sessions, reqid); 1458 fs->username = xstrdup(username); 1459 } 1460 report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n", 1461 result, username); 1462 } 1463 1464 void 1465 lka_report_smtp_link_identify(const char *direction, struct timeval *tv, 1466 uint64_t reqid, const char *method, const char *heloname) 1467 { 1468 report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n", 1469 method, heloname); 1470 } 1471 1472 void 1473 lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers) 1474 { 1475 report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n", 1476 ciphers); 1477 } 1478 1479 void 1480 lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) 1481 { 1482 report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n", 1483 msgid); 1484 } 1485 1486 void 1487 lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) 1488 { 1489 report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n", 1490 msgid); 1491 } 1492 1493 void 1494 lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) 1495 { 1496 const char *result; 1497 1498 switch (ok) { 1499 case 1: 1500 result = "ok"; 1501 break; 1502 case 0: 1503 result = "permfail"; 1504 break; 1505 default: 1506 result = "tempfail"; 1507 break; 1508 } 1509 report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n", 1510 msgid, result, address); 1511 } 1512 1513 void 1514 lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) 1515 { 1516 const char *result; 1517 1518 switch (ok) { 1519 case 1: 1520 result = "ok"; 1521 break; 1522 case 0: 1523 result = "permfail"; 1524 break; 1525 default: 1526 result = "tempfail"; 1527 break; 1528 } 1529 report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n", 1530 msgid, result, address); 1531 } 1532 1533 void 1534 lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid) 1535 { 1536 report_smtp_broadcast(reqid, direction, tv, "tx-envelope", 1537 "%08x|%016"PRIx64"\n", msgid, evpid); 1538 } 1539 1540 void 1541 lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok) 1542 { 1543 const char *result; 1544 1545 switch (ok) { 1546 case 1: 1547 result = "ok"; 1548 break; 1549 case 0: 1550 result = "permfail"; 1551 break; 1552 default: 1553 result = "tempfail"; 1554 break; 1555 } 1556 report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n", 1557 msgid, result); 1558 } 1559 1560 void 1561 lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz) 1562 { 1563 report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n", 1564 msgid, msgsz); 1565 } 1566 1567 void 1568 lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) 1569 { 1570 report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n", 1571 msgid); 1572 } 1573 1574 void 1575 lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command) 1576 { 1577 report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n", 1578 command); 1579 } 1580 1581 void 1582 lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response) 1583 { 1584 report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n", 1585 response); 1586 } 1587 1588 void 1589 lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid, 1590 int phase, int response, const char *param) 1591 { 1592 const char *phase_name; 1593 const char *response_name; 1594 1595 switch (phase) { 1596 case FILTER_CONNECT: 1597 phase_name = "connected"; 1598 break; 1599 case FILTER_HELO: 1600 phase_name = "helo"; 1601 break; 1602 case FILTER_EHLO: 1603 phase_name = "ehlo"; 1604 break; 1605 case FILTER_STARTTLS: 1606 phase_name = "tls"; 1607 break; 1608 case FILTER_AUTH: 1609 phase_name = "auth"; 1610 break; 1611 case FILTER_MAIL_FROM: 1612 phase_name = "mail-from"; 1613 break; 1614 case FILTER_RCPT_TO: 1615 phase_name = "rcpt-to"; 1616 break; 1617 case FILTER_DATA: 1618 phase_name = "data"; 1619 break; 1620 case FILTER_DATA_LINE: 1621 phase_name = "data-line"; 1622 break; 1623 case FILTER_RSET: 1624 phase_name = "rset"; 1625 break; 1626 case FILTER_QUIT: 1627 phase_name = "quit"; 1628 break; 1629 case FILTER_NOOP: 1630 phase_name = "noop"; 1631 break; 1632 case FILTER_HELP: 1633 phase_name = "help"; 1634 break; 1635 case FILTER_WIZ: 1636 phase_name = "wiz"; 1637 break; 1638 case FILTER_COMMIT: 1639 phase_name = "commit"; 1640 break; 1641 default: 1642 phase_name = ""; 1643 } 1644 1645 switch (response) { 1646 case FILTER_PROCEED: 1647 response_name = "proceed"; 1648 break; 1649 case FILTER_REPORT: 1650 response_name = "report"; 1651 break; 1652 case FILTER_JUNK: 1653 response_name = "junk"; 1654 break; 1655 case FILTER_REWRITE: 1656 response_name = "rewrite"; 1657 break; 1658 case FILTER_REJECT: 1659 response_name = "reject"; 1660 break; 1661 case FILTER_DISCONNECT: 1662 response_name = "disconnect"; 1663 break; 1664 default: 1665 response_name = ""; 1666 } 1667 1668 report_smtp_broadcast(reqid, direction, tv, "filter-response", 1669 "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "", 1670 param ? param : ""); 1671 } 1672 1673 void 1674 lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid) 1675 { 1676 report_smtp_broadcast(reqid, direction, tv, "timeout", "\n"); 1677 } 1678 1679 void 1680 lka_report_filter_report(uint64_t reqid, const char *name, int builtin, 1681 const char *direction, struct timeval *tv, const char *message) 1682 { 1683 report_smtp_broadcast(reqid, direction, tv, "filter-report", 1684 "%s|%s|%s\n", builtin ? "builtin" : "proc", 1685 name, message); 1686 } 1687 1688 void 1689 lka_report_proc(const char *name, const char *line) 1690 { 1691 char buffer[LINE_MAX]; 1692 struct timeval tv; 1693 char *ep, *sp, *direction; 1694 uint64_t reqid; 1695 1696 if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer)) 1697 fatalx("Invalid report: line too long: %s", line); 1698 1699 errno = 0; 1700 tv.tv_sec = strtoll(buffer, &ep, 10); 1701 if (ep[0] != '.' || errno != 0) 1702 fatalx("Invalid report: invalid time: %s", line); 1703 sp = ep + 1; 1704 tv.tv_usec = strtol(sp, &ep, 10); 1705 if (ep[0] != '|' || errno != 0) 1706 fatalx("Invalid report: invalid time: %s", line); 1707 if (ep - sp != 6) 1708 fatalx("Invalid report: invalid time: %s", line); 1709 1710 direction = ep + 1; 1711 if (strncmp(direction, "smtp-in|", 8) == 0) { 1712 direction[7] = '\0'; 1713 direction += 7; 1714 #if 0 1715 } else if (strncmp(direction, "smtp-out|", 9) == 0) { 1716 direction[8] = '\0'; 1717 direction += 8; 1718 #endif 1719 } else 1720 fatalx("Invalid report: invalid direction: %s", line); 1721 1722 reqid = strtoull(sp, &ep, 16); 1723 if (ep[0] != '|' || errno != 0) 1724 fatalx("Invalid report: invalid reqid: %s", line); 1725 sp = ep + 1; 1726 1727 lka_report_filter_report(reqid, name, 0, direction, &tv, sp); 1728 } 1729