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
setup(void)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
restoreprivilege(void)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
reportprivilege(char * message)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
cleanup(void)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
main(int argc,char * argv[])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