mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00

The old out of tree patchseries has been completely dropped. v4.13 has most of the newer apparmor 3.x code in it. v4.14 has the rest except the af_unix mediation which is included as the last patch
397 lines
12 KiB
Diff
397 lines
12 KiB
Diff
From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001
|
|
From: John Johansen <john.johansen@canonical.com>
|
|
Date: Tue, 18 Jul 2017 22:56:22 -0700
|
|
Subject: [PATCH 06/17] 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>
|
|
(cherry picked from commit c6bf1adaecaa719d7c56338cc43b2982214f2f44)
|
|
---
|
|
security/apparmor/apparmorfs.c | 7 +++
|
|
security/apparmor/include/apparmor.h | 1 +
|
|
security/apparmor/include/audit.h | 2 +
|
|
security/apparmor/include/ipc.h | 6 +++
|
|
security/apparmor/include/sig_names.h | 95 +++++++++++++++++++++++++++++++++
|
|
security/apparmor/ipc.c | 99 +++++++++++++++++++++++++++++++++++
|
|
security/apparmor/lsm.c | 21 ++++++++
|
|
7 files changed, 231 insertions(+)
|
|
create mode 100644 security/apparmor/include/sig_names.h
|
|
|
|
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
|
|
index 2caeb748070c..a5f9e1aa51f7 100644
|
|
--- a/security/apparmor/apparmorfs.c
|
|
+++ b/security/apparmor/apparmorfs.c
|
|
@@ -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),
|
|
{ }
|
|
};
|
|
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
|
|
index aaf893f4e4f5..962a20a75e01 100644
|
|
--- a/security/apparmor/include/apparmor.h
|
|
+++ b/security/apparmor/include/apparmor.h
|
|
@@ -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
|
|
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
|
|
index c68839a44351..d9a156ae11b9 100644
|
|
--- a/security/apparmor/include/audit.h
|
|
+++ b/security/apparmor/include/audit.h
|
|
@@ -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;
|
|
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h
|
|
index 656fdb81c8a0..5ffc218d1e74 100644
|
|
--- a/security/apparmor/include/ipc.h
|
|
+++ b/security/apparmor/include/ipc.h
|
|
@@ -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 */
|
|
diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
|
|
new file mode 100644
|
|
index 000000000000..0d4395f231ca
|
|
--- /dev/null
|
|
+++ b/security/apparmor/include/sig_names.h
|
|
@@ -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 */
|
|
+};
|
|
+
|
|
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
|
|
index 11e66b5bbc42..66fb9ede9447 100644
|
|
--- a/security/apparmor/ipc.c
|
|
+++ b/security/apparmor/ipc.c
|
|
@@ -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);
|
|
+}
|
|
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
|
|
index 867bcd154c7e..af22f3dfbcce 100644
|
|
--- a/security/apparmor/lsm.c
|
|
+++ b/security/apparmor/lsm.c
|
|
@@ -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),
|
|
};
|
|
|
|
/*
|
|
--
|
|
2.11.0
|
|
|