From a1a7c7875560323b80e215ad6577f3aa595cc49f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 23 Apr 2014 11:38:04 -0700 Subject: [PATCH] Add the ability to specify ptrace rules ptrace rules currently take the form of ptrace [] [], ptrace_perm := read|trace|readby|tracedby ptrace_perms := ptrace_perm | '(' ptrace_perm+ ')' After having used the cross check (permission needed in both profiles) I am not sure it is correct for ptrace. Signed-off-by: John Johansen Acked-by: Seth Arnold --- parser/Makefile | 7 +- parser/parser.h | 1 + parser/parser_common.c | 1 + parser/parser_lex.l | 25 +++++-- parser/parser_main.c | 2 + parser/parser_misc.c | 3 + parser/parser_regex.c | 4 ++ parser/parser_yacc.y | 73 +++++++++++++++++++ parser/ptrace.c | 159 +++++++++++++++++++++++++++++++++++++++++ parser/ptrace.h | 52 ++++++++++++++ 10 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 parser/ptrace.c create mode 100644 parser/ptrace.h diff --git a/parser/Makefile b/parser/Makefile index 1d1b946ba..aa1038f3a 100644 --- a/parser/Makefile +++ b/parser/Makefile @@ -80,9 +80,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 common_optarg.c \ - signal.c + signal.c ptrace.c HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \ - rule.h common_optarg.h signal.h + rule.h common_optarg.h signal.h ptrace.h TOOLS = apparmor_parser OBJECTS = $(SRCS:.c=.o) @@ -248,6 +248,9 @@ dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H) signal.o: signal.c signal.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H) $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< +ptrace.o: ptrace.c ptrace.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 $@ $< diff --git a/parser/parser.h b/parser/parser.h index 8bc97c177..ad4e11c61 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -305,6 +305,7 @@ extern int kernel_supports_diff_encode; extern int kernel_supports_mount; extern int kernel_supports_dbus; extern int kernel_supports_signal; +extern int kernel_supports_ptrace; extern int conf_verbose; extern int conf_quiet; extern int names_only; diff --git a/parser/parser_common.c b/parser/parser_common.c index 829c04fea..bfd297a79 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -72,6 +72,7 @@ int kernel_supports_mount = 0; /* kernel supports mount rules */ int kernel_supports_dbus = 0; /* kernel supports dbus rules */ int kernel_supports_diff_encode = 0; /* kernel supports diff_encode */ int kernel_supports_signal = 0; /* kernel supports signal rules */ +int kernel_supports_ptrace = 0; /* kernel supports ptrace rules */ int conf_verbose = 0; int conf_quiet = 0; int names_only = 0; diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 6d5df300f..59b79fe83 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -260,6 +260,7 @@ LT_EQUAL <= %x MOUNT_MODE %x DBUS_MODE %x SIGNAL_MODE +%x PTRACE_MODE %x CHANGE_PROFILE_MODE %x INCLUDE @@ -274,7 +275,7 @@ LT_EQUAL <= } %} -{ +{ {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } } @@ -300,7 +301,7 @@ LT_EQUAL <= yyterminate(); } -{ +{ 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 @@ -478,6 +479,15 @@ LT_EQUAL <= { send { RETURN_TOKEN(TOK_SEND); } receive { RETURN_TOKEN(TOK_RECEIVE); } +} + +{ + trace { RETURN_TOKEN(TOK_TRACE); } + readby { RETURN_TOKEN(TOK_READBY); } + tracedby { RETURN_TOKEN(TOK_TRACEDBY); } +} + +{ read { RETURN_TOKEN(TOK_READ); } write { RETURN_TOKEN(TOK_WRITE); } {OPEN_PAREN} { @@ -489,9 +499,11 @@ LT_EQUAL <= } } -{ +{ {ARROW} { RETURN_TOKEN(TOK_ARROW); } +} +{ ({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) { yylval.id = processid(yytext, yyleng); RETURN_TOKEN(TOK_ID); @@ -581,13 +593,15 @@ LT_EQUAL <= case TOK_SIGNAL: state = SIGNAL_MODE; break; + case TOK_PTRACE: + state = PTRACE_MODE; default: /* nothing */ break; } PUSH_AND_RETURN(state, token); } -{ +{ {END_OF_RULE} { if (YY_START != INITIAL) POP_NODUMP(); @@ -600,7 +614,7 @@ LT_EQUAL <= } } -{ +{ [^\n] { DUMP_PREPROCESS; /* Something we didn't expect */ @@ -628,6 +642,7 @@ unordered_map state_names = { STATE_TABLE_ENT(MOUNT_MODE), STATE_TABLE_ENT(DBUS_MODE), STATE_TABLE_ENT(SIGNAL_MODE), + STATE_TABLE_ENT(PTRACE_MODE), STATE_TABLE_ENT(CHANGE_PROFILE_MODE), STATE_TABLE_ENT(INCLUDE), }; diff --git a/parser/parser_main.c b/parser/parser_main.c index a0968705d..2d318e700 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -697,6 +697,8 @@ static void set_supported_features(void) { kernel_supports_dbus = 1; if (strstr(features_string, "signal")) kernel_supports_signal = 1; + if (strstr(features_string, "ptrace {")) + kernel_supports_ptrace = 1; if (strstr(features_string, "diff_encode")) kernel_supports_diff_encode = 1; else if (dfaflags & DFA_CONTROL_DIFF_ENCODE) diff --git a/parser/parser_misc.c b/parser/parser_misc.c index eaf88eb62..a7441eb5c 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -151,6 +151,9 @@ static struct keyword_table keyword_table[] = { {"write", TOK_WRITE}, {"eavesdrop", TOK_EAVESDROP}, {"peer", TOK_PEER}, + {"trace", TOK_TRACE}, + {"tracedby", TOK_TRACEDBY}, + {"readby", TOK_READBY}, /* terminate */ {NULL, 0} diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 973582909..ea04dfe27 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -674,6 +674,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); +static const char *mediates_ptrace = CLASS_STR(AA_CLASS_PTRACE); int process_profile_policydb(Profile *prof) { @@ -703,6 +704,9 @@ int process_profile_policydb(Profile *prof) if (kernel_supports_signal && !prof->policy.rules->add_rule(mediates_signal, 0, AA_MAY_READ, 0, dfaflags)) goto out; + if (kernel_supports_ptrace && + !prof->policy.rules->add_rule(mediates_ptrace, 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); diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index 6ae3b5b22..ca6610c6b 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -138,6 +138,9 @@ void add_local_entry(Profile *prof); %token TOK_WRITE %token TOK_EAVESDROP %token TOK_PEER +%token TOK_TRACE +%token TOK_TRACEDBY +%token TOK_READBY /* rlimits */ %token TOK_RLIMIT @@ -171,6 +174,7 @@ void add_local_entry(Profile *prof); #include "mount.h" #include "dbus.h" #include "signal.h" + #include "ptrace.h" } %union { @@ -185,6 +189,7 @@ void add_local_entry(Profile *prof); mnt_rule *mnt_entry; dbus_rule *dbus_entry; signal_rule *signal_entry; + ptrace_rule *ptrace_entry; flagvals flags; int fmode; @@ -252,6 +257,10 @@ void add_local_entry(Profile *prof); %type signal_perms %type opt_signal_perm %type signal_rule +%type ptrace_perm +%type ptrace_perms +%type opt_ptrace_perm +%type ptrace_rule %type opt_named_transition %type opt_unsafe %type opt_file @@ -725,6 +734,22 @@ rules: rules opt_prefix signal_rule $$ = $1; } +rules: rules opt_prefix ptrace_rule + { + if ($2.owner) + yyerror(_("owner prefix not allowed on ptrace 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"); @@ -1285,6 +1310,54 @@ signal_rule: TOK_SIGNAL opt_signal_perm opt_conds TOK_END_OF_RULE $$ = ent; } +ptrace_perm: TOK_VALUE + { + if (strcmp($1, "trace") == 0 || strcmp($1, "write") == 0) + $$ = AA_MAY_TRACE; + else if (strcmp($1, "read") == 0) + $$ = AA_MAY_READ; + else if (strcmp($1, "tracedby") == 0) + $$ = AA_MAY_TRACEDBY; + else if (strcmp($1, "readby") == 0) + $$ = AA_MAY_READBY; + else if ($1) + parse_ptrace_mode($1, &$$, 1); + else + $$ = 0; + + if ($1) + free($1); + } + | TOK_TRACE { $$ = AA_MAY_TRACE; } + | TOK_TRACEDBY { $$ = AA_MAY_TRACEDBY; } + | TOK_READ { $$ = AA_MAY_READ; } + | TOK_WRITE { $$ = AA_MAY_TRACE; } + | TOK_READBY { $$ = AA_MAY_READBY; } + | TOK_MODE + { + parse_ptrace_mode($1, &$$, 1); + free($1); + } + +ptrace_perms: { /* nothing */ $$ = 0; } + | ptrace_perms ptrace_perm { $$ = $1 | $2; } + | ptrace_perms TOK_COMMA ptrace_perm { $$ = $1 | $3; } + +opt_ptrace_perm: { /* nothing */ $$ = 0; } + | ptrace_perm { $$ = $1; } + | TOK_OPENPAREN ptrace_perms TOK_CLOSEPAREN { $$ = $2; } + +ptrace_rule: TOK_PTRACE opt_ptrace_perm opt_conds TOK_END_OF_RULE + { + ptrace_rule *ent = new ptrace_rule($2, $3, NULL); + $$ = ent; + } + | TOK_PTRACE opt_ptrace_perm opt_conds TOK_ID TOK_END_OF_RULE + { + ptrace_rule *ent = new ptrace_rule($2, $3, $4); + $$ = ent; + } + hat_start: TOK_CARET {} | TOK_HAT {} diff --git a/parser/ptrace.c b/parser/ptrace.c new file mode 100644 index 000000000..05a458447 --- /dev/null +++ b/parser/ptrace.c @@ -0,0 +1,159 @@ +/* + * 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 "parser.h" +#include "profile.h" +#include "ptrace.h" + +#include +#include +#include +#include + +int parse_ptrace_mode(const char *str_mode, int *mode, int fail) +{ + return parse_X_mode("ptrace", AA_VALID_PTRACE_PERMS, str_mode, mode, fail); +} + +void ptrace_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 ptrace rules\n"); + + /* no valid conditionals atm */ + yyerror("invalid ptrace rule conditional \"%s\"\n", + cond_ent->name); + } +} + +ptrace_rule::ptrace_rule(int mode_p, struct cond_entry *conds, char *peer): + peer_label(peer), audit(0), deny(0) +{ + if (mode_p) { + if (mode_p & ~AA_VALID_PTRACE_PERMS) + yyerror("mode contains invalid permissions for ptrace\n"); + mode = mode_p; + } else { + mode = AA_VALID_PTRACE_PERMS; + } + + move_conditionals(conds); + free_cond_list(conds); +} + +ostream &ptrace_rule::dump(ostream &os) +{ + if (audit) + os << "audit "; + if (deny) + os << "deny "; + + os << "ptrace"; + + if (mode != AA_VALID_PTRACE_PERMS) { + os << " ("; + + if (mode & AA_MAY_READ) + os << "read "; + if (mode & AA_MAY_READBY) + os << "readby "; + if (mode & AA_MAY_TRACE) + os << "trace "; + if (mode & AA_MAY_TRACEDBY) + os << "tracedby "; + os << ")"; + } + + if (peer_label) + os << " " << peer_label; + + os << ",\n"; + + return os; +} + + +int ptrace_rule::expand_variables(void) +{ + return expand_entry_variables(&peer_label); +} + +/* do we want to warn once/profile or just once per compile?? */ +static void warn_once(const char *name) +{ + 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 << ") ptrace rules not enforced\n"; + warned_name = name; + } +} + +int ptrace_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_ptrace) { + warn_once(prof.name); + return RULE_NOT_SUPPORTED; + } + + /* always generate a label and ptrace entry */ + buffer << "(" << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_LABEL << "|)"; + + buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_PTRACE; + + 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_VALID_PTRACE_PERMS) { + if (!prof.policy.rules->add_rule(buf.c_str(), deny, mode, audit, + dfaflags)) + goto fail; + } + + return RULE_OK; + +fail: + return RULE_ERROR; +} + diff --git a/parser/ptrace.h b/parser/ptrace.h new file mode 100644 index 000000000..7d2a7ca55 --- /dev/null +++ b/parser/ptrace.h @@ -0,0 +1,52 @@ +/* + * 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_PTRACE_H +#define __AA_PTRACE_H + +#include "immunix.h" +#include "parser.h" + +#define AA_MAY_TRACE AA_MAY_WRITE +#define AA_MAY_READBY 0x10 /* MAY_CREATE in new encoding */ +#define AA_MAY_TRACEDBY AA_MAY_APPEND +#define AA_VALID_PTRACE_PERMS (AA_MAY_READ | AA_MAY_TRACE | AA_MAY_READBY | \ + AA_MAY_TRACEDBY) + +int parse_ptrace_mode(const char *str_mode, int *mode, int fail); + +class ptrace_rule: public rule_t { + void move_conditionals(struct cond_entry *conds); +public: + char *peer_label; + int mode; + int audit; + int deny; + + ptrace_rule(int mode, struct cond_entry *conds, char *peer); + virtual ~ptrace_rule() + { + 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_PTRACE_H */