[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