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
prepfile(const char * filename,mode_t mode)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
docleanup(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
main(int argc,char * argv[])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