[apparmor] [PATCH 2/4] parser: Add support for DBus rules
Seth Arnold
seth.arnold at canonical.com
Mon Jul 29 22:40:35 UTC 2013
On Sat, Jul 27, 2013 at 02:45:16AM -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 <tyhicks at canonical.com>
Thanks, some comments inline..
> ---
> parser/Makefile | 7 +-
> parser/dbus.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++
> 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, 735 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..a5f9520
> --- /dev/null
> +++ b/parser/dbus.c
> @@ -0,0 +1,186 @@
> +/*
> + * 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 copy_conditionals(struct dbus_entry *ent, struct cond_entry *conds)
> +{
> + struct cond_entry *cond_ent;
> +
> + list_for_each(conds, cond_ent) {
> + char **ent_member = NULL;
> +
> + /* 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) {
> + ent_member = &ent->bus;
> + } else if (strcmp(cond_ent->name, "name") == 0) {
> + ent_member = &ent->name;
> + } else if (strcmp(cond_ent->name, "label") == 0) {
> + ent_member = &ent->peer_label;
> + } else if (strcmp(cond_ent->name, "path") == 0) {
> + ent_member = &ent->path;
> + } else if (strcmp(cond_ent->name, "interface") == 0) {
> + ent_member = &ent->interface;
> + } else if (strcmp(cond_ent->name, "member") == 0) {
> + ent_member = &ent->member;
> + } else {
> + yyerror("invalid dbus conditional \"%s\"\n",
> + cond_ent->name);
> + }
> +
> + if (ent_member) {
> + if (*ent_member) {
> + yyerror("dbus conditional \"%s\" can only be specified once\n",
> + cond_ent->name);
> + }
> +
> + (*ent_member) = cond_ent->vals->value;
> + }
> +
> + cond_ent->vals->value = NULL;
> + }
> +}
This is a bit intricate :) I've read through it a few times and I'm
not convinced I understand it. It seems a strange place to put the 'in'
check, and the pointer handling seems more complicated than ideal. I'm
sorry though, I don't have a concrete suggestion for improvement.
> +
> +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;
> +
> + /* Copy the global and/or subject conditionals and check the results */
> + copy_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");
> +
> + /* Copy the peer conditionals */
> + copy_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>{
I think this <MOUNT_MODE,DBUS_MODE> is odd -- it looks to me like it'll
parse {ARROW} in a DBus rule...
> {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..92ea1f4 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%llx\n"),
> + mode);
llx is for long long arguments; mode is an int.
> + 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;
Is this code still needed? You've got explicit tokens lower down...
> + 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 {}
>
Thanks!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20130729/37a8cd90/attachment-0001.pgp>
More information about the AppArmor
mailing list