1 From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001
2 From: John Johansen <john.johansen@canonical.com>
3 Date: Tue, 18 Jul 2017 22:56:22 -0700
4 Subject: [PATCH 06/17] apparmor: add the ability to mediate signals
6 Add signal mediation where the signal can be mediated based on the
7 signal, direction, or the label or the peer/target. The signal perms
8 are verified on a cross check to ensure policy consistency in the case
9 of incremental policy load/replacement.
11 The optimization of skipping the cross check when policy is guaranteed
12 to be consistent (single compile unit) remains to be done.
14 policy rules have the form of
15 SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ]
16 [ SIGNAL SET ] [ SIGNAL PEER ]
18 SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST
20 SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL
23 SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' |
26 SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')'
28 SIGNAL LIST = Comma or space separated list of SIGNALS
30 SIGNALS = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' |
31 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' |
32 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' |
33 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' |
34 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' |
35 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32'
38 SIGNAL PEER = 'peer' '=' AARE
41 signal, # allow all signals
42 signal send set=(hup, kill) peer=foo,
44 Signed-off-by: John Johansen <john.johansen@canonical.com>
45 Acked-by: Seth Arnold <seth.arnold@canonical.com>
46 (cherry picked from commit c6bf1adaecaa719d7c56338cc43b2982214f2f44)
48 security/apparmor/apparmorfs.c | 7 +++
49 security/apparmor/include/apparmor.h | 1 +
50 security/apparmor/include/audit.h | 2 +
51 security/apparmor/include/ipc.h | 6 +++
52 security/apparmor/include/sig_names.h | 95 +++++++++++++++++++++++++++++++++
53 security/apparmor/ipc.c | 99 +++++++++++++++++++++++++++++++++++
54 security/apparmor/lsm.c | 21 ++++++++
55 7 files changed, 231 insertions(+)
56 create mode 100644 security/apparmor/include/sig_names.h
58 diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
59 index 2caeb748070c..a5f9e1aa51f7 100644
60 --- a/security/apparmor/apparmorfs.c
61 +++ b/security/apparmor/apparmorfs.c
63 #include "include/audit.h"
64 #include "include/context.h"
65 #include "include/crypto.h"
66 +#include "include/ipc.h"
67 #include "include/policy_ns.h"
68 #include "include/label.h"
69 #include "include/policy.h"
70 @@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
74 +static struct aa_sfs_entry aa_sfs_entry_signal[] = {
75 + AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
79 static struct aa_sfs_entry aa_sfs_entry_domain[] = {
80 AA_SFS_FILE_BOOLEAN("change_hat", 1),
81 AA_SFS_FILE_BOOLEAN("change_hatv", 1),
82 @@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
83 AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
84 AA_SFS_DIR("caps", aa_sfs_entry_caps),
85 AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
86 + AA_SFS_DIR("signal", aa_sfs_entry_signal),
87 AA_SFS_DIR("query", aa_sfs_entry_query),
90 diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
91 index aaf893f4e4f5..962a20a75e01 100644
92 --- a/security/apparmor/include/apparmor.h
93 +++ b/security/apparmor/include/apparmor.h
95 #define AA_CLASS_RLIMITS 5
96 #define AA_CLASS_DOMAIN 6
97 #define AA_CLASS_PTRACE 9
98 +#define AA_CLASS_SIGNAL 10
99 #define AA_CLASS_LABEL 16
101 #define AA_CLASS_LAST AA_CLASS_LABEL
102 diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
103 index c68839a44351..d9a156ae11b9 100644
104 --- a/security/apparmor/include/audit.h
105 +++ b/security/apparmor/include/audit.h
106 @@ -86,6 +86,7 @@ enum audit_type {
107 #define OP_SHUTDOWN "socket_shutdown"
109 #define OP_PTRACE "ptrace"
110 +#define OP_SIGNAL "signal"
112 #define OP_EXEC "exec"
114 @@ -126,6 +127,7 @@ struct apparmor_audit_data {
122 diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h
123 index 656fdb81c8a0..5ffc218d1e74 100644
124 --- a/security/apparmor/include/ipc.h
125 +++ b/security/apparmor/include/ipc.h
126 @@ -27,8 +27,14 @@ struct aa_profile;
128 #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
129 AA_MAY_BE_READ | AA_MAY_BE_TRACED)
130 +#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
132 +#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
133 + "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
134 + "xcpu xfsz vtalrm prof winch io pwr sys emt lost"
136 int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
138 +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
140 #endif /* __AA_IPC_H */
141 diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
143 index 000000000000..0d4395f231ca
145 +++ b/security/apparmor/include/sig_names.h
147 +#include <linux/signal.h>
149 +#define SIGUNKNOWN 0
150 +#define MAXMAPPED_SIG 35
151 +/* provide a mapping of arch signal to internal signal # for mediation
152 + * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
153 + * map to the same entry those that may/or may not get a separate entry
155 +static const int sig_map[MAXMAPPED_SIG] = {
156 + [0] = MAXMAPPED_SIG, /* existence test */
161 + [SIGTRAP] = 5, /* -, 5, - */
162 + [SIGABRT] = 6, /* SIGIOT: -, 6, - */
163 + [SIGBUS] = 7, /* 10, 7, 10 */
166 + [SIGUSR1] = 10, /* 30, 10, 16 */
168 + [SIGUSR2] = 12, /* 31, 12, 17 */
172 + [SIGSTKFLT] = 16, /* -, 16, - */
173 + [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */
174 + [SIGCONT] = 18, /* 19, 18, 25 */
175 + [SIGSTOP] = 19, /* 17, 19, 23 */
176 + [SIGTSTP] = 20, /* 18, 20, 24 */
177 + [SIGTTIN] = 21, /* 21, 21, 26 */
178 + [SIGTTOU] = 22, /* 22, 22, 27 */
179 + [SIGURG] = 23, /* 16, 23, 21 */
180 + [SIGXCPU] = 24, /* 24, 24, 30 */
181 + [SIGXFSZ] = 25, /* 25, 25, 31 */
182 + [SIGVTALRM] = 26, /* 26, 26, 28 */
183 + [SIGPROF] = 27, /* 27, 27, 29 */
184 + [SIGWINCH] = 28, /* 28, 28, 20 */
185 + [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */
186 + [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */
188 + [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */
191 + [SIGEMT] = 32, /* 7, - , 7 */
193 +#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */
194 + [SIGLOST] = 33, /* unused on Linux */
196 +#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
197 + [SIGUNUSED] = 34, /* -, 31, - */
201 +/* this table is ordered post sig_map[sig] mapping */
202 +static const char *const sig_names[MAXMAPPED_SIG + 1] = {
239 + "exists", /* always last existence test mapped to MAXMAPPED_SIG */
242 diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
243 index 11e66b5bbc42..66fb9ede9447 100644
244 --- a/security/apparmor/ipc.c
245 +++ b/security/apparmor/ipc.c
247 #include "include/context.h"
248 #include "include/policy.h"
249 #include "include/ipc.h"
250 +#include "include/sig_names.h"
253 * audit_ptrace_mask - convert mask to permission string
254 @@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
258 +static inline int map_signal_num(int sig)
260 + if (sig > SIGRTMAX)
262 + else if (sig >= SIGRTMIN)
263 + return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */
264 + else if (sig <= MAXMAPPED_SIG)
265 + return sig_map[sig];
270 + * audit_file_mask - convert mask to permission string
271 + * @buffer: buffer to write string to (NOT NULL)
272 + * @mask: permission mask to convert
274 +static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
276 + if (mask & MAY_READ)
277 + audit_log_string(ab, "receive");
278 + if (mask & MAY_WRITE)
279 + audit_log_string(ab, "send");
283 + * audit_cb - call back for signal specific audit fields
284 + * @ab: audit_buffer (NOT NULL)
285 + * @va: audit struct to audit values of (NOT NULL)
287 +static void audit_signal_cb(struct audit_buffer *ab, void *va)
289 + struct common_audit_data *sa = va;
291 + if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
292 + audit_log_format(ab, " requested_mask=");
293 + audit_signal_mask(ab, aad(sa)->request);
294 + if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
295 + audit_log_format(ab, " denied_mask=");
296 + audit_signal_mask(ab, aad(sa)->denied);
299 + if (aad(sa)->signal <= MAXMAPPED_SIG)
300 + audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
302 + audit_log_format(ab, " signal=rtmin+%d",
303 + aad(sa)->signal - 128);
304 + audit_log_format(ab, " peer=");
305 + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
306 + FLAGS_NONE, GFP_ATOMIC);
309 +/* TODO: update to handle compound name&name2, conditionals */
310 +static void profile_match_signal(struct aa_profile *profile, const char *label,
311 + int signal, struct aa_perms *perms)
313 + unsigned int state;
315 + /* TODO: secondary cache check <profile, profile, perm> */
316 + state = aa_dfa_next(profile->policy.dfa,
317 + profile->policy.start[AA_CLASS_SIGNAL],
319 + state = aa_dfa_match(profile->policy.dfa, state, label);
320 + aa_compute_perms(profile->policy.dfa, state, perms);
323 +static int profile_signal_perm(struct aa_profile *profile,
324 + struct aa_profile *peer, u32 request,
325 + struct common_audit_data *sa)
327 + struct aa_perms perms;
329 + if (profile_unconfined(profile) ||
330 + !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
333 + aad(sa)->peer = &peer->label;
334 + profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
336 + aa_apply_modes_to_perms(profile, &perms);
337 + return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
340 +static int aa_signal_cross_perm(struct aa_profile *sender,
341 + struct aa_profile *target,
342 + struct common_audit_data *sa)
344 + return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
345 + profile_signal_perm(target, sender, MAY_READ, sa));
348 +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
350 + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
352 + aad(&sa)->signal = map_signal_num(sig);
353 + return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
356 diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
357 index 867bcd154c7e..af22f3dfbcce 100644
358 --- a/security/apparmor/lsm.c
359 +++ b/security/apparmor/lsm.c
360 @@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task,
364 +static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
365 + int sig, u32 secid)
367 + struct aa_label *cl, *tl;
371 + /* TODO: after secid to label mapping is done.
372 + * Dealing with USB IO specific behavior
375 + cl = __begin_current_label_crit_section();
376 + tl = aa_get_task_label(target);
377 + error = aa_may_signal(cl, tl, sig);
379 + __end_current_label_crit_section(cl);
384 static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
385 LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
386 LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
387 @@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
388 LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
390 LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
391 + LSM_HOOK_INIT(task_kill, apparmor_task_kill),