1 /* $OpenBSD: access.c,v 1.1 2014/04/27 22:18:25 guenther Exp $ */ 2 /* 3 * Written by Philip Guenther <guenther@openbsd.org> 2014 Public Domain. 4 */ 5 6 #include <sys/stat.h> 7 #include <err.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <limits.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 #define UID_YES 991 17 #define UID_NO 990 18 #define GID_YES 991 19 #define GID_NO 990 20 21 char dir[] = "testdir"; 22 char exists[] = "testdir/testfile"; 23 char r_r_exists[] = "testdir/testfile_r_r"; 24 char r_w_exists[] = "testdir/testfile_r_w"; 25 char w_r_exists[] = "testdir/testfile_w_r"; 26 char w_w_exists[] = "testdir/testfile_w_w"; 27 char x_x_exists[] = "testdir/testfile_x_x"; 28 char noexists[] = "testdir/nosuchfile"; 29 30 char temp[] = "/tmp/accessXXXXXXXXX"; 31 32 struct tests { 33 int err, eaccess; 34 uid_t ruid, euid; 35 gid_t rgid, egid; 36 int amode; 37 const char *filename; 38 } tests[] = { 39 /* RETURNS EACC RUID EUID RGID EGID AMODE FILENAME */ 40 /* negative tests */ 41 /* unable to search through the directory */ 42 { EACCES, 0, UID_NO, UID_NO, GID_NO, GID_NO, F_OK, exists }, 43 { EACCES, 0, UID_NO, UID_YES, GID_NO, GID_NO, F_OK, exists }, 44 { EACCES, 0, UID_NO, UID_NO, GID_NO, GID_YES, F_OK, exists }, 45 { EACCES, 0, UID_NO, UID_YES, GID_NO, GID_YES, F_OK, exists }, 46 { EACCES, 1, UID_NO, UID_NO, GID_NO, GID_NO, F_OK, exists }, 47 { EACCES, 1, UID_YES, UID_NO, GID_NO, GID_NO, F_OK, exists }, 48 { EACCES, 1, UID_NO, UID_NO, GID_YES, GID_NO, F_OK, exists }, 49 { EACCES, 1, UID_YES, UID_NO, GID_YES, GID_NO, F_OK, exists }, 50 /* can search to it, but the file ain't there */ 51 { ENOENT, 0, UID_YES, UID_NO, GID_NO, GID_NO, F_OK, noexists }, 52 { ENOENT, 0, UID_NO, UID_NO, GID_YES, GID_NO, F_OK, noexists }, 53 { ENOENT, 0, UID_YES, UID_NO, GID_YES, GID_NO, F_OK, noexists }, 54 { ENOENT, 1, UID_NO, UID_YES, GID_NO, GID_NO, F_OK, noexists }, 55 { ENOENT, 1, UID_NO, UID_NO, GID_NO, GID_YES, F_OK, noexists }, 56 { ENOENT, 1, UID_NO, UID_YES, GID_NO, GID_YES, F_OK, noexists }, 57 /* can search to it, but the file doesn't have read perm */ 58 { EACCES, 0, UID_YES, UID_NO, GID_NO, GID_NO, R_OK, w_w_exists }, 59 { EACCES, 0, UID_NO, UID_NO, GID_YES, GID_NO, R_OK, w_w_exists }, 60 { EACCES, 0, UID_YES, UID_NO, GID_YES, GID_NO, R_OK, w_w_exists }, 61 { EACCES, 1, UID_NO, UID_YES, GID_NO, GID_NO, R_OK, w_w_exists }, 62 { EACCES, 1, UID_NO, UID_NO, GID_NO, GID_YES, R_OK, w_w_exists }, 63 /* can search to it, but the file doesn't have the right read perm */ 64 { EACCES, 0, UID_YES, UID_NO, GID_NO, GID_NO, R_OK, w_r_exists }, 65 { EACCES, 0, UID_NO, UID_NO, GID_YES, GID_NO, R_OK, r_w_exists }, 66 { EACCES, 1, UID_NO, UID_YES, GID_NO, GID_NO, R_OK, w_r_exists }, 67 { EACCES, 1, UID_NO, UID_NO, GID_NO, GID_YES, R_OK, r_w_exists }, 68 /* if correct user, then group perms are ignored */ 69 { EACCES, 0, UID_YES, UID_NO, GID_YES, GID_NO, R_OK, w_r_exists }, 70 { EACCES, 1, UID_NO, UID_YES, GID_NO, GID_YES, R_OK, w_r_exists }, 71 { EACCES, 0, UID_YES, UID_YES, GID_YES, GID_YES, R_OK, w_r_exists }, 72 { EACCES, 1, UID_YES, UID_YES, GID_YES, GID_YES, R_OK, w_r_exists }, 73 74 /* positive tests */ 75 { 0, 0, UID_YES, UID_NO, GID_NO, GID_NO, R_OK, r_w_exists }, 76 { 0, 0, UID_NO, UID_NO, GID_YES, GID_NO, R_OK, w_r_exists }, 77 { 0, 0, UID_YES, UID_NO, GID_YES, GID_NO, R_OK, r_w_exists }, 78 { 0, 0, UID_YES, UID_NO, GID_YES, GID_NO, R_OK, r_r_exists }, 79 { 0, 1, UID_NO, UID_YES, GID_NO, GID_NO, R_OK, r_w_exists }, 80 { 0, 1, UID_NO, UID_NO, GID_NO, GID_YES, R_OK, w_r_exists }, 81 { 0, 1, UID_NO, UID_YES, GID_NO, GID_YES, R_OK, r_w_exists }, 82 { 0, 1, UID_NO, UID_YES, GID_NO, GID_YES, R_OK, r_r_exists }, 83 84 { 0, 0, UID_YES, UID_YES, GID_YES, GID_YES, R_OK, r_w_exists }, 85 { 0, 0, UID_YES, UID_YES, GID_YES, GID_YES, R_OK, r_r_exists }, 86 { 0, 1, UID_YES, UID_YES, GID_YES, GID_YES, R_OK, r_w_exists }, 87 { 0, 1, UID_YES, UID_YES, GID_YES, GID_YES, R_OK, r_r_exists }, 88 89 { 0 } 90 }; 91 92 static void 93 prepfile(const char *filename, mode_t mode) 94 { 95 int fd; 96 97 if ((fd = open(filename, O_WRONLY|O_CREAT, 600)) < 0) 98 err(1, "open %s", filename); 99 close(fd); 100 if (chown(filename, UID_YES, GID_YES)) 101 err(1, "chown %s %d:%d", filename, UID_YES, GID_YES); 102 if (chmod(filename, mode)) 103 err(1, "chmod %s %o", filename, mode); 104 } 105 106 static void 107 docleanup(void) 108 { 109 setresuid(0, 0, 0); 110 remove(exists); 111 remove(r_r_exists); 112 remove(r_w_exists); 113 remove(w_r_exists); 114 remove(w_w_exists); 115 remove(x_x_exists); 116 remove(dir); 117 chdir("/"); 118 remove(temp); 119 } 120 121 int 122 main(int argc, char *argv[]) 123 { 124 char buf[200]; 125 struct tests *t; 126 int ret, result; 127 gid_t supp_group = GID_NO; 128 129 if (geteuid() != 0) { 130 if (getuid() != 0) 131 errx(0, "must be run as root"); 132 else if (setuid(0)) 133 err(1, "setuid"); 134 } 135 if (setgroups(1, &supp_group)) 136 err(1, "setgroups"); 137 138 if (mkdtemp(temp) == NULL) 139 err(1, "mkdtemp"); 140 141 if (chdir(temp)) { 142 ret = errno; 143 remove(temp); 144 errc(1, ret, "chdir"); 145 } 146 if (chmod(temp, 0755)) 147 err(1, "chmod %s %o", temp, 0755); 148 149 atexit(docleanup); 150 151 umask(0); 152 if (mkdir(dir, 0750)) 153 err(1, "mkdir"); 154 prepfile(exists, 0); 155 prepfile(r_r_exists, 0440); 156 prepfile(r_w_exists, 0420); 157 prepfile(w_r_exists, 0240); 158 prepfile(w_w_exists, 0220); 159 prepfile(x_x_exists, 0110); 160 if (chown(dir, UID_YES, GID_YES)) 161 err(1, "chown %s %d:%d", dir, UID_YES, GID_YES); 162 163 result = 0; 164 for (t = tests; t->filename != NULL; t++) { 165 if (setresgid(t->rgid, t->egid, 0)) 166 err(1, "setresgid"); 167 if (setresuid(t->ruid, t->euid, 0)) 168 err(1, "setresuid"); 169 ret = faccessat(AT_FDCWD, t->filename, t->amode, 170 t->eaccess ? AT_EACCESS : 0); 171 if (ret) { 172 ret = errno; 173 strerror_r(ret, buf, sizeof buf); 174 } 175 if (ret != t->err) { 176 result = 1; 177 warnx("uid %d/%d gid %d/%d mode %d eaccess %d %s:" 178 " %s instead of %s", 179 t->ruid, t->euid, t->rgid, t->egid, 180 t->amode, t->eaccess, t->filename, 181 ret ? buf : "success", 182 t->err ? strerror(t->err) : "success"); 183 } 184 if (setresuid(0, 0, 0)) 185 err(1, "setresuid restore"); 186 } 187 188 return (result); 189 } 190