/*
 *   Copyright (c) 2010 - 2012
 *   Canonical Ltd. (All rights reserved)
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of version 2 of the GNU General Public
 *   License published by the Free Software Foundation.
 *
 *   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, contact Novell, Inc. or Canonical,
 *   Ltd.
 */
#include <iostream>
#include <stdlib.h>
#include <stdarg.h>

#include "parser.h"
#include "file_cache.h"

/* Policy versioning is determined by a combination of 3 values:
 * policy_version:     version of txt policy
 * parser_abi_version: version of abi revision of policy generated by parser
 * kernel_abi_version: version of abi revision for the kernel
 *
 * The version info is stored in a single 32 bit version field in the
 * header portion of each binary policy file.
 *
 * policy_version:
 *   a gross revision number indicating what features and semantics are
 *   expected by the text policy. This does not necessarily map directly
 *   to a feature set as a kernel may not have all the supported features
 *   patched/builtin.
 *
 *   policy_version is not supported by kernels that only support v5
 *   kernel abi, so it will not be written when creating policy for
 *   those kernels.
 *
 * kernel_abi_version:
 *   should be set to the highest version supported by both the parser and
 *   the kernel.
 *   This allows new kernels to detect old userspaces, and new parsers
 *   to support old kernels and policies semantics.
 *
 * parser_abi_version:
 *   should be bumped when a compiler error or some other event happens
 *   and policy cache needs to be forced to be recomputed, when the
 *   policy_version or kernel version has not changed.
 *
 *   parser_abi_version is not supported by kernels that only support
 *   v5 kernel abi so it will not be written when creating policy for those
 *   kernels.
 *
 * Default values set to v5 kernel abi before the different versioning
 * numbers where supported.
 */
uint32_t policy_version = 2;
uint32_t parser_abi_version = 2;
uint32_t kernel_abi_version = 5;

int force_complain = 0;
int perms_create = 0;                   /* perms contain create flag */
int net_af_max_override = -1;           /* use kernel to determine af_max */
int kernel_load = 1;
int kernel_supports_setload = 0;	/* kernel supports atomic set loads */
int features_supports_network = 0;	/* kernel supports network rules */
int features_supports_networkv8 = 0;	/* kernel supports 4.17 network rules */
int features_supports_inet = 0; 	/* kernel supports inet network rules */
int features_supports_unix = 0;		/* kernel supports unix socket rules */
int kernel_supports_policydb = 0;	/* kernel supports new policydb */
int features_supports_mount = 0;	/* kernel supports mount rules */
bool features_supports_detached_mount = false;
int features_supports_dbus = 0;		/* kernel supports dbus rules */
int kernel_supports_diff_encode = 0;	/* kernel supports diff_encode */
int features_supports_signal = 0;	/* kernel supports signal rules */
int features_supports_ptrace = 0;	/* kernel supports ptrace rules */
int features_supports_stacking = 0;	/* kernel supports stacking */
int features_supports_domain_xattr = 0;	/* x attachment cond */
int features_supports_userns = 0;	/* kernel supports user namespace */
int features_supports_posix_mqueue = 0;	/* kernel supports mqueue rules */
int features_supports_sysv_mqueue = 0;	/* kernel supports mqueue rules */
int features_supports_io_uring = 0;	/* kernel supports io_uring rules */
int features_supports_flag_interruptible = 0;
int features_supports_flag_signal = 0;
int features_supports_flag_error = 0;
int kernel_supports_oob = 0;		/* out of band transitions */
int kernel_supports_promptdev = 0;	/* prompt via audit perms */
int kernel_supports_permstable32 = 0;	/* extended permissions */
int kernel_supports_permstable32_version = -1;	/* extended permissions (-1 absent, 1..n version) */
int prompt_compat_mode = PROMPT_COMPAT_UNKNOWN;
int kernel_supports_state32 = 0;	/* 32 bit state table entries */
int kernel_supports_flags_table = 0;	/* state flags stored in table */
int conf_verbose = 0;
int conf_quiet = 0;
int names_only = 0;
int current_lineno = 1;
int option = OPTION_ADD;


const char *progname = __FILE__;
char *profile_ns = NULL;
char *profilename = NULL;
char *current_filename = NULL;

FILE *ofile = NULL;

IncludeCache_t *g_includecache;

optflags parseopts = {
	.control = (optflags_t)(CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE | CONTROL_DFA_MINIMIZE | CONTROL_DFA_DIFF_ENCODE | CONTROL_RULE_MERGE |
				/* TODO: remove when we have better auto
				 * selection on when/which explicit denies
				 * to remove
				 */
				CONTROL_DFA_FILTER_DENY),
	.dump = 0,
	.warn = DEFAULT_WARNINGS,
	.Werror = 0
};


#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
int read_implies_exec = 0;
#endif

void pwarnf(bool werr, const char *fmt, ...)
{
        va_list arg;
        char *newfmt;

        if (conf_quiet || names_only || option == OPTION_REMOVE)
                return;

        if (asprintf(&newfmt, _("%s from %s (%s%sline %d): %s"),
		     werr ? _("Warning converted to Error") : _("Warning"),
		     profilename ? profilename : "stdin",
		     current_filename ? current_filename : "",
		     current_filename ? " " : "",
		     current_lineno,
		     fmt) == -1)
                return;

        va_start(arg, fmt);
        vfprintf(stderr, newfmt, arg);
        va_end(arg);

        free(newfmt);

	if (werr) {
		fflush(stderr);
		exit(1);
	}
}

/* do we want to warn once/profile or just once per compile?? */
void common_warn_once(const char *name, const char *msg, const char **warned_name)
{
	if ((parseopts.warn & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
		if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
			cerr << "Warning converted to Error";
		else
			cerr << "Warning";
		cerr << " from profile " << name << " (";
		if (current_filename)
			cerr << current_filename;
		else
			cerr << "stdin";
		cerr << "): " << msg << "\n";
		*warned_name = name;
	}

	if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
		exit(1);
}

bool prompt_compat_mode_supported(int mode)
{
	if (mode == PROMPT_COMPAT_PERMSV2 &&
	    kernel_supports_permstable32_version >= 2)
		return true;
	/*
	else if (mode == PROMPT_COMPAT_DEV &&
		 kernel_supports_promptdev)
		return true;
	*/
	else if (mode == PROMPT_COMPAT_FLAG &&
		 kernel_supports_permstable32)
		return true;
	/*
	else if (mode == PROMPT_COMPAT_PERMSV1 &&
		 (kernel_supports_permstable32_v1))
		return true;
	*/
	else if (mode == PROMPT_COMPAT_IGNORE)
		return true;

	return false;
}

int default_prompt_compat_mode()
{
	if (prompt_compat_mode_supported(PROMPT_COMPAT_PERMSV2))
		return PROMPT_COMPAT_PERMSV2;
	if (prompt_compat_mode_supported(PROMPT_COMPAT_DEV))
		return PROMPT_COMPAT_DEV;
	if (prompt_compat_mode_supported(PROMPT_COMPAT_FLAG))
		return PROMPT_COMPAT_FLAG;
	if (prompt_compat_mode_supported(PROMPT_COMPAT_PERMSV1))
		return PROMPT_COMPAT_PERMSV1;
	if (prompt_compat_mode_supported(PROMPT_COMPAT_IGNORE))
		return PROMPT_COMPAT_IGNORE;
	return PROMPT_COMPAT_IGNORE;
}

void print_prompt_compat_mode(FILE *f)
{
	switch (prompt_compat_mode) {
	case PROMPT_COMPAT_IGNORE:
		fprintf(f, "ignore");
		break;
	case PROMPT_COMPAT_FLAG:
		fprintf(f, "flag");
		break;
	case PROMPT_COMPAT_PERMSV2:
		fprintf(f, "permsv2");
		break;
	case PROMPT_COMPAT_PERMSV1:
		fprintf(f, "permsv1");
		break;
	case PROMPT_COMPAT_DEV:
		fprintf(stderr, "dev");
		break;
	default:
		fprintf(f, "Unknown prompt compat mode '%d'", prompt_compat_mode);
	}
}
