1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2013 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD: stable/12/lib/libcasper/services/cap_pwd/cap_pwd.c 335678 2018-06-26 19:57:47Z oshogbo $");
34
35 #include <sys/types.h>
36 #include <sys/nv.h>
37
38 #include <assert.h>
39 #include <errno.h>
40 #include <pwd.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include <libcasper.h>
46 #include <libcasper_service.h>
47
48 #include "cap_pwd.h"
49
50 static struct passwd gpwd;
51 static char *gbuffer;
52 static size_t gbufsize;
53
54 static int
passwd_resize(void)55 passwd_resize(void)
56 {
57 char *buf;
58
59 if (gbufsize == 0)
60 gbufsize = 1024;
61 else
62 gbufsize *= 2;
63
64 buf = gbuffer;
65 gbuffer = realloc(buf, gbufsize);
66 if (gbuffer == NULL) {
67 free(buf);
68 gbufsize = 0;
69 return (ENOMEM);
70 }
71 memset(gbuffer, 0, gbufsize);
72
73 return (0);
74 }
75
76 static int
passwd_unpack_string(const nvlist_t * nvl,const char * fieldname,char ** fieldp,char ** bufferp,size_t * bufsizep)77 passwd_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp,
78 char **bufferp, size_t *bufsizep)
79 {
80 const char *str;
81 size_t len;
82
83 str = nvlist_get_string(nvl, fieldname);
84 len = strlcpy(*bufferp, str, *bufsizep);
85 if (len >= *bufsizep)
86 return (ERANGE);
87 *fieldp = *bufferp;
88 *bufferp += len + 1;
89 *bufsizep -= len + 1;
90
91 return (0);
92 }
93
94 static int
passwd_unpack(const nvlist_t * nvl,struct passwd * pwd,char * buffer,size_t bufsize)95 passwd_unpack(const nvlist_t *nvl, struct passwd *pwd, char *buffer,
96 size_t bufsize)
97 {
98 int error;
99
100 if (!nvlist_exists_string(nvl, "pw_name"))
101 return (EINVAL);
102
103 explicit_bzero(pwd, sizeof(*pwd));
104
105 error = passwd_unpack_string(nvl, "pw_name", &pwd->pw_name, &buffer,
106 &bufsize);
107 if (error != 0)
108 return (error);
109 pwd->pw_uid = (uid_t)nvlist_get_number(nvl, "pw_uid");
110 pwd->pw_gid = (gid_t)nvlist_get_number(nvl, "pw_gid");
111 pwd->pw_change = (time_t)nvlist_get_number(nvl, "pw_change");
112 error = passwd_unpack_string(nvl, "pw_passwd", &pwd->pw_passwd, &buffer,
113 &bufsize);
114 if (error != 0)
115 return (error);
116 error = passwd_unpack_string(nvl, "pw_class", &pwd->pw_class, &buffer,
117 &bufsize);
118 if (error != 0)
119 return (error);
120 error = passwd_unpack_string(nvl, "pw_gecos", &pwd->pw_gecos, &buffer,
121 &bufsize);
122 if (error != 0)
123 return (error);
124 error = passwd_unpack_string(nvl, "pw_dir", &pwd->pw_dir, &buffer,
125 &bufsize);
126 if (error != 0)
127 return (error);
128 error = passwd_unpack_string(nvl, "pw_shell", &pwd->pw_shell, &buffer,
129 &bufsize);
130 if (error != 0)
131 return (error);
132 pwd->pw_expire = (time_t)nvlist_get_number(nvl, "pw_expire");
133 pwd->pw_fields = (int)nvlist_get_number(nvl, "pw_fields");
134
135 return (0);
136 }
137
138 static int
cap_getpwcommon_r(cap_channel_t * chan,const char * cmd,const char * login,uid_t uid,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)139 cap_getpwcommon_r(cap_channel_t *chan, const char *cmd, const char *login,
140 uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
141 struct passwd **result)
142 {
143 nvlist_t *nvl;
144 bool getpw_r;
145 int error;
146
147 nvl = nvlist_create(0);
148 nvlist_add_string(nvl, "cmd", cmd);
149 if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0) {
150 /* Add nothing. */
151 } else if (strcmp(cmd, "getpwnam") == 0 ||
152 strcmp(cmd, "getpwnam_r") == 0) {
153 nvlist_add_string(nvl, "name", login);
154 } else if (strcmp(cmd, "getpwuid") == 0 ||
155 strcmp(cmd, "getpwuid_r") == 0) {
156 nvlist_add_number(nvl, "uid", (uint64_t)uid);
157 } else {
158 abort();
159 }
160 nvl = cap_xfer_nvlist(chan, nvl);
161 if (nvl == NULL) {
162 assert(errno != 0);
163 *result = NULL;
164 return (errno);
165 }
166 error = (int)nvlist_get_number(nvl, "error");
167 if (error != 0) {
168 nvlist_destroy(nvl);
169 *result = NULL;
170 return (error);
171 }
172
173 if (!nvlist_exists_string(nvl, "pw_name")) {
174 /* Not found. */
175 nvlist_destroy(nvl);
176 *result = NULL;
177 return (0);
178 }
179
180 getpw_r = (strcmp(cmd, "getpwent_r") == 0 ||
181 strcmp(cmd, "getpwnam_r") == 0 || strcmp(cmd, "getpwuid_r") == 0);
182
183 for (;;) {
184 error = passwd_unpack(nvl, pwd, buffer, bufsize);
185 if (getpw_r || error != ERANGE)
186 break;
187 assert(buffer == gbuffer);
188 assert(bufsize == gbufsize);
189 error = passwd_resize();
190 if (error != 0)
191 break;
192 /* Update pointers after resize. */
193 buffer = gbuffer;
194 bufsize = gbufsize;
195 }
196
197 nvlist_destroy(nvl);
198
199 if (error == 0)
200 *result = pwd;
201 else
202 *result = NULL;
203
204 return (error);
205 }
206
207 static struct passwd *
cap_getpwcommon(cap_channel_t * chan,const char * cmd,const char * login,uid_t uid)208 cap_getpwcommon(cap_channel_t *chan, const char *cmd, const char *login,
209 uid_t uid)
210 {
211 struct passwd *result;
212 int error, serrno;
213
214 serrno = errno;
215
216 error = cap_getpwcommon_r(chan, cmd, login, uid, &gpwd, gbuffer,
217 gbufsize, &result);
218 if (error != 0) {
219 errno = error;
220 return (NULL);
221 }
222
223 errno = serrno;
224
225 return (result);
226 }
227
228 struct passwd *
cap_getpwent(cap_channel_t * chan)229 cap_getpwent(cap_channel_t *chan)
230 {
231
232 return (cap_getpwcommon(chan, "getpwent", NULL, 0));
233 }
234
235 struct passwd *
cap_getpwnam(cap_channel_t * chan,const char * login)236 cap_getpwnam(cap_channel_t *chan, const char *login)
237 {
238
239 return (cap_getpwcommon(chan, "getpwnam", login, 0));
240 }
241
242 struct passwd *
cap_getpwuid(cap_channel_t * chan,uid_t uid)243 cap_getpwuid(cap_channel_t *chan, uid_t uid)
244 {
245
246 return (cap_getpwcommon(chan, "getpwuid", NULL, uid));
247 }
248
249 int
cap_getpwent_r(cap_channel_t * chan,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)250 cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer,
251 size_t bufsize, struct passwd **result)
252 {
253
254 return (cap_getpwcommon_r(chan, "getpwent_r", NULL, 0, pwd, buffer,
255 bufsize, result));
256 }
257
258 int
cap_getpwnam_r(cap_channel_t * chan,const char * name,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)259 cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd,
260 char *buffer, size_t bufsize, struct passwd **result)
261 {
262
263 return (cap_getpwcommon_r(chan, "getpwnam_r", name, 0, pwd, buffer,
264 bufsize, result));
265 }
266
267 int
cap_getpwuid_r(cap_channel_t * chan,uid_t uid,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)268 cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd, char *buffer,
269 size_t bufsize, struct passwd **result)
270 {
271
272 return (cap_getpwcommon_r(chan, "getpwuid_r", NULL, uid, pwd, buffer,
273 bufsize, result));
274 }
275
276 int
cap_setpassent(cap_channel_t * chan,int stayopen)277 cap_setpassent(cap_channel_t *chan, int stayopen)
278 {
279 nvlist_t *nvl;
280
281 nvl = nvlist_create(0);
282 nvlist_add_string(nvl, "cmd", "setpassent");
283 nvlist_add_bool(nvl, "stayopen", stayopen != 0);
284 nvl = cap_xfer_nvlist(chan, nvl);
285 if (nvl == NULL)
286 return (0);
287 if (nvlist_get_number(nvl, "error") != 0) {
288 errno = nvlist_get_number(nvl, "error");
289 nvlist_destroy(nvl);
290 return (0);
291 }
292 nvlist_destroy(nvl);
293
294 return (1);
295 }
296
297 static void
cap_set_end_pwent(cap_channel_t * chan,const char * cmd)298 cap_set_end_pwent(cap_channel_t *chan, const char *cmd)
299 {
300 nvlist_t *nvl;
301
302 nvl = nvlist_create(0);
303 nvlist_add_string(nvl, "cmd", cmd);
304 /* Ignore any errors, we have no way to report them. */
305 nvlist_destroy(cap_xfer_nvlist(chan, nvl));
306 }
307
308 void
cap_setpwent(cap_channel_t * chan)309 cap_setpwent(cap_channel_t *chan)
310 {
311
312 cap_set_end_pwent(chan, "setpwent");
313 }
314
315 void
cap_endpwent(cap_channel_t * chan)316 cap_endpwent(cap_channel_t *chan)
317 {
318
319 cap_set_end_pwent(chan, "endpwent");
320 }
321
322 int
cap_pwd_limit_cmds(cap_channel_t * chan,const char * const * cmds,size_t ncmds)323 cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds)
324 {
325 nvlist_t *limits, *nvl;
326 unsigned int i;
327
328 if (cap_limit_get(chan, &limits) < 0)
329 return (-1);
330 if (limits == NULL) {
331 limits = nvlist_create(0);
332 } else {
333 if (nvlist_exists_nvlist(limits, "cmds"))
334 nvlist_free_nvlist(limits, "cmds");
335 }
336 nvl = nvlist_create(0);
337 for (i = 0; i < ncmds; i++)
338 nvlist_add_null(nvl, cmds[i]);
339 nvlist_move_nvlist(limits, "cmds", nvl);
340 return (cap_limit_set(chan, limits));
341 }
342
343 int
cap_pwd_limit_fields(cap_channel_t * chan,const char * const * fields,size_t nfields)344 cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields,
345 size_t nfields)
346 {
347 nvlist_t *limits, *nvl;
348 unsigned int i;
349
350 if (cap_limit_get(chan, &limits) < 0)
351 return (-1);
352 if (limits == NULL) {
353 limits = nvlist_create(0);
354 } else {
355 if (nvlist_exists_nvlist(limits, "fields"))
356 nvlist_free_nvlist(limits, "fields");
357 }
358 nvl = nvlist_create(0);
359 for (i = 0; i < nfields; i++)
360 nvlist_add_null(nvl, fields[i]);
361 nvlist_move_nvlist(limits, "fields", nvl);
362 return (cap_limit_set(chan, limits));
363 }
364
365 int
cap_pwd_limit_users(cap_channel_t * chan,const char * const * names,size_t nnames,uid_t * uids,size_t nuids)366 cap_pwd_limit_users(cap_channel_t *chan, const char * const *names,
367 size_t nnames, uid_t *uids, size_t nuids)
368 {
369 nvlist_t *limits, *users;
370 char nvlname[64];
371 unsigned int i;
372 int n;
373
374 if (cap_limit_get(chan, &limits) < 0)
375 return (-1);
376 if (limits == NULL) {
377 limits = nvlist_create(0);
378 } else {
379 if (nvlist_exists_nvlist(limits, "users"))
380 nvlist_free_nvlist(limits, "users");
381 }
382 users = nvlist_create(0);
383 for (i = 0; i < nuids; i++) {
384 n = snprintf(nvlname, sizeof(nvlname), "uid%u", i);
385 assert(n > 0 && n < (int)sizeof(nvlname));
386 nvlist_add_number(users, nvlname, (uint64_t)uids[i]);
387 }
388 for (i = 0; i < nnames; i++) {
389 n = snprintf(nvlname, sizeof(nvlname), "name%u", i);
390 assert(n > 0 && n < (int)sizeof(nvlname));
391 nvlist_add_string(users, nvlname, names[i]);
392 }
393 nvlist_move_nvlist(limits, "users", users);
394 return (cap_limit_set(chan, limits));
395 }
396
397
398 /*
399 * Service functions.
400 */
401 static bool
pwd_allowed_cmd(const nvlist_t * limits,const char * cmd)402 pwd_allowed_cmd(const nvlist_t *limits, const char *cmd)
403 {
404
405 if (limits == NULL)
406 return (true);
407
408 /*
409 * If no limit was set on allowed commands, then all commands
410 * are allowed.
411 */
412 if (!nvlist_exists_nvlist(limits, "cmds"))
413 return (true);
414
415 limits = nvlist_get_nvlist(limits, "cmds");
416 return (nvlist_exists_null(limits, cmd));
417 }
418
419 static int
pwd_allowed_cmds(const nvlist_t * oldlimits,const nvlist_t * newlimits)420 pwd_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
421 {
422 const char *name;
423 void *cookie;
424 int type;
425
426 cookie = NULL;
427 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
428 if (type != NV_TYPE_NULL)
429 return (EINVAL);
430 if (!pwd_allowed_cmd(oldlimits, name))
431 return (ENOTCAPABLE);
432 }
433
434 return (0);
435 }
436
437 static bool
pwd_allowed_user(const nvlist_t * limits,const char * uname,uid_t uid)438 pwd_allowed_user(const nvlist_t *limits, const char *uname, uid_t uid)
439 {
440 const char *name;
441 void *cookie;
442 int type;
443
444 if (limits == NULL)
445 return (true);
446
447 /*
448 * If no limit was set on allowed users, then all users are allowed.
449 */
450 if (!nvlist_exists_nvlist(limits, "users"))
451 return (true);
452
453 limits = nvlist_get_nvlist(limits, "users");
454 cookie = NULL;
455 while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
456 switch (type) {
457 case NV_TYPE_NUMBER:
458 if (uid != (uid_t)-1 &&
459 nvlist_get_number(limits, name) == (uint64_t)uid) {
460 return (true);
461 }
462 break;
463 case NV_TYPE_STRING:
464 if (uname != NULL &&
465 strcmp(nvlist_get_string(limits, name),
466 uname) == 0) {
467 return (true);
468 }
469 break;
470 default:
471 abort();
472 }
473 }
474
475 return (false);
476 }
477
478 static int
pwd_allowed_users(const nvlist_t * oldlimits,const nvlist_t * newlimits)479 pwd_allowed_users(const nvlist_t *oldlimits, const nvlist_t *newlimits)
480 {
481 const char *name, *uname;
482 void *cookie;
483 uid_t uid;
484 int type;
485
486 cookie = NULL;
487 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
488 switch (type) {
489 case NV_TYPE_NUMBER:
490 uid = (uid_t)nvlist_get_number(newlimits, name);
491 uname = NULL;
492 break;
493 case NV_TYPE_STRING:
494 uid = (uid_t)-1;
495 uname = nvlist_get_string(newlimits, name);
496 break;
497 default:
498 return (EINVAL);
499 }
500 if (!pwd_allowed_user(oldlimits, uname, uid))
501 return (ENOTCAPABLE);
502 }
503
504 return (0);
505 }
506
507 static bool
pwd_allowed_field(const nvlist_t * limits,const char * field)508 pwd_allowed_field(const nvlist_t *limits, const char *field)
509 {
510
511 if (limits == NULL)
512 return (true);
513
514 /*
515 * If no limit was set on allowed fields, then all fields are allowed.
516 */
517 if (!nvlist_exists_nvlist(limits, "fields"))
518 return (true);
519
520 limits = nvlist_get_nvlist(limits, "fields");
521 return (nvlist_exists_null(limits, field));
522 }
523
524 static int
pwd_allowed_fields(const nvlist_t * oldlimits,const nvlist_t * newlimits)525 pwd_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
526 {
527 const char *name;
528 void *cookie;
529 int type;
530
531 cookie = NULL;
532 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
533 if (type != NV_TYPE_NULL)
534 return (EINVAL);
535 if (!pwd_allowed_field(oldlimits, name))
536 return (ENOTCAPABLE);
537 }
538
539 return (0);
540 }
541
542 static bool
pwd_pack(const nvlist_t * limits,const struct passwd * pwd,nvlist_t * nvl)543 pwd_pack(const nvlist_t *limits, const struct passwd *pwd, nvlist_t *nvl)
544 {
545 int fields;
546
547 if (pwd == NULL)
548 return (true);
549
550 /*
551 * If either name or UID is allowed, we allow it.
552 */
553 if (!pwd_allowed_user(limits, pwd->pw_name, pwd->pw_uid))
554 return (false);
555
556 fields = pwd->pw_fields;
557
558 if (pwd_allowed_field(limits, "pw_name")) {
559 nvlist_add_string(nvl, "pw_name", pwd->pw_name);
560 } else {
561 nvlist_add_string(nvl, "pw_name", "");
562 fields &= ~_PWF_NAME;
563 }
564 if (pwd_allowed_field(limits, "pw_uid")) {
565 nvlist_add_number(nvl, "pw_uid", (uint64_t)pwd->pw_uid);
566 } else {
567 nvlist_add_number(nvl, "pw_uid", (uint64_t)-1);
568 fields &= ~_PWF_UID;
569 }
570 if (pwd_allowed_field(limits, "pw_gid")) {
571 nvlist_add_number(nvl, "pw_gid", (uint64_t)pwd->pw_gid);
572 } else {
573 nvlist_add_number(nvl, "pw_gid", (uint64_t)-1);
574 fields &= ~_PWF_GID;
575 }
576 if (pwd_allowed_field(limits, "pw_change")) {
577 nvlist_add_number(nvl, "pw_change", (uint64_t)pwd->pw_change);
578 } else {
579 nvlist_add_number(nvl, "pw_change", (uint64_t)0);
580 fields &= ~_PWF_CHANGE;
581 }
582 if (pwd_allowed_field(limits, "pw_passwd")) {
583 nvlist_add_string(nvl, "pw_passwd", pwd->pw_passwd);
584 } else {
585 nvlist_add_string(nvl, "pw_passwd", "");
586 fields &= ~_PWF_PASSWD;
587 }
588 if (pwd_allowed_field(limits, "pw_class")) {
589 nvlist_add_string(nvl, "pw_class", pwd->pw_class);
590 } else {
591 nvlist_add_string(nvl, "pw_class", "");
592 fields &= ~_PWF_CLASS;
593 }
594 if (pwd_allowed_field(limits, "pw_gecos")) {
595 nvlist_add_string(nvl, "pw_gecos", pwd->pw_gecos);
596 } else {
597 nvlist_add_string(nvl, "pw_gecos", "");
598 fields &= ~_PWF_GECOS;
599 }
600 if (pwd_allowed_field(limits, "pw_dir")) {
601 nvlist_add_string(nvl, "pw_dir", pwd->pw_dir);
602 } else {
603 nvlist_add_string(nvl, "pw_dir", "");
604 fields &= ~_PWF_DIR;
605 }
606 if (pwd_allowed_field(limits, "pw_shell")) {
607 nvlist_add_string(nvl, "pw_shell", pwd->pw_shell);
608 } else {
609 nvlist_add_string(nvl, "pw_shell", "");
610 fields &= ~_PWF_SHELL;
611 }
612 if (pwd_allowed_field(limits, "pw_expire")) {
613 nvlist_add_number(nvl, "pw_expire", (uint64_t)pwd->pw_expire);
614 } else {
615 nvlist_add_number(nvl, "pw_expire", (uint64_t)0);
616 fields &= ~_PWF_EXPIRE;
617 }
618 nvlist_add_number(nvl, "pw_fields", (uint64_t)fields);
619
620 return (true);
621 }
622
623 static int
pwd_getpwent(const nvlist_t * limits,const nvlist_t * nvlin __unused,nvlist_t * nvlout)624 pwd_getpwent(const nvlist_t *limits, const nvlist_t *nvlin __unused,
625 nvlist_t *nvlout)
626 {
627 struct passwd *pwd;
628
629 for (;;) {
630 errno = 0;
631 pwd = getpwent();
632 if (errno != 0)
633 return (errno);
634 if (pwd_pack(limits, pwd, nvlout))
635 return (0);
636 }
637
638 /* NOTREACHED */
639 }
640
641 static int
pwd_getpwnam(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)642 pwd_getpwnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
643 {
644 struct passwd *pwd;
645 const char *name;
646
647 if (!nvlist_exists_string(nvlin, "name"))
648 return (EINVAL);
649 name = nvlist_get_string(nvlin, "name");
650 assert(name != NULL);
651
652 errno = 0;
653 pwd = getpwnam(name);
654 if (errno != 0)
655 return (errno);
656
657 (void)pwd_pack(limits, pwd, nvlout);
658
659 return (0);
660 }
661
662 static int
pwd_getpwuid(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)663 pwd_getpwuid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
664 {
665 struct passwd *pwd;
666 uid_t uid;
667
668 if (!nvlist_exists_number(nvlin, "uid"))
669 return (EINVAL);
670
671 uid = (uid_t)nvlist_get_number(nvlin, "uid");
672
673 errno = 0;
674 pwd = getpwuid(uid);
675 if (errno != 0)
676 return (errno);
677
678 (void)pwd_pack(limits, pwd, nvlout);
679
680 return (0);
681 }
682
683 static int
pwd_setpassent(const nvlist_t * limits __unused,const nvlist_t * nvlin,nvlist_t * nvlout __unused)684 pwd_setpassent(const nvlist_t *limits __unused, const nvlist_t *nvlin,
685 nvlist_t *nvlout __unused)
686 {
687 int stayopen;
688
689 if (!nvlist_exists_bool(nvlin, "stayopen"))
690 return (EINVAL);
691
692 stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
693
694 return (setpassent(stayopen) == 0 ? EFAULT : 0);
695 }
696
697 static int
pwd_setpwent(const nvlist_t * limits __unused,const nvlist_t * nvlin __unused,nvlist_t * nvlout __unused)698 pwd_setpwent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused,
699 nvlist_t *nvlout __unused)
700 {
701
702 setpwent();
703
704 return (0);
705 }
706
707 static int
pwd_endpwent(const nvlist_t * limits __unused,const nvlist_t * nvlin __unused,nvlist_t * nvlout __unused)708 pwd_endpwent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused,
709 nvlist_t *nvlout __unused)
710 {
711
712 endpwent();
713
714 return (0);
715 }
716
717 static int
pwd_limit(const nvlist_t * oldlimits,const nvlist_t * newlimits)718 pwd_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
719 {
720 const nvlist_t *limits;
721 const char *name;
722 void *cookie;
723 int error, type;
724
725 if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
726 !nvlist_exists_nvlist(newlimits, "cmds")) {
727 return (ENOTCAPABLE);
728 }
729 if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
730 !nvlist_exists_nvlist(newlimits, "fields")) {
731 return (ENOTCAPABLE);
732 }
733 if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "users") &&
734 !nvlist_exists_nvlist(newlimits, "users")) {
735 return (ENOTCAPABLE);
736 }
737
738 cookie = NULL;
739 while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
740 if (type != NV_TYPE_NVLIST)
741 return (EINVAL);
742 limits = nvlist_get_nvlist(newlimits, name);
743 if (strcmp(name, "cmds") == 0)
744 error = pwd_allowed_cmds(oldlimits, limits);
745 else if (strcmp(name, "fields") == 0)
746 error = pwd_allowed_fields(oldlimits, limits);
747 else if (strcmp(name, "users") == 0)
748 error = pwd_allowed_users(oldlimits, limits);
749 else
750 error = EINVAL;
751 if (error != 0)
752 return (error);
753 }
754
755 return (0);
756 }
757
758 static int
pwd_command(const char * cmd,const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)759 pwd_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
760 nvlist_t *nvlout)
761 {
762 int error;
763
764 if (!pwd_allowed_cmd(limits, cmd))
765 return (ENOTCAPABLE);
766
767 if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0)
768 error = pwd_getpwent(limits, nvlin, nvlout);
769 else if (strcmp(cmd, "getpwnam") == 0 || strcmp(cmd, "getpwnam_r") == 0)
770 error = pwd_getpwnam(limits, nvlin, nvlout);
771 else if (strcmp(cmd, "getpwuid") == 0 || strcmp(cmd, "getpwuid_r") == 0)
772 error = pwd_getpwuid(limits, nvlin, nvlout);
773 else if (strcmp(cmd, "setpassent") == 0)
774 error = pwd_setpassent(limits, nvlin, nvlout);
775 else if (strcmp(cmd, "setpwent") == 0)
776 error = pwd_setpwent(limits, nvlin, nvlout);
777 else if (strcmp(cmd, "endpwent") == 0)
778 error = pwd_endpwent(limits, nvlin, nvlout);
779 else
780 error = EINVAL;
781
782 return (error);
783 }
784
785 CREATE_SERVICE("system.pwd", pwd_limit, pwd_command, 0);
786