[apparmor] [patch 17/24] Add the ability to specify ptrace rules
john.johansen at canonical.com
john.johansen at canonical.com
Fri Mar 7 17:31:38 UTC 2014
ptrace rules currently take the form of
ptrace [<ptrace_perms>] [<peer_profile_name>],
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 <john.johansen at canonical.com>
---
parser/Makefile | 8 ++
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 | 142 +++++++++++++++++++++++++++++++++++++++++++++++++
parser/ptrace.h | 52 +++++++++++++++++
10 files changed, 304 insertions(+), 7 deletions(-)
--- 2.9-test.orig/parser/Makefile
+++ 2.9-test/parser/Makefile
@@ -79,9 +79,10 @@
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 signal.c
+ parser_alias.c mount.c dbus.c lib.c profile.cc rule.c signal.c \
+ ptrace.c
HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
- rule.h signal.h
+ rule.h signal.h ptrace.h
TOOLS = apparmor_parser
OBJECTS = $(SRCS:.c=.o)
@@ -243,6 +244,9 @@
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 $@ $<
--- 2.9-test.orig/parser/parser.h
+++ 2.9-test/parser/parser.h
@@ -301,6 +301,7 @@
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;
--- 2.9-test.orig/parser/parser_common.c
+++ 2.9-test/parser/parser_common.c
@@ -71,6 +71,7 @@
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 kernel_supports_ptrace = 0; /* kernel supports ptrace 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
@@ -254,6 +254,7 @@
%x MOUNT_MODE
%x DBUS_MODE
%x SIGNAL_MODE
+%x PTRACE_MODE
%x CHANGE_PROFILE_MODE
%x INCLUDE
@@ -268,7 +269,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,SIGNAL_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,PTRACE_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
}
@@ -294,7 +295,7 @@
yyterminate();
}
-<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE>{
+<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_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
@@ -470,6 +471,15 @@
<DBUS_MODE,SIGNAL_MODE>{
send { RETURN_TOKEN(TOK_SEND); }
receive { RETURN_TOKEN(TOK_RECEIVE); }
+}
+
+<PTRACE_MODE>{
+ trace { RETURN_TOKEN(TOK_TRACE); }
+ viewedby { RETURN_TOKEN(TOK_READBY); }
+ tracedby { RETURN_TOKEN(TOK_TRACEDBY); }
+}
+
+<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
read { RETURN_TOKEN(TOK_READ); }
write { RETURN_TOKEN(TOK_WRITE); }
{OPEN_PAREN} {
@@ -481,9 +491,11 @@
}
}
-<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE>{
+<MOUNT_MODE>{
{ARROW} { RETURN_TOKEN(TOK_ARROW); }
+}
+<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
yylval.id = processid(yytext, yyleng);
RETURN_TOKEN(TOK_ID);
@@ -573,13 +585,15 @@
case TOK_SIGNAL:
state = SIGNAL_MODE;
break;
+ case TOK_PTRACE:
+ state = PTRACE_MODE;
default: /* nothing */
break;
}
PUSH_AND_RETURN(state, token);
}
-<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE>{
+<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE>{
{END_OF_RULE} {
if (YY_START != INITIAL)
POP();
@@ -592,7 +606,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,SIGNAL_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,PTRACE_MODE>{
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
@@ -620,6 +634,7 @@
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),
};
--- 2.9-test.orig/parser/parser_main.c
+++ 2.9-test/parser/parser_main.c
@@ -849,6 +849,8 @@
kernel_supports_dbus = 1;
if (strstr(features_string, "signal"))
kernel_supports_signal = 1;
+ if (strstr(features_string, "ptrace {"))
+ kernel_supports_ptrace = 1;
}
int process_binary(int option, const char *profilename)
--- 2.9-test.orig/parser/parser_misc.c
+++ 2.9-test/parser/parser_misc.c
@@ -150,6 +150,9 @@
{"write", TOK_WRITE},
{"eavesdrop", TOK_EAVESDROP},
{"peer", TOK_PEER},
+ {"trace", TOK_TRACE},
+ {"tracedby", TOK_TRACEDBY},
+ {"readby", TOK_READBY},
/* terminate */
{NULL, 0}
--- 2.9-test.orig/parser/parser_regex.c
+++ 2.9-test/parser/parser_regex.c
@@ -674,6 +674,7 @@
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 @@
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);
--- 2.9-test.orig/parser/parser_yacc.y
+++ 2.9-test/parser/parser_yacc.y
@@ -138,6 +138,9 @@
%token TOK_WRITE
%token TOK_EAVESDROP
%token TOK_PEER
+%token TOK_TRACE
+%token TOK_TRACEDBY
+%token TOK_READBY
/* rlimits */
%token TOK_RLIMIT
@@ -170,6 +173,7 @@
#include "mount.h"
#include "dbus.h"
#include "signal.h"
+ #include "ptrace.h"
}
%union {
@@ -184,6 +188,7 @@
mnt_rule *mnt_entry;
dbus_rule *dbus_entry;
signal_rule *signal_entry;
+ ptrace_rule *ptrace_entry;
flagvals flags;
int fmode;
@@ -250,6 +255,10 @@
%type <fmode> signal_perms
%type <fmode> opt_signal_perm
%type <signal_entry> signal_rule
+%type <fmode> ptrace_perm
+%type <fmode> ptrace_perms
+%type <fmode> opt_ptrace_perm
+%type <ptrace_entry> ptrace_rule
%type <transition> opt_named_transition
%type <boolean> opt_unsafe
%type <boolean> opt_file
@@ -723,6 +732,22 @@
$$ = $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");
@@ -1282,6 +1307,54 @@
$$ = 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 {}
--- /dev/null
+++ 2.9-test/parser/ptrace.c
@@ -0,0 +1,142 @@
+/*
+ * 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 <iomanip>
+#include <string>
+#include <sstream>
+
+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);
+}
+
+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) {
+ pwarn("profile %s ptrace rules not enforced\n", 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;
+}
+
--- /dev/null
+++ 2.9-test/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 */
More information about the AppArmor
mailing list