Add signal rules and make sure the parser encodes support for them if the supported feature set reports supporting them.
The current format of the signal rule is [audit] [deny] signal [<signal_perms>] [<signal_set>] <target_profile>, signal_perm := 'send'|'receive'|'r'|'w'|'rw' signal_perms := <signal_perm> | '(' <signal_perm> ([,]<signal_perm>)* ')' signal := ("hup"|"int"|"quit"|"ill"|"trap"|"abrt"|"bus"|"fpe"|"kill"| "usr1"|"segv"|"usr2"|"pipe"|"alrm"|"term"|"tkflt"|"chld"| "cont"|"stop"|"stp"|"ttin"|"ttou"|"urg"|"xcpu"|"xfsz"|"vtalrm"| "prof"|"winch"|"io"|"pwr"|"sys"|"emt"|"exists") signal_set := set=<signal> | '(' <signal> ([,]<signal>)* ')' it does not currently follow the peer=() format, and there is some question as to whether it should or not. Input welcome. --- parser/Makefile | 8 + parser/parser.h | 5 parser/parser_common.c | 1 parser/parser_lex.l | 22 ++- parser/parser_main.c | 2 parser/parser_misc.c | 1 parser/parser_regex.c | 7 - parser/parser_yacc.y | 68 +++++++++++ parser/policydb.h | 1 parser/signal.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++ parser/signal.h | 58 +++++++++ 11 files changed, 451 insertions(+), 12 deletions(-) --- 2.9-test.orig/parser/Makefile +++ 2.9-test/parser/Makefile @@ -79,8 +79,9 @@ 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 mount.c dbus.c lib.c profile.cc rule.c -HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h rule.h + parser_alias.c mount.c dbus.c lib.c profile.cc rule.c signal.c +HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \ + rule.h signal.h TOOLS = apparmor_parser OBJECTS = $(SRCS:.c=.o) @@ -239,6 +240,9 @@ dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H) $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< +signal.o: signal.c signal.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H) + $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< + profile.o: profile.cc profile.h parser.h $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< --- 2.9-test.orig/parser/parser.h +++ 2.9-test/parser/parser.h @@ -300,6 +300,7 @@ extern int kernel_supports_policydb; extern int kernel_supports_mount; extern int kernel_supports_dbus; +extern int kernel_supports_signal; extern int conf_verbose; extern int conf_quiet; extern int names_only; @@ -331,7 +332,9 @@ extern const char *basedir; /* parser_regex.c */ -extern const char *default_match_pattern; +#define default_match_pattern "[^\\000]*" +#define anyone_match_pattern "[^\\000]+" + extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, std::string& pcre, int *first_re_pos); extern int build_list_val_expr(std::string& buffer, struct value_list *list); --- 2.9-test.orig/parser/parser_common.c +++ 2.9-test/parser/parser_common.c @@ -70,6 +70,7 @@ 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 */ +int kernel_supports_signal = 0; /* kernel supports signal rules */ int conf_verbose = 0; int conf_quiet = 0; int names_only = 0; --- 2.9-test.orig/parser/parser_lex.l +++ 2.9-test/parser/parser_lex.l @@ -253,6 +253,7 @@ %x RLIMIT_MODE %x MOUNT_MODE %x DBUS_MODE +%x SIGNAL_MODE %x CHANGE_PROFILE_MODE %x INCLUDE @@ -267,7 +268,7 @@ } %} -<INITIAL,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE>{ +<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>{ {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } } @@ -293,7 +294,7 @@ yyterminate(); } -<INITIAL,MOUNT_MODE,DBUS_MODE>{ +<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE>{ {VARIABLE_NAME}/{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 @@ -462,12 +463,15 @@ } <DBUS_MODE>{ + bind { RETURN_TOKEN(TOK_BIND); } + eavesdrop { RETURN_TOKEN(TOK_EAVESDROP); } +} + +<DBUS_MODE,SIGNAL_MODE>{ send { RETURN_TOKEN(TOK_SEND); } receive { RETURN_TOKEN(TOK_RECEIVE); } - bind { RETURN_TOKEN(TOK_BIND); } read { RETURN_TOKEN(TOK_READ); } write { RETURN_TOKEN(TOK_WRITE); } - eavesdrop { RETURN_TOKEN(TOK_EAVESDROP); } {OPEN_PAREN} { yy_push_state(LIST_VAL_MODE); RETURN_TOKEN(TOK_OPENPAREN); @@ -478,7 +482,7 @@ } } -<MOUNT_MODE,DBUS_MODE>{ +<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE>{ {ARROW} { RETURN_TOKEN(TOK_ARROW); } ({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) { @@ -567,13 +571,16 @@ case TOK_DBUS: state = DBUS_MODE; break; + case TOK_SIGNAL: + state = SIGNAL_MODE; + break; default: /* nothing */ break; } PUSH_AND_RETURN(state, token); } -<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE>{ +<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE>{ {END_OF_RULE} { if (YY_START != INITIAL) yy_pop_state(); @@ -586,7 +593,7 @@ } } -<INITIAL,SUB_ID,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE>{ +<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>{ [^\n] { DUMP_PREPROCESS; /* Something we didn't expect */ @@ -613,6 +620,7 @@ STATE_TABLE_ENT(RLIMIT_MODE), STATE_TABLE_ENT(MOUNT_MODE), STATE_TABLE_ENT(DBUS_MODE), + STATE_TABLE_ENT(SIGNAL_MODE), STATE_TABLE_ENT(CHANGE_PROFILE_MODE), STATE_TABLE_ENT(INCLUDE), }; --- 2.9-test.orig/parser/parser_main.c +++ 2.9-test/parser/parser_main.c @@ -847,6 +847,8 @@ kernel_supports_mount = 1; if (strstr(features_string, "dbus")) kernel_supports_dbus = 1; + if (strstr(features_string, "signal")) + kernel_supports_signal = 1; } int process_binary(int option, const char *profilename) --- 2.9-test.orig/parser/parser_misc.c +++ 2.9-test/parser/parser_misc.c @@ -142,6 +142,7 @@ {"pivot_root", TOK_PIVOTROOT}, {"in", TOK_IN}, {"dbus", TOK_DBUS}, + {"signal", TOK_SIGNAL}, {"send", TOK_SEND}, {"receive", TOK_RECEIVE}, {"bind", TOK_BIND}, --- 2.9-test.orig/parser/parser_regex.c +++ 2.9-test/parser/parser_regex.c @@ -43,8 +43,6 @@ e_parse_error, }; -/* match any char except \000 0 or more times */ -const char *default_match_pattern = "[^\\000]*"; /* Filters out multiple slashes (except if the first two are slashes, * that's a distinct namespace in linux) and trailing slashes. @@ -675,6 +673,7 @@ 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); int process_profile_policydb(Profile *prof) { @@ -701,6 +700,10 @@ if (kernel_supports_dbus && !prof->policy.rules->add_rule(mediates_dbus, 0, AA_MAY_READ, 0, dfaflags)) goto out; + if (kernel_supports_signal && + !prof->policy.rules->add_rule(mediates_signal, 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); delete prof->policy.rules; --- 2.9-test.orig/parser/parser_yacc.y +++ 2.9-test/parser/parser_yacc.y @@ -28,6 +28,8 @@ #include <fcntl.h> #include <libintl.h> #include <sys/apparmor.h> + +#include <iostream> #define _(s) gettext(s) /* #define DEBUG */ @@ -128,6 +130,7 @@ %token TOK_PIVOTROOT %token TOK_IN %token TOK_DBUS +%token TOK_SIGNAL %token TOK_SEND %token TOK_RECEIVE %token TOK_BIND @@ -166,6 +169,7 @@ #include "profile.h" #include "mount.h" #include "dbus.h" + #include "signal.h" } %union { @@ -179,6 +183,7 @@ mnt_rule *mnt_entry; dbus_rule *dbus_entry; + signal_rule *signal_entry; flagvals flags; int fmode; @@ -241,6 +246,10 @@ %type <fmode> dbus_perms %type <fmode> opt_dbus_perm %type <dbus_entry> dbus_rule +%type <fmode> signal_perm +%type <fmode> signal_perms +%type <fmode> opt_signal_perm +%type <signal_entry> signal_rule %type <transition> opt_named_transition %type <boolean> opt_unsafe %type <boolean> opt_file @@ -698,6 +707,22 @@ $$ = $1; } +rules: rules opt_prefix signal_rule + { + if ($2.owner) + yyerror(_("owner prefix not allowed on signal 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"); @@ -1214,6 +1239,49 @@ $$ = ent; } +signal_perm: TOK_VALUE + { + if (strcmp($1, "send") == 0 || strcmp($1, "write") == 0) + $$ = AA_MAY_SEND; + else if (strcmp($1, "receive") == 0 || strcmp($1, "read") == 0) + $$ = AA_MAY_RECEIVE; + else if ($1) { + parse_signal_mode($1, &$$, 1); + } else + $$ = 0; + + if ($1) + free($1); + } + | TOK_SEND { $$ = AA_MAY_SEND; } + | TOK_RECEIVE { $$ = AA_MAY_RECEIVE; } + | TOK_READ { $$ = AA_MAY_RECEIVE; } + | TOK_WRITE { $$ = AA_MAY_SEND; } + | TOK_MODE + { + parse_signal_mode($1, &$$, 1); + free($1); + } + +signal_perms: { /* nothing */ $$ = 0; } + | signal_perms signal_perm { $$ = $1 | $2; } + | signal_perms TOK_COMMA signal_perm { $$ = $1 | $3; } + +opt_signal_perm: { /* nothing */ $$ = 0; } + | signal_perm { $$ = $1; } + | TOK_OPENPAREN signal_perms TOK_CLOSEPAREN { $$ = $2; } + +signal_rule: TOK_SIGNAL opt_signal_perm opt_conds TOK_END_OF_RULE + { + signal_rule *ent = new signal_rule($2, $3, NULL); + $$ = ent; + } + | TOK_SIGNAL opt_signal_perm opt_conds TOK_ID TOK_END_OF_RULE + { + signal_rule *ent = new signal_rule($2, $3, $4); + $$ = ent; + } + hat_start: TOK_CARET {} | TOK_HAT {} --- 2.9-test.orig/parser/policydb.h +++ 2.9-test/parser/policydb.h @@ -31,6 +31,7 @@ #define AA_CLASS_MOUNT 7 #define AA_CLASS_NS_DOMAIN 8 #define AA_CLASS_PTRACE 9 +#define AA_CLASS_SIGNAL 10 #define AA_CLASS_ENV 16 --- /dev/null +++ 2.9-test/parser/signal.c @@ -0,0 +1,290 @@ +/* + * 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 <sstream> +#include <map> + +#include "parser.h" +#include "profile.h" +#include "parser_yacc.h" +#include "signal.h" + +#define MAXMAPPED_SIG 35 +#define MINRT_SIG 128 /* base of RT sigs */ +#define MAXRT_SIG 32 /* Max RT above MINRT_SIG */ + +/* Signal names mapped to and internal ordering */ +static struct signal_map { const char *name; int num; } signal_map[] = { + {"hup", 1}, + {"int", 2}, + {"quit", 3}, + {"ill", 4}, + {"trap", 5}, + {"abrt", 6}, + {"bus", 7}, + {"fpe", 8}, + {"kill", 9}, + {"usr1", 10}, + {"segv", 11}, + {"usr2", 12}, + {"pipe", 13}, + {"alrm", 14}, + {"term", 15}, + {"tkflt", 16}, + {"chld", 17}, + {"cont", 18}, + {"stop", 19}, + {"stp", 20}, + {"ttin", 21}, + {"ttou", 22}, + {"urg", 23}, + {"xcpu", 24}, + {"xfsz", 25}, + {"vtalrm", 26}, + {"prof", 27}, + {"winch", 28}, + {"io", 29}, + {"pwr", 30}, + {"sys", 31}, + {"emt", 32}, + {"exists", 35}, + + /* terminate */ + {NULL, 0} +}; + +/* this table is ordered post sig_map[sig] mapping */ +static const char *const sig_names[MAXMAPPED_SIG + 1] = { + "unknown", + "hup", + "int", + "quit", + "ill", + "trap", + "abrt", + "bus", + "fpe", + "kill", + "usr1", + "segv", + "usr2", + "pipe", + "alrm", + "term", + "tkflt", + "chld", + "cont", + "stop", + "stp", + "ttin", + "ttou", + "urg", + "xcpu", + "xfsz", + "vtalrm", + "prof", + "winch", + "io", + "pwr", + "sys", + "emt", + "lost", + "unused", + + "exists", /* always last existance test mapped to MAXMAPPED_SIG */ +}; + + +int parse_signal_mode(const char *str_mode, int *mode, int fail) +{ + return parse_X_mode("signal", AA_VALID_SIGNAL_PERMS, str_mode, mode, fail); +} + +static int find_signal_mapping(const char *sig) +{ + if (strncmp("rtmin+", sig, 6) == 0) { + char *end; + unsigned long n = strtoul(sig + 6, &end, 10); + if (end == sig || n > MAXRT_SIG) + return -1; + return MINRT_SIG + n; + } else { + for (int i = 0; signal_map[i].name; i++) { + if (strcmp(sig, signal_map[i].name) == 0) + return signal_map[i].num; + } + } + return -1; +} + +void signal_rule::extract_sigs(struct value_list **list) +{ + struct value_list *entry, *tmp, *prev = NULL; + list_for_each_safe(*list, entry, tmp) { + int i = find_signal_mapping(entry->value); + if (i != -1) { + signals.insert(i); + list_remove_at(*list, prev, entry); + free_value_list(entry); + } else { + yyerror("unknown signal \"%s\"\n", entry->value); + prev = entry; + } + } +} + +void signal_rule::move_conditionals(struct cond_entry *conds) +{ + struct cond_entry *cond_ent; + + list_for_each(conds, cond_ent) { + /* for now disallow keyword 'in' (list) */ + if (!cond_ent->eq) + yyerror("keyword \"in\" is not allowed in signal rules\n"); + /* use alternation instead???? + * set=(kill,usr1) vs. set={kill,usr1} + */ + if (strcmp(cond_ent->name, "set") == 0) { + extract_sigs(&cond_ent->vals); + } else { + yyerror("invalid signal rule conditional \"%s\"\n", + cond_ent->name); + } + } +} + +signal_rule::signal_rule(int mode_p, struct cond_entry *conds, + char *peer): + signals(), peer_label(peer), audit(0), deny(0) +{ + if (mode_p) { + mode = mode_p; + if (mode & ~AA_VALID_SIGNAL_PERMS) + yyerror("mode contains invalid permission for signals\n"); + } else { + mode = AA_VALID_SIGNAL_PERMS; + } + + move_conditionals(conds); + + free_cond_list(conds); +} + +ostream &signal_rule::dump(ostream &os) +{ + if (audit) + os << "audit "; + if (deny) + os << "deny "; + + os << "signal"; + + if (mode != AA_VALID_SIGNAL_PERMS) { + os << " ("; + + if (mode & AA_MAY_SEND) + os << "send "; + if (mode & AA_MAY_RECEIVE) + os << "receive "; + os << ")"; + } + + if (!signals.empty()) { + os << " set=("; + for (Signals::iterator i = signals.begin(); i != signals.end(); + i++) { + if (i != signals.begin()) + os << ", "; + if (*i < MINRT_SIG) + os << sig_names[*i]; + else + os << "rtmin+" << (*i - MINRT_SIG); + } + os << ")"; + } + + if (peer_label) + os << " " << peer_label; + + os << ",\n"; + + return os; +} + +int signal_rule::expand_variables(void) +{ + return expand_entry_variables(&peer_label); +} + +int signal_rule::gen_policy_re(Profile &prof) +{ + std::ostringstream buffer; + std::string buf; + + pattern_t ptype; + int pos; + + /* ?? do we want to generate the rules in the policy so that it + * the compile could be used on another kernel unchanged?? + * Current caching doesn't support this but in the future maybe + */ + if (!kernel_supports_signal) { + pwarn("profile %s signal rules not enforced\n", prof.name); + return RULE_NOT_SUPPORTED; + } + + buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_SIGNAL; + + if (signals.size()) { + buffer << "["; + for (Signals::iterator i = signals.begin(); i != signals.end(); i++) { + buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << *i; + } + buffer << "]"; + } else { + /* match any char */ + buffer << "."; + } + + 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 (mode & (AA_MAY_SEND | AA_MAY_RECEIVE)) { + if (!prof.policy.rules->add_rule(buf.c_str(), deny, mode, audit, + dfaflags)) + goto fail; + } + + return RULE_OK; + +fail: + return RULE_ERROR; +} --- /dev/null +++ 2.9-test/parser/signal.h @@ -0,0 +1,58 @@ +/* + * 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_SIGNAL_H +#define __AA_SIGNAL_H + +#include "parser.h" +#include "rule.h" +#include "profile.h" + + +#define AA_MAY_SEND (1 << 1) +#define AA_MAY_RECEIVE (1 << 2) +#define AA_VALID_SIGNAL_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE) + + +typedef set<int> Signals; + +int parse_signal_mode(const char *str_mode, int *mode, int fail); + +class signal_rule: public rule_t { + void extract_sigs(struct value_list **list); + void move_conditionals(struct cond_entry *conds); +public: + Signals signals; + char *peer_label; + int mode; + int audit; + int deny; + + signal_rule(int mode, struct cond_entry *conds, char *peer); + virtual ~signal_rule() { + signals.clear(); + free(peer_label); + }; + + virtual ostream &dump(ostream &os); + virtual int expand_variables(void); + virtual int gen_policy_re(Profile &prof); + virtual void post_process(Profile &prof __unused) { }; +}; + +#endif /* __AA_SIGNAL_H */ -- AppArmor mailing list AppArmor@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor