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