1 /* $OpenBSD: control.c,v 1.83 2013/03/11 17:40:11 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/tree.h> 24 #include <sys/param.h> 25 #include <sys/stat.h> 26 #include <sys/socket.h> 27 #include <sys/un.h> 28 29 #include <err.h> 30 #include <errno.h> 31 #include <event.h> 32 #include <fcntl.h> 33 #include <imsg.h> 34 #include <pwd.h> 35 #include <signal.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <time.h> 40 #include <unistd.h> 41 42 #include "smtpd.h" 43 #include "log.h" 44 45 #define CONTROL_BACKLOG 5 46 47 struct ctl_conn { 48 uint32_t id; 49 uint8_t flags; 50 #define CTL_CONN_NOTIFY 0x01 51 struct mproc mproc; 52 uid_t euid; 53 gid_t egid; 54 }; 55 56 struct { 57 struct event ev; 58 int fd; 59 } control_state; 60 61 static void control_imsg(struct mproc *, struct imsg *); 62 static void control_shutdown(void); 63 static void control_listen(void); 64 static void control_accept(int, short, void *); 65 static void control_close(struct ctl_conn *); 66 static void control_sig_handler(int, short, void *); 67 static void control_dispatch_ext(struct mproc *, struct imsg *); 68 static void control_digest_update(const char *, size_t, int); 69 70 static struct stat_backend *stat_backend = NULL; 71 extern const char *backend_stat; 72 73 static uint32_t connid = 0; 74 static struct tree ctl_conns; 75 static struct stat_digest digest; 76 77 #define CONTROL_FD_RESERVE 5 78 79 static void 80 control_imsg(struct mproc *p, struct imsg *imsg) 81 { 82 struct ctl_conn *c; 83 struct stat_value val; 84 struct msg m; 85 const char *key; 86 const void *data; 87 size_t sz; 88 89 if (p->proc == PROC_SMTP) { 90 switch (imsg->hdr.type) { 91 case IMSG_SMTP_ENQUEUE_FD: 92 c = tree_get(&ctl_conns, imsg->hdr.peerid); 93 if (c == NULL) 94 return; 95 m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg->fd, 96 NULL, 0); 97 return; 98 } 99 } 100 if (p->proc == PROC_SCHEDULER) { 101 switch (imsg->hdr.type) { 102 case IMSG_CTL_LIST_MESSAGES: 103 c = tree_get(&ctl_conns, imsg->hdr.peerid); 104 if (c == NULL) 105 return; 106 m_forward(&c->mproc, imsg); 107 return; 108 } 109 } 110 if (p->proc == PROC_QUEUE) { 111 switch (imsg->hdr.type) { 112 case IMSG_CTL_LIST_ENVELOPES: 113 c = tree_get(&ctl_conns, imsg->hdr.peerid); 114 if (c == NULL) 115 return; 116 m_forward(&c->mproc, imsg); 117 return; 118 } 119 } 120 121 switch (imsg->hdr.type) { 122 case IMSG_STAT_INCREMENT: 123 m_msg(&m, imsg); 124 m_get_string(&m, &key); 125 m_get_data(&m, &data, &sz); 126 m_end(&m); 127 memmove(&val, data, sz); 128 if (stat_backend) 129 stat_backend->increment(key, val.u.counter); 130 control_digest_update(key, val.u.counter, 1); 131 return; 132 case IMSG_STAT_DECREMENT: 133 m_msg(&m, imsg); 134 m_get_string(&m, &key); 135 m_get_data(&m, &data, &sz); 136 m_end(&m); 137 memmove(&val, data, sz); 138 if (stat_backend) 139 stat_backend->decrement(key, val.u.counter); 140 control_digest_update(key, val.u.counter, 0); 141 return; 142 case IMSG_STAT_SET: 143 m_msg(&m, imsg); 144 m_get_string(&m, &key); 145 m_get_data(&m, &data, &sz); 146 m_end(&m); 147 memmove(&val, data, sz); 148 if (stat_backend) 149 stat_backend->set(key, &val); 150 return; 151 } 152 153 errx(1, "control_imsg: unexpected %s imsg", 154 imsg_to_str(imsg->hdr.type)); 155 } 156 157 static void 158 control_sig_handler(int sig, short event, void *p) 159 { 160 switch (sig) { 161 case SIGINT: 162 case SIGTERM: 163 control_shutdown(); 164 break; 165 default: 166 fatalx("control_sig_handler: unexpected signal"); 167 } 168 } 169 170 pid_t 171 control(void) 172 { 173 struct sockaddr_un sun; 174 int fd; 175 mode_t old_umask; 176 pid_t pid; 177 struct passwd *pw; 178 struct event ev_sigint; 179 struct event ev_sigterm; 180 181 switch (pid = fork()) { 182 case -1: 183 fatal("control: cannot fork"); 184 case 0: 185 env->sc_pid = getpid(); 186 break; 187 default: 188 return (pid); 189 } 190 191 purge_config(PURGE_EVERYTHING); 192 193 pw = env->sc_pw; 194 195 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 196 fatal("control: socket"); 197 198 bzero(&sun, sizeof(sun)); 199 sun.sun_family = AF_UNIX; 200 if (strlcpy(sun.sun_path, SMTPD_SOCKET, 201 sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) 202 fatal("control: socket name too long"); 203 204 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) 205 fatalx("control socket already listening"); 206 207 if (unlink(SMTPD_SOCKET) == -1) 208 if (errno != ENOENT) 209 fatal("control: cannot unlink socket"); 210 211 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 212 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 213 (void)umask(old_umask); 214 fatal("control: bind"); 215 } 216 (void)umask(old_umask); 217 218 if (chmod(SMTPD_SOCKET, 219 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { 220 (void)unlink(SMTPD_SOCKET); 221 fatal("control: chmod"); 222 } 223 224 session_socket_blockmode(fd, BM_NONBLOCK); 225 control_state.fd = fd; 226 227 stat_backend = env->sc_stat; 228 stat_backend->init(); 229 230 if (chroot(pw->pw_dir) == -1) 231 fatal("control: chroot"); 232 if (chdir("/") == -1) 233 fatal("control: chdir(\"/\")"); 234 235 config_process(PROC_CONTROL); 236 237 if (setgroups(1, &pw->pw_gid) || 238 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 239 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 240 fatal("control: cannot drop privileges"); 241 242 imsg_callback = control_imsg; 243 event_init(); 244 245 signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL); 246 signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL); 247 signal_add(&ev_sigint, NULL); 248 signal_add(&ev_sigterm, NULL); 249 signal(SIGPIPE, SIG_IGN); 250 signal(SIGHUP, SIG_IGN); 251 252 tree_init(&ctl_conns); 253 254 bzero(&digest, sizeof digest); 255 digest.startup = time(NULL); 256 257 config_peer(PROC_SCHEDULER); 258 config_peer(PROC_QUEUE); 259 config_peer(PROC_SMTP); 260 config_peer(PROC_MFA); 261 config_peer(PROC_PARENT); 262 config_peer(PROC_LKA); 263 config_peer(PROC_MDA); 264 config_peer(PROC_MTA); 265 config_done(); 266 267 control_listen(); 268 269 if (event_dispatch() < 0) 270 fatal("event_dispatch"); 271 control_shutdown(); 272 273 return (0); 274 } 275 276 static void 277 control_shutdown(void) 278 { 279 log_info("info: control process exiting"); 280 unlink(SMTPD_SOCKET); 281 _exit(0); 282 } 283 284 static void 285 control_listen(void) 286 { 287 if (listen(control_state.fd, CONTROL_BACKLOG) == -1) 288 fatal("control_listen"); 289 290 event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST, 291 control_accept, NULL); 292 event_add(&control_state.ev, NULL); 293 } 294 295 /* ARGSUSED */ 296 static void 297 control_accept(int listenfd, short event, void *arg) 298 { 299 int connfd; 300 socklen_t len; 301 struct sockaddr_un sun; 302 struct ctl_conn *c; 303 304 if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) 305 goto pause; 306 307 len = sizeof(sun); 308 if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) { 309 if (errno == ENFILE || errno == EMFILE) 310 goto pause; 311 if (errno == EINTR || errno == EWOULDBLOCK || 312 errno == ECONNABORTED) 313 return; 314 fatal("control_accept: accept"); 315 } 316 317 session_socket_blockmode(connfd, BM_NONBLOCK); 318 319 c = xcalloc(1, sizeof(*c), "control_accept"); 320 if (getpeereid(connfd, &c->euid, &c->egid) == -1) 321 fatal("getpeereid"); 322 c->id = ++connid; 323 c->mproc.handler = control_dispatch_ext; 324 c->mproc.data = c; 325 mproc_init(&c->mproc, connfd); 326 mproc_enable(&c->mproc); 327 tree_xset(&ctl_conns, c->id, c); 328 329 stat_backend->increment("control.session", 1); 330 return; 331 332 pause: 333 log_warnx("warn: ctl client limit hit, disabling new connections"); 334 event_del(&control_state.ev); 335 } 336 337 static void 338 control_close(struct ctl_conn *c) 339 { 340 tree_xpop(&ctl_conns, c->id); 341 mproc_clear(&c->mproc); 342 free(c); 343 344 stat_backend->decrement("control.session", 1); 345 346 if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) 347 return; 348 349 if (!event_pending(&control_state.ev, EV_READ, NULL)) { 350 log_warnx("warn: re-enabling ctl connections"); 351 event_add(&control_state.ev, NULL); 352 } 353 } 354 355 static void 356 control_digest_update(const char *key, size_t value, int incr) 357 { 358 size_t *p; 359 360 p = NULL; 361 362 if (!strcmp(key, "smtp.session")) { 363 if (incr) 364 p = &digest.clt_connect; 365 else 366 digest.clt_disconnect += value; 367 } 368 else if (!strcmp(key, "scheduler.envelope")) { 369 if (incr) 370 p = &digest.evp_enqueued; 371 else 372 digest.evp_dequeued += value; 373 } 374 else if (!strcmp(key, "scheduler.envelope.expired")) 375 p = &digest.evp_expired; 376 else if (!strcmp(key, "scheduler.envelope.removed")) 377 p = &digest.evp_removed; 378 else if (!strcmp(key, "scheduler.delivery.ok")) 379 p = &digest.dlv_ok; 380 else if (!strcmp(key, "scheduler.delivery.permfail")) 381 p = &digest.dlv_permfail; 382 else if (!strcmp(key, "scheduler.delivery.tempfail")) 383 p = &digest.dlv_tempfail; 384 else if (!strcmp(key, "scheduler.delivery.loop")) 385 p = &digest.dlv_loop; 386 387 else if (!strcmp(key, "queue.bounce")) 388 p = &digest.evp_bounce; 389 390 if (p) { 391 if (incr) 392 *p = *p + value; 393 else 394 *p = *p - value; 395 } 396 } 397 398 /* ARGSUSED */ 399 static void 400 control_dispatch_ext(struct mproc *p, struct imsg *imsg) 401 { 402 struct ctl_conn *c; 403 int v; 404 struct stat_kv *kvp; 405 char *key; 406 struct stat_value val; 407 size_t len; 408 409 c = p->data; 410 411 if (imsg == NULL) { 412 control_close(c); 413 return; 414 } 415 416 switch (imsg->hdr.type) { 417 case IMSG_SMTP_ENQUEUE_FD: 418 if (env->sc_flags & (SMTPD_SMTP_PAUSED | 419 SMTPD_CONFIGURING | SMTPD_EXITING)) { 420 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 421 return; 422 } 423 m_compose(p_smtp, IMSG_SMTP_ENQUEUE_FD, c->id, 0, -1, 424 &c->euid, sizeof(c->euid)); 425 return; 426 427 case IMSG_STATS: 428 if (c->euid) 429 goto badcred; 430 m_compose(p, IMSG_STATS, 0, 0, -1, NULL, 0); 431 return; 432 433 case IMSG_DIGEST: 434 if (c->euid) 435 goto badcred; 436 digest.timestamp = time(NULL); 437 m_compose(p, IMSG_DIGEST, 0, 0, -1, &digest, sizeof digest); 438 return; 439 440 case IMSG_STATS_GET: 441 if (c->euid) 442 goto badcred; 443 kvp = imsg->data; 444 if (! stat_backend->iter(&kvp->iter, &key, &val)) 445 kvp->iter = NULL; 446 else { 447 strlcpy(kvp->key, key, sizeof kvp->key); 448 kvp->val = val; 449 } 450 m_compose(p, IMSG_STATS_GET, 0, 0, -1, kvp, sizeof *kvp); 451 return; 452 453 case IMSG_CTL_SHUTDOWN: 454 /* NEEDS_FIX */ 455 log_debug("debug: received shutdown request"); 456 457 if (c->euid) 458 goto badcred; 459 460 if (env->sc_flags & SMTPD_EXITING) { 461 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 462 return; 463 } 464 env->sc_flags |= SMTPD_EXITING; 465 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 466 m_compose(p_parent, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); 467 return; 468 469 case IMSG_CTL_VERBOSE: 470 if (c->euid) 471 goto badcred; 472 473 if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) 474 goto badcred; 475 476 memcpy(&v, imsg->data, sizeof(v)); 477 verbose = v; 478 log_verbose(verbose); 479 480 m_create(p_parent, IMSG_CTL_VERBOSE, 0, 0, -1, 9); 481 m_add_int(p_parent, verbose); 482 m_close(p_parent); 483 484 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 485 return; 486 487 case IMSG_CTL_TRACE: 488 if (c->euid) 489 goto badcred; 490 491 if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) 492 goto badcred; 493 494 memcpy(&v, imsg->data, sizeof(v)); 495 verbose |= v; 496 log_verbose(verbose); 497 498 m_create(p_parent, IMSG_CTL_TRACE, 0, 0, -1, 9); 499 m_add_int(p_parent, v); 500 m_close(p_parent); 501 502 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 503 return; 504 505 case IMSG_CTL_UNTRACE: 506 if (c->euid) 507 goto badcred; 508 509 if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) 510 goto badcred; 511 512 memcpy(&v, imsg->data, sizeof(v)); 513 verbose &= ~v; 514 log_verbose(verbose); 515 516 m_create(p_parent, IMSG_CTL_UNTRACE, 0, 0, -1, 9); 517 m_add_int(p_parent, v); 518 m_close(p_parent); 519 520 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 521 return; 522 523 case IMSG_CTL_PROFILE: 524 if (c->euid) 525 goto badcred; 526 527 if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) 528 goto badcred; 529 530 memcpy(&v, imsg->data, sizeof(v)); 531 profiling |= v; 532 533 m_create(p_parent, IMSG_CTL_PROFILE, 0, 0, -1, 9); 534 m_add_int(p_parent, v); 535 m_close(p_parent); 536 537 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 538 return; 539 540 case IMSG_CTL_UNPROFILE: 541 if (c->euid) 542 goto badcred; 543 544 if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) 545 goto badcred; 546 547 memcpy(&v, imsg->data, sizeof(v)); 548 profiling &= ~v; 549 550 m_create(p_parent, IMSG_CTL_UNPROFILE, 0, 0, -1, 9); 551 m_add_int(p_parent, v); 552 m_close(p_parent); 553 554 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 555 return; 556 557 case IMSG_CTL_PAUSE_MDA: 558 if (c->euid) 559 goto badcred; 560 561 if (env->sc_flags & SMTPD_MDA_PAUSED) { 562 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 563 return; 564 } 565 log_info("info: mda paused"); 566 env->sc_flags |= SMTPD_MDA_PAUSED; 567 m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0); 568 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 569 return; 570 571 case IMSG_CTL_PAUSE_MTA: 572 if (c->euid) 573 goto badcred; 574 575 if (env->sc_flags & SMTPD_MTA_PAUSED) { 576 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 577 return; 578 } 579 log_info("info: mta paused"); 580 env->sc_flags |= SMTPD_MTA_PAUSED; 581 m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0); 582 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 583 return; 584 585 case IMSG_CTL_PAUSE_SMTP: 586 if (c->euid) 587 goto badcred; 588 589 if (env->sc_flags & SMTPD_SMTP_PAUSED) { 590 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 591 return; 592 } 593 log_info("info: smtp paused"); 594 env->sc_flags |= SMTPD_SMTP_PAUSED; 595 m_compose(p_smtp, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0); 596 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 597 return; 598 599 case IMSG_CTL_RESUME_MDA: 600 if (c->euid) 601 goto badcred; 602 603 if (! (env->sc_flags & SMTPD_MDA_PAUSED)) { 604 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 605 return; 606 } 607 log_info("info: mda resumed"); 608 env->sc_flags &= ~SMTPD_MDA_PAUSED; 609 m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0); 610 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 611 return; 612 613 case IMSG_CTL_RESUME_MTA: 614 if (c->euid) 615 goto badcred; 616 617 if (!(env->sc_flags & SMTPD_MTA_PAUSED)) { 618 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 619 return; 620 } 621 log_info("info: mta resumed"); 622 env->sc_flags &= ~SMTPD_MTA_PAUSED; 623 m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0); 624 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 625 return; 626 627 case IMSG_CTL_RESUME_SMTP: 628 if (c->euid) 629 goto badcred; 630 631 if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) { 632 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 633 return; 634 } 635 log_info("info: smtp resumed"); 636 env->sc_flags &= ~SMTPD_SMTP_PAUSED; 637 m_forward(p_smtp, imsg); 638 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 639 return; 640 641 case IMSG_CTL_LIST_MESSAGES: 642 if (c->euid) 643 goto badcred; 644 m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1, 645 imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); 646 return; 647 648 case IMSG_CTL_LIST_ENVELOPES: 649 if (c->euid) 650 goto badcred; 651 m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1, 652 imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); 653 return; 654 655 case IMSG_CTL_SCHEDULE: 656 if (c->euid) 657 goto badcred; 658 659 m_forward(p_scheduler, imsg); 660 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 661 return; 662 663 case IMSG_CTL_REMOVE: 664 if (c->euid) 665 goto badcred; 666 667 m_forward(p_scheduler, imsg); 668 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 669 return; 670 671 case IMSG_LKA_UPDATE_TABLE: 672 if (c->euid) 673 goto badcred; 674 675 /* table name too long */ 676 len = strlen(imsg->data); 677 if (len >= MAX_LINE_SIZE) 678 goto invalid; 679 680 m_forward(p_lka, imsg); 681 m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 682 return; 683 684 default: 685 log_debug("debug: control_dispatch_ext: " 686 "error handling %s imsg", 687 imsg_to_str(imsg->hdr.type)); 688 return; 689 } 690 badcred: 691 invalid: 692 m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 693 } 694