xref: /NextBSD/libexec/casper/grp/grp.c (revision 287e3b14e9552995def1802ec9c5034f4adf28ec)
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/nv.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <grp.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <libcapsicum.h>
42 #include <libcasper.h>
43 #include <pjdlog.h>
44 
45 static bool
grp_allowed_cmd(const nvlist_t * limits,const char * cmd)46 grp_allowed_cmd(const nvlist_t *limits, const char *cmd)
47 {
48 
49 	if (limits == NULL)
50 		return (true);
51 
52 	/*
53 	 * If no limit was set on allowed commands, then all commands
54 	 * are allowed.
55 	 */
56 	if (!nvlist_exists_nvlist(limits, "cmds"))
57 		return (true);
58 
59 	limits = nvlist_get_nvlist(limits, "cmds");
60 	return (nvlist_exists_null(limits, cmd));
61 }
62 
63 static int
grp_allowed_cmds(const nvlist_t * oldlimits,const nvlist_t * newlimits)64 grp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
65 {
66 	const char *name;
67 	void *cookie;
68 	int type;
69 
70 	cookie = NULL;
71 	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
72 		if (type != NV_TYPE_NULL)
73 			return (EINVAL);
74 		if (!grp_allowed_cmd(oldlimits, name))
75 			return (ENOTCAPABLE);
76 	}
77 
78 	return (0);
79 }
80 
81 static bool
grp_allowed_group(const nvlist_t * limits,const char * gname,gid_t gid)82 grp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid)
83 {
84 	const char *name;
85 	void *cookie;
86 	int type;
87 
88 	if (limits == NULL)
89 		return (true);
90 
91 	/*
92 	 * If no limit was set on allowed groups, then all groups are allowed.
93 	 */
94 	if (!nvlist_exists_nvlist(limits, "groups"))
95 		return (true);
96 
97 	limits = nvlist_get_nvlist(limits, "groups");
98 	cookie = NULL;
99 	while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
100 		switch (type) {
101 		case NV_TYPE_NUMBER:
102 			if (gid != (gid_t)-1 &&
103 			    nvlist_get_number(limits, name) == (uint64_t)gid) {
104 				return (true);
105 			}
106 			break;
107 		case NV_TYPE_STRING:
108 			if (gname != NULL &&
109 			    strcmp(nvlist_get_string(limits, name),
110 			    gname) == 0) {
111 				return (true);
112 			}
113 			break;
114 		default:
115 			PJDLOG_ABORT("Unexpected type %d.", type);
116 		}
117 	}
118 
119 	return (false);
120 }
121 
122 static int
grp_allowed_groups(const nvlist_t * oldlimits,const nvlist_t * newlimits)123 grp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits)
124 {
125 	const char *name, *gname;
126 	void *cookie;
127 	gid_t gid;
128 	int type;
129 
130 	cookie = NULL;
131 	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
132 		switch (type) {
133 		case NV_TYPE_NUMBER:
134 			gid = (gid_t)nvlist_get_number(newlimits, name);
135 			gname = NULL;
136 			break;
137 		case NV_TYPE_STRING:
138 			gid = (gid_t)-1;
139 			gname = nvlist_get_string(newlimits, name);
140 			break;
141 		default:
142 			return (EINVAL);
143 		}
144 		if (!grp_allowed_group(oldlimits, gname, gid))
145 			return (ENOTCAPABLE);
146 	}
147 
148 	return (0);
149 }
150 
151 static bool
grp_allowed_field(const nvlist_t * limits,const char * field)152 grp_allowed_field(const nvlist_t *limits, const char *field)
153 {
154 
155 	if (limits == NULL)
156 		return (true);
157 
158 	/*
159 	 * If no limit was set on allowed fields, then all fields are allowed.
160 	 */
161 	if (!nvlist_exists_nvlist(limits, "fields"))
162 		return (true);
163 
164 	limits = nvlist_get_nvlist(limits, "fields");
165 	return (nvlist_exists_null(limits, field));
166 }
167 
168 static int
grp_allowed_fields(const nvlist_t * oldlimits,const nvlist_t * newlimits)169 grp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
170 {
171 	const char *name;
172 	void *cookie;
173 	int type;
174 
175 	cookie = NULL;
176 	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
177 		if (type != NV_TYPE_NULL)
178 			return (EINVAL);
179 		if (!grp_allowed_field(oldlimits, name))
180 			return (ENOTCAPABLE);
181 	}
182 
183 	return (0);
184 }
185 
186 static bool
grp_pack(const nvlist_t * limits,const struct group * grp,nvlist_t * nvl)187 grp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl)
188 {
189 	char nvlname[64];
190 	int n;
191 
192 	if (grp == NULL)
193 		return (true);
194 
195 	/*
196 	 * If either name or GID is allowed, we allow it.
197 	 */
198 	if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid))
199 		return (false);
200 
201 	if (grp_allowed_field(limits, "gr_name"))
202 		nvlist_add_string(nvl, "gr_name", grp->gr_name);
203 	else
204 		nvlist_add_string(nvl, "gr_name", "");
205 	if (grp_allowed_field(limits, "gr_passwd"))
206 		nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd);
207 	else
208 		nvlist_add_string(nvl, "gr_passwd", "");
209 	if (grp_allowed_field(limits, "gr_gid"))
210 		nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid);
211 	else
212 		nvlist_add_number(nvl, "gr_gid", (uint64_t)-1);
213 	if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) {
214 		unsigned int ngroups;
215 
216 		for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) {
217 			n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]",
218 			    ngroups);
219 			assert(n > 0 && n < sizeof(nvlname));
220 			nvlist_add_string(nvl, nvlname, grp->gr_mem[ngroups]);
221 		}
222 		nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups);
223 	}
224 
225 	return (true);
226 }
227 
228 static int
grp_getgrent(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)229 grp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
230 {
231 	struct group *grp;
232 
233 	for (;;) {
234 		errno = 0;
235 		grp = getgrent();
236 		if (errno != 0)
237 			return (errno);
238 		if (grp_pack(limits, grp, nvlout))
239 			return (0);
240 	}
241 
242 	/* NOTREACHED */
243 }
244 
245 static int
grp_getgrnam(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)246 grp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
247 {
248 	struct group *grp;
249 	const char *name;
250 
251 	if (!nvlist_exists_string(nvlin, "name"))
252 		return (EINVAL);
253 	name = nvlist_get_string(nvlin, "name");
254 	PJDLOG_ASSERT(name != NULL);
255 
256 	errno = 0;
257 	grp = getgrnam(name);
258 	if (errno != 0)
259 		return (errno);
260 
261 	(void)grp_pack(limits, grp, nvlout);
262 
263 	return (0);
264 }
265 
266 static int
grp_getgrgid(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)267 grp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
268 {
269 	struct group *grp;
270 	gid_t gid;
271 
272 	if (!nvlist_exists_number(nvlin, "gid"))
273 		return (EINVAL);
274 
275 	gid = (gid_t)nvlist_get_number(nvlin, "gid");
276 
277 	errno = 0;
278 	grp = getgrgid(gid);
279 	if (errno != 0)
280 		return (errno);
281 
282 	(void)grp_pack(limits, grp, nvlout);
283 
284 	return (0);
285 }
286 
287 static int
grp_setgroupent(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)288 grp_setgroupent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
289 {
290 	int stayopen;
291 
292 	if (!nvlist_exists_bool(nvlin, "stayopen"))
293 		return (EINVAL);
294 
295 	stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
296 
297 	return (setgroupent(stayopen) == 0 ? EFAULT : 0);
298 }
299 
300 static int
grp_setgrent(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)301 grp_setgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
302 {
303 
304 	return (setgrent() == 0 ? EFAULT : 0);
305 }
306 
307 static int
grp_endgrent(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)308 grp_endgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
309 {
310 
311 	endgrent();
312 
313 	return (0);
314 }
315 
316 static int
grp_limit(const nvlist_t * oldlimits,const nvlist_t * newlimits)317 grp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
318 {
319 	const nvlist_t *limits;
320 	const char *name;
321 	void *cookie;
322 	int error, type;
323 
324 	if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
325 	    !nvlist_exists_nvlist(newlimits, "cmds")) {
326 		return (ENOTCAPABLE);
327 	}
328 	if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
329 	    !nvlist_exists_nvlist(newlimits, "fields")) {
330 		return (ENOTCAPABLE);
331 	}
332 	if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") &&
333 	    !nvlist_exists_nvlist(newlimits, "groups")) {
334 		return (ENOTCAPABLE);
335 	}
336 
337 	cookie = NULL;
338 	while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
339 		if (type != NV_TYPE_NVLIST)
340 			return (EINVAL);
341 		limits = nvlist_get_nvlist(newlimits, name);
342 		if (strcmp(name, "cmds") == 0)
343 			error = grp_allowed_cmds(oldlimits, limits);
344 		else if (strcmp(name, "fields") == 0)
345 			error = grp_allowed_fields(oldlimits, limits);
346 		else if (strcmp(name, "groups") == 0)
347 			error = grp_allowed_groups(oldlimits, limits);
348 		else
349 			error = EINVAL;
350 		if (error != 0)
351 			return (error);
352 	}
353 
354 	return (0);
355 }
356 
357 static int
grp_command(const char * cmd,const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)358 grp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
359     nvlist_t *nvlout)
360 {
361 	int error;
362 
363 	if (!grp_allowed_cmd(limits, cmd))
364 		return (ENOTCAPABLE);
365 
366 	if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0)
367 		error = grp_getgrent(limits, nvlin, nvlout);
368 	else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0)
369 		error = grp_getgrnam(limits, nvlin, nvlout);
370 	else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0)
371 		error = grp_getgrgid(limits, nvlin, nvlout);
372 	else if (strcmp(cmd, "setgroupent") == 0)
373 		error = grp_setgroupent(limits, nvlin, nvlout);
374 	else if (strcmp(cmd, "setgrent") == 0)
375 		error = grp_setgrent(limits, nvlin, nvlout);
376 	else if (strcmp(cmd, "endgrent") == 0)
377 		error = grp_endgrent(limits, nvlin, nvlout);
378 	else
379 		error = EINVAL;
380 
381 	return (error);
382 }
383 
384 int
main(int argc,char * argv[])385 main(int argc, char *argv[])
386 {
387 
388 	return (service_start("system.grp", PARENT_FILENO, grp_limit,
389 	    grp_command, argc, argv));
390 }
391