diff --git a/parser/parser.h b/parser/parser.h index 74896e072..ef9d452e4 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -36,7 +36,8 @@ struct cod_pattern { }; struct cod_entry { - char * name ; + char *namespace; + char *name ; struct codomain *codomain ; /* Special codomain defined * just for this executable */ int mode ; /* mode is 'or' of AA_* bits */ @@ -67,6 +68,7 @@ struct aa_network_entry { }; struct codomain { + char *namespace; char *name; /* codomain name */ char *sub_name; /* subdomain name or NULL */ int default_deny; /* TRUE or FALSE */ @@ -176,6 +178,7 @@ struct var_string { extern char *progname; extern char *subdomainbase; extern char *profilename; +extern char *profile_namespace; /* from parser_main */ extern int force_complain; @@ -201,7 +204,7 @@ extern char *processunquoted(char *string, int len); extern int get_keyword_token(const char *keyword); extern char *process_var(const char *var); extern int parse_mode(const char *mode); -extern struct cod_entry *new_entry(char *id, int mode); +extern struct cod_entry *new_entry(char *namespace, char *id, int mode); extern struct cod_net_entry *new_network_entry(int action, struct ipv4_endpoints *addrs, char *interface); diff --git a/parser/parser_interface.c b/parser/parser_interface.c index 92af3d378..9ffafc84e 100644 --- a/parser/parser_interface.c +++ b/parser/parser_interface.c @@ -702,6 +702,15 @@ int sd_serialize_top_profile(sd_serialize *p, struct codomain *profile) if (!sd_write32(p, version)) return 0; + + if (profile_namespace) { + if (!sd_write_string(p, profile_namespace, "namespace")) + return 0; + } else if (profile->namespace) { + if (!sd_write_string(p, profile->namespace, "namespace")) + return 0; + } + return sd_serialize_profile(p, profile, profile->parent ? 1 : 0); } @@ -746,16 +755,38 @@ int sd_serialize_codomain(int option, struct codomain *cod) free(filename); if (option == OPTION_REMOVE) { - char *name; + char *name, *ns = NULL; + int len = 0; + + if (profile_namespace) { + len += strlen(profile_namespace) + 1; + ns = profile_namespace; + } else if (cod->namespace) { + len += strlen(cod->namespace) + 1; + ns = cod->namespace; + } if (cod->parent) { name = malloc(strlen(cod->name) + 3 + - strlen(cod->parent->name)); + strlen(cod->parent->name) + len); if (!name) { - PERROR(_("Unable to remove ^%s\n"), cod->name); + PERROR(_("Memory Allocation Error: Unable to remove ^%s\n"), cod->name); error = -errno; goto exit; } - sprintf(name, "%s//%s", cod->parent->name, cod->name); + if (ns) + sprintf(name, "%s:%s//%s", ns, + cod->parent->name, cod->name); + else + sprintf(name, "%s//%s", cod->parent->name, + cod->name); + } else if (ns) { + name = malloc(len + strlen(cod->name) + 1); + if (!name) { + PERROR(_("Memory Allocation Error: Unable to remove %s:%s."), ns, cod->name); + error = -errno; + goto exit; + } + sprintf(name, "%s:%s", ns, cod->name); } else { name = cod->name; } @@ -763,7 +794,7 @@ int sd_serialize_codomain(int option, struct codomain *cod) wsize = write(fd, name, size); if (wsize < 0) error = -errno; - if (cod->parent) + if (cod->parent || ns) free(name); } else { diff --git a/parser/parser_lex.l b/parser/parser_lex.l index ac5b1fab8..5792962d4 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -346,14 +346,21 @@ ADD_ASSIGN \+= return TOK_HAT; } -{KEYWORD} { +{COLON} { + PDEBUG("Found a colon\n"); + return TOK_COLON; + } + +{VARIABLE_NAME} { int token = get_keyword_token(yytext); /* special cases */ switch (token) { case -1: /* no token found */ - yyerror(_("Found unexpected keyword: '%s'"), yytext); + yylval = (YYSTYPE) processunquoted(yytext, yyleng); + PDEBUG("Found id: \"%s\"\n", yylval); + return TOK_ID; break; case TOK_VIA: BEGIN(IF_MODE); /* look for an interface name next */ diff --git a/parser/parser_main.c b/parser/parser_main.c index 5894d623a..d821a9436 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -67,6 +67,7 @@ char *subdomainbase = NULL; char *profilename; char *match_string = NULL; int regex_type = AARE_DFA; +char *profile_namespace = NULL; extern int current_lineno; @@ -89,6 +90,7 @@ struct option long_options[] = { {"stdout", 0, 0, 'S'}, {"match-string", 1, 0, 'm'}, {"quiet", 0, 0, 'q'}, + {"namespace", 1, 0, 'n'}, {NULL, 0, 0, 0}, }; @@ -119,6 +121,7 @@ static void display_usage(char *command) "-f n, --subdomainfs n Set location of apparmor filesystem\n" "-S, --stdout Write output to stdout\n" "-m n, --match-string n Use only match features n\n" + "-n n, --namespace n Set Namespace for the profile\n" "-q, --quiet Don't emit warnings\n", command); } @@ -149,7 +152,7 @@ static int process_args(int argc, char *argv[]) int count = 0; option = OPTION_ADD; - while ((c = getopt_long(argc, argv, "adf:hrRvpI:b:CNSm:q", long_options, &o)) != -1) + while ((c = getopt_long(argc, argv, "adf:hrRvpI:b:CNSm:qn:", long_options, &o)) != -1) { switch (c) { case 0: @@ -215,6 +218,9 @@ static int process_args(int argc, char *argv[]) case 'q': conf_quiet = 1; break; + case 'n': + profile_namespace = strdup(optarg); + break; default: display_usage(progname); exit(0); diff --git a/parser/parser_merge.c b/parser/parser_merge.c index 2bcdab987..de9c5e892 100644 --- a/parser/parser_merge.c +++ b/parser/parser_merge.c @@ -43,7 +43,20 @@ static int file_comp(const void *c1, const void *c2) struct cod_entry **e1, **e2; e1 = (struct cod_entry **)c1; e2 = (struct cod_entry **)c2; + int res = 0; + //PERROR("strcmp %s %s\n", (*e1)->name, (*e2)->name); + if ((*e1)->namespace) { + if ((*e2)->namespace) + res = strcmp((*e1)->namespace, (*e2)->namespace); + else + return 1; + } else if ((*e2)->namespace) { + return -1; + } + if (res) + return res; + return strcmp((*e1)->name, (*e2)->name); } diff --git a/parser/parser_misc.c b/parser/parser_misc.c index f61c36052..cd186d0a4 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -632,7 +632,7 @@ struct cod_net_entry *new_network_entry(int action, return entry; } -struct cod_entry *new_entry(char *id, int mode) +struct cod_entry *new_entry(char *namespace, char *id, int mode) { struct cod_entry *entry = NULL; @@ -640,7 +640,8 @@ struct cod_entry *new_entry(char *id, int mode) if (!entry) return NULL; - entry->name = id ? id : NULL; + entry->namespace = namespace; + entry->name = id; entry->mode = mode; entry->deny = FALSE; @@ -662,6 +663,7 @@ struct cod_entry *copy_cod_entry(struct cod_entry *orig) if (!entry) return NULL; + entry->namespace = orig->namespace ? strdup(orig->namespace) : NULL; entry->name = strdup(orig->name); entry->mode = orig->mode; entry->deny = orig->deny; @@ -693,6 +695,8 @@ void free_cod_entries(struct cod_entry *list) return; if (list->next) free_cod_entries(list->next); + if (list->namespace) + free(list->namespace); if (list->name) free(list->name); if (list->pat.regex) @@ -765,8 +769,13 @@ void debug_cod_entries(struct cod_entry *list) if (item->name) printf("\tName:\t(%s)\n", item->name); + else printf("\tName:\tNULL\n"); + + if (item->namespace) + printf("\tNamespace:\t(%s)\n", item->namespace); + } } @@ -865,6 +874,9 @@ const char *capability_to_name(unsigned int cap) void debug_cod_list(struct codomain *cod) { unsigned int i; + if (cod->namespace) + printf("Namespcae:\t\t%s\n", cod->namespace); + if (cod->name) printf("Name:\t\t%s\n", cod->name); else diff --git a/parser/parser_policy.c b/parser/parser_policy.c index 92dfd5ef9..35d16ce56 100644 --- a/parser/parser_policy.c +++ b/parser/parser_policy.c @@ -41,8 +41,20 @@ void *policy_list = NULL; static int codomain_compare(const void *a, const void *b) { - return strcmp(((struct codomain *) a)->name, - ((struct codomain *) b)->name); + struct codomain *A = (struct codomain *) a; + struct codomain *B = (struct codoamin *) b; + + int res = 0; + if (A->namespace) { + if (B->namespace) + res = strcmp(A->namespace, B->namespace); + else + res = -1; + } else if (B->namespace) + res = 1; + if (res) + return res; + return strcmp(A->name, B->name); } void add_to_list(struct codomain *codomain) diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 21c43aea0..a032d8383 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -502,6 +502,18 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry) if (!aare_add_rule(dfarules, tbuf, entry->mode)) ret = FALSE; + if (entry->mode & AA_CHANGE_PROFILE) { + char lbuf[2*PATH_MAX + 8]; + if (entry->namespace) + sprintf(lbuf, "%s//%s", entry->namespace, entry->name); + else + sprintf(lbuf, "%s", entry->namespace, entry->name); + ptype = convert_aaregex_to_pcre(lbuf, 0, tbuf, 2*PATH_MAX + 8); + if (ptype == ePatternInvalid) + return FALSE; + if (!aare_add_rule(dfarules, tbuf, AA_CHANGE_PROFILE)) + return FALSE; + } return ret; } diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index 6f4affee5..d59244c36 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -67,7 +67,7 @@ struct value_list { }; void free_value_list(struct value_list *list); -struct cod_entry *do_file_rule(char *id, int mode); +struct cod_entry *do_file_rule(char *namespace, char *id, int mode); %} @@ -238,6 +238,10 @@ profile: TOK_ID flags TOK_OPEN rules TOK_CLOSE if (!cod) { yyerror(_("Memory allocation error.")); } + + if ($1[0] != '/') + yyerror(_("Profile names must begin with a '/'.")); + cod->name = $1; cod->flags = $2; if (force_complain) @@ -251,6 +255,31 @@ profile: TOK_ID flags TOK_OPEN rules TOK_CLOSE $$ = cod; }; +profile: TOK_ID TOK_COLON TOK_ID flags TOK_OPEN rules TOK_CLOSE + { + struct codomain *cod = $6; + PDEBUG("Matched: id (%s:%s) open rules close\n", $1, $3); + if (!cod) { + yyerror(_("Memory allocation error.")); + } + + if ($3[0] != '/') + yyerror(_("Profile names must begin with a '/'.")); + + cod->namespace = $1; + cod->name = $3; + cod->flags = $4; + if (force_complain) + cod->flags = force_complain_flags; + + PDEBUG("%s: flags='%s%s'\n", + $1, + cod->flags.complain ? "complain, " : "", + cod->flags.audit ? "audit" : ""); + + $$ = cod; + }; + varlist: { /* nothing */ } varlist: varlist varassign @@ -567,36 +596,36 @@ expr: TOK_DEFINED TOK_BOOL_VAR rule: TOK_ID file_mode TOK_END_OF_RULE { - $$ = do_file_rule($1, $2); + $$ = do_file_rule(NULL, $1, $2); }; rule: TOK_SET_VAR file_mode TOK_END_OF_RULE { - $$ = do_file_rule($1, $2); + $$ = do_file_rule(NULL, $1, $2); }; rule: file_mode TOK_ID TOK_END_OF_RULE { - $$ = do_file_rule($2, $1 & ~AA_EXEC_UNSAFE); + $$ = do_file_rule(NULL, $2, $1 & ~AA_EXEC_UNSAFE); }; rule: file_mode TOK_SET_VAR TOK_END_OF_RULE { - $$ = do_file_rule($2, $1 & ~AA_EXEC_UNSAFE); + $$ = do_file_rule(NULL, $2, $1 & ~AA_EXEC_UNSAFE); }; rule: TOK_UNSAFE file_mode TOK_ID TOK_END_OF_RULE { if (!($2 & AA_MAY_EXEC)) yyerror(_("unsafe rule missing exec permissions")); - $$ = do_file_rule($3, $2 | AA_EXEC_UNSAFE); + $$ = do_file_rule(NULL, $3, $2 | AA_EXEC_UNSAFE); }; rule: TOK_UNSAFE file_mode TOK_SET_VAR TOK_END_OF_RULE { if (!($2 & AA_MAY_EXEC)) yyerror(_("unsafe rule missing exec permissions")); - $$ = do_file_rule($3, $2 | AA_EXEC_UNSAFE); + $$ = do_file_rule(NULL, $3, $2 | AA_EXEC_UNSAFE); }; rule: TOK_ID file_mode TOK_ID @@ -937,7 +966,18 @@ change_profile: TOK_CHANGE_PROFILE TOK_ID TOK_END_OF_RULE { struct cod_entry *entry; PDEBUG("Matched change_profile: tok_id (%s)\n", $2); - entry = new_entry($2, AA_CHANGE_PROFILE); + entry = new_entry(NULL, $2, AA_CHANGE_PROFILE); + if (!entry) + yyerror(_("Memory allocation error.")); + PDEBUG("change_profile.entry: (%s)\n", entry->name); + $$ = entry; + }; + +change_profile: TOK_CHANGE_PROFILE TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE + { + struct cod_entry *entry; + PDEBUG("Matched change_profile: tok_id (%s:%s)\n", $2, $4); + entry = new_entry($2, $4, AA_CHANGE_PROFILE); if (!entry) yyerror(_("Memory allocation error.")); PDEBUG("change_profile.entry: (%s)\n", entry->name); @@ -1017,11 +1057,11 @@ void free_value_list(struct value_list *list) } } -struct cod_entry *do_file_rule(char *id, int mode) +struct cod_entry *do_file_rule(char *namespace, char *id, int mode) { struct cod_entry *entry; PDEBUG("Matched: tok_id (%s) tok_mode (0x%x)\n", id, mode); - entry = new_entry(id, mode); + entry = new_entry(namespace, id, mode); if (!entry) yyerror(_("Memory allocation error.")); PDEBUG("rule.entry: (%s)\n", entry->name);