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