]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/services/cap_pwd/cap_pwd.c
Convert casperd(8) daemon to the libcasper.
[FreeBSD/FreeBSD.git] / lib / libcasper / services / cap_pwd / cap_pwd.c
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$");
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
48 static struct passwd gpwd;
49 static char *gbuffer;
50 static size_t gbufsize;
51
52 static int
53 passwd_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
74 static int
75 passwd_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
92 static int
93 passwd_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
136 static int
137 cap_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
205 static struct passwd *
206 cap_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
226 struct passwd *
227 cap_getpwent(cap_channel_t *chan)
228 {
229
230         return (cap_getpwcommon(chan, "getpwent", NULL, 0));
231 }
232
233 struct passwd *
234 cap_getpwnam(cap_channel_t *chan, const char *login)
235 {
236
237         return (cap_getpwcommon(chan, "getpwnam", login, 0));
238 }
239
240 struct passwd *
241 cap_getpwuid(cap_channel_t *chan, uid_t uid)
242 {
243
244         return (cap_getpwcommon(chan, "getpwuid", NULL, uid));
245 }
246
247 int
248 cap_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
256 int
257 cap_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
265 int
266 cap_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
274 int
275 cap_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
295 static void
296 cap_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
306 void
307 cap_setpwent(cap_channel_t *chan)
308 {
309
310         cap_set_end_pwent(chan, "setpwent");
311 }
312
313 void
314 cap_endpwent(cap_channel_t *chan)
315 {
316
317         cap_set_end_pwent(chan, "endpwent");
318 }
319
320 int
321 cap_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
341 int
342 cap_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
363 int
364 cap_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  */
399 static bool
400 pwd_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
417 static int
418 pwd_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
435 static bool
436 pwd_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
476 static int
477 pwd_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
505 static bool
506 pwd_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
522 static int
523 pwd_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
540 static bool
541 pwd_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
621 static int
622 pwd_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
639 static int
640 pwd_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
660 static int
661 pwd_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
681 static int
682 pwd_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
695 static int
696 pwd_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
705 static int
706 pwd_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
715 static int
716 pwd_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
756 static int
757 pwd_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
783 CREATE_SERVICE("system.pwd", pwd_limit, pwd_command);