361 lines
8.4 KiB
C
361 lines
8.4 KiB
C
/*-
|
|
* 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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
/*
|
|
* 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);
|
|
}
|