xref: /freebsd/lib/libcasper/services/cap_pwd/cap_pwd.c (revision 2668e76d6e764c5c361156ffa3d39eb02ce8e5d9)
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
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
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
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
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 *
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 *
225 cap_getpwent(cap_channel_t *chan)
226 {
227 
228 	return (cap_getpwcommon(chan, "getpwent", NULL, 0));
229 }
230 
231 struct passwd *
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 *
239 cap_getpwuid(cap_channel_t *chan, uid_t uid)
240 {
241 
242 	return (cap_getpwcommon(chan, "getpwuid", NULL, uid));
243 }
244 
245 int
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
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
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
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
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
305 cap_setpwent(cap_channel_t *chan)
306 {
307 
308 	cap_set_end_pwent(chan, "setpwent");
309 }
310 
311 void
312 cap_endpwent(cap_channel_t *chan)
313 {
314 
315 	cap_set_end_pwent(chan, "endpwent");
316 }
317 
318 int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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