xref: /NextBSD/lib/libcapsicum/libcapsicum_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/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 #include <unistd.h>
43 
44 #include "libcapsicum.h"
45 #include "libcapsicum_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 **)_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 **)_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 	memset(grp, 0, 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, 0);
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, 0);
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, 0);
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, 0));
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,gid_t * gids,size_t ngids)409 cap_grp_limit_groups(cap_channel_t *chan, const char * const *names,
410     size_t nnames, 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