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