mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	apparmor: add the ability to mediate signals
Add signal mediation where the signal can be mediated based on the
signal, direction, or the label or the peer/target. The signal perms
are verified on a cross check to ensure policy consistency in the case
of incremental policy load/replacement.
The optimization of skipping the cross check when policy is guaranteed
to be consistent (single compile unit) remains to be done.
policy rules have the form of
  SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ]
                [ SIGNAL SET ] [ SIGNAL PEER ]
  SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST
  SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL
                           ACCESS ')'
  SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' |
                    'receive' )
  SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')'
  SIGNAL LIST = Comma or space separated list of SIGNALS
  SIGNALS = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' |
              'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' |
	      'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' |
	      'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' |
	      'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' |
	      'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32'
            )
  SIGNAL PEER = 'peer' '=' AARE
eg.
  signal,                                 # allow all signals
  signal send set=(hup, kill) peer=foo,
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
			
			
This commit is contained in:
		
							parent
							
								
									c5561700c9
								
							
						
					
					
						commit
						cd1dbf76b2
					
				
					 7 changed files with 231 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -32,6 +32,7 @@
 | 
			
		|||
#include "include/audit.h"
 | 
			
		||||
#include "include/context.h"
 | 
			
		||||
#include "include/crypto.h"
 | 
			
		||||
#include "include/ipc.h"
 | 
			
		||||
#include "include/policy_ns.h"
 | 
			
		||||
#include "include/label.h"
 | 
			
		||||
#include "include/policy.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
 | 
			
		|||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct aa_sfs_entry aa_sfs_entry_signal[] = {
 | 
			
		||||
	AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct aa_sfs_entry aa_sfs_entry_domain[] = {
 | 
			
		||||
	AA_SFS_FILE_BOOLEAN("change_hat",	1),
 | 
			
		||||
	AA_SFS_FILE_BOOLEAN("change_hatv",	1),
 | 
			
		||||
| 
						 | 
				
			
			@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
 | 
			
		|||
	AA_SFS_DIR("rlimit",			aa_sfs_entry_rlimit),
 | 
			
		||||
	AA_SFS_DIR("caps",			aa_sfs_entry_caps),
 | 
			
		||||
	AA_SFS_DIR("ptrace",			aa_sfs_entry_ptrace),
 | 
			
		||||
	AA_SFS_DIR("signal",			aa_sfs_entry_signal),
 | 
			
		||||
	AA_SFS_DIR("query",			aa_sfs_entry_query),
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@
 | 
			
		|||
#define AA_CLASS_RLIMITS	5
 | 
			
		||||
#define AA_CLASS_DOMAIN		6
 | 
			
		||||
#define AA_CLASS_PTRACE		9
 | 
			
		||||
#define AA_CLASS_SIGNAL		10
 | 
			
		||||
#define AA_CLASS_LABEL		16
 | 
			
		||||
 | 
			
		||||
#define AA_CLASS_LAST		AA_CLASS_LABEL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,6 +86,7 @@ enum audit_type {
 | 
			
		|||
#define OP_SHUTDOWN "socket_shutdown"
 | 
			
		||||
 | 
			
		||||
#define OP_PTRACE "ptrace"
 | 
			
		||||
#define OP_SIGNAL "signal"
 | 
			
		||||
 | 
			
		||||
#define OP_EXEC "exec"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +127,7 @@ struct apparmor_audit_data {
 | 
			
		|||
			long pos;
 | 
			
		||||
			const char *ns;
 | 
			
		||||
		} iface;
 | 
			
		||||
		int signal;
 | 
			
		||||
		struct {
 | 
			
		||||
			int rlim;
 | 
			
		||||
			unsigned long max;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,8 +27,14 @@ struct aa_profile;
 | 
			
		|||
 | 
			
		||||
#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
 | 
			
		||||
			     AA_MAY_BE_READ | AA_MAY_BE_TRACED)
 | 
			
		||||
#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
 | 
			
		||||
 | 
			
		||||
#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
 | 
			
		||||
	"segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
 | 
			
		||||
	"xcpu xfsz vtalrm prof winch io pwr sys emt lost"
 | 
			
		||||
 | 
			
		||||
int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
 | 
			
		||||
		  u32 request);
 | 
			
		||||
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
 | 
			
		||||
 | 
			
		||||
#endif /* __AA_IPC_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										95
									
								
								security/apparmor/include/sig_names.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								security/apparmor/include/sig_names.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
#include <linux/signal.h>
 | 
			
		||||
 | 
			
		||||
#define SIGUNKNOWN 0
 | 
			
		||||
#define MAXMAPPED_SIG 35
 | 
			
		||||
/* provide a mapping of arch signal to internal signal # for mediation
 | 
			
		||||
 * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
 | 
			
		||||
 * map to the same entry those that may/or may not get a separate entry
 | 
			
		||||
 */
 | 
			
		||||
static const int sig_map[MAXMAPPED_SIG] = {
 | 
			
		||||
	[0] = MAXMAPPED_SIG,	/* existence test */
 | 
			
		||||
	[SIGHUP] = 1,
 | 
			
		||||
	[SIGINT] = 2,
 | 
			
		||||
	[SIGQUIT] = 3,
 | 
			
		||||
	[SIGILL] = 4,
 | 
			
		||||
	[SIGTRAP] = 5,		/* -, 5, - */
 | 
			
		||||
	[SIGABRT] = 6,		/*  SIGIOT: -, 6, - */
 | 
			
		||||
	[SIGBUS] = 7,		/* 10, 7, 10 */
 | 
			
		||||
	[SIGFPE] = 8,
 | 
			
		||||
	[SIGKILL] = 9,
 | 
			
		||||
	[SIGUSR1] = 10,		/* 30, 10, 16 */
 | 
			
		||||
	[SIGSEGV] = 11,
 | 
			
		||||
	[SIGUSR2] = 12,		/* 31, 12, 17 */
 | 
			
		||||
	[SIGPIPE] = 13,
 | 
			
		||||
	[SIGALRM] = 14,
 | 
			
		||||
	[SIGTERM] = 15,
 | 
			
		||||
	[SIGSTKFLT] = 16,	/* -, 16, - */
 | 
			
		||||
	[SIGCHLD] = 17,		/* 20, 17, 18.  SIGCHLD -, -, 18 */
 | 
			
		||||
	[SIGCONT] = 18,		/* 19, 18, 25 */
 | 
			
		||||
	[SIGSTOP] = 19,		/* 17, 19, 23 */
 | 
			
		||||
	[SIGTSTP] = 20,		/* 18, 20, 24 */
 | 
			
		||||
	[SIGTTIN] = 21,		/* 21, 21, 26 */
 | 
			
		||||
	[SIGTTOU] = 22,		/* 22, 22, 27 */
 | 
			
		||||
	[SIGURG] = 23,		/* 16, 23, 21 */
 | 
			
		||||
	[SIGXCPU] = 24,		/* 24, 24, 30 */
 | 
			
		||||
	[SIGXFSZ] = 25,		/* 25, 25, 31 */
 | 
			
		||||
	[SIGVTALRM] = 26,	/* 26, 26, 28 */
 | 
			
		||||
	[SIGPROF] = 27,		/* 27, 27, 29 */
 | 
			
		||||
	[SIGWINCH] = 28,	/* 28, 28, 20 */
 | 
			
		||||
	[SIGIO] = 29,		/* SIGPOLL: 23, 29, 22 */
 | 
			
		||||
	[SIGPWR] = 30,		/* 29, 30, 19.  SIGINFO 29, -, - */
 | 
			
		||||
#ifdef SIGSYS
 | 
			
		||||
	[SIGSYS] = 31,		/* 12, 31, 12. often SIG LOST/UNUSED */
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SIGEMT
 | 
			
		||||
	[SIGEMT] = 32,		/* 7, - , 7 */
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(SIGLOST) && SIGPWR != SIGLOST		/* sparc */
 | 
			
		||||
	[SIGLOST] = 33,		/* unused on Linux */
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
 | 
			
		||||
	[SIGUNUSED] = 34,	/* -, 31, - */
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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",
 | 
			
		||||
	"stkflt",
 | 
			
		||||
	"chld",
 | 
			
		||||
	"cont",
 | 
			
		||||
	"stop",
 | 
			
		||||
	"stp",
 | 
			
		||||
	"ttin",
 | 
			
		||||
	"ttou",
 | 
			
		||||
	"urg",
 | 
			
		||||
	"xcpu",
 | 
			
		||||
	"xfsz",
 | 
			
		||||
	"vtalrm",
 | 
			
		||||
	"prof",
 | 
			
		||||
	"winch",
 | 
			
		||||
	"io",
 | 
			
		||||
	"pwr",
 | 
			
		||||
	"sys",
 | 
			
		||||
	"emt",
 | 
			
		||||
	"lost",
 | 
			
		||||
	"unused",
 | 
			
		||||
 | 
			
		||||
	"exists",	/* always last existence test mapped to MAXMAPPED_SIG */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +20,7 @@
 | 
			
		|||
#include "include/context.h"
 | 
			
		||||
#include "include/policy.h"
 | 
			
		||||
#include "include/ipc.h"
 | 
			
		||||
#include "include/sig_names.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * audit_ptrace_mask - convert mask to permission string
 | 
			
		||||
| 
						 | 
				
			
			@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static inline int map_signal_num(int sig)
 | 
			
		||||
{
 | 
			
		||||
	if (sig > SIGRTMAX)
 | 
			
		||||
		return SIGUNKNOWN;
 | 
			
		||||
	else if (sig >= SIGRTMIN)
 | 
			
		||||
		return sig - SIGRTMIN + 128;	/* rt sigs mapped to 128 */
 | 
			
		||||
	else if (sig <= MAXMAPPED_SIG)
 | 
			
		||||
		return sig_map[sig];
 | 
			
		||||
	return SIGUNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * audit_file_mask - convert mask to permission string
 | 
			
		||||
 * @buffer: buffer to write string to (NOT NULL)
 | 
			
		||||
 * @mask: permission mask to convert
 | 
			
		||||
 */
 | 
			
		||||
static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
 | 
			
		||||
{
 | 
			
		||||
	if (mask & MAY_READ)
 | 
			
		||||
		audit_log_string(ab, "receive");
 | 
			
		||||
	if (mask & MAY_WRITE)
 | 
			
		||||
		audit_log_string(ab, "send");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * audit_cb - call back for signal specific audit fields
 | 
			
		||||
 * @ab: audit_buffer  (NOT NULL)
 | 
			
		||||
 * @va: audit struct to audit values of  (NOT NULL)
 | 
			
		||||
 */
 | 
			
		||||
static void audit_signal_cb(struct audit_buffer *ab, void *va)
 | 
			
		||||
{
 | 
			
		||||
	struct common_audit_data *sa = va;
 | 
			
		||||
 | 
			
		||||
	if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
 | 
			
		||||
		audit_log_format(ab, " requested_mask=");
 | 
			
		||||
		audit_signal_mask(ab, aad(sa)->request);
 | 
			
		||||
		if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
 | 
			
		||||
			audit_log_format(ab, " denied_mask=");
 | 
			
		||||
			audit_signal_mask(ab, aad(sa)->denied);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (aad(sa)->signal <= MAXMAPPED_SIG)
 | 
			
		||||
		audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
 | 
			
		||||
	else
 | 
			
		||||
		audit_log_format(ab, " signal=rtmin+%d",
 | 
			
		||||
				 aad(sa)->signal - 128);
 | 
			
		||||
	audit_log_format(ab, " peer=");
 | 
			
		||||
	aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
 | 
			
		||||
			FLAGS_NONE, GFP_ATOMIC);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: update to handle compound name&name2, conditionals */
 | 
			
		||||
static void profile_match_signal(struct aa_profile *profile, const char *label,
 | 
			
		||||
				 int signal, struct aa_perms *perms)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int state;
 | 
			
		||||
 | 
			
		||||
	/* TODO: secondary cache check <profile, profile, perm> */
 | 
			
		||||
	state = aa_dfa_next(profile->policy.dfa,
 | 
			
		||||
			    profile->policy.start[AA_CLASS_SIGNAL],
 | 
			
		||||
			    signal);
 | 
			
		||||
	state = aa_dfa_match(profile->policy.dfa, state, label);
 | 
			
		||||
	aa_compute_perms(profile->policy.dfa, state, perms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int profile_signal_perm(struct aa_profile *profile,
 | 
			
		||||
			       struct aa_profile *peer, u32 request,
 | 
			
		||||
			       struct common_audit_data *sa)
 | 
			
		||||
{
 | 
			
		||||
	struct aa_perms perms;
 | 
			
		||||
 | 
			
		||||
	if (profile_unconfined(profile) ||
 | 
			
		||||
	    !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	aad(sa)->peer = &peer->label;
 | 
			
		||||
	profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
 | 
			
		||||
			     &perms);
 | 
			
		||||
	aa_apply_modes_to_perms(profile, &perms);
 | 
			
		||||
	return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aa_signal_cross_perm(struct aa_profile *sender,
 | 
			
		||||
				struct aa_profile *target,
 | 
			
		||||
				struct common_audit_data *sa)
 | 
			
		||||
{
 | 
			
		||||
	return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
 | 
			
		||||
		      profile_signal_perm(target, sender, MAY_READ, sa));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
 | 
			
		||||
{
 | 
			
		||||
	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
 | 
			
		||||
 | 
			
		||||
	aad(&sa)->signal = map_signal_num(sig);
 | 
			
		||||
	return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
 | 
			
		||||
				      &sa);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task,
 | 
			
		|||
	return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
 | 
			
		||||
			      int sig, u32 secid)
 | 
			
		||||
{
 | 
			
		||||
	struct aa_label *cl, *tl;
 | 
			
		||||
	int error;
 | 
			
		||||
 | 
			
		||||
	if (secid)
 | 
			
		||||
		/* TODO: after secid to label mapping is done.
 | 
			
		||||
		 *  Dealing with USB IO specific behavior
 | 
			
		||||
		 */
 | 
			
		||||
		return 0;
 | 
			
		||||
	cl = __begin_current_label_crit_section();
 | 
			
		||||
	tl = aa_get_task_label(target);
 | 
			
		||||
	error = aa_may_signal(cl, tl, sig);
 | 
			
		||||
	aa_put_label(tl);
 | 
			
		||||
	__end_current_label_crit_section(cl);
 | 
			
		||||
 | 
			
		||||
	return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
 | 
			
		||||
	LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
 | 
			
		||||
	LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
 | 
			
		||||
| 
						 | 
				
			
			@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
 | 
			
		|||
	LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
 | 
			
		||||
 | 
			
		||||
	LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
 | 
			
		||||
	LSM_HOOK_INIT(task_kill, apparmor_task_kill),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue