parser: first step implementing fine grained mediation for unix domain sockets

This patch implements parsing of fine grained mediation for unix domain
sockets, that have abstract and anonymous paths. Sockets with file
system paths are handled by regular file access rules.

The unix network rules follow the general fine grained network
rule pattern of

  [<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]

specifically for af_unix this is

  [<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]

  <qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ]

  <access expr> = ( <access> | <access list> )

  <access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' |
               'connect' | 'shutdown' | 'getattr' | 'setattr' |
	       'getopt' | 'setopt' |
               'send' | 'receive' | 'r' | 'w' | 'rw' )
  (some access modes are incompatible with some rules or require additional
   parameters)

  <access list> = '(' <access> ( [','] <WS> <access> )* ')'

  <WS> = white space

  <rule conds> = ( <type cond> | <protocol cond> )*
     each cond can appear at most once

  <type cond> = 'type' '='  ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )

  <protocol cond> = 'protocol' '='  ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )

  <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.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
This commit is contained in:
John Johansen 2014-09-03 13:22:26 -07:00 committed by Steve Beattie
parent 0d4f802572
commit dd44858e60
38 changed files with 1253 additions and 27 deletions

View file

@ -80,9 +80,10 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c 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 @@ ptrace.o: ptrace.cc ptrace.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_
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 $@ $<

171
parser/af_rule.cc Normal file
View file

@ -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;
}

78
parser/af_rule.h Normal file
View file

@ -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 */

386
parser/af_unix.cc Normal file
View file

@ -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;
}

60
parser/af_unix.h Normal file
View file

@ -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 */

View file

@ -54,7 +54,7 @@ B<COMMENT> = '#' I<TEXT>
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<DBUS ACCESS> = ( 'send' | 'receive' | 'bind' | 'eavesdrop' ) (some accesses a
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 @@ Example AppArmor DBus rules:
# 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

View file

@ -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.

View file

@ -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 {
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);

View file

@ -291,6 +291,7 @@ extern int kernel_supports_mount;
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;

View file

@ -66,6 +66,7 @@ int net_af_max_override = -1; /* use kernel to determine af_max */
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 */

View file

@ -258,6 +258,7 @@ LT_EQUAL <=
%x DBUS_MODE
%x SIGNAL_MODE
%x PTRACE_MODE
%x UNIX_MODE
%x CHANGE_PROFILE_MODE
%x INCLUDE
@ -272,7 +273,7 @@ LT_EQUAL <=
}
%}
<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 @@ LT_EQUAL <=
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 <=
{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 @@ LT_EQUAL <=
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 @@ LT_EQUAL <=
{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 @@ LT_EQUAL <=
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 @@ LT_EQUAL <=
}
}
<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 @@ unordered_map<int, string> state_names = {
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),
};

View file

@ -686,10 +686,14 @@ static void set_supported_features(void) {
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"))

View file

@ -107,6 +107,7 @@ struct keyword_table {
static struct keyword_table keyword_table[] = {
/* network */
{"network", TOK_NETWORK},
{"unix", TOK_UNIX},
/* misc keywords */
{"capability", TOK_CAPABILITY},
{"if", TOK_IF},

View file

@ -665,14 +665,17 @@ int post_process_policydb_ents(Profile *prof)
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 @@ int process_profile_policydb(Profile *prof)
* 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 @@ int process_profile_policydb(Profile *prof)
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);

View file

@ -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 @@ void add_local_entry(Profile *prof);
%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 @@ void add_local_entry(Profile *prof);
#include "dbus.h"
#include "signal.h"
#include "ptrace.h"
#include "af_unix.h"
}
%union {
@ -188,6 +200,7 @@ void add_local_entry(Profile *prof);
dbus_rule *dbus_entry;
signal_rule *signal_entry;
ptrace_rule *ptrace_entry;
unix_rule *unix_entry;
flagvals flags;
int fmode;
@ -259,6 +272,10 @@ void add_local_entry(Profile *prof);
%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 @@ rules: rules opt_prefix network_rule
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)
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 @@ rules: rules opt_prefix ptrace_rule
$$ = $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");
@ -1270,6 +1300,84 @@ dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE
$$ = 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;
}
signal_perm: TOK_VALUE
{
if (strcmp($1, "send") == 0 || strcmp($1, "write") == 0)

View file

@ -59,6 +59,20 @@ void ProfileList::dump_profile_names(bool children)
}
}
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();

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION unix bind with non-bind member modifier
#=EXRESULT FAIL
#
profile foo {
unix bind peer=(path=@foo ),
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION dbus bind with non-bind interface modifier
#=EXRESULT FAIL
#
profile foo {
unix bind label=foo path=@bar,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION unix entry with a bad modifier
#=EXRESULT FAIL
profile foo {
unix send type=dgram modifier=foo,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION unix entry with a repeated modifier
#=EXRESULT FAIL
profile foo {
unix send type=stream type=dgram,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION unix entry with a bad 'in' keyword
#=EXRESULT FAIL
profile foo {
unix send type in (dgram, stream),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION unix entry with a bad multivalue modifier
#=EXRESULT FAIL
profile foo {
unix send type=(stream, dgram),
}

View file

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

View file

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

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION unix rule with a bad expansion
#=EXRESULT FAIL
#
profile foo {
unix bind path=abcd]efg,
}

View file

@ -0,0 +1,8 @@
#
#=DESCRIPTION unix rule with a bad peer regex expansion
#=EXRESULT FAIL
#
profile foo {
dbus send peer=(label=spla{t,r ),
}

View file

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

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix implicit bind acceptance test
#=EXRESULT PASS
profile a_profile {
unix path=@SomeService,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix send test
#=EXRESULT PASS
profile a_profile {
unix (send),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send) peer=(label=foo),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (receive),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (connect),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send, receive),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send, receive, connect),
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send) path=none,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send) path=@foo,
}

View file

@ -0,0 +1,7 @@
#
#=DESCRIPTION simple unix msg test
#=EXRESULT PASS
profile a_profile {
unix (send) peer=(path=@foo),
}