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