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
group_resize(void)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
group_unpack_string(const nvlist_t * nvl,const char * fieldname,char ** fieldp,char ** bufferp,size_t * bufsizep)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
group_unpack_members(const nvlist_t * nvl,char *** fieldp,char ** bufferp,size_t * bufsizep)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 **)(void *)_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 **)(void *)_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
group_unpack(const nvlist_t * nvl,struct group * grp,char * buffer,size_t bufsize)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
cap_getgrcommon_r(cap_channel_t * chan,const char * cmd,const char * name,gid_t gid,struct group * grp,char * buffer,size_t bufsize,struct group ** result)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 *
cap_getgrcommon(cap_channel_t * chan,const char * cmd,const char * name,gid_t gid)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 *
cap_getgrent(cap_channel_t * chan)266 cap_getgrent(cap_channel_t *chan)
267 {
268
269 return (cap_getgrcommon(chan, "getgrent", NULL, 0));
270 }
271
272 struct group *
cap_getgrnam(cap_channel_t * chan,const char * name)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 *
cap_getgrgid(cap_channel_t * chan,gid_t gid)280 cap_getgrgid(cap_channel_t *chan, gid_t gid)
281 {
282
283 return (cap_getgrcommon(chan, "getgrgid", NULL, gid));
284 }
285
286 int
cap_getgrent_r(cap_channel_t * chan,struct group * grp,char * buffer,size_t bufsize,struct group ** result)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
cap_getgrnam_r(cap_channel_t * chan,const char * name,struct group * grp,char * buffer,size_t bufsize,struct group ** result)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
cap_getgrgid_r(cap_channel_t * chan,gid_t gid,struct group * grp,char * buffer,size_t bufsize,struct group ** result)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
cap_setgroupent(cap_channel_t * chan,int stayopen)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
cap_setgrent(cap_channel_t * chan)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
cap_endgrent(cap_channel_t * chan)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
cap_grp_limit_cmds(cap_channel_t * chan,const char * const * cmds,size_t ncmds)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
cap_grp_limit_fields(cap_channel_t * chan,const char * const * fields,size_t nfields)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
cap_grp_limit_groups(cap_channel_t * chan,const char * const * names,size_t nnames,const gid_t * gids,size_t ngids)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
grp_allowed_cmd(const nvlist_t * limits,const char * cmd)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
grp_allowed_cmds(const nvlist_t * oldlimits,const nvlist_t * newlimits)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
grp_allowed_group(const nvlist_t * limits,const char * gname,gid_t gid)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
grp_allowed_groups(const nvlist_t * oldlimits,const nvlist_t * newlimits)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
grp_allowed_field(const nvlist_t * limits,const char * field)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
grp_allowed_fields(const nvlist_t * oldlimits,const nvlist_t * newlimits)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
grp_pack(const nvlist_t * limits,const struct group * grp,nvlist_t * nvl)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
grp_getgrent(const nvlist_t * limits,const nvlist_t * nvlin __unused,nvlist_t * nvlout)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
grp_getgrnam(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)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
grp_getgrgid(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)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
grp_setgroupent(const nvlist_t * limits __unused,const nvlist_t * nvlin,nvlist_t * nvlout __unused)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
grp_setgrent(const nvlist_t * limits __unused,const nvlist_t * nvlin __unused,nvlist_t * nvlout __unused)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
grp_endgrent(const nvlist_t * limits __unused,const nvlist_t * nvlin __unused,nvlist_t * nvlout __unused)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
grp_limit(const nvlist_t * oldlimits,const nvlist_t * newlimits)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
grp_command(const char * cmd,const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)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