root/tools/regression/security/cap_test/cap_test.h
/*-
 * Copyright (c) 2008-2011 Robert N. M. Watson
 * Copyright (c) 2011 Jonathan Anderson
 * All rights reserved.
 *
 * 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 AUTHOR 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 AUTHOR 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.
 */

#ifndef CAP_TEST_H
#define CAP_TEST_H

#include <err.h>

/*
 * Define a file required by a test. The test can't complete without the file,
 * so if we don't have it, just die.
 */
#define REQUIRE(fd)     do {                                            \
        if ((fd) < 0)                                                   \
                err(-1, "%s:%d: Missing required file '%s'",            \
                        __FILE__, __LINE__, #fd);                       \
} while (0)

/* Whether a test passed or failed. */
#define PASSED  0
#define FAILED  1

/* A test has failed; print a message and clear the 'success' flag. */
#define FAIL(...)       do {                                            \
        warn(__VA_ARGS__);                                              \
        success = FAILED;                                               \
} while (0)

/* As above, but do not print the errno message. */
#define FAILX(...)      do {                                            \
        warnx(__VA_ARGS__);                                             \
        success = FAILED;                                               \
} while (0)

/* Like an assertion, but don't kill the test, just fail and keep going. */
#define CHECK(condition) do {                                           \
        if (!(condition))                                               \
                FAILX("%s:%d: Assertion '%s' failed",                   \
                    __func__, __LINE__, #condition);                    \
} while (0)

/* Make sure that a system call's return value is >= 0. */
#define CHECK_SYSCALL_SUCCEEDS(syscall, ...) do {                       \
        if (syscall(__VA_ARGS__) < 0)                                   \
                FAIL("%s() at line %d: %s failed",                      \
                    __func__, __LINE__, #syscall);                      \
} while (0)

/* Make sure that a system call fails with the correct errno. */
#define CHECK_SYSCALL_FAILS(expected_errno, syscall, ...)       do {    \
        if (syscall(__VA_ARGS__) < 0) {                                 \
                if (errno != expected_errno)                            \
                        FAIL("%s() at line %d: %s",                     \
                            __func__, __LINE__, #syscall);              \
        } else {                                                        \
                FAILX("%s() at line %d: %s succeeded; it should've failed", \
                    __func__, __LINE__, #syscall);                      \
        }                                                               \
} while (0)

/* Make sure that a system call fails, but not with a particular errno. */
#define CHECK_SYSCALL_FAILS_BUT_NOT_WITH(bad_errno, syscall, ...)       do { \
        if (syscall(__VA_ARGS__) < 0) {                                 \
                if (errno == bad_errno)                                 \
                        FAIL("%s() at line %d: %s",                     \
                            __func__, __LINE__, #syscall);              \
        } else {                                                        \
                FAILX("%s() at line %d: %s succeeded; it should've failed", \
                    __func__, __LINE__, #syscall);                      \
        }                                                               \
} while (0)

/* A system call should fail with ECAPMODE. */
#define CHECK_CAPMODE(...) \
        CHECK_SYSCALL_FAILS(ECAPMODE, __VA_ARGS__)

/* A system call should fail, but not with ECAPMODE. */
#define CHECK_NOT_CAPMODE(...) \
        CHECK_SYSCALL_FAILS_BUT_NOT_WITH(ECAPMODE, __VA_ARGS__)

/* A system call should fail with ENOTCAPABLE. */
#define CHECK_NOTCAPABLE(...) \
        CHECK_SYSCALL_FAILS(ENOTCAPABLE, __VA_ARGS__)

/* Ensure that 'rights' are a subset of 'max'. */
#define CHECK_RIGHTS(rights, max)       do {                            \
        if ((success == PASSED) && (rights != max))                     \
                FAILX("Rights of opened file (%jx) > maximum (%jx)",    \
                    (cap_rights_t) rights, (cap_rights_t) max);         \
} while (0)

/* Create a capability from a file descriptor, make sure it succeeds. */
#define MAKE_CAPABILITY(to, from, rights)       do {                    \
        cap_rights_t _rights;                                           \
        REQUIRE(to = cap_new(from, rights));                            \
        CHECK_SYSCALL_SUCCEEDS(cap_getrights, to, &_rights);            \
        if ((success == PASSED) && (_rights != (rights)))               \
                FAILX("New capability's rights (%jx) != %jx",           \
                    _rights, (cap_rights_t) (rights));                  \
} while (0)

/*
 * A top-level test should take no arguments and return an integer value,
 * either PASSED or FAILED.
 *
 * Errors such as SIGSEGV will be caught and interpreted as FAILED.
 */
typedef int     (*test_function)(void);

/* Information about a test. */
struct test {
        char            *t_name;
        test_function    t_run;
        int              t_result;
};

/*
 * Run a test in a child process so that cap_enter(2) doesn't mess up
 * subsequent tests.
 */
int     execute(int id, struct test*);

int     test_capmode(void);
int     test_capabilities(void);
int     test_fcntl(void);
int     test_pdfork(void);
int     test_pdkill(void);
int     test_pdwait(void);
int     test_relative(void);
int     test_sysctl(void);

#endif /* CAP_TEST_H */