
/* Authors: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
 *
 * Copyright (C) 2003 Tresys Technology, LLC
 *	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, version 2.
 */

/* 
 * displaypol.c
 *
 * Test program to the contents of a binary policy in text
 * form.  This program currently only displays the
 * avtab (including conditional avtab) rules.
 *
 * 	displaypol binary_pol_file
 */

#include <sepol/policydb/policydb.h>
#include <sepol/policydb/avtab.h>
#include <sepol/policydb/services.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/expand.h>
#include <sepol/policydb/util.h>
#include <sepol/policydb/polcaps.h>
#include <getopt.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>

const char * dom_priv_filename = "se_domain_privilege_data.P";
const char * typetrans_filename = "se_typetrans_data.P";
const char * file_creation_filename = "se_file_creation_data.P";

FILE * dom_priv_out;
FILE * typetrans_out;
FILE * file_creation_out;

static policydb_t policydb;

void usage(char *progname)
{
	printf("usage:  %s binary_pol_file\n\n", progname);
	exit(1);
}

int render_access_mask(uint32_t mask, avtab_key_t * key, policydb_t * p,
		       char **perm)
{
	*perm = sepol_av_to_string(p, key->target_class, mask);
	return 0;
}

int render_type(uint32_t type, policydb_t * p, char **type_str)
{
	*type_str = p->p_type_val_to_name[type - 1];
	return 0;
}

int render_key(avtab_key_t * key, policydb_t * p, 
				char **stype, char **ttype, char **tclass)
{
	*stype = p->p_type_val_to_name[key->source_type - 1];
	*ttype = p->p_type_val_to_name[key->target_type - 1];
	*tclass = p->p_class_val_to_name[key->target_class - 1];
//	if (stype && ttype)
//		fprintf(fp, "%s %s : %s ", stype, ttype, tclass);
//	else if (stype)
//		fprintf(fp, "%s %u : %s ", stype, key->target_type, tclass);
//	else if (ttype)
//		fprintf(fp, "%u %s : %s ", key->source_type, ttype, tclass);
//	else
//		fprintf(fp, "%u %u : %s ", key->source_type, key->target_type,
//			tclass);
	return 0;
}

// dom_priv(stype, ttype, tclass, [p1,p2...])

void output_perm_list(char *perm)
{
	size_t i;

	fprintf(dom_priv_out, "['");
	for (i = 1; i < strlen(perm); ++i) {
		if (perm[i] == ' ')
			fprintf(dom_priv_out, "', '");
		else
			fprintf(dom_priv_out, "%c", perm[i]);
	}
	fprintf(dom_priv_out, "']");
}

void output_dom_priv(char *stype, char *ttype, char *tclass, char *perm)
{
	if (!stype || !ttype || !tclass || !perm)
		return;

	fprintf(dom_priv_out, "dom_priv('%s', '%s', '%s', ", stype, ttype, tclass);
	output_perm_list(perm);
	fprintf(dom_priv_out, ").\n");
}

void output_typetrans(char *stype, char *ttype, char *type_str)
{
	fprintf(typetrans_out,
				"se_typetrans(old_dom('%s'), new_dom('%s'), type('%s')).\n",
				stype, type_str, ttype);
}

void output_file_creation(char *stype, char *ttype, char *tclass, char *type_str)
{
	fprintf(file_creation_out,
				"se_file_creation(domain('%s'), folder_type('%s'), "
				"newfile_class('%s'), newfile_type('%s')).\n",
				stype, ttype, tclass, type_str);
}

/* 'what' values for this function */
#define	RENDER_UNCONDITIONAL	0x0001	/* render all regardless of enabled state */
#define RENDER_ENABLED		0x0002
#define RENDER_DISABLED		0x0004
#define RENDER_CONDITIONAL	(RENDER_ENABLED|RENDER_DISABLED)

int render_av_rule(avtab_key_t * key, avtab_datum_t * datum, uint32_t what,
		   policydb_t * p, FILE * fp)
{
	char *stype, *ttype, *tclass;
	char *perm;
	char *type_str;
//	fprintf(fp, "what = 0x%04u specified = 0x%04hx\n", what, key->specified);

	if (!(what & RENDER_UNCONDITIONAL)) {
		if (what != RENDER_CONDITIONAL && (((what & RENDER_ENABLED)
						    && !(key->
							 specified &
							 AVTAB_ENABLED))
						   || ((what & RENDER_DISABLED)
						       && (key->
							   specified &
							   AVTAB_ENABLED)))) {
			return 0;	/* doesn't match selection criteria */
		}
	}

/*
	if (!(what & RENDER_UNCONDITIONAL)) {
		if (key->specified & AVTAB_ENABLED)
			fprintf(fp, "[enabled] ");
		else if (!(key->specified & AVTAB_ENABLED))
			fprintf(fp, "[disabled] ");
	}
*/

	if (key->specified & AVTAB_AV) {
		if (key->specified & AVTAB_ALLOWED) {
			render_key(key, p, &stype, &ttype, &tclass);
			render_access_mask(datum->data, key, p, &perm);
			output_dom_priv(stype, ttype, tclass, perm);
		}
		if (key->specified & AVTAB_AUDITALLOW) {
			render_key(key, p, &stype, &ttype, &tclass);
			render_access_mask(datum->data, key, p, &perm);
			output_dom_priv(stype, ttype, tclass, perm);
		}
		if (key->specified & AVTAB_AUDITDENY) {
//			fprintf(fp, "dontaudit ");
//			render_key(key, p, fp);
//			/* We inverse the mask for dontaudit since the mask is internally stored
//			 * as a auditdeny mask */
//			render_access_mask(~datum->data, key, p, fp);
//			fprintf(fp, ";\n");
		}
	} else if (key->specified & AVTAB_TYPE) {
		if (key->specified & AVTAB_TRANSITION) {
			render_key(key, p, &stype, &ttype, &tclass);
			render_type(datum->data, p, &type_str);
			if (strcmp(tclass, "process") == 0)
				output_typetrans(stype, ttype, type_str);
			if (strcmp(tclass, "file") == 0 || strcmp(tclass, "dir") == 0)
				output_file_creation(stype, ttype, tclass, type_str);
		}
		if (key->specified & AVTAB_MEMBER) {
//			fprintf(fp, "type_member ");
//			render_key(key, p, fp);
//			render_type(datum->data, p, fp);
//			fprintf(fp, ";\n");
		}
		if (key->specified & AVTAB_CHANGE) {
//			fprintf(fp, "type_change ");
//			render_key(key, p, fp);
//			render_type(datum->data, p, fp);
//			fprintf(fp, ";\n");
		}
	} else {
		fprintf(fp, "     ERROR: no valid rule type specified\n");
		return -1;
	}
	return 0;
}

int display_avtab(avtab_t * a, uint32_t what, policydb_t * p, FILE * fp)
{
	int i;
	avtab_ptr_t cur;
	avtab_t expa;

	if (avtab_init(&expa))
		goto oom;
	if (expand_avtab(p, a, &expa)) {
		avtab_destroy(&expa);
		goto oom;
	}

	/* hmm...should have used avtab_map. */
	for (i = 0; i < expa.nslot; i++) {
		for (cur = expa.htable[i]; cur; cur = cur->next) {
			render_av_rule(&cur->key, &cur->datum, what, p, fp);
		}
	}
	avtab_destroy(&expa);
	fprintf(fp, "\n");
	return 0;
      oom:
	fprintf(stderr, "out of memory\n");
	return 1;
}

int display_bools(policydb_t * p, FILE * fp)
{
	int i;

	for (i = 0; i < p->p_bools.nprim; i++) {
		fprintf(fp, "%s : %d\n", p->p_bool_val_to_name[i],
			p->bool_val_to_struct[i]->state);
	}
	return 0;
}

void display_expr(policydb_t * p, cond_expr_t * exp, FILE * fp)
{

	cond_expr_t *cur;
	for (cur = exp; cur != NULL; cur = cur->next) {
		switch (cur->expr_type) {
		case COND_BOOL:
			fprintf(fp, "%s ",
				p->p_bool_val_to_name[cur->bool - 1]);
			break;
		case COND_NOT:
			fprintf(fp, "! ");
			break;
		case COND_OR:
			fprintf(fp, "|| ");
			break;
		case COND_AND:
			fprintf(fp, "&& ");
			break;
		case COND_XOR:
			fprintf(fp, "^ ");
			break;
		case COND_EQ:
			fprintf(fp, "== ");
			break;
		case COND_NEQ:
			fprintf(fp, "!= ");
			break;
		default:
			fprintf(fp, "error!");
			break;
		}
	}
}

int display_cond_expressions(policydb_t * p, FILE * fp)
{
	cond_node_t *cur;
	cond_av_list_t *av_cur, *expl = NULL;
	avtab_t expa;

	for (cur = p->cond_list; cur != NULL; cur = cur->next) {
		fprintf(fp, "expression: ");
		display_expr(p, cur->expr, fp);
		fprintf(fp, "current state: %d\n", cur->cur_state);
		fprintf(fp, "True list:\n");
		if (avtab_init(&expa))
			goto oom;
		if (expand_cond_av_list(p, cur->true_list, &expl, &expa)) {
			avtab_destroy(&expa);
			goto oom;
		}
		for (av_cur = expl; av_cur != NULL; av_cur = av_cur->next) {
			fprintf(fp, "\t");
			render_av_rule(&av_cur->node->key, &av_cur->node->datum,
				       RENDER_CONDITIONAL, p, fp);
		}
		cond_av_list_destroy(expl);
		avtab_destroy(&expa);
		fprintf(fp, "False list:\n");
		if (avtab_init(&expa))
			goto oom;
		if (expand_cond_av_list(p, cur->false_list, &expl, &expa)) {
			avtab_destroy(&expa);
			goto oom;
		}
		for (av_cur = expl; av_cur != NULL; av_cur = av_cur->next) {
			fprintf(fp, "\t");
			render_av_rule(&av_cur->node->key, &av_cur->node->datum,
				       RENDER_CONDITIONAL, p, fp);
		}
		cond_av_list_destroy(expl);
		avtab_destroy(&expa);
	}
	return 0;

      oom:
	fprintf(stderr, "out of memory\n");
	return 1;
}

int display_handle_unknown(policydb_t * p, FILE * out_fp)
{
	if (p->handle_unknown == ALLOW_UNKNOWN)
		fprintf(out_fp, "Allow unknown classes and permisions\n");
	else if (p->handle_unknown == DENY_UNKNOWN)
		fprintf(out_fp, "Deny unknown classes and permisions\n");
	else if (p->handle_unknown == REJECT_UNKNOWN)
		fprintf(out_fp, "Reject unknown classes and permisions\n");
	return 0;
}

int change_bool(char *name, int state, policydb_t * p, FILE * fp)
{
	cond_bool_datum_t *bool;

	bool = hashtab_search(p->p_bools.table, name);
	if (bool == NULL) {
		fprintf(fp, "Could not find bool %s\n", name);
		return -1;
	}
	bool->state = state;
	evaluate_conds(p);
	return 0;
}

int menu()
{
	printf("\nSelect a command:\n");
	printf("1)  display unconditional AVTAB\n");
	printf("2)  display conditional AVTAB (entirely)\n");
	printf("3)  display conditional AVTAG (only ENABLED rules)\n");
	printf("4)  display conditional AVTAB (only DISABLED rules)\n");
	printf("5)  display conditional bools\n");
	printf("6)  display conditional expressions\n");
	printf("7)  change a boolean value\n");
	printf("\n");
	printf("c)  display policy capabilities\n");
	printf("u)  display unknown handling setting\n");
	printf("f)  set output file\n");
	printf("m)  display menu\n");
	printf("q)  quit\n");
	return 0;
}

int main(int argc, char **argv)
{
	FILE *out_fp = stdout;
	int fd, ret;
	struct stat sb;
	void *map;
	struct policy_file pf;

	if (argc != 2)
		usage(argv[0]);

	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open '%s':  %s\n",
			argv[1], strerror(errno));
		exit(1);
	}
	if (fstat(fd, &sb) < 0) {
		fprintf(stderr, "Can't stat '%s':  %s\n",
			argv[1], strerror(errno));
		exit(1);
	}
	map =
	    mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
	if (map == MAP_FAILED) {
		fprintf(stderr, "Can't map '%s':  %s\n",
			argv[1], strerror(errno));
		exit(1);
	}

	/* read the binary policy */
	fprintf(out_fp, "Reading policy...\n");
	policy_file_init(&pf);
	pf.type = PF_USE_MEMORY;
	pf.data = map;
	pf.len = sb.st_size;
	if (policydb_init(&policydb)) {
		fprintf(stderr, "%s:  Out of memory!\n", argv[0]);
		exit(1);
	}
	ret = policydb_read(&policydb, &pf, 1);
	if (ret) {
		fprintf(stderr,
			"%s:  error(s) encountered while parsing configuration\n",
			argv[0]);
		exit(1);
	}

	fprintf(stdout, "binary policy file loaded\n\n");
	close(fd);

	dom_priv_out = fopen(dom_priv_filename, "w");
	assert(dom_priv_out);
	typetrans_out = fopen(typetrans_filename, "w");
	assert(typetrans_out);
	file_creation_out = fopen(file_creation_filename, "w");
	assert(file_creation_out);

	display_avtab(&policydb.te_avtab, RENDER_UNCONDITIONAL, &policydb, out_fp);
	display_avtab(&policydb.te_cond_avtab, RENDER_ENABLED, &policydb, out_fp);

	fclose(dom_priv_out);
	fclose(typetrans_out);
	fclose(file_creation_out);

	return 0;
}

/* FLASK */
