xref: /freebsd/tools/regression/security/access/testaccess.c (revision 9336e0699bda8a301cd2bfa37106b6ec5e32012e)
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  * $FreeBSD$
33  */
34 
35 #include <sys/types.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 
43 /*
44  * Regression test to check some basic cases and see if access() and
45  * eaccess() are using the correct portions of the process credential.
46  * This test relies on running with privilege, and on UFS filesystem
47  * semantics.  Running the test in other environments may result
48  * in incorrect failure identification.
49  *
50  * Note that this may also break if filesystem access control is
51  * broken, or if the ability to check and set credentials is broken.
52  *
53  * Note that this test uses two hard-coded non-root UIDs; on multi-user
54  * systems, these UIDs may be in use by an untrusted user, in which
55  * case those users could interfere with the test.
56  */
57 
58 #define	ROOT_UID	(uid_t)0
59 #define	WHEEL_GID	(gid_t)0
60 #define	TEST_UID_ONE	(uid_t)500
61 #define	TEST_GID_ONE	(gid_t)500
62 #define	TEST_UID_TWO	(uid_t)501
63 #define	TEST_GID_TWO	(gid_t)501
64 
65 struct file_description {
66 	char	*fd_name;
67 	uid_t	 fd_owner;
68 	gid_t	 fd_group;
69 	mode_t	 fd_mode;
70 };
71 
72 static struct file_description fd_list[] = {
73 {"test1", ROOT_UID, WHEEL_GID, 0400},
74 {"test2", TEST_UID_ONE, WHEEL_GID,0400},
75 {"test3", TEST_UID_TWO, WHEEL_GID, 0400},
76 {"test4", ROOT_UID, WHEEL_GID, 0040},
77 {"test5", ROOT_UID, TEST_GID_ONE, 0040},
78 {"test6", ROOT_UID, TEST_GID_TWO, 0040}};
79 
80 static int fd_list_count = sizeof(fd_list) /
81     sizeof(struct file_description);
82 
83 int
84 setup(void)
85 {
86 	int i, error;
87 
88 	for (i = 0; i < fd_list_count; i++) {
89 		error = open(fd_list[i].fd_name, O_CREAT | O_EXCL, fd_list[i].fd_mode);
90 		if (error == -1) {
91 			perror("open");
92 			return (error);
93 		}
94 		close(error);
95 		error = chown(fd_list[i].fd_name, fd_list[i].fd_owner,
96 		    fd_list[i].fd_group);
97 		if (error) {
98 			perror("chown");
99 			return (error);
100 		}
101 	}
102 	return (0);
103 }
104 
105 int
106 restoreprivilege(void)
107 {
108 	int error;
109 
110 	error = setreuid(ROOT_UID, ROOT_UID);
111 	if (error)
112 		return (error);
113 
114 	error = setregid(WHEEL_GID, WHEEL_GID);
115 	if (error)
116 		return (error);
117 
118 	return (0);
119 }
120 
121 int
122 reportprivilege(char *message)
123 {
124 	uid_t euid, ruid, suid;
125 	gid_t egid, rgid, sgid;
126 	int error;
127 
128 	error = getresuid(&ruid, &euid, &suid);
129 	if (error) {
130 		perror("getresuid");
131 		return (error);
132 	}
133 
134 	error = getresgid(&rgid, &egid, &sgid);
135 	if (error) {
136 		perror("getresgid");
137 		return (error);
138 	}
139 
140 	if (message)
141 		printf("%s: ", message);
142 	printf("ruid: %d, euid: %d, suid: %d,     ", ruid, euid, suid);
143 	printf("rgid: %d, egid: %d, sgid: %d\n", rgid, egid, sgid);
144 
145 	return (0);
146 }
147 
148 int
149 cleanup(void)
150 {
151 	int i, error;
152 
153 	error = restoreprivilege();
154 	if (error) {
155 		perror("restoreprivilege");
156 		return (error);
157 	}
158 
159 	for (i = 0; i < fd_list_count; i++) {
160 		error = unlink(fd_list[i].fd_name);
161 		if (error)
162 			return (error);
163 	}
164 
165 	return (0);
166 }
167 
168 int
169 main(int argc, char *argv[])
170 {
171 	int error, errorseen;
172 
173 	if (geteuid() != 0) {
174 		fprintf(stderr, "testaccess must run as root.\n");
175 		exit (EXIT_FAILURE);
176 	}
177 
178 	error = setup();
179 	if (error) {
180 		cleanup();
181 		exit (EXIT_FAILURE);
182 	}
183 
184 	/* Make sure saved uid is set appropriately. */
185 	error = setresuid(ROOT_UID, ROOT_UID, ROOT_UID);
186 	if (error) {
187 		perror("setresuid");
188 		cleanup();
189 	}
190 
191 	/* Clear out additional groups. */
192 	error = setgroups(0, NULL);
193 	if (error) {
194 		perror("setgroups");
195 		cleanup();
196 	}
197 
198 	/* Make sure saved gid is set appropriately. */
199 	error = setresgid(WHEEL_GID, WHEEL_GID, WHEEL_GID);
200 	if (error) {
201 		perror("setresgid");
202 		cleanup();
203 	}
204 
205 	/*
206 	 * UID-only tests.
207 	 */
208 
209 	/* Check that saved uid is not used */
210 	error = setresuid(TEST_UID_ONE, TEST_UID_ONE, ROOT_UID);
211 	if (error) {
212 		perror("setresuid.1");
213 		cleanup();
214 		exit (EXIT_FAILURE);
215 	}
216 
217 	errorseen = 0;
218 
219 	error = access("test1", R_OK);
220 	if (!error) {
221 		fprintf(stderr, "saved uid used instead of real uid\n");
222 		errorseen++;
223 	}
224 
225 #ifdef EACCESS_AVAILABLE
226 	error = eaccess("test1", R_OK);
227 	if (!error) {
228 		fprintf(stderr, "saved uid used instead of effective uid\n");
229 		errorseen++;
230 	}
231 #endif
232 
233 	error = restoreprivilege();
234 	if (error) {
235 		perror("restoreprivilege");
236 		cleanup();
237 		exit (EXIT_FAILURE);
238 	}
239 
240 	error = setresuid(TEST_UID_ONE, TEST_UID_TWO, ROOT_UID);
241 	if (error) {
242 		perror("setresid.2");
243 		cleanup();
244 		exit (EXIT_FAILURE);
245 	}
246 
247 	/* Check that the real uid is used, not the effective uid */
248 	error = access("test2", R_OK);
249 	if (error) {
250 		fprintf(stderr, "Effective uid was used instead of real uid in access().\n");
251 		errorseen++;
252 	}
253 
254 #ifdef EACCESS_AVAILABLE
255 	/* Check that the effective uid is used, not the real uid */
256 	error = eaccess("test3", R_OK);
257 	if (error) {
258 		fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n");
259 		errorseen++;
260 	}
261 #endif
262 
263 	/* Check that the real uid is used, not the effective uid */
264 	error = access("test3", R_OK);
265 	if (!error) {
266 		fprintf(stderr, "Effective uid was used instead of real uid in access().\n");
267 		errorseen++;
268 	}
269 
270 #ifdef EACCESS_AVAILABLE
271 	/* Check that the effective uid is used, not the real uid */
272 	error = eaccess("test2", R_OK);
273 	if (!error) {
274 		fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n");
275 		errorseen++;
276 	}
277 #endif
278 
279 	error = restoreprivilege();
280 	if (error) {
281 		perror("restoreprivilege");
282 		cleanup();
283 		exit (EXIT_FAILURE);
284 	}
285 
286 	error = setresgid(TEST_GID_ONE, TEST_GID_TWO, WHEEL_GID);
287 	if (error) {
288 		perror("setresgid.1");
289 		cleanup();
290 		exit (EXIT_FAILURE);
291 	}
292 
293 	/* Set non-root effective uid to avoid excess privilege. */
294 	error = setresuid(TEST_UID_ONE, TEST_UID_ONE, ROOT_UID);
295 	if (error) {
296 		perror("setresuid.3");
297 		cleanup();
298 		exit (EXIT_FAILURE);
299 	}
300 
301 	/* Check that the saved gid is not used */
302 	error = access("test4", R_OK);
303 	if (!error) {
304 		fprintf(stderr, "saved gid used instead of real gid\n");
305 	}
306 
307 #ifdef EACCESS_AVAILABLE
308 	error = eaccess("test4", R_OK);
309 	if (!error) {
310 		fprintf(stderr, "saved gid used instead of effective gid\n");
311 		errorseen++;
312 	}
313 #endif
314 
315 	/* Check that the real gid is used, not the effective gid */
316 	error = access("test5", R_OK);
317 	if (error) {
318 		fprintf(stderr, "Effective gid was used instead of real gid in access().\n");
319 		errorseen++;
320 	}
321 
322 #ifdef EACCESS_AVAILABLE
323 	/* Check that the effective gid is used, not the real gid */
324 	error = eaccess("test6", R_OK);
325 	if (error) {
326 		fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n");
327 		errorseen++;
328 	}
329 #endif
330 
331 	/* Check that the real gid is used, not the effective gid */
332 	error = access("test6", R_OK);
333 	if (!error) {
334 		fprintf(stderr, "Effective gid was used instead of real gid in access().\n");
335 		errorseen++;
336 	}
337 
338 #ifdef EACCESS_AVAILABLE
339 	/* Check that the effective gid is used, not the real gid */
340 	error = eaccess("test5", R_OK);
341 	if (!error) {
342 		fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n");
343 		errorseen++;
344 	}
345 #endif
346 
347 	fprintf(stderr, "%d errors seen.\n", errorseen);
348 
349 	/*
350 	 * All tests done, restore and clean up
351 	 */
352 
353 	error = cleanup();
354 	if (error) {
355 		perror("cleanup");
356 		exit (EXIT_FAILURE);
357 	}
358 
359 	exit (EXIT_SUCCESS);
360 }
361