[apparmor] [PATCH 2/4] parser: Add support for DBus rules
Tyler Hicks
tyhicks at canonical.com
Sat Jul 27 09:45:16 UTC 2013
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>
---
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
More information about the AppArmor
mailing list