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

john.johansen at canonical.com john.johansen at canonical.com
Tue Aug 26 00:06:07 UTC 2014


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.

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),
+}




More information about the AppArmor mailing list