[apparmor] [PATCH 09/10] From 4a2e9db884d07ecedfda31f0ca3f2f8eb6a5476d Mon Sep 17 00:00:00 2001 From: John Johansen <john.johansen at canonical.com> Date: Sat, 3 Nov 2012 08:19:54 -0700 Subject: [PATCH 09/10] Convert codomain to a class
John Johansen
john.johansen at canonical.com
Fri Jul 26 03:21:29 UTC 2013
On 07/25/2013 07:09 PM, Seth Arnold wrote:
> On Sun, Jul 21, 2013 at 10:32:52PM -0700, John Johansen wrote:
>> Conver the codomain to a class, and the policy lists that store
>> codomains to stl containers instead of glibc twalk.
>>
>> Signed-off-by: John Johansen <john.johansen at canonical.com>
>
> Some comments inline..
>
>> ---
>> parser/Makefile | 7 +-
>> parser/parser.h | 144 +++---------
>> parser/parser_alias.c | 27 ++-
>> parser/parser_interface.c | 95 ++++----
>> parser/parser_lex.l | 1 +
>> parser/parser_merge.c | 32 +--
>> parser/parser_misc.c | 70 +-----
>> parser/parser_policy.c | 578 ++++++++++++----------------------------------
>> parser/parser_regex.c | 130 +++++------
>> parser/parser_symtab.c | 3 +
>> parser/parser_variable.c | 67 +++---
>> parser/parser_yacc.y | 150 ++++++------
>> parser/profile.cc | 89 +++++++
>> parser/profile.h | 241 +++++++++++++++++++
>> parser/unit_test.h | 53 +++++
>> 15 files changed, 813 insertions(+), 874 deletions(-)
>> create mode 100644 parser/profile.cc
>> create mode 100644 parser/profile.h
>> create mode 100644 parser/unit_test.h
>>
>> diff --git a/parser/Makefile b/parser/Makefile
>> index 3c205f1..4deb74b 100644
>> --- a/parser/Makefile
>> +++ b/parser/Makefile
>> @@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
>> SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
>> parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
>> parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
>> - parser_alias.c mount.c lib.c
>> -HDRS = parser.h parser_include.h immunix.h mount.h lib.h
>> + parser_alias.c mount.c lib.c profile.cc
>> +HDRS = parser.h parser_include.h immunix.h mount.h lib.h profile.h
>> TOOLS = apparmor_parser
>>
>> OBJECTS = $(SRCS:.c=.o)
>> @@ -207,6 +207,9 @@ mount.o: mount.c mount.h parser.h immunix.h
>> lib.o: lib.c lib.h parser.h
>> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>>
>> +profile.o: profile.cc profile.h parser.h
>> + $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>> +
>> parser_version.h: Makefile
>> @echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
>> @mv -f .ver $@
>> diff --git a/parser/parser.h b/parser/parser.h
>> index 3642833..724cf04 100644
>> --- a/parser/parser.h
>> +++ b/parser/parser.h
>> @@ -22,12 +22,19 @@
>> #ifndef __AA_PARSER_H
>> #define __AA_PARSER_H
>>
>> +
>> +#include <string.h>
>
> I didn't see any new string functions / methods in this file.
>
hrmm I'll poke at it, its been too long to remember why I did it.
> And for my curiosity, does this get the C string.h? Or does this get
> turned by the compiler into "#include <string>" internally? Enquiring
> minds want to know...
>
Hrmm actually I'm not sure, I'll have to dig into how the stdlib is
handling this.
the goal here isn't whole sale conversion to C++ but gradual conversion
and converting where it makes sense and for consistency.
That is a lot of C style fns will remain for quite a while. Heck even
when I write new C++ it tends to be more C'ish
>> #include <netinet/in.h>
>> #include <sys/resource.h>
>> #include "immunix.h"
>> #include "libapparmor_re/apparmor_re.h"
>> #include "libapparmor_re/aare_rules.h"
>>
>> +using namespace std;
>> +
>> +#include <set>
>
> I also didn't see any new set functions / methods in this file.
>
so I am going to assume this is because I think I originally had
class Profile defined in parser.h before moving it to its own file.
>> +class Profile;
>> +
>> struct mnt_ent;
>>
>> /* Global variable to pass token to lexer. Will be replaced by parameter
>> @@ -43,12 +50,6 @@ struct prefixes {
>> int owner;
>> };
>>
>> -struct flagval {
>> - int hat;
>> - int complain;
>> - int audit;
>> - int path;
>> -};
>>
>> struct named_transition {
>> int present;
>> @@ -79,7 +80,7 @@ struct cod_entry {
>> char *name;
>> char *link_name;
>> char *nt_name;
>> - struct codomain *codomain; /* Special codomain defined
>> + Profile *prof; /* Special profile defined
>> * just for this executable */
>> int mode; /* mode is 'or' of AA_* bits */
>> int audit; /* audit flags for mode */
>> @@ -114,56 +115,6 @@ struct alt_name {
>> struct alt_name *next;
>> };
>>
>> -struct codomain {
>> - char *ns;
>> - char *name; /* codomain name */
>> - char *attachment;
>> - struct alt_name *altnames;
>> - void *xmatch;
>> - size_t xmatch_size;
>> - int xmatch_len;
>> -
>> - /* char *sub_name; */ /* subdomain name or NULL */
>> - /* int default_deny; */ /* TRUE or FALSE */
>> - int local;
>> - int local_mode; /* true if local, not hat */
>> - int local_audit;
>> -
>> - struct codomain *parent;
>> -
>> - struct flagval flags;
>> -
>> - uint64_t capabilities;
>> - uint64_t audit_caps;
>> - uint64_t deny_caps;
>> - uint64_t quiet_caps;
>> -
>> - unsigned int *network_allowed; /* array of type masks
>> - * indexed by AF_FAMILY */
>> - unsigned int *audit_network;
>> - unsigned int *deny_network;
>> - unsigned int *quiet_network;
>> -
>> - struct aa_rlimits rlimits;
>> -
>> - char *exec_table[AA_EXEC_COUNT];
>> - struct cod_entry *entries;
>> - struct mnt_entry *mnt_ents;
>> -
>> - void *hat_table;
>> - //struct codomain *next;
>> -
>> - aare_ruleset_t *dfarules;
>> - int dfarule_count;
>> - void *dfa;
>> - size_t dfa_size;
>> -
>> - aare_ruleset_t *policy_rules;
>> - int policy_rule_count;
>> - void *policy_dfa;
>> - size_t policy_dfa_size;
>> -};
>> -
>> struct sd_hat {
>> char *hat_name;
>> unsigned int hat_magic;
>> @@ -301,16 +252,14 @@ extern int yylex(void);
>> extern char *basedir;
>>
>> /* parser_regex.c */
>> -extern int process_regex(struct codomain *cod);
>> +extern int process_regex(Profile *prof);
>> extern int post_process_entry(struct cod_entry *entry);
>> extern void reset_regex(void);
>>
>> -extern int process_policydb(struct codomain *cod);
>> -
>> -extern int process_policy_ents(struct codomain *cod);
>> +extern int process_policy_ents(Profile *prof);
>>
>> /* parser_variable.c */
>> -extern int process_variables(struct codomain *cod);
>> +extern int process_variables(Profile *prof);
>> extern struct var_string *split_out_var(char *string);
>> extern void free_var_string(struct var_string *var);
>>
>> @@ -340,12 +289,15 @@ extern struct aa_network_entry *network_entry(const char *family,
>> const char *protocol);
>> extern size_t get_af_max(void);
>>
>> -extern void debug_cod_list(struct codomain *list);
>> /* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
>> extern int str_to_boolean(const char* str);
>> extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
>> extern void free_cod_entries(struct cod_entry *list);
>> extern void free_mnt_entries(struct mnt_entry *list);
>> +extern void __debug_capabilities(uint64_t capset, const char *name);
>> +void __debug_network(unsigned int *array, const char *name);
>> +void debug_cod_entries(struct cod_entry *list);
>> +
>>
>> /* parser_symtab.c */
>> struct set_value {;
>> @@ -364,71 +316,41 @@ void free_symtabs(void);
>>
>> /* parser_alias.c */
>> extern int new_alias(const char *from, const char *to);
>> -extern void replace_aliases(struct codomain *cod);
>> +extern int replace_profile_aliases(Profile *prof);
>> extern void free_aliases(void);
>>
>> /* parser_merge.c */
>> -extern int codomain_merge_rules(struct codomain *cod);
>> +extern int profile_merge_rules(Profile *prof);
>>
>> /* parser_interface.c */
>> typedef struct __sdserialize sd_serialize;
>> -extern int load_codomain(int option, struct codomain *cod);
>> -extern int sd_serialize_profile(sd_serialize *p, struct codomain *cod,
>> +extern int load_profile(int option, Profile *prof);
>> +extern int sd_serialize_profile(sd_serialize *p, Profile *prof,
>> int flatten);
>> extern int sd_load_buffer(int option, char *buffer, int size);
>> extern int cache_fd;
>>
>>
>> /* parser_policy.c */
>> -extern void add_to_list(struct codomain *codomain);
>> -extern void add_hat_to_policy(struct codomain *policy, struct codomain *hat);
>> -extern void add_entry_to_policy(struct codomain *policy, struct cod_entry *entry);
>> -extern void post_process_file_entries(struct codomain *cod);
>> -extern void post_process_mnt_entries(struct codomain *cod);
>> +extern void add_to_list(Profile *profile);
>> +extern void add_hat_to_policy(Profile *policy, Profile *hat);
>> +extern void add_entry_to_policy(Profile *policy, struct cod_entry *entry);
>> +extern void post_process_file_entries(Profile *prof);
>> +extern void post_process_mnt_entries(Profile *prof);
>> extern int post_process_policy(int debug_only);
>> -extern int process_hat_regex(struct codomain *cod);
>> -extern int process_hat_variables(struct codomain *cod);
>> -extern int process_hat_policydb(struct codomain *cod);
>> +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(struct codomain *cod);
>> -extern struct codomain *merge_policy(struct codomain *a, struct codomain *b);
>> +extern int merge_hat_rules(Profile *prof);
>> +extern Profile *merge_policy(Profile *a, Profile *b);
>> extern int load_policy(int option);
>> -extern int load_hats(sd_serialize *p, struct codomain *cod);
>> -extern int load_flattened_hats(struct codomain *cod);
>> -extern void free_policy(struct codomain *cod);
>> -extern void dump_policy(void);
>> -extern void dump_policy_hats(struct codomain *cod);
>> +extern int load_hats(sd_serialize *p, Profile *prof);
>> +extern int load_flattened_hats(Profile *prof, int option);
>> +extern void dump_policy_hats(Profile *prof);
>> extern void dump_policy_names(void);
>> +void dump_policy(void);
>>
>> void free_policies(void);
>>
>> -#ifdef UNIT_TEST
>> -/* For the unit-test builds, we must include function stubs for stuff that
>> - * only exists in the excluded object files; everything else should live
>> - * in parser_common.c.
>> - */
>> -
>> -/* parser_yacc.y */
>> -void yyerror(const char *msg, ...)
>> -{
>> - va_list arg;
>> - char buf[PATH_MAX];
>> -
>> - va_start(arg, msg);
>> - vsnprintf(buf, sizeof(buf), msg, arg);
>> - va_end(arg);
>> -
>> - PERROR(_("AppArmor parser error: %s\n"), buf);
>> -
>> - exit(1);
>> -}
>> -
>> -#define MY_TEST(statement, error) \
>> - if (!(statement)) { \
>> - PERROR("FAIL: %s\n", error); \
>> - rc = 1; \
>> - }
>> -
>> -#endif
>> -
>> #endif /** __AA_PARSER_H */
>> diff --git a/parser/parser_alias.c b/parser/parser_alias.c
>> index aee882e..aea9f1a 100644
>> --- a/parser/parser_alias.c
>> +++ b/parser/parser_alias.c
>> @@ -25,6 +25,7 @@
>>
>> #include "immunix.h"
>> #include "parser.h"
>> +#include "profile.h"
>>
>> struct alias_rule {
>> char *from;
>> @@ -105,7 +106,7 @@ static char *do_alias(struct alias_rule *alias, const char *target)
>> return n;
>> }
>>
>> -static struct codomain *target_cod;
>> +static Profile *target_prof;
>> static struct cod_entry *target_list;
>> static void process_entries(const void *nodep, VISIT value, int __unused level)
>> {
>> @@ -155,7 +156,7 @@ static void process_entries(const void *nodep, VISIT value, int __unused level)
>> static void process_name(const void *nodep, VISIT value, int __unused level)
>> {
>> struct alias_rule **t = (struct alias_rule **) nodep;
>> - struct codomain *cod = target_cod;
>> + Profile *prof = target_prof;
>> char *name;
>> int len;
>>
>> @@ -164,10 +165,10 @@ static void process_name(const void *nodep, VISIT value, int __unused level)
>>
>> len = strlen((*t)->from);
>>
>> - if (cod->attachment)
>> - name = cod->attachment;
>> + if (prof->attachment)
>> + name = prof->attachment;
>> else
>> - name = cod->name;
>> + name = prof->name;
>
> Not something to do in -this- patch, but this looks like prime candidate
> for a new method in class Profile. (I presume a lot of this file will
> eventually move into Profile anyway? Maybe not wholesale functions, but
> pieces of these functions..)
>
Oh yes, and all the rules will get there own classes and inherit from a base
class.
>>
>> if (name && strncmp((*t)->from, name, len) == 0) {
>> struct alt_name *alt;
>> @@ -179,21 +180,23 @@ static void process_name(const void *nodep, VISIT value, int __unused level)
>> if (!alt)
>> return;
>> alt->name = n;
>> - alt->next = cod->altnames;
>> - cod->altnames = alt;
>> + alt->next = prof->altnames;
>> + prof->altnames = alt;
>> }
>> }
>>
>> -void replace_aliases(struct codomain *cod)
>> +int replace_profile_aliases(Profile *prof)
>> {
>> - target_cod = cod;
>> + target_prof = prof;
>> twalk(alias_table, process_name);
>>
>> - if (cod->entries) {
>> - target_list = cod->entries;
>> - target_cod = cod;
>> + if (prof->entries) {
>> + target_list = prof->entries;
>> + target_prof = prof;
>> twalk(alias_table, process_entries);
>> }
>> +
>> + return 0;
>> }
>>
>> static void free_alias(void *nodep)
>> diff --git a/parser/parser_interface.c b/parser/parser_interface.c
>> index 77f3d2e..d47cca2 100644
>> --- a/parser/parser_interface.c
>> +++ b/parser/parser_interface.c
>> @@ -25,6 +25,7 @@
>> #define _(s) gettext(s)
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "libapparmor_re/apparmor_re.h"
>>
>> #include <unistd.h>
>> @@ -59,7 +60,7 @@
>>
>> #define SUBDOMAIN_INTERFACE_DFA_VERSION 5
>>
>> -int sd_serialize_codomain(int option, struct codomain *cod);
>> +int sd_serialize_profile(int option, Profile *prof);
>>
>> static void print_error(int error)
>> {
>> @@ -100,30 +101,30 @@ static void print_error(int error)
>> }
>> }
>>
>> -int load_codomain(int option, struct codomain *cod)
>> +int load_profile(int option, Profile *prof)
>> {
>> int retval = 0;
>> int error = 0;
>>
>> - PDEBUG("Serializing policy for %s.\n", cod->name);
>> - retval = sd_serialize_codomain(option, cod);
>> + PDEBUG("Serializing policy for %s.\n", prof->name);
>> + retval = sd_serialize_profile(option, prof);
>>
>> if (retval < 0) {
>> error = retval; /* yeah, we'll just report the last error */
>> switch (option) {
>> case OPTION_ADD:
>> PERROR(_("%s: Unable to add \"%s\". "),
>> - progname, cod->name);
>> + progname, prof->name);
>> print_error(error);
>> break;
>> case OPTION_REPLACE:
>> PERROR(_("%s: Unable to replace \"%s\". "),
>> - progname, cod->name);
>> + progname, prof->name);
>> print_error(error);
>> break;
>> case OPTION_REMOVE:
>> PERROR(_("%s: Unable to remove \"%s\". "),
>> - progname, cod->name);
>> + progname, prof->name);
>> print_error(error);
>> break;
>> case OPTION_STDOUT:
>> @@ -144,15 +145,15 @@ int load_codomain(int option, struct codomain *cod)
>> switch (option) {
>> case OPTION_ADD:
>> printf(_("Addition succeeded for \"%s\".\n"),
>> - cod->name);
>> + prof->name);
>> break;
>> case OPTION_REPLACE:
>> printf(_("Replacement succeeded for \"%s\".\n"),
>> - cod->name);
>> + prof->name);
>> break;
>> case OPTION_REMOVE:
>> printf(_("Removal succeeded for \"%s\".\n"),
>> - cod->name);
>> + prof->name);
>> break;
>> case OPTION_STDOUT:
>> case OPTION_OFILE:
>> @@ -544,7 +545,7 @@ int count_tailglob_ents(struct cod_entry *list)
>> return count;
>> }
>>
>> -int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
>> +int sd_serialize_profile(sd_serialize *p, Profile *profile,
>> int flattened)
>> {
>> uint64_t allowed_caps;
>> @@ -608,12 +609,12 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
>>
>> #define low_caps(X) ((u32) ((X) & 0xffffffff))
>> #define high_caps(X) ((u32) (((X) >> 32) & 0xffffffff))
>> - allowed_caps = (profile->capabilities) & ~profile->deny_caps;
>> + allowed_caps = (profile->caps.allow) & ~profile->caps.deny;
>> if (!sd_write32(p, low_caps(allowed_caps)))
>> return 0;
>> - if (!sd_write32(p, low_caps(allowed_caps & profile->audit_caps)))
>> + if (!sd_write32(p, low_caps(allowed_caps & profile->caps.audit)))
>> return 0;
>> - if (!sd_write32(p, low_caps(profile->deny_caps & profile->quiet_caps)))
>> + if (!sd_write32(p, low_caps(profile->caps.deny & profile->caps.quiet)))
>> return 0;
>> if (!sd_write32(p, 0))
>> return 0;
>> @@ -622,9 +623,9 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
>> return 0;
>> if (!sd_write32(p, high_caps(allowed_caps)))
>> return 0;
>> - if (!sd_write32(p, high_caps(allowed_caps & profile->audit_caps)))
>> + if (!sd_write32(p, high_caps(allowed_caps & profile->caps.audit)))
>> return 0;
>> - if (!sd_write32(p, high_caps(profile->deny_caps & profile->quiet_caps)))
>> + if (!sd_write32(p, high_caps(profile->caps.deny & profile->caps.quiet)))
>> return 0;
>> if (!sd_write32(p, 0))
>> return 0;
>> @@ -634,42 +635,42 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
>> if (!sd_serialize_rlimits(p, &profile->rlimits))
>> return 0;
>>
>> - if (profile->network_allowed && kernel_supports_network) {
>> + if (profile->net.allow && kernel_supports_network) {
>> size_t i;
>> if (!sd_write_array(p, "net_allowed_af", get_af_max()))
>> return 0;
>> for (i = 0; i < get_af_max(); i++) {
>> - u16 allowed = profile->network_allowed[i] &
>> - ~profile->deny_network[i];
>> + u16 allowed = profile->net.allow[i] &
>> + ~profile->net.deny[i];
>> if (!sd_write16(p, allowed))
>> return 0;
>> - if (!sd_write16(p, allowed & profile->audit_network[i]))
>> + if (!sd_write16(p, allowed & profile->net.audit[i]))
>> return 0;
>> - if (!sd_write16(p, profile->deny_network[i] & profile->quiet_network[i]))
>> + if (!sd_write16(p, profile->net.deny[i] & profile->net.quiet[i]))
>> return 0;
>> }
>> if (!sd_write_arrayend(p))
>> return 0;
>> - } else if (profile->network_allowed)
>> + } else if (profile->net.allow)
>> pwarn(_("profile %s network rules not enforced\n"), profile->name);
>>
>> - if (profile->policy_dfa) {
>> + if (profile->policy.dfa) {
>> if (!sd_write_struct(p, "policydb"))
>> return 0;
>> - if (!sd_serialize_dfa(p, profile->policy_dfa, profile->policy_dfa_size))
>> + if (!sd_serialize_dfa(p, profile->policy.dfa, profile->policy.size))
>> return 0;
>> if (!sd_write_structend(p))
>> return 0;
>> }
>>
>> /* either have a single dfa or lists of different entry types */
>> - if (!sd_serialize_dfa(p, profile->dfa, profile->dfa_size))
>> + if (!sd_serialize_dfa(p, profile->dfa.dfa, profile->dfa.size))
>> return 0;
>>
>> if (!sd_serialize_xtable(p, profile->exec_table))
>> return 0;
>>
>> - if (profile->hat_table) {
>> + if (!profile->hat_table.empty()) {
>> if (!sd_write_list(p, "hats"))
>> return 0;
>> if (load_hats(p, profile) != 0)
>> @@ -684,7 +685,7 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
>> return 1;
>> }
>>
>> -int sd_serialize_top_profile(sd_serialize *p, struct codomain *profile)
>> +int sd_serialize_top_profile(sd_serialize *p, Profile *profile)
>> {
>> int version;
>>
>> @@ -708,7 +709,7 @@ int sd_serialize_top_profile(sd_serialize *p, struct codomain *profile)
>> }
>>
>> int cache_fd = -1;
>> -int sd_serialize_codomain(int option, struct codomain *cod)
>> +int sd_serialize_profile(int option, Profile *prof)
>> {
>> int fd = -1;
>> int error = -ENOMEM, size, wsize;
>> @@ -763,34 +764,34 @@ int sd_serialize_codomain(int option, struct codomain *cod)
>> if (profile_ns) {
>> len += strlen(profile_ns) + 2;
>> ns = profile_ns;
>> - } else if (cod->ns) {
>> - len += strlen(cod->ns) + 2;
>> - ns = cod->ns;
>> + } else if (prof->ns) {
>> + len += strlen(prof->ns) + 2;
>> + ns = prof->ns;
>> }
>> - if (cod->parent) {
>> - name = (char *) malloc(strlen(cod->name) + 3 +
>> - strlen(cod->parent->name) + len);
>> + if (prof->parent) {
>> + name = (char *) malloc(strlen(prof->name) + 3 +
>> + strlen(prof->parent->name) + len);
>> if (!name) {
>> - PERROR(_("Memory Allocation Error: Unable to remove ^%s\n"), cod->name);
>> + PERROR(_("Memory Allocation Error: Unable to remove ^%s\n"), prof->name);
>> error = -errno;
>> goto exit;
>> }
>> if (ns)
>> sprintf(name, ":%s:%s//%s", ns,
>> - cod->parent->name, cod->name);
>> + prof->parent->name, prof->name);
>> else
>> - sprintf(name, "%s//%s", cod->parent->name,
>> - cod->name);
>> + sprintf(name, "%s//%s", prof->parent->name,
>> + prof->name);
>> } else if (ns) {
>> - name = (char *) malloc(len + strlen(cod->name) + 1);
>> + name = (char *) malloc(len + strlen(prof->name) + 1);
>> if (!name) {
>> - PERROR(_("Memory Allocation Error: Unable to remove %s:%s."), ns, cod->name);
>> + PERROR(_("Memory Allocation Error: Unable to remove %s:%s."), ns, prof->name);
>> error = -errno;
>> goto exit;
>> }
>> - sprintf(name, ":%s:%s", ns, cod->name);
>> + sprintf(name, ":%s:%s", ns, prof->name);
>> } else {
>> - name = cod->name;
>> + name = prof->name;
>> }
>> size = strlen(name) + 1;
>> if (kernel_load) {
>> @@ -798,7 +799,7 @@ int sd_serialize_codomain(int option, struct codomain *cod)
>> if (wsize < 0)
>> error = -errno;
>> }
>> - if (cod->parent || ns)
>> + if (prof->parent || ns)
>> free(name);
>> } else {
>>
>> @@ -810,11 +811,11 @@ int sd_serialize_codomain(int option, struct codomain *cod)
>> goto exit;
>> }
>>
>> - if (!sd_serialize_top_profile(work_area, cod)) {
>> + if (!sd_serialize_top_profile(work_area, prof)) {
>> close(fd);
>> free_sd_serial(work_area);
>> PERROR(_("unable to serialize profile %s\n"),
>> - cod->name);
>> + prof->name);
>> goto exit;
>> }
>>
>> @@ -844,8 +845,8 @@ int sd_serialize_codomain(int option, struct codomain *cod)
>>
>> close(fd);
>>
>> - if (cod->hat_table && option != OPTION_REMOVE) {
>> - if (load_flattened_hats(cod) != 0)
>> + if (!prof->hat_table.empty() && option != OPTION_REMOVE) {
>> + if (load_flattened_hats(prof, option) != 0)
>> return 0;
>> }
>>
>> diff --git a/parser/parser_lex.l b/parser/parser_lex.l
>> index 6150141..453885c 100644
>> --- a/parser/parser_lex.l
>> +++ b/parser/parser_lex.l
>> @@ -37,6 +37,7 @@
>> #define _(s) gettext(s)
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "parser_include.h"
>> #include "parser_yacc.h"
>> #include "lib.h"
>> diff --git a/parser/parser_merge.c b/parser/parser_merge.c
>> index 3b0baea..31d12dd 100644
>> --- a/parser/parser_merge.c
>> +++ b/parser/parser_merge.c
>> @@ -25,7 +25,7 @@
>> #define _(s) gettext(s)
>>
>> #include "parser.h"
>> -
>> +#include "profile.h"
>>
>> static int file_comp(const void *c1, const void *c2)
>> {
>> @@ -74,27 +74,27 @@ static int file_comp(const void *c1, const void *c2)
>> return strcmp((*e1)->name, (*e2)->name);
>> }
>>
>> -static int process_file_entries(struct codomain *cod)
>> +static int process_file_entries(Profile *prof)
>> {
>> int n, count;
>> struct cod_entry *flist, *cur, *next;
>> struct cod_entry **table;
>>
>> - for (flist = cod->entries, n = 0; flist; flist = flist->next)
>> + for (flist = prof->entries, n = 0; flist; flist = flist->next)
>> n++;
>>
>> count = n;
>> if (count < 2)
>> - return 1;
>> + return 0;
>>
>> table = (struct cod_entry **) malloc(sizeof(struct cod_entry *) * (count + 1));
>> if (!table) {
>> PERROR(_("Couldn't merge entries. Out of Memory\n"));
>> - return 0;
>> + return ENOMEM;
>> }
>>
>> n = 0;
>> - for (flist = cod->entries; flist; flist = flist->next) {
>> + for (flist = prof->entries; flist; flist = flist->next) {
>> table[n] = flist;
>> n++;
>> }
>> @@ -110,8 +110,8 @@ static int process_file_entries(struct codomain *cod)
>> if (!is_merged_x_consistent(cur->mode, next->mode)) {
>> PERROR(_("profile %s: has merged rule %s with "
>> "conflicting x modifiers\n"),
>> - cod->name, cur->name);
>> - return 0;
>> + prof->name, cur->name);
>> + return -1;
>> }
>> //if (next->audit)
>> //fprintf(stderr, "warning: merging rule 0x%x %s\n", next->audit, next->name);
>> @@ -136,22 +136,14 @@ static int process_file_entries(struct codomain *cod)
>> }
>> }
>> cur->next = NULL;
>> - cod->entries = table[0];
>> + prof->entries = table[0];
>>
>> free(table);
>>
>> - return 1;
>> + return 0;
>> }
>>
>> -int codomain_merge_rules(struct codomain *cod)
>> +int profile_merge_rules(Profile *prof)
>> {
>> - if (!process_file_entries(cod))
>> - goto fail;
>> -
>> - /* XXX return error from this */
>> - merge_hat_rules(cod);
>> -
>> - return 1;
>> -fail:
>> - return 0;
>> + return process_file_entries(prof);
>> }
>> diff --git a/parser/parser_misc.c b/parser/parser_misc.c
>> index 51a0768..1708ea3 100644
>> --- a/parser/parser_misc.c
>> +++ b/parser/parser_misc.c
>> @@ -36,6 +36,7 @@
>> #include <unistd.h>
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "parser_yacc.h"
>> #include "mount.h"
>>
>> @@ -854,23 +855,6 @@ void debug_cod_entries(struct cod_entry *list)
>> }
>> }
>>
>> -void debug_flags(struct codomain *cod)
>> -{
>> - printf("Profile Mode:\t");
>> -
>> - if (cod->flags.complain)
>> - printf("Complain");
>> - else
>> - printf("Enforce");
>> -
>> - if (cod->flags.audit)
>> - printf(", Audit");
>> -
>> - if (cod->flags.hat)
>> - printf(", Hat");
>> -
>> - printf("\n");
>> -}
>>
>> static const char *capnames[] = {
>> "chown",
>> @@ -931,17 +915,6 @@ void __debug_capabilities(uint64_t capset, const char *name)
>> }
>> printf("\n");
>> }
>> -void debug_capabilities(struct codomain *cod)
>> -{
>> - if (cod->capabilities != 0ull)
>> - __debug_capabilities(cod->capabilities, "Capabilities");
>> - if (cod->audit_caps != 0ull)
>> - __debug_capabilities(cod->audit_caps, "Audit Caps");
>> - if (cod->deny_caps != 0ull)
>> - __debug_capabilities(cod->deny_caps, "Deny Caps");
>> - if (cod->quiet_caps != 0ull)
>> - __debug_capabilities(cod->quiet_caps, "Quiet Caps");
>> -}
>>
>> /* Bleah C++ doesn't have non-trivial designated initializers so we just
>> * have to make sure these are in order. This means we are more brittle
>> @@ -1031,44 +1004,6 @@ void __debug_network(unsigned int *array, const char *name)
>> printf("\n");
>> }
>>
>> -void debug_network(struct codomain *cod)
>> -{
>> - if (cod->network_allowed)
>> - __debug_network(cod->network_allowed, "Network");
>> - if (cod->audit_network)
>> - __debug_network(cod->audit_network, "Audit Net");
>> - if (cod->deny_network)
>> - __debug_network(cod->deny_network, "Deny Net");
>> - if (cod->quiet_network)
>> - __debug_network(cod->quiet_network, "Quiet Net");
>> -
>> -}
>> -
>> -void debug_cod_list(struct codomain *cod)
>> -{
>> - if (cod->ns)
>> - printf("Ns:\t\t%s\n", cod->ns);
>> -
>> - if (cod->name)
>> - printf("Name:\t\t%s\n", cod->name);
>> - else
>> - printf("Name:\t\tNULL\n");
>> -
>> - if (cod->local)
>> - printf("Local To:\t%s\n", cod->parent->name);
>> -
>> - debug_flags(cod);
>> -
>> - debug_capabilities(cod);
>> -
>> - debug_network(cod);
>> -
>> - if (cod->entries)
>> - debug_cod_entries(cod->entries);
>> -
>> - printf("\n");
>> - dump_policy_hats(cod);
>> -}
>>
>> struct value_list *new_value_list(char *value)
>> {
>> @@ -1167,6 +1102,9 @@ void print_cond_entry(struct cond_entry *ent)
>> }
>>
>> #ifdef UNIT_TEST
>> +
>> +#include "unit_test.h"
>> +
>> int test_str_to_boolean(void)
>> {
>> int rc = 0;
>> diff --git a/parser/parser_policy.c b/parser/parser_policy.c
>> index 76a65c8..93c239c 100644
>> --- a/parser/parser_policy.c
>> +++ b/parser/parser_policy.c
>> @@ -19,6 +19,8 @@
>> * Ltd.
>> */
>>
>> +#include <algorithm>
>> +
>> #include <stdio.h>
>> #include <stdlib.h>
>> #include <stdarg.h>
>> @@ -29,6 +31,7 @@
>> #define _(s) gettext(s)
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "mount.h"
>> #include "parser_yacc.h"
>>
>> @@ -40,70 +43,40 @@
>> #endif
>> #define NPDEBUG(fmt, args...) /* Do nothing */
>>
>> -void *policy_list = NULL;
>>
>> -static int codomain_compare(const void *a, const void *b)
>> -{
>> - struct codomain *A = (struct codomain *) a;
>> - struct codomain *B = (struct codomain *) b;
>> +ProfileList policy_list;
>>
>> - int res = 0;
>> - if (A->ns) {
>> - if (B->ns)
>> - res = strcmp(A->ns, B->ns);
>> - else
>> - res = -1;
>> - } else if (B->ns)
>> - res = 1;
>> - if (res)
>> - return res;
>> - return strcmp(A->name, B->name);
>> -}
>>
>> -void add_to_list(struct codomain *codomain)
>> +void add_to_list(Profile *prof)
>> {
>> - struct codomain **result;
>> -
>> - result = (struct codomain **) tsearch(codomain, &policy_list, codomain_compare);
>> - if (!result) {
>> - PERROR("Memory allocation error\n");
>> - exit(1);
>> - }
>> -
>> - if (*result != codomain) {
>> + pair<ProfileList::iterator, bool> res = policy_list.insert(prof);
>
> I realize it's a near-direct copy, but would it be a good idea to move
> away from global variables while making these changes?
>
>
>> + if (!res.second) {
>> PERROR("Multiple definitions for profile %s exist,"
>> - "bailing out.\n", codomain->name);
>> + "bailing out.\n", prof->name);
>> exit(1);
>> }
>> }
>>
>> -void add_hat_to_policy(struct codomain *cod, struct codomain *hat)
>> +void add_hat_to_policy(Profile *prof, Profile *hat)
>> {
>> - struct codomain **result;
>> -
>> - hat->parent = cod;
>> + hat->parent = prof;
>>
>> - result = (struct codomain **) tsearch(hat, &(cod->hat_table), codomain_compare);
>> - if (!result) {
>> - PERROR("Memory allocation error\n");
>> - exit(1);
>> - }
>> -
>> - if (*result != hat) {
>> + pair<ProfileList::iterator, bool> res = prof->hat_table.insert(hat);
>> + if (!res.second) {
>> PERROR("Multiple definitions for hat %s in profile %s exist,"
>> - "bailing out.\n", hat->name, cod->name);
>> + "bailing out.\n", hat->name, prof->name);
>> exit(1);
>> }
>> }
>>
>> -static int add_entry_to_x_table(struct codomain *cod, char *name)
>> +static int add_entry_to_x_table(Profile *prof, char *name)
>> {
>> int i;
>> for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
>> - if (!cod->exec_table[i]) {
>> - cod->exec_table[i] = name;
>> + if (!prof->exec_table[i]) {
>> + prof->exec_table[i] = name;
>> return i;
>> - } else if (strcmp(cod->exec_table[i], name) == 0) {
>> + } else if (strcmp(prof->exec_table[i], name) == 0) {
>> /* name already in table */
>> free(name);
>> return i;
>> @@ -113,7 +86,7 @@ static int add_entry_to_x_table(struct codomain *cod, char *name)
>> return 0;
>> }
>>
>> -static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
>> +static int add_named_transition(Profile *prof, struct cod_entry *entry)
>> {
>> char *name = NULL;
>>
>> @@ -122,7 +95,7 @@ static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
>> char *sub = strstr(entry->nt_name, "//");
>> /* does the subprofile name match the rule */
>>
>> - if (sub && strncmp(cod->name, sub, sub - entry->nt_name) &&
>> + if (sub && strncmp(prof->name, sub, sub - entry->nt_name) &&
>> strcmp(sub + 2, entry->name) == 0) {
>> free(entry->nt_name);
>> entry->nt_name = NULL;
>> @@ -137,13 +110,13 @@ static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
>> return AA_EXEC_LOCAL >> 10;
>> }
>> /* specified as cix so profile name is implicit */
>> - name = (char *) malloc(strlen(cod->name) + strlen(entry->nt_name)
>> + name = (char *) malloc(strlen(prof->name) + strlen(entry->nt_name)
>> + 3);
>> if (!name) {
>> PERROR("Memory allocation error\n");
>> exit(1);
>> }
>> - sprintf(name, "%s//%s", cod->name, entry->nt_name);
>> + sprintf(name, "%s//%s", prof->name, entry->nt_name);
>> free(entry->nt_name);
>> entry->nt_name = name;
>> }
>> @@ -163,26 +136,26 @@ static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
>> name = entry->nt_name;
>> }
>>
>> - return add_entry_to_x_table(cod, name);
>> + return add_entry_to_x_table(prof, name);
>> }
>>
>> -void add_entry_to_policy(struct codomain *cod, struct cod_entry *entry)
>> +void add_entry_to_policy(Profile *prof, struct cod_entry *entry)
>> {
>> - entry->next = cod->entries;
>> - cod->entries = entry;
>> + entry->next = prof->entries;
>> + prof->entries = entry;
>> }
>>
>> -void post_process_file_entries(struct codomain *cod)
>> +void post_process_file_entries(Profile *prof)
>> {
>> struct cod_entry *entry;
>> int cp_mode = 0;
>>
>> - list_for_each(cod->entries, entry) {
>> + list_for_each(prof->entries, entry) {
>> if (entry->nt_name) {
>> int mode = 0;
>> - int n = add_named_transition(cod, entry);
>> + int n = add_named_transition(prof, entry);
>> if (!n) {
>> - PERROR("Profile %s has too many specified profile transitions.\n", cod->name);
>> + PERROR("Profile %s has too many specified profile transitions.\n", prof->name);
>> exit(1);
>> }
>> if (entry->mode & AA_USER_EXEC)
>> @@ -214,20 +187,20 @@ void post_process_file_entries(struct codomain *cod)
>> PERROR("Memory allocation error\n");
>> exit(1);
>> }
>> - add_entry_to_policy(cod, new_ent);
>> + add_entry_to_policy(prof, new_ent);
>> }
>> }
>>
>> -void post_process_mnt_entries(struct codomain *cod)
>> +void post_process_mnt_entries(Profile *prof)
>> {
>> struct mnt_entry *entry;
>>
>> - list_for_each(cod->mnt_ents, entry) {
>> + list_for_each(prof->mnt_ents, entry) {
>> if (entry->trans) {
>> unsigned int mode = 0;
>> - int n = add_entry_to_x_table(cod, entry->trans);
>> + int n = add_entry_to_x_table(prof, entry->trans);
>> if (!n) {
>> - PERROR("Profile %s has too many specified profile transitions.\n", cod->name);
>> + PERROR("Profile %s has too many specified profile transitions.\n", prof->name);
>> exit(1);
>> }
>>
>> @@ -244,335 +217,90 @@ void post_process_mnt_entries(struct codomain *cod)
>> }
>>
>>
>> -static void __merge_rules(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - if (!codomain_merge_rules(*t)) {
>> - PERROR(_("ERROR merging rules for profile %s, failed to load\n"),
>> - (*t)->name);
>> - exit(1);
>> - }
>> -}
>> -
>> -int post_merge_rules(void)
>> -{
>> - twalk(policy_list, __merge_rules);
>> - return 0;
>> -}
>> -
>> -int merge_hat_rules(struct codomain *cod)
>> -{
>> - twalk(cod->hat_table, __merge_rules);
>> - return 0;
>> -}
>> -
>> -static void __process_regex(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - if (process_regex(*t) != 0) {
>> - PERROR(_("ERROR processing regexs for profile %s, failed to load\n"),
>> - (*t)->name);
>> - exit(1);
>> - }
>> -}
>> -
>> -int post_process_regex(void)
>> -{
>> - twalk(policy_list, __process_regex);
>> - return 0;
>> -}
>> -
>> -int process_hat_regex(struct codomain *cod)
>> -{
>> - twalk(cod->hat_table, __process_regex);
>> - return 0;
>> -}
>> -
>> -static void __process_policydb(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - if (process_policydb(*t) != 0) {
>> - PERROR(_("ERROR processing policydb rules for profile %s, failed to load\n"),
>> - (*t)->name);
>> - exit(1);
>> - }
>> -}
>> -
>> -int post_process_policydb(void)
>> -{
>> - twalk(policy_list, __process_policydb);
>> - return 0;
>> -}
>> -
>> -int process_hat_policydb(struct codomain *cod)
>> -{
>> - twalk(cod->hat_table, __process_policydb);
>> - return 0;
>> -}
>> -
>> -static void __process_variables(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - if (process_variables(*t) != 0) {
>> - PERROR(_("ERROR expanding variables for profile %s, failed to load\n"),
>> - (*t)->name);
>> - exit(1);
>> - }
>> -}
>> -
>> -int post_process_variables(void)
>> -{
>> - twalk(policy_list, __process_variables);
>> - return 0;
>> -}
>> -
>> -int process_hat_variables(struct codomain *cod)
>> -{
>> - twalk(cod->hat_table, __process_variables);
>> - return 0;
>> -}
>> -
>> -
>> -static void __process_alias(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - replace_aliases((*t));
>> -
>> - if ((*t)->hat_table)
>> - twalk((*t)->hat_table, __process_alias);
>> -}
>> -
>> -int post_process_alias(void)
>> -{
>> - twalk(policy_list, __process_alias);
>> - return 0;
>> -}
>> -
>> #define CHANGEHAT_PATH "/proc/[0-9]*/attr/current"
>>
>> /* add file rules to access /proc files to call change_hat()
>> */
>> -static void __add_hat_rules_parent(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> +static int profile_add_hat_rules(Profile *prof)
>> {
>> - struct codomain **t = (struct codomain **) nodep;
>> struct cod_entry *entry;
>>
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - /* don't add hat rules if a parent profile with no hats */
>> - if (!(*t)->hat_table && !(*t)->parent)
>> - return;
>> -
>> - /* don't add hat rules for local_profiles */
>> - if ((*t)->local)
>> - return;
>> + /* TODO: ??? fix logic for when to add to hat/base vs. local */
>> + /* don't add hat rules for local_profiles or base profiles */
>> + if (prof->local || prof->hat_table.empty())
>> + return 0;
>>
>> + /* add entry to hat */
>> entry = new_entry(NULL, strdup(CHANGEHAT_PATH), AA_MAY_WRITE, NULL);
>> - if (!entry) {
>> - PERROR(_("ERROR adding hat access rule for profile %s\n"),
>> - (*t)->name);
>> - exit(1);
>> - }
>> - add_entry_to_policy(*t, entry);
>> + if (!entry)
>> + return ENOMEM;
>>
>> - twalk((*t)->hat_table, __add_hat_rules_parent);
>> -}
>> -
>> -static int add_hat_rules(void)
>> -{
>> - twalk(policy_list, __add_hat_rules_parent);
>> + add_entry_to_policy(prof, entry);
>>
>> return 0;
>> }
>>
>> -/* Yuck, is their no other way to pass arguments to a twalk action */
>> -static int __load_option;
>> -static int __load_error;
>> -
>> -static void __load_policy(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> +int load_policy_list(ProfileList &list, int option)
>> {
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder || __load_error)
>> - return;
>> + int res = 0;
>>
>> - if (load_codomain(__load_option, *t) != 0) {
>> - __load_error = -EINVAL;
>> + for (ProfileList::iterator i = list.begin(); i != list.end(); i++) {
>> + res = load_profile(option, *i);
>> + if (!res)
>> + break;
>> }
>> -}
>>
>> -int load_policy(int option)
>> -{
>> - __load_option = option;
>> - __load_error = 0;
>> - twalk(policy_list, __load_policy);
>> - return __load_error;
>> + return res;
>> }
>>
>> -/* Yuck, is their no other way to pass arguments to a twalk action */
>> -static sd_serialize *__p;
>> -
>> -static int __load_hat_error;
>> -static void __load_hat(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> +int load_flattened_hats(Profile *prof, int option)
>> {
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder || __load_hat_error)
>> - return;
>> -
>> - if (!sd_serialize_profile(__p, *t, 0)) {
>> - PERROR(_("ERROR in profile %s, failed to load\n"),
>> - (*t)->name);
>> - __load_hat_error = -EINVAL;
>> - }
>> + return load_policy_list(prof->hat_table, option);
>> }
>>
>> -static int __load_flattened_hat_error;
>> -static void __load_flattened_hat(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> +int load_policy(int option)
>> {
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder || __load_flattened_hat_error)
>> - return;
>> -
>> - if (load_codomain(__load_option, *t) != 0) {
>> - __load_flattened_hat_error = -EINVAL;
>> - }
>> + return load_policy_list(policy_list, option);
>> }
>>
>> -int load_flattened_hats(struct codomain *cod)
>> +int load_hats(sd_serialize *p, Profile *prof)
>> {
>> - __load_flattened_hat_error = 0;
>> - twalk(cod->hat_table, __load_flattened_hat);
>> - return __load_flattened_hat_error;
>> -}
>> + for (ProfileList::iterator i = prof->hat_table.begin(); i != prof->hat_table.end(); i++) {
>> + if (!sd_serialize_profile(p, *i, 0)) {
>> + PERROR(_("ERROR in profile %s, failed to load\n"),
>> + (*i)->name);
>> + return -EINVAL;
>> + }
>> + }
>>
>> -int load_hats(sd_serialize *p, struct codomain *cod)
>> -{
>> - __p = p;
>> - __load_hat_error = 0;
>> - twalk(cod->hat_table, __load_hat);
>> - return __load_hat_error;
>> + return 0;
>> }
>>
>> -static void __dump_policy(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - debug_cod_list(*t);
>> -}
>>
>> void dump_policy(void)
>> {
>> - twalk(policy_list, __dump_policy);
>> -}
>> -
>> -void dump_policy_hats(struct codomain *cod)
>> -{
>> - twalk(cod->hat_table, __dump_policy);
>> -}
>> -
>> -/* Gar */
>> -static struct codomain *__dump_policy_name;
>> -
>> -static void __dump_policy_hatnames(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - printf("%s//%s\n", __dump_policy_name->name, (*t)->name);
>> -}
>> -
>> -void dump_policy_hatnames(struct codomain *cod)
>> -{
>> - __dump_policy_name = cod;
>> - twalk(cod->hat_table, __dump_policy_hatnames);
>> -}
>> -
>> -static void __dump_policy_names(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> -
>> - printf("%s\n", (*t)->name);
>> - dump_policy_hatnames(*t);
>> + policy_list.dump();
>> }
>>
>> void dump_policy_names(void)
>> {
>> - twalk(policy_list, __dump_policy_names);
>> -}
>> -
>> -/* gar, more global arguments */
>> -struct codomain *__hat_merge_policy;
>> -
>> -static void __merge_hat(const void *nodep, const VISIT value,
>> - const int __unused depth)
>> -{
>> - struct codomain **t = (struct codomain **) nodep;
>> -
>> - if (value == preorder || value == endorder)
>> - return;
>> - add_hat_to_policy(__hat_merge_policy, (*t));
>> + policy_list.dump_profile_names(true);
>> }
>>
>> -/* merge_hats: merges hat_table into hat_table owned by cod */
>> -static void merge_hats(struct codomain *cod, void *hats)
>> +/* merge_hats: merges hat_table into hat_table owned by prof */
>> +static void merge_hats(Profile *prof, ProfileList &hats)
>> {
>> - __hat_merge_policy = cod;
>> - twalk(hats, __merge_hat);
>> -}
>> + for (ProfileList::iterator i = hats.begin(); i != hats.end(); ) {
>> + ProfileList::iterator cur = i++;
>> + add_hat_to_policy(prof, *cur);
>> + hats.erase(cur);
>> + }
>>
>> -/* don't want to free the hat entries in the table, as they were pushed
>> - * onto the other table. */
>> -static void empty_destroy(void __unused *nodep)
>> -{
>> - return;
>> }
>>
>> -struct codomain *merge_policy(struct codomain *a, struct codomain *b)
>> +Profile *merge_policy(Profile *a, Profile *b)
>> {
>> - struct codomain *ret = a;
>> + Profile *ret = a;
>> struct cod_entry *last;
>>
>> if (!a) {
>> @@ -600,130 +328,112 @@ struct codomain *merge_policy(struct codomain *a, struct codomain *b)
>> a->flags.complain = a->flags.complain || b->flags.complain;
>> a->flags.audit = a->flags.audit || b->flags.audit;
>>
>> - a->capabilities |= b->capabilities;
>> - a->audit_caps |= b->audit_caps;
>> - a->deny_caps |= b->deny_caps;
>> - a->quiet_caps |= b->quiet_caps;
>> + a->caps.allow |= b->caps.allow;
>> + a->caps.audit |= b->caps.audit;
>> + a->caps.deny |= b->caps.deny;
>> + a->caps.quiet |= b->caps.quiet;
>>
>> - if (a->network_allowed) {
>> + if (a->net.allow) {
>> size_t i;
>> for (i = 0; i < get_af_max(); i++) {
>> - a->network_allowed[i] |= b->network_allowed[i];
>> - a->audit_network[i] |= b->audit_network[i];
>> - a->deny_network[i] |= b->deny_network[i];
>> - a->quiet_network[i] |= b->quiet_network[i];
>> + a->net.allow[i] |= b->net.allow[i];
>> + a->net.audit[i] |= b->net.audit[i];
>> + a->net.deny[i] |= b->net.deny[i];
>> + a->net.quiet[i] |= b->net.quiet[i];
>> }
>> }
>>
>> merge_hats(a, b->hat_table);
>> - tdestroy(b->hat_table, &empty_destroy);
>> - b->hat_table = NULL;
>>
>> - free_policy(b);
>> + delete b;
>> out:
>> return ret;
>> }
>>
>> -int post_process_policy(int debug_only)
>> +
>> +int process_profile_rules(Profile *profile)
>> {
>> - int retval = 0;
>> + int error;
>> +
>> + error = process_profile_regex(profile);
>> + if (error) {
>> + PERROR(_("ERROR processing regexs for profile %s, failed to load\n"), profile->name);
>> + exit(1);
>> + return error;
>> + }
>>
>> - retval = add_hat_rules();
>> - if (retval != 0) {
>> - PERROR(_("%s: Errors found during postprocessing. Aborting.\n"),
>> - progname);
>> - return retval;
>> + error = process_profile_policydb(profile);
>> + if (error) {
>> + PERROR(_("ERROR processing policydb rules for profile %s, failed to load\n"),
>> + (profile)->name);
>> + exit(1);
>> + return error;
>> }
>>
>> - retval = post_process_variables();
>> - if (retval != 0) {
>> - PERROR(_("%s: Errors found during regex postprocess. Aborting.\n"),
>> - progname);
>> - return retval;
>> + return 0;
>> +}
>> +
>> +int post_process_policy_list(ProfileList &list, int debug_only);
>> +int post_process_profile(Profile *profile, int debug_only)
>> +{
>> + int error = 0;
>> +
>> + error = profile_add_hat_rules(profile);
>> + if (error) {
>> + PERROR(_("ERROR adding hat access rule for profile %s\n"),
>> + profile->name);
>> + return error;
>> }
>>
>> - retval = post_process_alias();
>> - if (retval != 0) {
>> - PERROR(_("%s: Errors found during postprocess. Aborting.\n"),
>> - progname);
>> - return retval;
>> + error = process_profile_variables(profile);
>> + if (error) {
>> + PERROR(_("ERROR expanding variables for profile %s, failed to load\n"), profile->name);
>> + exit(1);
>> + return error;
>> }
>>
>> - retval = post_merge_rules();
>> - if (retval != 0) {
>> - PERROR(_("%s: Errors found in combining rules postprocessing. Aborting.\n"),
>> - progname);
>> - return retval;
>> + error = replace_profile_aliases(profile);
>> + if (error) {
>> + PERROR(_("ERROR replacing aliases for profile %s, failed to load\n"), profile->name);
>> + return error;
>> }
>>
>> - if (!debug_only) {
>> - retval = post_process_regex();
>> - if (retval != 0) {
>> - PERROR(_("%s: Errors found during regex postprocess. Aborting.\n"),
>> - progname);
>> - return retval;
>> - }
>> + error = profile_merge_rules(profile);
>> + if (error) {
>> + PERROR(_("ERROR merging rules for profile %s, failed to load\n"), profile->name);
>> + exit(1);
>> + return error;
>> }
>>
>> if (!debug_only) {
>> - retval = post_process_policydb();
>> - if (retval != 0) {
>> - PERROR(_("%s: Errors found during policydb postprocess. Aborting.\n"),
>> - progname);
>> - return retval;
>> - }
>> + error = process_profile_rules(profile);
>> + if (error)
>> + return error;
>> }
>>
>> - return retval;
>> -}
>> + error = post_process_policy_list(profile->hat_table, debug_only);
>>
>> -void free_hat_entry(void *nodep)
>> -{
>> - struct codomain *t = (struct codomain *)nodep;
>> - free_policy(t);
>> + return error;
>> }
>>
>> -void free_hat_table(void *hat_table)
>> +int post_process_policy_list(ProfileList &list, int debug_only)
>> {
>> - if (hat_table)
>> - tdestroy(hat_table, &free_hat_entry);
>> + int error = 0;
>> + for (ProfileList::iterator i = list.begin(); i != list.end(); i++) {
>> + error = post_process_profile(*i, debug_only);
>> + if (error)
>> + break;
>> + }
>> +
>> + return error;
>> }
>>
>> -void free_policy(struct codomain *cod)
>> +int post_process_policy(int debug_only)
>> {
>> - if (!cod)
>> - return;
>> - free_hat_table(cod->hat_table);
>> - free_cod_entries(cod->entries);
>> - free_mnt_entries(cod->mnt_ents);
>> - if (cod->dfarules)
>> - aare_delete_ruleset(cod->dfarules);
>> - if (cod->dfa)
>> - free(cod->dfa);
>> - if (cod->policy_rules)
>> - aare_delete_ruleset(cod->policy_rules);
>> - if (cod->policy_dfa)
>> - free(cod->policy_dfa);
>> - if (cod->name)
>> - free(cod->name);
>> - if (cod->attachment)
>> - free(cod->attachment);
>> - if (cod->ns)
>> - free(cod->ns);
>> - if (cod->network_allowed)
>> - free(cod->network_allowed);
>> - if (cod->audit_network)
>> - free(cod->audit_network);
>> - if (cod->deny_network)
>> - free(cod->deny_network);
>> - if (cod->quiet_network)
>> - free(cod->quiet_network);
>> - free(cod);
>> + return post_process_policy_list(policy_list, debug_only);
>> }
>>
>> void free_policies(void)
>> {
>> - if (policy_list)
>> - tdestroy(policy_list, (__free_fn_t)&free_policy);
>> - policy_list = NULL;
>> + policy_list.clear();
>> }
>> diff --git a/parser/parser_regex.c b/parser/parser_regex.c
>> index ce9d7a3..d65aa81 100644
>> --- a/parser/parser_regex.c
>> +++ b/parser/parser_regex.c
>> @@ -26,6 +26,7 @@
>> /* #define DEBUG */
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "libapparmor_re/apparmor_re.h"
>> #include "libapparmor_re/aare_rules.h"
>> #include "mount.h"
>> @@ -383,30 +384,30 @@ static const char *local_name(const char *name)
>> return name;
>> }
>>
>> -static int process_profile_name_xmatch(struct codomain *cod)
>> +static int process_profile_name_xmatch(Profile *prof)
>> {
>> char tbuf[PATH_MAX + 3]; /* +3 for ^, $ and \0 */
>> pattern_t ptype;
>> const char *name;
>>
>> /* don't filter_slashes for profile names */
>> - if (cod->attachment)
>> - name = cod->attachment;
>> + if (prof->attachment)
>> + name = prof->attachment;
>> else
>> - name = local_name(cod->name);
>> + name = local_name(prof->name);
>> ptype = convert_aaregex_to_pcre(name, 0, tbuf, PATH_MAX + 3,
>> - &cod->xmatch_len);
>> + &prof->xmatch_len);
>> if (ptype == ePatternBasic)
>> - cod->xmatch_len = strlen(name);
>> + prof->xmatch_len = strlen(name);
>>
>> if (ptype == ePatternInvalid) {
>> PERROR(_("%s: Invalid profile name '%s' - bad regular expression\n"), progname, name);
>> return FALSE;
>> - } else if (ptype == ePatternBasic && !(cod->altnames || cod->attachment)) {
>> + } else if (ptype == ePatternBasic && !(prof->altnames || prof->attachment)) {
>> /* no regex so do not set xmatch */
>> - cod->xmatch = NULL;
>> - cod->xmatch_len = 0;
>> - cod->xmatch_size = 0;
>> + prof->xmatch = NULL;
>> + prof->xmatch_len = 0;
>> + prof->xmatch_size = 0;
>> } else {
>> /* build a dfa */
>> aare_ruleset_t *rule = aare_new_ruleset(0);
>> @@ -416,9 +417,9 @@ static int process_profile_name_xmatch(struct codomain *cod)
>> aare_delete_ruleset(rule);
>> return FALSE;
>> }
>> - if (cod->altnames) {
>> + if (prof->altnames) {
>> struct alt_name *alt;
>> - list_for_each(cod->altnames, alt) {
>> + list_for_each(prof->altnames, alt) {
>> int len;
>> ptype = convert_aaregex_to_pcre(alt->name, 0,
>> tbuf,
>> @@ -426,18 +427,18 @@ static int process_profile_name_xmatch(struct codomain *cod)
>> &len);
>> if (ptype == ePatternBasic)
>> len = strlen(alt->name);
>> - if (len < cod->xmatch_len)
>> - cod->xmatch_len = len;
>> + if (len < prof->xmatch_len)
>> + prof->xmatch_len = len;
>> if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
>> aare_delete_ruleset(rule);
>> return FALSE;
>> }
>> }
>> }
>> - cod->xmatch = aare_create_dfa(rule, &cod->xmatch_size,
>> + prof->xmatch = aare_create_dfa(rule, &prof->xmatch_size,
>> dfaflags);
>> aare_delete_ruleset(rule);
>> - if (!cod->xmatch)
>> + if (!prof->xmatch)
>> return FALSE;
>> }
>>
>> @@ -549,70 +550,54 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
>> return TRUE;
>> }
>>
>> -int post_process_entries(struct codomain *cod)
>> +int post_process_entries(Profile *prof)
>> {
>> int ret = TRUE;
>> struct cod_entry *entry;
>> int count = 0;
>>
>> - list_for_each(cod->entries, entry) {
>> - if (!process_dfa_entry(cod->dfarules, entry))
>> + list_for_each(prof->entries, entry) {
>> + if (!process_dfa_entry(prof->dfa.rules, entry))
>> ret = FALSE;
>> count++;
>> }
>>
>> - cod->dfarule_count = count;
>> + prof->dfa.count = count;
>> return ret;
>> }
>>
>> -int process_regex(struct codomain *cod)
>> +int process_profile_regex(Profile *prof)
>> {
>> int error = -1;
>>
>> - if (!process_profile_name_xmatch(cod))
>> + if (!process_profile_name_xmatch(prof))
>> goto out;
>>
>> - cod->dfarules = aare_new_ruleset(0);
>> - if (!cod->dfarules)
>> + prof->dfa.rules = aare_new_ruleset(0);
>> + if (!prof->dfa.rules)
>> goto out;
>>
>> - if (!post_process_entries(cod))
>> + if (!post_process_entries(prof))
>> goto out;
>>
>> - if (cod->dfarule_count > 0) {
>> - cod->dfa = aare_create_dfa(cod->dfarules, &cod->dfa_size,
>> - dfaflags);
>> - aare_delete_ruleset(cod->dfarules);
>> - cod->dfarules = NULL;
>> - if (!cod->dfa)
>> + if (prof->dfa.count > 0) {
>> + prof->dfa.dfa = aare_create_dfa(prof->dfa.rules, &prof->dfa.size,
>> + dfaflags);
>> + aare_delete_ruleset(prof->dfa.rules);
>> + prof->dfa.rules = NULL;
>> + if (!prof->dfa.dfa)
>> goto out;
>> /*
>> - if (cod->dfa_size == 0) {
>> + if (prof->dfa_size == 0) {
>> PERROR(_("profile %s: has merged rules (%s) with "
>> "multiple x modifiers\n"),
>> - cod->name, (char *) cod->dfa);
>> - free(cod->dfa);
>> - cod->dfa = NULL;
>> + prof->name, (char *) prof->dfa);
>> + free(prof->dfa);
>> + prof->dfa = NULL;
>> goto out;
>> }
>> */
>> }
>> - /*
>> - * Post process subdomain(s):
>> - *
>> - * They are chained from the toplevel subdomain pointer
>> - * thru each <codomain> next pointer.
>> -
>> - * i.e first subdomain is list->subdomain
>> - * second subdomain is list->subdomain->next
>> - *
>> - * N.B sub-subdomains are not valid so:
>> - * if (list->subdomain) {
>> - * assert(list->subdomain->subdomain == NULL)
>> - * }
>> - */
>> - if (process_hat_regex(cod) != 0)
>> - goto out;
>>
>> error = 0;
>>
>> @@ -1039,59 +1024,57 @@ fail:
>> }
>>
>>
>> -int post_process_mnt_ents(struct codomain *cod)
>> +int post_process_mnt_ents(Profile *prof)
>> {
>> int ret = TRUE;
>> int count = 0;
>>
>> /* Add fns for rules that should be added to policydb here */
>> - if (cod->mnt_ents && kernel_supports_mount) {
>> + if (prof->mnt_ents && kernel_supports_mount) {
>> struct mnt_entry *entry;
>> - list_for_each(cod->mnt_ents, entry) {
>> - if (!process_mnt_entry(cod->policy_rules, entry))
>> + list_for_each(prof->mnt_ents, entry) {
>> + if (!process_mnt_entry(prof->policy.rules, entry))
>> ret = FALSE;
>> count++;
>> }
>> - } else if (cod->mnt_ents && !kernel_supports_mount)
>> - pwarn("profile %s mount rules not enforced\n", cod->name);
>> + } else if (prof->mnt_ents && !kernel_supports_mount)
>> + pwarn("profile %s mount rules not enforced\n", prof->name);
>>
>> - cod->policy_rule_count += count;
>> + prof->policy.count += count;
>>
>> return ret;
>> }
>>
>> -int post_process_policydb_ents(struct codomain *cod)
>> +int post_process_policydb_ents(Profile *prof)
>> {
>> - if (!post_process_mnt_ents(cod))
>> + if (!post_process_mnt_ents(prof))
>> return FALSE;
>>
>> return TRUE;
>> }
>>
>> -int process_policydb(struct codomain *cod)
>> +int process_profile_policydb(Profile *prof)
>> {
>> int error = -1;
>>
>> - cod->policy_rules = aare_new_ruleset(0);
>> - if (!cod->policy_rules)
>> + prof->policy.rules = aare_new_ruleset(0);
>> + if (!prof->policy.rules)
>> goto out;
>>
>> - if (!post_process_policydb_ents(cod))
>> + if (!post_process_policydb_ents(prof))
>> goto out;
>>
>> - if (cod->policy_rule_count > 0) {
>> - cod->policy_dfa = aare_create_dfa(cod->policy_rules,
>> - &cod->policy_dfa_size,
>> + if (prof->policy.count > 0) {
>> + prof->policy.dfa = aare_create_dfa(prof->policy.rules,
>> + &prof->policy.size,
>> dfaflags);
>> - aare_delete_ruleset(cod->policy_rules);
>> - cod->policy_rules = NULL;
>> - if (!cod->policy_dfa)
>> + aare_delete_ruleset(prof->policy.rules);
>> + prof->policy.rules = NULL;
>> + if (!prof->policy.dfa)
>> goto out;
>> }
>>
>> aare_reset_matchflags();
>> - if (process_hat_policydb(cod) != 0)
>> - goto out;
>>
>> error = 0;
>>
>> @@ -1105,6 +1088,9 @@ void reset_regex(void)
>> }
>>
>> #ifdef UNIT_TEST
>> +
>> +#include "unit_test.h"
>> +
>> static int test_filter_slashes(void)
>> {
>> int rc = 0;
>> diff --git a/parser/parser_symtab.c b/parser/parser_symtab.c
>> index 322551e..f10a264 100644
>> --- a/parser/parser_symtab.c
>> +++ b/parser/parser_symtab.c
>> @@ -539,6 +539,9 @@ void free_symtabs(void)
>> }
>>
>> #ifdef UNIT_TEST
>> +
>> +#include "unit_test.h"
>> +
>> int main(void)
>> {
>> int rc = 0;
>> diff --git a/parser/parser_variable.c b/parser/parser_variable.c
>> index c17123a..460afbb 100644
>> --- a/parser/parser_variable.c
>> +++ b/parser/parser_variable.c
>> @@ -28,6 +28,7 @@
>> /* #define DEBUG */
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "mount.h"
>>
>> static inline char *get_var_end(char *var)
>> @@ -136,12 +137,11 @@ static int expand_entry_variables(char **name, void *entry,
>> int (dup_and_chain)(void *))
>> {
>> struct set_value *valuelist;
>> - int ret = TRUE;
>> char *value;
>> struct var_string *split_var;
>>
>> if (!entry) /* can happen when entry is optional */
>> - return ret;
>> + return 0;
>>
>> while ((split_var = split_out_var(*name))) {
>> valuelist = get_set_var(split_var->var);
>> @@ -167,7 +167,7 @@ static int expand_entry_variables(char **name, void *entry,
>> split_var->prefix ? split_var->prefix : "",
>> value,
>> split_var->suffix ? split_var->suffix : "") == -1)
>> - return FALSE;
>> + return -1;
>>
>> while ((value = get_next_set_value(&valuelist))) {
>> if (!dup_and_chain(entry)) {
>> @@ -180,12 +180,12 @@ static int expand_entry_variables(char **name, void *entry,
>> if (asprintf(name, "%s%s%s",
>> split_var->prefix ? split_var->prefix : "", value,
>> split_var->suffix ? split_var->suffix : "") == -1)
>> - return FALSE;
>> + return -1;
>> }
>>
>> free_var_string(split_var);
>> }
>> - return ret;
>> + return 0;
>> }
>>
>> int clone_and_chain_cod(void *v)
>> @@ -215,63 +215,60 @@ int clone_and_chain_mnt(void *v)
>>
>> static int process_variables_in_entries(struct cod_entry *entry_list)
>> {
>> - int ret = TRUE, rc;
>> + int error = 0;
>> struct cod_entry *entry;
>>
>> list_for_each(entry_list, entry) {
>> - rc = expand_entry_variables(&entry->name, entry,
>> - clone_and_chain_cod);
>> - if (!rc)
>> - return FALSE;
>> + error = expand_entry_variables(&entry->name, entry,
>> + clone_and_chain_cod);
>> + if (error)
>> + return error;
>> }
>>
>> - return ret;
>> + return 0;
>> }
>>
>> /* does not currently support expansion of vars in options */
>> static int process_variables_in_mnt_entries(struct mnt_entry *entry_list)
>> {
>> - int ret = TRUE, rc;
>> + int error = 0;
>> struct mnt_entry *entry;
>>
>> list_for_each(entry_list, entry) {
>> - rc = expand_entry_variables(&entry->mnt_point, entry,
>> - clone_and_chain_mnt);
>> - if (!rc)
>> - return FALSE;
>> - rc = expand_entry_variables(&entry->device, entry,
>> - clone_and_chain_mnt);
>> - if (!rc)
>> - return FALSE;
>> - rc = expand_entry_variables(&entry->trans, entry,
>> - clone_and_chain_mnt);
>> - if (!rc)
>> - return FALSE;
>> + error = expand_entry_variables(&entry->mnt_point, entry,
>> + clone_and_chain_mnt);
>> + if (error)
>> + return error;
>> + error = expand_entry_variables(&entry->device, entry,
>> + clone_and_chain_mnt);
>> + if (error)
>> + return error;
>> + error = expand_entry_variables(&entry->trans, entry,
>> + clone_and_chain_mnt);
>> + if (error)
>> + return error;
>>
>> }
>>
>> - return ret;
>> + return 0;
>> }
>>
>> -int process_variables(struct codomain *cod)
>> +int process_profile_variables(Profile *prof)
>> {
>> int error = 0;
>>
>> - if (!process_variables_in_entries(cod->entries)) {
>> - error = -1;
>> - }
>> + error = process_variables_in_entries(prof->entries);
>>
>> - if (!process_variables_in_mnt_entries(cod->mnt_ents)) {
>> - error = -1;
>> - }
>> + if (!error)
>> + error = process_variables_in_mnt_entries(prof->mnt_ents);
>>
>> - if (process_hat_variables(cod) != 0) {
>> - error = -1;
>> - }
>> return error;
>> }
>>
>> #ifdef UNIT_TEST
>> +
>> +#include "unit_test.h"
>> +
>> int test_get_var_end(void)
>> {
>> int rc = 0;
>> diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y
>> index 4939d4f..8d4fb37 100644
>> --- a/parser/parser_yacc.y
>> +++ b/parser/parser_yacc.y
>> @@ -32,6 +32,7 @@
>> /* #define DEBUG */
>>
>> #include "parser.h"
>> +#include "profile.h"
>> #include "mount.h"
>> #include "parser_include.h"
>> #include <unistd.h>
>> @@ -75,7 +76,7 @@ struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
>> struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
>> char *transition);
>>
>> -void add_local_entry(struct codomain *cod);
>> +void add_local_entry(Profile *prof);
>>
>> %}
>>
>> @@ -156,12 +157,12 @@ void add_local_entry(struct codomain *cod);
>> char *flag_id;
>> char *mode;
>> struct aa_network_entry *network_entry;
>> - struct codomain *cod;
>> + Profile *prof;
>> struct cod_net_entry *net_entry;
>> struct cod_entry *user_entry;
>> struct mnt_entry *mnt_entry;
>>
>> - struct flagval flags;
>> + flagvals flags;
>> int fmode;
>> uint64_t cap;
>> unsigned int allowed_protocol;
>> @@ -179,12 +180,12 @@ void add_local_entry(struct codomain *cod);
>> %type <id> TOK_CONDID
>> %type <mode> TOK_MODE
>> %type <fmode> file_mode
>> -%type <cod> profile_base
>> -%type <cod> profile
>> -%type <cod> rules
>> -%type <cod> hat
>> -%type <cod> local_profile
>> -%type <cod> cond_rule
>> +%type <prof> profile_base
>> +%type <prof> profile
>
> Extra space here...
>
>> +%type <prof> rules
>> +%type <prof> hat
>> +%type <prof> local_profile
>> +%type <prof> cond_rule
>> %type <network_entry> network_rule
>> %type <user_entry> rule
>> %type <user_entry> file_rule
>> @@ -245,37 +246,37 @@ opt_id: { /* nothing */ $$ = NULL; }
>>
>> profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
>> {
>> - struct codomain *cod = $5;
>> + Profile *prof = $5;
>>
>> - if (!cod) {
>> + if (!prof) {
>> yyerror(_("Memory allocation error."));
>> }
>>
>> - cod->name = $1;
>> - cod->attachment = $2;
>> + prof->name = $1;
>> + prof->attachment = $2;
>> if ($2 && $2[0] != '/')
>> /* we don't support variables as part of the profile
>> * name or attachment atm
>> */
>> yyerror(_("Profile attachment must begin with a '/'."));
>> - cod->flags = $3;
>> + prof->flags = $3;
>> if (force_complain)
>> - cod->flags.complain = 1;
>> + prof->flags.complain = 1;
>>
>> - post_process_file_entries(cod);
>> - post_process_mnt_entries(cod);
>> + post_process_file_entries(prof);
>> + post_process_mnt_entries(prof);
>> PDEBUG("%s: flags='%s%s'\n",
>> $2,
>> - cod->flags.complain ? "complain, " : "",
>> - cod->flags.audit ? "audit" : "");
>> + prof->flags.complain ? "complain, " : "",
>> + prof->flags.audit ? "audit" : "");
>>
>> - $$ = cod;
>> + $$ = prof;
>>
>> };
>>
>> profile: opt_profile_flag opt_ns profile_base
>> {
>> - struct codomain *cod = $3;
>> + Profile *prof = $3;
>> if ($2)
>> PDEBUG("Matched: %s://%s { ... }\n", $2, $3->name);
>> else
>> @@ -284,31 +285,31 @@ profile: opt_profile_flag opt_ns profile_base
>> if ($3->name[0] != '/' && !($1 || $2))
>> yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
>>
>> - cod->ns = $2;
>> + prof->ns = $2;
>> if ($1 == 2)
>> - cod->flags.hat = 1;
>> - $$ = cod;
>> + prof->flags.hat = 1;
>> + $$ = prof;
>> };
>>
>> local_profile: TOK_PROFILE profile_base
>> {
>>
>> - struct codomain *cod = $2;
>> + Profile *prof = $2;
>>
>> if ($2)
>> - PDEBUG("Matched: local profile %s { ... }\n", cod->name);
>> - cod->local = 1;
>> - $$ = cod;
>> + PDEBUG("Matched: local profile %s { ... }\n", prof->name);
>> + prof->local = 1;
>> + $$ = prof;
>> };
>>
>> hat: hat_start profile_base
>> {
>> - struct codomain *cod = $2;
>> + Profile *prof = $2;
>> if ($2)
>> - PDEBUG("Matched: hat %s { ... }\n", cod->name);
>> + PDEBUG("Matched: hat %s { ... }\n", prof->name);
>>
>> - cod->flags.hat = 1;
>> - $$ = cod;
>> + prof->flags.hat = 1;
>> + $$ = prof;
>> };
>>
>> preamble: { /* nothing */ }
>> @@ -414,7 +415,7 @@ valuelist: valuelist TOK_VALUE
>> }
>>
>> flags: { /* nothing */
>> - struct flagval fv = { 0, 0, 0, 0 };
>> + struct flagvals fv = { 0, 0, 0, 0 };
>
> This seems odd, isn't flagvals a class? Would this be better written as
> flagvals fv = flagvals(0,0,0,0); ? (Or are bits of cfront-style
> implementation with us still? :)
>
>>
>> $$ = fv;
>> };
>> @@ -461,7 +462,7 @@ flagvals: flagval
>>
>> flagval: TOK_VALUE
>> {
>> - struct flagval fv = { 0, 0, 0, 0 };
>> + struct flagvals fv = { 0, 0, 0, 0 };
>> if (strcmp($1, "debug") == 0) {
>> yyerror(_("Profile flag 'debug' is no longer valid."));
>> } else if (strcmp($1, "complain") == 0) {
>> @@ -514,13 +515,12 @@ opt_prefix: opt_audit_flag opt_deny opt_owner_flag
>> }
>>
>> rules: { /* nothing */
>> - struct codomain *cod = NULL;
>> - cod = (struct codomain *) calloc(1, sizeof(struct codomain));
>> - if (!cod) {
>> + Profile *prof = new Profile();
>> + if (!prof) {
>> yyerror(_("Memory allocation error."));
>> }
>
> Funny whitespace around here..
>
>
>>
>> - $$ = cod;
>> + $$ = prof;
>> };
>>
>> rules: rules opt_prefix rule
>> @@ -580,8 +580,8 @@ rules: rules opt_prefix TOK_OPEN rules TOK_CLOSE
>> add_entry_to_policy($1, entry);
>> }
>> $4->entries = NULL;
>> - // fix me transfer rules and free sub codomain
>> - free_policy($4);
>> + // fix me transfer rules and free sub profile
>> + delete $4;
>> $$ = $1;
>> };
>>
>> @@ -594,40 +594,40 @@ rules: rules opt_prefix network_rule
>> yyerror(_("owner prefix not allowed"));
>> if (!$3)
>> yyerror(_("Assert: `network_rule' return invalid protocol."));
>> - if (!$1->network_allowed) {
>> - $1->network_allowed = (unsigned int *) calloc(get_af_max(),
>> + if (!$1->net.allow) {
>> + $1->net.allow = (unsigned int *) calloc(get_af_max(),
>> sizeof(unsigned int));
>> - $1->audit_network = (unsigned int *)calloc(get_af_max(),
>> + $1->net.audit = (unsigned int *)calloc(get_af_max(),
>> sizeof(unsigned int));
>> - $1->deny_network = (unsigned int *)calloc(get_af_max(),
>> + $1->net.deny = (unsigned int *)calloc(get_af_max(),
>> sizeof(unsigned int));
>> - $1->quiet_network = (unsigned int *)calloc(get_af_max(),
>> + $1->net.quiet = (unsigned int *)calloc(get_af_max(),
>> sizeof(unsigned int));
>> - if (!$1->network_allowed || !$1->audit_network ||
>> - !$1->deny_network || !$1->quiet_network)
>> + if (!$1->net.allow || !$1->net.audit ||
>> + !$1->net.deny || !$1->net.quiet)
>> yyerror(_("Memory allocation error."));
>> }
>> list_for_each_safe($3, entry, tmp) {
>> if (entry->type > SOCK_PACKET) {
>> /* setting mask instead of a bit */
>> if ($2.deny) {
>> - $1->deny_network[entry->family] |= entry->type;
>> + $1->net.deny[entry->family] |= entry->type;
>> if (!$2.audit)
>> - $1->quiet_network[entry->family] |= entry->type;
>> + $1->net.quiet[entry->family] |= entry->type;
>> } else {
>> - $1->network_allowed[entry->family] |= entry->type;
>> + $1->net.allow[entry->family] |= entry->type;
>> if ($2.audit)
>> - $1->audit_network[entry->family] |= entry->type;
>> + $1->net.audit[entry->family] |= entry->type;
>> }
>> } else {
>> if ($2.deny) {
>> - $1->deny_network[entry->family] |= 1 << entry->type;
>> + $1->net.deny[entry->family] |= 1 << entry->type;
>> if (!$2.audit)
>> - $1->quiet_network[entry->family] |= 1 << entry->type;
>> + $1->net.quiet[entry->family] |= 1 << entry->type;
>> } else {
>> - $1->network_allowed[entry->family] |= 1 << entry->type;
>> + $1->net.allow[entry->family] |= 1 << entry->type;
>> if ($2.audit)
>> - $1->audit_network[entry->family] |= 1 << entry->type;
>> + $1->net.audit[entry->family] |= 1 << entry->type;
>> }
>> }
>> free(entry);
>> @@ -665,12 +665,12 @@ rules: rules opt_prefix capability
>> yyerror(_("owner prefix not allow on mount rules"));
>>
>> if ($2.deny)
>> - $1->deny_caps |= $3;
>> + $1->caps.deny |= $3;
>> else
>> - $1->capabilities |= $3;
>> + $1->caps.allow |= $3;
>>
>> if (!$2.audit)
>> - $1->quiet_caps |= $3;
>> + $1->caps.quiet |= $3;
>> $$ = $1;
>> };
>>
>> @@ -793,40 +793,40 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
>>
>> cond_rule: TOK_IF expr TOK_OPEN rules TOK_CLOSE
>> {
>> - struct codomain *ret = NULL;
>> + Profile *ret = NULL;
>> PDEBUG("Matched: found conditional rules\n");
>> if ($2) {
>> ret = $4;
>> } else {
>> - free_policy($4);
>> + delete $4;
>> }
>> $$ = ret;
>> }
>>
>> cond_rule: TOK_IF expr TOK_OPEN rules TOK_CLOSE TOK_ELSE TOK_OPEN rules TOK_CLOSE
>> {
>> - struct codomain *ret = NULL;
>> + Profile *ret = NULL;
>> PDEBUG("Matched: found conditional else rules\n");
>> if ($2) {
>> ret = $4;
>> - free_policy($8);
>> + delete $8;
>> } else {
>> ret = $8;
>> - free_policy($4);
>> + delete $4;
>> }
>> $$ = ret;
>> }
>>
>> cond_rule: TOK_IF expr TOK_OPEN rules TOK_CLOSE TOK_ELSE cond_rule
>> {
>> - struct codomain *ret = NULL;
>> + Profile *ret = NULL;
>> PDEBUG("Matched: found conditional else-if rules\n");
>> if ($2) {
>> ret = $4;
>> - free_policy($7);
>> + delete $7;
>> } else {
>> ret = $7;
>> - free_policy($4);
>> + delete $4;
>> }
>> $$ = ret;
>> }
>> @@ -1222,25 +1222,25 @@ struct cod_entry *do_file_rule(char *ns, char *id, int mode,
>> /* Note: NOT currently in use, used for
>> * /foo x -> { /bah, } style transitions
>> */
>> -void add_local_entry(struct codomain *cod)
>> +void add_local_entry(Profile *prof)
>> {
>> /* ugh this has to be called after the hat is attached to its parent */
>> - if (cod->local_mode) {
>> + if (prof->local_mode) {
>> struct cod_entry *entry;
>> - char *trans = (char *) malloc(strlen(cod->parent->name) +
>> - strlen(cod->name) + 3);
>> - char *name = strdup(cod->name);
>> + char *trans = (char *) malloc(strlen(prof->parent->name) +
>> + strlen(prof->name) + 3);
>> + char *name = strdup(prof->name);
>> if (!trans)
>> yyerror(_("Memory allocation error."));
>> - sprintf(name, "%s//%s", cod->parent->name, cod->name);
>> + sprintf(name, "%s//%s", prof->parent->name, prof->name);
>>
>> - entry = new_entry(NULL, name, cod->local_mode, NULL);
>> - entry->audit = cod->local_audit;
>> + entry = new_entry(NULL, name, prof->local_mode, NULL);
>> + entry->audit = prof->local_audit;
>> entry->nt_name = trans;
>> if (!entry)
>> yyerror(_("Memory allocation error."));
>>
>> - add_entry_to_policy(cod, entry);
>> + add_entry_to_policy(prof, entry);
>> }
>> }
>>
>> diff --git a/parser/profile.cc b/parser/profile.cc
>> new file mode 100644
>> index 0000000..3033985
>> --- /dev/null
>> +++ b/parser/profile.cc
>> @@ -0,0 +1,89 @@
>> +/*
>> + * Copyright (c) 2012
>
> 2013 too :)
>
>> + * 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.
>> + */
>> +
>> +#include "profile.h"
>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +
>> +bool deref_profileptr_lt::operator()(Profile * const &lhs, Profile * const &rhs) const
>> +{
>> + return *lhs < *rhs;
>> +};
>> +
>> +pair<ProfileList::iterator,bool> ProfileList::insert(Profile *p)
>> +{
>> + return list.insert(p);
>> +}
>> +
>> +void ProfileList::erase(ProfileList::iterator pos)
>> +{
>> + list.erase(pos);
>> +}
>> +
>> +void ProfileList::clear(void)
>> +{
>> + for(ProfileList::iterator i = list.begin(); i != list.end(); ) {
>> + ProfileList::iterator k = i++;
>> + delete *k;
>> + list.erase(k);
>> + }
>> +}
>> +
>> +void ProfileList::dump(void)
>> +{
>> + for(ProfileList::iterator i = list.begin(); i != list.end(); i++) {
>> + (*i)->dump();
>> + }
>> +}
>> +
>> +void ProfileList::dump_profile_names(bool children)
>> +{
>> + for (ProfileList::iterator i = list.begin(); i != list.end();i++) {
>> + (*i)->dump_name(true);
>> + printf("\n");
>> + if (children && !(*i)->hat_table.empty())
>> + (*i)->hat_table.dump_profile_names(children);
>> + }
>> +}
>> +
>> +Profile::~Profile()
>> +{
>> + hat_table.clear();
>> + free_cod_entries(entries);
>> + free_mnt_entries(mnt_ents);
>> + if (dfa.rules)
>> + aare_delete_ruleset(dfa.rules);
>> + if (dfa.dfa)
>> + free(dfa.dfa);
>> + if (policy.rules)
>> + aare_delete_ruleset(policy.rules);
>> + if (policy.dfa)
>> + free(policy.dfa);
>> + if (name)
>> + free(name);
>> + if (attachment)
>> + free(attachment);
>> + if (ns)
>> + free(ns);
>> + if (net.allow)
>> + free(net.allow);
>> + if (net.audit)
>> + free(net.audit);
>> + if (net.deny)
>> + free(net.deny);
>> + if (net.quiet)
>> + free(net.quiet);
>> +}
>> +
>> diff --git a/parser/profile.h b/parser/profile.h
>> new file mode 100644
>> index 0000000..67e1b60
>> --- /dev/null
>> +++ b/parser/profile.h
>> @@ -0,0 +1,241 @@
>> +/*
>> + * Copyright (c) 2012
>
> And 2013...
>
hah well the first iteration of these was in 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.
>> + */
>> +#ifndef __AA_PROFILE_H
>> +#define __AA_PROFILE_H
>> +
>> +#include <set>
>> +
>> +#include "parser.h"
>> +
>> +class Profile;
>> +
>> +class block {
>> +public:
>> +
>> +};
>
> I don't get it. :)
>
hehe well uh you see it was planning for future structing but it doesn't
make a whole lot of sense here.
The block is going to be a container of rules, so a profile is a block
but so is
audit {
}
>> +
>> +
>> +struct deref_profileptr_lt {
>> + bool operator()(Profile * const &lhs, Profile * const &rhs) const;
>> +};
>> +
>> +class ProfileList {
>> +public:
>> + set<Profile *, deref_profileptr_lt> list;
>> +
>> + typedef set<Profile *, deref_profileptr_lt>::iterator iterator;
>> + iterator begin() { return list.begin(); }
>> + iterator end() { return list.end(); }
>> +
>> + ProfileList() { };
>> + virtual ~ProfileList() { clear(); }
>> + virtual bool empty(void) { return list.empty(); }
>> + virtual pair<ProfileList::iterator,bool> insert(Profile *);
>> + virtual void erase(ProfileList::iterator pos);
>
> Why virtual? nothing subclasses from ProfileList.
>
good question, I'm not sure why I did that
>> + void clear(void);
>> + void dump(void);
>> + void dump_profile_names(bool children);
>> +};
>> +
>> +
>> +
>> +class flagvals {
>> +public:
>> + int hat;
>> + int complain;
>> + int audit;
>> + int path;
>> +
>> + void dump(void)
>> + {
>> + printf("Profile Mode:\t");
>> +
>> + if (complain)
>> + printf("Complain");
>> + else
>> + printf("Enforce");
>
> Does this need to grow an 'Unconfined' option as well? Or is that
> further along than this?
>
yes it will grow unconfined in a later patch that actually allows
specifying the unconfined mode
>> +
>> + if (audit)
>> + printf(", Audit");
>> +
>> + if (hat)
>> + printf(", Hat");
>> +
>> + printf("\n");
>> + }
>> +};
>> +
>> +struct capabilities {
>> + uint64_t allow;
>> + uint64_t audit;
>> + uint64_t deny;
>> + uint64_t quiet;
>> +
>> + capabilities(void) { allow = audit = deny = quiet; }
>
> Probably this is missing = 0ull; maybe re-write like the dfa_stuff()
> constructor?
>
yes its missing a 0 and this is a constructor that does the same thing
its just a different way of acheiving it. Why one or the other? Heck if
I know, I doubt I would have even noticed the difference without you
pointing it out;
>> +
>> + void dump()
>> + {
>> + if (allow != 0ull)
>> + __debug_capabilities(allow, "Capabilities");
>> + if (audit != 0ull)
>> + __debug_capabilities(audit, "Audit Caps");
>> + if (deny != 0ull)
>> + __debug_capabilities(deny, "Deny Caps");
>> + if (quiet != 0ull)
>> + __debug_capabilities(quiet, "Quiet Caps");
>> + };
>
> And I'm no fan of the indenting in this psuedo-method...
>
>
hrmm me neither
>> +};
>> +
>> +struct network {
>> + unsigned int *allow; /* array of type masks
>> + * indexed by AF_FAMILY */
>
> Funny indenting on the * indexed ...
>
yep
>> + unsigned int *audit;
>> + unsigned int *deny;
>> + unsigned int *quiet;
>> +
>> + network(void) { allow = audit = deny = quiet = NULL; }
>> +
>> + void dump(void) {
>> + if (allow)
>> + __debug_network(allow, "Network");
>> + if (audit)
>> + __debug_network(audit, "Audit Net");
>> + if (deny)
>> + __debug_network(deny, "Deny Net");
>> + if (quiet)
>> + __debug_network(quiet, "Quiet Net");
>> + }
>> +};
>> +
>> +struct dfa_stuff {
>> + aare_ruleset_t *rules;
>> + int count;
>> + void *dfa;
>> + size_t size;
>> +
>> + dfa_stuff(void): rules(NULL), count(0), dfa(NULL), size(0) { }
>> +};
>
> Haha, awesome name.
>
:)
>> +
>> +class Profile {
>> +public:
>> + char *ns;
>> + char *name;
>> + char *attachment;
>> + struct alt_name *altnames;
>> + void *xmatch;
>> + size_t xmatch_size;
>> + int xmatch_len;
>> +
>> + /* char *sub_name; */ /* subdomain name or NULL */
>> + /* int default_deny; */ /* TRUE or FALSE */
>> + int local;
>> + int local_mode; /* true if local, not hat */
>> + int local_audit;
>> +
>> + Profile *parent;
>> +
>> + struct flagvals flags;
>> + struct capabilities caps;
>> + struct network net;
>> +
>> + struct aa_rlimits rlimits;
>> +
>> + char *exec_table[AA_EXEC_COUNT];
>> + struct cod_entry *entries;
>> + struct mnt_entry *mnt_ents;
>> +
>> + ProfileList hat_table;
>> +
>> + struct dfa_stuff dfa;
>> + struct dfa_stuff policy;
>> +
>> + Profile(void)
>> + {
>> + ns = name = attachment = NULL;
>> + altnames = NULL;
>> + xmatch = NULL;
>> + xmatch_size = 0;
>> + xmatch_len = 0;
>> +
>> + local = local_mode = local_audit = 0;
>> +
>> + parent = NULL;
>> +
>> + flags = { 0, 0, 0, 0};
>> + rlimits = { 0 };
>> +
>> + std:fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL);
>> +
>> + entries = NULL;
>> + mnt_ents = NULL;
>> +
>> +
>> + };
>> +
>> + virtual ~Profile();
>> +
>> + bool operator<(Profile const &rhs)const
>> + {
>> + if (ns) {
>> + if (rhs.ns) {
>> + int res = strcmp(ns, rhs.ns);
>> + if (res != 0)
>> + return res < 0;
>> + } else
>> + return false;
>> + } else if (rhs.ns)
>> + return true;
>> + return strcmp(name, rhs.name) < 0;
>> + }
>> +
>> + void dump(void)
>> + {
>> + if (ns)
>> + printf("Ns:\t\t%s\n", ns);
>> +
>> + if (name)
>> + printf("Name:\t\t%s\n", name);
>> + else
>> + printf("Name:\t\t<NULL>\n");
>> +
>> + if (local) {
>> + if (parent)
>> + printf("Local To:\t%s\n", parent->name);
>> + else
>> + printf("Local To:\t<NULL>\n");
>> + }
>> +
>> + flags.dump();
>> + caps.dump();
>> + net.dump();
>> +
>> + if (entries)
>> + debug_cod_entries(entries);
>> +
>> + printf("\n");
>> + hat_table.dump();
>> + }
>> +
>> + void dump_name(bool fqp)
>> + {
>> + if (fqp && parent) {
>> + parent->dump_name(fqp);
>> + printf("//%s", name);
>> + } else
>> + printf("%s", name);
>> + }
>> +};
>> +
>> +
>> +#endif /* __AA_PROFILE_H */
>> diff --git a/parser/unit_test.h b/parser/unit_test.h
>> new file mode 100644
>> index 0000000..3b7cc5b
>> --- /dev/null
>> +++ b/parser/unit_test.h
>> @@ -0,0 +1,53 @@
>> +/*
>> + * Copyright (c) 2012
>
> 2013
>
>> + * 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.
>> + */
>> +#ifndef __AA_UNIT_TEST_H
>> +#define __AA_UNIT_TEST_H
>> +
>> +#ifdef UNIT_TEST
>> +/* For the unit-test builds, we must include function stubs for stuff that
>> + * only exists in the excluded object files; everything else should live
>> + * in parser_common.c.
>> + */
>> +
>> +#include <stdarg.h>
>> +#include <stdlib.h>
>> +#include <linux/limits.h>
>> +
>> +#undef _
>> +#define _(s) (s)
>> +
>> +/* parser_yacc.y */
>> +void yyerror(const char *msg, ...)
>> +{
>> + va_list arg;
>> + char buf[PATH_MAX];
>> +
>> + va_start(arg, msg);
>> + vsnprintf(buf, sizeof(buf), msg, arg);
>> + va_end(arg);
>> +
>> + PERROR(_("AppArmor parser error: %s\n"), buf);
>> +
>> + exit(1);
>> +}
>> +
>> +#define MY_TEST(statement, error) \
>> + if (!(statement)) { \
>> + PERROR("FAIL: %s\n", error); \
>> + rc = 1; \
>> + }
>> +
>> +#endif
>> +
>> +#endif /* __AA_UNIT_TEST_H */
>> --
>> 1.8.1.2
>
> Thanks
>
>
>
More information about the AppArmor
mailing list