2011-03-13 05:49:15 -07:00
|
|
|
/*
|
|
|
|
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
|
|
|
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
2013-10-14 14:36:05 -07:00
|
|
|
* Copyright 2009-2013 Canonical Ltd. (All rights reserved)
|
2011-03-13 05:49:15 -07:00
|
|
|
*
|
|
|
|
* The libapparmor library is licensed under the terms of the GNU
|
|
|
|
* Lesser General Public License, version 2.1. Please see the file
|
|
|
|
* COPYING.LGPL.
|
|
|
|
*
|
|
|
|
* This library 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 Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Wrapper around the dfa to convert aa rules into a dfa
|
|
|
|
*/
|
|
|
|
|
2011-03-13 05:50:34 -07:00
|
|
|
#include <ostream>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
2011-03-13 05:49:15 -07:00
|
|
|
#include <sstream>
|
|
|
|
#include <ext/stdio_filebuf.h>
|
2011-03-13 05:50:34 -07:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
2011-03-13 05:49:15 -07:00
|
|
|
|
|
|
|
#include "aare_rules.h"
|
|
|
|
#include "expr-tree.h"
|
|
|
|
#include "parse.h"
|
|
|
|
#include "hfa.h"
|
2011-12-15 05:06:32 -08:00
|
|
|
#include "chfa.h"
|
2011-03-13 05:50:34 -07:00
|
|
|
#include "../immunix.h"
|
2011-03-13 05:49:15 -07:00
|
|
|
|
|
|
|
|
2015-06-25 14:08:55 -06:00
|
|
|
class UniquePerm {
|
|
|
|
public:
|
|
|
|
bool deny;
|
|
|
|
bool exact_match;
|
|
|
|
uint32_t perms;
|
|
|
|
uint32_t audit;
|
|
|
|
|
|
|
|
bool operator<(UniquePerm const &rhs)const
|
|
|
|
{
|
|
|
|
if (deny == rhs.deny) {
|
|
|
|
if (exact_match == rhs.exact_match) {
|
|
|
|
if (perms == rhs.perms)
|
|
|
|
return audit < rhs.audit;
|
|
|
|
return perms < rhs.perms;
|
|
|
|
}
|
|
|
|
return exact_match;
|
|
|
|
}
|
|
|
|
return deny;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class UniquePermsCache {
|
|
|
|
public:
|
|
|
|
typedef map<UniquePerm, Node*> UniquePermMap;
|
|
|
|
typedef UniquePermMap::iterator iterator;
|
|
|
|
UniquePermMap nodes;
|
|
|
|
|
|
|
|
UniquePermsCache(void) { };
|
|
|
|
~UniquePermsCache() { clear(); }
|
|
|
|
|
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
for (iterator i = nodes.begin(); i != nodes.end(); i++) {
|
|
|
|
delete i->second;
|
|
|
|
}
|
|
|
|
nodes.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *insert(bool deny, uint32_t perms, uint32_t audit,
|
|
|
|
bool exact_match)
|
|
|
|
{
|
|
|
|
UniquePerm tmp = { deny, exact_match, perms, audit };
|
|
|
|
iterator res = nodes.find(tmp);
|
|
|
|
if (res == nodes.end()) {
|
|
|
|
Node *node;
|
|
|
|
if (deny)
|
|
|
|
node = new DenyMatchFlag(perms, audit);
|
|
|
|
else if (exact_match)
|
|
|
|
node = new ExactMatchFlag(perms, audit);
|
|
|
|
else
|
|
|
|
node = new MatchFlag(perms, audit);
|
|
|
|
pair<iterator, bool> val = nodes.insert(make_pair(tmp, node));
|
|
|
|
if (val.second == false)
|
|
|
|
return val.first->second;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
return res->second;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static UniquePermsCache unique_perms;
|
|
|
|
|
2011-03-13 05:49:15 -07:00
|
|
|
|
2014-04-23 10:57:16 -07:00
|
|
|
aare_rules::~aare_rules(void)
|
2011-03-13 05:49:15 -07:00
|
|
|
{
|
2014-04-23 10:57:16 -07:00
|
|
|
if (root)
|
|
|
|
root->release();
|
2012-12-10 17:08:19 -08:00
|
|
|
|
2015-06-25 14:08:55 -06:00
|
|
|
unique_perms.clear();
|
2011-03-13 05:49:15 -07:00
|
|
|
}
|
|
|
|
|
2014-04-23 10:57:16 -07:00
|
|
|
bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
|
|
|
|
uint32_t audit, dfaflags_t flags)
|
2011-03-13 05:49:15 -07:00
|
|
|
{
|
2014-04-23 10:57:16 -07:00
|
|
|
return add_rule_vec(deny, perms, audit, 1, &rule, flags);
|
2011-03-13 05:49:15 -07:00
|
|
|
}
|
|
|
|
|
2013-09-27 16:13:22 -07:00
|
|
|
void aare_reset_matchflags(void)
|
2011-03-13 05:49:15 -07:00
|
|
|
{
|
2015-06-25 14:08:55 -06:00
|
|
|
unique_perms.clear();
|
2011-03-13 05:49:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-03 14:24:37 -07:00
|
|
|
void aare_rules::add_to_rules(Node *tree, Node *perms)
|
|
|
|
{
|
|
|
|
if (reverse)
|
|
|
|
flip_tree(tree);
|
|
|
|
if (root)
|
|
|
|
root = new AltNode(root, new CatNode(tree, perms));
|
|
|
|
else
|
|
|
|
root = new CatNode(tree, perms);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Node *cat_with_null_seperator(Node *l, Node *r)
|
|
|
|
{
|
|
|
|
return new CatNode(new CatNode(l, new CharNode(0)), r);
|
|
|
|
}
|
|
|
|
|
2014-09-03 14:40:08 -07:00
|
|
|
bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
|
|
|
|
int count, const char **rulev, dfaflags_t flags)
|
|
|
|
{
|
|
|
|
Node *tree = NULL, *accept;
|
|
|
|
int exact_match;
|
|
|
|
|
|
|
|
if (regex_parse(&tree, rulev[0]))
|
|
|
|
return false;
|
|
|
|
for (int i = 1; i < count; i++) {
|
|
|
|
Node *subtree = NULL;
|
|
|
|
if (regex_parse(&subtree, rulev[i]))
|
|
|
|
return false;
|
|
|
|
tree = cat_with_null_seperator(tree, subtree);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we have an expression with or without wildcards. This
|
|
|
|
* determines how exec modifiers are merged in accept_perms() based
|
|
|
|
* on how we split permission bitmasks here.
|
|
|
|
*/
|
|
|
|
exact_match = 1;
|
|
|
|
for (depth_first_traversal i(tree); i && exact_match; i++) {
|
|
|
|
if (dynamic_cast<StarNode *>(*i) ||
|
|
|
|
dynamic_cast<PlusNode *>(*i) ||
|
|
|
|
dynamic_cast<AnyCharNode *>(*i) ||
|
|
|
|
dynamic_cast<CharSetNode *>(*i) ||
|
|
|
|
dynamic_cast<NotCharSetNode *>(*i))
|
|
|
|
exact_match = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reverse)
|
|
|
|
flip_tree(tree);
|
|
|
|
|
2015-06-25 14:08:55 -06:00
|
|
|
accept = unique_perms.insert(deny, perms, audit, exact_match);
|
2014-09-03 14:40:08 -07:00
|
|
|
|
2011-03-13 05:53:08 -07:00
|
|
|
if (flags & DFA_DUMP_RULE_EXPR) {
|
|
|
|
cerr << "rule: ";
|
|
|
|
cerr << rulev[0];
|
|
|
|
for (int i = 1; i < count; i++) {
|
|
|
|
cerr << "\\x00";
|
|
|
|
cerr << rulev[i];
|
|
|
|
}
|
|
|
|
cerr << " -> ";
|
|
|
|
tree->dump(cerr);
|
2012-02-24 04:17:19 -08:00
|
|
|
if (deny)
|
|
|
|
cerr << " deny";
|
2014-09-03 14:40:08 -07:00
|
|
|
cerr << " (0x" << hex << perms <<"/" << audit << dec << ")";
|
2012-02-24 04:17:19 -08:00
|
|
|
accept->dump(cerr);
|
|
|
|
cerr << "\n\n";
|
2011-03-13 05:49:15 -07:00
|
|
|
}
|
2011-03-13 05:53:08 -07:00
|
|
|
|
2014-09-03 14:24:37 -07:00
|
|
|
add_to_rules(tree, accept);
|
2011-03-13 05:53:08 -07:00
|
|
|
|
2014-04-23 10:57:16 -07:00
|
|
|
rule_count++;
|
2011-03-13 05:49:15 -07:00
|
|
|
|
2014-04-23 10:57:16 -07:00
|
|
|
return true;
|
2011-03-13 05:49:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* create a dfa from the ruleset
|
|
|
|
* returns: buffer contain dfa tables, @size set to the size of the tables
|
|
|
|
* else NULL on failure
|
|
|
|
*/
|
2014-04-23 10:57:16 -07:00
|
|
|
void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
|
2011-03-13 05:49:15 -07:00
|
|
|
{
|
2011-03-13 05:53:08 -07:00
|
|
|
char *buffer = NULL;
|
|
|
|
|
2014-04-23 10:57:16 -07:00
|
|
|
label_nodes(root);
|
2011-03-13 05:53:08 -07:00
|
|
|
if (flags & DFA_DUMP_TREE) {
|
|
|
|
cerr << "\nDFA: Expression Tree\n";
|
2014-04-23 10:57:16 -07:00
|
|
|
root->dump(cerr);
|
2011-03-13 05:53:08 -07:00
|
|
|
cerr << "\n\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & DFA_CONTROL_TREE_SIMPLE) {
|
2014-04-23 10:57:16 -07:00
|
|
|
root = simplify_tree(root, flags);
|
2011-03-13 05:53:08 -07:00
|
|
|
|
|
|
|
if (flags & DFA_DUMP_SIMPLE_TREE) {
|
|
|
|
cerr << "\nDFA: Simplified Expression Tree\n";
|
2014-04-23 10:57:16 -07:00
|
|
|
root->dump(cerr);
|
2011-03-13 05:53:08 -07:00
|
|
|
cerr << "\n\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stringstream stream;
|
|
|
|
try {
|
2014-04-23 10:57:16 -07:00
|
|
|
DFA dfa(root, flags);
|
2011-03-13 05:53:08 -07:00
|
|
|
if (flags & DFA_DUMP_UNIQ_PERMS)
|
|
|
|
dfa.dump_uniq_perms("dfa");
|
|
|
|
|
|
|
|
if (flags & DFA_CONTROL_MINIMIZE) {
|
|
|
|
dfa.minimize(flags);
|
|
|
|
|
|
|
|
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
|
|
|
dfa.dump_uniq_perms("minimized dfa");
|
|
|
|
}
|
2012-02-16 07:41:40 -08:00
|
|
|
|
2012-02-16 07:43:02 -08:00
|
|
|
if (flags & DFA_CONTROL_FILTER_DENY &&
|
|
|
|
flags & DFA_CONTROL_MINIMIZE &&
|
|
|
|
dfa.apply_and_clear_deny()) {
|
2012-02-16 07:41:40 -08:00
|
|
|
/* Do a second minimization pass as removal of deny
|
|
|
|
* information has moved some states from accepting
|
|
|
|
* to none accepting partitions
|
|
|
|
*
|
|
|
|
* TODO: add this as a tail pass to minimization
|
|
|
|
* so we don't need to do a full second pass
|
|
|
|
*/
|
|
|
|
dfa.minimize(flags);
|
|
|
|
|
|
|
|
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
|
|
|
dfa.dump_uniq_perms("minimized dfa");
|
|
|
|
}
|
|
|
|
|
2011-03-13 05:53:08 -07:00
|
|
|
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
|
|
|
|
dfa.remove_unreachable(flags);
|
|
|
|
|
|
|
|
if (flags & DFA_DUMP_STATES)
|
|
|
|
dfa.dump(cerr);
|
|
|
|
|
|
|
|
if (flags & DFA_DUMP_GRAPH)
|
|
|
|
dfa.dump_dot_graph(cerr);
|
|
|
|
|
|
|
|
map<uchar, uchar> eq;
|
|
|
|
if (flags & DFA_CONTROL_EQUIV) {
|
|
|
|
eq = dfa.equivalence_classes(flags);
|
|
|
|
dfa.apply_equivalence_classes(eq);
|
|
|
|
|
|
|
|
if (flags & DFA_DUMP_EQUIV) {
|
|
|
|
cerr << "\nDFA equivalence class\n";
|
|
|
|
dump_equivalence_classes(cerr, eq);
|
|
|
|
}
|
|
|
|
} else if (flags & DFA_DUMP_EQUIV)
|
|
|
|
cerr << "\nDFA did not generate an equivalence class\n";
|
|
|
|
|
2014-01-09 16:55:55 -08:00
|
|
|
if (flags & DFA_CONTROL_DIFF_ENCODE) {
|
|
|
|
dfa.diff_encode(flags);
|
|
|
|
|
|
|
|
if (flags & DFA_DUMP_DIFF_ENCODE)
|
|
|
|
dfa.dump_diff_encode(cerr);
|
|
|
|
}
|
|
|
|
|
2011-12-15 05:06:32 -08:00
|
|
|
CHFA chfa(dfa, eq, flags);
|
2011-03-13 05:53:08 -07:00
|
|
|
if (flags & DFA_DUMP_TRANS_TABLE)
|
2011-12-15 05:06:32 -08:00
|
|
|
chfa.dump(cerr);
|
|
|
|
chfa.flex_table(stream, "");
|
2011-03-13 05:53:08 -07:00
|
|
|
}
|
|
|
|
catch(int error) {
|
|
|
|
*size = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
stringbuf *buf = stream.rdbuf();
|
|
|
|
|
|
|
|
buf->pubseekpos(0);
|
|
|
|
*size = buf->in_avail();
|
|
|
|
|
|
|
|
buffer = (char *)malloc(*size);
|
|
|
|
if (!buffer)
|
|
|
|
return NULL;
|
|
|
|
buf->sgetn(buffer, *size);
|
|
|
|
return buffer;
|
2011-03-13 05:49:15 -07:00
|
|
|
}
|