[apparmor] [PATCH 2/4] parser: Add support for DBus rules

Seth Arnold seth.arnold at canonical.com
Mon Jul 29 22:40:35 UTC 2013


On Sat, Jul 27, 2013 at 02:45:16AM -0700, Tyler Hicks wrote:
> This patch implements the parsing of DBus rules.
> 
> It attempts to catch all corner cases, such as specifying a bind
> permission with an interface conditional or specifying a subject name
> conditional and a peer name conditional in the same rule.
> 
> It introduces the concept of conditional lists to the lexer and parser
> in order to handle 'peer=(label=/usr/bin/foo name=com.foo.bar)', since
> the existing list support in the lexer only supports a list of values.
> 
> The DBus rules are encoded as follows:
> 
> bus,name<bind_perm>,peer_label,path,interface,member<rw_perms>
> 
> Bind rules stop matching at name<bind_perm>. Note that name is used for
> the subject name in bind rules and the peer name in rw rules. The
> function new_dbus_entry() is what does the proper sanitization to make
> sure that if a name conditional is specified, that it is the subject
> name in the case of a bind rule or that it is the peer name in the case
> of a rw rule.
> 
> Signed-off-by: Tyler Hicks <tyhicks at canonical.com>

Thanks, some comments inline..

> ---
>  parser/Makefile        |   7 +-
>  parser/dbus.c          | 186 +++++++++++++++++++++++++++++++++++++++++++++++++
>  parser/dbus.h          |  48 +++++++++++++
>  parser/immunix.h       |   7 ++
>  parser/parser.h        |   7 ++
>  parser/parser_lex.l    | 165 +++++++++++++++++++++++++++++++++++++++++--
>  parser/parser_misc.c   | 103 +++++++++++++++++++++++++++
>  parser/parser_policy.c |   2 +
>  parser/parser_regex.c  | 132 ++++++++++++++++++++++++++++++++++-
>  parser/parser_yacc.y   |  89 +++++++++++++++++++++++
>  10 files changed, 735 insertions(+), 11 deletions(-)
>  create mode 100644 parser/dbus.c
>  create mode 100644 parser/dbus.h
> 
> diff --git a/parser/Makefile b/parser/Makefile
> index f859f0e..7f691ca 100644
> --- a/parser/Makefile
> +++ b/parser/Makefile
> @@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
>  SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
>         parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
>         parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
> -       parser_alias.c mount.c lib.c
> -HDRS = parser.h parser_include.h immunix.h mount.h lib.h
> +       parser_alias.c mount.c dbus.c lib.c
> +HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h
>  TOOLS = apparmor_parser
>  
>  OBJECTS = $(SRCS:.c=.o)
> @@ -207,6 +207,9 @@ mount.o: mount.c mount.h parser.h immunix.h
>  lib.o: lib.c lib.h parser.h
>  	$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
>  
> +dbus.o: dbus.c dbus.h parser.h immunix.h
> +	$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
> +
>  parser_version.h: Makefile
>  	@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
>  	@mv -f .ver $@
> diff --git a/parser/dbus.c b/parser/dbus.c
> new file mode 100644
> index 0000000..a5f9520
> --- /dev/null
> +++ b/parser/dbus.c
> @@ -0,0 +1,186 @@
> +/*
> + *   Copyright (c) 2013
> + *   Canonical, Ltd. (All rights reserved)
> + *
> + *   This program is free software; you can redistribute it and/or
> + *   modify it under the terms of version 2 of the GNU General Public
> + *   License published by the Free Software Foundation.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + *
> + *   You should have received a copy of the GNU General Public License
> + *   along with this program; if not, contact Novell, Inc. or Canonical
> + *   Ltd.
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "parser.h"
> +#include "parser_yacc.h"
> +#include "dbus.h"
> +
> +void free_dbus_entry(struct dbus_entry *ent)
> +{
> +	if (!ent)
> +		return;
> +	free(ent->bus);
> +	free(ent->name);
> +	free(ent->peer_label);
> +	free(ent->path);
> +	free(ent->interface);
> +	free(ent->member);
> +
> +	free(ent);
> +}
> +
> +static int list_len(struct value_list *v)
> +{
> +	int len = 0;
> +	struct value_list *tmp;
> +
> +	list_for_each(v, tmp)
> +		len++;
> +
> +	return len;
> +}
> +
> +static void copy_conditionals(struct dbus_entry *ent, struct cond_entry *conds)
> +{
> +	struct cond_entry *cond_ent;
> +
> +	list_for_each(conds, cond_ent) {
> +		char **ent_member = NULL;
> +
> +		/* for now disallow keyword 'in' (list) */
> +		if (!cond_ent->eq)
> +			yyerror("keyword \"in\" is not allowed in dbus rules\n");
> +		if (list_len(cond_ent->vals) > 1)
> +			yyerror("dbus conditional \"%s\" only supports a single value\n",
> +				cond_ent->name);
> +
> +		if (strcmp(cond_ent->name, "bus") == 0) {
> +			ent_member = &ent->bus;
> +		} else if (strcmp(cond_ent->name, "name") == 0) {
> +			ent_member = &ent->name;
> +		} else if (strcmp(cond_ent->name, "label") == 0) {
> +			ent_member = &ent->peer_label;
> +		} else if (strcmp(cond_ent->name, "path") == 0) {
> +			ent_member = &ent->path;
> +		} else if (strcmp(cond_ent->name, "interface") == 0) {
> +			ent_member = &ent->interface;
> +		} else if (strcmp(cond_ent->name, "member") == 0) {
> +			ent_member = &ent->member;
> +		} else {
> +			yyerror("invalid dbus conditional \"%s\"\n",
> +				cond_ent->name);
> +		}
> +
> +		if (ent_member) {
> +			if (*ent_member) {
> +				yyerror("dbus conditional \"%s\" can only be specified once\n",
> +					cond_ent->name);
> +			}
> +
> +			(*ent_member) = cond_ent->vals->value;
> +		}
> +
> +		cond_ent->vals->value = NULL;
> +	}
> +}

This is a bit intricate :) I've read through it a few times and I'm
not convinced I understand it. It seems a strange place to put the 'in'
check, and the pointer handling seems more complicated than ideal. I'm
sorry though, I don't have a concrete suggestion for improvement.

> +
> +struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds,
> +				  struct cond_entry *peer_conds)
> +{
> +	struct dbus_entry *ent;
> +	int name_is_subject_cond = 0, message_rule = 0, service_rule = 0;
> +
> +	ent = (struct dbus_entry*) calloc(1, sizeof(struct dbus_entry));
> +	if (!ent)
> +		goto out;
> +
> +	/* Copy the global and/or subject conditionals and check the results */
> +	copy_conditionals(ent, conds);
> +	if (ent->name)
> +		name_is_subject_cond = 1;
> +	if (ent->peer_label)
> +		yyerror("dbus \"label\" conditional can only be used inside of the \"peer=()\" grouping\n");
> +
> +	/* Copy the peer conditionals */
> +	copy_conditionals(ent, peer_conds);
> +
> +	if (ent->path || ent->interface || ent->member || ent->peer_label ||
> +	    (ent->name && !name_is_subject_cond))
> +		message_rule = 1;
> +
> +	if (ent->name && name_is_subject_cond)
> +		service_rule = 1;
> +
> +	if (message_rule && service_rule)
> +		yyerror("dbus rule contains message conditionals and service conditionals\n");
> +
> +	/* Copy mode. If no mode was specified, assign an implied mode. */
> +	if (mode) {
> +		ent->mode = mode;
> +		if (ent->mode & ~AA_VALID_DBUS_PERMS)
> +			yyerror("mode contains unknown dbus accesss\n");
> +		else if (message_rule && (ent->mode & AA_DBUS_BIND))
> +			yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n");
> +		else if (service_rule && (ent->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)))
> +			yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n");
> +	} else {
> +		ent->mode = AA_VALID_DBUS_PERMS;
> +		if (message_rule)
> +			ent->mode &= ~AA_DBUS_BIND;
> +		else if (service_rule)
> +			ent->mode &= ~(AA_DBUS_SEND | AA_DBUS_RECEIVE);
> +	}
> +
> +out:
> +	free_cond_list(conds);
> +	return ent;
> +}
> +
> +
> +void print_dbus_entry(struct dbus_entry *ent)
> +{
> +	if (ent->audit)
> +		fprintf(stderr, "audit ");
> +	if (ent->deny)
> +		fprintf(stderr, "deny ");
> +
> +	fprintf(stderr, "dbus ( ");
> +
> +	if (ent->mode & AA_DBUS_SEND)
> +		fprintf(stderr, "send ");
> +	if (ent->mode & AA_DBUS_RECEIVE)
> +		fprintf(stderr, "receive ");
> +	if (ent->mode & AA_DBUS_BIND)
> +		fprintf(stderr, "bind ");
> +	fprintf(stderr, ")");
> +
> +	if (ent->bus)
> +		fprintf(stderr, " bus=\"%s\"", ent->bus);
> +	if ((ent->mode & AA_DBUS_BIND) && ent->name)
> +		fprintf(stderr, " name=\"%s\"", ent->name);
> +	if (ent->path)
> +		fprintf(stderr, " path=\"%s\"", ent->path);
> +	if (ent->interface)
> +		fprintf(stderr, " interface=\"%s\"", ent->interface);
> +	if (ent->member)
> +		fprintf(stderr, " member=\"%s\"", ent->member);
> +
> +	if (!(ent->mode & AA_DBUS_BIND) && (ent->peer_label || ent->name)) {
> +		fprintf(stderr, " peer=( ");
> +		if (ent->peer_label)
> +			fprintf(stderr, "label=\"%s\" ", ent->peer_label);
> +		if (ent->name)
> +			fprintf(stderr, "name=\"%s\" ", ent->name);
> +		fprintf(stderr, ")");
> +	}
> +
> +	fprintf(stderr, ",\n");
> +}
> diff --git a/parser/dbus.h b/parser/dbus.h
> new file mode 100644
> index 0000000..32991f2
> --- /dev/null
> +++ b/parser/dbus.h
> @@ -0,0 +1,48 @@
> +/*
> + *   Copyright (c) 2013
> + *   Canonical, Ltd. (All rights reserved)
> + *
> + *   This program is free software; you can redistribute it and/or
> + *   modify it under the terms of version 2 of the GNU General Public
> + *   License published by the Free Software Foundation.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + *
> + *   You should have received a copy of the GNU General Public License
> + *   along with this program; if not, contact Novell, Inc. or Canonical
> + *   Ltd.
> + */
> +
> +#ifndef __AA_DBUS_H
> +#define __AA_DBUS_H
> +
> +#include "parser.h"
> +
> +struct dbus_entry {
> +	char *bus;
> +	/**
> +	 * Be careful! ->name can be the subject or the peer name, depending on
> +	 * whether the rule is a bind rule or a send/receive rule. See the
> +	 * comments in new_dbus_entry() for details.
> +	 */
> +	char *name;
> +	char *peer_label;
> +	char *path;
> +	char *interface;
> +	char *member;
> +	int mode;
> +	int audit;
> +	int deny;
> +
> +	struct dbus_entry *next;
> +};
> +
> +void free_dbus_entry(struct dbus_entry *ent);
> +struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds,
> +				  struct cond_entry *peer_conds);
> +void print_dbus_entry(struct dbus_entry *ent);
> +
> +#endif /* __AA_DBUS_H */
> diff --git a/parser/immunix.h b/parser/immunix.h
> index ebb2d2e..f5064e8 100644
> --- a/parser/immunix.h
> +++ b/parser/immunix.h
> @@ -40,6 +40,13 @@
>  #define AA_EXEC_MOD_2			(1 << 12)
>  #define AA_EXEC_MOD_3			(1 << 13)
>  
> +#define AA_DBUS_SEND			AA_MAY_WRITE
> +#define AA_DBUS_RECEIVE			AA_MAY_READ
> +#define AA_DBUS_BIND			(1 << 6)
> +
> +#define AA_VALID_DBUS_PERMS		(AA_DBUS_SEND | AA_DBUS_RECEIVE | \
> +					 AA_DBUS_BIND)
> +
>  #define AA_BASE_PERMS			(AA_MAY_EXEC | AA_MAY_WRITE | \
>  					 AA_MAY_READ | AA_MAY_APPEND | \
>  					 AA_MAY_LINK | AA_MAY_LOCK | \
> diff --git a/parser/parser.h b/parser/parser.h
> index 8199f43..6d7e84b 100644
> --- a/parser/parser.h
> +++ b/parser/parser.h
> @@ -142,6 +142,7 @@ struct codomain {
>  
>  	char *exec_table[AA_EXEC_COUNT];
>  	struct cod_entry *entries;
> +	struct dbus_entry *dbus_ents;
>  	struct mnt_entry *mnt_ents;
>  
>  	void *hat_table;
> @@ -301,6 +302,8 @@ extern char *basedir;
>  /* parser_regex.c */
>  extern int process_regex(struct codomain *cod);
>  extern int post_process_entry(struct cod_entry *entry);
> +extern int process_dbus(struct codomain *cod);
> +
>  extern void reset_regex(void);
>  
>  extern int process_policydb(struct codomain *cod);
> @@ -319,6 +322,7 @@ extern void free_value_list(struct value_list *list);
>  extern void print_value_list(struct value_list *list);
>  extern struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list);
>  extern void free_cond_entry(struct cond_entry *ent);
> +extern void free_cond_list(struct cond_entry *ents);
>  extern void print_cond_entry(struct cond_entry *ent);
>  extern char *processid(char *string, int len);
>  extern char *processquoted(char *string, int len);
> @@ -328,6 +332,7 @@ extern int name_to_capability(const char *keyword);
>  extern int get_rlimit(const char *name);
>  extern char *process_var(const char *var);
>  extern int parse_mode(const char *mode);
> +extern int parse_dbus_mode(const char *str_mode, int *mode, int fail);
>  extern struct cod_entry *new_entry(char *namespace, char *id, int mode,
>  				   char *link_id);
>  extern struct aa_network_entry *new_network_ent(unsigned int family,
> @@ -344,6 +349,7 @@ extern int str_to_boolean(const char* str);
>  extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
>  extern void free_cod_entries(struct cod_entry *list);
>  extern void free_mnt_entries(struct mnt_entry *list);
> +extern void free_dbus_entries(struct dbus_entry *list);
>  
>  /* parser_symtab.c */
>  struct set_value {;
> @@ -385,6 +391,7 @@ extern void post_process_file_entries(struct codomain *cod);
>  extern void post_process_mnt_entries(struct codomain *cod);
>  extern int post_process_policy(int debug_only);
>  extern int process_hat_regex(struct codomain *cod);
> +extern int process_hat_dbus(struct codomain *cod);
>  extern int process_hat_variables(struct codomain *cod);
>  extern int process_hat_policydb(struct codomain *cod);
>  extern int post_merge_rules(void);
> diff --git a/parser/parser_lex.l b/parser/parser_lex.l
> index 539e16a..7761dde 100644
> --- a/parser/parser_lex.l
> +++ b/parser/parser_lex.l
> @@ -53,6 +53,12 @@
>  #define NPDEBUG(fmt, args...)	/* Do nothing */
>  
>  #define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0)
> +#define RETURN_TOKEN(X) \
> +do { \
> +	DUMP_PREPROCESS; \
> +	PDEBUG("Matched: %s\n", yytext); \
> +	return (X); \
> +} while (0)
>  
>  #define YY_NO_INPUT
>  
> @@ -198,9 +204,11 @@ POST_VAR_ID_CHARS	[^ \t\n"!,]{-}[=\+]
>  POST_VAR_ID 	{POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS})
>  LIST_VALUE_ID_CHARS	[^ \t\n"!,]{-}[()]
>  LIST_VALUE_ID	{LIST_VALUE_ID_CHARS}+
> +QUOTED_LIST_VALUE_ID	{LIST_VALUE_ID}|\"{LIST_VALUE_ID}\"
>  ID_CHARS_NOEQ	[^ \t\n"!,]{-}[=]
> +LEADING_ID_CHARS_NOEQ	[^ \t\n"!,]{-}[=()]
>  ID_NOEQ		{ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
> -IDS_NOEQ	{ID_NOEQ}+
> +IDS_NOEQ	{LEADING_ID_CHARS_NOEQ}{ID_NOEQ}*
>  ALLOWED_QUOTED_ID 	[^\0"]|\\\"
>  QUOTED_ID	\"{ALLOWED_QUOTED_ID}*\"
>  
> @@ -228,11 +236,16 @@ LT_EQUAL	<=
>  %x SUB_ID
>  %x SUB_VALUE
>  %x EXTCOND_MODE
> +%x EXTCONDLIST_MODE
>  %x NETWORK_MODE
>  %x LIST_VAL_MODE
> +%x LIST_COND_MODE
> +%x LIST_COND_VAL
> +%x LIST_COND_PAREN_VAL
>  %x ASSIGN_MODE
>  %x RLIMIT_MODE
>  %x MOUNT_MODE
> +%x DBUS_MODE
>  %x CHANGE_PROFILE_MODE
>  %x INCLUDE
>  
> @@ -278,7 +291,7 @@ LT_EQUAL	<=
>  	if ( !YY_CURRENT_BUFFER ) yyterminate();
>  }
>  
> -<INITIAL,MOUNT_MODE>{
> +<INITIAL,MOUNT_MODE,DBUS_MODE>{
>  	{VARIABLE_NAME}/{WS}*=	{
>  				/* we match to the = in the lexer so that
>  				 * can switch scanner state.  By the time
> @@ -286,11 +299,19 @@ LT_EQUAL	<=
>  				 * as bison may have requested the next
>  				 * token from the scanner
>  				 */
> +				int token = get_keyword_token(yytext);
> +
>  				DUMP_PREPROCESS;
> -				PDEBUG("conditional %s=\n", yytext);
> -				yylval.id = processid(yytext, yyleng);
> -				yy_push_state(EXTCOND_MODE);
> -				return TOK_CONDID;
> +				if (token == TOK_PEER) {
> +					PDEBUG("conditional list %s=\n", yytext);
> +					yy_push_state(EXTCONDLIST_MODE);
> +					return TOK_CONDLISTID;
> +				} else {
> +					PDEBUG("conditional %s=\n", yytext);
> +					yylval.id = processid(yytext, yyleng);
> +					yy_push_state(EXTCOND_MODE);
> +					return TOK_CONDID;
> +				}
>  			}
>  	{VARIABLE_NAME}/{WS}+in{WS}*\(	{
>  				/* we match to 'in' in the lexer so that
> @@ -422,6 +443,116 @@ LT_EQUAL	<=
>  
>  }
>  
> +<LIST_COND_VAL>{
> +	{WS}+		{ DUMP_PREPROCESS; /* Eat whitespace */ }
> +
> +	({LIST_VALUE_ID}|{QUOTED_LIST_VALUE_ID}) {
> +			DUMP_PREPROCESS;
> +			yylval.id = processid(yytext, yyleng);
> +			PDEBUG("listcond value: \"%s\"\n",  yylval.id);
> +			yy_pop_state();
> +			return TOK_VALUE;
> +		}
> +
> +	[^\n]	{
> +			DUMP_PREPROCESS;
> +			/* Something we didn't expect */
> +			yyerror(_("Found unexpected character: '%s'"), yytext);
> +		}
> +}
> +
> +<LIST_COND_PAREN_VAL>{
> +	{CLOSE_PAREN}	{
> +			DUMP_PREPROCESS;
> +			yy_pop_state();
> +			}
> +
> +	{WS}+		{ DUMP_PREPROCESS; /* Eat whitespace */ }
> +
> +	({LIST_VALUE_ID}|{QUOTED_LIST_VALUE_ID}) {
> +			DUMP_PREPROCESS;
> +			yylval.id = processid(yytext, yyleng);
> +			PDEBUG("listcond paren value: \"%s\"\n",  yylval.id);
> +			return TOK_VALUE;
> +		}
> +
> +	[^\n]	{
> +			DUMP_PREPROCESS;
> +			/* Something we didn't expect */
> +			yyerror(_("Found unexpected character: '%s'"), yytext);
> +		}
> +}
> +
> +<LIST_COND_MODE>{
> +	{CLOSE_PAREN}	{
> +			DUMP_PREPROCESS;
> +			PDEBUG("listcond: )\n");
> +			yy_pop_state();
> +			return TOK_CLOSEPAREN;
> +			}
> +
> +	{WS}+		{ DUMP_PREPROCESS; /* Eat whitespace */ }
> +
> +	{COMMA}		{
> +			DUMP_PREPROCESS;
> +			PDEBUG("listcond: , \n");
> +			/* Eat comma, its an optional separator */
> +			}
> +
> +	{ID_CHARS_NOEQ}+	{
> +			DUMP_PREPROCESS;
> +			PDEBUG("listcond conditional %s=\n", yytext);
> +			yylval.id = processid(yytext, yyleng);
> +			return TOK_CONDID;
> +			}
> +
> +	{EQUALS}{WS}*{OPEN_PAREN}	{
> +			DUMP_PREPROCESS;
> +			yy_push_state(LIST_COND_PAREN_VAL);
> +			return TOK_EQUALS;
> +			}
> +
> +	{EQUALS}	{
> +			DUMP_PREPROCESS;
> +			yy_push_state(LIST_COND_VAL);
> +			return TOK_EQUALS;
> +			}
> +
> +	[^\n]		{
> +			DUMP_PREPROCESS;
> +			/* Something we didn't expect */
> +			yyerror(_("Found unexpected character: '%s'"), yytext);
> +			}
> +}
> +
> +<EXTCONDLIST_MODE>{
> +	{WS}+		{ DUMP_PREPROCESS; /* Eat whitespace */ }
> +
> +	{EQUALS}	{
> +			DUMP_PREPROCESS;
> +			return TOK_EQUALS;
> +		}
> +
> +	{OPEN_PAREN}	{
> +			DUMP_PREPROCESS;
> +			PDEBUG("extcondlist (\n");
> +			/* Don't push state here as this is a transition
> +			 * start condition and we want to return to the start
> +			 * condition that invoked <EXTCONDLIST_MODE> when
> +			 * LIST_VAL_ID is done
> +			 */
> +			BEGIN(LIST_COND_MODE);
> +			return TOK_OPENPAREN;
> +		}
> +
> +	[^\n]	{
> +			DUMP_PREPROCESS;
> +			/* Something we didn't expect */
> +			yyerror(_("Found unexpected character: '%s' %d"), yytext, *yytext);
> +		}
> +
> +}
> +
>  <ASSIGN_MODE>{
>  	{WS}+		{ DUMP_PREPROCESS; /* Eat whitespace */ }
>  
> @@ -547,7 +678,23 @@ LT_EQUAL	<=
>  			}
>  }
>  
> -<MOUNT_MODE>{
> +<DBUS_MODE>{
> +	send		{ RETURN_TOKEN(TOK_SEND); }
> +	receive		{ RETURN_TOKEN(TOK_RECEIVE); }
> +	bind		{ RETURN_TOKEN(TOK_BIND); }
> +	read		{ RETURN_TOKEN(TOK_READ); }
> +	write		{ RETURN_TOKEN(TOK_WRITE); }
> +	{OPEN_PAREN}	{
> +			yy_push_state(LIST_VAL_MODE);
> +			RETURN_TOKEN(TOK_OPENPAREN);
> +			}
> +	(r|w|rw|wr)/([[:space:],])	{
> +			yylval.mode = strdup(yytext);
> +			RETURN_TOKEN(TOK_MODE);
> +			}
> +}
> +
> +<MOUNT_MODE,DBUS_MODE>{

I think this <MOUNT_MODE,DBUS_MODE> is odd -- it looks to me like it'll
parse {ARROW} in a DBus rule...

>  	{WS}+		{  DUMP_PREPROCESS; /* Ignoring whitespace */ }
>  
>  	{ARROW}		{
> @@ -709,6 +856,10 @@ LT_EQUAL	<=
>  				PDEBUG("Entering mount\n");
>  				yy_push_state(MOUNT_MODE);
>  				break;
> +			case TOK_DBUS:
> +				PDEBUG("Entering dbus\n");
> +				yy_push_state(DBUS_MODE);
> +				break;
>  			default: /* nothing */
>  				break;
>  			}
> diff --git a/parser/parser_misc.c b/parser/parser_misc.c
> index 5f211b9..92ea1f4 100644
> --- a/parser/parser_misc.c
> +++ b/parser/parser_misc.c
> @@ -38,6 +38,7 @@
>  #include "parser.h"
>  #include "parser_yacc.h"
>  #include "mount.h"
> +#include "dbus.h"
>  
>  /* #define DEBUG */
>  #ifdef DEBUG
> @@ -85,6 +86,14 @@ static struct keyword_table keyword_table[] = {
>  	{"unmount",		TOK_UMOUNT},
>  	{"pivot_root",		TOK_PIVOTROOT},
>  	{"in",			TOK_IN},
> +	{"dbus",		TOK_DBUS},
> +	{"send",                TOK_SEND},
> +	{"receive",             TOK_RECEIVE},
> +	{"bind",                TOK_BIND},
> +	{"read",                TOK_READ},
> +	{"write",               TOK_WRITE},
> +	{"peer",		TOK_PEER},
> +
>  	/* terminate */
>  	{NULL, 0}
>  };
> @@ -724,6 +733,81 @@ int parse_mode(const char *str_mode)
>  	return mode;
>  }
>  
> +static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused)
> +{
> +	int mode = 0;
> +	const char *p;
> +
> +	PDEBUG("Parsing DBus mode: %s\n", str_mode);
> +
> +	if (!str_mode)
> +		return 0;
> +
> +	p = str_mode;
> +	while (*p) {
> +		char this = *p;
> +		char lower;
> +
> +reeval:
> +		switch (this) {
> +		case COD_READ_CHAR:
> +			PDEBUG("Parsing DBus mode: found %s READ\n", mode_desc);
> +			mode |= AA_DBUS_RECEIVE;
> +			break;
> +
> +		case COD_WRITE_CHAR:
> +			PDEBUG("Parsing DBus mode: found %s WRITE\n",
> +			       mode_desc);
> +			mode |= AA_DBUS_SEND;
> +			break;
> +
> +		/* error cases */
> +
> +		default:
> +			lower = tolower(this);
> +			switch (lower) {
> +			case COD_READ_CHAR:
> +			case COD_WRITE_CHAR:
> +				PDEBUG("Parsing DBus mode: found invalid upper case char %c\n",
> +				       this);
> +				warn_uppercase();
> +				this = lower;
> +				goto reeval;
> +				break;
> +			default:
> +				if (fail)
> +					yyerror(_("Internal: unexpected DBus mode character '%c' in input"),
> +						this);
> +				else
> +					return 0;
> +				break;
> +			}
> +			break;
> +		}
> +		p++;
> +	}
> +
> +	PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode);
> +
> +	*result = mode;
> +	return 1;
> +}
> +
> +int parse_dbus_mode(const char *str_mode, int *mode, int fail)
> +{
> +	*mode = 0;
> +	if (!parse_dbus_sub_mode(str_mode, mode, fail, ""))
> +		return 0;
> +	if (*mode & ~AA_VALID_DBUS_PERMS) {
> +		if (fail)
> +			yyerror(_("Internal error generated invalid DBus perm 0x%llx\n"),
> +				  mode);

llx is for long long arguments; mode is an int.

> +		else
> +			return 0;
> +	}
> +	return 1;
> +}
> +
>  struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id)
>  {
>  	struct cod_entry *entry = NULL;
> @@ -803,6 +887,16 @@ void free_mnt_entries(struct mnt_entry *list)
>  	free(list);
>  }
>  
> +void free_dbus_entries(struct dbus_entry *list)
> +{
> +	if (!list)
> +		return;
> +	if (list->next)
> +		free_dbus_entries(list->next);
> +
> +	free_dbus_entry(list);
> +}
> +
>  static void debug_base_perm_mask(int mask)
>  {
>  	if (HAS_MAY_READ(mask))
> @@ -1148,6 +1242,15 @@ void free_cond_entry(struct cond_entry *ent)
>  	}
>  }
>  
> +void free_cond_list(struct cond_entry *ents)
> +{
> +	struct cond_entry *entry, *tmp;
> +
> +	list_for_each_safe(ents, entry, tmp) {
> +		free_cond_entry(entry);
> +	}
> +}
> +
>  void print_cond_entry(struct cond_entry *ent)
>  {
>  	if (ent) {
> diff --git a/parser/parser_policy.c b/parser/parser_policy.c
> index dce1b0d..9673a10 100644
> --- a/parser/parser_policy.c
> +++ b/parser/parser_policy.c
> @@ -30,6 +30,7 @@
>  
>  #include "parser.h"
>  #include "mount.h"
> +#include "dbus.h"
>  #include "parser_yacc.h"
>  
>  /* #define DEBUG */
> @@ -818,6 +819,7 @@ void free_policy(struct codomain *cod)
>  	free_hat_table(cod->hat_table);
>  	free_cod_entries(cod->entries);
>  	free_mnt_entries(cod->mnt_ents);
> +	free_dbus_entries(cod->dbus_ents);
>  	if (cod->dfarules)
>  		aare_delete_ruleset(cod->dfarules);
>  	if (cod->dfa)
> diff --git a/parser/parser_regex.c b/parser/parser_regex.c
> index 0ba8114..9dd2977 100644
> --- a/parser/parser_regex.c
> +++ b/parser/parser_regex.c
> @@ -29,6 +29,7 @@
>  #include "libapparmor_re/apparmor_re.h"
>  #include "libapparmor_re/aare_rules.h"
>  #include "mount.h"
> +#include "dbus.h"
>  #include "policydb.h"
>  
>  enum error_type {
> @@ -1041,7 +1042,107 @@ fail:
>  }
>  
>  
> -int post_process_policydb_ents(struct codomain *cod)
> +static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry)
> +{
> +	char busbuf[PATH_MAX + 3];
> +	char namebuf[PATH_MAX + 3];
> +	char peer_labelbuf[PATH_MAX + 3];
> +	char pathbuf[PATH_MAX + 3];
> +	char ifacebuf[PATH_MAX + 3];
> +	char memberbuf[PATH_MAX + 3];
> +	char *p, *vec[6];
> +
> +	pattern_t ptype;
> +	int pos;
> +
> +	if (!entry) 		/* shouldn't happen */
> +		return TRUE;
> +
> +	p = busbuf;
> +	p += sprintf(p, "\\x%02x", AA_CLASS_DBUS);
> +
> +	if (entry->bus) {
> +		ptype = convert_aaregex_to_pcre(entry->bus, 0, p,
> +						PATH_MAX+3 - (p - busbuf), &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		strcpy(p, "[^\\000]*");
> +	}
> +	vec[0] = busbuf;
> +
> +	if (entry->name) {
> +		ptype = convert_aaregex_to_pcre(entry->name, 0, namebuf,
> +						PATH_MAX+3, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[1] = namebuf;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[1] = "[^\\000]*";
> +	}
> +
> +	if (entry->peer_label) {
> +		ptype = convert_aaregex_to_pcre(entry->peer_label, 0,
> +						peer_labelbuf, PATH_MAX+3,
> +						&pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[2] = peer_labelbuf;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[2] = "[^\\000]*";
> +	}
> +
> +	if (entry->path) {
> +		ptype = convert_aaregex_to_pcre(entry->path, 0, pathbuf,
> +						PATH_MAX+3, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[3] = pathbuf;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[3] = "[^\\000]*";
> +	}
> +
> +	if (entry->interface) {
> +		ptype = convert_aaregex_to_pcre(entry->interface, 0, ifacebuf,
> +						PATH_MAX+3, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[4] = ifacebuf;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[4] = "[^\\000]*";
> +	}
> +
> +	if (entry->member) {
> +		ptype = convert_aaregex_to_pcre(entry->member, 0, memberbuf,
> +						PATH_MAX+3, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[5] = memberbuf;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[5] = "[^\\000]*";
> +	}
> +
> +	if (entry->mode & AA_DBUS_BIND) {
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->mode & AA_DBUS_BIND, entry->audit & AA_DBUS_BIND, 2, vec, dfaflags))
> +			goto fail;
> +	}
> +	if (entry->mode & ~AA_DBUS_BIND) {
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->mode, entry->audit, 6, vec, dfaflags))
> +			goto fail;
> +	}
> +	return TRUE;
> +
> +fail:
> +	return FALSE;
> +}
> +
> +static int post_process_mnt_ents(struct codomain *cod)
>  {
>  	int ret = TRUE;
>  	int count = 0;
> @@ -1058,10 +1159,37 @@ int post_process_policydb_ents(struct codomain *cod)
>  	} else if (cod->mnt_ents && !kernel_supports_mount)
>  		pwarn("profile %s mount rules not enforced\n", cod->name);
>  
> -	cod->policy_rule_count = count;
> +	cod->policy_rule_count += count;
>  	return ret;
>  }
>  
> +static int post_process_dbus_ents(struct codomain *cod)
> +{
> +	int ret = TRUE;
> +	struct dbus_entry *entry;
> +	int count = 0;
> +
> +	list_for_each(cod->dbus_ents, entry) {
> +		if (regex_type == AARE_DFA &&
> +		    !process_dbus_entry(cod->policy_rules, entry))
> +			ret = FALSE;
> +		count++;
> +	}
> +
> +	cod->policy_rule_count += count;
> +	return ret;
> +}
> +
> +int post_process_policydb_ents(struct codomain *cod)
> +{
> +	if (!post_process_mnt_ents(cod))
> +		return FALSE;
> +	if (!post_process_dbus_ents(cod))
> +		return FALSE;
> +
> +	return TRUE;
> +}
> +
>  int process_policydb(struct codomain *cod)
>  {
>  	int error = -1;
> diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y
> index 351a173..0fedbc8 100644
> --- a/parser/parser_yacc.y
> +++ b/parser/parser_yacc.y
> @@ -33,6 +33,7 @@
>  
>  #include "parser.h"
>  #include "mount.h"
> +#include "dbus.h"
>  #include "parser_include.h"
>  #include <unistd.h>
>  #include <netinet/in.h>
> @@ -81,6 +82,7 @@ void add_local_entry(struct codomain *cod);
>  
>  %token TOK_ID
>  %token TOK_CONDID
> +%token TOK_CONDLISTID
>  %token TOK_CARET
>  %token TOK_OPEN
>  %token TOK_CLOSE
> @@ -122,6 +124,13 @@ void add_local_entry(struct codomain *cod);
>  %token TOK_UMOUNT
>  %token TOK_PIVOTROOT
>  %token TOK_IN
> +%token TOK_DBUS
> +%token TOK_SEND
> +%token TOK_RECEIVE
> +%token TOK_BIND
> +%token TOK_READ
> +%token TOK_WRITE
> +%token TOK_PEER
>  
>   /* rlimits */
>  %token TOK_RLIMIT
> @@ -158,6 +167,7 @@ void add_local_entry(struct codomain *cod);
>  	struct cod_net_entry *net_entry;
>  	struct cod_entry *user_entry;
>  	struct mnt_entry *mnt_entry;
> +	struct dbus_entry *dbus_entry;
>  
>  	struct flagval flags;
>  	int fmode;
> @@ -174,6 +184,7 @@ void add_local_entry(struct codomain *cod);
>  
>  %type <id> 	TOK_ID
>  %type <id>	TOK_CONDID
> +%type <id>	TOK_CONDLISTID
>  %type <mode> 	TOK_MODE
>  %type <fmode>   file_mode
>  %type <cod>	profile_base
> @@ -192,6 +203,8 @@ void add_local_entry(struct codomain *cod);
>  %type <mnt_entry> mnt_rule
>  %type <cond_entry> opt_conds
>  %type <cond_entry> cond
> +%type <cond_entry> cond_list
> +%type <cond_entry> opt_cond_list
>  %type <flags>	flags
>  %type <flags>	flagvals
>  %type <flags>	flagval
> @@ -211,6 +224,10 @@ void add_local_entry(struct codomain *cod);
>  %type <boolean> opt_flags
>  %type <id>	opt_namespace
>  %type <id>	opt_id
> +%type <fmode>	dbus_perm
> +%type <fmode>	dbus_perms
> +%type <fmode>	opt_dbus_perm
> +%type <dbus_entry>	dbus_rule
>  %type <transition> opt_named_transition
>  %type <boolean> opt_unsafe
>  %type <boolean> opt_file
> @@ -680,6 +697,25 @@ rules: rules opt_audit_flag mnt_rule
>  		$$ = $1;
>  	}
>  
> +rules:  rules opt_audit_flag TOK_DENY dbus_rule
> +	{
> +		$4->deny = $4->mode;
> +		if ($2)
> +			$4->audit = $4->mode;
> +		$4->next = $1->dbus_ents;
> +		$1->dbus_ents = $4;
> +		$$ = $1;
> +	}
> +
> +rules: rules opt_audit_flag dbus_rule
> +	{
> +		if ($2)
> +			$3->audit = $3->mode;
> +		$3->next = $1->dbus_ents;
> +		$1->dbus_ents = $3;
> +		$$ = $1;
> +	}
> +
>  rules:	rules change_profile
>  	{
>  		PDEBUG("matched: rules change_profile\n");
> @@ -1103,6 +1139,14 @@ opt_conds: { /* nothing */ $$ = NULL; }
>  		$$ = $2;
>  	}
>  
> +cond_list: TOK_CONDLISTID TOK_EQUALS TOK_OPENPAREN opt_conds TOK_CLOSEPAREN
> +	{
> +		$$ = $4;
> +	}
> +
> +opt_cond_list: { /* nothing */ $$ = NULL; }
> +	| cond_list { $$ = $1; }
> +
>  mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE
>  	{
>  		$$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT);
> @@ -1142,6 +1186,51 @@ mnt_rule: TOK_PIVOTROOT opt_conds opt_id opt_named_transition TOK_END_OF_RULE
>  		$$ = do_pivot_rule($2, $3, name);
>  	}
>  
> +dbus_perm: TOK_VALUE
> +	{
> +		if (strcmp($1, "bind") == 0)
> +			$$ = AA_DBUS_BIND;
> +		else if (strcmp($1, "send") == 0 || strcmp($1, "write") == 0)
> +			$$ = AA_DBUS_SEND;
> +		else if (strcmp($1, "receive") == 0 || strcmp($1, "read") == 0)
> +			$$ = AA_DBUS_RECEIVE;

Is this code still needed? You've got explicit tokens lower down...

> +		else if ($1) {
> +			parse_dbus_mode($1, &$$, 1);
> +		} else
> +			$$ = 0;
> +
> +		if ($1)
> +			free($1);
> +	}
> +	| TOK_BIND { $$ = AA_DBUS_BIND; }
> +	| TOK_SEND { $$ = AA_DBUS_SEND; }
> +	| TOK_RECEIVE { $$ = AA_DBUS_RECEIVE; }
> +	| TOK_READ { $$ = AA_DBUS_RECEIVE; }
> +	| TOK_WRITE { $$ = AA_DBUS_SEND; }
> +	| TOK_MODE
> +	{
> +		parse_dbus_mode($1, &$$, 1);
> +	}
> +
> +dbus_perms: { /* nothing */ $$ = 0; }
> +	| dbus_perms dbus_perm { $$ = $1 | $2; }
> +	| dbus_perms TOK_COMMA dbus_perm { $$ = $1 | $3; }
> +
> +opt_dbus_perm: { /* nothing */ $$ = 0; }
> +	| dbus_perm  { $$ = $1; }
> +	| TOK_OPENPAREN dbus_perms TOK_CLOSEPAREN { $$ = $2; }
> +
> +dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE
> +	{
> +		struct dbus_entry *ent;
> +
> +		ent = new_dbus_entry($2, $3, $4);
> +		if (!ent) {
> +			yyerror(_("Memory allocation error."));
> +		}
> +		$$ = ent;
> +	}
> +
>  hat_start: TOK_CARET {}
>  	| TOK_HAT {}
>  

Thanks!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20130729/37a8cd90/attachment-0001.pgp>


More information about the AppArmor mailing list