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