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