]>
Commit | Line | Data |
---|---|---|
1 | From: John Johansen <jjohansen@suse.de> | |
2 | Subject: AppArmor: Simplified network controls for AppArmor | |
3 | ||
4 | Simple network control determining which network families a confined | |
5 | application has access to. | |
6 | ||
7 | Signed-off-by: John Johansen <jjohansen@suse.de> | |
8 | ||
9 | --- | |
10 | security/apparmor/Makefile | 7 + | |
11 | security/apparmor/apparmor.h | 9 ++ | |
12 | security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++- | |
13 | security/apparmor/main.c | 107 ++++++++++++++++++++++++++++- | |
14 | security/apparmor/module_interface.c | 26 ++++++- | |
15 | 5 files changed, 271 insertions(+), 7 deletions(-) | |
16 | ||
17 | --- a/security/apparmor/Makefile | |
18 | +++ b/security/apparmor/Makefile | |
19 | @@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l | |
20 | quiet_cmd_make-caps = GEN $@ | |
21 | cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ | |
22 | ||
23 | -$(obj)/main.o : $(obj)/capability_names.h | |
24 | +quiet_cmd_make-af = GEN $@ | |
25 | +cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ | |
26 | + | |
27 | +$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h | |
28 | $(obj)/capability_names.h : $(srctree)/include/linux/capability.h | |
29 | $(call cmd,make-caps) | |
30 | +$(obj)/af_names.h : $(srctree)/include/linux/socket.h | |
31 | + $(call cmd,make-af) | |
32 | --- a/security/apparmor/apparmor.h | |
33 | +++ b/security/apparmor/apparmor.h | |
34 | @@ -16,6 +16,8 @@ | |
35 | #include <linux/fs.h> | |
36 | #include <linux/binfmts.h> | |
37 | #include <linux/rcupdate.h> | |
38 | +#include <linux/socket.h> | |
39 | +#include <net/sock.h> | |
40 | ||
41 | /* | |
42 | * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags | |
43 | @@ -212,6 +214,9 @@ struct aa_profile { | |
44 | struct list_head task_contexts; | |
45 | spinlock_t lock; | |
46 | unsigned long int_flags; | |
47 | + u16 network_families[AF_MAX]; | |
48 | + u16 audit_network[AF_MAX]; | |
49 | + u16 quiet_network[AF_MAX]; | |
50 | }; | |
51 | ||
52 | extern struct list_head profile_ns_list; | |
53 | @@ -258,6 +263,7 @@ struct aa_audit { | |
54 | int request_mask, denied_mask, audit_mask; | |
55 | struct iattr *iattr; | |
56 | pid_t task, parent; | |
57 | + int family, type, protocol; | |
58 | int error_code; | |
59 | }; | |
60 | ||
61 | @@ -319,6 +325,9 @@ extern void aa_change_task_context(struc | |
62 | struct aa_profile *previous_profile); | |
63 | extern int aa_may_ptrace(struct aa_task_context *cxt, | |
64 | struct aa_profile *tracee); | |
65 | +extern int aa_net_perm(struct aa_profile *profile, char *operation, | |
66 | + int family, int type, int protocol); | |
67 | +extern int aa_revalidate_sk(struct sock *sk, char *operation); | |
68 | ||
69 | /* lsm.c */ | |
70 | extern int apparmor_initialized; | |
71 | --- a/security/apparmor/lsm.c | |
72 | +++ b/security/apparmor/lsm.c | |
73 | @@ -18,6 +18,7 @@ | |
74 | #include <linux/ctype.h> | |
75 | #include <linux/sysctl.h> | |
76 | #include <linux/audit.h> | |
77 | +#include <net/sock.h> | |
78 | ||
79 | #include "apparmor.h" | |
80 | #include "inline.h" | |
81 | @@ -680,6 +681,117 @@ static void apparmor_task_free_security( | |
82 | aa_release(task); | |
83 | } | |
84 | ||
85 | +static int apparmor_socket_create(int family, int type, int protocol, int kern) | |
86 | +{ | |
87 | + struct aa_profile *profile; | |
88 | + int error = 0; | |
89 | + | |
90 | + if (kern) | |
91 | + return 0; | |
92 | + | |
93 | + profile = aa_get_profile(current); | |
94 | + if (profile) | |
95 | + error = aa_net_perm(profile, "socket_create", family, | |
96 | + type, protocol); | |
97 | + aa_put_profile(profile); | |
98 | + | |
99 | + return error; | |
100 | +} | |
101 | + | |
102 | +static int apparmor_socket_post_create(struct socket *sock, int family, | |
103 | + int type, int protocol, int kern) | |
104 | +{ | |
105 | + struct sock *sk = sock->sk; | |
106 | + | |
107 | + if (kern) | |
108 | + return 0; | |
109 | + | |
110 | + return aa_revalidate_sk(sk, "socket_post_create"); | |
111 | +} | |
112 | + | |
113 | +static int apparmor_socket_bind(struct socket *sock, | |
114 | + struct sockaddr *address, int addrlen) | |
115 | +{ | |
116 | + struct sock *sk = sock->sk; | |
117 | + | |
118 | + return aa_revalidate_sk(sk, "socket_bind"); | |
119 | +} | |
120 | + | |
121 | +static int apparmor_socket_connect(struct socket *sock, | |
122 | + struct sockaddr *address, int addrlen) | |
123 | +{ | |
124 | + struct sock *sk = sock->sk; | |
125 | + | |
126 | + return aa_revalidate_sk(sk, "socket_connect"); | |
127 | +} | |
128 | + | |
129 | +static int apparmor_socket_listen(struct socket *sock, int backlog) | |
130 | +{ | |
131 | + struct sock *sk = sock->sk; | |
132 | + | |
133 | + return aa_revalidate_sk(sk, "socket_listen"); | |
134 | +} | |
135 | + | |
136 | +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) | |
137 | +{ | |
138 | + struct sock *sk = sock->sk; | |
139 | + | |
140 | + return aa_revalidate_sk(sk, "socket_accept"); | |
141 | +} | |
142 | + | |
143 | +static int apparmor_socket_sendmsg(struct socket *sock, | |
144 | + struct msghdr *msg, int size) | |
145 | +{ | |
146 | + struct sock *sk = sock->sk; | |
147 | + | |
148 | + return aa_revalidate_sk(sk, "socket_sendmsg"); | |
149 | +} | |
150 | + | |
151 | +static int apparmor_socket_recvmsg(struct socket *sock, | |
152 | + struct msghdr *msg, int size, int flags) | |
153 | +{ | |
154 | + struct sock *sk = sock->sk; | |
155 | + | |
156 | + return aa_revalidate_sk(sk, "socket_recvmsg"); | |
157 | +} | |
158 | + | |
159 | +static int apparmor_socket_getsockname(struct socket *sock) | |
160 | +{ | |
161 | + struct sock *sk = sock->sk; | |
162 | + | |
163 | + return aa_revalidate_sk(sk, "socket_getsockname"); | |
164 | +} | |
165 | + | |
166 | +static int apparmor_socket_getpeername(struct socket *sock) | |
167 | +{ | |
168 | + struct sock *sk = sock->sk; | |
169 | + | |
170 | + return aa_revalidate_sk(sk, "socket_getpeername"); | |
171 | +} | |
172 | + | |
173 | +static int apparmor_socket_getsockopt(struct socket *sock, int level, | |
174 | + int optname) | |
175 | +{ | |
176 | + struct sock *sk = sock->sk; | |
177 | + | |
178 | + return aa_revalidate_sk(sk, "socket_getsockopt"); | |
179 | +} | |
180 | + | |
181 | +static int apparmor_socket_setsockopt(struct socket *sock, int level, | |
182 | + int optname) | |
183 | +{ | |
184 | + struct sock *sk = sock->sk; | |
185 | + | |
186 | + return aa_revalidate_sk(sk, "socket_setsockopt"); | |
187 | +} | |
188 | + | |
189 | +static int apparmor_socket_shutdown(struct socket *sock, int how) | |
190 | +{ | |
191 | + struct sock *sk = sock->sk; | |
192 | + | |
193 | + return aa_revalidate_sk(sk, "socket_shutdown"); | |
194 | +} | |
195 | + | |
196 | static int apparmor_getprocattr(struct task_struct *task, char *name, | |
197 | char **value) | |
198 | { | |
199 | @@ -780,9 +892,6 @@ struct security_operations apparmor_ops | |
200 | .capable = apparmor_capable, | |
201 | .syslog = cap_syslog, | |
202 | ||
203 | - .netlink_send = cap_netlink_send, | |
204 | - .netlink_recv = cap_netlink_recv, | |
205 | - | |
206 | .bprm_apply_creds = cap_bprm_apply_creds, | |
207 | .bprm_set_security = apparmor_bprm_set_security, | |
208 | .bprm_secureexec = apparmor_bprm_secureexec, | |
209 | @@ -820,6 +929,20 @@ struct security_operations apparmor_ops | |
210 | ||
211 | .getprocattr = apparmor_getprocattr, | |
212 | .setprocattr = apparmor_setprocattr, | |
213 | + | |
214 | + .socket_create = apparmor_socket_create, | |
215 | + .socket_post_create = apparmor_socket_post_create, | |
216 | + .socket_bind = apparmor_socket_bind, | |
217 | + .socket_connect = apparmor_socket_connect, | |
218 | + .socket_listen = apparmor_socket_listen, | |
219 | + .socket_accept = apparmor_socket_accept, | |
220 | + .socket_sendmsg = apparmor_socket_sendmsg, | |
221 | + .socket_recvmsg = apparmor_socket_recvmsg, | |
222 | + .socket_getsockname = apparmor_socket_getsockname, | |
223 | + .socket_getpeername = apparmor_socket_getpeername, | |
224 | + .socket_getsockopt = apparmor_socket_getsockopt, | |
225 | + .socket_setsockopt = apparmor_socket_setsockopt, | |
226 | + .socket_shutdown = apparmor_socket_shutdown, | |
227 | }; | |
228 | ||
229 | void info_message(const char *str) | |
230 | --- a/security/apparmor/main.c | |
231 | +++ b/security/apparmor/main.c | |
232 | @@ -14,6 +14,9 @@ | |
233 | #include <linux/audit.h> | |
234 | #include <linux/mount.h> | |
235 | #include <linux/ptrace.h> | |
236 | +#include <linux/socket.h> | |
237 | +#include <linux/net.h> | |
238 | +#include <net/sock.h> | |
239 | ||
240 | #include "apparmor.h" | |
241 | ||
242 | @@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au | |
243 | audit_log_format(ab, " %s=\"%s::%s\"", name, user, other); | |
244 | } | |
245 | ||
246 | +static const char *address_families[] = { | |
247 | +#include "af_names.h" | |
248 | +}; | |
249 | + | |
250 | +static const char *sock_types[] = { | |
251 | + "unknown(0)", | |
252 | + "stream", | |
253 | + "dgram", | |
254 | + "raw", | |
255 | + "rdm", | |
256 | + "seqpacket", | |
257 | + "dccp", | |
258 | + "unknown(7)", | |
259 | + "unknown(8)", | |
260 | + "unknown(9)", | |
261 | + "packet", | |
262 | +}; | |
263 | + | |
264 | /** | |
265 | * aa_audit - Log an audit event to the audit subsystem | |
266 | * @profile: profile to check against | |
267 | @@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi | |
268 | audit_log_untrustedstring(ab, sa->name2); | |
269 | } | |
270 | ||
271 | - audit_log_format(ab, " pid=%d", current->pid); | |
272 | + if (sa->family || sa->type) { | |
273 | + if (address_families[sa->family]) | |
274 | + audit_log_format(ab, " family=\"%s\"", | |
275 | + address_families[sa->family]); | |
276 | + else | |
277 | + audit_log_format(ab, " family=\"unknown(%d)\"", | |
278 | + sa->family); | |
279 | + | |
280 | + if (sock_types[sa->type]) | |
281 | + audit_log_format(ab, " sock_type=\"%s\"", | |
282 | + sock_types[sa->type]); | |
283 | + else | |
284 | + audit_log_format(ab, " sock_type=\"unknown(%d)\"", | |
285 | + sa->type); | |
286 | + | |
287 | + audit_log_format(ab, " protocol=%d", sa->protocol); | |
288 | + } | |
289 | + | |
290 | + audit_log_format(ab, " pid=%d", current->pid); | |
291 | ||
292 | if (profile) { | |
293 | audit_log_format(ab, " profile="); | |
294 | @@ -767,6 +806,72 @@ int aa_link(struct aa_profile *profile, | |
295 | ||
296 | return error; | |
297 | } | |
298 | + | |
299 | +int aa_net_perm(struct aa_profile *profile, char *operation, | |
300 | + int family, int type, int protocol) | |
301 | +{ | |
302 | + struct aa_audit sa; | |
303 | + int error = 0; | |
304 | + u16 family_mask, audit_mask, quiet_mask; | |
305 | + | |
306 | + if ((family < 0) || (family >= AF_MAX)) | |
307 | + return -EINVAL; | |
308 | + | |
309 | + if ((type < 0) || (type >= SOCK_MAX)) | |
310 | + return -EINVAL; | |
311 | + | |
312 | + /* unix domain and netlink sockets are handled by ipc */ | |
313 | + if (family == AF_UNIX || family == AF_NETLINK) | |
314 | + return 0; | |
315 | + | |
316 | + family_mask = profile->network_families[family]; | |
317 | + audit_mask = profile->audit_network[family]; | |
318 | + quiet_mask = profile->quiet_network[family]; | |
319 | + | |
320 | + error = (family_mask & (1 << type)) ? 0 : -EACCES; | |
321 | + | |
322 | + memset(&sa, 0, sizeof(sa)); | |
323 | + sa.operation = operation; | |
324 | + sa.gfp_mask = GFP_KERNEL; | |
325 | + sa.family = family; | |
326 | + sa.type = type; | |
327 | + sa.protocol = protocol; | |
328 | + sa.error_code = error; | |
329 | + | |
330 | + if (likely(!error)) { | |
331 | + if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask)) | |
332 | + return 0; | |
333 | + } else if (!((1 << type) & ~quiet_mask)) { | |
334 | + return error; | |
335 | + } | |
336 | + | |
337 | + error = aa_audit(profile, &sa); | |
338 | + | |
339 | + return error; | |
340 | +} | |
341 | + | |
342 | +int aa_revalidate_sk(struct sock *sk, char *operation) | |
343 | +{ | |
344 | + struct aa_profile *profile; | |
345 | + int error = 0; | |
346 | + | |
347 | + /* this is some debugging code to flush out the network hooks that | |
348 | + that are called in interrupt context */ | |
349 | + if (in_interrupt()) { | |
350 | + printk("AppArmor Debug: Hook being called from interrupt context\n"); | |
351 | + dump_stack(); | |
352 | + return 0; | |
353 | + } | |
354 | + | |
355 | + profile = aa_get_profile(current); | |
356 | + if (profile) | |
357 | + error = aa_net_perm(profile, operation, | |
358 | + sk->sk_family, sk->sk_type, | |
359 | + sk->sk_protocol); | |
360 | + aa_put_profile(profile); | |
361 | + | |
362 | + return error; | |
363 | +} | |
364 | ||
365 | /******************************* | |
366 | * Global task related functions | |
367 | --- a/security/apparmor/module_interface.c | |
368 | +++ b/security/apparmor/module_interface.c | |
369 | @@ -321,8 +321,8 @@ static struct aa_profile *aa_unpack_prof | |
370 | struct aa_audit *sa) | |
371 | { | |
372 | struct aa_profile *profile = NULL; | |
373 | - | |
374 | - int error = -EPROTO; | |
375 | + size_t size = 0; | |
376 | + int i, error = -EPROTO; | |
377 | ||
378 | profile = alloc_aa_profile(); | |
379 | if (!profile) | |
380 | @@ -355,6 +355,28 @@ static struct aa_profile *aa_unpack_prof | |
381 | if (!aa_is_u32(e, &(profile->set_caps), NULL)) | |
382 | goto fail; | |
383 | ||
384 | + size = aa_is_array(e, "net_allowed_af"); | |
385 | + if (size) { | |
386 | + if (size > AF_MAX) | |
387 | + goto fail; | |
388 | + | |
389 | + for (i = 0; i < size; i++) { | |
390 | + if (!aa_is_u16(e, &profile->network_families[i], NULL)) | |
391 | + goto fail; | |
392 | + if (!aa_is_u16(e, &profile->audit_network[i], NULL)) | |
393 | + goto fail; | |
394 | + if (!aa_is_u16(e, &profile->quiet_network[i], NULL)) | |
395 | + goto fail; | |
396 | + } | |
397 | + if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) | |
398 | + goto fail; | |
399 | + /* allow unix domain and netlink sockets they are handled | |
400 | + * by IPC | |
401 | + */ | |
402 | + } | |
403 | + profile->network_families[AF_UNIX] = 0xffff; | |
404 | + profile->network_families[AF_NETLINK] = 0xffff; | |
405 | + | |
406 | /* get file rules */ | |
407 | profile->file_rules = aa_unpack_dfa(e); | |
408 | if (IS_ERR(profile->file_rules)) { |