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