Skip to content

Commit cd1dbf7

Browse files
committed
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 <[email protected]> Acked-by: Seth Arnold <[email protected]>
1 parent c556170 commit cd1dbf7

File tree

7 files changed

+231
-0
lines changed

7 files changed

+231
-0
lines changed

security/apparmor/apparmorfs.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "include/audit.h"
3333
#include "include/context.h"
3434
#include "include/crypto.h"
35+
#include "include/ipc.h"
3536
#include "include/policy_ns.h"
3637
#include "include/label.h"
3738
#include "include/policy.h"
@@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
21292130
{ }
21302131
};
21312132

2133+
static struct aa_sfs_entry aa_sfs_entry_signal[] = {
2134+
AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
2135+
{ }
2136+
};
2137+
21322138
static struct aa_sfs_entry aa_sfs_entry_domain[] = {
21332139
AA_SFS_FILE_BOOLEAN("change_hat", 1),
21342140
AA_SFS_FILE_BOOLEAN("change_hatv", 1),
@@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
21792185
AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit),
21802186
AA_SFS_DIR("caps", aa_sfs_entry_caps),
21812187
AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace),
2188+
AA_SFS_DIR("signal", aa_sfs_entry_signal),
21822189
AA_SFS_DIR("query", aa_sfs_entry_query),
21832190
{ }
21842191
};

security/apparmor/include/apparmor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define AA_CLASS_RLIMITS 5
2929
#define AA_CLASS_DOMAIN 6
3030
#define AA_CLASS_PTRACE 9
31+
#define AA_CLASS_SIGNAL 10
3132
#define AA_CLASS_LABEL 16
3233

3334
#define AA_CLASS_LAST AA_CLASS_LABEL

security/apparmor/include/audit.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ enum audit_type {
8686
#define OP_SHUTDOWN "socket_shutdown"
8787

8888
#define OP_PTRACE "ptrace"
89+
#define OP_SIGNAL "signal"
8990

9091
#define OP_EXEC "exec"
9192

@@ -126,6 +127,7 @@ struct apparmor_audit_data {
126127
long pos;
127128
const char *ns;
128129
} iface;
130+
int signal;
129131
struct {
130132
int rlim;
131133
unsigned long max;

security/apparmor/include/ipc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,14 @@ struct aa_profile;
2727

2828
#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
2929
AA_MAY_BE_READ | AA_MAY_BE_TRACED)
30+
#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
31+
32+
#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
33+
"segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
34+
"xcpu xfsz vtalrm prof winch io pwr sys emt lost"
3035

3136
int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
3237
u32 request);
38+
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
3339

3440
#endif /* __AA_IPC_H */

security/apparmor/include/sig_names.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include <linux/signal.h>
2+
3+
#define SIGUNKNOWN 0
4+
#define MAXMAPPED_SIG 35
5+
/* provide a mapping of arch signal to internal signal # for mediation
6+
* those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
7+
* map to the same entry those that may/or may not get a separate entry
8+
*/
9+
static const int sig_map[MAXMAPPED_SIG] = {
10+
[0] = MAXMAPPED_SIG, /* existence test */
11+
[SIGHUP] = 1,
12+
[SIGINT] = 2,
13+
[SIGQUIT] = 3,
14+
[SIGILL] = 4,
15+
[SIGTRAP] = 5, /* -, 5, - */
16+
[SIGABRT] = 6, /* SIGIOT: -, 6, - */
17+
[SIGBUS] = 7, /* 10, 7, 10 */
18+
[SIGFPE] = 8,
19+
[SIGKILL] = 9,
20+
[SIGUSR1] = 10, /* 30, 10, 16 */
21+
[SIGSEGV] = 11,
22+
[SIGUSR2] = 12, /* 31, 12, 17 */
23+
[SIGPIPE] = 13,
24+
[SIGALRM] = 14,
25+
[SIGTERM] = 15,
26+
[SIGSTKFLT] = 16, /* -, 16, - */
27+
[SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */
28+
[SIGCONT] = 18, /* 19, 18, 25 */
29+
[SIGSTOP] = 19, /* 17, 19, 23 */
30+
[SIGTSTP] = 20, /* 18, 20, 24 */
31+
[SIGTTIN] = 21, /* 21, 21, 26 */
32+
[SIGTTOU] = 22, /* 22, 22, 27 */
33+
[SIGURG] = 23, /* 16, 23, 21 */
34+
[SIGXCPU] = 24, /* 24, 24, 30 */
35+
[SIGXFSZ] = 25, /* 25, 25, 31 */
36+
[SIGVTALRM] = 26, /* 26, 26, 28 */
37+
[SIGPROF] = 27, /* 27, 27, 29 */
38+
[SIGWINCH] = 28, /* 28, 28, 20 */
39+
[SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */
40+
[SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */
41+
#ifdef SIGSYS
42+
[SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */
43+
#endif
44+
#ifdef SIGEMT
45+
[SIGEMT] = 32, /* 7, - , 7 */
46+
#endif
47+
#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */
48+
[SIGLOST] = 33, /* unused on Linux */
49+
#endif
50+
#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
51+
[SIGUNUSED] = 34, /* -, 31, - */
52+
#endif
53+
};
54+
55+
/* this table is ordered post sig_map[sig] mapping */
56+
static const char *const sig_names[MAXMAPPED_SIG + 1] = {
57+
"unknown",
58+
"hup",
59+
"int",
60+
"quit",
61+
"ill",
62+
"trap",
63+
"abrt",
64+
"bus",
65+
"fpe",
66+
"kill",
67+
"usr1",
68+
"segv",
69+
"usr2",
70+
"pipe",
71+
"alrm",
72+
"term",
73+
"stkflt",
74+
"chld",
75+
"cont",
76+
"stop",
77+
"stp",
78+
"ttin",
79+
"ttou",
80+
"urg",
81+
"xcpu",
82+
"xfsz",
83+
"vtalrm",
84+
"prof",
85+
"winch",
86+
"io",
87+
"pwr",
88+
"sys",
89+
"emt",
90+
"lost",
91+
"unused",
92+
93+
"exists", /* always last existence test mapped to MAXMAPPED_SIG */
94+
};
95+

security/apparmor/ipc.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "include/context.h"
2121
#include "include/policy.h"
2222
#include "include/ipc.h"
23+
#include "include/sig_names.h"
2324

2425
/**
2526
* audit_ptrace_mask - convert mask to permission string
@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
121122
}
122123

123124

125+
static inline int map_signal_num(int sig)
126+
{
127+
if (sig > SIGRTMAX)
128+
return SIGUNKNOWN;
129+
else if (sig >= SIGRTMIN)
130+
return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */
131+
else if (sig <= MAXMAPPED_SIG)
132+
return sig_map[sig];
133+
return SIGUNKNOWN;
134+
}
135+
136+
/**
137+
* audit_file_mask - convert mask to permission string
138+
* @buffer: buffer to write string to (NOT NULL)
139+
* @mask: permission mask to convert
140+
*/
141+
static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
142+
{
143+
if (mask & MAY_READ)
144+
audit_log_string(ab, "receive");
145+
if (mask & MAY_WRITE)
146+
audit_log_string(ab, "send");
147+
}
148+
149+
/**
150+
* audit_cb - call back for signal specific audit fields
151+
* @ab: audit_buffer (NOT NULL)
152+
* @va: audit struct to audit values of (NOT NULL)
153+
*/
154+
static void audit_signal_cb(struct audit_buffer *ab, void *va)
155+
{
156+
struct common_audit_data *sa = va;
157+
158+
if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
159+
audit_log_format(ab, " requested_mask=");
160+
audit_signal_mask(ab, aad(sa)->request);
161+
if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
162+
audit_log_format(ab, " denied_mask=");
163+
audit_signal_mask(ab, aad(sa)->denied);
164+
}
165+
}
166+
if (aad(sa)->signal <= MAXMAPPED_SIG)
167+
audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
168+
else
169+
audit_log_format(ab, " signal=rtmin+%d",
170+
aad(sa)->signal - 128);
171+
audit_log_format(ab, " peer=");
172+
aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
173+
FLAGS_NONE, GFP_ATOMIC);
174+
}
175+
176+
/* TODO: update to handle compound name&name2, conditionals */
177+
static void profile_match_signal(struct aa_profile *profile, const char *label,
178+
int signal, struct aa_perms *perms)
179+
{
180+
unsigned int state;
181+
182+
/* TODO: secondary cache check <profile, profile, perm> */
183+
state = aa_dfa_next(profile->policy.dfa,
184+
profile->policy.start[AA_CLASS_SIGNAL],
185+
signal);
186+
state = aa_dfa_match(profile->policy.dfa, state, label);
187+
aa_compute_perms(profile->policy.dfa, state, perms);
188+
}
189+
190+
static int profile_signal_perm(struct aa_profile *profile,
191+
struct aa_profile *peer, u32 request,
192+
struct common_audit_data *sa)
193+
{
194+
struct aa_perms perms;
195+
196+
if (profile_unconfined(profile) ||
197+
!PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
198+
return 0;
199+
200+
aad(sa)->peer = &peer->label;
201+
profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
202+
&perms);
203+
aa_apply_modes_to_perms(profile, &perms);
204+
return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
205+
}
206+
207+
static int aa_signal_cross_perm(struct aa_profile *sender,
208+
struct aa_profile *target,
209+
struct common_audit_data *sa)
210+
{
211+
return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
212+
profile_signal_perm(target, sender, MAY_READ, sa));
213+
}
214+
215+
int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
216+
{
217+
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
218+
219+
aad(&sa)->signal = map_signal_num(sig);
220+
return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
221+
&sa);
222+
}

security/apparmor/lsm.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task,
656656
return error;
657657
}
658658

659+
static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
660+
int sig, u32 secid)
661+
{
662+
struct aa_label *cl, *tl;
663+
int error;
664+
665+
if (secid)
666+
/* TODO: after secid to label mapping is done.
667+
* Dealing with USB IO specific behavior
668+
*/
669+
return 0;
670+
cl = __begin_current_label_crit_section();
671+
tl = aa_get_task_label(target);
672+
error = aa_may_signal(cl, tl, sig);
673+
aa_put_label(tl);
674+
__end_current_label_crit_section(cl);
675+
676+
return error;
677+
}
678+
659679
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
660680
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
661681
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
@@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
697717
LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
698718

699719
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
720+
LSM_HOOK_INIT(task_kill, apparmor_task_kill),
700721
};
701722

702723
/*

0 commit comments

Comments
 (0)