testaccess.c revision 96709
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: head/tools/regression/security/access/testaccess.c 96709 2002-05-16 05:03:56Z trhodes $
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
65struct file_description {
66	char	*fd_name;
67	uid_t	 fd_owner;
68	gid_t	 fd_group;
69	mode_t	 fd_mode;
70};
71
72static 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
80static int fd_list_count = sizeof(fd_list) /
81    sizeof(struct file_description);
82
83int
84setup(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
105int
106restoreprivilege(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
121int
122reportprivilege(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
148int
149cleanup(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
168int
169main(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