1 /* $OpenBSD: mproc.c,v 1.35 2019/10/03 05:50:28 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Eric Faurot <eric@faurot.net> 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/socket.h> 21 #include <sys/tree.h> 22 #include <sys/queue.h> 23 #include <sys/uio.h> 24 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 #include <arpa/nameser.h> 28 29 #include <err.h> 30 #include <errno.h> 31 #include <event.h> 32 #include <imsg.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <limits.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "smtpd.h" 40 #include "log.h" 41 42 static void mproc_dispatch(int, short, void *); 43 44 static ssize_t imsg_read_nofd(struct imsgbuf *); 45 46 int 47 mproc_fork(struct mproc *p, const char *path, char *argv[]) 48 { 49 int sp[2]; 50 51 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) 52 return (-1); 53 54 io_set_nonblocking(sp[0]); 55 io_set_nonblocking(sp[1]); 56 57 if ((p->pid = fork()) == -1) 58 goto err; 59 60 if (p->pid == 0) { 61 /* child process */ 62 dup2(sp[0], STDIN_FILENO); 63 if (closefrom(STDERR_FILENO + 1) == -1) 64 exit(1); 65 66 execv(path, argv); 67 err(1, "execv: %s", path); 68 } 69 70 /* parent process */ 71 close(sp[0]); 72 mproc_init(p, sp[1]); 73 return (0); 74 75 err: 76 log_warn("warn: Failed to start process %s, instance of %s", argv[0], path); 77 close(sp[0]); 78 close(sp[1]); 79 return (-1); 80 } 81 82 void 83 mproc_init(struct mproc *p, int fd) 84 { 85 imsg_init(&p->imsgbuf, fd); 86 } 87 88 void 89 mproc_clear(struct mproc *p) 90 { 91 log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid); 92 93 event_del(&p->ev); 94 close(p->imsgbuf.fd); 95 imsg_clear(&p->imsgbuf); 96 } 97 98 void 99 mproc_enable(struct mproc *p) 100 { 101 if (p->enable == 0) { 102 log_trace(TRACE_MPROC, "mproc: %s -> %s: enabled", 103 proc_name(smtpd_process), 104 proc_name(p->proc)); 105 p->enable = 1; 106 } 107 mproc_event_add(p); 108 } 109 110 void 111 mproc_disable(struct mproc *p) 112 { 113 if (p->enable == 1) { 114 log_trace(TRACE_MPROC, "mproc: %s -> %s: disabled", 115 proc_name(smtpd_process), 116 proc_name(p->proc)); 117 p->enable = 0; 118 } 119 mproc_event_add(p); 120 } 121 122 void 123 mproc_event_add(struct mproc *p) 124 { 125 short events; 126 127 if (p->enable) 128 events = EV_READ; 129 else 130 events = 0; 131 132 if (p->imsgbuf.w.queued) 133 events |= EV_WRITE; 134 135 if (p->events) 136 event_del(&p->ev); 137 138 p->events = events; 139 if (events) { 140 event_set(&p->ev, p->imsgbuf.fd, events, mproc_dispatch, p); 141 event_add(&p->ev, NULL); 142 } 143 } 144 145 static void 146 mproc_dispatch(int fd, short event, void *arg) 147 { 148 struct mproc *p = arg; 149 struct imsg imsg; 150 ssize_t n; 151 152 p->events = 0; 153 154 if (event & EV_READ) { 155 156 if (p->proc == PROC_CLIENT) 157 n = imsg_read_nofd(&p->imsgbuf); 158 else 159 n = imsg_read(&p->imsgbuf); 160 161 switch (n) { 162 case -1: 163 if (errno == EAGAIN) 164 break; 165 log_warn("warn: %s -> %s: imsg_read", 166 proc_name(smtpd_process), p->name); 167 fatal("exiting"); 168 /* NOTREACHED */ 169 case 0: 170 /* this pipe is dead, so remove the event handler */ 171 log_debug("debug: %s -> %s: pipe closed", 172 proc_name(smtpd_process), p->name); 173 p->handler(p, NULL); 174 return; 175 default: 176 break; 177 } 178 } 179 180 if (event & EV_WRITE) { 181 n = msgbuf_write(&p->imsgbuf.w); 182 if (n == 0 || (n == -1 && errno != EAGAIN)) { 183 /* this pipe is dead, so remove the event handler */ 184 log_debug("debug: %s -> %s: pipe closed", 185 proc_name(smtpd_process), p->name); 186 p->handler(p, NULL); 187 return; 188 } 189 } 190 191 for (;;) { 192 if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { 193 194 if (smtpd_process == PROC_CONTROL && 195 p->proc == PROC_CLIENT) { 196 log_warnx("warn: client sent invalid imsg " 197 "over control socket"); 198 p->handler(p, NULL); 199 return; 200 } 201 log_warn("fatal: %s: error in imsg_get for %s", 202 proc_name(smtpd_process), p->name); 203 fatalx(NULL); 204 } 205 if (n == 0) 206 break; 207 208 p->handler(p, &imsg); 209 210 imsg_free(&imsg); 211 } 212 213 mproc_event_add(p); 214 } 215 216 /* This should go into libutil */ 217 static ssize_t 218 imsg_read_nofd(struct imsgbuf *ibuf) 219 { 220 ssize_t n; 221 char *buf; 222 size_t len; 223 224 buf = ibuf->r.buf + ibuf->r.wpos; 225 len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 226 227 while ((n = recv(ibuf->fd, buf, len, 0)) == -1) { 228 if (errno != EINTR) 229 return (n); 230 } 231 232 ibuf->r.wpos += n; 233 return (n); 234 } 235 236 void 237 m_forward(struct mproc *p, struct imsg *imsg) 238 { 239 imsg_compose(&p->imsgbuf, imsg->hdr.type, imsg->hdr.peerid, 240 imsg->hdr.pid, imsg->fd, imsg->data, 241 imsg->hdr.len - sizeof(imsg->hdr)); 242 243 if (imsg->hdr.type != IMSG_STAT_DECREMENT && 244 imsg->hdr.type != IMSG_STAT_INCREMENT) 245 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (forward)", 246 proc_name(smtpd_process), 247 proc_name(p->proc), 248 imsg->hdr.len - sizeof(imsg->hdr), 249 imsg_to_str(imsg->hdr.type)); 250 251 mproc_event_add(p); 252 } 253 254 void 255 m_compose(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, 256 void *data, size_t len) 257 { 258 imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len); 259 260 if (type != IMSG_STAT_DECREMENT && 261 type != IMSG_STAT_INCREMENT) 262 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", 263 proc_name(smtpd_process), 264 proc_name(p->proc), 265 len, 266 imsg_to_str(type)); 267 268 mproc_event_add(p); 269 } 270 271 void 272 m_composev(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, 273 int fd, const struct iovec *iov, int n) 274 { 275 size_t len; 276 int i; 277 278 imsg_composev(&p->imsgbuf, type, peerid, pid, fd, iov, n); 279 280 len = 0; 281 for (i = 0; i < n; i++) 282 len += iov[i].iov_len; 283 284 if (type != IMSG_STAT_DECREMENT && 285 type != IMSG_STAT_INCREMENT) 286 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", 287 proc_name(smtpd_process), 288 proc_name(p->proc), 289 len, 290 imsg_to_str(type)); 291 292 mproc_event_add(p); 293 } 294 295 void 296 m_create(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) 297 { 298 p->m_pos = 0; 299 p->m_type = type; 300 p->m_peerid = peerid; 301 p->m_pid = pid; 302 p->m_fd = fd; 303 } 304 305 void 306 m_add(struct mproc *p, const void *data, size_t len) 307 { 308 size_t alloc; 309 void *tmp; 310 311 if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) { 312 log_warnx("warn: message too large"); 313 fatal(NULL); 314 } 315 316 alloc = p->m_alloc ? p->m_alloc : 128; 317 while (p->m_pos + len > alloc) 318 alloc *= 2; 319 if (alloc != p->m_alloc) { 320 log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu", 321 proc_name(smtpd_process), 322 proc_name(p->proc), 323 p->m_alloc, 324 alloc); 325 326 tmp = recallocarray(p->m_buf, p->m_alloc, alloc, 1); 327 if (tmp == NULL) 328 fatal("realloc"); 329 p->m_alloc = alloc; 330 p->m_buf = tmp; 331 } 332 333 memmove(p->m_buf + p->m_pos, data, len); 334 p->m_pos += len; 335 } 336 337 void 338 m_close(struct mproc *p) 339 { 340 if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, 341 p->m_buf, p->m_pos) == -1) 342 fatal("imsg_compose"); 343 344 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", 345 proc_name(smtpd_process), 346 proc_name(p->proc), 347 p->m_pos, 348 imsg_to_str(p->m_type)); 349 350 mproc_event_add(p); 351 } 352 353 void 354 m_flush(struct mproc *p) 355 { 356 if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, 357 p->m_buf, p->m_pos) == -1) 358 fatal("imsg_compose"); 359 360 log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)", 361 proc_name(smtpd_process), 362 proc_name(p->proc), 363 p->m_pos, 364 imsg_to_str(p->m_type)); 365 366 p->m_pos = 0; 367 368 if (imsg_flush(&p->imsgbuf) == -1) 369 fatal("imsg_flush"); 370 } 371 372 static struct imsg * current; 373 374 static void 375 m_error(const char *error) 376 { 377 char buf[512]; 378 379 (void)snprintf(buf, sizeof buf, "%s: %s: %s", 380 proc_name(smtpd_process), 381 imsg_to_str(current->hdr.type), 382 error); 383 fatalx("%s", buf); 384 } 385 386 void 387 m_msg(struct msg *m, struct imsg *imsg) 388 { 389 current = imsg; 390 m->pos = imsg->data; 391 m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr)); 392 } 393 394 void 395 m_end(struct msg *m) 396 { 397 if (m->pos != m->end) 398 m_error("not at msg end"); 399 } 400 401 int 402 m_is_eom(struct msg *m) 403 { 404 return (m->pos == m->end); 405 } 406 407 static inline void 408 m_get(struct msg *m, void *dst, size_t sz) 409 { 410 if (sz > MAX_IMSGSIZE || 411 m->end - m->pos < (ssize_t)sz) 412 fatalx("msg too short"); 413 414 memmove(dst, m->pos, sz); 415 m->pos += sz; 416 } 417 418 void 419 m_add_int(struct mproc *m, int v) 420 { 421 m_add(m, &v, sizeof(v)); 422 }; 423 424 void 425 m_add_u32(struct mproc *m, uint32_t u32) 426 { 427 m_add(m, &u32, sizeof(u32)); 428 }; 429 430 void 431 m_add_size(struct mproc *m, size_t sz) 432 { 433 m_add(m, &sz, sizeof(sz)); 434 }; 435 436 void 437 m_add_time(struct mproc *m, time_t v) 438 { 439 m_add(m, &v, sizeof(v)); 440 }; 441 442 void 443 m_add_timeval(struct mproc *m, struct timeval *tv) 444 { 445 m_add(m, tv, sizeof(*tv)); 446 } 447 448 449 void 450 m_add_string(struct mproc *m, const char *v) 451 { 452 if (v) { 453 m_add(m, "s", 1); 454 m_add(m, v, strlen(v) + 1); 455 } 456 else 457 m_add(m, "\0", 1); 458 }; 459 460 void 461 m_add_data(struct mproc *m, const void *v, size_t len) 462 { 463 m_add_size(m, len); 464 m_add(m, v, len); 465 }; 466 467 void 468 m_add_id(struct mproc *m, uint64_t v) 469 { 470 m_add(m, &v, sizeof(v)); 471 } 472 473 void 474 m_add_evpid(struct mproc *m, uint64_t v) 475 { 476 m_add(m, &v, sizeof(v)); 477 } 478 479 void 480 m_add_msgid(struct mproc *m, uint32_t v) 481 { 482 m_add(m, &v, sizeof(v)); 483 } 484 485 void 486 m_add_sockaddr(struct mproc *m, const struct sockaddr *sa) 487 { 488 m_add_size(m, sa->sa_len); 489 m_add(m, sa, sa->sa_len); 490 } 491 492 void 493 m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr) 494 { 495 m_add(m, maddr, sizeof(*maddr)); 496 } 497 498 void 499 m_add_envelope(struct mproc *m, const struct envelope *evp) 500 { 501 char buf[sizeof(*evp)]; 502 503 envelope_dump_buffer(evp, buf, sizeof(buf)); 504 m_add_evpid(m, evp->id); 505 m_add_string(m, buf); 506 } 507 508 void 509 m_add_params(struct mproc *m, struct dict *d) 510 { 511 const char *key; 512 char *value; 513 void *iter; 514 515 if (d == NULL) { 516 m_add_size(m, 0); 517 return; 518 } 519 m_add_size(m, dict_count(d)); 520 iter = NULL; 521 while (dict_iter(d, &iter, &key, (void **)&value)) { 522 m_add_string(m, key); 523 m_add_string(m, value); 524 } 525 } 526 527 void 528 m_get_int(struct msg *m, int *i) 529 { 530 m_get(m, i, sizeof(*i)); 531 } 532 533 void 534 m_get_u32(struct msg *m, uint32_t *u32) 535 { 536 m_get(m, u32, sizeof(*u32)); 537 } 538 539 void 540 m_get_size(struct msg *m, size_t *sz) 541 { 542 m_get(m, sz, sizeof(*sz)); 543 } 544 545 void 546 m_get_time(struct msg *m, time_t *t) 547 { 548 m_get(m, t, sizeof(*t)); 549 } 550 551 void 552 m_get_timeval(struct msg *m, struct timeval *tv) 553 { 554 m_get(m, tv, sizeof(*tv)); 555 } 556 557 void 558 m_get_string(struct msg *m, const char **s) 559 { 560 uint8_t *end; 561 char c; 562 563 if (m->pos >= m->end) 564 m_error("msg too short"); 565 566 c = *m->pos++; 567 if (c == '\0') { 568 *s = NULL; 569 return; 570 } 571 572 if (m->pos >= m->end) 573 m_error("msg too short"); 574 end = memchr(m->pos, 0, m->end - m->pos); 575 if (end == NULL) 576 m_error("unterminated string"); 577 578 *s = m->pos; 579 m->pos = end + 1; 580 } 581 582 void 583 m_get_data(struct msg *m, const void **data, size_t *sz) 584 { 585 m_get_size(m, sz); 586 587 if (*sz == 0) { 588 *data = NULL; 589 return; 590 } 591 592 if (m->pos + *sz > m->end) 593 m_error("msg too short"); 594 595 *data = m->pos; 596 m->pos += *sz; 597 } 598 599 void 600 m_get_evpid(struct msg *m, uint64_t *evpid) 601 { 602 m_get(m, evpid, sizeof(*evpid)); 603 } 604 605 void 606 m_get_msgid(struct msg *m, uint32_t *msgid) 607 { 608 m_get(m, msgid, sizeof(*msgid)); 609 } 610 611 void 612 m_get_id(struct msg *m, uint64_t *id) 613 { 614 m_get(m, id, sizeof(*id)); 615 } 616 617 void 618 m_get_sockaddr(struct msg *m, struct sockaddr *sa) 619 { 620 size_t len; 621 622 m_get_size(m, &len); 623 m_get(m, sa, len); 624 } 625 626 void 627 m_get_mailaddr(struct msg *m, struct mailaddr *maddr) 628 { 629 m_get(m, maddr, sizeof(*maddr)); 630 } 631 632 void 633 m_get_envelope(struct msg *m, struct envelope *evp) 634 { 635 uint64_t evpid; 636 const char *buf; 637 638 m_get_evpid(m, &evpid); 639 m_get_string(m, &buf); 640 641 if (!envelope_load_buffer(evp, buf, strlen(buf))) 642 fatalx("failed to retrieve envelope"); 643 evp->id = evpid; 644 } 645 646 void 647 m_get_params(struct msg *m, struct dict *d) 648 { 649 size_t c; 650 const char *key; 651 const char *value; 652 char *tmp; 653 654 dict_init(d); 655 656 m_get_size(m, &c); 657 658 for (; c; c--) { 659 m_get_string(m, &key); 660 m_get_string(m, &value); 661 if ((tmp = strdup(value)) == NULL) 662 fatal("m_get_params"); 663 dict_set(d, key, tmp); 664 } 665 } 666 667 void 668 m_clear_params(struct dict *d) 669 { 670 char *value; 671 672 while (dict_poproot(d, (void **)&value)) 673 free(value); 674 } 675