]>
Commit | Line | Data |
---|---|---|
daaa955e AM |
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 | |
5 | ||
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. | |
10 | ||
11 | The optimization of skipping the cross check when policy is guaranteed | |
12 | to be consistent (single compile unit) remains to be done. | |
13 | ||
14 | policy rules have the form of | |
15 | SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ] | |
16 | [ SIGNAL SET ] [ SIGNAL PEER ] | |
17 | ||
18 | SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST | |
19 | ||
20 | SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL | |
21 | ACCESS ')' | |
22 | ||
23 | SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' | | |
24 | 'receive' ) | |
25 | ||
26 | SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')' | |
27 | ||
28 | SIGNAL LIST = Comma or space separated list of SIGNALS | |
29 | ||
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' | |
36 | ) | |
37 | ||
38 | SIGNAL PEER = 'peer' '=' AARE | |
39 | ||
40 | eg. | |
41 | signal, # allow all signals | |
42 | signal send set=(hup, kill) peer=foo, | |
43 | ||
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) | |
47 | --- | |
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 | |
57 | ||
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 | |
62 | @@ -32,6 +32,7 @@ | |
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[] = { | |
71 | { } | |
72 | }; | |
73 | ||
74 | +static struct aa_sfs_entry aa_sfs_entry_signal[] = { | |
75 | + AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK), | |
76 | + { } | |
77 | +}; | |
78 | + | |
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), | |
88 | { } | |
89 | }; | |
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 | |
94 | @@ -28,6 +28,7 @@ | |
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 | |
100 | ||
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" | |
108 | ||
109 | #define OP_PTRACE "ptrace" | |
110 | +#define OP_SIGNAL "signal" | |
111 | ||
112 | #define OP_EXEC "exec" | |
113 | ||
114 | @@ -126,6 +127,7 @@ struct apparmor_audit_data { | |
115 | long pos; | |
116 | const char *ns; | |
117 | } iface; | |
118 | + int signal; | |
119 | struct { | |
120 | int rlim; | |
121 | unsigned long max; | |
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; | |
127 | ||
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) | |
131 | + | |
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" | |
135 | ||
136 | int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, | |
137 | u32 request); | |
138 | +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); | |
139 | ||
140 | #endif /* __AA_IPC_H */ | |
141 | diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h | |
142 | new file mode 100644 | |
143 | index 000000000000..0d4395f231ca | |
144 | --- /dev/null | |
145 | +++ b/security/apparmor/include/sig_names.h | |
146 | @@ -0,0 +1,95 @@ | |
147 | +#include <linux/signal.h> | |
148 | + | |
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 | |
154 | + */ | |
155 | +static const int sig_map[MAXMAPPED_SIG] = { | |
156 | + [0] = MAXMAPPED_SIG, /* existence test */ | |
157 | + [SIGHUP] = 1, | |
158 | + [SIGINT] = 2, | |
159 | + [SIGQUIT] = 3, | |
160 | + [SIGILL] = 4, | |
161 | + [SIGTRAP] = 5, /* -, 5, - */ | |
162 | + [SIGABRT] = 6, /* SIGIOT: -, 6, - */ | |
163 | + [SIGBUS] = 7, /* 10, 7, 10 */ | |
164 | + [SIGFPE] = 8, | |
165 | + [SIGKILL] = 9, | |
166 | + [SIGUSR1] = 10, /* 30, 10, 16 */ | |
167 | + [SIGSEGV] = 11, | |
168 | + [SIGUSR2] = 12, /* 31, 12, 17 */ | |
169 | + [SIGPIPE] = 13, | |
170 | + [SIGALRM] = 14, | |
171 | + [SIGTERM] = 15, | |
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, -, - */ | |
187 | +#ifdef SIGSYS | |
188 | + [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */ | |
189 | +#endif | |
190 | +#ifdef SIGEMT | |
191 | + [SIGEMT] = 32, /* 7, - , 7 */ | |
192 | +#endif | |
193 | +#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */ | |
194 | + [SIGLOST] = 33, /* unused on Linux */ | |
195 | +#endif | |
196 | +#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS | |
197 | + [SIGUNUSED] = 34, /* -, 31, - */ | |
198 | +#endif | |
199 | +}; | |
200 | + | |
201 | +/* this table is ordered post sig_map[sig] mapping */ | |
202 | +static const char *const sig_names[MAXMAPPED_SIG + 1] = { | |
203 | + "unknown", | |
204 | + "hup", | |
205 | + "int", | |
206 | + "quit", | |
207 | + "ill", | |
208 | + "trap", | |
209 | + "abrt", | |
210 | + "bus", | |
211 | + "fpe", | |
212 | + "kill", | |
213 | + "usr1", | |
214 | + "segv", | |
215 | + "usr2", | |
216 | + "pipe", | |
217 | + "alrm", | |
218 | + "term", | |
219 | + "stkflt", | |
220 | + "chld", | |
221 | + "cont", | |
222 | + "stop", | |
223 | + "stp", | |
224 | + "ttin", | |
225 | + "ttou", | |
226 | + "urg", | |
227 | + "xcpu", | |
228 | + "xfsz", | |
229 | + "vtalrm", | |
230 | + "prof", | |
231 | + "winch", | |
232 | + "io", | |
233 | + "pwr", | |
234 | + "sys", | |
235 | + "emt", | |
236 | + "lost", | |
237 | + "unused", | |
238 | + | |
239 | + "exists", /* always last existence test mapped to MAXMAPPED_SIG */ | |
240 | +}; | |
241 | + | |
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 | |
246 | @@ -20,6 +20,7 @@ | |
247 | #include "include/context.h" | |
248 | #include "include/policy.h" | |
249 | #include "include/ipc.h" | |
250 | +#include "include/sig_names.h" | |
251 | ||
252 | /** | |
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, | |
255 | } | |
256 | ||
257 | ||
258 | +static inline int map_signal_num(int sig) | |
259 | +{ | |
260 | + if (sig > SIGRTMAX) | |
261 | + return SIGUNKNOWN; | |
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]; | |
266 | + return SIGUNKNOWN; | |
267 | +} | |
268 | + | |
269 | +/** | |
270 | + * audit_file_mask - convert mask to permission string | |
271 | + * @buffer: buffer to write string to (NOT NULL) | |
272 | + * @mask: permission mask to convert | |
273 | + */ | |
274 | +static void audit_signal_mask(struct audit_buffer *ab, u32 mask) | |
275 | +{ | |
276 | + if (mask & MAY_READ) | |
277 | + audit_log_string(ab, "receive"); | |
278 | + if (mask & MAY_WRITE) | |
279 | + audit_log_string(ab, "send"); | |
280 | +} | |
281 | + | |
282 | +/** | |
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) | |
286 | + */ | |
287 | +static void audit_signal_cb(struct audit_buffer *ab, void *va) | |
288 | +{ | |
289 | + struct common_audit_data *sa = va; | |
290 | + | |
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); | |
297 | + } | |
298 | + } | |
299 | + if (aad(sa)->signal <= MAXMAPPED_SIG) | |
300 | + audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); | |
301 | + else | |
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); | |
307 | +} | |
308 | + | |
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) | |
312 | +{ | |
313 | + unsigned int state; | |
314 | + | |
315 | + /* TODO: secondary cache check <profile, profile, perm> */ | |
316 | + state = aa_dfa_next(profile->policy.dfa, | |
317 | + profile->policy.start[AA_CLASS_SIGNAL], | |
318 | + signal); | |
319 | + state = aa_dfa_match(profile->policy.dfa, state, label); | |
320 | + aa_compute_perms(profile->policy.dfa, state, perms); | |
321 | +} | |
322 | + | |
323 | +static int profile_signal_perm(struct aa_profile *profile, | |
324 | + struct aa_profile *peer, u32 request, | |
325 | + struct common_audit_data *sa) | |
326 | +{ | |
327 | + struct aa_perms perms; | |
328 | + | |
329 | + if (profile_unconfined(profile) || | |
330 | + !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) | |
331 | + return 0; | |
332 | + | |
333 | + aad(sa)->peer = &peer->label; | |
334 | + profile_match_signal(profile, peer->base.hname, aad(sa)->signal, | |
335 | + &perms); | |
336 | + aa_apply_modes_to_perms(profile, &perms); | |
337 | + return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); | |
338 | +} | |
339 | + | |
340 | +static int aa_signal_cross_perm(struct aa_profile *sender, | |
341 | + struct aa_profile *target, | |
342 | + struct common_audit_data *sa) | |
343 | +{ | |
344 | + return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa), | |
345 | + profile_signal_perm(target, sender, MAY_READ, sa)); | |
346 | +} | |
347 | + | |
348 | +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) | |
349 | +{ | |
350 | + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); | |
351 | + | |
352 | + aad(&sa)->signal = map_signal_num(sig); | |
353 | + return xcheck_labels_profiles(sender, target, aa_signal_cross_perm, | |
354 | + &sa); | |
355 | +} | |
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, | |
361 | return error; | |
362 | } | |
363 | ||
364 | +static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, | |
365 | + int sig, u32 secid) | |
366 | +{ | |
367 | + struct aa_label *cl, *tl; | |
368 | + int error; | |
369 | + | |
370 | + if (secid) | |
371 | + /* TODO: after secid to label mapping is done. | |
372 | + * Dealing with USB IO specific behavior | |
373 | + */ | |
374 | + return 0; | |
375 | + cl = __begin_current_label_crit_section(); | |
376 | + tl = aa_get_task_label(target); | |
377 | + error = aa_may_signal(cl, tl, sig); | |
378 | + aa_put_label(tl); | |
379 | + __end_current_label_crit_section(cl); | |
380 | + | |
381 | + return error; | |
382 | +} | |
383 | + | |
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), | |
389 | ||
390 | LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), | |
391 | + LSM_HOOK_INIT(task_kill, apparmor_task_kill), | |
392 | }; | |
393 | ||
394 | /* | |
395 | -- | |
396 | 2.11.0 | |
397 |