Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*-
 * 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);
}