[apparmor] [patch 1/1] parser: Add support for unix domain socket rules.
Seth Arnold
seth.arnold at canonical.com
Fri Aug 8 01:54:31 UTC 2014
On Wed, Aug 06, 2014 at 07:17:25AM -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.
Several bugs and several questions inline.
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 | 21 -
> parser/af_rule.cc | 186 ++++++++++++
> parser/af_rule.h | 80 +++++
> parser/af_unix.cc | 387 +++++++++++++++++++++++++
> parser/af_unix.h | 60 +++
> parser/apparmor.d.pod | 163 ++++++++++
> parser/network.c | 5
> parser/network.h | 40 ++
> 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/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
> 37 files changed, 1261 insertions(+), 32 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)
> @@ -233,7 +234,7 @@
> parser_common.o: parser_common.c parser.h
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> -mount.o: mount.c mount.h parser.h immunix.h rule.h
> +mount.o: mount.cc mount.h parser.h immunix.h rule.h
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> common_optarg.o: common_optarg.c common_optarg.h parser.h libapparmor_re/apparmor_re.h
> @@ -242,22 +243,28 @@
> lib.o: lib.c lib.h parser.h
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> -dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
> +dbus.o: dbus.cc dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> -signal.o: signal.c signal.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
> +signal.o: signal.cc signal.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> -ptrace.o: ptrace.c ptrace.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
> +ptrace.o: ptrace.cc ptrace.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H)
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> 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.h af_names.h $(APPARMOR_H)
> + $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
> +
> +af_unix.o: af_unix.cc af_unix.h network.h af_rule.h parser.h immunix.h parser_yacc.h rule.h af_names.h $(APPARMOR_H)
> + $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
> +
> profile.o: profile.cc profile.h parser.h network.h
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> -rule.o: rule.c rule.h policydb.h
> +rule.o: rule.cc rule.h policydb.h
> $(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
>
> parser_version.h: Makefile
> --- /dev/null
> +++ 2.9-test/parser/af_rule.cc
> @@ -0,0 +1,186 @@
> +/*
> + * 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 */
> +};
> +
> +int af_rule::cond_check(struct supported_cond *conds, struct cond_entry *ent,
> + bool peer, const char *rname) {
> + struct supported_cond *i;
This method definition { isn't in the first column, as is usual for
methods.
> + 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;
> +}
> +
> +#if 0
> +/* create a unix rule that corresponds to the old network rule */
> +void af_rule::net_rule(Profile &prof, unsigned int sock_type, bool allow,
> + bool deny)
> +{
> + /* TODO: */
> + /* old rules where socket endpoint only based and allowed everything */
> +
> +
> +}
> +#endif
> +
> +ostream &af_rule::dump_prefix(ostream &os)
> +{
> + if (audit)
> + os << "audit ";
> + if (deny)
> + os << "deny ";
> + return os;
> +}
> +
> +ostream &af_rule::dump_local(ostream &os)
> +{
> +
> + os << "ptrace";
Shouldn't be "ptrace" :) "local"? "unix"?
> + 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) {
> + if (peer_label)
> + os << " label=\"" << peer_label << "\"";
> + }
> + return os;
I think one of these if conditions is incorrect; also no need for { }
> +}
> +
> +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,80 @@
> +/*
> + * 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)
> + {}
> +
> + af_rule(unsigned int af, unsigned int sock_type, bool allow, bool deny);
This constructor doesn't seem to initialize anything; the pointers might
still point to garbage and the values given aren't saved anywhere...
> +
> + virtual ~af_rule()
> + {
> + free(sock_type);
> + free(proto);
> + free(label);
> + free(peer_label);
> + };
> +
> + int 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,387 @@
> +/*
> + * 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;
> +cerr << "created network rule\n";
> +}
> +
> +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.deny[AF_UNIX] |= 1 << sock_type_n;
This !audit branch looks incorrect.
> + } 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_ACCEPT,
> + audit & AA_NET_ACCEPT,
> + dfaflags))
Should add_rule() be using AA_NET_ACCEPT here?
> + 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);
These constructors don't appear to initialize anything...
> + 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
"losslessly"
> +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,44 @@
>
> #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_BIND 0x00200000
> +#define AA_NET_ACCEPT 0x00400000
> +#define AA_NET_LISTEN 0x00800000
> +
> +#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 +90,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
> @@ -67,6 +67,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
> @@ -259,6 +259,7 @@
> %x DBUS_MODE
> %x SIGNAL_MODE
> %x PTRACE_MODE
> +%x UNIX_MODE
> %x CHANGE_PROFILE_MODE
> %x INCLUDE
>
> @@ -273,7 +274,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 */ }
> }
>
> @@ -299,7 +300,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
> @@ -469,8 +470,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); }
> }
>
> @@ -485,7 +501,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} {
> @@ -501,7 +517,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);
> @@ -593,13 +609,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();
> @@ -612,7 +632,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 */
> @@ -641,6 +661,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)
Should this be CLASS_STR(X) MAKE_STR(Y)? Or CLASS_STR(X) CLASS_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;
GETOPT and SETOPT were left in all-caps here.
> + 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;
> }
>
> --- /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/20140807/afab39b3/attachment-0001.pgp>
More information about the AppArmor
mailing list