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