xref: /openbsd/regress/sys/kern/access/access.c (revision 2ca8472c)
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