Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+231
+7
security/apparmor/apparmorfs.c
··· 32 32 #include "include/audit.h" 33 33 #include "include/context.h" 34 34 #include "include/crypto.h" 35 + #include "include/ipc.h" 35 36 #include "include/policy_ns.h" 36 37 #include "include/label.h" 37 38 #include "include/policy.h" ··· 2130 2129 { } 2131 2130 }; 2132 2131 2132 + static struct aa_sfs_entry aa_sfs_entry_signal[] = { 2133 + AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK), 2134 + { } 2135 + }; 2136 + 2133 2137 static struct aa_sfs_entry aa_sfs_entry_domain[] = { 2134 2138 AA_SFS_FILE_BOOLEAN("change_hat", 1), 2135 2139 AA_SFS_FILE_BOOLEAN("change_hatv", 1), ··· 2185 2179 AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), 2186 2180 AA_SFS_DIR("caps", aa_sfs_entry_caps), 2187 2181 AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), 2182 + AA_SFS_DIR("signal", aa_sfs_entry_signal), 2188 2183 AA_SFS_DIR("query", aa_sfs_entry_query), 2189 2184 { } 2190 2185 };
+1
security/apparmor/include/apparmor.h
··· 28 28 #define AA_CLASS_RLIMITS 5 29 29 #define AA_CLASS_DOMAIN 6 30 30 #define AA_CLASS_PTRACE 9 31 + #define AA_CLASS_SIGNAL 10 31 32 #define AA_CLASS_LABEL 16 32 33 33 34 #define AA_CLASS_LAST AA_CLASS_LABEL
+2
security/apparmor/include/audit.h
··· 86 86 #define OP_SHUTDOWN "socket_shutdown" 87 87 88 88 #define OP_PTRACE "ptrace" 89 + #define OP_SIGNAL "signal" 89 90 90 91 #define OP_EXEC "exec" 91 92 ··· 127 126 long pos; 128 127 const char *ns; 129 128 } iface; 129 + int signal; 130 130 struct { 131 131 int rlim; 132 132 unsigned long max;
+6
security/apparmor/include/ipc.h
··· 27 27 28 28 #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ 29 29 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" 30 35 31 36 int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, 32 37 u32 request); 38 + int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); 33 39 34 40 #endif /* __AA_IPC_H */
+95
security/apparmor/include/sig_names.h
··· 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 +
+99
security/apparmor/ipc.c
··· 20 20 #include "include/context.h" 21 21 #include "include/policy.h" 22 22 #include "include/ipc.h" 23 + #include "include/sig_names.h" 23 24 24 25 /** 25 26 * audit_ptrace_mask - convert mask to permission string ··· 122 121 } 123 122 124 123 124 + static inline int map_signal_num(int sig) 125 + { 126 + if (sig > SIGRTMAX) 127 + return SIGUNKNOWN; 128 + else if (sig >= SIGRTMIN) 129 + return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */ 130 + else if (sig <= MAXMAPPED_SIG) 131 + return sig_map[sig]; 132 + return SIGUNKNOWN; 133 + } 134 + 135 + /** 136 + * audit_file_mask - convert mask to permission string 137 + * @buffer: buffer to write string to (NOT NULL) 138 + * @mask: permission mask to convert 139 + */ 140 + static void audit_signal_mask(struct audit_buffer *ab, u32 mask) 141 + { 142 + if (mask & MAY_READ) 143 + audit_log_string(ab, "receive"); 144 + if (mask & MAY_WRITE) 145 + audit_log_string(ab, "send"); 146 + } 147 + 148 + /** 149 + * audit_cb - call back for signal specific audit fields 150 + * @ab: audit_buffer (NOT NULL) 151 + * @va: audit struct to audit values of (NOT NULL) 152 + */ 153 + static void audit_signal_cb(struct audit_buffer *ab, void *va) 154 + { 155 + struct common_audit_data *sa = va; 156 + 157 + if (aad(sa)->request & AA_SIGNAL_PERM_MASK) { 158 + audit_log_format(ab, " requested_mask="); 159 + audit_signal_mask(ab, aad(sa)->request); 160 + if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) { 161 + audit_log_format(ab, " denied_mask="); 162 + audit_signal_mask(ab, aad(sa)->denied); 163 + } 164 + } 165 + if (aad(sa)->signal <= MAXMAPPED_SIG) 166 + audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); 167 + else 168 + audit_log_format(ab, " signal=rtmin+%d", 169 + aad(sa)->signal - 128); 170 + audit_log_format(ab, " peer="); 171 + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, 172 + FLAGS_NONE, GFP_ATOMIC); 173 + } 174 + 175 + /* TODO: update to handle compound name&name2, conditionals */ 176 + static void profile_match_signal(struct aa_profile *profile, const char *label, 177 + int signal, struct aa_perms *perms) 178 + { 179 + unsigned int state; 180 + 181 + /* TODO: secondary cache check <profile, profile, perm> */ 182 + state = aa_dfa_next(profile->policy.dfa, 183 + profile->policy.start[AA_CLASS_SIGNAL], 184 + signal); 185 + state = aa_dfa_match(profile->policy.dfa, state, label); 186 + aa_compute_perms(profile->policy.dfa, state, perms); 187 + } 188 + 189 + static int profile_signal_perm(struct aa_profile *profile, 190 + struct aa_profile *peer, u32 request, 191 + struct common_audit_data *sa) 192 + { 193 + struct aa_perms perms; 194 + 195 + if (profile_unconfined(profile) || 196 + !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) 197 + return 0; 198 + 199 + aad(sa)->peer = &peer->label; 200 + profile_match_signal(profile, peer->base.hname, aad(sa)->signal, 201 + &perms); 202 + aa_apply_modes_to_perms(profile, &perms); 203 + return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); 204 + } 205 + 206 + static int aa_signal_cross_perm(struct aa_profile *sender, 207 + struct aa_profile *target, 208 + struct common_audit_data *sa) 209 + { 210 + return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa), 211 + profile_signal_perm(target, sender, MAY_READ, sa)); 212 + } 213 + 214 + int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) 215 + { 216 + DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); 217 + 218 + aad(&sa)->signal = map_signal_num(sig); 219 + return xcheck_labels_profiles(sender, target, aa_signal_cross_perm, 220 + &sa); 221 + }
+21
security/apparmor/lsm.c
··· 656 656 return error; 657 657 } 658 658 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 + 659 679 static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { 660 680 LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), 661 681 LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), ··· 717 697 LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec), 718 698 719 699 LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), 700 + LSM_HOOK_INIT(task_kill, apparmor_task_kill), 720 701 }; 721 702 722 703 /*