1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 The FreeBSD Foundation 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/dnv.h> 33 #include <sys/nv.h> 34 #include <sys/param.h> 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <grp.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 #include <libcasper.h> 43 #include <libcasper_service.h> 44 45 #include "cap_grp.h" 46 47 static struct group ggrp; 48 static char *gbuffer; 49 static size_t gbufsize; 50 51 static int 52 group_resize(void) 53 { 54 char *buf; 55 56 if (gbufsize == 0) 57 gbufsize = 1024; 58 else 59 gbufsize *= 2; 60 61 buf = gbuffer; 62 gbuffer = realloc(buf, gbufsize); 63 if (gbuffer == NULL) { 64 free(buf); 65 gbufsize = 0; 66 return (ENOMEM); 67 } 68 memset(gbuffer, 0, gbufsize); 69 70 return (0); 71 } 72 73 static int 74 group_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp, 75 char **bufferp, size_t *bufsizep) 76 { 77 const char *str; 78 size_t len; 79 80 str = nvlist_get_string(nvl, fieldname); 81 len = strlcpy(*bufferp, str, *bufsizep); 82 if (len >= *bufsizep) 83 return (ERANGE); 84 *fieldp = *bufferp; 85 *bufferp += len + 1; 86 *bufsizep -= len + 1; 87 88 return (0); 89 } 90 91 static int 92 group_unpack_members(const nvlist_t *nvl, char ***fieldp, char **bufferp, 93 size_t *bufsizep) 94 { 95 const char *mem; 96 char **outstrs, *str, nvlname[64]; 97 size_t nmem, datasize, strsize; 98 unsigned int ii; 99 int n; 100 101 if (!nvlist_exists_number(nvl, "gr_nmem")) { 102 datasize = _ALIGNBYTES + sizeof(char *); 103 if (datasize >= *bufsizep) 104 return (ERANGE); 105 outstrs = (char **)_ALIGN(*bufferp); 106 outstrs[0] = NULL; 107 *fieldp = outstrs; 108 *bufferp += datasize; 109 *bufsizep -= datasize; 110 return (0); 111 } 112 113 nmem = (size_t)nvlist_get_number(nvl, "gr_nmem"); 114 datasize = _ALIGNBYTES + sizeof(char *) * (nmem + 1); 115 for (ii = 0; ii < nmem; ii++) { 116 n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); 117 assert(n > 0 && n < (int)sizeof(nvlname)); 118 mem = dnvlist_get_string(nvl, nvlname, NULL); 119 if (mem == NULL) 120 return (EINVAL); 121 datasize += strlen(mem) + 1; 122 } 123 124 if (datasize >= *bufsizep) 125 return (ERANGE); 126 127 outstrs = (char **)_ALIGN(*bufferp); 128 str = (char *)outstrs + sizeof(char *) * (nmem + 1); 129 for (ii = 0; ii < nmem; ii++) { 130 n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); 131 assert(n > 0 && n < (int)sizeof(nvlname)); 132 mem = nvlist_get_string(nvl, nvlname); 133 strsize = strlen(mem) + 1; 134 memcpy(str, mem, strsize); 135 outstrs[ii] = str; 136 str += strsize; 137 } 138 assert(ii == nmem); 139 outstrs[ii] = NULL; 140 141 *fieldp = outstrs; 142 *bufferp += datasize; 143 *bufsizep -= datasize; 144 145 return (0); 146 } 147 148 static int 149 group_unpack(const nvlist_t *nvl, struct group *grp, char *buffer, 150 size_t bufsize) 151 { 152 int error; 153 154 if (!nvlist_exists_string(nvl, "gr_name")) 155 return (EINVAL); 156 157 explicit_bzero(grp, sizeof(*grp)); 158 159 error = group_unpack_string(nvl, "gr_name", &grp->gr_name, &buffer, 160 &bufsize); 161 if (error != 0) 162 return (error); 163 error = group_unpack_string(nvl, "gr_passwd", &grp->gr_passwd, &buffer, 164 &bufsize); 165 if (error != 0) 166 return (error); 167 grp->gr_gid = (gid_t)nvlist_get_number(nvl, "gr_gid"); 168 error = group_unpack_members(nvl, &grp->gr_mem, &buffer, &bufsize); 169 if (error != 0) 170 return (error); 171 172 return (0); 173 } 174 175 static int 176 cap_getgrcommon_r(cap_channel_t *chan, const char *cmd, const char *name, 177 gid_t gid, struct group *grp, char *buffer, size_t bufsize, 178 struct group **result) 179 { 180 nvlist_t *nvl; 181 bool getgr_r; 182 int error; 183 184 nvl = nvlist_create(0); 185 nvlist_add_string(nvl, "cmd", cmd); 186 if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) { 187 /* Add nothing. */ 188 } else if (strcmp(cmd, "getgrnam") == 0 || 189 strcmp(cmd, "getgrnam_r") == 0) { 190 nvlist_add_string(nvl, "name", name); 191 } else if (strcmp(cmd, "getgrgid") == 0 || 192 strcmp(cmd, "getgrgid_r") == 0) { 193 nvlist_add_number(nvl, "gid", (uint64_t)gid); 194 } else { 195 abort(); 196 } 197 nvl = cap_xfer_nvlist(chan, nvl); 198 if (nvl == NULL) { 199 assert(errno != 0); 200 *result = NULL; 201 return (errno); 202 } 203 error = (int)nvlist_get_number(nvl, "error"); 204 if (error != 0) { 205 nvlist_destroy(nvl); 206 *result = NULL; 207 return (error); 208 } 209 210 if (!nvlist_exists_string(nvl, "gr_name")) { 211 /* Not found. */ 212 nvlist_destroy(nvl); 213 *result = NULL; 214 return (0); 215 } 216 217 getgr_r = (strcmp(cmd, "getgrent_r") == 0 || 218 strcmp(cmd, "getgrnam_r") == 0 || strcmp(cmd, "getgrgid_r") == 0); 219 220 for (;;) { 221 error = group_unpack(nvl, grp, buffer, bufsize); 222 if (getgr_r || error != ERANGE) 223 break; 224 assert(buffer == gbuffer); 225 assert(bufsize == gbufsize); 226 error = group_resize(); 227 if (error != 0) 228 break; 229 /* Update pointers after resize. */ 230 buffer = gbuffer; 231 bufsize = gbufsize; 232 } 233 234 nvlist_destroy(nvl); 235 236 if (error == 0) 237 *result = grp; 238 else 239 *result = NULL; 240 241 return (error); 242 } 243 244 static struct group * 245 cap_getgrcommon(cap_channel_t *chan, const char *cmd, const char *name, 246 gid_t gid) 247 { 248 struct group *result; 249 int error, serrno; 250 251 serrno = errno; 252 253 error = cap_getgrcommon_r(chan, cmd, name, gid, &ggrp, gbuffer, 254 gbufsize, &result); 255 if (error != 0) { 256 errno = error; 257 return (NULL); 258 } 259 260 errno = serrno; 261 262 return (result); 263 } 264 265 struct group * 266 cap_getgrent(cap_channel_t *chan) 267 { 268 269 return (cap_getgrcommon(chan, "getgrent", NULL, 0)); 270 } 271 272 struct group * 273 cap_getgrnam(cap_channel_t *chan, const char *name) 274 { 275 276 return (cap_getgrcommon(chan, "getgrnam", name, 0)); 277 } 278 279 struct group * 280 cap_getgrgid(cap_channel_t *chan, gid_t gid) 281 { 282 283 return (cap_getgrcommon(chan, "getgrgid", NULL, gid)); 284 } 285 286 int 287 cap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer, 288 size_t bufsize, struct group **result) 289 { 290 291 return (cap_getgrcommon_r(chan, "getgrent_r", NULL, 0, grp, buffer, 292 bufsize, result)); 293 } 294 295 int 296 cap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp, 297 char *buffer, size_t bufsize, struct group **result) 298 { 299 300 return (cap_getgrcommon_r(chan, "getgrnam_r", name, 0, grp, buffer, 301 bufsize, result)); 302 } 303 304 int 305 cap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer, 306 size_t bufsize, struct group **result) 307 { 308 309 return (cap_getgrcommon_r(chan, "getgrgid_r", NULL, gid, grp, buffer, 310 bufsize, result)); 311 } 312 313 int 314 cap_setgroupent(cap_channel_t *chan, int stayopen) 315 { 316 nvlist_t *nvl; 317 318 nvl = nvlist_create(0); 319 nvlist_add_string(nvl, "cmd", "setgroupent"); 320 nvlist_add_bool(nvl, "stayopen", stayopen != 0); 321 nvl = cap_xfer_nvlist(chan, nvl); 322 if (nvl == NULL) 323 return (0); 324 if (nvlist_get_number(nvl, "error") != 0) { 325 errno = nvlist_get_number(nvl, "error"); 326 nvlist_destroy(nvl); 327 return (0); 328 } 329 nvlist_destroy(nvl); 330 331 return (1); 332 } 333 334 int 335 cap_setgrent(cap_channel_t *chan) 336 { 337 nvlist_t *nvl; 338 339 nvl = nvlist_create(0); 340 nvlist_add_string(nvl, "cmd", "setgrent"); 341 nvl = cap_xfer_nvlist(chan, nvl); 342 if (nvl == NULL) 343 return (0); 344 if (nvlist_get_number(nvl, "error") != 0) { 345 errno = nvlist_get_number(nvl, "error"); 346 nvlist_destroy(nvl); 347 return (0); 348 } 349 nvlist_destroy(nvl); 350 351 return (1); 352 } 353 354 void 355 cap_endgrent(cap_channel_t *chan) 356 { 357 nvlist_t *nvl; 358 359 nvl = nvlist_create(0); 360 nvlist_add_string(nvl, "cmd", "endgrent"); 361 /* Ignore any errors, we have no way to report them. */ 362 nvlist_destroy(cap_xfer_nvlist(chan, nvl)); 363 } 364 365 int 366 cap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds) 367 { 368 nvlist_t *limits, *nvl; 369 unsigned int i; 370 371 if (cap_limit_get(chan, &limits) < 0) 372 return (-1); 373 if (limits == NULL) { 374 limits = nvlist_create(0); 375 } else { 376 if (nvlist_exists_nvlist(limits, "cmds")) 377 nvlist_free_nvlist(limits, "cmds"); 378 } 379 nvl = nvlist_create(0); 380 for (i = 0; i < ncmds; i++) 381 nvlist_add_null(nvl, cmds[i]); 382 nvlist_move_nvlist(limits, "cmds", nvl); 383 return (cap_limit_set(chan, limits)); 384 } 385 386 int 387 cap_grp_limit_fields(cap_channel_t *chan, const char * const *fields, 388 size_t nfields) 389 { 390 nvlist_t *limits, *nvl; 391 unsigned int i; 392 393 if (cap_limit_get(chan, &limits) < 0) 394 return (-1); 395 if (limits == NULL) { 396 limits = nvlist_create(0); 397 } else { 398 if (nvlist_exists_nvlist(limits, "fields")) 399 nvlist_free_nvlist(limits, "fields"); 400 } 401 nvl = nvlist_create(0); 402 for (i = 0; i < nfields; i++) 403 nvlist_add_null(nvl, fields[i]); 404 nvlist_move_nvlist(limits, "fields", nvl); 405 return (cap_limit_set(chan, limits)); 406 } 407 408 int 409 cap_grp_limit_groups(cap_channel_t *chan, const char * const *names, 410 size_t nnames, const gid_t *gids, size_t ngids) 411 { 412 nvlist_t *limits, *groups; 413 unsigned int i; 414 char nvlname[64]; 415 int n; 416 417 if (cap_limit_get(chan, &limits) < 0) 418 return (-1); 419 if (limits == NULL) { 420 limits = nvlist_create(0); 421 } else { 422 if (nvlist_exists_nvlist(limits, "groups")) 423 nvlist_free_nvlist(limits, "groups"); 424 } 425 groups = nvlist_create(0); 426 for (i = 0; i < ngids; i++) { 427 n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); 428 assert(n > 0 && n < (int)sizeof(nvlname)); 429 nvlist_add_number(groups, nvlname, (uint64_t)gids[i]); 430 } 431 for (i = 0; i < nnames; i++) { 432 n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); 433 assert(n > 0 && n < (int)sizeof(nvlname)); 434 nvlist_add_string(groups, nvlname, names[i]); 435 } 436 nvlist_move_nvlist(limits, "groups", groups); 437 return (cap_limit_set(chan, limits)); 438 } 439 440 /* 441 * Service functions. 442 */ 443 static bool 444 grp_allowed_cmd(const nvlist_t *limits, const char *cmd) 445 { 446 447 if (limits == NULL) 448 return (true); 449 450 /* 451 * If no limit was set on allowed commands, then all commands 452 * are allowed. 453 */ 454 if (!nvlist_exists_nvlist(limits, "cmds")) 455 return (true); 456 457 limits = nvlist_get_nvlist(limits, "cmds"); 458 return (nvlist_exists_null(limits, cmd)); 459 } 460 461 static int 462 grp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits) 463 { 464 const char *name; 465 void *cookie; 466 int type; 467 468 cookie = NULL; 469 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 470 if (type != NV_TYPE_NULL) 471 return (EINVAL); 472 if (!grp_allowed_cmd(oldlimits, name)) 473 return (ENOTCAPABLE); 474 } 475 476 return (0); 477 } 478 479 static bool 480 grp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid) 481 { 482 const char *name; 483 void *cookie; 484 int type; 485 486 if (limits == NULL) 487 return (true); 488 489 /* 490 * If no limit was set on allowed groups, then all groups are allowed. 491 */ 492 if (!nvlist_exists_nvlist(limits, "groups")) 493 return (true); 494 495 limits = nvlist_get_nvlist(limits, "groups"); 496 cookie = NULL; 497 while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { 498 switch (type) { 499 case NV_TYPE_NUMBER: 500 if (gid != (gid_t)-1 && 501 nvlist_get_number(limits, name) == (uint64_t)gid) { 502 return (true); 503 } 504 break; 505 case NV_TYPE_STRING: 506 if (gname != NULL && 507 strcmp(nvlist_get_string(limits, name), 508 gname) == 0) { 509 return (true); 510 } 511 break; 512 default: 513 abort(); 514 } 515 } 516 517 return (false); 518 } 519 520 static int 521 grp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits) 522 { 523 const char *name, *gname; 524 void *cookie; 525 gid_t gid; 526 int type; 527 528 cookie = NULL; 529 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 530 switch (type) { 531 case NV_TYPE_NUMBER: 532 gid = (gid_t)nvlist_get_number(newlimits, name); 533 gname = NULL; 534 break; 535 case NV_TYPE_STRING: 536 gid = (gid_t)-1; 537 gname = nvlist_get_string(newlimits, name); 538 break; 539 default: 540 return (EINVAL); 541 } 542 if (!grp_allowed_group(oldlimits, gname, gid)) 543 return (ENOTCAPABLE); 544 } 545 546 return (0); 547 } 548 549 static bool 550 grp_allowed_field(const nvlist_t *limits, const char *field) 551 { 552 553 if (limits == NULL) 554 return (true); 555 556 /* 557 * If no limit was set on allowed fields, then all fields are allowed. 558 */ 559 if (!nvlist_exists_nvlist(limits, "fields")) 560 return (true); 561 562 limits = nvlist_get_nvlist(limits, "fields"); 563 return (nvlist_exists_null(limits, field)); 564 } 565 566 static int 567 grp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits) 568 { 569 const char *name; 570 void *cookie; 571 int type; 572 573 cookie = NULL; 574 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 575 if (type != NV_TYPE_NULL) 576 return (EINVAL); 577 if (!grp_allowed_field(oldlimits, name)) 578 return (ENOTCAPABLE); 579 } 580 581 return (0); 582 } 583 584 static bool 585 grp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl) 586 { 587 char nvlname[64]; 588 int n; 589 590 if (grp == NULL) 591 return (true); 592 593 /* 594 * If either name or GID is allowed, we allow it. 595 */ 596 if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid)) 597 return (false); 598 599 if (grp_allowed_field(limits, "gr_name")) 600 nvlist_add_string(nvl, "gr_name", grp->gr_name); 601 else 602 nvlist_add_string(nvl, "gr_name", ""); 603 if (grp_allowed_field(limits, "gr_passwd")) 604 nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd); 605 else 606 nvlist_add_string(nvl, "gr_passwd", ""); 607 if (grp_allowed_field(limits, "gr_gid")) 608 nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid); 609 else 610 nvlist_add_number(nvl, "gr_gid", (uint64_t)-1); 611 if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) { 612 unsigned int ngroups; 613 614 for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) { 615 n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", 616 ngroups); 617 assert(n > 0 && n < (ssize_t)sizeof(nvlname)); 618 nvlist_add_string(nvl, nvlname, grp->gr_mem[ngroups]); 619 } 620 nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups); 621 } 622 623 return (true); 624 } 625 626 static int 627 grp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin __unused, 628 nvlist_t *nvlout) 629 { 630 struct group *grp; 631 632 for (;;) { 633 errno = 0; 634 grp = getgrent(); 635 if (errno != 0) 636 return (errno); 637 if (grp_pack(limits, grp, nvlout)) 638 return (0); 639 } 640 641 /* NOTREACHED */ 642 } 643 644 static int 645 grp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 646 { 647 struct group *grp; 648 const char *name; 649 650 if (!nvlist_exists_string(nvlin, "name")) 651 return (EINVAL); 652 name = nvlist_get_string(nvlin, "name"); 653 assert(name != NULL); 654 655 errno = 0; 656 grp = getgrnam(name); 657 if (errno != 0) 658 return (errno); 659 660 (void)grp_pack(limits, grp, nvlout); 661 662 return (0); 663 } 664 665 static int 666 grp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 667 { 668 struct group *grp; 669 gid_t gid; 670 671 if (!nvlist_exists_number(nvlin, "gid")) 672 return (EINVAL); 673 674 gid = (gid_t)nvlist_get_number(nvlin, "gid"); 675 676 errno = 0; 677 grp = getgrgid(gid); 678 if (errno != 0) 679 return (errno); 680 681 (void)grp_pack(limits, grp, nvlout); 682 683 return (0); 684 } 685 686 static int 687 grp_setgroupent(const nvlist_t *limits __unused, const nvlist_t *nvlin, 688 nvlist_t *nvlout __unused) 689 { 690 int stayopen; 691 692 if (!nvlist_exists_bool(nvlin, "stayopen")) 693 return (EINVAL); 694 695 stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0; 696 697 return (setgroupent(stayopen) == 0 ? EFAULT : 0); 698 } 699 700 static int 701 grp_setgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, 702 nvlist_t *nvlout __unused) 703 { 704 705 setgrent(); 706 707 return (0); 708 } 709 710 static int 711 grp_endgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, 712 nvlist_t *nvlout __unused) 713 { 714 715 endgrent(); 716 717 return (0); 718 } 719 720 static int 721 grp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 722 { 723 const nvlist_t *limits; 724 const char *name; 725 void *cookie; 726 int error, type; 727 728 if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") && 729 !nvlist_exists_nvlist(newlimits, "cmds")) { 730 return (ENOTCAPABLE); 731 } 732 if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") && 733 !nvlist_exists_nvlist(newlimits, "fields")) { 734 return (ENOTCAPABLE); 735 } 736 if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") && 737 !nvlist_exists_nvlist(newlimits, "groups")) { 738 return (ENOTCAPABLE); 739 } 740 741 cookie = NULL; 742 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 743 if (type != NV_TYPE_NVLIST) 744 return (EINVAL); 745 limits = nvlist_get_nvlist(newlimits, name); 746 if (strcmp(name, "cmds") == 0) 747 error = grp_allowed_cmds(oldlimits, limits); 748 else if (strcmp(name, "fields") == 0) 749 error = grp_allowed_fields(oldlimits, limits); 750 else if (strcmp(name, "groups") == 0) 751 error = grp_allowed_groups(oldlimits, limits); 752 else 753 error = EINVAL; 754 if (error != 0) 755 return (error); 756 } 757 758 return (0); 759 } 760 761 static int 762 grp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 763 nvlist_t *nvlout) 764 { 765 int error; 766 767 if (!grp_allowed_cmd(limits, cmd)) 768 return (ENOTCAPABLE); 769 770 if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) 771 error = grp_getgrent(limits, nvlin, nvlout); 772 else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0) 773 error = grp_getgrnam(limits, nvlin, nvlout); 774 else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0) 775 error = grp_getgrgid(limits, nvlin, nvlout); 776 else if (strcmp(cmd, "setgroupent") == 0) 777 error = grp_setgroupent(limits, nvlin, nvlout); 778 else if (strcmp(cmd, "setgrent") == 0) 779 error = grp_setgrent(limits, nvlin, nvlout); 780 else if (strcmp(cmd, "endgrent") == 0) 781 error = grp_endgrent(limits, nvlin, nvlout); 782 else 783 error = EINVAL; 784 785 return (error); 786 } 787 788 CREATE_SERVICE("system.grp", grp_limit, grp_command, 0); 789