[apparmor] [patch 02/24] Convert mount and dbus to be subclasses of a generic rule class

Seth Arnold seth.arnold at canonical.com
Tue Mar 11 06:40:01 UTC 2014


On Fri, Mar 07, 2014 at 09:31:23AM -0800, john.johansen at canonical.com wrote:
> This will simplify add new features as most of the code can reside in
> its own class. There are still things to improve but its a start.
> 
> Signed-off-by: John Johansen <john.johansen at canonical.com>

Sorry, I only made it through the first half of the patch. Some small
notes inline:

> 
> ---
>  parser/Makefile          |   13 -
>  parser/dbus.c            |  388 ++++++++++++++++++++++++----------
>  parser/dbus.h            |   32 ++
>  parser/mount.c           |  534 +++++++++++++++++++++++++++++++++++++----------
>  parser/mount.h           |   32 ++
>  parser/parser.h          |   22 +
>  parser/parser_misc.c     |  101 --------
>  parser/parser_policy.c   |   29 --
>  parser/parser_regex.c    |  489 -------------------------------------------
>  parser/parser_variable.c |   66 +----
>  parser/parser_yacc.y     |   51 ++--
>  parser/profile.cc        |    6 
>  parser/profile.h         |    7 
>  parser/rule.c            |   23 ++
>  parser/rule.h            |   51 ++++
>  15 files changed, 906 insertions(+), 938 deletions(-)
> 
> --- 2.9-test.orig/parser/Makefile
> +++ 2.9-test/parser/Makefile
> @@ -79,8 +79,8 @@
>  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 dbus.c lib.c profile.cc
> -HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h
> +       parser_alias.c mount.c dbus.c lib.c profile.cc rule.c
> +HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h rule.h
>  TOOLS = apparmor_parser
>  
>  OBJECTS = $(SRCS:.c=.o)
> @@ -188,7 +188,7 @@
>  parser_yacc.c parser_yacc.h: parser_yacc.y parser.h profile.h
>  	$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
>  
> -parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h
> +parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h
>  	$(LEX) ${LEXFLAGS} -o$@ $<
>  
>  parser_lex.o: parser_lex.c parser.h parser_yacc.h
> @@ -230,18 +230,21 @@
>  parser_common.o: parser_common.c parser.h
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
> -mount.o: mount.c mount.h parser.h immunix.h
> +mount.o: mount.c mount.h parser.h immunix.h rule.h
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
>  lib.o: lib.c lib.h parser.h
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
> -dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h $(APPARMOR_H)
> +dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
>  profile.o: profile.cc profile.h parser.h
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
> +rule.o: rule.c rule.h policydb.h
> +	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
> +
>  parser_version.h: Makefile
>  	@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
>  	@mv -f .ver $@
> --- 2.9-test.orig/parser/dbus.c
> +++ 2.9-test/parser/dbus.c
> @@ -20,23 +20,90 @@
>  #include <string.h>
>  #include <sys/apparmor.h>
>  
> +#include <iomanip>
> +#include <string>
> +#include <sstream>
> +
>  #include "parser.h"
>  #include "profile.h"
>  #include "parser_yacc.h"
>  #include "dbus.h"
>  
> -void free_dbus_entry(struct dbus_entry *ent)
> +#define _(s) gettext(s)
> +
> +static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused)
>  {
> -	if (!ent)
> -		return;
> -	free(ent->bus);
> -	free(ent->name);
> -	free(ent->peer_label);
> -	free(ent->path);
> -	free(ent->interface);
> -	free(ent->member);
> +	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 current = *p;
> +		char lower;
> +
> +reeval:
> +		switch (current) {
> +		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(current);
> +			switch (lower) {
> +			case COD_READ_CHAR:
> +			case COD_WRITE_CHAR:
> +				PDEBUG("Parsing DBus mode: found invalid upper case char %c\n",
> +				       current);
> +				warn_uppercase();
> +				current = lower;
> +				goto reeval;
> +				break;
> +			default:
> +				if (fail)
> +					yyerror(_("Internal: unexpected DBus mode character '%c' in input"),
> +						current);
> +				else
> +					return 0;
> +				break;
> +			}
> +			break;
> +		}
> +		p++;
> +	}
> +
> +	PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode);
> +
> +	*result = mode;
> +	return 1;
> +}
>  
> -	free(ent);
> +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%x\n"),
> +				  mode);
> +		else
> +			return 0;
> +	}
> +	return 1;
>  }
>  
>  static int list_len(struct value_list *v)
> @@ -60,7 +127,7 @@
>  	cond_ent->vals->value = NULL;
>  }
>  
> -static void move_conditionals(struct dbus_entry *ent, struct cond_entry *conds)
> +void dbus_rule::move_conditionals(struct cond_entry *conds)
>  {
>  	struct cond_entry *cond_ent;
>  
> @@ -73,17 +140,17 @@
>  				cond_ent->name);
>  
>  		if (strcmp(cond_ent->name, "bus") == 0) {
> -			move_conditional_value(&ent->bus, cond_ent);
> +			move_conditional_value(&bus, cond_ent);
>  		} else if (strcmp(cond_ent->name, "name") == 0) {
> -			move_conditional_value(&ent->name, cond_ent);
> +			move_conditional_value(&name, cond_ent);
>  		} else if (strcmp(cond_ent->name, "label") == 0) {
> -			move_conditional_value(&ent->peer_label, cond_ent);
> +			move_conditional_value(&peer_label, cond_ent);
>  		} else if (strcmp(cond_ent->name, "path") == 0) {
> -			move_conditional_value(&ent->path, cond_ent);
> +			move_conditional_value(&path, cond_ent);
>  		} else if (strcmp(cond_ent->name, "interface") == 0) {
> -			move_conditional_value(&ent->interface, cond_ent);
> +			move_conditional_value(&interface, cond_ent);
>  		} else if (strcmp(cond_ent->name, "member") == 0) {
> -			move_conditional_value(&ent->member, cond_ent);
> +			move_conditional_value(&member, cond_ent);
>  		} else {
>  			yyerror("invalid dbus conditional \"%s\"\n",
>  				cond_ent->name);
> @@ -91,129 +158,236 @@
>  	}
>  }
>  
> -struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds,
> -				  struct cond_entry *peer_conds)
> +dbus_rule::dbus_rule(int mode_p, struct cond_entry *conds,
> +		     struct cond_entry *peer_conds):
> +	bus(NULL), name(NULL), peer_label(NULL), path(NULL), interface(NULL), member(NULL),
> +	mode(0), audit(0), deny(0)
>  {
> -	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;
> -
>  	/* Move the global/subject conditionals over & check the results */
> -	move_conditionals(ent, conds);
> -	if (ent->name)
> +	move_conditionals(conds);
> +	if (name)
>  		name_is_subject_cond = 1;
> -	if (ent->peer_label)
> +	if (peer_label)
>  		yyerror("dbus \"label\" conditional can only be used inside of the \"peer=()\" grouping\n");
>  
>  	/* Move the peer conditionals */
> -	move_conditionals(ent, peer_conds);
> +	move_conditionals(peer_conds);
>  
> -	if (ent->path || ent->interface || ent->member || ent->peer_label ||
> -	    (ent->name && !name_is_subject_cond))
> +	if (path || interface || member || peer_label ||
> +	    (name && !name_is_subject_cond))
>  		message_rule = 1;
>  
> -	if (ent->name && name_is_subject_cond)
> +	if (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)
> +	if (mode_p) {
> +		mode = mode_p;
> +		if (mode & ~AA_VALID_DBUS_PERMS)
>  			yyerror("mode contains unknown dbus accesss\n");
> -		else if (message_rule && (ent->mode & AA_DBUS_BIND))
> +		else if (message_rule && (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)))
> +		else if (service_rule && (mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)))
>  			yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n");
> -		else if (ent->mode & AA_DBUS_EAVESDROP &&
> -			 (ent->path || ent->interface || ent->member ||
> -			  ent->peer_label || ent->name)) {
> +		else if (mode & AA_DBUS_EAVESDROP &&
> +			 (path || interface || member ||
> +			  peer_label || name)) {
>  			yyerror("dbus \"eavesdrop\" access can only contain a bus conditional\n");
>  		}
>  	} else {
>  		if (message_rule)
> -			ent->mode = (AA_DBUS_SEND | AA_DBUS_RECEIVE);
> +			mode = (AA_DBUS_SEND | AA_DBUS_RECEIVE);
>  		else if (service_rule)
> -			ent->mode = (AA_DBUS_BIND);
> +			mode = (AA_DBUS_BIND);
>  		else
> -			ent->mode = AA_VALID_DBUS_PERMS;
> +			mode = AA_VALID_DBUS_PERMS;
>  	}
>  
> -out:
>  	free_cond_list(conds);
>  	free_cond_list(peer_conds);
> -	return ent;
>  }
>  
> -struct dbus_entry *dup_dbus_entry(struct dbus_entry *orig)
> +ostream &dbus_rule::dump(ostream &os)
> +{
> +	if (audit)
> +		os << "audit ";
> +	if (deny)
> +		os << "deny ";
> +
> +	os << "dbus ( ";
> +
> +	if (mode & AA_DBUS_SEND)
> +		os << "send ";
> +	if (mode & AA_DBUS_RECEIVE)
> +		os << "receive ";
> +	if (mode & AA_DBUS_BIND)
> +		os << "bind ";
> +	if (mode & AA_DBUS_EAVESDROP)
> +		os << "eavesdrop ";
> +	os << ")";
> +
> +	if (bus)
> +		os << " bus=\"" << bus << "\"";
> +	if ((mode & AA_DBUS_BIND) && name)
> +		os << " name=\"" << name << "\"";
> +	if (path)
> +		os << " path=\"" << path << "\"";
> +	if (interface)
> +		os << " interface=\"" << interface << "\"";
> +	if (member)
> +		os << " member=\"" << member << os << "\"";
> +
> +	if (!(mode & AA_DBUS_BIND) && (peer_label || name)) {
> +		os << " peer=( ";
> +		if (peer_label)
> +			os << "label=\"" << peer_label << "\" ";
> +		if (name)
> +			os << "name=\"" << name << "\" ";
> +		os << ")";
> +	}
> +
> +	os << ",\n";
> +
> +	return os;
> +}
> +
> +int dbus_rule::expand_variables(void)
> +{
> +	int error = expand_entry_variables(&bus);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&name);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&peer_label);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&path);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&interface);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&member);
> +	if (error)
> +		return error;
> +
> +	return 0;
> +}
> +
> +int dbus_rule::gen_policy_re(Profile &prof)
>  {
> -	struct dbus_entry *ent = NULL;
> -	ent = (struct dbus_entry *) calloc(1, sizeof(struct dbus_entry));
> -	if (!ent)
> -		return NULL;
> -
> -	DUP_STRING(orig, ent, bus, err);
> -	DUP_STRING(orig, ent, name, err);
> -	DUP_STRING(orig, ent, peer_label, err);
> -	DUP_STRING(orig, ent, path, err);
> -	DUP_STRING(orig, ent, interface, err);
> -	DUP_STRING(orig, ent, member, err);
> -	ent->mode = orig->mode;
> -	ent->audit = orig->audit;
> -	ent->deny = orig->deny;
> -
> -	ent->next = orig->next;
> -
> -	return ent;
> -
> -err:
> -	free_dbus_entry(ent);
> -	return NULL;
> -}
> -
> -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 ");
> -	if (ent->mode & AA_DBUS_EAVESDROP)
> -		fprintf(stderr, "eavesdrop ");
> -	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, ")");
> +	std::string busbuf;
> +	std::string namebuf;
> +	std::string peer_labelbuf;
> +	std::string pathbuf;
> +	std::string ifacebuf;
> +	std::string memberbuf;
> +	std::ostringstream buffer;
> +	const char *vec[6];
> +
> +	pattern_t ptype;
> +	int pos;
> +
> +	if (!kernel_supports_dbus) {
> +		pwarn("profile %s dbus rules not enforced\n", prof.name);
> +		return RULE_NOT_SUPPORTED;
>  	}
>  
> -	fprintf(stderr, ",\n");
> +	buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DBUS;
> +	busbuf.append(buffer.str());
> +
> +	if (bus) {
> +		ptype = convert_aaregex_to_pcre(bus, 0, busbuf, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		busbuf.append(default_match_pattern);
> +	}
> +	vec[0] = busbuf.c_str();
> +
> +	if (name) {
> +		ptype = convert_aaregex_to_pcre(name, 0, namebuf, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[1] = namebuf.c_str();
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[1] = default_match_pattern;
> +	}
> +
> +	if (peer_label) {
> +		ptype = convert_aaregex_to_pcre(peer_label, 0,
> +						peer_labelbuf, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[2] = peer_labelbuf.c_str();
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[2] = default_match_pattern;
> +	}
> +
> +	if (path) {
> +		ptype = convert_aaregex_to_pcre(path, 0, pathbuf, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[3] = pathbuf.c_str();
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[3] = default_match_pattern;
> +	}
> +
> +	if (interface) {
> +		ptype = convert_aaregex_to_pcre(interface, 0, ifacebuf, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[4] = ifacebuf.c_str();
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[4] = default_match_pattern;
> +	}
> +
> +	if (member) {
> +		ptype = convert_aaregex_to_pcre(member, 0, memberbuf, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +		vec[5] = memberbuf.c_str();
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		vec[5] = default_match_pattern;
> +	}
> +
> +	if (mode & AA_DBUS_BIND) {
> +		if (!aare_add_rule_vec(prof.policy.rules, deny,
> +				       mode & AA_DBUS_BIND,
> +				       audit & AA_DBUS_BIND,
> +				       2, vec, dfaflags))
> +			goto fail;
> +	}
> +	if (mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) {
> +		if (!aare_add_rule_vec(prof.policy.rules, deny,
> +				mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
> +				audit & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
> +				6, vec, dfaflags))
> +			goto fail;
> +	}
> +	if (mode & AA_DBUS_EAVESDROP) {
> +		if (!aare_add_rule_vec(prof.policy.rules, deny,
> +				mode & AA_DBUS_EAVESDROP,
> +				audit & AA_DBUS_EAVESDROP,
> +				1, vec, dfaflags))
> +			goto fail;
> +	}
> +
> +	prof.policy.count++;
> +	return RULE_OK;
> +
> +fail:
> +	return RULE_ERROR;
>  }
> --- 2.9-test.orig/parser/dbus.h
> +++ 2.9-test/parser/dbus.h
> @@ -20,8 +20,14 @@
>  #define __AA_DBUS_H
>  
>  #include "parser.h"
> +#include "rule.h"
> +#include "profile.h"
>  
> -struct dbus_entry {
> +extern int parse_dbus_mode(const char *str_mode, int *mode, int fail);
> +
> +class dbus_rule: public rule_t {
> +	void move_conditionals(struct cond_entry *conds);
> +public:
>  	char *bus;
>  	/**
>  	 * Be careful! ->name can be the subject or the peer name, depending on
> @@ -37,13 +43,23 @@
>  	int audit;
>  	int deny;
>  
> -	struct dbus_entry *next;
> -};
> +	dbus_rule(int mode_p, struct cond_entry *conds,
> +		  struct cond_entry *peer_conds);
> +	virtual ~dbus_rule() {
> +		free(bus);
> +		free(name);
> +		free(peer_label);
> +		free(path);
> +		free(interface);
> +		free(member);
> +	};
> +
> +	virtual ostream &dump(ostream &os);
> +	virtual int expand_variables(void);
> +	virtual int gen_policy_re(Profile &prof);
> +	virtual void post_process(Profile &prof __unused) { };
>  
> -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);
> -struct dbus_entry *dup_dbus_entry(struct dbus_entry *ent);
> -void print_dbus_entry(struct dbus_entry *ent);
> +
> +};
>  
>  #endif /* __AA_DBUS_H */
> --- 2.9-test.orig/parser/mount.c
> +++ 2.9-test/parser/mount.c
> @@ -215,8 +215,11 @@
>  
>  #include <stdlib.h>
>  #include <string.h>
> +#include <linux/limits.h>
>  
>  #include "parser.h"
> +#include "policydb.h"
> +#include "profile.h"
>  #include "mount.h"
>  
>  struct mnt_keyword_table {
> @@ -389,146 +392,455 @@
>  	return list;
>  }
>  
> -struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device,
> -				struct cond_entry *dst_conds __unused, char *mnt_point,
> -				int allow)
> +mnt_rule::mnt_rule(struct cond_entry *src_conds, char *device_p,
> +		   struct cond_entry *dst_conds __unused, char *mnt_point_p,
> +		   int allow_p):
> +	mnt_point(mnt_point_p), device(device_p), trans(NULL), opts(NULL),
> +	audit(0), deny(0)
>  {
>  	/* FIXME: dst_conds are ignored atm */
> +	aa_class = AA_CLASS_MOUNT;
>  
> -	struct mnt_entry *ent;
> -	ent = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
> -	if (ent) {
> -		ent->mnt_point = mnt_point;
> -		ent->device = device;
> -		ent->dev_type = extract_fstype(&src_conds);
> -
> -		ent->flags = 0;
> -		ent->inv_flags = 0;
> -
> -		if (src_conds) {
> -			unsigned int flags = 0, inv_flags = 0;
> -			struct value_list *list = extract_options(&src_conds, 0);
> -
> -			ent->opts = extract_options(&src_conds, 1);
> -			if (ent->opts)
> -				ent->flags = extract_flags(&ent->opts,
> -							   &ent->inv_flags);
> -
> -			if (list) {
> -				flags = extract_flags(&list, &inv_flags);
> -				/* these flags are optional so set both */
> -				flags |= inv_flags;
> -				inv_flags |= flags;
> -
> -				ent->flags |= flags;
> -				ent->inv_flags |= inv_flags;
> -
> -				if (ent->opts)
> -					list_append(ent->opts, list);
> -				else if (list)
> -					ent->opts = list;
> -			}
> -		}
> +	dev_type = extract_fstype(&src_conds);
>  
> -		if (allow & AA_DUMMY_REMOUNT) {
> -			allow = AA_MAY_MOUNT;
> -			ent->flags |= MS_REMOUNT;
> -			ent->inv_flags = 0;
> -		} else if (!(ent->flags | ent->inv_flags)) {
> -			/* no flag options, and not remount, allow everything */
> -			ent->flags = MS_ALL_FLAGS;
> -			ent->inv_flags = MS_ALL_FLAGS;
> -		}
> +	if (src_conds) {
> +		struct value_list *list = extract_options(&src_conds, 0);
>  
> -		ent->allow = allow;
> +		opts = extract_options(&src_conds, 1);
> +		if (opts)
> +			flags = extract_flags(&opts, &inv_flags);
>  
> -		if (src_conds) {
> -			PERROR("  unsupported mount conditions\n");
> -			exit(1);
> +		if (list) {
> +			unsigned int tmpflags, tmpinv_flags = 0;
> +
> +			tmpflags = extract_flags(&list, &tmpinv_flags);
> +			/* these flags are optional so set both */
> +			tmpflags |= tmpinv_flags;
> +			tmpinv_flags |= tmpflags;
> +
> +			flags |= tmpflags;
> +			inv_flags |= tmpinv_flags;
> +
> +			if (opts)
> +				list_append(opts, list);
> +			else if (list)
> +				opts = list;
>  		}
>  	}
>  
> -	return ent;
> +	if (allow_p & AA_DUMMY_REMOUNT) {
> +		allow_p = AA_MAY_MOUNT;
> +		flags |= MS_REMOUNT;
> +		inv_flags = 0;
> +	} else if (!(flags | inv_flags)) {
> +		/* no flag options, and not remount, allow everything */
> +		flags = MS_ALL_FLAGS;
> +		inv_flags = MS_ALL_FLAGS;
> +	}
> +
> +	allow = allow_p;
> +
> +	if (src_conds) {
> +		PERROR("  unsupported mount conditions\n");
> +		exit(1);
> +	}
>  }
>  
> -void free_mnt_entry(struct mnt_entry *ent)
> +ostream &mnt_rule::dump(ostream &os)
>  {
> -	if (!ent)
> -		return;
> +	if (allow & AA_MAY_MOUNT)
> +		os << "mount";
> +	else if (allow & AA_MAY_UMOUNT)
> +		os << "umount";
> +	else if (allow & AA_MAY_PIVOTROOT)
> +		os << "pivotroot";
> +	else
> +		os << "error: unknonwn mount perm";
>  
> -	free_mnt_entry(ent->next);
> -	free_value_list(ent->opts);
> -	free_value_list(ent->dev_type);
> -	free(ent->device);
> -	free(ent->mnt_point);
> -	free(ent->trans);
> +	os << " (0x" << hex << flags << " - 0x" << inv_flags << ") ";
> +	if (dev_type) {
> +		os << " type=";
> +		print_value_list(dev_type);
> +	}
> +	if (opts) {
> +		os << " options=";
> +		print_value_list(opts);
> +	}
> +	if (device)
> +		os << " " << device;
> +	if (mnt_point)
> +		os << " -> " << mnt_point;
> +	if (trans)
> +		os << " -> " << trans;
> +
> +	const char *prefix = deny ? "deny" : "";
> +	os << " " << prefix << "(0x" << hex << allow << "/0x" << audit << ")";
> +	os << ",\n";
>  
> -	free(ent);
> +	return os;
>  }
>  
> -struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig)
> +/* does not currently support expansion of vars in options */
> +int mnt_rule::expand_variables(void)
>  {
> -	struct mnt_entry *entry = NULL;
> +	int error = 0;
> +
> +	error = expand_entry_variables(&mnt_point);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&device);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&trans);
> +	if (error)
> +		return error;
> +
> +	return 0;
> +}
>  
> -	entry = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
> -	if (!entry)
> -		return NULL;
> +static int build_mnt_flags(char *buffer, int size, unsigned int flags,
> +			   unsigned int inv_flags)
> +{
> +	char *p = buffer;
> +	int i, len = 0;
>  
> -	DUP_STRING(orig, entry, mnt_point, err);
> -	DUP_STRING(orig, entry, device, err);
> -	DUP_STRING(orig, entry, trans, err);
> +	if (flags == MS_ALL_FLAGS) {
> +		/* all flags are optional */
> +	  len = snprintf(p, size, "%s", default_match_pattern);
> +		if (len < 0 || len >= size)
> +			return FALSE;
> +		return TRUE;
> +	}

Some odd whitespace with the line starting len = snprintf

> +	for (i = 0; i <= 31; ++i) {
> +		if ((flags & inv_flags) & (1 << i))
> +			len = snprintf(p, size, "(\\x%02x|)", i + 1);
> +		else if (flags & (1 << i))
> +			len = snprintf(p, size, "\\x%02x", i + 1);
> +		else	/* no entry = not set */
> +			continue;
> +
> +		if (len < 0 || len >= size)
> +			return FALSE;
> +		p += len;
> +		size -= len;
> +	}
>  
> -	entry->dev_type = dup_value_list(orig->dev_type);
> -	if (orig->dev_type && !(entry->dev_type))
> -		goto err;
> +	/* this needs to go once the backend is updated. */
> +	if (buffer == p) {
> +		/* match nothing - use impossible 254 as regex parser doesn't
> +		 * like the empty string
> +		 */
> +		if (size < 9)
> +			return FALSE;
>  
> -	entry->opts = dup_value_list(orig->opts);
> -	if (orig->opts && !(entry->opts))
> -		goto err;
> +		strcpy(p, "(\\xfe|)");
> +	}
>  
> -	entry->flags = orig->flags;
> -	entry->inv_flags = orig->inv_flags;
> +	return TRUE;
> +}
>  
> -	entry->allow = orig->allow;
> -	entry->audit = orig->audit;
> -	entry->deny = orig->deny;
> +static int build_mnt_opts(std::string& buffer, struct value_list *opts)
> +{
> +	struct value_list *ent;
> +	pattern_t ptype;
> +	int pos;
> +
> +	if (!opts) {
> +		buffer.append(default_match_pattern);
> +		return TRUE;
> +	}
>  
> -	entry->next = orig->next;
> +	list_for_each(opts, ent) {
> +		ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos);
> +		if (ptype == ePatternInvalid)
> +			return FALSE;
>  
> -	return entry;
> +		if (ent->next)
> +			buffer.append(",");
> +	}
>  
> -err:
> -	free_mnt_entry(entry);
> -	return NULL;
> +	return TRUE;
>  }
>  
> -void print_mnt_entry(struct mnt_entry *entry)
> +int mnt_rule::gen_policy_re(Profile &prof)
>  {
> -	if (entry->allow & AA_MAY_MOUNT)
> -		fprintf(stderr, "mount");
> -	else if (entry->allow & AA_MAY_UMOUNT)
> -		fprintf(stderr, "umount");
> -	else if (entry->allow & AA_MAY_PIVOTROOT)
> -		fprintf(stderr, "pivotroot");
> -	else
> -		fprintf(stderr, "error: unknonwn mount perm");
> +	std::string mntbuf;
> +	std::string devbuf;
> +	std::string typebuf;
> +	char flagsbuf[PATH_MAX + 3];
> +	std::string optsbuf;
> +	char class_mount_hdr[64];
> +	const char *vec[5];
> +	int count = 0;
> +	unsigned int tmpflags, tmpinv_flags;
> +
> +	if (!kernel_supports_mount) {
> +		pwarn("profile %s mount rules not enforced\n", prof.name);
> +		return RULE_NOT_SUPPORTED;
> +	}
>  
> -	fprintf(stderr, " (0x%x - 0x%x) ", entry->flags, entry->inv_flags);
> -	if (entry->dev_type) {
> -		fprintf(stderr, " type=");
> -		print_value_list(entry->dev_type);
> -	}
> -	if (entry->opts) {
> -		fprintf(stderr, " options=");
> -		print_value_list(entry->opts);
> -	}
> -	if (entry->device)
> -		fprintf(stderr, " %s", entry->device);
> -	if (entry->mnt_point)
> -		fprintf(stderr, " -> %s", entry->mnt_point);
> -	if (entry->trans)
> -		fprintf(stderr, " -> %s", entry->trans);
> +	sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT);
>  
> -	fprintf(stderr, " %s (0x%x/0x%x)", entry->deny ? "deny" : "", entry->allow, entry->audit); 
> -	fprintf(stderr, ",\n");
> +	/* a single mount rule may result in multiple matching rules being
> +	 * created in the backend to cover all the possible choices
> +	 */
> +
> +	if ((allow & AA_MAY_MOUNT) && (flags & MS_REMOUNT)
> +	    && !device && !dev_type) {
> +		int tmpallow;
> +		/* remount can't be conditional on device and type */
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (mnt_point) {
> +			/* both device && mnt_point or just mnt_point */
> +			if (!convert_entry(mntbuf, mnt_point))
> +				goto fail;
> +			vec[0] = mntbuf.c_str();
> +		} else {
> +			if (!convert_entry(mntbuf, device))
> +				goto fail;
> +			vec[0] = mntbuf.c_str();
> +		}
> +		/* skip device */
> +		vec[1] = default_match_pattern;
> +		/* skip type */
> +		vec[2] = default_match_pattern;
> +
> +		tmpflags = flags;
> +		tmpinv_flags = inv_flags;
> +		if (tmpflags != MS_ALL_FLAGS)
> +			tmpflags &= MS_REMOUNT_FLAGS;
> +		if (tmpinv_flags != MS_ALL_FLAGS)
> +			tmpflags &= MS_REMOUNT_FLAGS;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +
> +		if (opts)
> +			tmpallow = AA_MATCH_CONT;
> +		else
> +			tmpallow = allow;
> +
> +		/* rule for match without required data || data MATCH_CONT */
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, tmpallow,
> +				       audit | AA_AUDIT_MNT_DATA, 4,
> +				       vec, dfaflags))
> +			goto fail;
> +		count++;
> +
> +		if (opts) {
> +			/* rule with data match required */
> +			optsbuf.clear();
> +			if (!build_mnt_opts(optsbuf, opts))
> +				goto fail;
> +			vec[4] = optsbuf.c_str();
> +			if (!aare_add_rule_vec(prof.policy.rules, deny,
> +					       allow,
> +					       audit | AA_AUDIT_MNT_DATA,
> +					       5, vec, dfaflags))
> +				goto fail;
> +			count++;
> +		}
> +	}
> +	if ((allow & AA_MAY_MOUNT) && (flags & MS_BIND)
> +	    && !dev_type && !opts) {
> +		/* bind mount rules can't be conditional on dev_type or data */
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (!convert_entry(mntbuf, mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf.c_str();
> +		if (!clear_and_convert_entry(devbuf, device))
> +			goto fail;
> +		vec[1] = devbuf.c_str();
> +		/* skip type */
> +		vec[2] = default_match_pattern;
> +
> +		tmpflags = flags;
> +		tmpinv_flags = inv_flags;
> +		if (tmpflags != MS_ALL_FLAGS)
> +			tmpflags &= MS_BIND_FLAGS;
> +		if (tmpinv_flags != MS_ALL_FLAGS)
> +			tmpflags &= MS_BIND_FLAGS;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, allow,
> +				       audit, 4, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((allow & AA_MAY_MOUNT) &&
> +	    (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
> +	    && !device && !dev_type && !opts) {
> +		/* change type base rules can not be conditional on device,
> +		 * device type or data
> +		 */
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (!convert_entry(mntbuf, mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf.c_str();
> +		/* skip device and type */
> +		vec[1] = default_match_pattern;
> +		vec[2] = default_match_pattern;
> +
> +		tmpflags = flags;
> +		tmpinv_flags = inv_flags;
> +		if (tmpflags != MS_ALL_FLAGS)
> +			tmpflags &= MS_MAKE_FLAGS;
> +		if (tmpinv_flags != MS_ALL_FLAGS)
> +			tmpflags &= MS_MAKE_FLAGS;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, allow,
> +				       audit, 4, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((allow & AA_MAY_MOUNT) && (flags & MS_MOVE)
> +	    && !dev_type && !opts) {
> +		/* mount move rules can not be conditional on dev_type,
> +		 * or data
> +		 */
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (!convert_entry(mntbuf, mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf.c_str();
> +		if (!clear_and_convert_entry(devbuf, device))
> +			goto fail;
> +		vec[1] = devbuf.c_str();
> +		/* skip type */
> +		vec[2] = default_match_pattern;
> +
> +		tmpflags = flags;
> +		tmpinv_flags = inv_flags;
> +		if (tmpflags != MS_ALL_FLAGS)
> +			tmpflags &= MS_MOVE_FLAGS;
> +		if (tmpinv_flags != MS_ALL_FLAGS)
> +			tmpflags &= MS_MOVE_FLAGS;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, allow,
> +				       audit, 4, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((allow & AA_MAY_MOUNT) &&
> +	    (flags | inv_flags) & ~MS_CMDS) {
> +		int tmpallow;
> +		/* generic mount if flags are set that are not covered by
> +		 * above commands
> +		 */
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (!convert_entry(mntbuf, mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf.c_str();
> +		if (!clear_and_convert_entry(devbuf, device))
> +			goto fail;
> +		vec[1] = devbuf.c_str();
> +		typebuf.clear();
> +		if (!build_list_val_expr(typebuf, dev_type))
> +			goto fail;
> +		vec[2] = typebuf.c_str();
> +
> +		tmpflags = flags;
> +		tmpinv_flags = inv_flags;
> +		if (tmpflags != MS_ALL_FLAGS)
> +			tmpflags &= ~MS_CMDS;
> +		if (tmpinv_flags != MS_ALL_FLAGS)
> +			tmpinv_flags &= ~MS_CMDS;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +
> +		if (opts)
> +			tmpallow = AA_MATCH_CONT;
> +		else
> +			tmpallow = allow;
> +
> +		/* rule for match without required data || data MATCH_CONT */
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, tmpallow,
> +				       audit | AA_AUDIT_MNT_DATA, 4,
> +				       vec, dfaflags))
> +			goto fail;
> +		count++;
> +
> +		if (opts) {
> +			/* rule with data match required */
> +			optsbuf.clear();
> +			if (!build_mnt_opts(optsbuf, opts))
> +				goto fail;
> +			vec[4] = optsbuf.c_str();
> +			if (!aare_add_rule_vec(prof.policy.rules, deny,
> +					       allow,
> +					       audit | AA_AUDIT_MNT_DATA,
> +					       5, vec, dfaflags))
> +				goto fail;
> +			count++;
> +		}
> +	}
> +	if (allow & AA_MAY_UMOUNT) {
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (!convert_entry(mntbuf, mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf.c_str();
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, allow,
> +				       audit, 1, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if (allow & AA_MAY_PIVOTROOT) {
> +		/* rule class single byte header */
> +		mntbuf.assign(class_mount_hdr);
> +		if (!convert_entry(mntbuf, mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf.c_str();
> +		if (!clear_and_convert_entry(devbuf, device))
> +			goto fail;
> +		vec[1] = devbuf.c_str();
> +		if (!aare_add_rule_vec(prof.policy.rules, deny, allow,
> +				       audit, 2, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +
> +	if (!count)
> +		/* didn't actually encode anything */
> +		goto fail;
> +
> +	prof.policy.count++;
> +	return RULE_OK;
> +

Is prof.policy.count++ correct or should that be:
prof.policy.count += count; ?

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/20140310/3c2f2936/attachment-0001.pgp>


More information about the AppArmor mailing list