/*
 *   Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
 *   NOVELL (All rights reserved)
 *
 *   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.
 */

#ifndef __AA_PARSER_H
#define __AA_PARSER_H


#include <endian.h>
#include <stdarg.h>
#include <string.h>
#include <sys/resource.h>

#include <libintl.h>
#define _(s) gettext(s)

#include <sys/apparmor.h>

#include "file_cache.h"
#include "immunix.h"
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
#include "rule.h"
#include "common_flags.h"

#include <string>
#include <zstd.h>

#include <set>


#define MODULE_NAME "apparmor"

/* Global variable to pass token to lexer.  Will be replaced by parameter
 * when lexer and parser are made reentrant
 */
extern int parser_token;


#define WARN_RULE_NOT_ENFORCED	0x1
#define WARN_RULE_DOWNGRADED	0x2
#define WARN_ABI		0x4
#define WARN_DEPRECATED		0x8
#define WARN_CONFIG		0x10
#define WARN_CACHE		0x20
#define WARN_DEBUG_CACHE	0x40
#define WARN_JOBS		0x80
#define WARN_DANGEROUS		0x100
#define WARN_UNEXPECTED		0x200
#define WARN_FORMAT		0x400
#define WARN_MISSING		0x800
#define WARN_OVERRIDE		0x1000
#define WARN_INCLUDE		0x2000

#define WARN_DEV (WARN_RULE_NOT_ENFORCED | WARN_RULE_DOWNGRADED | WARN_ABI | \
		  WARN_DEPRECATED | WARN_DANGEROUS | WARN_UNEXPECTED | \
		  WARN_FORMAT | WARN_MISSING | WARN_OVERRIDE | \
		  WARN_DEBUG_CACHE | WARN_INCLUDE)

#define DEFAULT_WARNINGS (WARN_CONFIG | WARN_CACHE | WARN_JOBS | \
			  WARN_UNEXPECTED | WARN_OVERRIDE)

#define WARN_ALL (WARN_RULE_NOT_ENFORCED | WARN_RULE_DOWNGRADED | WARN_ABI | \
		  WARN_DEPRECATED | WARN_CONFIG | WARN_CACHE | \
		  WARN_DEBUG_CACHE | WARN_JOBS | WARN_DANGEROUS | \
		  WARN_UNEXPECTED | WARN_FORMAT | WARN_MISSING | \
		  WARN_OVERRIDE | WARN_INCLUDE)

typedef enum {
	ZSTD_COMPRESS_NONE,
	ZSTD_COMPRESS_POLICY,
	ZSTD_COMPRESS_PRECOMPRESSED,
	ZSTD_COMPRESS_GUESS,
	ZSTD_COMPRESS_RECOMPRESS,
} zstd_compress_t;

// ZSTD_CLEVEL_DEFAULT value is a bit low, use a prechosen value for AppArmor
// as the default
#define ZSTD_DISABLED			0
#define ZSTD_LEVEL_UNSPECIFIED		-1
#define ZSTD_COMPRESS_DEFAULT_VALUE	10
#define ZSTD_COMPRESS_FAST_VALUE	3
#define ZSTD_COMPRESS_HIGH_VALUE	20
#define ZSTD_COMPRESS_MIN_VALUE		ZSTD_minCLevel()
#define ZSTD_COMPRESS_MAX_VALUE		ZSTD_maxCLevel()

typedef enum pattern_t pattern_t;


struct cod_pattern {
	char *regex;		// posix regex
};

struct value_list {
	char *value;

	struct value_list *next;
};

int cmp_value_list(value_list *lhs, value_list *rhs);

enum class cond_comp {
	NONE,
	EQ,
	IN,
	SET,
};

struct cond_entry {
	char *name;
	cond_comp comp; /* '=' or 'in' was used in specifying list */
	struct value_list *vals;

	struct cond_entry *next;
};

struct cond_entry_list {
	char *name;

	struct cond_entry *list;
};

struct cod_entry {
	int priority;
	char *name;
	int len;
	int llen;
	union {
		char *link_name;
		char *onexec;
	};
	char *nt_name;
	Profile *prof;		 	/* Special profile defined
					 * just for this executable */
	perm32_t perms;			/* perms is 'or' of AA_* bits */
	audit_t audit;
	rule_mode_t rule_mode;

	bool alias_ignore;		/* ignore for alias processing */

	bool subset;

	pattern_t pattern_type;
	struct cod_pattern pat;

	struct cod_entry *next;
};

struct aa_rlimits {
	unsigned int specified;			/* limits that are set */
	rlim_t limits[RLIMIT_NLIMITS];
};

struct alt_name {
	char *name;
	struct alt_name *next;
};

struct sd_hat {
	char *hat_name;
	unsigned int hat_magic;
};

struct var_string {
	char *prefix;
	char *var;
	char *suffix;
};

#define COD_READ_CHAR 		'r'
#define COD_WRITE_CHAR 		'w'
#define COD_APPEND_CHAR		'a'
#define COD_EXEC_CHAR 		'x'
#define COD_LINK_CHAR 		'l'
#define COD_LOCK_CHAR		'k'
#define COD_MMAP_CHAR		'm'
#define COD_INHERIT_CHAR 	'i'
#define COD_UNCONFINED_CHAR	'U'
#define COD_UNSAFE_UNCONFINED_CHAR	'u'
#define COD_PROFILE_CHAR	'P'
#define COD_UNSAFE_PROFILE_CHAR	'p'
#define COD_LOCAL_CHAR		'C'
#define COD_UNSAFE_LOCAL_CHAR	'c'

#define OPTION_ADD      1
#define OPTION_REMOVE   2
#define OPTION_REPLACE  3
#define OPTION_STDOUT	4
#define OPTION_OFILE	5

extern int preprocess_only;

#define PATH_CHROOT_REL 0x1
#define PATH_NS_REL 0x2
#define PATH_CHROOT_NSATTACH 0x4
#define PATH_CHROOT_NO_ATTACH 0x8
#define PATH_MEDIATE_DELETED 0x10
#define PATH_DELEGATE_DELETED 0x20
#define PATH_ATTACH 0x40
#define PATH_NO_ATTACH 0x80
#define PATH_IPC_ATTACH 0x100


#ifdef DEBUG
#define PDEBUG(fmt, args...)				\
do {							\
	int pdebug_error = errno;			\
	fprintf(stderr, "parser: " fmt, ## args);	\
	errno = pdebug_error;				\
} while (0)
#else
#define PDEBUG(fmt, args...)	/* Do nothing */
#endif
#define NPDEBUG(fmt, args...)	/* Do nothing */

#define PERROR(fmt, args...)			\
do {						\
	int perror_error = errno;		\
	fprintf(stderr, fmt, ## args);		\
	errno = perror_error;			\
} while (0)

#define MIN_PORT 0
#define MAX_PORT 65535

#ifndef aa_unused
#define aa_unused __attribute__ ((unused))
#endif


#define list_first(LIST) (LIST)
#define list_for_each(LIST, ENTRY) \
	for ((ENTRY) = (LIST); (ENTRY); (ENTRY) = (ENTRY)->next)
#define list_for_each_safe(LIST, ENTRY, TMP) \
	for ((ENTRY) = (LIST), (TMP) = (LIST) ? (LIST)->next : NULL; (ENTRY); (ENTRY) = (TMP), (TMP) = (TMP) ? (TMP)->next : NULL)
#define list_last_entry(LIST, ENTRY) \
	for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
#define list_append(LISTA, LISTB)		\
	do {					\
		typeof(LISTA) ___tmp;		\
		list_last_entry((LISTA), ___tmp);\
		___tmp->next = (LISTB);		\
	} while (0)

#define list_len(LIST)		\
({				\
	int len = 0;		\
	typeof(LIST) tmp;		\
	list_for_each((LIST), tmp)	\
		len++;		\
	len;			\
})

#define list_pop(LIST)				\
({						\
	typeof(LIST) _entry = (LIST);		\
	if (LIST) {				\
		(LIST) = (LIST)->next;		\
		_entry->next = NULL;		\
	}					\
	_entry;					\
})

#define list_remove_at(LIST, PREV, ENTRY)			\
	if (PREV)						\
		(PREV)->next = (ENTRY)->next;			\
	if ((ENTRY) == (LIST))					\
		(LIST) = (ENTRY)->next;				\
	(ENTRY)->next = NULL;					\


#define DUP_STRING(orig, new, field, fail_target) \
	do {									\
		(new)->field = ((orig)->field) ? strdup((orig)->field) : NULL;	\
		if (((orig)->field) && !((new)->field))				\
				goto fail_target;				\
	} while (0)


#define u8  unsigned char
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t

#define cpu_to_le16(x) ((u16)(htole16 ((u16) x)))
#define cpu_to_le32(x) ((u32)(htole32 ((u32) x)))
#define cpu_to_le64(x) ((u64)(htole64 ((u64) x)))

/* The encoding for kernal abi > 5 is
 * 28-31: reserved
 * 20-27: policy version
 * 12-19: policy abi version
 * 11:    force complain flag
 * 10:    reserved
 * 0-9:   kernel abi version
 */
#define ENCODE_VERSION(C, P, PABI, KABI)		\
({							\
	u32 version = (KABI) & 0x3ff;			\
	if ((KABI) > 5) {				\
		version |= (C) ? 1 << 11 : 0;		\
		version |= ((PABI) & 0xff) << 12;	\
		version |= ((P) & 0xff) << 20;		\
	}						\
	version;					\
})

/* The parser fills these variable in automatically */
#define PROFILE_NAME_VARIABLE "profile_name"
#define PROFILE_ATTACH_VAR "attach_path"
#define PROFILE_EXEC_VAR "exec_path"

/* from parser_common.c */
extern uint32_t policy_version;
extern uint32_t parser_abi_version;
extern uint32_t kernel_abi_version;

extern aa_features *pinned_features;
extern aa_features *policy_features;
extern aa_features *override_features;
extern aa_features *kernel_features;

extern int force_complain;
extern int perms_create;
extern int net_af_max_override;
extern int kernel_load;
extern int kernel_supports_setload;
extern bool kernel_supports_zstd_load;
extern int features_supports_network;
extern int features_supports_networkv8;
extern bool features_supports_networkv9;
extern bool features_supports_inetv8;
extern bool features_supports_inetv9;
extern bool features_supports_networkv9_skb;
extern bool features_supports_ifacev9_skb;
extern int features_supports_identity_names;
extern int kernel_supports_policydb;
extern int kernel_supports_diff_encode;
extern int features_supports_mount;
extern bool features_supports_detached_mount;
extern int features_supports_dbus;
extern int features_supports_signal;
extern int features_supports_ptrace;
extern bool features_supports_unixv7;
extern bool features_supports_unixv9;
extern int features_supports_stacking;
extern int features_supports_domain_xattr;
extern int features_supports_userns;
extern int features_supports_posix_mqueue;
extern int features_supports_sysv_mqueue;
extern int features_supports_io_uring;
extern int features_supports_flag_interruptible;
extern int features_supports_flag_signal;
extern int features_supports_flag_error;
extern int features_supports_flag_disconnected_ipc;
extern int kernel_supports_oob;
extern int kernel_supports_promptdev;
extern int kernel_supports_permstable32;
extern int kernel_supports_permstable32_v1;
extern int prompt_compat_mode;
extern int kernel_supports_state32;
extern int kernel_supports_flags_table;
extern int conf_verbose;
extern int conf_quiet;
extern int names_only;
extern int option;
extern int current_lineno;
extern const char *progname;
extern char *profilename;
extern char *profile_ns;
extern char *current_filename;
extern FILE *ofile;
extern int read_implies_exec;
extern IncludeCache_t *g_includecache;

extern void common_warn_once(const char *name, const char *msg, const char **warned_name);
bool prompt_compat_mode_supported(int mode);
int default_prompt_compat_mode();
void print_prompt_compat_mode(FILE *f);

void pvwarnf(bool werr, const char *fmt, va_list ap);

static inline void pwarn(unsigned int warn_flag, const char *fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
static inline void pwarn(unsigned int warn_flag, const char *fmt, ...)
{
	if (parseopts.warn & warn_flag) {
		va_list ap;

		va_start(ap, fmt);
		pvwarnf(parseopts.Werror & warn_flag, fmt, ap);
		va_end(ap);
	}
}

/* from parser_main (cannot be used in tst builds) */
extern int force_complain;
extern void display_version(void);
extern int show_cache;
extern int skip_cache;
extern int skip_read_cache;
extern int write_cache;
extern int cond_clear_cache;
extern int force_clear_cache;
extern int create_cache_dir;
extern int preprocess_only;
extern int skip_mode_force;
extern int abort_on_error;
extern int skip_bad_cache_rebuild;
extern int mru_skip_cache;
extern zstd_compress_t zstd_compress_policy;
extern int zstd_compress_level;

/* provided by parser_lex.l (cannot be used in tst builds) */
extern FILE *yyin;
extern void yyrestart(FILE *fp);
extern int yyparse(void);
extern void yyerror(const char *msg, ...) __attribute__((noreturn, format(printf, 1, 2)));
extern int yylex(void);

/* parser_include.c */
extern const char *basedir;

/* parser_regex.c */
#define default_match_pattern "[^\\000]*"
#define anyone_match_pattern "[^\\000]+"

#define glob_default	0
#define glob_null	1
const char *local_name(const char *name);
extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
					 std::string& pcre, int *first_re_pos);
extern bool build_list_val_expr(std::string& buffer, struct value_list *list);
extern bool convert_entry(std::string& buffer, char *entry);
extern int clear_and_convert_entry(std::string& buffer, char *entry);
extern int process_regex(Profile *prof);
extern int post_process_entry(struct cod_entry *entry);

extern int process_policydb(Profile *prof);

extern int process_policy_ents(Profile *prof);
extern void filter_slashes(char *path);
extern const char *local_name(const char *name);

/* parser_variable.c */
int expand_entry_variables(char **name);
extern int process_variables(Profile *prof);

/* parser_misc.c */
extern void warn_uppercase(void);
extern int is_blacklisted(const char *name, const char *path);
extern struct value_list *new_value_list(char *value);
extern void free_value_list(struct value_list *list);
extern void print_value_list(struct value_list *list, std::ostream &os);
extern struct cond_entry *new_cond_entry(char *name, cond_comp comp, struct value_list *list);
extern void move_conditional_value(const char *rulename, char **dst_ptr,
				   struct cond_entry *cond_ent);
extern void free_cond_entry(struct cond_entry *ent);
extern void free_cond_list(struct cond_entry *ents);
extern void free_cond_entry_list(struct cond_entry_list &cond);
extern void print_cond_entry(struct cond_entry *ent);
extern char *processid(const char *string, int len);
extern char *processquoted(const char *string, int len);
extern char *processunquoted(const char *string, int len);
extern int get_keyword_token(const char *keyword);
extern int get_rlimit(const char *name);
extern char *process_var(const char *var);
extern perm32_t parse_perms(const char *permstr);
extern int parse_X_perms(const char *X, int valid, const char *str_perms, perm32_t *perms, int fail);
bool label_contains_ns(const char *label);
bool parse_label(bool *_stack, char **_ns, char **_name,
		 const char *label, bool yyerr);
extern struct cod_entry *new_entry(char *id, perm32_t perms, char *link_id);

/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
extern int str_to_boolean(const char* str);
extern int null_strcmp(const char *s1, const char *s2);
extern bool strcomp (const char *lhs, const char *rhs);
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
extern void free_cod_entries(struct cod_entry *list);
void debug_cod_entries(struct cod_entry *list);
bool check_x_qualifier(struct cod_entry *entry, const char *&error);
bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error);


#define SECONDS_P_MS (1000LL * 1000LL)
long long convert_time_units(long long value, long long base, const char *units);

/* parser_alias.c */
extern int new_alias(const char *from, const char *to);
extern int replace_profile_aliases(Profile *prof);
extern void free_aliases(void);

/* parser_merge.c */
extern int profile_merge_rules(Profile *prof);

/* parser_interface.c */
extern int load_profile(int option, aa_kernel_interface *kernel_interface,
			Profile *prof, int cache_fd);
extern void sd_serialize_profile(std::ostringstream &buf, Profile *prof,
				int flatten);
extern int sd_load_buffer(int option, char *buffer, int size);
extern size_t compress_policy_zstd(const char* raw_data_str, size_t raw_data_size, char** compressed_buffer);
extern size_t recompress_policy_zstd(const char *compressed_data, size_t compressed_size, char **recompressed_buffer);
extern int cache_fd;


/* parser_policy.c */
extern void add_to_list(Profile *profile);
extern void add_hat_to_policy(Profile *policy, Profile *hat);
extern int add_entry_to_x_table(Profile *prof, char *name);
extern void add_entry_to_policy(Profile *policy, struct cod_entry *entry);
extern int post_process_policy(int debug_only);
extern int process_profile_regex(Profile *prof);
extern int process_profile_variables(Profile *prof);
extern int process_profile_policydb(Profile *prof);
extern int post_merge_rules(void);
extern int merge_hat_rules(Profile *prof);
extern Profile *merge_policy(Profile *a, Profile *b);
extern int load_policy(int option, aa_kernel_interface *kernel_interface,
		       int cache_fd);
extern int load_hats(std::ostringstream &buf, Profile *prof);
extern int load_flattened_hats(Profile *prof, int option,
			       aa_kernel_interface *kernel_interface,
			       int cache_fd);
extern void dump_policy_hats(Profile *prof);
extern void dump_policy_names(void);
void dump_policy(void);

void free_policies(void);

/* parser_main.c */
extern void set_supported_features();

/* default_features.c */
extern const char *match_n_abi;
extern const char *match_c_abi;
extern const char *match_cn_abi;
extern const char *default_features_abi;

#endif /** __AA_PARSER_H */
