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