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]>
---
 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;
+       }
+}
+
+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>{
        {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);
+               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

Reply via email to