]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/services/cap_grp/cap_grp.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r301441, and update
[FreeBSD/FreeBSD.git] / lib / libcasper / services / cap_grp / cap_grp.c
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
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         memset(grp, 0, 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, 0);
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, 0);
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, 0);
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, 0));
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, 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);