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