diff --git a/parser/Makefile b/parser/Makefile index f859f0e1f..7f691ca64 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 dbus.c lib.c +HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.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 $(CC) $(EXTRA_CFLAGS) -c -o $@ $< +dbus.o: dbus.c dbus.h parser.h immunix.h + $(CC) $(EXTRA_CFLAGS) -c -o $@ $< + parser_version.h: Makefile @echo \#define PARSER_VERSION \"$(VERSION)\" > .ver @mv -f .ver $@ diff --git a/parser/dbus.c b/parser/dbus.c new file mode 100644 index 000000000..d08462d32 --- /dev/null +++ b/parser/dbus.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact Novell, Inc. or Canonical + * Ltd. + */ + +#include +#include + +#include "parser.h" +#include "parser_yacc.h" +#include "dbus.h" + +void free_dbus_entry(struct dbus_entry *ent) +{ + if (!ent) + return; + free(ent->bus); + free(ent->name); + free(ent->peer_label); + free(ent->path); + free(ent->interface); + free(ent->member); + + free(ent); +} + +static int list_len(struct value_list *v) +{ + int len = 0; + struct value_list *tmp; + + list_for_each(v, tmp) + len++; + + return len; +} + +static void move_conditional_value(char **dst_ptr, struct cond_entry *cond_ent) +{ + if (*dst_ptr) + yyerror("dbus conditional \"%s\" can only be specified once\n", + cond_ent->name); + + *dst_ptr = cond_ent->vals->value; + cond_ent->vals->value = NULL; +} + +static void move_conditionals(struct dbus_entry *ent, struct cond_entry *conds) +{ + struct cond_entry *cond_ent; + + list_for_each(conds, cond_ent) { + /* for now disallow keyword 'in' (list) */ + if (!cond_ent->eq) + yyerror("keyword \"in\" is not allowed in dbus rules\n"); + if (list_len(cond_ent->vals) > 1) + yyerror("dbus conditional \"%s\" only supports a single value\n", + cond_ent->name); + + if (strcmp(cond_ent->name, "bus") == 0) { + move_conditional_value(&ent->bus, cond_ent); + } else if (strcmp(cond_ent->name, "name") == 0) { + move_conditional_value(&ent->name, cond_ent); + } else if (strcmp(cond_ent->name, "label") == 0) { + move_conditional_value(&ent->peer_label, cond_ent); + } else if (strcmp(cond_ent->name, "path") == 0) { + move_conditional_value(&ent->path, cond_ent); + } else if (strcmp(cond_ent->name, "interface") == 0) { + move_conditional_value(&ent->interface, cond_ent); + } else if (strcmp(cond_ent->name, "member") == 0) { + move_conditional_value(&ent->member, cond_ent); + } else { + yyerror("invalid dbus conditional \"%s\"\n", + cond_ent->name); + } + } +} + +struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds, + struct cond_entry *peer_conds) +{ + struct dbus_entry *ent; + int name_is_subject_cond = 0, message_rule = 0, service_rule = 0; + + ent = (struct dbus_entry*) calloc(1, sizeof(struct dbus_entry)); + if (!ent) + goto out; + + /* Move the global/subject conditionals over & check the results */ + move_conditionals(ent, conds); + if (ent->name) + name_is_subject_cond = 1; + if (ent->peer_label) + yyerror("dbus \"label\" conditional can only be used inside of the \"peer=()\" grouping\n"); + + /* Move the peer conditionals */ + move_conditionals(ent, peer_conds); + + if (ent->path || ent->interface || ent->member || ent->peer_label || + (ent->name && !name_is_subject_cond)) + message_rule = 1; + + if (ent->name && name_is_subject_cond) + service_rule = 1; + + if (message_rule && service_rule) + yyerror("dbus rule contains message conditionals and service conditionals\n"); + + /* Copy mode. If no mode was specified, assign an implied mode. */ + if (mode) { + ent->mode = mode; + if (ent->mode & ~AA_VALID_DBUS_PERMS) + yyerror("mode contains unknown dbus accesss\n"); + else if (message_rule && (ent->mode & AA_DBUS_BIND)) + yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n"); + else if (service_rule && (ent->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE))) + yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n"); + } else { + ent->mode = AA_VALID_DBUS_PERMS; + if (message_rule) + ent->mode &= ~AA_DBUS_BIND; + else if (service_rule) + ent->mode &= ~(AA_DBUS_SEND | AA_DBUS_RECEIVE); + } + +out: + free_cond_list(conds); + return ent; +} + + +void print_dbus_entry(struct dbus_entry *ent) +{ + if (ent->audit) + fprintf(stderr, "audit "); + if (ent->deny) + fprintf(stderr, "deny "); + + fprintf(stderr, "dbus ( "); + + if (ent->mode & AA_DBUS_SEND) + fprintf(stderr, "send "); + if (ent->mode & AA_DBUS_RECEIVE) + fprintf(stderr, "receive "); + if (ent->mode & AA_DBUS_BIND) + fprintf(stderr, "bind "); + fprintf(stderr, ")"); + + if (ent->bus) + fprintf(stderr, " bus=\"%s\"", ent->bus); + if ((ent->mode & AA_DBUS_BIND) && ent->name) + fprintf(stderr, " name=\"%s\"", ent->name); + if (ent->path) + fprintf(stderr, " path=\"%s\"", ent->path); + if (ent->interface) + fprintf(stderr, " interface=\"%s\"", ent->interface); + if (ent->member) + fprintf(stderr, " member=\"%s\"", ent->member); + + if (!(ent->mode & AA_DBUS_BIND) && (ent->peer_label || ent->name)) { + fprintf(stderr, " peer=( "); + if (ent->peer_label) + fprintf(stderr, "label=\"%s\" ", ent->peer_label); + if (ent->name) + fprintf(stderr, "name=\"%s\" ", ent->name); + fprintf(stderr, ")"); + } + + fprintf(stderr, ",\n"); +} diff --git a/parser/dbus.h b/parser/dbus.h new file mode 100644 index 000000000..32991f2f6 --- /dev/null +++ b/parser/dbus.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact Novell, Inc. or Canonical + * Ltd. + */ + +#ifndef __AA_DBUS_H +#define __AA_DBUS_H + +#include "parser.h" + +struct dbus_entry { + char *bus; + /** + * Be careful! ->name can be the subject or the peer name, depending on + * whether the rule is a bind rule or a send/receive rule. See the + * comments in new_dbus_entry() for details. + */ + char *name; + char *peer_label; + char *path; + char *interface; + char *member; + int mode; + int audit; + int deny; + + struct dbus_entry *next; +}; + +void free_dbus_entry(struct dbus_entry *ent); +struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds, + struct cond_entry *peer_conds); +void print_dbus_entry(struct dbus_entry *ent); + +#endif /* __AA_DBUS_H */ diff --git a/parser/immunix.h b/parser/immunix.h index ebb2d2ec6..f5064e841 100644 --- a/parser/immunix.h +++ b/parser/immunix.h @@ -40,6 +40,13 @@ #define AA_EXEC_MOD_2 (1 << 12) #define AA_EXEC_MOD_3 (1 << 13) +#define AA_DBUS_SEND AA_MAY_WRITE +#define AA_DBUS_RECEIVE AA_MAY_READ +#define AA_DBUS_BIND (1 << 6) + +#define AA_VALID_DBUS_PERMS (AA_DBUS_SEND | AA_DBUS_RECEIVE | \ + AA_DBUS_BIND) + #define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \ AA_MAY_READ | AA_MAY_APPEND | \ AA_MAY_LINK | AA_MAY_LOCK | \ diff --git a/parser/parser.h b/parser/parser.h index 8199f4357..6d7e84b95 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -142,6 +142,7 @@ struct codomain { char *exec_table[AA_EXEC_COUNT]; struct cod_entry *entries; + struct dbus_entry *dbus_ents; struct mnt_entry *mnt_ents; void *hat_table; @@ -301,6 +302,8 @@ extern char *basedir; /* parser_regex.c */ extern int process_regex(struct codomain *cod); extern int post_process_entry(struct cod_entry *entry); +extern int process_dbus(struct codomain *cod); + extern void reset_regex(void); extern int process_policydb(struct codomain *cod); @@ -319,6 +322,7 @@ extern void free_value_list(struct value_list *list); extern void print_value_list(struct value_list *list); extern struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list); extern void free_cond_entry(struct cond_entry *ent); +extern void free_cond_list(struct cond_entry *ents); extern void print_cond_entry(struct cond_entry *ent); extern char *processid(char *string, int len); extern char *processquoted(char *string, int len); @@ -328,6 +332,7 @@ extern int name_to_capability(const char *keyword); extern int get_rlimit(const char *name); extern char *process_var(const char *var); extern int parse_mode(const char *mode); +extern int parse_dbus_mode(const char *str_mode, int *mode, int fail); extern struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id); extern struct aa_network_entry *new_network_ent(unsigned int family, @@ -344,6 +349,7 @@ 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 free_dbus_entries(struct dbus_entry *list); /* parser_symtab.c */ struct set_value {; @@ -385,6 +391,7 @@ extern void post_process_file_entries(struct codomain *cod); extern void post_process_mnt_entries(struct codomain *cod); extern int post_process_policy(int debug_only); extern int process_hat_regex(struct codomain *cod); +extern int process_hat_dbus(struct codomain *cod); extern int process_hat_variables(struct codomain *cod); extern int process_hat_policydb(struct codomain *cod); extern int post_merge_rules(void); diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 539e16a71..7761ddef1 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -53,6 +53,12 @@ #define NPDEBUG(fmt, args...) /* Do nothing */ #define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0) +#define RETURN_TOKEN(X) \ +do { \ + DUMP_PREPROCESS; \ + PDEBUG("Matched: %s\n", yytext); \ + return (X); \ +} while (0) #define YY_NO_INPUT @@ -198,9 +204,11 @@ POST_VAR_ID_CHARS [^ \t\n"!,]{-}[=\+] POST_VAR_ID {POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS}) LIST_VALUE_ID_CHARS [^ \t\n"!,]{-}[()] LIST_VALUE_ID {LIST_VALUE_ID_CHARS}+ +QUOTED_LIST_VALUE_ID {LIST_VALUE_ID}|\"{LIST_VALUE_ID}\" ID_CHARS_NOEQ [^ \t\n"!,]{-}[=] +LEADING_ID_CHARS_NOEQ [^ \t\n"!,]{-}[=()] ID_NOEQ {ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ}) -IDS_NOEQ {ID_NOEQ}+ +IDS_NOEQ {LEADING_ID_CHARS_NOEQ}{ID_NOEQ}* ALLOWED_QUOTED_ID [^\0"]|\\\" QUOTED_ID \"{ALLOWED_QUOTED_ID}*\" @@ -228,11 +236,16 @@ LT_EQUAL <= %x SUB_ID %x SUB_VALUE %x EXTCOND_MODE +%x EXTCONDLIST_MODE %x NETWORK_MODE %x LIST_VAL_MODE +%x LIST_COND_MODE +%x LIST_COND_VAL +%x LIST_COND_PAREN_VAL %x ASSIGN_MODE %x RLIMIT_MODE %x MOUNT_MODE +%x DBUS_MODE %x CHANGE_PROFILE_MODE %x INCLUDE @@ -278,7 +291,7 @@ LT_EQUAL <= if ( !YY_CURRENT_BUFFER ) yyterminate(); } -{ +{ {VARIABLE_NAME}/{WS}*= { /* we match to the = in the lexer so that * can switch scanner state. By the time @@ -286,11 +299,19 @@ LT_EQUAL <= * as bison may have requested the next * token from the scanner */ + int token = get_keyword_token(yytext); + DUMP_PREPROCESS; - PDEBUG("conditional %s=\n", yytext); - yylval.id = processid(yytext, yyleng); - yy_push_state(EXTCOND_MODE); - return TOK_CONDID; + if (token == TOK_PEER) { + PDEBUG("conditional list %s=\n", yytext); + yy_push_state(EXTCONDLIST_MODE); + return TOK_CONDLISTID; + } else { + PDEBUG("conditional %s=\n", yytext); + yylval.id = processid(yytext, yyleng); + yy_push_state(EXTCOND_MODE); + return TOK_CONDID; + } } {VARIABLE_NAME}/{WS}+in{WS}*\( { /* we match to 'in' in the lexer so that @@ -422,6 +443,116 @@ LT_EQUAL <= } +{ + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } + + ({LIST_VALUE_ID}|{QUOTED_LIST_VALUE_ID}) { + DUMP_PREPROCESS; + yylval.id = processid(yytext, yyleng); + PDEBUG("listcond value: \"%s\"\n", yylval.id); + yy_pop_state(); + return TOK_VALUE; + } + + [^\n] { + DUMP_PREPROCESS; + /* Something we didn't expect */ + yyerror(_("Found unexpected character: '%s'"), yytext); + } +} + +{ + {CLOSE_PAREN} { + DUMP_PREPROCESS; + yy_pop_state(); + } + + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } + + ({LIST_VALUE_ID}|{QUOTED_LIST_VALUE_ID}) { + DUMP_PREPROCESS; + yylval.id = processid(yytext, yyleng); + PDEBUG("listcond paren value: \"%s\"\n", yylval.id); + return TOK_VALUE; + } + + [^\n] { + DUMP_PREPROCESS; + /* Something we didn't expect */ + yyerror(_("Found unexpected character: '%s'"), yytext); + } +} + +{ + {CLOSE_PAREN} { + DUMP_PREPROCESS; + PDEBUG("listcond: )\n"); + yy_pop_state(); + return TOK_CLOSEPAREN; + } + + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } + + {COMMA} { + DUMP_PREPROCESS; + PDEBUG("listcond: , \n"); + /* Eat comma, its an optional separator */ + } + + {ID_CHARS_NOEQ}+ { + DUMP_PREPROCESS; + PDEBUG("listcond conditional %s=\n", yytext); + yylval.id = processid(yytext, yyleng); + return TOK_CONDID; + } + + {EQUALS}{WS}*{OPEN_PAREN} { + DUMP_PREPROCESS; + yy_push_state(LIST_COND_PAREN_VAL); + return TOK_EQUALS; + } + + {EQUALS} { + DUMP_PREPROCESS; + yy_push_state(LIST_COND_VAL); + return TOK_EQUALS; + } + + [^\n] { + DUMP_PREPROCESS; + /* Something we didn't expect */ + yyerror(_("Found unexpected character: '%s'"), yytext); + } +} + +{ + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } + + {EQUALS} { + DUMP_PREPROCESS; + return TOK_EQUALS; + } + + {OPEN_PAREN} { + DUMP_PREPROCESS; + PDEBUG("extcondlist (\n"); + /* Don't push state here as this is a transition + * start condition and we want to return to the start + * condition that invoked when + * LIST_VAL_ID is done + */ + BEGIN(LIST_COND_MODE); + return TOK_OPENPAREN; + } + + [^\n] { + DUMP_PREPROCESS; + /* Something we didn't expect */ + yyerror(_("Found unexpected character: '%s' %d"), yytext, *yytext); + } + +} + { {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } @@ -547,7 +678,23 @@ LT_EQUAL <= } } -{ +{ + send { RETURN_TOKEN(TOK_SEND); } + receive { RETURN_TOKEN(TOK_RECEIVE); } + bind { RETURN_TOKEN(TOK_BIND); } + read { RETURN_TOKEN(TOK_READ); } + write { RETURN_TOKEN(TOK_WRITE); } + {OPEN_PAREN} { + yy_push_state(LIST_VAL_MODE); + RETURN_TOKEN(TOK_OPENPAREN); + } + (r|w|rw|wr)/([[:space:],]) { + yylval.mode = strdup(yytext); + RETURN_TOKEN(TOK_MODE); + } +} + +{ {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } {ARROW} { @@ -709,6 +856,10 @@ LT_EQUAL <= PDEBUG("Entering mount\n"); yy_push_state(MOUNT_MODE); break; + case TOK_DBUS: + PDEBUG("Entering dbus\n"); + yy_push_state(DBUS_MODE); + break; default: /* nothing */ break; } diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 5f211b945..d8647373c 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -38,6 +38,7 @@ #include "parser.h" #include "parser_yacc.h" #include "mount.h" +#include "dbus.h" /* #define DEBUG */ #ifdef DEBUG @@ -85,6 +86,14 @@ static struct keyword_table keyword_table[] = { {"unmount", TOK_UMOUNT}, {"pivot_root", TOK_PIVOTROOT}, {"in", TOK_IN}, + {"dbus", TOK_DBUS}, + {"send", TOK_SEND}, + {"receive", TOK_RECEIVE}, + {"bind", TOK_BIND}, + {"read", TOK_READ}, + {"write", TOK_WRITE}, + {"peer", TOK_PEER}, + /* terminate */ {NULL, 0} }; @@ -724,6 +733,81 @@ int parse_mode(const char *str_mode) return mode; } +static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused) +{ + int mode = 0; + const char *p; + + PDEBUG("Parsing DBus mode: %s\n", str_mode); + + if (!str_mode) + return 0; + + p = str_mode; + while (*p) { + char this = *p; + char lower; + +reeval: + switch (this) { + case COD_READ_CHAR: + PDEBUG("Parsing DBus mode: found %s READ\n", mode_desc); + mode |= AA_DBUS_RECEIVE; + break; + + case COD_WRITE_CHAR: + PDEBUG("Parsing DBus mode: found %s WRITE\n", + mode_desc); + mode |= AA_DBUS_SEND; + break; + + /* error cases */ + + default: + lower = tolower(this); + switch (lower) { + case COD_READ_CHAR: + case COD_WRITE_CHAR: + PDEBUG("Parsing DBus mode: found invalid upper case char %c\n", + this); + warn_uppercase(); + this = lower; + goto reeval; + break; + default: + if (fail) + yyerror(_("Internal: unexpected DBus mode character '%c' in input"), + this); + else + return 0; + break; + } + break; + } + p++; + } + + PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode); + + *result = mode; + return 1; +} + +int parse_dbus_mode(const char *str_mode, int *mode, int fail) +{ + *mode = 0; + if (!parse_dbus_sub_mode(str_mode, mode, fail, "")) + return 0; + if (*mode & ~AA_VALID_DBUS_PERMS) { + if (fail) + yyerror(_("Internal error generated invalid DBus perm 0x%x\n"), + mode); + else + return 0; + } + return 1; +} + struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id) { struct cod_entry *entry = NULL; @@ -803,6 +887,16 @@ void free_mnt_entries(struct mnt_entry *list) free(list); } +void free_dbus_entries(struct dbus_entry *list) +{ + if (!list) + return; + if (list->next) + free_dbus_entries(list->next); + + free_dbus_entry(list); +} + static void debug_base_perm_mask(int mask) { if (HAS_MAY_READ(mask)) @@ -1148,6 +1242,15 @@ void free_cond_entry(struct cond_entry *ent) } } +void free_cond_list(struct cond_entry *ents) +{ + struct cond_entry *entry, *tmp; + + list_for_each_safe(ents, entry, tmp) { + free_cond_entry(entry); + } +} + void print_cond_entry(struct cond_entry *ent) { if (ent) { diff --git a/parser/parser_policy.c b/parser/parser_policy.c index dce1b0dd4..9673a1066 100644 --- a/parser/parser_policy.c +++ b/parser/parser_policy.c @@ -30,6 +30,7 @@ #include "parser.h" #include "mount.h" +#include "dbus.h" #include "parser_yacc.h" /* #define DEBUG */ @@ -818,6 +819,7 @@ void free_policy(struct codomain *cod) free_hat_table(cod->hat_table); free_cod_entries(cod->entries); free_mnt_entries(cod->mnt_ents); + free_dbus_entries(cod->dbus_ents); if (cod->dfarules) aare_delete_ruleset(cod->dfarules); if (cod->dfa) diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 0ba81149f..9dd297772 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -29,6 +29,7 @@ #include "libapparmor_re/apparmor_re.h" #include "libapparmor_re/aare_rules.h" #include "mount.h" +#include "dbus.h" #include "policydb.h" enum error_type { @@ -1041,7 +1042,107 @@ fail: } -int post_process_policydb_ents(struct codomain *cod) +static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry) +{ + char busbuf[PATH_MAX + 3]; + char namebuf[PATH_MAX + 3]; + char peer_labelbuf[PATH_MAX + 3]; + char pathbuf[PATH_MAX + 3]; + char ifacebuf[PATH_MAX + 3]; + char memberbuf[PATH_MAX + 3]; + char *p, *vec[6]; + + pattern_t ptype; + int pos; + + if (!entry) /* shouldn't happen */ + return TRUE; + + p = busbuf; + p += sprintf(p, "\\x%02x", AA_CLASS_DBUS); + + if (entry->bus) { + ptype = convert_aaregex_to_pcre(entry->bus, 0, p, + PATH_MAX+3 - (p - busbuf), &pos); + if (ptype == ePatternInvalid) + goto fail; + } else { + /* match any char except \000 0 or more times */ + strcpy(p, "[^\\000]*"); + } + vec[0] = busbuf; + + if (entry->name) { + ptype = convert_aaregex_to_pcre(entry->name, 0, namebuf, + PATH_MAX+3, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[1] = namebuf; + } else { + /* match any char except \000 0 or more times */ + vec[1] = "[^\\000]*"; + } + + if (entry->peer_label) { + ptype = convert_aaregex_to_pcre(entry->peer_label, 0, + peer_labelbuf, PATH_MAX+3, + &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[2] = peer_labelbuf; + } else { + /* match any char except \000 0 or more times */ + vec[2] = "[^\\000]*"; + } + + if (entry->path) { + ptype = convert_aaregex_to_pcre(entry->path, 0, pathbuf, + PATH_MAX+3, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[3] = pathbuf; + } else { + /* match any char except \000 0 or more times */ + vec[3] = "[^\\000]*"; + } + + if (entry->interface) { + ptype = convert_aaregex_to_pcre(entry->interface, 0, ifacebuf, + PATH_MAX+3, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[4] = ifacebuf; + } else { + /* match any char except \000 0 or more times */ + vec[4] = "[^\\000]*"; + } + + if (entry->member) { + ptype = convert_aaregex_to_pcre(entry->member, 0, memberbuf, + PATH_MAX+3, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[5] = memberbuf; + } else { + /* match any char except \000 0 or more times */ + vec[5] = "[^\\000]*"; + } + + if (entry->mode & AA_DBUS_BIND) { + if (!aare_add_rule_vec(dfarules, entry->deny, entry->mode & AA_DBUS_BIND, entry->audit & AA_DBUS_BIND, 2, vec, dfaflags)) + goto fail; + } + if (entry->mode & ~AA_DBUS_BIND) { + if (!aare_add_rule_vec(dfarules, entry->deny, entry->mode, entry->audit, 6, vec, dfaflags)) + goto fail; + } + return TRUE; + +fail: + return FALSE; +} + +static int post_process_mnt_ents(struct codomain *cod) { int ret = TRUE; int count = 0; @@ -1058,10 +1159,37 @@ int post_process_policydb_ents(struct codomain *cod) } else if (cod->mnt_ents && !kernel_supports_mount) pwarn("profile %s mount rules not enforced\n", cod->name); - cod->policy_rule_count = count; + cod->policy_rule_count += count; return ret; } +static int post_process_dbus_ents(struct codomain *cod) +{ + int ret = TRUE; + struct dbus_entry *entry; + int count = 0; + + list_for_each(cod->dbus_ents, entry) { + if (regex_type == AARE_DFA && + !process_dbus_entry(cod->policy_rules, entry)) + ret = FALSE; + count++; + } + + cod->policy_rule_count += count; + return ret; +} + +int post_process_policydb_ents(struct codomain *cod) +{ + if (!post_process_mnt_ents(cod)) + return FALSE; + if (!post_process_dbus_ents(cod)) + return FALSE; + + return TRUE; +} + int process_policydb(struct codomain *cod) { int error = -1; diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index 351a17338..0fedbc895 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -33,6 +33,7 @@ #include "parser.h" #include "mount.h" +#include "dbus.h" #include "parser_include.h" #include #include @@ -81,6 +82,7 @@ void add_local_entry(struct codomain *cod); %token TOK_ID %token TOK_CONDID +%token TOK_CONDLISTID %token TOK_CARET %token TOK_OPEN %token TOK_CLOSE @@ -122,6 +124,13 @@ void add_local_entry(struct codomain *cod); %token TOK_UMOUNT %token TOK_PIVOTROOT %token TOK_IN +%token TOK_DBUS +%token TOK_SEND +%token TOK_RECEIVE +%token TOK_BIND +%token TOK_READ +%token TOK_WRITE +%token TOK_PEER /* rlimits */ %token TOK_RLIMIT @@ -158,6 +167,7 @@ void add_local_entry(struct codomain *cod); struct cod_net_entry *net_entry; struct cod_entry *user_entry; struct mnt_entry *mnt_entry; + struct dbus_entry *dbus_entry; struct flagval flags; int fmode; @@ -174,6 +184,7 @@ void add_local_entry(struct codomain *cod); %type TOK_ID %type TOK_CONDID +%type TOK_CONDLISTID %type TOK_MODE %type file_mode %type profile_base @@ -192,6 +203,8 @@ void add_local_entry(struct codomain *cod); %type mnt_rule %type opt_conds %type cond +%type cond_list +%type opt_cond_list %type flags %type flagvals %type flagval @@ -211,6 +224,10 @@ void add_local_entry(struct codomain *cod); %type opt_flags %type opt_namespace %type opt_id +%type dbus_perm +%type dbus_perms +%type opt_dbus_perm +%type dbus_rule %type opt_named_transition %type opt_unsafe %type opt_file @@ -680,6 +697,25 @@ rules: rules opt_audit_flag mnt_rule $$ = $1; } +rules: rules opt_audit_flag TOK_DENY dbus_rule + { + $4->deny = $4->mode; + if ($2) + $4->audit = $4->mode; + $4->next = $1->dbus_ents; + $1->dbus_ents = $4; + $$ = $1; + } + +rules: rules opt_audit_flag dbus_rule + { + if ($2) + $3->audit = $3->mode; + $3->next = $1->dbus_ents; + $1->dbus_ents = $3; + $$ = $1; + } + rules: rules change_profile { PDEBUG("matched: rules change_profile\n"); @@ -1103,6 +1139,14 @@ opt_conds: { /* nothing */ $$ = NULL; } $$ = $2; } +cond_list: TOK_CONDLISTID TOK_EQUALS TOK_OPENPAREN opt_conds TOK_CLOSEPAREN + { + $$ = $4; + } + +opt_cond_list: { /* nothing */ $$ = NULL; } + | cond_list { $$ = $1; } + mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE { $$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT); @@ -1142,6 +1186,51 @@ mnt_rule: TOK_PIVOTROOT opt_conds opt_id opt_named_transition TOK_END_OF_RULE $$ = do_pivot_rule($2, $3, name); } +dbus_perm: TOK_VALUE + { + if (strcmp($1, "bind") == 0) + $$ = AA_DBUS_BIND; + else if (strcmp($1, "send") == 0 || strcmp($1, "write") == 0) + $$ = AA_DBUS_SEND; + else if (strcmp($1, "receive") == 0 || strcmp($1, "read") == 0) + $$ = AA_DBUS_RECEIVE; + else if ($1) { + parse_dbus_mode($1, &$$, 1); + } else + $$ = 0; + + if ($1) + free($1); + } + | TOK_BIND { $$ = AA_DBUS_BIND; } + | TOK_SEND { $$ = AA_DBUS_SEND; } + | TOK_RECEIVE { $$ = AA_DBUS_RECEIVE; } + | TOK_READ { $$ = AA_DBUS_RECEIVE; } + | TOK_WRITE { $$ = AA_DBUS_SEND; } + | TOK_MODE + { + parse_dbus_mode($1, &$$, 1); + } + +dbus_perms: { /* nothing */ $$ = 0; } + | dbus_perms dbus_perm { $$ = $1 | $2; } + | dbus_perms TOK_COMMA dbus_perm { $$ = $1 | $3; } + +opt_dbus_perm: { /* nothing */ $$ = 0; } + | dbus_perm { $$ = $1; } + | TOK_OPENPAREN dbus_perms TOK_CLOSEPAREN { $$ = $2; } + +dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE + { + struct dbus_entry *ent; + + ent = new_dbus_entry($2, $3, $4); + if (!ent) { + yyerror(_("Memory allocation error.")); + } + $$ = ent; + } + hat_start: TOK_CARET {} | TOK_HAT {}