xref: /freebsd/tools/regression/security/access/testaccess.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*-
2  * Copyright (c) 2001 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * Written at NAI Labs at Network Associates by Robert Watson for the
27  * TrustedBSD Project.
28  *
29  * Work sponsored by Defense Advanced Research Projects Agency under the
30  * CHATS research program, CBOSS project.
31  */
32 
33 #include <sys/types.h>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 
41 /*
42  * Regression test to check some basic cases and see if access() and
43  * eaccess() are using the correct portions of the process credential.
44  * This test relies on running with privilege, and on UFS filesystem
45  * semantics.  Running the test in other environments may result
46  * in incorrect failure identification.
47  *
48  * Note that this may also break if filesystem access control is
49  * broken, or if the ability to check and set credentials is broken.
50  *
51  * Note that this test uses two hard-coded non-root UIDs; on multi-user
52  * systems, these UIDs may be in use by an untrusted user, in which
53  * case those users could interfere with the test.
54  */
55 
56 #define	ROOT_UID	(uid_t)0
57 #define	WHEEL_GID	(gid_t)0
58 #define	TEST_UID_ONE	(uid_t)500
59 #define	TEST_GID_ONE	(gid_t)500
60 #define	TEST_UID_TWO	(uid_t)501
61 #define	TEST_GID_TWO	(gid_t)501
62 
63 struct file_description {
64 	char	*fd_name;
65 	uid_t	 fd_owner;
66 	gid_t	 fd_group;
67 	mode_t	 fd_mode;
68 };
69 
70 static struct file_description fd_list[] = {
71 {"test1", ROOT_UID, WHEEL_GID, 0400},
72 {"test2", TEST_UID_ONE, WHEEL_GID,0400},
73 {"test3", TEST_UID_TWO, WHEEL_GID, 0400},
74 {"test4", ROOT_UID, WHEEL_GID, 0040},
75 {"test5", ROOT_UID, TEST_GID_ONE, 0040},
76 {"test6", ROOT_UID, TEST_GID_TWO, 0040}};
77 
78 static int fd_list_count = sizeof(fd_list) /
79     sizeof(struct file_description);
80 
81 int
82 setup(void)
83 {
84 	int i, error;
85 
86 	for (i = 0; i < fd_list_count; i++) {
87 		error = open(fd_list[i].fd_name, O_CREAT | O_EXCL, fd_list[i].fd_mode);
88 		if (error == -1) {
89 			perror("open");
90 			return (error);
91 		}
92 		close(error);
93 		error = chown(fd_list[i].fd_name, fd_list[i].fd_owner,
94 		    fd_list[i].fd_group);
95 		if (error) {
96 			perror("chown");
97 			return (error);
98 		}
99 	}
100 	return (0);
101 }
102 
103 int
104 restoreprivilege(void)
105 {
106 	int error;
107 
108 	error = setreuid(ROOT_UID, ROOT_UID);
109 	if (error)
110 		return (error);
111 
112 	error = setregid(WHEEL_GID, WHEEL_GID);
113 	if (error)
114 		return (error);
115 
116 	return (0);
117 }
118 
119 int
120 reportprivilege(char *message)
121 {
122 	uid_t euid, ruid, suid;
123 	gid_t egid, rgid, sgid;
124 	int error;
125 
126 	error = getresuid(&ruid, &euid, &suid);
127 	if (error) {
128 		perror("getresuid");
129 		return (error);
130 	}
131 
132 	error = getresgid(&rgid, &egid, &sgid);
133 	if (error) {
134 		perror("getresgid");
135 		return (error);
136 	}
137 
138 	if (message)
139 		printf("%s: ", message);
140 	printf("ruid: %d, euid: %d, suid: %d,     ", ruid, euid, suid);
141 	printf("rgid: %d, egid: %d, sgid: %d\n", rgid, egid, sgid);
142 
143 	return (0);
144 }
145 
146 int
147 cleanup(void)
148 {
149 	int i, error;
150 
151 	error = restoreprivilege();
152 	if (error) {
153 		perror("restoreprivilege");
154 		return (error);
155 	}
156 
157 	for (i = 0; i < fd_list_count; i++) {
158 		error = unlink(fd_list[i].fd_name);
159 		if (error)
160 			return (error);
161 	}
162 
163 	return (0);
164 }
165 
166 int
167 main(int argc, char *argv[])
168 {
169 	int error, errorseen;
170 
171 	if (geteuid() != 0) {
172 		fprintf(stderr, "testaccess must run as root.\n");
173 		exit (EXIT_FAILURE);
174 	}
175 
176 	error = setup();
177 	if (error) {
178 		cleanup();
179 		exit (EXIT_FAILURE);
180 	}
181 
182 	/* Make sure saved uid is set appropriately. */
183 	error = setresuid(ROOT_UID, ROOT_UID, ROOT_UID);
184 	if (error) {
185 		perror("setresuid");
186 		cleanup();
187 	}
188 
189 	/* Clear out additional groups. */
190 	error = setgroups(0, NULL);
191 	if (error) {
192 		perror("setgroups");
193 		cleanup();
194 	}
195 
196 	/* Make sure saved gid is set appropriately. */
197 	error = setresgid(WHEEL_GID, WHEEL_GID, WHEEL_GID);
198 	if (error) {
199 		perror("setresgid");
200 		cleanup();
201 	}
202 
203 	/*
204 	 * UID-only tests.
205 	 */
206 
207 	/* Check that saved uid is not used */
208 	error = setresuid(TEST_UID_ONE, TEST_UID_ONE, ROOT_UID);
209 	if (error) {
210 		perror("setresuid.1");
211 		cleanup();
212 		exit (EXIT_FAILURE);
213 	}
214 
215 	errorseen = 0;
216 
217 	error = access("test1", R_OK);
218 	if (!error) {
219 		fprintf(stderr, "saved uid used instead of real uid\n");
220 		errorseen++;
221 	}
222 
223 #ifdef EACCESS_AVAILABLE
224 	error = eaccess("test1", R_OK);
225 	if (!error) {
226 		fprintf(stderr, "saved uid used instead of effective uid\n");
227 		errorseen++;
228 	}
229 #endif
230 
231 	error = restoreprivilege();
232 	if (error) {
233 		perror("restoreprivilege");
234 		cleanup();
235 		exit (EXIT_FAILURE);
236 	}
237 
238 	error = setresuid(TEST_UID_ONE, TEST_UID_TWO, ROOT_UID);
239 	if (error) {
240 		perror("setresid.2");
241 		cleanup();
242 		exit (EXIT_FAILURE);
243 	}
244 
245 	/* Check that the real uid is used, not the effective uid */
246 	error = access("test2", R_OK);
247 	if (error) {
248 		fprintf(stderr, "Effective uid was used instead of real uid in access().\n");
249 		errorseen++;
250 	}
251 
252 #ifdef EACCESS_AVAILABLE
253 	/* Check that the effective uid is used, not the real uid */
254 	error = eaccess("test3", R_OK);
255 	if (error) {
256 		fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n");
257 		errorseen++;
258 	}
259 #endif
260 
261 	/* Check that the real uid is used, not the effective uid */
262 	error = access("test3", R_OK);
263 	if (!error) {
264 		fprintf(stderr, "Effective uid was used instead of real uid in access().\n");
265 		errorseen++;
266 	}
267 
268 #ifdef EACCESS_AVAILABLE
269 	/* Check that the effective uid is used, not the real uid */
270 	error = eaccess("test2", R_OK);
271 	if (!error) {
272 		fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n");
273 		errorseen++;
274 	}
275 #endif
276 
277 	error = restoreprivilege();
278 	if (error) {
279 		perror("restoreprivilege");
280 		cleanup();
281 		exit (EXIT_FAILURE);
282 	}
283 
284 	error = setresgid(TEST_GID_ONE, TEST_GID_TWO, WHEEL_GID);
285 	if (error) {
286 		perror("setresgid.1");
287 		cleanup();
288 		exit (EXIT_FAILURE);
289 	}
290 
291 	/* Set non-root effective uid to avoid excess privilege. */
292 	error = setresuid(TEST_UID_ONE, TEST_UID_ONE, ROOT_UID);
293 	if (error) {
294 		perror("setresuid.3");
295 		cleanup();
296 		exit (EXIT_FAILURE);
297 	}
298 
299 	/* Check that the saved gid is not used */
300 	error = access("test4", R_OK);
301 	if (!error) {
302 		fprintf(stderr, "saved gid used instead of real gid\n");
303 	}
304 
305 #ifdef EACCESS_AVAILABLE
306 	error = eaccess("test4", R_OK);
307 	if (!error) {
308 		fprintf(stderr, "saved gid used instead of effective gid\n");
309 		errorseen++;
310 	}
311 #endif
312 
313 	/* Check that the real gid is used, not the effective gid */
314 	error = access("test5", R_OK);
315 	if (error) {
316 		fprintf(stderr, "Effective gid was used instead of real gid in access().\n");
317 		errorseen++;
318 	}
319 
320 #ifdef EACCESS_AVAILABLE
321 	/* Check that the effective gid is used, not the real gid */
322 	error = eaccess("test6", R_OK);
323 	if (error) {
324 		fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n");
325 		errorseen++;
326 	}
327 #endif
328 
329 	/* Check that the real gid is used, not the effective gid */
330 	error = access("test6", R_OK);
331 	if (!error) {
332 		fprintf(stderr, "Effective gid was used instead of real gid in access().\n");
333 		errorseen++;
334 	}
335 
336 #ifdef EACCESS_AVAILABLE
337 	/* Check that the effective gid is used, not the real gid */
338 	error = eaccess("test5", R_OK);
339 	if (!error) {
340 		fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n");
341 		errorseen++;
342 	}
343 #endif
344 
345 	fprintf(stderr, "%d errors seen.\n", errorseen);
346 
347 	/*
348 	 * All tests done, restore and clean up
349 	 */
350 
351 	error = cleanup();
352 	if (error) {
353 		perror("cleanup");
354 		exit (EXIT_FAILURE);
355 	}
356 
357 	exit (EXIT_SUCCESS);
358 }
359