diff --git a/parser/immunix.h b/parser/immunix.h index 157a4f492..77085234f 100644 --- a/parser/immunix.h +++ b/parser/immunix.h @@ -33,6 +33,21 @@ #define AA_MAY_LOCK (1 << 5) #define AA_EXEC_MMAP (1 << 6) +#define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \ + AA_MAY_READ | AA_MAY_APPEND | \ + AA_MAY_LINK | AA_MAY_LOCK | \ + AA_EXEC_MMAP) +#define AA_USER_SHIFT 0 +#define AA_GROUP_SHIFT 7 +#define AA_OTHER_SHIFT 14 + +#define AA_USER_PERMS (AA_BASE_PERMS << AA_USER_SHIFT) +#define AA_GROUP_PERMS (AA_BASE_PERMS << AA_GROUP_SHIFT) +#define AA_OTHER_PERMS (AA_BASE_PERMS << AA_OTHER_SHIFT) + +#define AA_FILE_PERMS (AA_USER_PERMS | AA_GROUP_PERMS | \ + AA_OTHER_PERMS) + #define AA_CHANGE_PROFILE (1 << 26) #define AA_EXEC_UNSAFE (1 << 27) @@ -51,6 +66,17 @@ #define AA_EXEC_PROFILE (AA_EXEC_MOD_1) #define AA_EXEC_PROFILE_OR_INHERIT (AA_EXEC_MOD_0 | AA_EXEC_MOD_1) +#define AA_VALID_PERMS (AA_FILE_PERMS | AA_CHANGE_PROFILE | \ + AA_EXEC_UNSAFE | AA_EXEC_MODIFIERS) + +#define AA_EXEC_BITS ((AA_MAY_EXEC << AA_USER_SHIFT) | \ + (AA_MAY_EXEC << AA_GROUP_SHIFT) | \ + (AA_MAY_EXEC << AA_OTHER_SHIFT)) + +#define SHIFT_MODE(MODE, SHIFT) ((((MODE) & AA_BASE_PERMS) << (SHIFT))\ + | ((MODE) & ~AA_FILE_PERMS)) +#define SHIFT_TO_BASE(MODE, SHIFT) ((((MODE) & AA_FILE_PERMS) >> (SHIFT))\ + | ((MODE) & ~AA_FILE_PERMS)) #define AA_HAT_SIZE 975 /* Maximum size of a subdomain * ident (hat) */ diff --git a/parser/libapparmor_re/regexp.y b/parser/libapparmor_re/regexp.y index bc365d86a..ce69ed94f 100644 --- a/parser/libapparmor_re/regexp.y +++ b/parser/libapparmor_re/regexp.y @@ -1529,7 +1529,6 @@ if (exact_match_perms) fprintf(stderr, "exact match perms without exec modifiers!!!\n"); perms |= exact_match_perms; } -if ((perms & AA_EXEC_MODIFIERS) > AA_EXEC_PROFILE_OR_INHERIT) fprintf(stderr, "bad accept perm 0x%x\n", perms); if (perms & AA_ERROR_BIT) { fprintf(stderr, "error bit 0x%x\n", perms); exit(255); @@ -1544,8 +1543,8 @@ if ((perms & AA_EXEC_MODIFIERS) > AA_EXEC_PROFILE_OR_INHERIT) fprintf(stderr, "b extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, uint32_t perms) { static MatchFlag *match_flags[sizeof(perms) * 8 - 4 + 8]; - static MatchFlag *exec_match_flags[8]; - static ExactMatchFlag *exact_match_flags[8]; + static MatchFlag *exec_match_flags[8 * 3]; + static ExactMatchFlag *exact_match_flags[8 * 3]; Node *tree, *accept; int exact_match; @@ -1554,8 +1553,7 @@ extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, uint32_t perms) if (regexp_parse(&tree, rule)) return 0; -if ((perms & AA_EXEC_MODIFIERS) > AA_EXEC_PROFILE_OR_INHERIT) fprintf(stderr, "bad accept perm 0x%x when adding rule\n", perms); - if ((perms & AA_MAY_EXEC) && !(perms & AA_EXEC_MODIFIERS)) + if ((perms & AA_EXEC_BITS) && !(perms & AA_EXEC_MODIFIERS)) fprintf(stderr, "Rule with exec bits and not exec modifiers\n\t 0x%x %s\n", perms, rule); /* * Check if we have an expression with or without wildcards. This @@ -1583,8 +1581,13 @@ if ((perms & AA_EXEC_MODIFIERS) > AA_EXEC_PROFILE_OR_INHERIT) fprintf(stderr, "b perms &= ~mask; Node *flag; - if ((mask & AA_MAY_EXEC) && (perms & AA_EXEC_MODIFIERS)) { + if ((mask & AA_EXEC_BITS) && (perms & AA_EXEC_MODIFIERS)) { int index = (perms & AA_EXEC_MODIFIERS) >> AA_EXEC_MOD_SHIFT; + if (mask & (AA_MAY_EXEC << AA_GROUP_SHIFT)) + index += 8; + else if (mask & (AA_MAY_EXEC << AA_OTHER_SHIFT)) + index += 16; + if (exact_match) { if (exact_match_flags[index]) flag = exact_match_flags[index]->dup(); @@ -1617,8 +1620,8 @@ if ((perms & AA_EXEC_MODIFIERS) > AA_EXEC_PROFILE_OR_INHERIT) fprintf(stderr, "b rules->root = new AltNode(rules->root, new CatNode(tree, accept)); return 1; -} +} /* create a dfa from the ruleset * returns: buffer contain dfa tables, @size set to the size of the tables diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 05b77a37c..f65f2b47b 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -54,7 +54,7 @@ END_OF_RULE [,] SEPERATOR {UP} RANGE - MODE_CHARS ([RrWwaLlMmk])|([Pp][Xx])|([Uu][Xx])|([Ii][Xx])|([Pp][Ii][Xx]) -MODES {MODE_CHARS}+ +MODES ({MODE_CHARS}+)|({MODE_CHARS}*:{MODE_CHARS}*:{MODE_CHARS}*) WS [[:blank:]] NUMBER [[:digit:]]+ ID [^ \t\n"!,]|(,[^ \t\n"!]) diff --git a/parser/parser_merge.c b/parser/parser_merge.c index f167fa96c..cf95a0949 100644 --- a/parser/parser_merge.c +++ b/parser/parser_merge.c @@ -80,7 +80,7 @@ static int process_file_entries(struct codomain *cod) table[count] = NULL; #define X_CONFLICT(a, b) \ - (HAS_MAY_EXEC(a) && HAS_MAY_EXEC(b) && \ + (((a) & AA_EXEC_BITS) && ((b) & AA_EXEC_BITS) && \ (((a) & (AA_EXEC_MODIFIERS | AA_EXEC_UNSAFE)) != \ ((b) & (AA_EXEC_MODIFIERS | AA_EXEC_UNSAFE)))) diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 587ac89c3..b30de73da 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -401,10 +401,9 @@ static void warn_uppercase(void) warned_uppercase = 1; } } -int parse_mode(const char *str_mode) + +static int parse_sub_mode(const char *str_mode, const char *mode_desc) { - /* The 'check' int is a bit of a kludge, but we need some context - when we're doing permission checking */ #define IS_DIFF_QUAL(mode, q) (((mode) & AA_MAY_EXEC) && (((mode) & (AA_EXEC_MODIFIERS | AA_EXEC_UNSAFE)) != (q))) @@ -426,31 +425,31 @@ int parse_mode(const char *str_mode) reeval: switch (this) { case COD_READ_CHAR: - PDEBUG("Parsing mode: found READ\n"); + PDEBUG("Parsing mode: found %s READ\n", mode_desc); mode |= AA_MAY_READ; break; case COD_WRITE_CHAR: - PDEBUG("Parsing mode: found WRITE\n"); + PDEBUG("Parsing mode: found %s WRITE\n", mode_desc); if ((mode & AA_MAY_APPEND) && !(mode & AA_MAY_WRITE)) yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive.")); mode |= AA_MAY_WRITE | AA_MAY_APPEND; break; case COD_APPEND_CHAR: - PDEBUG("Parsing mode: found APPEND\n"); + PDEBUG("Parsing mode: found %s APPEND\n", mode_desc); if (mode & AA_MAY_WRITE) yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive.")); mode |= AA_MAY_APPEND; break; case COD_LINK_CHAR: - PDEBUG("Parsing mode: found LINK\n"); + PDEBUG("Parsing mode: found %s LINK\n", mode_desc); mode |= AA_MAY_LINK; break; case COD_LOCK_CHAR: - PDEBUG("Parsing mode: found LOCK\n"); + PDEBUG("Parsing mode: found %s LOCK\n", mode_desc); mode |= AA_MAY_LOCK; break; @@ -513,15 +512,18 @@ reeval: break; case COD_MMAP_CHAR: - PDEBUG("Parsing mode: found MMAP\n"); + PDEBUG("Parsing mode: found %s MMAP\n", mode_desc); mode |= AA_EXEC_MMAP; break; case COD_EXEC_CHAR: - PDEBUG("Parsing mode: found EXEC\n"); + PDEBUG("Parsing mode: found %s EXEC\n", mode_desc); yyerror(_("Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'")); break; + case ':': + goto out; + break; /* error cases */ default: @@ -549,12 +551,54 @@ reeval: p++; } - +out: PDEBUG("Parsed mode: %s 0x%x\n", str_mode, mode); return mode; } +int parse_mode(const char *str_mode) +{ + const char *next, *pos = str_mode; + int tmp, mode = 0; + next = strchr(str_mode, ':'); + if (!next) { + tmp = parse_sub_mode(str_mode, ""); + mode = SHIFT_MODE(tmp, AA_USER_SHIFT); + mode |= SHIFT_MODE(tmp, AA_GROUP_SHIFT); + mode |= SHIFT_MODE(tmp, AA_OTHER_SHIFT); + if (mode & ~AA_VALID_PERMS) + yyerror(_("Internal error generated invalid perm 0x%llx\n"), mode); + return mode; + } + /* user:group:other */ + if (next > pos) + mode = SHIFT_MODE(parse_sub_mode(pos, "user"), AA_USER_SHIFT); + pos = next + 1; + next = strchr(pos, ':'); + if (next > pos) { + tmp = parse_sub_mode(pos, "group"); + if ((mode & AA_EXEC_BITS) && (tmp & AA_EXEC_BITS) && + (mode & AA_EXEC_MODIFIERS) != (tmp & AA_EXEC_MODIFIERS)) + yyerror(_("conflicting x modifiers between user and group permissions.")); + mode |= SHIFT_MODE(tmp, AA_GROUP_SHIFT); + } + pos = next + 1; + if (*pos) { + tmp = parse_sub_mode(pos, "other"); + if ((mode & AA_EXEC_BITS) && (tmp & AA_EXEC_BITS) && + (mode & AA_EXEC_MODIFIERS) != (tmp & AA_EXEC_MODIFIERS)) + yyerror(_("conflicting x modifiers between other and user:group permissions.")); + mode |= SHIFT_MODE(tmp, AA_OTHER_SHIFT); + } + if (mode & ~AA_VALID_PERMS) + yyerror(_("Internal error generated invalid perm 0x%llx\n"), mode); + if (!mode) + yyerror(_("Invalid permission permission \"::\" - no permission specified.")); + + return mode; +} + struct cod_entry *new_entry(char *namespace, char *id, int mode) { struct cod_entry *entry = NULL; @@ -618,6 +662,38 @@ void free_cod_entries(struct cod_entry *list) free(list); } +static void debug_base_perm_mask(int mask) +{ + if (HAS_MAY_READ(mask)) + printf("%c", COD_READ_CHAR); + if (HAS_MAY_WRITE(mask)) + printf("%c", COD_WRITE_CHAR); + if (HAS_MAY_APPEND(mask)) + printf("%c", COD_APPEND_CHAR); + if (HAS_MAY_LINK(mask)) + printf("%c", COD_LINK_CHAR); + if (HAS_MAY_LOCK(mask)) + printf("%c", COD_LOCK_CHAR); + if (HAS_EXEC_INHERIT(mask)) + printf("%c", COD_INHERIT_CHAR); + if (HAS_EXEC_UNCONFINED(mask)) { + if (HAS_EXEC_UNSAFE(mask)) + printf("%c", COD_UNSAFE_UNCONFINED_CHAR); + else + printf("%c", COD_UNCONFINED_CHAR); + } + if (HAS_EXEC_PROFILE(mask)) { + if (HAS_EXEC_UNSAFE(mask)) + printf("%c", COD_UNSAFE_PROFILE_CHAR); + else + printf("%c", COD_PROFILE_CHAR); + } + if (HAS_EXEC_MMAP(mask)) + printf("%c", COD_MMAP_CHAR); + if (HAS_MAY_EXEC(mask)) + printf("%c", COD_EXEC_CHAR); +} + void debug_cod_entries(struct cod_entry *list) { struct cod_entry *item = NULL; @@ -629,37 +705,15 @@ void debug_cod_entries(struct cod_entry *list) printf("Item is NULL!\n"); printf("Mode:\t"); - if (HAS_MAY_READ(item->mode)) - printf("%c", COD_READ_CHAR); - if (HAS_MAY_WRITE(item->mode)) - printf("%c", COD_WRITE_CHAR); - if (HAS_MAY_APPEND(item->mode)) - printf("%c", COD_APPEND_CHAR); - if (HAS_MAY_LINK(item->mode)) - printf("%c", COD_LINK_CHAR); - if (HAS_MAY_LOCK(item->mode)) - printf("%c", COD_LOCK_CHAR); - if (HAS_EXEC_INHERIT(item->mode)) - printf("%c", COD_INHERIT_CHAR); - if (HAS_EXEC_UNCONFINED(item->mode)) { - if (HAS_EXEC_UNSAFE(item->mode)) - printf("%c", COD_UNSAFE_UNCONFINED_CHAR); - else - printf("%c", COD_UNCONFINED_CHAR); - } - if (HAS_EXEC_PROFILE(item->mode)) { - if (HAS_EXEC_UNSAFE(item->mode)) - printf("%c", COD_UNSAFE_PROFILE_CHAR); - else - printf("%c", COD_PROFILE_CHAR); - } - if (HAS_EXEC_MMAP(item->mode)) - printf("%c", COD_MMAP_CHAR); - if (HAS_MAY_EXEC(item->mode)) - printf("%c", COD_EXEC_CHAR); if (HAS_CHANGE_PROFILE(item->mode)) printf(" change_profile"); - + if (HAS_EXEC_UNSAFE(item->mode)) + printf(" unsafe"); + debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_USER_SHIFT)); + printf(":"); + debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_GROUP_SHIFT)); + printf(":"); + debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_OTHER_SHIFT)); if (item->name) printf("\tName:\t(%s)\n", item->name); diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 7f1568768..a447afd32 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -497,8 +497,14 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry) /* ix implies m but the apparmor module does not add m bit to * dfa states like it does for pcre */ - if ((entry->mode & AA_EXEC_MODIFIERS) == AA_EXEC_INHERIT) - entry->mode |= AA_EXEC_MMAP; + if ((entry->mode & AA_EXEC_MODIFIERS) == AA_EXEC_INHERIT) { + if (HAS_MAY_EXEC(SHIFT_TO_BASE(entry->mode, AA_OTHER_SHIFT))) + entry->mode |= AA_EXEC_MMAP << AA_OTHER_SHIFT; + if (HAS_MAY_EXEC(SHIFT_TO_BASE(entry->mode, AA_GROUP_SHIFT))) + entry->mode |= AA_EXEC_MMAP << AA_GROUP_SHIFT; + if (HAS_MAY_EXEC(SHIFT_TO_BASE(entry->mode, AA_USER_SHIFT))) + entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT; + } if (!aare_add_rule(dfarules, tbuf, entry->mode)) ret = FALSE; diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index 1ec1d2407..56a3c6a7a 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -523,7 +523,7 @@ rule: file_mode id_or_var TOK_END_OF_RULE rule: TOK_UNSAFE file_mode id_or_var TOK_END_OF_RULE { - if (!($2 & AA_MAY_EXEC)) + if (!($2 & AA_EXEC_BITS)) yyerror(_("unsafe rule missing exec permissions")); $$ = do_file_rule(NULL, $3, $2 | AA_EXEC_UNSAFE); }; @@ -599,6 +599,8 @@ hat_start: TOK_SEP {} file_mode: TOK_MODE { + /* A single TOK_MODE maps to the same permission in all + * of user:group:other */ $$ = parse_mode($1); free($1); }