Skip to content

Commit

Permalink
apparmor: add the ability to mediate signals
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
John Johansen committed Sep 22, 2017
1 parent c556170 commit cd1dbf7
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 0 deletions.
7 changes: 7 additions & 0 deletions security/apparmor/apparmorfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
{ }
};
Expand Down
1 change: 1 addition & 0 deletions security/apparmor/include/apparmor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions security/apparmor/include/audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ enum audit_type {
#define OP_SHUTDOWN "socket_shutdown"

#define OP_PTRACE "ptrace"
#define OP_SIGNAL "signal"

#define OP_EXEC "exec"

Expand Down Expand Up @@ -126,6 +127,7 @@ struct apparmor_audit_data {
long pos;
const char *ns;
} iface;
int signal;
struct {
int rlim;
unsigned long max;
Expand Down
6 changes: 6 additions & 0 deletions security/apparmor/include/ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 95 additions & 0 deletions security/apparmor/include/sig_names.h
Original file line number Diff line number Diff line change
@@ -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 */
};

99 changes: 99 additions & 0 deletions security/apparmor/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
21 changes: 21 additions & 0 deletions security/apparmor/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
};

/*
Expand Down

0 comments on commit cd1dbf7

Please sign in to comment.