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