[apparmor] [patch 02/12] parser: Add support for unix domain socket rules.

Seth Arnold seth.arnold at canonical.com
Tue Aug 26 22:33:13 UTC 2014


On Mon, Aug 25, 2014 at 05:06:07PM -0700, john.johansen at canonical.com wrote:
> This patch implements parsing of fine grained mediation for unix domain
> sockets, that have abstract and anonymous paths. Sockets with file
> system paths are handled by regular file access rules.

Acked-by: Seth Arnold <seth.arnold at canonical.com>

Thanks

> the unix network rules follow the general fine grained network
> rule pattern of
> 
>   [<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
> 
> specifically for af_unix this is
> 
>   [<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
> 
>   <qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ]
> 
>   <access expr> = ( <access> | <access list> )
> 
>   <access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' |
>                'connect' | 'shutdown' | 'getattr' | 'setattr' |
> 	       'getopt' | 'setopt' |
>                'send' | 'receive' | 'r' | 'w' | 'rw' )
>   (some access modes are incompatible with some rules or require additional
>    parameters)
> 
>   <access list> = '(' <access> ( [','] <WS> <access> )* ')' 
> 
>   <WS> = white space
> 
>   <rule conds> = ( <type cond> | <protocol cond> )*
>      each cond can appear at most once
> 
>   <type cond> = 'type' '='  ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
> 
>   <protocol cond> = 'protocol' '='  ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
> ???? hrmmm not an in list so should be an alternation for multiple
> 
> 
>   <local expr> = ( <path cond> | <attr cond> | <opt cond> )*
>      each cond can appear at most once
> 
>   <peer expr> = 'peer' '=' ( <path cond> | <label cond> )+
>      each cond can appear at most once
> 
>   <path cond> = 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> 
>   <label cond> = 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> 
>   <attr cond> = 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> 
>   <opt cond> = 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> 
>   <AARE> = ?*[]{}^ ( see man page )
> 
>  unix domain socket rules are accumulated so that the granted unix
>  socket permissions are the union of all the listed unix rule permissions.
> 
>  unix domain socket rules are broad and general and become more restrictive
>  as further information is specified. Policy may be specified down to
>  the path and label level. The content of the communication is not
>  examined.
> 
>  Some permissions are not compatible with all unix rules.
> 
>  unix socket rule permissions are implied when a rule does not explicitly
>  state an access list. By default if a rule does not have an access list
>  all permissions that are compatible with the specified set of local
>  and peer conditionals are implied.
> 
>  The 'server', 'r', 'w' and 'rw' permissions are aliases for other permissions.
>  server = (create, bind, listen, accept)
>  r = (receive, getattr, getopt)
>  w = (create, connect, send, setattr, setopt)
> 
> 
> In addition it supports the v7 kernel abi semantics around generic
> network rules. The v7 abi removes the masking unix and netlink
> address families from the generic masking and uses fine grained
> mediation for an address type if supplied.
> 
> This means that the rules
> 
>   network unix,
>   network netlink,
> 
> are now enforced instead of ignored. The parser previously could accept
> these but the kernel would ignore anything written to them. If a network
> rule is supplied it takes precedence over the finer grained mediation
> rule. If permission is not granted via a broad network access rule
> fine grained mediation is applied.
> 
> ??? should we do this as if fine grained is present use it and then
> fallback to broader rules ????
> 
> probably.
> 
> 
> 
> ---
>  parser/Makefile                                |   11 
>  parser/af_rule.cc                              |  171 +++++++++++
>  parser/af_rule.h                               |   78 +++++
>  parser/af_unix.cc                              |  386 +++++++++++++++++++++++++
>  parser/af_unix.h                               |   60 +++
>  parser/apparmor.d.pod                          |  163 ++++++++++
>  parser/network.c                               |    5 
>  parser/network.h                               |   41 ++
>  parser/parser.h                                |    1 
>  parser/parser_common.c                         |    1 
>  parser/parser_lex.l                            |   35 +-
>  parser/parser_main.c                           |    4 
>  parser/parser_misc.c                           |    1 
>  parser/parser_regex.c                          |   13 
>  parser/parser_yacc.y                           |  134 +++++++-
>  parser/profile.cc                              |   14 
>  parser/tst/simple_tests/unix/bad_bind_1.sd     |    8 
>  parser/tst/simple_tests/unix/bad_bind_2.sd     |    8 
>  parser/tst/simple_tests/unix/bad_modifier_1.sd |    7 
>  parser/tst/simple_tests/unix/bad_modifier_2.sd |    7 
>  parser/tst/simple_tests/unix/bad_modifier_3.sd |    7 
>  parser/tst/simple_tests/unix/bad_modifier_4.sd |    7 
>  parser/tst/simple_tests/unix/bad_peer_1.sd     |    9 
>  parser/tst/simple_tests/unix/bad_regex_01.sd   |    8 
>  parser/tst/simple_tests/unix/bad_regex_02.sd   |    8 
>  parser/tst/simple_tests/unix/bad_regex_03.sd   |    8 
>  parser/tst/simple_tests/unix/bad_regex_04.sd   |    8 
>  parser/tst/simple_tests/unix/ok_bind_1.sd      |    7 
>  parser/tst/simple_tests/unix/ok_msg_1.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_10.sd      |    7 
>  parser/tst/simple_tests/unix/ok_msg_2.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_3.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_4.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_5.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_6.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_7.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_8.sd       |    7 
>  parser/tst/simple_tests/unix/ok_msg_9.sd       |    7 
>  38 files changed, 1253 insertions(+), 27 deletions(-)
> 
> --- 2.9-test.orig/parser/Makefile
> +++ 2.9-test/parser/Makefile
> @@ -80,9 +80,10 @@
>         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 common_optarg.c lib.c network.c \
> -       mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc
> +       mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
> +       af_rule.cc af_unix.cc
>  HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
> -       rule.h common_optarg.h signal.h ptrace.h network.h
> +       rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h
>  TOOLS = apparmor_parser
>  
>  OBJECTS = $(SRCS:.c=.o)
> @@ -254,6 +255,12 @@
>  network.o: network.c network.h parser.h immunix.h parser_yacc.h rule.h af_names.h $(APPARMOR_H)
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
> +af_rule.o: af_rule.cc af_rule.h network.h parser.h immunix.h parser_yacc.h rule
> +	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
> +
> +af_unix.o: af_unix.cc af_unix.h network.h af_rule.h parser.h immunix.h parser_y
> +	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
> +
>  profile.o: profile.cc profile.h parser.h network.h
>  	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>  
> --- /dev/null
> +++ 2.9-test/parser/af_rule.cc
> @@ -0,0 +1,171 @@
> +/*
> + *   Copyright (c) 2014
> + *   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 <sys/apparmor.h>
> +
> +#include <iomanip>
> +#include <string>
> +#include <iostream>
> +#include <sstream>
> +
> +#include "network.h"
> +#include "parser.h"
> +#include "profile.h"
> +#include "parser_yacc.h"
> +#include "af_rule.h"
> +
> +
> +/* need to move this table to stl */
> +static struct supported_cond supported_conds[] = {
> +	{ "type", true, false, false, local_cond },
> +	{ "protocol", false, false, false, local_cond },
> +	{ "label", true, false, false, peer_cond },
> +	{ NULL, false, false, false, local_cond },	/* eol sentinal */
> +};
> +
> +bool af_rule::cond_check(struct supported_cond *conds, struct cond_entry *ent,
> +			 bool peer, const char *rname)
> +{
> +	struct supported_cond *i;
> +	for (i = conds; i->name; i++) {
> +		if (strcmp(ent->name, i->name) != 0)
> +			continue;
> +		if (!i->supported)
> +			yyerror("%s rule: '%s' conditional is not currently supported\n", rname, ent->name);
> +		if (!peer && (i->side == peer_cond))
> +			yyerror("%s rule: '%s' conditional is only valid in the peer expression\n", rname, ent->name);
> +		if (peer && (i->side == local_cond))
> +			yyerror("%s rule: '%s' conditional is not allowed in the peer expression\n", rname, ent->name);
> +		if (!ent->eq && !i->in)
> +			yyerror("%s rule: keyword 'in' is not allowed in '%s' socket conditional\n", rname, ent->name);
> +		if (list_len(ent->vals) > 1 && !i->multivalue)
> +			yyerror("%s rule: conditional '%s' only supports a single value\n", rname, ent->name);
> +		return true;
> +	}
> +
> +	/* not in support table */
> +	return false;
> +}
> +
> +/* generic af supported conds.
> + * returns: true if processed, else false
> + */
> +int af_rule::move_base_cond(struct cond_entry *ent, bool peer)
> +{
> +	if (!cond_check(supported_conds, ent, peer, "unknown"))
> +		return false;
> +
> +	if (strcmp(ent->name, "type") == 0) {
> +		move_conditional_value("socket rule", &sock_type, ent);
> +		sock_type_n = net_find_type_val(sock_type);
> +		if (sock_type_n == -1)
> +			yyerror("socket rule: invalid socket type '%s'", sock_type);
> +	} else if (strcmp(ent->name, "protocol") == 0) {
> +		yyerror("socket rule: 'protocol' conditional is not currently supported\n");
> +	} else if (strcmp(ent->name, "label") == 0) {
> +		if (peer)
> +			move_conditional_value("unix", &label, ent);
> +		else
> +			move_conditional_value("unix", &peer_label, ent);
> +	} else
> +		return false;
> +
> +	return true;
> +}
> +
> +ostream &af_rule::dump_prefix(ostream &os)
> +{
> +	if (audit)
> +		os << "audit ";
> +	if (deny)
> +		os << "deny ";
> +	return os;
> +}
> +
> +ostream &af_rule::dump_local(ostream &os)
> +{
> +	if (mode != AA_VALID_NET_PERMS) {
> +		os << " (";
> +
> +		if (mode & AA_NET_SEND)
> +			os << "send ";
> +		if (mode & AA_NET_RECEIVE)
> +			os << "receive ";
> +		if (mode & AA_NET_CREATE)
> +			os << "create ";
> +		if (mode & AA_NET_SHUTDOWN)
> +			os << "shutdown ";
> +		if (mode & AA_NET_CONNECT)
> +			os << "connect ";
> +		if (mode & AA_NET_SETATTR)
> +			os << "setattr ";
> +		if (mode & AA_NET_GETATTR)
> +			os << "getattr ";
> +		if (mode & AA_NET_BIND)
> +			os << "bind ";
> +		if (mode & AA_NET_ACCEPT)
> +			os << "accept ";
> +		if (mode & AA_NET_LISTEN)
> +			os << "listen ";
> +		if (mode & AA_NET_SETOPT)
> +			os << "setopt ";
> +		if (mode & AA_NET_GETOPT)
> +			os << "getopt ";
> +		os << ")";
> +	}
> +
> +	if (sock_type)
> +		os << " type=" << sock_type;
> +	if (proto)
> +		os << " protocol=" << proto;
> +	return os;
> +}
> +
> +ostream &af_rule::dump_peer(ostream &os)
> +{
> +	if (peer_label)
> +		os << " label=\"" << peer_label << "\"";
> +
> +	return os;
> +}
> +
> +ostream &af_rule::dump(ostream &os)
> +{
> +	os << dump_prefix(os);
> +	os << af_name;
> +	os << dump_local(os);
> +	if (has_peer_conds())
> +		os << " peer=(" << dump_peer(os) << ")";
> +	os << ",\n";
> +
> +	return os;
> +}
> +
> +int af_rule::expand_variables(void)
> +{
> +	int error = expand_entry_variables(&label);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&peer_label);
> +	if (error)
> +		return error;
> +
> +	return 0;
> +}
> --- /dev/null
> +++ 2.9-test/parser/af_rule.h
> @@ -0,0 +1,78 @@
> +/*
> + *   Copyright (c) 2014
> + *   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_AF_RULE_H
> +#define __AA_AF_RULE_H
> +
> +#include "immunix.h"
> +#include "network.h"
> +#include "parser.h"
> +#include "profile.h"
> +
> +#include "rule.h"
> +
> +enum cond_side { local_cond, peer_cond, either_cond };
> +
> +struct supported_cond {
> +	const char *name;
> +	bool supported;
> +	bool in;
> +	bool multivalue;
> +	enum cond_side side ;
> +};
> +
> +class af_rule: public rule_t {
> +public:
> +	std::string af_name;
> +	char *sock_type;
> +	int sock_type_n;
> +	char *proto;
> +	int proto_n;
> +	char *label;
> +	char *peer_label;
> +	int mode;
> +	int audit;
> +	bool deny;
> +
> +	af_rule(const char *name): af_name(name), sock_type(NULL),
> +		sock_type_n(-1), proto(NULL), proto_n(0), label(NULL),
> +		peer_label(NULL), mode(0), audit(0), deny(0)
> +	{}
> +
> +	virtual ~af_rule()
> +	{
> +		free(sock_type);
> +		free(proto);
> +		free(label);
> +		free(peer_label);
> +	};
> +
> +	bool cond_check(struct supported_cond *cond, struct cond_entry *ent,
> +			bool peer, const char *rname);
> +	int move_base_cond(struct cond_entry *conds, bool peer);
> +
> +	virtual bool has_peer_conds(void) { return peer_label ? true : false; }
> +	virtual ostream &dump_prefix(ostream &os);
> +	virtual ostream &dump_local(ostream &os);
> +	virtual ostream &dump_peer(ostream &os);
> +	virtual ostream &dump(ostream &os);
> +	virtual int expand_variables(void);
> +	virtual int gen_policy_re(Profile &prof) = 0;
> +	virtual void post_process(Profile &prof __unused) { };
> +};
> +
> +#endif /* __AA_AF_RULE_H */
> --- /dev/null
> +++ 2.9-test/parser/af_unix.cc
> @@ -0,0 +1,386 @@
> +/*
> + *   Copyright (c) 2014
> + *   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 <sys/apparmor.h>
> +
> +#include <iomanip>
> +#include <string>
> +#include <iostream>
> +#include <sstream>
> +
> +#include "network.h"
> +#include "parser.h"
> +#include "profile.h"
> +#include "af_unix.h"
> +
> +int parse_unix_mode(const char *str_mode, int *mode, int fail)
> +{
> +	return parse_X_mode("unix", AA_VALID_NET_PERMS, str_mode, mode, fail);
> +}
> +
> +
> +static struct supported_cond supported_conds[] = {
> +	{ "path", true, false, false, either_cond },
> +	{ NULL, false, false, false, local_cond },	/* sentinal */
> +};
> +
> +void unix_rule::move_conditionals(struct cond_entry *conds)
> +{
> +	struct cond_entry *ent;
> +
> +	list_for_each(conds, ent) {
> +
> +		if (!cond_check(supported_conds, ent, false, "unix") &&
> +		    !move_base_cond(ent, false)) {
> +			yyerror("unix rule: invalid conditional '%s'\n",
> +				ent->name);
> +			continue;
> +		}
> +		if (strcmp(ent->name, "path") == 0) {
> +			move_conditional_value("unix socket", &path, ent);
> +			if (path[0] != '@' && strcmp(path, "none") != 0)
> +				yyerror("unix rule: invalid value for path='%s'\n", path);
> +		}
> +
> +		/* TODO: add conditionals for
> +		 *   listen queue length
> +		 *   attrs that can be read/set
> +		 *   ops that can be read/set
> +		 * allow in on
> +		 *   type, protocol
> +		 * local label match, and set
> +		 */
> +	}
> +}
> +
> +void unix_rule::move_peer_conditionals(struct cond_entry *conds)
> +{
> +	struct cond_entry *ent;
> +
> +	list_for_each(conds, ent) {
> +		if (!cond_check(supported_conds, ent, true, "unix") &&
> +		    !move_base_cond(ent, true)) {
> +			yyerror("unix rule: invalid peer conditional '%s'\n",
> +				ent->name);
> +			continue;
> +		}
> +		if (strcmp(ent->name, "path") == 0) {
> +			move_conditional_value("unix", &peer_path, ent);
> +			if (peer_path[0] != '@' && strcmp(path, "none") != 0)
> +				yyerror("unix rule: invalid value for path='%s'\n", peer_path);
> +		}
> +	}
> +}
> +
> +unix_rule::unix_rule(unsigned int type_p, bool audit_p, bool denied):
> +	af_rule("unix"), path(NULL), peer_path(NULL)
> +{
> +	if (type_p != 0xffffffff) {
> +		sock_type_n = type_p;
> +		sock_type = strdup(net_find_type_name(type_p));
> +		if (!sock_type)
> +			yyerror("socket rule: invalid socket type '%d'", type_p);
> +	}
> +	mode = AA_VALID_NET_PERMS;
> +	audit = audit_p ? AA_VALID_NET_PERMS : 0;
> +	deny = denied;
> +}
> +
> +unix_rule::unix_rule(int mode_p, struct cond_entry *conds,
> +		     struct cond_entry *peer_conds):
> +	af_rule("unix"), path(NULL), peer_path(NULL)
> +{
> +	move_conditionals(conds);
> +	move_peer_conditionals(peer_conds);
> +
> +	if (mode_p) {
> +		mode = mode_p;
> +		if (mode & ~AA_VALID_NET_PERMS)
> +			yyerror("mode contains invalid permissions for unix socket rules\n");
> +		else if ((mode & AA_NET_BIND) &&
> +			 ((mode & AA_PEER_NET_PERMS) || has_peer_conds()))
> +			/* Do we want to loosen this? */
> +			yyerror("unix socket 'bind' access cannot be used with message rule conditionals\n");
> +		else if ((mode & AA_NET_LISTEN) &&
> +			 ((mode & AA_PEER_NET_PERMS) || has_peer_conds()))
> +			/* Do we want to loosen this? */
> +			yyerror("unix socket 'listen' access cannot be used with message rule conditionals\n");
> +		else if ((mode & AA_NET_ACCEPT) &&
> +			 ((mode & AA_PEER_NET_PERMS) || has_peer_conds()))
> +			/* Do we want to loosen this? */
> +			yyerror("unix socket 'accept' access cannot be used with message rule conditionals\n");
> +	} else {
> +		mode = AA_VALID_NET_PERMS;
> +	}
> +
> +	free_cond_list(conds);
> +	free_cond_list(peer_conds);
> +
> +}
> +
> +ostream &unix_rule::dump_local(ostream &os)
> +{
> +	af_rule::dump_local(os);
> +	if (path)
> +		os << "path='" << path << "'";
> +	return os;
> +}
> +
> +ostream &unix_rule::dump_peer(ostream &os)
> +{
> +	af_rule::dump_peer(os);
> +	if (peer_path)
> +		os << "path='" << peer_path << "'";
> +	return os;
> +}
> +
> +
> +int unix_rule::expand_variables(void)
> +{
> +	int error = af_rule::expand_variables();
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&path);
> +	if (error)
> +		return error;
> +	error = expand_entry_variables(&peer_path);
> +	if (error)
> +		return error;
> +
> +	return 0;
> +}
> +
> +/* do we want to warn once/profile or just once per compile?? */
> +static void warn_once(const char *name, const char *msg)
> +{
> +	static const char *warned_name = NULL;
> +
> +	if (warned_name != name) {
> +		cerr << "Warning from profile " << name << " (";
> +		if (current_filename)
> +			cerr << current_filename;
> +		else
> +			cerr << "stdin";
> +		cerr << "): " << msg << "\n";
> +		warned_name = name;
> +	}
> +}
> +
> +static void warn_once(const char *name)
> +{
> +	warn_once(name, "extended network unix socket rules not enforced");
> +}
> +
> +std::ostringstream &writeu16(std::ostringstream &o, int v)
> +{
> +	u16 tmp = htobe16((u16) v);
> +	char *c = (char *) &tmp;
> +	o << "\\x" << std::setfill('0') << std::setw(2) << std::hex << *c++;
> +	o << "\\x" << std::setfill('0') << std::setw(2) << std::hex << *c;
> +	return o;
> +}
> +
> +#define CMD_ADDR	1
> +#define CMD_LISTEN	2
> +#define CMD_ACCEPT	3
> +#define CMD_OPT		4
> +
> +void unix_rule::downgrade_rule(Profile &prof) {
> +	if (!prof.net.allow && !prof.alloc_net_table())
> +		yyerror(_("Memory allocation error."));
> +	if (deny) {
> +		prof.net.deny[AF_UNIX] |= 1 << sock_type_n;
> +		if (!audit)
> +			prof.net.quiet[AF_UNIX] |= 1 << sock_type_n;
> +	} else {
> +		prof.net.allow[AF_UNIX] |= 1 << sock_type_n;
> +		if (audit)
> +			prof.net.audit[AF_UNIX] |= 1 << sock_type_n;
> +	}
> +}
> +
> +int unix_rule::gen_policy_re(Profile &prof)
> +{
> +	std::ostringstream buffer, tmp;
> +	std::string buf;
> +
> +	pattern_t ptype;
> +	int pos;
> +	int mask = mode;
> +
> +	/* always generate a downgraded rule. This doesn't change generated
> +	 * policy size and allows the binary policy to be loaded against
> +	 * older kernels and be enforced to the best of the old network
> +	 * rules ability
> +	 */
> +	downgrade_rule(prof);
> +	if (!kernel_supports_unix) {
> +		if (kernel_supports_network) {
> +			/* only warn if we are building against a kernel
> +			 * that requires downgrading */
> +			warn_once(prof.name, "downgrading extended network unix socket rule to generic network rule\n");
> +			/* TODO: add ability to abort instead of downgrade */
> +			return RULE_OK;
> +		}
> +		warn_once(prof.name);
> +		return RULE_NOT_SUPPORTED;
> +	}
> +
> +
> +	buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NET;
> +	buffer << writeu16(buffer, AF_UNIX);
> +	if (sock_type)
> +		buffer << writeu16(buffer, sock_type_n);
> +	else
> +		buffer << "..";
> +	if (proto)
> +		buffer << writeu16(buffer, proto_n);
> +	else
> +		buffer << "..";
> +
> +	if (mask & AA_NET_CREATE) {
> +		buf = buffer.str();
> +		if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +						 AA_NET_CREATE,
> +						 audit & AA_NET_CREATE,
> +						 dfaflags))
> +			goto fail;
> +		mask &= ~AA_NET_CREATE;
> +	}
> +
> +	/* local addr */
> +	if (path) {
> +		if (strcmp(path, "none") == 0) {
> +			buffer << "\\x01";
> +		} else {
> +			/* skip leading @ */
> +			ptype = convert_aaregex_to_pcre(path + 1, 0, buf, &pos);
> +			if (ptype == ePatternInvalid)
> +				goto fail;
> +			/* kernel starts abstract with \0 */
> +			buffer << "\\x00";
> +			buffer << buf;
> +		}
> +	} else
> +		buffer << ".*";
> +
> +	/* change to out of band separator */
> +	buffer << "\\x00";
> +
> +	if (mask & AA_LOCAL_NET_PERMS) {
> +		/* local label option */
> +		if (label) {
> +			ptype = convert_aaregex_to_pcre(label, 0, buf, &pos);
> +			if (ptype == ePatternInvalid)
> +				goto fail;
> +			/* kernel starts abstract with \0 */
> +			buffer << buf;
> +		} else
> +			tmp << anyone_match_pattern;
> +		buffer << "\\x00";
> +
> +		/* create already masked off */
> +		if (mask & AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD) {
> +			buf = buffer.str();
> +			if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +							 mask & AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD,
> +							 audit & AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD,
> +							 dfaflags))
> +				goto fail;
> +		}
> +
> +		/* cmd selector - drop accept??? */
> +		if (mask & AA_NET_ACCEPT) {
> +			tmp.str(buffer.str());
> +			tmp << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_ACCEPT;
> +			buf = tmp.str();
> +			if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +							 AA_NET_ACCEPT,
> +							 audit & AA_NET_ACCEPT,
> +							 dfaflags))
> +				goto fail;
> +		}
> +		if (mask & AA_NET_LISTEN) {
> +			tmp.str(buffer.str());
> +			tmp << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_LISTEN;
> +			/* TODO: backlog conditional */
> +			tmp << "..";
> +			buf = tmp.str();
> +			if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +							 AA_NET_LISTEN,
> +							 audit & AA_NET_LISTEN,
> +							 dfaflags))
> +				goto fail;
> +		}
> +		if (mask & AA_NET_OPT) {
> +			tmp.str(buffer.str());
> +			tmp << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_OPT;
> +			/* TODO: sockopt conditional */
> +			tmp << "..";
> +			buf = tmp.str();
> +			if (!prof.policy.rules->add_rule(buf.c_str(), deny,
> +							 AA_NET_OPT,
> +							 audit & AA_NET_OPT,
> +							 dfaflags))
> +				goto fail;
> +		}
> +		mask &= ~AA_LOCAL_NET_PERMS;
> +	}
> +
> +	if (mask & AA_PEER_NET_PERMS) {
> +		/* cmd selector */
> +		buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_ADDR;
> +
> +		/* peer addr */
> +		if (peer_path) {
> +			if (strcmp(path, "none") == 0) {
> +				buffer << "\\x01";
> +			} else {
> +				/* skip leading @ */
> +				ptype = convert_aaregex_to_pcre(peer_path + 1, 0, buf, &pos);
> +				if (ptype == ePatternInvalid)
> +					goto fail;
> +				/* kernel starts abstract with \0 */
> +				buffer << "\\x00";
> +				buffer << buf;
> +			}
> +		}
> +		/* change to out of band separator */
> +		buffer << "\\x00";
> +
> +		if (peer_label) {
> +			ptype = convert_aaregex_to_pcre(peer_label, 0, buf, &pos);
> +			if (ptype == ePatternInvalid)
> +				goto fail;
> +			buffer << buf;
> +		} else {
> +			buffer << anyone_match_pattern;
> +		}
> +
> +		buf = buffer.str();
> +		if (!prof.policy.rules->add_rule(buf.c_str(), deny, mode & AA_PEER_NET_PERMS, audit, dfaflags))
> +			goto fail;
> +	}
> +
> +	return RULE_OK;
> +
> +fail:
> +	return RULE_ERROR;
> +}
> --- /dev/null
> +++ 2.9-test/parser/af_unix.h
> @@ -0,0 +1,60 @@
> +/*
> + *   Copyright (c) 2014
> + *   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_AF_UNIX_H
> +#define __AA_AF_UNIX_H
> +
> +#include "immunix.h"
> +#include "network.h"
> +#include "parser.h"
> +#include "profile.h"
> +#include "af_rule.h"
> +
> +int parse_unix_mode(const char *str_mode, int *mode, int fail);
> +
> +class unix_rule: public af_rule {
> +	void move_conditionals(struct cond_entry *conds);
> +	void move_peer_conditionals(struct cond_entry *conds);
> +	void downgrade_rule(Profile &prof);
> +public:
> +	char *path;
> +	char *peer_path;
> +	int mode;
> +	int audit;
> +	bool deny;
> +
> +	unix_rule(unsigned int type_p, bool audit_p, bool denied);
> +	unix_rule(int mode, struct cond_entry *conds,
> +		  struct cond_entry *peer_conds);
> +	virtual ~unix_rule()
> +	{
> +		free(path);
> +		free(peer_path);
> +	};
> +
> +	virtual bool has_peer_conds(void) {
> +		return af_rule::has_peer_conds() || peer_path;
> +	}
> +
> +	virtual ostream &dump_local(ostream &os);
> +	virtual ostream &dump_peer(ostream &os);
> +	virtual int expand_variables(void);
> +	virtual int gen_policy_re(Profile &prof);
> +	virtual void post_process(Profile &prof __unused) { };
> +};
> +
> +#endif /* __AA_AF_UNIX_H */
> --- 2.9-test.orig/parser/apparmor.d.pod
> +++ 2.9-test/parser/apparmor.d.pod
> @@ -54,7 +54,7 @@
>  
>  B<TEXT> = any characters
>  
> -B<PROFILE> = [ I<COMMENT> ... ] [ I<VARIABLE ASSIGNMENT> ... ] ( '"' I<PROGRAM> '"' | I<PROGRAM> ) [ 'flags=(complain)' ]'{' [ ( I<RESOURCE RULE> | I<COMMENT> | I<INCLUDE> | I<SUBPROFILE> | 'capability ' I<CAPABILITY> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<DBUS RULE> | I<FILE RULE> | 'change_profile -> ' I<PROGRAMCHILD> ) ... ] '}'
> +B<PROFILE> = [ I<COMMENT> ... ] [ I<VARIABLE ASSIGNMENT> ... ] ( '"' I<PROGRAM> '"' | I<PROGRAM> ) [ 'flags=(complain)' ]'{' [ ( I<RESOURCE RULE> | I<COMMENT> | I<INCLUDE> | I<SUBPROFILE> | 'capability ' I<CAPABILITY> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<DBUS RULE> | I<UNIX RULE> I<FILE RULE> | 'change_profile -> ' I<PROGRAMCHILD> ) ... ] '}'
>  
>  B<SUBPROFILE> = [ I<COMMENT> ... ] ( I<PROGRAMHAT> | 'profile ' I<PROGRAMCHILD> ) '{' [ ( I<FILE RULE> | I<COMMENT> | I<INCLUDE> ) ... ] '}'
>  
> @@ -157,6 +157,38 @@
>  
>  B<AARE> = B<?*[]{}^> (see below for meanings)
>  
> +B<UNIX RILE> = [ I<UNIX QUALIFIERS> ] 'unix' [ I<UNIX ACCESS EXPR> ] [ I<UNIX RULE CONDS> ] [ I<UNIX LOCAL EXPR> ] [ I<UNIX PEER EXPR> ]
> +
> +B<UNIX QUALIFIERS> = [ 'audit' ] [ 'allow' | 'deny' ]
> +
> +B<UNIX ACCESS EXPR> = ( I<UNIX ACCESS> | I<UNIX ACCESS LIST> )
> +
> +B<UNIX ACCESS> = ( 'create' | 'bind' | 'listen' | 'accept' | 'connect' | 'shutdown' | 'getattr' | 'setattr' | 'getopt' | 'setopt' | 'send' | 'receive' | 'r' | 'w' | 'rw' )
> + (some access modes are incompatible with some rules or require additional parameters)
> +
> +B<UNIX ACCESS LIST> = '(' I<UNIX ACCESS> ( [','] <UNIX ACCESS> )* ')'
> +
> +B<UNIX RULE CONDS> = ( I<TYPE COND> | I<PROTO COND> )
> + each cond can appear at most once
> +
> +B<TYPE COND> = 'type' '='  ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
> +
> +B<PROTO COND> = 'protocol' '='  ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
> +
> +B<UNIX LOCAL EXPR> = ( I<UNIX PATH COND> | I<UNIX LABEL COND> | I<UNIX ATTR COND> | I<UNIX OPT COND> )*
> + each cond can appear at most once
> +
> +B<UNIX PEER EXPR> = 'peer' '=' ( I<UNIX PATH COND> | I<UNIX LABEL COND> )+
> + each cond can appear at most once
> +
> +B<UNIX PATH COND> 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> +
> +B<UNIX LABEL COND> 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> +
> +B<UNIX ATTR COND> 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> +
> +B<UNIX OPT COND> 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
> +
>  B<FILE RULE> = I<RULE QUALIFIER> ( '"' I<FILEGLOB> '"' | I<FILEGLOB> ) I<ACCESS> ','
>  
>  B<RULE QUALIFIER> = [ 'audit' ] [ 'deny' ] [ 'owner' ]
> @@ -851,6 +883,135 @@
>      # Allow and audit all eavesdropping
>      audit dbus eavesdrop,
>  
> +=head2 Unix socket rules
> +
> +AppArmor supports fine grained mediation of unix domain abstract and
> +anonymous sockets. Unix domain sockets with file system paths are
> +mediated via file access rules.
> +TODO: do we want to revise this to allow certain permission to be
> +      specified by unix rules that can not be specified via file
> +      paths?
> +
> +Abstract unix domain sockets is a nonprotable Linux extension of unix
> +domain sockets, see man 7 unix for more information.
> +
> +=head3 Unix socket paths
> +
> +The path component of a unix domain socket is specified by the
> +  path=
> +conditional. If a path conditional is not specified as part of a rule
> +then the rule matches both abstract and anonymous sockets.
> +
> +In apparmor the path of an abstract unix domain socket begins with the
> +I<@> character, similar to how they are reported by netstat -x. The name
> +then follows and may contain pattern matching and any characters including
> +the null character. In apparmor null characters must be specified by using
> +an escape sequence I<\000> or I<\x00>. The pattern matching is the same
> +as is used by path matching so * will not match I</> even though it
> +has no special meaning with in an abstract socket name. Eg.
> +  unix path=@*,
> +
> +Anonymous unix domain sockets have no path associated with them, however
> +it can be specified with the special I<none> keyword to indicate the
> +rule only applies to anonymous unix domain sockets. Eg.
> +  unix path=none,
> +
> +If the path component of a rule is not specified then the rule applies
> +to both abstract and anonymous sockets.
> +
> +=head3 Unix socket permissions
> +Unix domain socket rules are accumulated so that the granted unix              
> +socket permissions are the union of all the listed unix rule permissions.      
> +
> +Unix domain socket rules are broad and general and become more restrictive     
> +as further information is specified. Policy may be specified down to           
> +the path and label level. The content of the communication is not              
> +examined.                                                                      
> +
> +Unix socket rule permissions are implied when a rule does not explicitly       
> +state an access list. By default if a rule does not have an access list        
> +all permissions that are compatible with the specified set of local            
> +and peer conditionals are implied.                                             
> +
> +The create, bind, listen, shutdown, getattr, setattr permissions are
> +applied to the local socket. The accept, connect, send, receive permissions
> +apply to the combination of a local and peer. Currently it is required that
> +create, bind, listen, shutdown, getattr, and settr permission are only
> +specified in rules that do not have a peer component.
> +???TODO: Do we really want this????
> +
> +If a rule is specified with a peer component it will not imply the
> +create, bind, listen, shutdown, getattr, or setattr permissions.
> +
> +??? TODO: Describe explicitly labeled sockets ???? !!!
> +
> +=head3 Example Unix domain socket rules:
> +
> +    # Allow all permissions to unix sockets
> +    unix,
> +
> +    # Explicitly allow all unix permissions
> +    unix (create, listen, accept, connect, send, receive, getattr, setattr, setopt, getopt),
> +
> +    # Explicitly deny unix socket access ??? should this block unix file as well???
> +    deny unix,
> +
> +    
> +  unix type=stream,
> +
> +  unix type=dgram,
> +
> +  unix path=none
> +
> +  unix path=@foo,
> +
> +  unix type=stream path=@foo,
> +
> +  unix server path=@foo,
> +
> +  unix accept path=@foo peer=(label=/bar),
> +
> +  unix receive path=@foo peer=(label=/bar),
> +
> +
> +  unix path=none
> +
> +
> +=head3 Abstract unix domain sockets autobind
> +
> +Abstract unix domain sockets can autobind to an address. The autobind
> +address is a unique 5 digit string of decimal numbers, eg. @00001. There
> +is nothing that prevents a task from manually binding to addresses with a
> +similar pattern so it is impossible to reliably identify autobind addresses
> +from a regular address.
> +
> +=head3 Interaction of network rules and fine grained unix domain socket rules
> +
> +The coarse grained networking rules can be used to control unix domain
> +sockets as well. When fine grained unix domain socket mediation is available
> +the coase grained network rule is mapped into the equivalent unix socket
> +rule.
> +
> +Eg.
> +    network unix,  =>  unix,
> +
> +    network unix stream,   =>  unix stream,
> +
> +Fine grained mediation rules however can not be lossly converted back
> +to the coarse grained network rule. Eg
> +
> +   unix bind path=@example,
> +
> +Has no exact match under coarse grained network rules, the closest match is
> +the much wider permission rule of.
> +   network unix,
> +
> +TODO: ??? should we make unix rules imply this when fine grained mediation
> +is not available, or do we fail? Warning to wider is similar to the
> +current behavior of loading policy which specify rules that can't be
> +enforced. Hrmmm this behavior really needs to be a config option, to
> +fail or warn.
> +
>  =head2 Variables
>  
>  AppArmor's policy language allows embedding variables into file rules
> --- 2.9-test.orig/parser/network.c
> +++ 2.9-test/parser/network.c
> @@ -31,6 +31,11 @@
>  #include "network.h"
>  
>  
> +int parse_net_mode(const char *str_mode, int *mode, int fail)
> +{
> +	return parse_X_mode("net", AA_VALID_NET_PERMS, str_mode, mode, fail);
> +}
> +
>  /* Bleah C++ doesn't have non-trivial designated initializers so we just
>   * have to make sure these are in order.  This means we are more brittle
>   * but there isn't much we can do.
> --- 2.9-test.orig/parser/network.h
> +++ 2.9-test/parser/network.h
> @@ -33,7 +33,45 @@
>  
>  #include "parser.h"
>  #include "rule.h"
> -#include "profile.h"
> +
> +
> +#define AA_NET_WRITE		0x0002
> +#define AA_NET_SEND		AA_NET_WRITE
> +#define AA_NET_READ		0x0004
> +#define AA_NET_RECEIVE		AA_NET_READ
> +
> +#define AA_NET_CREATE		0x0010
> +#define AA_NET_SHUTDOWN		0x0020		/* alias delete */
> +#define AA_NET_CONNECT		0x0040		/* alias open */
> +
> +#define AA_NET_SETATTR		0x0100
> +#define AA_NET_GETATTR		0x0200
> +
> +//#define AA_NET_CHMOD		0x1000		/* pair */
> +//#define AA_NET_CHOWN		0x2000		/* pair */
> +//#define AA_NET_CHGRP		0x4000		/* pair */
> +//#define AA_NET_LOCK		0x8000		/* LINK_SUBSET overlaid */
> +
> +#define AA_NET_ACCEPT		0x00100000
> +#define AA_NET_BIND		0x00200000
> +#define AA_NET_LISTEN		0x00400000
> +
> +#define AA_NET_SETOPT		0x01000000
> +#define AA_NET_GETOPT		0x02000000
> +
> +#define AA_CONT_MATCH		0x08000000
> +
> +#define AA_VALID_NET_PERMS (AA_NET_SEND | AA_NET_RECEIVE | AA_NET_CREATE | \
> +			    AA_NET_SHUTDOWN | AA_NET_CONNECT | \
> +			    AA_NET_SETATTR | AA_NET_GETATTR | AA_NET_BIND | \
> +			    AA_NET_ACCEPT | AA_NET_LISTEN | AA_NET_SETOPT | \
> +			    AA_NET_GETOPT | AA_CONT_MATCH)
> +#define AA_LOCAL_NET_PERMS (AA_NET_CREATE | AA_NET_SHUTDOWN | AA_NET_SETATTR |\
> +			    AA_NET_GETATTR | AA_NET_BIND | AA_NET_ACCEPT |    \
> +			    AA_NET_LISTEN | AA_NET_SETOPT | AA_NET_GETOPT)
> +#define AA_NET_OPT	(AA_NET_SETOPT | AA_NET_GETOPT)
> +#define AA_LOCAL_NET_CMD (AA_NET_ACCEPT | AA_NET_LISTEN | AA_NET_OPT)
> +#define AA_PEER_NET_PERMS (AA_VALID_NET_PERMS & ~AA_LOCAL_NET_PERMS)
>  
>  struct network_tuple {
>  	const char *family_name;
> @@ -53,6 +91,7 @@
>  	struct aa_network_entry *next;
>  };
>  
> +int parse_net_mode(const char *str_mode, int *mode, int fail);
>  extern struct aa_network_entry *new_network_ent(unsigned int family,
>  						unsigned int type,
>  						unsigned int protocol);
> --- 2.9-test.orig/parser/parser.h
> +++ 2.9-test/parser/parser.h
> @@ -291,6 +291,7 @@
>  extern int kernel_supports_dbus;
>  extern int kernel_supports_signal;
>  extern int kernel_supports_ptrace;
> +extern int kernel_supports_unix;
>  extern int conf_verbose;
>  extern int conf_quiet;
>  extern int names_only;
> --- 2.9-test.orig/parser/parser_common.c
> +++ 2.9-test/parser/parser_common.c
> @@ -66,6 +66,7 @@
>  int kernel_load = 1;
>  int kernel_supports_setload = 0;	/* kernel supports atomic set loads */
>  int kernel_supports_network = 0;        /* kernel supports network rules */
> +int kernel_supports_unix = 0;		/* kernel supports unix socket rules */
>  int kernel_supports_policydb = 0;	/* kernel supports new policydb */
>  int kernel_supports_mount = 0;	        /* kernel supports mount rules */
>  int kernel_supports_dbus = 0;		/* kernel supports dbus rules */
> --- 2.9-test.orig/parser/parser_lex.l
> +++ 2.9-test/parser/parser_lex.l
> @@ -258,6 +258,7 @@
>  %x DBUS_MODE
>  %x SIGNAL_MODE
>  %x PTRACE_MODE
> +%x UNIX_MODE
>  %x CHANGE_PROFILE_MODE
>  %x INCLUDE
>  
> @@ -272,7 +273,7 @@
>  	}
>  %}
>  
> -<INITIAL,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
> +<INITIAL,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
>  	{WS}+	{  DUMP_PREPROCESS; /* Ignoring whitespace */ }
>  }
>  
> @@ -298,7 +299,7 @@
>  		yyterminate();
>  }
>  
> -<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
> +<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
>  	peer/{WS}*={WS}*\(	{
>  		/* we match to the = in the lexer so that we can switch scanner
>  		 * state.  By the time the parser see the = it may be too late
> @@ -468,8 +469,23 @@
>  	{LT_EQUAL}	{ RETURN_TOKEN(TOK_LE); }
>  }
>  
> -<DBUS_MODE>{
> +<UNIX_MODE>{
> +	create	{ RETURN_TOKEN(TOK_CREATE); }
> +	listen	{ RETURN_TOKEN(TOK_LISTEN); }
> +	accept	{ RETURN_TOKEN(TOK_ACCEPT); }
> +	connect	{ RETURN_TOKEN(TOK_CONNECT); }
> +	getattr	{ RETURN_TOKEN(TOK_GETATTR); }
> +	setattr	{ RETURN_TOKEN(TOK_SETATTR); }
> +	getopt	{ RETURN_TOKEN(TOK_GETOPT); }
> +	setopt	{ RETURN_TOKEN(TOK_SETOPT); }
> +	shutdown	{ RETURN_TOKEN(TOK_SHUTDOWN); }
> +}
> +
> +<DBUS_MODE,UNIX_MODE>{
>  	bind		{ RETURN_TOKEN(TOK_BIND); }
> +}
> +
> +<DBUS_MODE>{
>  	eavesdrop	{ RETURN_TOKEN(TOK_EAVESDROP); }
>  }
>  
> @@ -484,7 +500,7 @@
>  	tracedby	{ RETURN_TOKEN(TOK_TRACEDBY); }
>  }
>  
> -<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
> +<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
>  	read		{ RETURN_TOKEN(TOK_READ); }
>  	write		{ RETURN_TOKEN(TOK_WRITE); }
>  	{OPEN_PAREN}	{
> @@ -500,7 +516,7 @@
>  	{ARROW}		{ RETURN_TOKEN(TOK_ARROW); }
>  }
>  
> -<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
> +<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
>  	({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
>  		yylval.id = processid(yytext, yyleng);
>  		RETURN_TOKEN(TOK_ID);
> @@ -592,13 +608,17 @@
>  		break;
>  	case TOK_PTRACE:
>  		state = PTRACE_MODE;
> +		break;
> +	case TOK_UNIX:
> +		state = UNIX_MODE;
> +		break;
>  	default: /* nothing */
>  		break;
>  	}
>  	PUSH_AND_RETURN(state, token);
>  }
>  
> -<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
> +<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
>  	{END_OF_RULE}	{
>  		if (YY_START != INITIAL)
>  			POP_NODUMP();
> @@ -611,7 +631,7 @@
>  	}
>  }
>  
> -<INITIAL,SUB_ID,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
> +<INITIAL,SUB_ID,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
>  	[^\n]	{
>  		DUMP_PREPROCESS;
>  		/* Something we didn't expect */
> @@ -640,6 +660,7 @@
>  	STATE_TABLE_ENT(DBUS_MODE),
>  	STATE_TABLE_ENT(SIGNAL_MODE),
>  	STATE_TABLE_ENT(PTRACE_MODE),
> +	STATE_TABLE_ENT(UNIX_MODE),
>  	STATE_TABLE_ENT(CHANGE_PROFILE_MODE),
>  	STATE_TABLE_ENT(INCLUDE),
>  };
> --- 2.9-test.orig/parser/parser_main.c
> +++ 2.9-test/parser/parser_main.c
> @@ -686,10 +686,14 @@
>  		kernel_supports_policydb = 1;
>  	if (strstr(features_string, "v6"))
>  		kernel_abi_version = 6;
> +	if (strstr(features_string, "v7"))
> +		kernel_abi_version = 7;
>  	if (strstr(features_string, "set_load"))
>  		kernel_supports_setload = 1;
>  	if (strstr(features_string, "network"))
>  		kernel_supports_network = 1;
> +	if (strstr(features_string, "af_unix"))
> +		kernel_supports_unix = 1;
>  	if (strstr(features_string, "mount"))
>  		kernel_supports_mount = 1;
>  	if (strstr(features_string, "dbus"))
> --- 2.9-test.orig/parser/parser_misc.c
> +++ 2.9-test/parser/parser_misc.c
> @@ -107,6 +107,7 @@
>  static struct keyword_table keyword_table[] = {
>  	/* network */
>  	{"network",		TOK_NETWORK},
> +	{"unix",		TOK_UNIX},
>  	/* misc keywords */
>  	{"capability",		TOK_CAPABILITY},
>  	{"if",			TOK_IF},
> --- 2.9-test.orig/parser/parser_regex.c
> +++ 2.9-test/parser/parser_regex.c
> @@ -665,14 +665,17 @@
>  	return TRUE;
>  }
>  
> -#define MAKE_STR(X) #X
> -#define CLASS_STR(X) "\\d" MAKE_STR(X)
> +#define MAKE_STR(A) #A
> +#define CLASS_STR(X) "\\000\\d" MAKE_STR(X)
> +#define CLASS_SUB_STR(X, Y) MAKE_STR(X) MAKE_STR(Y)
>  
>  static const char *mediates_file = CLASS_STR(AA_CLASS_FILE);
>  static const char *mediates_mount = CLASS_STR(AA_CLASS_MOUNT);
>  static const char *mediates_dbus =  CLASS_STR(AA_CLASS_DBUS);
>  static const char *mediates_signal =  CLASS_STR(AA_CLASS_SIGNAL);
>  static const char *mediates_ptrace =  CLASS_STR(AA_CLASS_PTRACE);
> +static const char *mediates_extended_net = CLASS_STR(AA_CLASS_NET);
> +static const char *mediates_net_unix = CLASS_SUB_STR(AA_CLASS_NET, AF_UNIX);
>  
>  int process_profile_policydb(Profile *prof)
>  {
> @@ -689,7 +692,7 @@
>  	 * to be supported
>  	 */
>  
> -	/* note: this activates unix domain sockets mediation on connect */
> +	/* note: this activates fs based unix domain sockets mediation on connect */
>  	if (kernel_abi_version > 5 &&
>  	    !prof->policy.rules->add_rule(mediates_file, 0, AA_MAY_READ, 0, dfaflags))
>  		goto out;
> @@ -705,6 +708,10 @@
>  	if (kernel_supports_ptrace &&
>  	    !prof->policy.rules->add_rule(mediates_ptrace, 0, AA_MAY_READ, 0, dfaflags))
>  		goto out;
> +	if (kernel_supports_unix &&
> +	    (!prof->policy.rules->add_rule(mediates_extended_net, 0, AA_MAY_READ, 0, dfaflags) ||
> +	     !prof->policy.rules->add_rule(mediates_net_unix, 0, AA_MAY_READ, 0, dfaflags)))
> +		goto out;
>  
>  	if (prof->policy.rules->rule_count > 0) {
>  		prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size, dfaflags);
> --- 2.9-test.orig/parser/parser_yacc.y
> +++ 2.9-test/parser/parser_yacc.y
> @@ -36,6 +36,7 @@
>  #include "profile.h"
>  #include "mount.h"
>  #include "dbus.h"
> +#include "af_unix.h"
>  #include "parser_include.h"
>  #include <unistd.h>
>  #include <netinet/in.h>
> @@ -103,6 +104,16 @@
>  %token TOK_DEFINED
>  %token TOK_CHANGE_PROFILE
>  %token TOK_NETWORK
> +%token TOK_UNIX
> +%token TOK_CREATE
> +%token TOK_SHUTDOWN
> +%token TOK_ACCEPT
> +%token TOK_CONNECT
> +%token TOK_LISTEN
> +%token TOK_SETOPT
> +%token TOK_GETOPT
> +%token TOK_SETATTR
> +%token TOK_GETATTR
>  %token TOK_HAT
>  %token TOK_UNSAFE
>  %token TOK_SAFE
> @@ -173,6 +184,7 @@
>  	#include "dbus.h"
>  	#include "signal.h"
>  	#include "ptrace.h"
> +	#include "af_unix.h"
>  }
>  
>  %union {
> @@ -188,6 +200,7 @@
>  	dbus_rule *dbus_entry;
>  	signal_rule *signal_entry;
>  	ptrace_rule *ptrace_entry;
> +	unix_rule *unix_entry;
>  
>  	flagvals flags;
>  	int fmode;
> @@ -259,6 +272,10 @@
>  %type <fmode>	ptrace_perms
>  %type <fmode>	opt_ptrace_perm
>  %type <ptrace_entry>	ptrace_rule
> +%type <fmode>	net_perm
> +%type <fmode>	net_perms
> +%type <fmode>	opt_net_perm
> +%type <unix_entry>	unix_rule
>  %type <transition> opt_named_transition
>  %type <boolean> opt_unsafe
>  %type <boolean> opt_file
> @@ -641,20 +658,17 @@
>  			yyerror(_("owner prefix not allowed"));
>  		if (!$3)
>  			yyerror(_("Assert: `network_rule' return invalid protocol."));
> -		if (!$1->net.allow) {
> -			$1->net.allow = (unsigned int *) calloc(get_af_max(),
> -						     sizeof(unsigned int));
> -			$1->net.audit = (unsigned int *)calloc(get_af_max(),
> -						   sizeof(unsigned int));
> -			$1->net.deny = (unsigned int *)calloc(get_af_max(),
> -						     sizeof(unsigned int));
> -			$1->net.quiet = (unsigned int *)calloc(get_af_max(),
> -						     sizeof(unsigned int));
> -			if (!$1->net.allow || !$1->net.audit ||
> -			    !$1->net.deny || !$1->net.quiet)
> -				yyerror(_("Memory allocation error."));
> -		}
> +		if (!$1->alloc_net_table())
> +			yyerror(_("Memory allocation error."));
>  		list_for_each_safe($3, entry, tmp) {
> +
> +			/* map to extended mediation if available */
> +			if (entry->family == AF_UNIX && kernel_supports_unix) {
> +				unix_rule *rule = new unix_rule(entry->type, $2.audit, $2.deny);
> +				if (!rule)
> +					yyerror(_("Memory allocation error."));
> +				$1->rule_ents.push_back(rule);
> +			}
>  			if (entry->type > SOCK_PACKET) {
>  				/* setting mask instead of a bit */
>  				if ($2.deny) {
> @@ -748,6 +762,22 @@
>  		$$ = $1;
>  	}
>  
> +rules:  rules opt_prefix unix_rule
> +	{
> +		if ($2.owner)
> +			yyerror(_("owner prefix not allowed on unix rules"));
> +		if ($2.deny && $2.audit) {
> +			$3->deny = 1;
> +		} else if ($2.deny) {
> +			$3->deny = 1;
> +			$3->audit = $3->mode;
> +		} else if ($2.audit) {
> +			$3->audit = $3->mode;
> +		}
> +		$1->rule_ents.push_back($3);
> +		$$ = $1;
> +	}
> +
>  rules:	rules change_profile
>  	{
>  		PDEBUG("matched: rules change_profile\n");
> @@ -1267,6 +1297,84 @@
>  		if (!ent) {
>  			yyerror(_("Memory allocation error."));
>  		}
> +		$$ = ent;
> +	}
> +
> +net_perm: TOK_VALUE
> +	{
> +		if (strcmp($1, "create") == 0)
> +			$$ = AA_NET_CREATE;
> +		else if (strcmp($1, "bind") == 0)
> +			$$ = AA_NET_BIND;
> +		else if (strcmp($1, "listen") == 0)
> +			$$ = AA_NET_LISTEN;
> +		else if (strcmp($1, "accept") == 0)
> +			$$ = AA_NET_ACCEPT;
> +		else if (strcmp($1, "connect") == 0)
> +			$$ = AA_NET_CONNECT;
> +		else if (strcmp($1, "shutdown") == 0)
> +			$$ = AA_NET_SHUTDOWN;
> +		else if (strcmp($1, "getattr") == 0)
> +			$$ = AA_NET_GETATTR;
> +		else if (strcmp($1, "setattr") == 0)
> +			$$ = AA_NET_SETATTR;
> +		else if (strcmp($1, "getopt") == 0)
> +			$$ = AA_NET_GETOPT;
> +		else if (strcmp($1, "setopt") == 0)
> +			$$ = AA_NET_SETOPT;
> +		else if (strcmp($1, "send") == 0 || strcmp($1, "write") == 0)
> +			$$ = AA_NET_SEND;
> +		else if (strcmp($1, "receive") == 0 || strcmp($1, "read") == 0)
> +			$$ = AA_NET_RECEIVE;
> +		else if ($1) {
> +			parse_net_mode($1, &$$, 1);
> +		} else
> +			$$ = 0;
> +
> +		if ($1)
> +			free($1);
> +	}
> +	| TOK_CREATE { $$ = AA_NET_CREATE; }
> +	| TOK_BIND { $$ = AA_NET_BIND; }
> +	| TOK_LISTEN { $$ = AA_NET_LISTEN; }
> +	| TOK_ACCEPT { $$ = AA_NET_ACCEPT; }
> +	| TOK_CONNECT { $$ = AA_NET_CONNECT; }
> +	| TOK_SHUTDOWN { $$ = AA_NET_SHUTDOWN; }
> +	| TOK_GETATTR { $$ = AA_NET_GETATTR; }
> +	| TOK_SETATTR { $$ = AA_NET_SETATTR; }
> +	| TOK_GETOPT { $$ = AA_NET_GETOPT; }
> +	| TOK_SETOPT { $$ = AA_NET_SETOPT; }
> +	| TOK_SEND { $$ = AA_NET_SEND; }
> +	| TOK_RECEIVE { $$ = AA_NET_RECEIVE; }
> +	| TOK_READ { $$ = AA_NET_RECEIVE; }
> +	| TOK_WRITE { $$ = AA_NET_SEND; }
> +	| TOK_MODE
> +	{
> +		parse_unix_mode($1, &$$, 1);
> +		free($1);
> +	}
> +
> +net_perms: { /* nothing */ $$ = 0; }
> +	| net_perms net_perm { $$ = $1 | $2; }
> +	| net_perms TOK_COMMA net_perm { $$ = $1 | $3; }
> +
> +opt_net_perm: { /* nothing */ $$ = 0; }
> +	| net_perm  { $$ = $1; }
> +	| TOK_OPENPAREN net_perms TOK_CLOSEPAREN { $$ = $2; }
> +
> +unix_rule: TOK_UNIX opt_net_perm opt_conds opt_cond_list TOK_END_OF_RULE
> +	{
> +		unix_rule *ent;
> +
> +		if ($4.name) {
> +			if (strcmp($4.name, "peer") != 0)
> +				yyerror(_("unix rule: invalid conditional group %s=()"), $4.name);
> +			free($4.name);
> +		}
> +		ent = new unix_rule($2, $3, $4.list);
> +		if (!ent) {
> +			yyerror(_("Memory allocation error."));
> +		}
>  		$$ = ent;
>  	}
>  
> --- 2.9-test.orig/parser/profile.cc
> +++ 2.9-test/parser/profile.cc
> @@ -59,6 +59,20 @@
>  	}
>  }
>  
> +bool Profile::alloc_net_table()
> +{
> +	if (net.allow)
> +		return true;
> +	net.allow = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
> +	net.audit = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
> +	net.deny = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
> +	net.quiet = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
> +	if (!net.allow || !net.audit || !net.deny || !net.quiet)
> +		return false;
> +
> +	return true;
> +}
> +
>  Profile::~Profile()
>  {
>  	hat_table.clear();
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_bind_1.sd
> @@ -0,0 +1,8 @@
> +#
> +#=DESCRIPTION unix bind with non-bind member modifier
> +#=EXRESULT FAIL
> +#
> +
> +profile foo {
> +  unix bind peer=(path=@foo ),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_bind_2.sd
> @@ -0,0 +1,8 @@
> +#
> +#=DESCRIPTION dbus bind with non-bind interface modifier
> +#=EXRESULT FAIL
> +#
> +
> +profile foo {
> +  unix bind label=foo path=@bar,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_modifier_1.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION unix entry with a bad modifier
> +#=EXRESULT FAIL
> +
> +profile foo {
> +  unix send type=dgram modifier=foo,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_modifier_2.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION unix entry with a repeated modifier
> +#=EXRESULT FAIL
> +
> +profile foo {
> +  unix send type=stream type=dgram,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_modifier_3.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION unix entry with a bad 'in' keyword
> +#=EXRESULT FAIL
> +
> +profile foo {
> +  unix send type in (dgram, stream),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_modifier_4.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION unix entry with a bad multivalue modifier
> +#=EXRESULT FAIL
> +
> +profile foo {
> +  unix send type=(stream, dgram),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_peer_1.sd
> @@ -0,0 +1,9 @@
> +#
> +#=Description unix rule with bad 'peer'
> +#=EXRESULT FAIL
> +#
> +
> +# path must be none for anonymous or start with @ for abstract
> +profile foo {
> +  unix send peer(path=wat),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_regex_01.sd
> @@ -0,0 +1,8 @@
> +#
> +#=DESCRIPTION unix rule with a bad path regex expansion
> +#=EXRESULT FAIL
> +#
> +
> +profile foo {
> +  unix send path=@foo{one,two peer=(label=splat),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_regex_02.sd
> @@ -0,0 +1,8 @@
> +#
> +#=DESCRIPTION unix rule with a bad expansion
> +#=EXRESULT FAIL
> +#
> +
> +profile foo {
> +  unix bind path=abcd]efg,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_regex_03.sd
> @@ -0,0 +1,8 @@
> +#
> +#=DESCRIPTION unix rule with a bad peer regex expansion
> +#=EXRESULT FAIL
> +#
> +
> +profile foo {
> +  dbus send peer=(label=spla{t,r ),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/bad_regex_04.sd
> @@ -0,0 +1,8 @@
> +#
> +#=DESCRIPTION unix rule with a bad path regex expansion
> +#=EXRESULT FAIL
> +#
> +
> +profile foo {
> +  unix send path=/some/random/{path peer=(label=splat),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_bind_1.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix implicit bind acceptance test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix path=@SomeService,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_1.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix send test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_10.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send) peer=(label=foo),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_2.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (receive),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_3.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (connect),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_4.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_5.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send, receive),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_6.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send, receive, connect),
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_7.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send) path=none,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_8.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send) path=@foo,
> +}
> --- /dev/null
> +++ 2.9-test/parser/tst/simple_tests/unix/ok_msg_9.sd
> @@ -0,0 +1,7 @@
> +#
> +#=DESCRIPTION simple unix msg test
> +#=EXRESULT PASS
> +
> +profile a_profile {
> +  unix (send) peer=(path=@foo),
> +}
> 
> 
> -- 
> AppArmor mailing list
> AppArmor at lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20140826/0a117ede/attachment-0001.pgp>


More information about the AppArmor mailing list