1296047Soshogbo/*-
2296047Soshogbo * Copyright (c) 2013 The FreeBSD Foundation
3296047Soshogbo * All rights reserved.
4296047Soshogbo *
5296047Soshogbo * This software was developed by Pawel Jakub Dawidek under sponsorship from
6296047Soshogbo * the FreeBSD Foundation.
7296047Soshogbo *
8296047Soshogbo * Redistribution and use in source and binary forms, with or without
9296047Soshogbo * modification, are permitted provided that the following conditions
10296047Soshogbo * are met:
11296047Soshogbo * 1. Redistributions of source code must retain the above copyright
12296047Soshogbo *    notice, this list of conditions and the following disclaimer.
13296047Soshogbo * 2. Redistributions in binary form must reproduce the above copyright
14296047Soshogbo *    notice, this list of conditions and the following disclaimer in the
15296047Soshogbo *    documentation and/or other materials provided with the distribution.
16296047Soshogbo *
17296047Soshogbo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18296047Soshogbo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19296047Soshogbo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20296047Soshogbo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21296047Soshogbo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22296047Soshogbo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23296047Soshogbo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24296047Soshogbo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25296047Soshogbo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26296047Soshogbo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27296047Soshogbo * SUCH DAMAGE.
28296047Soshogbo */
29296047Soshogbo
30296047Soshogbo#include <sys/cdefs.h>
31296047Soshogbo__FBSDID("$FreeBSD$");
32296047Soshogbo
33296047Soshogbo#include <sys/dnv.h>
34296047Soshogbo#include <sys/nv.h>
35296047Soshogbo#include <sys/param.h>
36296047Soshogbo
37296047Soshogbo#include <assert.h>
38296047Soshogbo#include <errno.h>
39296047Soshogbo#include <grp.h>
40296047Soshogbo#include <stdlib.h>
41296047Soshogbo#include <string.h>
42296047Soshogbo
43296047Soshogbo#include <libcasper.h>
44296047Soshogbo#include <libcasper_service.h>
45296047Soshogbo
46296047Soshogbo#include "cap_grp.h"
47296047Soshogbo
48296047Soshogbostatic struct group ggrp;
49296047Soshogbostatic char *gbuffer;
50296047Soshogbostatic size_t gbufsize;
51296047Soshogbo
52296047Soshogbostatic int
53296047Soshogbogroup_resize(void)
54296047Soshogbo{
55296047Soshogbo	char *buf;
56296047Soshogbo
57296047Soshogbo	if (gbufsize == 0)
58296047Soshogbo		gbufsize = 1024;
59296047Soshogbo	else
60296047Soshogbo		gbufsize *= 2;
61296047Soshogbo
62296047Soshogbo	buf = gbuffer;
63296047Soshogbo	gbuffer = realloc(buf, gbufsize);
64296047Soshogbo	if (gbuffer == NULL) {
65296047Soshogbo		free(buf);
66296047Soshogbo		gbufsize = 0;
67296047Soshogbo		return (ENOMEM);
68296047Soshogbo	}
69296047Soshogbo	memset(gbuffer, 0, gbufsize);
70296047Soshogbo
71296047Soshogbo	return (0);
72296047Soshogbo}
73296047Soshogbo
74296047Soshogbostatic int
75296047Soshogbogroup_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp,
76296047Soshogbo    char **bufferp, size_t *bufsizep)
77296047Soshogbo{
78296047Soshogbo	const char *str;
79296047Soshogbo	size_t len;
80296047Soshogbo
81296047Soshogbo	str = nvlist_get_string(nvl, fieldname);
82296047Soshogbo	len = strlcpy(*bufferp, str, *bufsizep);
83296047Soshogbo	if (len >= *bufsizep)
84296047Soshogbo		return (ERANGE);
85296047Soshogbo	*fieldp = *bufferp;
86296047Soshogbo	*bufferp += len + 1;
87296047Soshogbo	*bufsizep -= len + 1;
88296047Soshogbo
89296047Soshogbo	return (0);
90296047Soshogbo}
91296047Soshogbo
92296047Soshogbostatic int
93296047Soshogbogroup_unpack_members(const nvlist_t *nvl, char ***fieldp, char **bufferp,
94296047Soshogbo    size_t *bufsizep)
95296047Soshogbo{
96296047Soshogbo	const char *mem;
97296047Soshogbo	char **outstrs, *str, nvlname[64];
98296047Soshogbo	size_t nmem, datasize, strsize;
99296047Soshogbo	unsigned int ii;
100296047Soshogbo	int n;
101296047Soshogbo
102296047Soshogbo	if (!nvlist_exists_number(nvl, "gr_nmem")) {
103296047Soshogbo		datasize = _ALIGNBYTES + sizeof(char *);
104296047Soshogbo		if (datasize >= *bufsizep)
105296047Soshogbo			return (ERANGE);
106296047Soshogbo		outstrs = (char **)_ALIGN(*bufferp);
107296047Soshogbo		outstrs[0] = NULL;
108296047Soshogbo		*fieldp = outstrs;
109296047Soshogbo		*bufferp += datasize;
110296047Soshogbo		*bufsizep -= datasize;
111296047Soshogbo		return (0);
112296047Soshogbo	}
113296047Soshogbo
114296047Soshogbo	nmem = (size_t)nvlist_get_number(nvl, "gr_nmem");
115296047Soshogbo	datasize = _ALIGNBYTES + sizeof(char *) * (nmem + 1);
116296047Soshogbo	for (ii = 0; ii < nmem; ii++) {
117296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii);
118296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
119296047Soshogbo		mem = dnvlist_get_string(nvl, nvlname, NULL);
120296047Soshogbo		if (mem == NULL)
121296047Soshogbo			return (EINVAL);
122296047Soshogbo		datasize += strlen(mem) + 1;
123296047Soshogbo	}
124296047Soshogbo
125296047Soshogbo	if (datasize >= *bufsizep)
126296047Soshogbo		return (ERANGE);
127296047Soshogbo
128296047Soshogbo	outstrs = (char **)_ALIGN(*bufferp);
129296047Soshogbo	str = (char *)outstrs + sizeof(char *) * (nmem + 1);
130296047Soshogbo	for (ii = 0; ii < nmem; ii++) {
131296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii);
132296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
133296047Soshogbo		mem = nvlist_get_string(nvl, nvlname);
134296047Soshogbo		strsize = strlen(mem) + 1;
135296047Soshogbo		memcpy(str, mem, strsize);
136296047Soshogbo		outstrs[ii] = str;
137296047Soshogbo		str += strsize;
138296047Soshogbo	}
139296047Soshogbo	assert(ii == nmem);
140296047Soshogbo	outstrs[ii] = NULL;
141296047Soshogbo
142296047Soshogbo	*fieldp = outstrs;
143296047Soshogbo	*bufferp += datasize;
144296047Soshogbo	*bufsizep -= datasize;
145296047Soshogbo
146296047Soshogbo	return (0);
147296047Soshogbo}
148296047Soshogbo
149296047Soshogbostatic int
150296047Soshogbogroup_unpack(const nvlist_t *nvl, struct group *grp, char *buffer,
151296047Soshogbo    size_t bufsize)
152296047Soshogbo{
153296047Soshogbo	int error;
154296047Soshogbo
155296047Soshogbo	if (!nvlist_exists_string(nvl, "gr_name"))
156296047Soshogbo		return (EINVAL);
157296047Soshogbo
158296047Soshogbo	memset(grp, 0, sizeof(*grp));
159296047Soshogbo
160296047Soshogbo	error = group_unpack_string(nvl, "gr_name", &grp->gr_name, &buffer,
161296047Soshogbo	    &bufsize);
162296047Soshogbo	if (error != 0)
163296047Soshogbo		return (error);
164296047Soshogbo	error = group_unpack_string(nvl, "gr_passwd", &grp->gr_passwd, &buffer,
165296047Soshogbo	    &bufsize);
166296047Soshogbo	if (error != 0)
167296047Soshogbo		return (error);
168296047Soshogbo	grp->gr_gid = (gid_t)nvlist_get_number(nvl, "gr_gid");
169296047Soshogbo	error = group_unpack_members(nvl, &grp->gr_mem, &buffer, &bufsize);
170296047Soshogbo	if (error != 0)
171296047Soshogbo		return (error);
172296047Soshogbo
173296047Soshogbo	return (0);
174296047Soshogbo}
175296047Soshogbo
176296047Soshogbostatic int
177296047Soshogbocap_getgrcommon_r(cap_channel_t *chan, const char *cmd, const char *name,
178296047Soshogbo    gid_t gid, struct group *grp, char *buffer, size_t bufsize,
179296047Soshogbo    struct group **result)
180296047Soshogbo{
181296047Soshogbo	nvlist_t *nvl;
182296047Soshogbo	bool getgr_r;
183296047Soshogbo	int error;
184296047Soshogbo
185296047Soshogbo	nvl = nvlist_create(0);
186296047Soshogbo	nvlist_add_string(nvl, "cmd", cmd);
187296047Soshogbo	if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) {
188296047Soshogbo		/* Add nothing. */
189296047Soshogbo	} else if (strcmp(cmd, "getgrnam") == 0 ||
190296047Soshogbo	    strcmp(cmd, "getgrnam_r") == 0) {
191296047Soshogbo		nvlist_add_string(nvl, "name", name);
192296047Soshogbo	} else if (strcmp(cmd, "getgrgid") == 0 ||
193296047Soshogbo	    strcmp(cmd, "getgrgid_r") == 0) {
194296047Soshogbo		nvlist_add_number(nvl, "gid", (uint64_t)gid);
195296047Soshogbo	} else {
196296047Soshogbo		abort();
197296047Soshogbo	}
198296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
199296047Soshogbo	if (nvl == NULL) {
200296047Soshogbo		assert(errno != 0);
201296047Soshogbo		*result = NULL;
202296047Soshogbo		return (errno);
203296047Soshogbo	}
204296047Soshogbo	error = (int)nvlist_get_number(nvl, "error");
205296047Soshogbo	if (error != 0) {
206296047Soshogbo		nvlist_destroy(nvl);
207296047Soshogbo		*result = NULL;
208296047Soshogbo		return (error);
209296047Soshogbo	}
210296047Soshogbo
211296047Soshogbo	if (!nvlist_exists_string(nvl, "gr_name")) {
212296047Soshogbo		/* Not found. */
213296047Soshogbo		nvlist_destroy(nvl);
214296047Soshogbo		*result = NULL;
215296047Soshogbo		return (0);
216296047Soshogbo	}
217296047Soshogbo
218296047Soshogbo	getgr_r = (strcmp(cmd, "getgrent_r") == 0 ||
219296047Soshogbo	    strcmp(cmd, "getgrnam_r") == 0 || strcmp(cmd, "getgrgid_r") == 0);
220296047Soshogbo
221296047Soshogbo	for (;;) {
222296047Soshogbo		error = group_unpack(nvl, grp, buffer, bufsize);
223296047Soshogbo		if (getgr_r || error != ERANGE)
224296047Soshogbo			break;
225296047Soshogbo		assert(buffer == gbuffer);
226296047Soshogbo		assert(bufsize == gbufsize);
227296047Soshogbo		error = group_resize();
228296047Soshogbo		if (error != 0)
229296047Soshogbo			break;
230296047Soshogbo		/* Update pointers after resize. */
231296047Soshogbo		buffer = gbuffer;
232296047Soshogbo		bufsize = gbufsize;
233296047Soshogbo	}
234296047Soshogbo
235296047Soshogbo	nvlist_destroy(nvl);
236296047Soshogbo
237296047Soshogbo	if (error == 0)
238296047Soshogbo		*result = grp;
239296047Soshogbo	else
240296047Soshogbo		*result = NULL;
241296047Soshogbo
242296047Soshogbo	return (error);
243296047Soshogbo}
244296047Soshogbo
245296047Soshogbostatic struct group *
246296047Soshogbocap_getgrcommon(cap_channel_t *chan, const char *cmd, const char *name,
247296047Soshogbo    gid_t gid)
248296047Soshogbo{
249296047Soshogbo	struct group *result;
250296047Soshogbo	int error, serrno;
251296047Soshogbo
252296047Soshogbo	serrno = errno;
253296047Soshogbo
254296047Soshogbo	error = cap_getgrcommon_r(chan, cmd, name, gid, &ggrp, gbuffer,
255296047Soshogbo	    gbufsize, &result);
256296047Soshogbo	if (error != 0) {
257296047Soshogbo		errno = error;
258296047Soshogbo		return (NULL);
259296047Soshogbo	}
260296047Soshogbo
261296047Soshogbo	errno = serrno;
262296047Soshogbo
263296047Soshogbo	return (result);
264296047Soshogbo}
265296047Soshogbo
266296047Soshogbostruct group *
267296047Soshogbocap_getgrent(cap_channel_t *chan)
268296047Soshogbo{
269296047Soshogbo
270296047Soshogbo	return (cap_getgrcommon(chan, "getgrent", NULL, 0));
271296047Soshogbo}
272296047Soshogbo
273296047Soshogbostruct group *
274296047Soshogbocap_getgrnam(cap_channel_t *chan, const char *name)
275296047Soshogbo{
276296047Soshogbo
277296047Soshogbo	return (cap_getgrcommon(chan, "getgrnam", name, 0));
278296047Soshogbo}
279296047Soshogbo
280296047Soshogbostruct group *
281296047Soshogbocap_getgrgid(cap_channel_t *chan, gid_t gid)
282296047Soshogbo{
283296047Soshogbo
284296047Soshogbo	return (cap_getgrcommon(chan, "getgrgid", NULL, gid));
285296047Soshogbo}
286296047Soshogbo
287296047Soshogboint
288296047Soshogbocap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer,
289296047Soshogbo    size_t bufsize, struct group **result)
290296047Soshogbo{
291296047Soshogbo
292296047Soshogbo	return (cap_getgrcommon_r(chan, "getgrent_r", NULL, 0, grp, buffer,
293296047Soshogbo	    bufsize, result));
294296047Soshogbo}
295296047Soshogbo
296296047Soshogboint
297296047Soshogbocap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp,
298296047Soshogbo    char *buffer, size_t bufsize, struct group **result)
299296047Soshogbo{
300296047Soshogbo
301296047Soshogbo	return (cap_getgrcommon_r(chan, "getgrnam_r", name, 0, grp, buffer,
302296047Soshogbo	    bufsize, result));
303296047Soshogbo}
304296047Soshogbo
305296047Soshogboint
306296047Soshogbocap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer,
307296047Soshogbo    size_t bufsize, struct group **result)
308296047Soshogbo{
309296047Soshogbo
310296047Soshogbo	return (cap_getgrcommon_r(chan, "getgrgid_r", NULL, gid, grp, buffer,
311296047Soshogbo	    bufsize, result));
312296047Soshogbo}
313296047Soshogbo
314296047Soshogboint
315296047Soshogbocap_setgroupent(cap_channel_t *chan, int stayopen)
316296047Soshogbo{
317296047Soshogbo	nvlist_t *nvl;
318296047Soshogbo
319296047Soshogbo	nvl = nvlist_create(0);
320296047Soshogbo	nvlist_add_string(nvl, "cmd", "setgroupent");
321296047Soshogbo	nvlist_add_bool(nvl, "stayopen", stayopen != 0);
322296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
323296047Soshogbo	if (nvl == NULL)
324296047Soshogbo		return (0);
325296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
326296047Soshogbo		errno = nvlist_get_number(nvl, "error");
327296047Soshogbo		nvlist_destroy(nvl);
328296047Soshogbo		return (0);
329296047Soshogbo	}
330296047Soshogbo	nvlist_destroy(nvl);
331296047Soshogbo
332296047Soshogbo	return (1);
333296047Soshogbo}
334296047Soshogbo
335296047Soshogboint
336296047Soshogbocap_setgrent(cap_channel_t *chan)
337296047Soshogbo{
338296047Soshogbo	nvlist_t *nvl;
339296047Soshogbo
340296047Soshogbo	nvl = nvlist_create(0);
341296047Soshogbo	nvlist_add_string(nvl, "cmd", "setgrent");
342296047Soshogbo	nvl = cap_xfer_nvlist(chan, nvl, 0);
343296047Soshogbo	if (nvl == NULL)
344296047Soshogbo		return (0);
345296047Soshogbo	if (nvlist_get_number(nvl, "error") != 0) {
346296047Soshogbo		errno = nvlist_get_number(nvl, "error");
347296047Soshogbo		nvlist_destroy(nvl);
348296047Soshogbo		return (0);
349296047Soshogbo	}
350296047Soshogbo	nvlist_destroy(nvl);
351296047Soshogbo
352296047Soshogbo	return (1);
353296047Soshogbo}
354296047Soshogbo
355296047Soshogbovoid
356296047Soshogbocap_endgrent(cap_channel_t *chan)
357296047Soshogbo{
358296047Soshogbo	nvlist_t *nvl;
359296047Soshogbo
360296047Soshogbo	nvl = nvlist_create(0);
361296047Soshogbo	nvlist_add_string(nvl, "cmd", "endgrent");
362296047Soshogbo	/* Ignore any errors, we have no way to report them. */
363296047Soshogbo	nvlist_destroy(cap_xfer_nvlist(chan, nvl, 0));
364296047Soshogbo}
365296047Soshogbo
366296047Soshogboint
367296047Soshogbocap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds)
368296047Soshogbo{
369296047Soshogbo	nvlist_t *limits, *nvl;
370296047Soshogbo	unsigned int i;
371296047Soshogbo
372296047Soshogbo	if (cap_limit_get(chan, &limits) < 0)
373296047Soshogbo		return (-1);
374296047Soshogbo	if (limits == NULL) {
375296047Soshogbo		limits = nvlist_create(0);
376296047Soshogbo	} else {
377296047Soshogbo		if (nvlist_exists_nvlist(limits, "cmds"))
378296047Soshogbo			nvlist_free_nvlist(limits, "cmds");
379296047Soshogbo	}
380296047Soshogbo	nvl = nvlist_create(0);
381296047Soshogbo	for (i = 0; i < ncmds; i++)
382296047Soshogbo		nvlist_add_null(nvl, cmds[i]);
383296047Soshogbo	nvlist_move_nvlist(limits, "cmds", nvl);
384296047Soshogbo	return (cap_limit_set(chan, limits));
385296047Soshogbo}
386296047Soshogbo
387296047Soshogboint
388296047Soshogbocap_grp_limit_fields(cap_channel_t *chan, const char * const *fields,
389296047Soshogbo    size_t nfields)
390296047Soshogbo{
391296047Soshogbo	nvlist_t *limits, *nvl;
392296047Soshogbo	unsigned int i;
393296047Soshogbo
394296047Soshogbo	if (cap_limit_get(chan, &limits) < 0)
395296047Soshogbo		return (-1);
396296047Soshogbo	if (limits == NULL) {
397296047Soshogbo		limits = nvlist_create(0);
398296047Soshogbo	} else {
399296047Soshogbo		if (nvlist_exists_nvlist(limits, "fields"))
400296047Soshogbo			nvlist_free_nvlist(limits, "fields");
401296047Soshogbo	}
402296047Soshogbo	nvl = nvlist_create(0);
403296047Soshogbo	for (i = 0; i < nfields; i++)
404296047Soshogbo		nvlist_add_null(nvl, fields[i]);
405296047Soshogbo	nvlist_move_nvlist(limits, "fields", nvl);
406296047Soshogbo	return (cap_limit_set(chan, limits));
407296047Soshogbo}
408296047Soshogbo
409296047Soshogboint
410296047Soshogbocap_grp_limit_groups(cap_channel_t *chan, const char * const *names,
411296047Soshogbo    size_t nnames, gid_t *gids, size_t ngids)
412296047Soshogbo{
413296047Soshogbo	nvlist_t *limits, *groups;
414296047Soshogbo	unsigned int i;
415296047Soshogbo	char nvlname[64];
416296047Soshogbo	int n;
417296047Soshogbo
418296047Soshogbo	if (cap_limit_get(chan, &limits) < 0)
419296047Soshogbo		return (-1);
420296047Soshogbo	if (limits == NULL) {
421296047Soshogbo		limits = nvlist_create(0);
422296047Soshogbo	} else {
423296047Soshogbo		if (nvlist_exists_nvlist(limits, "groups"))
424296047Soshogbo			nvlist_free_nvlist(limits, "groups");
425296047Soshogbo	}
426296047Soshogbo	groups = nvlist_create(0);
427296047Soshogbo	for (i = 0; i < ngids; i++) {
428296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "gid%u", i);
429296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
430296047Soshogbo		nvlist_add_number(groups, nvlname, (uint64_t)gids[i]);
431296047Soshogbo	}
432296047Soshogbo	for (i = 0; i < nnames; i++) {
433296047Soshogbo		n = snprintf(nvlname, sizeof(nvlname), "gid%u", i);
434296047Soshogbo		assert(n > 0 && n < (int)sizeof(nvlname));
435296047Soshogbo		nvlist_add_string(groups, nvlname, names[i]);
436296047Soshogbo	}
437296047Soshogbo	nvlist_move_nvlist(limits, "groups", groups);
438296047Soshogbo	return (cap_limit_set(chan, limits));
439296047Soshogbo}
440296047Soshogbo
441296047Soshogbo/*
442296047Soshogbo * Service functions.
443296047Soshogbo */
444296047Soshogbostatic bool
445296047Soshogbogrp_allowed_cmd(const nvlist_t *limits, const char *cmd)
446296047Soshogbo{
447296047Soshogbo
448296047Soshogbo	if (limits == NULL)
449296047Soshogbo		return (true);
450296047Soshogbo
451296047Soshogbo	/*
452296047Soshogbo	 * If no limit was set on allowed commands, then all commands
453296047Soshogbo	 * are allowed.
454296047Soshogbo	 */
455296047Soshogbo	if (!nvlist_exists_nvlist(limits, "cmds"))
456296047Soshogbo		return (true);
457296047Soshogbo
458296047Soshogbo	limits = nvlist_get_nvlist(limits, "cmds");
459296047Soshogbo	return (nvlist_exists_null(limits, cmd));
460296047Soshogbo}
461296047Soshogbo
462296047Soshogbostatic int
463296047Soshogbogrp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
464296047Soshogbo{
465296047Soshogbo	const char *name;
466296047Soshogbo	void *cookie;
467296047Soshogbo	int type;
468296047Soshogbo
469296047Soshogbo	cookie = NULL;
470296047Soshogbo	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
471296047Soshogbo		if (type != NV_TYPE_NULL)
472296047Soshogbo			return (EINVAL);
473296047Soshogbo		if (!grp_allowed_cmd(oldlimits, name))
474296047Soshogbo			return (ENOTCAPABLE);
475296047Soshogbo	}
476296047Soshogbo
477296047Soshogbo	return (0);
478296047Soshogbo}
479296047Soshogbo
480296047Soshogbostatic bool
481296047Soshogbogrp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid)
482296047Soshogbo{
483296047Soshogbo	const char *name;
484296047Soshogbo	void *cookie;
485296047Soshogbo	int type;
486296047Soshogbo
487296047Soshogbo	if (limits == NULL)
488296047Soshogbo		return (true);
489296047Soshogbo
490296047Soshogbo	/*
491296047Soshogbo	 * If no limit was set on allowed groups, then all groups are allowed.
492296047Soshogbo	 */
493296047Soshogbo	if (!nvlist_exists_nvlist(limits, "groups"))
494296047Soshogbo		return (true);
495296047Soshogbo
496296047Soshogbo	limits = nvlist_get_nvlist(limits, "groups");
497296047Soshogbo	cookie = NULL;
498296047Soshogbo	while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
499296047Soshogbo		switch (type) {
500296047Soshogbo		case NV_TYPE_NUMBER:
501296047Soshogbo			if (gid != (gid_t)-1 &&
502296047Soshogbo			    nvlist_get_number(limits, name) == (uint64_t)gid) {
503296047Soshogbo				return (true);
504296047Soshogbo			}
505296047Soshogbo			break;
506296047Soshogbo		case NV_TYPE_STRING:
507296047Soshogbo			if (gname != NULL &&
508296047Soshogbo			    strcmp(nvlist_get_string(limits, name),
509296047Soshogbo			    gname) == 0) {
510296047Soshogbo				return (true);
511296047Soshogbo			}
512296047Soshogbo			break;
513296047Soshogbo		default:
514296047Soshogbo			abort();
515296047Soshogbo		}
516296047Soshogbo	}
517296047Soshogbo
518296047Soshogbo	return (false);
519296047Soshogbo}
520296047Soshogbo
521296047Soshogbostatic int
522296047Soshogbogrp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits)
523296047Soshogbo{
524296047Soshogbo	const char *name, *gname;
525296047Soshogbo	void *cookie;
526296047Soshogbo	gid_t gid;
527296047Soshogbo	int type;
528296047Soshogbo
529296047Soshogbo	cookie = NULL;
530296047Soshogbo	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
531296047Soshogbo		switch (type) {
532296047Soshogbo		case NV_TYPE_NUMBER:
533296047Soshogbo			gid = (gid_t)nvlist_get_number(newlimits, name);
534296047Soshogbo			gname = NULL;
535296047Soshogbo			break;
536296047Soshogbo		case NV_TYPE_STRING:
537296047Soshogbo			gid = (gid_t)-1;
538296047Soshogbo			gname = nvlist_get_string(newlimits, name);
539296047Soshogbo			break;
540296047Soshogbo		default:
541296047Soshogbo			return (EINVAL);
542296047Soshogbo		}
543296047Soshogbo		if (!grp_allowed_group(oldlimits, gname, gid))
544296047Soshogbo			return (ENOTCAPABLE);
545296047Soshogbo	}
546296047Soshogbo
547296047Soshogbo	return (0);
548296047Soshogbo}
549296047Soshogbo
550296047Soshogbostatic bool
551296047Soshogbogrp_allowed_field(const nvlist_t *limits, const char *field)
552296047Soshogbo{
553296047Soshogbo
554296047Soshogbo	if (limits == NULL)
555296047Soshogbo		return (true);
556296047Soshogbo
557296047Soshogbo	/*
558296047Soshogbo	 * If no limit was set on allowed fields, then all fields are allowed.
559296047Soshogbo	 */
560296047Soshogbo	if (!nvlist_exists_nvlist(limits, "fields"))
561296047Soshogbo		return (true);
562296047Soshogbo
563296047Soshogbo	limits = nvlist_get_nvlist(limits, "fields");
564296047Soshogbo	return (nvlist_exists_null(limits, field));
565296047Soshogbo}
566296047Soshogbo
567296047Soshogbostatic int
568296047Soshogbogrp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
569296047Soshogbo{
570296047Soshogbo	const char *name;
571296047Soshogbo	void *cookie;
572296047Soshogbo	int type;
573296047Soshogbo
574296047Soshogbo	cookie = NULL;
575296047Soshogbo	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
576296047Soshogbo		if (type != NV_TYPE_NULL)
577296047Soshogbo			return (EINVAL);
578296047Soshogbo		if (!grp_allowed_field(oldlimits, name))
579296047Soshogbo			return (ENOTCAPABLE);
580296047Soshogbo	}
581296047Soshogbo
582296047Soshogbo	return (0);
583296047Soshogbo}
584296047Soshogbo
585296047Soshogbostatic bool
586296047Soshogbogrp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl)
587296047Soshogbo{
588296047Soshogbo	char nvlname[64];
589296047Soshogbo	int n;
590296047Soshogbo
591296047Soshogbo	if (grp == NULL)
592296047Soshogbo		return (true);
593296047Soshogbo
594296047Soshogbo	/*
595296047Soshogbo	 * If either name or GID is allowed, we allow it.
596296047Soshogbo	 */
597296047Soshogbo	if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid))
598296047Soshogbo		return (false);
599296047Soshogbo
600296047Soshogbo	if (grp_allowed_field(limits, "gr_name"))
601296047Soshogbo		nvlist_add_string(nvl, "gr_name", grp->gr_name);
602296047Soshogbo	else
603296047Soshogbo		nvlist_add_string(nvl, "gr_name", "");
604296047Soshogbo	if (grp_allowed_field(limits, "gr_passwd"))
605296047Soshogbo		nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd);
606296047Soshogbo	else
607296047Soshogbo		nvlist_add_string(nvl, "gr_passwd", "");
608296047Soshogbo	if (grp_allowed_field(limits, "gr_gid"))
609296047Soshogbo		nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid);
610296047Soshogbo	else
611296047Soshogbo		nvlist_add_number(nvl, "gr_gid", (uint64_t)-1);
612296047Soshogbo	if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) {
613296047Soshogbo		unsigned int ngroups;
614296047Soshogbo
615296047Soshogbo		for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) {
616296047Soshogbo			n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]",
617296047Soshogbo			    ngroups);
618296047Soshogbo			assert(n > 0 && n < (ssize_t)sizeof(nvlname));
619296047Soshogbo			nvlist_add_string(nvl, nvlname, grp->gr_mem[ngroups]);
620296047Soshogbo		}
621296047Soshogbo		nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups);
622296047Soshogbo	}
623296047Soshogbo
624296047Soshogbo	return (true);
625296047Soshogbo}
626296047Soshogbo
627296047Soshogbostatic int
628296047Soshogbogrp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin __unused,
629296047Soshogbo    nvlist_t *nvlout)
630296047Soshogbo{
631296047Soshogbo	struct group *grp;
632296047Soshogbo
633296047Soshogbo	for (;;) {
634296047Soshogbo		errno = 0;
635296047Soshogbo		grp = getgrent();
636296047Soshogbo		if (errno != 0)
637296047Soshogbo			return (errno);
638296047Soshogbo		if (grp_pack(limits, grp, nvlout))
639296047Soshogbo			return (0);
640296047Soshogbo	}
641296047Soshogbo
642296047Soshogbo	/* NOTREACHED */
643296047Soshogbo}
644296047Soshogbo
645296047Soshogbostatic int
646296047Soshogbogrp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
647296047Soshogbo{
648296047Soshogbo	struct group *grp;
649296047Soshogbo	const char *name;
650296047Soshogbo
651296047Soshogbo	if (!nvlist_exists_string(nvlin, "name"))
652296047Soshogbo		return (EINVAL);
653296047Soshogbo	name = nvlist_get_string(nvlin, "name");
654296047Soshogbo	assert(name != NULL);
655296047Soshogbo
656296047Soshogbo	errno = 0;
657296047Soshogbo	grp = getgrnam(name);
658296047Soshogbo	if (errno != 0)
659296047Soshogbo		return (errno);
660296047Soshogbo
661296047Soshogbo	(void)grp_pack(limits, grp, nvlout);
662296047Soshogbo
663296047Soshogbo	return (0);
664296047Soshogbo}
665296047Soshogbo
666296047Soshogbostatic int
667296047Soshogbogrp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
668296047Soshogbo{
669296047Soshogbo	struct group *grp;
670296047Soshogbo	gid_t gid;
671296047Soshogbo
672296047Soshogbo	if (!nvlist_exists_number(nvlin, "gid"))
673296047Soshogbo		return (EINVAL);
674296047Soshogbo
675296047Soshogbo	gid = (gid_t)nvlist_get_number(nvlin, "gid");
676296047Soshogbo
677296047Soshogbo	errno = 0;
678296047Soshogbo	grp = getgrgid(gid);
679296047Soshogbo	if (errno != 0)
680296047Soshogbo		return (errno);
681296047Soshogbo
682296047Soshogbo	(void)grp_pack(limits, grp, nvlout);
683296047Soshogbo
684296047Soshogbo	return (0);
685296047Soshogbo}
686296047Soshogbo
687296047Soshogbostatic int
688296047Soshogbogrp_setgroupent(const nvlist_t *limits __unused, const nvlist_t *nvlin,
689296047Soshogbo    nvlist_t *nvlout __unused)
690296047Soshogbo{
691296047Soshogbo	int stayopen;
692296047Soshogbo
693296047Soshogbo	if (!nvlist_exists_bool(nvlin, "stayopen"))
694296047Soshogbo		return (EINVAL);
695296047Soshogbo
696296047Soshogbo	stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
697296047Soshogbo
698296047Soshogbo	return (setgroupent(stayopen) == 0 ? EFAULT : 0);
699296047Soshogbo}
700296047Soshogbo
701296047Soshogbostatic int
702296047Soshogbogrp_setgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused,
703296047Soshogbo    nvlist_t *nvlout __unused)
704296047Soshogbo{
705296047Soshogbo
706301167Sed	setgrent();
707301167Sed
708301167Sed	return (0);
709296047Soshogbo}
710296047Soshogbo
711296047Soshogbostatic int
712296047Soshogbogrp_endgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused,
713296047Soshogbo    nvlist_t *nvlout __unused)
714296047Soshogbo{
715296047Soshogbo
716296047Soshogbo	endgrent();
717296047Soshogbo
718296047Soshogbo	return (0);
719296047Soshogbo}
720296047Soshogbo
721296047Soshogbostatic int
722296047Soshogbogrp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
723296047Soshogbo{
724296047Soshogbo	const nvlist_t *limits;
725296047Soshogbo	const char *name;
726296047Soshogbo	void *cookie;
727296047Soshogbo	int error, type;
728296047Soshogbo
729296047Soshogbo	if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
730296047Soshogbo	    !nvlist_exists_nvlist(newlimits, "cmds")) {
731296047Soshogbo		return (ENOTCAPABLE);
732296047Soshogbo	}
733296047Soshogbo	if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
734296047Soshogbo	    !nvlist_exists_nvlist(newlimits, "fields")) {
735296047Soshogbo		return (ENOTCAPABLE);
736296047Soshogbo	}
737296047Soshogbo	if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") &&
738296047Soshogbo	    !nvlist_exists_nvlist(newlimits, "groups")) {
739296047Soshogbo		return (ENOTCAPABLE);
740296047Soshogbo	}
741296047Soshogbo
742296047Soshogbo	cookie = NULL;
743296047Soshogbo	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
744296047Soshogbo		if (type != NV_TYPE_NVLIST)
745296047Soshogbo			return (EINVAL);
746296047Soshogbo		limits = nvlist_get_nvlist(newlimits, name);
747296047Soshogbo		if (strcmp(name, "cmds") == 0)
748296047Soshogbo			error = grp_allowed_cmds(oldlimits, limits);
749296047Soshogbo		else if (strcmp(name, "fields") == 0)
750296047Soshogbo			error = grp_allowed_fields(oldlimits, limits);
751296047Soshogbo		else if (strcmp(name, "groups") == 0)
752296047Soshogbo			error = grp_allowed_groups(oldlimits, limits);
753296047Soshogbo		else
754296047Soshogbo			error = EINVAL;
755296047Soshogbo		if (error != 0)
756296047Soshogbo			return (error);
757296047Soshogbo	}
758296047Soshogbo
759296047Soshogbo	return (0);
760296047Soshogbo}
761296047Soshogbo
762296047Soshogbostatic int
763296047Soshogbogrp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
764296047Soshogbo    nvlist_t *nvlout)
765296047Soshogbo{
766296047Soshogbo	int error;
767296047Soshogbo
768296047Soshogbo	if (!grp_allowed_cmd(limits, cmd))
769296047Soshogbo		return (ENOTCAPABLE);
770296047Soshogbo
771296047Soshogbo	if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0)
772296047Soshogbo		error = grp_getgrent(limits, nvlin, nvlout);
773296047Soshogbo	else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0)
774296047Soshogbo		error = grp_getgrnam(limits, nvlin, nvlout);
775296047Soshogbo	else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0)
776296047Soshogbo		error = grp_getgrgid(limits, nvlin, nvlout);
777296047Soshogbo	else if (strcmp(cmd, "setgroupent") == 0)
778296047Soshogbo		error = grp_setgroupent(limits, nvlin, nvlout);
779296047Soshogbo	else if (strcmp(cmd, "setgrent") == 0)
780296047Soshogbo		error = grp_setgrent(limits, nvlin, nvlout);
781296047Soshogbo	else if (strcmp(cmd, "endgrent") == 0)
782296047Soshogbo		error = grp_endgrent(limits, nvlin, nvlout);
783296047Soshogbo	else
784296047Soshogbo		error = EINVAL;
785296047Soshogbo
786296047Soshogbo	return (error);
787296047Soshogbo}
788296047Soshogbo
789301572SoshogboCREATE_SERVICE("system.grp", grp_limit, grp_command, 0);
790