2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-29 05:17:59 +00:00

Add Audit control to AppArmor through, the use of audit and deny

key words.  Deny is also used to subtract permissions from the
profiles permission set.

the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched.  Audit
permissions accumulate just like standard permissions.

  eg.
  audit /bin/foo rw,

  will force an audit message when the file /bin/foo is opened for
  read or write.

  audit /etc/shadow w,
  /etc/shadow r,
  will force an audit message when /etc/shadow is opened for writing.
  The audit message is per permission bit so only opening the file
  for read access will not, force an audit message.

  audit can also be used in block form instead of prepending audit
  to every rule.

  audit {
    /bin/foo rw,
    /etc/shadow w,
  }
  /etc/shadow r,	# don't audit r access to /etc/shadow


the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
  so that the tools don't prompt for what has been denied in
  previous profiling sessions.
- it subtracts globally from the allowed permissions.  Deny permissions
  accumulate in the the deny set just as allow permissions accumulate
  then, the deny set is subtracted from the allow set.
- it quiets known rejects.  The default audit behavior of deny rules
  is to quiet known rejects so that audit logs are not flooded
  with already known rejects.  To have known rejects logged prepend
  the audit keyword to the deny rule.  Deny rules do not have a
  block form.

eg.
  deny /foo/bar rw,
  audit deny /etc/shadow w,

  audit {
     deny owner /blah w,
     deny other /foo w,
     deny /etc/shadow w,
  }
This commit is contained in:
John Johansen 2008-03-13 17:39:03 +00:00
parent 36ad7de2c5
commit a3c0753b89
8 changed files with 308 additions and 108 deletions

View File

@ -845,21 +845,27 @@ DFA::~DFA()
class MatchFlag : public AcceptNode { class MatchFlag : public AcceptNode {
public: public:
MatchFlag(uint32_t flag) : flag(flag) {} MatchFlag(uint32_t flag, uint32_t audit) : flag(flag), audit(audit) {}
ostream& dump(ostream& os) ostream& dump(ostream& os)
{ {
return os << '<' << flag << '>'; return os << '<' << flag << '>';
} }
uint32_t flag; uint32_t flag;
uint32_t audit;
}; };
class ExactMatchFlag : public MatchFlag { class ExactMatchFlag : public MatchFlag {
public: public:
ExactMatchFlag(uint32_t flag) : MatchFlag(flag) {} ExactMatchFlag(uint32_t flag, uint32_t audit) : MatchFlag(flag, audit) {}
}; };
uint32_t accept_perms(State *state); class DenyMatchFlag : public MatchFlag {
public:
DenyMatchFlag(uint32_t flag, uint32_t quiet) : MatchFlag(flag, quiet) {}
};
uint32_t accept_perms(State *state, uint32_t *audit_ctl);
/** /**
* verify that there are no conflicting X permissions on the dfa * verify that there are no conflicting X permissions on the dfa
@ -869,7 +875,7 @@ uint32_t accept_perms(State *state);
State *DFA::verify_perms(void) State *DFA::verify_perms(void)
{ {
for (States::iterator i = states.begin(); i != states.end(); i++) { for (States::iterator i = states.begin(); i != states.end(); i++) {
uint32_t accept = accept_perms(*i); uint32_t accept = accept_perms(*i, NULL);
if (*i == start || accept) { if (*i == start || accept) {
if (accept & AA_ERROR_BIT) if (accept & AA_ERROR_BIT)
return *i; return *i;
@ -884,13 +890,14 @@ State *DFA::verify_perms(void)
void DFA::dump(ostream& os) void DFA::dump(ostream& os)
{ {
for (States::iterator i = states.begin(); i != states.end(); i++) { for (States::iterator i = states.begin(); i != states.end(); i++) {
uint32_t accept = accept_perms(*i); uint32_t accept, audit;
accept = accept_perms(*i, &audit);
if (*i == start || accept) { if (*i == start || accept) {
os << **i; os << **i;
if (*i == start) if (*i == start)
os << " <=="; os << " <==";
if (accept) { if (accept) {
os << " (0x" << hex << accept << dec << ')'; os << " (0x" << hex << accept << " " << audit << dec << ')';
} }
os << endl; os << endl;
} }
@ -923,7 +930,7 @@ void DFA::dump_dot_graph(ostream& os)
if (*i == start) { if (*i == start) {
os << "\t\tstyle=bold" << endl; os << "\t\tstyle=bold" << endl;
} }
uint32_t perms = accept_perms(*i); uint32_t perms = accept_perms(*i, NULL);
if (perms) { if (perms) {
os << "\t\tlabel=\"" << **i << "\\n(" os << "\t\tlabel=\"" << **i << "\\n("
<< perms << ")\"" << endl; << perms << ")\"" << endl;
@ -1096,6 +1103,7 @@ public:
private: private:
vector<uint32_t> accept; vector<uint32_t> accept;
vector<uint32_t> accept2;
DefaultBase default_base; DefaultBase default_base;
NextCheck next_check; NextCheck next_check;
map<const State *, size_t> num; map<const State *, size_t> num;
@ -1142,8 +1150,12 @@ TransitionTable::TransitionTable(DFA& dfa, map<uchar, uchar>& eq)
} }
accept.resize(dfa.states.size()); accept.resize(dfa.states.size());
for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) accept2.resize(dfa.states.size());
accept[num[*i]] = accept_perms(*i); for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
uint32_t audit_ctl;
accept[num[*i]] = accept_perms(*i, &audit_ctl);
accept2[num[*i]] = audit_ctl;
}
} }
/** /**
@ -1397,6 +1409,7 @@ void TransitionTable::flex_table(ostream& os, const char *name)
th.th_hsize = htonl(hsize); th.th_hsize = htonl(hsize);
th.th_ssize = htonl(hsize + th.th_ssize = htonl(hsize +
flex_table_size(accept.begin(), accept.end()) + flex_table_size(accept.begin(), accept.end()) +
flex_table_size(accept2.begin(), accept2.end()) +
(eq.size() ? (eq.size() ?
flex_table_size(equiv_vec.begin(), equiv_vec.end()) : 0) + flex_table_size(equiv_vec.begin(), equiv_vec.end()) : 0) +
flex_table_size(base_vec.begin(), base_vec.end()) + flex_table_size(base_vec.begin(), base_vec.end()) +
@ -1409,6 +1422,7 @@ void TransitionTable::flex_table(ostream& os, const char *name)
write_flex_table(os, YYTD_ID_ACCEPT, accept.begin(), accept.end()); write_flex_table(os, YYTD_ID_ACCEPT, accept.begin(), accept.end());
write_flex_table(os, YYTD_ID_ACCEPT2, accept2.begin(), accept2.end());
if (eq.size()) if (eq.size())
write_flex_table(os, YYTD_ID_EC, equiv_vec.begin(), equiv_vec.end()); write_flex_table(os, YYTD_ID_EC, equiv_vec.begin(), equiv_vec.end());
write_flex_table(os, YYTD_ID_BASE, base_vec.begin(), base_vec.end()); write_flex_table(os, YYTD_ID_BASE, base_vec.begin(), base_vec.end());
@ -1503,41 +1517,66 @@ static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
* have any exact matches, then they override the execute and safe * have any exact matches, then they override the execute and safe
* execute flags. * execute flags.
*/ */
uint32_t accept_perms(State *state) uint32_t accept_perms(State *state, uint32_t *audit_ctl)
{ {
uint32_t perms = 0, exact_match_perms = 0; uint32_t perms = 0, exact_match_perms = 0, audit = 0, exact_audit = 0,
quiet = 0, deny = 0;
for (State::iterator i = state->begin(); i != state->end(); i++) { for (State::iterator i = state->begin(); i != state->end(); i++) {
MatchFlag *match; MatchFlag *match;
if (!(match= dynamic_cast<MatchFlag *>(*i))) if (!(match= dynamic_cast<MatchFlag *>(*i)))
continue; continue;
if (dynamic_cast<ExactMatchFlag *>(match)) { if (dynamic_cast<ExactMatchFlag *>(match)) {
/* exact match only ever happens with x */
if (!is_merged_x_consistent(exact_match_perms, if (!is_merged_x_consistent(exact_match_perms,
match->flag)) match->flag))
exact_match_perms |= AA_ERROR_BIT; exact_match_perms |= AA_ERROR_BIT;
exact_match_perms |= match->flag; exact_match_perms |= match->flag;
exact_audit |= match->audit;
} else if (dynamic_cast<DenyMatchFlag *>(match)) {
deny |= match->flag;
quiet |= match->audit;
} else { } else {
if (!is_merged_x_consistent(perms, match->flag)) if (!is_merged_x_consistent(perms, match->flag))
perms |= AA_ERROR_BIT; perms |= AA_ERROR_BIT;
perms |= match->flag; perms |= match->flag;
audit |= match->audit;
} }
} }
//if (audit || quiet)
//fprintf(stderr, "perms: 0x%x, audit: 0x%x exact: 0x%x eaud: 0x%x deny: 0x%x quiet: 0x%x\n", perms, audit, exact_match_perms, exact_audit, deny, quiet);
perms |= exact_match_perms & perms |= exact_match_perms &
~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE); ~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE);
if (exact_match_perms & AA_USER_EXEC_TYPE) if (exact_match_perms & AA_USER_EXEC_TYPE) {
perms = (exact_match_perms & AA_USER_EXEC_TYPE) | perms = (exact_match_perms & AA_USER_EXEC_TYPE) |
(perms & ~AA_USER_EXEC_TYPE); (perms & ~AA_USER_EXEC_TYPE);
audit = (exact_audit & AA_USER_EXEC_TYPE) |
if (exact_match_perms & AA_OTHER_EXEC_TYPE) (audit & ~ AA_USER_EXEC_TYPE);
}
if (exact_match_perms & AA_OTHER_EXEC_TYPE) {
perms = (exact_match_perms & AA_OTHER_EXEC_TYPE) | perms = (exact_match_perms & AA_OTHER_EXEC_TYPE) |
(perms & ~AA_OTHER_EXEC_TYPE); (perms & ~AA_OTHER_EXEC_TYPE);
audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
(audit & ~AA_OTHER_EXEC_TYPE);
}
if (perms & AA_USER_EXEC & deny)
perms &= ~AA_USER_EXEC_TYPE;
if (perms & AA_ERROR_BIT) { if (perms & AA_OTHER_EXEC & deny)
fprintf(stderr, "error bit 0x%x\n", perms); perms &= ~AA_OTHER_EXEC_TYPE;
exit(255);
} perms &= ~deny;
if (audit_ctl)
*audit_ctl = PACK_AUDIT_CTL(audit, quiet & deny);
// if (perms & AA_ERROR_BIT) {
// fprintf(stderr, "error bit 0x%x\n", perms);
// exit(255);
//}
//if (perms & AA_EXEC_BITS) //if (perms & AA_EXEC_BITS)
//fprintf(stderr, "accept perm: 0x%x\n", perms); //fprintf(stderr, "accept perm: 0x%x\n", perms);
@ -1548,17 +1587,20 @@ uint32_t accept_perms(State *state)
return perms; return perms;
} }
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, uint32_t perms) extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
uint32_t perms, uint32_t audit)
{ {
return aare_add_rule_vec(rules, perms, 1, &rule); return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule);
} }
extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, uint32_t perms, extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
uint32_t perms, uint32_t audit,
int count, char **rulev) int count, char **rulev)
{ {
static MatchFlag *match_flags[sizeof(perms) * 8 - 1]; static MatchFlag *match_flags[2][sizeof(perms) * 8 - 1];
static MatchFlag *exec_match_flags[64 * 2]; /* mods + unsafe *u::o*/ static DenyMatchFlag *deny_flags[2][sizeof(perms) * 8 - 1];
static ExactMatchFlag *exact_match_flags[64 * 2]; /* mods + unsafe *u::o*/ static MatchFlag *exec_match_flags[2][(AA_EXEC_COUNT << 1) * 2]; /* mods + unsafe *u::o*/
static ExactMatchFlag *exact_match_flags[2][(AA_EXEC_COUNT << 1) * 2];/* mods + unsafe *u::o*/
Node *tree = NULL, *accept; Node *tree = NULL, *accept;
int exact_match; int exact_match;
@ -1595,58 +1637,73 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, uint32_t perms,
if (rules->reverse) if (rules->reverse)
flip_tree(tree); flip_tree(tree);
#define ALL_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE)
/* 0x3f == 5 bits x mods + 1 bit unsafe mask, after shift */
#define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 8)) & 0x3f) #define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 8)) & 0x3f)
if (perms & ALL_EXEC_TYPE && (!perms & AA_EXEC_BITS)) //if (perms & ALL_AA_EXEC_TYPE && (!perms & AA_EXEC_BITS))
fprintf(stderr, "adding X rule without MAY_EXEC: 0x%x %s\n", perms, rulev[0]); // fprintf(stderr, "adding X rule without MAY_EXEC: 0x%x %s\n", perms, rulev[0]);
//if (perms & ALL_EXEC_TYPE) //if (perms & ALL_EXEC_TYPE)
// fprintf(stderr, "adding X rule %s 0x%x\n", rulev[0], perms); // fprintf(stderr, "adding X rule %s 0x%x\n", rulev[0], perms);
//if (audit)
//fprintf(stderr, "adding rule with audit bits set: 0x%x %s\n", audit, rulev[0]);
/* the permissions set is assumed to be non-empty if any audit
* bits are specified */
accept = NULL; accept = NULL;
for (unsigned int n = 0; perms && n < (sizeof(perms) * 8) - 1; n++) { for (unsigned int n = 0; perms && n < (sizeof(perms) * 8) - 1; n++) {
uint32_t mask = 1 << n; uint32_t mask = 1 << n;
if (perms & mask) { if (perms & mask) {
int ai = audit & mask ? 1 : 0;
perms &= ~mask; perms &= ~mask;
Node *flag; Node *flag;
if (mask & AA_EXEC_BITS) { if (mask & ALL_AA_EXEC_TYPE)
/* these cases are covered by EXEC_BITS */
continue;
if (deny) {
if (deny_flags[ai][n]) {
flag = deny_flags[ai][n]->dup();
} else {
//fprintf(stderr, "Adding deny ai %d mask 0x%x audit 0x%x\n", ai, mask, audit & mask);
deny_flags[ai][n] = new DenyMatchFlag(mask, audit&mask);
flag = deny_flags[ai][n];
}
} else if (mask & AA_EXEC_BITS) {
uint32_t eperm = 0; uint32_t eperm = 0;
uint32_t index = 0; uint32_t index = 0;
if (mask & AA_USER_EXEC_TYPE) { if (mask & AA_USER_EXEC) {
eperm = mask | (perms & AA_USER_EXEC_TYPE); eperm = mask | (perms & AA_USER_EXEC_TYPE);
index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT); index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT);
} else { } else {
eperm = mask | (perms & AA_OTHER_EXEC_TYPE); eperm = mask | (perms & AA_OTHER_EXEC_TYPE);
index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + 16; index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + (AA_EXEC_COUNT << 1);
} }
//fprintf(stderr, "index %d eperm 0x%x\n", index, eperm); //fprintf(stderr, "index %d eperm 0x%x\n", index, eperm);
if (exact_match) { if (exact_match) {
if (exact_match_flags[index]) { if (exact_match_flags[ai][index]) {
flag = exact_match_flags[index]->dup(); flag = exact_match_flags[ai][index]->dup();
} else { } else {
exact_match_flags[index] = new ExactMatchFlag(eperm); exact_match_flags[ai][index] = new ExactMatchFlag(eperm, audit&mask);
flag = exact_match_flags[index]; flag = exact_match_flags[ai][index];
} }
} else { } else {
if (exec_match_flags[index]) { if (exec_match_flags[ai][index]) {
flag = exec_match_flags[index]->dup(); flag = exec_match_flags[ai][index]->dup();
} else { } else {
exec_match_flags[index] = new MatchFlag(eperm); exec_match_flags[ai][index] = new MatchFlag(eperm, audit&mask);
flag = exec_match_flags[index]; flag = exec_match_flags[ai][index];
} }
} }
} else if (mask & ALL_EXEC_TYPE) {
/* these cases are covered by EXEC_BITS */
continue;
} else { } else {
if (match_flags[n]) { if (match_flags[ai][n]) {
flag = match_flags[n]->dup(); flag = match_flags[ai][n]->dup();
} else { } else {
match_flags[n] = new MatchFlag(mask); match_flags[ai][n] = new MatchFlag(mask, audit&mask);
flag = match_flags[n]; flag = match_flags[ai][n];
} }
} }
if (accept) if (accept)

View File

@ -42,6 +42,7 @@ struct cod_entry {
struct codomain *codomain; /* Special codomain defined struct codomain *codomain; /* Special codomain defined
* just for this executable */ * just for this executable */
int mode; /* mode is 'or' of AA_* bits */ int mode; /* mode is 'or' of AA_* bits */
int audit; /* audit flags for mode */
int deny; /* TRUE or FALSE */ int deny; /* TRUE or FALSE */
int subset; int subset;
@ -72,8 +73,15 @@ struct codomain {
struct flagval flags; struct flagval flags;
unsigned int capabilities; unsigned int capabilities;
unsigned int audit_caps;
unsigned int deny_caps;
unsigned int quiet_caps;
unsigned int *network_allowed; /* array of type masks unsigned int *network_allowed; /* array of type masks
* indexed by AF_FAMILY */ * indexed by AF_FAMILY */
unsigned int *audit_network;
unsigned int *deny_network;
unsigned int *quiet_network;
struct cod_entry *entries; struct cod_entry *entries;
void *hat_table; void *hat_table;

View File

@ -58,7 +58,7 @@
#define SD_STR_LEN (sizeof(u16)) #define SD_STR_LEN (sizeof(u16))
#define SUBDOMAIN_INTERFACE_VERSION 2 #define SUBDOMAIN_INTERFACE_VERSION 2
#define SUBDOMAIN_INTERFACE_DFA_VERSION 3 #define SUBDOMAIN_INTERFACE_DFA_VERSION 4
int sd_serialize_codomain(int option, struct codomain *cod); int sd_serialize_codomain(int option, struct codomain *cod);
@ -206,7 +206,7 @@ struct __sdserialize {
sd_serialize *alloc_sd_serial(void) sd_serialize *alloc_sd_serial(void)
{ {
sd_serialize *p = malloc(sizeof(sd_serialize)); sd_serialize *p = calloc(1, sizeof(sd_serialize));
if (!p) if (!p)
return NULL; return NULL;
p->buffer = malloc(BUFFERINC); p->buffer = malloc(BUFFERINC);
@ -529,6 +529,7 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
int flattened) int flattened)
{ {
struct cod_entry *entry; struct cod_entry *entry;
u32 allowed_caps;
if (!sd_write_struct(p, "profile")) if (!sd_write_struct(p, "profile"))
return 0; return 0;
@ -560,7 +561,12 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
return 0; return 0;
if (!sd_write_structend(p)) if (!sd_write_structend(p))
return 0; return 0;
if (!sd_write32(p, profile->capabilities)) allowed_caps = profile->capabilities & ~profile->deny_caps;
if (!sd_write32(p, allowed_caps))
return 0;
if (!sd_write32(p, allowed_caps & profile->audit_caps))
return 0;
if (!sd_write32(p, profile->deny_caps & profile->quiet_caps))
return 0; return 0;
if (profile->network_allowed) { if (profile->network_allowed) {
@ -568,7 +574,13 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
if (!sd_write_array(p, "net_allowed_af", AF_MAX)) if (!sd_write_array(p, "net_allowed_af", AF_MAX))
return 0; return 0;
for (i = 0; i < AF_MAX; i++) { for (i = 0; i < AF_MAX; i++) {
if (!sd_write16(p, profile->network_allowed[i])) u16 allowed = profile->network_allowed[i] &
~profile->deny_network[i];
if (!sd_write16(p, allowed))
return 0;
if (!sd_write16(p, allowed & profile->audit_network[i]))
return 0;
if (!sd_write16(p, profile->deny_network[i] & profile->quiet_network[i]))
return 0; return 0;
} }
if (!sd_write_arrayend(p)) if (!sd_write_arrayend(p))

View File

@ -53,7 +53,7 @@ COLON :
END_OF_RULE [,] END_OF_RULE [,]
SEPERATOR {UP} SEPERATOR {UP}
RANGE - RANGE -
MODE_CHARS ([RrWwaLlMmk])|([Pp][Xx])|([Uu][Xx])|([Ii][Xx])|([Pp][Ii][Xx]) MODE_CHARS ([RrWwaLlMmkXx])|([Pp][Xx])|([Uu][Xx])|([Ii][Xx])|([Pp][Ii][Xx])
MODES {MODE_CHARS}+ MODES {MODE_CHARS}+
WS [[:blank:]] WS [[:blank:]]
NUMBER [[:digit:]]+ NUMBER [[:digit:]]+

View File

@ -64,6 +64,9 @@ static int file_comp(const void *c1, const void *c2)
if (res) if (res)
return res; return res;
if ((*e1)->deny != (*e2)->deny)
return (*e1)->deny < (*e2)->deny ? -1 : 1;
return strcmp((*e1)->name, (*e2)->name); return strcmp((*e1)->name, (*e2)->name);
} }
@ -105,7 +108,10 @@ static int process_file_entries(struct codomain *cod)
cod->name, cur->name); cod->name, cur->name);
return 0; return 0;
} }
//if (next->audit)
//fprintf(stderr, "warning: merging rule 0x%x %s\n", next->audit, next->name);
cur->mode |= next->mode; cur->mode |= next->mode;
cur->audit |= next->audit;
free(next->name); free(next->name);
if (next->link_name) if (next->link_name)
free(next->link_name); free(next->link_name);

View File

@ -62,7 +62,11 @@ static struct keyword_table keyword_table[] = {
{"unsafe", TOK_UNSAFE}, {"unsafe", TOK_UNSAFE},
{"link", TOK_LINK}, {"link", TOK_LINK},
{"owner", TOK_OWNER}, {"owner", TOK_OWNER},
{"user", TOK_OWNER},
{"other", TOK_OTHER},
{"subset", TOK_SUBSET}, {"subset", TOK_SUBSET},
{"audit", TOK_AUDIT},
{"deny", TOK_DENY},
/* terminate */ /* terminate */
{NULL, 0} {NULL, 0}
}; };
@ -520,8 +524,11 @@ reeval:
break; break;
case COD_EXEC_CHAR: case COD_EXEC_CHAR:
PDEBUG("Parsing mode: found %s EXEC\n", mode_desc); /* this is valid for deny rules, and named transitions
yyerror(_("Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'")); * but invalid for regular x transitions
* sort it out later.
*/
mode |= AA_MAY_EXEC;
break; break;
/* error cases */ /* error cases */
@ -572,7 +579,7 @@ struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id)
{ {
struct cod_entry *entry = NULL; struct cod_entry *entry = NULL;
entry = (struct cod_entry *)malloc(sizeof(struct cod_entry)); entry = (struct cod_entry *)calloc(1, sizeof(struct cod_entry));
if (!entry) if (!entry)
return NULL; return NULL;
@ -580,6 +587,7 @@ struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id)
entry->name = id; entry->name = id;
entry->link_name = link_id; entry->link_name = link_id;
entry->mode = mode; entry->mode = mode;
entry->audit = 0;
entry->deny = FALSE; entry->deny = FALSE;
entry->pattern_type = ePatternInvalid; entry->pattern_type = ePatternInvalid;
@ -596,7 +604,7 @@ struct cod_entry *copy_cod_entry(struct cod_entry *orig)
{ {
struct cod_entry *entry = NULL; struct cod_entry *entry = NULL;
entry = (struct cod_entry *)malloc(sizeof(struct cod_entry)); entry = (struct cod_entry *)calloc(1, sizeof(struct cod_entry));
if (!entry) if (!entry)
return NULL; return NULL;

View File

@ -503,8 +503,21 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
AA_EXEC_INHERIT) AA_EXEC_INHERIT)
entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT; entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT;
if (!aare_add_rule(dfarules, tbuf, entry->mode)) /* the link bit on the first pair entry should not get masked
return FALSE; * out by a deny rule, as both pieces of the link pair must
* match. audit info for the link is carried on the second
* entry of the pair
*/
if (entry->deny && (entry->mode & AA_LINK_BITS)) {
if (!aare_add_rule(dfarules, tbuf, entry->deny,
entry->mode & ~AA_LINK_BITS,
entry->audit & ~AA_LINK_BITS))
return FALSE;
} else {
if (!aare_add_rule(dfarules, tbuf, entry->deny, entry->mode,
entry->audit))
return FALSE;
}
if (entry->mode & (AA_LINK_BITS)) { if (entry->mode & (AA_LINK_BITS)) {
/* add the pair rule */ /* add the pair rule */
char lbuf[PATH_MAX + 8]; char lbuf[PATH_MAX + 8];
@ -522,7 +535,7 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
perms |= LINK_TO_LINK_SUBSET(perms); perms |= LINK_TO_LINK_SUBSET(perms);
vec[1] = "/[^/].*"; vec[1] = "/[^/].*";
} }
if (!aare_add_rule_vec(dfarules, perms, 2, vec)) if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec))
return FALSE; return FALSE;
} }
if (entry->mode & AA_CHANGE_PROFILE) { if (entry->mode & AA_CHANGE_PROFILE) {
@ -530,10 +543,10 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
char *vec[2]; char *vec[2];
vec[0] = entry->namespace; vec[0] = entry->namespace;
vec[1] = entry->name; vec[1] = entry->name;
if (!aare_add_rule_vec(dfarules, AA_CHANGE_PROFILE, 2, vec)) if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec))
return FALSE; return FALSE;
} else { } else {
if (!aare_add_rule(dfarules, entry->name, AA_CHANGE_PROFILE)) if (!aare_add_rule(dfarules, entry->name, 0, AA_CHANGE_PROFILE, 0))
return FALSE; return FALSE;
} }
} }

View File

@ -95,7 +95,10 @@ struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
%token TOK_COLON %token TOK_COLON
%token TOK_LINK %token TOK_LINK
%token TOK_OWNER %token TOK_OWNER
%token TOK_OTHER
%token TOK_SUBSET %token TOK_SUBSET
%token TOK_AUDIT
%token TOK_DENY
/* capabilities */ /* capabilities */
%token TOK_CAPABILITY %token TOK_CAPABILITY
@ -136,8 +139,6 @@ struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
%type <cod> cond_rule %type <cod> cond_rule
%type <network_entry> network_rule %type <network_entry> network_rule
%type <user_entry> rule %type <user_entry> rule
%type <user_entry> owner_rule
%type <user_entry> owner_rules
%type <flags> flags %type <flags> flags
%type <flags> flagvals %type <flags> flagvals
%type <flags> flagval %type <flags> flagval
@ -152,6 +153,8 @@ struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
%type <boolean> expr %type <boolean> expr
%type <id> id_or_var %type <id> id_or_var
%type <boolean> opt_subset_flag %type <boolean> opt_subset_flag
%type <boolean> opt_audit_flag
%type <boolean> opt_owner_flag
%% %%
@ -291,7 +294,7 @@ varassign: TOK_BOOL_VAR TOK_EQUALS TOK_VALUE
valuelist: TOK_VALUE valuelist: TOK_VALUE
{ {
struct value_list *new = malloc(sizeof(struct value_list)); struct value_list *new = calloc(1, sizeof(struct value_list));
if (!new) if (!new)
yyerror(_("Memory allocation error.")); yyerror(_("Memory allocation error."));
PDEBUG("Matched: value (%s)\n", $1); PDEBUG("Matched: value (%s)\n", $1);
@ -303,7 +306,7 @@ valuelist: TOK_VALUE
valuelist: valuelist TOK_VALUE valuelist: valuelist TOK_VALUE
{ {
struct value_list *new = malloc(sizeof(struct value_list)); struct value_list *new = calloc(1, sizeof(struct value_list));
if (!new) if (!new)
yyerror(_("Memory allocation error.")); yyerror(_("Memory allocation error."));
PDEBUG("Matched: value (%s)\n", $1); PDEBUG("Matched: value (%s)\n", $1);
@ -361,6 +364,13 @@ flagval: TOK_FLAG_ID
opt_subset_flag: { /* nothing */ $$ = 0; } opt_subset_flag: { /* nothing */ $$ = 0; }
| TOK_SUBSET { $$ = 1; } | TOK_SUBSET { $$ = 1; }
opt_audit_flag: { /* nothing */ $$ = 0; }
| TOK_AUDIT { $$ = 1; };
opt_owner_flag: { /* nothing */ $$ = 0; }
| TOK_OWNER { $$ = 1; };
| TOK_OTHER { $$ = 2; };
rules: { /* nothing */ rules: { /* nothing */
struct codomain *cod = NULL; struct codomain *cod = NULL;
cod = (struct codomain *) calloc(1, sizeof(struct codomain)); cod = (struct codomain *) calloc(1, sizeof(struct codomain));
@ -371,50 +381,151 @@ rules: { /* nothing */
$$ = cod; $$ = cod;
}; };
rules: rules rule /* can't fold TOK_DENY in as opt_deny_flag as it messes up the generated
* parser, even though it shouldn't
*/
rules: rules opt_audit_flag TOK_DENY opt_owner_flag rule
{ {
PDEBUG("matched: rules rule\n"); PDEBUG("matched: rules rule\n");
PDEBUG("rules rule: (%s)\n", $2->name); PDEBUG("rules rule: (%s)\n", $5->name);
if (!$2) if (!$5)
yyerror(_("Assert: `rule' returned NULL.")); yyerror(_("Assert: `rule' returned NULL."));
add_entry_to_policy($1, $2); $5->deny = 1;
if (($5->mode & AA_EXEC_BITS) && ($5->mode & ALL_AA_EXEC_TYPE))
yyerror(_("Invalid mode, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'"));
if ($4 == 1)
$5->mode &= (AA_USER_PERMS | AA_SHARED_PERMS);
else if ($4 == 2)
$5->mode &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
/* only set audit ctl quieting if the rule is not audited */
if (!$2)
$5->audit = $5->mode & ~ALL_AA_EXEC_TYPE;
add_entry_to_policy($1, $5);
$$ = $1; $$ = $1;
}; };
rules: rules TOK_OWNER owner_rule rules: rules opt_audit_flag opt_owner_flag rule
{
PDEBUG("matched: rules rule\n");
PDEBUG("rules rule: (%s)\n", $4->name);
if (!$4)
yyerror(_("Assert: `rule' returned NULL."));
if (($4->mode & AA_EXEC_BITS) && !($4->mode & ALL_AA_EXEC_TYPE))
yyerror(_("Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'"));
if ($3 == 1)
$4->mode &= (AA_USER_PERMS | AA_SHARED_PERMS);
else if ($3 == 2)
$4->mode &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
if ($2)
$4->audit = $4->mode & ~ALL_AA_EXEC_TYPE;
add_entry_to_policy($1, $4);
$$ = $1;
};
rules: rules opt_audit_flag opt_owner_flag TOK_OPEN rules TOK_CLOSE
{ {
struct cod_entry *entry, *tmp; struct cod_entry *entry, *tmp;
PDEBUG("matched: audit block\n");
PDEBUG("matched: rules owner_rules\n"); list_for_each_safe($5->entries, entry, tmp) {
PDEBUG("rules owner_rules: (%s)\n", $3->name); entry->next = NULL;
if ($3) { if (entry->mode & AA_EXEC_BITS) {
list_for_each_safe($3, entry, tmp) { if (entry->deny &&
entry->next = NULL; (entry->mode & ALL_AA_EXEC_TYPE))
add_entry_to_policy($1, entry); yyerror(_("Invalid mode, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'"));
else if (!entry->deny &&
!(entry->mode & ALL_AA_EXEC_TYPE))
yyerror(_("Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'"));
} }
if ($3 == 1)
entry->mode &= (AA_USER_PERMS | AA_SHARED_PERMS);
else if ($3 == 2)
entry->mode &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
if ($2 && !entry->deny)
entry->audit = entry->mode & ~ALL_AA_EXEC_TYPE;
else if (!$2 && entry->deny)
entry->audit = entry->mode & ~ALL_AA_EXEC_TYPE;
add_entry_to_policy($1, entry);
} }
$5->entries = NULL;
// fix me transfer rules and free sub codomain
free_policy($5);
$$ = $1; $$ = $1;
}; };
rules: rules network_rule rules: rules opt_audit_flag TOK_DENY network_rule
{ {
struct aa_network_entry *entry, *tmp; struct aa_network_entry *entry, *tmp;
PDEBUG("Matched: network rule\n"); PDEBUG("Matched: network rule\n");
if (!$2) if (!$4)
yyerror(_("Assert: `network_rule' return invalid protocol.")); yyerror(_("Assert: `network_rule' return invalid protocol."));
if (!$1->network_allowed) { if (!$1->network_allowed) {
$1->network_allowed = calloc(AF_MAX, $1->network_allowed = calloc(AF_MAX,
sizeof(unsigned int)); sizeof(unsigned int));
if (!$1->network_allowed) $1->audit_network = calloc(AF_MAX,
sizeof(unsigned int));
$1->deny_network = calloc(AF_MAX,
sizeof(unsigned int));
$1->quiet_network = calloc(AF_MAX,
sizeof(unsigned int));
if (!$1->network_allowed || !$1->audit_network ||
!$1->deny_network || !$1->quiet_network)
yyerror(_("Memory allocation error.")); yyerror(_("Memory allocation error."));
} }
list_for_each_safe($2, entry, tmp) { list_for_each_safe($4, entry, tmp) {
if (entry->type > SOCK_PACKET) {
/* setting mask instead of a bit */
$1->deny_network[entry->family] |= entry->type;
if (!$2)
$1->quiet_network[entry->family] |= entry->type;
} else {
$1->deny_network[entry->family] |= 1 << entry->type;
if (!$2)
$1->quiet_network[entry->family] |= 1 << entry->type;
}
free(entry);
}
$$ = $1
}
rules: rules opt_audit_flag network_rule
{
struct aa_network_entry *entry, *tmp;
PDEBUG("Matched: network rule\n");
if (!$3)
yyerror(_("Assert: `network_rule' return invalid protocol."));
if (!$1->network_allowed) {
$1->network_allowed = calloc(AF_MAX,
sizeof(unsigned int));
$1->audit_network = calloc(AF_MAX,
sizeof(unsigned int));
$1->deny_network = calloc(AF_MAX,
sizeof(unsigned int));
$1->quiet_network = calloc(AF_MAX,
sizeof(unsigned int));
if (!$1->network_allowed || !$1->audit_network ||
!$1->deny_network || !$1->quiet_network)
yyerror(_("Memory allocation error."));
}
list_for_each_safe($3, entry, tmp) {
if (entry->type > SOCK_PACKET) { if (entry->type > SOCK_PACKET) {
/* setting mask instead of a bit */ /* setting mask instead of a bit */
$1->network_allowed[entry->family] |= entry->type; $1->network_allowed[entry->family] |= entry->type;
if ($2)
$1->audit_network[entry->family] |= entry->type;
} else { } else {
$1->network_allowed[entry->family] |= 1 << entry->type; $1->network_allowed[entry->family] |= 1 << entry->type;
if ($2)
$1->audit_network[entry->family] |= 1 << entry->type;
} }
free(entry); free(entry);
} }
@ -432,9 +543,19 @@ rules: rules change_profile
$$ = $1; $$ = $1;
}; };
rules: rules capability rules: rules opt_audit_flag TOK_DENY capability
{ {
$1->capabilities = $1->capabilities | $2; $1->deny_caps |= $4;
if (!$2)
$1->quiet_caps |= $4;
$$ = $1;
};
rules: rules opt_audit_flag capability
{
$1->capabilities |= $3;
if ($2)
$1->audit_caps |= $3;
$$ = $1; $$ = $1;
}; };
@ -536,31 +657,6 @@ expr: TOK_DEFINED TOK_BOOL_VAR
id_or_var: TOK_ID { $$ = $1; } id_or_var: TOK_ID { $$ = $1; }
id_or_var: TOK_SET_VAR { $$ = $1; }; id_or_var: TOK_SET_VAR { $$ = $1; };
owner_rule: TOK_OPEN owner_rules TOK_CLOSE
{
$$ = $2;
};
owner_rule: rule
{
/* mask mode to owner permissions */
if ($1) {
$1->mode &= (AA_USER_PERMS | AA_SHARED_PERMS);
}
$$ = $1;
};
owner_rules: { $$ = NULL; };
owner_rules: owner_rules rule
{
if ($2) {
$2->mode &= (AA_USER_PERMS | AA_SHARED_PERMS);
$2->next = $1;
}
$$ = $2;
};
rule: id_or_var file_mode TOK_END_OF_RULE rule: id_or_var file_mode TOK_END_OF_RULE
{ {
$$ = do_file_rule(NULL, $1, $2, NULL); $$ = do_file_rule(NULL, $1, $2, NULL);
@ -680,7 +776,7 @@ hat_start: TOK_SEP {}
file_mode: TOK_MODE file_mode: TOK_MODE
{ {
/* A single TOK_MODE maps to the same permission in all /* A single TOK_MODE maps to the same permission in all
* of user:group:other */ * of user::other */
$$ = parse_mode($1); $$ = parse_mode($1);
free($1); free($1);
} }
@ -709,7 +805,7 @@ change_profile: TOK_CHANGE_PROFILE TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
capability: TOK_CAPABILITY caps TOK_END_OF_RULE capability: TOK_CAPABILITY caps TOK_END_OF_RULE
{ {
$$ = $2; $$ = $2;
}; };
caps: caps TOK_ID caps: caps TOK_ID