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