1 /* 2 * dhcpcd - DHCP client daemon 3 * SPDX-License-Identifier: BSD-2-Clause 4 * Copyright (c) 2006-2025 Roy Marples <roy (at) marples.name> 5 * All rights reserved 6 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/socket.h> 30 #include <sys/stat.h> 31 #include <sys/uio.h> 32 #include <sys/un.h> 33 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <grp.h> 37 #include <pwd.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <time.h> 42 #include <unistd.h> 43 44 #include "config.h" // IWYU pragma: keep 45 #include "common.h" 46 #include "control.h" 47 #include "dhcpcd.h" 48 #include "eloop.h" 49 #include "if.h" 50 #include "logerr.h" 51 #include "privsep.h" 52 53 #ifndef SUN_LEN 54 #define SUN_LEN(su) \ 55 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) 56 #endif 57 58 #define SUN_MODE_USR (S_IRUSR | S_IWUSR) 59 #define SUN_MODE_GRP (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) 60 #define SUN_MODE_ALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) 61 #define GID_SET(gid) ((gid) != (gid_t) - 1) 62 63 #define LISTEN_BACKLOG 5 64 65 static void control_handle_data(void *, unsigned short); 66 67 static void 68 control_queue_free(struct fd_list *fd) 69 { 70 struct fd_data *fdp; 71 72 while ((fdp = TAILQ_FIRST(&fd->queue))) { 73 TAILQ_REMOVE(&fd->queue, fdp, next); 74 if (fdp->data_size != 0) 75 free(fdp->data); 76 free(fdp); 77 } 78 79 #ifdef CTL_FREE_LIST 80 while ((fdp = TAILQ_FIRST(&fd->free_queue))) { 81 TAILQ_REMOVE(&fd->free_queue, fdp, next); 82 if (fdp->data_size != 0) 83 free(fdp->data); 84 free(fdp); 85 } 86 #endif 87 } 88 89 void 90 control_free(struct fd_list *fd) 91 { 92 eloop_event_delete(fd->ctx->eloop, fd->fd); 93 close(fd->fd); 94 TAILQ_REMOVE(&fd->ctx->control_fds, fd, next); 95 control_queue_free(fd); 96 free(fd); 97 } 98 99 static void 100 control_hangup(struct fd_list *fd) 101 { 102 control_free(fd); 103 } 104 105 static ssize_t 106 control_handle_read(struct fd_list *fd) 107 { 108 uid_t uid; 109 gid_t gid, in_gid; 110 char buf[BUFSIZ]; 111 struct iovec iov[] = { { 112 .iov_base = buf, 113 .iov_len = sizeof(buf), 114 } }; 115 struct msghdr msg = { 116 .msg_iov = iov, 117 .msg_iovlen = __arraycount(iov), 118 }; 119 ssize_t bytes, err; 120 121 bytes = recvmsg(fd->fd, &msg, 0); 122 if (bytes == -1) 123 logerr(__func__); 124 if (bytes == -1 || bytes == 0) 125 return bytes; 126 127 if (getpeereid(fd->fd, &uid, &gid) == -1) { 128 logerr("%s: getpeereid", __func__); 129 return -1; 130 } 131 132 #ifdef PRIVSEP 133 if (IN_PRIVSEP(fd->ctx)) { 134 in_gid = fd->ctx->control_group; 135 err = ps_root_user_ingroup(fd->ctx, uid, gid, in_gid); 136 if (err == -1) { 137 logerr(__func__); 138 return -1; 139 } 140 if (err == 0) { 141 fd->flags &= ~FD_CONTROL; 142 in_gid = fd->ctx->read_group; 143 err = ps_root_user_ingroup(fd->ctx, uid, gid, in_gid); 144 if (err == -1) { 145 logerr(__func__); 146 return -1; 147 } 148 if (err == 0) 149 fd->flags &= ~FD_READ; 150 else 151 fd->flags |= FD_READ; 152 } else 153 fd->flags |= FD_CONTROL | FD_READ; 154 155 err = ps_ctl_handleargs(fd, buf, (size_t)bytes); 156 if (err == -1) { 157 logerr(__func__); 158 return 0; 159 } 160 161 if (fd->flags & FD_LISTEN) 162 fd->flags &= ~FD_COMMAND; 163 else 164 fd->flags |= FD_COMMAND; 165 166 iov[0].iov_len = (size_t)bytes; 167 /* If ps_ctl_handleargs returns 0 that means it didn't 168 * do anything with the command, so pass it to the manager. */ 169 if (err == 0 && ps_ctl_sendmsg(fd, &msg) == -1) { 170 logerr(__func__); 171 return -1; 172 } 173 return 1; 174 } 175 #endif 176 177 in_gid = fd->ctx->control_group; 178 err = control_user_ingroup(uid, gid, in_gid); 179 if (err == -1) { 180 logerr(__func__); 181 return -1; 182 } 183 if (err == 0) { 184 fd->flags &= ~FD_CONTROL; 185 in_gid = fd->ctx->read_group; 186 err = control_user_ingroup(uid, gid, in_gid); 187 if (err == -1) { 188 logerr(__func__); 189 return -1; 190 } 191 if (err == 0) 192 fd->flags &= ~FD_READ; 193 else 194 fd->flags |= FD_READ; 195 } else 196 fd->flags |= FD_CONTROL | FD_READ; 197 return control_recvmsg(fd, &msg, (size_t)bytes); 198 } 199 200 static ssize_t 201 control_handle_write(struct fd_list *fd) 202 { 203 struct iovec iov[4]; 204 struct msghdr msg = { .msg_iov = iov }; 205 struct fd_data *data; 206 ssize_t len; 207 #ifdef PRIVSEP 208 size_t peer_len; 209 #endif 210 211 data = TAILQ_FIRST(&fd->queue); 212 213 #ifdef PRIVSEP 214 if (data->data_peer_id != 0) { 215 /* Control messages for a peer are prefixed with it's fd 216 * fd and message length. */ 217 iov[msg.msg_iovlen].iov_base = &data->data_peer_id; 218 iov[msg.msg_iovlen].iov_len = sizeof(data->data_peer_id); 219 msg.msg_iovlen++; 220 221 peer_len = data->data_len; 222 if (data->data_flags & FD_DATA_SENDLEN) 223 peer_len += sizeof(data->data_len); 224 iov[msg.msg_iovlen].iov_base = &peer_len; 225 iov[msg.msg_iovlen].iov_len = sizeof(peer_len); 226 msg.msg_iovlen++; 227 } 228 #endif 229 230 if (data->data_flags & FD_DATA_SENDLEN) { 231 iov[msg.msg_iovlen].iov_base = &data->data_len; 232 iov[msg.msg_iovlen].iov_len = sizeof(data->data_len); 233 msg.msg_iovlen++; 234 } 235 236 iov[msg.msg_iovlen].iov_base = data->data; 237 iov[msg.msg_iovlen].iov_len = data->data_len; 238 msg.msg_iovlen++; 239 240 len = sendmsg(fd->fd, &msg, 0); 241 if (len == -1) { 242 if (errno != EPIPE && errno != ENOTCONN) { 243 // We don't get ELE_HANGUP for some reason 244 logerr("%s: write", __func__); 245 } 246 control_hangup(fd); 247 return -1; 248 } 249 250 TAILQ_REMOVE(&fd->queue, data, next); 251 #ifdef CTL_FREE_LIST 252 TAILQ_INSERT_TAIL(&fd->free_queue, data, next); 253 #else 254 if (data->data_size != 0) 255 free(data->data); 256 free(data); 257 #endif 258 259 if (TAILQ_FIRST(&fd->queue) != NULL) 260 return len; 261 262 /* Done sending data, stop watching write to fd */ 263 if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ, 264 control_handle_data, fd) == -1) 265 logerr("%s: eloop_event_add", __func__); 266 return len; 267 } 268 269 static void 270 control_handle_data(void *arg, unsigned short events) 271 { 272 struct fd_list *fd = arg; 273 ssize_t err; 274 275 if (events & ELE_HANGUP) 276 goto hangup; 277 278 if (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP))) 279 logerrx("%s: unexpected event 0x%04x", __func__, events); 280 281 if (events & ELE_WRITE && !(events & ELE_HANGUP)) { 282 err = control_handle_write(fd); 283 if (err == -1) 284 goto hangup; 285 } 286 if (events & ELE_READ) { 287 err = control_handle_read(fd); 288 if ((err == -1 && errno != EPERM) || err == 0) 289 goto hangup; 290 } 291 292 return; 293 294 hangup: 295 control_hangup(fd); 296 } 297 298 int 299 control_recvmsg(struct fd_list *fd, struct msghdr *msg, size_t len) 300 { 301 struct iovec *iov; 302 char *p, *e; 303 char *argvp[255], **ap; 304 int argc; 305 306 if (msg->msg_iovlen == 0) { 307 errno = EINVAL; 308 return -1; 309 } 310 311 iov = msg->msg_iov; 312 p = (char *)iov->iov_base; 313 314 /* Each command is \n terminated 315 * Each argument is NULL separated */ 316 while (len != 0) { 317 argc = 0; 318 ap = argvp; 319 while (len != 0) { 320 if (*p == '\0') { 321 p++; 322 len--; 323 continue; 324 } 325 e = memchr(p, '\0', len); 326 if (e == NULL) { 327 errno = EINVAL; 328 logerrx("%s: no terminator", __func__); 329 return -1; 330 } 331 if ((size_t)argc + 1 >= 332 sizeof(argvp) / sizeof(argvp[0])) { 333 errno = ENOBUFS; 334 logerrx("%s: no arg buffer", __func__); 335 return -1; 336 } 337 *ap++ = p; 338 argc++; 339 e++; 340 len -= (size_t)(e - p); 341 p = e; 342 e--; 343 if (*(--e) == '\n') { 344 *e = '\0'; 345 break; 346 } 347 } 348 if (argc == 0) { 349 logerrx("%s: no args", __func__); 350 continue; 351 } 352 *ap = NULL; 353 if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) { 354 logerr("%s: dhcpcd_handleargs", __func__); 355 if (errno != EINTR && errno != EAGAIN && errno != EPERM) 356 return -1; 357 } 358 if (!(fd->flags & FD_LISTEN)) 359 fd->flags |= FD_COMMAND; 360 } 361 362 return 1; 363 } 364 365 struct fd_list * 366 control_find(struct dhcpcd_ctx *ctx, int fd) 367 { 368 struct fd_list *l; 369 370 TAILQ_FOREACH(l, &ctx->control_fds, next) { 371 if (l->fd == fd) 372 return l; 373 } 374 375 errno = ESRCH; 376 return NULL; 377 } 378 379 struct fd_list * 380 control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags) 381 { 382 struct fd_list *l; 383 size_t cnt; 384 struct fd_list *n; 385 #ifdef PRIVSEP 386 unsigned int id; 387 #endif 388 389 l = control_find(ctx, fd); 390 if (l != NULL) { 391 l->flags = flags; 392 return l; 393 } 394 395 #ifdef PRIVSEP 396 again: 397 id = ++ctx->ps_control_id; 398 if (id == 0) /* wrapped */ 399 id = ++ctx->ps_control_id; 400 #endif 401 cnt = 0; 402 TAILQ_FOREACH(n, &ctx->control_fds, next) { 403 if (++cnt >= CONTROL_PEER_MAX) { 404 errno = ENOBUFS; 405 return NULL; 406 } 407 #ifdef PRIVSEP 408 if (n->id == id) 409 goto again; 410 #endif 411 } 412 413 l = malloc(sizeof(*l)); 414 if (l == NULL) 415 return NULL; 416 417 l->ctx = ctx; 418 l->fd = fd; 419 l->flags = flags; 420 TAILQ_INIT(&l->queue); 421 #ifdef CTL_FREE_LIST 422 TAILQ_INIT(&l->free_queue); 423 #endif 424 #ifdef PRIVSEP 425 l->peer_id = 0; 426 l->id = id; 427 #endif 428 TAILQ_INSERT_TAIL(&ctx->control_fds, l, next); 429 return l; 430 } 431 432 static void 433 control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags, 434 unsigned short events) 435 { 436 struct sockaddr_un run; 437 socklen_t len; 438 struct fd_list *l; 439 int fd, flags = 1; 440 441 if (events != ELE_READ) 442 logerrx("%s: unexpected event 0x%04x", __func__, events); 443 444 len = sizeof(run); 445 if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1) 446 goto error; 447 if ((flags = fcntl(fd, F_GETFD, 0)) == -1 || 448 fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) 449 goto error; 450 if ((flags = fcntl(fd, F_GETFL, 0)) == -1 || 451 fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) 452 goto error; 453 454 l = control_new(ctx, fd, fd_flags); 455 if (l == NULL) 456 goto error; 457 458 if (eloop_event_add(ctx->eloop, l->fd, ELE_READ, control_handle_data, 459 l) == -1) 460 logerr("%s: eloop_event_add", __func__); 461 462 return; 463 464 error: 465 logerr(__func__); 466 if (fd != -1) 467 close(fd); 468 } 469 470 static void 471 control_handle(void *arg, unsigned short events) 472 { 473 struct dhcpcd_ctx *ctx = arg; 474 475 control_handle1(ctx, ctx->control_fd, 0, events); 476 } 477 478 static int 479 make_path(char *path, size_t len, const char *ifname, sa_family_t family) 480 { 481 const char *per; 482 483 switch (family) { 484 case AF_INET: 485 per = "-4"; 486 break; 487 case AF_INET6: 488 per = "-6"; 489 break; 490 default: 491 per = ""; 492 break; 493 } 494 return snprintf(path, len, CONTROLSOCKET, ifname ? ifname : "", 495 ifname ? per : "", "", ifname ? "." : ""); 496 } 497 498 static int 499 make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family) 500 { 501 int fd; 502 503 if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) 504 return -1; 505 memset(sa, 0, sizeof(*sa)); 506 sa->sun_family = AF_UNIX; 507 make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family); 508 return fd; 509 } 510 511 static int 512 control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) 513 { 514 struct sockaddr_un sa; 515 mode_t mode; 516 gid_t grp = getgid(); 517 int fd; 518 socklen_t len; 519 520 fd = make_sock(&sa, ifname, family); 521 if (fd == -1) 522 return -1; 523 524 len = (socklen_t)SUN_LEN(&sa); 525 if (GID_SET(ctx->control_group) && GID_SET(ctx->read_group)) { 526 if (ctx->control_group == ctx->read_group) { 527 mode = SUN_MODE_GRP; 528 grp = ctx->control_group; 529 } else 530 mode = SUN_MODE_ALL; 531 } else if (!GID_SET(ctx->control_group) && !GID_SET(ctx->read_group)) 532 mode = SUN_MODE_USR; 533 else if (GID_SET(ctx->control_group)) { 534 mode = SUN_MODE_GRP; 535 grp = ctx->control_group; 536 } else { 537 mode = SUN_MODE_GRP; 538 grp = ctx->read_group; 539 } 540 541 unlink(sa.sun_path); 542 if (bind(fd, (struct sockaddr *)&sa, len) == -1 || 543 chmod(sa.sun_path, mode) == -1 || 544 chown(sa.sun_path, geteuid(), grp) == -1 || 545 listen(fd, LISTEN_BACKLOG) == -1) 546 goto err; 547 548 #ifdef PRIVSEP_RIGHTS 549 if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_getsockopt(fd) == -1) 550 goto err; 551 #endif 552 553 strlcpy(ctx->control_sock, sa.sun_path, sizeof(ctx->control_sock)); 554 return fd; 555 556 err: 557 close(fd); 558 unlink(sa.sun_path); 559 return -1; 560 } 561 562 int 563 control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family) 564 { 565 int fd; 566 567 #ifdef PRIVSEP 568 if (IN_PRIVSEP_SE(ctx)) { 569 make_path(ctx->control_sock, sizeof(ctx->control_sock), ifname, 570 family); 571 return 0; 572 } 573 #endif 574 575 if ((fd = control_start1(ctx, ifname, family)) == -1) 576 return -1; 577 578 ctx->control_fd = fd; 579 if (eloop_event_add(ctx->eloop, fd, ELE_READ, control_handle, ctx) == 580 -1) 581 logerr("%s: eloop_event_add", __func__); 582 583 return ctx->control_fd; 584 } 585 586 static int 587 control_unlink(struct dhcpcd_ctx *ctx, const char *file) 588 { 589 int retval = 0; 590 591 errno = 0; 592 #ifdef PRIVSEP 593 if (IN_PRIVSEP(ctx)) 594 retval = (int)ps_root_unlink(ctx, file); 595 else 596 #else 597 UNUSED(ctx); 598 #endif 599 retval = unlink(file); 600 601 return retval == -1 && errno != ENOENT ? -1 : 0; 602 } 603 604 int 605 control_stop(struct dhcpcd_ctx *ctx) 606 { 607 int retval = 0; 608 struct fd_list *l; 609 610 while ((l = TAILQ_FIRST(&ctx->control_fds)) != NULL) { 611 control_free(l); 612 } 613 614 #ifdef PRIVSEP 615 if (IN_PRIVSEP_SE(ctx)) { 616 if (ctx->control_sock[0] != '\0' && 617 ps_root_unlink(ctx, ctx->control_sock) == -1) 618 retval = -1; 619 return retval; 620 } else if (ctx->options & DHCPCD_FORKED) 621 return retval; 622 #endif 623 624 if (ctx->control_fd != -1) { 625 eloop_event_delete(ctx->eloop, ctx->control_fd); 626 close(ctx->control_fd); 627 ctx->control_fd = -1; 628 if (control_unlink(ctx, ctx->control_sock) == -1) 629 retval = -1; 630 } 631 632 return retval; 633 } 634 635 int 636 control_open(const char *ifname, sa_family_t family) 637 { 638 struct sockaddr_un sa; 639 int fd; 640 641 if ((fd = make_sock(&sa, ifname, family)) != -1) { 642 socklen_t len; 643 644 len = (socklen_t)SUN_LEN(&sa); 645 if (connect(fd, (struct sockaddr *)&sa, len) == -1) { 646 close(fd); 647 fd = -1; 648 } 649 } 650 return fd; 651 } 652 653 ssize_t 654 control_send(struct dhcpcd_ctx *ctx, int argc, char *const *argv) 655 { 656 char buffer[1024]; 657 int i; 658 size_t len, l; 659 660 if (argc > 255) { 661 errno = ENOBUFS; 662 return -1; 663 } 664 len = 0; 665 for (i = 0; i < argc; i++) { 666 l = strlen(argv[i]) + 1; 667 if (len + l > sizeof(buffer)) { 668 errno = ENOBUFS; 669 return -1; 670 } 671 memcpy(buffer + len, argv[i], l); 672 len += l; 673 } 674 return write(ctx->control_fd, buffer, len); 675 } 676 677 ssize_t 678 control_queuef(struct fd_list *fd, const void *data, size_t data_len, 679 unsigned int flags) 680 { 681 struct fd_data *d; 682 unsigned short events; 683 684 if (data_len == 0) { 685 errno = EINVAL; 686 return -1; 687 } 688 689 #ifdef CTL_FREE_LIST 690 struct fd_data *df; 691 692 d = NULL; 693 TAILQ_FOREACH(df, &fd->free_queue, next) { 694 if (d == NULL || d->data_size < df->data_size) { 695 d = df; 696 if (d->data_size <= data_len) 697 break; 698 } 699 } 700 if (d != NULL) 701 TAILQ_REMOVE(&fd->free_queue, d, next); 702 else 703 #endif 704 { 705 d = calloc(1, sizeof(*d)); 706 if (d == NULL) 707 return -1; 708 } 709 710 if (d->data_size == 0) 711 d->data = NULL; 712 if (d->data_size < data_len) { 713 void *nbuf = realloc(d->data, data_len); 714 if (nbuf == NULL) { 715 free(d->data); 716 free(d); 717 return -1; 718 } 719 d->data = nbuf; 720 d->data_size = data_len; 721 } 722 memcpy(d->data, data, data_len); 723 d->data_len = data_len; 724 d->data_flags = flags; 725 #ifdef PRIVSEP 726 d->data_peer_id = fd->peer_id; 727 #endif 728 729 TAILQ_INSERT_TAIL(&fd->queue, d, next); 730 events = ELE_WRITE; 731 if (fd->flags & FD_LISTEN) 732 events |= ELE_READ; 733 if (eloop_event_add(fd->ctx->eloop, fd->fd, events, control_handle_data, 734 fd) == -1) 735 return -1; 736 return (ssize_t)d->data_len; 737 } 738 739 ssize_t 740 control_queue(struct fd_list *fd, const void *data, size_t data_len) 741 { 742 return control_queuef(fd, data, data_len, FD_DATA_SENDLEN); 743 } 744 745 int 746 control_user_ingroup(uid_t uid, gid_t gid, gid_t grpid) 747 { 748 #ifdef __APPLE__ 749 /* why is getgrouplist API is different ..... */ 750 int *groups = NULL, *gp; 751 #define GID (int) 752 #else 753 gid_t *groups = NULL, *gp; 754 #define GID 755 #endif 756 struct passwd *pw; 757 int ngroups = 10, err = -1; 758 759 /* Allow root */ 760 if (uid == 0) 761 return 1; 762 if (!GID_SET(grpid)) 763 return 0; 764 765 pw = getpwuid(uid); 766 if (pw == NULL) 767 return 0; /* unknown user is not privileged */ 768 769 groups = reallocarray(groups, (size_t)ngroups, sizeof(*groups)); 770 if (groups == NULL) 771 return -1; 772 773 if (getgrouplist(pw->pw_name, GID gid, groups, &ngroups) == -1) { 774 gp = reallocarray(groups, (size_t)ngroups, sizeof(*groups)); 775 if (gp == NULL) 776 goto out; 777 groups = gp; 778 if (getgrouplist(pw->pw_name, GID gid, groups, &ngroups) == -1) 779 goto out; 780 } 781 782 for (gp = groups; ngroups != 0; ngroups--, gp++) { 783 if (*gp == GID grpid) { 784 err = 1; 785 goto out; 786 } 787 } 788 err = 0; 789 790 out: 791 free(groups); 792 return err; 793 } 794 795 ssize_t 796 control_handle_listen(struct fd_list *fd) 797 { 798 int err; 799 800 if (fd->flags & FD_READ) { 801 err = 0; 802 if (!(fd->flags & FD_COMMAND)) 803 fd->flags |= FD_LISTEN; 804 } else { 805 err = errno = EPERM; 806 logwarn(__func__); 807 } 808 return control_queuef(fd, &err, sizeof(err), 0); 809 } 810