mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 16:35:02 +01:00

There is one significant difference in the encoding of the network rules. Before this change, when the parser was encoding a "network," rule, it would generate an entry for every family and every type/protocol. After this patch the parser should generate an entry for every family, but the type/protocol is changed to .. in the pcre syntax. There should be no difference in behavior. Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
438 lines
9.9 KiB
C++
438 lines
9.9 KiB
C++
/*
|
|
* Copyright (c) 2012
|
|
* 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.
|
|
*/
|
|
#ifndef __AA_PROFILE_H
|
|
#define __AA_PROFILE_H
|
|
|
|
#include <set>
|
|
#include <string>
|
|
#include <iostream>
|
|
|
|
#include "capability.h"
|
|
#include "parser.h"
|
|
#include "rule.h"
|
|
#include "libapparmor_re/aare_rules.h"
|
|
#include "network.h"
|
|
#include "signal.h"
|
|
|
|
class Profile;
|
|
|
|
class block {
|
|
public:
|
|
|
|
};
|
|
|
|
|
|
struct deref_profileptr_lt {
|
|
bool operator()(Profile * const &lhs, Profile * const &rhs) const;
|
|
};
|
|
|
|
class ProfileList {
|
|
public:
|
|
set<Profile *, deref_profileptr_lt> list;
|
|
|
|
typedef set<Profile *, deref_profileptr_lt>::iterator iterator;
|
|
iterator begin() { return list.begin(); }
|
|
iterator end() { return list.end(); }
|
|
|
|
ProfileList() { };
|
|
virtual ~ProfileList() { clear(); }
|
|
virtual bool empty(void) { return list.empty(); }
|
|
virtual pair<ProfileList::iterator,bool> insert(Profile *);
|
|
virtual void erase(ProfileList::iterator pos);
|
|
void clear(void);
|
|
void dump(void);
|
|
void dump_profile_names(bool children);
|
|
};
|
|
|
|
extern const char*profile_mode_table[];
|
|
/* use profile_mode_packed to convert to the packed representation */
|
|
enum profile_mode {
|
|
MODE_UNSPECIFIED = 0,
|
|
MODE_ENFORCE = 1,
|
|
MODE_COMPLAIN = 2,
|
|
MODE_KILL = 3,
|
|
MODE_UNCONFINED = 4,
|
|
MODE_PROMPT = 5,
|
|
MODE_CONFLICT = 6 /* greater than MODE_LAST */
|
|
};
|
|
#define MODE_LAST MODE_PROMPT
|
|
|
|
static inline enum profile_mode operator++(enum profile_mode &mode)
|
|
{
|
|
mode = (enum profile_mode)((int) mode + 1);
|
|
return mode;
|
|
}
|
|
|
|
static inline enum profile_mode merge_profile_mode(enum profile_mode l, enum profile_mode r)
|
|
{
|
|
if (l == r || r == MODE_UNSPECIFIED)
|
|
return l;
|
|
else if (l == MODE_UNSPECIFIED)
|
|
return r;
|
|
return MODE_CONFLICT;
|
|
}
|
|
|
|
static inline uint32_t profile_mode_packed(enum profile_mode mode)
|
|
{
|
|
/* kernel doesn't have an unspecified mode everything
|
|
* shifts down by 1
|
|
*/
|
|
if ((uint32_t) mode)
|
|
return (uint32_t) mode - 1;
|
|
/* unspecified defaults to same as enforce */
|
|
return 0;
|
|
}
|
|
|
|
static inline void mode_dump(ostream &os, enum profile_mode mode)
|
|
{
|
|
if (mode <= MODE_LAST)
|
|
os << profile_mode_table[(int) mode];
|
|
else
|
|
os << "unknown";
|
|
}
|
|
|
|
static inline enum profile_mode str_to_mode(const char *str)
|
|
{
|
|
for (enum profile_mode i = MODE_ENFORCE; i <= MODE_LAST; ++i) {
|
|
if (strcmp(profile_mode_table[i], str) == 0)
|
|
return i;
|
|
}
|
|
|
|
return MODE_UNSPECIFIED;
|
|
};
|
|
|
|
#define FLAG_HAT 1
|
|
#define FLAG_DEBUG1 2
|
|
#define FLAG_DEBUG2 4
|
|
#define FLAG_INTERRUPTIBLE 8
|
|
|
|
/* sigh, used in parse union so needs trivial constructors. */
|
|
class flagvals {
|
|
public:
|
|
int flags;
|
|
enum profile_mode mode;
|
|
int audit;
|
|
int path;
|
|
char *disconnected_path;
|
|
int signal;
|
|
|
|
// stupid not constructor constructors
|
|
void init(void)
|
|
{
|
|
flags = 0;
|
|
mode = MODE_UNSPECIFIED;
|
|
audit = 0;
|
|
path = 0;
|
|
disconnected_path = NULL;
|
|
signal = 0;
|
|
}
|
|
void init(const char *str)
|
|
{
|
|
init();
|
|
enum profile_mode pmode = str_to_mode(str);
|
|
|
|
if (strcmp(str, "debug") == 0) {
|
|
/* DEBUG2 is left for internal compiler use atm */
|
|
flags |= FLAG_DEBUG1;
|
|
} else if (pmode) {
|
|
mode = pmode;
|
|
} else if (strcmp(str, "audit") == 0) {
|
|
audit = 1;
|
|
} else if (strcmp(str, "chroot_relative") == 0) {
|
|
path |= PATH_CHROOT_REL;
|
|
} else if (strcmp(str, "namespace_relative") == 0) {
|
|
path |= PATH_NS_REL;
|
|
} else if (strcmp(str, "mediate_deleted") == 0) {
|
|
path |= PATH_MEDIATE_DELETED;
|
|
} else if (strcmp(str, "delegate_deleted") == 0) {
|
|
path |= PATH_DELEGATE_DELETED;
|
|
} else if (strcmp(str, "attach_disconnected") == 0) {
|
|
path |= PATH_ATTACH;
|
|
} else if (strcmp(str, "no_attach_disconnected") == 0) {
|
|
path |= PATH_NO_ATTACH;
|
|
} else if (strcmp(str, "chroot_attach") == 0) {
|
|
path |= PATH_CHROOT_NSATTACH;
|
|
} else if (strcmp(str, "chroot_no_attach") == 0) {
|
|
path |= PATH_CHROOT_NO_ATTACH;
|
|
} else if (strncmp(str, "attach_disconnected.path=", 25) == 0) {
|
|
/* TODO: make this a proper parse */
|
|
path |= PATH_ATTACH;
|
|
disconnected_path = strdup(str + 25);
|
|
} else if (strncmp(str, "kill.signal=", 12) == 0) {
|
|
/* TODO: make this a proper parse */
|
|
signal = find_signal_mapping(str + 12);
|
|
if (signal == -1)
|
|
yyerror("unknown signal specified for kill.signal=\'%s\'\n", str + 12);
|
|
} else if (strcmp(str, "interruptible") == 0) {
|
|
flags |= FLAG_INTERRUPTIBLE;
|
|
} else {
|
|
yyerror(_("Invalid profile flag: %s."), str);
|
|
}
|
|
}
|
|
|
|
ostream &dump(ostream &os)
|
|
{
|
|
os << "Mode: ";
|
|
mode_dump(os, mode);
|
|
if (audit)
|
|
os << ", Audit";
|
|
|
|
if (flags & FLAG_HAT)
|
|
os << ", Hat";
|
|
|
|
if (disconnected_path)
|
|
os << ", attach_disconnected.path=" << disconnected_path;
|
|
if (signal)
|
|
os << ", kill.signal=" << signal;
|
|
os << "\n";
|
|
|
|
return os;
|
|
}
|
|
ostream &debug(ostream &os)
|
|
{
|
|
#ifdef DEBUG
|
|
return dump(os);
|
|
#else
|
|
return os;
|
|
#endif
|
|
}
|
|
|
|
/* warning for now disconnected_path is just passed on (not copied),
|
|
* or leaked on error. It is not freed here, It is freed when the
|
|
* profile destroys it self.
|
|
*/
|
|
void merge(const flagvals &rhs)
|
|
{
|
|
if (merge_profile_mode(mode, rhs.mode) == MODE_CONFLICT)
|
|
yyerror(_("Profile flag '%s' conflicts with '%s'"),
|
|
profile_mode_table[mode],
|
|
profile_mode_table[rhs.mode]);
|
|
mode = merge_profile_mode(mode, rhs.mode);
|
|
audit = audit || rhs.audit;
|
|
path = path | rhs.path;
|
|
if ((path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
|
|
(PATH_CHROOT_REL | PATH_NS_REL))
|
|
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
|
|
|
|
if ((path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
|
|
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
|
|
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
|
|
if ((path & (PATH_ATTACH | PATH_NO_ATTACH)) ==
|
|
(PATH_ATTACH | PATH_NO_ATTACH))
|
|
yyerror(_("Profile flag attach_disconnected conflicts with no_attach_disconnected"));
|
|
if ((path & (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) ==
|
|
(PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH))
|
|
yyerror(_("Profile flag chroot_attach conflicts with chroot_no_attach"));
|
|
|
|
if (rhs.disconnected_path) {
|
|
if (disconnected_path) {
|
|
if (strcmp(disconnected_path, rhs.disconnected_path) != 0) {
|
|
yyerror(_("Profile flag attach_disconnected set to conflicting values: '%s' and '%s'"), disconnected_path, rhs.disconnected_path);
|
|
}
|
|
// same ignore rhs.disconnect_path
|
|
} else {
|
|
disconnected_path = rhs.disconnected_path;
|
|
}
|
|
}
|
|
if (rhs.signal) {
|
|
if (signal) {
|
|
if (signal != rhs.signal) {
|
|
yyerror(_("Profile flag kill.signal set to conflicting values: '%d' and '%d'"), signal, rhs.signal);
|
|
}
|
|
// same so do nothing
|
|
} else {
|
|
signal = rhs.signal;
|
|
}
|
|
}
|
|
|
|
/* if we move to dupping disconnected_path will need to have
|
|
* an assignment and copy constructor and a destructor
|
|
*/
|
|
}
|
|
};
|
|
|
|
struct capabilities {
|
|
uint64_t allow;
|
|
uint64_t audit;
|
|
uint64_t deny;
|
|
uint64_t quiet;
|
|
|
|
capabilities(void) { allow = audit = deny = quiet = 0; }
|
|
|
|
void dump()
|
|
{
|
|
if (allow != 0ull)
|
|
__debug_capabilities(allow, "Capabilities");
|
|
if (audit != 0ull)
|
|
__debug_capabilities(audit, "Audit Caps");
|
|
if (deny != 0ull)
|
|
__debug_capabilities(deny, "Deny Caps");
|
|
if (quiet != 0ull)
|
|
__debug_capabilities(quiet, "Quiet Caps");
|
|
};
|
|
};
|
|
|
|
struct dfa_stuff {
|
|
aare_rules *rules;
|
|
void *dfa;
|
|
size_t size;
|
|
|
|
dfa_stuff(void): rules(NULL), dfa(NULL), size(0) { }
|
|
};
|
|
|
|
class Profile {
|
|
public:
|
|
char *ns;
|
|
char *name;
|
|
char *attachment;
|
|
struct alt_name *altnames;
|
|
void *xmatch;
|
|
size_t xmatch_size;
|
|
int xmatch_len;
|
|
|
|
struct cond_entry_list xattrs;
|
|
|
|
/* char *sub_name; */ /* subdomain name or NULL */
|
|
/* int default_deny; */ /* TRUE or FALSE */
|
|
bool local;
|
|
|
|
Profile *parent;
|
|
|
|
flagvals flags;
|
|
struct capabilities caps;
|
|
network_rule net;
|
|
|
|
struct aa_rlimits rlimits;
|
|
|
|
char *exec_table[AA_EXEC_COUNT];
|
|
struct cod_entry *entries;
|
|
RuleList rule_ents;
|
|
|
|
ProfileList hat_table;
|
|
|
|
struct dfa_stuff dfa;
|
|
struct dfa_stuff policy;
|
|
|
|
Profile(void)
|
|
{
|
|
ns = name = attachment = NULL;
|
|
altnames = NULL;
|
|
xmatch = NULL;
|
|
xmatch_size = 0;
|
|
xmatch_len = 0;
|
|
|
|
xattrs.list = NULL;
|
|
xattrs.name = NULL;
|
|
|
|
parent = NULL;
|
|
|
|
flags.init();
|
|
rlimits = {0, {}};
|
|
|
|
std::fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL);
|
|
|
|
entries = NULL;
|
|
};
|
|
|
|
virtual ~Profile();
|
|
|
|
bool operator<(Profile const &rhs)const
|
|
{
|
|
if (ns) {
|
|
if (rhs.ns) {
|
|
int res = strcmp(ns, rhs.ns);
|
|
if (res != 0)
|
|
return res < 0;
|
|
} else
|
|
return false;
|
|
} else if (rhs.ns)
|
|
return true;
|
|
return strcmp(name, rhs.name) < 0;
|
|
}
|
|
|
|
/*
|
|
* Requires the merged rules have customized methods
|
|
* cmp(), is_mergeable() and merge()
|
|
*/
|
|
virtual int merge_rules(void);
|
|
|
|
void dump(void)
|
|
{
|
|
if (ns)
|
|
printf("Ns:\t\t%s\n", ns);
|
|
|
|
if (name)
|
|
printf("Name:\t\t%s\n", name);
|
|
else
|
|
printf("Name:\t\t<NULL>\n");
|
|
|
|
if (parent)
|
|
printf("Local To:\t%s\n", parent->name);
|
|
else
|
|
printf("Local To:\t<NULL>\n");
|
|
|
|
flags.dump(cerr);
|
|
caps.dump();
|
|
|
|
if (entries)
|
|
debug_cod_entries(entries);
|
|
|
|
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) {
|
|
(*i)->dump(cout);
|
|
}
|
|
|
|
printf("\n");
|
|
hat_table.dump();
|
|
}
|
|
|
|
std::string hname(void)
|
|
{
|
|
if (!parent)
|
|
return name;
|
|
|
|
return parent->hname() + "//" + name;
|
|
}
|
|
|
|
/* assumes ns is set as part of profile creation */
|
|
std::string fqname(void)
|
|
{
|
|
if (parent)
|
|
return parent->fqname() + "//" + name;
|
|
else if (!ns)
|
|
return hname();
|
|
return ":" + std::string(ns) + "://" + hname();
|
|
}
|
|
|
|
std::string get_name(bool fqp)
|
|
{
|
|
if (fqp)
|
|
return fqname();
|
|
return hname();
|
|
}
|
|
|
|
void dump_name(bool fqp)
|
|
{
|
|
cout << get_name(fqp);;
|
|
}
|
|
|
|
void post_parse_profile(void);
|
|
void add_implied_rules(void);
|
|
|
|
protected:
|
|
const char *warned_name = NULL;
|
|
virtual void warn_once(const char *name, const char *msg);
|
|
};
|
|
|
|
|
|
#endif /* __AA_PROFILE_H */
|