root/lib/libcasper/services/cap_pwd/tests/pwd_test.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2013 The FreeBSD Foundation
 *
 * This software was developed by Pawel Jakub Dawidek under sponsorship from
 * the FreeBSD Foundation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/capsicum.h>
#include <sys/nv.h>

#include <assert.h>
#include <err.h>
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <libcasper.h>

#include <casper/cap_pwd.h>

static int ntest = 1;

#define CHECK(expr)     do {                                            \
        if ((expr))                                                     \
                printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__);   \
        else                                                            \
                printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \
        fflush(stdout);                                                 \
        ntest++;                                                        \
} while (0)
#define CHECKX(expr)     do {                                           \
        if ((expr)) {                                                   \
                printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__);   \
        } else {                                                        \
                printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \
                exit(1);                                                \
        }                                                               \
        fflush(stdout);                                                 \
        ntest++;                                                        \
} while (0)

#define UID_ROOT        0
#define UID_OPERATOR    2

#define GETPWENT0       0x0001
#define GETPWENT1       0x0002
#define GETPWENT2       0x0004
#define GETPWENT        (GETPWENT0 | GETPWENT1 | GETPWENT2)
#define GETPWENT_R0     0x0008
#define GETPWENT_R1     0x0010
#define GETPWENT_R2     0x0020
#define GETPWENT_R      (GETPWENT_R0 | GETPWENT_R1 | GETPWENT_R2)
#define GETPWNAM        0x0040
#define GETPWNAM_R      0x0080
#define GETPWUID        0x0100
#define GETPWUID_R      0x0200

static bool
passwd_compare(const struct passwd *pwd0, const struct passwd *pwd1)
{

        if (pwd0 == NULL && pwd1 == NULL)
                return (true);
        if (pwd0 == NULL || pwd1 == NULL)
                return (false);

        if (strcmp(pwd0->pw_name, pwd1->pw_name) != 0)
                return (false);

        if (pwd0->pw_passwd != NULL || pwd1->pw_passwd != NULL) {
                if (pwd0->pw_passwd == NULL || pwd1->pw_passwd == NULL)
                        return (false);
                if (strcmp(pwd0->pw_passwd, pwd1->pw_passwd) != 0)
                        return (false);
        }

        if (pwd0->pw_uid != pwd1->pw_uid)
                return (false);

        if (pwd0->pw_gid != pwd1->pw_gid)
                return (false);

        if (pwd0->pw_change != pwd1->pw_change)
                return (false);

        if (pwd0->pw_class != NULL || pwd1->pw_class != NULL) {
                if (pwd0->pw_class == NULL || pwd1->pw_class == NULL)
                        return (false);
                if (strcmp(pwd0->pw_class, pwd1->pw_class) != 0)
                        return (false);
        }

        if (pwd0->pw_gecos != NULL || pwd1->pw_gecos != NULL) {
                if (pwd0->pw_gecos == NULL || pwd1->pw_gecos == NULL)
                        return (false);
                if (strcmp(pwd0->pw_gecos, pwd1->pw_gecos) != 0)
                        return (false);
        }

        if (pwd0->pw_dir != NULL || pwd1->pw_dir != NULL) {
                if (pwd0->pw_dir == NULL || pwd1->pw_dir == NULL)
                        return (false);
                if (strcmp(pwd0->pw_dir, pwd1->pw_dir) != 0)
                        return (false);
        }

        if (pwd0->pw_shell != NULL || pwd1->pw_shell != NULL) {
                if (pwd0->pw_shell == NULL || pwd1->pw_shell == NULL)
                        return (false);
                if (strcmp(pwd0->pw_shell, pwd1->pw_shell) != 0)
                        return (false);
        }

        if (pwd0->pw_expire != pwd1->pw_expire)
                return (false);

        if (pwd0->pw_fields != pwd1->pw_fields)
                return (false);

        return (true);
}

static unsigned int
runtest_cmds(cap_channel_t *cappwd)
{
        char bufs[1024], bufc[1024];
        unsigned int result;
        struct passwd *pwds, *pwdc;
        struct passwd sts, stc;

        result = 0;

        setpwent();
        cap_setpwent(cappwd);

        pwds = getpwent();
        pwdc = cap_getpwent(cappwd);
        if (passwd_compare(pwds, pwdc)) {
                result |= GETPWENT0;
                pwds = getpwent();
                pwdc = cap_getpwent(cappwd);
                if (passwd_compare(pwds, pwdc))
                        result |= GETPWENT1;
        }

        getpwent_r(&sts, bufs, sizeof(bufs), &pwds);
        cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc);
        if (passwd_compare(pwds, pwdc)) {
                result |= GETPWENT_R0;
                getpwent_r(&sts, bufs, sizeof(bufs), &pwds);
                cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc);
                if (passwd_compare(pwds, pwdc))
                        result |= GETPWENT_R1;
        }

        setpwent();
        cap_setpwent(cappwd);

        getpwent_r(&sts, bufs, sizeof(bufs), &pwds);
        cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc);
        if (passwd_compare(pwds, pwdc))
                result |= GETPWENT_R2;

        pwds = getpwent();
        pwdc = cap_getpwent(cappwd);
        if (passwd_compare(pwds, pwdc))
                result |= GETPWENT2;

        pwds = getpwnam("root");
        pwdc = cap_getpwnam(cappwd, "root");
        if (passwd_compare(pwds, pwdc)) {
                pwds = getpwnam("operator");
                pwdc = cap_getpwnam(cappwd, "operator");
                if (passwd_compare(pwds, pwdc))
                        result |= GETPWNAM;
        }

        getpwnam_r("root", &sts, bufs, sizeof(bufs), &pwds);
        cap_getpwnam_r(cappwd, "root", &stc, bufc, sizeof(bufc), &pwdc);
        if (passwd_compare(pwds, pwdc)) {
                getpwnam_r("operator", &sts, bufs, sizeof(bufs), &pwds);
                cap_getpwnam_r(cappwd, "operator", &stc, bufc, sizeof(bufc),
                    &pwdc);
                if (passwd_compare(pwds, pwdc))
                        result |= GETPWNAM_R;
        }

        pwds = getpwuid(UID_ROOT);
        pwdc = cap_getpwuid(cappwd, UID_ROOT);
        if (passwd_compare(pwds, pwdc)) {
                pwds = getpwuid(UID_OPERATOR);
                pwdc = cap_getpwuid(cappwd, UID_OPERATOR);
                if (passwd_compare(pwds, pwdc))
                        result |= GETPWUID;
        }

        getpwuid_r(UID_ROOT, &sts, bufs, sizeof(bufs), &pwds);
        cap_getpwuid_r(cappwd, UID_ROOT, &stc, bufc, sizeof(bufc), &pwdc);
        if (passwd_compare(pwds, pwdc)) {
                getpwuid_r(UID_OPERATOR, &sts, bufs, sizeof(bufs), &pwds);
                cap_getpwuid_r(cappwd, UID_OPERATOR, &stc, bufc, sizeof(bufc),
                    &pwdc);
                if (passwd_compare(pwds, pwdc))
                        result |= GETPWUID_R;
        }

        return (result);
}

static void
test_cmds(cap_channel_t *origcappwd)
{
        cap_channel_t *cappwd;
        const char *cmds[7], *fields[10], *names[6];
        uid_t uids[5];

        fields[0] = "pw_name";
        fields[1] = "pw_passwd";
        fields[2] = "pw_uid";
        fields[3] = "pw_gid";
        fields[4] = "pw_change";
        fields[5] = "pw_class";
        fields[6] = "pw_gecos";
        fields[7] = "pw_dir";
        fields[8] = "pw_shell";
        fields[9] = "pw_expire";

        names[0] = "root";
        names[1] = "toor";
        names[2] = "daemon";
        names[3] = "operator";
        names[4] = "bin";
        names[5] = "kmem";

        uids[0] = 0;
        uids[1] = 1;
        uids[2] = 2;
        uids[3] = 3;
        uids[4] = 5;

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == 0);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == 0);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: setpwent
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cap_setpwent(cappwd);

        cmds[0] = "getpwent";
        cmds[1] = "getpwent_r";
        cmds[2] = "getpwnam";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "setpwent";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWENT_R0 |
            GETPWENT_R1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: setpwent
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cap_setpwent(cappwd);

        cmds[0] = "getpwent";
        cmds[1] = "getpwent_r";
        cmds[2] = "getpwnam";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "setpwent";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWENT_R0 |
            GETPWENT_R1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: getpwent
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent_r";
        cmds[2] = "getpwnam";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwent";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT_R2 |
            GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: getpwent
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent_r";
        cmds[2] = "getpwnam";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwent";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT_R2 |
            GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: getpwent_r
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwnam";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwent_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 |
            GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwnam, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: getpwent_r
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwnam";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwent_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 |
            GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: getpwnam
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwnam";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam_r,
         *       getpwuid, getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: getpwnam
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam_r";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwnam";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM_R | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam,
         *       getpwuid, getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: getpwnam_r
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwnam_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam,
         *       getpwuid, getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: getpwnam_r
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwuid";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwnam_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWUID | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid_r
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: getpwuid
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwuid";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWNAM_R | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid_r
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: getpwuid
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwuid";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWNAM_R | GETPWUID_R));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid
         * users:
         *     names: root, toor, daemon, operator, bin, kmem
         *     uids:
         * Disallow:
         * cmds: getpwuid_r
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWNAM_R | GETPWUID));

        cap_close(cappwd);

        /*
         * Allow:
         * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r,
         *       getpwuid
         * users:
         *     names:
         *     uids: 0, 1, 2, 3, 5
         * Disallow:
         * cmds: getpwuid_r
         * users:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0);
        cmds[0] = "setpwent";
        cmds[1] = "getpwent";
        cmds[2] = "getpwent_r";
        cmds[3] = "getpwnam";
        cmds[4] = "getpwnam_r";
        cmds[5] = "getpwuid";
        cmds[6] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE);
        cmds[0] = "getpwuid_r";
        CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE);
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0);

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R |
            GETPWNAM | GETPWNAM_R | GETPWUID));

        cap_close(cappwd);
}

#define PW_NAME         _PWF_NAME
#define PW_PASSWD       _PWF_PASSWD
#define PW_UID          _PWF_UID
#define PW_GID          _PWF_GID
#define PW_CHANGE       _PWF_CHANGE
#define PW_CLASS        _PWF_CLASS
#define PW_GECOS        _PWF_GECOS
#define PW_DIR          _PWF_DIR
#define PW_SHELL        _PWF_SHELL
#define PW_EXPIRE       _PWF_EXPIRE

static unsigned int
passwd_fields(const struct passwd *pwd)
{
        unsigned int result;

        result = 0;

        if (pwd->pw_name != NULL && pwd->pw_name[0] != '\0')
                result |= PW_NAME;
//      else
//              printf("No pw_name\n");

        if (pwd->pw_passwd != NULL && pwd->pw_passwd[0] != '\0')
                result |= PW_PASSWD;
        else if ((pwd->pw_fields & _PWF_PASSWD) != 0)
                result |= PW_PASSWD;
//      else
//              printf("No pw_passwd\n");

        if (pwd->pw_uid != (uid_t)-1)
                result |= PW_UID;
//      else
//              printf("No pw_uid\n");

        if (pwd->pw_gid != (gid_t)-1)
                result |= PW_GID;
//      else
//              printf("No pw_gid\n");

        if (pwd->pw_change != 0 || (pwd->pw_fields & _PWF_CHANGE) != 0)
                result |= PW_CHANGE;
//      else
//              printf("No pw_change\n");

        if (pwd->pw_class != NULL && pwd->pw_class[0] != '\0')
                result |= PW_CLASS;
        else if ((pwd->pw_fields & _PWF_CLASS) != 0)
                result |= PW_CLASS;
//      else
//              printf("No pw_class\n");

        if (pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0')
                result |= PW_GECOS;
        else if ((pwd->pw_fields & _PWF_GECOS) != 0)
                result |= PW_GECOS;
//      else
//              printf("No pw_gecos\n");

        if (pwd->pw_dir != NULL && pwd->pw_dir[0] != '\0')
                result |= PW_DIR;
        else if ((pwd->pw_fields & _PWF_DIR) != 0)
                result |= PW_DIR;
//      else
//              printf("No pw_dir\n");

        if (pwd->pw_shell != NULL && pwd->pw_shell[0] != '\0')
                result |= PW_SHELL;
        else if ((pwd->pw_fields & _PWF_SHELL) != 0)
                result |= PW_SHELL;
//      else
//              printf("No pw_shell\n");

        if (pwd->pw_expire != 0 || (pwd->pw_fields & _PWF_EXPIRE) != 0)
                result |= PW_EXPIRE;
//      else
//              printf("No pw_expire\n");

if (false && pwd->pw_fields != (int)result) {
printf("fields=0x%x != result=0x%x\n", (const unsigned int)pwd->pw_fields, result);
printf("           fields result\n");
printf("PW_NAME    %d      %d\n", (pwd->pw_fields & PW_NAME) != 0, (result & PW_NAME) != 0);
printf("PW_PASSWD  %d      %d\n", (pwd->pw_fields & PW_PASSWD) != 0, (result & PW_PASSWD) != 0);
printf("PW_UID     %d      %d\n", (pwd->pw_fields & PW_UID) != 0, (result & PW_UID) != 0);
printf("PW_GID     %d      %d\n", (pwd->pw_fields & PW_GID) != 0, (result & PW_GID) != 0);
printf("PW_CHANGE  %d      %d\n", (pwd->pw_fields & PW_CHANGE) != 0, (result & PW_CHANGE) != 0);
printf("PW_CLASS   %d      %d\n", (pwd->pw_fields & PW_CLASS) != 0, (result & PW_CLASS) != 0);
printf("PW_GECOS   %d      %d\n", (pwd->pw_fields & PW_GECOS) != 0, (result & PW_GECOS) != 0);
printf("PW_DIR     %d      %d\n", (pwd->pw_fields & PW_DIR) != 0, (result & PW_DIR) != 0);
printf("PW_SHELL   %d      %d\n", (pwd->pw_fields & PW_SHELL) != 0, (result & PW_SHELL) != 0);
printf("PW_EXPIRE  %d      %d\n", (pwd->pw_fields & PW_EXPIRE) != 0, (result & PW_EXPIRE) != 0);
}

//printf("result=0x%x\n", result);
        return (result);
}

static bool
runtest_fields(cap_channel_t *cappwd, unsigned int expected)
{
        char buf[1024];
        struct passwd *pwd;
        struct passwd st;

//printf("expected=0x%x\n", expected);
        cap_setpwent(cappwd);
        pwd = cap_getpwent(cappwd);
        if ((passwd_fields(pwd) & ~expected) != 0)
                return (false);

        cap_setpwent(cappwd);
        cap_getpwent_r(cappwd, &st, buf, sizeof(buf), &pwd);
        if ((passwd_fields(pwd) & ~expected) != 0)
                return (false);

        pwd = cap_getpwnam(cappwd, "root");
        if ((passwd_fields(pwd) & ~expected) != 0)
                return (false);

        cap_getpwnam_r(cappwd, "root", &st, buf, sizeof(buf), &pwd);
        if ((passwd_fields(pwd) & ~expected) != 0)
                return (false);

        pwd = cap_getpwuid(cappwd, UID_ROOT);
        if ((passwd_fields(pwd) & ~expected) != 0)
                return (false);

        cap_getpwuid_r(cappwd, UID_ROOT, &st, buf, sizeof(buf), &pwd);
        if ((passwd_fields(pwd) & ~expected) != 0)
                return (false);

        return (true);
}

static void
test_fields(cap_channel_t *origcappwd)
{
        cap_channel_t *cappwd;
        const char *fields[10];

        /* No limits. */

        CHECK(runtest_fields(origcappwd, PW_NAME | PW_PASSWD | PW_UID |
            PW_GID | PW_CHANGE | PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL |
            PW_EXPIRE));

        /*
         * Allow:
         * fields: pw_name, pw_passwd, pw_uid, pw_gid, pw_change, pw_class,
         *         pw_gecos, pw_dir, pw_shell, pw_expire
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_name";
        fields[1] = "pw_passwd";
        fields[2] = "pw_uid";
        fields[3] = "pw_gid";
        fields[4] = "pw_change";
        fields[5] = "pw_class";
        fields[6] = "pw_gecos";
        fields[7] = "pw_dir";
        fields[8] = "pw_shell";
        fields[9] = "pw_expire";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0);

        CHECK(runtest_fields(origcappwd, PW_NAME | PW_PASSWD | PW_UID |
            PW_GID | PW_CHANGE | PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL |
            PW_EXPIRE));

        cap_close(cappwd);

        /*
         * Allow:
         * fields: pw_name, pw_passwd, pw_uid, pw_gid, pw_change
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_name";
        fields[1] = "pw_passwd";
        fields[2] = "pw_uid";
        fields[3] = "pw_gid";
        fields[4] = "pw_change";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0);
        fields[5] = "pw_class";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 &&
            errno == ENOTCAPABLE);
        fields[0] = "pw_class";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 &&
            errno == ENOTCAPABLE);

        CHECK(runtest_fields(cappwd, PW_NAME | PW_PASSWD | PW_UID |
            PW_GID | PW_CHANGE));

        cap_close(cappwd);

        /*
         * Allow:
         * fields: pw_class, pw_gecos, pw_dir, pw_shell, pw_expire
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_class";
        fields[1] = "pw_gecos";
        fields[2] = "pw_dir";
        fields[3] = "pw_shell";
        fields[4] = "pw_expire";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0);
        fields[5] = "pw_uid";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 &&
            errno == ENOTCAPABLE);
        fields[0] = "pw_uid";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 &&
            errno == ENOTCAPABLE);

        CHECK(runtest_fields(cappwd, PW_CLASS | PW_GECOS | PW_DIR |
            PW_SHELL | PW_EXPIRE));

        cap_close(cappwd);

        /*
         * Allow:
         * fields: pw_name, pw_uid, pw_change, pw_gecos, pw_shell
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_name";
        fields[1] = "pw_uid";
        fields[2] = "pw_change";
        fields[3] = "pw_gecos";
        fields[4] = "pw_shell";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0);
        fields[5] = "pw_class";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 &&
            errno == ENOTCAPABLE);
        fields[0] = "pw_class";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 &&
            errno == ENOTCAPABLE);

        CHECK(runtest_fields(cappwd, PW_NAME | PW_UID | PW_CHANGE |
            PW_GECOS | PW_SHELL));

        cap_close(cappwd);

        /*
         * Allow:
         * fields: pw_passwd, pw_gid, pw_class, pw_dir, pw_expire
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_passwd";
        fields[1] = "pw_gid";
        fields[2] = "pw_class";
        fields[3] = "pw_dir";
        fields[4] = "pw_expire";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0);
        fields[5] = "pw_uid";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 &&
            errno == ENOTCAPABLE);
        fields[0] = "pw_uid";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 &&
            errno == ENOTCAPABLE);

        CHECK(runtest_fields(cappwd, PW_PASSWD | PW_GID | PW_CLASS |
            PW_DIR | PW_EXPIRE));

        cap_close(cappwd);

        /*
         * Allow:
         * fields: pw_uid, pw_class, pw_shell
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_uid";
        fields[1] = "pw_class";
        fields[2] = "pw_shell";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 3) == 0);
        fields[3] = "pw_change";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 4) == -1 &&
            errno == ENOTCAPABLE);
        fields[0] = "pw_change";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 &&
            errno == ENOTCAPABLE);

        CHECK(runtest_fields(cappwd, PW_UID | PW_CLASS | PW_SHELL));

        cap_close(cappwd);

        /*
         * Allow:
         * fields: pw_change
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        fields[0] = "pw_change";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == 0);
        fields[1] = "pw_uid";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 2) == -1 &&
            errno == ENOTCAPABLE);
        fields[0] = "pw_uid";
        CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 &&
            errno == ENOTCAPABLE);

        CHECK(runtest_fields(cappwd, PW_CHANGE));

        cap_close(cappwd);
}

static bool
runtest_users(cap_channel_t *cappwd, const char **names, const uid_t *uids,
    size_t nusers)
{
        char buf[1024];
        struct passwd *pwd;
        struct passwd st;
        unsigned int i, got;

        cap_setpwent(cappwd);
        got = 0;
        for (;;) {
                pwd = cap_getpwent(cappwd);
                if (pwd == NULL)
                        break;
                got++;
                for (i = 0; i < nusers; i++) {
                        if (strcmp(names[i], pwd->pw_name) == 0 &&
                            uids[i] == pwd->pw_uid) {
                                break;
                        }
                }
                if (i == nusers)
                        return (false);
        }
        if (got != nusers)
                return (false);

        cap_setpwent(cappwd);
        got = 0;
        for (;;) {
                cap_getpwent_r(cappwd, &st, buf, sizeof(buf), &pwd);
                if (pwd == NULL)
                        break;
                got++;
                for (i = 0; i < nusers; i++) {
                        if (strcmp(names[i], pwd->pw_name) == 0 &&
                            uids[i] == pwd->pw_uid) {
                                break;
                        }
                }
                if (i == nusers)
                        return (false);
        }
        if (got != nusers)
                return (false);

        for (i = 0; i < nusers; i++) {
                pwd = cap_getpwnam(cappwd, names[i]);
                if (pwd == NULL)
                        return (false);
        }

        for (i = 0; i < nusers; i++) {
                cap_getpwnam_r(cappwd, names[i], &st, buf, sizeof(buf), &pwd);
                if (pwd == NULL)
                        return (false);
        }

        for (i = 0; i < nusers; i++) {
                pwd = cap_getpwuid(cappwd, uids[i]);
                if (pwd == NULL)
                        return (false);
        }

        for (i = 0; i < nusers; i++) {
                cap_getpwuid_r(cappwd, uids[i], &st, buf, sizeof(buf), &pwd);
                if (pwd == NULL)
                        return (false);
        }

        return (true);
}

static void
test_users(cap_channel_t *origcappwd)
{
        cap_channel_t *cappwd;
        const char *names[6];
        uid_t uids[6];

        /*
         * Allow:
         * users:
         *     names: root, toor, daemon, operator, bin, tty
         *     uids:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "root";
        names[1] = "toor";
        names[2] = "daemon";
        names[3] = "operator";
        names[4] = "bin";
        names[5] = "tty";
        CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0);
        uids[0] = 0;
        uids[1] = 0;
        uids[2] = 1;
        uids[3] = 2;
        uids[4] = 3;
        uids[5] = 4;

        CHECK(runtest_users(cappwd, names, uids, 6));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names: daemon, operator, bin
         *     uids:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "daemon";
        names[1] = "operator";
        names[2] = "bin";
        CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == 0);
        names[3] = "tty";
        CHECK(cap_pwd_limit_users(cappwd, names, 4, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "tty";
        CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "daemon";
        uids[0] = 1;
        uids[1] = 2;
        uids[2] = 3;

        CHECK(runtest_users(cappwd, names, uids, 3));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names: daemon, bin, tty
         *     uids:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "daemon";
        names[1] = "bin";
        names[2] = "tty";
        CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == 0);
        names[3] = "operator";
        CHECK(cap_pwd_limit_users(cappwd, names, 4, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "operator";
        CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "daemon";
        uids[0] = 1;
        uids[1] = 3;
        uids[2] = 4;

        CHECK(runtest_users(cappwd, names, uids, 3));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names:
         *     uids: 1, 2, 3
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "daemon";
        names[1] = "operator";
        names[2] = "bin";
        uids[0] = 1;
        uids[1] = 2;
        uids[2] = 3;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == 0);
        uids[3] = 4;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 4) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 4;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 1;

        CHECK(runtest_users(cappwd, names, uids, 3));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names:
         *     uids: 1, 3, 4
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "daemon";
        names[1] = "bin";
        names[2] = "tty";
        uids[0] = 1;
        uids[1] = 3;
        uids[2] = 4;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == 0);
        uids[3] = 5;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 4) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 5;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 1;

        CHECK(runtest_users(cappwd, names, uids, 3));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names: bin
         *     uids:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "bin";
        CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == 0);
        names[1] = "operator";
        CHECK(cap_pwd_limit_users(cappwd, names, 2, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "operator";
        CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "bin";
        uids[0] = 3;

        CHECK(runtest_users(cappwd, names, uids, 1));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names: daemon, tty
         *     uids:
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "daemon";
        names[1] = "tty";
        CHECK(cap_pwd_limit_users(cappwd, names, 2, NULL, 0) == 0);
        names[2] = "operator";
        CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "operator";
        CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 &&
            errno == ENOTCAPABLE);
        names[0] = "daemon";
        uids[0] = 1;
        uids[1] = 4;

        CHECK(runtest_users(cappwd, names, uids, 2));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names:
         *     uids: 3
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "bin";
        uids[0] = 3;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == 0);
        uids[1] = 4;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 2) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 4;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 3;

        CHECK(runtest_users(cappwd, names, uids, 1));

        cap_close(cappwd);

        /*
         * Allow:
         * users:
         *     names:
         *     uids: 1, 4
         */
        cappwd = cap_clone(origcappwd);
        CHECK(cappwd != NULL);

        names[0] = "daemon";
        names[1] = "tty";
        uids[0] = 1;
        uids[1] = 4;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 2) == 0);
        uids[2] = 3;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 3;
        CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 &&
            errno == ENOTCAPABLE);
        uids[0] = 1;

        CHECK(runtest_users(cappwd, names, uids, 2));

        cap_close(cappwd);
}

int
main(void)
{
        cap_channel_t *capcas, *cappwd;

        printf("1..188\n");
        fflush(stdout);

        capcas = cap_init();
        CHECKX(capcas != NULL);

        cappwd = cap_service_open(capcas, "system.pwd");
        CHECKX(cappwd != NULL);

        cap_close(capcas);

        /* No limits. */

        CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM |
            GETPWNAM_R | GETPWUID | GETPWUID_R));

        test_cmds(cappwd);

        test_fields(cappwd);

        test_users(cappwd);

        cap_close(cappwd);

        exit(0);
}