Commit | Line | Data |
---|---|---|
0ed3b28a JJ |
1 | /* |
2 | * AppArmor security module | |
3 | * | |
4 | * This file contains AppArmor ipc mediation | |
5 | * | |
6 | * Copyright (C) 1998-2008 Novell/SUSE | |
b2d09ae4 | 7 | * Copyright 2009-2017 Canonical Ltd. |
0ed3b28a JJ |
8 | * |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License as | |
11 | * published by the Free Software Foundation, version 2 of the | |
12 | * License. | |
13 | */ | |
14 | ||
15 | #include <linux/gfp.h> | |
16 | #include <linux/ptrace.h> | |
17 | ||
18 | #include "include/audit.h" | |
19 | #include "include/capability.h" | |
d8889d49 | 20 | #include "include/cred.h" |
0ed3b28a | 21 | #include "include/policy.h" |
33f8bf58 | 22 | #include "include/ipc.h" |
cd1dbf76 | 23 | #include "include/sig_names.h" |
0ed3b28a | 24 | |
290f458a JJ |
25 | /** |
26 | * audit_ptrace_mask - convert mask to permission string | |
27 | * @buffer: buffer to write string to (NOT NULL) | |
28 | * @mask: permission mask to convert | |
29 | */ | |
30 | static void audit_ptrace_mask(struct audit_buffer *ab, u32 mask) | |
31 | { | |
32 | switch (mask) { | |
33 | case MAY_READ: | |
34 | audit_log_string(ab, "read"); | |
35 | break; | |
36 | case MAY_WRITE: | |
37 | audit_log_string(ab, "trace"); | |
38 | break; | |
39 | case AA_MAY_BE_READ: | |
40 | audit_log_string(ab, "readby"); | |
41 | break; | |
42 | case AA_MAY_BE_TRACED: | |
43 | audit_log_string(ab, "tracedby"); | |
44 | break; | |
45 | } | |
46 | } | |
47 | ||
0ed3b28a | 48 | /* call back to audit ptrace fields */ |
637f688d | 49 | static void audit_ptrace_cb(struct audit_buffer *ab, void *va) |
0ed3b28a JJ |
50 | { |
51 | struct common_audit_data *sa = va; | |
b2d09ae4 | 52 | |
290f458a JJ |
53 | if (aad(sa)->request & AA_PTRACE_PERM_MASK) { |
54 | audit_log_format(ab, " requested_mask="); | |
55 | audit_ptrace_mask(ab, aad(sa)->request); | |
56 | ||
57 | if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { | |
58 | audit_log_format(ab, " denied_mask="); | |
59 | audit_ptrace_mask(ab, aad(sa)->denied); | |
60 | } | |
61 | } | |
ef88a7ac | 62 | audit_log_format(ab, " peer="); |
637f688d JJ |
63 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, |
64 | FLAGS_NONE, GFP_ATOMIC); | |
0ed3b28a JJ |
65 | } |
66 | ||
0dda0b3f | 67 | /* assumes check for PROFILE_MEDIATES is already done */ |
290f458a JJ |
68 | /* TODO: conditionals */ |
69 | static int profile_ptrace_perm(struct aa_profile *profile, | |
0dda0b3f JJ |
70 | struct aa_label *peer, u32 request, |
71 | struct common_audit_data *sa) | |
290f458a JJ |
72 | { |
73 | struct aa_perms perms = { }; | |
74 | ||
0dda0b3f JJ |
75 | aad(sa)->peer = peer; |
76 | aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request, | |
290f458a JJ |
77 | &perms); |
78 | aa_apply_modes_to_perms(profile, &perms); | |
79 | return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); | |
80 | } | |
81 | ||
0dda0b3f JJ |
82 | static int profile_tracee_perm(struct aa_profile *tracee, |
83 | struct aa_label *tracer, u32 request, | |
84 | struct common_audit_data *sa) | |
0ed3b28a | 85 | { |
0dda0b3f JJ |
86 | if (profile_unconfined(tracee) || unconfined(tracer) || |
87 | !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE)) | |
88 | return 0; | |
89 | ||
90 | return profile_ptrace_perm(tracee, tracer, request, sa); | |
91 | } | |
92 | ||
93 | static int profile_tracer_perm(struct aa_profile *tracer, | |
94 | struct aa_label *tracee, u32 request, | |
95 | struct common_audit_data *sa) | |
96 | { | |
97 | if (profile_unconfined(tracer)) | |
98 | return 0; | |
99 | ||
290f458a | 100 | if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) |
0dda0b3f JJ |
101 | return profile_ptrace_perm(tracer, tracee, request, sa); |
102 | ||
103 | /* profile uses the old style capability check for ptrace */ | |
104 | if (&tracer->label == tracee) | |
b2d09ae4 | 105 | return 0; |
0ed3b28a | 106 | |
b2d09ae4 | 107 | aad(sa)->label = &tracer->label; |
0dda0b3f | 108 | aad(sa)->peer = tracee; |
b2d09ae4 JJ |
109 | aad(sa)->request = 0; |
110 | aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1); | |
ef88a7ac | 111 | |
b2d09ae4 | 112 | return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb); |
0ed3b28a JJ |
113 | } |
114 | ||
115 | /** | |
116 | * aa_may_ptrace - test if tracer task can trace the tracee | |
b2d09ae4 JJ |
117 | * @tracer: label of the task doing the tracing (NOT NULL) |
118 | * @tracee: task label to be traced | |
119 | * @request: permission request | |
0ed3b28a JJ |
120 | * |
121 | * Returns: %0 else error code if permission denied or error | |
122 | */ | |
b2d09ae4 JJ |
123 | int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, |
124 | u32 request) | |
0ed3b28a | 125 | { |
0dda0b3f JJ |
126 | struct aa_profile *profile; |
127 | u32 xrequest = request << PTRACE_PERM_SHIFT; | |
b2d09ae4 | 128 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE); |
0ed3b28a | 129 | |
0dda0b3f JJ |
130 | return xcheck_labels(tracer, tracee, profile, |
131 | profile_tracer_perm(profile, tracee, request, &sa), | |
132 | profile_tracee_perm(profile, tracer, xrequest, &sa)); | |
0ed3b28a JJ |
133 | } |
134 | ||
0ed3b28a | 135 | |
cd1dbf76 JJ |
136 | static inline int map_signal_num(int sig) |
137 | { | |
138 | if (sig > SIGRTMAX) | |
139 | return SIGUNKNOWN; | |
140 | else if (sig >= SIGRTMIN) | |
3acfd5f5 | 141 | return sig - SIGRTMIN + SIGRT_BASE; |
f7dc4c9a | 142 | else if (sig < MAXMAPPED_SIG) |
cd1dbf76 JJ |
143 | return sig_map[sig]; |
144 | return SIGUNKNOWN; | |
145 | } | |
146 | ||
147 | /** | |
148 | * audit_file_mask - convert mask to permission string | |
149 | * @buffer: buffer to write string to (NOT NULL) | |
150 | * @mask: permission mask to convert | |
151 | */ | |
152 | static void audit_signal_mask(struct audit_buffer *ab, u32 mask) | |
153 | { | |
154 | if (mask & MAY_READ) | |
155 | audit_log_string(ab, "receive"); | |
156 | if (mask & MAY_WRITE) | |
157 | audit_log_string(ab, "send"); | |
158 | } | |
159 | ||
160 | /** | |
161 | * audit_cb - call back for signal specific audit fields | |
162 | * @ab: audit_buffer (NOT NULL) | |
163 | * @va: audit struct to audit values of (NOT NULL) | |
164 | */ | |
165 | static void audit_signal_cb(struct audit_buffer *ab, void *va) | |
166 | { | |
167 | struct common_audit_data *sa = va; | |
168 | ||
169 | if (aad(sa)->request & AA_SIGNAL_PERM_MASK) { | |
170 | audit_log_format(ab, " requested_mask="); | |
171 | audit_signal_mask(ab, aad(sa)->request); | |
172 | if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) { | |
173 | audit_log_format(ab, " denied_mask="); | |
174 | audit_signal_mask(ab, aad(sa)->denied); | |
175 | } | |
176 | } | |
3acfd5f5 JJ |
177 | if (aad(sa)->signal == SIGUNKNOWN) |
178 | audit_log_format(ab, "signal=unknown(%d)", | |
179 | aad(sa)->unmappedsig); | |
180 | else if (aad(sa)->signal < MAXMAPPED_SIGNAME) | |
cd1dbf76 JJ |
181 | audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); |
182 | else | |
183 | audit_log_format(ab, " signal=rtmin+%d", | |
3acfd5f5 | 184 | aad(sa)->signal - SIGRT_BASE); |
cd1dbf76 JJ |
185 | audit_log_format(ab, " peer="); |
186 | aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, | |
187 | FLAGS_NONE, GFP_ATOMIC); | |
188 | } | |
189 | ||
cd1dbf76 | 190 | static int profile_signal_perm(struct aa_profile *profile, |
3dc6b1ce | 191 | struct aa_label *peer, u32 request, |
cd1dbf76 JJ |
192 | struct common_audit_data *sa) |
193 | { | |
194 | struct aa_perms perms; | |
3dc6b1ce | 195 | unsigned int state; |
cd1dbf76 JJ |
196 | |
197 | if (profile_unconfined(profile) || | |
198 | !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) | |
199 | return 0; | |
200 | ||
3dc6b1ce JJ |
201 | aad(sa)->peer = peer; |
202 | /* TODO: secondary cache check <profile, profile, perm> */ | |
203 | state = aa_dfa_next(profile->policy.dfa, | |
204 | profile->policy.start[AA_CLASS_SIGNAL], | |
205 | aad(sa)->signal); | |
206 | aa_label_match(profile, peer, state, false, request, &perms); | |
cd1dbf76 JJ |
207 | aa_apply_modes_to_perms(profile, &perms); |
208 | return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); | |
209 | } | |
210 | ||
cd1dbf76 JJ |
211 | int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) |
212 | { | |
3dc6b1ce | 213 | struct aa_profile *profile; |
cd1dbf76 JJ |
214 | DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); |
215 | ||
216 | aad(&sa)->signal = map_signal_num(sig); | |
3acfd5f5 | 217 | aad(&sa)->unmappedsig = sig; |
3dc6b1ce JJ |
218 | return xcheck_labels(sender, target, profile, |
219 | profile_signal_perm(profile, target, MAY_WRITE, &sa), | |
220 | profile_signal_perm(profile, sender, MAY_READ, &sa)); | |
cd1dbf76 | 221 | } |