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