/*- * Copyright (c) 2001 Networks Associates Technology, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Written at NAI Labs at Network Associates by Robert Watson for the * TrustedBSD Project. * * Work sponsored by Defense Advanced Research Projects Agency under the * CHATS research program, CBOSS project. */ #include #include #include #include #include #include /* * Regression test to check some basic cases and see if access() and * eaccess() are using the correct portions of the process credential. * This test relies on running with privilege, and on UFS filesystem * semantics. Running the test in other environments may result * in incorrect failure identification. * * Note that this may also break if filesystem access control is * broken, or if the ability to check and set credentials is broken. * * Note that this test uses two hard-coded non-root UIDs; on multi-user * systems, these UIDs may be in use by an untrusted user, in which * case those users could interfere with the test. */ #define ROOT_UID (uid_t)0 #define WHEEL_GID (gid_t)0 #define TEST_UID_ONE (uid_t)500 #define TEST_GID_ONE (gid_t)500 #define TEST_UID_TWO (uid_t)501 #define TEST_GID_TWO (gid_t)501 struct file_description { char *fd_name; uid_t fd_owner; gid_t fd_group; mode_t fd_mode; }; static struct file_description fd_list[] = { {"test1", ROOT_UID, WHEEL_GID, 0400}, {"test2", TEST_UID_ONE, WHEEL_GID,0400}, {"test3", TEST_UID_TWO, WHEEL_GID, 0400}, {"test4", ROOT_UID, WHEEL_GID, 0040}, {"test5", ROOT_UID, TEST_GID_ONE, 0040}, {"test6", ROOT_UID, TEST_GID_TWO, 0040}}; static int fd_list_count = sizeof(fd_list) / sizeof(struct file_description); int setup(void) { int i, error; for (i = 0; i < fd_list_count; i++) { error = open(fd_list[i].fd_name, O_CREAT | O_EXCL, fd_list[i].fd_mode); if (error == -1) { perror("open"); return (error); } close(error); error = chown(fd_list[i].fd_name, fd_list[i].fd_owner, fd_list[i].fd_group); if (error) { perror("chown"); return (error); } } return (0); } int restoreprivilege(void) { int error; error = setreuid(ROOT_UID, ROOT_UID); if (error) return (error); error = setregid(WHEEL_GID, WHEEL_GID); if (error) return (error); return (0); } int reportprivilege(char *message) { uid_t euid, ruid, suid; gid_t egid, rgid, sgid; int error; error = getresuid(&ruid, &euid, &suid); if (error) { perror("getresuid"); return (error); } error = getresgid(&rgid, &egid, &sgid); if (error) { perror("getresgid"); return (error); } if (message) printf("%s: ", message); printf("ruid: %d, euid: %d, suid: %d, ", ruid, euid, suid); printf("rgid: %d, egid: %d, sgid: %d\n", rgid, egid, sgid); return (0); } int cleanup(void) { int i, error; error = restoreprivilege(); if (error) { perror("restoreprivilege"); return (error); } for (i = 0; i < fd_list_count; i++) { error = unlink(fd_list[i].fd_name); if (error) return (error); } return (0); } int main(int argc, char *argv[]) { int error, errorseen; if (geteuid() != 0) { fprintf(stderr, "testaccess must run as root.\n"); exit (EXIT_FAILURE); } error = setup(); if (error) { cleanup(); exit (EXIT_FAILURE); } /* Make sure saved uid is set appropriately. */ error = setresuid(ROOT_UID, ROOT_UID, ROOT_UID); if (error) { perror("setresuid"); cleanup(); } /* Clear out additional groups. */ error = setgroups(0, NULL); if (error) { perror("setgroups"); cleanup(); } /* Make sure saved gid is set appropriately. */ error = setresgid(WHEEL_GID, WHEEL_GID, WHEEL_GID); if (error) { perror("setresgid"); cleanup(); } /* * UID-only tests. */ /* Check that saved uid is not used */ error = setresuid(TEST_UID_ONE, TEST_UID_ONE, ROOT_UID); if (error) { perror("setresuid.1"); cleanup(); exit (EXIT_FAILURE); } errorseen = 0; error = access("test1", R_OK); if (!error) { fprintf(stderr, "saved uid used instead of real uid\n"); errorseen++; } #ifdef EACCESS_AVAILABLE error = eaccess("test1", R_OK); if (!error) { fprintf(stderr, "saved uid used instead of effective uid\n"); errorseen++; } #endif error = restoreprivilege(); if (error) { perror("restoreprivilege"); cleanup(); exit (EXIT_FAILURE); } error = setresuid(TEST_UID_ONE, TEST_UID_TWO, ROOT_UID); if (error) { perror("setresid.2"); cleanup(); exit (EXIT_FAILURE); } /* Check that the real uid is used, not the effective uid */ error = access("test2", R_OK); if (error) { fprintf(stderr, "Effective uid was used instead of real uid in access().\n"); errorseen++; } #ifdef EACCESS_AVAILABLE /* Check that the effective uid is used, not the real uid */ error = eaccess("test3", R_OK); if (error) { fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n"); errorseen++; } #endif /* Check that the real uid is used, not the effective uid */ error = access("test3", R_OK); if (!error) { fprintf(stderr, "Effective uid was used instead of real uid in access().\n"); errorseen++; } #ifdef EACCESS_AVAILABLE /* Check that the effective uid is used, not the real uid */ error = eaccess("test2", R_OK); if (!error) { fprintf(stderr, "Real uid was used instead of effective uid in eaccess().\n"); errorseen++; } #endif error = restoreprivilege(); if (error) { perror("restoreprivilege"); cleanup(); exit (EXIT_FAILURE); } error = setresgid(TEST_GID_ONE, TEST_GID_TWO, WHEEL_GID); if (error) { perror("setresgid.1"); cleanup(); exit (EXIT_FAILURE); } /* Set non-root effective uid to avoid excess privilege. */ error = setresuid(TEST_UID_ONE, TEST_UID_ONE, ROOT_UID); if (error) { perror("setresuid.3"); cleanup(); exit (EXIT_FAILURE); } /* Check that the saved gid is not used */ error = access("test4", R_OK); if (!error) { fprintf(stderr, "saved gid used instead of real gid\n"); } #ifdef EACCESS_AVAILABLE error = eaccess("test4", R_OK); if (!error) { fprintf(stderr, "saved gid used instead of effective gid\n"); errorseen++; } #endif /* Check that the real gid is used, not the effective gid */ error = access("test5", R_OK); if (error) { fprintf(stderr, "Effective gid was used instead of real gid in access().\n"); errorseen++; } #ifdef EACCESS_AVAILABLE /* Check that the effective gid is used, not the real gid */ error = eaccess("test6", R_OK); if (error) { fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n"); errorseen++; } #endif /* Check that the real gid is used, not the effective gid */ error = access("test6", R_OK); if (!error) { fprintf(stderr, "Effective gid was used instead of real gid in access().\n"); errorseen++; } #ifdef EACCESS_AVAILABLE /* Check that the effective gid is used, not the real gid */ error = eaccess("test5", R_OK); if (!error) { fprintf(stderr, "Real gid was used instead of effective gid in eaccess().\n"); errorseen++; } #endif fprintf(stderr, "%d errors seen.\n", errorseen); /* * All tests done, restore and clean up */ error = cleanup(); if (error) { perror("cleanup"); exit (EXIT_FAILURE); } exit (EXIT_SUCCESS); }