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

Seth Arnold seth.arnold at canonical.com
Sat Apr 19 00:21:15 UTC 2014


On Tue, Apr 15, 2014 at 10:22:27AM -0700, john.johansen at canonical.com wrote:
> 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>

Acked-by: Seth Arnold <seth.arnold at canonical.com>

Thanks

> 
> ---
>  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
> @@ -305,6 +305,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>{
>  	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
> @@ -472,6 +473,15 @@
>  <DBUS_MODE,SIGNAL_MODE>{
>  	send		{ RETURN_TOKEN(TOK_SEND); }
>  	receive		{ RETURN_TOKEN(TOK_RECEIVE); }
> +}
> +
> +<PTRACE_MODE>{
> +	trace		{ RETURN_TOKEN(TOK_TRACE); }
> +	readby		{ 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}	{
> @@ -483,9 +493,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);
> @@ -575,13 +587,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();
> @@ -594,7 +608,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 */
> @@ -622,6 +636,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
> @@ -171,6 +174,7 @@
>  	#include "mount.h"
>  	#include "dbus.h"
>  	#include "signal.h"
> +	#include "ptrace.h"
>  }
>  
>  %union {
> @@ -185,6 +189,7 @@
>  	mnt_rule *mnt_entry;
>  	dbus_rule *dbus_entry;
>  	signal_rule *signal_entry;
> +	ptrace_rule *ptrace_entry;
>  
>  	flagvals flags;
>  	int fmode;
> @@ -252,6 +257,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
> @@ -725,6 +734,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");
> @@ -1285,6 +1310,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 */
> 
> 
> -- 
> AppArmor mailing list
> AppArmor at lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20140418/43c77ac2/attachment.pgp>


More information about the AppArmor mailing list