root/regress/sys/kern/access/access.c
/*      $OpenBSD: access.c,v 1.1 2014/04/27 22:18:25 guenther Exp $     */
/*
 *      Written by Philip Guenther <guenther@openbsd.org> 2014 Public Domain.
 */

#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define UID_YES         991
#define UID_NO          990
#define GID_YES         991
#define GID_NO          990

char dir[] = "testdir";
char exists[] = "testdir/testfile";
char r_r_exists[] = "testdir/testfile_r_r";
char r_w_exists[] = "testdir/testfile_r_w";
char w_r_exists[] = "testdir/testfile_w_r";
char w_w_exists[] = "testdir/testfile_w_w";
char x_x_exists[] = "testdir/testfile_x_x";
char noexists[] = "testdir/nosuchfile";

char temp[] = "/tmp/accessXXXXXXXXX";

struct tests {
        int     err, eaccess;
        uid_t   ruid, euid;
        gid_t   rgid, egid;
        int     amode;
        const   char *filename;
} tests[] = {
 /* RETURNS   EACC RUID     EUID     RGID     EGID      AMODE  FILENAME */
 /* negative tests */
 /* unable to search through the directory */
 { EACCES,      0, UID_NO,  UID_NO,  GID_NO,  GID_NO,   F_OK,   exists },
 { EACCES,      0, UID_NO,  UID_YES, GID_NO,  GID_NO,   F_OK,   exists },
 { EACCES,      0, UID_NO,  UID_NO,  GID_NO,  GID_YES,  F_OK,   exists },
 { EACCES,      0, UID_NO,  UID_YES, GID_NO,  GID_YES,  F_OK,   exists },
 { EACCES,      1, UID_NO,  UID_NO,  GID_NO,  GID_NO,   F_OK,   exists },
 { EACCES,      1, UID_YES, UID_NO,  GID_NO,  GID_NO,   F_OK,   exists },
 { EACCES,      1, UID_NO,  UID_NO,  GID_YES, GID_NO,   F_OK,   exists },
 { EACCES,      1, UID_YES, UID_NO,  GID_YES, GID_NO,   F_OK,   exists },
 /* can search to it, but the file ain't there */
 { ENOENT,      0, UID_YES, UID_NO,  GID_NO,  GID_NO,   F_OK,   noexists },
 { ENOENT,      0, UID_NO,  UID_NO,  GID_YES, GID_NO,   F_OK,   noexists },
 { ENOENT,      0, UID_YES, UID_NO,  GID_YES, GID_NO,   F_OK,   noexists },
 { ENOENT,      1, UID_NO,  UID_YES, GID_NO,  GID_NO,   F_OK,   noexists },
 { ENOENT,      1, UID_NO,  UID_NO,  GID_NO,  GID_YES,  F_OK,   noexists },
 { ENOENT,      1, UID_NO,  UID_YES, GID_NO,  GID_YES,  F_OK,   noexists },
 /* can search to it, but the file doesn't have read perm */
 { EACCES,      0, UID_YES, UID_NO,  GID_NO,  GID_NO,   R_OK,   w_w_exists },
 { EACCES,      0, UID_NO,  UID_NO,  GID_YES, GID_NO,   R_OK,   w_w_exists },
 { EACCES,      0, UID_YES, UID_NO,  GID_YES, GID_NO,   R_OK,   w_w_exists },
 { EACCES,      1, UID_NO,  UID_YES, GID_NO,  GID_NO,   R_OK,   w_w_exists },
 { EACCES,      1, UID_NO,  UID_NO,  GID_NO,  GID_YES,  R_OK,   w_w_exists },
 /* can search to it, but the file doesn't have the right read perm */
 { EACCES,      0, UID_YES, UID_NO,  GID_NO,  GID_NO,   R_OK,   w_r_exists },
 { EACCES,      0, UID_NO,  UID_NO,  GID_YES, GID_NO,   R_OK,   r_w_exists },
 { EACCES,      1, UID_NO,  UID_YES, GID_NO,  GID_NO,   R_OK,   w_r_exists },
 { EACCES,      1, UID_NO,  UID_NO,  GID_NO,  GID_YES,  R_OK,   r_w_exists },
 /* if correct user, then group perms are ignored */
 { EACCES,      0, UID_YES, UID_NO,  GID_YES, GID_NO,   R_OK,   w_r_exists },
 { EACCES,      1, UID_NO,  UID_YES, GID_NO,  GID_YES,  R_OK,   w_r_exists },
 { EACCES,      0, UID_YES, UID_YES, GID_YES, GID_YES,  R_OK,   w_r_exists },
 { EACCES,      1, UID_YES, UID_YES, GID_YES, GID_YES,  R_OK,   w_r_exists },

 /* positive tests */
 { 0,           0, UID_YES, UID_NO,  GID_NO,  GID_NO,   R_OK,   r_w_exists },
 { 0,           0, UID_NO,  UID_NO,  GID_YES, GID_NO,   R_OK,   w_r_exists },
 { 0,           0, UID_YES, UID_NO,  GID_YES, GID_NO,   R_OK,   r_w_exists },
 { 0,           0, UID_YES, UID_NO,  GID_YES, GID_NO,   R_OK,   r_r_exists },
 { 0,           1, UID_NO,  UID_YES, GID_NO,  GID_NO,   R_OK,   r_w_exists },
 { 0,           1, UID_NO,  UID_NO,  GID_NO,  GID_YES,  R_OK,   w_r_exists },
 { 0,           1, UID_NO,  UID_YES, GID_NO,  GID_YES,  R_OK,   r_w_exists },
 { 0,           1, UID_NO,  UID_YES, GID_NO,  GID_YES,  R_OK,   r_r_exists },

 { 0,           0, UID_YES, UID_YES, GID_YES, GID_YES,  R_OK,   r_w_exists },
 { 0,           0, UID_YES, UID_YES, GID_YES, GID_YES,  R_OK,   r_r_exists },
 { 0,           1, UID_YES, UID_YES, GID_YES, GID_YES,  R_OK,   r_w_exists },
 { 0,           1, UID_YES, UID_YES, GID_YES, GID_YES,  R_OK,   r_r_exists },

 { 0 }
};

static void
prepfile(const char *filename, mode_t mode)
{
        int fd;

        if ((fd = open(filename, O_WRONLY|O_CREAT, 600)) < 0)
                err(1, "open %s", filename);
        close(fd);
        if (chown(filename, UID_YES, GID_YES))
                err(1, "chown %s %d:%d", filename, UID_YES, GID_YES);
        if (chmod(filename, mode))
                err(1, "chmod %s %o", filename, mode);
}

static void
docleanup(void)
{
        setresuid(0, 0, 0);
        remove(exists);
        remove(r_r_exists);
        remove(r_w_exists);
        remove(w_r_exists);
        remove(w_w_exists);
        remove(x_x_exists);
        remove(dir);
        chdir("/");
        remove(temp);
}

int
main(int argc, char *argv[])
{       
        char buf[200];
        struct tests *t;
        int ret, result;
        gid_t supp_group = GID_NO;

        if (geteuid() != 0) {
                if (getuid() != 0)
                        errx(0, "must be run as root");
                else if (setuid(0))
                        err(1, "setuid");
        }
        if (setgroups(1, &supp_group))
                err(1, "setgroups");

        if (mkdtemp(temp) == NULL)
                err(1, "mkdtemp");

        if (chdir(temp)) {
                ret = errno;
                remove(temp);
                errc(1, ret, "chdir");
        }
        if (chmod(temp, 0755))
                err(1, "chmod %s %o", temp, 0755);

        atexit(docleanup);

        umask(0);
        if (mkdir(dir, 0750))
                err(1, "mkdir");
        prepfile(exists, 0);
        prepfile(r_r_exists, 0440);
        prepfile(r_w_exists, 0420);
        prepfile(w_r_exists, 0240);
        prepfile(w_w_exists, 0220);
        prepfile(x_x_exists, 0110);
        if (chown(dir, UID_YES, GID_YES))
                err(1, "chown %s %d:%d", dir, UID_YES, GID_YES);

        result = 0;
        for (t = tests; t->filename != NULL; t++) {
                if (setresgid(t->rgid, t->egid, 0))
                        err(1, "setresgid");
                if (setresuid(t->ruid, t->euid, 0))
                        err(1, "setresuid");
                ret = faccessat(AT_FDCWD, t->filename, t->amode,
                    t->eaccess ? AT_EACCESS : 0);
                if (ret) {
                        ret = errno;
                        strerror_r(ret, buf, sizeof buf);
                }
                if (ret != t->err) {
                        result = 1;
                        warnx("uid %d/%d gid %d/%d mode %d eaccess %d %s:"
                            " %s instead of %s",
                            t->ruid, t->euid, t->rgid, t->egid,
                            t->amode, t->eaccess, t->filename,
                            ret ? buf : "success",
                            t->err ? strerror(t->err) : "success");
                }
                if (setresuid(0, 0, 0))
                        err(1, "setresuid restore");
        }

        return (result);
}