/******************************************************************************/
/* This program is free software;  you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by       */
/* the Free Software Foundation; either version 2 of the License, or          */
/* (at your option) any later version.                                        */
/*                                                                            */
/* This program is distributed in the hope that it will be useful,            */
/* but WITHOUT ANY WARRANTY;  without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See                  */
/* the GNU General Public License for more details.                           */
/*                                                                            */
/* You should have received a copy of the GNU General Public License          */
/* along with this program;  if not, write to the Free Software               */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
/*                                                                            */
/******************************************************************************/
/*
 * tomoyo_policy_memory_test.c
 *
 * Testing program for security/tomoyo/
 *
 * Copyright (C) 2005-2010  NTT DATA CORPORATION
 */
/*
 * Usage: Run this program using init= boot option.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>

static void BUG(const char *msg)
{
	printf("%s", msg);
	fflush(stdout);
	while (1)
		sleep(100);
}

static const char *policy_file = NULL;
static const char *policy = NULL;

static void get_meminfo(unsigned int *policy_memory)
{
	FILE *fp = fopen("/sys/kernel/security/tomoyo/meminfo", "r");
	if (!fp || fscanf(fp, "Policy: %u", policy_memory) != 1 || fclose(fp))
		BUG("BUG: Policy read error\n");
}

static void check_policy_common(const int found_expected, const int id)
{
	FILE *fp = fopen(policy_file, "r");
	char buffer[8192];
	int policy_found = 0;
	memset(buffer, 0, sizeof(buffer));
	if (!fp)
		BUG("BUG: Policy read error\n");
	while (fgets(buffer, sizeof(buffer) - 1, fp)) {
		char *cp = strchr(buffer, '\n');
		if (cp)
			*cp = '\0';
		if (strcmp(buffer, policy))
			continue;
		policy_found = 1;
		break;
	}
	fclose(fp);
	if (policy_found != found_expected) {
		printf("BUG: Policy write error: %s %s at %d\n", policy,
		       found_expected ? "not added" : "not deleted", id);
		BUG("");
	}
}

static inline void check_policy_written(FILE * fp, const int id)
{
	fflush(fp);
	check_policy_common(1, id);
}

static inline void check_policy_deleted(FILE * fp, const int id)
{
	fflush(fp);
	check_policy_common(0, id);
}

static const char *domain_testcases[] = {
	"allow_create /tmp/mknod_reg_test 0600",
	"allow_create /tmp/open_test 0600",
	"allow_create /tmp/open_test 0600",
	"allow_create /tmp/open_test 0600",
	"allow_execute /bin/true",
	"allow_execute /bin/true",
	"allow_execute /bin/true0",
	"allow_execute /bin/true1",
	"allow_execute /bin/true2",
	"allow_execute /bin/true3",
	"allow_execute /bin/true4",
	"allow_execute /bin/true5",
	"allow_execute /bin/true6",
	"allow_execute /bin/true7",
	"allow_execute /bin/true7",
	"allow_execute /bin/true7",
	"allow_execute /bin/true8",
	"allow_ioctl socket:[family=2:type=2:protocol=17] 0-35122",
	"allow_ioctl socket:[family=2:type=2:protocol=17] 35122-35124",
	"allow_link /tmp/link_source_test /tmp/link_dest_test",
	"allow_mkblock /tmp/mknod_blk_test 0600 1 0",
	"allow_mkchar /tmp/mknod_chr_test 0600 1 3",
	"allow_mkdir /tmp/mkdir_test/ 0755",
	"allow_mkfifo /tmp/mknod_fifo_test 0600",
	"allow_mkfifo /tmp/mknod_fifo_test 0600",
	"allow_mksock /tmp/mknod_sock_test 0600",
	"allow_mksock /tmp/socket_test 0600",
	"allow_read /bin/true",
	"allow_read /bin/true",
	"allow_read /dev/null",
	"allow_read /dev/null",
	"allow_read /dev/null",
	"allow_read /dev/null",
	"allow_read /dev/null",
	"allow_read /dev/null",
	"allow_read /foo",
	"allow_read /proc/sys/net/ipv4/ip_local_port_range",
	"allow_read /proc/sys/net/ipv4/ip_local_port_range",
	"allow_read/write /bar",
	"allow_read/write /dev/null",
	"allow_read/write /dev/null",
	"allow_read/write /proc/sys/net/ipv4/ip_local_port_range",
	"allow_read/write /proc/sys/net/ipv4/ip_local_port_range",
	"allow_read/write /tmp/fifo",
	"allow_read/write /tmp/fifo",
	"allow_read/write /tmp/rewrite_test",
	"allow_rename /tmp/rename_source_test /tmp/rename_dest_test",
	"allow_rmdir /tmp/rmdir_test/",
	"allow_symlink /symlink",
	"allow_symlink /symlink",
	"allow_symlink /symlink",
	"allow_symlink /symlink",
	"allow_symlink /tmp/symlink_source_test",
	"allow_symlink /tmp/symlink_source_test",
	"allow_symlink /tmp/symlink_source_test",
	"allow_symlink /tmp/symlink_source_test",
	"allow_symlink /tmp/symlink_source_test",
	"allow_truncate /tmp/rewrite_test",
	"allow_truncate /tmp/truncate_test",
	"allow_truncate /tmp/truncate_test",
	"allow_unlink /tmp/unlink_test",
	"allow_write /123",
	"allow_write /dev/null",
	"allow_write /dev/null",
	"allow_write /devfile",
	"allow_write /devfile",
	"allow_write /proc/sys/net/ipv4/ip_local_port_range",
	"allow_write /proc/sys/net/ipv4/ip_local_port_range",
	"allow_write /tmp/open_test",
	"allow_write /tmp/open_test",
	"allow_write /tmp/open_test",
	"allow_write /tmp/truncate_test",
	"allow_write /tmp/truncate_test",
	"allow_rewrite /tmp/rewrite_test",
	"allow_rewrite /tmp/rewrite_test",
	"allow_mount /dev/sda1 /mnt/sda1/ ext3 0x123",
	"allow_mount /dev/sda1 /mnt/sda1/ ext3 123",
	"allow_mount /dev/sda1 /mnt/sda1/ ext3 0123",
	"allow_mount /dev/sda1 /mnt/sda1/ ext3 0x123",
	"allow_mount /dev/sda1 /mnt/sda1/ ext3 123",
	"allow_mount /dev/sda1 /mnt/sda1/ ext3 0123",
	"allow_chroot /",
	"allow_chroot /",
	"allow_chroot /mnt/",
	"allow_pivot_root / /proc/",
	"allow_pivot_root /mnt/ /proc/mnt/",
	"allow_unmount /",
	"allow_unmount /proc/",
	NULL
};

static void domain_policy_test(const unsigned int before)
{
	unsigned int after;
	int j;
	policy_file = "/sys/kernel/security/tomoyo/domain_policy";
	for (j = 0; domain_testcases[j]; j++) {
		int i;
		FILE *fp = fopen(policy_file, "w");
		if (!fp)
			BUG("BUG: Policy write error\n");
		fprintf(fp, "<kernel>\n");
		policy = domain_testcases[j];
		printf("Processing: %s\n", policy);
		for (i = 0; i < 100; i++) {
			fprintf(fp, "%s\n", policy);
			if (!i)
				check_policy_written(fp, 1);
			fprintf(fp, "delete %s\n", policy);
		}
		check_policy_deleted(fp, 1);
		for (i = 0; i < 100; i++)
			fprintf(fp, "%s\n", policy);
		check_policy_written(fp, 2);
		fprintf(fp, "delete %s\n", policy);
		check_policy_deleted(fp, 2);
		fclose(fp);
		for (i = 0; i < 30; i++) {
			usleep(100000);
			get_meminfo(&after);
			if (before == after)
				break;
		}
		if (before != after) {
			printf("Policy: %d\n", after - before);
			BUG("Policy read/write test: Fail\n");
		}
	}
	for (j = 0; j < 10; j++) {
		int i;
		FILE *fp = fopen(policy_file, "w");
		if (!fp)
			BUG("BUG: Policy write error\n");
		fprintf(fp, "<kernel> /sbin/init\n");
		for (i = 0; domain_testcases[i]; i++)
			fprintf(fp, "%s\n", domain_testcases[i]);
		fprintf(fp, "delete <kernel> /sbin/init\n");
		fclose(fp);
		for (i = 0; i < 50; i++) {
			usleep(100000);
			get_meminfo(&after);
			if (before == after)
				break;
		}
		if (before != after) {
			printf("Policy: %d\n", after - before);
			BUG("Policy read/write test: Fail\n");
		}
	}
}

static const char *exception_testcases[] = {
	"allow_read /tmp/mknod_reg_test",
	"allow_env HOME",
	"path_group PG1 /",
	"path_group PG2 /",
	"address_group AG3 0.0.0.0",
	"address_group AG3 1.2.3.4-5.6.7.8",
	"address_group AG3 f:ee:ddd:cccc:b:aa:999:8888",
	"address_group AG4 0:1:2:3:4:5:6:7-8:90:a00:b000:c00:d0:e:f000",
	"number_group NG1 1000",
	"number_group NG2 10-0x100000",
	"number_group NG3 01234567-0xABCDEF89",
	"deny_autobind 1024",
	"deny_autobind 32668-65535",
	"deny_autobind 0-1023",
	"initialize_domain /usr/sbin/sshd",
	"no_initialize_domain /usr/sbin/sshd",
	"initialize_domain /usr/sbin/sshd from /bin/bash",
	"no_initialize_domain /usr/sbin/sshd from /bin/bash",
	"initialize_domain /usr/sbin/sshd from "
	    "<kernel> /bin/mingetty/bin/bash",
	"no_initialize_domain /usr/sbin/sshd from "
	    "<kernel> /bin/mingetty/bin/bash",
	"keep_domain <kernel> /usr/sbin/sshd /bin/bash",
	"no_keep_domain <kernel> /usr/sbin/sshd /bin/bash",
	"keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash",
	"no_keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash",
	"keep_domain /bin/pwd from /bin/bash",
	"no_keep_domain /bin/pwd from /bin/bash",
	"file_pattern /proc/\\$/task/\\$/environ",
	"file_pattern /proc/\\$/task/\\$/auxv",
	"allow_read /etc/ld.so.cache",
	"allow_read /proc/meminfo",
	"allow_read /proc/sys/kernel/version",
	"allow_read /etc/localtime",
	"allow_read /proc/self/task/\\$/attr/current",
	"allow_read /proc/self/task/\\$/oom_score",
	"allow_read /proc/self/wchan",
	"allow_read /lib/ld-2.5.so",
	"file_pattern pipe:[\\$]",
	"file_pattern socket:[\\$]",
	"file_pattern /var/cache/logwatch/logwatch.\\*/",
	"file_pattern /var/cache/logwatch/logwatch.\\*/\\*",
	"deny_rewrite /var/log/\\*",
	"deny_rewrite /var/log/\\*/\\*",
	"aggregator /etc/rc.d/rc\\?.d/\\?\\+\\+smb /etc/rc.d/init.d/smb",
	"aggregator /etc/rc.d/rc\\?.d/\\?\\+\\+crond /etc/rc.d/init.d/crond",
	NULL
};

static void exception_policy_test(const unsigned int before)
{
	unsigned int after;
	int j;
	policy_file = "/sys/kernel/security/tomoyo/exception_policy";
	for (j = 0; exception_testcases[j]; j++) {
		int i;
		FILE *fp = fopen(policy_file, "w");
		if (!fp)
			BUG("BUG: Policy write error\n");
		policy = exception_testcases[j];
		printf("Processing: %s\n", policy);
		for (i = 0; i < 100; i++) {
			fprintf(fp, "%s\n", policy);
			if (!i)
				check_policy_written(fp, 1);
			fprintf(fp, "delete %s\n", policy);
		}
		check_policy_deleted(fp, 1);
		for (i = 0; i < 100; i++)
			fprintf(fp, "%s\n", policy);
		check_policy_written(fp, 2);
		fprintf(fp, "delete %s\n", policy);
		check_policy_deleted(fp, 2);
		fclose(fp);
		for (i = 0; i < 30; i++) {
			usleep(100000);
			get_meminfo(&after);
			if (before == after)
				break;
		}
		if (before != after) {
			printf("Policy: %d\n", after - before);
			BUG("Policy read/write test: Fail\n");
		}
	}
	for (j = 0; j < 10; j++) {
		int i;
		FILE *fp = fopen(policy_file, "w");
		if (!fp)
			BUG("BUG: Policy write error\n");
		for (i = 0; exception_testcases[i]; i++)
			fprintf(fp, "%s\n", exception_testcases[i]);
		for (i = 0; exception_testcases[i]; i++)
			fprintf(fp, "delete %s\n", exception_testcases[i]);
		fclose(fp);
		for (i = 0; i < 50; i++) {
			usleep(100000);
			get_meminfo(&after);
			if (before == after)
				break;
		}
		if (before != after) {
			printf("Policy: %d\n", after - before);
			BUG("Policy read/write test: Fail\n");
		}
	}
}

int main(void)
{
	unsigned int before;
	mount("/proc", "/proc/", "proc", 0, NULL);
	get_meminfo(&before);
	domain_policy_test(before);
	exception_policy_test(before);
	BUG("Policy read/write test: Success\n");
	return 0;
}
