[apparmor] [patch 20/21] Add the ability to specify ptrace rules

john.johansen at canonical.com john.johansen at canonical.com
Mon Mar 17 23:29:30 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        |    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(-)

--- 2.9-test.orig/parser/Makefile
+++ 2.9-test/parser/Makefile
@@ -80,9 +80,9 @@
        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 @@
 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
@@ -302,6 +302,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
@@ -72,6 +72,7 @@
 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;
--- 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
@@ -697,6 +697,8 @@
 		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)
--- 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,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 <iomanip>
+#include <string>
+#include <iostream>
+#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);
+}
+
+/* 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;
+}
+
--- /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