On Tue, Jul 30, 2013 at 10:56:03AM -0700, Tyler Hicks wrote: > This patch implements the parsing of DBus rules. > > It attempts to catch all corner cases, such as specifying a bind > permission with an interface conditional or specifying a subject name > conditional and a peer name conditional in the same rule. > > It introduces the concept of conditional lists to the lexer and parser > in order to handle 'peer=(label=/usr/bin/foo name=com.foo.bar)', since > the existing list support in the lexer only supports a list of values. > > The DBus rules are encoded as follows: > > bus,name<bind_perm>,peer_label,path,interface,member<rw_perms> > > Bind rules stop matching at name<bind_perm>. Note that name is used for > the subject name in bind rules and the peer name in rw rules. The > function new_dbus_entry() is what does the proper sanitization to make > sure that if a name conditional is specified, that it is the subject > name in the case of a bind rule or that it is the peer name in the case > of a rw rule. > > Signed-off-by: Tyler Hicks <[email protected]>
Acked-by: Seth Arnold <[email protected]> I like the new function :) thanks! > --- > > * Changes in v2: > - Rename copy_conditionals() to move_conditionals(). The char *value is > copied from the cond_entry to the dbus_entry and then the pointer in the > cond_entry is set to NULL, so calling it a move is more correct. > - Make pointer handling in move_conditionals() easier to read by creating a > new function, move_conditional_value(), that ensures an existing > conditional value will not be clobbered before moving the pointer. > - When printing the int mode in parse_dbus_mode(), use %x instead of %llx. > > parser/Makefile | 7 +- > parser/dbus.c | 183 > +++++++++++++++++++++++++++++++++++++++++++++++++ > parser/dbus.h | 48 +++++++++++++ > parser/immunix.h | 7 ++ > parser/parser.h | 7 ++ > parser/parser_lex.l | 165 ++++++++++++++++++++++++++++++++++++++++++-- > parser/parser_misc.c | 103 ++++++++++++++++++++++++++++ > parser/parser_policy.c | 2 + > parser/parser_regex.c | 132 ++++++++++++++++++++++++++++++++++- > parser/parser_yacc.y | 89 ++++++++++++++++++++++++ > 10 files changed, 732 insertions(+), 11 deletions(-) > create mode 100644 parser/dbus.c > create mode 100644 parser/dbus.h > > diff --git a/parser/Makefile b/parser/Makefile > index f859f0e..7f691ca 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 0000000..d08462d > --- /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 <stdlib.h> > +#include <string.h> > + > +#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 0000000..32991f2 > --- /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 ebb2d2e..f5064e8 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 8199f43..6d7e84b 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 539e16a..7761dde 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(); > } > > -<INITIAL,MOUNT_MODE>{ > +<INITIAL,MOUNT_MODE,DBUS_MODE>{ > {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 <= > > } > > +<LIST_COND_VAL>{ > + {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); > + } > +} > + > +<LIST_COND_PAREN_VAL>{ > + {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); > + } > +} > + > +<LIST_COND_MODE>{ > + {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); > + } > +} > + > +<EXTCONDLIST_MODE>{ > + {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 <EXTCONDLIST_MODE> 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); > + } > + > +} > + > <ASSIGN_MODE>{ > {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } > > @@ -547,7 +678,23 @@ LT_EQUAL <= > } > } > > -<MOUNT_MODE>{ > +<DBUS_MODE>{ > + 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); > + } > +} > + > +<MOUNT_MODE,DBUS_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 5f211b9..d864737 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 dce1b0d..9673a10 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 0ba8114..9dd2977 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 351a173..0fedbc8 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 <unistd.h> > #include <netinet/in.h> > @@ -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 <id> TOK_ID > %type <id> TOK_CONDID > +%type <id> TOK_CONDLISTID > %type <mode> TOK_MODE > %type <fmode> file_mode > %type <cod> profile_base > @@ -192,6 +203,8 @@ void add_local_entry(struct codomain *cod); > %type <mnt_entry> mnt_rule > %type <cond_entry> opt_conds > %type <cond_entry> cond > +%type <cond_entry> cond_list > +%type <cond_entry> opt_cond_list > %type <flags> flags > %type <flags> flagvals > %type <flags> flagval > @@ -211,6 +224,10 @@ void add_local_entry(struct codomain *cod); > %type <boolean> opt_flags > %type <id> opt_namespace > %type <id> opt_id > +%type <fmode> dbus_perm > +%type <fmode> dbus_perms > +%type <fmode> opt_dbus_perm > +%type <dbus_entry> dbus_rule > %type <transition> opt_named_transition > %type <boolean> opt_unsafe > %type <boolean> 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 {} > > -- > 1.8.3.2 > > > -- > AppArmor mailing list > [email protected] > Modify settings or unsubscribe at: > https://lists.ubuntu.com/mailman/listinfo/apparmor >
signature.asc
Description: Digital signature
-- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
