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